Browse Source

Material RTTI slowly coming together

Marko Pintera 13 năm trước cách đây
mục cha
commit
52ab5975f5

+ 1 - 0
CamelotRenderer/CamelotRenderer.vcxproj

@@ -188,6 +188,7 @@
     <ClCompile Include="Source\CmImporter.cpp" />
     <ClCompile Include="Source\CmInput.cpp" />
     <ClCompile Include="Source\CmMaterial.cpp" />
+    <ClCompile Include="Source\CmMaterialRTTI.cpp" />
     <ClCompile Include="Source\CmMesh.cpp" />
     <ClCompile Include="Source\CmMeshData.cpp" />
     <ClCompile Include="Source\CmPass.cpp" />

+ 6 - 0
CamelotRenderer/CamelotRenderer.vcxproj.filters

@@ -79,6 +79,9 @@
     <Filter Include="Source Files\Renderer">
       <UniqueIdentifier>{307618fb-e6a0-41ed-b274-fb1f5f4c6f74}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Source Files\RTTI">
+      <UniqueIdentifier>{dc50e07b-6351-4bc2-8bfa-cc3fc1d26c39}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <Text Include="TODO.txt" />
@@ -441,5 +444,8 @@
     <ClCompile Include="Source\CmRendererManager.cpp">
       <Filter>Source Files\Renderer</Filter>
     </ClCompile>
+    <ClCompile Include="Source\CmMaterialRTTI.cpp">
+      <Filter>Source Files\RTTI</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 6 - 1
CamelotRenderer/Include/CmGpuProgramParams.h

