Entrio Jul 15, 2013 at 8:09 AM Edited Jul 15, 2013 at 10:54 AM After discussing my problem here, I was able to get a basic bounding box for a shape. However i now have trouble re-calculating the position of the bounding box after moving the body. Please see video and screenshots below: Here is a bounding box for my object Here is how I calculate it (Thanks HAL_9000 PS: I am aware of the spelling mistake :) Note: My "terrrain" consists of kinematic bodies Here is my issue Youtube Video ```Body selectedBody = null; Fixture selectedFicture = null; Vector2[] boundingBox; ``` ```public void AttemptSelect(Vector2 Point) { List selectionBoxes = new List(); Vector2[] ret = null; Fixture f = World.TestPoint(Point); if (f != null) { ObjectSelected = true; selectedFicture = f; Body b = f.Body; for (int x = 0; x < b.FixtureList.Count; x++) { AABB aabb; f = b.FixtureList[x]; f.GetAABB(out aabb, 0); if (ret == null) ret = aabb.Vertices.ToArray(); selectionBoxes.Add(aabb.Vertices.ToArray()); } for (int x = 0; x < selectionBoxes.Count; x++) { Vector2[] v = selectionBoxes[x]; for (int y = 0; y < v.Length; y++) { //Top Left if (v[y].X < ret[0].X) ret[0].X = v[y].X; if (v[y].Y < ret[0].Y) ret[0].Y = v[y].Y; //Top Right if (v[y].X > ret[1].X) ret[1].X = v[y].X; if (v[y].Y < ret[1].Y) ret[1].Y = v[y].Y; //Bottom Right if (v[y].X > ret[2].X) ret[2].X = v[y].X; if (v[y].Y > ret[2].Y) ret[2].Y = v[y].Y; //Bottom Left if (v[y].X < ret[3].X) ret[3].X = v[y].X; if (v[y].Y > ret[3].Y) ret[3].Y = v[y].Y; } } boundingBox = ret; }else { ObjectSelected = false; selectedFicture = null; boundingBox = null; } } ``` I then attempt to move it the following way ```// Manipulate teh selected object if (level.ObjectSelected) { Vector2 newPos = new Vector2(); if (input.KeyboardState.IsKeyDown(Keys.Left)) { newPos.X -= 0.002f; } if (input.KeyboardState.IsKeyDown(Keys.Right)) { newPos.X += 0.002f; } if (input.KeyboardState.IsKeyDown(Keys.Up)) { newPos.Y -= 0.002f; } if (input.KeyboardState.IsKeyDown(Keys.Down)) { newPos.Y += 0.002f; } //newPos = ConvertUnits.ToDisplayUnits(newPos); level.MoveSelectedObject(newPos); } public void MoveSelectedObject(Vector2 moveAmount) { selectedFicture.Body.Position += moveAmount; } ``` And here is how I attempt to recalculate the boundingbox's position ```void UpdateSelectionBox() { if(selectedFicture != null) { List selectionBoxes = new List(); Vector2[] ret = null; Fixture f = selectedFicture; Body b = f.Body; for (int x = 0; x < b.FixtureList.Count; x++) { AABB aabb; f = b.FixtureList[x]; f.GetAABB(out aabb, 0); if (ret == null) ret = aabb.Vertices.ToArray(); selectionBoxes.Add(aabb.Vertices.ToArray()); } for (int x = 0; x < selectionBoxes.Count; x++) { Vector2[] v = selectionBoxes[x]; for (int y = 0; y < v.Length; y++) { //Top Left if (v[y].X < ret[0].X) ret[0].X = v[y].X; if (v[y].Y < ret[0].Y) ret[0].Y = v[y].Y; //Top Right if (v[y].X > ret[1].X) ret[1].X = v[y].X; if (v[y].Y < ret[1].Y) ret[1].Y = v[y].Y; //Bottom Right if (v[y].X > ret[2].X) ret[2].X = v[y].X; if (v[y].Y > ret[2].Y) ret[2].Y = v[y].Y; //Bottom Left if (v[y].X < ret[3].X) ret[3].X = v[y].X; if (v[y].Y > ret[3].Y) ret[3].Y = v[y].Y; } } boundingBox = ret; } } ``` Also, here is my draw logic ```if(ObjectSelected) if(boundingBox != null) { // draw teh bounding box Boolean lastIteration = false; int count = boundingBox.Count(); for(int b = 1; b <= count; b++) { if(b == count) { lastIteration = true; spriteBatch.DrawLine(lineTexture, ConvertUnits.ToDisplayUnits(boundingBox[b - 1]), ConvertUnits.ToDisplayUnits(boundingBox[0])); }else { spriteBatch.DrawLine(lineTexture, ConvertUnits.ToDisplayUnits(boundingBox[b - 1]), ConvertUnits.ToDisplayUnits(boundingBox[b])); } } } ``` HAL_9000 Jul 15, 2013 at 11:57 PM Strange because my demo tracks the body around the screen. Any chance you can upload your project as a zip somewhere? Or a testbed screen with the logic you're using? genbox Jul 16, 2013 at 3:39 AM I've not followed your progress, as I'm busy with other things, so I'll just post a note: A body is basically just a point in space with some properties. That point in space also determines the position of the fixtures, as they are constantly translated (moved) by the coordinates of the body. Quick example: A rectangle shape has the following properties: width = 1, height = 2, position = (0,0) The shape is attached with a fixture, to a body with the following properties: position = (0,0). Then you move the body to (5,5), where is the shape now? at (5,5)? - No, it is still at (0,0). But why is that? It is because the shape uses its own coordinate system, and when a body moves, it does not affect the shape. To get the real position of the shape, you have to get the transform (translation/movement & rotation/angle) of the body, and apply it to the shape. In your case, you need to get the vertices of the fixtures and transform them by the body transform - then you can calculate an enclosing AABB. Entrio Jul 16, 2013 at 6:25 PM Thanks guys, for some reason I omitted the transform bit out. Works perfect now! Just a quick note, to get the position at which to draw my texture I have to apply the transform also? HAL_9000 Jul 16, 2013 at 6:39 PM No you don't need the transform for that. ConvertUnits.ToDisplayUnits(body.Position) is all you should need. Entrio Jul 16, 2013 at 6:46 PM Edited Jul 16, 2013 at 7:09 PM Well, that's what I also thought but for some reason when I create the body, its position is at 0,0. Here is my code ```public EditorPhysicsObject(Level level, Vertices verts) { levelRef = level; this.Verticies = verts; Name = String.Format("terrain_{0}_{1}", DateTime.Now.Second, DateTime.Now.Millisecond); // Create it!!! List list = BayazitDecomposer.ConvexPartition(Verticies); body = BodyFactory.CreateCompoundPolygon(levelRef.World, list, 0.1f); body.BodyType = BodyType.Kinematic; body.UserData = this; fixtures = body.FixtureList; texture = levelRef.Services.AssetCreator.TextureFromVertices(verts, MaterialType.Waves, Color.White, 1.0f); } ``` and here is a breakpoint after the body creation, as you can see, position is 0,0 here is what it looks like and here is my draw code ```(public void Draw(SpriteBatch spriteBatch, Texture2D lineTexture) { Transform trans; Body.GetTransform(out trans); if(Selected) { int count = BoundingBox.Count(); for (int b = 1; b <= count; b++) { if (b == count) spriteBatch.DrawLine(lineTexture, ConvertUnits.ToDisplayUnits(BoundingBox[b - 1]), ConvertUnits.ToDisplayUnits(BoundingBox[0])); else spriteBatch.DrawLine(lineTexture, ConvertUnits.ToDisplayUnits(BoundingBox[b - 1]), ConvertUnits.ToDisplayUnits(BoundingBox[b])); } } spriteBatch.Draw(texture, ConvertUnits.ToDisplayUnits(body.Position), Color.White); }) ``` Entrio Jul 16, 2013 at 7:10 PM Edited Jul 16, 2013 at 7:11 PM However when I move the body, the textures moves also. i think this might have something to do when i create the polygon from vertices.... Do i need to manually specify polygon position or does it take into the account the position of vertices in the world space? HAL_9000 Jul 16, 2013 at 8:19 PM Farseer uses the center of the body when positioning objects but XNA uses the top-left of the texture. You can specify an origin in the draw method to compensate. You can work out the origin for an irregular shape like this: `````` Vector2 centroid = -this.Verticies.GetCentroid(); this.Verticies.Translate(ref centroid); Vector2 Origin = -centroid; `````` And the draw: `````` sb.Draw(theTexture, ConvertUnits.ToDisplayUnits(sprite.Body.Position), null, Color.White, sprite.Body.Rotation, Origin, 1f, SpriteEffects.None, 1f); `````` Entrio Jul 19, 2013 at 11:12 AM Edited Jul 19, 2013 at 11:18 AM for some reason I am unable to get it to work. My body is created at 0,0 when i create it from the vertices. Here is a video describing my issue. Here is my creation code: ```Vector2 Centroid; Body body; public EditorPhysicsObject(Level level, Vertices verts) { levelRef = level; this.Verticies = verts; Name = String.Format("terrain_{0}_{1}", DateTime.Now.Second, DateTime.Now.Millisecond); // Create it!!! List list = BayazitDecomposer.ConvexPartition(Verticies); body = BodyFactory.CreateCompoundPolygon(levelRef.World, list, 0.1f); body.BodyType = BodyType.Kinematic; body.UserData = this; fixtures = body.FixtureList; texture = levelRef.Services.AssetCreator.TextureFromVertices(verts, MaterialType.Test, Color.White, 5.0f); Centroid = -verts.GetCentroid(); this.Verticies.Translate(ref Centroid); Centroid = ConvertUnits.ToDisplayUnits(-Centroid); } ``` Here is my draw code ``` public void Draw(SpriteBatch spriteBatch, Texture2D lineTexture) { Color c = Color.White; if(Selected) { c = Color.GreenYellow; int count = BoundingBox.Count(); for (int b = 1; b <= count; b++) { if (b == count) spriteBatch.DrawLine(lineTexture, ConvertUnits.ToDisplayUnits(BoundingBox[b - 1]), ConvertUnits.ToDisplayUnits(BoundingBox[0])); else spriteBatch.DrawLine(lineTexture, ConvertUnits.ToDisplayUnits(BoundingBox[b - 1]), ConvertUnits.ToDisplayUnits(BoundingBox[b])); } } Vector2 pos = ConvertUnits.ToDisplayUnits(body.Position); spriteBatch.Draw(texture, new Rectangle((int)pos.X, (int)pos.Y, texture.Width, texture.Height), null, c, 0f, Centroid, SpriteEffects.None, 0); } ``` Unfortunately I cant upload teh project somewhere as its a part of a large solution HAL_9000 Jul 21, 2013 at 7:09 PM From the looks of it verts is the variable holding the vertices for the polygon you're trying to make. So I think you need to change the line: this.Verticies.Translate(ref Centroid); to verts.Translate(ref Centroid); Also, if memory serves you shouldn't need to be using ConvertUnits on the line Centroid = ConvertUnits.ToDisplayUnits(-Centroid); As the vertices are the actual size of the polygon, not the sim units. Entrio Jul 22, 2013 at 3:16 AM Thanks for taking your time to have a look but I have tried the said solutions and its not working. It still seems that my body.position is always at 0,0 upon creation. I have uploaded my entire solution, the issue I am having is in the CS2Editor.EditorPhysicsObject Counterstrike2D.rar (10.4 MB) https://mega.co.nz/#!jwgXRQRB!byYcJ31we5OvhC0QpLo8vitiP95tqCrV5uKzPZzV7aE I am totaly at a loss why my shape is at the right place but body always get created at 0,0 HAL_9000 Jul 22, 2013 at 5:49 AM Bodies are always created at 0,0. You specify where you want them to go after they're created. The problem is the centroid can't be calculated so the body position / texture position + origin never match. It looks like the verts aren't right. Something like not clockwise / concave / translated or something. Hopefully someone far smarter than I will read this and explain why. I couldn't figure it out so I just cheated and used the awesome tools in the engine ^^ Basically it generates the texture from the verts got by the mouse and generates the body from the texture using the tried and tested PolygonTools. `````` public EditorPhysicsObject(Level level, Vertices verts) { levelRef = level; Name = String.Format("terrain_{0}_{1}", DateTime.Now.Second, DateTime.Now.Millisecond); texture = levelRef.Services.AssetCreator.TextureFromVertices(verts, MaterialType.Test, Color.White, 5.0f); uint[] data = new uint[texture.Width * texture.Height]; texture.GetData(data); Vertices textureVertice = PolygonTools.CreatePolygon(data, texture.Width, true); Centroid = -textureVertice.GetCentroid(); textureVertice.Translate(ref Centroid); Centroid= -Centroid; //Here we simplify the vertices //This is totally optional but can save loads of vertices being created as most aren't needed to make the shape. //You may need to tweak the value passed to the procedure //Pass 1 if you want maximum detail //Pass higher numbers to get progressively less detail (currently 600) //Or just comment this line out and swap the comments on the lines below it Vertices simplifiedVerts = FarseerPhysics.Common.PolygonManipulation.SimplifyTools.ReduceByDistance(textureVertice, 600f); // List ListOfVerts = BayazitDecomposer.ConvexPartition(textureVertice); List ListOfVerts = BayazitDecomposer.ConvexPartition(simplifiedVerts); Vector2 vertScale = new Vector2(ConvertUnits.ToSimUnits(1)) * 1f; foreach (Vertices vertices in ListOfVerts) { vertices.Scale(ref vertScale); } body = BodyFactory.CreateCompoundPolygon(levelRef.World, ListOfVerts, 0.1f); body.BodyType = BodyType.Kinematic; body.UserData = this; fixtures = body.FixtureList; } `````` Entrio Jul 22, 2013 at 7:25 AM Thanks HAL for your workaround, it will have to do for now i guess, I would however prefer the bodies to be drawn where the vertices are and not at 0,0 Thanks all the same, maybe genbox or other devs might be able to help me out HAL_9000 Jul 22, 2013 at 8:05 PM The engine will never do that for you. You have to specify where you want the bodies to go. Here's a quick function. It's not perfect but should get you in the right direction. `````` public EditorPhysicsObject(Level level, Vertices verts) { levelRef = level; Name = String.Format("terrain_{0}_{1}", DateTime.Now.Second, DateTime.Now.Millisecond); texture = levelRef.Services.AssetCreator.TextureFromVertices(verts, MaterialType.Test, Color.White, 5.0f); uint[] data = new uint[texture.Width * texture.Height]; texture.GetData(data); Vertices textureVertice = PolygonTools.CreatePolygon(data, texture.Width, true); //Here we simplify the vertices //This is totally optional but can save loads of vertices being created as most aren't needed to make the shape. //You may need to tweak the value passed to the procedure //Pass 1 if you want maximum detail //Pass higher numbers to get progresively less detail //Or just comment this line out and swap the comments on the lines below it // Vertices simplifiedVerts = FarseerPhysics.Common.PolygonManipulation.SimplifyTools.ReduceByDistance(textureVertice, 600f); Centroid = -textureVertice.GetCentroid(); textureVertice.Translate(ref Centroid); Centroid = -Centroid; List ListOfVerts = BayazitDecomposer.ConvexPartition(textureVertice); // List ListOfVerts = BayazitDecomposer.ConvexPartition(simplifiedVerts); //scale the vertices from graphics space to sim space Vector2 vertScale = new Vector2(ConvertUnits.ToSimUnits(1)) * 1f; foreach (Vertices vertices in ListOfVerts) { vertices.Scale(ref vertScale); } body = BodyFactory.CreateCompoundPolygon(levelRef.World, ListOfVerts, 0.1f); body.Position = GetPositionForVerts(verts); body.BodyType = BodyType.Kinematic; body.UserData = this; fixtures = body.FixtureList; } public Vector2 GetPositionForVerts(Vertices v) { Vector2[] verts = v.ToArray(); for (int i = 0; i < v.Count; i++) { //Top Left if (v[i].X < verts[0].X) verts[0].X = v[i].X; if (v[i].Y < verts[0].Y) verts[0].Y = v[i].Y; //Top Right if (v[i].X > verts[1].X) verts[1].X = v[i].X; if (v[i].Y < verts[1].Y) verts[1].Y = v[i].Y; //Bottom Right if (v[i].X > verts[2].X) verts[2].X = v[i].X; if (v[i].Y > verts[2].Y) verts[2].Y = v[i].Y; //Bottom Left if (v[i].X < verts[3].X) verts[3].X = v[i].X; if (v[i].Y > verts[3].Y) verts[3].Y = v[i].Y; } Vector2 ret = new Vector2(); float x = (verts[1].X - verts[0].X) / 2f; float y = (verts[2].Y - verts[1].Y) / 2f; ret.X = x; ret.Y = y; ret.X += verts[0].X; ret.Y += verts[0].Y; return ret; } ``````