瀏覽代碼

Initial Bullet constraint implementation.
Changed DrawDebugGeometry() to be a virtual function in Component.

Lasse Öörni 13 年之前
父節點
當前提交
ef421ef32d

+ 1 - 23
Bin/Data/Scripts/Editor/EditorView.as

@@ -334,29 +334,7 @@ void HandlePostRenderUpdate()
 
     // Visualize the currently selected components
     for (uint i = 0; i < selectedComponents.length; ++i)
-    {
-        Drawable@ drawable = cast<Drawable>(selectedComponents[i]);
-        if (drawable !is null)
-            drawable.DrawDebugGeometry(debug, false);
-        else
-        {
-            RigidBody@ body = cast<RigidBody>(selectedComponents[i]);
-            if (body !is null)
-                body.DrawDebugGeometry(debug, false);
-            else
-            {
-                CollisionShape@ shape = cast<CollisionShape>(selectedComponents[i]);
-                if (shape !is null)
-                    shape.DrawDebugGeometry(debug, false);
-                else
-                {
-                    Joint@ joint = cast<Joint>(selectedComponents[i]);
-                    if (joint !is null)
-                        joint.DrawDebugGeometry(debug, false);
-                }
-            }
-        }
-    }
+        selectedComponents[i].DrawDebugGeometry(debug, false);
 
     if (renderingDebug)
         renderer.DrawDebugGeometry(false);

+ 1 - 1
Docs/Reference.dox

@@ -823,7 +823,7 @@ The other physics components are:
 
 - RigidBody: a physics object instance. Its parameters include mass, linear/angular velocities, friction and restitution.
 - CollisionShape: defines physics collision geometry. The supported shapes are box, sphere, cylinder, capsule, cone, triangle mesh and convex hull.
-- Joint: connects two RigidBodies together, or one RigidBody to a static point in the world. Currently ball and hinge joints are supported.
+- Constraint: connects two RigidBodies together, or one RigidBody to a static point in the world. Currently point and hinge constraints are supported.
 
 Both a RigidBody and at least one CollisionShape component must exist in a scene node for it to behave physically (a collision shape by itself does nothing.) Several collision shapes may exist in the same node to create compound shapes. An offset position and rotation relative to the node's transform can be specified for each. Triangle mesh and convex hull geometries require specifying a Model resource and the LOD level to use.
 

+ 75 - 60
Docs/ScriptAPI.dox

@@ -1188,6 +1188,7 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
 
 Properties:<br>
 - ShortStringHash type (readonly)
@@ -1296,6 +1297,7 @@ Methods:<br>
 - void Remove()
 - void MarkNetworkUpdate() const
 - void Update(float, float)
+- void DrawDebugGeometry(DebugRenderer@, bool)
 
 Properties:<br>
 - ShortStringHash type (readonly)
@@ -1417,6 +1419,62 @@ Properties:<br>
 - PhysicsWorld@ physicsWorld (readonly)
 
 
+Bone
+
+Properties:<br>
+- Node@ node (readonly)
+- String name
+- Vector3 initialPosition
+- Quaternion initialRotation
+- Vector3 initialScale
+- bool animated
+- float radius
+- BoundingBox boundingBox
+
+
+Skeleton
+
+Methods:<br>
+- void Reset()
+- Bone@ GetBone(const String&) const
+
+Properties:<br>
+- Bone@ rootBone (readonly)
+- uint numBones (readonly)
+- Bone@[] bones (readonly)
+
+
+DebugRenderer
+
+Methods:<br>
+- bool Load(File@)
+- bool Save(File@)
+- bool LoadXML(const XMLElement&)
+- bool SaveXML(XMLElement&)
+- void ApplyAttributes()
+- bool SetAttribute(const String&, const Variant&)
+- Variant GetAttribute(const String&)
+- void Remove()
+- void MarkNetworkUpdate() const
+- void AddLine(const Vector3&, const Vector3&, const Color&, bool arg3 = true)
+- void AddNode(Node@, bool arg1 = true)
+- void AddBoundingBox(const BoundingBox&, const Color&, bool arg2 = true)
+- void AddFrustum(const Frustum&, const Color&, bool arg2 = true)
+- void AddPolyhedron(const Polyhedron&, const Color&, bool arg2 = true)
+- void AddSphere(const Sphere&, const Color&, bool arg2 = true)
+- void AddSkeleton(Skeleton@, const Color&, bool arg2 = true)
+- void DrawDebugGeometry(DebugRenderer@, bool)
+
+Properties:<br>
+- ShortStringHash type (readonly)
+- String& typeName (readonly)
+- uint numAttributes (readonly)
+- Variant[] attributes
+- AttributeInfo&[] attributeInfos (readonly)
+- uint id (readonly)
+- Node@ node (readonly)
+
+
 Camera
 
 Methods:<br>
@@ -1429,6 +1487,7 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
 - void SetOrthoSize(const Vector2&)
 - Frustum GetSplitFrustum(float, float) const
 - Ray GetScreenRay(float, float)
@@ -1465,31 +1524,6 @@ Properties:<br>
 - Vector3 upVector (readonly)
 
 
-Bone
-
-Properties:<br>
-- Node@ node (readonly)
-- String name
-- Vector3 initialPosition
-- Quaternion initialRotation
-- Vector3 initialScale
-- bool animated
-- float radius
-- BoundingBox boundingBox
-
-
-Skeleton
-
-Methods:<br>
-- void Reset()
-- Bone@ GetBone(const String&) const
-
-Properties:<br>
-- Bone@ rootBone (readonly)
-- uint numBones (readonly)
-- Bone@[] bones (readonly)
-
-
 Texture
 
 Methods:<br>
@@ -1731,36 +1765,6 @@ Properties:<br>
 - uint numTracks (readonly)
 
 
-DebugRenderer
-
-Methods:<br>
-- bool Load(File@)
-- bool Save(File@)
-- bool LoadXML(const XMLElement&)
-- bool SaveXML(XMLElement&)
-- void ApplyAttributes()
-- bool SetAttribute(const String&, const Variant&)
-- Variant GetAttribute(const String&)
-- void Remove()
-- void MarkNetworkUpdate() const
-- void AddLine(const Vector3&, const Vector3&, const Color&, bool arg3 = true)
-- void AddNode(Node@, bool arg1 = true)
-- void AddBoundingBox(const BoundingBox&, const Color&, bool arg2 = true)
-- void AddFrustum(const Frustum&, const Color&, bool arg2 = true)
-- void AddPolyhedron(const Polyhedron&, const Color&, bool arg2 = true)
-- void AddSphere(const Sphere&, const Color&, bool arg2 = true)
-- void AddSkeleton(Skeleton@, const Color&, bool arg2 = true)
-
-Properties:<br>
-- ShortStringHash type (readonly)
-- String& typeName (readonly)
-- uint numAttributes (readonly)
-- Variant[] attributes
-- AttributeInfo&[] attributeInfos (readonly)
-- uint id (readonly)
-- Node@ node (readonly)
-
-
 Drawable
 
 Methods:<br>
@@ -2109,6 +2113,7 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
 - bool Play(const String&, uint8, bool, float arg3 = 0.0f)
 - bool PlayExclusive(const String&, uint8, bool, float arg3 = 0.0f)
 - void Stop(const String&, float arg1 = 0.0f)
@@ -2216,6 +2221,7 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
 - void SetActive(bool, bool)
 
 Properties:<br>
@@ -2258,6 +2264,7 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
 - void Resize(const BoundingBox&, uint)
 - void DrawDebugGeometry(bool) const
 - void AddManualDrawable(Drawable@)
@@ -2407,6 +2414,7 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
 - void Play(Sound@)
 - void Play(Sound@, float)
 - void Play(Sound@, float, float)
@@ -2444,6 +2452,7 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
 - void Play(Sound@)
 - void Play(Sound@, float)
 - void Play(Sound@, float, float)
@@ -3869,6 +3878,7 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
 
 Properties:<br>
 - ShortStringHash type (readonly)
@@ -3950,6 +3960,7 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
 - void SetSphere(float, const Vector3& arg1 = Vector3 ( ), const Quaternion& arg2 = Quaternion ( ))
 - void SetBox(const Vector3&, const Vector3& arg1 = Vector3 ( ), const Quaternion& arg2 = Quaternion ( ))
 - void SetCylinder(float, float, const Vector3& arg2 = Vector3 ( ), const Quaternion& arg3 = Quaternion ( ))
@@ -3958,7 +3969,6 @@ Methods:<br>
 - void SetTriangleMesh(Model@, uint, const Vector3& arg2 = Vector3 ( 1 , 1 , 1 ), const Vector3& arg3 = Vector3 ( ), const Quaternion& arg4 = Quaternion ( ))
 - void SetConvexHull(Model@, uint, const Vector3& arg2 = Vector3 ( 1 , 1 , 1 ), const Vector3& arg3 = Vector3 ( ), const Quaternion& arg4 = Quaternion ( ))
 - void SetTransform(const Vector3&, const Quaternion&)