@@ -573,7 +573,12 @@ namespace CamelotEngine {
 		@param dest Pointer to a buffer to receive the values
 		*/
 		void _readRawConstants(size_t physicalIndex, size_t count, int* dest);
-
+		/** Read a texture from the underlying texture 
+		array at the given physical index.
+		@param physicalIndex The array position of the texture
+		@param dest Reference of the texture to store
+		*/
+		void _readTexture(size_t physicalIndex, TextureRef& dest);
 		/** Write a 4-element floating-point parameter to the program directly to 
 		the underlying constants buffer.
 		@note You can use these methods if you have already derived the physical

+ 220 - 0
CamelotRenderer/Include/CmMaterialRTTI.h

@@ -2,13 +2,217 @@
 
 #include "CmPrerequisites.h"
 #include "CmRTTIType.h"
+#include "CmVector2.h"
+#include "CmVector3.h"
+#include "CmVector4.h"
+#include "CmMatrix3.h"
+#include "CmMatrix4.h"
 #include "CmMaterial.h"
+#include "CmGpuProgramParams.h"
+#include "CmShader.h"
+#include "CmDebug.h"
+#include "CmException.h"
+#include "CmKeyValuePair.h"
 
 namespace CamelotEngine
 {
 	class CM_EXPORT MaterialRTTI : public RTTIType<Material, Resource, MaterialRTTI>
 	{
 	private:
+		enum MaterialParamType
+		{
+			MPT_FLOAT,
+			MPT_INT,
+			MPT_TEX
+		};
+
+		struct FloatParam
+		{
+			FloatParam() {}
+
+			FloatParam(UINT32 bufferIdx, GpuConstantType type, UINT32 count)
+				:mBufferIdx(bufferIdx), mType(type), mCount(count)
+			{ }
+
+			UINT32 mBufferIdx;
+			UINT32 mCount;
+			GpuConstantType mType;
+		};
+
+		class FloatParamKVPRTTI;
+		typedef KeyValuePair<String, FloatParam, FloatParamKVPRTTI> FloatParamKVP;
+
+		class FloatParamKVPRTTI : public KeyValuePairRTTI<String, FloatParam>
+		{
+		public:
+			virtual const String& getRTTIName()
+			{
+				static String name = "FloatParamKVP";
+				return name;
+			}
+
+			virtual UINT32 getRTTIId()
+			{
+				return TID_FloatParamKVP;
+			}
+
+			virtual std::shared_ptr<IReflectable> newRTTIObject()
+			{
+				return std::shared_ptr<FloatParamKVP>(new FloatParamKVP());
+			}
+		};
+
+		class TexParamKVPRTTI;
+		typedef KeyValuePair<String, TextureRef, TexParamKVPRTTI> TexParamKVP;
+
+		class TexParamKVPRTTI : public KeyValuePairRTTI<String, TextureRef>
+		{
+		public:
+			virtual const String& getRTTIName()
+			{
+				static String name = "TexParamKVP";
+				return name;
+			}
+
+			virtual UINT32 getRTTIId()
+			{
+				return TID_MaterialTexParamKVP;
+			}
+
+			virtual std::shared_ptr<IReflectable> newRTTIObject()
+			{
+				return std::shared_ptr<TexParamKVP>(new TexParamKVP());
+			}
+		};
+
+		struct MaterialParams : public IReflectable
+		{
+			map<String, FloatParam>::type mFloatParams;
+			map<String, TextureRef>::type mTextureParams;
+
+			vector<float>::type mFloatBuffer;
+
+			/************************************************************************/
+			/* 								RTTI		                     		*/
+			/************************************************************************/
+
+		public:
+			friend class MaterialParamsRTTI;
+
+			static RTTITypeBase* getRTTIStatic();
+			RTTITypeBase* getRTTI() const;
+		};
+
+		class MaterialParamsRTTI : public RTTIType<MaterialParams, IReflectable, MaterialParamsRTTI>
+		{
+		private:
+			std::shared_ptr<FloatParamKVP> getFloatParam(MaterialParams* obj, UINT32 idx)
+			{
+				UINT32 curIdx = 0;
+				for(auto iter = obj->mFloatParams.begin(); iter != obj->mFloatParams.end(); ++iter)
+				{
+					if(curIdx == idx)
+					{
+						return std::shared_ptr<FloatParamKVP>(new FloatParamKVP(iter->first, iter->second));
+					}
+
+					curIdx++;
+				}
+
+				CM_EXCEPT(InternalErrorException, "Invalid index.");
+			}
+
+			void setFloatParam(MaterialParams* obj, UINT32 idx, std::shared_ptr<FloatParamKVP> value)
+			{
+				obj->mFloatParams[value->mKey] = value->mValue;
+			}
+
+			void setNumFloatParams(MaterialParams* obj, UINT32 size)
+			{
+				// Do nothing. Map is expanded automatically as entries are added
+			}
+
+			UINT32 getNumFloatParams(MaterialParams* obj)
+			{
+				obj->mFloatParams.size();
+			}
+
+			std::shared_ptr<TexParamKVP> getTexParam(MaterialParams* obj, UINT32 idx)
+			{
+				UINT32 curIdx = 0;
+				for(auto iter = obj->mTextureParams.begin(); iter != obj->mTextureParams.end(); ++iter)
+				{
+					if(curIdx == idx)
+					{
+						return std::shared_ptr<TexParamKVP>(new TexParamKVP(iter->first, iter->second));
+					}
+
+					curIdx++;
+				}
+
+				CM_EXCEPT(InternalErrorException, "Invalid index.");
+			}
+
+			void setTexParam(MaterialParams* obj, UINT32 idx, std::shared_ptr<TexParamKVP> value)
+			{
+				obj->mTextureParams[value->mKey] = value->mValue;
+			}
+
+			void setNumTexParams(MaterialParams* obj, UINT32 size)
+			{
+				// Do nothing. Map is expanded automatically as entries are added
+			}
+
+			UINT32 getNumTexParams(MaterialParams* obj)
+			{
+				obj->mTextureParams.size();
+			}
+
+			ManagedDataBlock getFloatBuffer(MaterialParams* obj)
+			{
+				size_t bufferSize = obj->mFloatBuffer.size();
+				float* buffer = new float[bufferSize];
+
+				for(size_t i = 0; i < bufferSize; i++)
+					buffer[i] = obj->mFloatBuffer[i];
+
+				return ManagedDataBlock((UINT8*)buffer, bufferSize, true);
+			}
+
+			void setFloatBuffer(MaterialParams* obj, ManagedDataBlock data)
+			{
+				obj->mFloatBuffer.clear();
+
+				float* buffer = (float*)data.getData();
+				for(UINT32 i = 0; i < data.getSize(); i++)
+					obj->mFloatBuffer.push_back(buffer[i]);
+
+				data.destroy();
+			}
+
+		public:
+			MaterialParamsRTTI()
+			{
+				
+			}
+
+			virtual const String& getRTTIName()
+			{
+				static String name = "MaterialParams";
+				return name;
+			}
+
+			virtual UINT32 getRTTIId()
+			{
+				return TID_MaterialParams;
+			}
+
+			virtual std::shared_ptr<IReflectable> newRTTIObject()
+			{
+				return std::shared_ptr<MaterialParams>(new MaterialParams());
+			}
+		};
+
 		ShaderPtr getShader(Material* obj)
 		{
 			return obj->getShader();
@@ -19,10 +223,26 @@ namespace CamelotEngine
 			obj->setShader(val);
 		}
 
