jaquadro / LilyPath

A 2D path and shape drawing library for MonoGame and XNA.
MIT License
93 stars 12 forks source link

Fill Path does not provide a source rectangle? #11

Closed ghost closed 6 years ago

ghost commented 6 years ago

Hey there,

Really loving this library with all my heart. I'm currently working on a game that requires polygons - I draw these using DrawBatch.FillPath(TextureBrush, Polygon) - problem is that when the polygon moves, so does the texture inside. It seems that Polygon both references the position in the texture (using a modular type repetition) and also the position on the screen.

Would this be a conflicting issue with the 2D Camera matrix, or am I missing something?

c826dea35d259d7c2947dc063a187451

Regards,

jaquadro commented 6 years ago

You might need to provide more information on how you're batching these draw calls.

My first thought is you might get around this by drawing each polygon in its own batch, drawing the polygon at a fixed location relative to origin, and then using transforms to position them in the proper location on the screen.

Probably for simplicity, the API never surfaced more complex ways of handling UV coordinates. There's a few ways it could be dealt with of varying difficulty. Easiest might to to try and encode an offset in the texture brush, which might cover this case, but I don't know if it would be adequate if rotation were introduced.

My analysis of all this might be a bit rusty. It's been a few years since I did any major work on this.

ghost commented 6 years ago

You might need to provide more information on how you're batching these draw calls.

I've got a Texture of an asteroid that can be repeated in each direction, and I've got a polygon that I draw at the position it's at.

So I'll start off calling the draw batch like so (using a 2D camera matrix):

`

        DrawBatch.Begin(DrawSortMode.Deferred, null, null, null, null, null, Camera.Matrix);

`

Which is then proceeded by a call to the draw batch.

`
drawbatch.FillPath(Brush, BoundVertices);

`

drawing the polygon at a fixed location relative to origin, and then using transforms to position them in the proper location on the screen.

The texture is not placed at any different position if I change the transform matrix, now I don't mean to be rude but I also fail to understand how adding a matrix transform will change the way a triangulated polygon is drawn with a texture.

Edit:

The texture is not placed at any different position if I change the transform matrix, now I don't mean to be rude but I also fail to understand how adding a matrix transform will change the way a triangulated polygon is drawn with a texture.

With which I mean - if I didn't add a matrix, it should always be rendering the texture from the top left, to the bottom right, it should not be related to the positioning of my polygon. At least, this is back when I used to draw polygons myself.

Included is an example in Monogame (Visual Studio 2017), void of any camera matrix, in which you can see the issue more clearly.

Game1.zip

jaquadro commented 6 years ago

When you call one of the draw functions, the position elements of the vertex array are the same points you provide. The UV elements are also assigned the same values, which also causes your texture to wrap when you're dealing with values larger than 0..width/height (UVs are internally scaled relative to texture dimensions). This is what gives the appearance of your texture being fixed to the world and your polygons "sliding over it". That's because it basically is.

So it can be influenced by how the world or camera matrix are setup ahead of the draw call.

Alternatively, now that I'm looking closer at the code, the Tranform property on the TextureBrush may already be enough to provide what you want. Before each FillPath call, update the position component of the Transform property of the brush, so the texture will be offset by the same amount. Then it should track the moving asteroid. You can tweak the transform further if scale is an issue.

jaquadro commented 6 years ago

I did some testing with your example, and my advice mostly holds. I did need to make a small tweak to the project, which I've just checked in changes for. But basically, you'd change your drawing block to

var brush = new TextureBrush(texture);
brush.Transform = Matrix.CreateTranslation(new Vector3(-Path[0].X, -Path[0].Y, 0)) * brush.Transform;

drawBatch.FillPath(brush, Path);

The change to LilyPath is that the TextureBrush transform is by default a scale matrix of 1/W, 1/H, which you'd multiply against your offset translation.

ghost commented 6 years ago

Cheers! Works great now.