Quellcode durchsuchen

Merge pull request #100 from blackberry/next

Fixes for Android 3D Audio.
Sean Paul Taylor vor 13 Jahren
Ursprung
Commit
ae18f7eef1
50 geänderte Dateien mit 612 neuen und 913 gelöschten Zeilen
  1. 1 0
      gameplay-encoder/gameplay-bundle.txt
  2. 1 0
      gameplay-encoder/gameplay-encoder.vcxproj
  3. 1 0
      gameplay-encoder/gameplay-encoder.vcxproj.filters
  4. 6 2
      gameplay-encoder/src/FileIO.cpp
  5. 1 1
      gameplay-encoder/src/GPBFile.h
  6. 4 0
      gameplay-encoder/src/Node.cpp
  7. 18 2
      gameplay-template/android/jni/template.Android.mk
  8. 1 1
      gameplay/android/jni/Android.mk
  9. 4 4
      gameplay/src/Animation.cpp
  10. 6 2
      gameplay/src/Animation.h
  11. 1 7
      gameplay/src/AnimationClip.cpp
  12. 1 1
      gameplay/src/AnimationClip.h
  13. 1 11
      gameplay/src/AnimationController.cpp
  14. 0 1
      gameplay/src/AnimationController.h
  15. 5 5
      gameplay/src/AnimationTarget.cpp
  16. 5 8
      gameplay/src/AnimationTarget.h
  17. 0 64
      gameplay/src/AudioBuffer.cpp
  18. 0 14
      gameplay/src/AudioBuffer.h
  19. 2 144
      gameplay/src/AudioController.cpp
  20. 1 9
      gameplay/src/AudioController.h
  21. 7 248
      gameplay/src/AudioSource.cpp
  22. 5 22
      gameplay/src/AudioSource.h
  23. 2 11
      gameplay/src/Base.h
  24. 250 17
      gameplay/src/Bundle.cpp
  25. 31 2
      gameplay/src/Bundle.h
  26. 19 78
      gameplay/src/Control.cpp
  27. 0 16
      gameplay/src/Control.h
  28. 4 4
      gameplay/src/Form.cpp
  29. 6 4
      gameplay/src/Form.h
  30. 4 4
      gameplay/src/Material.cpp
  31. 6 4
      gameplay/src/Material.h
  32. 6 45
      gameplay/src/MaterialParameter.cpp
  33. 1 3
      gameplay/src/MaterialParameter.h
  34. 13 2
      gameplay/src/MeshSkin.cpp
  35. 4 1
      gameplay/src/MeshSkin.h
  36. 1 1
      gameplay/src/Model.cpp
  37. 25 7
      gameplay/src/Node.cpp
  38. 5 3
      gameplay/src/Node.h
  39. 5 5
      gameplay/src/ParticleEmitter.cpp
  40. 6 4
      gameplay/src/ParticleEmitter.h
  41. 92 4
      gameplay/src/Properties.cpp
  42. 9 4
      gameplay/src/Properties.h
  43. 10 9
      gameplay/src/SceneLoader.cpp
  44. 6 2
      gameplay/src/SceneLoader.h
  45. 6 6
      gameplay/src/Theme.cpp
  46. 7 5
      gameplay/src/Theme.h
  47. 1 10
      gameplay/src/ThemeStyle.cpp
  48. 1 4
      gameplay/src/ThemeStyle.h
  49. 20 97
      gameplay/src/Transform.cpp
  50. 1 15
      gameplay/src/Transform.h

+ 1 - 0
gameplay-encoder/gameplay-bundle.txt

@@ -137,6 +137,7 @@ Reference
 2->Node
                 type                    enum NodeType
                 transform               float[16]
+                parent_id               string
                 children                Node[]
                 camera                  Camera
                 light                   Light

+ 1 - 0
gameplay-encoder/gameplay-encoder.vcxproj

@@ -98,6 +98,7 @@
     <ClInclude Include="src\VertexElement.h" />
   </ItemGroup>
   <ItemGroup>
+    <None Include="gameplay-bundle.txt" />
     <None Include="src\Curve.inl" />
     <None Include="src\Quaternion.inl" />
     <None Include="src\Vector2.inl" />

+ 1 - 0
gameplay-encoder/gameplay-encoder.vcxproj.filters

@@ -269,6 +269,7 @@
     <None Include="src\Curve.inl">
       <Filter>src</Filter>
     </None>
+    <None Include="gameplay-bundle.txt" />
   </ItemGroup>
   <ItemGroup>
     <Filter Include="src">

+ 6 - 2
gameplay-encoder/src/FileIO.cpp

@@ -64,8 +64,12 @@ void write(const std::string& str, FILE* file)
 {
     // Write the length of the string
     write(str.size(), file);
-    // Write the array of characters of the string
-    write(str.c_str(), file);
+    
+    if (str.size() > 0)
+    {
+        // Write the array of characters of the string
+        write(str.c_str(), file);
+    }
 }
 
 void writeZero(FILE* file)

+ 1 - 1
gameplay-encoder/src/GPBFile.h

@@ -21,7 +21,7 @@ namespace gameplay
  * Increment the version number when making a change that break binary compatibility.
  * [0] is major, [1] is minor.
  */
-const unsigned char GPB_VERSION[2] = {1, 1};
+const unsigned char GPB_VERSION[2] = {1, 2};
 
 /**
  * The GamePlay Binary file class handles writing the GamePlay Binary file.

+ 4 - 0
gameplay-encoder/src/Node.cpp

@@ -40,6 +40,10 @@ void Node::writeBinary(FILE* file)
     write(type, file);
 
     write(_transform.m, 16, file);
+
+    // write parent's id
+    write((_parent) ? _parent->getId() : std::string(), file);
+
     // children
     write(getChildCount(), file); // write number of children
     for (Node* node = getFirstChild(); node != NULL; node = node->getNextSibling())

+ 18 - 2
gameplay-template/android/jni/template.Android.mk

@@ -17,6 +17,8 @@ SAMPLE_PATH := $(call my-dir)/../../src
 LIBPNG_PATH := ../GAMEPLAY_PATH/external-deps/libpng/lib/android/arm
 ZLIB_PATH := ../GAMEPLAY_PATH/external-deps/zlib/lib/android/arm
 BULLET_PATH := ../GAMEPLAY_PATH/external-deps/bullet/lib/android/arm
+VORBIS_PATH := ../GAMEPLAY_PATH/external-deps/oggvorbis/lib/android/arm
+OPENAL_PATH := ../GAMEPLAY_PATH/external-deps/openal/lib/android/arm
 
 # gameplay
 LOCAL_PATH := ../GAMEPLAY_PATH/gameplay/android/obj/local/armeabi
@@ -46,6 +48,20 @@ LOCAL_MODULE    := libbullet
 LOCAL_SRC_FILES := libbullet.a
 include $(PREBUILT_STATIC_LIBRARY)
 
+# libvorbis
+LOCAL_PATH := $(VORBIS_PATH)
+include $(CLEAR_VARS)
+LOCAL_MODULE    := libvorbis
+LOCAL_SRC_FILES := libvorbis.a
+include $(PREBUILT_STATIC_LIBRARY)
+
+# libOpenAL
+LOCAL_PATH := $(OPENAL_PATH)
+include $(CLEAR_VARS)
+LOCAL_MODULE    := libOpenAL
+LOCAL_SRC_FILES := libOpenAL.a
+include $(PREBUILT_STATIC_LIBRARY)
+
 # TEMPLATE_PROJECT
 LOCAL_PATH := $(SAMPLE_PATH)
 include $(CLEAR_VARS)
@@ -54,9 +70,9 @@ LOCAL_MODULE    := TEMPLATE_PROJECT
 LOCAL_SRC_FILES := ../GAMEPLAY_PATH/gameplay/src/gameplay-main-android.cpp TemplateGame.cpp
 
 LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv2 -lOpenSLES
-LOCAL_CFLAGS    := -D__ANDROID__ -I"../GAMEPLAY_PATH/external-deps/bullet/include" -I"../GAMEPLAY_PATH/external-deps/libpng/include" -I"../GAMEPLAY_PATH/gameplay/src"
+LOCAL_CFLAGS    := -D__ANDROID__ -I"../GAMEPLAY_PATH/external-deps/bullet/include" -I"../GAMEPLAY_PATH/external-deps/libpng/include" -I"../GAMEPLAY_PATH/external-deps/oggvorbis/include" -I"../GAMEPLAY_PATH/external-deps/openal/include" -I"../GAMEPLAY_PATH/gameplay/src"
 
-LOCAL_STATIC_LIBRARIES := android_native_app_glue libgameplay libpng libzlib libbullet
+LOCAL_STATIC_LIBRARIES := android_native_app_glue libgameplay libpng libzlib libbullet libvorbis libOpenAL
 
 include $(BUILD_SHARED_LIBRARY)
 $(call import-module,android/native_app_glue)

+ 1 - 1
gameplay/android/jni/Android.mk

@@ -17,7 +17,7 @@ LOCAL_PATH := $(call my-dir)/../../src
 include $(CLEAR_VARS)
 LOCAL_MODULE    := libgameplay
 LOCAL_SRC_FILES := AbsoluteLayout.cpp Animation.cpp AnimationClip.cpp AnimationController.cpp AnimationTarget.cpp AnimationValue.cpp AudioBuffer.cpp AudioController.cpp AudioListener.cpp AudioSource.cpp BoundingBox.cpp BoundingSphere.cpp Bundle.cpp Button.cpp Camera.cpp CheckBox.cpp Container.cpp Control.cpp Curve.cpp DebugNew.cpp DepthStencilTarget.cpp Effect.cpp FileSystem.cpp FlowLayout.cpp Font.cpp Form.cpp FrameBuffer.cpp Frustum.cpp Game.cpp gameplay-main-android.cpp Image.cpp Joint.cpp Label.cpp Layout.cpp Light.cpp Material.cpp MaterialParameter.cpp Matrix.cpp Mesh.cpp MeshBatch.cpp MeshPart.cpp MeshSkin.cpp Model.cpp Node.cpp ParticleEmitter.cpp Pass.cpp PhysicsCharacter.cpp PhysicsCollisionObject.cpp PhysicsCollisionShape.cpp PhysicsConstraint.cpp PhysicsController.cpp PhysicsFixedConstraint.cpp PhysicsGenericConstraint.cpp PhysicsGhostObject.cpp PhysicsHingeConstraint.cpp PhysicsMotionState.cpp PhysicsRigidBody.cpp PhysicsSocketConstraint.cpp PhysicsSpringConstraint.cpp Plane.cpp PlatformAndroid.cpp Properties.cpp Quaternion.cpp RadioButton.cpp Ray.cpp Rectangle.cpp Ref.cpp RenderState.cpp RenderTarget.cpp Scene.cpp SceneLoader.cpp Slider.cpp SpriteBatch.cpp Technique.cpp TextBox.cpp Texture.cpp Theme.cpp ThemeStyle.cpp Transform.cpp Vector2.cpp Vector3.cpp Vector4.cpp VertexAttributeBinding.cpp VertexFormat.cpp VerticalLayout.cpp
-LOCAL_CFLAGS := -D__ANDROID__ -I"../../external-deps/bullet/include" -I"../../external-deps/libpng/include"
+LOCAL_CFLAGS := -D__ANDROID__ -I"../../external-deps/bullet/include" -I"../../external-deps/libpng/include" -I"../../external-deps/oggvorbis/include" -I"../../external-deps/openal/include"
 LOCAL_STATIC_LIBRARIES := android_native_app_glue
 
 include $(BUILD_STATIC_LIBRARY)

+ 4 - 4
gameplay/src/Animation.cpp

@@ -105,14 +105,14 @@ unsigned long Animation::getDuration() const
     return _duration;
 }
 
-void Animation::createClips(const char* animationFile)
+void Animation::createClips(const char* url)
 {
-    assert(animationFile);
+    assert(url);
 
-    Properties* properties = Properties::create(animationFile);
+    Properties* properties = Properties::create(url);
     assert(properties);
 
-    Properties* pAnimation = properties->getNextNamespace();
+    Properties* pAnimation = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
     assert(pAnimation);
     
     int frameCount = pAnimation->getInt("frameCount");

+ 6 - 2
gameplay/src/Animation.h

@@ -43,9 +43,13 @@ public:
     unsigned long getDuration() const;
 
     /**
-     * Creates an AnimationClip from an .animation file.
+     * Creates an AnimationClip from the Properties object defined at the specified URL, 
+     * where the URL is of the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
+     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional).
+     * 
+     * @param url The URL pointing to the Properties object containing the clip definitions.
      */
