Procházet zdrojové kódy

Merge branch 'next' of https://github.com/blackberry-gaming/GamePlay into next-kcunney

Kieran Cunney před 13 roky
rodič
revize
c832d80ba1
42 změnil soubory, kde provedl 734 přidání a 754 odebrání
  1. 10 1
      .gitignore
  2. 1 0
      gameplay-encoder/gameplay-bundle.txt
  3. 1 0
      gameplay-encoder/gameplay-encoder.vcxproj
  4. 1 0
      gameplay-encoder/gameplay-encoder.vcxproj.filters
  5. 6 2
      gameplay-encoder/src/FileIO.cpp
  6. 1 1
      gameplay-encoder/src/GPBFile.h
  7. 4 0
      gameplay-encoder/src/Node.cpp
  8. 18 2
      gameplay-template/android/jni/template.Android.mk
  9. 15 15
      gameplay/.cproject
  10. 1 1
      gameplay/android/jni/Android.mk
  11. 4 4
      gameplay/src/Animation.cpp
  12. 6 2
      gameplay/src/Animation.h
  13. 4 4
      gameplay/src/AnimationTarget.cpp
  14. 5 3
      gameplay/src/AnimationTarget.h
  15. 0 64
      gameplay/src/AudioBuffer.cpp
  16. 0 14
      gameplay/src/AudioBuffer.h
  17. 9 154
      gameplay/src/AudioController.cpp
  18. 3 10
      gameplay/src/AudioController.h
  19. 27 244
      gameplay/src/AudioSource.cpp
  20. 5 22
      gameplay/src/AudioSource.h
  21. 2 11
      gameplay/src/Base.h
  22. 250 17
      gameplay/src/Bundle.cpp
  23. 31 2
      gameplay/src/Bundle.h
  24. 9 6
      gameplay/src/FileSystem.cpp
  25. 4 4
      gameplay/src/Form.cpp
  26. 6 4
      gameplay/src/Form.h
  27. 4 4
      gameplay/src/Material.cpp
  28. 6 4
      gameplay/src/Material.h
  29. 1 0
      gameplay/src/MeshSkin.h
  30. 18 4
      gameplay/src/Node.cpp
  31. 5 3
      gameplay/src/Node.h
  32. 5 5
      gameplay/src/ParticleEmitter.cpp
  33. 6 4
      gameplay/src/ParticleEmitter.h
  34. 133 107
      gameplay/src/PlatformAndroid.cpp
  35. 92 4
      gameplay/src/Properties.cpp
  36. 9 4
      gameplay/src/Properties.h
  37. 10 9
      gameplay/src/SceneLoader.cpp
  38. 6 2
      gameplay/src/SceneLoader.h
  39. 6 6
      gameplay/src/Theme.cpp
  40. 7 5
      gameplay/src/Theme.h
  41. 1 1
      gameplay/src/VertexAttributeBinding.cpp
  42. 2 5
      gameplay/src/gameplay-main-android.cpp

+ 10 - 1
.gitignore

@@ -156,4 +156,13 @@ Thumbs.db
 /gameplay-samples/sample04-particles/Device-Debug
 /gameplay-samples/sample04-particles/Debug
 /gameplay-samples/sample04-particles/DebugMem
-/gameplay-samples/sample03-character/res/gamepad.xcf
+/gameplay-samples/sample03-character/res/gamepad.xcf
+/gameplay-samples/sample04-particles/android/src
+/gameplay-samples/sample04-particles/android/assets
+/gameplay-samples/sample04-particles/android/bin
+/gameplay-samples/sample04-particles/android/gen
+/gameplay-samples/sample04-particles/android/libs
+/gameplay-samples/sample04-particles/android/obj
+/gameplay-samples/sample04-particles/android/proguard.cfg
+/gameplay-samples/sample04-particles/android/local.properties
+/gameplay-samples/sample04-particles/android/project.properties

+ 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)

+ 15 - 15
gameplay/.cproject

@@ -17,7 +17,7 @@
 						<toolChain id="com.qnx.qcc.toolChain.staticLib.debug.1686166742" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
 							<option id="com.qnx.qcc.option.cpu.545743487" name="Target CPU:" superClass="com.qnx.qcc.option.cpu" value="com.qnx.qcc.option.gen.cpu.armle-v7" valueType="enumerated"/>
 							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.158841187" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Device-Debug}" id="org.eclipse.cdt.build.core.internal.builder.159190287" superClass="org.eclipse.cdt.build.core.internal.builder"/>
