direction of contact normals

Feb 25, 2009 at 12:03 PM
Edited Mar 28, 2009 at 4:16 PM
when 2 Geoms collide, what defines the direction of the normals? f.e. a square falls on a floor, are the normals up or down then? it seems to depend on the creation order of the Geoms.

edit (28.03.2009):
see also: http://farseerphysics.codeplex.com/Thread/View.aspx?ThreadId=51404
Feb 28, 2009 at 11:29 AM
hmm, someone?
Coordinator
Feb 28, 2009 at 2:52 PM
Sorry yobiv, simply forgot to answer your question.

First a little explanation. (others read this thread too).

A normal is a line that is perpendicular to another line. In 2D we call that a normal vector. A normal can be calculated for all surfaces and in even more dimensions than 2. More info can be found at this wikipedia article.
Farseer Physics uses the normal vector when collisions happen. We need the normal to make realistic collisionresponses.

You square have a normal vector on each side. (up, down, left, right) and when it falls on a square floor, the normal of the square is taken into account. The floor also have normal vectors on each side and when the square falls on the floor, a collision response is calculated that pushes the 2 objects away from each other by using their normals. Newton's third law states that for every action there is an equal and opposite reaction. This means that both the square and the floor moves, but since we can ignore this behavior with static bodies, that law is not always true in our engine.
Mar 3, 2009 at 8:30 PM
wouldn't that mean:
if the floor is static, the force that pushes the falling object away points upwards(?). if so, why does it depend on the order of creation?!
if both objects collide with the same velocity, the force that pushes both objects away is zero?
Coordinator
Mar 3, 2009 at 8:46 PM
Where have you seen that the order of creation is involved?
Newton said that actions have the same and opposite reaction. When two geometries is heading towards each other with the same force applied to them and they collide, they will stop in the middle. You could possible try it out with the SimpleSamples Demo2. Just apply a force of x=-30 on the circle and x=30 on the rectangle. Stop applying force to the bodies the instant they collide.
If one of the forces is bigger than the other, the bigger force will "win" and push the other body in its direction.
Mar 3, 2009 at 9:32 PM
Edited Mar 3, 2009 at 9:33 PM
replace Demo with the code below:

1. test:
in OnCollision1() and OnCollision2(), set a breakpoint to return statement
run demo
normal is 0/1 in both methods (as intended)

2. test:
stop debugging
in LoadContent(), swap rectangle1 and rectangle2 block
run demo
normal is 0/-1 o_O

3. test:
remove gravity
in LoadContent(), comment isStatic = true and uncomment ApplyForce()
linearVelocity is the same amount



using System.Text;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Factories;
using FarseerGames.GettingStarted.DrawingSystem;
using FarseerGames.GettingStarted.ScreenSystem;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace FarseerGames.GettingStarted.Demos.Demo2
{
    public class Demo2Screen : GameScreen
    {
        private Body _rectangleBody1;
        private Geom _rectangleGeom1;
        private Body _rectangleBody2;
        private Geom _rectangleGeom2;
        private Vector2 _rectangleOrigin;
        private Texture2D _rectangleTexture;

        public override void Initialize()
        {
            PhysicsSimulator = new PhysicsSimulator(Vector2.UnitY * 100);
            //PhysicsSimulator = new PhysicsSimulator();
            PhysicsSimulatorView = new PhysicsSimulatorView(PhysicsSimulator);

            base.Initialize();
        }

        public override void LoadContent()
        {
            _rectangleTexture = DrawingHelper.CreateRectangleTexture(ScreenManager.GraphicsDevice, 128, 128, Color.Gold,
                                                                     Color.Black);
            _rectangleOrigin = new Vector2(_rectangleTexture.Width / 2f, _rectangleTexture.Height / 2f);

            _rectangleBody1 = BodyFactory.Instance.CreateRectangleBody( PhysicsSimulator, 128, 128, 1 );
            _rectangleBody1.Position = new Vector2( 512, 128 );
            _rectangleGeom1 = GeomFactory.Instance.CreateRectangleGeom( PhysicsSimulator, _rectangleBody1, 128, 128 );
            _rectangleGeom1.OnCollision += OnCollision1;
            _rectangleGeom1.Tag = "rect1";

            _rectangleBody2 = BodyFactory.Instance.CreateRectangleBody( PhysicsSimulator, 128, 128, 1 );
            _rectangleBody2.Position = new Vector2( 512, 384 );
            _rectangleGeom2 = GeomFactory.Instance.CreateRectangleGeom( PhysicsSimulator, _rectangleBody2, 128, 128 );
            _rectangleGeom2.OnCollision += OnCollision2;
            _rectangleGeom2.Tag = "rect2";

            _rectangleBody2.IsStatic = true;

            //_rectangleBody1.ApplyForce( Vector2.UnitY * 10000 );
            //_rectangleBody2.ApplyForce( Vector2.UnitY * -10000 );

            base.LoadContent();
        }

        private bool OnCollision1( Geom g1, Geom g2, ContactList contactList )
        {
            Vector2 normal = Vector2.Zero;
            foreach( var contact in contactList )
            {
                normal += contact.Normal;
            }
            normal.Normalize();

            return true;
        }

        private bool OnCollision2( Geom g1, Geom g2, ContactList contactList )
        {
            Vector2 normal = Vector2.Zero;
            foreach( var contact in contactList )
            {
                normal += contact.Normal;
            }
            normal.Normalize();

            return true;
        }

        public override void Draw(GameTime gameTime)
        {
            ScreenManager.SpriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            ScreenManager.SpriteBatch.Draw( _rectangleTexture, _rectangleGeom1.Position, null, Color.White,
                                           _rectangleGeom1.Rotation, _rectangleOrigin, 1, SpriteEffects.None, 0);
            ScreenManager.SpriteBatch.Draw( _rectangleTexture, _rectangleGeom2.Position, null, Color.White,
                                           _rectangleGeom2.Rotation, _rectangleOrigin, 1, SpriteEffects.None, 0 );
            ScreenManager.SpriteBatch.End();

            base.Draw(gameTime);
        }

        public static string GetTitle()
        {
            return "Demo2: Non-deterministic contact normals";
        }

        public static string GetDetails()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("This demo shows that contact normals not deterministic :)");
            return sb.ToString();
        }
    }
}
Mar 5, 2009 at 5:48 PM
> Where have you seen that the order of creation is involved?

do you get the same result with demo test1 and test2 (normal pointing up, normal pointing down)? (sorry, for the bump!)
Mar 6, 2009 at 11:19 AM
Hi, if I'm allowed to, I'll chain in this discussion :)
I tried the test code and get the same results: depending on creation order of the boxes I get either normal [0,1] or [0,-1], which somehow doesn't  fit in my mind.
Am I missing something crucial here?

Side note: what is the intention of test 3?
Mar 7, 2009 at 9:11 AM
when you debug into the collision events on test 3 you see that both bodies have the SAME velocity, therefore - if i understand genbox correctly - the bodies should produce no separation force and the contact normals be zero. as it turns out, there are contact normals (zeros wouldn't be helpful anyway), but again i don't know how the direction is determined.