瀏覽代碼

Merge pull request #206 from blackberry-gaming/next-dgough

Added node cloning and fixed 3D sound
Sean Paul Taylor 13 年之前
父節點
當前提交
1c38a394f5
共有 54 個文件被更改,包括 966 次插入112 次删除
  1. 2 0
      gameplay/gameplay.vcxproj
  2. 6 0
      gameplay/gameplay.vcxproj.filters
  3. 50 2
      gameplay/src/Animation.cpp
  4. 44 9
      gameplay/src/Animation.h
  5. 2 2
      gameplay/src/AnimationClip.cpp
  6. 1 1
      gameplay/src/AnimationController.cpp
  7. 28 0
      gameplay/src/AnimationTarget.cpp
  8. 21 1
      gameplay/src/AnimationTarget.h
  9. 12 12
      gameplay/src/AudioBuffer.cpp
  10. 1 1
      gameplay/src/AudioController.cpp
  11. 1 1
      gameplay/src/AudioController.h
  12. 13 2
      gameplay/src/AudioListener.cpp
  13. 8 0
      gameplay/src/AudioListener.h
  14. 43 8
      gameplay/src/AudioSource.cpp
  15. 15 6
      gameplay/src/AudioSource.h
  16. 20 0
      gameplay/src/Camera.cpp
  17. 9 0
      gameplay/src/Camera.h
  18. 38 0
      gameplay/src/CloneContext.cpp
  19. 86 0
      gameplay/src/CloneContext.h
  20. 1 1
      gameplay/src/Container.cpp
  21. 1 1
      gameplay/src/FileSystem.cpp
  22. 8 4
      gameplay/src/Font.h
  23. 1 1
      gameplay/src/Form.cpp
  24. 10 0
      gameplay/src/Joint.cpp
  25. 24 0
      gameplay/src/Joint.h
  26. 26 0
      gameplay/src/Light.cpp
  27. 15 5
      gameplay/src/Light.h
  28. 18 0
      gameplay/src/Material.cpp
  29. 9 0
      gameplay/src/Material.h
  30. 80 2
      gameplay/src/MaterialParameter.cpp
  31. 11 2
      gameplay/src/MaterialParameter.h
  32. 1 1
      gameplay/src/MeshBatch.h
  33. 60 1
      gameplay/src/MeshSkin.cpp
  34. 29 1
      gameplay/src/MeshSkin.h
  35. 16 2
      gameplay/src/Model.cpp
  36. 10 1
      gameplay/src/Model.h
  37. 67 7
      gameplay/src/Node.cpp
  38. 55 10
      gameplay/src/Node.h
  39. 3 3
      gameplay/src/ParticleEmitter.h
  40. 9 4
      gameplay/src/Pass.cpp
  41. 15 0
      gameplay/src/Pass.h
  42. 26 4
      gameplay/src/RenderState.cpp
  43. 23 5
      gameplay/src/RenderState.h
  44. 1 0
      gameplay/src/SpriteBatch.h
  45. 14 4
      gameplay/src/Technique.cpp
  46. 8 1
      gameplay/src/Technique.h
  47. 8 0
      gameplay/src/Transform.cpp
  48. 2 1
      gameplay/src/Transform.h
  49. 2 1
      gameplay/src/Vector2.cpp
  50. 3 1
      gameplay/src/Vector2.h
  51. 2 1
      gameplay/src/Vector3.cpp
  52. 3 1
      gameplay/src/Vector3.h
  53. 2 1
      gameplay/src/Vector4.cpp
  54. 3 1
      gameplay/src/Vector4.h

+ 2 - 0
gameplay/gameplay.vcxproj

@@ -30,6 +30,7 @@
     <ClCompile Include="src\Button.cpp" />
     <ClCompile Include="src\Camera.cpp" />
     <ClCompile Include="src\CheckBox.cpp" />
+    <ClCompile Include="src\CloneContext.cpp" />
     <ClCompile Include="src\Container.cpp" />
     <ClCompile Include="src\Control.cpp" />
     <ClCompile Include="src\Curve.cpp" />
@@ -119,6 +120,7 @@
     <ClInclude Include="src\Button.h" />
     <ClInclude Include="src\Camera.h" />
     <ClInclude Include="src\CheckBox.h" />
+    <ClInclude Include="src\CloneContext.h" />
     <ClInclude Include="src\Container.h" />
     <ClInclude Include="src\Control.h" />
     <ClInclude Include="src\Curve.h" />

+ 6 - 0
gameplay/gameplay.vcxproj.filters

@@ -273,6 +273,9 @@
     <ClCompile Include="src\PhysicsGhostObject.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\CloneContext.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -539,6 +542,9 @@
     <ClInclude Include="src\PhysicsGhostObject.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\CloneContext.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">

+ 50 - 2
gameplay/src/Animation.cpp

@@ -27,6 +27,11 @@ Animation::Animation(const char* id, AnimationTarget* target, int propertyId, un
     createChannel(target, propertyId, keyCount, keyTimes, keyValues, keyInValue, keyOutValue, type);
 }
 
+Animation::Animation(const char* id)
+    : _controller(Game::getInstance()->getAnimationController()), _id(id), _duration(0), _defaultClip(NULL), _clips(NULL)
+{
+}
+
 Animation::~Animation()
 {
     if (_defaultClip)
@@ -54,8 +59,9 @@ Animation::~Animation()
 }
 
 Animation::Channel::Channel(Animation* animation, AnimationTarget* target, int propertyId, Curve* curve, unsigned long duration)