+							<builder buildPath="${workspace_loc:/gameplay/Device-Debug}" id="org.eclipse.cdt.build.core.internal.builder.159190287" keepEnvironmentInBuildfile="false" name="CDT Internal Builder" superClass="org.eclipse.cdt.build.core.internal.builder"/>
 							<tool id="com.qnx.qcc.tool.compiler.96907942" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
 								<option id="com.qnx.qcc.option.compile.debug.1765481355" name="Debug (-g)" superClass="com.qnx.qcc.option.compile.debug" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.security.311918799" name="Enhanced Security (-fstack-protector-all)" superClass="com.qnx.qcc.option.compiler.security" value="true" valueType="boolean"/>
@@ -25,8 +25,8 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.2133604142" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/bullet/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/oggvorbis/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.997142816" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -78,8 +78,8 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1670164593" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/bullet/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/oggvorbis/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.1380846613" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -130,8 +130,8 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1503059677" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/bullet/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/oggvorbis/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.81809638" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -185,8 +185,8 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1769677874" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/bullet/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/oggvorbis/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.2007171407" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -238,8 +238,8 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.847642559" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/bullet/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/oggvorbis/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.1038720310" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -291,8 +291,8 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.513622172" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/bullet/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/oggvorbis/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.1961855927" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -345,8 +345,8 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1685994750" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/bullet/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../external-deps/oggvorbis/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.1658185881" superClass="com.qnx.qcc.inputType.compiler"/>

+ 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.

+ 4 - 4
gameplay/src/AnimationTarget.cpp

@@ -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 - 3
gameplay/src/AnimationTarget.h

@@ -54,14 +54,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. 

+ 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
 };
 
 }

+ 9 - 154
gameplay/src/AudioController.cpp

@@ -4,25 +4,13 @@
 #include "AudioBuffer.h"
 #include "AudioSource.h"
 
-
 namespace gameplay
 {
 
-std::list<AudioSource*> AudioController::_playingSources;
-
-
-#ifndef __ANDROID__
-AudioController::AudioController() 
-    : _alcDevice(NULL), _alcContext(NULL)
-{
-}
-#else
 AudioController::AudioController() 
-    : _engineObject(NULL), _engineEngine(NULL), _outputMixObject(NULL), _listenerObject(NULL),
-    _listenerDoppler(NULL), _listenerLocation(NULL)
+    : _alcDevice(NULL), _alcContext(NULL), _pausingSource(NULL)
 {
 }
-#endif
 
 AudioController::~AudioController()
 {
@@ -30,7 +18,6 @@ AudioController::~AudioController()
 
 void AudioController::initialize()
 {
-#ifndef __ANDROID__
     _alcDevice = alcOpenDevice (NULL);
     if (!_alcDevice)
     {
@@ -53,52 +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;
-    }
-#endif
 }
 
 void AudioController::finalize()
 {
-#ifndef __ANDROID__
     alcMakeContextCurrent(NULL);
     if (_alcContext)
     {
@@ -110,55 +55,36 @@ 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()
 {
-    std::list<AudioSource*>::iterator itr = _playingSources.begin();
+    std::set<AudioSource*>::iterator itr = _playingSources.begin();
 
     // For each source that is playing, pause it.
     AudioSource* source = NULL;
     while (itr != _playingSources.end())
     {
         source = *itr;
-        if (source->getState() == AudioSource::PLAYING)
-        {
-            source->pause();
-        }
+        _pausingSource = source;
+        source->pause();
+        _pausingSource = NULL;
         itr++;
     }
 }
 
 void AudioController::resume()
-{
-#ifndef __ANDROID__    
+{   
     alcMakeContextCurrent(_alcContext);
-#endif
-    std::list<AudioSource*>::iterator itr = _playingSources.begin();
+
+    std::set<AudioSource*>::iterator itr = _playingSources.begin();
 
     // For each source that is playing, resume it.
     AudioSource* source = NULL;
     while (itr != _playingSources.end())
     {
         source = *itr;
-        if (source->getState() == AudioSource::PAUSED)
-        {
-            source->play();
-        }
+        source->resume();
         itr++;
     }
 }
@@ -168,81 +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)
-        {
-            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;
-            }
-        }
-        
-        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
     }
 }
 

+ 3 - 10
gameplay/src/AudioController.h

@@ -54,18 +54,11 @@ 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
-    static std::list<AudioSource*> _playingSources;     // List of currently running sources.
+    std::set<AudioSource*> _playingSources;
+    AudioSource* _pausingSource;
 };
 
 }

