|
@@ -14,6 +14,7 @@
|
|
|
#include <Jolt/Physics/Collision/Shape/ScaledShape.h>
|
|
|
#include <Jolt/Physics/Collision/CollisionDispatch.h>
|
|
|
#include <Jolt/Core/QuickSort.h>
|
|
|
+#include <Jolt/Core/ScopeExit.h>
|
|
|
#include <Jolt/Geometry/ConvexSupport.h>
|
|
|
#include <Jolt/Geometry/GJKClosestPoint.h>
|
|
|
#ifdef JPH_DEBUG_RENDERER
|
|
@@ -86,6 +87,7 @@ void CharacterVsCharacterCollisionSimple::CastCharacter(const CharacterVirtual *
|
|
|
|
|
|
CharacterVirtual::CharacterVirtual(const CharacterVirtualSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem) :
|
|
|
CharacterBase(inSettings, inSystem),
|
|
|
+ mID(inSettings->mID),
|
|
|
mBackFaceMode(inSettings->mBackFaceMode),
|
|
|
mPredictiveContactDistance(inSettings->mPredictiveContactDistance),
|
|
|
mMaxCollisionIterations(inSettings->mMaxCollisionIterations),
|
|
@@ -102,6 +104,8 @@ CharacterVirtual::CharacterVirtual(const CharacterVirtualSettings *inSettings, R
|
|
|
mRotation(inRotation),
|
|
|
mUserData(inUserData)
|
|
|
{
|
|
|
+ JPH_ASSERT(!mID.IsInvalid());
|
|
|
+
|
|
|
// Copy settings
|
|
|
SetMaxStrength(inSettings->mMaxStrength);
|
|
|
SetMass(inSettings->mMass);
|
|
@@ -203,12 +207,13 @@ void CharacterVirtual::sFillContactProperties(const CharacterVirtual *inCharacte
|
|
|
outContact.mMaterial = inCollector.GetContext()->GetMaterial(inResult.mSubShapeID2);
|
|
|
}
|
|
|
|
|
|
-void CharacterVirtual::sFillCharacterContactProperties(Contact &outContact, CharacterVirtual *inOtherCharacter, RVec3Arg inBaseOffset, const CollideShapeResult &inResult)
|
|
|
+void CharacterVirtual::sFillCharacterContactProperties(Contact &outContact, const CharacterVirtual *inOtherCharacter, RVec3Arg inBaseOffset, const CollideShapeResult &inResult)
|
|
|
{
|
|
|
outContact.mPosition = inBaseOffset + inResult.mContactPointOn2;
|
|
|
outContact.mLinearVelocity = inOtherCharacter->GetLinearVelocity();
|
|
|
outContact.mSurfaceNormal = outContact.mContactNormal = -inResult.mPenetrationAxis.NormalizedOr(Vec3::sZero());
|
|
|
outContact.mDistance = -inResult.mPenetrationDepth;
|
|
|
+ outContact.mCharacterIDB = inOtherCharacter->GetID();
|
|
|
outContact.mCharacterB = inOtherCharacter;
|
|
|
outContact.mSubShapeIDB = inResult.mSubShapeID2;
|
|
|
outContact.mMotionTypeB = EMotionType::Kinematic; // Other character is kinematic, we can't directly move it
|
|
@@ -305,8 +310,8 @@ void CharacterVirtual::ContactCastCollector::AddHit(const ShapeCastResult &inRes
|
|
|
&& inResult.mPenetrationAxis.Dot(mDisplacement) > 0.0f) // Ignore penetrations that we're moving away from
|
|
|
{
|
|
|
// Test if this contact should be ignored
|
|
|
- for (const IgnoredContact &c : mIgnoredContacts)
|
|
|
- if (c.mBodyID == inResult.mBodyID2 && c.mSubShapeID == inResult.mSubShapeID2)
|
|
|
+ for (const ContactKey &c : mIgnoredContacts)
|
|
|
+ if (c.mBodyB == inResult.mBodyID2 && c.mSubShapeIDB == inResult.mSubShapeID2)
|
|
|
return;
|
|
|
|
|
|
Contact contact;
|
|
@@ -428,14 +433,14 @@ void CharacterVirtual::RemoveConflictingContacts(TempContactList &ioContacts, Ig
|
|
|
if (contact1.mDistance < contact2.mDistance)
|
|
|
{
|
|
|
// Discard the 2nd contact
|
|
|
- outIgnoredContacts.emplace_back(contact2.mBodyB, contact2.mSubShapeIDB);
|
|
|
+ outIgnoredContacts.emplace_back(contact2);
|
|
|
ioContacts.erase(ioContacts.begin() + c2);
|
|
|
c2--;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// Discard the first contact
|
|
|
- outIgnoredContacts.emplace_back(contact1.mBodyB, contact1.mSubShapeIDB);
|
|
|
+ outIgnoredContacts.emplace_back(contact1);
|
|
|
ioContacts.erase(ioContacts.begin() + c1);
|
|
|
c1--;
|
|
|
break;
|
|
@@ -456,14 +461,38 @@ bool CharacterVirtual::ValidateContact(const Contact &inContact) const
|
|
|
return mListener->OnContactValidate(this, inContact.mBodyB, inContact.mSubShapeIDB);
|
|
|
}
|
|
|
|
|
|
-void CharacterVirtual::ContactAdded(const Contact &inContact, CharacterContactSettings &ioSettings) const
|
|
|
+void CharacterVirtual::ContactAdded(const Contact &inContact, CharacterContactSettings &ioSettings)
|
|
|
{
|
|
|
if (mListener != nullptr)
|
|
|
{
|
|
|
- if (inContact.mCharacterB != nullptr)
|
|
|
- mListener->OnCharacterContactAdded(this, inContact.mCharacterB, inContact.mSubShapeIDB, inContact.mPosition, -inContact.mContactNormal, ioSettings);
|
|
|
+ // Check if we already know this contact
|
|
|
+ ListenerContacts::iterator it = mListenerContacts.find(inContact);
|
|
|
+ if (it != mListenerContacts.end())
|
|
|
+ {
|
|
|
+ // Max 1 contact persisted callback
|
|
|
+ if (++it->second.mCount == 1)
|
|
|
+ {
|
|
|
+ if (inContact.mCharacterB != nullptr)
|
|
|
+ mListener->OnCharacterContactPersisted(this, inContact.mCharacterB, inContact.mSubShapeIDB, inContact.mPosition, -inContact.mContactNormal, ioSettings);
|
|
|
+ else
|
|
|
+ mListener->OnContactPersisted(this, inContact.mBodyB, inContact.mSubShapeIDB, inContact.mPosition, -inContact.mContactNormal, ioSettings);
|
|
|
+ it->second.mSettings = ioSettings;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Reuse the settings from the last call
|
|
|
+ ioSettings = it->second.mSettings;
|
|
|
+ }
|
|
|
+ }
|
|
|
else
|
|
|
- mListener->OnContactAdded(this, inContact.mBodyB, inContact.mSubShapeIDB, inContact.mPosition, -inContact.mContactNormal, ioSettings);
|
|
|
+ {
|
|
|
+ // New contact
|
|
|
+ if (inContact.mCharacterB != nullptr)
|
|
|
+ mListener->OnCharacterContactAdded(this, inContact.mCharacterB, inContact.mSubShapeIDB, inContact.mPosition, -inContact.mContactNormal, ioSettings);
|
|
|
+ else
|
|
|
+ mListener->OnContactAdded(this, inContact.mBodyB, inContact.mSubShapeIDB, inContact.mPosition, -inContact.mContactNormal, ioSettings);
|
|
|
+ mListenerContacts.insert(ListenerContacts::value_type(inContact, ioSettings));
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -538,7 +567,7 @@ bool CharacterVirtual::GetFirstContactForSweep(RVec3Arg inPosition, Vec3Arg inDi
|
|
|
mCharacterVsCharacterCollision->CastCharacter(this, start, inDisplacement, settings, base_offset, collector);
|
|
|
}
|
|
|
|
|
|
- if (contact.mBodyB.IsInvalid() && contact.mCharacterB == nullptr)
|
|
|
+ if (contact.mBodyB.IsInvalid() && contact.mCharacterIDB.IsInvalid())
|
|
|
return false;
|
|
|
|
|
|
// Store contact
|
|
@@ -630,7 +659,7 @@ void CharacterVirtual::DetermineConstraints(TempContactList &inContacts, float i
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-bool CharacterVirtual::HandleContact(Vec3Arg inVelocity, Constraint &ioConstraint, float inDeltaTime) const
|
|
|
+bool CharacterVirtual::HandleContact(Vec3Arg inVelocity, Constraint &ioConstraint, float inDeltaTime)
|
|
|
{
|
|
|
Contact &contact = *ioConstraint.mContact;
|
|
|
|
|
@@ -638,6 +667,9 @@ bool CharacterVirtual::HandleContact(Vec3Arg inVelocity, Constraint &ioConstrain
|
|
|
if (!ValidateContact(contact))
|
|
|
return false;
|
|
|
|
|
|
+ // We collided
|
|
|
+ contact.mHadCollision = true;
|
|
|
+
|
|
|
// Send contact added event
|
|
|
CharacterContactSettings settings;
|
|
|
ContactAdded(contact, settings);
|
|
@@ -702,7 +734,7 @@ void CharacterVirtual::SolveConstraints(Vec3Arg inVelocity, float inDeltaTime, f
|
|
|
#ifdef JPH_DEBUG_RENDERER
|
|
|
, bool inDrawConstraints
|
|
|
#endif // JPH_DEBUG_RENDERER
|
|
|
- ) const
|
|
|
+ )
|
|
|
{
|
|
|
// If there are no constraints we can immediately move to our target
|
|
|
if (ioConstraints.empty())
|
|
@@ -795,21 +827,16 @@ void CharacterVirtual::SolveConstraints(Vec3Arg inVelocity, float inDeltaTime, f
|
|
|
if (c->mContact->mWasDiscarded)
|
|
|
continue;
|
|
|
|
|
|
- // Check if we made contact with this before
|
|
|
- if (!c->mContact->mHadCollision)
|
|
|
+ // Handle the contact
|
|
|
+ if (!c->mContact->mHadCollision
|
|
|
+ && !HandleContact(velocity, *c, inDeltaTime))
|
|
|
{
|
|
|
- // Handle the contact
|
|
|
- if (!HandleContact(velocity, *c, inDeltaTime))
|
|
|
- {
|
|
|
- // Constraint should be ignored, remove it from the list
|
|
|
- c->mContact->mWasDiscarded = true;
|
|
|
-
|
|
|
- // Mark it as ignored for GetFirstContactForSweep
|
|
|
- ioIgnoredContacts.emplace_back(c->mContact->mBodyB, c->mContact->mSubShapeIDB);
|
|
|
- continue;
|
|
|
- }
|
|
|
+ // Constraint should be ignored, remove it from the list
|
|
|
+ c->mContact->mWasDiscarded = true;
|
|
|
|
|
|
- c->mContact->mHadCollision = true;
|
|
|
+ // Mark it as ignored for GetFirstContactForSweep
|
|
|
+ ioIgnoredContacts.emplace_back(*c->mContact);
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
// Cancel velocity of constraint if it cannot push the character
|
|
@@ -970,8 +997,12 @@ void CharacterVirtual::UpdateSupportingContact(bool inSkipContactVelocityCheck,
|
|
|
&& c.mDistance < mCollisionTolerance
|
|
|
&& (inSkipContactVelocityCheck || c.mSurfaceNormal.Dot(mLinearVelocity - c.mLinearVelocity) <= 1.0e-4f))
|
|
|
{
|
|
|
- if (ValidateContact(c) && !c.mIsSensorB)
|
|
|
+ if (ValidateContact(c))
|
|
|
+ {
|
|
|
+ CharacterContactSettings dummy;
|
|
|
+ ContactAdded(c, dummy);
|
|
|
c.mHadCollision = true;
|
|
|
+ }
|
|
|
else
|
|
|
c.mWasDiscarded = true;
|
|
|
}
|
|
@@ -990,7 +1021,7 @@ void CharacterVirtual::UpdateSupportingContact(bool inSkipContactVelocityCheck,
|
|
|
const Contact *deepest_contact = nullptr;
|
|
|
float smallest_distance = FLT_MAX;
|
|
|
for (const Contact &c : mActiveContacts)
|
|
|
- if (c.mHadCollision)
|
|
|
+ if (c.mHadCollision && !c.mWasDiscarded)
|
|
|
{
|
|
|
// Calculate the angle between the plane normal and the up direction
|
|
|
float cos_angle = c.mSurfaceNormal.Dot(mUp);
|
|
@@ -1138,16 +1169,20 @@ void CharacterVirtual::UpdateSupportingContact(bool inSkipContactVelocityCheck,
|
|
|
|
|
|
void CharacterVirtual::StoreActiveContacts(const TempContactList &inContacts, TempAllocator &inAllocator)
|
|
|
{
|
|
|
+ StartTrackingContactChanges();
|
|
|
+
|
|
|
mActiveContacts.assign(inContacts.begin(), inContacts.end());
|
|
|
|
|
|
UpdateSupportingContact(true, inAllocator);
|
|
|
+
|
|
|
+ FinishTrackingContactChanges();
|
|
|
}
|
|
|
|
|
|
void CharacterVirtual::MoveShape(RVec3 &ioPosition, Vec3Arg inVelocity, float inDeltaTime, ContactList *outActiveContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator
|
|
|
#ifdef JPH_DEBUG_RENDERER
|
|
|
, bool inDrawConstraints
|
|
|
#endif // JPH_DEBUG_RENDERER
|
|
|
- ) const
|
|
|
+ )
|
|
|
{
|
|
|
JPH_DET_LOG("CharacterVirtual::MoveShape: pos: " << ioPosition << " vel: " << inVelocity << " dt: " << inDeltaTime);
|
|
|
|
|
@@ -1248,6 +1283,7 @@ Vec3 CharacterVirtual::CancelVelocityTowardsSteepSlopes(Vec3Arg inDesiredVelocit
|
|
|
Vec3 desired_velocity = inDesiredVelocity;
|
|
|
for (const Contact &c : mActiveContacts)
|
|
|
if (c.mHadCollision
|
|
|
+ && !c.mWasDiscarded
|
|
|
&& IsSlopeTooSteep(c.mSurfaceNormal))
|
|
|
{
|
|
|
// Note that we use the contact normal to allow for better sliding as the surface normal may be in the opposite direction of movement.
|
|
@@ -1264,12 +1300,72 @@ Vec3 CharacterVirtual::CancelVelocityTowardsSteepSlopes(Vec3Arg inDesiredVelocit
|
|
|
return desired_velocity;
|
|
|
}
|
|
|
|
|
|
+void CharacterVirtual::StartTrackingContactChanges()
|
|
|
+{
|
|
|
+ // Check if we're starting for the first time
|
|
|
+ if (++mTrackingContactChanges > 1)
|
|
|
+ return;
|
|
|
+
|
|
|
+ // No need to track anything if we don't have a listener
|
|
|
+ JPH_ASSERT(mListenerContacts.empty());
|
|
|
+ if (mListener == nullptr)
|
|
|
+ return;
|
|
|
+
|
|
|
+ // Mark all current contacts as not seen
|
|
|
+ mListenerContacts.reserve(ListenerContacts::size_type(mActiveContacts.size()));
|
|
|
+ for (const Contact &c : mActiveContacts)
|
|
|
+ if (c.mHadCollision)
|
|
|
+ mListenerContacts.insert(ListenerContacts::value_type(c, ListenerContactValue()));
|
|
|
+}
|
|
|
+
|
|
|
+void CharacterVirtual::FinishTrackingContactChanges()
|
|
|
+{
|
|
|
+ // Check if we have to do anything
|
|
|
+ int count = --mTrackingContactChanges;
|
|
|
+ JPH_ASSERT(count >= 0, "Called FinishTrackingContactChanges more times than StartTrackingContactChanges");
|
|
|
+ if (count > 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ // No need to track anything if we don't have a listener
|
|
|
+ if (mListener == nullptr)
|
|
|
+ return;
|
|
|
+
|
|
|
+ // Since we can do multiple operations (e.g. Update followed by WalkStairs)
|
|
|
+ // we can end up with contacts that were marked as active to the listener but that are
|
|
|
+ // no longer in the active contact list. We go over all contacts and mark them again
|
|
|
+ // to ensure that these lists are in sync.
|
|
|
+ for (ListenerContacts::value_type &c : mListenerContacts)
|
|
|
+ c.second.mCount = 0;
|
|
|
+ for (const Contact &c : mActiveContacts)
|
|
|
+ if (c.mHadCollision)
|
|
|
+ {
|
|
|
+ ListenerContacts::iterator it = mListenerContacts.find(c);
|
|
|
+ JPH_ASSERT(it != mListenerContacts.end());
|
|
|
+ it->second.mCount = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Call contact removal callbacks
|
|
|
+ for (ListenerContacts::iterator it = mListenerContacts.begin(); it != mListenerContacts.end(); ++it)
|
|
|
+ if (it->second.mCount == 0)
|
|
|
+ {
|
|
|
+ const ContactKey &c = it->first;
|
|
|
+ if (!c.mCharacterIDB.IsInvalid())
|
|
|
+ mListener->OnCharacterContactRemoved(this, c.mCharacterIDB, c.mSubShapeIDB);
|
|
|
+ else
|
|
|
+ mListener->OnContactRemoved(this, c.mBodyB, c.mSubShapeIDB);
|
|
|
+ }
|
|
|
+ mListenerContacts.ClearAndKeepMemory();
|
|
|
+}
|
|
|
+
|
|
|
void CharacterVirtual::Update(float inDeltaTime, Vec3Arg inGravity, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator)
|
|
|
{
|
|
|
// If there's no delta time, we don't need to do anything
|
|
|
if (inDeltaTime <= 0.0f)
|
|
|
return;
|
|
|
|
|
|
+ StartTrackingContactChanges();
|
|
|
+ JPH_SCOPE_EXIT([this]() { FinishTrackingContactChanges(); });
|
|
|
+
|
|
|
// Remember delta time for checking if we're supported by the ground
|
|
|
mLastDeltaTime = inDeltaTime;
|
|
|
|
|
@@ -1416,6 +1512,7 @@ bool CharacterVirtual::CanWalkStairs(Vec3Arg inLinearVelocity) const
|
|
|
// Check contacts for steep slopes
|
|
|
for (const Contact &c : mActiveContacts)
|
|
|
if (c.mHadCollision
|
|
|
+ && !c.mWasDiscarded
|
|
|
&& c.mSurfaceNormal.Dot(horizontal_velocity - c.mLinearVelocity) < 0.0f // Pushing into the contact
|
|
|
&& IsSlopeTooSteep(c.mSurfaceNormal)) // Slope too steep
|
|
|
return true;
|
|
@@ -1425,6 +1522,9 @@ bool CharacterVirtual::CanWalkStairs(Vec3Arg inLinearVelocity) const
|
|
|
|
|
|
bool CharacterVirtual::WalkStairs(float inDeltaTime, Vec3Arg inStepUp, Vec3Arg inStepForward, Vec3Arg inStepForwardTest, Vec3Arg inStepDownExtra, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator)
|
|
|
{
|
|
|
+ StartTrackingContactChanges();
|
|
|
+ JPH_SCOPE_EXIT([this]() { FinishTrackingContactChanges(); });
|
|
|
+
|
|
|
// Move up
|
|
|
Vec3 up = inStepUp;
|
|
|
Contact contact;
|
|
@@ -1453,6 +1553,7 @@ bool CharacterVirtual::WalkStairs(float inDeltaTime, Vec3Arg inStepUp, Vec3Arg i
|
|
|
steep_slope_normals.reserve(mActiveContacts.size());
|
|
|
for (const Contact &c : mActiveContacts)
|
|
|
if (c.mHadCollision
|
|
|
+ && !c.mWasDiscarded
|
|
|
&& c.mSurfaceNormal.Dot(horizontal_velocity - c.mLinearVelocity) < 0.0f // Pushing into the contact
|
|
|
&& IsSlopeTooSteep(c.mSurfaceNormal)) // Slope too steep
|
|
|
steep_slope_normals.push_back(c.mSurfaceNormal);
|
|
@@ -1562,6 +1663,9 @@ bool CharacterVirtual::WalkStairs(float inDeltaTime, Vec3Arg inStepUp, Vec3Arg i
|
|
|
|
|
|
bool CharacterVirtual::StickToFloor(Vec3Arg inStepDown, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator)
|
|
|
{
|
|
|
+ StartTrackingContactChanges();
|
|
|
+ JPH_SCOPE_EXIT([this]() { FinishTrackingContactChanges(); });
|
|
|
+
|
|
|
// Try to find the floor
|
|
|
Contact contact;
|
|
|
IgnoredContactList dummy_ignored_contacts(inAllocator);
|
|
@@ -1587,6 +1691,9 @@ bool CharacterVirtual::StickToFloor(Vec3Arg inStepDown, const BroadPhaseLayerFil
|
|
|
|
|
|
void CharacterVirtual::ExtendedUpdate(float inDeltaTime, Vec3Arg inGravity, const ExtendedUpdateSettings &inSettings, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator)
|
|
|
{
|
|
|
+ StartTrackingContactChanges();
|
|
|
+ JPH_SCOPE_EXIT([this]() { FinishTrackingContactChanges(); });
|
|
|
+
|
|
|
// Update the velocity
|
|
|
Vec3 desired_velocity = mLinearVelocity;
|
|
|
mLinearVelocity = CancelVelocityTowardsSteepSlopes(desired_velocity);
|
|
@@ -1663,37 +1770,54 @@ void CharacterVirtual::ExtendedUpdate(float inDeltaTime, Vec3Arg inGravity, cons
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void CharacterVirtual::ContactKey::SaveState(StateRecorder &inStream) const
|
|
|
+{
|
|
|
+ inStream.Write(mBodyB);
|
|
|
+ inStream.Write(mCharacterIDB);
|
|
|
+ inStream.Write(mSubShapeIDB);
|
|
|
+}
|
|
|
+
|
|
|
+void CharacterVirtual::ContactKey::RestoreState(StateRecorder &inStream)
|
|
|
+{
|
|
|
+ inStream.Read(mBodyB);
|
|
|
+ inStream.Read(mCharacterIDB);
|
|
|
+ inStream.Read(mSubShapeIDB);
|
|
|
+}
|
|
|
+
|
|
|
void CharacterVirtual::Contact::SaveState(StateRecorder &inStream) const
|
|
|
{
|
|
|
+ ContactKey::SaveState(inStream);
|
|
|
+
|
|
|
inStream.Write(mPosition);
|
|
|
inStream.Write(mLinearVelocity);
|
|
|
inStream.Write(mContactNormal);
|
|
|
inStream.Write(mSurfaceNormal);
|
|
|
inStream.Write(mDistance);
|
|
|
inStream.Write(mFraction);
|
|
|
- inStream.Write(mBodyB);
|
|
|
- inStream.Write(mSubShapeIDB);
|
|
|
inStream.Write(mMotionTypeB);
|
|
|
+ inStream.Write(mIsSensorB);
|
|
|
inStream.Write(mHadCollision);
|
|
|
inStream.Write(mWasDiscarded);
|
|
|
inStream.Write(mCanPushCharacter);
|
|
|
- // Cannot store user data (may be a pointer) and material
|
|
|
+ // Cannot store pointers to character B, user data and material
|
|
|
}
|
|
|
|
|
|
void CharacterVirtual::Contact::RestoreState(StateRecorder &inStream)
|
|
|
{
|
|
|
+ ContactKey::RestoreState(inStream);
|
|
|
+
|
|
|
inStream.Read(mPosition);
|
|
|
inStream.Read(mLinearVelocity);
|
|
|
inStream.Read(mContactNormal);
|
|
|
inStream.Read(mSurfaceNormal);
|
|
|
inStream.Read(mDistance);
|
|
|
inStream.Read(mFraction);
|
|
|
- inStream.Read(mBodyB);
|
|
|
- inStream.Read(mSubShapeIDB);
|
|
|
inStream.Read(mMotionTypeB);
|
|
|
+ inStream.Read(mIsSensorB);
|
|
|
inStream.Read(mHadCollision);
|
|
|
inStream.Read(mWasDiscarded);
|
|
|
inStream.Read(mCanPushCharacter);
|
|
|
+ mCharacterB = nullptr; // Cannot restore character B
|
|
|
mUserData = 0; // Cannot restore user data
|
|
|
mMaterial = PhysicsMaterial::sDefault; // Cannot restore material
|
|
|
}
|