-    : _animation(animation), _target(target), _propertyId(propertyId), _curve(curve), _duration(duration)
+    : _animation(animation), _target(target), _propertyId(propertyId), _duration(duration)
 {
+    _curveRef = Animation::CurveRef::create(curve);
     // get property component count, and ensure the property exists on the AnimationTarget by getting the property component count.
     assert(_target->getAnimationPropertyComponentCount(propertyId));
 
@@ -64,12 +70,47 @@ Animation::Channel::Channel(Animation* animation, AnimationTarget* target, int p
     _target->addChannel(this);
 }
 
+Animation::Channel::Channel(const Channel& copy, Animation* animation, AnimationTarget* target)
+    : _animation(animation), _target(target), _propertyId(copy._propertyId), _duration(copy._duration)
+{
+    _curveRef = copy._curveRef;
+    _curveRef->addRef();
+
+    _animation->addRef();
+    _target->addChannel(this);
+}
+
 Animation::Channel::~Channel()
 {
-    SAFE_DELETE(_curve);
+    SAFE_RELEASE(_curveRef);
     SAFE_RELEASE(_animation);
 }
 
+Curve* Animation::Channel::getCurve() const
+{
+    return _curveRef->getCurve();
+}
+
+Animation::CurveRef* Animation::CurveRef::create(Curve* curve)
+{
+    return new CurveRef(curve);
+}
+
+Curve* Animation::CurveRef::getCurve() const
+{
+    return _curve;
+}
+
+Animation::CurveRef::CurveRef(Curve* curve)
+    : _curve(curve)
+{
+}
+
+Animation::CurveRef::~CurveRef()
+{
+    SAFE_DELETE(_curve);
+}
+
 const char* Animation::getId() const
 {
     return _id.c_str();
@@ -372,4 +413,11 @@ void Animation::setTransformRotationOffset(Curve* curve, unsigned int propertyId
     return;
 }
 
+Animation* Animation::clone()
+{
+    Animation* animation = new Animation(getId());
+    _controller->addAnimation(animation);
+    return animation;
+}
+
 }

+ 44 - 9
gameplay/src/Animation.h

@@ -3,6 +3,7 @@
 
 #include "Ref.h"
 #include "Properties.h"
+#include "Curve.h"
 
 namespace gameplay
 {
@@ -10,7 +11,6 @@ namespace gameplay
 class AnimationTarget;
 class AnimationController;
 class AnimationClip;
-class Curve;
 
 /**
  * Defines a generic property animation.
@@ -96,6 +96,26 @@ public:
 
 private:
 
+    /**
+     * Defines a reference counted Curve wrapper.
+     * 
+     * Multiple channels can share the same Curve.
+     */
+    class CurveRef : public Ref
+    {
+    public:
+        static CurveRef* create(Curve* curve);
+        Curve* getCurve() const;
+
+    private:
+        CurveRef(Curve* curve);
+        CurveRef(const CurveRef&); // Hidden copy constructor.
+        ~CurveRef();
+        CurveRef& operator=(const CurveRef&); // Hidden copy assignment operator.
+
+        Curve* _curve;
+    };
+
     /**
      * Defines a channel which holds the target, target property, curve values, and duration.
      *
@@ -112,23 +132,21 @@ private:
     private:
 
         Channel(Animation* animation, AnimationTarget* target, int propertyId, Curve* curve, unsigned long duration);
-        Channel(const Channel& copy);
+        Channel(const Channel& copy, Animation* animation, AnimationTarget* target);
+        Channel(const Channel&); // Hidden copy constructor.
         ~Channel();
+        Channel& operator=(const Channel&); // Hidden copy assignment operator.
+        Curve* getCurve() const;
 
         Animation* _animation;                // Reference to the animation this channel belongs to.
         AnimationTarget* _target;             // The target of this channel.
         int _propertyId;                      // The target property this channel targets.
-        Curve* _curve;                        // The curve used to represent the animation data.
+        CurveRef* _curveRef;                  // The curve used to represent the animation data.
         unsigned long _duration;              // The length of the animation (in milliseconds).
     };
 
     /**
-     * Constructor.
-     */
-    Animation();
-
-    /**
-     * Constructor.
+     * Hidden copy constructor.
      */
     Animation(const Animation& copy);
 
@@ -142,11 +160,21 @@ private:
      */
     Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, unsigned int type);
 
+    /**
+     * Constructor.
+     */
+    Animation(const char* id);
+
     /**
      * Destructor.
      */
     ~Animation();
 
+    /**
+     * Hidden copy assignment operator.
+     */
+    Animation& operator=(const Animation&);
+
     /**
      * Creates the default clip.
      */
@@ -191,6 +219,13 @@ private:
      * Sets the rotation offset in a Curve representing a Transform's animation data.
      */
     void setTransformRotationOffset(Curve* curve, unsigned int propertyId);
+
+    /**
+     * Clones this animation.
+     * 
+     * @return The newly created animation.
+     */
+    Animation* clone();
     
     AnimationController* _controller;       // The AnimationController that this Animation will run on.
     std::string _id;                        // The Animation's ID.

+ 2 - 2
gameplay/src/AnimationClip.cpp

@@ -19,7 +19,7 @@ AnimationClip::AnimationClip(const char* id, Animation* animation, unsigned long
     unsigned int channelCount = _animation->_channels.size();    
     for (unsigned int i = 0; i < channelCount; i++)
     {
-        _values.push_back(new AnimationValue(_animation->_channels[i]->_curve->getComponentCount()));
+        _values.push_back(new AnimationValue(_animation->_channels[i]->getCurve()->getComponentCount()));
     }
 }
 
@@ -446,7 +446,7 @@ bool AnimationClip::update(unsigned long elapsedTime, std::list<AnimationTarget*
             activeTargets->push_front(target);
 
         // Evaluate the point on Curve
-        channel->_curve->evaluate(percentComplete, value->_value);
+        channel->getCurve()->evaluate(percentComplete, value->_value);
         // Set the animation value on the target property.
         target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight);
     }

+ 1 - 1
gameplay/src/AnimationController.cpp

@@ -229,7 +229,7 @@ Animation* AnimationController::createAnimation(const char* id, AnimationTarget*
     
     const char* keyOutStr = animationProperties->getString("keyOut");
     float* keyOut = NULL;
-    if(keyOutStr)
+    if (keyOutStr)
     {   
         keyOut = new float[components];
         startOffset = 0;

+ 28 - 0
gameplay/src/AnimationTarget.cpp

@@ -118,6 +118,34 @@ void AnimationTarget::deleteChannel(Animation::Channel* channel)
     }
 }
 
+void AnimationTarget::cloneInto(AnimationTarget* target, CloneContext &context) const
+{
+    if (_animationChannels)
+    {
+        for (std::vector<Animation::Channel*>::const_iterator it = _animationChannels->begin(); it != _animationChannels->end(); ++it)
+        {
+            Animation::Channel* channel = *it;
+            assert(channel->_animation);
+
+            bool animationCloned = false;
+
+            // Don't clone the Animaton if it is already in the CloneContext.
+            Animation* animation = context.findClonedAnimation(channel->_animation);
+            if (animation == NULL)
+            {
+                animation = channel->_animation->clone();
+                animationCloned = true;
+            }
+            assert(animation);
+
+            context.registerClonedAnimation(channel->_animation, animation);
+            
+            Animation::Channel* channelCopy = new Animation::Channel(*channel, animation, target);
+            animation->addChannel(channelCopy);
+        }
+    }
+}
+
 }
 
 

+ 21 - 1
gameplay/src/AnimationTarget.h

@@ -3,6 +3,7 @@
 
 #include "Curve.h"
 #include "AnimationController.h"
+#include "CloneContext.h"
 
 namespace gameplay
 {
@@ -44,6 +45,7 @@ public:
      * 
      * @param propertyId The ID of the property on the AnimationTarget to set the animation property value on.
      * @param value The container to set the animation property value in.
+     * @param blendWeight The blend weight.
      */
     virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f) = 0;
 
@@ -65,10 +67,28 @@ protected:
      */
     virtual ~AnimationTarget();
 
-    void addChannel(Animation::Channel* animation);
+    /**
+     * Adds the given animation channel to this animation target.
+     * 
+     * @param channel The animation channel to add.
+     */
+    void addChannel(Animation::Channel* channel);
 
+    /**
+     * Deletes the given animation channel from this animation target.
+     * 
+     * @param channel The animation channel to delete.
+     */
     void deleteChannel(Animation::Channel* channel);
 
+    /**
+     * Copies data from this animation target into the given target for the purpose of cloning.
+     * 
+     * @param target The target to copy into.
+     * @param context The clone context.
+     */
+    void cloneInto(AnimationTarget* target, CloneContext &context) const;
+
     TargetType _targetType;             // The type of target this is.
 
     char _animationPropertyBitFlag;     // Bit flag used to indicate which properties on the AnimationTarget are currently animating.

+ 12 - 12
gameplay/src/AudioBuffer.cpp

@@ -132,23 +132,23 @@ cleanup:
     return NULL;
 #else
     // Get the file header in order to determine the type.
-    AAsset* asset = AAssetManager_open(__assetManager, path, AASSET_MODE_RANDOM);
+    AAsset* asset = AAssetManager_open(__assetManager, path, AASSET_MODE_RANDOM);
     char header[12];
     if (AAsset_read(asset, header, 12) != 12)
     {
         LOG_ERROR_VARG("Invalid audio buffer file: %s", path);
         return NULL;
-    }
-
-    // Get the file descriptor for the audio file.
-    off_t start, length;
-    int fd = AAsset_openFileDescriptor(asset, &start, &length);
-    if (fd < 0)
-    {
-        LOG_ERROR_VARG("Failed to open file descriptor for asset: %s", path);
-        return NULL;
-    }
-    AAsset_close(asset);
+    }
+
+    // Get the file descriptor for the audio file.
+    off_t start, length;
+    int fd = AAsset_openFileDescriptor(asset, &start, &length);
+    if (fd < 0)
+    {
+        LOG_ERROR_VARG("Failed to open file descriptor for asset: %s", path);
+        return NULL;
+    }
+    AAsset_close(asset);
     SLDataLocator_AndroidFD data = {SL_DATALOCATOR_ANDROIDFD, fd, start, length};
 
     // Set the appropriate mime type information.