-- void DrawDebugGeometry(DebugRenderer@, bool)
 
 Properties:<br>
 - ShortStringHash type (readonly)
@@ -3989,6 +3999,7 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
 - void SetTransform(const Vector3&, const Quaternion&)
 - void SetCollisionLayerAndMask(uint, uint)
 - void ApplyForce(const Vector3&)
@@ -3999,7 +4010,6 @@ Methods:<br>
 - void ApplyTorqueImpulse(const Vector3&)
 - void ResetForces()
 - void Activate()
-- void DrawDebugGeometry(DebugRenderer@, bool)
 
 Properties:<br>
 - ShortStringHash type (readonly)
@@ -4033,7 +4043,7 @@ Properties:<br>
 - CollisionEventMode collisionEventMode
 
 
-Joint
+Constraint
 
 Methods:<br>
 - bool Load(File@)
@@ -4045,7 +4055,6 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
-- void Clear()
 - void DrawDebugGeometry(DebugRenderer@, bool)
 
 Properties:<br>
@@ -4056,11 +4065,15 @@ Properties:<br>
 - AttributeInfo&[] attributeInfos (readonly)
 - uint id (readonly)
 - Node@ node (readonly)
+- ConstraintType constraintType
 - Vector3& position
+- Vector3& otherPosition
 - Vector3& axis
+- Vector3& otherAxis
+- float lowLimit
+- float highLimit
 - RigidBody@ ownBody (readonly)
 - RigidBody@ otherBody
-- JointType jointType
 
 
 PhysicsRaycastResult
@@ -4084,6 +4097,7 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
 - void Update(float)
 - void UpdateCollisions()
 - PhysicsRaycastResult[]@ Raycast(const Ray&, float arg1 = M_INFINITY, uint arg2 = 0xffff)
@@ -4135,6 +4149,7 @@ Methods:<br>
 - Variant GetAttribute(const String&)
 - void Remove()
 - void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
 - bool CreateObject(ScriptFile@, const String&)
 - bool Execute(const String&, const Variant[]@)
 - bool Execute(const String&)

+ 3 - 2
Engine/Engine/APITemplates.h

@@ -368,7 +368,7 @@ template <class T> void RegisterSerializable(asIScriptEngine* engine, const char
 }
 
 /// Template function for registering a class derived from Component.
-template <class T> void RegisterComponent(asIScriptEngine* engine, const char* className, bool nodeRegistered = true)
+template <class T> void RegisterComponent(asIScriptEngine* engine, const char* className, bool nodeRegistered = true, bool debugRendererRegistered = true)
 {
     RegisterSerializable<T>(engine, className);
     RegisterSubclass<Component, T>(engine, "Component", className);
@@ -377,6 +377,8 @@ template <class T> void RegisterComponent(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "uint get_id()", asMETHODPR(T, GetID, () const, unsigned), asCALL_THISCALL);
     if (nodeRegistered)
         engine->RegisterObjectMethod(className, "Node@+ get_node() const", asMETHODPR(T, GetNode, () const, Node*), asCALL_THISCALL);
+    if (debugRendererRegistered)
+        engine->RegisterObjectMethod(className, "void DrawDebugGeometry(DebugRenderer@+, bool)", asMETHODPR(T, DrawDebugGeometry, (DebugRenderer*, bool), void), asCALL_THISCALL);
 }
 
 static Component* NodeCreateComponent(const String& typeName, CreateMode mode, Node* ptr)