+		std::shared_ptr<MaterialParams> getMaterialParams(Material* obj)
+		{
+			return boost::any_cast<std::shared_ptr<MaterialParams>>(obj->mRTTIData);
+		}
+
+		void setMaterialParams(Material* obj, std::shared_ptr<MaterialParams> value)
+		{
+			obj->mRTTIData = value;
+		}
+
+		virtual void onSerializationStarted(IReflectable* obj);
+		virtual void onSerializationEnded(IReflectable* obj);
+		virtual void onDeserializationStarted(IReflectable* obj);
+		virtual void onDeserializationEnded(IReflectable* obj);
+
 	public:
 		MaterialRTTI()
 		{
 			addReflectablePtrField("mShader", 0, &MaterialRTTI::getShader, &MaterialRTTI::setShader);
+			addReflectablePtrField("mMaterialParams", 1, &MaterialRTTI::getMaterialParams, &MaterialRTTI::setMaterialParams);
 		}
 
 		virtual const String& getRTTIName()

+ 4 - 1
CamelotRenderer/Include/CmPrerequisites.h

@@ -165,7 +165,10 @@ namespace CamelotEngine
 		TID_Pass = 1014,
 		TID_Technique = 1015,
 		TID_Shader = 1016,
-		TID_Material = 1017
+		TID_Material = 1017,
+		TID_MaterialParams = 1018,
+		TID_FloatParamKVP = 1019,
+		TID_MaterialTexParamKVP = 1020
 	};
 }
 

+ 6 - 0
CamelotRenderer/Source/CmGpuProgramParams.cpp

@@ -392,6 +392,12 @@ namespace CamelotEngine
 		assert(physicalIndex + count <= mIntConstants.size());
 		memcpy(dest, &mIntConstants[physicalIndex], sizeof(int) * count);
 	}
+	//-----------------------------------------------------------------------------
+	void GpuProgramParameters::_readTexture(size_t physicalIndex, TextureRef& dest)
+	{
+		assert(physicalIndex < mTextures.size());
+		dest = mTextures[physicalIndex];
+	}
 	//---------------------------------------------------------------------
 	GpuLogicalIndexUse* GpuProgramParameters::_getFloatConstantLogicalIndexUse(
 		size_t logicalIndex, size_t requestedSize, UINT16 variability)

+ 37 - 0
CamelotRenderer/Source/CmManagedDataBlock.cpp

