Error in texture mapping for polygons

Topics: Developer Forum, Project Management Forum, User Forum
Apr 9, 2011 at 9:33 AM

Hey Everyone,

   I have been trying to solve this problem for quite some time and I am yet to find a solution. I am trying to make a little game like Crayon Physics in which the player uses the mouse to make random objects and then play around with them in a physics environment. The problem is that whenever I draw an irregular object the texture and the body of the object are not aligned. The body is in the correct position, the texture is misplaced. Strangely enough if I draw perfect squares or rectangles the bodies and the textures line up perfectly !

 

Here is a link to a sample image

http://i56.tinypic.com/35l9m45.png

 

Please help me out, I have to deliver this project in a few weeks and I am already behind schedule !

I downloaded the complete build of Farseer 3.3 and changed one of its sample game demos to my needs, I am posting the entire code of the sample game file, none of the other files have been changed as such.

I have used parts of the texture to vertices documentation to make 2 of the 3 objects.

Please help me ! Thanks in advance !

 

namespace FarseerPhysics.SamplesFramework
{
    internal class PhysicsPolygonDemo: PhysicsGameScreen, IDemoScreen
    {
        #region Variables
        
        public Body bodyWorld, lineBody;
        private Body polygon,dynoPoly;
        private Fixture squareBody;
        private Sprite squareSprite, polygonSprite,dynoSprite;
        private Vertices vertices = new Vertices();
        private Vertices dynamicVertices = new Vertices();
        private Vector2 origin,test,dynoOrigin;
        private ContentManager content;
        private Texture2D backTex;
        private bool storeCoordinates = false;
        private int count = 0;
        private SpriteFont font;

        #endregion variables 

        #region IDemoScreen Members

        public string GetTitle()
        {
            return "Physics Polygon Test";
        }

        public string GetDetails()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("Use the mouse to form polygons!");
            sb.AppendLine(string.Empty);
            sb.AppendLine("GamePad:");
            sb.AppendLine("  - Exit to menu: Back button");
            sb.AppendLine(string.Empty);
            sb.AppendLine("Keyboard:");
            sb.AppendLine("  - Exit to menu: Escape");
            return sb.ToString();
        }

        #endregion

        
        public override void LoadContent()
        {
            
            base.LoadContent();

            //Set Gravity 
            World.Gravity = new Vector2(0.0f, 0.0f);

            //Set camera to center of world to help in drawing ( No negative values )
            Camera.MoveCamera(new Vector2(ScreenManager.GraphicsDevice.Viewport.Width/2f,ScreenManager.GraphicsDevice.Viewport.Height/2f));
            Camera.Jump2Target();

            #region Creating World Bounds

            Vertices bounds = new Vertices(4); //Create screen bounds
            bounds.Add(new Vector2(2.5f, 1.6f));
            bounds.Add(new Vector2(ConvertUnits.ToSimUnits(ScreenManager.GraphicsDevice.Viewport.Width) - 2.25f, 1.6f));
            bounds.Add(new Vector2(ConvertUnits.ToSimUnits(ScreenManager.GraphicsDevice.Viewport.Width) - 2.25f, ConvertUnits.ToSimUnits(ScreenManager.GraphicsDevice.Viewport.Height) - 1.65f));
            bounds.Add(new Vector2(2.5f, ConvertUnits.ToSimUnits(ScreenManager.GraphicsDevice.Viewport.Height) - 1.65f));

            bodyWorld = BodyFactory.CreateLoopShape(World, bounds); //Set the bounds to the world
            lineBody = BodyFactory.CreateBody(World); //Create the world for the mouse drawing line

            #endregion


            #region Load Background and Font

            content = new ContentManager(ScreenManager.Game.Services, "Content");
            backTex = content.Load<Texture2D>("Common/blackboard");
            font = content.Load<SpriteFont>("Fonts/menufont");

            #endregion

            
            #region Creating World Objects and Sprites

            //Square

            squareBody = FixtureFactory.CreateRectangle(World,ConvertUnits.ToSimUnits(50f),ConvertUnits.ToSimUnits(50f),ConvertUnits.ToSimUnits(10f),ConvertUnits.ToSimUnits(new Vector2(200f,200f)));
            squareBody.Body.BodyType = BodyType.Dynamic;
            squareSprite = new Sprite(ScreenManager.Assets.TextureFromShape(squareBody.Shape,MaterialType.Dots,Color.White,1f));

            //Weird Traingle'ish Polygon

            vertices.Add(new Vector2(250f, 250f));
            vertices.Add(new Vector2(300f, 250f));
            vertices.Add(new Vector2(300f, 300f));
            vertices.Add(new Vector2(220f, 300f));

            Vector2 scale = ConvertUnits.ToSimUnits(1f, 1f);
            vertices.Scale(ref scale);

            Vector2 centroid = -vertices.GetCentroid();
            vertices.Translate(ref centroid);
            origin = -centroid;       
              
            List<Vertices> list = BayazitDecomposer.ConvexPartition(vertices);
            polygonSprite = new Sprite(ScreenManager.Assets.TextureFromVertices(vertices, MaterialType.Dots, Color.White, 1f));            
            polygon = BodyFactory.CreateCompoundPolygon(World, list, 1f, origin);
            polygon.BodyType = BodyType.Dynamic;
            
            
            #endregion



        }


