Преглед изворни кода

Merge pull request #221 from blackberry-gaming/next-sgrenier

Next sgrenier
Steve Grenier пре 13 година
родитељ
комит
9a87799f68

+ 71 - 2
gameplay/src/Node.cpp

@@ -7,17 +7,22 @@
 #include "PhysicsCharacter.h"
 #include "Game.h"
 
+// Node dirty flags
 #define NODE_DIRTY_WORLD 1
 #define NODE_DIRTY_BOUNDS 2
 #define NODE_DIRTY_ALL (NODE_DIRTY_WORLD | NODE_DIRTY_BOUNDS)
 
+// Node property flags
+#define NODE_FLAG_VISIBLE 1
+#define NODE_FLAG_TRANSPARENT 2
+
 namespace gameplay
 {
 
 Node::Node(const char* id)
     : _scene(NULL), _firstChild(NULL), _nextSibling(NULL), _prevSibling(NULL), _parent(NULL), _childCount(NULL),
-    _camera(NULL), _light(NULL), _model(NULL), _form(NULL), _audioSource(NULL), _particleEmitter(NULL),
-	_collisionObject(NULL), _dirtyBits(NODE_DIRTY_ALL), _notifyHierarchyChanged(true)
+    _nodeFlags(NODE_FLAG_VISIBLE), _camera(NULL), _light(NULL), _model(NULL), _form(NULL), _audioSource(NULL), _particleEmitter(NULL),
+	_collisionObject(NULL), _dirtyBits(NODE_DIRTY_ALL), _notifyHierarchyChanged(true), _userData(NULL)
 {
     if (id)
     {
@@ -45,6 +50,15 @@ Node::~Node()
     SAFE_RELEASE(_particleEmitter);
     SAFE_RELEASE(_form);
     SAFE_DELETE(_collisionObject);
+
+    // Cleanup user data
+    if (_userData)
+    {
+        // Call custom cleanup callback if specified
+        if (_userData->cleanupCallback)
+            _userData->cleanupCallback(_userData->pointer);
+        SAFE_DELETE(_userData);
+    }
 }
 
 Node* Node::create(const char* id)
@@ -195,6 +209,61 @@ Node* Node::getParent() const
     return _parent;
 }
 
+bool Node::isVisible() const
+{
+    return ((_nodeFlags & NODE_FLAG_VISIBLE) == NODE_FLAG_VISIBLE);
+}
+
+void Node::setVisible(bool visible)
+{
+    if (visible)
+        _nodeFlags |= NODE_FLAG_VISIBLE;
+    else
+        _nodeFlags &= ~NODE_FLAG_VISIBLE;
+}
+
+bool Node::isTransparent() const
+{
+    return ((_nodeFlags & NODE_FLAG_TRANSPARENT) == NODE_FLAG_TRANSPARENT);
+}
+
+void Node::setTransparent(bool transparent)
+{
+    if (transparent)
+        _nodeFlags |= NODE_FLAG_TRANSPARENT;
+    else
+        _nodeFlags &= ~NODE_FLAG_TRANSPARENT;
+}
+
+void* Node::getUserPointer() const
+{
+    return (_userData ? _userData->pointer : NULL);
+}
+
+void Node::setUserPointer(void* pointer, void (*cleanupCallback)(void*))
+{
+    // If existing user pointer is being changed, call cleanup function to free previous pointer
+    if (_userData && _userData->pointer && _userData->cleanupCallback && pointer != _userData->pointer)
+    {
+        _userData->cleanupCallback(_userData->pointer);
+    }
+
+    if (pointer)
+    {
+        // Assign user pointer
+        if (_userData == NULL)
+            _userData = new UserData();
+
+        _userData->pointer = pointer;
+        _userData->cleanupCallback = cleanupCallback;
+    }
+    else
+    {
+        // Clear user pointer
+        SAFE_DELETE(_userData);
+    }
+}
+
 unsigned int Node::getChildCount() const
 {
     return _childCount;

+ 72 - 0
gameplay/src/Node.h

@@ -113,6 +113,70 @@ public:
      */
     Node* getParent() const;
 
+    /**
+     * Returns whether this node is visible (true by default).
+     *
+     * @return Whether the node is visible.
+     */
+    bool isVisible() const;
+
+    /**
+     * Sets whether this node is visible.
+     *
+     * @return Whether this node is visible.
+     */
+    void setVisible(bool visible);
+
+    /**
+     * Returns whether this node is transparent (false by default).
+     *
+     * All nodes are opaque by default, unless otherwise set as
+     * transparent using the setTransparent method. These methods
+     * can be used to flag nodes as transparent and then query the
+     * property during game execution, for example to render all
+     * opaque objects first, followed by transparent objects with
+     * alpha blending enabled.
+     *
+     * @return Whether the node is transparent.
+     */
+    bool isTransparent() const;
+
+    /**
+     * Sets whether this node is transparent.
+     *
+     * @param transparent Whether the node is transparent.
+     */
+    void setTransparent(bool transparent);
+
+    /**
+     * Returns the user pointer for this node.
+     *
+     * @return The user pointer for this node.
+     * @see setUserPointer(void*)
+     */
+    void* getUserPointer() const;
+
+    /**
+     * Sets the user pointer for this node.
+     *
+     * The user pointer is initially NULL and can be set to anything.
+     * This is normally used to store game-specific data, such as 
+     * game state for a particular node.  For example, attributes
+     * for a game character, such as hit points, stamina, etc can
+     * be defined in a game structure and stored in this field.
+     *
+     * When a node is deleted, the (optional) cleanup callback
+     * function passed to this function is called to allow the 
+     * user to free any memory associated with the user pointer.
+     *
+     * @param pointer User pointer.
+     * @param cleanupCallback Optional callback that is called when the
+     *      Node is being destroyed (or when the user pointer changes),
+     *      to allow the user to cleanup any memory associated with the
+     *      user pointer.
+     */
+    void setUserPointer(void* pointer, void (*cleanupCallback)(void*) = NULL);
+
     /**
      * Returns the number of direct children of this item.
      *
@@ -530,6 +594,12 @@ private:
 
 protected:
 
+    struct UserData
+    {
+        void* pointer;
+        void (*cleanupCallback)(void*);
+    };
+
     Scene* _scene;
     std::string _id;
     Node* _firstChild;
@@ -537,6 +607,7 @@ protected:
     Node* _prevSibling;
     Node* _parent;
     unsigned int _childCount;
+    unsigned int _nodeFlags;
     Camera* _camera;
     Light* _light;
     Model* _model;
@@ -548,6 +619,7 @@ protected:
     mutable int _dirtyBits;
     bool _notifyHierarchyChanged;
     mutable BoundingSphere _bounds;
+    UserData* _userData;
 };
 
 /**

+ 38 - 51
gameplay/src/PhysicsCharacter.cpp

@@ -10,16 +10,6 @@
 #include "Game.h"
 #include "PhysicsController.h"
 
-// Amount to walk collision normal when attempting to repair a collision.
-// To small a value will result in inefficient collision repairs (several iterations
-// to fix a collision and slow resolution), whereas larger values will result
-// in less accurate collision resolution.
-//#define COLLISION_REPAIR_INCREMENT 0.2f
-#define COLLISION_REPAIR_MARGIN 1.0f
-
-// Maximum number of iterations used to perform perform collision repair each update.
-#define COLLISION_REPAIR_MAX_ITERATIONS 4
-
 namespace gameplay
 {
 
@@ -37,6 +27,7 @@ public:
 		if (convexResult.m_hitCollisionObject == _me)
 			return btScalar(1.0);
 
+        /*
 		btVector3 hitNormalWorld;
 		if (normalInWorldSpace)
 		{
@@ -52,8 +43,9 @@ public:
         {
 			return btScalar(1.0);
 		}
+        */
 
-		return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace);
+		return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
 	}
 
 protected:
