DanielChappuis / reactphysics3d

Open source C++ physics engine library in 3D
http://www.reactphysics3d.com
zlib License
1.54k stars 223 forks source link

Assertions for length tests should not use MACHINE_EPSILON #12

Closed jagoly closed 9 years ago

jagoly commented 9 years ago

As one example, see the getUnit method of Vector3. It has the line assert(lengthVector > MACHINE_EPSILON). This causes an error when the length is less than that value, which does not seem like what we want. Instead, it should just test that the length is non-zero, as zero can be represented exactly by all floating point numbers.

DanielChappuis commented 9 years ago

I think it is OK because we do not want to prevent only division by exactly zero but we also want to avoid division by very small numbers that cannot be represented by a float which will cause the result of the division to be +/- infinity.

You can take a look at this thread for instance : http://stackoverflow.com/questions/12825471/division-by-zero-prevention-checking-the-divisors-expression-doesnt-result-in

jagoly commented 9 years ago

Perhaps, but if so, something is definitely still wrong. In my tests, I'm getting many different crashes from this. Surely it'd be more reasonable to just do a !=0.0 or inf test at each stage of the calculations? After all, as it is assertions are failing when they definitely should not - these small values are not getting into the engine directly from the user, but the engine itself is generating them.

DanielChappuis commented 9 years ago

I'm pretty sure that the test with 0.0 would be wrong. We need to use some epsilon here to avoid division by a very small number. However, maybe using the assert is not a really good idea. I think I will remove the assert and return a zero length vector if the length is very small.

This is what is done in Box2D for instance. Take a look at the b2Vec2::Normalize() method here.

jagoly commented 9 years ago

Somewhat related I suppose, but another assert that is getting raised quite a lot for me is the on line 51 of https://github.com/DanielChappuis/reactphysics3d/blob/testbed/src/constraint/ContactPoint.cpp

DanielChappuis commented 9 years ago

About this assert in ContactPoint.cpp, do you have a full stack trace ? I would like to know where in the code a contact point with negative penetration depth is created.

jagoly commented 9 years ago

Ok, I think this is what you wanted (sorry, never posted a stack trace before)

Edit: Hang on, this one doesn't look very useful, I'll try and get a better one

Thread 1 (Thread 0x7ffff7fbd900 (LWP 5740)):
#0  0x00007ffff6938cc9 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
        resultvar = 0
        pid = 5740
        selftid = 5740
#1  0x00007ffff693c0d8 in __GI_abort () at abort.c:89
        save_stage = 2
        act = {__sigaction_handler = {sa_handler = 0x7fffffffe81e, sa_sigaction = 0x7fffffffe81e}, sa_mask = {__val = {140737331588380, 6974784, 50, 4294967295, 140737330230499, 4294967296, 140737488346288, 51539598672, 140737488346704, 17674348, 0, 0, 0, 21474836480, 140737354072064, 140737331603504}}, sa_flags = 6974868, sa_restorer = 0x6a6e00 <reactphysics3d::ContactPoint::ContactPoint(reactphysics3d::ContactPointInfo const&)::__PRETTY_FUNCTION__>}
        sigs = {__val = {32, 0 <repeats 15 times>}}
#2  0x00007ffff6931b86 in __assert_fail_base (fmt=0x7ffff6a82830 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=assertion@entry=0x6a6d94 "mPenetrationDepth > 0.0", file=file@entry=0x6a6d40 "/home/jagoly/Projects/reactphysics3d/src/reactphysics3d/constraint/ContactPoint.cpp", line=line@entry=50, function=function@entry=0x6a6e00 <reactphysics3d::ContactPoint::ContactPoint(reactphysics3d::ContactPointInfo const&)::__PRETTY_FUNCTION__> "reactphysics3d::ContactPoint::ContactPoint(const reactphysics3d::ContactPointInfo&)") at assert.c:92
        str = 0x14588dd0 "\260\242\036\001"
        total = 4096