-    void createClips(const char* animationFile);
+    void createClips(const char* url);
     
     /**
      * Creates an AnimationClip from the Animation.

+ 1 - 7
gameplay/src/AnimationClip.cpp

@@ -304,7 +304,7 @@ void AnimationClip::addEndListener(AnimationClip::Listener* listener)
     _endListeners->push_back(listener);
 }
 
-bool AnimationClip::update(unsigned long elapsedTime, std::list<AnimationTarget*>* activeTargets)
+bool AnimationClip::update(unsigned long elapsedTime)
 {
     if (isClipStateBitSet(CLIP_IS_PAUSED_BIT))
     {
@@ -439,12 +439,6 @@ bool AnimationClip::update(unsigned long elapsedTime, std::list<AnimationTarget*
         target = channel->_target;
         value = _values[i];
 
-        // If the target's _animationPropertyBitFlag is clear, we can assume that this is the first
-        // animation channel to act on the target and we can add the target to the list of
-        // active targets stored by the AnimationController.
-        if (target->_animationPropertyBitFlag == 0x00)
-            activeTargets->push_front(target);
-
         // Evaluate the point on Curve
         channel->getCurve()->evaluate(percentComplete, value->_value);
         // Set the animation value on the target property.

+ 1 - 1
gameplay/src/AnimationClip.h

@@ -283,7 +283,7 @@ private:
     /**
      * Updates the animation with the elapsed time.
      */
-    bool update(unsigned long elapsedTime, std::list<AnimationTarget*>* activeTargets);
+    bool update(unsigned long elapsedTime);
 
     /**
      * Handles when the AnimationClip begins.

+ 1 - 11
gameplay/src/AnimationController.cpp

@@ -109,7 +109,7 @@ void AnimationController::update(long elapsedTime)
             _runningClips.push_back(clip);
             clipIter = _runningClips.erase(clipIter);
         }
-        else if (clip->update(elapsedTime, &_activeTargets))
+        else if (clip->update(elapsedTime))
         {
             SAFE_RELEASE(clip);
             clipIter = _runningClips.erase(clipIter);
@@ -120,16 +120,6 @@ void AnimationController::update(long elapsedTime)
         }
     }
 
-    // Loop through active AnimationTarget's and reset their _animationPropertyBitFlag for the next frame.
-    std::list<AnimationTarget*>::iterator targetItr = _activeTargets.begin();
-    while (targetItr != _activeTargets.end())
-    {
-        AnimationTarget* target = (*targetItr);
-        target->_animationPropertyBitFlag = 0x00;
-        targetItr++;
-    }
-    _activeTargets.clear();
-    
     if (_runningClips.empty())
         _state = IDLE;
 }

+ 0 - 1
gameplay/src/AnimationController.h

@@ -98,7 +98,6 @@ private:
     
     State _state;                                 // The current state of the AnimationController.
     std::list<AnimationClip*> _runningClips;      // A list of running AnimationClips.
-    std::list<AnimationTarget*> _activeTargets;   // A list of animating AnimationTargets.
 };
 
 }

+ 5 - 5
gameplay/src/AnimationTarget.cpp

@@ -8,7 +8,7 @@ namespace gameplay
 {
 
 AnimationTarget::AnimationTarget()
-    : _targetType(SCALAR), _animationPropertyBitFlag(0x00), _animationChannels(NULL)
+    : _targetType(SCALAR), _animationChannels(NULL)
 {
 }
 
@@ -47,14 +47,14 @@ Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsi
     return animation;
 }
 
-Animation* AnimationTarget::createAnimation(const char* id, const char* animationFile)
+Animation* AnimationTarget::createAnimation(const char* id, const char* url)
 {
-    assert(animationFile);
+    assert(url);
     
-    Properties* p = Properties::create(animationFile);
+    Properties* p = Properties::create(url);
     assert(p);
 
-    Animation* animation = createAnimation(id, p->getNextNamespace());
+    Animation* animation = createAnimation(id, (strlen(p->getNamespace()) > 0) ? p : p->getNextNamespace());
 
     SAFE_DELETE(p);
 

+ 5 - 8
gameplay/src/AnimationTarget.h

@@ -55,14 +55,16 @@ public:
     Animation* createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, Curve::InterpolationType type);
 
     /**
-     * Creates an animation on this target using the data from the given properties object. 
+     * Creates an animation on this target using the data from the Properties object defined at the specified URL, 
+     * where the URL is of the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
+     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional). 
      * 
      * @param id The ID of the animation.
-     * @param animationFile The animation file defining the animation data.
+     * @param url The URL pointing to the Properties object defining the animation data.
      *
      * @return The newly created animation.
      */
-    Animation* createAnimation(const char* id, const char* animationFile);
+    Animation* createAnimation(const char* id, const char* url);
 
     /**
      * Creates an animation on this target using the data from the given properties object. 
@@ -202,11 +204,6 @@ protected:
      */
     TargetType _targetType;
 
-    /**
-     * Bit flag used to indicate which properties on the AnimationTarget are currently animating.
-     */ 
-    unsigned char _animationPropertyBitFlag;
-
 private:
 
     /**

+ 0 - 64
gameplay/src/AudioBuffer.cpp

@@ -2,26 +2,16 @@
 #include "AudioBuffer.h"
 #include "FileSystem.h"
 
-#ifdef __ANDROID__
-extern AAssetManager* __assetManager;
-#endif
-
 namespace gameplay
 {
 
 // Audio buffer cache
 static std::vector<AudioBuffer*> __buffers;
 
-#ifndef __ANDROID__
 AudioBuffer::AudioBuffer(const char* path, ALuint buffer)
     : _filePath(path), _alBuffer(buffer)
 {
 }
-#else
-AudioBuffer::AudioBuffer(const char* path) : _filePath(path)
-{
-}
-#endif
 
 AudioBuffer::~AudioBuffer()
 {
@@ -36,13 +26,11 @@ AudioBuffer::~AudioBuffer()
         }
     }
 
-#ifndef __ANDROID__
     if (_alBuffer)
     {
         alDeleteBuffers(1, &_alBuffer);
         _alBuffer = 0;
     }
-#endif
 }
 
 AudioBuffer* AudioBuffer::create(const char* path)
@@ -62,7 +50,6 @@ AudioBuffer* AudioBuffer::create(const char* path)
         }
     }
 
-#ifndef __ANDROID__
     ALuint alBuffer;
     ALCenum al_error;
 
@@ -130,58 +117,8 @@ cleanup:
     if (alBuffer)
         alDeleteBuffers(1, &alBuffer);
     return NULL;
-#else
-    // Get the file header in order to determine the type.
-    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);
-    SLDataLocator_AndroidFD data = {SL_DATALOCATOR_ANDROIDFD, fd, start, length};
-
-    // Set the appropriate mime type information.
-    SLDataFormat_MIME mime;
-    mime.formatType = SL_DATAFORMAT_MIME;
-    std::string pathStr = path;
-    if (memcmp(header, "RIFF", 4) == 0)
-    {
-        mime.mimeType = (SLchar*)"audio/x-wav";
-        mime.containerType = SL_CONTAINERTYPE_WAV;
-    }
-    else if (memcmp(header, "OggS", 4) == 0)
-    {
-        mime.mimeType = (SLchar*)"application/ogg";
-        mime.containerType = SL_CONTAINERTYPE_OGG;
-    }
-    else
-    {
-        LOG_ERROR_VARG("Unsupported audio file: %s", path);
-    }
-
-    buffer = new AudioBuffer(path);
-    buffer->_data = data;
-    buffer->_mime = mime;
-
-    // Add the buffer to the cache.
-    __buffers.push_back(buffer);
-
-    return buffer;
-#endif
 }
 
-#ifndef __ANDROID__
 bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
 {
     unsigned char stream[12];
@@ -376,6 +313,5 @@ bool AudioBuffer::loadOgg(FILE* file, ALuint buffer)
 
     return true;
 }
-#endif
 
 }

+ 0 - 14
gameplay/src/AudioBuffer.h

@@ -19,17 +19,10 @@ class AudioBuffer : public Ref
 
 private:
     
-#ifndef __ANDROID__
     /**
      * Constructor.
      */
     AudioBuffer(const char* path, ALuint buffer);
-#else
-    /**
-     * Constructor.
-     */
-    AudioBuffer(const char* path);
-#endif
 
     /**
      * Destructor.
@@ -45,19 +38,12 @@ private:
      */
     static AudioBuffer* create(const char* path);
     
-#ifndef __ANDROID__
     static bool loadWav(FILE* file, ALuint buffer);
     
     static bool loadOgg(FILE* file, ALuint buffer);
-#endif
 
     std::string _filePath;
-#ifndef __ANDROID__
     ALuint _alBuffer;
-#else
-    SLDataLocator_AndroidFD _data;
-    SLDataFormat_MIME _mime;
-#endif
 };
 
 }

+ 2 - 144
gameplay/src/AudioController.cpp

@@ -4,22 +4,13 @@
 #include "AudioBuffer.h"
 #include "AudioSource.h"
 
-
 namespace gameplay
 {
 
-#ifndef __ANDROID__
 AudioController::AudioController() 
     : _alcDevice(NULL), _alcContext(NULL), _pausingSource(NULL)
 {
 }
-#else
-AudioController::AudioController() 
-    : _engineObject(NULL), _engineEngine(NULL), _outputMixObject(NULL), _listenerObject(NULL),
-    _listenerDoppler(NULL), _listenerLocation(NULL), _pausingSource(NULL)
-{
-}
-#endif
 
 AudioController::~AudioController()
 {
@@ -27,7 +18,6 @@ AudioController::~AudioController()
 
 void AudioController::initialize()
 {
-#ifndef __ANDROID__
     _alcDevice = alcOpenDevice (NULL);
     if (!_alcDevice)
     {
@@ -50,88 +40,10 @@ void AudioController::initialize()
     {
         LOG_ERROR_VARG("AudioController::initialize() error. Unable to make OpenAL context current. Error: %d\n", alcErr);
     }
-#else
-    // Create the engine.
-    SLresult result = slCreateEngine(&_engineObject, 0, NULL, 0, NULL, NULL);
-    if (result != SL_RESULT_SUCCESS)
-    {
-        LOG_ERROR("AudioController::initialize() error. Unable to create OpenSL engine.");
-        return;
-    }
-
-    // Realize the engine.
-    result = (*_engineObject)->Realize(_engineObject, SL_BOOLEAN_FALSE);
-    if (result != SL_RESULT_SUCCESS)
-    {
-        LOG_ERROR("AudioController::initialize() error. Unable to realize OpenSL engine.");
-        return;
-    }
-
-    // Get the engine interface in order to create other objects later on.
-    result = (*_engineObject)->GetInterface(_engineObject, SL_IID_ENGINE, &_engineEngine);
-    if (result != SL_RESULT_SUCCESS)
-    {
-        LOG_ERROR("AudioController::initialize() error. Unable to retrieve OpenSL engine interface.");
-        return;
-    }
-
-    // Create the output mix.
-    result = (*_engineEngine)->CreateOutputMix(_engineEngine, &_outputMixObject, 0, NULL, NULL);
-    if (result != SL_RESULT_SUCCESS)
-    {
-        LOG_ERROR("AudioController::initialize() error. Unable to create OpenSL output mix.");
-        return;
-    }
-
-    // Realize the output mix.
-    result = (*_outputMixObject)->Realize(_outputMixObject, SL_BOOLEAN_FALSE);
-    if (result != SL_RESULT_SUCCESS)
-    {
-        LOG_ERROR("AudioController::initialize() error. Unable to realize OpenSL output mix.");
-        return;
-    }
-
-    // Load the listener and its supported interfaces.
-    if (!_listenerObject)
-    {
-        const SLInterfaceID interfaces[3] = {SL_IID_3DDOPPLER, SL_IID_3DLOCATION};
-        const SLboolean required[3] = {SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE};
-        SLresult result = (*_engineEngine)->CreateListener(_engineEngine, &_listenerObject, 2, interfaces, required);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN_VARG("AudioController: failed to create listener (%u).", result);
-            return;
-        }
-
-        result = (*_listenerObject)->Realize(_listenerObject, SL_BOOLEAN_FALSE);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioController: failed to realize listener.");
-            return;
-        }
-
-        // Get the doppler interface in order to set the listener's velocity.
-        result = (*_listenerObject)->GetInterface(_listenerObject, SL_IID_3DDOPPLER, &_listenerDoppler);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioController: Unable to retrieve listener doppler interface.");
-            return;
-        }
-
-        // Get the location interface in order to set the listener's position and orientation.
-        result = (*_listenerObject)->GetInterface(_listenerObject, SL_IID_3DLOCATION, &_listenerLocation);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioController: Unable to retrieve listener location interface.");
-            return;
-        }
-    }
-#endif
 }
 
 void AudioController::finalize()
 {
-#ifndef __ANDROID__
     alcMakeContextCurrent(NULL);
     if (_alcContext)
     {
@@ -143,20 +55,6 @@ void AudioController::finalize()
         alcCloseDevice(_alcDevice);
         _alcDevice = NULL;
     }
-#else
-    if (_outputMixObject != NULL)
-    {
-        (*_outputMixObject)->Destroy(_outputMixObject);
-        _outputMixObject = NULL;
-    }
-
-    if (_engineObject != NULL)
-    {
-        (*_engineObject)->Destroy(_engineObject);
-        _engineObject = NULL;
-        _engineEngine = NULL;
-    }
-#endif
 }
 
 void AudioController::pause()
