Răsfoiți Sursa

Changed Constraint API to specify the other body position / axis explicitly, and to specify limits as Vector2.
Fixed bugs in assigning parented RigidBody transforms after simulation step.
Optimized Constraint SetPosition() / SetAxis() to not recreate the constraint.

Lasse Öörni 13 ani în urmă
părinte
comite
9f331e56db

+ 3 - 3
Bin/Data/Scripts/Editor/EditorView.as

@@ -320,7 +320,7 @@ void HandlePostRenderUpdate()
     for (uint i = 0; i < selectedNodes.length; ++i)
     for (uint i = 0; i < selectedNodes.length; ++i)
     {
     {
         Node@ node = selectedNodes[i];
         Node@ node = selectedNodes[i];
-        debug.AddNode(node, false);
+        debug.AddNode(node, 1.0, false);
         for (uint j = 0; j < node.numComponents; ++j)
         for (uint j = 0; j < node.numComponents; ++j)
         {
         {
             Drawable@ drawable = cast<Drawable>(node.components[j]);
             Drawable@ drawable = cast<Drawable>(node.components[j]);
@@ -375,7 +375,7 @@ void ViewRaycast(bool mouseClick)
             Drawable@ drawable = result.drawable;
             Drawable@ drawable = result.drawable;
             if (debug !is null)
             if (debug !is null)
             {
             {
-                debug.AddNode(drawable.node, false);
+                debug.AddNode(drawable.node, 1.0, false);
                 drawable.DrawDebugGeometry(debug, false);
                 drawable.DrawDebugGeometry(debug, false);
             }
             }
             selected = drawable;
             selected = drawable;
@@ -396,7 +396,7 @@ void ViewRaycast(bool mouseClick)
             RigidBody@ body = result.body;
             RigidBody@ body = result.body;
             if (debug !is null)
             if (debug !is null)
             {
             {
-                debug.AddNode(body.node, false);
+                debug.AddNode(body.node, 1.0, false);
                 body.DrawDebugGeometry(debug, false);
                 body.DrawDebugGeometry(debug, false);
             }
             }
             selected = body;
             selected = body;

+ 6 - 3
Docs/Reference.dox

@@ -839,13 +839,16 @@ By default rigid bodies can move and rotate about all 3 coordinate axes when for
 
 
 To prevent tunneling of a fast moving rigid body through obstacles, continuous collision detection can be used. It approximates the object as a swept sphere, but has a performance cost, so it should be used only when necessary. Call \ref RigidBody::SetCcdRadius "SetCcdRadius()" and \ref RigidBody::SetCcdMotionThreshold "SetCcdMotionThreshold()" with non-zero values to enable. To prevent false collisions, the body's actual collision shape should completely contain the radius. The motion threshold is the required motion per simulation step for CCD to kick in: for example a box with size 1 should have motion threshold 1 as well.
 To prevent tunneling of a fast moving rigid body through obstacles, continuous collision detection can be used. It approximates the object as a swept sphere, but has a performance cost, so it should be used only when necessary. Call \ref RigidBody::SetCcdRadius "SetCcdRadius()" and \ref RigidBody::SetCcdMotionThreshold "SetCcdMotionThreshold()" with non-zero values to enable. To prevent false collisions, the body's actual collision shape should completely contain the radius. The motion threshold is the required motion per simulation step for CCD to kick in: for example a box with size 1 should have motion threshold 1 as well.
 
 
-All physics calculations are performed in world space. Nodes containing a RigidBody component should be parented to the Scene (root node) to ensure correct operation.
+All physics calculations are performed in world space. Nodes containing a RigidBody component should preferably be parented to the Scene (root node) to ensure independent
+motion. For ragdolls this is not absolute, as retaining proper bone hierarchy is more important, but be aware that the ragdoll bones may drift far from the animated model's root scene node.
 
 
 \section Physics_ConstraintParameters Constraint parameters
 \section Physics_ConstraintParameters Constraint parameters
 
 
-Hinge and slider constraints support defining limits for the motion. To be generic, these are encoded slightly unintuitively into Vector3's.
+Constraint position (and axis if relevant) needs to be defined relative to both connected bodies. If a constraint connects a body to the static world, then the "other body position" and "other body axis" mean the static end's position in world space.
+
+Hinge and slider constraints support defining limits for the motion. To be generic, these are encoded slightly unintuitively into Vector2's.
 For a hinge constraint, the low and high limit X coordinates define the minimum and maximum angle in degrees. For example -45 to 45. For a slider constraint,
 For a hinge constraint, the low and high limit X coordinates define the minimum and maximum angle in degrees. For example -45 to 45. For a slider constraint,
-the X coordinates define the maximum linear motion in world space units, and the Y coordinates define maximum angular motion in degrees. The cone twist constraint uses only the high limit to define the maximum angles (minimum angle is always -maximum) in the following manner: The X coordinate is the limit of the twist (main) axis, while Y and Z are the limits of the swinging motion about the other axes.
+the X coordinates define the maximum linear motion in world space units, and the Y coordinates define maximum angular motion in degrees. The cone twist constraint uses only the high limit to define the maximum angles (minimum angle is always -maximum) in the following manner: The X coordinate is the limit of the twist (main) axis, while Y is the limit of the swinging motion about the other axes.
 
 
 \section Physics_Events Physics events
 \section Physics_Events Physics events
 
 

+ 7 - 3
Docs/ScriptAPI.dox

@@ -1457,7 +1457,7 @@ Methods:<br>
 - void Remove()
 - void Remove()
 - void MarkNetworkUpdate() const
 - void MarkNetworkUpdate() const
 - void AddLine(const Vector3&, const Vector3&, const Color&, bool arg3 = true)
 - void AddLine(const Vector3&, const Vector3&, const Color&, bool arg3 = true)
-- void AddNode(Node@, bool arg1 = true)
+- void AddNode(Node@, float arg1 = 1.0, bool arg2 = true)
 - void AddBoundingBox(const BoundingBox&, const Color&, bool arg2 = true)
 - void AddBoundingBox(const BoundingBox&, const Color&, bool arg2 = true)
 - void AddFrustum(const Frustum&, const Color&, bool arg2 = true)
 - void AddFrustum(const Frustum&, const Color&, bool arg2 = true)
 - void AddPolyhedron(const Polyhedron&, const Color&, bool arg2 = true)
 - void AddPolyhedron(const Polyhedron&, const Color&, bool arg2 = true)
@@ -4056,6 +4056,7 @@ Methods:<br>
 - void Remove()
 - void Remove()
 - void MarkNetworkUpdate() const
 - void MarkNetworkUpdate() const
 - void DrawDebugGeometry(DebugRenderer@, bool)
 - void DrawDebugGeometry(DebugRenderer@, bool)
+- void SetParameters(ConstraintType, RigidBody@, const Vector3&, const Vector3&, const Vector3&, const Vector3&, const Vector2&, const Vector2&, bool)
 
 
 Properties:<br>
 Properties:<br>
 - ShortStringHash type (readonly)
 - ShortStringHash type (readonly)
@@ -4068,8 +4069,11 @@ Properties:<br>
 - ConstraintType constraintType
 - ConstraintType constraintType
 - Vector3& position
 - Vector3& position
 - Vector3& axis
 - Vector3& axis
-- Vector3& highLimit
-- Vector3& lowLimit
+- Vector3& otherPosition
+- Vector3& otherAxis
+- Vector2& highLimit
+- Vector2& lowLimit
+- bool disableCollision
 - RigidBody@ ownBody (readonly)
 - RigidBody@ ownBody (readonly)
 - RigidBody@ otherBody
 - RigidBody@ otherBody
 
 

+ 1 - 1
Engine/Engine/GraphicsAPI.cpp

@@ -882,7 +882,7 @@ static void RegisterDebugRenderer(asIScriptEngine* engine)
 {
 {
     RegisterComponent<DebugRenderer>(engine, "DebugRenderer", true, false);
     RegisterComponent<DebugRenderer>(engine, "DebugRenderer", true, false);
     engine->RegisterObjectMethod("DebugRenderer", "void AddLine(const Vector3&in, const Vector3&in, const Color&in, bool depthTest = true)", asMETHODPR(DebugRenderer, AddLine, (const Vector3&, const Vector3&, const Color&, bool), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("DebugRenderer", "void AddLine(const Vector3&in, const Vector3&in, const Color&in, bool depthTest = true)", asMETHODPR(DebugRenderer, AddLine, (const Vector3&, const Vector3&, const Color&, bool), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("DebugRenderer", "void AddNode(Node@+, bool depthTest = true)", asMETHOD(DebugRenderer, AddNode), asCALL_THISCALL);
+    engine->RegisterObjectMethod("DebugRenderer", "void AddNode(Node@+, float scale = 1.0, bool depthTest = true)", asMETHOD(DebugRenderer, AddNode), asCALL_THISCALL);
     engine->RegisterObjectMethod("DebugRenderer", "void AddBoundingBox(const BoundingBox&in, const Color&in, bool depthTest = true)", asMETHODPR(DebugRenderer, AddBoundingBox, (const BoundingBox&, const Color&, bool), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("DebugRenderer", "void AddBoundingBox(const BoundingBox&in, const Color&in, bool depthTest = true)", asMETHODPR(DebugRenderer, AddBoundingBox, (const BoundingBox&, const Color&, bool), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("DebugRenderer", "void AddFrustum(const Frustum&in, const Color&in, bool depthTest = true)", asMETHOD(DebugRenderer, AddFrustum), asCALL_THISCALL);
     engine->RegisterObjectMethod("DebugRenderer", "void AddFrustum(const Frustum&in, const Color&in, bool depthTest = true)", asMETHOD(DebugRenderer, AddFrustum), asCALL_THISCALL);
     engine->RegisterObjectMethod("DebugRenderer", "void AddPolyhedron(const Polyhedron&in, const Color&in, bool depthTest = true)", asMETHOD(DebugRenderer, AddPolyhedron), asCALL_THISCALL);
     engine->RegisterObjectMethod("DebugRenderer", "void AddPolyhedron(const Polyhedron&in, const Color&in, bool depthTest = true)", asMETHOD(DebugRenderer, AddPolyhedron), asCALL_THISCALL);

+ 11 - 4
Engine/Engine/PhysicsAPI.cpp

@@ -169,16 +169,23 @@ static void RegisterConstraint(asIScriptEngine* engine)
     engine->RegisterEnumValue("ConstraintType", "CONSTRAINT_CONETWIST", CONSTRAINT_CONETWIST);
     engine->RegisterEnumValue("ConstraintType", "CONSTRAINT_CONETWIST", CONSTRAINT_CONETWIST);
     
     
     RegisterComponent<Constraint>(engine, "Constraint");
     RegisterComponent<Constraint>(engine, "Constraint");
+    engine->RegisterObjectMethod("Constraint", "void SetParameters(ConstraintType, RigidBody@+, const Vector3&in, const Vector3&in, const Vector3&in, const Vector3&in, const Vector2&in, const Vector2&in, bool)", asMETHOD(Constraint, SetParameters), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "void set_constraintType(ConstraintType)", asMETHOD(Constraint, SetConstraintType), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "void set_constraintType(ConstraintType)", asMETHOD(Constraint, SetConstraintType), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "ConstraintType get_constraintType() const", asMETHOD(Constraint, GetConstraintType), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "ConstraintType get_constraintType() const", asMETHOD(Constraint, GetConstraintType), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "void set_position(const Vector3&in)", asMETHOD(Constraint, SetPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "void set_position(const Vector3&in)", asMETHOD(Constraint, SetPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "const Vector3& get_position() const", asMETHOD(Constraint, GetPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "const Vector3& get_position() const", asMETHOD(Constraint, GetPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "void set_axis(const Vector3&in)", asMETHOD(Constraint, SetAxis), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "void set_axis(const Vector3&in)", asMETHOD(Constraint, SetAxis), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "const Vector3& get_axis() const", asMETHOD(Constraint, GetAxis), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "const Vector3& get_axis() const", asMETHOD(Constraint, GetAxis), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Constraint", "void set_highLimit(const Vector3&in)", asMETHOD(Constraint, SetHighLimit), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Constraint", "const Vector3& get_highLimit() const", asMETHOD(Constraint, GetHighLimit), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Constraint", "void set_lowLimit(const Vector3&in)", asMETHOD(Constraint, SetLowLimit), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Constraint", "const Vector3& get_lowLimit() const", asMETHOD(Constraint, GetLowLimit), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "void set_otherPosition(const Vector3&in)", asMETHOD(Constraint, SetOtherPosition), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "const Vector3& get_otherPosition() const", asMETHOD(Constraint, GetOtherPosition), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "void set_otherAxis(const Vector3&in)", asMETHOD(Constraint, SetOtherAxis), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "const Vector3& get_otherAxis() const", asMETHOD(Constraint, GetOtherAxis), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "void set_highLimit(const Vector2&in)", asMETHOD(Constraint, SetHighLimit), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "const Vector2& get_highLimit() const", asMETHOD(Constraint, GetHighLimit), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "void set_lowLimit(const Vector2&in)", asMETHOD(Constraint, SetLowLimit), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "const Vector2& get_lowLimit() const", asMETHOD(Constraint, GetLowLimit), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "void set_disableCollision(bool)", asMETHOD(Constraint, SetDisableCollision), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "bool get_disableCollision() const" ,asMETHOD(Constraint, GetDisableCollision), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "RigidBody@+ get_ownBody() const", asMETHOD(Constraint, GetOwnBody), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "RigidBody@+ get_ownBody() const", asMETHOD(Constraint, GetOwnBody), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "void set_otherBody(RigidBody@+)", asMETHOD(Constraint, SetOtherBody), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "void set_otherBody(RigidBody@+)", asMETHOD(Constraint, SetOtherBody), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "RigidBody@+ get_otherBody() const", asMETHOD(Constraint, GetOtherBody), asCALL_THISCALL);
     engine->RegisterObjectMethod("Constraint", "RigidBody@+ get_otherBody() const", asMETHOD(Constraint, GetOtherBody), asCALL_THISCALL);

+ 7 - 7
Engine/Graphics/DebugRenderer.cpp

@@ -84,7 +84,7 @@ void DebugRenderer::AddLine(const Vector3& start, const Vector3& end, unsigned c
         noDepthLines_.Push(DebugLine(start, end, color));
         noDepthLines_.Push(DebugLine(start, end, color));
 }
 }
 
 
-void DebugRenderer::AddNode(Node* node, bool depthTest)
+void DebugRenderer::AddNode(Node* node, float scale, bool depthTest)
 {
 {
     if (!node)
     if (!node)
         return;
         return;
@@ -94,15 +94,15 @@ void DebugRenderer::AddNode(Node* node, bool depthTest)
     
     
     if (depthTest)
     if (depthTest)
     {
     {
-        lines_.Push(DebugLine(start, start + rotation * Vector3::RIGHT, Color::RED.ToUInt()));
-        lines_.Push(DebugLine(start, start + rotation * Vector3::UP, Color::GREEN.ToUInt()));
-        lines_.Push(DebugLine(start, start + rotation * Vector3::FORWARD, Color::BLUE.ToUInt()));
+        lines_.Push(DebugLine(start, start + rotation * (scale * Vector3::RIGHT), Color::RED.ToUInt()));
+        lines_.Push(DebugLine(start, start + rotation * (scale * Vector3::UP), Color::GREEN.ToUInt()));
+        lines_.Push(DebugLine(start, start + rotation * (scale * Vector3::FORWARD), Color::BLUE.ToUInt()));
     }
     }
     else
     else
     {
     {
-        noDepthLines_.Push(DebugLine(start, start + rotation * Vector3::RIGHT, Color::RED.ToUInt()));
-        noDepthLines_.Push(DebugLine(start, start + rotation * Vector3::UP, Color::GREEN.ToUInt()));
-        noDepthLines_.Push(DebugLine(start, start + rotation * Vector3::FORWARD, Color::BLUE.ToUInt()));
+        noDepthLines_.Push(DebugLine(start, start + rotation * (scale * Vector3::RIGHT), Color::RED.ToUInt()));
+        noDepthLines_.Push(DebugLine(start, start + rotation * (scale * Vector3::UP), Color::GREEN.ToUInt()));
+        noDepthLines_.Push(DebugLine(start, start + rotation * (scale * Vector3::FORWARD), Color::BLUE.ToUInt()));
     }
     }
 }
 }
 
 

+ 2 - 2
Engine/Graphics/DebugRenderer.h

@@ -81,8 +81,8 @@ public:
     void AddLine(const Vector3& start, const Vector3& end, const Color& color, bool depthTest = true);
     void AddLine(const Vector3& start, const Vector3& end, const Color& color, bool depthTest = true);
     /// Add a line with color already converted to unsigned.
     /// Add a line with color already converted to unsigned.
     void AddLine(const Vector3& start, const Vector3& end, unsigned color, bool depthTest = true);
     void AddLine(const Vector3& start, const Vector3& end, unsigned color, bool depthTest = true);
-    /// Add a scene node represented as its local axes.
-    void AddNode(Node* node, bool depthTest = true);
+    /// Add a scene node represented as its coordinate axes.
+    void AddNode(Node* node, float scale = 1.0f, bool depthTest = true);
     /// Add a bounding box.
     /// Add a bounding box.
     void AddBoundingBox(const BoundingBox& box, const Color& color, bool depthTest = true);
     void AddBoundingBox(const BoundingBox& box, const Color& color, bool depthTest = true);
     /// Add a bounding box with transform.
     /// Add a bounding box with transform.

+ 140 - 49
Engine/Physics/Constraint.cpp

@@ -57,14 +57,13 @@ Constraint::Constraint(Context* context) :
     type_(CONSTRAINT_POINT),
     type_(CONSTRAINT_POINT),
     position_(Vector3::ZERO),
     position_(Vector3::ZERO),
     axis_(Vector3::RIGHT),
     axis_(Vector3::RIGHT),
-    otherBodyPosition_(Vector3::ZERO),
-    otherBodyAxis_(Vector3::RIGHT),
-    lowLimit_(Vector3::ZERO),
-    highLimit_(Vector3::ZERO),
+    otherPosition_(Vector3::ZERO),
+    otherAxis_(Vector3::RIGHT),
+    highLimit_(Vector2::ZERO),
+    lowLimit_(Vector2::ZERO),
     otherBodyNodeID_(0),
     otherBodyNodeID_(0),
     disableCollision_(false),
     disableCollision_(false),
-    recreateConstraint_(false),
-    otherBodyPositionValid_(false)
+    recreateConstraint_(false)
 {
 {
 }
 }
 
 
@@ -83,11 +82,11 @@ void Constraint::RegisterObject(Context* context)
     ENUM_ATTRIBUTE(Constraint, "Constraint Type", type_, typeNames, CONSTRAINT_POINT, AM_DEFAULT);
     ENUM_ATTRIBUTE(Constraint, "Constraint Type", type_, typeNames, CONSTRAINT_POINT, AM_DEFAULT);
     ATTRIBUTE(Constraint, VAR_VECTOR3, "Position", position_, Vector3::ZERO, AM_DEFAULT);
     ATTRIBUTE(Constraint, VAR_VECTOR3, "Position", position_, Vector3::ZERO, AM_DEFAULT);
     ATTRIBUTE(Constraint, VAR_VECTOR3, "Axis", axis_, Vector3::RIGHT, AM_DEFAULT);
     ATTRIBUTE(Constraint, VAR_VECTOR3, "Axis", axis_, Vector3::RIGHT, AM_DEFAULT);
-    ATTRIBUTE(Constraint, VAR_VECTOR3, "Other Body Position", otherBodyPosition_, Vector3::ZERO, AM_DEFAULT | AM_NOEDIT);
-    ATTRIBUTE(Constraint, VAR_VECTOR3, "Other Body Axis", otherBodyAxis_, Vector3::RIGHT, AM_DEFAULT | AM_NOEDIT);
-    REF_ACCESSOR_ATTRIBUTE(Constraint, VAR_VECTOR3, "High Limit", GetHighLimit, SetHighLimit, Vector3, Vector3::ZERO, AM_DEFAULT);
-    REF_ACCESSOR_ATTRIBUTE(Constraint, VAR_VECTOR3, "Low Limit", GetLowLimit, SetLowLimit, Vector3, Vector3::ZERO, AM_DEFAULT);
+    ATTRIBUTE(Constraint, VAR_VECTOR3, "Other Body Position", otherPosition_, Vector3::ZERO, AM_DEFAULT);
+    ATTRIBUTE(Constraint, VAR_VECTOR3, "Other Body Axis", otherAxis_, Vector3::RIGHT, AM_DEFAULT);
     ATTRIBUTE(Constraint, VAR_INT, "Other Body NodeID", otherBodyNodeID_, 0, AM_DEFAULT | AM_NODEID);
     ATTRIBUTE(Constraint, VAR_INT, "Other Body NodeID", otherBodyNodeID_, 0, AM_DEFAULT | AM_NODEID);
+    REF_ACCESSOR_ATTRIBUTE(Constraint, VAR_VECTOR2, "High Limit", GetHighLimit, SetHighLimit, Vector2, Vector2::ZERO, AM_DEFAULT);
+    REF_ACCESSOR_ATTRIBUTE(Constraint, VAR_VECTOR2, "Low Limit", GetLowLimit, SetLowLimit, Vector2, Vector2::ZERO, AM_DEFAULT);
     ATTRIBUTE(Constraint, VAR_BOOL, "Disable Collision", disableCollision_, false, AM_DEFAULT);
     ATTRIBUTE(Constraint, VAR_BOOL, "Disable Collision", disableCollision_, false, AM_DEFAULT);
 }
 }
 
 
@@ -97,11 +96,7 @@ void Constraint::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
     
     
     // Change of any non-accessor attribute requires recreation of the constraint
     // Change of any non-accessor attribute requires recreation of the constraint
     if (!attr.accessor_)
     if (!attr.accessor_)
-    {
         recreateConstraint_ = true;
         recreateConstraint_ = true;
-        if (attr.offset_ == offsetof(Constraint, otherBodyPosition_) || attr.offset_ == offsetof(Constraint, otherBodyAxis_))
-            otherBodyPositionValid_ = true;
-    }
 }
 }
 
 
 void Constraint::ApplyAttributes()
 void Constraint::ApplyAttributes()
@@ -176,9 +171,7 @@ void Constraint::SetPosition(const Vector3& position)
     if (position != position_)
     if (position != position_)
     {
     {
         position_ = position;
         position_ = position;
-        /// \todo Optimize and do not recreate the constraint
-        if (constraint_)
-            CreateConstraint();
+        ApplyFrames();
         MarkNetworkUpdate();
         MarkNetworkUpdate();
     }
     }
 }
 }
@@ -188,14 +181,32 @@ void Constraint::SetAxis(const Vector3& axis)
     if (axis != axis_)
     if (axis != axis_)
     {
     {
         axis_ = axis;
         axis_ = axis;
-        /// \todo Optimize and do not recreate the constraint
-        if (constraint_ && constraint_->getConstraintType() == HINGE_CONSTRAINT_TYPE)
-            CreateConstraint();
+        ApplyFrames();
         MarkNetworkUpdate();
         MarkNetworkUpdate();
     }
     }
 }
 }
 
 
-void Constraint::SetHighLimit(const Vector3& limit)
+void Constraint::SetOtherPosition(const Vector3& position)
+{
+    if (position != otherPosition_)
+    {
+        otherPosition_ = position;
+        ApplyFrames();
+        MarkNetworkUpdate();
+    }
+}
+
+void Constraint::SetOtherAxis(const Vector3& axis)
+{
+    if (axis != otherAxis_)
+    {
+        otherAxis_ = axis;
+        ApplyFrames();
+        MarkNetworkUpdate();
+    }
+}
+
+void Constraint::SetHighLimit(const Vector2& limit)
 {
 {
     if (limit != highLimit_)
     if (limit != highLimit_)
     {
     {
@@ -205,7 +216,7 @@ void Constraint::SetHighLimit(const Vector3& limit)
     }
     }
 }
 }
 
 
-void Constraint::SetLowLimit(const Vector3& limit)
+void Constraint::SetLowLimit(const Vector2& limit)
 {
 {
     if (limit != lowLimit_)
     if (limit != lowLimit_)
     {
     {
@@ -215,6 +226,39 @@ void Constraint::SetLowLimit(const Vector3& limit)
     }
     }
 }
 }
 
 
+void Constraint::SetDisableCollision(bool disable)
+{
+    if (disable != disableCollision_)
+    {
+        disableCollision_ = disable;
+        CreateConstraint();
+        MarkNetworkUpdate();
+    }
+}
+
+void Constraint::SetParameters(ConstraintType type, RigidBody* otherBody, const Vector3& position, const Vector3& axis, const Vector3& otherPosition, const Vector3& otherAxis, const Vector2& highLimit, const Vector2& lowLimit, bool disableCollision)
+{
+    if (otherBody_)
+        otherBody_->RemoveConstraint(this);
+    
+    type_ = type;
+    otherBody_ = otherBody;
+    position_ = position;
+    axis_ = axis;
+    otherPosition_ = otherPosition;
+    otherAxis_ = otherAxis;
+    highLimit_ = highLimit;
+    lowLimit_ = lowLimit;
+    disableCollision_ = disableCollision;
+    
+    // Update the connected body attribute
+    Node* otherNode = otherBody_ ? otherBody_->GetNode() : 0;
+    otherBodyNodeID_ = otherNode ? otherNode->GetID() : 0;
+    
+    CreateConstraint();
+    MarkNetworkUpdate();
+}
+
 void Constraint::ReleaseConstraint()
 void Constraint::ReleaseConstraint()
 {
 {
     if (constraint_)
     if (constraint_)
@@ -254,8 +298,9 @@ void Constraint::OnNodeSet(Node* node)
 
 
 void Constraint::OnMarkedDirty(Node* node)
 void Constraint::OnMarkedDirty(Node* node)
 {
 {
+    /// \todo This does not catch the connected body node's scale changing
     if (!node->GetWorldScale().Equals(cachedWorldScale_))
     if (!node->GetWorldScale().Equals(cachedWorldScale_))
-        CreateConstraint();
+        ApplyFrames();
 }
 }
 
 
 void Constraint::CreateConstraint()
 void Constraint::CreateConstraint()
@@ -276,36 +321,25 @@ void Constraint::CreateConstraint()
     if (!otherBody)
     if (!otherBody)
         otherBody = &btTypedConstraint::getFixedBody();
         otherBody = &btTypedConstraint::getFixedBody();
     
     
-    btTransform ownInverse = ownBody->getWorldTransform().inverse();
-    btTransform otherInverse = otherBody->getWorldTransform().inverse();
-    
-    // If the deserialized constraint other body position is valid, use it, but only this time
-    if (otherBodyPositionValid_)
-        otherBodyPositionValid_ = false;
-    else
-    {
-        // Otherwise calculate it from own body's position
-        otherBodyPosition_ = ToVector3(otherInverse * (ownBody->getWorldTransform() * ToBtVector3(position_ *
-            cachedWorldScale_)));
-        otherBodyAxis_ = ToVector3(otherInverse.getBasis() * (ownBody->getWorldTransform().getBasis() *
-            ToBtVector3(axis_)));
-    }
+    Vector3 ownBodyScaledPosition = position_ * cachedWorldScale_;
+    Vector3 otherBodyScaledPosition = otherBody_ ? otherPosition_ * otherBody_->GetNode()->GetWorldScale() :
+        otherPosition_;
     
     
     switch (type_)
     switch (type_)
     {
     {
     case CONSTRAINT_POINT:
     case CONSTRAINT_POINT:
         {
         {
-            constraint_ = new btPoint2PointConstraint(*ownBody, *otherBody, ToBtVector3(position_ * cachedWorldScale_),
-                ToBtVector3(otherBodyPosition_));
+            constraint_ = new btPoint2PointConstraint(*ownBody, *otherBody, ToBtVector3(ownBodyScaledPosition),
+                ToBtVector3(otherBodyScaledPosition));
         }
         }
         break;
         break;
         
         
     case CONSTRAINT_HINGE:
     case CONSTRAINT_HINGE:
         {
         {
             Quaternion ownRotation(Vector3::FORWARD, axis_);
             Quaternion ownRotation(Vector3::FORWARD, axis_);
-            Quaternion otherRotation(Vector3::FORWARD, otherBodyAxis_);
-            btTransform ownFrame(ToBtQuaternion(ownRotation), ToBtVector3(position_ * cachedWorldScale_));
-            btTransform otherFrame(ToBtQuaternion(otherRotation), ToBtVector3(otherBodyPosition_));
+            Quaternion otherRotation(Vector3::FORWARD, otherAxis_);
+            btTransform ownFrame(ToBtQuaternion(ownRotation), ToBtVector3(ownBodyScaledPosition));
+            btTransform otherFrame(ToBtQuaternion(otherRotation), ToBtVector3(otherBodyScaledPosition));
             constraint_ = new btHingeConstraint(*ownBody, *otherBody, ownFrame, otherFrame);
             constraint_ = new btHingeConstraint(*ownBody, *otherBody, ownFrame, otherFrame);
         }
         }
         break;
         break;
@@ -313,9 +347,9 @@ void Constraint::CreateConstraint()
     case CONSTRAINT_SLIDER:
     case CONSTRAINT_SLIDER:
         {
         {
             Quaternion ownRotation(Vector3::RIGHT, axis_);
             Quaternion ownRotation(Vector3::RIGHT, axis_);
-            Quaternion otherRotation(Vector3::RIGHT, otherBodyAxis_);
-            btTransform ownFrame(ToBtQuaternion(ownRotation), ToBtVector3(position_ * cachedWorldScale_));
-            btTransform otherFrame(ToBtQuaternion(otherRotation), ToBtVector3(otherBodyPosition_));
+            Quaternion otherRotation(Vector3::RIGHT, otherAxis_);
+            btTransform ownFrame(ToBtQuaternion(ownRotation), ToBtVector3(ownBodyScaledPosition));
+            btTransform otherFrame(ToBtQuaternion(otherRotation), ToBtVector3(otherBodyScaledPosition));
             constraint_ = new btSliderConstraint(*ownBody, *otherBody, ownFrame, otherFrame, false);
             constraint_ = new btSliderConstraint(*ownBody, *otherBody, ownFrame, otherFrame, false);
         }
         }
         break;
         break;
@@ -323,9 +357,9 @@ void Constraint::CreateConstraint()
     case CONSTRAINT_CONETWIST:
     case CONSTRAINT_CONETWIST:
         {
         {
             Quaternion ownRotation(Vector3::RIGHT, axis_);
             Quaternion ownRotation(Vector3::RIGHT, axis_);
-            Quaternion otherRotation(Vector3::RIGHT, otherBodyAxis_);
-            btTransform ownFrame(ToBtQuaternion(ownRotation), ToBtVector3(position_ * cachedWorldScale_));
-            btTransform otherFrame(ToBtQuaternion(otherRotation), ToBtVector3(otherBodyPosition_));
+            Quaternion otherRotation(Vector3::RIGHT, otherAxis_);
+            btTransform ownFrame(ToBtQuaternion(ownRotation), ToBtVector3(ownBodyScaledPosition));
+            btTransform otherFrame(ToBtQuaternion(otherRotation), ToBtVector3(otherBodyScaledPosition));
             constraint_ = new btConeTwistConstraint(*ownBody, *otherBody, ownFrame, otherFrame);
             constraint_ = new btConeTwistConstraint(*ownBody, *otherBody, ownFrame, otherFrame);
         }
         }
         break;
         break;
@@ -341,6 +375,63 @@ void Constraint::CreateConstraint()
     physicsWorld_->GetWorld()->addConstraint(constraint_, disableCollision_);
     physicsWorld_->GetWorld()->addConstraint(constraint_, disableCollision_);
 }
 }
 
 
+void Constraint::ApplyFrames()
+{
+    if (!constraint_)
+        return;
+    
+    if (node_)
+        cachedWorldScale_ = node_->GetWorldScale();
+    
+    Vector3 ownBodyScaledPosition = position_ * cachedWorldScale_;
+    Vector3 otherBodyScaledPosition = otherBody_ ? otherPosition_ * otherBody_->GetNode()->GetWorldScale() :
+        otherPosition_;
+    
+    switch (constraint_->getConstraintType())
+    {
+    case POINT2POINT_CONSTRAINT_TYPE:
+        {
+            btPoint2PointConstraint* pointConstraint = static_cast<btPoint2PointConstraint*>(constraint_);
+            pointConstraint->setPivotA(ToBtVector3(ownBodyScaledPosition));
+            pointConstraint->setPivotB(ToBtVector3(otherBodyScaledPosition));
+        }
+        break;
+        
+    case HINGE_CONSTRAINT_TYPE:
+        {
+            btHingeConstraint* hingeConstraint = static_cast<btHingeConstraint*>(constraint_);
+            Quaternion ownRotation(Vector3::FORWARD, axis_);
+            Quaternion otherRotation(Vector3::FORWARD, otherAxis_);
+            btTransform ownFrame(ToBtQuaternion(ownRotation), ToBtVector3(ownBodyScaledPosition));
+            btTransform otherFrame(ToBtQuaternion(otherRotation), ToBtVector3(otherBodyScaledPosition));
+            hingeConstraint->setFrames(ownFrame, otherFrame);
+        }
+        break;
+        
+    case SLIDER_CONSTRAINT_TYPE:
+        {
+            btSliderConstraint* sliderConstraint = static_cast<btSliderConstraint*>(constraint_);
+            Quaternion ownRotation(Vector3::RIGHT, axis_);
+            Quaternion otherRotation(Vector3::RIGHT, otherAxis_);
+            btTransform ownFrame(ToBtQuaternion(ownRotation), ToBtVector3(ownBodyScaledPosition));
+            btTransform otherFrame(ToBtQuaternion(otherRotation), ToBtVector3(otherBodyScaledPosition));
+            sliderConstraint->setFrames(ownFrame, otherFrame);
+        }
+        break;
+        
+    case CONETWIST_CONSTRAINT_TYPE:
+        {
+            btConeTwistConstraint* coneTwistConstraint = static_cast<btConeTwistConstraint*>(constraint_);
+            Quaternion ownRotation(Vector3::RIGHT, axis_);
+            Quaternion otherRotation(Vector3::RIGHT, otherAxis_);
+            btTransform ownFrame(ToBtQuaternion(ownRotation), ToBtVector3(ownBodyScaledPosition));
+            btTransform otherFrame(ToBtQuaternion(otherRotation), ToBtVector3(otherBodyScaledPosition));
+            coneTwistConstraint->setFrames(ownFrame, otherFrame);
+        }
+        break;
+    }
+}
+
 void Constraint::ApplyLimits()
 void Constraint::ApplyLimits()
 {
 {
     if (!constraint_)
     if (!constraint_)
@@ -368,7 +459,7 @@ void Constraint::ApplyLimits()
     case CONETWIST_CONSTRAINT_TYPE:
     case CONETWIST_CONSTRAINT_TYPE:
         {
         {
             btConeTwistConstraint* coneTwistConstraint = static_cast<btConeTwistConstraint*>(constraint_);
             btConeTwistConstraint* coneTwistConstraint = static_cast<btConeTwistConstraint*>(constraint_);
-            coneTwistConstraint->setLimit(highLimit_.y_ * M_DEGTORAD, highLimit_.z_ * M_DEGTORAD, highLimit_.x_ * M_DEGTORAD);
+            coneTwistConstraint->setLimit(highLimit_.y_ * M_DEGTORAD, highLimit_.y_ * M_DEGTORAD, highLimit_.x_ * M_DEGTORAD);
         }
         }
         break;
         break;
     }
     }

+ 24 - 10
Engine/Physics/Constraint.h

@@ -71,10 +71,18 @@ public:
     void SetPosition(const Vector3& position);
     void SetPosition(const Vector3& position);
     /// %Set constraint axis relative to own body.
     /// %Set constraint axis relative to own body.
     void SetAxis(const Vector3& axis);
     void SetAxis(const Vector3& axis);
+    /// %Set constraint position relative to other body. If constraint connects to static world, this is the static position in world space.
+    void SetOtherPosition(const Vector3& position);
+    /// %Set constraint axis relative to other body. If constraint connects to static world, this is the static axis in world space.
+    void SetOtherAxis(const Vector3& axis);
     /// %Set high limit. Interpretation is constraint type specific.
     /// %Set high limit. Interpretation is constraint type specific.
-    void SetHighLimit(const Vector3& limit);
+    void SetHighLimit(const Vector2& limit);
     /// %Set low limit. Interpretation is constraint type specific.
     /// %Set low limit. Interpretation is constraint type specific.
-    void SetLowLimit(const Vector3& limit);
+    void SetLowLimit(const Vector2& limit);
+    /// %Set whether to disable collisions between connected bodies.
+    void SetDisableCollision(bool disable);
+    /// Set all constraint parameters at once.
+    void SetParameters(ConstraintType type, RigidBody* otherBody, const Vector3& position, const Vector3& axis, const Vector3& otherPosition, const Vector3& otherAxis, const Vector2& highLimit, const Vector2& lowLimit, bool disableCollision);
     
     
     /// Return physics world.
     /// Return physics world.
     PhysicsWorld* GetPhysicsWorld() const { return physicsWorld_; }
     PhysicsWorld* GetPhysicsWorld() const { return physicsWorld_; }
@@ -90,10 +98,16 @@ public:
     const Vector3& GetPosition() const { return position_; }
     const Vector3& GetPosition() const { return position_; }
     /// Return constraint axis relative to own body.
     /// Return constraint axis relative to own body.
     const Vector3& GetAxis() const { return axis_; }
     const Vector3& GetAxis() const { return axis_; }
+    /// Return constraint position relative to other body.
+    const Vector3& GetOtherPosition() const { return otherPosition_; }
+    /// Return constraint axis relative to other body.
+    const Vector3& GetOtherAxis() const { return otherAxis_; }
     /// Return high limit.
     /// Return high limit.
-    const Vector3& GetHighLimit() const { return highLimit_; }
+    const Vector2& GetHighLimit() const { return highLimit_; }
     /// Return low limit.
     /// Return low limit.
-    const Vector3& GetLowLimit() const { return lowLimit_; }
+    const Vector2& GetLowLimit() const { return lowLimit_; }
+    /// Return whether collisions between connected bodies are disabled.
+    bool GetDisableCollision() const { return disableCollision_; }
     
     
     /// Release the constraint.
     /// Release the constraint.
     void ReleaseConstraint();
     void ReleaseConstraint();
@@ -107,6 +121,8 @@ protected:
 private:
 private:
     /// Create the constraint.
     /// Create the constraint.
     void CreateConstraint();
     void CreateConstraint();
+    /// Apply constraint frames.
+    void ApplyFrames();
     /// Apply high and low constraint limits.
     /// Apply high and low constraint limits.
     void ApplyLimits();
     void ApplyLimits();
     
     
@@ -125,21 +141,19 @@ private:
     /// Constraint axis.
     /// Constraint axis.
     Vector3 axis_;
     Vector3 axis_;
     /// Constraint other body position.
     /// Constraint other body position.
-    Vector3 otherBodyPosition_;
+    Vector3 otherPosition_;
     /// Constraint other body axis.
     /// Constraint other body axis.
-    Vector3 otherBodyAxis_;
+    Vector3 otherAxis_;
     /// Cached world scale for determining if the constraint position needs update.
     /// Cached world scale for determining if the constraint position needs update.
     Vector3 cachedWorldScale_;
     Vector3 cachedWorldScale_;
     /// High limit.
     /// High limit.
-    Vector3 highLimit_;
+    Vector2 highLimit_;
     /// Low limit.
     /// Low limit.
-    Vector3 lowLimit_;
+    Vector2 lowLimit_;
     /// Other body node ID for pending constraint recreation.
     /// Other body node ID for pending constraint recreation.
     int otherBodyNodeID_;
     int otherBodyNodeID_;
     /// Disable collision between connected bodies flag.
     /// Disable collision between connected bodies flag.
     bool disableCollision_;
     bool disableCollision_;
     /// Recreate constraint flag.
     /// Recreate constraint flag.
     bool recreateConstraint_;
     bool recreateConstraint_;
-    /// Other body position valid flag. Used to indicate that it should be used when recreating the joint.
-    bool otherBodyPositionValid_;
 };
 };

+ 20 - 20
Engine/Physics/PhysicsWorld.cpp

@@ -100,6 +100,7 @@ PhysicsWorld::PhysicsWorld(Context* context) :
     timeAcc_(0.0f),
     timeAcc_(0.0f),
     maxNetworkAngularVelocity_(DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY),
     maxNetworkAngularVelocity_(DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY),
     interpolation_(true),
     interpolation_(true),
+    applyingTransforms_(false),
     debugRenderer_(0),
     debugRenderer_(0),
     debugMode_(btIDebugDraw::DBG_DrawWireframe | btIDebugDraw::DBG_DrawConstraints)
     debugMode_(btIDebugDraw::DBG_DrawWireframe | btIDebugDraw::DBG_DrawConstraints)
 {
 {
@@ -189,6 +190,7 @@ void PhysicsWorld::Update(float timeStep)
     PROFILE(UpdatePhysics);
     PROFILE(UpdatePhysics);
     
     
     float internalTimeStep = 1.0f / fps_;
     float internalTimeStep = 1.0f / fps_;
+    delayedWorldTransforms_.Clear();
     
     
     if (interpolation_)
     if (interpolation_)
     {
     {
@@ -204,6 +206,24 @@ void PhysicsWorld::Update(float timeStep)
             timeAcc_ -= internalTimeStep;
             timeAcc_ -= internalTimeStep;
         }
         }
     }
     }
+    
+    // Apply delayed (parented) world transforms now
+    while (!delayedWorldTransforms_.Empty())
+    {
+        for (HashMap<RigidBody*, DelayedWorldTransform>::Iterator i = delayedWorldTransforms_.Begin();
+            i != delayedWorldTransforms_.End(); )
+        {
+            HashMap<RigidBody*, DelayedWorldTransform>::Iterator current = i++;
+            const DelayedWorldTransform& transform = current->second_;
+            
+            // If parent's transform has already been assigned, can proceed
+            if (!delayedWorldTransforms_.Contains(transform.parentRigidBody_))
+            {
+                transform.rigidBody_->ApplyWorldTransform(transform.worldPosition_, transform.worldRotation_);
+                delayedWorldTransforms_.Erase(current);
+            }
+        }
+    }
 }
 }
 
 
 void PhysicsWorld::UpdateCollisions()
 void PhysicsWorld::UpdateCollisions()
@@ -446,8 +466,6 @@ void PhysicsWorld::PreStep(float timeStep)
     eventData[P_TIMESTEP] = timeStep;
     eventData[P_TIMESTEP] = timeStep;
     SendEvent(E_PHYSICSPRESTEP, eventData);
     SendEvent(E_PHYSICSPRESTEP, eventData);
     
     
-    delayedWorldTransforms_.Clear();
-    
     // Start profiling block for the actual simulation step
     // Start profiling block for the actual simulation step
 #ifdef ENABLE_PROFILING
 #ifdef ENABLE_PROFILING
     Profiler* profiler = GetSubsystem<Profiler>();
     Profiler* profiler = GetSubsystem<Profiler>();
@@ -464,24 +482,6 @@ void PhysicsWorld::PostStep(float timeStep)
         profiler->EndBlock();
         profiler->EndBlock();
 #endif
 #endif
     
     
-    // Apply delayed (parented) world transforms now
-    while (!delayedWorldTransforms_.Empty())
-    {
-        for (HashMap<RigidBody*, DelayedWorldTransform>::Iterator i = delayedWorldTransforms_.Begin();
-            i != delayedWorldTransforms_.End(); )
-        {
-            HashMap<RigidBody*, DelayedWorldTransform>::Iterator current = i++;
-            const DelayedWorldTransform& transform = current->second_;
-            
-            // If parent's transform has already been assigned, can proceed
-            if (!delayedWorldTransforms_.Contains(transform.parentRigidBody_))
-            {
-                transform.rigidBody_->ApplyWorldTransform(transform.worldPosition_, transform.worldRotation_);
-                delayedWorldTransforms_.Erase(current);
-            }
-        }
-    }
-    
     SendCollisionEvents();
     SendCollisionEvents();
     
     
     // Send post-step event
     // Send post-step event

+ 11 - 10
Engine/Physics/PhysicsWorld.h

@@ -124,20 +124,15 @@ public:
     void SetInterpolation(bool enable);
     void SetInterpolation(bool enable);
     /// %Set maximum angular velocity for network replication.
     /// %Set maximum angular velocity for network replication.
     void SetMaxNetworkAngularVelocity(float velocity);
     void SetMaxNetworkAngularVelocity(float velocity);
-    /// %Set simulation step time accumulator.
-    void SetTimeAccumulator(float time);
     /// Perform a physics world raycast and return all hits.
     /// Perform a physics world raycast and return all hits.
-    void Raycast(PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask =
-        M_MAX_UNSIGNED);
+    void Raycast(PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask = M_MAX_UNSIGNED);
     /// Perform a physics world raycast and return the closest hit.
     /// Perform a physics world raycast and return the closest hit.
-    void RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, unsigned collisionMask =
-        M_MAX_UNSIGNED);
+    void RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, unsigned collisionMask = M_MAX_UNSIGNED);
     /// Perform a physics world swept sphere test and return the closest hit.
     /// Perform a physics world swept sphere test and return the closest hit.
-    void SphereCast(PhysicsRaycastResult& result, const Ray& ray, float radius, float maxDistance, unsigned collisionMask =
-        M_MAX_UNSIGNED);
-    /// Perform a sphere test into the physics world.
+    void SphereCast(PhysicsRaycastResult& result, const Ray& ray, float radius, float maxDistance, unsigned collisionMask = M_MAX_UNSIGNED);
+    /// Return rigid bodies by a sphere query.
     void GetRigidBodies(PODVector<RigidBody*>& result, const Sphere& sphere, unsigned collisionMask = M_MAX_UNSIGNED);
     void GetRigidBodies(PODVector<RigidBody*>& result, const Sphere& sphere, unsigned collisionMask = M_MAX_UNSIGNED);
-    /// Perform a bounding box test into the physics world.
+    /// Return rigid bodies by a box query.
     void GetRigidBodies(PODVector<RigidBody*>& result, const BoundingBox& box, unsigned collisionMask = M_MAX_UNSIGNED);
     void GetRigidBodies(PODVector<RigidBody*>& result, const BoundingBox& box, unsigned collisionMask = M_MAX_UNSIGNED);
     /// Return gravity.
     /// Return gravity.
     Vector3 GetGravity() const;
     Vector3 GetGravity() const;
@@ -175,6 +170,10 @@ public:
     void CleanupGeometryCache();
     void CleanupGeometryCache();
     /// Return the collision geometry cache.
     /// Return the collision geometry cache.
     Map<String, SharedPtr<CollisionGeometryData> >& GetGeometryCache() { return geometryCache_; }
     Map<String, SharedPtr<CollisionGeometryData> >& GetGeometryCache() { return geometryCache_; }
+    /// Set node dirtying to be disregarded.
+    void SetApplyingTransforms(bool enable) { applyingTransforms_ = enable; }
+    /// Return whether node dirtying should be disregarded.
+    bool IsApplyingTransforms() const { return applyingTransforms_; }
     
     
 protected:
 protected:
     /// Handle node being assigned.
     /// Handle node being assigned.
@@ -224,6 +223,8 @@ private:
     float maxNetworkAngularVelocity_;
     float maxNetworkAngularVelocity_;
     /// Interpolation flag.
     /// Interpolation flag.
     bool interpolation_;
     bool interpolation_;
+    /// Applying transforms flag.
+    bool applyingTransforms_;
     /// Debug renderer.
     /// Debug renderer.
     DebugRenderer* debugRenderer_;
     DebugRenderer* debugRenderer_;
     /// Debug draw flags.
     /// Debug draw flags.

+ 5 - 6
Engine/Physics/RigidBody.cpp

@@ -72,7 +72,6 @@ RigidBody::RigidBody(Context* context) :
     lastRotation_(Quaternion::IDENTITY),
     lastRotation_(Quaternion::IDENTITY),
     kinematic_(false),
     kinematic_(false),
     phantom_(false),
     phantom_(false),
-    inSetTransform_(false),
     hasSmoothedTransform_(false),
     hasSmoothedTransform_(false),
     readdBody_(false)
     readdBody_(false)
 {
 {
@@ -640,7 +639,7 @@ bool RigidBody::IsActive() const
 
 
 void RigidBody::ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation)
 void RigidBody::ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation)
 {
 {
-    inSetTransform_ = true;
+    physicsWorld_->SetApplyingTransforms(true);
     
     
     // Apply transform to the SmoothedTransform component instead of node transform if available
     // Apply transform to the SmoothedTransform component instead of node transform if available
     SmoothedTransform* transform = 0;
     SmoothedTransform* transform = 0;
@@ -662,7 +661,7 @@ void RigidBody::ApplyWorldTransform(const Vector3& newWorldPosition, const Quate
         lastRotation_ = node_->GetWorldRotation();
         lastRotation_ = node_->GetWorldRotation();
     }
     }
     
     
-    inSetTransform_ = false;
+    physicsWorld_->SetApplyingTransforms(false);
 }
 }
 
 
 void RigidBody::UpdateMass()
 void RigidBody::UpdateMass()
@@ -727,7 +726,7 @@ void RigidBody::OnMarkedDirty(Node* node)
     // If node transform changes, apply it back to the physics transform. However, do not do this when a SmoothedTransform
     // If node transform changes, apply it back to the physics transform. However, do not do this when a SmoothedTransform
     // is in use, because in that case the node transform will be constantly updated into smoothed, possibly non-physical
     // is in use, because in that case the node transform will be constantly updated into smoothed, possibly non-physical
     // states; rather follow the SmoothedTransform target transform directly
     // states; rather follow the SmoothedTransform target transform directly
-    if (!inSetTransform_ && !hasSmoothedTransform_)
+    if ((!physicsWorld_ || !physicsWorld_->IsApplyingTransforms()) && !hasSmoothedTransform_)
     {
     {
         // Physics operations are not safe from worker threads
         // Physics operations are not safe from worker threads
         Scene* scene = GetScene();
         Scene* scene = GetScene();
@@ -851,13 +850,13 @@ void RigidBody::AddBodyToWorld()
 void RigidBody::HandleTargetPosition(StringHash eventType, VariantMap& eventData)
 void RigidBody::HandleTargetPosition(StringHash eventType, VariantMap& eventData)
 {
 {
     // Copy the smoothing target position to the rigid body
     // Copy the smoothing target position to the rigid body
-    if (!inSetTransform_)
+    if (!physicsWorld_ || !physicsWorld_->IsApplyingTransforms())
         SetPosition(static_cast<SmoothedTransform*>(GetEventSender())->GetTargetWorldPosition());
         SetPosition(static_cast<SmoothedTransform*>(GetEventSender())->GetTargetWorldPosition());
 }
 }
 
 
 void RigidBody::HandleTargetRotation(StringHash eventType, VariantMap& eventData)
 void RigidBody::HandleTargetRotation(StringHash eventType, VariantMap& eventData)
 {
 {
     // Copy the smoothing target rotation to the rigid body
     // Copy the smoothing target rotation to the rigid body
-    if (!inSetTransform_)
+    if (!physicsWorld_ || !physicsWorld_->IsApplyingTransforms())
         SetRotation(static_cast<SmoothedTransform*>(GetEventSender())->GetTargetWorldRotation());
         SetRotation(static_cast<SmoothedTransform*>(GetEventSender())->GetTargetWorldRotation());
 }
 }

+ 0 - 2
Engine/Physics/RigidBody.h

@@ -240,8 +240,6 @@ private:
     bool kinematic_;
     bool kinematic_;
     /// Phantom flag.
     /// Phantom flag.
     bool phantom_;
     bool phantom_;
-    /// Whether is in Bullet's transform update. Node dirtying is ignored at this point to prevent endless recursion.
-    bool inSetTransform_;
     /// Smoothed transform mode.
     /// Smoothed transform mode.
     bool hasSmoothedTransform_;
     bool hasSmoothedTransform_;
     /// Readd body to world flag.
     /// Readd body to world flag.