#3  0x00007ffff6931c32 in __GI___assert_fail (assertion=0x6a6d94 "mPenetrationDepth > 0.0", file=0x6a6d40 "/home/jagoly/Projects/reactphysics3d/src/reactphysics3d/constraint/ContactPoint.cpp", line=50, function=0x6a6e00 <reactphysics3d::ContactPoint::ContactPoint(reactphysics3d::ContactPointInfo const&)::__PRETTY_FUNCTION__> "reactphysics3d::ContactPoint::ContactPoint(const reactphysics3d::ContactPointInfo&)") at assert.c:101
No locals.
#4  0x0000000000642d92 in reactphysics3d::ContactPoint::ContactPoint (this=0x16aa8f28, contactInfo=...) at /home/jagoly/Projects/reactphysics3d/src/reactphysics3d/constraint/ContactPoint.cpp:50
No locals.
#5  0x000000000063dcee in reactphysics3d::CollisionDetection::createContact (this=0xccc6b8, overlappingPair=0x147cc138, contactInfo=0x10db040) at /home/jagoly/Projects/reactphysics3d/src/reactphysics3d/collision/CollisionDetection.cpp:406
        contact = 0x147cc148
        __PRETTY_FUNCTION__ = "void reactphysics3d::CollisionDetection::createContact(reactphysics3d::OverlappingPair*, const reactphysics3d::ContactPointInfo*)"
#6  0x000000000063d13d in reactphysics3d::CollisionDetection::computeNarrowPhase (this=0xccc6b8) at /home/jagoly/Projects/reactphysics3d/src/reactphysics3d/collision/CollisionDetection.cpp:229
        contactInfo = 0x10db040
        shape2 = 0x907e1c0
        body2 = 0xa486660
        isBody2Active = true
        bodiesIndex = {first = 141, second = 156}
        pair = 0x147cc138
        narrowPhaseAlgorithm = @0xccc768: {_vptr.NarrowPhaseAlgorithm = 0x8f7d30 <vtable for reactphysics3d::SphereVsSphereAlgorithm+16>, mMemoryAllocator = @0xccc818, mCurrentOverlappingPair = 0x147cc138}
        shape1 = 0x907dc98
        body1 = 0xa485490
        isBody1Active = true
        it = {_M_node = 0x1268c10}
        __PRETTY_FUNCTION__ = "void reactphysics3d::CollisionDetection::computeNarrowPhase()"
#7  0x000000000063c70a in reactphysics3d::CollisionDetection::computeCollisionDetection (this=0xccc6b8) at /home/jagoly/Projects/reactphysics3d/src/reactphysics3d/collision/CollisionDetection.cpp:63
No locals.
#8  0x0000000000659235 in reactphysics3d::DynamicsWorld::update (this=0xccc6b0, timeStep=0.0416666679) at /home/jagoly/Projects/reactphysics3d/src/reactphysics3d/engine/DynamicsWorld.cpp:117
No locals.
jagoly commented 9 years ago

The scene that is causing this is fairly simple:

Each of these defines a Rigid Body: http://hastebin.com/banifoxelo.hs http://hastebin.com/desemovoka.hs http://hastebin.com/azikeranid.hs

I create one bigbox object at (0,0,0), and then 111 of the balls at (-3.f, 0.f, 1.f+num0.4f), and 111 of the dice at (3.f, 0.f, 1.f+num0.4f). I run the simulation at (1.f/24.f), have gravity set to (0,0,-1), velocity iterations set to 18 and position iterations set to 10. I use Z as my world space up axis.

Edit: if the problem doesn't show up, which it doesn't every time, then you can set the number of balls/dice to a higher number say 250, and eventually the problem will occur.

I've had lots of other scenes that eventually cause this to happen, but this is just one specific one that I've got right now. Not sure how I can really post a minimal example of this, as there is a lot involved, and my code base is quite large. You should be able to build a scene that demonstrates the issue from the above description though, if you need to.

Also, here is the program state when it crashes, hopefully it will be somewhat useful.