@@ -620,7 +622,6 @@ template <class T> void RegisterDrawable(asIScriptEngine* engine, const char* cl
 {
     RegisterComponent<T>(engine, className);
     RegisterSubclass<Drawable, T>(engine, "Drawable", className);
-    engine->RegisterObjectMethod(className, "void DrawDebugGeometry(DebugRenderer@+, bool)", asMETHOD(T, DrawDebugGeometry), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_inView() const", asFUNCTION(DrawableIsInView), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "void set_visible(bool)", asMETHOD(T, SetVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_visible() const", asMETHOD(T, IsVisible), asCALL_THISCALL);

+ 11 - 4
Engine/Engine/GraphicsAPI.cpp

@@ -36,6 +36,7 @@
 #include "ParticleEmitter.h"
 #include "PostProcess.h"
 #include "Scene.h"
+#include "SmoothedTransform.h"
 #include "Technique.h"
 #include "Texture2D.h"
 #include "TextureCube.h"
@@ -879,7 +880,7 @@ static DebugRenderer* SceneGetDebugRenderer(Scene* ptr)
 
 static void RegisterDebugRenderer(asIScriptEngine* engine)
 {
-    RegisterComponent<DebugRenderer>(engine, "DebugRenderer");
+    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 AddNode(Node@+, 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);
@@ -889,6 +890,12 @@ static void RegisterDebugRenderer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("DebugRenderer", "void AddSkeleton(Skeleton@+, const Color&in, bool depthTest = true)", asMETHOD(DebugRenderer, AddSkeleton), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "DebugRenderer@+ get_debugRenderer() const", asFUNCTION(SceneGetDebugRenderer), asCALL_CDECL_OBJLAST);
     engine->RegisterGlobalFunction("DebugRenderer@+ get_debugRenderer()", asFUNCTION(GetDebugRenderer), asCALL_CDECL);
+    
+    // Component::DrawDebugGeometry() can be registered now
+    engine->RegisterObjectMethod("Component", "void DrawDebugGeometry(DebugRenderer@+, bool)", asMETHOD(Component, DrawDebugGeometry), asCALL_THISCALL);
+    engine->RegisterObjectMethod("SmoothedTransform", "void DrawDebugGeometry(DebugRenderer@+, bool)", asMETHOD(SmoothedTransform, DrawDebugGeometry), asCALL_THISCALL);
+    engine->RegisterObjectMethod("DebugRenderer", "void DrawDebugGeometry(DebugRenderer@+, bool)", asMETHOD(DebugRenderer, DrawDebugGeometry), asCALL_THISCALL);
+
 }
 
 static void ConstructRayQueryResult(RayQueryResult* ptr)
@@ -997,7 +1004,7 @@ static void RegisterOctree(asIScriptEngine* engine)
     
     RegisterComponent<Octree>(engine, "Octree");
     engine->RegisterObjectMethod("Octree", "void Resize(const BoundingBox&in, uint)", asMETHOD(Octree, Resize), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Octree", "void DrawDebugGeometry(bool) const", asMETHOD(Octree, DrawDebugGeometry), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Octree", "void DrawDebugGeometry(bool) const", asMETHODPR(Octree, DrawDebugGeometry, (bool), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Octree", "void AddManualDrawable(Drawable@+)", asMETHOD(Octree, AddManualDrawable), asCALL_THISCALL);
     engine->RegisterObjectMethod("Octree", "void RemoveManualDrawable(Drawable@+)", asMETHOD(Octree, RemoveManualDrawable), asCALL_THISCALL);
     engine->RegisterObjectMethod("Octree", "Array<RayQueryResult>@ Raycast(const Ray&in, RayQueryLevel level = RAY_TRIANGLE, float maxDistance = M_INFINITY, uint8 drawableFlags = DRAWABLE_ANY, uint viewMask = DEFAULT_VIEWMASK) const", asFUNCTION(OctreeRaycast), asCALL_CDECL_OBJLAST);
@@ -1014,14 +1021,14 @@ static void RegisterOctree(asIScriptEngine* engine)
 
 void RegisterGraphicsAPI(asIScriptEngine* engine)
 {
-    RegisterCamera(engine);
     RegisterSkeleton(engine);
+    RegisterDebugRenderer(engine);
+    RegisterCamera(engine);
     RegisterTextures(engine);
     RegisterMaterial(engine);
     RegisterPostProcess(engine);
     RegisterModel(engine);
     RegisterAnimation(engine);
-    RegisterDebugRenderer(engine);
     RegisterDrawable(engine);
     RegisterLight(engine);
     RegisterZone(engine);

+ 25 - 22
Engine/Engine/PhysicsAPI.cpp

@@ -24,7 +24,7 @@
 #include "Precompiled.h"
 #include "APITemplates.h"
 #include "CollisionShape.h"
-#include "Joint.h"
+#include "Constraint.h"
 #include "PhysicsWorld.h"
 #include "RigidBody.h"
 #include "Scene.h"
@@ -75,7 +75,6 @@ static void RegisterCollisionShape(asIScriptEngine* engine)
     engine->RegisterObjectMethod("CollisionShape", "void SetTriangleMesh(Model@+, uint, const Vector3&in scale = Vector3(1, 1, 1), const Vector3&in pos = Vector3(), const Quaternion&in rot = Quaternion())", asMETHOD(CollisionShape, SetTriangleMesh), asCALL_THISCALL);
     engine->RegisterObjectMethod("CollisionShape", "void SetConvexHull(Model@+, uint, const Vector3&in scale = Vector3(1, 1, 1), const Vector3&in pos = Vector3(), const Quaternion&in rot = Quaternion())", asMETHOD(CollisionShape, SetConvexHull), asCALL_THISCALL);
     engine->RegisterObjectMethod("CollisionShape", "void SetTransform(const Vector3&in, const Quaternion&in)", asMETHOD(CollisionShape, SetTransform), asCALL_THISCALL);
-    engine->RegisterObjectMethod("CollisionShape", "void DrawDebugGeometry(DebugRenderer@+, bool)", asMETHOD(CollisionShape, DrawDebugGeometry), asCALL_THISCALL);
     engine->RegisterObjectMethod("CollisionShape", "void set_shapeType(ShapeType)", asMETHOD(CollisionShape, SetShapeType), asCALL_THISCALL);
     engine->RegisterObjectMethod("CollisionShape", "ShapeType get_shapeType() const", asMETHOD(CollisionShape, GetShapeType), asCALL_THISCALL);
     engine->RegisterObjectMethod("CollisionShape", "void set_size(const Vector3&in)", asMETHOD(CollisionShape, SetSize), asCALL_THISCALL);
@@ -113,7 +112,6 @@ static void RegisterRigidBody(asIScriptEngine* engine)
     engine->RegisterObjectMethod("RigidBody", "void ApplyTorqueImpulse(const Vector3&in)", asMETHOD(RigidBody, ApplyTorqueImpulse), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void ResetForces()", asMETHOD(RigidBody, ResetForces), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void Activate()", asMETHOD(RigidBody, Activate), asCALL_THISCALL);
-    engine->RegisterObjectMethod("RigidBody", "void DrawDebugGeometry(DebugRenderer@+, bool)", asMETHOD(RigidBody, DrawDebugGeometry), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void set_mass(float)", asMETHOD(RigidBody, SetMass), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "float get_mass() const", asMETHOD(RigidBody, GetMass), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void set_position(Vector3)", asMETHOD(RigidBody, SetPosition), asCALL_THISCALL);
@@ -162,25 +160,30 @@ static void RegisterRigidBody(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Variant", "RigidBody@+ GetRigidBody() const", asFUNCTION(GetVariantPtr<RigidBody>), asCALL_CDECL_OBJLAST);
 }
 
-static void RegisterJoint(asIScriptEngine* engine)
+static void RegisterConstraint(asIScriptEngine* engine)
 {
-    engine->RegisterEnum("JointType");
-    engine->RegisterEnumValue("JointType", "JOINT_NONE", JOINT_NONE);
-    engine->RegisterEnumValue("JointType", "JOINT_BALL", JOINT_BALL);
-    engine->RegisterEnumValue("JointType", "JOINT_HINGE", JOINT_HINGE);
+    engine->RegisterEnum("ConstraintType");
+    engine->RegisterEnumValue("ConstraintType", "CONSTRAINT_POINT", CONSTRAINT_POINT);
+    engine->RegisterEnumValue("ConstraintType", "CONSTRAINT_HINGE", CONSTRAINT_HINGE);
     
-    RegisterComponent<Joint>(engine, "Joint");
-    engine->RegisterObjectMethod("Joint", "void Clear()", asMETHOD(Joint, Clear), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Joint", "void DrawDebugGeometry(DebugRenderer@+, bool)", asMETHOD(Joint, DrawDebugGeometry), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Joint", "void set_position(const Vector3&in)", asMETHOD(Joint, SetPosition), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Joint", "const Vector3& get_position() const", asMETHOD(Joint, GetPosition), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Joint", "void set_axis(const Vector3&in)", asMETHOD(Joint, SetAxis), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Joint", "const Vector3& get_axis() const", asMETHOD(Joint, GetAxis), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Joint", "RigidBody@+ get_ownBody() const", asMETHOD(Joint, GetOwnBody), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Joint", "void set_otherBody(RigidBody@+)", asMETHOD(Joint, SetOtherBody), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Joint", "RigidBody@+ get_otherBody() const", asMETHOD(Joint, GetOtherBody), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Joint", "bool set_jointType(JointType)", asMETHOD(Joint, SetJointType), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Joint", "JointType get_jointType() const", asMETHOD(Joint, GetJointType), asCALL_THISCALL);
+    RegisterComponent<Constraint>(engine, "Constraint");
+    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", "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", "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_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", "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", "float get_lowLimit() const", asMETHOD(Constraint, GetLowLimit), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "void set_lowLimit(float)", asMETHOD(Constraint, SetLowLimit), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "float get_highLimit() const", asMETHOD(Constraint, GetHighLimit), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Constraint", "void set_highLimit(float)", asMETHOD(Constraint, SetHighLimit), 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", "RigidBody@+ get_otherBody() const", asMETHOD(Constraint, GetOtherBody), asCALL_THISCALL);
 }
 
 static CScriptArray* PhysicsWorldRaycast(const Ray& ray, float maxDistance, unsigned collisionMask, PhysicsWorld* ptr)
@@ -221,7 +224,7 @@ static void RegisterPhysicsWorld(asIScriptEngine* engine)
     engine->RegisterObjectMethod("PhysicsWorld", "Array<PhysicsRaycastResult>@ Raycast(const Ray&in, float maxDistance = M_INFINITY, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldRaycast), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("PhysicsWorld", "PhysicsRaycastResult RaycastSingle(const Ray&in, float maxDistance = M_INFINITY, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldRaycastSingle), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("PhysicsWorld", "PhysicsRaycastResult SphereCast(const Ray&in, float, float maxDistance = M_INFINITY, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldSphereCast), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod("PhysicsWorld", "void DrawDebugGeometry(bool)", asMETHOD(PhysicsWorld, DrawDebugGeometry), asCALL_THISCALL);
+    engine->RegisterObjectMethod("PhysicsWorld", "void DrawDebugGeometry(bool)", asMETHODPR(PhysicsWorld, DrawDebugGeometry, (bool), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "void set_gravity(Vector3)", asMETHOD(PhysicsWorld, SetGravity), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "Vector3 get_gravity() const", asMETHOD(PhysicsWorld, GetGravity), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "void set_fps(int)", asMETHOD(PhysicsWorld, SetFps), asCALL_THISCALL);
@@ -239,6 +242,6 @@ void RegisterPhysicsAPI(asIScriptEngine* engine)
 {
     RegisterCollisionShape(engine);
     RegisterRigidBody(engine);
-    RegisterJoint(engine);
+    RegisterConstraint(engine);
     RegisterPhysicsWorld(engine);
 }

+ 2 - 2
Engine/Engine/SceneAPI.cpp

@@ -54,7 +54,7 @@ static void RegisterNode(asIScriptEngine* engine)
     engine->RegisterEnumValue("CreateMode", "LOCAL", LOCAL);
     
     // Register Component first. At this point Node is not yet registered, so can not register GetNode for Component
-    RegisterComponent<Component>(engine, "Component", false);
+    RegisterComponent<Component>(engine, "Component", false, false);
     RegisterNode<Node>(engine, "Node");
     engine->RegisterObjectMethod("Node", "bool SaveXML(File@+)", asFUNCTION(NodeSaveXML), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Node", "Node@+ Clone(CreateMode mode = REPLICATED)", asMETHOD(Node, Clone), asCALL_THISCALL);
@@ -135,7 +135,7 @@ static CScriptArray* GetAvailableComponents(Scene* ptr)
 
 static void RegisterSmoothedTransform(asIScriptEngine* engine)
 {
-    RegisterComponent<SmoothedTransform>(engine, "SmoothedTransform");
+    RegisterComponent<SmoothedTransform>(engine, "SmoothedTransform", true, false);
     engine->RegisterObjectMethod("SmoothedTransform", "void Update(float, float)", asMETHOD(SmoothedTransform, Update), asCALL_THISCALL);
     engine->RegisterObjectMethod("SmoothedTransform", "void set_targetPosition(const Vector3&in)", asMETHOD(SmoothedTransform, SetTargetPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("SmoothedTransform", "const Vector3& get_targetPosition() const", asMETHOD(SmoothedTransform, GetTargetPosition), asCALL_THISCALL);

+ 1 - 2
Engine/Graphics/AnimatedModel.h

@@ -29,7 +29,6 @@
 
 class Animation;
 class AnimationState;
-class DebugRenderer;
 
 /// Animated model component.
 class AnimatedModel : public StaticModel
@@ -62,7 +61,7 @@ public:
     virtual void UpdateGeometry(const FrameInfo& frame);
     /// Return whether a geometry update is necessary, and if it can happen in a worker thread.
     virtual UpdateGeometryType GetUpdateGeometryType();
-    /// Add debug geometry to the debug renderer.
+    /// Visualize the component as debug geometry.
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     
     /// %Set model.

+ 1 - 2
Engine/Graphics/Drawable.h

@@ -40,7 +40,6 @@ static const int DRAWABLES_PER_WORK_ITEM = 16;
 static const int MAX_VERTEX_LIGHTS = 6;
 
 class Camera;
-class DebugRenderer;
 class Geometry;
 class Light;
 class OcclusionBuffer;
@@ -135,7 +134,7 @@ public:
     virtual unsigned GetNumOccluderTriangles() { return 0; }
     /// Draw to occlusion buffer. Return true if did not run out of triangles.
     virtual bool DrawOcclusion(OcclusionBuffer* buffer) { return true; }
-    /// Add debug geometry to the debug renderer.
+    /// Visualize the component as debug geometry.
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     
     /// %Set draw distance.

+ 1 - 1
Engine/Graphics/Light.h

@@ -163,7 +163,7 @@ public:
     virtual void ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results);
     /// Calculate distance and prepare batches for rendering. May be called from worker thread(s), possibly re-entrantly.
     virtual void UpdateBatches(const FrameInfo& frame);
-    /// Add debug geometry to the debug renderer.
+    /// Visualize the component as debug geometry.
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     
     /// %Set light type.

+ 11 - 4
Engine/Graphics/Octree.cpp

@@ -353,6 +353,16 @@ void Octree::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
     Resize(worldBoundingBox_, numLevels_);
 }
 
+void Octree::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    if (debug)
+    {
+        PROFILE(OctreeDrawDebug);
+        
+        Octant::DrawDebugGeometry(debug, depthTest);
+    }
+}
+
 void Octree::Resize(const BoundingBox& box, unsigned numLevels)
 {
     PROFILE(ResizeOctree);
@@ -519,11 +529,8 @@ void Octree::QueueReinsertion(Drawable* drawable)
 
 void Octree::DrawDebugGeometry(bool depthTest)
 {
-    PROFILE(OctreeDrawDebug);
-    
     DebugRenderer* debug = GetComponent<DebugRenderer>();
-    if (debug)
-        Octant::DrawDebugGeometry(debug, depthTest);
+    DrawDebugGeometry(debug, depthTest);
 }
 
 void Octree::UpdateDrawables(const FrameInfo& frame)

+ 3 - 1
Engine/Graphics/Octree.h

@@ -166,6 +166,8 @@ public:
     
     /// Handle attribute change.
     virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& src);
+    /// Visualize the component as debug geometry.
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     
     /// Resize octree. If octree is not empty, drawable objects will be temporarily moved to the root.
     void Resize(const BoundingBox& box, unsigned numLevels);
@@ -189,7 +191,7 @@ public:
     void QueueUpdate(Drawable* drawable);
     /// Mark drawable object as requiring a reinsertion. Is thread-safe.
     void QueueReinsertion(Drawable* drawable);
-    /// Add debug geometry to the debug renderer.
+    /// Visualize the component as debug geometry.
     void DrawDebugGeometry(bool depthTest);
     
 private:

+ 0 - 1
Engine/Graphics/Renderer.h

@@ -30,7 +30,6 @@
 #include "Mutex.h"
 #include "Viewport.h"
 
-class DebugRenderer;
 class Geometry;
 class Drawable;
 class Light;

+ 1 - 1
Engine/Graphics/Zone.h

@@ -41,7 +41,7 @@ public:
     
     /// Handle attribute write access.
     virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& src);
-    /// Add debug geometry to the debug renderer.
+    /// Visualize the component as debug geometry.
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     
     /// %Set bounding box. Will be used as an oriented bounding box to test whether objects or the camera are inside.

+ 52 - 48
Engine/Physics/CollisionShape.cpp

@@ -209,7 +209,7 @@ CollisionShape::CollisionShape(Context* context) :
     lodLevel_(0),
     cachedWorldScale_(Vector3::ONE),
     margin_(DEFAULT_COLLISION_MARGIN),
-    dirty_(false)
+    recreateShape_(false)
 {
 }
 
@@ -237,16 +237,43 @@ void CollisionShape::RegisterObject(Context* context)
 void CollisionShape::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 {
     Component::OnSetAttribute(attr, src);
-    dirty_ = true;
+    
+    // Change of any attribute needs the collision shape to be recreated
+    recreateShape_ = true;
 }
 
 void CollisionShape::ApplyAttributes()
 {
-    if (dirty_)
+    if (recreateShape_)
     {
         UpdateShape();
         NotifyRigidBody();
-        dirty_ = false;
+        recreateShape_ = false;
+    }
+}
+
+void CollisionShape::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    if (debug && physicsWorld_ && shape_ && node_)
+    {
+        physicsWorld_->SetDebugRenderer(debug);
+        physicsWorld_->SetDebugDepthTest(depthTest);
+        
+        // Use the rigid body's world transform if possible, as it may be different from the rendering transform
+        Matrix3x4 worldTransform;
+        RigidBody* body = GetComponent<RigidBody>();
+        if (body)
+            worldTransform = Matrix3x4(body->GetPosition(), body->GetRotation(), node_->GetWorldScale());
+        else
+            worldTransform = node_->GetWorldTransform();
+        Vector3 worldPosition = worldTransform * position_;
+        Quaternion worldRotation = worldTransform.Rotation() * rotation_;
+        
+        btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
+        world->debugDrawObject(btTransform(ToBtQuaternion(worldRotation), ToBtVector3(worldPosition)), shape_, btVector3(0.0f,
+            1.0f, 0.0f));
+        
+        physicsWorld_->SetDebugRenderer(0);
     }
 }
 
@@ -468,36 +495,11 @@ void CollisionShape::NotifyRigidBody()
     }
 }
 
-void CollisionShape::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
-{
-    if (debug && physicsWorld_ && shape_ && node_)
-    {
-        physicsWorld_->SetDebugRenderer(debug);
-        physicsWorld_->SetDebugDepthTest(depthTest);
-        
-        // Use the rigid body's world transform if possible, as it may be different from the rendering transform
-        Matrix3x4 worldTransform;
-        RigidBody* body = GetComponent<RigidBody>();
-        if (body)
-            worldTransform = Matrix3x4(body->GetPosition(), body->GetRotation(), node_->GetWorldScale());
-        else
-            worldTransform = node_->GetWorldTransform();
-        Vector3 worldPosition = worldTransform * position_;
-        Quaternion worldRotation = worldTransform.Rotation() * rotation_;
-        
-        btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
-        world->debugDrawObject(btTransform(ToBtQuaternion(worldRotation), ToBtVector3(worldPosition)), shape_, btVector3(0.0f,
-            1.0f, 0.0f));
-        
-        physicsWorld_->SetDebugRenderer(0);
-    }
-}
-
 void CollisionShape::SetModelAttr(ResourceRef value)
 {
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     model_ = cache->GetResource<Model>(value.id_);
-    dirty_ = true;
+    recreateShape_ = true;
     MarkNetworkUpdate();
 }
 
@@ -506,6 +508,24 @@ ResourceRef CollisionShape::GetModelAttr() const
     return GetResourceRef(model_, Model::GetTypeStatic());
 }
 
+void CollisionShape::ReleaseShape()
+{
+    btCompoundShape* compound = GetParentCompoundShape();
+    if (shape_ && compound)
+    {
+        compound->removeChildShape(shape_);
+        rigidBody_->UpdateMass();
+    }
+    
+    delete shape_;
+    shape_ = 0;
+    
+    geometry_.Reset();
+    
+    if (physicsWorld_)
+        physicsWorld_->CleanupGeometryCache();
+}
+
 void CollisionShape::OnNodeSet(Node* node)
 {
     if (node)
@@ -516,6 +536,8 @@ void CollisionShape::OnNodeSet(Node* node)
             physicsWorld_ = scene->GetComponent<PhysicsWorld>();
             if (physicsWorld_)
                 physicsWorld_->AddCollisionShape(this);
+            else
+                LOGERROR("No physics world component in scene, can not create collision shape");
         }
         node->AddListener(this);
         
@@ -654,21 +676,3 @@ void CollisionShape::UpdateShape()
     if (physicsWorld_)
         physicsWorld_->CleanupGeometryCache();
 }
-
-void CollisionShape::ReleaseShape()
-{
-    btCompoundShape* compound = GetParentCompoundShape();
-    if (shape_ && compound)
-    {
-        compound->removeChildShape(shape_);
-        rigidBody_->UpdateMass();
-    }
-    
-    delete shape_;
-    shape_ = 0;
-    
-    geometry_.Reset();
-    
-    if (physicsWorld_)
-        physicsWorld_->CleanupGeometryCache();
-}

+ 6 - 7
Engine/Physics/CollisionShape.h

@@ -28,7 +28,6 @@
 #include "Component.h"
 #include "Quaternion.h"
 
-class DebugRenderer;
 class Geometry;
 class Model;
 class PhysicsWorld;
@@ -103,6 +102,8 @@ public:
     virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& src);
     /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
     virtual void ApplyAttributes();
+    /// Visualize the component as debug geometry.
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     
     /// %Set as a sphere.
     void SetSphere(float diameter, const Vector3& position = Vector3::ZERO, const Quaternion& rotation = Quaternion::IDENTITY);
@@ -156,12 +157,12 @@ public:
     
     /// Update the new collision shape to the RigidBody, and tell it to update its mass.
     void NotifyRigidBody();
-    /// Add debug geometry to the debug renderer.
-    void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     /// %Set model attribute.
     void SetModelAttr(ResourceRef value);
     /// Return model attribute.
     ResourceRef GetModelAttr() const;
+    /// Release the collision shape.
+    void ReleaseShape();
     
 protected:
     /// Handle node being assigned.
@@ -174,8 +175,6 @@ private:
     btCompoundShape* GetParentCompoundShape();
     /// Update the collision shape after attribute changes.
     void UpdateShape();
-    /// Release the collision shape.
-    void ReleaseShape();
     
     /// Physics world.
     WeakPtr<PhysicsWorld> physicsWorld_;
@@ -201,6 +200,6 @@ private:
     unsigned lodLevel_;
     /// Collision margin.
     float margin_;
-    /// Dirty flag.
-    bool dirty_;
+    /// Recrease collision shape flag.
+    bool recreateShape_;
 };

+ 288 - 0
Engine/Physics/Constraint.cpp

@@ -0,0 +1,288 @@
+//
+// Urho3D Engine
+// Copyright (c) 2008-2012 Lasse Öörni
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "Precompiled.h"
+#include "Context.h"
+#include "DebugRenderer.h"
+#include "Constraint.h"
+#include "Log.h"
+#include "PhysicsUtils.h"
+#include "PhysicsWorld.h"
+#include "Profiler.h"
+#include "RigidBody.h"
+#include "Scene.h"
+
+#include "BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h"
+#include "BulletDynamics/ConstraintSolver/btHingeConstraint.h"
+#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
+
+#include "DebugNew.h"
+
+static const String typeNames[] =
+{
+    "Point",
+    "Hinge",
+    ""
+};
+
+static const float DEFAULT_LOW_LIMIT = -180.0f;
+static const float DEFAULT_HIGH_LIMIT = 180.0f;
+
+OBJECTTYPESTATIC(Constraint);
+
+Constraint::Constraint(Context* context) :
+    Component(context),
+    constraint_(0),
+    type_(CONSTRAINT_POINT),
+    position_(Vector3::ZERO),
+    otherPosition_(Vector3::ZERO),
+    axis_(Vector3::ZERO),
+    otherAxis_(Vector3::ZERO),
+    lowLimit_(DEFAULT_LOW_LIMIT),
+    highLimit_(DEFAULT_HIGH_LIMIT),
+    otherBodyNodeID_(0),
+    disableCollision_(false),
+    recreateConstraint_(false)
+{
+}
+
+Constraint::~Constraint()
+{
+    ReleaseConstraint();
+    
+    if (physicsWorld_)
+        physicsWorld_->RemoveConstraint(this);
+}
+
+void Constraint::RegisterObject(Context* context)
+{
+    context->RegisterFactory<Constraint>();
+    
+    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, "Axis", axis_, Vector3::ZERO, AM_DEFAULT);
+    ATTRIBUTE(Constraint, VAR_INT, "Other Body NodeID", otherBodyNodeID_, 0, AM_DEFAULT | AM_NODEID);
+    ATTRIBUTE(Constraint, VAR_VECTOR3, "Other Body Position", otherPosition_, Vector3::ZERO, AM_DEFAULT);
+    ATTRIBUTE(Constraint, VAR_VECTOR3, "Other Body Axis", otherAxis_, Vector3::ZERO, AM_DEFAULT);
+    ATTRIBUTE(Constraint, VAR_FLOAT, "Low Limit", lowLimit_, DEFAULT_LOW_LIMIT, AM_DEFAULT);
+    ATTRIBUTE(Constraint, VAR_FLOAT, "High Limit", highLimit_, DEFAULT_HIGH_LIMIT, AM_DEFAULT);
+    ATTRIBUTE(Constraint, VAR_BOOL, "Disable Collision", disableCollision_, false, AM_DEFAULT);
+}
+
+void Constraint::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
+{
+    Component::OnSetAttribute(attr, src);
+    
+    // Change of any attribute needs recreation of the constraint
+    recreateConstraint_ = true;
+}
+
+void Constraint::ApplyAttributes()
+{
+    if (recreateConstraint_)
+    {
+        otherBody_.Reset();
+        
+        Scene* scene = GetScene();
+        if (scene && otherBodyNodeID_)
+        {
+            Node* otherNode = scene->GetNode(otherBodyNodeID_);
+            if (otherNode)
+                otherBody_ = otherNode->GetComponent<RigidBody>();
+        }
+        
+        CreateConstraint();
+        recreateConstraint_ = false;
+    }
+}
+
+void Constraint::GetDependencyNodes(PODVector<Node*>& dest)
+{
+    if (otherBody_ && otherBody_->GetNode())
+        dest.Push(otherBody_->GetNode());
+}
+
+void Constraint::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    if (debug && physicsWorld_ && constraint_)
+    {
+        physicsWorld_->SetDebugRenderer(debug);
+        physicsWorld_->SetDebugDepthTest(depthTest);
+        physicsWorld_->GetWorld()->debugDrawConstraint(constraint_);
+        physicsWorld_->SetDebugRenderer(0);
+    }
+}
+
+void Constraint::SetConstraintType(ConstraintType type)
+{
+    type_ = type;
+    CreateConstraint();
+}
+
+void Constraint::SetOtherBody(RigidBody* body)
+{
+    if (otherBody_ != body)
+    {
+        otherBody_ = body;
+        
+        // Update the connected body attribute
+        Node* otherNode = otherBody_ ? otherBody_->GetNode() : 0;
+        otherBodyNodeID_ = otherNode ? otherNode->GetID() : 0;
+        
+        CreateConstraint();
+    }
+}
+
+void Constraint::SetPosition(const Vector3& position)
+{
+    if (position != position_)
+    {
+        position_ = position;
+        CreateConstraint();
+    }
+}
+
+void Constraint::SetOtherPosition(const Vector3& position)
+{
+    if (position != otherPosition_)
+    {
+        otherPosition_ = position;
+        CreateConstraint();
+    }
+}
+
+void Constraint::SetAxis(const Vector3& axis)
+{
+    if (axis != axis_)
+    {
+        axis_ = axis;
+        if (type_ == CONSTRAINT_HINGE)
+            CreateConstraint();
+    }
+}
+
+void Constraint::SetOtherAxis(const Vector3& axis)
+{
+    if (axis != otherAxis_)
+    {
+        otherAxis_ = axis;
+        if (type_ == CONSTRAINT_HINGE)
+            CreateConstraint();
+    }
+}
+
+void Constraint::SetLowLimit(float limit)
+{
+    lowLimit_ = limit;
+    
+    if (constraint_ && constraint_->getConstraintType() == HINGE_CONSTRAINT_TYPE)
+    {
+        btHingeConstraint* hingeConstraint = static_cast<btHingeConstraint*>(constraint_);
+        hingeConstraint->setLimit(lowLimit_ * M_DEGTORAD, highLimit_ * M_DEGTORAD);
+    }
+}
+
+void Constraint::SetHighLimit(float limit)
+{
+    highLimit_ = limit;
+    
+    if (constraint_ && constraint_->getConstraintType() == HINGE_CONSTRAINT_TYPE)
+    {
+        btHingeConstraint* hingeConstraint = static_cast<btHingeConstraint*>(constraint_);
+        hingeConstraint->setLimit(lowLimit_ * M_DEGTORAD, highLimit_ * M_DEGTORAD);
+    }
+}
+
+void Constraint::ReleaseConstraint()
+{
+    if (constraint_)
+    {
+        if (physicsWorld_)
+            physicsWorld_->GetWorld()->removeConstraint(constraint_);
+        
+        delete constraint_;
+        constraint_ = 0;
+    }
+}
+
+void Constraint::OnNodeSet(Node* node)
+{
+    if (node)
+    {
+        Scene* scene = GetScene();
+        if (scene)
+        {
+            physicsWorld_ = scene->GetComponent<PhysicsWorld>();
+            if (physicsWorld_)
+                physicsWorld_->AddConstraint(this);
+            else
+                LOGERROR("No physics world component in scene, can not create constraint");
+        }
+        node->AddListener(this);
+    }
+}
+
+void Constraint::CreateConstraint()
+{
+    PROFILE(CreateConstraint);
+    
+    ReleaseConstraint();
+    
+    ownBody_ = GetComponent<RigidBody>();
+    btRigidBody* ownBody = ownBody_ ? ownBody_->GetBody() : 0;
+    btRigidBody* otherBody = otherBody_ ? otherBody_->GetBody() : 0;
+    
+    if (!physicsWorld_ || !ownBody)
+        return;
+    
+    switch (type_)
+    {
+    case CONSTRAINT_POINT:
+        if (otherBody)
+            constraint_ = new btPoint2PointConstraint(*ownBody, *otherBody, ToBtVector3(position_), ToBtVector3(otherPosition_));
+        else
+            constraint_ = new btPoint2PointConstraint(*ownBody, ToBtVector3(position_));
+        break;
+        
+    case CONSTRAINT_HINGE:
+        {
+            btHingeConstraint* hingeConstraint;
+            if (otherBody)
+            {
+                constraint_ = hingeConstraint = new btHingeConstraint(*ownBody, *otherBody, ToBtVector3(position_),
+                    ToBtVector3(otherPosition_), ToBtVector3(axis_.Normalized()), ToBtVector3(otherAxis_.Normalized()));
+            }
+            else
+            {
+                constraint_ = hingeConstraint = new btHingeConstraint(*ownBody, ToBtVector3(position_),
+                    ToBtVector3(axis_.Normalized()));
+            }
+            
+            hingeConstraint->setLimit(lowLimit_ * M_DEGTORAD, highLimit_ * M_DEGTORAD);
+        }
+        break;
+    }
+    
+    constraint_->setUserConstraintPtr(this);
+    physicsWorld_->GetWorld()->addConstraint(constraint_, disableCollision_);
+}

