浏览代码

Added box collision shape.

Lasse Öörni 13 年之前
父节点
当前提交
ee3095e603

+ 3 - 0
Bin/Data/Scripts/Editor/EditorImport.as

@@ -250,6 +250,8 @@ void ImportTundraScene(const String&in fileName)
             // Create collision shape
             if (shapeType >= 0)
             {
+                /// \todo Reimplement
+                /*
                 // If mesh has scaling, undo it for the collision shape
                 bodySize.x /= meshScale.x;
                 bodySize.y /= meshScale.y;
@@ -295,6 +297,7 @@ void ImportTundraScene(const String&in fileName)
                     //    mass = 0.0;
                     body.mass = mass;
                 }
+                */
             }
 
             // Store pending parent assignment if necessary

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

@@ -405,13 +405,13 @@ void ViewRaycast(bool mouseClick)
         Array<PhysicsRaycastResult> result = editorScene.physicsWorld.Raycast(cameraRay, camera.farClip);
         if (!result.empty)
         {
-            CollisionShape@ shape = result[0].collisionShape;
-            if (debug !is null)
+            RigidBody@ body = result[0].body;
+            if (body !is null)
             {
-                debug.AddNode(shape.node, false);
-                shape.DrawDebugGeometry(debug, false);
+                debug.AddNode(body.node, false);
+                body.DrawDebugGeometry(debug, false);
             }
-            selected = shape;
+            selected = body;
         }
     }
 

+ 14 - 18
Bin/Data/Scripts/TestScene.as

@@ -99,7 +99,7 @@ void InitScene()
     testScene.CreateComponent("DebugRenderer");
 
     world.gravity = Vector3(0.0, -9.81, 0.0);
-    world.fps = 60;
+    world.fps = 100;
 
     Node@ zoneNode = testScene.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -131,10 +131,9 @@ void InitScene()
         object.material = cache.GetResource("Material", "Materials/Test.xml");
         object.occluder = true;
 
-        /*
-        CollisionShape@ shape = objectNode.CreateComponent("CollisionShape");
-        shape.SetBox(Vector3(2.0, 2.0, 2.0));
-        */
+        RigidBody@ body = objectNode.CreateComponent("RigidBody");
+        BoxShape@ shape = objectNode.CreateComponent("BoxShape");
+        shape.size = Vector3(2.0, 2.0, 2.0);
     }
 
     for (uint i = 0; i < 50; ++i)
@@ -147,10 +146,9 @@ void InitScene()
         object.material = cache.GetResource("Material", "Materials/Test.xml");
         object.castShadows = true;
 
-        /*
-        CollisionShape@ shape = objectNode.CreateComponent("CollisionShape");
-        shape.SetBox(Vector3(2.0, 2.0, 2.0));
-        */
+        RigidBody@ body = objectNode.CreateComponent("RigidBody");
+        BoxShape@ shape = objectNode.CreateComponent("BoxShape");
+        shape.size = Vector3(2.0, 2.0, 2.0);
     }
 
     for (uint i = 0; i < 10; ++i)
@@ -165,10 +163,9 @@ void InitScene()
         object.castShadows = true;
         object.occluder = true;
 
-        /*
-        CollisionShape@ shape = objectNode.CreateComponent("CollisionShape");
-        shape.SetBox(Vector3(2.0, 2.0, 2.0));
-        */
+        RigidBody@ body = objectNode.CreateComponent("RigidBody");
+        BoxShape@ shape = objectNode.CreateComponent("BoxShape");
+        shape.size = Vector3(2.0, 2.0, 2.0);
     }
 
     for (uint i = 0; i < 50; ++i)
@@ -412,14 +409,13 @@ void HandleSpawnBox(StringHash eventType, VariantMap& eventData)
     newNode.rotation = rotation;
     newNode.SetScale(0.1);
 
