소스 검색

Added memory leak detection support (when GAMEPLAY_MEM_LEAK_DETECTION is defined - off by default) for tracking new/delete allocations and also gameplay::Ref allocations.
Added DebugMem configurations in visual studio project for windows platform, which builds with memory leak detection enabled.
Fixed a number of memory leaks and memory corruption issues throughout the gameplay library and samples.
Fixed a bug where ParticleEmitter was incorrectly changing depth write state outside of StateBlocks, causing StateBlock's "default state" to get out of sync with GL.

Steve Grenier 14 년 전
부모
커밋
420da906ac

+ 13 - 0
gameplay.sln

@@ -28,31 +28,44 @@ EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
+		DebugMem|Win32 = DebugMem|Win32
 		Release|Win32 = Release|Win32
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|Win32.ActiveCfg = Debug|Win32
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|Win32.Build.0 = Debug|Win32
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|Win32.Build.0 = DebugMem|Win32
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|Win32.ActiveCfg = Release|Win32
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|Win32.Build.0 = Release|Win32
 		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|Win32.ActiveCfg = Debug|Win32
 		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|Win32.Build.0 = Debug|Win32
+		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|Win32.Build.0 = DebugMem|Win32
 		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|Win32.ActiveCfg = Release|Win32
 		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|Win32.Build.0 = Release|Win32
 		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Debug|Win32.ActiveCfg = Debug|Win32
 		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Debug|Win32.Build.0 = Debug|Win32
+		{9A515C8B-3320-4C5C-9754-211E91206C9D}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{9A515C8B-3320-4C5C-9754-211E91206C9D}.DebugMem|Win32.Build.0 = DebugMem|Win32
 		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Release|Win32.ActiveCfg = Release|Win32
 		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Release|Win32.Build.0 = Release|Win32
 		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|Win32.ActiveCfg = Debug|Win32
 		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|Win32.Build.0 = Debug|Win32
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|Win32.Build.0 = DebugMem|Win32
 		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|Win32.ActiveCfg = Release|Win32
 		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|Win32.Build.0 = Release|Win32
 		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|Win32.ActiveCfg = Debug|Win32
 		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|Win32.Build.0 = Debug|Win32
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|Win32.Build.0 = DebugMem|Win32
 		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|Win32.ActiveCfg = Release|Win32
 		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|Win32.Build.0 = Release|Win32
 		{9D69B743-4872-4DD1-8E30-0087C64298D7}.Debug|Win32.ActiveCfg = Debug|Win32
 		{9D69B743-4872-4DD1-8E30-0087C64298D7}.Debug|Win32.Build.0 = Debug|Win32
+		{9D69B743-4872-4DD1-8E30-0087C64298D7}.DebugMem|Win32.ActiveCfg = Debug|Win32
+		{9D69B743-4872-4DD1-8E30-0087C64298D7}.DebugMem|Win32.Build.0 = Debug|Win32
 		{9D69B743-4872-4DD1-8E30-0087C64298D7}.Release|Win32.ActiveCfg = Release|Win32
 		{9D69B743-4872-4DD1-8E30-0087C64298D7}.Release|Win32.Build.0 = Release|Win32
 	EndGlobalSection

+ 41 - 0
gameplay/gameplay.vcxproj

@@ -1,6 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="DebugMem|Win32">
+      <Configuration>DebugMem</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
       <Platform>Win32</Platform>
@@ -24,6 +28,7 @@
     <ClCompile Include="src\BoundingSphere.cpp" />
     <ClCompile Include="src\Camera.cpp" />
     <ClCompile Include="src\Curve.cpp" />
+    <ClCompile Include="src\DebugNew.cpp" />
     <ClCompile Include="src\Effect.cpp" />
     <ClCompile Include="src\FileSystem.cpp" />
     <ClCompile Include="src\Font.cpp" />
@@ -79,6 +84,7 @@
     <ClInclude Include="src\BoundingSphere.h" />
     <ClInclude Include="src\Camera.h" />
     <ClInclude Include="src\Curve.h" />
+    <ClInclude Include="src\DebugNew.h" />
     <ClInclude Include="src\Effect.h" />
     <ClInclude Include="src\FileSystem.h" />
     <ClInclude Include="src\Font.h" />
@@ -155,6 +161,11 @@
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
   </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
@@ -167,6 +178,9 @@
   <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
   </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
   <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
   </ImportGroup>
@@ -174,9 +188,15 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <OutDir>$(Configuration)\</OutDir>
   </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|Win32'">
+    <OutDir>$(Configuration)\</OutDir>
+  </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <IntDir>$(Configuration)\</IntDir>
   </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|Win32'">
+    <IntDir>$(Configuration)\</IntDir>
+  </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <OutDir>$(Configuration)\</OutDir>
   </PropertyGroup>
@@ -191,11 +211,32 @@
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>..\external-deps\openal\include\AL;..\external-deps\alut\include\AL;..\external-deps\glew\include;..\external-deps\libpng\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <RuntimeTypeInfo>
+      </RuntimeTypeInfo>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;GAMEPLAY_MEM_LEAK_DETECTION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\external-deps\openal\include\AL;..\external-deps\alut\include\AL;..\external-deps\glew\include;..\external-deps\libpng\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <RuntimeTypeInfo>true</RuntimeTypeInfo>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
     </Link>
+    <Lib>
+      <Verbose>
+      </Verbose>
+    </Lib>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>

+ 6 - 0
gameplay/gameplay.vcxproj.filters

@@ -168,6 +168,9 @@
     <ClCompile Include="src\RenderState.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\DebugNew.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -338,6 +341,9 @@
     <ClInclude Include="src\RenderState.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\DebugNew.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">

+ 0 - 2
gameplay/src/Animation.cpp

