Bug report

Topics: Developer Forum
Aug 21, 2012 at 2:24 PM
This code causes a bug:

                var tmp = FarseerPhysics.Factories.BodyFactory.CreateRectangle(world, 1, 1, 1);                 tmp.Position = new Vector2(0, 0);                 tmp.BodyType = BodyType.Dynamic;                 tmp.CollisionCategories = Category.Cat2;                 tmp.Enabled = false;
GetFatAABB gets called with a proxId of -1 wich causes an out of bounds exception.

If I don't set the CollisionCategories or don't disable etc. everything works fine.
Coordinator
Aug 28, 2012 at 12:25 AM

That code snippet is used about 500 times in the Testbed and Samples framework, and they work fine. Something else must be wrong in your code.

Could you post some sample code that reproduce this? Preferably as a test inside the Testbed project.

Aug 28, 2012 at 4:33 PM

OK, I  tried to build a minimal example and there it doesn't happen. So it seems to be some kind of interaction with something else. But as soon as I put the code above somewhere in my game the same error happens.

I tried to condense it down and the smallest example I've got is this:

 

 class Program
    {
        static void Main(string[] args)
        {            
            var world = new World(new Vector2(0, 9.81f));

            var foo = new NormalModel(world);
            var bar = new Monocycle(world);

            for (int i = 0; i < 100; i++)
            {
                world.Step(0.1f);
            }
        }

        class NormalModel
        {
            public NormalModel(World world)
            {
                createNormalPhysicsModel(world, Vector2.Zero);
            }
            private Body body;
            private Body wheel;

            protected void createNormalPhysicsModel(World world, Vector2 initialPosition)
            {
                body = BodyFactory.CreateRectangle(world, 0.41f, 1.1f, 55);
                body.Position = initialPosition;
                body.BodyType = BodyType.Dynamic;

                wheel = BodyFactory.CreateCircle(world, .23f, 30);
                wheel.Position = initialPosition + new Vector2(0, .55f);
                wheel.BodyType = BodyType.Dynamic;
            }
        }

        class Monocycle
        {
            public Monocycle(World world)
            {
                createNormalPhysicsModel(world, Vector2.Zero);
            }

            private Body body;
            private Body wheel;
            protected void createNormalPhysicsModel(World world, Vector2 initialPosition)
            {
                body = BodyFactory.CreateRectangle(world, 0.51f, 0.51f, 1);
                body.Position = initialPosition - new Vector2(0, 0.1f);
                body.BodyType = BodyType.Dynamic;

                wheel = BodyFactory.CreateCircle(world, 0.6f, 25);
                wheel.Position = initialPosition;
                wheel.BodyType = BodyType.Dynamic;
                wheel.CollisionCategories = Category.Cat2; // comment this out and it works

                body.Enabled = false;
                wheel.Enabled = false;
            }
        }
    }

 

If I comment out the creation of the NormalModel it works, if I comment out wheel.CollisionCategories = Category.Cat2 it works, ...

Aug 28, 2012 at 9:24 PM
Edited Aug 28, 2012 at 9:34 PM

It will work if you put wheel.Enabled before setting the category. Also if you step the world before setting enabled to false even with a very small timestep (like 0.01f).

To Genbox: I think what is happening is this. Creating the body creates the proxies and adds them to the broadphase. Setting the collision category will cause a Refilter() on the fixture which will touch the proxy adding it to the move buffer. At this point, however, we have two identical proxyIDs in the move buffer, don't we? One from AddProxy and one from TouchProxy.

Setting enabled=false will attempt to destroy the fixture proxies which  will cause UnBufferMove in the broadphase which however returns prematurely when it finds a matching proxyID. Or something along those lines (this is way over my head). Then in the World.Step() TestOverlap will get called with a proxyID of -1 probably because there is a proxy that is overlapping something and was supposed to be set to -1 and cleared somewhere in the update but wasn't. I think TouchProxy should check if the proxy is not already in the move buffer before actually adding it.

[EDIT] I see in Commit 94309 that the premature return from UnbufferMove was removed so this bug should be fixed in current svn.