-    /*
-    CollisionShape@ shape = newNode.CreateComponent("CollisionShape");
-    shape.SetBox(Vector3(2, 2, 2));
-    */
-
     RigidBody@ body = newNode.CreateComponent("RigidBody");
     body.mass = 1;
     body.linearVelocity = rotation * Vector3(0.0, 1.0, 10.0);
+    body.friction = 0.75;
+
+    BoxShape@ shape = newNode.CreateComponent("BoxShape");
+    shape.size = Vector3(2.0, 2.0, 2.0);
 
     StaticModel@ object = newNode.CreateComponent("StaticModel");
     object.model = cache.GetResource("Model", "Models/Box.mdl");

+ 8 - 7
Engine/Engine/APITemplates.h

@@ -691,16 +691,17 @@ template <class T> void RegisterTexture(asIScriptEngine* engine, const char* cla
     engine->RegisterObjectMethod(className, "bool get_dataLost() const", asMETHOD(T, IsDataLost), asCALL_THISCALL);
 }
 
-/// Template function for registering a class derived from CollisionShape
+/// Template function for registering a class derived from CollisionShape.
 template <class T> void RegisterCollisionShape(asIScriptEngine* engine, const char* className)
 {
-    RegisterResource<T>(engine, className);
-    RegisterSubclass<Texture, T>(engine, "CollisionShape", className);
+    RegisterComponent<T>(engine, className);
+    RegisterSubclass<CollisionShape, T>(engine, "CollisionShape", className);
     engine->RegisterObjectMethod(className, "void SetTransform(const Vector3&in, const Quaternion&in)", asMETHOD(T, SetTransform), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_position(const Vector3&in)", asMETHOD(CollisionShape, SetPosition), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "const Vector3& get_position() const", asMETHOD(CollisionShape, GetPosition), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_rotation(const Quaternion&in)", asMETHOD(CollisionShape, SetRotation), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "const Quaternion& get_rotation() const", asMETHOD(CollisionShape, GetRotation), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void DrawDebugGeometry(DebugRenderer@+, bool)", asMETHOD(T, DrawDebugGeometry), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void set_position(const Vector3&in)", asMETHOD(T, SetPosition), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "const Vector3& get_position() const", asMETHOD(T, GetPosition), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void set_rotation(const Quaternion&in)", asMETHOD(T, SetRotation), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "const Quaternion& get_rotation() const", asMETHOD(T, GetRotation), asCALL_THISCALL);
 }
 
 static CScriptArray* UIElementGetChildren(bool recursive, UIElement* ptr)

+ 11 - 6
Engine/Engine/PhysicsAPI.cpp

@@ -23,7 +23,7 @@
 
 #include "Precompiled.h"
 #include "APITemplates.h"
-#include "CollisionShape.h"
+#include "BoxShape.h"
 #include "Joint.h"
 #include "PhysicsWorld.h"
 #include "RigidBody.h"
@@ -60,12 +60,16 @@ static CScriptArray* PhysicsWorldRaycast(const Ray& ray, float maxDistance, unsi
     return VectorToArray<PhysicsRaycastResult>(result, "Array<PhysicsRaycastResult>");
 }
 
-static void RegisterCollisionShape(asIScriptEngine* engine)
+static void RegisterCollisionShapes(asIScriptEngine* engine)
 {
-    // RegisterCollisionShape<BoxShape>(engine, "BoxShape");
+    RegisterCollisionShape<CollisionShape>(engine, "CollisionShape");
+    
+    RegisterCollisionShape<BoxShape>(engine, "BoxShape");
+    engine->RegisterObjectMethod("BoxShape", "void set_size(const Vector3&in)", asMETHOD(BoxShape, SetSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("BoxShape", "const Vector3& get_size() const", asMETHOD(BoxShape, GetSize), asCALL_THISCALL);
     
     // Register Variant GetPtr() for CollisionShape
-    //engine->RegisterObjectMethod("Variant", "CollisionShape@+ GetCollisionShape() const", asFUNCTION(GetVariantPtr<CollisionShape>), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("Variant", "CollisionShape@+ GetCollisionShape() const", asFUNCTION(GetVariantPtr<CollisionShape>), asCALL_CDECL_OBJLAST);
 }
 
 static void RegisterRigidBody(asIScriptEngine* engine)
@@ -81,6 +85,7 @@ 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);
@@ -105,7 +110,7 @@ static void RegisterRigidBody(asIScriptEngine* engine)
     engine->RegisterObjectMethod("RigidBody", "float get_angularDamping() const", asMETHOD(RigidBody, GetAngularDamping), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void set_friction(float)", asMETHOD(RigidBody, SetFriction), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "float get_friction() const", asMETHOD(RigidBody, GetFriction), asCALL_THISCALL);
-   engine->RegisterObjectMethod("RigidBody", "void set_restitution(float)", asMETHOD(RigidBody, SetRestitution), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RigidBody", "void set_restitution(float)", asMETHOD(RigidBody, SetRestitution), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "float get_restitution() const", asMETHOD(RigidBody, GetRestitution), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void set_useGravity(bool)", asMETHOD(RigidBody, SetUseGravity), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "bool get_useGravity() const", asMETHOD(RigidBody, GetUseGravity), asCALL_THISCALL);
@@ -172,7 +177,7 @@ static void RegisterPhysicsWorld(asIScriptEngine* engine)
 
 void RegisterPhysicsAPI(asIScriptEngine* engine)
 {
-    RegisterCollisionShape(engine);
+    RegisterCollisionShapes(engine);
     RegisterRigidBody(engine);
     RegisterJoint(engine);
     RegisterPhysicsWorld(engine);

+ 83 - 0
Engine/Physics/BoxShape.cpp

@@ -0,0 +1,83 @@
+//
+// 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 "BoxShape.h"
+#include "Context.h"
+#include "Node.h"
+#include "PhysicsUtils.h"
+
+#include <BulletCollision/CollisionShapes/btBoxShape.h>
+
+OBJECTTYPESTATIC(BoxShape);
+
+BoxShape::BoxShape(Context* context) :
+    CollisionShape(context),
+    collisionShape_(0),
+    size_(Vector3::ONE)
+{
+}
+
+BoxShape::~BoxShape()
+{
+    delete collisionShape_;
+    collisionShape_ = 0;
+    
+    NotifyRigidBody();
+}
+
+void BoxShape::RegisterObject(Context* context)
+{
+    context->RegisterFactory<BoxShape>();
+    
+    ATTRIBUTE(BoxShape, VAR_VECTOR3, "Offset Position", position_, Vector3::ZERO, AM_DEFAULT);
+    ATTRIBUTE(BoxShape, VAR_QUATERNION, "Offset Rotation", rotation_, Quaternion::IDENTITY, AM_DEFAULT);
+    ATTRIBUTE(BoxShape, VAR_VECTOR3, "Size", size_, Vector3::ONE, AM_DEFAULT);
+}
+
+btCollisionShape* BoxShape::GetCollisionShape() const
+{
+    return collisionShape_;
+}
+
+void BoxShape::SetSize(const Vector3& size)
+{
+    if (size != size_)
+    {
+        size_ = size;
+        UpdateCollisionShape();
+        NotifyRigidBody();
+    }
+}
+
+void BoxShape::UpdateCollisionShape()
+{
+    if (node_)
+    {
+        delete collisionShape_;
+        collisionShape_ = 0;
+        
+        Vector3 worldSize = node_->GetWorldScale() * size_;
+        collisionShape_ = new btBoxShape(ToBtVector3(worldSize * 0.5f));
+    }
+}

+ 60 - 0
Engine/Physics/BoxShape.h

@@ -0,0 +1,60 @@
+//
+// 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 "CollisionShape.h"
+
+class btBoxShape;
+
+/// Box collision shape component.
+class BoxShape : public CollisionShape
+{
+    OBJECT(BoxShape);
+    
+public:
+    /// Construct.
+    BoxShape(Context* context);
+    /// Destruct.
+    ~BoxShape();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+    
+    /// Return Bullet collision shape.
+    virtual btCollisionShape* GetCollisionShape() const;
+    
+    /// %Set box size.
+    void SetSize(const Vector3& size);
+    /// Return box size.
+    const Vector3& GetSize() { return size_; }
+    
+protected:
+    /// Update the collision shape.
+    virtual void UpdateCollisionShape();
+    
+private:
+    /// Collision shape.
+    btBoxShape* collisionShape_;
+    /// Box size.
+    Vector3 size_;
+};

+ 29 - 4
Engine/Physics/CollisionShape.cpp

@@ -250,11 +250,14 @@ HeightfieldData::~HeightfieldData()
 {
 }
 
+OBJECTTYPESTATIC(CollisionShape);
+
 CollisionShape::CollisionShape(Context* context) :
     Component(context),
     position_(Vector3::ZERO),
     rotation_(Quaternion::IDENTITY),
-    dirty_(true)
+    cachedWorldScale_(Vector3::ONE),
+    dirty_(false)
 {
 }
 
@@ -273,7 +276,10 @@ void CollisionShape::OnSetAttribute(const AttributeInfo& attr, const Variant& sr
 void CollisionShape::ApplyAttributes()
 {
     if (dirty_)
+    {
+        UpdateCollisionShape();
         NotifyRigidBody();
+    }
 }
 
 void CollisionShape::SetPosition(const Vector3& position)
@@ -297,6 +303,7 @@ void CollisionShape::SetTransform(const Vector3& position, const Quaternion& rot
 
 void CollisionShape::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 {
+    /// \todo Implement
 }
 
 void CollisionShape::OnNodeSet(Node* node)
@@ -311,13 +318,31 @@ void CollisionShape::OnNodeSet(Node* node)
                 physicsWorld_->AddCollisionShape(this);
         }
         node->AddListener(this);
+        UpdateCollisionShape();
+        NotifyRigidBody();
+    }
+}
+
+void CollisionShape::OnMarkedDirty(Node* node)
+{
+    Vector3 newWorldScale = node_->GetWorldScale();
+    if (newWorldScale != cachedWorldScale_)
+    {
+        UpdateCollisionShape();
+        NotifyRigidBody();
+        
+        cachedWorldScale_ = newWorldScale;
     }
 }
 
 void CollisionShape::NotifyRigidBody()
 {
-    RigidBody* rigidBody = GetComponent<RigidBody>();
-    if (rigidBody)
-        rigidBody->UpdateCollisionShape(this);
+    // We need to notify the rigid body also after having been removed from the node, so maintain a weak pointer to it.
+    if (!rigidBody_)
+        rigidBody_ = GetComponent<RigidBody>();
+    
+    if (rigidBody_)
+        rigidBody_->RefreshCollisionShapes();
+    
     dirty_ = false;
 }

+ 13 - 4
Engine/Physics/CollisionShape.h

@@ -32,6 +32,7 @@ class DebugRenderer;
 class Geometry;
 class Model;
 class PhysicsWorld;
+class RigidBody;
 
 class btCollisionShape;
 
@@ -85,8 +86,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();
-    /// Create and return a collision shape instance, subject to scene node scaling. Called by RigidBody.
-    virtual btCollisionShape* CreateCollisionShape() = 0;
+    /// Return Bullet collision shape.
+    virtual btCollisionShape* GetCollisionShape() const = 0;
     
     /// %Set offset position.
     void SetPosition(const Vector3& position);
@@ -108,15 +109,23 @@ public:
 protected:
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
-    /// Notify the RigidBody of changed collision shape transform.
+    /// Handle node transform being dirtied.
+    virtual void OnMarkedDirty(Node* node);
+    /// Update the collision shape after attribute changes.
+    virtual void UpdateCollisionShape() = 0;
+    /// Notify the RigidBody of changed or removed collision shape.
     void NotifyRigidBody();
     
     /// Physics world.
     WeakPtr<PhysicsWorld> physicsWorld_;
+    /// Rigid body.
+    WeakPtr<RigidBody> rigidBody_;
     /// Offset position.
     Vector3 position_;
     /// Offset rotation.
     Quaternion rotation_;
-    /// Collision shape dirty flag.
+    /// Cached world scale for determining if the collision shape needs update.
+    Vector3 cachedWorldScale_;
+    /// Dirty flag.
     bool dirty_;
 };

+ 11 - 3
Engine/Physics/PhysicsWorld.cpp

@@ -22,7 +22,7 @@
 //
 
 #include "Precompiled.h"
-#include "CollisionShape.h"
+#include "BoxShape.h"
 #include "Context.h"
 #include "DebugRenderer.h"
 #include "Joint.h"
@@ -129,8 +129,8 @@ void PhysicsWorld::RegisterObject(Context* context)
     
     ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_VECTOR3, "Gravity", GetGravity, SetGravity, Vector3, DEFAULT_GRAVITY, AM_DEFAULT);
     ATTRIBUTE(PhysicsWorld, VAR_INT, "Physics FPS", fps_, DEFAULT_FPS, AM_DEFAULT);
+    ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Net Max Angular Vel.", maxNetworkAngularVelocity_, DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY, AM_DEFAULT);
     ATTRIBUTE(PhysicsWorld, VAR_BOOL, "Interpolation", interpolation_, true, AM_FILE);
-    ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Network Max Angular Velocity", maxNetworkAngularVelocity_, DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY, AM_DEFAULT);
 }
 
 void PhysicsWorld::drawLine(const btVector3& from, const btVector3& to, const btVector3& color)