+ 141 - 0
Engine/Physics/Constraint.h

@@ -0,0 +1,141 @@
+//
+// Urho3D Engine
+// Copyright (c) 2008-2012 Lasse Öörni
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "Component.h"
+#include "Vector3.h"
+
+/// Supported constraint types.
+enum ConstraintType
+{
+    CONSTRAINT_POINT = 0,
+    CONSTRAINT_HINGE
+};
+
+class PhysicsWorld;
+class RigidBody;
+class btTypedConstraint;
+
+/// Physics constraint component. Connects two rigid bodies together, or one rigid body to a static point.
+class Constraint : public Component
+{
+    OBJECT(Constraint);
+    
+public:
+    /// Construct.
+    Constraint(Context* context);
+    /// Destruct.
+    ~Constraint();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+    
+    /// Handle attribute write access.
+    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& src);
+    /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
+    virtual void ApplyAttributes();
+    /// Return the depended on nodes to order network updates.
+    virtual void GetDependencyNodes(PODVector<Node*>& dest);
+    /// Visualize the component as debug geometry.
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+    
+    /// %Set constraint type and recreate the constraint.
+    void SetConstraintType(ConstraintType type);
+    /// %Set other body to connect to. Set to null to connect to the static world.
+    void SetOtherBody(RigidBody* body);
+    /// %Set position relative to own body.
+    void SetPosition(const Vector3& position);
+    /// %Set position relative to other body.
+    void SetOtherPosition(const Vector3& position);
+    /// %Set axis relative to own body.
+    void SetAxis(const Vector3& axis);
+    /// %Set axis relative to other body.
+    void SetOtherAxis(const Vector3& axis);
+    /// %Set low limit.
+    void SetLowLimit(float limit);
+    /// %Set high limit.
+    void SetHighLimit(float limit);
+    
+    /// Return physics world.
+    PhysicsWorld* GetPhysicsWorld() const { return physicsWorld_; }
+    /// Return Bullet constraint.
+    btTypedConstraint* GetConstraint() const { return constraint_; }
+    /// Return constraint type.
+    ConstraintType GetConstraintType() const { return type_; }
+    /// Return rigid body in own scene node.
+    RigidBody* GetOwnBody() const { return ownBody_; }
+    /// Return the other rigid body. May be null if connected to the static world.
+    RigidBody* GetOtherBody() const { return otherBody_; }
+    /// Return position relative to own body.
+    const Vector3& GetPosition() const { return position_; }
+    /// Return position relative to other body.
+    const Vector3& GetOtherPosition() const { return otherPosition_; }
+    /// Return axis relative to own body.
+    const Vector3& GetAxis() const { return axis_; }
+    /// Return axis relative to other body.
+    const Vector3& GetOtherAxis() const { return otherAxis_; }
+    /// Return low limit.
+    float GetLowLimit() const { return lowLimit_; }
+    /// Return high limit.
+    float GetHighLimit() const { return highLimit_; }
+    
+    /// Release the constraint.
+    void ReleaseConstraint();
+    
+protected:
+    /// Handle node being assigned.
+    virtual void OnNodeSet(Node* node);
+    
+private:
+    /// Create the constraint.
+    void CreateConstraint();
+    
+    /// Physics world.
+    WeakPtr<PhysicsWorld> physicsWorld_;
+    /// Own rigid body.
+    WeakPtr<RigidBody> ownBody_;
+    /// Other rigid body.
+    WeakPtr<RigidBody> otherBody_;
+    /// Bullet constraint.
+    btTypedConstraint* constraint_;
+    /// Constraint type.
+    ConstraintType type_;
+    /// Constraint position relative to own body.
+    Vector3 position_;
+    /// Constraint position relative to other body.
+    Vector3 otherPosition_;
+    /// Constraint axis relative to own body.
+    Vector3 axis_;
+    /// Constraint axis relative to other body.
+    Vector3 otherAxis_;
+    /// Low limit.
+    float lowLimit_;
+    /// High limit.
+    float highLimit_;
+    /// Other body node ID for pending constraint recreation.
+    int otherBodyNodeID_;
+    /// Disable collision between connected bodies flag.
+    bool disableCollision_;
+    /// Recreate constraint flag.
+    bool recreateConstraint_;
+};