@@ -0,0 +1,37 @@
+#include "CmManagedDataBlock.h"
+#include "CmException.h"
+
+namespace CamelotEngine
+{
+	ManagedDataBlock::ManagedDataBlock(UINT8* data, UINT32 size, bool managed)
+		:mData(data), mSize(size), mManaged(managed), mIsDataOwner(true)
+	{ }
+
+	ManagedDataBlock::ManagedDataBlock(const ManagedDataBlock& source)
+	{
+		mData = source.mData;
+		mSize = source.mSize;
+		mManaged = source.mManaged;
+
+		mIsDataOwner = true;
+		source.mIsDataOwner = false;
+	}
+
+	ManagedDataBlock::~ManagedDataBlock()
+	{
+		if(mManaged && mIsDataOwner)
+			delete[] mData;
+	}
+
+	void ManagedDataBlock::destroy()
+	{
+		if(mManaged)
+			CM_EXCEPT(InternalErrorException, "Trying to manually destroy managed data.");
+
+		if(mData != nullptr)
+			delete[] mData;
+
+		mSize = 0;
+		mData = nullptr;
+	}
+}

+ 159 - 0
CamelotRenderer/Source/CmMaterialRTTI.cpp

@@ -0,0 +1,159 @@
+#include "CmMaterialRTTI.h"
+
+namespace CamelotEngine
+{
+	RTTITypeBase* MaterialRTTI::MaterialParams::getRTTIStatic()
+	{
+		return MaterialParamsRTTI::instance();
+	}
+
+	RTTITypeBase* MaterialRTTI::MaterialParams::getRTTI() const
+	{
+		return MaterialParams::getRTTIStatic();
+	}
+
+	void MaterialRTTI::onSerializationStarted(IReflectable* obj)
+	{
+		Material* material = static_cast<Material*>(obj);
+		std::shared_ptr<MaterialParams> params = std::shared_ptr<MaterialParams>(new MaterialParams());
+
+		vector<GpuProgramParametersPtr>::type allParams;
+		for(size_t i = 0; i < material->mParameters.size(); i++)
+		{
+			if(material->mParameters[i].mFragParams != nullptr)
+				allParams.push_back(material->mParameters[i].mFragParams);
+
+			if(material->mParameters[i].mVertParams != nullptr)
+				allParams.push_back(material->mParameters[i].mVertParams);
+
+			if(material->mParameters[i].mGeomParams != nullptr)
+				allParams.push_back(material->mParameters[i].mGeomParams);
+		}
+
+		for(size_t i = 0; i < allParams.size(); i++)
+		{
+			const GpuNamedConstants& namedConstants = allParams[i]->getConstantDefinitions();
+
+			float tempValue[16];
+			for(auto iter = namedConstants.map.begin(); iter != namedConstants.map.end(); ++iter)
+			{
+				const GpuConstantDefinition& def = iter->second;
+
+				if(def.constType == GCT_SAMPLER2D)
+				{
+					TextureRef value;
+					allParams[i]->_readTexture(iter->second.physicalIndex, value);
+					params->mTextureParams[iter->first] = value;
+				}
+				else
+				{
+					UINT32 fieldSize = def.getElementSize(def.constType, false);
+					switch(def.constType)
+					{
+					case GCT_FLOAT1:
+					case GCT_FLOAT2:
+					case GCT_FLOAT3:
+					case GCT_FLOAT4:
+					case GCT_MATRIX_3X3:
+					case GCT_MATRIX_4X4:
+					case GCT_SAMPLER2D:
+						break;
+					default:
+						CM_EXCEPT(InternalErrorException, "Material parameter type not supported! Type: " + toString(def.constType));
+					}
+
+					if(fieldSize > 16)
+						CM_EXCEPT(InternalErrorException, "Field size larger than the supported size.");
+
+					allParams[i]->_readRawConstants(iter->second.physicalIndex, fieldSize, tempValue);
+
+					auto iterFind = params->mFloatParams.find(iter->first);
+					if(iterFind == params->mFloatParams.end())
+					{
+						params->mFloatParams[iter->first] = FloatParam(params->mFloatBuffer.size(), def.constType, fieldSize);
+
+						for(size_t j = 0; j < fieldSize; j++)
+							params->mFloatBuffer.push_back(tempValue[j]);
+					}
+					else
+						gDebug().logWarning("While saving material found multiple parameters with the same name. Only saving the first.");
+				}
+			}
+		}
+
+		material->mRTTIData = params;
+	}
+
+	void MaterialRTTI::onSerializationEnded(IReflectable* obj)
+	{
+		Material* material = static_cast<Material*>(obj);
+
+		material->mRTTIData = nullptr; // This will delete temporary data as it's stored in a unique ptr
+	}
+
+	void MaterialRTTI::onDeserializationStarted(IReflectable* obj)
+	{
+		// Do nothing
+	}
+
+	void MaterialRTTI::onDeserializationEnded(IReflectable* obj)
+	{
+		Material* material = static_cast<Material*>(obj);
+		std::shared_ptr<MaterialParams> params = boost::any_cast<std::shared_ptr<MaterialParams>>(material->mRTTIData);
+
+		vector<GpuProgramParametersPtr>::type allParams;
+		for(size_t i = 0; i < material->mParameters.size(); i++)
+		{
+			if(material->mParameters[i].mFragParams != nullptr)
+				allParams.push_back(material->mParameters[i].mFragParams);
+
+			if(material->mParameters[i].mVertParams != nullptr)
+				allParams.push_back(material->mParameters[i].mVertParams);
+
+			if(material->mParameters[i].mGeomParams != nullptr)
+				allParams.push_back(material->mParameters[i].mGeomParams);
+		}
+
+		for(size_t i = 0; i < allParams.size(); i++)
+		{
+			const GpuNamedConstants& namedConstants = allParams[i]->getConstantDefinitions();
+
+			float tempValue[16];
+			for(auto iter = namedConstants.map.begin(); iter != namedConstants.map.end(); ++iter)
+			{
+				const GpuConstantDefinition& def = iter->second;
+
+				if(def.constType == GCT_SAMPLER2D)
+				{
+					auto iterFind = params->mTextureParams.find(iter->first);
+
+					if(iterFind != params->mTextureParams.end())
+						allParams[i]->setNamedConstant(iter->first, iterFind->second);
+				}
+				else
+				{
+					auto iterFind = params->mFloatParams.find(iter->first);
+
+					if(iterFind != params->mFloatParams.end() && iterFind->second.mType == def.constType)
+					{
+						FloatParam param = iterFind->second;
+						UINT32 fieldSize = def.getElementSize(def.constType, false);
+
+						if(fieldSize != param.mCount)
+							CM_EXCEPT(InternalErrorException, "Deserializing material parameter but field sizes don't match.");
+
+						if(fieldSize > 16)
+							CM_EXCEPT(InternalErrorException, "Field size larger than the supported size.");
+
+						for(size_t j = 0; j < fieldSize; j++)
+							tempValue[j] = params->mFloatBuffer[param.mBufferIdx + j];
+
+						allParams[i]->_writeRawConstants(def.physicalIndex, tempValue, fieldSize);
+					}
+				}
+			}
+		}
+
+		material->mRTTIData = nullptr; // This will delete temporary data as it's stored in a unique ptr
+	}
+}