@@ -146,7 +146,7 @@ void PhysicsWorld::reportErrorWarning(const char* warningString)
 
 void PhysicsWorld::Update(float timeStep)
 {
-    PROFILE(Physics_Update);
+    PROFILE(UpdatePhysics);
     
     float internalTimeStep = 1.0f / fps_;
     
@@ -189,6 +189,12 @@ void PhysicsWorld::SetMaxNetworkAngularVelocity(float velocity)
 
 void PhysicsWorld::Raycast(PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask)
 {
+    /// \todo Implement
+}
+
+void PhysicsWorld::RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, unsigned collisionMask)
+{
+    /// \todo Implement
 }
 
 Vector3 PhysicsWorld::GetGravity() const
@@ -303,11 +309,13 @@ void PhysicsWorld::PostStep(float timeStep)
 
 void PhysicsWorld::SendCollisionEvents()
 {
+    /// \todo Implement
 }
 
 void RegisterPhysicsLibrary(Context* context)
 {
     Joint::RegisterObject(context);
     RigidBody::RegisterObject(context);
+    BoxShape::RegisterObject(context);
     PhysicsWorld::RegisterObject(context);
 }

+ 3 - 0
Engine/Physics/PhysicsWorld.h

@@ -135,6 +135,9 @@ public:
     /// Perform a physics world raycast.
     void Raycast(PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask =
         M_MAX_UNSIGNED);