+ 27 - 244
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,63 +131,35 @@ 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();
+    if (audioController->_playingSources.find(this) == audioController->_playingSources.end())
+        audioController->_playingSources.insert(this);
 }
 
 void AudioSource::pause()
 {
-#ifndef __ANDROID__
     alSourcePause(_alSource);
-#else
-    if (_playerPlay != NULL)
+
+    // 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.
+    AudioController* audioController = Game::getInstance()->getAudioController();
+    if (audioController->_pausingSource != this)
     {
-        SLresult result = (*_playerPlay)->SetPlayState(_playerPlay, SL_PLAYSTATE_PAUSED);
-        if (result != SL_RESULT_SUCCESS)
+        std::set<AudioSource*>::iterator iter = audioController->_playingSources.find(this);
+        if (iter != audioController->_playingSources.end())
         {
-            WARN("AudioSource::pause() failed to set player state.");
+            WARN("\n\nRemoving an audio source from the list of playing sources...\n\n\n");
+            audioController->_playingSources.erase(iter);
         }
     }
-#endif
 }
 
 void AudioSource::resume()
@@ -302,34 +172,18 @@ 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();
+    std::set<AudioSource*>::iterator iter = audioController->_playingSources.find(this);
+    if (iter != audioController->_playingSources.end())
+        audioController->_playingSources.erase(iter);
 }
 
 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
@@ -339,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);
 
@@ -349,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;
 }
 
@@ -370,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;
 }
 
@@ -393,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;
 }
 
@@ -415,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;
 }
 
@@ -463,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)
@@ -496,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;
 };
 
 }

+ 9 - 6
gameplay/src/FileSystem.cpp

@@ -162,13 +162,16 @@ FILE* FileSystem::openFile(const char* path, const char* mode)
     if (stat(fullPath.c_str(), &s) != 0)
     {
         AAsset* asset = AAssetManager_open(__assetManager, path, AASSET_MODE_RANDOM);
-        const void* data = AAsset_getBuffer(asset);
-        int length = AAsset_getLength(asset);
-        FILE* file = fopen(fullPath.c_str(), "wb");
+        if (asset)
+        {
+            const void* data = AAsset_getBuffer(asset);
+            int length = AAsset_getLength(asset);
+            FILE* file = fopen(fullPath.c_str(), "wb");
         
-        int ret = fwrite(data, sizeof(unsigned char), length, file);
-        assert(ret == length);
-        fclose(file);
+            int ret = fwrite(data, sizeof(unsigned char), length, file);
+            assert(ret == length);
+            fclose(file);
+        }
     }
 #endif
     

+ 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.

+ 1 - 0
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:
 

+ 18 - 4
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())
@@ -998,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.

+ 133 - 107
gameplay/src/PlatformAndroid.cpp

@@ -13,38 +13,32 @@
 #include <android/log.h>
 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
 
-#define TOUCH_COUNT_MAX     4
-
-using namespace std;
-
+// Externally referenced global variables.
 struct android_app* __state;
 AAssetManager* __assetManager;
-std::string __assetsPath;
-bool __initialized = false;
-bool __destroyed = false;
 
+static bool __initialized;
+static bool __suspended;
 static EGLDisplay __eglDisplay = EGL_NO_DISPLAY;
 static EGLContext __eglContext = EGL_NO_CONTEXT;
 static EGLSurface __eglSurface = EGL_NO_SURFACE;
 static EGLConfig __eglConfig = 0;
-int __width;
-int __height;
-
-struct timespec __timespec;
+static int __width;
+static int __height;
+static struct timespec __timespec;
 static long __timeStart;
 static long __timeAbsolute;
 static bool __vsync = WINDOW_VSYNC;
-
-ASensorManager* __sensorManager;
-ASensorEventQueue* __sensorEventQueue;
-ASensorEvent __sensorEvent;
-const ASensor* __accelerometerSensor;
-
-static int __orientationAngle = 90; // Landscape by default.
+static ASensorManager* __sensorManager;
+static ASensorEventQueue* __sensorEventQueue;
+static ASensorEvent __sensorEvent;
+static const ASensor* __accelerometerSensor;
+static int __orientationAngle = 90;
 static bool __multiTouch = false;
 static int __primaryTouchId = -1;