+ 1 - 1
gameplay/src/AudioController.cpp

@@ -170,7 +170,7 @@ void AudioController::update(long elapsedTime)
     {
 #ifndef __ANDROID__
         alListenerf(AL_GAIN, listener->getGain());
-        alListenerfv(AL_ORIENTATION, (ALfloat*)&listener->getOrientationForward());
+        alListenerfv(AL_ORIENTATION, (ALfloat*)listener->getOrientation());
         alListenerfv(AL_VELOCITY, (ALfloat*)&listener->getVelocity());
         alListenerfv(AL_POSITION, (ALfloat*)&listener->getPosition());
 #else

+ 1 - 1
gameplay/src/AudioController.h

@@ -58,7 +58,7 @@ private:
     ALCdevice* _alcDevice;
     ALCcontext* _alcContext;
 #else
-    SLObjectItf _engineObject;
+    SLObjectItf _engineObject;
     SLEngineItf _engineEngine;
     SLObjectItf _outputMixObject;
     SLObjectItf _listenerObject;

+ 13 - 2
gameplay/src/AudioListener.cpp

@@ -21,6 +21,8 @@ AudioListener::~AudioListener()
 
 AudioListener* AudioListener::getInstance()
 {
+    if (!__audioListenerInstance)
+        new AudioListener();
     return __audioListenerInstance;
 }
 
@@ -54,6 +56,11 @@ void AudioListener::setVelocity(const Vector3& velocity)
     _velocity = velocity;
 }
 
+const float* AudioListener::getOrientation() const
+{
+    return (const float*)&_orientation[0];
+}
+
 const Vector3& AudioListener::getOrientationForward() const 
 { 
     return _orientation[0]; 
@@ -106,8 +113,12 @@ void AudioListener::transformChanged(Transform* transform, long cookie)
 {
     if (transform)
     {
-        setPosition(transform->getTranslation());
-        setOrientation(transform->getForwardVector(), transform->getUpVector());
+        Node* node = static_cast<Node*>(transform);
+        setPosition(node->getTranslationWorld());
+        
+        Vector3 up;
+        node->getWorldMatrix().getUpVector(&up);
+        setOrientation(node->getForwardVectorWorld(), up);
     }
 }
 

+ 8 - 0
gameplay/src/AudioListener.h

@@ -67,6 +67,14 @@ public:
      */
     void setVelocity(const Vector3& velocity);
 
+    /**
+     * Gets the float pointer to the orientation of the audio listener.
+     * Orientation is represented as 6 floats. (forward.x, forward.y, forward.z, up.x, up.y, up.z).
+     * 
+     * @return Pointer to the 6 orientation float values.
+     */
+    const float* getOrientation() const;
+
     /**
      * Gets the forward orientation vector of the audio listener.
      *

+ 43 - 8
gameplay/src/AudioSource.cpp

@@ -26,37 +26,37 @@ AudioSource::AudioSource(AudioBuffer* buffer, const SLObjectItf& player)
 {
     // Get the different interfaces for the OpenSL audio player that we need.
     SLresult result = (*_playerObject)->GetInterface(_playerObject, SL_IID_3DDOPPLER, &_playerDoppler);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::AudioSource() - Failed to get 3D doppler interface for OpenSL audio player.");
     }
     
     result = (*_playerObject)->GetInterface(_playerObject, SL_IID_3DLOCATION, &_playerLocation);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::AudioSource() - Failed to get 3D location interface for OpenSL audio player.");
     }
 
     result = (*_playerObject)->GetInterface(_playerObject, SL_IID_PLAY, &_playerPlay);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::AudioSource() - Failed to get play interface for OpenSL audio player.");
     }
 
     result = (*_playerObject)->GetInterface(_playerObject, SL_IID_PITCH, &_playerPitch);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::AudioSource() - Failed to get rate pitch interface for OpenSL audio player.");
     }
 
     result = (*_playerObject)->GetInterface(_playerObject, SL_IID_SEEK, &_playerSeek);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::AudioSource() - Failed to get seek interface for OpenSL audio player.");
     }
 
     result = (*_playerObject)->GetInterface(_playerObject, SL_IID_VOLUME, &_playerVolume);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::AudioSource() - Failed to get volume interface for OpenSL audio player.");
     }
@@ -159,7 +159,7 @@ AudioSource* AudioSource::create(const char* path)
     }
 
     result = (*player)->Realize(player, SL_BOOLEAN_FALSE);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::create - Failed to realize OpenSL audio player.");
     }
@@ -454,6 +454,8 @@ void AudioSource::setNode(Node* node)
         if (_node)
         {
             _node->addListener(this);
+            // Update the audio source position.
+            transformChanged(_node, 0);
         }
     }
 }
@@ -461,7 +463,8 @@ void AudioSource::setNode(Node* node)
 void AudioSource::transformChanged(Transform* transform, long cookie)
 {
 #ifndef __ANDROID__
-    alSourcefv(_alSource, AL_POSITION, (const ALfloat*)&transform->getTranslation());
+    if (_node)
+        alSourcefv(_alSource, AL_POSITION, (const ALfloat*)&_node->getTranslationWorld());
 #else
     if (_playerLocation)
     {
@@ -478,4 +481,36 @@ void AudioSource::transformChanged(Transform* transform, long cookie)
 #endif
 }
 
+AudioSource* AudioSource::clone(CloneContext &context) const
+{
+#ifndef __ANDROID__
+    ALuint alSource = 0;
+    alGenSources(1, &alSource);
+    if (alGetError() != AL_NO_ERROR)
+    {
+        LOG_ERROR("AudioSource::createAudioSource - Error generating audio source.");
+        return NULL;
+    }
+    AudioSource* audioClone = new AudioSource(_buffer, alSource);
+#else
+    // TODO: Implement cloning audio source for Android
+    AudioSource* audioClone = new AudioSource(AudioBuffer* buffer, const SLObjectItf& player);
+
+#endif
+
+    audioClone->setLooped(isLooped());
+    audioClone->setGain(getGain());
+    audioClone->setPitch(getPitch());
+    audioClone->setVelocity(getVelocity());
+    if (Node* node = audioClone->getNode())
+    {
+        Node* clonedNode = context.findClonedNode(node);
+        if (clonedNode)
+        {
+            audioClone->setNode(clonedNode);
+        }
+    }
+    return audioClone;
+}
+
 }

+ 15 - 6
gameplay/src/AudioSource.h

@@ -176,15 +176,24 @@ private:
      */
     void transformChanged(Transform* transform, long cookie);
 
+    /**
+     * Clones the audio source and returns a new audio source.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created audio source.
+     */
+    AudioSource* clone(CloneContext &context) const;
+
 #ifndef __ANDROID__
     ALuint _alSource;
 #else
-    SLObjectItf _playerObject;
-    SL3DDopplerItf _playerDoppler;
-    SL3DLocationItf _playerLocation;
-    SLPlayItf _playerPlay;
-    SLPitchItf _playerPitch;
-    SLSeekItf _playerSeek;
+    SLObjectItf _playerObject;
+    SL3DDopplerItf _playerDoppler;
+    SL3DLocationItf _playerLocation;
+    SLPlayItf _playerPlay;
+    SLPitchItf _playerPitch;
+    SLSeekItf _playerSeek;
     SLVolumeItf _playerVolume;
     SLmillibel _maxVolume;
 #endif

+ 20 - 0
gameplay/src/Camera.cpp

@@ -343,6 +343,26 @@ void Camera::pickRay(const Viewport* viewport, float x, float y, Ray* dst)
     dst->set(nearPoint, direction);
 }
 