@@ -176,10 +74,9 @@ void AudioController::pause()
 }
 
 void AudioController::resume()
-{
-#ifndef __ANDROID__    
+{   
     alcMakeContextCurrent(_alcContext);
-#endif
+
     std::set<AudioSource*>::iterator itr = _playingSources.begin();
 
     // For each source that is playing, resume it.
@@ -197,49 +94,10 @@ void AudioController::update(long elapsedTime)
     AudioListener* listener = AudioListener::getInstance();
     if (listener)
     {
-#ifndef __ANDROID__
         alListenerf(AL_GAIN, listener->getGain());
         alListenerfv(AL_ORIENTATION, (ALfloat*)listener->getOrientation());
         alListenerfv(AL_VELOCITY, (ALfloat*)&listener->getVelocity());
         alListenerfv(AL_POSITION, (ALfloat*)&listener->getPosition());
-#else
-        if (_listenerObject)
-        {
-            SLVec3D f;
-            f.x = listener->getOrientationForward().x;
-            f.y = listener->getOrientationForward().y;
-            f.z = listener->getOrientationForward().z;
-            SLVec3D a;
-            a.x = listener->getOrientationUp().x;
-            a.y = listener->getOrientationUp().y;
-            a.z = listener->getOrientationUp().z;
-            SLresult result = (*_listenerLocation)->SetOrientationVectors(_listenerLocation, &f, &a);
-            if (result != SL_RESULT_SUCCESS)
-            {
-                WARN("AudioController: Unable to set listener orientation.");
-            }
-
-            SLVec3D p;
-            p.x = listener->getPosition().x;
-            p.y = listener->getPosition().y;
-            p.z = listener->getPosition().z;
-            result = (*_listenerLocation)->SetLocationCartesian(_listenerLocation, &p);
-            if (result != SL_RESULT_SUCCESS)
-            {
-                WARN("AudioController: Unable to set listener location.");
-            }
-
-            SLVec3D v;
-            v.x = listener->getVelocity().x;
-            v.y = listener->getVelocity().y;
-            v.z = listener->getVelocity().z;
-            result = (*_listenerDoppler)->SetVelocityCartesian(_listenerDoppler, &v);
-            if (result != SL_RESULT_SUCCESS)
-            {
-                WARN("AudioController: Unable to set listener velocity.");
-            }
-        }
-#endif
     }
 }
 

+ 1 - 9
gameplay/src/AudioController.h

@@ -54,17 +54,9 @@ private:
      */
     void update(long elapsedTime);
 
-#ifndef __ANDROID__
+
     ALCdevice* _alcDevice;
     ALCcontext* _alcContext;
-#else
-    SLObjectItf _engineObject;
-    SLEngineItf _engineEngine;
-    SLObjectItf _outputMixObject;
-    SLObjectItf _listenerObject;
-    SL3DDopplerItf _listenerDoppler;
-    SL3DLocationItf _listenerLocation;
-#endif
     std::set<AudioSource*> _playingSources;
     AudioSource* _pausingSource;
 };

+ 7 - 248
gameplay/src/AudioSource.cpp

@@ -9,8 +9,6 @@
 namespace gameplay
 {
 
-
-#ifndef __ANDROID__
 AudioSource::AudioSource(AudioBuffer* buffer, ALuint source) 
     : _alSource(source), _buffer(buffer), _looped(true), _gain(1.0f), _pitch(1.0f), _node(NULL)
 {
@@ -20,116 +18,42 @@ AudioSource::AudioSource(AudioBuffer* buffer, ALuint source)
     alSourcef(_alSource, AL_GAIN, _gain);
     alSourcefv(_alSource, AL_VELOCITY, (const ALfloat*)&_velocity);
 }
-#else
-AudioSource::AudioSource(AudioBuffer* buffer, const SLObjectItf& player)
-    : _playerObject(player), _playerDoppler(NULL), _playerLocation(NULL), _playerPlay(NULL), _playerPitch(NULL),
-    _playerSeek(NULL), _playerVolume(NULL), _buffer(buffer), _looped(true), _gain(1.0f), _pitch(1.0f), _node(NULL)
-{
-    // 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)
-    {
-        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)
-    {
-        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)
-    {
-        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)
-    {
-        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)
-    {
-        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)
-    {
-        WARN("AudioSource::AudioSource() - Failed to get volume interface for OpenSL audio player.");
-    }
-
-    // Get the max volume level (used to convert from our API's parameter to OpenSL's expected units).
-    if (_playerVolume)
-    {
-        result = (*_playerVolume)->GetMaxVolumeLevel(_playerVolume, &_maxVolume);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioSource::AudioSource() - Failed to get the max volume level for OpenSL audio player (needed for parameter conversion).");
-        }
-    }
-
-    setLooped(_looped);
-    setPitch(_pitch);
-    setGain(_gain);
-    setVelocity(_velocity);
-}
-#endif
 
 AudioSource::~AudioSource()
 {
-#ifndef __ANDROID__
     if (_alSource)
     {
         alDeleteSources(1, &_alSource);
         _alSource = 0;
     }
-#else
-    if (_playerObject)
-    {
-        (*_playerObject)->Destroy(_playerObject);
-        _playerObject = NULL;
-        _playerDoppler = NULL;
-        _playerLocation = NULL;
-        _playerPlay = NULL;
-        _playerPitch = NULL;
-        _playerSeek = NULL;
-        _playerVolume = NULL;
-    }
-#endif
-
     SAFE_RELEASE(_buffer);
 }
 
-AudioSource* AudioSource::create(const char* path)
+AudioSource* AudioSource::create(const char* url)
 {
-    assert(path);
+    assert(url);
 
     // Load from a .audio file.
-    std::string pathStr = path;
+    std::string pathStr = url;
     if (pathStr.find(".audio") != pathStr.npos)
     {
-        Properties* properties = Properties::create(path);
+        Properties* properties = Properties::create(url);
         assert(properties);
         if (properties == NULL)
         {
             return NULL;
         }
 
-        AudioSource* audioSource = create(properties->getNextNamespace());
+        AudioSource* audioSource = create((strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace());
         SAFE_DELETE(properties);
         return audioSource;
     }
 
-    // Create an audio buffer from this path.
-    AudioBuffer* buffer = AudioBuffer::create(path);
+    // Create an audio buffer from this URL.
+    AudioBuffer* buffer = AudioBuffer::create(url);
     if (buffer == NULL)
         return NULL;
 
-#ifndef __ANDROID__
     // Load the audio source.
     ALuint alSource = 0;
 
@@ -142,31 +66,6 @@ AudioSource* AudioSource::create(const char* path)
     }
     
     return new AudioSource(buffer, alSource);
-#else
-    AudioController* audioController = Game::getInstance()->getAudioController();
-    SLDataLocator_OutputMix locator = {SL_DATALOCATOR_OUTPUTMIX, audioController->_outputMixObject};
-
-    SLDataSource dataSource = {&buffer->_data, &buffer->_mime};
-    SLDataSink dataSink = {&locator, NULL};
-
-    SLObjectItf player;
-    const SLInterfaceID interfaces[] = {SL_IID_3DDOPPLER, SL_IID_3DLOCATION, SL_IID_PLAY, SL_IID_PITCH, SL_IID_SEEK, SL_IID_VOLUME};
-    const SLboolean required[] = {SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE};
-    SLresult result = (*audioController->_engineEngine)->CreateAudioPlayer(audioController->_engineEngine, &player, &dataSource, &dataSink, 6, interfaces, required);
-    if (result != SL_RESULT_SUCCESS)
-    {
-        WARN("AudioSource::create - Failed to create OpenSL audio player.");
-        return NULL;
-    }
-
-    result = (*player)->Realize(player, SL_BOOLEAN_FALSE);
-    if (result != SL_RESULT_SUCCESS)
-    {
-        WARN("AudioSource::create - Failed to realize OpenSL audio player.");
-    }
-
-    return new AudioSource(buffer, player);
-#endif
 }
 
 AudioSource* AudioSource::create(Properties* properties)
@@ -218,7 +117,6 @@ AudioSource* AudioSource::create(Properties* properties)
 
 AudioSource::State AudioSource::getState() const
 {
-#ifndef __ANDROID__
     ALint state;
     alGetSourcei(_alSource, AL_SOURCE_STATE, &state);
 
@@ -233,47 +131,12 @@ AudioSource::State AudioSource::getState() const
         default:         
             return INITIAL;
     }
-#else
-    if (_playerPlay != NULL)
-    {
-        SLuint32 state;
-        SLresult result = (*_playerPlay)->GetPlayState(_playerPlay, &state);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioSource::getState() failed to get player state.");
-        }
-
-        switch (state)
-        {
-            case SL_PLAYSTATE_PLAYING:
-                return PLAYING;
-            case SL_PLAYSTATE_PAUSED:
-                return PAUSED;
-            case SL_PLAYSTATE_STOPPED:
-                return STOPPED;
-            default:
-                return INITIAL;
-        }
-    }
-#endif
-
     return INITIAL;
 }
 
 void AudioSource::play()
 {
-#ifndef __ANDROID__
     alSourcePlay(_alSource);
-#else
-    if (_playerPlay != NULL)
-    {
-        SLresult result = (*_playerPlay)->SetPlayState(_playerPlay, SL_PLAYSTATE_PLAYING);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioSource::play() failed to set player state.");
-        }
-    }
-#endif
 
     // Add the source to the controller's list of currently playing sources.
     AudioController* audioController = Game::getInstance()->getAudioController();
@@ -283,18 +146,7 @@ void AudioSource::play()
 
 void AudioSource::pause()
 {
-#ifndef __ANDROID__
     alSourcePause(_alSource);
-#else
-    if (_playerPlay != NULL)
-    {
-        SLresult result = (*_playerPlay)->SetPlayState(_playerPlay, SL_PLAYSTATE_PAUSED);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioSource::pause() failed to set player state.");
-        }
-    }
-#endif
 
     // Remove the source from the controller's set of currently playing sources
     // if the source is being paused by the user and not the controller itself.
@@ -320,18 +172,7 @@ void AudioSource::resume()
 
 void AudioSource::stop()
 {
-#ifndef __ANDROID__
     alSourceStop(_alSource);
-#else
-    if (_playerPlay != NULL)
-    {
-        SLresult result = (*_playerPlay)->SetPlayState(_playerPlay, SL_PLAYSTATE_STOPPED);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioSource::stop() failed to set player state.");
-        }
-    }
-#endif
 
     // Remove the source from the controller's set of currently playing sources.
     AudioController* audioController = Game::getInstance()->getAudioController();
@@ -342,18 +183,7 @@ void AudioSource::stop()
 
 void AudioSource::rewind()
 {
-#ifndef __ANDROID__
     alSourceRewind(_alSource);
-#else
-    if (_playerPlay != NULL)
-    {
-        SLresult result = (*_playerPlay)->SetMarkerPosition(_playerPlay, 0);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioSource::rewind() failed to set player marker position.");
-        }
-    }
-#endif
 }
 
 bool AudioSource::isLooped() const