@@ -64,7 +64,6 @@ Animation::Channel::Channel(AnimationTarget* target, int propertyId, Curve* curv
     assert(target->getAnimationPropertyComponentCount(propertyId));
 
     _target = target;
-    _target->addRef();
     _propertyId = propertyId;
     _curve = curve;
     _duration = duration;
@@ -72,7 +71,6 @@ Animation::Channel::Channel(AnimationTarget* target, int propertyId, Curve* curv
 
 Animation::Channel::~Channel()
 {
-    SAFE_RELEASE(_target);
     SAFE_DELETE(_curve);
 }
 

+ 8 - 24
gameplay/src/AnimationClip.cpp

@@ -27,37 +27,21 @@ AnimationClip::AnimationClip(const char* id, Animation* animation, unsigned long
 
 AnimationClip::~AnimationClip()
 {
+    // Explicitly stop this clip if it's currently playing so it gets removed from the controller
+    if (_isPlaying)
+    {
+        stop();
+    }
+
     std::vector<AnimationValue*>::iterator valueIter = _values.begin();
     while (valueIter != _values.end())
     {
         SAFE_DELETE(*valueIter);
         valueIter++;
     }
-    _values.clear();
-
-    if (_beginListeners)
-    {
-        std::vector<Listener*>::iterator bIter = _beginListeners->begin();
-        while (bIter != _beginListeners->end())
-        {
-            SAFE_DELETE(*bIter);
-            bIter++;
-        }
-        _beginListeners->clear();
-        SAFE_DELETE(_beginListeners);
-    }
 
-    if (_endListeners)
-    {
-        std::vector<Listener*>::iterator eIter = _endListeners->begin();
-        while (eIter != _endListeners->end())
-        {
-            SAFE_DELETE(*eIter);
-            eIter++;
-        }
-        _endListeners->clear();
-        SAFE_DELETE(_endListeners);
-    }
+    SAFE_DELETE(_beginListeners);
+    SAFE_DELETE(_endListeners);
 }
 
 const char* AnimationClip::getID() const

+ 3 - 1
gameplay/src/AnimationController.cpp

@@ -113,11 +113,11 @@ void AnimationController::stopAllAnimations()
     while (clipIter != _runningClips.end())
     {
         AnimationClip* clip = *clipIter;
-        clipIter = _runningClips.erase(clipIter);
         clip->_isPlaying = false;
         SAFE_RELEASE(clip);
         clipIter++;
     }
+    _runningClips.clear();
 
     _state = IDLE;
 }
@@ -229,6 +229,8 @@ void AnimationController::destroyAnimation(Animation* animation)
 
 void AnimationController::destroyAllAnimations()
 {
+    stopAllAnimations();
+
     std::vector<Animation*>::iterator itr = _animations.begin();
     
     while (itr != _animations.end())

+ 1 - 2
gameplay/src/AudioSource.cpp

@@ -167,14 +167,13 @@ void AudioSource::setNode(Node* node)
         if (_node)
         {
             _node->removeListener(this);
-            SAFE_RELEASE(_node);
         }
 
         // Connect the new node.
         _node = node;
+
         if (_node)
         {
-            _node->addRef();
             _node->addListener(this);
         }
     }

+ 8 - 2
gameplay/src/Base.h

@@ -5,6 +5,8 @@
 #define BASE_H_
 
 // C/C++
+#include <new>
+#include <cstdio>
 #include <cassert>
 #include <memory>
 #include <iostream>
@@ -20,7 +22,6 @@
 #include <hash_map>
 #include <algorithm>
 #include <ctime>
-#include <cstdio>
 #include <limits>
 #include <functional>
 #include <string.h>
@@ -62,6 +63,11 @@ extern void printError(const char* format, ...);
 #define WARN(x) printError(x)
 #define WARN_VARG(x, ...) printError(x, __VA_ARGS__)
 
+// Debug new for memory leak detection
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#include "DebugNew.h"
+#endif
+
 // Object deletion macro
 #define SAFE_DELETE(x) \
     if (x) \
@@ -239,4 +245,4 @@ extern GLenum __gl_error_code;
     #pragma warning( disable : 4996 )
 #endif
 
-#endif 
+#endif

+ 1 - 6
gameplay/src/Camera.cpp

@@ -36,7 +36,6 @@ Camera::Camera(float zoomX, float zoomY, float aspectRatio, float nearPlane, flo
 
 Camera::~Camera()
 {
-    SAFE_RELEASE(_node);
 }
 
 Camera* Camera::createPerspective(float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
@@ -144,17 +143,13 @@ void Camera::setNode(Node* node)
         if (_node)
         {
             _node->removeListener(this);
-
-            // Disconnect our current node.
-            SAFE_RELEASE(_node);
         }
 
         // Connect the new node.
         _node = node;
+
         if (_node)
         {
-            _node->addRef();
-
             _node->addListener(this);
         }
 

+ 1 - 0
gameplay/src/Curve.cpp

@@ -31,6 +31,7 @@ Curve::Curve(unsigned int pointCount, unsigned int componentCount)
 Curve::~Curve()
 {
     SAFE_DELETE_ARRAY(_points);
+    SAFE_DELETE_ARRAY(_quaternionOffsets);
 }
 
 Curve::Point::Point()

+ 162 - 0
gameplay/src/DebugNew.cpp

@@ -0,0 +1,162 @@
+/**
+ * DebugNew.cpp
+ */
+
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+
+#include <new>
+#include <exception>
+#include <cstdio>
+#include <cstdarg>
+
+struct MemoryAllocationRecord
+{
+    unsigned long address;          // address returned to the caller after allocation
+    unsigned int size;              // size of the allocation request
+    const char* file;               // source file of allocation request
+    int line;                       // source line of the allocation request
+    MemoryAllocationRecord* next;
+    MemoryAllocationRecord* prev;
+};
+
+MemoryAllocationRecord* __memoryAllocations = 0;
+int __memoryAllocationCount = 0;
+
+void* debugAlloc(std::size_t size, const char* file, int line);
+void debugFree(void* p);
+
+#ifdef _MSC_VER
+#pragma warning( disable : 4290 )
+#endif
+
+void* operator new (std::size_t size, const char* file, int line)
+{
+    return debugAlloc(size, file, line);
+}
+
+void* operator new[] (std::size_t size, const char* file, int line)
+{
+    return operator new (size, file, line);
+}
+
+void* operator new (std::size_t size) throw(std::bad_alloc)
+{
+    return operator new (size, "", 0);
+}
+
+void* operator new[] (std::size_t size) throw(std::bad_alloc)
+{
+    return operator new (size, "", 0);
+}
+
+void* operator new (std::size_t size, const std::nothrow_t&) throw()
+{
+    return operator new (size, "", 0);
+}
+
+void* operator new[] (std::size_t size, const std::nothrow_t&) throw()
+{
+    return operator new (size, "", 0);
+}
+
+void operator delete (void* p) throw()
+{
+    debugFree(p);
+}
+
+void operator delete[] (void* p) throw()
+{
+    operator delete (p);
+}
+
+void operator delete (void* p, const char* file, int line) throw()
+{
+    operator delete (p);
+}
+
+void operator delete[] (void* p, const char* file, int line) throw()
+{
+    operator delete (p);
+}
+
+#ifdef _MSC_VER
+#pragma warning( default : 4290 )
+#endif
+
+// Include Base.h (needed for logging macros) AFTER new operator impls
+#include "Base.h"
+
+void* debugAlloc(size_t size, const char* file, int line)
+{
+    // Allocate memory + size for a MemoryAlloctionRecord
+    unsigned char* mem = (unsigned char*)malloc(size + sizeof(MemoryAllocationRecord));
+
+    MemoryAllocationRecord* rec = (MemoryAllocationRecord*)mem;
+
+    // Move memory pointer past record
+    mem += sizeof(MemoryAllocationRecord);
+
+    rec->address = (unsigned long)mem;
+    rec->size = size;
+    rec->file = file;
+    rec->line = line;
+    rec->next = __memoryAllocations;
+    rec->prev = 0;
+
+    if (__memoryAllocations)
+        __memoryAllocations->prev = rec;
+    __memoryAllocations = rec;
+    ++__memoryAllocationCount;
+
+    return mem;
+}
+
+void debugFree(void* p)
+{
+    assert(p);
+
+    // Backup passed in pointer to access memory allocation record
+    void* mem = ((unsigned char*)p) - sizeof(MemoryAllocationRecord);
+
+    MemoryAllocationRecord* rec = (MemoryAllocationRecord*)mem;
+
+    // Sanity check: ensure that address in record matches passed in address
+    if (rec->address != (unsigned long)p)
+    {
+        gameplay::printError("[memory] CORRUPTION: Attempting to free memory address with invalid memory allocation record.");
+        return;
+    }
+
+    // Link this item out
+    if (__memoryAllocations == rec)
+        __memoryAllocations = rec->next;
+    if (rec->prev)
+        rec->prev->next = rec->next;
+    if (rec->next)
+        rec->next->prev = rec->prev;
+    --__memoryAllocationCount;
+
+    // Free the address from the original alloc location (before mem allocation record)
+    free(mem);
+}
+
+extern void printMemoryLeaks()
+{
+    // Dump general heap memory leaks
+    if (__memoryAllocationCount == 0)
+    {
+        gameplay::printError("[memory] All HEAP allocations successfully cleaned up (no leaks detected).");
+    }
+    else
+    {
+        gameplay::printError("[memory] WARNING: %d HEAP allocations still active in memory.", __memoryAllocationCount);
+        MemoryAllocationRecord* rec = __memoryAllocations;
+        while (rec)
+        {
+            gameplay::printError("[memory] LEAK: HEAP allocation leak of size %d leak from line %d in file '%s'.", rec->size, rec->line, rec->file);
+            rec = rec->next;
+        }
+    }
+}
+
+#endif

+ 42 - 0
gameplay/src/DebugNew.h

@@ -0,0 +1,42 @@
+/**
+ * DebugNew.h
+ *
+ * Global overrides of the new and delete operators for memory tracking.
+ * This file is only included when memory leak detection is explicitly
+ * request via the pre-processor defintion GAMEPLAY_MEM_LEAK_DETECTION.
+ */
+
+#ifndef DEBUGNEW_H_
+#define DEBUGNEW_H_
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+
+#include <new>
+#include <exception>
+
+// Prints all heap and reference leaks to stderr.
+extern void printMemoryLeaks();
+
+// global new/delete operator overloads
+#ifdef _MSC_VER
+#pragma warning( disable : 4290 ) // C++ exception specification ignored.
+#endif
+void* operator new (std::size_t size, const char* file, int line);
+void* operator new[] (std::size_t size, const char* file, int line);
+void* operator new (std::size_t size) throw(std::bad_alloc);
+void* operator new[] (std::size_t size) throw(std::bad_alloc);
+void* operator new (std::size_t size, const std::nothrow_t&) throw();
+void* operator new[] (std::size_t size, const std::nothrow_t&) throw();
+void operator delete (void* p) throw();
+void operator delete[] (void* p) throw();
+void operator delete (void* p, const char* file, int line) throw();
+void operator delete[] (void* p, const char* file, int line) throw();
+#ifdef _MSC_VER
+#pragma warning( default : 4290 )
+#endif
+
+// Re-define new to use versions with file and line number
+#define DEBUG_NEW new (__FILE__, __LINE__)
+#define new DEBUG_NEW
+
+#endif
+#endif

+ 32 - 13
gameplay/src/Game.cpp

@@ -5,6 +5,7 @@
 #include "Base.h"
 #include "Game.h"
 #include "Platform.h"
+#include "RenderState.h"
 
 // Extern global variables
 GLenum __gl_error_code = GL_NO_ERROR;
@@ -19,7 +20,8 @@ long Game::_pausedTimeTotal = 0L;
 Game::Game() 
     : _state(UNINITIALIZED), 
       _frameLastFPS(0), _frameCount(0), _frameRate(0), 
-      _clearColor(Vector4::zero()), _clearDepth(1.0f), _clearStencil(0)
+      _clearDepth(1.0f), _clearStencil(0),
+      _animationController(NULL), _audioController(NULL)
 {
     assert(__gameInstance == NULL);
     __gameInstance = this;
@@ -33,6 +35,11 @@ Game::~Game()
 {
     // Do not call any virtual functions from the destructor.
     // Finalization is done from outside this class.
+
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+    Ref::printLeaks();
+    printMemoryLeaks();
+#endif
 }
 
 Game* Game::getInstance()
@@ -92,8 +99,13 @@ bool Game::startup()
     if (_state != UNINITIALIZED)
         return false;
 
-    _animationController.initialize();
-    _audioController.initialize();
+    RenderState::initialize();
+
+    _animationController = new AnimationController();
+    _animationController->initialize();
+
+    _audioController = new AudioController();
+    _audioController->initialize();
 
     // Call user initialization.
     initialize();
@@ -109,8 +121,15 @@ void Game::shutdown()
     {
         finalize();
 
-        _animationController.finalize();
-        _audioController.finalize();
+        _animationController->finalize();
+        delete _animationController;
+        _animationController = NULL;
+
+        _audioController->finalize();
+        delete _audioController;
+        _audioController = NULL;
+
+        RenderState::finalize();
     }
 
     _state = UNINITIALIZED;
@@ -122,8 +141,8 @@ void Game::pause()
     {
         _state = PAUSED;
         _pausedTimeLast = Platform::getAbsoluteTime();
-        _animationController.pause();
-        _audioController.pause();
+        _animationController->pause();
+        _audioController->pause();
     }
 }
 
@@ -133,8 +152,8 @@ void Game::resume()
     {
         _state = RUNNING;
         _pausedTimeTotal += Platform::getAbsoluteTime() - _pausedTimeLast;
-        _animationController.resume();
-        _audioController.resume();
+        _animationController->resume();
+        _audioController->resume();
     }
 }
 
@@ -155,12 +174,12 @@ void Game::frame()
     lastFrameTime = frameTime;
 
     // Update the schedule and running animations.
-    _animationController.update(elapsedTime);
+    _animationController->update(elapsedTime);
     // Application Update.
     update(elapsedTime);
 
     // Audio Rendering.
-    _audioController.update(elapsedTime);
+    _audioController->update(elapsedTime);
     // Graphics Rendering.
     render(elapsedTime);
 
@@ -224,10 +243,10 @@ void Game::clear(ClearFlags flags, const Vector4& clearColor, float clearDepth,
 
 AnimationController* Game::getAnimationController()
 {
-    return &_animationController;
+    return _animationController;
 }
 
-const AudioController& Game::getAudioController() const
+const AudioController* Game::getAudioController() const
 {
     return _audioController;
 }

+ 4 - 8
gameplay/src/Game.h

@@ -18,7 +18,6 @@ namespace gameplay
  */
 class Game
 {
-
 public:
 
     /**
@@ -34,8 +33,7 @@ public:
     /**
      * Flags used when clearing the active frame buffer targets.
      */
-
-     enum ClearFlags
+    enum ClearFlags
     {
         CLEAR_COLOR = GL_COLOR_BUFFER_BIT,
         CLEAR_DEPTH = GL_DEPTH_BUFFER_BIT,
@@ -46,8 +44,6 @@ public:
         CLEAR_COLOR_DEPTH_STENCIL = CLEAR_COLOR | CLEAR_DEPTH | CLEAR_STENCIL
     };
 
-
-
     /**
      * Destructor.
      */
@@ -168,7 +164,7 @@ public:
      *
      * @return The audio controller for this game.
      */
-    const AudioController& getAudioController() const;
+    const AudioController* getAudioController() const;
 
     /**
      * Gets the animation controller for managing control of animations
@@ -279,8 +275,8 @@ private:
     Vector4 _clearColor;                        // The clear color value last used for clearing the color buffer.
     float _clearDepth;                          // The clear depth value last used for clearing the depth buffer.
     int _clearStencil;                          // The clear stencil value last used for clearing the stencil buffer.
-    AnimationController _animationController;   // Controls the scheduling and running of animations.
-    AudioController _audioController;           // Controls audio sources that are playing in the game.
+    AnimationController* _animationController;  // Controls the scheduling and running of animations.
+    AudioController* _audioController;          // Controls audio sources that are playing in the game.
 };
 
 }

+ 2 - 13
gameplay/src/Light.cpp

@@ -70,19 +70,8 @@ Node* Light::getNode() const
 
 void Light::setNode(Node* node)
 {
-    if (_node != node)
-    {
-        // Disconnect our current node.
-        SAFE_RELEASE(_node);
-
-        // Connect the new node.
-        _node = node;
-
-        if (_node)
-        {
-            _node->addRef();
-        }
-    }
+    // Connect the new node.
+    _node = node;
 }
 
 const Vector3& Light::getColor() const

+ 2 - 0
gameplay/src/MaterialParameter.h

@@ -269,6 +269,7 @@ void MaterialParameter::bindValue(ClassType* classInstance, ParameterType (Class
     clearValue();
 
     _value.method = new MethodValueBinding<ClassType, ParameterType>(this, classInstance, valueMethod);
+    _dynamic = true;
     _type = MaterialParameter::METHOD;
 }
 
@@ -278,6 +279,7 @@ void MaterialParameter::bindValue(ClassType* classInstance, ParameterType (Class
     clearValue();
 
     _value.method = new MethodArrayBinding<ClassType, ParameterType>(this, classInstance, valueMethod, countMethod);
+    _dynamic = true;
     _type = MaterialParameter::METHOD;
 }
 

+ 12 - 10
gameplay/src/Matrix.cpp

@@ -44,20 +44,22 @@ Matrix::~Matrix()
 
 const Matrix& Matrix::identity()
 {
-    static Matrix* m = new Matrix( 1, 0, 0, 0,
-                                   0, 1, 0, 0,
-                                   0, 0, 1, 0,
-                                   0, 0, 0, 1 );
-    return *m;
+    static Matrix m(
+        1, 0, 0, 0,
+        0, 1, 0, 0,
+        0, 0, 1, 0,
+        0, 0, 0, 1 );
+    return m;
 }
 
 const Matrix& Matrix::zero()
 {
-    static Matrix* m = new Matrix( 0, 0, 0, 0,
-                                   0, 0, 0, 0,
-                                   0, 0, 0, 0,
-                                   0, 0, 0, 0 );
-    return *m;
+    static Matrix m(
+        0, 0, 0, 0,
+        0, 0, 0, 0,
+        0, 0, 0, 0,
+        0, 0, 0, 0 );
+    return m;
 }
 
 void Matrix::createLookAt(const Vector3& eyePosition, const Vector3& targetPosition, const Vector3& up, Matrix* dst)

+ 0 - 11
gameplay/src/MeshSkin.cpp

@@ -80,13 +80,7 @@ void MeshSkin::setJoint(Joint* joint, unsigned int index)
 {
     assert(index < _joints.size());
 
-    if (_joints[index])
-    {
-        SAFE_RELEASE(_joints[index]);
-    }
-
     _joints[index] = joint;
-    joint->addRef();
 }
 
 Vector4* MeshSkin::getMatrixPalette() const
@@ -112,11 +106,6 @@ Joint* MeshSkin::getJoint(unsigned int index) const
 
 void MeshSkin::clearJoints()
 {
-    unsigned int prevCount = _joints.size();
-    for (unsigned int i = 0; i < prevCount; i++)
-    {
-        SAFE_RELEASE(_joints[i]);
-    }
     _joints.clear();
 }
 

+ 1 - 11
gameplay/src/Model.cpp

@@ -197,17 +197,7 @@ Node* Model::getNode() const
 
 void Model::setNode(Node* node)
 {
-    if (_node != node)
-    {
-        SAFE_RELEASE(_node);
-
-        _node = node;
-
-        if (_node)
-        {
-            node->addRef();
-        }
-    }
+    _node = node;
 
     // Re-bind node related material parameters
     if (node)

+ 23 - 35
gameplay/src/Node.cpp

@@ -80,11 +80,22 @@ void Node::addChild(Node* child)
 {
     assert(child);
 
+    if (child->_parent == this)
+    {
+        // This node is already present in our hierarchy
+        return;
+    }
+
+    child->addRef();
+
     // If the item belongs to another hierarchy, remove it first.
-    Node* parent = child->_parent;
-    if (parent)
+    if (child->_parent)
     {
-        parent->removeChild(child);
+        child->_parent->removeChild(child);
+    }
+    else if (child->_scene)
+    {
+        child->_scene->removeNode(child);
     }
 
     // Order is irrelevant, so add to the beginning of the list.
@@ -103,9 +114,10 @@ void Node::addChild(Node* child)
 
     ++_childCount;
 
-    // Fire events.
-    child->parentChanged(parent);
-    childAdded(child);
+    if (_notifyHierarchyChanged)
+    {
+        hierarchyChanged();
+    }
 }
 
 void Node::removeChild(Node* child)
@@ -118,6 +130,8 @@ void Node::removeChild(Node* child)
 
     // Call remove on the child.
     child->remove();
+
+    SAFE_RELEASE(child);
 }
 
 void Node::removeAllChildren()
@@ -133,7 +147,7 @@ void Node::removeAllChildren()
     hierarchyChanged();
 }
 
-void Node   ::remove()
+void Node::remove()
 {
     // Re-link our neighbours.
     if (_prevSibling)
@@ -161,11 +175,9 @@ void Node   ::remove()
     _prevSibling = NULL;
     _parent = NULL;
 
-    // Fire events.
-    if (parent)
+    if (parent && parent->_notifyHierarchyChanged)
     {
-        parentChanged(parent);
-        parent->childRemoved(this);
+        parent->hierarchyChanged();
     }
 }
 
@@ -788,28 +800,4 @@ void Node::setParticleEmitter(ParticleEmitter* emitter)
     }
 }
 
-void Node::childAdded(Node* child)
-{
-    child->addRef();
-
-    if (_notifyHierarchyChanged)
-    {
-        hierarchyChanged();
-    }
-}
-
-void Node::childRemoved(Node* child)
-{
-    SAFE_RELEASE(child);
-
-    if (_notifyHierarchyChanged)
-    {
-        hierarchyChanged();
-    }
-}
-
-void Node::parentChanged(Node* oldParent)
-{
-}
-
 }

+ 0 - 21
gameplay/src/Node.h

@@ -414,27 +414,6 @@ protected:
      */
     void remove();
 
-    /**
-     * Called when a child is added to this item in the tree.
-     * 
-     * @param child The child that was added.
-     */
-    virtual void childAdded(Node* child);
-
-    /**
-     * Called when a child is removed from this item in the tree.
-     *
-     * @param child The child that was removed.
-     */
-    virtual void childRemoved(Node* child);
-
-    /**
-     * Called when the parent of this node changes.
-     *
-     * @param oldParent The previous parent for this node.
-     */
-    virtual void parentChanged(Node* oldParent);
-
     void transformChanged();
 
     void hierarchyChanged();

+ 6 - 0
gameplay/src/Package.cpp

@@ -488,6 +488,7 @@ Node* Package::readNode(Scene* sceneContext, Node* nodeContext)
     if (camera)
     {
         node->setCamera(camera);
+        SAFE_RELEASE(camera);
     }
 
     // Read light
@@ -495,6 +496,7 @@ Node* Package::readNode(Scene* sceneContext, Node* nodeContext)
     if (light)
     {
         node->setLight(light);
+        SAFE_RELEASE(light);
     }
 
     // Read model
@@ -502,6 +504,7 @@ Node* Package::readNode(Scene* sceneContext, Node* nodeContext)
     if (model)
     {
         node->setModel(model);
+        SAFE_RELEASE(model);
     }
 
     return node;
@@ -639,6 +642,7 @@ Model* Package::readModel(Scene* sceneContext, Node* nodeContext)
         if (mesh)
         {
             Model* model = Model::create(mesh);
+            SAFE_RELEASE(mesh);
 
             // Read skin
             unsigned char hasSkin;
@@ -1075,10 +1079,12 @@ Mesh* Package::loadMesh(const char* id)
         if (part == NULL)
         {
             LOG_ERROR_VARG("Failed to create mesh part (i=%d): %s", i, id);
+            SAFE_DELETE_ARRAY(indexData);
             SAFE_RELEASE(mesh);
             return NULL;
         }
         part->setIndexData(indexData, 0, indexCount);
+        SAFE_DELETE_ARRAY(indexData);
     }
 
     fseek(_file, position, SEEK_SET);

+ 10 - 23
gameplay/src/ParticleEmitter.cpp

@@ -34,11 +34,12 @@ ParticleEmitter::ParticleEmitter(SpriteBatch* batch, unsigned int particleCountM
     _timePerEmission(EMISSION_RATE_TIME_INTERVAL), _timeLast(0L), _timeRunning(0L)
 {
     _particles = new Particle[particleCountMax];
+
+    _spriteBatch->getStateBlock()->setDepthWrite(false);
 }
 
 ParticleEmitter::~ParticleEmitter()
 {
-    SAFE_RELEASE(_node);
     SAFE_DELETE(_spriteBatch);
     SAFE_DELETE_ARRAY(_particles);
     SAFE_DELETE_ARRAY(_spriteTextureCoords);
@@ -60,6 +61,7 @@ ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlendin
 
     // Use default SpriteBatch material.
     SpriteBatch* batch =  SpriteBatch::create(texture, NULL, particleCountMax);
+    texture->release(); // batch owns the texture
     assert(batch);
 
     ParticleEmitter* emitter = new ParticleEmitter(batch, particleCountMax);
@@ -71,7 +73,9 @@ ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlendin
     emitter->_spriteTextureHeight = texture->getHeight();
     emitter->_spriteTextureWidthRatio = 1.0f / (float)texture->getWidth();
     emitter->_spriteTextureHeightRatio = 1.0f / (float)texture->getHeight();
-    emitter->setSpriteFrameCoords(1, new Rectangle((float)texture->getWidth(), (float)texture->getHeight()));
+
+    Rectangle texCoord((float)texture->getWidth(), (float)texture->getHeight());
+    emitter->setSpriteFrameCoords(1, &texCoord);
 
     return emitter;
 }
@@ -618,6 +622,8 @@ void ParticleEmitter::setSpriteFrameCoords(unsigned int frameCount, int width, i
     }
 
     setSpriteFrameCoords(frameCount, frameCoords);
+
+    SAFE_DELETE_ARRAY(frameCoords);
 }
 
 Node* ParticleEmitter::getNode() const
@@ -627,19 +633,8 @@ Node* ParticleEmitter::getNode() const
 
 void ParticleEmitter::setNode(Node* node)
 {
-    if (_node != node)
-    {
-        // Disconnect our current node.
-        SAFE_RELEASE(_node);
-
-        // Connect the new node.
-        _node = node;
-
-        if (_node)
-        {
-            _node->addRef();
-        }
-    }
+    // Connect the new node.
+    _node = node;
 }
 
 void ParticleEmitter::setOrbit(bool orbitPosition, bool orbitVelocity, bool orbitAcceleration)
@@ -889,16 +884,8 @@ void ParticleEmitter::draw()
             }
         }
 
-        // Disable writing to the depth buffer.
-        GLboolean depthMask;
-        glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMask);
-        glDepthMask(GL_FALSE);
-
         // Render.
         _spriteBatch->end();
-
-        // Turn the depth mask back on if it was on before.
-        glDepthMask(depthMask);
     }
 }
 