+Camera* Camera::clone(CloneContext &context) const
+{
+    Camera* cameraClone = NULL;
+    if (getCameraType() == PERSPECTIVE)
+    {
+        cameraClone = createPerspective(_fieldOfView, _aspectRatio, _nearPlane, _farPlane);
+    }
+    else if (getCameraType() == ORTHOGRAPHIC)
+    {
+        cameraClone = createOrthographic(getZoomX(), getZoomY(), getAspectRatio(), _nearPlane, _farPlane);
+    }
+    assert(cameraClone);
+
+    if (Node* node = context.findClonedNode(getNode()))
+    {
+        cameraClone->setNode(node);
+    }
+    return cameraClone;
+}
+
 void Camera::transformChanged(Transform* transform, long cookie)
 {
     _dirtyBits |= CAMERA_DIRTY_VIEW | CAMERA_DIRTY_INV_VIEW | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;

+ 9 - 0
gameplay/src/Camera.h

@@ -245,6 +245,15 @@ private:
      */
     virtual ~Camera();
 
+    /**
+     * Clones the camera and returns a new camera.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created camera.
+     */
+    Camera* clone(CloneContext &context) const;
+
     /**
      * @see Transform::Listener::transformChanged
      */

+ 38 - 0
gameplay/src/CloneContext.cpp

@@ -0,0 +1,38 @@
+#include "CloneContext.h"
+
+namespace gameplay
+{
+
+CloneContext::CloneContext()
+{
+    
+}
+
+CloneContext::~CloneContext()
+{
+
+}
+
+Animation* CloneContext::findClonedAnimation(const Animation* animation)
+{
+    AnimationMap::iterator it = _clonedAnimations.find(animation);
+    return it != _clonedAnimations.end() ? it->second : NULL;
+}
+
+void CloneContext::registerClonedAnimation(const Animation* original, Animation* clone)
+{
+    _clonedAnimations[original] = clone;
+}
+
+Node* CloneContext::findClonedNode(const Node* node)
+{
+    NodeMap::iterator it = _clonedNodes.find(node);
+    return it != _clonedNodes.end() ? it->second : NULL;
+}
+
+void CloneContext::registerClonedNode(const Node* original, Node* clone)
+{
+    _clonedNodes[original] = clone;
+}
+
+}

+ 86 - 0
gameplay/src/CloneContext.h

@@ -0,0 +1,86 @@
+#ifndef CLONECONTEXT_H_
+#define CLONECONTEXT_H_
+
+#include <map>
+
+namespace gameplay
+{
+    class Animation;
+    class Node;
+
+/**
+ * CloneContext represents the context data that is kept when cloning a node.
+ * 
+ * The CloneContext is used to make sure objects don't get cloned twice.
+ */
+class CloneContext
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    CloneContext();
+
+    /**
+     * Destructor.
+     */
+    ~CloneContext();
+
+    /**
+     * Finds the cloned animation of the given animation or NULL if this animation was not registered with this context.
+     * 
+     * @param animation The animation to search for the cloned copy of.
+     * 
+     * @return The cloned animation or NULL if not found.
+     */
+    Animation* findClonedAnimation(const Animation* animation);
+
+    /**
+     * Registers the cloned animation with this context so that it doesn't get cloned twice.
+     * 
+     * @param original The pointer to the original animation.
+     * @param clone The pointer to the cloned animation.
+     */
+    void registerClonedAnimation(const Animation* original, Animation* clone);
+
+    /**
+     * Finds the cloned node of the given node or NULL if this node was not registered with this context.
+     * 
+     * @param node The node to search for the cloned copy of.
+     * 
+     * @return The cloned node or NULL if not found.
+     */
+    Node* findClonedNode(const Node* node);
+
+    /**
+     * Registers the cloned node with this context so that it doens't get cloned twice.
+     * 
+     * @param original The pointer to the original node.
+     * @param clone The pointer to the cloned node.
+     */
+    void registerClonedNode(const Node* original, Node* clone);
+
+private:
+    
+    /**
+     * Hidden copy constructor.
+     */
+    CloneContext(const CloneContext&);
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    CloneContext& operator=(const CloneContext&);
+
+private:
+    typedef std::map<const Animation*, Animation*> AnimationMap;
+    typedef std::map<const Node*, Node*> NodeMap;
+
+    AnimationMap _clonedAnimations;
+    NodeMap _clonedNodes;
+};
+
+}
+
+#endif

+ 1 - 1
gameplay/src/Container.cpp

@@ -34,7 +34,7 @@ namespace gameplay
     Container* Container::create(Layout::Type type)
     {
         Layout* layout = NULL;
-        switch(type)
+        switch (type)
         {
         case Layout::LAYOUT_ABSOLUTE:
             layout = AbsoluteLayout::create();

+ 1 - 1
gameplay/src/FileSystem.cpp

@@ -110,7 +110,7 @@ bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
             filename.assign(wfilename.begin(), wfilename.end());
             files.push_back(filename);
         }
-    } while(FindNextFile(hFind, &FindFileData) != 0);
+    } while (FindNextFile(hFind, &FindFileData) != 0);
 
     FindClose(hFind);
     return true;

+ 8 - 4
gameplay/src/Font.h

@@ -157,8 +157,8 @@ public:
      *
      * @param text The text to measure.
      * @param size
-     * @param width Destination for the text's width.
-     * @param height Destination for the text's height.
+     * @param widthOut Destination for the text's width.
+     * @param heightOut Destination for the text's height.
      */
     void measureText(const char* text, unsigned int size, unsigned int* widthOut, unsigned int* heightOut);
 
@@ -177,11 +177,15 @@ public:
     void measureText(const char* text, const Rectangle& clip, unsigned int size, Rectangle* out,
                      Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool ignoreClip = false);
 