contactInfo @0x10db040  reactphysics3d::ContactPointInfo &
    localPoint1 @0x10db060  reactphysics3d::Vector3
        x   0.0835847855    reactphysics3d::decimal
        y   0.0315114185    reactphysics3d::decimal
        z   0.120501518 reactphysics3d::decimal
    localPoint2 @0x10db06c  reactphysics3d::Vector3
        x   -0.05452298 reactphysics3d::decimal
        y   -0.0761594623   reactphysics3d::decimal
        z   0.117162205 reactphysics3d::decimal
    normal  @0x10db050  reactphysics3d::Vector3
        x   -0.972410023    reactphysics3d::decimal
        y   0.233278275 reactphysics3d::decimal
        z   2.88089109e-06  reactphysics3d::decimal
    penetrationDepth    0   reactphysics3d::decimal
    shape1  @0x907dc98  reactphysics3d::ProxyShape
        mBody   @0xa485490  reactphysics3d::RigidBody
            [reactphysics3d::CollisionB...] @0xa485490  reactphysics3d::CollisionBody
                [reactphysics3d::Body]  @0xa485490  reactphysics3d::Body
                    [vptr]  0x8f7910 <vtable for reactphysics3d::RigidBody+16>  
                    mID 141 reactphysics3d::bodyindex
                    mIsActive   true    bool
                    mIsAllowedToSleep   true    bool
                    mIsAlreadyInIsland  true    bool
                    mIsSleeping false   bool
                    mSleepTime  0   reactphysics3d::decimal
                    mUserData   0x0 void *
                mContactManifoldsList   @0x11f8250  reactphysics3d::ContactManifoldListElement
                    contactManifold @0x11c0878  reactphysics3d::ContactManifold
                        mContactPoints  @0x11c0888  reactphysics3d::ContactPoint *[4]
                            [0] @0x16aabfe8 reactphysics3d::ContactPoint
                                mBody1  @0x90818b0  reactphysics3d::RigidBody
                                    [reactphysics3d::CollisionB...] @0x90818b0  reactphysics3d::CollisionBody
                                        [reactphysics3d::Body]  @0x90818b0  reactphysics3d::Body
                                            [vptr]  0x8f7910 <vtable for reactphysics3d::RigidBody+16>  
                                            mID 0   reactphysics3d::bodyindex
                                            mIsActive   true    bool
                                            mIsAllowedToSleep   true    bool
                                            mIsAlreadyInIsland  false   bool
                                            mIsSleeping false   bool
                                            mSleepTime  0   reactphysics3d::decimal
                                            mUserData   0x0 void *
                                        mContactManifoldsList   @0x11f8740  reactphysics3d::ContactManifoldListElement
                                        mNbCollisionShapes  5   reactphysics3d::uint
                                        mProxyCollisionShapes   @0x907ac20  reactphysics3d::ProxyShape
                                        mTransform  @0x90818d4  reactphysics3d::Transform
                                        mType   reactphysics3d::STATIC (0)  reactphysics3d::BodyType
                                        mWorld  @0xccc6b0   reactphysics3d::DynamicsWorld &
                                    mAngularDamping 0   reactphysics3d::decimal
                                    mAngularVelocity    @0x9081938  reactphysics3d::Vector3
                                    mCenterOfMassLocal  @0x9081914  reactphysics3d::Vector3
                                    mCenterOfMassWorld  @0x9081920  reactphysics3d::Vector3
                                    mExternalForce  @0x9081944  reactphysics3d::Vector3
                                    mExternalTorque @0x9081950  reactphysics3d::Vector3
                                    mInertiaTensorLocal @0x9081960  reactphysics3d::Matrix3x3
                                    mInertiaTensorLocalInverse  @0x9081990  reactphysics3d::Matrix3x3
                                    mInitMass   0   reactphysics3d::decimal
                                    mIsGravityEnabled   true    bool
                                    mJointsList 0x0 reactphysics3d::JointListElement *
                                    mLinearDamping  0   reactphysics3d::decimal
                                    mLinearVelocity @0x908192c  reactphysics3d::Vector3
                                    mMassInverse    0   reactphysics3d::decimal
                                    mMaterial   @0x90819c8  reactphysics3d::Material
                                mBody2  @0xa485490  reactphysics3d::RigidBody
                                mFrictionImpulse1   0   reactphysics3d::decimal
                                mFrictionImpulse2   0   reactphysics3d::decimal
                                mFrictionVectors    @0x16aac03c reactphysics3d::Vector3 [2]
                                mIsRestingContact   true    bool
                                mLocalPointOnBody1  @0x16aac008 reactphysics3d::Vector3
                                mLocalPointOnBody2  @0x16aac014 reactphysics3d::Vector3
                                mNormal @0x16aabff8 reactphysics3d::Vector3
                                mPenetrationDepth   0.00653360737   reactphysics3d::decimal
                                mPenetrationImpulse 0   reactphysics3d::decimal
                                mWorldPointOnBody1  @0x16aac020 reactphysics3d::Vector3
                                mWorldPointOnBody2  @0x16aac02c reactphysics3d::Vector3
                            [1] @0x16a7eb90 reactphysics3d::ContactPoint
                            [2] @0x16a7eb90 reactphysics3d::ContactPoint
                            [3] @0x730  reactphysics3d::ContactPoint
                        mFrictionImpulse1   0.00110250153   reactphysics3d::decimal
                        mFrictionImpulse2   -8.15459558e-08 reactphysics3d::decimal
                        mFrictionTwistImpulse   3.59582386e-09  reactphysics3d::decimal
                        mFrictionVector1    @0x11c08ac  reactphysics3d::Vector3
                        mFrictionVector2    @0x11c08b8  reactphysics3d::Vector3
                        mIsAlreadyInIsland  true    bool
                        mMemoryAllocator    @0xccc818   reactphysics3d::MemoryAllocator &
                        mNbContactPoints    2   reactphysics3d::uint
                        mShape1 @0x907aac0  reactphysics3d::ProxyShape
                        mShape2 @0x907dc98  reactphysics3d::ProxyShape
                    next    0x0 reactphysics3d::ContactManifoldListElement *
                mNbCollisionShapes  1   reactphysics3d::uint
                mProxyCollisionShapes   @0x907dc98  reactphysics3d::ProxyShape
                mTransform  @0xa4854b4  reactphysics3d::Transform
                mType   reactphysics3d::DYNAMIC (2) reactphysics3d::BodyType
                mWorld  @0xccc6b0   reactphysics3d::DynamicsWorld &
            mAngularDamping 0.899999976 reactphysics3d::decimal
            mAngularVelocity    @0xa485518  reactphysics3d::Vector3
            mCenterOfMassLocal  @0xa4854f4  reactphysics3d::Vector3
            mCenterOfMassWorld  @0xa485500  reactphysics3d::Vector3
            mExternalForce  @0xa485524  reactphysics3d::Vector3
            mExternalTorque @0xa485530  reactphysics3d::Vector3
            mInertiaTensorLocal @0xa485540  reactphysics3d::Matrix3x3
            mInertiaTensorLocalInverse  @0xa485570  reactphysics3d::Matrix3x3
            mInitMass   0.600000024 reactphysics3d::decimal
            mIsGravityEnabled   true    bool
            mJointsList 0x0 reactphysics3d::JointListElement *
            mLinearDamping  0.300000012 reactphysics3d::decimal
            mLinearVelocity @0xa48550c  reactphysics3d::Vector3
            mMassInverse    1.66666663  reactphysics3d::decimal
            mMaterial   @0xa4855a8  reactphysics3d::Material
        mBroadPhaseID   289 int
        mCachedCollisionData    0x0 void *
        mCollideWithMaskBits    65535   unsigned short
        mCollisionCategoryBits  1   unsigned short
        mCollisionShape @0xcb23d90  reactphysics3d::SphereShape
        mLocalToBodyTransform   @0x907dca8  reactphysics3d::Transform
        mMass   0.600000024 reactphysics3d::decimal
        mNext   0x0 reactphysics3d::ProxyShape *
        mUserData   0x0 void *
    shape2  @0x907e1c0  reactphysics3d::ProxyShape