+ 5 - 5
gameplay/src/Platform.h

@@ -17,6 +17,11 @@ class Platform
 {
 public:
 
+    /**
+     * Destructor.
+     */
+    ~Platform();
+
     /**
      * Creates a platform for the specified game which is will interacte with.
      *
@@ -98,11 +103,6 @@ private:
      */
     Platform(const Platform& copy);
 
-    /**
-     * Destructor.
-     */
-    ~Platform();
-
     Game* _game;
 
 };

+ 4 - 4
gameplay/src/Quaternion.cpp

@@ -35,14 +35,14 @@ Quaternion::~Quaternion()
 
 const Quaternion& Quaternion::identity()
 {
-    static Quaternion* value = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
-    return *value;
+    static Quaternion value(0.0f, 0.0f, 0.0f, 1.0f);
+    return value;
 }
 
 const Quaternion& Quaternion::zero()
 {
-    static Quaternion* value = new Quaternion(0.0f, 0.0f, 0.0f, 0.0f);
-    return *value;
+    static Quaternion value(0.0f, 0.0f, 0.0f, 0.0f);
+    return value;
 }
 
 bool Quaternion::isIdentity() const

+ 86 - 0
gameplay/src/Ref.cpp

@@ -4,13 +4,22 @@
 
 #include "Base.h"
 #include "Ref.h"