+ 2 - 0
CamelotUtility/CamelotUtility.vcxproj

@@ -82,6 +82,7 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="..\CamelotRenderer\Source\CmManagedDataBlock.cpp" />
     <ClCompile Include="Source\CmUUID.cpp" />
     <ClCompile Include="Source\CmWorkQueue.cpp" />
     <ClCompile Include="Source\Win32\CmTimer.cpp" />
@@ -95,6 +96,7 @@
     <ClInclude Include="Include\CmFileSerializer.h" />
     <ClInclude Include="Include\CmFileSystem.h" />
     <ClInclude Include="Include\CmIReflectable.h" />
+    <ClInclude Include="Include\CmKeyValuePair.h" />
     <ClInclude Include="Include\CmLog.h" />
     <ClInclude Include="Include\CmManagedDataBlock.h" />
     <ClInclude Include="Include\CmMathAsm.h" />

+ 6 - 0
CamelotUtility/CamelotUtility.vcxproj.filters

@@ -192,6 +192,9 @@
     <ClInclude Include="Include\CmWorkQueue.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmKeyValuePair.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Include\CmAxisAlignedBox.cpp">
@@ -281,5 +284,8 @@
     <ClCompile Include="Source\CmWorkQueue.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\CamelotRenderer\Source\CmManagedDataBlock.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 5 - 0