+ 0 - 168
Engine/Physics/Joint.cpp

@@ -1,168 +0,0 @@
-//
-// Urho3D Engine
-// Copyright (c) 2008-2012 Lasse Öörni
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#include "Precompiled.h"
-#include "Context.h"
-#include "DebugRenderer.h"
-#include "Joint.h"
-#include "Log.h"
-#include "PhysicsWorld.h"
-#include "RigidBody.h"
-#include "Scene.h"
-
-#include "DebugNew.h"
-
-static const String typeNames[] =
-{
-    "None",
-    "Ball",
-    "Hinge",
-    ""
-};
-
-OBJECTTYPESTATIC(Joint);
-
-Joint::Joint(Context* context) :
-    Component(context),
-    type_(JOINT_NONE),
-    position_(Vector3::ZERO),
-    axis_(Vector3::ZERO),
-    otherBodyNodeID_(0),
-    recreateJoint_(false)
-{
-}
-
-Joint::~Joint()
-{
-    Clear();
-    
-    if (physicsWorld_)
-        physicsWorld_->RemoveJoint(this);
-}
-
-void Joint::RegisterObject(Context* context)
-{
-    context->RegisterFactory<Joint>();
-    
-    ENUM_ATTRIBUTE(Joint, "Joint Type", type_, typeNames, JOINT_NONE, AM_DEFAULT);
-    REF_ACCESSOR_ATTRIBUTE(Joint, VAR_VECTOR3, "Position", GetPosition, SetPosition, Vector3, Vector3::ZERO, AM_DEFAULT | AM_LATESTDATA);
-    REF_ACCESSOR_ATTRIBUTE(Joint, VAR_VECTOR3, "Axis", GetAxis, SetAxis, Vector3, Vector3::ZERO, AM_DEFAULT | AM_LATESTDATA);
-    ATTRIBUTE(Joint, VAR_INT, "Other Body NodeID", otherBodyNodeID_, 0, AM_DEFAULT | AM_NODEID);
-}
-
-void Joint::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
-{
-    Component::OnSetAttribute(attr, src);
-    
-    // Change of the joint type or connected body requires the joint to be recreated
-    if (attr.offset_ == offsetof(Joint, type_) || attr.offset_ == offsetof(Joint, otherBodyNodeID_))
-        recreateJoint_ = true;
-}
-
-void Joint::ApplyAttributes()
-{
-    if (recreateJoint_)
-    {
-        otherBody_.Reset();
-        
-        Scene* scene = GetScene();
-        if (scene && otherBodyNodeID_)
-        {
-            Node* otherNode = scene->GetNode(otherBodyNodeID_);
-            if (otherNode)
-                otherBody_ = otherNode->GetComponent<RigidBody>();
-        }
-        
-        SetJointType(type_);
-        recreateJoint_ = false;
-    }
-}
-
-void Joint::GetDependencyNodes(PODVector<Node*>& dest)
-{
-    if (otherBody_ && otherBody_->GetNode())
-        dest.Push(otherBody_->GetNode());
-}
-
-void Joint::Clear()
-{
-    type_ = JOINT_NONE;
-}
-
-bool Joint::SetJointType(JointType type)
-{
-    Clear();
-    return true;
-}
-
-void Joint::SetOtherBody(RigidBody* body)
-{
-    if (otherBody_ != body)
-    {
-        otherBody_ = body;
-        
-        // Update the connected body attribute
-        Node* otherNode = otherBody_ ? otherBody_->GetNode() : 0;
-        otherBodyNodeID_ = otherNode ? otherNode->GetID() : 0;
-    }
-}
-
-void Joint::SetPosition(const Vector3& position)
-{
-}
-
-void Joint::SetAxis(const Vector3& axis)
-{
-}
-
-const Vector3& Joint::GetPosition() const
-{
-    return position_;
-}
-
-const Vector3& Joint::GetAxis() const
-{
-    return axis_;
-}
-
-void Joint::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
-{
-}
-
-void Joint::OnNodeSet(Node* node)
-{
-    if (node)
-    {
-        Scene* scene = GetScene();
-        if (scene)
-        {
-            physicsWorld_ = scene->GetComponent<PhysicsWorld>();
-            if (physicsWorld_)
-                physicsWorld_->AddJoint(this);
-        }
-        node->AddListener(this);
-        
-        // Set default position at the node
-        position_ = node->GetWorldPosition();
-    }
-}