this    @0x16aa8f28 reactphysics3d::ContactPoint
    mBody1  @0xa485490  reactphysics3d::RigidBody
        [reactphysics3d::CollisionB...] @0xa485490  reactphysics3d::CollisionBody
        mAngularDamping 0.899999976 reactphysics3d::decimal
        mAngularVelocity    @0xa485518  reactphysics3d::Vector3
        mCenterOfMassLocal  @0xa4854f4  reactphysics3d::Vector3
        mCenterOfMassWorld  @0xa485500  reactphysics3d::Vector3
        mExternalForce  @0xa485524  reactphysics3d::Vector3
        mExternalTorque @0xa485530  reactphysics3d::Vector3
        mInertiaTensorLocal @0xa485540  reactphysics3d::Matrix3x3
        mInertiaTensorLocalInverse  @0xa485570  reactphysics3d::Matrix3x3
        mInitMass   0.600000024 reactphysics3d::decimal
        mIsGravityEnabled   true    bool
        mJointsList 0x0 reactphysics3d::JointListElement *
        mLinearDamping  0.300000012 reactphysics3d::decimal
        mLinearVelocity @0xa48550c  reactphysics3d::Vector3
        mMassInverse    1.66666663  reactphysics3d::decimal
        mMaterial   @0xa4855a8  reactphysics3d::Material
    mBody2  @0xa486660  reactphysics3d::RigidBody
    mFrictionImpulse1   0   reactphysics3d::decimal
    mFrictionImpulse2   0   reactphysics3d::decimal
    mFrictionVectors    @0x16aa8f7c reactphysics3d::Vector3 [2]
        [0] @0x16aa8f7c reactphysics3d::Vector3
            x   0   reactphysics3d::decimal
            y   0   reactphysics3d::decimal
            z   0   reactphysics3d::decimal
        [1] @0x16aa8f88 reactphysics3d::Vector3
            x   0   reactphysics3d::decimal
            y   0   reactphysics3d::decimal
            z   0   reactphysics3d::decimal
    mIsRestingContact   false   bool
    mLocalPointOnBody1  @0x16aa8f48 reactphysics3d::Vector3
    mLocalPointOnBody2  @0x16aa8f54 reactphysics3d::Vector3
    mNormal @0x16aa8f38 reactphysics3d::Vector3
    mPenetrationDepth   0   reactphysics3d::decimal
    mPenetrationImpulse 0   reactphysics3d::decimal
    mWorldPointOnBody1  @0x16aa8f60 reactphysics3d::Vector3
    mWorldPointOnBody2  @0x16aa8f6c reactphysics3d::Vector3