CamelotUtility/Include/CmIReflectable.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "CmPrerequisitesUtil.h"
+#include <boost/any.hpp>
 
 namespace CamelotEngine
 {
@@ -45,5 +46,9 @@ namespace CamelotEngine
 		bool isDerivedFrom(RTTITypeBase* base);
 
 		UINT32 getTypeId();
+
+	protected:
+		boost::any mRTTIData; // Temporary per-instance data storage used during various RTTI operations.
+							  // Needed since there is one RTTI class instance per type and sometimes we need per-instance data.
 	};
 }

+ 99 - 0
CamelotUtility/Include/CmKeyValuepair.h

@@ -0,0 +1,99 @@
+#pragma once
+
+#include "CmPrerequisitesUtil.h"
+#include "CmIReflectable.h"
+#include "CmRTTIType.h"
+
+namespace CamelotEngine
+{
+	template <typename Key, typename Value>
+	class KeyValuePairRTTI;
+
+	template <typename Key, typename Value>
+	class KeyValuePairBase : public IReflectable
+	{
+	public:
+		KeyValuePairBase() {}
+		KeyValuePairBase(const Key& key, const Value& value)
+			:mKey(key), mValue(value)
+		{ }
+
+		Key mKey;
+		Value mValue;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+
+	public:
+		static RTTITypeBase* getRTTIStatic()
+		{
+			return KeyValuePairRTTI<Key, Value>::instance();
+		}
+
+		RTTITypeBase* getRTTI() const
+		{
+			return KeyValuePairBase<Key, Value>::getRTTIStatic();
+		}
+	};
+
+	template <typename Key, typename Value>
+	class KeyValuePairRTTI : public RTTIType<KeyValuePairBase<Key, Value>, IReflectable, KeyValuePairRTTI<Key, Value>>
+	{
+	private:
+		Key& getKey(KeyValuePairBase<Key, Value>* obj) { return obj->mKey; }
+		void setKey(KeyValuePairBase<Key, Value>* obj,  Key& val) { obj->mKey = val; }
+
+		Value& getValue(KeyValuePairBase<Key, Value>* obj) { return obj->mValue; }
+		void setValue(KeyValuePairBase<Key, Value>* obj,  Value& val) { obj->mValue = val; }
+
+	public:
+		KeyValuePairRTTI()
+		{
+			addPlainField("mKey", 0, &KeyValuePairRTTI::getKey, &KeyValuePairRTTI::setKey);
+			addPlainField("mValue", 1, &KeyValuePairRTTI::getValue, &KeyValuePairRTTI::setValue);
+		}
+
+	public:
+		virtual const String& getRTTIName()
+		{
+			static String name = "Abstract";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId()
+		{
+			return TID_Abstract;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		{
+			CM_EXCEPT(InternalErrorException, "Cannot instantiate abstract class.");
+		}
+	};
+
+	template <typename Key, typename Value, typename RTTIClass>
+	class KeyValuePair : public KeyValuePairBase<Key, Value>
+	{
+	public:
+		KeyValuePair() {}
+		KeyValuePair(const Key& key, const Value& value)
+			:KeyValuePairBase<Key, Value>(key, value)
+		{ }
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+
+	public:
+		static RTTITypeBase* getRTTIStatic()
+		{
+			return RTTIClass::instance();
+		}
+
+		RTTITypeBase* getRTTI() const
+		{
+			return KeyValuePair<Key, Value, RTTIClass>::getRTTIStatic();
+		}
+	};
+}

+ 8 - 18
CamelotUtility/Include/CmManagedDataBlock.h

@@ -7,7 +7,7 @@ namespace CamelotEngine
 	/**
 	 * @brief	Data block holding an array of bytes, usually used in serialization.
 	 */
-	class ManagedDataBlock
+	class CM_UTILITY_EXPORT ManagedDataBlock
 	{
 	public:
 		/**
@@ -23,25 +23,15 @@ namespace CamelotEngine
 		 * 							instead (e.g. maybe it's needed in a different format). 
 		 * 							In that case set managed to true so it can be properly freed.
 		 */
-		ManagedDataBlock(UINT8* data, UINT32 size, bool managed)
-			:mData(data), mSize(size), mManaged(managed), mIsDataOwner(true)
-		{ }
+		ManagedDataBlock(UINT8* data, UINT32 size, bool managed);
+		ManagedDataBlock(const ManagedDataBlock& source);
 
-		ManagedDataBlock(const ManagedDataBlock& source)
-		{
-			mData = source.mData;
-			mSize = source.mSize;
-			mManaged = source.mManaged;
+		~ManagedDataBlock();
 
-			mIsDataOwner = true;
-			source.mIsDataOwner = false;
-		}
-
-		~ManagedDataBlock()
-		{
-			if(mManaged && mIsDataOwner)
-				delete[] mData;
-		}
+		/**
+		 * @brief	Destroy underlying buffers for unmanaged data blocks.
+		 */
+		void destroy();
 
 		UINT8* getData() { return mData; }
 		UINT32 getSize() { return mData ? mSize : 0; }

+ 5 - 0
CamelotUtility/Include/CmRTTIType.h

@@ -51,6 +51,11 @@ namespace CamelotEngine
 		virtual const String& getRTTIName() = 0;
 		virtual UINT32 getRTTIId() = 0;
 
+		virtual void onSerializationStarted(IReflectable* obj) {}
+		virtual void onSerializationEnded(IReflectable* obj) {}
+		virtual void onDeserializationStarted(IReflectable* obj) {}
+		virtual void onDeserializationEnded(IReflectable* obj) {}
+
 		template <class ObjectType, class DataType>
 		void setPlainValue(ObjectType* object, const std::string& name, DataType& value)
 		{

+ 5 - 0
CamelotUtility/Include/CmTypes.h

@@ -19,4 +19,9 @@ namespace CamelotEngine
 	typedef unsigned long long UINT64;
 	typedef long long INT64;
 #endif
+
+	enum TypeID_Utility
+	{
+		TID_Abstract = 50 // Special type ID used for Abstract classes. Only type ID that may be used by more than one class.
+	};
 }

+ 42 - 3
CamelotUtility/Source/CmBinarySerializer.cpp

@@ -208,6 +208,8 @@ namespace CamelotEngine
 		// If an object has base classes, we need to iterate through all of them
 		do
 		{
+			si->onSerializationStarted(object);
+
 			// Encode object ID & type
 			ObjectMetaData objectMetaData = encodeObjectMetaData(objectId, si->getRTTIId(), isBaseClass);
 			COPY_TO_BUFFER(&objectMetaData, sizeof(ObjectMetaData))
@@ -255,7 +257,10 @@ namespace CamelotEngine
 
 								buffer = complexTypeToBuffer(&childObject, buffer, bufferLength, bytesWritten, flushBufferCallback);
 								if(buffer == nullptr)
+								{
+									si->onSerializationEnded(object);
 									return nullptr;
+								}
 							}
 
 							break;
@@ -276,7 +281,12 @@ namespace CamelotEngine
 								{
 									mTotalBytesWritten += *bytesWritten;
 									buffer = flushBufferCallback(buffer - *bytesWritten, *bytesWritten, bufferLength);
-									if(buffer == nullptr || bufferLength < typeSize) return nullptr;
+									if(buffer == nullptr || bufferLength < typeSize)
+									{
+										return nullptr;
+										si->onSerializationEnded(object);
+									}
+
 									*bytesWritten = 0;
 								}
 
@@ -314,7 +324,10 @@ namespace CamelotEngine
 
 							buffer = complexTypeToBuffer(&childObject, buffer, bufferLength, bytesWritten, flushBufferCallback);
 							if(buffer == nullptr)
+							{
+								si->onSerializationEnded(object);
 								return nullptr;
+							}
 
 							break;
 						}
@@ -332,7 +345,11 @@ namespace CamelotEngine
 							{
 								mTotalBytesWritten += *bytesWritten;
 								buffer = flushBufferCallback(buffer - *bytesWritten, *bytesWritten, bufferLength);
-								if(buffer == nullptr || bufferLength < typeSize) return nullptr;
+								if(buffer == nullptr || bufferLength < typeSize)
+								{
+									si->onSerializationEnded(object);
+									return nullptr;
+								}
 								*bytesWritten = 0;
 							}
 
@@ -374,7 +391,11 @@ namespace CamelotEngine
 								
 									mTotalBytesWritten += *bytesWritten;
 									buffer = flushBufferCallback(buffer - *bytesWritten, *bytesWritten, bufferLength);
-									if(buffer == nullptr || bufferLength == 0) return nullptr;
+									if(buffer == nullptr || bufferLength == 0) 
+									{
+										si->onSerializationEnded(object);
+										return nullptr;
+									}
 									*bytesWritten = 0;
 								}
 							}