+    /// Perform a physics world raycast and return the closest result.
+    void RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, unsigned collisionMask =
+        M_MAX_UNSIGNED);
     
     /// Return gravity.
     Vector3 GetGravity() const;

+ 75 - 13
Engine/Physics/RigidBody.cpp

@@ -28,6 +28,7 @@
 #include "MemoryBuffer.h"
 #include "PhysicsUtils.h"
 #include "PhysicsWorld.h"
+#include "Profiler.h"
 #include "ResourceCache.h"
 #include "ResourceEvents.h"
 #include "RigidBody.h"
@@ -41,6 +42,8 @@
 static const float DEFAULT_MASS = 0.0f;
 static const float DEFAULT_FRICTION = 0.5f;
 static const float DEFAULT_RESTITUTION = 0.0f;
+static const float DEFAULT_LINEAR_REST_THRESHOLD = 0.8f;
+static const float DEFAULT_ANGULAR_REST_THRESHOLD = 1.0f;
 static const unsigned DEFAULT_COLLISION_GROUP = 0xffff;
 
 OBJECTTYPESTATIC(RigidBody);
@@ -48,13 +51,13 @@ OBJECTTYPESTATIC(RigidBody);
 RigidBody::RigidBody(Context* context) :
     Component(context),
     body_(0),