-bool __displayKeyboard = false;
+static bool __displayKeyboard = false;
 
+// OpenGL VAO functions.
 static const char* __glExtensions;
 PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL;
 PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL;
@@ -54,7 +48,7 @@ PFNGLISVERTEXARRAYOESPROC glIsVertexArray = NULL;
 namespace gameplay
 {
 
-long timespec2millis(struct timespec *a)
+static long timespec2millis(struct timespec *a)
 {
     return a->tv_sec*1000 + a->tv_nsec/1000000;
 }
@@ -67,7 +61,7 @@ extern void printError(const char* format, ...)
     va_end(argptr);
 }
 
-EGLenum checkErrorEGL(const char* msg)
+static EGLenum checkErrorEGL(const char* msg)
 {
     static const char* errmsg[] =
     {
@@ -93,7 +87,7 @@ EGLenum checkErrorEGL(const char* msg)
 }
 
 // Initialized EGL resources.
-bool initEGL()
+static bool initEGL()
 {
     // Hard-coded to 32-bit/OpenGL ES 2.0.
     const EGLint eglConfigAttrs[] =
@@ -122,31 +116,34 @@ bool initEGL()
         EGL_NONE
     };
 
-    // Get the EGL display and initialize.
-    __eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (__eglDisplay == EGL_NO_DISPLAY)
+    if (__eglDisplay == EGL_NO_DISPLAY && __eglContext == EGL_NO_CONTEXT)
     {
-        checkErrorEGL("eglGetDisplay");
-        goto error;
-    }
+        // Get the EGL display and initialize.
+        __eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        if (__eglDisplay == EGL_NO_DISPLAY)
+        {
+            checkErrorEGL("eglGetDisplay");
+            goto error;
+        }
     
-    if (eglInitialize(__eglDisplay, NULL, NULL) != EGL_TRUE)
-    {
-        checkErrorEGL("eglInitialize");
-        goto error;
-    }
+        if (eglInitialize(__eglDisplay, NULL, NULL) != EGL_TRUE)
+        {
+            checkErrorEGL("eglInitialize");
+            goto error;
+        }
     
-    if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) != EGL_TRUE || eglConfigCount == 0)
-    {
-        checkErrorEGL("eglChooseConfig");
-        goto error;
-    }
+        if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) != EGL_TRUE || eglConfigCount == 0)
+        {
+            checkErrorEGL("eglChooseConfig");
+            goto error;
+        }
     
-    __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
-    if (__eglContext == EGL_NO_CONTEXT)
-    {
-        checkErrorEGL("eglCreateContext");
-        goto error;
+        __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
+        if (__eglContext == EGL_NO_CONTEXT)
+        {
+            checkErrorEGL("eglCreateContext");
+            goto error;
+        }
     }
     
     // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
@@ -191,12 +188,42 @@ bool initEGL()
     return true;
     
 error:
-
     return false;
 }
 