+#include "Game.h"
 
 namespace gameplay
 {
 
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+void* trackRef(Ref* ref);
+void untrackRef(Ref* ref, void* record);
+#endif
+
 Ref::Ref() :
     _refCount(1)
 {
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+    __record = trackRef(this);
+#endif
 }
 
 Ref::~Ref()
@@ -26,8 +35,85 @@ void Ref::release()
 {
     if ((--_refCount) <= 0)
     {
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+        untrackRef(this, __record);
+#endif
         delete this;
     }
 }
 
+unsigned int Ref::getRefCount() const
+{
+    return _refCount;
+}
+
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+
+struct RefAllocationRecord
+{
+    Ref* ref;
+    RefAllocationRecord* next;
+    RefAllocationRecord* prev;
+};
+
+RefAllocationRecord* __refAllocations = 0;
+int __refAllocationCount = 0;
+
+void Ref::printLeaks()
+{
+    // Dump Ref object memory leaks
+    if (__refAllocationCount == 0)
+    {
+        printError("[memory] All Ref objects successfully cleaned up (no leaks detected).");
+    }
+    else
+    {
+        printError("[memory] WARNING: %d Ref objects still active in memory.", __refAllocationCount);
+        for (RefAllocationRecord* rec = __refAllocations; rec != NULL; rec = rec->next)
+        {
+            Ref* ref = rec->ref;
+            const char* type = typeid(*ref).name();
+            printError("[memory] LEAK: Ref object '%s' still active with reference count %d.", (type ? type : ""), ref->getRefCount());
+        }
+    }
+}
+
+void* trackRef(Ref* ref)
+{
+    // Create memory allocation record
+    RefAllocationRecord* rec = (RefAllocationRecord*)malloc(sizeof(RefAllocationRecord));
+    rec->ref = ref;
+    rec->next = __refAllocations;
+    rec->prev = 0;
+
+    if (__refAllocations)
+        __refAllocations->prev = rec;
+    __refAllocations = rec;
+    ++__refAllocationCount;
+
+    return rec;
+}
+
+void untrackRef(Ref* ref, void* record)
+{
+    RefAllocationRecord* rec = (RefAllocationRecord*)record;
+    if (rec->ref != ref)
+    {
+        printError("[memory] CORRUPTION: Attempting to free Ref with invalid ref tracking record.");
+        return;
+    }
+
+    // Link this item out
+    if (__refAllocations == rec)
+        __refAllocations = rec->next;
+    if (rec->prev)
+        rec->prev->next = rec->next;
+    if (rec->next)
+        rec->next->prev = rec->prev;
+    free((void*)rec);
+    --__refAllocationCount;
+}
+
+#endif
+
 }