DanielChappuis commented 9 years ago

Does the crash happen at the beginning of the simulation or after some time ? Are you sure that your bodies does not collide in their initial positions at the beginning ?

Probably not related to this issue but if you are working on a real-time application, you should probably use 1/60.0f for your timestep. It will result in a more precise simulation. By doing this, you might need to reduce the number of velocity iterations to something around 10 and position iterations to something around 5 if your application is running too slow.

jagoly commented 9 years ago

1: No, always after some time, but not always the same amount of some. Sometimes it doesn't happen at all. Related to this, is RP3D designed to always get the same results given the same input? As in, if you run a simulation multiple times, each one should run exactly the same? It does seem to do that most of the time, but occasionally, the sim will run completely differently.

2: I run it at 1/24.f because that is the same speed that I run all of my other logic at, so it fits in nicely with the rest of my engine. As it is, I don't seem to be having any accuracy issues. Also to note is that I have tried running the sim slower (1/60 up to 1/300) and all of the issues I've brought up occur then as well.

DanielChappuis commented 9 years ago
  1. ReactPhysics3D is not designed to give a deterministic result (same results from initial state every time) for the moment. Making a physics engine 100% deterministic is not a simple task. Of course, this would be useful in the future.
  2. Yes, the timestep is not involved in the negative penetration depth issue. Most of the time, the physics time step is higher than the rendering framerate. We usually take several physics timesteps in a single rendering frame.
DanielChappuis commented 9 years ago

Did you change the default collision margin of the shapes ?

Your scene looks like the one in the "CollisionShapes" example of ReactPhysics3D. Maybe you can compare your code with this example to see what are the differences ?

jagoly commented 9 years ago

no, I've not changed the collision margins at all. The settings I listed are the only ones changed, everything else is default.

DanielChappuis commented 9 years ago

I have made modifications in getUnit() and normalize() methods of Vector2 and Vector3 classes to handle division by zero in a better way. I have also removed the assert tests here because we simply do not want a zero vector to be normalized at all instead of getting an assert error.

This change has been made in commit 8a26d8a0ca72ab2f1211902f7c5551adedb8ff16 in the develop branch. This will be available in the next release of the library.