+static void destroyEGLSurface()
+{
+    if (__eglDisplay != EGL_NO_DISPLAY)
+    {
+        eglMakeCurrent(__eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    }
+
+    if (__eglSurface != EGL_NO_SURFACE)
+    {
+        eglDestroySurface(__eglDisplay, __eglSurface);
+        __eglSurface = EGL_NO_SURFACE;
+    }
+}
+
+static void destroyEGLMain()
+{
+    destroyEGLSurface();
+
+    if (__eglContext != EGL_NO_CONTEXT)
+    {
+        eglDestroyContext(__eglDisplay, __eglContext);
+        __eglContext = EGL_NO_CONTEXT;
+    }
+
+    if (__eglDisplay != EGL_NO_DISPLAY)
+    {
+        eglTerminate(__eglDisplay);
+        __eglDisplay = EGL_NO_DISPLAY;
+    }
+}
+
 // Display the android virtual keyboard.
-void displayKeyboard(android_app* state, bool show)
+static void displayKeyboard(android_app* state, bool show)
 { 
     // The following functions is supposed to show / hide functins from a native activity.. but currently do not work. 
     // ANativeActivity_showSoftInput(state->activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT);
@@ -256,7 +283,7 @@ void displayKeyboard(android_app* state, bool show)
 }
 
 // Gets the Keyboard::Key enumeration constant that corresponds to the given Android key code.
-Keyboard::Key getKey(int keycode, int metastate)
+static Keyboard::Key getKey(int keycode, int metastate)
 {
     bool shiftOn = (metastate == AMETA_SHIFT_ON);
     
@@ -604,7 +631,7 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
 }
 
 // Process the next main command.
-static void engine_handle_cmd(struct android_app* app, int32_t cmd) 
+static void engine_handle_cmd(struct android_app* app, int32_t cmd)
 {
     switch (cmd) 
     {
@@ -612,23 +639,47 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd)
             // The window is being shown, get it ready.
             if (app->window != NULL)
             {
+                initEGL();
                 __initialized = true;
             }
             break;
         case APP_CMD_TERM_WINDOW:
-            {
-                __destroyed = true;
-                break;
-            }
+            destroyEGLSurface();
+            __initialized = false;
+            break;
+        case APP_CMD_DESTROY:
+            Game::getInstance()->exit();
+            destroyEGLMain();
+            __initialized = false;
+            break;
         case APP_CMD_GAINED_FOCUS:
             // When our app gains focus, we start monitoring the accelerometer.
             if (__accelerometerSensor != NULL) 
             {
                 ASensorEventQueue_enableSensor(__sensorEventQueue, __accelerometerSensor);
-                // We'd like to get 60 events per second (in us).
+                // We'd like to get 60 events per second (in microseconds).
                 ASensorEventQueue_setEventRate(__sensorEventQueue, __accelerometerSensor, (1000L/60)*1000);
             }
-            Game::getInstance()->resume();
+
+            if (Game::getInstance()->getState() == Game::UNINITIALIZED)
+            {
+                Game::getInstance()->run(__width, __height);
+            }
+            else
+            {
+                Game::getInstance()->resume();
+            }
+            break;
+        case APP_CMD_RESUME:
+            if (__initialized)
+            {
+                Game::getInstance()->resume();
+            }
+            __suspended = false;
+            break;
+        case APP_CMD_PAUSE:
+            Game::getInstance()->pause();
+            __suspended = true;
             break;
         case APP_CMD_LOST_FOCUS:
             // When our app loses focus, we stop monitoring the accelerometer.
@@ -637,7 +688,6 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd)
             {
                 ASensorEventQueue_disableSensor(__sensorEventQueue, __accelerometerSensor);
             }
-            Game::getInstance()->pause();
             break;
     }
 }