+ 0 - 108
Engine/Physics/Joint.h

@@ -1,108 +0,0 @@
-//
-// Urho3D Engine
-// Copyright (c) 2008-2012 Lasse Öörni
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#pragma once
-
-#include "Component.h"
-#include "Vector3.h"
-
-/// Supported joint types.
-enum JointType
-{
-    JOINT_NONE = 0,
-    JOINT_BALL,
-    JOINT_HINGE
-};
-
-class PhysicsWorld;
-class RigidBody;
-
-/// Physics joint component. Connects two rigid bodies together, or one rigid body to a static point.
-class Joint : public Component
-{
-    OBJECT(Joint);
-    
-public:
-    /// Construct.
-    Joint(Context* context);
-    /// Destruct.
-    ~Joint();
-    /// Register object factory.
-    static void RegisterObject(Context* context);
-    
-    /// Handle attribute write access.
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& src);
-    /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
-    virtual void ApplyAttributes();
-    /// Return the depended on nodes to order network updates.
-    virtual void GetDependencyNodes(PODVector<Node*>& dest);
-    
-    /// Remove the joint.
-    void Clear();
-    /// %Set joint type and recreate the joint. Return true if successful.
-    bool SetJointType(JointType type);
-    /// %Set other body to connect to.
-    void SetOtherBody(RigidBody* body);
-    /// %Set joint world-space position.
-    void SetPosition(const Vector3& position);
-    /// %Set joint world-space axis.
-    void SetAxis(const Vector3& axis);
-    
-    /// Return physics world.
-    PhysicsWorld* GetPhysicsWorld() const { return physicsWorld_; }
-    /// Return joint type.
-    JointType GetJointType() const { return type_; }
-    /// Return rigid body in own scene node.
-    RigidBody* GetOwnBody() const { return ownBody_; }
-    /// Return the other rigid body. May be null if connected to the static world.
-    RigidBody* GetOtherBody() const { return otherBody_; }
-    /// Return joint world-space position.
-    const Vector3& GetPosition() const;
-    /// Return joint world-space axis.
-    const Vector3& GetAxis() const;
-    
-    /// Add debug geometry to the debug renderer.
-    void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
-    
-protected:
-    /// Handle node being assigned.
-    virtual void OnNodeSet(Node* node);
-    
-private:
-    /// Physics world.
-    WeakPtr<PhysicsWorld> physicsWorld_;
-    /// Own rigid body.
-    WeakPtr<RigidBody> ownBody_;
-    /// Other rigid body.
-    WeakPtr<RigidBody> otherBody_;
-    /// Joint type.
-    JointType type_;
-    /// Joint world-space position.
-    mutable Vector3 position_;
-    /// Joint world-space axis.
-    mutable Vector3 axis_;
-    /// Other body node ID for pending joint recreation.
-    int otherBodyNodeID_;
-    /// Recreate joint flag.
-    bool recreateJoint_;
-};