@@ -389,6 +410,8 @@ namespace CamelotEngine
 				}
 			}
 
+			si->onSerializationEnded(object);
+
 			si = si->getBaseClass();
 			isBaseClass = true;
 
@@ -406,8 +429,13 @@ namespace CamelotEngine
 
 		RTTITypeBase* si = nullptr;
 		if(object != nullptr)
+		{
 			si = object->getRTTI();
 
+			if(si != nullptr)
+				si->onDeserializationStarted(object.get());
+		}
+
 		if((bytesRead + sizeof(ObjectMetaData)) > dataLength)
 		{
 			CM_EXCEPT(InternalErrorException, 
@@ -443,6 +471,9 @@ namespace CamelotEngine
 
 			if(isObjectMetaData(metaData)) // We've reached a new object
 			{
+				if(si != nullptr)
+					si->onDeserializationEnded(object.get());
+
 				if((bytesRead + sizeof(ObjectMetaData)) > dataLength)
 				{
 					CM_EXCEPT(InternalErrorException, 
@@ -471,6 +502,11 @@ namespace CamelotEngine
 						si = nullptr;	
 					}
 
+					if(si != nullptr)
+					{
+						si->onDeserializationStarted(object.get());
+					}
+
 					data += sizeof(ObjectMetaData);
 					bytesRead += sizeof(ObjectMetaData);
 					continue;
@@ -718,6 +754,9 @@ namespace CamelotEngine
 			}
 		}
 
+		if(si != nullptr)
+			si->onDeserializationEnded(object.get());
+
 		return false;
 	}
 

+ 3 - 0
CamelotUtility/Source/CmIReflectable.cpp

@@ -51,6 +51,9 @@ namespace CamelotEngine
 
 	bool IReflectable::isTypeIdDuplicate(UINT32 typeId)
 	{
+		if(typeId == TID_Abstract)
+			return false;
+
 		return IReflectable::getRTTIfromTypeId(typeId) != nullptr;
 	}