@@ -654,28 +704,6 @@ Platform::Platform(const Platform& copy)
 
 Platform::~Platform()
 {
-    if (__eglDisplay != EGL_NO_DISPLAY)
-    {
-        eglMakeCurrent(__eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    }
-
-    if (__eglSurface != EGL_NO_SURFACE)
-    {
-        eglDestroySurface(__eglDisplay, __eglSurface);
-        __eglSurface = EGL_NO_SURFACE;
-    }
-
-    if (__eglContext != EGL_NO_CONTEXT)
-    {
-        eglDestroyContext(__eglDisplay, __eglContext);
-        __eglContext = EGL_NO_CONTEXT;
-    }
-
-    if (__eglDisplay != EGL_NO_DISPLAY)
-    {
-        eglTerminate(__eglDisplay);
-        __eglDisplay = EGL_NO_DISPLAY;
-    }
 }
 
 Platform* Platform::create(Game* game)
@@ -687,6 +715,9 @@ Platform* Platform::create(Game* game)
 
 int Platform::enterMessagePump()
 {
+    __initialized = false;
+    __suspended = false;
+
     // Get the android application's activity.
     ANativeActivity* activity = __state->activity;
     JavaVM* jvm = __state->activity->vm;
@@ -702,12 +733,13 @@ int Platform::enterMessagePump()
     const char* packageName;
     jboolean isCopy;
     packageName = env->GetStringUTFChars((jstring)result, &isCopy);
+    jvm->DetachCurrentThread();
     
     // Set the default path to store the resources.
-    __assetsPath = "/mnt/sdcard/android/data/";
-    __assetsPath += packageName;
-    __assetsPath += "/";
-    FileSystem::setResourcePath(__assetsPath.c_str());    
+    std::string assetsPath = "/mnt/sdcard/android/data/";
+    assetsPath += packageName;
+    assetsPath += "/";
+    FileSystem::setResourcePath(assetsPath.c_str());    
         
     // Get the asset manager to get the resources from the .apk file.
     __assetManager = __state->activity->assetManager; 
@@ -726,8 +758,6 @@ int Platform::enterMessagePump()
     __timeStart = timespec2millis(&__timespec);
     __timeAbsolute = 0L;
     
-    bool initializeGame = true;
-    
     while (true)
     {
         // Read all pending events.
@@ -735,12 +765,10 @@ int Platform::enterMessagePump()
         int events;
         struct android_poll_source* source;
         
-        bool _shouldPoll = !(__initialized && Game::getInstance()->getState() == Game::UNINITIALIZED) && (Game::getInstance()->getState() != Game::PAUSED);
-        
-        while ((ident=ALooper_pollAll( _shouldPoll ? 0 : -1, NULL, &events, (void**)&source)) >= 0) 
+        while ((ident=ALooper_pollAll(!__suspended ? 0 : -1, NULL, &events, (void**)&source)) >= 0) 
         {
             // Process this event.
-            if (source != NULL) 
+            if (source != NULL)
                 source->process(__state, source);
             
             // If a sensor has data, process it now.
@@ -748,20 +776,14 @@ int Platform::enterMessagePump()
                 ASensorEventQueue_getEvents(__sensorEventQueue, &__sensorEvent, 1);
             
             if (__state->destroyRequested != 0)
-                break;
-        }
-        
-        if (__initialized && initializeGame)
-        {
-            gameplay::initEGL();
-            WARN_VARG("Platform::enterMessagePump() - width: %d  height: %d assetsPath: %s", __width, __height, __assetsPath.c_str());
-            _game->run(__width, __height);
-            initializeGame = false;
+            {
+                return 0;
+            }
         }
         
         // Idle time (no events left to process) is spent rendering.
         // We skip rendering when the app is paused.
-        if (__initialized && _game->getState() != Game::PAUSED)
+        if (__initialized && !__suspended)
         {
             _game->frame();
 
@@ -781,23 +803,27 @@ int Platform::enterMessagePump()
             int rc = eglSwapBuffers(__eglDisplay, __eglSurface);
             if (rc != EGL_TRUE)
             {
-                perror("eglSwapBuffers");
-                _game->exit();
-                break;
+                EGLint error = eglGetError();
+                if (error == EGL_BAD_NATIVE_WINDOW)
+                {
+                    if (__state->window != NULL)
+                    {
+                        destroyEGLSurface();
+                        initEGL();
+                    }
+                    __initialized = true;
+                }
+                else
+                {
+                    perror("eglSwapBuffers");
+                    break;
+                }
             }
         }
-        
-        // Check if we are exiting.
-        if ((__state->destroyRequested != 0) || (__initialized && Game::getInstance()->getState() == Game::UNINITIALIZED))
-        {
-            break;
-        }
             
         // Display the keyboard.
         gameplay::displayKeyboard(__state, __displayKeyboard);
     }
-
-    jvm->DetachCurrentThread();
 }
 
 void Platform::signalShutdown() 

+ 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 - 1
gameplay/src/VertexAttributeBinding.cpp

@@ -195,7 +195,7 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexF
 
         if (attrib == -1)
         {
-            WARN_VARG("Warning: Vertex element with usage '%s' in mesh '%s' does not correspond to an attribute in effect '%s'.", VertexFormat::toString(e.usage), mesh->getUrl(), effect->getId());
+            //WARN_VARG("Warning: Vertex element with usage '%s' in mesh '%s' does not correspond to an attribute in effect '%s'.", VertexFormat::toString(e.usage), mesh->getUrl(), effect->getId());
         }
         else
         {

+ 2 - 5
gameplay/src/gameplay-main-android.cpp

@@ -18,15 +18,12 @@ void android_main(struct android_app* state)
     
     __state = state;
     Game* game = Game::getInstance();
-    assert(game != NULL);
     Platform* platform = Platform::create(game);
-    assert(platform != NULL);
     platform->enterMessagePump();
-    Game::getInstance()->exit();
     delete platform;
     
-    // Android specific : the process needs to exit to 
-    // to trigger cleanup of global resources (such as game).
+    // Android specific : the process needs to exit to trigger
+    // cleanup of global and static resources (such as the game).
     exit(0);
 }