-    // Get an index into a string corresponding to the character nearest the given location within the clip region.
+    /**
+     * Get an index into a string corresponding to the character nearest the given location within the clip region.
+     */
     unsigned int getIndexAtLocation(const char* text, const Rectangle& clip, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
                                     Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
 
-    // Get the location of the character at the given index.
+    /**
+     * Get the location of the character at the given index.
+     */
     void getLocationAtIndex(const char* text, const Rectangle& clip, unsigned int size, Vector2* outLocation, const unsigned int destIndex,
                             Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
 

+ 1 - 1
gameplay/src/Form.cpp

@@ -78,7 +78,7 @@ namespace gameplay
     Form* Form::create(const char* themeFile, Layout::Type type)
     {
         Layout* layout;
-        switch(type)
+        switch (type)
         {
         case Layout::LAYOUT_ABSOLUTE:
             layout = AbsoluteLayout::create();

+ 10 - 0
gameplay/src/Joint.cpp

@@ -19,6 +19,16 @@ Joint* Joint::create(const char* id)
     return new Joint(id);
 }
 
+Node* Joint::cloneSingleNode(CloneContext &context) const
+{
+    Joint* copy = Joint::create(getId());
+    context.registerClonedNode(this, copy);
+    copy->_bindPose = _bindPose;
+    copy->_skinCount = _skinCount;
+    Node::cloneInto(copy, context);
+    return copy;
+}
+
 Node::Type Joint::getType() const
 {
     return Node::JOINT;

+ 24 - 0
gameplay/src/Joint.h

@@ -53,6 +53,16 @@ protected:
      */
     static Joint* create(const char* id);
 
+    /**
+     * Clones a single node and its data but not its children.
+     * This method returns a node pointer but actually creates a Joint.
+     * 
+     * @param context The clone context.
+     * 
+     * @return Pointer to the newly created joint.
+     */
+    virtual Node* cloneSingleNode(CloneContext &context) const;
+
     /**
      * Sets the inverse bind pose matrix.
      * 
@@ -64,6 +74,20 @@ protected:
 
     void transformChanged();
 
+private:
+
+    /**
+     * Hidden copy constructor.
+     */
+    Joint(const Joint& copy);
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    Joint& operator=(const Joint&);
+
+protected:
+
     Matrix _bindPose;
     bool _jointMatrixDirty;
     unsigned int _skinCount;

+ 26 - 0
gameplay/src/Light.cpp

@@ -196,6 +196,32 @@ float Light::getOuterAngleCos()  const
     return _spot->outerAngleCos;
 }
 
+Light* Light::clone(CloneContext &context) const
+{
+    Light* lightClone = NULL;
+    switch (_type)
+    {
+    case DIRECTIONAL:
+        lightClone = createDirectional(getColor());
+        break;
+    case POINT:
+        lightClone = createPoint(getColor(), getRange());
+        break;
+    case SPOT:
+        lightClone = createSpot(getColor(), getRange(), getInnerAngle(), getOuterAngle());
+        break;
+    default:
+        assert(false);
+    }
+    assert(lightClone);
+
+    if (Node* node = context.findClonedNode(getNode()))
+    {
+        lightClone->setNode(node);
+    }
+    return lightClone;
+}
+
 Light::Directional::Directional(const Vector3& color)
     : color(color)
 {

+ 15 - 5
gameplay/src/Light.h

@@ -3,6 +3,7 @@
 
 #include "Ref.h"
 #include "Vector3.h"
+#include "CloneContext.h"
 
 namespace gameplay
 {
@@ -54,8 +55,8 @@ public:
      * 
      * @param color The light's color.
      * @param range The light's range.
-     * @param innerCosAngle The light's inner angle (in radians).
-     * @param outerCosAngle The light's outer angle (in radians).
+     * @param innerAngle The light's inner angle (in radians).
+     * @param outerAngle The light's outer angle (in radians).
      * 
      * @return The new spot light.
      */
@@ -104,7 +105,7 @@ public:
     /**
      * Sets the range of point or spot light.
      *
-     * @param range of point or spot light.
+     * @param range The range of point or spot light.
      */
     void setRange(float range);
 
@@ -125,7 +126,7 @@ public:
     /**
      * Sets the inner angle of a spot light (in radians).
      *
-     * @param inner angle of spot light (in radians).
+     * @param innerAngle The angle of spot light (in radians).
      */
     void setInnerAngle(float innerAngle);
 
@@ -139,7 +140,7 @@ public:
     /**
      * Sets the outer angle of a spot light (in radians).
      *
-     * @param outer angle of spot light (in radians).
+     * @param outerAngle The angle of spot light (in radians).
      */
     void setOuterAngle(float outerAngle);
 
@@ -223,6 +224,15 @@ private:
      */
     void setNode(Node* node);
 
+    /**
+     * Clones the light and returns a new light.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created light.
+     */
+    Light* clone(CloneContext &context) const;
+
     Light::Type _type;
     union
     {

+ 18 - 0
gameplay/src/Material.cpp

@@ -125,6 +125,24 @@ Material* Material::create(const char* vshPath, const char* fshPath, const char*
     return material;
 }
 
+Material* Material::clone(CloneContext &context) const
+{
+    Material* material = new Material();
+    RenderState::cloneInto(material, context);
+
+    for (std::vector<Technique*>::const_iterator it = _techniques.begin(); it != _techniques.end(); ++it)
+    {
+        const Technique* technique = *it;
+        Technique* techniqueClone = technique->clone(material, context);
+        material->_techniques.push_back(techniqueClone);
+        if (_currentTechnique == technique)
+        {
+            material->_currentTechnique = techniqueClone;
+        }
+    }
+    return material;
+}
+
 unsigned int Material::getTechniqueCount() const
 {
     return _techniques.size();

+ 9 - 0
gameplay/src/Material.h

@@ -68,6 +68,15 @@ public:
      */
     static Material* create(const char* vshPath, const char* fshPath, const char* defines = NULL);
 
+    /**
+     * Clones this material.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created material.
+     */
+    Material* clone(CloneContext &context) const;
+
     /**
      * Returns the number of techniques in the material.
      *

+ 80 - 2
gameplay/src/MaterialParameter.cpp

@@ -32,7 +32,7 @@ void MaterialParameter::clearValue()
             SAFE_DELETE_ARRAY(_value.intPtrValue);
             break;
         case MaterialParameter::METHOD:
-            SAFE_DELETE(_value.method);
+            SAFE_RELEASE(_value.method);
             break;
         }
 
@@ -294,7 +294,7 @@ unsigned int MaterialParameter::getAnimationPropertyComponentCount(int propertyI
     {
         case ANIMATE_UNIFORM:
         {
-            switch(_type)
+            switch (_type)
             {
                 // These types don't support animation.
                 case NONE:
@@ -478,4 +478,82 @@ void MaterialParameter::applyAnimationValue(AnimationValue* value, float blendWe
     }
 }
 
+void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const
+{
+    materialParameter->_type = _type;
+    materialParameter->_count = _count;
+    materialParameter->_dynamic = _dynamic;
+    materialParameter->_uniform = _uniform;
+    switch (_type)
+    {
+    case NONE:
+        break;
+    case FLOAT:
+        materialParameter->setValue(_value.floatValue);
+        break;
+    case INT:
+        materialParameter->setValue(_value.intValue);
+        break;
+    case VECTOR2:
+    {
+        Vector2* value = reinterpret_cast<Vector2*>(_value.floatPtrValue);
+        if (_count == 1)
+        {
+            materialParameter->setValue(*value);
+        }
+        else
+        {
+            materialParameter->setValue(value, _count);
+        }
+        break;
+    }   
+    case VECTOR3:
+    {
+        Vector3* value = reinterpret_cast<Vector3*>(_value.floatPtrValue);
+        if (_count == 1)
+        {
+            materialParameter->setValue(*value);
+        }
+        else
+        {
+            materialParameter->setValue(value, _count);
+        }
+        break;
+    }
+    case VECTOR4:
+    {
+        Vector4* value = reinterpret_cast<Vector4*>(_value.floatPtrValue);
+        if (_count == 1)
+        {
+            materialParameter->setValue(*value);
+        }
+        else
+        {
+            materialParameter->setValue(value, _count);
+        }
+        break;
+    }
+    case MATRIX:
+    {
+        Matrix* value = reinterpret_cast<Matrix*>(_value.floatPtrValue);
+        if (_count == 1)
+        {
+            materialParameter->setValue(*value);
+        }
+        else
+        {
+            materialParameter->setValue(value, _count);
+        }
+        break;
+    }
+    case SAMPLER:
+        materialParameter->setValue(_value.samplerValue);
+        break;
+    case METHOD:
+        materialParameter->_value.method = _value.method;
+        materialParameter->_value.method->addRef();
+        break;
+    }
+}
+
 }

+ 11 - 2
gameplay/src/MaterialParameter.h

@@ -171,14 +171,16 @@ private:
     /**
      * Interface implemented by templated method bindings for simple storage and iteration.
      */
-    class MethodBinding
+    class MethodBinding : public Ref
     {
     public:
+        virtual void setValue(Effect* effect) = 0;
+
+    protected:
         /**
          * Destructor.
          */
         virtual ~MethodBinding() { }
-        virtual void setValue(Effect* effect) = 0;
     };
 
     /**
@@ -257,6 +259,13 @@ private:
 
     void applyAnimationValue(AnimationValue* value, float blendWeight, int components);
 
+    /**
+     * Copies the data from this MaterialParameter into the given MaterialParameter.
+     * 
+     * @param materialParameter The MaterialParameter to copy the data to.
+     */
+    void cloneInto(MaterialParameter* materialParameter) const;
+
     unsigned int _count;
     bool _dynamic;
     std::string _name;

+ 1 - 1
gameplay/src/MeshBatch.h

@@ -54,7 +54,7 @@ public:
     /**
      * Explicitly sets a new capacity for the batch.
      *
-     * @param The new batch capacity.
+     * @param capacity The new batch capacity.
      */
     void setCapacity(unsigned int capacity);
 

+ 60 - 1
gameplay/src/MeshSkin.cpp

@@ -9,7 +9,7 @@ namespace gameplay
 {
 
 MeshSkin::MeshSkin()
-    : _rootJoint(NULL), _matrixPalette(NULL), _model(NULL)
+    : _rootJoint(NULL), _rootNode(NULL), _matrixPalette(NULL), _model(NULL)
 {
 }
 
@@ -57,6 +57,37 @@ Joint* MeshSkin::getJoint(const char* id) const
     return NULL;
 }
 
+MeshSkin* MeshSkin::clone() const
+{
+    MeshSkin* skin = new MeshSkin();
+    skin->_bindShape = _bindShape;
+    if (_rootNode && _rootJoint)
+    {
+        const unsigned int jointCount = getJointCount();
+        skin->setJointCount(jointCount);
+
+        assert(skin->_rootNode == NULL);
+        skin->_rootNode = _rootNode->clone();
+        Node* node = skin->_rootNode->findNode(_rootJoint->getId());
+        assert(node);
+        skin->_rootJoint = static_cast<Joint*>(node);
+        for (unsigned int i = 0; i < jointCount; ++i)
+        {
+            Joint* oldJoint = getJoint(i);
+            
+            Joint* newJoint = static_cast<Joint*>(skin->_rootJoint->findNode(oldJoint->getId()));
+            if (!newJoint)
+            {
+                if (strcmp(skin->_rootJoint->getId(), oldJoint->getId()) == 0)
+                    newJoint = static_cast<Joint*>(skin->_rootJoint);
+            }
+            assert(newJoint);
+            skin->setJoint(newJoint, i);
+        }
+    }
+    return skin;
+}
+
 void MeshSkin::setJointCount(unsigned int jointCount)
 {
     // Erase the joints vector and release all joints
@@ -145,6 +176,21 @@ void MeshSkin::setRootJoint(Joint* joint)
     {
         _rootJoint->getParent()->addListener(this, 1);
     }
+
+    Node* newRootNode = _rootJoint;
+    if (newRootNode)
+    {
+        // Find the top level parent node of the root joint
+        for (Node* node = newRootNode->getParent(); node != NULL; node = node->getParent())
+        {
+            if (node->getParent() == NULL)
+            {
+                newRootNode = node;
+                break;
+            }
+        }
+    }
+    setRootNode(newRootNode);
 }
 
 void MeshSkin::transformChanged(Transform* transform, long cookie)
@@ -179,6 +225,19 @@ int MeshSkin::getJointIndex(Joint* joint) const
     return -1;
 }
 
+void MeshSkin::setRootNode(Node* node)
+{
+    if (_rootNode != node)
+    {
+        SAFE_RELEASE(_rootNode);
+        _rootNode = node;
+        if (_rootNode)
+        {
+            _rootNode->addRef();
+        }
+    }
+}
+
 void MeshSkin::clearJoints()
 {
     setRootJoint(NULL);

+ 29 - 1
gameplay/src/MeshSkin.h

@@ -2,7 +2,7 @@
 #define MESHSKIN_H_
 
 #include "Matrix.h"
-#include "Transform.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -115,10 +115,27 @@ private:
      */
     MeshSkin();
 
+    /**
+     * Hidden copy constructor.
+     */
+    MeshSkin(const MeshSkin&);
+
     /**
      * Destructor.
      */
     ~MeshSkin();
+    
+    /**
+     * Hidden copy assignment operator.
+     */
+    MeshSkin& operator=(const MeshSkin&);
+
+    /**
+     * Clones the MeshSkin and the joints that it references.
+     * 
+     * @return The newly created MeshSkin.
+     */
+    MeshSkin* clone() const;
 
     /**
      * Sets the number of joints that can be stored in this skin.
@@ -136,6 +153,13 @@ private:
      */
     void setJoint(Joint* joint, unsigned int index);
 
+    /**
+     * Sets the root node of this mesh skin.
+     * 
+     * @param node The node to set as the root node, may be NULL.
+     */
+    void setRootNode(Node* node);
+
     /**
      * Clears the list of joints and releases each joint.
      */
@@ -144,6 +168,10 @@ private:
     Matrix _bindShape;
     std::vector<Joint*> _joints;
     Joint* _rootJoint;
+    // Pointer to the root node of the mesh skin.
+    // The purpose is so that the joint hierarchy doesn't need to be in the scene.
+    // If the joints are not in the scene then something has to hold a reference to it.
+    Node* _rootNode;
 
     // Pointer to the array of palette matrices.
     // This array is passed to the vertex shader as a uniform.

+ 16 - 2
gameplay/src/Model.cpp

@@ -195,7 +195,7 @@ bool Model::hasMaterial(unsigned int partIndex) const
     return (partIndex < _partCount && _partMaterials && _partMaterials[partIndex]);
 }
 
-MeshSkin* Model::getSkin()
+MeshSkin* Model::getSkin() const
 {
     return _skin;
 }
@@ -209,7 +209,8 @@ void Model::setSkin(MeshSkin* skin)
 
         // Assign the new skin
         _skin = skin;
-        _skin->_model = this;
+        if (_skin)
+            _skin->_model = this;
     }
 }
 
@@ -357,6 +358,19 @@ void Model::validatePartCount()
     }
 }
 
+Model* Model::clone(CloneContext &context)
+{
+    Model* model = Model::create(getMesh());
+    if (getSkin())
+    {
+        model->setSkin(getSkin()->clone());
+    }
+    Material* materialClone = getMaterial()->clone(context);
+    model->setMaterial(materialClone); // TODO: Don't forget material parts
+    materialClone->release();
+    return model;
+}
+
 void Model::setMaterialNodeBinding(Material *material)
 {
     if (_node)

+ 10 - 1
gameplay/src/Model.h

@@ -123,7 +123,7 @@ public:
      * 
      * @return The MeshSkin, or NULL if one is not set.
      */
-    MeshSkin* getSkin();
+    MeshSkin* getSkin() const;
 
     /**
      * Returns the node that is associated with this model.
@@ -177,6 +177,15 @@ private:
 
     void validatePartCount();
 
+    /**
+     * Clones the model and returns a new model.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The new cloned model.
+     */
+    Model* clone(CloneContext &context);
+
     Mesh* _mesh;
     Material* _material;
     unsigned int _partCount;

+ 67 - 7
gameplay/src/Node.cpp

@@ -2,6 +2,7 @@
 #include "Node.h"
 #include "Scene.h"
 #include "Joint.h"
+#include "Game.h"
 
 #define NODE_DIRTY_WORLD 1
 #define NODE_DIRTY_BOUNDS 2
@@ -21,11 +22,6 @@ Node::Node(const char* id)
     }
 }
 
-Node::Node(const Node& node)
-{
-    // hidden
-}
-
 Node::~Node()
 {
     removeAllChildren();
@@ -202,7 +198,7 @@ unsigned int Node::getChildCount() const
     return _childCount;
 }
 
-Node* Node::findNode(const char* id, bool recursive, bool exactMatch)
+Node* Node::findNode(const char* id, bool recursive, bool exactMatch) const
 {
     assert(id);
     
@@ -232,7 +228,7 @@ Node* Node::findNode(const char* id, bool recursive, bool exactMatch)
     return NULL;
 }   
 
-unsigned int Node::findNodes(const char* id, std::vector<Node*>& nodes, bool recursive, bool exactMatch)
+unsigned int Node::findNodes(const char* id, std::vector<Node*>& nodes, bool recursive, bool exactMatch) const
 {
     assert(id);
     
@@ -703,6 +699,70 @@ const BoundingSphere& Node::getBoundingSphere() const
     return _bounds;
 }
 
+
+Node* Node::clone() const
+{
+    CloneContext context;
+    return cloneRecursive(context);
+}
+
+Node* Node::cloneSingleNode(CloneContext &context) const
+{
+    Node* copy = Node::create(getId());
+    context.registerClonedNode(this, copy);
+    cloneInto(copy, context);
+    return copy;
+}
+
+Node* Node::cloneRecursive(CloneContext &context) const
+{
+    Node* copy = cloneSingleNode(context);
+
+    for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
+    {
+        Node* childCopy = child->cloneRecursive(context);
+        copy->addChild(childCopy); // TODO: Does child order matter?
+        childCopy->release();
+    }
+    return copy;
+}
+
+void Node::cloneInto(Node* node, CloneContext &context) const
+{
+    Transform::cloneInto(node, context);
+
+    // TODO: Clone the rest of the node data.
+    //node->setCamera(getCamera());
+    //node->setLight(getLight());
+
+    if (Camera* camera = getCamera())
+    {
+        Camera* cameraClone = camera->clone(context);
+        node->setCamera(cameraClone);
+        cameraClone->release();
+    }
+    if (Light* light = getLight())
+    {
+        Light* lightClone = lightClone = light->clone(context);
+        node->setLight(lightClone);
+        lightClone->release();
+    }
+    if (AudioSource* audio = getAudioSource())
+    {
+        AudioSource* audioClone = audio->clone(context);
+        node->setAudioSource(audioClone);
+        audioClone->release();
+    }
+    if (Model* model = getModel())
+    {
+        Model* modelClone = model->clone(context);
+        node->setModel(modelClone);
+        modelClone->release();
+    }
+    node->_world = _world;
+    node->_bounds = _bounds;
+}
+
 AudioSource* Node::getAudioSource() const
 {
     return _audioSource;

+ 55 - 10
gameplay/src/Node.h

@@ -122,18 +122,18 @@ public:
     /**
      * Returns the first child node that matches the given ID.
      *
-     * This method checks the specified ID against its own ID, as well as its 
-     * immediate children nodes. If recursive is true, it also traverses the
-     * Node's hierarchy.
+     * This method checks the specified ID against its immediate child nodes 
+     * but does not check the ID against itself.
+     * If recursive is true, it also traverses the Node's hierarchy with a breadth first search.
      *
      * @param id The ID of the child to find.
-     * @param recursive true to search recursively all the node's children, false for only direct children.
+     * @param recursive True to search recursively all the node's children, false for only direct children.
      * @param exactMatch true if only nodes whose ID exactly matches the specified ID are returned,
      *        or false if nodes that start with the given ID are returned.
      * 
      * @return The Node found or NULL if not found.
      */
-    Node* findNode(const char* id, bool recursive = true, bool exactMatch = true);
+    Node* findNode(const char* id, bool recursive = true, bool exactMatch = true) const;
 
     /**
      * Returns all child nodes that match the given ID.
@@ -146,7 +146,7 @@ public:
      * 
      * @return The number of matches found.
      */
-    unsigned int findNodes(const char* id, std::vector<Node*>& nodes, bool recursive = true, bool exactMatch = true);
+    unsigned int findNodes(const char* id, std::vector<Node*>& nodes, bool recursive = true, bool exactMatch = true) const;
 
     /**
      * Gets the scene.
@@ -443,6 +443,13 @@ public:
      */
     const BoundingSphere& getBoundingSphere() const;
 
+    /**
+     * Clones the node and all of its child nodes.
+     * 
+     * @return A new node.
+     */
+    Node* clone() const;
+
 protected:
 
     /**
@@ -451,14 +458,35 @@ protected:
     Node(const char* id);
 
     /**
-     * Copy constructor.
+     * Destructor.
      */
-    Node(const Node& copy);
+    virtual ~Node();
 
     /**
-     * Destructor.
+     * Clones a single node and its data but not its children.
+     * 
+     * @param context The clone context.
+     * 
+     * @return Pointer to the newly created node.
      */
-    virtual ~Node();
+    virtual Node* cloneSingleNode(CloneContext &context) const;
+
+    /**
+     * Recursively clones this node and its children.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created node.
+     */
+    Node* cloneRecursive(CloneContext &context) const;
+
+    /**
+     * Copies the data from this node into the given node.
+     * 
+     * @param node The node to copy the data to.
+     * @param context The clone context.
+     */
+    void cloneInto(Node* node, CloneContext &context) const;
 
     /**
      * Removes this node from its parent.
@@ -470,6 +498,9 @@ protected:
      */
     void transformChanged();
 
+    /**
+     * Called when this Node's hierarchy changes.
+     */
     void hierarchyChanged();
 
     /**
@@ -477,6 +508,20 @@ protected:
      */
     void setBoundsDirty();
 
+private:
+
+    /**
+     * Hidden copy constructor.
+     */
+    Node(const Node& copy);
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    Node& operator=(const Node&);
+
+protected:
+
     Scene* _scene;
     std::string _id;
     Node* _firstChild;

+ 3 - 3
gameplay/src/ParticleEmitter.h

@@ -432,8 +432,8 @@ public:
      * Gets the maximum rotation speed of each emitted particle.
      * This determines the speed of rotation of each particle's screen-facing billboard.
      *
-     * @param min The minimum rotation speed (per particle).
-     * @param max The maximum rotation speed (per particle).
+     * @param speedMin The minimum rotation speed (per particle).
+     * @param speedMax The maximum rotation speed (per particle).
      */
     void setRotationPerParticle(float speedMin, float speedMax);
 
@@ -494,7 +494,7 @@ public:
     /**
      * Sets whether particles cycle through the sprite frames.
      *
-     * @param animating Whether to animate particles through the sprite frames.
+     * @param animated Whether to animate particles through the sprite frames.
      */
     void setSpriteAnimated(bool animated);
 

+ 9 - 4
gameplay/src/Pass.cpp

@@ -14,10 +14,6 @@ Pass::Pass(const char* id, Technique* technique, Effect* effect) :
     RenderState::_parent = _technique;
 }
 
-Pass::Pass(const Pass& copy)
-{
-}
-
 Pass::~Pass()
 {
     SAFE_RELEASE(_effect);
@@ -83,4 +79,13 @@ void Pass::unbind()
     }
 }
 