+ 33 - 19
Engine/Physics/PhysicsWorld.cpp

@@ -23,9 +23,9 @@
 
 #include "Precompiled.h"
 #include "CollisionShape.h"
+#include "Constraint.h"
 #include "Context.h"
 #include "DebugRenderer.h"
-#include "Joint.h"
 #include "Log.h"
 #include "Mutex.h"
 #include "PhysicsEvents.h"
@@ -97,12 +97,15 @@ PhysicsWorld::~PhysicsWorld()
 {
     if (scene_)
     {
-        // Force all remaining joints, rigid bodies and collision shapes to release themselves
-        for (PODVector<Joint*>::Iterator i = joints_.Begin(); i != joints_.End(); ++i)
-            (*i)->Clear();
+        // Force all remaining constraints, rigid bodies and collision shapes to release themselves
+        for (PODVector<Constraint*>::Iterator i = constraints_.Begin(); i != constraints_.End(); ++i)
+            (*i)->ReleaseConstraint();
         
         for (PODVector<RigidBody*>::Iterator i = rigidBodies_.Begin(); i != rigidBodies_.End(); ++i)
             (*i)->ReleaseBody();
+        
+        for (PODVector<CollisionShape*>::Iterator i = collisionShapes_.Begin(); i != collisionShapes_.End(); ++i)
+            (*i)->ReleaseShape();
     }
     
     // Remove any cached geometries that still remain
@@ -140,6 +143,19 @@ void PhysicsWorld::drawLine(const btVector3& from, const btVector3& to, const bt
         debugRenderer_->AddLine(ToVector3(from), ToVector3(to), Color(color.x(), color.y(), color.z()), debugDepthTest_);
 }
 
+void PhysicsWorld::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    if (debug)
+    {
+        PROFILE(PhysicsDrawDebug);
+        
+        debugRenderer_ = debug;
+        debugDepthTest_ = depthTest;
+        world_->debugDrawWorld();
+        debugRenderer_ = 0;
+    }
+}
+
 void PhysicsWorld::reportErrorWarning(const char* warningString)
 {
     LOGWARNING("Physics: " + String(warningString));
@@ -300,16 +316,16 @@ void PhysicsWorld::RemoveCollisionShape(CollisionShape* shape)
         collisionShapes_.Erase(i);
 }
 
-void PhysicsWorld::AddJoint(Joint* joint)
+void PhysicsWorld::AddConstraint(Constraint* constraint)
 {
-    joints_.Push(joint);
+    constraints_.Push(constraint);
 }
 
-void PhysicsWorld::RemoveJoint(Joint* joint)
+void PhysicsWorld::RemoveConstraint(Constraint* constraint)
 {
-    PODVector<Joint*>::Iterator i = joints_.Find(joint);
-    if (i != joints_.End())
-        joints_.Erase(i);
+    PODVector<Constraint*>::Iterator i = constraints_.Find(constraint);
+    if (i != constraints_.End())
+        constraints_.Erase(i);
 }
 
 void PhysicsWorld::AddDelayedWorldTransform(const DelayedWorldTransform& transform)