+ 14 - 0
gameplay/src/Ref.h

@@ -40,6 +40,13 @@ public:
      */
     void release();
 
+    /**
+     * Returns the current reference count of this object.
+     *
+     * @return This object's reference count.
+     */
+    unsigned int getRefCount() const;
+
 protected:
 
     /**
@@ -55,6 +62,13 @@ protected:
 private:
 
     unsigned int _refCount;
+
+    // Memory leak diagnostic data (only included when GAMEPLAY_MEM_LEAK_DETECTION is defined)
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+    friend class Game;
+    static void printLeaks();
+    void* __record;
+#endif
 };
 
 }

+ 43 - 30
gameplay/src/RenderState.cpp

@@ -18,7 +18,7 @@
 namespace gameplay
 {
 
-RenderState::StateBlock RenderState::StateBlock::_defaultState;
+RenderState::StateBlock* RenderState::StateBlock::_defaultState = NULL;
 
 RenderState::RenderState()
     : _nodeBinding(NULL), _state(NULL), _parent(NULL)
@@ -44,6 +44,19 @@ RenderState::~RenderState()
     }
 }
 
+void RenderState::initialize()
+{
+    if (StateBlock::_defaultState == NULL)
+    {
+        StateBlock::_defaultState = StateBlock::create();
+    }
+}
+
+void RenderState::finalize()
+{
+    SAFE_RELEASE(StateBlock::_defaultState);
+}
+
 MaterialParameter* RenderState::getParameter(const char* name) const
 {
     assert(name);
@@ -305,75 +318,75 @@ void RenderState::StateBlock::bind()
 void RenderState::StateBlock::bindNoRestore()
 {
     // Update any state that differs from _defaultState and flip _defaultState bits
-    if ((_bits & RS_BLEND) && (_blendEnabled != _defaultState._blendEnabled))
+    if ((_bits & RS_BLEND) && (_blendEnabled != _defaultState->_blendEnabled))
     {
         _blendEnabled ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
-        _defaultState._blendEnabled = _blendEnabled;
+        _defaultState->_blendEnabled = _blendEnabled;
     }
-    if ((_bits & RS_BLEND_FUNC) && (_srcBlend != _defaultState._srcBlend || _dstBlend != _defaultState._dstBlend))
+    if ((_bits & RS_BLEND_FUNC) && (_srcBlend != _defaultState->_srcBlend || _dstBlend != _defaultState->_dstBlend))
     {
         glBlendFunc((GLenum)_srcBlend, (GLenum)_dstBlend);
-        _defaultState._srcBlend = _srcBlend;
-        _defaultState._dstBlend = _dstBlend;
+        _defaultState->_srcBlend = _srcBlend;
+        _defaultState->_dstBlend = _dstBlend;
     }
-    if ((_bits & RS_CULL_FACE) && (_cullFaceEnabled != _defaultState._cullFaceEnabled))
+    if ((_bits & RS_CULL_FACE) && (_cullFaceEnabled != _defaultState->_cullFaceEnabled))
     {
         _cullFaceEnabled ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE);
-        _defaultState._cullFaceEnabled = _cullFaceEnabled;
+        _defaultState->_cullFaceEnabled = _cullFaceEnabled;
     }
-    if ((_bits & RS_DEPTH_TEST) && (_depthTestEnabled != _defaultState._depthTestEnabled))
+    if ((_bits & RS_DEPTH_TEST) && (_depthTestEnabled != _defaultState->_depthTestEnabled))
     {
         _depthTestEnabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
-        _defaultState._depthTestEnabled = _depthTestEnabled;
+        _defaultState->_depthTestEnabled = _depthTestEnabled;
     }
-    if ((_bits & RS_DEPTH_WRITE) && (_depthWriteEnabled != _defaultState._depthWriteEnabled))
+    if ((_bits & RS_DEPTH_WRITE) && (_depthWriteEnabled != _defaultState->_depthWriteEnabled))
     {
         glDepthMask(_depthWriteEnabled);
-        _defaultState._depthWriteEnabled = _depthWriteEnabled;
+        _defaultState->_depthWriteEnabled = _depthWriteEnabled;
     }
 
-    _defaultState._bits |= _bits;
+    _defaultState->_bits |= _bits;
 }
 
 void RenderState::StateBlock::restore(long stateOverrideBits)
 {
     // If there is no state to restore (i.e. no non-default state), do nothing
-    if (_defaultState._bits == 0)
+    if (_defaultState->_bits == 0)
     {
         return;
     }
 
     // Restore any state that is not overridden and is not default
-    if (!(stateOverrideBits & RS_BLEND) && (_defaultState._bits & RS_BLEND))
+    if (!(stateOverrideBits & RS_BLEND) && (_defaultState->_bits & RS_BLEND))
     {
         glDisable(GL_BLEND);
-        _defaultState._bits &= ~RS_BLEND;
-        _defaultState._blendEnabled = false;
+        _defaultState->_bits &= ~RS_BLEND;
+        _defaultState->_blendEnabled = false;
     }
-    if (!(stateOverrideBits & RS_BLEND_FUNC) && (_defaultState._bits & RS_BLEND_FUNC))
+    if (!(stateOverrideBits & RS_BLEND_FUNC) && (_defaultState->_bits & RS_BLEND_FUNC))
     {
         glBlendFunc(GL_ONE, GL_ONE);
-        _defaultState._bits &= ~RS_BLEND_FUNC;
-        _defaultState._srcBlend = RenderState::BLEND_ONE;
-        _defaultState._dstBlend = RenderState::BLEND_ONE;
+        _defaultState->_bits &= ~RS_BLEND_FUNC;
+        _defaultState->_srcBlend = RenderState::BLEND_ONE;
+        _defaultState->_dstBlend = RenderState::BLEND_ONE;
     }
-    if (!(stateOverrideBits & RS_CULL_FACE) && (_defaultState._bits & RS_CULL_FACE))
+    if (!(stateOverrideBits & RS_CULL_FACE) && (_defaultState->_bits & RS_CULL_FACE))
     {
         glDisable(GL_CULL_FACE);
-        _defaultState._bits &= ~RS_CULL_FACE;
-        _defaultState._cullFaceEnabled = false;
+        _defaultState->_bits &= ~RS_CULL_FACE;
+        _defaultState->_cullFaceEnabled = false;
     }
-    if (!(stateOverrideBits & RS_DEPTH_TEST) && (_defaultState._bits & RS_DEPTH_TEST))
+    if (!(stateOverrideBits & RS_DEPTH_TEST) && (_defaultState->_bits & RS_DEPTH_TEST))
     {
         glDisable(GL_DEPTH_TEST);
-        _defaultState._bits &= ~RS_DEPTH_TEST;
-        _defaultState._depthTestEnabled = false;
+        _defaultState->_bits &= ~RS_DEPTH_TEST;
+        _defaultState->_depthTestEnabled = false;
     }
-    if (!(stateOverrideBits & RS_DEPTH_WRITE) && (_defaultState._bits & RS_DEPTH_WRITE))
+    if (!(stateOverrideBits & RS_DEPTH_WRITE) && (_defaultState->_bits & RS_DEPTH_WRITE))
     {
         glDepthMask(GL_TRUE);
-        _defaultState._bits &= ~RS_DEPTH_WRITE;
-        _defaultState._depthWriteEnabled = true;
+        _defaultState->_bits &= ~RS_DEPTH_WRITE;
+        _defaultState->_depthWriteEnabled = true;
     }
 }
 

+ 12 - 3
gameplay/src/RenderState.h

@@ -16,6 +16,7 @@ class Pass;
 
 class RenderState : public Ref
 {
+    friend class Game;
     friend class Material;
     friend class Technique;
     friend class Pass;
@@ -198,7 +199,7 @@ public:
         // State bits
         long _bits;
 
-        static StateBlock _defaultState;
+        static StateBlock* _defaultState;
     };
 
     /**
@@ -279,6 +280,16 @@ protected:
      */
     virtual ~RenderState();
 