-    collisionShape_(0),
+    compoundShape_(0),
     mass_(DEFAULT_MASS),
     collisionGroup_(DEFAULT_COLLISION_GROUP),
     collisionMask_(DEFAULT_COLLISION_GROUP),
     inSetTransform_(false)
 {
-    collisionShape_ = new btCompoundShape();
+    compoundShape_ = new btCompoundShape();
 }
 
 RigidBody::~RigidBody()
@@ -64,33 +67,33 @@ RigidBody::~RigidBody()
     if (physicsWorld_)
         physicsWorld_->RemoveRigidBody(this);
     
-    delete collisionShape_;
-    collisionShape_ = 0;
+    delete compoundShape_;
+    compoundShape_ = 0;
 }
 
 void RigidBody::RegisterObject(Context* context)
 {
     context->RegisterFactory<RigidBody>();
     
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Mass", GetMass, SetMass, float, DEFAULT_MASS, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_VECTOR3, "Physics Position", GetPosition, SetPosition, Vector3, Vector3::ZERO, AM_FILE | AM_NOEDIT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_QUATERNION, "Physics Rotation", GetRotation, SetRotation, Quaternion, Quaternion::IDENTITY, AM_FILE | AM_NOEDIT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Mass", GetMass, SetMass, float, DEFAULT_MASS, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Friction", GetFriction, SetFriction, float, DEFAULT_FRICTION, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Restitution", GetRestitution, SetRestitution, float, DEFAULT_RESTITUTION, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_VECTOR3, "Linear Velocity", GetLinearVelocity, SetLinearVelocity, Vector3, Vector3::ZERO, AM_DEFAULT | AM_LATESTDATA);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_VECTOR3, "Angular Velocity", GetAngularVelocity, SetAngularVelocity, Vector3, Vector3::ZERO, AM_FILE);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_VECTOR3, "Linear Factor", GetLinearFactor, SetLinearFactor, Vector3, Vector3::ONE, AM_DEFAULT | AM_LATESTDATA);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Rest Threshold", GetLinearRestThreshold, SetLinearRestThreshold, float, 0.01f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Damping", GetLinearDamping, SetLinearDamping, float, 0.0f, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_VECTOR3, "Angular Factor", GetAngularFactor, SetAngularFactor, Vector3, Vector3::ONE, AM_FILE);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Rest Threshold", GetAngularRestThreshold, SetAngularRestThreshold, float, 0.01f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Damping", GetLinearDamping, SetLinearDamping, float, 0.0f, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Damping", GetAngularDamping, SetAngularDamping, float, 0.01f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Friction", GetFriction, SetFriction, float, DEFAULT_FRICTION, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Restitution", GetRestitution, SetRestitution, float, DEFAULT_RESTITUTION, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Rest Threshold", GetLinearRestThreshold, SetLinearRestThreshold, float, 0.01f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Rest Threshold", GetAngularRestThreshold, SetAngularRestThreshold, float, 0.01f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_INT, "Collision Group", GetCollisionGroup, SetCollisionGroup, unsigned, DEFAULT_COLLISION_GROUP, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_INT, "Collision Mask", GetCollisionMask, SetCollisionMask, unsigned, DEFAULT_COLLISION_GROUP, AM_DEFAULT);
     REF_ACCESSOR_ATTRIBUTE(RigidBody, VAR_BUFFER, "Network Angular Velocity", GetNetAngularVelocityAttr, SetNetAngularVelocityAttr, PODVector<unsigned char>, PODVector<unsigned char>(), AM_NET | AM_LATESTDATA | AM_NOEDIT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_BOOL, "Use Gravity", GetUseGravity, SetUseGravity, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_BOOL, "Is Kinematic", IsKinematic, SetKinematic, bool, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(RigidBody, VAR_BOOL, "Is Phantom", IsPhantom, SetPhantom, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_INT, "Collision Group", GetCollisionGroup, SetCollisionGroup, unsigned, DEFAULT_COLLISION_GROUP, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_INT, "Collision Mask", GetCollisionMask, SetCollisionMask, unsigned, DEFAULT_COLLISION_GROUP, AM_DEFAULT);
 }
 
 void RigidBody::getWorldTransform(btTransform &worldTrans) const