+Pass* Pass::clone(Technique* technique, CloneContext &context) const
+{
+    Effect* effect = getEffect();
+    effect->addRef();
+    Pass* pass = new Pass(getId(), technique, effect);
+    RenderState::cloneInto(pass, context);
+    return pass;
+}
+
 }

+ 15 - 0
gameplay/src/Pass.h

@@ -87,6 +87,21 @@ private:
      */
     static Pass* create(const char* id, Technique* technique, const char* vshPath, const char* fshPath, const char* defines);
 
+    /**
+     * Hidden copy assignment operator.
+     */
+    Pass& operator=(const Pass&);
+
+    /**
+     * Clones the Pass and assigns it the given Technique.
+     * 
+     * @param technique The technique to assign to the new Pass.
+     * @param context The clone context.
+     * 
+     * @return The newly created Pass.
+     */
+    Pass* clone(Technique* technique, CloneContext &context) const;
+
     std::string _id;
     Technique* _technique;
     Effect* _effect;

+ 26 - 4
gameplay/src/RenderState.cpp

@@ -21,10 +21,6 @@ RenderState::RenderState()
 {
 }
 
-RenderState::RenderState(const RenderState& copy)
-{
-}
-
 RenderState::~RenderState()
 {
     SAFE_RELEASE(_state);
@@ -311,6 +307,32 @@ RenderState* RenderState::getTopmost(RenderState* below)
     return NULL;
 }
 
