Problem with creating a polygon from a Texture!

Oct 16, 2011 at 2:58 AM
Edited Oct 16, 2011 at 3:08 AM

Hi guys, was hoping you could help me out with a problem of mine:

I randomly generate some terrain and give the values to a texture. When I try to create a polygon from this texture, the program rarely complies, giving a variety of errors.

If there's anything blatantly stupid that I've done wrong, I'd love if you could help me out, here's the code: 

    public class GameWorld
    {
        public int[] terrainData;

        public Texture2D terrainTexture;

        public Body terrainBody;

        public GameWorld()
        {
            GenerateTerrain();
            GenerateTerrainTexture();
        }

        public void GenerateTerrain()
        {
            terrainData = new int[1000];

            double rand1 = MainGame.randomNumber.NextDouble() + 1;
            double rand2 = MainGame.randomNumber.NextDouble() + 2;
            double rand3 = MainGame.randomNumber.NextDouble() + 3;

            float offset = MainGame.graphics.GraphicsDevice.Viewport.Height / 2;
            float peakheight = 100;
            float flatness = 70;

            for (int x = 0; x < MainGame.graphics.GraphicsDevice.Viewport.Width; x++)
            {
                double height = peakheight / rand1 * Math.Sin((float)x / flatness * rand1 + rand1);
                height += peakheight / rand2 * Math.Sin((float)x / flatness * rand2 + rand2);
                height += peakheight / rand3 * Math.Sin((float)x / flatness * rand3 + rand3);
                height += offset;
                terrainData[x] = (int)height;
            }
        }

        public void GenerateTerrainTexture()
        {
            Color[] terrainForeground = new Color[MainGame.graphics.GraphicsDevice.Viewport.Width * MainGame.graphics.GraphicsDevice.Viewport.Height];

            for (int x = 0; x < MainGame.graphics.GraphicsDevice.Viewport.Width; x++)
            {
                for (int y = 0; y < MainGame.graphics.GraphicsDevice.Viewport.Height; y++)
                {
                    if (y > terrainData[x])
                    {
                        terrainForeground[x + y * MainGame.graphics.GraphicsDevice.Viewport.Width] = Color.Green;
                    }
                    else
                    {
                        terrainForeground[x + y * MainGame.graphics.GraphicsDevice.Viewport.Height] = Color.Transparent;
                    }
                }
            }

            terrainTexture = new Texture2D(MainGame.graphics.GraphicsDevice, (int)MainGame.graphics.GraphicsDevice.Viewport.Width, (int)MainGame.graphics.GraphicsDevice.Viewport.Height, false, SurfaceFormat.Color);
            terrainTexture.SetData(terrainForeground);

            uint[] data = new uint[terrainTexture.Width * terrainTexture.Height];

            terrainTexture.GetData(data);

            Vertices verts = PolygonTools.CreatePolygon(data, terrainTexture.Width, true);

            List<Vertices> list = BayazitDecomposer.ConvexPartition(verts);

            Vector2 vertScale = new Vector2(64);
            foreach (Vertices vertices in list)
            {
                vertices.Scale(ref vertScale);
            }

            terrainBody = BodyFactory.CreateCompoundPolygon(MainGame.physicsWorld, list, 1f, BodyType.Static);
        }

        public void DrawWorld()
        {
            MainGame.spriteBatch.Draw(terrainTexture, new Rectangle(0, 0, terrainTexture.Width, terrainTexture.Height), Color.White);
        }

Oct 18, 2011 at 2:32 PM

I've also tried creating a polygon from a set of vertices defined from the original terrain generator - to no avail.

I tried both the Bayazit decomposer and the Earclipper, both failed.

 

Where abouts am I going wrong?

Oct 19, 2011 at 10:57 PM
Edited Oct 19, 2011 at 11:15 PM

As far as i can see, i think that you tried to create scale 1 farseer unit = 64 pixels.

If so, than your scaling vector should be

Vector2 vertScale = new Vector2(0.015625);


But i'm also Farseer n00b so don't take me for granted. :)

cheerZZ

Oct 20, 2011 at 2:50 PM
Edited Oct 20, 2011 at 2:51 PM

Thanks for the suggestion, but it didn't work, and I'm utterly out of ideas.

 

Here's the error I'm getting, somebody may recognise it:

http://imgur.com/xYk3z

 

Oct 27, 2011 at 12:39 PM

Sorry to bump, but can anyone help? Still having problems.

Nov 1, 2011 at 8:05 PM

I'm not exactly sure what you are doing but could it be that your generated texture is not correct?

Anyways here is the code I use to create a polygon from Texture