@@ -121,6 +124,14 @@ void RigidBody::SetMass(float mass)
     {
         mass_ = mass;
         CreateBody();
+        
+        if (mass > 0.0f)
+            Activate();
+        else
+        {
+            SetLinearVelocity(Vector3::ZERO);
+            SetAngularVelocity(Vector3::ZERO);
+        }
     }
 }
 
@@ -497,6 +508,43 @@ bool RigidBody::IsActive() const
         return false;
 }
 
+void RigidBody::RefreshCollisionShapes()
+{
+    if (node_ && compoundShape_)
+    {
+        PROFILE(RefreshCollisionShapes);
+        
+        // Remove all existing shapes first
+        while (compoundShape_->getNumChildShapes())
+            compoundShape_->removeChildShapeByIndex(compoundShape_->getNumChildShapes() - 1);
+        
+        // Then search for CollisionShape components and get their current collision shapes
+        PODVector<CollisionShape*> shapes;
+        node_->GetDerivedComponents<CollisionShape>(shapes);
+        
+        for (PODVector<CollisionShape*>::ConstIterator i = shapes.Begin(); i != shapes.End(); ++i)
+        {
+            btCollisionShape* shape = (*i)->GetCollisionShape();
+            if (shape)
+            {
+                btTransform shapeTransform;
+                shapeTransform.setOrigin(ToBtVector3(node_->GetWorldScale() * (*i)->GetPosition()));
+                shapeTransform.setRotation(ToBtQuaternion((*i)->GetRotation()));
+                compoundShape_->addChildShape(shapeTransform, shape);
+            }
+        }
+        
+        // Refresh inertia whenever collision shapes change
+        if (body_)
+        {
+            btVector3 localInertia(0.0f, 0.0f, 0.0f);
+            if (mass_ > 0.0f)
+                compoundShape_->calculateLocalInertia(mass_, localInertia);
+            body_->setMassProps(mass_, localInertia);
+        }
+    }
+}
+
 void RigidBody::SetNetAngularVelocityAttr(const PODVector<unsigned char>& value)
 {
     float maxVelocity = physicsWorld_ ? physicsWorld_->GetMaxNetworkAngularVelocity() : DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY;
@@ -512,6 +560,11 @@ const PODVector<unsigned char>& RigidBody::GetNetAngularVelocityAttr() const
     return attrBuffer_.GetBuffer();
 }
 