      public override void HandleInput(InputHelper input, GameTime gameTime)
        {
            if (input.KeyboardState.IsKeyDown(Keys.Escape))
                ScreenManager.Game.Exit();

            
            #region Store Mouse Coordinates and Defining the Dynamic Polygon

            if (input.IsNewMouseButtonPress(MouseButtons.LeftButton) && count == 0)
                storeCoordinates = true;

            if (input.IsNewMouseButtonRelease(MouseButtons.LeftButton) && count == 0)
            {
                storeCoordinates = false;
                count = 1;
            }

            if (storeCoordinates && count == 0)
            {
                dynamicVertices.Add(new Vector2(input.MouseState.X, input.MouseState.Y));
            }
            else if (!storeCoordinates && count == 1)
            {

                Vector2 scale = ConvertUnits.ToSimUnits(new Vector2(1f, 1f));
                dynamicVertices.Scale(ref scale);
                
                Vector2 polyCentroid = -dynamicVertices.GetCentroid();
                dynamicVertices.Translate(ref polyCentroid);
                dynoOrigin = -polyCentroid;
                
                dynamicVertices = SimplifyTools.CollinearSimplify(dynamicVertices);

                List<Vertices> dynoList = BayazitDecomposer.ConvexPartition(dynamicVertices);
                dynoSprite = new Sprite(ScreenManager.Assets.TextureFromVertices(dynamicVertices, MaterialType.Dots, Color.White, 1f));
                dynoPoly = BodyFactory.CreateCompoundPolygon(World, dynoList, 1f,dynoOrigin);
                dynoPoly.BodyType = BodyType.Dynamic;

                count = 2;
            }

            #endregion


            
            base.HandleInput(input, gameTime);
        }



        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {

            World.Step((float)(gameTime.ElapsedGameTime.TotalMilliseconds * 0.001));
            
            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);


        }


       public override void Draw(GameTime gameTime)
        {
            
            #region Drawing Bounds

            ScreenManager.LineBatch.Begin(Camera.SimProjection, Camera.SimView);
            for (int i = 0; i < bodyWorld.FixtureList.Count; ++i)
            {
                ScreenManager.LineBatch.DrawLineShape(bodyWorld.FixtureList[i].Shape, Color.Black);
            }           
            ScreenManager.LineBatch.End();

            #endregion

            #region Draw Everything Else!

            ScreenManager.SpriteBatch.Begin(0, null, null, null, null, null, Camera.View);

            ScreenManager.SpriteBatch.Draw(backTex, new Rectangle(0, 0, (int)ScreenManager.GraphicsDevice.Viewport.Width, (int)ScreenManager.GraphicsDevice.Viewport.Height), Color.White);
            ScreenManager.SpriteBatch.Draw(squareSprite.texture, ConvertUnits.ToDisplayUnits(squareBody.Body.Position), null, Color.White, squareBody.Body.Rotation, squareSprite.origin, 1f, SpriteEffects.None, 0f);
            ScreenManager.SpriteBatch.Draw(polygonSprite.texture, ConvertUnits.ToDisplayUnits(polygon.Position), null, Color.White, polygon.Rotation, polygonSprite.origin, 1f, SpriteEffects.None, 0f);

            ScreenManager.SpriteBatch.DrawString(font, test.ToString(), new Vector2(5f, 5f), Color.White);

            if(count == 2)
                ScreenManager.SpriteBatch.Draw(dynoSprite.texture, ConvertUnits.ToDisplayUnits(dynoPoly.Position), null, Color.White, dynoPoly.Rotation, dynoSprite.origin, 1f, SpriteEffects.None, 0f);

            ScreenManager.SpriteBatch.End();

            #endregion



            base.Draw(gameTime);
        }
    }
}

Coordinator
Apr 9, 2011 at 12:28 PM

To draw the texture the correct place, you need to save the origin of the texture and use it in the draw method. Just like we do inside the samples.

Take note of the lines from the samples:

//1. To translate the vertices so the polygon is centered around the centroid.
Vector2 centroid = -textureVertices.GetCentroid();
textureVertices.Translate(ref centroid);

//2. To draw the texture the correct place.
_origin = -centroid;

And inside Draw():

ScreenManager.SpriteBatch.Begin(0, null, null, null, null, null, Camera.View);
ScreenManager.SpriteBatch.Draw(_polygonTexture, ConvertUnits.ToDisplayUnits(_compound.Position),
                                           null, Color.Tomato, _compound.Rotation, _origin, _scale, SpriteEffects.None, 0f);
ScreenManager.SpriteBatch.End();

Notice how we use the _origin variable in the draw.

Apr 9, 2011 at 4:27 PM

Hey GenBox,

Thanks for the reply ! I have tried that previously, here are the results are follows

http://i54.tinypic.com/flhagh.png

 

The textures now seem to be uniformly shifted by a certain value.

I was already using the translation code in my code, Is a matter of sequence ? Is the sequence of the code messing things up ?

 

Please help my fix this !!

Cheers!

Farhan

Coordinator
Apr 9, 2011 at 4:29 PM

The sequence needs to be the same as in the samples.

Developer
Apr 10, 2011 at 9:40 AM

You have to line up your textures with the body's center of mass. That's what the Sprite.Origin is for. It defaults to the texture center which is fine for regular shapes/fixtures.

polygonSprite = new Sprite(ScreenManager.Assets.TextureFromVertices(vertices, MaterialType.Dots, Color.White, 1f), AssetCreator.CalculateOrigin(polygon));            

Change your sprite creation to the above. You shouldn't need anything from the texture to vertices sample.

Apr 10, 2011 at 12:14 PM

PERFECT !!!!

 

Elsch, you sir are the man !!!

 

Cheers !!

Thanks a million !