+    /**
+     * Static initializer that is called during game startup.
+     */
+    static void initialize();
+
+    /**
+     * Static finalizer that is called during game shutdown.
+     */
+    static void finalize();
+
     /**
      * Sets the node that this render state is bound to.
      *
@@ -311,8 +322,6 @@ protected:
     Node* _nodeBinding;
     mutable StateBlock* _state;
     RenderState* _parent;
-   
-    static StateBlock* _restoreState;
 };
 
 }

+ 37 - 7
gameplay/src/Scene.cpp

@@ -21,6 +21,19 @@ Scene::Scene(const Scene& copy)
 
 Scene::~Scene()
 {
+    // Unbind our active camera from the audio listener
+    if (_activeCamera)
+    {
+        AudioListener* audioListener = AudioListener::getInstance();
+        if (audioListener && (audioListener->getCamera() == _activeCamera))
+        {
+            audioListener->setCamera(NULL);
+        }
+
+        SAFE_RELEASE(_activeCamera);
+    }
+
+    // Remove all nodes from the scene
     removeAllNodes();
 }
 
@@ -116,6 +129,14 @@ void Scene::addNode(Node* node)
 {
     assert(node);
 
+    if (node->_scene == this)
+    {
+        // The node is already a member of this scene.
+        return;
+    }
+
+    node->addRef();
+
     // If the node is part of another scene, remove it.
     if (node->_scene && node->_scene != this)
     {
@@ -125,7 +146,7 @@ void Scene::addNode(Node* node)
     // If the node is part of another node hierarchy, remove it.
     if (node->getParent())
     {
-        node->remove();
+        node->getParent()->removeChild(node);
     }
 
     // Link the new node into our list.
@@ -142,8 +163,6 @@ void Scene::addNode(Node* node)
 
     node->_scene = this;
 
-    node->addRef();
-
     ++_nodeCount;
 
     // If we don't have an active camera set, then check for one and set it.
@@ -164,9 +183,6 @@ void Scene::removeNode(Node* node)
     if (node->_scene != this)
         return;
 
-    node->remove();
-    node->_scene = NULL;
-
     if (node == _firstNode)
     {
         _firstNode = node->_nextSibling;
@@ -176,6 +192,9 @@ void Scene::removeNode(Node* node)
         _lastNode = node->_prevSibling;
     }
 
+    node->remove();
+    node->_scene = NULL;
+
     SAFE_RELEASE(node);
 
     --_nodeCount;
@@ -209,16 +228,26 @@ void Scene::setActiveCamera(Camera* camera)
     // Make sure we don't release the camera if the same camera is set twice.
     if (_activeCamera != camera)
     {
+        AudioListener* audioListener = AudioListener::getInstance();
+
         if (_activeCamera)
         {
+            // Unbind the active camera from the audio listener
+            if (audioListener && (audioListener->getCamera() == _activeCamera))
+            {
+                AudioListener::getInstance()->setCamera(NULL);
+            }
+
             SAFE_RELEASE(_activeCamera);
         }
 
         _activeCamera = camera;
+
         if (_activeCamera)
         {
             _activeCamera->addRef();
-            if (_bindAudioListenerToCamera && AudioListener::getInstance())
+
+            if (audioListener && _bindAudioListenerToCamera)
             {
                 AudioListener::getInstance()->setCamera(_activeCamera);
             }
@@ -231,6 +260,7 @@ void Scene::bindAudioListenerToCamera(bool bind)
     if (_bindAudioListenerToCamera != bind)
     {
         _bindAudioListenerToCamera = bind;
+
         if (AudioListener::getInstance())
         {
             AudioListener::getInstance()->setCamera(bind ? _activeCamera : NULL);

+ 8 - 8
gameplay/src/Vector2.cpp

@@ -39,26 +39,26 @@ Vector2::~Vector2()
 
 const Vector2& Vector2::zero()
 {
-    static Vector2* value = new Vector2(0.0f, 0.0f);
-    return *value;
+    static Vector2 value(0.0f, 0.0f);
+    return value;
 }
 
 const Vector2& Vector2::one()
 {
-    static Vector2* value = new Vector2(1.0f, 1.0f);
-    return *value;
+    static Vector2 value(1.0f, 1.0f);
+    return value;
 }
 
 const Vector2& Vector2::unitX()
 {
-    static Vector2* value = new Vector2(1.0f, 0.0f);
-    return *value;
+    static Vector2 value(1.0f, 0.0f);
+    return value;
 }
 
 const Vector2& Vector2::unitY()
 {
-    static Vector2* value = new Vector2(0.0f, 1.0f);
-    return *value;
+    static Vector2 value(0.0f, 1.0f);
+    return value;
 }
 
 bool Vector2::isZero() const

+ 12 - 12
gameplay/src/Vector4.cpp

@@ -54,38 +54,38 @@ Vector4::~Vector4()
 
 const Vector4& Vector4::zero()
 {
-    static Vector4* value = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
-    return *value;
+    static Vector4 value(0.0f, 0.0f, 0.0f, 0.0f);
+    return value;
 }
 
 const Vector4& Vector4::one()
 {
-    static Vector4* value = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
-    return *value;
+    static Vector4 value(1.0f, 1.0f, 1.0f, 1.0f);
+    return value;
 }
 
 const Vector4& Vector4::unitX()
 {
-    static Vector4* value = new Vector4(1.0f, 0.0f, 0.0f, 0.0f);
-    return *value;
+    static Vector4 value(1.0f, 0.0f, 0.0f, 0.0f);
+    return value;
 }
 
 const Vector4& Vector4::unitY()
 {
-    static Vector4* value = new Vector4(0.0f, 1.0f, 0.0f, 0.0f);
-    return *value;
+    static Vector4 value(0.0f, 1.0f, 0.0f, 0.0f);
+    return value;
 }
 
 const Vector4& Vector4::unitZ()
 {
-    static Vector4* value = new Vector4(0.0f, 0.0f, 1.0f, 0.0f);
-    return *value;
+    static Vector4 value(0.0f, 0.0f, 1.0f, 0.0f);
+    return value;
 }
 
 const Vector4& Vector4::unitW()
 {
-    static Vector4* value = new Vector4(0.0f, 0.0f, 0.0f, 1.0f);
-    return *value;
+    static Vector4 value(0.0f, 0.0f, 0.0f, 1.0f);
+    return value;
 }
 
 bool Vector4::isZero() const

+ 3 - 1
gameplay/src/gameplay-main-qnx.h

@@ -15,7 +15,9 @@ int main(int argc, char** argv)
     Game* game = Game::getInstance();
     assert(game != NULL);
     Platform* platform = Platform::create(game);
-    return platform->enterMessagePump();
+    int result = platform->enterMessagePump();
+    delete platform;
+    return result;
 }
 
 

+ 3 - 1
gameplay/src/gameplay-main-win32.h

@@ -22,7 +22,9 @@ extern "C" int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LP
     Game* game = Game::getInstance();
     assert(game != NULL);
     Platform* platform = Platform::create(game);
-    return platform->enterMessagePump();
+    int result = platform->enterMessagePump();
+    delete platform;
+    return result;
 }