@@ -67,7 +59,7 @@ PhysicsCharacter::PhysicsCharacter(Node* node, const PhysicsCollisionShape::Defi
     : PhysicsGhostObject(node, shape), _moveVelocity(0,0,0), _forwardVelocity(0.0f), _rightVelocity(0.0f),
     _fallVelocity(0, 0, 0), _currentVelocity(0,0,0), _normalizedVelocity(0,0,0),
     _colliding(false), _collisionNormal(0,0,0), _currentPosition(0,0,0),
-    _stepHeight(0.2f), _slopeAngle(0.0f), _cosSlopeAngle(0.0f), _physicsEnabled(true)
+    _stepHeight(0.1f), _slopeAngle(0.0f), _cosSlopeAngle(0.0f), _physicsEnabled(true)
 {
 	setMaxSlopeAngle(45.0f);
 
@@ -288,6 +280,7 @@ void PhysicsCharacter::setRotation(const Quaternion& rotation)
 {
     _node->setRotation(rotation);
 }
+
 void PhysicsCharacter::setForwardVelocity(float velocity)
 {
     _forwardVelocity = velocity;
@@ -361,27 +354,23 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
     if (_physicsEnabled)
     {
         //_colliding = fixCollision(collisionWorld);
-        /*_colliding = false;
+        _colliding = false;
         int stepCount = 0;
 	    while (fixCollision(collisionWorld))
 	    {
             _colliding = true;
 
-            // After a small number of attempts to fix a collision/penetration, give up.
-            // This hanldes the case where we are deeply penetrating some object and attempting
-            // to step out of it (by COLLISION_REPAIR_INCREMENT units) does not fix the collision.
-            if (++stepCount > COLLISION_REPAIR_MAX_ITERATIONS)
+            if (++stepCount > 4)
 		    {
-                WARN_VARG("Character '%s' could not recover from collision.", _node->getId());
+                // Most likely we are wedged between a number of different collision objects
 			    break;
 		    }
-	    }*/
+	    }
     }
 
     // Update current and target world positions
-	Vector3 startPosition;
-	_node->getWorldMatrix().getTranslation(&startPosition);
-    _currentPosition = BV(startPosition);
+	btVector3 startPosition = _ghostObject->getWorldTransform().getOrigin();
+	_currentPosition = startPosition;
 
     // Process movement in the up direction
     if (_physicsEnabled)
@@ -395,7 +384,8 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
 		stepDown(collisionWorld, deltaTimeStep);
 
     // Set new position
-	_node->translate(Vector3(_currentPosition.x(), _currentPosition.y(), _currentPosition.z()) - startPosition);
+	btVector3 translation = _currentPosition - startPosition;
+	_node->translate(translation.x(), translation.y(), translation.z());
 }
 
 void PhysicsCharacter::stepUp(btCollisionWorld* collisionWorld, btScalar time)
@@ -408,6 +398,7 @@ void PhysicsCharacter::stepUp(btCollisionWorld* collisionWorld, btScalar time)
     // 
     // Note that stepDown() will be called right after this, so the character will move back
     // down to collide with the ground so that he smoothly steps up stairs.
+	_currentPosition += btVector3(0, _stepHeight, 0);
 }
 
 void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, float time)