+void RenderState::cloneInto(RenderState* renderState, CloneContext& context) const
+{
+    for (std::map<std::string, AutoBinding>::const_iterator it = _autoBindings.begin(); it != _autoBindings.end(); ++it)
+    {
+        renderState->setParameterAutoBinding(it->first.c_str(), it->second);
+    }
+    for (std::vector<MaterialParameter*>::const_iterator it = _parameters.begin(); it != _parameters.end(); ++it)
+    {
+        const MaterialParameter* param = *it;
+
+        MaterialParameter* paramCopy = new MaterialParameter(param->getName());
+        param->cloneInto(paramCopy);
+
+        renderState->_parameters.push_back(paramCopy);
+    }
+    renderState->_parent = _parent;
+    if (Node* node = context.findClonedNode(_nodeBinding))
+    {
+        renderState->setNodeBinding(node);
+    }
+    if (_state)
+    {
+        renderState->setStateBlock(_state);
+    }
+}
+
 RenderState::StateBlock::StateBlock()
     : _blendEnabled(false), _cullFaceEnabled(false), _depthTestEnabled(false), _depthWriteEnabled(false),
       _srcBlend(RenderState::BLEND_ONE), _dstBlend(RenderState::BLEND_ONE), _bits(0L)

+ 23 - 5
gameplay/src/RenderState.h