@@ -363,8 +193,6 @@ bool AudioSource::isLooped() const
 
 void AudioSource::setLooped(bool looped)
 {
-#ifndef __ANDROID__
-     // Clear error state.
     alGetError();
     alSourcei(_alSource, AL_LOOPING, (looped) ? AL_TRUE : AL_FALSE);
 
@@ -373,17 +201,6 @@ void AudioSource::setLooped(bool looped)
     {
         LOG_ERROR_VARG("AudioSource::setLooped Error: %d", error);
     }
-#else
-    if (_playerSeek)
-    {
-        SLresult result = (*_playerSeek)->SetLoop(_playerSeek, looped, 0, SL_TIME_UNKNOWN);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioSource::setLooped() failed.");
-        }
-    }
-#endif
-
     _looped = looped;
 }
 
@@ -394,19 +211,7 @@ float AudioSource::getGain() const
 
 void AudioSource::setGain(float gain)
 {
-#ifndef __ANDROID__
     alSourcef(_alSource, AL_GAIN, gain);
-#else
-    if (_playerVolume)
-    {
-        SLmillibel volume = (gain < MATH_EPSILON) ? SL_MILLIBEL_MIN : (10.0f * log10(gain)) * 100;
-        SLresult result = (*_playerVolume)->SetVolumeLevel(_playerVolume, volume);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioSource::setGain() failed to set player gain.");
-        }
-    }
-#endif
     _gain = gain;
 }
 
@@ -417,18 +222,7 @@ float AudioSource::getPitch() const
 
 void AudioSource::setPitch(float pitch)
 {
-#ifndef __ANDROID__
     alSourcef(_alSource, AL_PITCH, pitch);
-#else
-    if (_playerPitch)
-    {
-        SLresult result = (*_playerPitch)->SetPitch(_playerPitch, (SLpermille)(pitch * 1000));
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioSource::setPitch() failed to set player pitch.");
-        }
-    }
-#endif
     _pitch = pitch;
 }
 
@@ -439,22 +233,7 @@ const Vector3& AudioSource::getVelocity() const
 
 void AudioSource::setVelocity(const Vector3& velocity)
 {
-#ifndef __ANDROID__
     alSourcefv(_alSource, AL_VELOCITY, (ALfloat*)&velocity);
-#else
-    if (_playerDoppler)
-    {
-        SLVec3D v;
-        v.x = velocity.x;
-        v.y = velocity.y;
-        v.z = velocity.z;
-        SLresult result = (*_playerDoppler)->SetVelocityCartesian(_playerDoppler, &v);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioSource::setVelocity - failed to set velocity.");
-        }
-    }
-#endif
     _velocity = velocity;
 }
 
@@ -487,31 +266,15 @@ void AudioSource::setNode(Node* node)
 
 void AudioSource::transformChanged(Transform* transform, long cookie)
 {
-#ifndef __ANDROID__
     if (_node)
     {
         Vector3 translation = _node->getTranslationWorld();
         alSourcefv(_alSource, AL_POSITION, (const ALfloat*)&translation.x);
     }
-#else
-    if (_playerLocation)
-    {
-        SLVec3D position;
-        position.x = transform->getTranslationX();
-        position.y = transform->getTranslationY();
-        position.z = transform->getTranslationZ();
-        SLresult result = (*_playerLocation)->SetLocationCartesian(_playerLocation, &position);
-        if (result != SL_RESULT_SUCCESS)
-        {
-            WARN("AudioSource::transformChanged - failed to update location.");
-        }
-    }
-#endif
 }
 
 AudioSource* AudioSource::clone(NodeCloneContext &context) const
 {
-#ifndef __ANDROID__
     ALuint alSource = 0;
     alGenSources(1, &alSource);
     if (alGetError() != AL_NO_ERROR)
@@ -520,11 +283,7 @@ AudioSource* AudioSource::clone(NodeCloneContext &context) const
         return NULL;
     }
     AudioSource* audioClone = new AudioSource(_buffer, alSource);
-#else
-    // TODO: Implement cloning audio source for Android
-    AudioSource* audioClone = new AudioSource(_buffer, _playerObject);
 
-#endif
     _buffer->addRef();
     audioClone->setLooped(isLooped());
     audioClone->setGain(getGain());

+ 5 - 22
gameplay/src/AudioSource.h

@@ -34,13 +34,14 @@ public:
     };
 
     /**
-     * Create an audio source. This is used to instantiate an Audio Source. Currently only wav, au, raw and .audio files are supported.
-     *
-     * @param path The relative location on disk of the sound file or .audio file.
+     * Create an audio source. This is used to instantiate an Audio Source. Currently only wav, au, and raw files are supported.
+     * Alternately, a URL specifying a Properties object that defines an audio source can be used (where the URL is of the format
+     * "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>" and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional).
      * 
+     * @param url The relative location on disk of the sound file or a URL specifying a Properties object defining an audio source.
      * @return The newly created audio source, or NULL if an audio source cannot be created.
      */
-    static AudioSource* create(const char* path);
+    static AudioSource* create(const char* url);
 
     /**
      * Create an audio source from the given properties object.
@@ -147,17 +148,10 @@ public:
 
 private:
 
-#ifndef __ANDROID__
     /**
      * Constructor that takes an AudioBuffer.
      */
     AudioSource(AudioBuffer* buffer, ALuint source);
-#else
-    /**
-     * Constructor that takes an AudioBuffer.
-     */
-    AudioSource(AudioBuffer* buffer, const SLObjectItf& player);
-#endif
 
     /**
      * Destructor.
@@ -183,18 +177,7 @@ private:
      */
     AudioSource* clone(NodeCloneContext &context) const;
 
-#ifndef __ANDROID__
     ALuint _alSource;
-#else
-    SLObjectItf _playerObject;
-    SL3DDopplerItf _playerDoppler;
-    SL3DLocationItf _playerLocation;
-    SLPlayItf _playerPlay;
-    SLPitchItf _playerPitch;
-    SLSeekItf _playerSeek;
-    SLVolumeItf _playerVolume;
-    SLmillibel _maxVolume;
-#endif
     AudioBuffer* _buffer;
     bool _looped;
     float _gain;

+ 2 - 11
gameplay/src/Base.h

@@ -40,10 +40,6 @@ using std::min;
 using std::max;
 using std::modf;
 
-#ifdef __ANDROID__
-#include <android/asset_manager.h>
-#endif
-
 // Common
 #ifndef NULL
 #define NULL     0
@@ -163,12 +159,8 @@ extern void printError(const char* format, ...);
     #define NOMINMAX
 #endif
 
-// Audio (OpenAL, OpenSL, OggVorbis)
-#ifdef __ANDROID__
-#include <SLES/OpenSLES.h>
-#include <SLES/OpenSLES_Android.h>
-#else
-#ifdef __QNX__
+// Audio (OpenAL/Vorbis)
+#if defined (__QNX__) || defined(__ANDROID__)
 #include <AL/al.h>
 #include <AL/alc.h>
 #elif WIN32
@@ -179,7 +171,6 @@ extern void printError(const char* format, ...);
 #include <OpenAL/alc.h>
 #endif
 #include <vorbis/vorbisfile.h>
-#endif
 
 // Image
 #include <png.h>

+ 250 - 17
gameplay/src/Bundle.cpp

@@ -6,7 +6,7 @@
 #include "Joint.h"
 
 #define BUNDLE_VERSION_MAJOR            1
-#define BUNDLE_VERSION_MINOR            1
+#define BUNDLE_VERSION_MINOR            2
 
 #define BUNDLE_TYPE_SCENE               1
 #define BUNDLE_TYPE_NODE                2
@@ -32,7 +32,7 @@ namespace gameplay
 static std::vector<Bundle*> __bundleCache;
 
 Bundle::Bundle(const char* path) :
-    _path(path), _referenceCount(0), _references(NULL), _file(NULL)
+    _path(path), _referenceCount(0), _references(NULL), _file(NULL), _trackedNodes(NULL)
 {
 }
 
@@ -411,18 +411,106 @@ Scene* Bundle::loadScene(const char* id)
 }
 
 Node* Bundle::loadNode(const char* id)
+{
+    return loadNode(id, NULL);
+}
+
+Node* Bundle::loadNode(const char* id, Scene* sceneContext)
 {
     assert(id);
 
     clearLoadSession();
 
-    Node* node = loadNode(id, NULL, NULL);
-   
+    // Load the node and any referenced joints with node tracking enabled.
+    _trackedNodes = new std::map<std::string, Node*>();
+    Node* node = loadNode(id, sceneContext, NULL);
     if (node)
+        resolveJointReferences(sceneContext, node);
+
+    // Load all animations targeting any nodes or mesh skins under this node's hierarchy.
+    for (unsigned int i = 0; i < _referenceCount; i++)
     {
-        resolveJointReferences(NULL, node);
+        Reference* ref = &_references[i];
+        if (ref->type == BUNDLE_TYPE_ANIMATIONS)
+        {
+            if (fseek(_file, ref->offset, SEEK_SET) != 0)
+            {
+                LOG_ERROR_VARG("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
+                SAFE_DELETE(_trackedNodes);
+                return NULL;
+            }
+            
+            // Read the number of animations in this object.
+            unsigned int animationCount;
+            if (!read(&animationCount))
+            {
+                LOG_ERROR_VARG("Failed to read %s for %s: %s", "animationCount", "Animations");
+                SAFE_DELETE(_trackedNodes);
+                return NULL;
+            }
+
+            for (unsigned int j = 0; j < animationCount; j++)
+            {
+                const std::string id = readString(_file);
+
+                // Read the number of animation channels in this animation.
+                unsigned int animationChannelCount;
+                if (!read(&animationChannelCount))
+                {
+                    LOG_ERROR_VARG("Failed to read %s for %s: %s", "animationChannelCount", "animation", id.c_str());
+                    SAFE_DELETE(_trackedNodes);
+                    return NULL;
+                }
+
+                Animation* animation = NULL;
+                for (unsigned int k = 0; k < animationChannelCount; k++)
+                {
+                    // read targetId
+                    std::string targetId = readString(_file);
+                    if (targetId.empty())
+                    {
+                        LOG_ERROR_VARG("Failed to read %s for %s: %s", "targetId", "animation", id.c_str());
+                        SAFE_DELETE(_trackedNodes);
+                        return NULL;
+                    }
+
+                    // If the target is one of the loaded nodes/joints, then load the animation.
+                    std::map<std::string, Node*>::iterator iter = _trackedNodes->find(targetId);
+                    if (iter != _trackedNodes->end())
+                    {
+                        // Read target attribute
+                        unsigned int targetAttribute;
+                        if (!read(&targetAttribute))
+                        {
+                            LOG_ERROR_VARG("Failed to read %s for %s: %s", "targetAttribute", "animation", id.c_str());
+                            SAFE_DELETE(_trackedNodes);
+                            return NULL;
+                        }
+
+                        AnimationTarget* target = iter->second;
+                        if (!target)
+                        {
+                            LOG_ERROR_VARG("Failed to read %s for %s: %s", "animation target", targetId.c_str(), id.c_str());
+                            SAFE_DELETE(_trackedNodes);
+                            return NULL;
+                        }
+
+                        animation = readAnimationChannelData(animation, id.c_str(), target, targetAttribute);
+                    }
+                    else
+                    {
+                        // Skip the animation channel (passing a target attribute of 
+                        // 0 causes the animation to not be created).
+                        unsigned int data;
+                        read(&data);
+                        readAnimationChannelData(NULL, id.c_str(), NULL, 0);
+                    }
+                }
+            }
+        }
     }
 
+    SAFE_DELETE(_trackedNodes);
     return node;
 }
 
@@ -458,6 +546,44 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext, Node* nodeContext)
     return node;
 }
 
+bool Bundle::skipNode()
+{
+    const char* id = getIdFromOffset();
+
+    // Skip the node's type.
+    unsigned int nodeType;
+    if (!read(&nodeType))
+    {
+        return false;
+    }
+    
+    // Skip over the node's transform and parent ID.
+    fseek(_file, sizeof(float) * 16, SEEK_CUR);
+    readString(_file);
+
+    // Skip over the node's children.
+    unsigned int childrenCount;
+    if (!read(&childrenCount))
+    {
+        return false;
+    }
+    else if (childrenCount > 0)
+    {
+        for (unsigned int i = 0; i < childrenCount; i++)
+        {
+            if (!skipNode())
+                return false;
+        }
+    }
+    
+    // Skip over the node's camera, light, and model attachments.
+    Camera* camera = readCamera(); SAFE_RELEASE(camera);
+    Light* light = readLight(); SAFE_RELEASE(light);
+    Model* model = readModel(id); SAFE_RELEASE(model);
+
+    return true;
+}
+
 Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
 {
     const char* id = getIdFromOffset();
@@ -482,6 +608,39 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
         return NULL;
     }
 