@@ -470,8 +461,10 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
     }
 
     // Check for collisions by performing a bullet convex sweep test
-    btTransform start = _ghostObject->getWorldTransform();
-	btTransform end = _ghostObject->getWorldTransform();
+    btTransform start;
+	btTransform end;
+	start.setIdentity();
+	end.setIdentity();
 
 	btScalar fraction = 1.0;
 	btScalar distance2;
@@ -480,22 +473,21 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 	{
         updateTargetPositionFromCollision(targetPosition, _collisionNormal);
 	}
- 
+
 	int maxIter = 10;
 
 	while (fraction > btScalar(0.01) && maxIter-- > 0)
 	{
 		start.setOrigin(_currentPosition);
 		end.setOrigin(targetPosition);
+
 		btVector3 sweepDirNegative(_currentPosition - targetPosition);
 
 		ClosestNotMeConvexResultCallback callback(_ghostObject, sweepDirNegative, btScalar(0.0));
 		callback.m_collisionFilterGroup = _ghostObject->getBroadphaseHandle()->m_collisionFilterGroup;
 		callback.m_collisionFilterMask = _ghostObject->getBroadphaseHandle()->m_collisionFilterMask;
-		//callback.m_collisionFilterGroup = btBroadphaseProxy::CharacterFilter;
-		//callback.m_collisionFilterMask = btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter;
 
-		_ghostObject->convexSweepTest(static_cast<btConvexShape*>(_collisionShape->getShape()), start, end, callback, 0.0f);//collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
+		_ghostObject->convexSweepTest(static_cast<btConvexShape*>(_collisionShape->getShape()), start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
 
 		fraction -= callback.m_closestHitFraction;
 
@@ -510,7 +502,8 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 				rb->applyImpulse(-normal);
 			}*/
 
-			updateTargetPositionFromCollision(targetPosition, callback.m_hitNormalWorld);
+            updateTargetPositionFromCollision(targetPosition, callback.m_hitNormalWorld);
+
 			btVector3 currentDir = targetPosition - _currentPosition;
 			distance2 = currentDir.length2();
 			if (distance2 > FLT_EPSILON)
@@ -536,32 +529,26 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 
 void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
 {
-    // Contribute gravity to fall velocity.
-    // TODO: This simple formula assumes no air friction, which is completely unrealistic
-    // (characters fall way too fast). We should consider how to support this without much
-    // added complexity.
-    btVector3 gravity = Game::getInstance()->getPhysicsController()->_world->getGravity();
+    // Contribute basic gravity to fall velocity.
+	btVector3 gravity = Game::getInstance()->getPhysicsController()->_world->getGravity();
     _fallVelocity += (gravity * time);
 
-    btVector3 targetPosition = _currentPosition + _fallVelocity;
+    btVector3 targetPosition = _currentPosition + (_fallVelocity * time);
+	targetPosition -= btVector3(0, _stepHeight, 0);
 
     // Perform a convex sweep test between current and target position
-	btTransform start = _ghostObject->getWorldTransform();
-	btTransform end = _ghostObject->getWorldTransform();
-    start.setOrigin(_currentPosition);
-    end.setOrigin(targetPosition);
-
-    // TODO: We probably have to perform sweep tests separately in stepForward and stepDown (and stepUp) since
-    // combining the full move into a single targetPosition and computing sweep test between currentPosition and targetPosition
-    // is ALYWAYS going to result in a collision at almost exactly currentPosition... this is because, when you are already
-    // on the floor and applying gravity, 
+	btTransform start;
+	btTransform end;
+	start.setIdentity();
+	end.setIdentity();
+	start.setOrigin(_currentPosition);
+	end.setOrigin(targetPosition);
+
     ClosestNotMeConvexResultCallback callback(_ghostObject, btVector3(0, 1, 0), _cosSlopeAngle);
 	callback.m_collisionFilterGroup = _ghostObject->getBroadphaseHandle()->m_collisionFilterGroup;
 	callback.m_collisionFilterMask = _ghostObject->getBroadphaseHandle()->m_collisionFilterMask;
-	//callback.m_collisionFilterGroup = btBroadphaseProxy::CharacterFilter;
-	//callback.m_collisionFilterMask = btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter;
 
-	_ghostObject->convexSweepTest(static_cast<btConvexShape*>(_collisionShape->getShape()), start, end, callback, 0.0f);//collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
+	_ghostObject->convexSweepTest(static_cast<btConvexShape*>(_collisionShape->getShape()), start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
 
 	if (callback.hasHit())
 	{
@@ -687,10 +674,10 @@ bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
                         _collisionNormal = pt.m_normalWorldOnB * directionSign;
 					}
 
+					//Node* node = Game::getInstance()->getPhysicsController()->getCollisionObject((btCollisionObject*)(manifold->getBody0() == _ghostObject ? manifold->getBody1() : manifold->getBody0()))->getNode();
+
                     // Calculate new position for object, which is translated back along the collision normal
-                    btVector3 n(pt.m_normalWorldOnB);
-                    n.normalize();
-					currentPosition += /*pt.m_normalWorldOnB*/n * directionSign * dist * COLLISION_REPAIR_MARGIN;// + _collisionShape->getMargin());// * COLLISION_REPAIR_INCREMENT;
+					currentPosition += pt.m_normalWorldOnB * directionSign * dist * 0.2f;
 					collision = true;
 				}
 			}

+ 7 - 5
gameplay/src/PhysicsController.cpp

@@ -235,6 +235,8 @@ void PhysicsController::initialize()
     _ghostPairCallback = bullet_new<btGhostPairCallback>();
     _world->getPairCache()->setInternalGhostPairCallback(_ghostPairCallback);
 
+	_world->getDispatchInfo().m_allowedCcdPenetration = 0.0001f;
+
     // Set up debug drawing.
     _debugDrawer = new DebugDrawer();
     _world->setDebugDrawer(_debugDrawer);
@@ -527,7 +529,7 @@ PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsC
 
 				if (shape.centerAbsolute)
 				{
-					computeCenterOfMass(Vector3(shape.data.box.center), scale, centerOfMassOffset);
+					computeCenterOfMass(Vector3(shape.data.box.center), Vector3::one(), centerOfMassOffset);
 				}
 				else
 				{
@@ -557,7 +559,7 @@ PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsC
 
 				if (shape.centerAbsolute)
 				{
-					computeCenterOfMass(Vector3(shape.data.sphere.center), scale, centerOfMassOffset);
+					computeCenterOfMass(Vector3(shape.data.sphere.center), Vector3::one(), centerOfMassOffset);
 				}
 				else
 				{
@@ -587,7 +589,7 @@ PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsC
 
 				if (shape.centerAbsolute)
 				{
-					computeCenterOfMass(Vector3(shape.data.capsule.center), scale, centerOfMassOffset);
+					computeCenterOfMass(Vector3(shape.data.capsule.center), Vector3::one(), centerOfMassOffset);
 				}
 				else
 				{
@@ -602,7 +604,7 @@ PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsC
 				BoundingBox box;
 				getBoundingBox(node, &box);
 				float radius = std::max((box.max.x - box.min.x) * 0.5f, (box.max.z - box.min.z) * 0.5f);
-				float height = (box.max.y - box.min.y) - radius * 2.0f;
+				float height = box.max.y - box.min.y;
 				collisionShape = createCapsule(radius, height, scale);
 
 				computeCenterOfMass(box.getCenter(), scale, centerOfMassOffset);
@@ -698,7 +700,7 @@ PhysicsCollisionShape* PhysicsController::createCapsule(float radius, float heig
 	if (girthScale < scale.z)
 		girthScale = scale.z;
 	float scaledRadius = radius * girthScale;
-	float scaledHeight = height * scale.y;
+	float scaledHeight = height * scale.y - radius * 2;
 
 	PhysicsCollisionShape* shape;
 

+ 11 - 1
gameplay/src/SceneLoader.cpp

@@ -61,7 +61,8 @@ Scene* SceneLoader::load(const char* filePath)
         SceneNodeProperty::PARTICLE |
         SceneNodeProperty::ROTATE |
         SceneNodeProperty::SCALE |
-        SceneNodeProperty::TRANSLATE);
+        SceneNodeProperty::TRANSLATE | 
+        SceneNodeProperty::TRANSPARENT);
     applyNodeProperties(scene, sceneProperties, SceneNodeProperty::CHARACTER | SceneNodeProperty::GHOST | SceneNodeProperty::RIGIDBODY);
     createAnimations(scene);
 
@@ -364,6 +365,11 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
                 node->setScale(s);
             break;
         }
+        case SceneNodeProperty::TRANSPARENT:
+        {
+            node->setTransparent(true);
+            break;
+        }
         default:
             WARN_VARG("Unsupported node property type: %d.", snp._type);
             break;
@@ -572,6 +578,10 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::SCALE);
                 }
+                else if (strcmp(name, "transparent") == 0)
+                {
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::TRANSPARENT);
+                }
                 else
                 {
                     WARN_VARG("Unsupported node property: %s = %s", name, ns->getString());

+ 2 - 1
gameplay/src/SceneLoader.h

@@ -55,7 +55,8 @@ private:
             TRANSLATE = 64,
             ROTATE = 128,
             SCALE = 256,
-            URL = 512
+            URL = 512,
+            TRANSPARENT = 1024
         };
 
         SceneNodeProperty(Type type, std::string file, std::string id, int index) : _type(type), _file(file), _id(id), _index(index) { }