@@ -2,6 +2,7 @@
 #define RENDERSTATE_H_
 
 #include "Ref.h"
+#include "CloneContext.h"
 
 namespace gameplay
 {
@@ -289,11 +290,6 @@ protected:
      */
     RenderState();
 
-    /**
-     * Hidden copy constructor.
-     */
-    RenderState(const RenderState& copy);
-
     /**
      * Destructor.
      */
@@ -336,6 +332,28 @@ protected:
      */
     RenderState* getTopmost(RenderState* below);
 
+    /**
+     * Copies the data from this RenderState into the given RenderState.
+     * 
+     * @param renderState The RenderState to copy the data to.
+     * @param context The clone context.
+     */
+    void cloneInto(RenderState* renderState, CloneContext& context) const;
+
+private:
+
+    /**
+     * Hidden copy constructor.
+     */
+    RenderState(const RenderState& copy);
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    RenderState& operator=(const RenderState&);
+
+protected:
+
     mutable std::vector<MaterialParameter*> _parameters;
     std::map<std::string, AutoBinding> _autoBindings;
     Node* _nodeBinding;

+ 1 - 0
gameplay/src/SpriteBatch.h

@@ -192,6 +192,7 @@ public:
      * @param u2 Texture coordinate.
      * @param v2 Texture coordinate.
      * @param color The color to tint the sprite. Use white for no tint.
+     * @param clip The clip rectangle.
      */
     void draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip);
 

+ 14 - 4
gameplay/src/Technique.cpp

@@ -13,10 +13,6 @@ Technique::Technique(const char* id, Material* material)
     RenderState::_parent = material;
 }
 
-Technique::Technique(const Technique& m)
-{
-}
-
 Technique::~Technique()
 {
     // Destroy all the passes.
@@ -57,4 +53,18 @@ Pass* Technique::getPass(const char* id) const
     return NULL;
 }
 
+Technique* Technique::clone(Material* material, CloneContext &context) const
+{
+    Technique* technique = new Technique(getId(), material);
+    technique->_material = material;
+    for (std::vector<Pass*>::const_iterator it = _passes.begin(); it != _passes.end(); ++it)
+    {
+        Pass* pass = *it;
+        Pass* passCopy = pass->clone(technique, context);
+        technique->_passes.push_back(passCopy);
+    }
+    RenderState::cloneInto(technique, context);
+    return technique;
+}
+
 }

+ 8 - 1
gameplay/src/Technique.h

@@ -51,13 +51,20 @@ private:
     /**
      * Hidden copy constructor.
      */
-    Technique(const Technique& t);
+    Technique(const Technique&);
 
     /**
      * Destructor.
      */
     ~Technique();
 
+    /**
+     * Hidden copy assignment operator.
+     */
+    Technique& operator=(const Technique&);
+
+    Technique* clone(Material* material, CloneContext &context) const;
+
     std::string _id;
     Material* _material;
     std::vector<Pass*> _passes;

+ 8 - 0
gameplay/src/Transform.cpp

@@ -780,6 +780,14 @@ void Transform::transformChanged()
     }
 }
 
+void Transform::cloneInto(Transform* transform, CloneContext &context) const
+{
+    AnimationTarget::cloneInto(transform, context);
+    transform->_scale.set(_scale);
+    transform->_rotation.set(_rotation);
+    transform->_translation.set(_translation);
+}
+
 void Transform::applyAnimationValueScaleX(float sx, float blendWeight)
 {
     if ((_animationPropertyBitFlag & ANIMATION_SCALE_X_BIT) != ANIMATION_SCALE_X_BIT)

+ 2 - 1
gameplay/src/Transform.h

@@ -674,7 +674,7 @@ public:
      * Transforms the specified vector and stores the
      * result in the original vector.
      *
-     * @param normal The vector to transform.
+     * @param vector The vector to transform.
      */
     void transformVector(Vector3* vector);
 
@@ -737,6 +737,7 @@ protected:
 
     void dirty();
     virtual void transformChanged();
+    void cloneInto(Transform* transform, CloneContext &context) const;
 
     Vector3 _scale;
     Quaternion _rotation;

+ 2 - 1
gameplay/src/Vector2.cpp

@@ -165,9 +165,10 @@ void Vector2::negate()
     y = -y;
 }
 
-void Vector2::normalize()
+Vector2& Vector2::normalize()
 {
     normalize(this);
+    return *this;
 }
 
 void Vector2::normalize(Vector2* dst)

+ 3 - 1
gameplay/src/Vector2.h

@@ -231,8 +231,10 @@ public:
      * after calling this method will be 1.0f). If the vector
      * already has unit length or if the length of the vector
      * is zero, this method does nothing.
+     * 
+     * @return This vector, after the normalization occurs.
      */
-    void normalize();
+    Vector2& normalize();
 
     /**
      * Normalizes this vector and stores the result in dst.

+ 2 - 1
gameplay/src/Vector3.cpp

@@ -230,9 +230,10 @@ void Vector3::negate()
     z = -z;
 }
 
-void Vector3::normalize()
+Vector3& Vector3::normalize()
 {
     normalize(this);
+    return *this;
 }
 
 void Vector3::normalize(Vector3* dst) const

+ 3 - 1
gameplay/src/Vector3.h

@@ -278,8 +278,10 @@ public:
      * after calling this method will be 1.0f). If the vector
      * already has unit length or if the length of the vector
      * is zero, this method does nothing.
+     * 
+     * @return This vector, after the normalization occurs.
      */
-    void normalize();
+    Vector3& normalize();
 
     /**
      * Normalizes this vector and stores the result in dst.

+ 2 - 1
gameplay/src/Vector4.cpp

@@ -233,9 +233,10 @@ void Vector4::negate()
     w = -w;
 }
 
-void Vector4::normalize()
+Vector4& Vector4::normalize()
 {
     normalize(this);
+    return *this;
 }
 
 void Vector4::normalize(Vector4* dst)

+ 3 - 1
gameplay/src/Vector4.h

@@ -269,8 +269,10 @@ public:
      * after calling this method will be 1.0f). If the vector
      * already has unit length or if the length of the vector
      * is zero, this method does nothing.
+     * 
+     * @return This vector, after the normalization occurs.
      */
-    void normalize();
+    Vector4& normalize();
 
     /**
      * Normalizes this vector and stores the result in dst.