+void RigidBody::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    /// \todo Implement
+}
+
 void RigidBody::OnMarkedDirty(Node* node)
 {
     // Physics operations are not safe from worker threads
@@ -554,6 +607,8 @@ void RigidBody::CreateBody()
     }
     
     btVector3 localInertia(0.0f, 0.0f, 0.0f);
+    if (mass_ > 0.0f)
+        compoundShape_->calculateLocalInertia(mass_, localInertia);
     
     btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
     if (body_)
@@ -562,11 +617,18 @@ void RigidBody::CreateBody()
         body_->setMassProps(mass_, localInertia);
     }
     else
-        body_ = new btRigidBody(mass_, this, collisionShape_, localInertia);
+    {
+        // Build the compound shape, then create the Bullet rigid body
+        RefreshCollisionShapes();
+        body_ = new btRigidBody(mass_, this, compoundShape_, localInertia);
+    }
     
     int flags = body_->getCollisionFlags();
     if (mass_ > 0.0f)
+    {
         flags &= ~btCollisionObject::CF_STATIC_OBJECT;
+        
+    }
     else
         flags |= btCollisionObject::CF_STATIC_OBJECT;
     body_->setCollisionFlags(flags);

+ 11 - 8
Engine/Physics/RigidBody.h

@@ -111,8 +111,6 @@ public:
     /// Activate rigid body if it was resting.
     void Activate();
     