@@ -319,12 +335,8 @@ void PhysicsWorld::AddDelayedWorldTransform(const DelayedWorldTransform& transfo
 
 void PhysicsWorld::DrawDebugGeometry(bool depthTest)
 {
-    PROFILE(PhysicsDrawDebug);
-    
-    debugDepthTest_ = depthTest;
-    debugRenderer_ = GetComponent<DebugRenderer>();
-    world_->debugDrawWorld();
-    debugRenderer_ = 0;
+    DebugRenderer* debug = GetComponent<DebugRenderer>();
+    DrawDebugGeometry(debug, depthTest);
 }
 
 void PhysicsWorld::SetDebugRenderer(DebugRenderer* debug)
@@ -389,7 +401,9 @@ void PhysicsWorld::PreStep(float timeStep)
 void PhysicsWorld::PostStep(float timeStep)
 {
 #ifdef ENABLE_PROFILING
-    GetSubsystem<Profiler>()->EndBlock();
+    Profiler* profiler = GetSubsystem<Profiler>();
+    if (profiler)
+        profiler->EndBlock();
 #endif
     
     // Apply delayed (parented) world transforms now
@@ -533,8 +547,8 @@ void PhysicsWorld::SendCollisionEvents()
 
 void RegisterPhysicsLibrary(Context* context)
 {
-    Joint::RegisterObject(context);
-    RigidBody::RegisterObject(context);
     CollisionShape::RegisterObject(context);
+    RigidBody::RegisterObject(context);
+    Constraint::RegisterObject(context);
     PhysicsWorld::RegisterObject(context);
 }

+ 9 - 8
Engine/Physics/PhysicsWorld.h

@@ -30,9 +30,8 @@
 #include <LinearMath/btIDebugDraw.h>
 
 class CollisionShape;
-class DebugRenderer;
 class Deserializer;
-class Joint;
+class Constraint;
 class Node;
 class Ray;
 class RigidBody;
@@ -110,6 +109,8 @@ public:
     virtual void setDebugMode(int debugMode) { debugMode_ = debugMode; }
     /// Return debug draw flags.
     virtual int getDebugMode() const { return debugMode_; }
+    /// Visualize the component as debug geometry.
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     
     /// Step the simulation forward.
     void Update(float timeStep);
@@ -151,10 +152,10 @@ public:
     void AddCollisionShape(CollisionShape* shape);
     /// Remove a collision shape. Called by CollisionShape.
     void RemoveCollisionShape(CollisionShape* shape);
-    /// Add a joint to keep track of. Called by Joint.
-    void AddJoint(Joint* joint);
-    /// Remove a joint. Called by Joint.
-    void RemoveJoint(Joint* joint);
+    /// Add a constraint to keep track of. Called by Constraint.
+    void AddConstraint(Constraint* joint);
+    /// Remove a constraint. Called by Constraint.
+    void RemoveConstraint(Constraint* joint);
     /// Add a delayed world transform assignment. Called by RigidBody.
     void AddDelayedWorldTransform(const DelayedWorldTransform& transform);
     /// Add debug geometry to the debug renderer.
@@ -201,8 +202,8 @@ private:
     PODVector<RigidBody*> rigidBodies_;
     /// Collision shapes in the world.
     PODVector<CollisionShape*> collisionShapes_;
-    /// Joints in the world.
-    PODVector<Joint*> joints_;
+    /// Constraints in the world.
+    PODVector<Constraint*> constraints_;
     /// Collision pairs on this frame.
     HashSet<Pair<RigidBody*, RigidBody*> > currentCollisions_;
     /// Collision pairs on the previous frame. Used to check if a collision is "new."

+ 35 - 32
Engine/Physics/RigidBody.cpp

@@ -167,6 +167,21 @@ void RigidBody::setWorldTransform(const btTransform &worldTrans)
     MarkNetworkUpdate();
 }
 
+void RigidBody::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    if (debug && physicsWorld_ && body_)
+    {
+        physicsWorld_->SetDebugRenderer(debug);
+        physicsWorld_->SetDebugDepthTest(depthTest);
+        
+        btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
+        world->debugDrawObject(body_->getWorldTransform(), compoundShape_, IsActive() ? btVector3(1.0f, 1.0f, 1.0f) : 
+            btVector3(0.0f, 1.0f, 0.0f));
+        
+        physicsWorld_->SetDebugRenderer(0);
+    }
+}
+
 void RigidBody::SetMass(float mass)
 {
     mass = Max(mass, 0.0f);
@@ -191,6 +206,7 @@ void RigidBody::SetPosition(Vector3 position)
         interpTrans.setOrigin(worldTrans.getOrigin());
         body_->setInterpolationWorldTransform(interpTrans);
         
+        Activate();
         MarkNetworkUpdate();
     }
 }
@@ -207,6 +223,7 @@ void RigidBody::SetRotation(Quaternion rotation)
         interpTrans.setRotation(worldTrans.getRotation());
         body_->setInterpolationWorldTransform(interpTrans);
         
+        Activate();
         MarkNetworkUpdate();
     }
 }
@@ -225,6 +242,7 @@ void RigidBody::SetTransform(const Vector3& position, const Quaternion& rotation
         interpTrans.setRotation(worldTrans.getRotation());
         body_->setInterpolationWorldTransform(interpTrans);
         
+        Activate();
         MarkNetworkUpdate();
     }
 }
@@ -484,7 +502,7 @@ void RigidBody::ResetForces()
 
 void RigidBody::Activate()
 {
-    if (body_ && !body_->isActive())
+    if (mass_ > 0.0f && body_ && !body_->isActive())
         body_->activate();
 }
 
@@ -654,21 +672,6 @@ void RigidBody::UpdateMass()
     }
 }
 
-void RigidBody::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
-{
-    if (debug && physicsWorld_ && body_)
-    {
-        physicsWorld_->SetDebugRenderer(debug);
-        physicsWorld_->SetDebugDepthTest(depthTest);
-        
-        btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
-        world->debugDrawObject(body_->getWorldTransform(), compoundShape_, IsActive() ? btVector3(1.0f, 1.0f, 1.0f) : 
-            btVector3(0.0f, 1.0f, 0.0f));
-        
-        physicsWorld_->SetDebugRenderer(0);
-    }
-}
-
 void RigidBody::SetNetAngularVelocityAttr(const PODVector<unsigned char>& value)
 {
     float maxVelocity = physicsWorld_ ? physicsWorld_->GetMaxNetworkAngularVelocity() : DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY;
@@ -684,6 +687,21 @@ const PODVector<unsigned char>& RigidBody::GetNetAngularVelocityAttr() const
     return attrBuffer_.GetBuffer();
 }
 
+void RigidBody::ReleaseBody()
+{
+    if (body_)
+    {
+        if (physicsWorld_)
+        {
+            btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
+            world->removeRigidBody(body_);
+        }
+        
+        delete body_;
+        body_ = 0;
+    }
+}
+
 void RigidBody::OnMarkedDirty(Node* node)
 {
     // If node transform changes, apply it back to the physics transform. However, do not do this when a SmoothedTransform
@@ -727,7 +745,7 @@ void RigidBody::OnNodeSet(Node* node)
             if (physicsWorld_)
                 physicsWorld_->AddRigidBody(this);
             else
-                LOGERROR("Null physics world, can not create rigid body");
+                LOGERROR("No physics world component in scene, can not create rigid body");
             
             AddBodyToWorld();
         }
@@ -803,21 +821,6 @@ void RigidBody::AddBodyToWorld()
     }
 }
 
-void RigidBody::ReleaseBody()
-{
-    if (body_)
-    {
-        if (physicsWorld_)
-        {
-            btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
-            world->removeRigidBody(body_);
-        }
-        
-        delete body_;
-        body_ = 0;
-    }
-}
-
 void RigidBody::HandleTargetPosition(StringHash eventType, VariantMap& eventData)
 {
     // Copy the smoothing target position to the rigid body

+ 4 - 7
Engine/Physics/RigidBody.h

@@ -29,7 +29,6 @@
 #include <LinearMath/btMotionState.h>
 
 class CollisionShape;
-class DebugRenderer;
 class PhysicsWorld;
 class SmoothedTransform;
 
@@ -49,8 +48,6 @@ class RigidBody : public Component, public btMotionState
 {
     OBJECT(RigidBody);
     
-    friend class PhysicsWorld;
-    
 public:
     /// Construct.
     RigidBody(Context* context);
@@ -67,6 +64,8 @@ public:
     virtual void getWorldTransform(btTransform &worldTrans) const;
     /// Update world transform from Bullet.
     virtual void setWorldTransform(const btTransform &worldTrans);
+    /// Visualize the component as debug geometry.
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     
     /// %Set mass. Zero mass makes the body static.
     void SetMass(float mass);
@@ -190,12 +189,12 @@ public:
     void ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation);
     /// Update mass and inertia of rigid body.
     void UpdateMass();
-    /// Add debug geometry to the debug renderer.
-    void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     /// %Set network angular velocity attribute.
     void SetNetAngularVelocityAttr(const PODVector<unsigned char>& value);
     /// Return network angular velocity attribute.
     const PODVector<unsigned char>& GetNetAngularVelocityAttr() const;
+    /// Remove the rigid body.
+    void ReleaseBody();
     
 protected:
     /// Handle node being assigned.
@@ -206,8 +205,6 @@ protected:
 private:
     /// Create the rigid body, or re-add to the physics world with changed flags. Calls UpdateMass().
     void AddBodyToWorld();
-    /// Remove the rigid body.
-    void ReleaseBody();
     /// Handle SmoothedTransform target position update.
     void HandleTargetPosition(StringHash eventType, VariantMap& eventData);
     /// Handle SmoothedTransform target rotation update.

+ 3 - 0
Engine/Scene/Component.h

@@ -26,6 +26,7 @@
 #include "Matrix3x4.h"
 #include "Serializable.h"
 
+class DebugRenderer;
 class Node;
 class Scene;
 
@@ -53,6 +54,8 @@ public:
     virtual bool SaveXML(XMLElement& dest);
     /// Return the depended on nodes to order network updates.
     virtual void GetDependencyNodes(PODVector<Node*>& dest) {};
+    /// Visualize the component as debug geometry.
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest) {};
     
     /// Remove from the scene node. If no other shared pointer references exist, causes immediate deletion.
     void Remove();

+ 1 - 1
Engine/Scene/Serializable.cpp

@@ -61,7 +61,7 @@ void Serializable::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
     switch (attr.type_)
     {
     case VAR_INT:
-        // If enum type, use the low 8 bits only (assume full value to be initialized)
+        // If enum type, use the low 8 bits only
         if (attr.enumNames_)
             *(reinterpret_cast<unsigned char*>(dest)) = src.GetInt();
         else