public static List<Fixture> ImageToPolygonBody(Texture2D texture, World world, float density,float scale_, ref Vector2 polygonOrigin)
        {
            List<Fixture> compundFixture;

            //Create an array to hold the data from the texture
            uint[] data = new uint[(texture.Width) * (texture.Height)];

            //Collect data from bitmap
            texture.GetData(data);

            //Create Polygon from Bitmap
            Vertices verts = PolygonTools.CreatePolygon(data, (texture.Width), (texture.Height), false);

            //Make sure that the origin of the texture is the centroid (real center of geometry)
            Vector2 scale = new Vector2(ConvertUnits.ToSimUnits(scale_), ConvertUnits.ToSimUnits(scale_));
            verts.Scale(ref scale);

            //Make sure that the origin of the texture is the centroid (real center of geometry)
            polygonOrigin = verts.GetCentroid();

            //Translate the polygon so that it aligns properly with centroid.
            Vector2 vertsTranslate = -polygonOrigin;
            verts.Translate(ref vertsTranslate);

            //We simplify the vertices found in the texture.
            //verts = SimplifyTools.ReduceByDistance(verts, 4f);

            //Decompose polygon into smaller chuncks that Farseer can process better
            List<Vertices> list;
            list = BayazitDecomposer.ConvexPartition(verts);

            //Create a single body with multiple fixtures
            compundFixture = FixtureFactory.CreateCompoundPolygon(world, list, density);

            return compundFixture;
        }
Nov 5, 2011 at 1:11 PM

My FixtureFactory doesn't have a "CreateCompoundPolygon" method :S

 

I'm using Farseer 3.3.1

Nov 9, 2011 at 8:39 PM

Come on man how lazy are u?

Luck for you that I'm in progress of updating to 3.3...

public static Body ImageToPolygonBody(Texture2D texture, World world, float density, float scale_, ref Vector2 polygonOrigin)
        {
            //Create an array to hold the data from the texture
            uint[] data = new uint[(texture.Width) * (texture.Height)];

            //Collect data from bitmap
            texture.GetData(data);

            //Create Polygon from Bitmap
            Vertices verts = PolygonTools.CreatePolygon(data, (texture.Width), false); 

            //Make sure that the origin of the texture is the centroid (real center of geometry)
            Vector2 scale = new Vector2(ConvertUnits.ToSimUnits(scale_), ConvertUnits.ToSimUnits(scale_));
            verts.Scale(ref scale);

            //Make sure that the origin of the texture is the centroid (real center of geometry)
            polygonOrigin = verts.GetCentroid();

            //Translate the polygon so that it aligns properly with centroid.
            Vector2 vertsTranslate = -polygonOrigin;
            verts.Translate(ref vertsTranslate);

            //We simplify the vertices found in the texture.
            //verts = SimplifyTools.ReduceByDistance(verts, 4f);

            //Decompose polygon into smaller chuncks that Farseer can process better
            List<Vertices> list;
            list = BayazitDecomposer.ConvexPartition(verts);

            //Create a body
            return BodyFactory.CreateCompoundPolygon(world, list, density);
           

        }
Dec 9, 2011 at 7:24 PM

Hi,

I'm a complete new comer to Farseer, but this has been really helpful to a project of mine. However, I get an exception on the following line:
list = BayazitDecomposer.ConvexPartition(verts);
saying: "Index was out of range. Must be non-negative and less than the size of the collection.Parameter name: index"

any suggestions as to why I get it...? I'd appreciate any help, thank you very much! :)

Dec 9, 2011 at 8:06 PM

Hi!

Glad to be able to help :)

I've had that problem too and it's because your image/texture has pixels that are not connected to eachother. So check the image and ensure that its a single "body". Otherwise it will not work.

Try it with a very simple square that you create in paint, gimp or similar and make that work first.

Dec 9, 2011 at 8:13 PM

Aaaah, I see. Thank you! Sorry to keep asking, but do you know any ways to "simplify" the image so that it doesn't use unconnected pixels in the image? Cause the images have lots of detail and I can't just go removing stuff, however, I suspect that those unconnected pixels are grass, flowers etc (it's a terrain element) so they're not important for collision detection... :/ Hope that makes sense :) Looking forward to your answer and really appreciate your help!

 

Dec 9, 2011 at 9:16 PM

Sorry, just noticed that you actually mention that in your code snippet... :P Thanks :)

Dec 12, 2011 at 10:38 PM

I am also having trouble with creating a polygon from a Texture. The Texture I have is just a big rounded square: http://farseerphysics.codeplex.com/discussions/279916

I was looking at the code Mesothere posted in TextureConverter.cs and I think there is a bug in it. polygonEntrance does not actually get set until polygons.Count > 2 (around line 390 in the file I have). But then polygonEntrance.Value is accessed when polygon.Count < 2. Have I missed something?

Dec 21, 2011 at 12:44 PM

I had the same problem. Then I looked at the 3.3.1 samples, and more specifically the Advanced sample that covers texture to polygon. You might need to adjust slightly for your simulation but that approach worked for me.  The example on the home screen is out of date and doesn't work.