-    /// Return physics world.
-    PhysicsWorld* GetPhysicsWorld() const { return physicsWorld_; }
     /// Return mass.
     float GetMass() const { return mass_; }
     /// Return rigid body world-space position.
@@ -156,17 +154,22 @@ public:
     /// Return collision mask.
     unsigned GetCollisionMask() const { return collisionMask_; }
     
-    /// Return the Bullet rigid body.
+    /// Return physics world.
+    PhysicsWorld* GetPhysicsWorld() const { return physicsWorld_; }
+    /// Return Bullet rigid body.
     btRigidBody* GetBody() const { return body_; }
-    /// Return the Bullet compound collision shape.
-    btCompoundShape* GetCollisionShape() const { return collisionShape_; }
-    /// Update a collision shape.
-    void UpdateCollisionShape(CollisionShape* shape);
+    /// Return Bullet compound collision shape.
+    btCompoundShape* GetCollisionShape() const { return compoundShape_; }
+    /// Refresh collision shape(s).
+    void RefreshCollisionShapes();
     /// %Set network angular velocity attribute.
     void SetNetAngularVelocityAttr(const PODVector<unsigned char>& value);
     /// Return network angular velocity attribute.
     const PODVector<unsigned char>& GetNetAngularVelocityAttr() const;
     
+    /// Add debug geometry to the debug renderer.
+    void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+    
 protected:
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
@@ -182,7 +185,7 @@ private:
     /// Bullet rigid body.
     btRigidBody* body_;
     /// Bullet compound collision shape.
-    btCompoundShape* collisionShape_;
+    btCompoundShape* compoundShape_;
     /// Physics world.
     WeakPtr<PhysicsWorld> physicsWorld_;
     /// Mass.

+ 14 - 0
Engine/Scene/Node.h

@@ -269,6 +269,8 @@ public:
     void GetDependencyNodes(PODVector<Node*>& dest) const;
     /// Return first component derived from class.
     template <class T> T* GetDerivedComponent() const;
+    /// Return components derived from class.
+    template <class T> void GetDerivedComponents(PODVector<T*>& dest) const;
     /// Template version of returning child nodes with a specific component.
     template <class T> void GetChildrenWithComponent(PODVector<Node*>& dest, bool recursive = false) const;
     /// Template version of returning a component by type.
@@ -368,3 +370,15 @@ template <class T> T* Node::GetDerivedComponent() const
     
     return 0;
 }
+
+template <class T> void Node::GetDerivedComponents(PODVector<T*>& dest) const
+{
+    dest.Clear();
+    
+    for (Vector<SharedPtr<Component> >::ConstIterator i = components_.Begin(); i != components_.End(); ++i)
+    {
+        T* component = dynamic_cast<T*>(i->Get());
+        if (component)
+            dest.Push(component);
+    }
+}