+    // If we are tracking nodes and it's not in the set yet, add it.
+    if (_trackedNodes)
+    {
+        std::map<std::string, Node*>::iterator iter = _trackedNodes->find(id);
+        if (iter != _trackedNodes->end())
+        {
+            SAFE_RELEASE(node);
+
+            // Skip over the node's transform and parent ID.
+            fseek(_file, sizeof(float) * 16, SEEK_CUR);
+            readString(_file);
+
+            // Skip over the node's children.
+            unsigned int childrenCount;
+            if (!read(&childrenCount))
+            {
+                return NULL;
+            }
+            else if (childrenCount > 0)
+            {
+                for (unsigned int i = 0; i < childrenCount; i++)
+                {
+                    if (!skipNode())
+                        return NULL;
+                }
+            }
+
+            return iter->second;
+        }
+        else
+            _trackedNodes->insert(std::make_pair(id, node));
+    }
+
     // If no loading context is set, set this node to the loading context
     if (sceneContext == NULL && nodeContext == NULL)
     {
@@ -497,6 +656,9 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
     }
     setTransform(transform, node);
 
+    // Skip over the parent ID.
+    readString(_file);
+
     // Read children
     unsigned int childrenCount;
     if (!read(&childrenCount))
@@ -509,7 +671,29 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
         // Read each child
         for (unsigned int i = 0; i < childrenCount; i++)
         {
-            Node* child = readNode(sceneContext, nodeContext);
+            // Search the passed in loading contexts (scene/node) first to see
+            // if we've already loaded this child node during this load session.
+            Node* child = NULL;
+            id = getIdFromOffset();
+
+            if (sceneContext)
+            {
+                child = sceneContext->findNode(id, true);
+            }
+            else if (nodeContext)
+            {
+                child = nodeContext->findNode(id, true);
+            }
+            
+            // If the child node wasn't already loaded, load it.
+            if (!child)
+                child = readNode(sceneContext, nodeContext);
+            else
+            {
+                // Otherwise, skip over its data in the file.
+                readNode(NULL, NULL);
+            }
+
             if (child)
             {
                 node->addChild(child);
@@ -535,7 +719,7 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
     }
 
     // Read model
-    Model* model = readModel(sceneContext, nodeContext, node->getId());
+    Model* model = readModel(node->getId());
     if (model)
     {
         node->setModel(model);
@@ -666,7 +850,7 @@ Light* Bundle::readLight()
     return light;
 }
 
-Model* Bundle::readModel(Scene* sceneContext, Node* nodeContext, const char* nodeId)
+Model* Bundle::readModel(const char* nodeId)
 {
     // Read mesh
     Mesh* mesh = NULL;
@@ -688,7 +872,7 @@ Model* Bundle::readModel(Scene* sceneContext, Node* nodeContext, const char* nod
             }
             if (hasSkin)
             {
-                MeshSkin* skin = readMeshSkin(sceneContext, nodeContext);
+                MeshSkin* skin = readMeshSkin();
                 if (skin)
                 {
                     model->setSkin(skin);
@@ -712,7 +896,7 @@ Model* Bundle::readModel(Scene* sceneContext, Node* nodeContext, const char* nod
     return NULL;
 }
 
-MeshSkin* Bundle::readMeshSkin(Scene* sceneContext, Node* nodeContext)
+MeshSkin* Bundle::readMeshSkin()
 {
     MeshSkin* meshSkin = new MeshSkin();
 
@@ -815,19 +999,63 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
         if (jointCount > 0)
         {
             Joint* rootJoint = skinData->skin->getJoint((unsigned int)0);
-            Node* parent = rootJoint->getParent();
-            while (parent)
+            Node* node = rootJoint;
+            Node* parent = node->getParent();
+            
+            while (true)
             {
-                if (skinData->skin->getJointIndex(static_cast<Joint*>(parent)) != -1)
+                if (parent)
+                {
+                    if (skinData->skin->getJointIndex(static_cast<Joint*>(parent)) != -1)
+                    {
+                        // Parent is a joint in the MeshSkin, so treat it as the new root
+                        rootJoint = static_cast<Joint*>(parent);
+                    }
+
+                    node = parent;
+                    parent = node->getParent();
+                }
+                else
                 {
-                    // Parent is a joint in the MeshSkin, so treat it as the new root
-                    rootJoint = static_cast<Joint*>(parent);
+                    std::string nodeID = node->getId();
+
+                    while (true)
+                    {
+                        // Get the node's type.
+                        Reference* ref = find(nodeID.c_str());
+                        if (ref == NULL)
+                        {
+                            LOG_ERROR_VARG("No object with name '%s' in bundle '%s'.", nodeID.c_str(), _path.c_str());
+                            break;
+                        }
+
+                        // Seek to the current node in the file so we can get it's parent ID.
+                        seekTo(nodeID.c_str(), ref->type);
+
+                        // Skip over the node type (1 unsigned int) and transform (16 floats) and read the parent id.
+                        fseek(_file, sizeof(unsigned int) + sizeof(float)*16, SEEK_CUR);
+                        std::string parentID = readString(_file);
+                        
+                        if (parentID.size() > 0)
+                            nodeID = parentID;
+                        else
+                            break;
+                    }
+
+                    if (nodeID != rootJoint->getId())
+                        loadNode(nodeID.c_str(), sceneContext, nodeContext);
+
+                    break;
                 }
-                parent = parent->getParent();
             }
+
             skinData->skin->setRootJoint(rootJoint);
         }
 
+        // Remove the joint hierarchy from the scene since it is owned by the mesh skin.
+        if (sceneContext)
+            sceneContext->removeNode(skinData->skin->_rootNode);
+
         // Done with this MeshSkinData entry
         SAFE_DELETE(_meshSkins[i]);
     }
@@ -906,6 +1134,11 @@ Animation* Bundle::readAnimationChannel(Scene* scene, Animation* animation, cons
         }
     }
 
+    return readAnimationChannelData(animation, animationId, target, targetAttribute);
+}
+
+Animation* Bundle::readAnimationChannelData(Animation* animation, const char* id, AnimationTarget* target, unsigned int targetAttribute)
+{
     std::vector<unsigned long> keyTimes;
     std::vector<float> values;
     std::vector<float> tangentsIn;
@@ -964,7 +1197,7 @@ Animation* Bundle::readAnimationChannel(Scene* scene, Animation* animation, cons
         if (animation == NULL)
         {
             // TODO: This code currently assumes LINEAR only
-            animation = target->createAnimation(animationId, targetAttribute, keyTimesCount, &keyTimes[0], &values[0], Curve::LINEAR);
+            animation = target->createAnimation(id, targetAttribute, keyTimesCount, &keyTimes[0], &values[0], Curve::LINEAR);
         }
         else
         {

+ 31 - 2
gameplay/src/Bundle.h

@@ -16,6 +16,7 @@ namespace gameplay
 class Bundle : public Ref
 {
     friend class PhysicsController;
+    friend class SceneLoader;
 
 public:
 
@@ -205,6 +206,11 @@ private:
      */
     Node* loadNode(const char* id, Scene* sceneContext, Node* nodeContext);
 
+    /**
+     * Internal method for SceneLoader to load a node into a scene.
+     */
+    Node* loadNode(const char* id, Scene* sceneContext);
+
     /**
      * Loads a mesh with the specified ID from the bundle.
      *
@@ -321,7 +327,7 @@ private:
      * 
      * @return A pointer to a new model or NULL if there was an error.
      */
-    Model* readModel(Scene* sceneContext, Node* nodeContext, const char* nodeId);
+    Model* readModel(const char* nodeId);
 
     /**
      * Reads mesh data from the current file position.
@@ -346,7 +352,7 @@ private:
      *
      * @return A pointer to a new mesh skin or NULL if there was an error.
      */
-    MeshSkin* readMeshSkin(Scene* sceneContext, Node* nodeContext);
+    MeshSkin* readMeshSkin();
 
     /**
      * Reads an animation from the current file position.
@@ -373,6 +379,21 @@ private:
      */
     Animation* readAnimationChannel(Scene* scene, Animation* animation, const char* animationId);
 
+    /**
+     * Reads the animation channel data at the current file position into the given animation
+     * (with the given animation target and target attribute).
+     * 
+     * Note: this is used by #loadNode(const char*, Scene*) and #readAnimationChannel(Scene*, Animation*, const char*).
+     * 
+     * @param animation The animation to the load channel into.
+     * @param id The ID of the animation that this channel is loaded into.
+     * @param target The animation target.
+     * @param targetAttribute The target attribute being animated.
+     * 
+     * @return The animation that the channel was loaded into.
+     */
+    Animation* readAnimationChannelData(Animation* animation, const char* id, AnimationTarget* target, unsigned int targetAttribute);
+
     /**
      * Sets the transformation matrix.
      *
@@ -388,12 +409,20 @@ private:
 
 private:
 
+    /**
+     * Skips over a Node's data within a bundle.
+     *
+     * @return True if the Node was successfully skipped; false otherwise.
+     */
+    bool skipNode();
+
     std::string _path;
     unsigned int _referenceCount;
     Reference* _references;
     FILE* _file;
 
     std::vector<MeshSkinData*> _meshSkins;
+    std::map<std::string, Node*>* _trackedNodes;
 };
 
 }

+ 19 - 78
gameplay/src/Control.cpp

@@ -827,8 +827,8 @@ namespace gameplay
             value->setFloat(1, _bounds.y);
             break;
         case ANIMATE_SIZE:
-            value->setFloat(0, _clipBounds.width);
-            value->setFloat(1, _clipBounds.height);
+            value->setFloat(0, _bounds.width);
+            value->setFloat(1, _bounds.height);
             break;
         case ANIMATE_POSITION_X:
             value->setFloat(0, _bounds.x);
@@ -837,10 +837,10 @@ namespace gameplay
             value->setFloat(0, _bounds.y);
             break;
         case ANIMATE_SIZE_WIDTH:
-            value->setFloat(0, _clipBounds.width);
+            value->setFloat(0, _bounds.width);
             break;
         case ANIMATE_SIZE_HEIGHT:
-            value->setFloat(0, _clipBounds.height);
+            value->setFloat(0, _bounds.height);
             break;
         case ANIMATE_OPACITY:
         default:
@@ -853,96 +853,37 @@ namespace gameplay
         switch(propertyId)
         {
         case ANIMATE_POSITION:
-            applyAnimationValuePositionX(value->getFloat(0), blendWeight);
-            applyAnimationValuePositionY(value->getFloat(1), blendWeight);
+            _bounds.x = Curve::lerp(blendWeight, _bounds.x, value->getFloat(0));
+            _bounds.y = Curve::lerp(blendWeight, _bounds.y, value->getFloat(1));
+            _dirty = true;
             break;
         case ANIMATE_POSITION_X:
-            applyAnimationValuePositionX(value->getFloat(0), blendWeight);
+            _bounds.x = Curve::lerp(blendWeight, _bounds.x, value->getFloat(0));
+            _dirty = true;
             break;
         case ANIMATE_POSITION_Y:
-            applyAnimationValuePositionY(value->getFloat(0), blendWeight);
+            _bounds.y = Curve::lerp(blendWeight, _bounds.y, value->getFloat(0));
+            _dirty = true;
             break;
         case ANIMATE_SIZE:
-            applyAnimationValueSizeWidth(value->getFloat(0), blendWeight);
-            applyAnimationValueSizeHeight(value->getFloat(1), blendWeight);
+            _bounds.width = Curve::lerp(blendWeight, _bounds.width, value->getFloat(0));
+            _bounds.height = Curve::lerp(blendWeight, _bounds.height, value->getFloat(1));
+            _dirty = true;
             break;
         case ANIMATE_SIZE_WIDTH:
-            applyAnimationValueSizeWidth(value->getFloat(0), blendWeight);
+            _bounds.width = Curve::lerp(blendWeight, _bounds.width, value->getFloat(0));
+            _dirty = true;
             break;
         case ANIMATE_SIZE_HEIGHT:
-            applyAnimationValueSizeHeight(value->getFloat(0), blendWeight);
+            _bounds.height = Curve::lerp(blendWeight, _bounds.height, value->getFloat(0));
+            _dirty = true;
             break;
         case ANIMATE_OPACITY:
-            applyAnimationValueOpacity();
+            _dirty = true;
         default:
             break;
         }
     }
-
-    void Control::applyAnimationValuePositionX(float x, float blendWeight)
-    {
-        if ((_animationPropertyBitFlag & ANIMATION_POSITION_X_BIT) != ANIMATION_POSITION_X_BIT)
-        {
-            _animationPropertyBitFlag |= ANIMATION_POSITION_X_BIT;
-        }
-        else
-        {
-            x = Curve::lerp(blendWeight, _bounds.x, x);
-        }
-        _bounds.x = x;
-        _dirty = true;
-    }
-    
-    void Control::applyAnimationValuePositionY(float y, float blendWeight)
-    {
-        if ((_animationPropertyBitFlag & ANIMATION_POSITION_Y_BIT) != ANIMATION_POSITION_Y_BIT)
-        {
-            _animationPropertyBitFlag |= ANIMATION_POSITION_Y_BIT;
-        }
-        else
-        {
-            y = Curve::lerp(blendWeight, _bounds.y, y);
-        }
-        _bounds.y = y;
-        _dirty = true;
-    }
-    
-    void Control::applyAnimationValueSizeWidth(float width, float blendWeight)
-    {
-        if ((_animationPropertyBitFlag & ANIMATION_SIZE_WIDTH_BIT) != ANIMATION_SIZE_WIDTH_BIT)
-        {
-            _animationPropertyBitFlag |= ANIMATION_SIZE_WIDTH_BIT;
-        }
-        else
-        {
-            width = Curve::lerp(blendWeight, _clipBounds.width, width);
-        }
-        _clipBounds.width = width;
-        _dirty = true;
-    }
-
-    void Control::applyAnimationValueSizeHeight(float height, float blendWeight)
-    {
-        if ((_animationPropertyBitFlag & ANIMATION_SIZE_HEIGHT_BIT) != ANIMATION_SIZE_HEIGHT_BIT)
-        {
-            _animationPropertyBitFlag |= ANIMATION_SIZE_HEIGHT_BIT;
-        }
-        else
-        {
-            height = Curve::lerp(blendWeight, _clipBounds.height, height);
-        }
-        _clipBounds.height = height;
-        _dirty = true;
-    }
-
-    void Control::applyAnimationValueOpacity()
-    {
-        if ((_animationPropertyBitFlag & ANIMATION_OPACITY_BIT) != ANIMATION_OPACITY_BIT)
-        {
-            _animationPropertyBitFlag |= ANIMATION_OPACITY_BIT;
-        }
-        _dirty = true;
-    }
     
     Theme::Style::Overlay** Control::getOverlays(unsigned char overlayTypes, Theme::Style::Overlay** overlays)
     {

+ 0 - 16
gameplay/src/Control.h

@@ -877,26 +877,10 @@ protected:
 
 private:
 
-    static const char ANIMATION_POSITION_X_BIT = 0x01;
-    static const char ANIMATION_POSITION_Y_BIT = 0x02;
-    static const char ANIMATION_SIZE_WIDTH_BIT = 0x04;
-    static const char ANIMATION_SIZE_HEIGHT_BIT = 0x08;
-    static const char ANIMATION_OPACITY_BIT = 0x10;
-
     /*
      * Constructor.
      */    
     Control(const Control& copy);
-    
-    void applyAnimationValuePositionX(float x, float blendWeight);
-    
-    void applyAnimationValuePositionY(float y, float blendWeight);
-    
-    void applyAnimationValueSizeWidth(float width, float blendWeight);
-    
-    void applyAnimationValueSizeHeight(float height, float blendWeight);
-    
-    void applyAnimationValueOpacity();
 
     Theme::Style::Overlay** getOverlays(unsigned char overlayTypes, Theme::Style::Overlay** overlays);
 

+ 4 - 4
gameplay/src/Form.cpp

@@ -36,18 +36,18 @@ namespace gameplay
         }
     }
 
-    Form* Form::create(const char* path)
+    Form* Form::create(const char* url)
     {
         // Load Form from .form file.
-        assert(path);
+        assert(url);
 
-        Properties* properties = Properties::create(path);
+        Properties* properties = Properties::create(url);
         assert(properties);
         if (properties == NULL)
             return NULL;
 
         // Check if the Properties is valid and has a valid namespace.
-        Properties* formProperties = properties->getNextNamespace();
+        Properties* formProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
         assert(formProperties);
         if (!formProperties || !(strcmp(formProperties->getNamespace(), "form") == 0))
         {

+ 6 - 4
gameplay/src/Form.h

@@ -50,11 +50,13 @@ class Form : public Container
 public:
 
     /**
-     * Create from properties file.
-     *
-     * @param path Path to the properties file to create a new form from.
+     * Creates a form using the data from the Properties object defined at the specified URL, 
+     * where the URL is of the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
+     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional). 
+     * 
+     * @param url The URL pointing to the Properties object defining the animation data. 
      */
-    static Form* create(const char* path);
+    static Form* create(const char* url);
 
     /**
      * Get a form from its ID.

+ 4 - 4
gameplay/src/Material.cpp

@@ -32,19 +32,19 @@ Material::~Material()
     }
 }
 
-Material* Material::create(const char* materialPath)
+Material* Material::create(const char* url)
 {
-    assert(materialPath);
+    assert(url);
 
     // Load the material properties from file
-    Properties* properties = Properties::create(materialPath);
+    Properties* properties = Properties::create(url);
     assert(properties);
     if (properties == NULL)
     {
         return NULL;
     }
 
-    Material* material = create(properties->getNextNamespace());
+    Material* material = create((strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace());
     SAFE_DELETE(properties);
 
     return material;

+ 6 - 4
gameplay/src/Material.h

@@ -28,13 +28,15 @@ class Material : public RenderState
 public:
 
     /**
-     * Creates a material from a specified file path.
-     *
-     * @param materialPath Path path to the material file.
+     * Creates a material using the data from the Properties object defined at the specified URL, 
+     * where the URL is of the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
+     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional). 
+     * 
+     * @param url The URL pointing to the Properties object defining the material.
      * 
      * @return A new Material.
      */
-    static Material* create(const char* materialPath);
+    static Material* create(const char* url);
 
     /**
      * Creates a material from the specified properties object.

+ 6 - 45
gameplay/src/MaterialParameter.cpp

@@ -394,50 +394,21 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
                 case FLOAT:
                 {
                     if (_count == 1)
-                    {
-                        if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
-                        {
-                            _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
-                            _value.floatValue = value->getFloat(0);
-                        }
-                        else
-                        {
-                            _value.floatValue = Curve::lerp(blendWeight, _value.floatValue, value->getFloat(0));
-                        }
-                    }
+                        _value.floatValue = Curve::lerp(blendWeight, _value.floatValue, value->getFloat(0));
                     else
-                    {
                         applyAnimationValue(value, blendWeight, 1);
-                    }                    
                     break;
                 }
                 case INT:
                 {
                     if (_count == 1)
                     {
-                        if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
-                        {
-                            _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
-                            _value.intValue = value->getFloat(0);
-                        }
-                        else
-                        {
-                            _value.intValue = Curve::lerp(blendWeight, _value.intValue, value->getFloat(0));
-                        }
+                        _value.intValue = Curve::lerp(blendWeight, _value.intValue, value->getFloat(0));
                     }
                     else
                     {
-                        if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
-                        {
-                            _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
-                            for (unsigned int i = 0; i < _count; i++)
-                                _value.intPtrValue[i] = value->getFloat(i);
-                        }
-                        else
-                        {
-                            for (unsigned int i = 0; i < _count; i++)
-                                _value.intPtrValue[i] = Curve::lerp(blendWeight, _value.intPtrValue[i], value->getFloat(i));
-                        }
+                        for (unsigned int i = 0; i < _count; i++)
+                            _value.intPtrValue[i] = Curve::lerp(blendWeight, _value.intPtrValue[i], value->getFloat(i));
                     }
                     break;
                 }
@@ -466,18 +437,8 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
 void MaterialParameter::applyAnimationValue(AnimationValue* value, float blendWeight, int components)
 {
     unsigned int count = _count * components;
-    if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
-    {
-        _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
-
-        for (unsigned int i = 0; i < count; i++)
-            _value.floatPtrValue[i] = value->getFloat(i);
-    }
-    else
-    {
-        for (unsigned int i = 0; i < count; i++)
-            _value.floatPtrValue[i] = Curve::lerp(blendWeight, _value.floatPtrValue[i], value->getFloat(i));
-    }
+    for (unsigned int i = 0; i < count; i++)
+        _value.floatPtrValue[i] = Curve::lerp(blendWeight, _value.floatPtrValue[i], value->getFloat(i));
 }
 
 void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const

+ 1 - 3
gameplay/src/MaterialParameter.h

@@ -167,9 +167,7 @@ public:
     void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
 
 private:
-
-    static const char ANIMATION_UNIFORM_BIT = 0x01;
-    
+   
     /**
      * Constructor.
      */

+ 13 - 2
gameplay/src/MeshSkin.cpp

@@ -57,7 +57,7 @@ Joint* MeshSkin::getJoint(const char* id) const
     return NULL;
 }
 
-MeshSkin* MeshSkin::clone() const
+MeshSkin* MeshSkin::clone(NodeCloneContext &context) const
 {
     MeshSkin* skin = new MeshSkin();
     skin->_bindShape = _bindShape;
@@ -67,7 +67,18 @@ MeshSkin* MeshSkin::clone() const
         skin->setJointCount(jointCount);
 
         assert(skin->_rootNode == NULL);
-        skin->_rootNode = _rootNode->clone();
+        
+        // Check if the root node has already been cloned.
+        if (Node* rootNode = context.findClonedNode(_rootNode))
+        {
+            skin->_rootNode = rootNode;
+            rootNode->addRef();
+        }
+        else
+        {
+            skin->_rootNode = _rootNode->cloneRecursive(context);
+        }
+        
         Node* node = skin->_rootNode->findNode(_rootJoint->getId());
         assert(node);
         skin->_rootJoint = static_cast<Joint*>(node);

+ 4 - 1
gameplay/src/MeshSkin.h

@@ -20,6 +20,7 @@ class MeshSkin : public Transform::Listener
     friend class Bundle;
     friend class Model;
     friend class Joint;
+    friend class Node;
 
 public:
 
@@ -134,9 +135,11 @@ private:
     /**
      * Clones the MeshSkin and the joints that it references.
      * 
+     * @param context The clone context.
+     * 
      * @return The newly created MeshSkin.
      */
-    MeshSkin* clone() const;
+    MeshSkin* clone(NodeCloneContext &context) const;
 
     /**
      * Sets the number of joints that can be stored in this skin.

+ 1 - 1
gameplay/src/Model.cpp

@@ -354,7 +354,7 @@ Model* Model::clone(NodeCloneContext &context)
     Model* model = Model::create(getMesh());
     if (getSkin())
     {
-        model->setSkin(getSkin()->clone());
+        model->setSkin(getSkin()->clone(context));
     }
     Material* materialClone = getMaterial()->clone(context);
     model->setMaterial(materialClone); // TODO: Don't forget material parts

+ 25 - 7
gameplay/src/Node.cpp

@@ -286,6 +286,20 @@ unsigned int Node::getChildCount() const
 Node* Node::findNode(const char* id, bool recursive, bool exactMatch) const
 {
     assert(id);
+
+    // If the node has a model with a mesh skin, search the skin's hierarchy as well.
+    Node* rootNode = NULL;
+    if (_model != NULL && _model->getSkin() != NULL && (rootNode = _model->getSkin()->_rootNode) != NULL)
+    {
+        if ((exactMatch && rootNode->_id == id) || (!exactMatch && rootNode->_id.find(id) == 0))
+            return rootNode;
+        
+        Node* match = rootNode->findNode(id, true, exactMatch);
+        if (match)
+        {
+            return match;
+        }
+    }
     
     // Search immediate children first.
     for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
@@ -866,10 +880,16 @@ Node* Node::cloneRecursive(NodeCloneContext &context) const
 {
     Node* copy = cloneSingleNode(context);
 
+    Node* lastChild = NULL;
     for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
+    {
+        lastChild = child;
+    }
+    // Loop through the nodes backwards because addChild adds the node to the front.
+    for (Node* child = lastChild; child != NULL; child = child->getPreviousSibling())
     {
         Node* childCopy = child->cloneRecursive(context);
-        copy->addChild(childCopy); // TODO: Does child order matter?
+        copy->addChild(childCopy);
         childCopy->release();
     }
     return copy;
@@ -880,8 +900,6 @@ void Node::cloneInto(Node* node, NodeCloneContext &context) const
     Transform::cloneInto(node, context);
 
     // TODO: Clone the rest of the node data.
-    //node->setCamera(getCamera());
-    //node->setLight(getLight());
 
     if (Camera* camera = getCamera())
     {
@@ -994,18 +1012,18 @@ PhysicsCollisionObject* Node::setCollisionObject(PhysicsCollisionObject::Type ty
     return _collisionObject;
 }
 
-PhysicsCollisionObject* Node::setCollisionObject(const char* filePath)
+PhysicsCollisionObject* Node::setCollisionObject(const char* url)
 {
     // Load the collision object properties from file.
-    Properties* properties = Properties::create(filePath);
+    Properties* properties = Properties::create(url);
     assert(properties);
     if (properties == NULL)
     {
-        WARN_VARG("Failed to load collision object file: %s", filePath);
+        WARN_VARG("Failed to load collision object file: %s", url);
         return NULL;
     }
 
-    PhysicsCollisionObject* collisionObject = setCollisionObject(properties->getNextNamespace());
+    PhysicsCollisionObject* collisionObject = setCollisionObject((strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace());
     SAFE_DELETE(properties);
 
     return collisionObject;

+ 5 - 3
gameplay/src/Node.h

@@ -520,11 +520,13 @@ public:
     PhysicsCollisionObject* setCollisionObject(PhysicsCollisionObject::Type type, const PhysicsCollisionShape::Definition& shape, PhysicsRigidBody::Parameters* rigidBodyParameters = NULL);
 
     /**
-     * Sets the physics collision object for this node using the definition in the given file.
+     * Sets the physics collision object for this node using the data from the Properties object defined at the specified URL, 
+     * where the URL is of the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
+     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional). 
      * 
-     * @param filePath The path to the file that set the collision object definition.
+     * @param url The URL pointing to the Properties object defining the physics collision object.
      */
-    PhysicsCollisionObject* setCollisionObject(const char* filePath);
+    PhysicsCollisionObject* setCollisionObject(const char* url);
 
     /**
      * Sets the physics collision object for this node from the given properties object.

+ 5 - 5
gameplay/src/ParticleEmitter.cpp

@@ -82,18 +82,18 @@ ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlendin
     return emitter;
 }
 
-ParticleEmitter* ParticleEmitter::create(const char* particleFile)
+ParticleEmitter* ParticleEmitter::create(const char* url)
 {
-    assert(particleFile);
+    assert(url);
 
-    Properties* properties = Properties::create(particleFile);
+    Properties* properties = Properties::create(url);
     if (!properties)
     {
-        LOG_ERROR_VARG("Error loading ParticleEmitter: Could not load file: %s", particleFile);
+        LOG_ERROR_VARG("Error loading ParticleEmitter: Could not load file: %s", url);
         return NULL;
     }
 
-    ParticleEmitter* particle = create(properties->getNextNamespace());
+    ParticleEmitter* particle = create((strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace());
     SAFE_DELETE(properties);
 
     return particle;

+ 6 - 4
gameplay/src/ParticleEmitter.h

@@ -154,13 +154,15 @@ public:
     };
 
     /**
-     * Creates a particle emitter from a .particle file.
-     *
-     * @param particleFile The .particle file to load.
+     * Creates a particle emitter using the data from the Properties object defined at the specified URL, 
+     * where the URL is of the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
+     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional). 
+     * 
+     * @param url The URL pointing to the Properties object defining the particle emitter.
      * 
      * @return An initialized ParticleEmitter.
      */
-    static ParticleEmitter* create(const char* particleFile);
+    static ParticleEmitter* create(const char* url);
 
     /**
      * Creates a particle emitter from the specified properties object.

+ 92 - 4
gameplay/src/Properties.cpp

@@ -46,11 +46,40 @@ Properties::Properties(FILE* file, const char* name, const char* id, const char*
     rewind();
 }
 
-Properties* Properties::create(const char* filePath)
+Properties* Properties::create(const char* url)
 {
-    assert(filePath);
+    assert(url);
 
-    FILE* file = FileSystem::openFile(filePath, "rb");
+    if (!url || strlen(url) == 0)
+    {
+        WARN("Attempting to create a Properties object from an empty URL!");
+        return NULL;
+    }
+
+    std::string urlString = url;
+    std::string fileString;
+    std::vector<std::string> namespacePath;
+
+    // If the url references a specific namespace within the file,
+    // calculate the full namespace path to the final namespace.
+    unsigned int loc = urlString.rfind("#");
+    if (loc != urlString.npos)
+    {
+        fileString = urlString.substr(0, loc);
+        std::string namespacePathString = urlString.substr(loc + 1);
+        while ((loc = namespacePathString.find("/")) != namespacePathString.npos)
+        {
+            namespacePath.push_back(namespacePathString.substr(0, loc));
+            namespacePathString = namespacePathString.substr(loc + 1);
+        }
+        namespacePath.push_back(namespacePathString);
+    }
+    else
+    {
+        fileString = url;
+    }
+
+    FILE* file = FileSystem::openFile(fileString.c_str(), "rb");
     if (!file)
     {
         return NULL;
@@ -62,7 +91,46 @@ Properties* Properties::create(const char* filePath)
 
     fclose(file);
 
-    return properties;
+    // If the url references a specific namespace within the file,
+    // return the specified namespace or notify the user if it cannot be found.
+    Properties* originalProperties = properties;
+    if (namespacePath.size() > 0)
+    {
+        unsigned int size = namespacePath.size();
+        Properties* iter = properties->getNextNamespace();
+        for (unsigned int i = 0; i < size;)
+        {
+            while (true)
+            {
+                if (strcmp(iter->getId(), namespacePath[i].c_str()) == 0)
+                {
+                    if (i != size - 1)
+                    {
+                        properties = iter->getNextNamespace();
+                        iter = properties;
+                    }
+                    else
+                        properties = iter;
+
+                    i++;
+                    break;
+                }
+                
+                iter = properties->getNextNamespace();
+                if (iter == NULL)
+                {
+                    WARN_VARG("Failed to load Properties object from URL '%s'.", url);
+                    return NULL;
+                }
+            }
+        }
+
+        properties = properties->clone();
+        SAFE_DELETE(originalProperties);
+        return properties;
+    }
+    else
+        return properties;
 }
 
 void Properties::readProperties(FILE* file)
@@ -817,4 +885,24 @@ bool Properties::getColor(const char* name, Vector4* out) const
     return false;
 }
 
+Properties* Properties::clone()
+{
+    Properties* p = new Properties();
+    
+    p->_namespace = _namespace;
+    p->_id = _id;
+    p->_parentID = _parentID;
+    p->_properties = _properties;
+    p->_propertiesItr = p->_properties.end();
+
+    unsigned int count = _namespaces.size();
+    for (unsigned int i = 0; i < count; i++)
+    {
+        p->_namespaces.push_back(_namespaces[i]->clone());
+    }
+    p->_namespacesItr = p->_namespaces.end();
+
+    return p;
+}
+
 }

+ 9 - 4
gameplay/src/Properties.h

@@ -142,11 +142,13 @@ public:
     };
 
     /**
-     * Creates a Properties runtime settings from a specified file path.
-     *
-     * @param filePath The file to create the properties from.
+     * Creates a Properties runtime settings from the specified URL, where the URL is of
+     * the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
+     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional).
+     * 
+     * @param url The URL to create the properties from.
      */
-    static Properties* create(const char* filePath);
+    static Properties* create(const char* url);
 
     /**
      * Destructor.
@@ -391,6 +393,9 @@ private:
     // Called by resolveInheritance().
     void mergeWith(Properties* overrides);
 
+    // Clones the Properties object.
+    Properties* clone();
+
     std::string _namespace;
     std::string _id;
     std::string _parentID;

+ 10 - 9
gameplay/src/SceneLoader.cpp

@@ -11,21 +11,21 @@ std::vector<SceneLoader::SceneAnimation> SceneLoader::_animations;
 std::vector<SceneLoader::SceneNode> SceneLoader::_sceneNodes;
 std::string SceneLoader::_path;
 
-Scene* SceneLoader::load(const char* filePath)
+Scene* SceneLoader::load(const char* url)
 {
-    assert(filePath);
+    assert(url);
 
     // Load the scene properties from file.
-    Properties* properties = Properties::create(filePath);
+    Properties* properties = Properties::create(url);
     assert(properties);
     if (properties == NULL)
     {
-        WARN_VARG("Failed to load scene file: %s", filePath);
+        WARN_VARG("Failed to load scene file: %s", url);
         return NULL;
     }
 
     // Check if the properties object is valid and has a valid namespace.
-    Properties* sceneProperties = properties->getNextNamespace();
+    Properties* sceneProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
     assert(sceneProperties);
     if (!sceneProperties || !(strcmp(sceneProperties->getNamespace(), "scene") == 0))
     {
@@ -36,6 +36,7 @@ Scene* SceneLoader::load(const char* filePath)
 
     // Get the path to the main GPB.
     _path = sceneProperties->getString("path");
+
     // Build the node URL/property and animation reference tables and load the referenced files.
     buildReferenceTables(sceneProperties);
     loadReferencedFiles();
@@ -452,7 +453,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                 {
                     if (sceneNode._exactMatch)
                     {
-                        Node* node = tmpBundle->loadNode(snp._id.c_str());
+                        Node* node = tmpBundle->loadNode(snp._id.c_str(), scene);
                         if (node)
                         {
                             node->setId(sceneNode._nodeID);
@@ -641,7 +642,7 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
         }
         else
         {
-            // TODO: Should we ignore these items? They could be used for generic properties file inheritence.
+            // TODO: Should we ignore these items? They could be used for generic properties file inheritance.
             WARN_VARG("Unsupported child namespace (of 'scene'): %s", ns->getNamespace());
         }
     }
@@ -772,8 +773,8 @@ Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
         return NULL;
     }
 
-    const char* sceneID = strlen(sceneProperties->getId()) == 0 ? NULL : sceneProperties->getId();
-    Scene* scene = bundle->loadScene(sceneID);
+    // TODO: Support loading a specific scene from a GPB file using the URL syntax (i.e. "res/scene.gpb#myscene").
+    Scene* scene = bundle->loadScene(NULL);
     if (!scene)
     {
         WARN_VARG("Failed to load scene from '%s'.", _path.c_str());

+ 6 - 2
gameplay/src/SceneLoader.h

@@ -20,9 +20,13 @@ class SceneLoader
 private:
 
     /**
-     * Loads a scene file file.
+     * Loads a scene using the data from the Properties object defined at the specified URL, 
+     * where the URL is of the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
+     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional). 
+     * 
+     * @param url The URL pointing to the Properties object defining the scene.
      */
-    static Scene* load(const char* filePath);
+    static Scene* load(const char* url);
     
     /**
      * Helper structures and functions for SceneLoader::load(const char*).

+ 6 - 6
gameplay/src/Theme.cpp

@@ -52,15 +52,15 @@ namespace gameplay
         }
     }
 
-    Theme* Theme::create(const char* path)
+    Theme* Theme::create(const char* url)
     {
-        assert(path);
+        assert(url);
 
         // Search theme cache first.
         for (unsigned int i = 0, count = __themeCache.size(); i < count; ++i)
         {
             Theme* t = __themeCache[i];
-            if (t->_path == path)
+            if (t->_url == url)
             {
                 // Found a match.
                 t->addRef();
@@ -70,7 +70,7 @@ namespace gameplay
         }
 
         // Load theme properties from file path.
-        Properties* properties = Properties::create(path);
+        Properties* properties = Properties::create(url);
         assert(properties);
         if (properties == NULL)
         {
@@ -78,7 +78,7 @@ namespace gameplay
         }
 
         // Check if the Properties is valid and has a valid namespace.
-        Properties* themeProperties = properties->getNextNamespace();
+        Properties* themeProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
         assert(themeProperties);
         if (!themeProperties || !(strcmp(themeProperties->getNamespace(), "theme") == 0))
         {
@@ -88,7 +88,7 @@ namespace gameplay
 
         // Create a new theme.
         Theme* theme = new Theme();
-        theme->_path = path;
+        theme->_url = url;
         
         // Parse the Properties object and set up the theme.
         const char* textureFile = themeProperties->getString("texture");

+ 7 - 5
gameplay/src/Theme.h

@@ -407,13 +407,15 @@ private:
     ~Theme();
 
     /**
-     * Creates an instance of a Theme from a theme file.
-     *
-     * @param path Path to a theme file.
+     * Creates an instance of a Theme using the data from the Properties object defined at the specified URL, 
+     * where the URL is of the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
+     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional). 
+     * 
+     * @param url The URL pointing to the Properties object defining the theme.
      *
      * @return A new Theme.
      */
-    static Theme* create(const char* path);
+    static Theme* create(const char* url);
 
     Theme::Style* getStyle(const char* id) const;
 
@@ -425,7 +427,7 @@ private:
 
     void lookUpSprites(const Properties* overlaySpace, ImageList** imageList, ThemeImage** mouseCursor, Skin** skin);
 
-    std::string _path;
+    std::string _url;
     Texture* _texture;
     SpriteBatch* _spriteBatch;
     std::vector<Style*> _styles;

+ 1 - 10
gameplay/src/ThemeStyle.cpp

@@ -451,16 +451,7 @@ void Theme::Style::Overlay::setAnimationPropertyValue(int propertyId, AnimationV
     {
         case ANIMATE_OPACITY:
         {
-            float opacity = value->getFloat(0);
-            if ((_animationPropertyBitFlag & ANIMATION_OPACITY_BIT) != ANIMATION_OPACITY_BIT)
-            {
-                _animationPropertyBitFlag |= ANIMATION_OPACITY_BIT;
-            }
-            else
-            {
-                opacity = Curve::lerp(blendWeight, _opacity, opacity);
-            }
-            _opacity = opacity;
+            _opacity = Curve::lerp(blendWeight, _opacity, value->getFloat(0));
             break;
         }
         default:

+ 1 - 4
gameplay/src/ThemeStyle.h

@@ -49,7 +49,6 @@ private:
     private:
 
         static const int ANIMATE_OPACITY = 1;
-        static const char ANIMATION_OPACITY_BIT = 0x01;
 
         Overlay();
             
@@ -145,9 +144,7 @@ private:
         void setImageList(Theme::ImageList* imageList);
             
         Theme::ImageList* getImageList() const;
-
-        void applyAnimationValueOpacity(float opacity, float blendWeight);
-
+        
         Skin* _skin;
         Theme::ThemeImage* _cursor;
         Theme::ImageList* _imageList;

+ 20 - 97
gameplay/src/Transform.cpp

@@ -665,80 +665,66 @@ void Transform::setAnimationPropertyValue(int propertyId, AnimationValue* value,
     {
         case ANIMATE_SCALE_UNIT:
         {
-            applyAnimationValueScaleX(value->getFloat(0), blendWeight);
-            applyAnimationValueScaleY(value->getFloat(0), blendWeight);
-            applyAnimationValueScaleZ(value->getFloat(0), blendWeight);
+            float scale = Curve::lerp(blendWeight, _scale.x, value->getFloat(0));
+            setScale(scale);
             break;
         }   
         case ANIMATE_SCALE:
         {
-            applyAnimationValueScaleX(value->getFloat(0), blendWeight);
-            applyAnimationValueScaleY(value->getFloat(1), blendWeight);
-            applyAnimationValueScaleZ(value->getFloat(2), blendWeight);
+            setScale(Curve::lerp(blendWeight, _scale.x, value->getFloat(0)), Curve::lerp(blendWeight, _scale.y, value->getFloat(1)), Curve::lerp(blendWeight, _scale.z, value->getFloat(2)));
             break;
         }
         case ANIMATE_SCALE_X:
         {
-            applyAnimationValueScaleX(value->getFloat(0), blendWeight);
+            setScaleX(Curve::lerp(blendWeight, _scale.x, value->getFloat(0)));
             break;
         }
         case ANIMATE_SCALE_Y:
         {
-            applyAnimationValueScaleY(value->getFloat(0), blendWeight);
+            setScaleY(Curve::lerp(blendWeight, _scale.y, value->getFloat(0)));
             break;
         }
         case ANIMATE_SCALE_Z:
         {
-            applyAnimationValueScaleZ(value->getFloat(0), blendWeight);
+            setScaleZ(Curve::lerp(blendWeight, _scale.z, value->getFloat(0)));
             break;
         }
         case ANIMATE_ROTATE:
         {
-            Quaternion q(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
-            applyAnimationValueRotation(&q, blendWeight);
+            applyAnimationValueRotation(value, 0, blendWeight);
             break;
         }
         case ANIMATE_TRANSLATE:
         {
-            applyAnimationValueTranslationX(value->getFloat(0), blendWeight);
-            applyAnimationValueTranslationY(value->getFloat(1), blendWeight);
-            applyAnimationValueTranslationZ(value->getFloat(2), blendWeight);
+            setTranslation(Curve::lerp(blendWeight, _translation.x, value->getFloat(0)), Curve::lerp(blendWeight, _translation.y, value->getFloat(1)), Curve::lerp(blendWeight, _translation.z, value->getFloat(2)));
             break;
         }
         case ANIMATE_TRANSLATE_X:
         {
-            applyAnimationValueTranslationX(value->getFloat(0), blendWeight);
+            setTranslationX(Curve::lerp(blendWeight, _translation.x, value->getFloat(0)));
             break;
         }
         case ANIMATE_TRANSLATE_Y:
         {
-            applyAnimationValueTranslationY(value->getFloat(0), blendWeight);
+            setTranslationY(Curve::lerp(blendWeight, _translation.y, value->getFloat(0)));
             break;
         }
         case ANIMATE_TRANSLATE_Z:
         {
-            applyAnimationValueTranslationZ(value->getFloat(0), blendWeight);
+            setTranslationZ(Curve::lerp(blendWeight, _translation.z, value->getFloat(0)));
             break;
         }
         case ANIMATE_ROTATE_TRANSLATE:
         {
-            Quaternion q(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
-            applyAnimationValueRotation(&q, blendWeight);
-            applyAnimationValueTranslationX(value->getFloat(4), blendWeight);
-            applyAnimationValueTranslationY(value->getFloat(5), blendWeight);
-            applyAnimationValueTranslationZ(value->getFloat(6), blendWeight);
+            applyAnimationValueRotation(value, 0, blendWeight);
+            setTranslation(Curve::lerp(blendWeight, _translation.x, value->getFloat(4)), Curve::lerp(blendWeight, _translation.y, value->getFloat(5)), Curve::lerp(blendWeight, _translation.z, value->getFloat(6)));
             break;
         }
         case ANIMATE_SCALE_ROTATE_TRANSLATE:
         {
-            applyAnimationValueScaleX(value->getFloat(0), blendWeight);
-            applyAnimationValueScaleY(value->getFloat(1), blendWeight);
-            applyAnimationValueScaleZ(value->getFloat(2), blendWeight);
-            Quaternion q(value->getFloat(3), value->getFloat(4), value->getFloat(5), value->getFloat(6));
-            applyAnimationValueRotation(&q, blendWeight);
-            applyAnimationValueTranslationX(value->getFloat(7), blendWeight);
-            applyAnimationValueTranslationY(value->getFloat(8), blendWeight);
-            applyAnimationValueTranslationZ(value->getFloat(9), blendWeight);
+            setScale(Curve::lerp(blendWeight, _scale.x, value->getFloat(0)), Curve::lerp(blendWeight, _scale.y, value->getFloat(1)), Curve::lerp(blendWeight, _scale.z, value->getFloat(2)));
+            applyAnimationValueRotation(value, 3, blendWeight);
+            setTranslation(Curve::lerp(blendWeight, _translation.x, value->getFloat(7)), Curve::lerp(blendWeight, _translation.y, value->getFloat(8)), Curve::lerp(blendWeight, _translation.z, value->getFloat(9)));
             break;
         }
         default:
@@ -798,74 +784,11 @@ void Transform::cloneInto(Transform* transform, NodeCloneContext &context) const
     transform->_translation.set(_translation);
 }
 
-void Transform::applyAnimationValueScaleX(float sx, float blendWeight)
+void Transform::applyAnimationValueRotation(AnimationValue* value, unsigned int index, float blendWeight)
 {
-    if ((_animationPropertyBitFlag & ANIMATION_SCALE_X_BIT) != ANIMATION_SCALE_X_BIT)
-        _animationPropertyBitFlag |= ANIMATION_SCALE_X_BIT;
-    else
-        sx = Curve::lerp(blendWeight, _scale.x, sx);
-
-    setScaleX(sx);
-}
-
-void Transform::applyAnimationValueScaleY(float sy, float blendWeight)
-{
-    if ((_animationPropertyBitFlag & ANIMATION_SCALE_Y_BIT) != ANIMATION_SCALE_Y_BIT)
-        _animationPropertyBitFlag |= ANIMATION_SCALE_Y_BIT;
-    else
-        sy = Curve::lerp(blendWeight, _scale.y, sy);
-
-    setScaleY(sy);
-}
-
-void Transform::applyAnimationValueScaleZ(float sz, float blendWeight)
-{
-    if ((_animationPropertyBitFlag & ANIMATION_SCALE_Z_BIT) != ANIMATION_SCALE_Z_BIT)
-        _animationPropertyBitFlag |= ANIMATION_SCALE_Z_BIT;
-    else
-        sz = Curve::lerp(blendWeight, _scale.z, sz);
-
-    setScaleZ(sz);
-}
-
-void Transform::applyAnimationValueRotation(Quaternion* q, float blendWeight)
-{
-    if ((_animationPropertyBitFlag & ANIMATION_ROTATION_BIT) != ANIMATION_ROTATION_BIT)
-        _animationPropertyBitFlag |= ANIMATION_ROTATION_BIT;
-    else
-        Quaternion::slerp(_rotation, *q, blendWeight, q);
-     
-    setRotation(*q);
-}
-
-void Transform::applyAnimationValueTranslationX(float tx, float blendWeight)
-{
-    if ((_animationPropertyBitFlag & ANIMATION_TRANSLATION_X_BIT) != ANIMATION_TRANSLATION_X_BIT)
-        _animationPropertyBitFlag |= ANIMATION_TRANSLATION_X_BIT;
-    else
-        tx = Curve::lerp(blendWeight, _translation.x, tx);
-
-    setTranslationX(tx);
-}
-
-void Transform::applyAnimationValueTranslationY(float ty, float blendWeight)
-{
-    if ((_animationPropertyBitFlag & ANIMATION_TRANSLATION_Y_BIT) != ANIMATION_TRANSLATION_Y_BIT)
-        _animationPropertyBitFlag |= ANIMATION_TRANSLATION_Y_BIT;
-    else
-        ty = Curve::lerp(blendWeight, _translation.y, ty);
-
-    setTranslationY(ty);
-}
-
-void Transform::applyAnimationValueTranslationZ(float tz, float blendWeight)
-{
-    if ((_animationPropertyBitFlag & ANIMATION_TRANSLATION_Z_BIT) != ANIMATION_TRANSLATION_Z_BIT)
-        _animationPropertyBitFlag |= ANIMATION_TRANSLATION_Z_BIT;
-    else
-        tz = Curve::lerp(blendWeight, _translation.z, tz);
-
-    setTranslationZ(tz);
+    Quaternion q(value->getFloat(index), value->getFloat(index + 1), value->getFloat(index + 2), value->getFloat(index + 3));
+    Quaternion::slerp(_rotation, q, blendWeight, &q);
+    setRotation(q);
 }
 
 }

+ 1 - 15
gameplay/src/Transform.h

@@ -811,21 +811,7 @@ protected:
     std::list<TransformListener>* _listeners;
 
 private:
-    static const char ANIMATION_SCALE_X_BIT = 0x01; 
-    static const char ANIMATION_SCALE_Y_BIT = 0x02; 
-    static const char ANIMATION_SCALE_Z_BIT = 0x04; 
-    static const char ANIMATION_ROTATION_BIT = 0x08;  
-    static const char ANIMATION_TRANSLATION_X_BIT = 0x10; 
-    static const char ANIMATION_TRANSLATION_Y_BIT = 0x20; 
-    static const char ANIMATION_TRANSLATION_Z_BIT = 0x40; 
-
-    void applyAnimationValueScaleX(float sx, float blendWeight);
-    void applyAnimationValueScaleY(float sy, float blendWeight);
-    void applyAnimationValueScaleZ(float sz, float blendWeight);
-    void applyAnimationValueRotation(Quaternion* q, float blendWeight);
-    void applyAnimationValueTranslationX(float tx, float blendWeight);
-    void applyAnimationValueTranslationY(float ty, float blendWeight);
-    void applyAnimationValueTranslationZ(float tz, float blendWeight);
+    void applyAnimationValueRotation(AnimationValue* value, unsigned int index, float blendWeight);
 };
 
 }