Browse Source

Additional 3d support

Brian Fiete 4 years ago
parent
commit
f26df6c86b

+ 1 - 1
BeefLibs/Beefy2D/src/BFApp.bf

@@ -727,7 +727,7 @@ namespace Beefy
 							0.10f,  0.00f,  1.05f, 0,
 							    0,      0,      0, 1);*/
 
-						mGraphics.SetShaderConstantData(0, &mColorMatrix.ValueRef, mColorMatrixDataDef);
+						mGraphics.SetVertexShaderConstantData(0, &mColorMatrix.ValueRef, mColorMatrixDataDef);
 					}
                     window.Draw(mGraphics);
                     window.PostDraw(mGraphics);

+ 52 - 15
BeefLibs/Beefy2D/src/gfx/Graphics.bf

@@ -78,7 +78,11 @@ namespace Beefy.gfx
         protected DisposeProxy mClipDisposeProxy ~ delete _;
         const int32 CLIP_STACK_SIZE = 256;
         public Rect?[] mClipStack = new Rect?[CLIP_STACK_SIZE] ~ delete _;
-		
+
+		public bool mTexWrap;
+		protected DisposeProxy mTexWrapDisableProxy ~ delete _;
+		protected DisposeProxy mTexWrapEnableProxy ~ delete _;
+
         public int32 mClipStackIdx = 0;
         public Rect? mClipRect = null;
 
@@ -106,6 +110,10 @@ namespace Beefy.gfx
             mClipDisposeProxy = new DisposeProxy();
             mClipDisposeProxy.mDisposeProxyDelegate = new => PopClip;
             mRenderStateDisposeProxy = new DisposeProxy();
+			mTexWrapDisableProxy = new DisposeProxy();
+			mTexWrapDisableProxy.mDisposeProxyDelegate = new () => { PopTexWrap(false); };
+			mTexWrapEnableProxy = new DisposeProxy();
+			mTexWrapEnableProxy.mDisposeProxyDelegate = new () => { PopTexWrap(true); };
 
             mWhiteDot = Image.LoadFromFile("!white");
 
@@ -341,15 +349,15 @@ namespace Beefy.gfx
             Rect rectThing = mClipRect.Value;
             mClipRect = rectThing;
 
-			var clipRenderState = AllocRenderState(mDefaultShader, mClipRect);
+			var clipRenderState = AllocRenderState(mDefaultShader, mClipRect, mTexWrap);
 
             //clipRenderState.ClipRect = mClipRect;
-            PushRenderState(clipRenderState);            
+            PushRenderState(clipRenderState);
 
             return mClipDisposeProxy;
         }
 
-		RenderState AllocRenderState(Shader shader, Rect? clipRect)
+		RenderState AllocRenderState(Shader shader, Rect? clipRect, bool texWrap)
 		{
 			RenderState renderState = null;
 			var curRenderState = mRenderStateStack[mRenderStateStackIdx];
@@ -365,6 +373,7 @@ namespace Beefy.gfx
 			}
 			else
 			    renderState = RenderState.Create(curRenderState);
+			renderState.TexWrap = texWrap;
 			renderState.Shader = shader;
 			renderState.ClipRect = clipRect;
 			return renderState;
@@ -375,16 +384,33 @@ namespace Beefy.gfx
 			mClipStackIdx++;
 			mClipStack[mClipStackIdx] = null;
 			mClipRect = null;
-			var clipRenderState = AllocRenderState(mDefaultShader, null);
+			var clipRenderState = AllocRenderState(mDefaultShader, mClipRect, mTexWrap);
             //clipRenderState.ClipRect = null;
             PushRenderState(clipRenderState);
 
             return mClipDisposeProxy;
         }
 
+		public DisposeProxy PushTexWrap(bool texWrap)
+		{
+			bool prevTexWrap = mTexWrap;
+			mTexWrap = texWrap;
+
+			var clipRenderState = AllocRenderState(mDefaultShader, mClipRect, mTexWrap);
+			PushRenderState(clipRenderState);
+
+			return prevTexWrap ? mTexWrapEnableProxy : mTexWrapDisableProxy;
+		}
+
+		protected void PopTexWrap(bool texWrap)
+		{
+			mTexWrap = texWrap;
+			PopRenderState();
+		}
+
 		public void PushTextRenderState()
 		{
-			var textRenderState = AllocRenderState(mTextShader, mClipRect);
+			var textRenderState = AllocRenderState(mTextShader, mClipRect, mTexWrap);
 			//textRenderState.ClipRect = mClipRect;
 			//textRenderState.Shader = mTextShader;
 			PushRenderState(textRenderState);
@@ -419,10 +445,10 @@ namespace Beefy.gfx
         static extern void Gfx_DrawIndexedVertices2D(int32 vertexSize, void* vtxData, int32 vtxCount, uint16* idxData, int32 idxCount, float a, float b, float c, float d, float tx, float ty, float z);
 
         [CallingConvention(.Stdcall), CLink]
-        static extern void Gfx_SetShaderConstantData(int32 slotIdx, void* data, int32 size);
+        static extern void Gfx_SetShaderConstantData(int32 usageIdx, int32 slotIdx, void* data, int32 size);
 
         [CallingConvention(.Stdcall), CLink]
-        static extern void Gfx_SetShaderConstantDataTyped(int32 slotIdx, void* data, int32 size, int32* typeData, int32 typeCount);
+        static extern void Gfx_SetShaderConstantDataTyped(int usageIdx, int32 slotIdx, void* data, int32 size, int32* typeData, int32 typeCount);
 
         [CallingConvention(.Stdcall), CLink]
         static extern void Gfx_DrawQuads(void* textureSegment, Vertex3D* vertices, int32 vtxCount);
@@ -778,22 +804,33 @@ namespace Beefy.gfx
                 mMatrix.a, mMatrix.b, mMatrix.c, mMatrix.d, mMatrix.tx, mMatrix.ty, ZDepth);
         }
 
-        public void SetShaderConstantData(int slotIdx, void* data, int size)
+        public void SetVertexShaderConstantData(int slotIdx, void* data, int size)
         {
-            Gfx_SetShaderConstantData((int32)slotIdx, data, (int32)size);
+            Gfx_SetShaderConstantData(0, (int32)slotIdx, data, (int32)size);
         }
 
-        public void SetShaderConstantData(int32 slotIdx, void* data, ConstantDataDefinition constantDataDefinition)
+		public void SetPixelShaderConstantData(int slotIdx, void* data, int size)
+		{
+		    Gfx_SetShaderConstantData(1, (int32)slotIdx, data, (int32)size);
+		}
+
+        public void SetVertexShaderConstantData(int32 slotIdx, void* data, ConstantDataDefinition constantDataDefinition)
         {
             int32* dataTypesPtr = (int32*)constantDataDefinition.mDataTypes.CArray();
-            Gfx_SetShaderConstantDataTyped(slotIdx, data, constantDataDefinition.mDataSize, dataTypesPtr, (int32)constantDataDefinition.mDataTypes.Count);
+            Gfx_SetShaderConstantDataTyped(0, slotIdx, data, constantDataDefinition.mDataSize, dataTypesPtr, (int32)constantDataDefinition.mDataTypes.Count);
         }
 
-        public void SetShaderConstantData(int32 slotIdx, Matrix4 matrix)
+		public void SetVertexShaderConstantData(int32 slotIdx, Matrix4 matrix)
+		{
+			var mtx = matrix;
+		    Gfx_SetShaderConstantData(0, slotIdx, &mtx, (int32)sizeof(Matrix4));
+		}
+
+        public void SetPixelShaderConstantData(int32 slotIdx, Matrix4 matrix)
         {
 			var mtx = matrix;
-            Gfx_SetShaderConstantData(slotIdx, &mtx, (int32)sizeof(Matrix4));
-        }        
+            Gfx_SetShaderConstantData(1, slotIdx, &mtx, (int32)sizeof(Matrix4));
+        }
 
         public float DrawString(StringView theString, float x, float y, FontAlign alignment = FontAlign.Left, float width = 0, FontOverflowMode overflowMode = FontOverflowMode.Overflow, FontMetrics* fontMetrics = null)
         {

+ 42 - 5
BeefLibs/Beefy2D/src/gfx/Model.bf

@@ -90,15 +90,24 @@ namespace Beefy.gfx
         public void* mNativeModelDef;        
         public float mFrameRate;
         public int32 mJointCount;
-        public Animation[] mAnims;
-        public Dictionary<String, Animation> mAnimMap = new Dictionary<String, Animation>();
+        public Animation[] mAnims ~ DeleteContainerAndItems!(_);
+        public Dictionary<String, Animation> mAnimMap = new Dictionary<String, Animation>() ~ DeleteDictionaryAndKeys!(_);
 
         [CallingConvention(.Stdcall), CLink]
-        extern static void* Res_OpenFBX(String fileName, void* nativeVertexDef);
+        extern static void* Res_OpenFBX(char8* fileName, void* nativeVertexDef);
+
+		[CallingConvention(.Stdcall), CLink]
+		extern static void* Res_OpenGLTF(char8* fileName, char8* baseDir, void* nativeVertexDef);
+
+		[CallingConvention(.Stdcall), CLink]
+		extern static void* Res_OpenModel(char8* fileName, char8* baseDir, void* nativeVertexDef);
 
         [CallingConvention(.Stdcall), CLink]
         extern static void* ModelDef_CreateModelInstance(void* nativeModel);
 
+		[CallingConvention(.Stdcall), CLink]
+		extern static char8* ModelDef_GetInfo(void* nativeModel);
+
         [CallingConvention(.Stdcall), CLink]
         extern static float ModelDef_GetFrameRate(void* nativeModel);
 
@@ -111,6 +120,12 @@ namespace Beefy.gfx
         [CallingConvention(.Stdcall), CLink]
         extern static void* ModelDef_GetAnimation(void* nativeModel, int32 animIdx);
 
+		[CallingConvention(.Stdcall), CLink]
+		extern static void ModelDef_SetTextures(void* nativeModel, int32 meshIdx, int32 primitivesIdx, char8** paths, int32 pathCount);
+
+		[CallingConvention(.Stdcall), CLink]
+		extern static Span<uint8> Res_SerializeModel(void* nativeModel);
+
         this(void* nativeModelDef)
         {
             mNativeModelDef = nativeModelDef;
@@ -129,9 +144,15 @@ namespace Beefy.gfx
             }
         }
 
-        public static ModelDef LoadModel(String fileName)
+        public static ModelDef LoadModel(String fileName, String baseDir)
         {
-            void* nativeModelDef = Res_OpenFBX(fileName, VertexDef.sVertexDefinition.mNativeVertexDefinition);
+			void* nativeModelDef = null;
+			if (fileName.EndsWith(".bfm", .OrdinalIgnoreCase))
+				nativeModelDef = Res_OpenModel(fileName, baseDir, VertexDef.sVertexDefinition.mNativeVertexDefinition);
+			else if (fileName.EndsWith(".fbx", .OrdinalIgnoreCase))
+            	nativeModelDef = Res_OpenFBX(fileName, VertexDef.sVertexDefinition.mNativeVertexDefinition);
+			else
+				nativeModelDef = Res_OpenGLTF(fileName, baseDir, VertexDef.sVertexDefinition.mNativeVertexDefinition);
             if (nativeModelDef == null)
                 return null;
             return new ModelDef(nativeModelDef);            
@@ -150,6 +171,22 @@ namespace Beefy.gfx
         {
             return mAnimMap[name];
         }
+
+		public void GetInfo(String str)
+		{
+			str.Append(ModelDef_GetInfo(mNativeModelDef));
+		}
+
+		public void SetTextures(int meshIdx, int primitivesIdx, Span<char8*> paths)
+		{
+			ModelDef_SetTextures(mNativeModelDef, (.)meshIdx, (.)primitivesIdx, paths.Ptr, (.)paths.Length);
+		}
+
+		public void Serialize(List<uint8> data)
+		{
+			var span = Res_SerializeModel(mNativeModelDef);
+			data.AddRange(span);
+		}
     }
 
     public class ModelInstance : RenderCmd

+ 12 - 1
BeefLibs/Beefy2D/src/gfx/RenderState.bf

@@ -31,6 +31,9 @@ namespace Beefy.gfx
         [CallingConvention(.Stdcall), CLink]
         static extern void RenderState_SetClip(void* renderState, float x, float y, float width, float height);
 
+		[CallingConvention(.Stdcall), CLink]
+		static extern void RenderState_SetTexWrap(void* renderState, bool texWrap);
+
         [CallingConvention(.Stdcall), CLink]
         static extern void RenderState_DisableClip(void* renderState);
 
@@ -103,7 +106,15 @@ namespace Beefy.gfx
                 else
                     RenderState_DisableClip(mNativeRenderState);
             }
-        }        
+        }
+
+		public bool TexWrap
+		{
+			set
+			{
+				RenderState_SetTexWrap(mNativeRenderState, value);
+			}
+		}
     }
 #else
     public class RenderState

+ 2 - 1
BeefLibs/corlib/src/Platform.bf

@@ -410,7 +410,8 @@ namespace System
 			AppData_LocalLow,
 			AppData_Roaming,
 			Programs,
-			Programs_Common
+			Programs_Common,
+			Documents
 		}
 
 		public static Result<void, Platform.Result> GetStrHelper(String outStr, delegate void (char8* outPtr, int32* outSize, Result* outResult) func)

+ 38 - 0
BeefLibs/corlib/src/Security/Cryptography/MD5.bf

@@ -1,5 +1,35 @@
 namespace System.Security.Cryptography
 {
+	struct HashEncode
+	{
+		// Only 63 chars - skip zero
+		const char8[?] cHash64bToChar = .( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+		'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
+		'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+		'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_' );
+		public static void HashEncode64(uint64 val, String outStr)
+		{
+			var val;
+			if ((int64)val < 0)
+			{
+				uint64 flippedNum = (uint64)-(int64)val;
+				// Only flip if the encoded result would actually be shorter
+				if (flippedNum <= 0x00FFFFFFFFFFFFFFL)
+				{
+					val = flippedNum;
+					outStr.Append('_');
+				}
+			}
+
+			for (int i = 0; i < 10; i++)
+			{
+				int charIdx = (int)((val >> (i * 6)) & 0x3F) - 1;
+				if (charIdx != -1)
+					outStr.Append(cHash64bToChar[charIdx]);
+			}
+		}
+	}
+
 	struct MD5Hash
 	{
 		public uint8[16] mHash;
@@ -50,6 +80,14 @@ namespace System.Security.Cryptography
 				val.ToString(strBuffer, "X2", null);
 			}
 		}
+
+		public void Encode(String outStr)
+		{
+#unwarn
+			HashEncode.HashEncode64(((uint64*)&mHash)[0], outStr);
+#unwarn
+			HashEncode.HashEncode64(((uint64*)&mHash)[1], outStr);
+		}
 	}
 
 	class MD5

+ 9 - 4
BeefySysLib/BeefySysLib.cpp

@@ -643,14 +643,14 @@ BF_EXPORT void BF_CALLTYPE Gfx_DrawIndexedVertices2D(int vertexSize, void* vtxDa
 	}
 }
 
-BF_EXPORT void BF_CALLTYPE Gfx_SetShaderConstantData(int slotIdx, void* constData, int size)
+BF_EXPORT void BF_CALLTYPE Gfx_SetShaderConstantData(int usageIdx, int slotIdx, void* constData, int size)
 {
-	gBFApp->mRenderDevice->mCurDrawLayer->SetShaderConstantData(slotIdx, constData, size);
+	gBFApp->mRenderDevice->mCurDrawLayer->SetShaderConstantData(usageIdx, slotIdx, constData, size);
 }
 
-BF_EXPORT void BF_CALLTYPE Gfx_SetShaderConstantDataTyped(int slotIdx, void* constData, int size, int* typeData, int typeCount)
+BF_EXPORT void BF_CALLTYPE Gfx_SetShaderConstantDataTyped(int usageIdx, int slotIdx, void* constData, int size, int* typeData, int typeCount)
 {
-	gBFApp->mRenderDevice->mCurDrawLayer->SetShaderConstantDataTyped(slotIdx, constData, size, typeData, typeCount);
+	gBFApp->mRenderDevice->mCurDrawLayer->SetShaderConstantDataTyped(usageIdx, slotIdx, constData, size, typeData, typeCount);
 }
 
 BF_EXPORT void BF_CALLTYPE Gfx_QueueRenderCmd(RenderCmd* renderCmd)
@@ -678,6 +678,11 @@ BF_EXPORT void BF_CALLTYPE RenderState_Delete(RenderState* renderState)
 	delete renderState;
 }
 
+BF_EXPORT void BF_CALLTYPE RenderState_SetTexWrap(RenderState* renderState, bool texWrap)
+{
+	renderState->SetTexWrap(texWrap);
+}
+
 BF_EXPORT void BF_CALLTYPE RenderState_SetClip(RenderState* renderState, float x, float y, float width, float height)
 {	
 	BF_ASSERT((width >= 0) && (height >= 0));

+ 4 - 0
BeefySysLib/BeefySysLib.vcxproj

@@ -445,6 +445,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"</Co
     <ClCompile Include="FileStream.cpp" />
     <ClCompile Include="gfx\DrawLayer.cpp" />
     <ClCompile Include="gfx\FTFont.cpp" />
+    <ClCompile Include="gfx\glTF.cpp" />
     <ClCompile Include="gfx\ModelDef.cpp" />
     <ClCompile Include="gfx\ModelInstance.cpp" />
     <ClCompile Include="gfx\RenderCmd.cpp" />
@@ -1933,6 +1934,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"</Co
     <ClCompile Include="util\CubicSpline.cpp" />
     <ClCompile Include="util\Hash.cpp" />
     <ClCompile Include="util\Heap.cpp" />
+    <ClCompile Include="util\Json.cpp" />
     <ClCompile Include="util\MappedFile.cpp" />
     <ClCompile Include="util\Matrix4.cpp" />
     <ClCompile Include="util\PerfTimer.cpp" />
@@ -1958,6 +1960,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"</Co
     <ClInclude Include="gfx\DrawLayer.h" />
     <ClInclude Include="gfx\Font.h" />
     <ClInclude Include="gfx\FTFont.h" />
+    <ClInclude Include="gfx\glTF.h" />
     <ClInclude Include="gfx\ModelDef.h" />
     <ClInclude Include="gfx\ModelInstance.h" />
     <ClInclude Include="gfx\RenderCmd.h" />
@@ -2161,6 +2164,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"</Co
     <ClInclude Include="util\Hash.h" />
     <ClInclude Include="util\HashSet.h" />
     <ClInclude Include="util\Heap.h" />
+    <ClInclude Include="util\Json.h" />
     <ClInclude Include="util\MappedFile.h" />
     <ClInclude Include="util\Matrix4.h" />
     <ClInclude Include="util\MultiHashSet.h" />

+ 12 - 0
BeefySysLib/BeefySysLib.vcxproj.filters

@@ -707,6 +707,12 @@
     <ClCompile Include="util\Heap.cpp">
       <Filter>src\util</Filter>
     </ClCompile>
+    <ClCompile Include="gfx\glTF.cpp">
+      <Filter>src\gfx</Filter>
+    </ClCompile>
+    <ClCompile Include="util\Json.cpp">
+      <Filter>src\util</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Common.h">
@@ -1075,6 +1081,12 @@
     <ClInclude Include="util\Heap.h">
       <Filter>src\util</Filter>
     </ClInclude>
+    <ClInclude Include="gfx\glTF.h">
+      <Filter>src\gfx</Filter>
+    </ClInclude>
+    <ClInclude Include="util\Json.h">
+      <Filter>src\util</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="third_party\libffi\i686-pc-cygwin\src\x86\win32.asm">

+ 17 - 1
BeefySysLib/Common.cpp

@@ -943,6 +943,21 @@ char* Beefy::LoadTextData(const StringImpl& path, int* size)
 	return data;
 }
 
+bool Beefy::LoadTextData(const StringImpl& path, StringImpl& str)
+{
+	int size = 0;
+	char* data = LoadTextData(path, &size);
+	if (data == NULL)
+		return false;
+	if ((str.mAllocSizeAndFlags & StringImpl::DynAllocFlag) != 0)
+		str.Release();	
+	str.mPtr = data;
+	str.mAllocSizeAndFlags = size | StringImpl::DynAllocFlag | StringImpl::StrPtrFlag;
+	str.mLength = size;
+	return true;
+}
+
+
 #ifdef BF_MINGW
 unsigned long long __cdecl _byteswap_uint64(unsigned long long _Int64)
 {
@@ -1262,4 +1277,5 @@ bool Beefy::RecursiveDeleteDirectory(const StringImpl& dirPath)
 void Beefy::BFFatalError(const char* message, const char* file, int line)
 {
 	BFFatalError(String(message), String(file), line);
-}
+}
+

+ 1 - 0
BeefySysLib/Common.h

@@ -220,6 +220,7 @@ int32 GetHighestBitSet(int32 n);
 
 uint8* LoadBinaryData(const StringImpl& path, int* size);
 char* LoadTextData(const StringImpl& path, int* size);
+bool LoadTextData(const StringImpl& path, StringImpl& str);
 int64 GetFileTimeWrite(const StringImpl& path);
 String GetFileDir(const StringImpl& path);
 String GetFileName(const StringImpl& path);

+ 8 - 0
BeefySysLib/DataStream.h

@@ -49,6 +49,14 @@ public:
 		Read((void*) &val, sizeof(T));
 	}
 
+	template <typename T>
+	T ReadT()
+	{
+		T val;
+		Read((void*)&val, sizeof(T));
+		return val;
+	}
+
 	virtual void			Write(float val);
 	virtual void			Write(uint8 val);
 	virtual void			Write(int8 val);

+ 3 - 2
BeefySysLib/gfx/DrawLayer.cpp

@@ -339,9 +339,9 @@ void DrawLayer::SetTexture(int texIdx, Texture* texture)
 	}
 }
 
-void DrawLayer::SetShaderConstantDataTyped(int slotIdx, void* constData, int size, int* typeData, int typeCount)
+void DrawLayer::SetShaderConstantDataTyped(int usageIdx, int slotIdx, void* constData, int size, int* typeData, int typeCount)
 {
-	SetShaderConstantData(slotIdx, constData, size);
+	SetShaderConstantData(usageIdx, slotIdx, constData, size);
 }
 
 ///
@@ -390,5 +390,6 @@ BF_EXPORT void BF_CALLTYPE DrawLayer_DrawToRenderTarget(DrawLayer* drawLayer, Te
 	renderDevice->PhysSetRenderState(renderDevice->mDefaultRenderState);
 	renderDevice->PhysSetRenderTarget(textureSegment->mTexture);	
 	drawLayer->Draw();
+	drawLayer->Clear();
 	renderDevice->mCurRenderTarget = prevTarget;
 }

+ 2 - 2
BeefySysLib/gfx/DrawLayer.h

@@ -99,8 +99,8 @@ public:
 	virtual DrawBatch*		AllocateBatch(int minVtxCount, int minIdxCount);
 	void					QueueRenderCmd(RenderCmd* renderCmd);
 	virtual RenderCmd*		CreateSetTextureCmd(int textureIdx, Texture* texture) = 0;
-	virtual void			SetShaderConstantData(int slotIdx, void* constData, int size) = 0;
-	virtual void			SetShaderConstantDataTyped(int slotIdx, void* constData, int size, int* typeData, int typeCount);
+	virtual void			SetShaderConstantData(int usageIdx, int slotIdx, void* constData, int size) = 0;
+	virtual void			SetShaderConstantDataTyped(int usageIdx, int slotIdx, void* constData, int size, int* typeData, int typeCount);
 
 public:
 	DrawLayer();

+ 207 - 2
BeefySysLib/gfx/ModelDef.cpp

@@ -2,9 +2,24 @@
 #include "BFApp.h"
 #include "gfx/RenderDevice.h"
 #include "gfx/ModelInstance.h"
+#include "util/Dictionary.h"
+#include "util/TLSingleton.h"
+#include "FileStream.h"
+#include "MemStream.h"
+
+#pragma warning(disable:4190)
 
 USING_NS_BF;
 
+struct ModelManager
+{
+public:
+	Dictionary<String, ModelMaterialDef*> mMaterialMap;	
+};
+
+ModelManager sModelManager;
+static TLSingleton<String> gModelDef_TLStrReturn;
+
 void Beefy::ModelAnimation::GetJointTranslation(int jointIdx, float frameNum, ModelJointTranslation* outJointTranslation)
 {
 	// Frame 35
@@ -40,6 +55,28 @@ BF_EXPORT ModelInstance* BF_CALLTYPE ModelDef_CreateModelInstance(ModelDef* mode
 	return gBFApp->mRenderDevice->CreateModelInstance(modelDef);
 }
 
+BF_EXPORT const char* BF_CALLTYPE ModelDef_GetInfo(ModelDef* modelDef)
+{
+	String& outString = *gModelDef_TLStrReturn.Get();
+	for (int meshIdx = 0; meshIdx < (int)modelDef->mMeshes.mSize; meshIdx++)	
+	{
+		auto mesh = modelDef->mMeshes[meshIdx];
+		for (int primIdx = 0; primIdx < (int)mesh.mPrimitives.mSize; primIdx++)		
+		{
+			auto prims = mesh.mPrimitives[primIdx];
+			outString += StrFormat("%d\t%d\t%d\t%d", meshIdx, primIdx, prims.mIndices.size(), prims.mVertices.size());
+			for (auto& texPath : prims.mTexPaths)
+			{
+				outString += "\t";
+				outString += texPath;
+			}
+
+			outString += "\n";
+		}
+	}
+	return outString.c_str();
+}
+
 BF_EXPORT float BF_CALLTYPE ModelDef_GetFrameRate(ModelDef* modelDef)
 {
 	return modelDef->mFrameRate;
@@ -60,6 +97,14 @@ BF_EXPORT ModelAnimation* BF_CALLTYPE ModelDef_GetAnimation(ModelDef* modelDef,
 	return &modelDef->mAnims[animIdx];
 }
 
+BF_EXPORT void BF_CALLTYPE ModelDef_SetTextures(ModelDef* modelDef, int32 meshIdx, int32 primitivesIdx, char** paths, int32 pathCount)
+{
+	auto& prims = modelDef->mMeshes[meshIdx].mPrimitives[primitivesIdx];
+	prims.mTexPaths.Clear();
+	for (int i = 0; i < pathCount; i++)
+		prims.mTexPaths.Add(paths[i]);
+}
+
 BF_EXPORT void BF_CALLTYPE ModelDefAnimation_GetJointTranslation(ModelAnimation* modelAnimation, int jointIdx, float frame, ModelJointTranslation* outJointTranslation)
 {
 	modelAnimation->GetJointTranslation(jointIdx, frame, outJointTranslation);
@@ -77,9 +122,169 @@ BF_EXPORT const char* BF_CALLTYPE ModelDefAnimation_GetName(ModelAnimation* mode
 
 BF_EXPORT void BF_CALLTYPE ModelDefAnimation_Clip(ModelAnimation* modelAnimation, int startFrame, int numFrames)
 {	
-	modelAnimation->mFrames.erase(modelAnimation->mFrames.begin(), modelAnimation->mFrames.begin() + startFrame);
-	modelAnimation->mFrames.erase(modelAnimation->mFrames.begin() + numFrames, modelAnimation->mFrames.end());
+	modelAnimation->mFrames.RemoveRange(0, startFrame);
+	modelAnimation->mFrames.RemoveRange(numFrames, modelAnimation->mFrames.Count() - numFrames);
+}
+
+ModelDef::~ModelDef()
+{
+	for (auto& materialInstance : mMaterials)
+	{
+		materialInstance.mDef->mRefCount--;
+	}
+}
+
+ModelMaterialDef* ModelMaterialDef::CreateOrGet(const StringImpl& prefix, const StringImpl& path)
+{	
+	StringT<128> key = prefix;
+	key += ":";
+	key += path;
+	ModelMaterialDef** modelMaterialDefPtr = NULL;
+	if (sModelManager.mMaterialMap.TryAdd(key, NULL, &modelMaterialDefPtr))
+		*modelMaterialDefPtr = new ModelMaterialDef();
+	return *modelMaterialDefPtr;
 }
 
 
+///
 
+class ModelReader
+{
+public:
+	ModelDef* mModelDef;
+	DataStream* mFS;
+
+public:
+	ModelReader(ModelDef* modelDef)
+	{
+		mFS = NULL;
+		mModelDef = modelDef;
+	}
+
+	~ModelReader()
+	{
+		delete mFS;
+	}
+
+	bool ReadFile()
+	{
+		int sig = mFS->ReadInt32();
+		if (sig != 0xBEEF0001)
+			return false;
+
+		int flags = mFS->ReadInt32();
+		Array<String> texNames;
+		int texNameSize = mFS->ReadInt32();
+		for (int i = 0; i < texNameSize; i++)
+		{
+			String path = mFS->ReadAscii32SizedString();
+			texNames.Add(path);
+		}
+
+		int idxCount = mFS->ReadInt32();
+
+		mModelDef->mMeshes.Add(ModelMesh());
+		auto& modelMesh = mModelDef->mMeshes[0];
+		modelMesh.mPrimitives.Add(ModelPrimitives());
+		auto& modelPrimitives = modelMesh.mPrimitives[0];
+
+		modelPrimitives.mFlags = (ModelPrimitives::Flags)flags;
+		modelPrimitives.mTexPaths = texNames;
+
+		modelPrimitives.mIndices.Resize(idxCount);
+		mFS->Read(modelPrimitives.mIndices.mVals, idxCount * 2);
+
+		int vtxCount = mFS->ReadInt32();
+		modelPrimitives.mVertices.Resize(vtxCount);
+		for (int i = 0; i < vtxCount; i++)
+		{
+			if ((flags & ModelPrimitives::Flags_Vertex_Position) != 0)
+				mFS->ReadT(modelPrimitives.mVertices[i].mPosition);
+			if ((flags & ModelPrimitives::Flags_Vertex_Tex0) != 0)
+				mFS->ReadT(modelPrimitives.mVertices[i].mTexCoords);
+			if ((flags & ModelPrimitives::Flags_Vertex_Tex1) != 0)
+				mFS->ReadT(modelPrimitives.mVertices[i].mBumpTexCoords);
+		}
+
+		return true;
+	}
+
+	bool ReadFile(const StringImpl& fileName, const StringImpl& baseDir)
+	{	
+		if (fileName.Contains(':'))
+		{
+			int colon = (int)fileName.IndexOf(':');
+			String addrStr = fileName.Substring(1, colon - 1);
+			String lenStr = fileName.Substring(colon + 1);
+			void* addr = (void*)(intptr)strtoll(addrStr.c_str(), NULL, 16);
+			int len = (int)strtol(lenStr.c_str(), NULL, 10);
+			MemStream* memStream = new MemStream(addr, len, false);
+			mFS = memStream;
+		}
+		else
+		{
+			FileStream* fs = new FileStream();
+			mFS = fs;
+			if (!fs->Open(fileName, "rb"))
+				return false;
+		}		
+		
+		return ReadFile();		
+	}
+};
+
+BF_EXPORT void* BF_CALLTYPE Res_OpenModel(const char* fileName, const char* baseDir, void* vertexDefinition)
+{
+	ModelDef* modelDef = new ModelDef();
+	modelDef->mLoadDir = baseDir;
+	ModelReader reader(modelDef);
+	if (!reader.ReadFile(fileName, baseDir))
+	{
+		delete modelDef;
+		return NULL;
+	}
+
+	return modelDef;
+}
+
+BF_EXPORT StringView BF_CALLTYPE Res_SerializeModel(ModelDef* modelDef)
+{
+	DynMemStream ms;
+	ms.Write((int)0xBEEF0001);
+
+	for (auto& mesh : modelDef->mMeshes)
+	{
+		for (auto& prims : mesh.mPrimitives)
+		{
+			ms.Write((int)prims.mFlags);
+			ms.Write(prims.mTexPaths.mSize);
+			for (auto& path : prims.mTexPaths)
+				ms.Write(path);
+
+			ms.Write((int)prims.mIndices.mSize);
+			ms.Write(prims.mIndices.mVals, prims.mIndices.mSize * 2);
+
+			ms.Write((int)prims.mVertices.mSize);
+
+			for (int i = 0; i < prims.mVertices.mSize; i++)
+			{
+				auto& vtx = prims.mVertices[i];
+				if ((prims.mFlags & ModelPrimitives::Flags_Vertex_Position) != 0)
+					ms.WriteT(vtx.mPosition);
+				if ((prims.mFlags & ModelPrimitives::Flags_Vertex_Tex0) != 0)
+					ms.WriteT(vtx.mTexCoords);
+				if ((prims.mFlags & ModelPrimitives::Flags_Vertex_Tex1) != 0)
+					ms.WriteT(vtx.mBumpTexCoords);
+				if ((prims.mFlags & ModelPrimitives::Flags_Vertex_Color) != 0)
+					ms.WriteT(vtx.mColor);
+				if ((prims.mFlags & ModelPrimitives::Flags_Vertex_Normal) != 0)
+					ms.WriteT(vtx.mNormal);
+				if ((prims.mFlags & ModelPrimitives::Flags_Vertex_Tangent) != 0)
+					ms.WriteT(vtx.mTangent);
+			}
+		}
+	}
+	
+	String& outString = *gModelDef_TLStrReturn.Get();
+	return outString;
+}

+ 115 - 9
BeefySysLib/gfx/ModelDef.h

@@ -3,6 +3,8 @@
 #include "Common.h"
 #include "util/Quaternion.h"
 #include "util/Vector.h"
+#include "util/Array.h"
+#include "gfx/Texture.h"
 #include <vector>
 
 NS_BF_BEGIN;
@@ -18,14 +20,14 @@ public:
 class ModelAnimationFrame
 {
 public:
-	std::vector<ModelJointTranslation> mJointTranslations;
+	Array<ModelJointTranslation> mJointTranslations;
 };
 
 class ModelAnimation
 {
 public:
 	String mName;
-	std::vector<ModelAnimationFrame> mFrames;
+	Array<ModelAnimationFrame> mFrames;
 
 public:
 	void GetJointTranslation(int jointIdx, float frameNum, ModelJointTranslation* outJointTranslation);
@@ -55,14 +57,113 @@ public:
 	Matrix4 mPoseInvMatrix;
 };
 
+class ModelMetalicRoughness
+{
+public:
+	Vector3 mBaseColorFactor;
+	float mMetallicFactor;
+	float mRoughnessFactor;
+
+public:
+	ModelMetalicRoughness()
+	{
+		mMetallicFactor = 0;
+		mRoughnessFactor = 0;
+	}
+};
+
+class ModelMaterialDef
+{
+public:
+	class TextureParameterValue	
+	{
+	public:
+		String mName;
+		String mTexturePath;
+
+	public:
+		TextureParameterValue()
+		{
+		
+		}
+
+		~TextureParameterValue()
+		{
+			
+		}
+	};
+
+public:
+	String mName;
+	int mRefCount;
+	bool mInitialized;
+	OwnedArray<TextureParameterValue> mTextureParameterValues;
+
+public:
+	ModelMaterialDef()
+	{
+		mRefCount = 0;
+		mInitialized = false;
+	}
+
+	static ModelMaterialDef* CreateOrGet(const StringImpl& prefix, const StringImpl& path);
+};
+
+class ModelMaterialInstance
+{
+public:
+	ModelMaterialDef* mDef;
+	String mName;
+	ModelMetalicRoughness mModelMetalicRoughness;
+};
+
+class ModelPrimitives
+{
+public:
+	enum Flags
+	{
+		Flags_None = 0,
+		Flags_Vertex_Position = 1,	
+		Flags_Vertex_Tex0 = 2,
+		Flags_Vertex_Tex1 = 4,
+		Flags_Vertex_Tex2 = 8,
+		Flags_Vertex_Color = 0x10,
+		Flags_Vertex_Normal = 0x20,
+		Flags_Vertex_Tangent = 0x40,
+	};
+
+public:
+	Array<ModelVertex> mVertices;
+	Array<uint16> mIndices;
+	ModelMaterialInstance* mMaterial;
+	Array<String> mTexPaths;	
+	Flags mFlags;
+
+public:
+	ModelPrimitives()
+	{
+		mMaterial = NULL;
+		mFlags = Flags_None;
+	}
+};
+
 class ModelMesh
 {
+public:
+	String mName;	
+	//String mTexFileName;
+	//String mBumpFileName;	
+	Array<ModelPrimitives> mPrimitives;
+};
+
+class ModelNode
+{
 public:
 	String mName;
-	std::vector<ModelVertex> mVertices;
-	std::vector<uint16> mIndices;
-	String mTexFileName;
-	String mBumpFileName;
+	Vector3 mTranslation;
+	Vector4 mRotation;
+	ModelMesh* mMesh;
+	Array<ModelNode*> mChildren;
 };
 
 class ModelDef
@@ -70,9 +171,14 @@ class ModelDef
 public:
 	String mLoadDir;
 	float mFrameRate;
-	std::vector<ModelMesh> mMeshes;
-	std::vector<ModelJoint> mJoints;
-	std::vector<ModelAnimation> mAnims;
+	Array<ModelMesh> mMeshes;
+	Array<ModelJoint> mJoints;
+	Array<ModelAnimation> mAnims;
+	Array<ModelNode> mNodes;
+	Array<ModelMaterialInstance> mMaterials;
+
+public:
+	~ModelDef();
 };
 
 NS_BF_END;

+ 2 - 2
BeefySysLib/gfx/ModelInstance.cpp

@@ -6,8 +6,8 @@ ModelInstance::ModelInstance(ModelDef* modelDef)
 {
 	mNext = NULL;
 	mModelDef = modelDef;
-	mJointTranslations.resize(mModelDef->mJoints.size());
-	mMeshesVisible.insert(mMeshesVisible.begin(), mModelDef->mMeshes.size(), true);
+	mJointTranslations.Resize(mModelDef->mJoints.size());	
+	mMeshesVisible.Insert(0, mModelDef->mMeshes.size(), true);
 }
 
 void Beefy::ModelInstance::SetJointPosition(int jointIdx, const ModelJointTranslation& jointTranslation)

+ 2 - 2
BeefySysLib/gfx/ModelInstance.h

@@ -11,8 +11,8 @@ class ModelInstance : public RenderCmd
 {
 public:
 	ModelDef* mModelDef;	
-	std::vector<ModelJointTranslation> mJointTranslations;
-	std::vector<bool> mMeshesVisible;
+	Array<ModelJointTranslation> mJointTranslations;
+	Array<bool> mMeshesVisible;
 
 public:
 	ModelInstance(ModelDef* modelDef);

+ 3 - 2
BeefySysLib/gfx/RenderDevice.cpp

@@ -21,6 +21,7 @@ RenderState::RenderState()
 	mDepthFunc = DepthFunc_Always;
 	mShader = NULL;	
 	mClipped = false;
+	mTexWrap = false;
 }
 
 RenderTarget::RenderTarget()
@@ -103,8 +104,8 @@ Texture* RenderDevice::LoadTexture(const StringImpl& fileName, int flags)
 	int dotPos = (int)fileName.LastIndexOf('.');
 	String ext;
 	if (dotPos != -1)
-		ext = fileName.Substring(dotPos);
-	
+		ext = fileName.Substring(dotPos);		
+
 	ImageData* imageData = NULL;
 	bool handled = false;
 	bool failed = false;

+ 2 - 0
BeefySysLib/gfx/RenderDevice.h

@@ -182,6 +182,7 @@ public:
 	bool					mWriteDepthBuffer;
 	DepthFunc				mDepthFunc;	
 	bool					mClipped;
+	bool					mTexWrap;
 	Rect					mClipRect;
 	CullMode				mCullMode;
 
@@ -190,6 +191,7 @@ public:
 	virtual ~RenderState() {}
 
 	virtual void SetShader(Shader* shader) { mShader = shader; }
+	virtual void SetTexWrap(bool wrap) { mTexWrap = wrap; }
 	virtual void SetClipped(bool clipped) { mClipped = clipped; }
 	virtual void SetClipRect(const Rect& rect) { mClipRect = rect; }
 	virtual void SetWriteDepthBuffer(bool writeDepthBuffer) { mWriteDepthBuffer = writeDepthBuffer; }

+ 875 - 0
BeefySysLib/gfx/glTF.cpp

@@ -0,0 +1,875 @@
+#include "glTF.h"
+#include "gfx/ModelInstance.h"
+#include "util/Json.h"
+#include "gfx/RenderDevice.h"
+#include "BFApp.h"
+
+USING_NS_BF;
+
+static bool IsWhitespace(char c)
+{
+	return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
+}
+
+class GLTFPropsParser
+{
+public:
+	enum NodeKind
+	{
+		NodeKind_None,
+		NodeKind_End,
+		NodeKind_LBrace,
+		NodeKind_RBrace,
+		NodeKind_LBracket,
+		NodeKind_RBracket,
+		NodeKind_Equals,
+		NodeKind_Index,
+		NodeKind_Integer,
+		NodeKind_Float,
+		NodeKind_String
+	};
+
+	struct Node
+	{
+	public:		
+		NodeKind mKind;
+		int mStart;
+		int mEnd;
+		union
+		{
+			int mValueInt;
+			float mValueFloat;
+		};
+
+	public:
+		Node()
+		{
+			mKind = NodeKind_None;
+			mStart = -1;
+			mEnd = -1;
+			mValueInt = 0;
+		}
+	};
+
+public:
+	char* mStart;
+	char* mPtr;
+	char* mEnd;
+	Node mNext;
+
+public:		
+	GLTFPropsParser(const StringImpl& str)
+	{
+		mStart = str.mPtr;
+		mPtr = mStart;
+		mEnd = mStart + str.mLength;
+	}
+
+// 	double ParseLiteralDouble()
+// 	{
+// 		char buf[256];
+// 		int len = BF_MAX(mTokenEnd - mTokenStart, 255);
+// 
+// 		memcpy(buf, &mSrc[mTokenStart], len);
+// 		char c = buf[len - 1];
+// 		if ((c == 'd') || (c == 'D') || (c == 'f') || (c == 'F'))
+// 			buf[len - 1] = '\0';
+// 		else
+// 			buf[len] = '\0';
+// 
+// 		return strtod(buf, NULL);
+// 	}
+
+	Node PeekNext()
+	{
+		if (mNext.mKind != NodeKind_None)
+			return mNext;
+
+		while (true)
+		{
+			if (mPtr >= mEnd)
+			{
+				mNext.mKind = NodeKind_End;
+				return mNext;
+			}
+				
+			char* start = mPtr;
+			char c = *(mPtr++);
+			
+			if (c == '{')
+				mNext.mKind = NodeKind_LBrace;
+			else if (c == '}')
+				mNext.mKind = NodeKind_RBrace;
+			else if (c == '[')
+				mNext.mKind = NodeKind_LBracket;
+			else if (c == ']')
+				mNext.mKind = NodeKind_RBracket;
+			else if (c == '=')
+				mNext.mKind = NodeKind_Equals;
+
+			if (mNext.mKind != NodeKind_None)
+			{
+				mNext.mStart = (int)(mPtr - mStart - 1);
+				mNext.mEnd = (int)(mPtr - mStart);				
+				return mNext;
+			}						
+
+			if ((c >= '0') && (c <= '9'))
+			{
+				bool hadDot = false;
+				while (mPtr < mEnd)
+				{
+					char c = *mPtr;
+					if (c == '.')
+					{
+						mPtr++;
+						hadDot = true;
+					}
+					else if ((c >= '0') && (c <= '9'))						
+					{						
+						mPtr++;
+					}
+					else
+						break;
+				}
+				
+				mNext.mStart = (int)(start - mStart);
+				mNext.mEnd = (int)(mPtr - mStart);
+				
+				char buf[256];
+				int len = BF_MIN((int)(mPtr - start), 255);
+
+				memcpy(buf, start, len);
+				char c = buf[len - 1];
+				if ((c == 'd') || (c == 'D') || (c == 'f') || (c == 'F'))
+					buf[len - 1] = '\0';
+				else
+					buf[len] = '\0';
+
+				if (hadDot)
+				{
+					mNext.mKind = NodeKind_Float;
+					mNext.mValueFloat = (float)strtod(buf, NULL);
+				}
+				else
+				{
+					mNext.mKind = NodeKind_Integer;
+					mNext.mValueInt = atoi(buf);
+				}
+				return mNext;
+			}
+
+			if (!IsWhitespace(c))
+			{
+				char* lastCPtr = start;
+				while (mPtr < mEnd)
+				{
+					char c = *mPtr;
+					if ((c == '}') || (c == '=') || (c == '[') || (c == '\r') || (c == '\n'))
+						break;		
+					if (c != ' ')
+						lastCPtr = mPtr;
+					mPtr++;
+				}
+				mPtr = lastCPtr + 1;
+
+				mNext.mStart = (int)(start - mStart);
+				mNext.mEnd = (int)(mPtr - mStart);
+				mNext.mKind = NodeKind_String;
+				return mNext;
+			}
+		}
+	}
+
+	Node GetNext()
+	{
+		auto node = PeekNext();
+		mNext = Node();
+		return node;
+	}
+
+	StringView GetStringView(const Node& node)
+	{
+		return StringView(mStart + node.mStart, node.mEnd - node.mStart);
+	}
+
+	StringView GetStringView(const Node& node, StringView& prefix)
+	{
+		auto stringView = StringView(mStart + node.mStart, node.mEnd - node.mStart);
+		if (!stringView.EndsWith('\''))
+		{
+			prefix = "";
+			return stringView;
+		}
+		
+		int strStartIdx = (int)stringView.IndexOf('\'');
+		prefix = StringView(stringView, 0, strStartIdx);
+		return StringView(stringView, strStartIdx + 1, (int)stringView.mLength - strStartIdx - 2);
+	}
+
+	bool GetNextStringView(StringView& prefix, StringView& value)
+	{
+		auto node = GetNext();
+		if (node.mKind != NodeKind_String)
+			return false;
+		auto stringView = StringView(mStart + node.mStart, node.mEnd - node.mStart);
+		if (!stringView.EndsWith('\''))
+		{
+			prefix = "";
+			value = stringView;
+			return true;
+		}
+		int strStartIdx = (int)stringView.IndexOf('\'');
+		prefix = StringView(stringView, 0, strStartIdx);
+		value = StringView(stringView, strStartIdx + 1, (int)stringView.mLength - strStartIdx - 2);
+		return true;
+	}
+};
+
+enum ComponentType
+{
+	Int8 = 5120,
+	UInt8 = 5121,
+	Int16 = 5122,
+	UInt16 = 5123,
+	UInt32 = 5125,
+	Float = 5126,
+};
+
+BF_EXPORT void* BF_CALLTYPE Res_OpenGLTF(const char* fileName, const char* baseDir, void* vertexDefinition)
+{
+	ModelDef* modelDef = new ModelDef();
+	GLTFReader reader(modelDef);
+	if (!reader.ReadFile(fileName, baseDir))
+	{
+		delete modelDef;
+		return NULL;
+	}
+
+	return modelDef;
+}
+
+GLTFReader::GLTFReader(ModelDef* modelDef)
+{
+	mModelDef = modelDef;
+}
+
+GLTFReader::~GLTFReader()
+{
+	
+}
+
+struct DataSpan
+{
+	uint8* mPtr;
+	int mSize;
+};
+
+struct DataAccessor
+{
+	uint8* mPtr;
+	int mSize;
+	int mCount;
+	ComponentType mComponentType;
+};
+
+template <typename T>
+static void ReadBuffer(DataAccessor& dataAccessor, T* outPtr, int outStride)
+{	
+	for (int i = 0; i < dataAccessor.mCount; i++)
+		*(T*)((uint8*)outPtr + i * outStride) = ((T*)dataAccessor.mPtr)[i];
+}
+
+template <typename T>
+static void ReadBuffer(DataAccessor& dataAccessor, T* outPtr, int outStride, int inStride)
+{
+	for (int i = 0; i < dataAccessor.mCount; i++)
+		*(T*)((uint8*)outPtr + i * outStride) = *(T*)(dataAccessor.mPtr + i * inStride);
+}
+
+static void TrySkipValue(GLTFPropsParser& propsParser)
+{
+	auto nextNode = propsParser.PeekNext();
+
+	if (nextNode.mKind == GLTFPropsParser::NodeKind_LBracket)
+	{
+		propsParser.GetNext();
+		propsParser.GetNext();
+		propsParser.GetNext();
+		nextNode = propsParser.PeekNext();
+	}
+
+	if (nextNode.mKind == GLTFPropsParser::NodeKind_Equals)
+	{
+		propsParser.GetNext();
+
+		int depth = 0;
+		do
+		{
+			auto node = propsParser.GetNext();
+			if (node.mKind == GLTFPropsParser::NodeKind_End)
+				return;
+			if (node.mKind == GLTFPropsParser::NodeKind_LBrace)
+				depth++;
+			else if (node.mKind == GLTFPropsParser::NodeKind_RBrace)
+				depth--;
+			if (node.mKind == GLTFPropsParser::NodeKind_LBracket)
+				depth++;
+			if (node.mKind == GLTFPropsParser::NodeKind_LBracket)
+				depth--;
+		} while (depth > 0);
+	}
+}
+
+static bool ExpectIndex(GLTFPropsParser& propsParser, int& idx)
+{
+	auto node = propsParser.GetNext();
+	if (node.mKind != GLTFPropsParser::NodeKind_LBracket)
+		return false;
+	node = propsParser.GetNext();
+	if (node.mKind != GLTFPropsParser::NodeKind_Integer)
+		return false;
+	idx = node.mValueInt;
+	node = propsParser.GetNext();
+	if (node.mKind != GLTFPropsParser::NodeKind_RBracket)
+		return false;
+	return true;
+};
+
+static bool  ExpectOpen(GLTFPropsParser& propsParser)
+{
+	if (propsParser.GetNext().mKind != GLTFPropsParser::NodeKind_LBrace)
+		return false;
+	return true;
+};
+
+static bool ExpectClose(GLTFPropsParser& propsParser)
+{
+	if (propsParser.GetNext().mKind != GLTFPropsParser::NodeKind_RBrace)
+		return false;
+	return true;
+};
+
+static bool ExpectEquals(GLTFPropsParser& propsParser)
+{
+	if (propsParser.GetNext().mKind != GLTFPropsParser::NodeKind_Equals)
+		return false;
+	return true;
+};
+
+bool GLTFReader::ParseMaterialDef(ModelMaterialDef* materialDef, const StringImpl& matText)
+{
+	GLTFPropsParser propsParser(matText);
+
+	while (true)
+	{
+		auto node = propsParser.GetNext();
+		if (node.mKind == GLTFPropsParser::NodeKind_End)
+			break;
+
+		if (node.mKind == GLTFPropsParser::NodeKind_String)
+		{
+			auto key = propsParser.GetStringView(node);
+			if (key == "Parent")
+			{
+				if (propsParser.GetNext().mKind != GLTFPropsParser::NodeKind_Equals)
+					return false;
+
+				auto valueNode = propsParser.GetNext();
+				if (valueNode.mKind != GLTFPropsParser::NodeKind_String)
+					return false;
+
+				StringView prefix;
+				StringView str = propsParser.GetStringView(valueNode, prefix);				
+				auto parentMaterialDef = LoadMaterial(str);
+			}
+			else if (key == "TextureParameterValues")
+			{
+				int count = 0;
+				if (!ExpectIndex(propsParser, count))
+					return false;
+				if (!ExpectEquals(propsParser))
+					return false;
+				if (!ExpectOpen(propsParser))
+					return false;
+
+				while (true)
+				{
+					node = propsParser.GetNext();
+					if (node.mKind == GLTFPropsParser::NodeKind_RBrace)
+						break;
+					if (node.mKind != GLTFPropsParser::NodeKind_String)
+						return false;
+
+					StringView prefix;
+					StringView str = propsParser.GetStringView(node, prefix);
+					if (str == "TextureParameterValues")
+					{
+						auto textureParamValue = materialDef->mTextureParameterValues.Alloc();
+
+						int idx = 0;
+						if (!ExpectIndex(propsParser, idx))
+							return false;
+						if (!ExpectEquals(propsParser))
+							return false;
+						if (!ExpectOpen(propsParser))
+							return false;
+
+						while (true)
+						{
+							node = propsParser.GetNext();
+							if (node.mKind == GLTFPropsParser::NodeKind_RBrace)
+								break;
+							if (node.mKind != GLTFPropsParser::NodeKind_String)
+								return false;
+							
+							str = propsParser.GetStringView(node, prefix);
+							if (str == "ParameterInfo")
+							{
+								if (!ExpectEquals(propsParser))
+									return false;
+								if (!ExpectOpen(propsParser))
+									return false;
+								if (!propsParser.GetNextStringView(prefix, str))
+									return false;
+								if (!ExpectEquals(propsParser))
+									return false;
+								if (!propsParser.GetNextStringView(prefix, str))
+									return false;
+								textureParamValue->mName = str;
+
+								if (!ExpectClose(propsParser))
+									return false;
+							}
+							else if (str == "ParameterValue")
+							{
+								if (!ExpectEquals(propsParser))
+									return false;
+								if (!propsParser.GetNextStringView(prefix, str))
+									return false;
+
+								String path = mRootDir;
+								path += str;
+								int dotPos = (int)path.IndexOf('.');
+								if (dotPos != -1)
+									path.RemoveToEnd(dotPos);
+								path += ".tga";
+
+								textureParamValue->mTexturePath = path;
+
+// 								Texture* texture = gBFApp->mRenderDevice->LoadTexture(path, 0);
+// 								textureParamValue->mTexture = texture;
+							}
+							else
+								TrySkipValue(propsParser);
+						}
+					}
+					else
+					{
+						TrySkipValue(propsParser);
+					}
+				}
+			}
+			else
+			{
+				TrySkipValue(propsParser);
+			}
+		}
+	}
+
+	return true;
+}
+
+ModelMaterialDef* GLTFReader::LoadMaterial(const StringImpl& relPath)
+{
+	String propsPath;
+	if (relPath.StartsWith('/'))
+	{
+		propsPath = mRootDir + relPath;
+		int dotPos = (int)propsPath.LastIndexOf('.');
+		if (dotPos > 0)
+			propsPath.RemoveToEnd(dotPos);
+		propsPath += ".props.txt";
+	}
+	else if (mBasePathName.Contains("staticmesh"))
+		propsPath = GetFileDir(mBasePathName) + "/" + relPath + ".props.txt";
+	else
+		propsPath = GetFileDir(mBasePathName) + "/materials/" + relPath + ".props.txt";
+
+	ModelMaterialDef* materialDef = ModelMaterialDef::CreateOrGet("GLTF", propsPath);
+
+	if (materialDef->mInitialized)
+		return materialDef;
+	
+	materialDef->mInitialized = true;
+
+	String propText;
+	if (LoadTextData(propsPath, propText))
+	{
+		if (!ParseMaterialDef(materialDef, propText))
+		{
+			// Had error
+		}
+
+	}		
+	return materialDef;
+}
+
+bool GLTFReader::LoadModelProps(const StringImpl& propsPath)
+{
+	String propText;
+	if (!LoadTextData(propsPath, propText))
+		return false;
+
+	GLTFPropsParser propsParser(propText);
+	while (true)
+	{
+		auto node = propsParser.GetNext();
+		if (node.mKind == GLTFPropsParser::NodeKind_End)
+			break;
+
+		if (node.mKind == GLTFPropsParser::NodeKind_String)
+		{
+			auto key = propsParser.GetStringView(node);
+			if (key == "StaticMaterials")
+			{
+				int count = 0;
+				if (!ExpectIndex(propsParser, count))
+					return false;
+				if (!ExpectEquals(propsParser))
+					return false;
+				if (!ExpectOpen(propsParser))
+					return false;
+
+				while (true)
+				{
+					node = propsParser.GetNext();
+					if (node.mKind == GLTFPropsParser::NodeKind_RBrace)
+						break;
+					if (node.mKind != GLTFPropsParser::NodeKind_String)
+						return false;
+
+					StringView prefix;
+					StringView str = propsParser.GetStringView(node, prefix);
+					if (str == "StaticMaterials")
+					{	
+						StaticMaterial staticMaterial;
+
+						int idx = 0;
+						if (!ExpectIndex(propsParser, idx))
+							return false;
+						if (!ExpectEquals(propsParser))
+							return false;
+						if (!ExpectOpen(propsParser))
+							return false;
+
+						while (true)
+						{
+							node = propsParser.GetNext();
+							if (node.mKind == GLTFPropsParser::NodeKind_RBrace)
+								break;
+							if (node.mKind != GLTFPropsParser::NodeKind_String)
+								return false;
+
+							str = propsParser.GetStringView(node, prefix);
+							if (str == "MaterialSlotName")
+							{
+								if (!ExpectEquals(propsParser))
+									return false;
+								if (!propsParser.GetNextStringView(prefix, str))
+									return false;
+								staticMaterial.mMaterialSlotName = str;
+							}
+							else if (str == "MaterialInterface")
+							{
+								if (!ExpectEquals(propsParser))
+									return false;
+								if (!propsParser.GetNextStringView(prefix, str))
+									return false;
+								staticMaterial.mMaterialDef = LoadMaterial(str);
+							}
+							else
+								TrySkipValue(propsParser);
+						}
+
+						mStaticMaterials.Add(staticMaterial);
+					}
+					else
+					{
+						TrySkipValue(propsParser);
+					}
+				}
+			}
+			else
+			{
+				TrySkipValue(propsParser);
+			}
+		}
+	}
+
+	return true;
+}
+
+bool GLTFReader::ReadFile(const StringImpl& filePath, const StringImpl& rootDir)
+{
+	String basePathName;
+	int dotPos = (int)filePath.LastIndexOf('.');
+	if (dotPos > 0)
+		basePathName = filePath.Substring(0, dotPos);
+	else
+		basePathName = basePathName;
+	mBasePathName = basePathName;
+	mRootDir = rootDir;
+
+	String jsonPath = basePathName + ".gltf";	
+	
+	char* textData = LoadTextData(jsonPath, NULL);
+	if (textData == NULL)
+		return false;
+	defer({ delete textData; });
+
+	Json* jRoot = Json::Parse(textData);
+	if (jRoot == NULL)
+		return false;
+	defer({ delete jRoot; });	
+
+	LoadModelProps(basePathName + ".props.txt");
+	
+	Array<Array<uint8>> buffers;
+	Array<DataSpan> bufferViews;
+	Array<DataAccessor> dataAccessors;
+	
+	if (auto jBuffers = jRoot->GetObjectItem("buffers"))
+	{
+		for (auto jBuffer = jBuffers->mChild; jBuffer != NULL; jBuffer = jBuffer->mNext)
+		{
+			Array<uint8> data;
+			if (auto jName = jBuffer->GetObjectItem("uri"))
+			{
+				if (jName->mValueString != NULL)
+				{
+					String dataPath = GetFileDir(basePathName) + "/" + jName->mValueString;
+
+					int size = 0;
+					uint8* rawData = LoadBinaryData(dataPath, &size);
+					if (rawData != NULL)					
+						data.Insert(0, rawData, size);					
+				}
+			}
+
+			buffers.Add(data);
+		}
+	}
+
+	if (auto jBufferViews = jRoot->GetObjectItem("bufferViews"))
+	{
+		for (auto jBufferView = jBufferViews->mChild; jBufferView != NULL; jBufferView = jBufferView->mNext)
+		{
+			int bufferIdx = 0;
+			int byteOffset = 0;
+			int byteLength = 0;
+
+			if (auto jBufferIdx = jBufferView->GetObjectItem("buffer"))
+				bufferIdx = jBufferIdx->mValueInt;
+			if (auto jByteOffset = jBufferView->GetObjectItem("byteOffset"))
+				byteOffset = jByteOffset->mValueInt;
+			if (auto jByteLength = jBufferView->GetObjectItem("byteLength"))
+				byteLength = jByteLength->mValueInt;			
+			bufferViews.Add(DataSpan{ buffers[bufferIdx].mVals + byteOffset, byteLength });
+		}
+	}	
+
+	if (auto jAccessors = jRoot->GetObjectItem("accessors"))
+	{
+		for (auto jAccessor = jAccessors->mChild; jAccessor != NULL; jAccessor = jAccessor->mNext)
+		{
+			DataAccessor dataAccessor = { 0 };
+			if (auto jBufferIdx = jAccessor->GetObjectItem("bufferView"))
+			{
+				DataSpan& dataSpan = bufferViews[jBufferIdx->mValueInt];
+				dataAccessor.mPtr = dataSpan.mPtr;
+				dataAccessor.mSize = dataSpan.mSize;
+			}
+			if (auto jCount = jAccessor->GetObjectItem("count"))
+				dataAccessor.mCount = jCount->mValueInt;
+			if (auto jCount = jAccessor->GetObjectItem("componentType"))
+				dataAccessor.mComponentType = (ComponentType)jCount->mValueInt;
+
+			dataAccessors.Add(dataAccessor);
+		}
+	}
+
+	auto _GetFloat3 = [&](Json* json, Vector3& vec)
+	{
+		int i = 0;
+		for (auto jItem = json->mChild; jItem != NULL; jItem = jItem->mNext)
+		{
+			if (i == 0)
+				vec.mX = (float)jItem->mValueDouble;
+			if (i == 1)
+				vec.mY = (float)jItem->mValueDouble;
+			if (i == 2)
+				vec.mZ = (float)jItem->mValueDouble;
+			i++;
+		}
+	};
+
+	auto _GetFloat4 = [&](Json* json, Vector4& vec)
+	{
+		int i = 0;
+		for (auto jItem = json->mChild; jItem != NULL; jItem = jItem->mNext)
+		{
+			if (i == 0)
+				vec.mX = (float)jItem->mValueDouble;
+			if (i == 1)
+				vec.mY = (float)jItem->mValueDouble;
+			if (i == 2)
+				vec.mZ = (float)jItem->mValueDouble;
+			if (i == 3)
+				vec.mW = (float)jItem->mValueDouble;
+			i++;
+		}
+	};	
+	
+	if (auto jMaterials = jRoot->GetObjectItem("materials"))
+	{
+		int materialIdx = 0;
+		for (auto jMaterial = jMaterials->mChild; jMaterial != NULL; jMaterial = jMaterial->mNext)
+		{
+			ModelMaterialInstance modelMaterialInstance;
+			if (auto jName = jMaterial->GetObjectItem("name"))
+			{
+				if (jName->mValueString != NULL)
+				{					
+					modelMaterialInstance.mName = jName->mValueString;					
+					String matPath = jName->mValueString;
+
+					if (materialIdx < mStaticMaterials.mSize)
+						matPath = mStaticMaterials[materialIdx].mMaterialSlotName;					
+
+					ModelMaterialDef* materialDef = LoadMaterial(matPath);
+					modelMaterialInstance.mDef = materialDef;
+				}
+			}
+			if (auto jPBRMetallicRoughness = jMaterial->GetObjectItem("pbrMetallicRoughness"))
+			{
+				
+			}			
+
+			mModelDef->mMaterials.Add(modelMaterialInstance);
+			materialIdx++;
+		}
+	}
+
+	if (auto jMeshes = jRoot->GetObjectItem("meshes"))
+	{
+		for (auto jMesh = jMeshes->mChild; jMesh != NULL; jMesh = jMesh->mNext)
+		{
+			ModelMesh modelMesh;
+
+			if (auto jName = jMesh->GetObjectItem("name"))
+			{
+				if (jName->mValueString != NULL)
+					modelMesh.mName = jName->mValueString;
+			}
+
+			if (auto jPrimitives = jMesh->GetObjectItem("primitives"))
+			{
+				modelMesh.mPrimitives.Resize(jPrimitives->GetArraySize());				
+
+				int primCount = 0;
+				for (auto jPrimitive = jPrimitives->mChild; jPrimitive != NULL; jPrimitive = jPrimitive->mNext)
+				{
+					ModelPrimitives& modelPrimitives = modelMesh.mPrimitives[primCount];
+
+					if (auto jIndices = jPrimitive->GetObjectItem("indices"))
+					{
+						auto& dataAccessor = dataAccessors[jIndices->mValueInt];						
+						modelPrimitives.mIndices.ResizeRaw(dataAccessor.mCount);
+						for (int i = 0; i < dataAccessor.mCount; i++)
+							modelPrimitives.mIndices[i] = *(uint16*)(dataAccessor.mPtr + i * 2);
+					}
+
+					if (auto jIndices = jPrimitive->GetObjectItem("material"))
+						modelPrimitives.mMaterial = &mModelDef->mMaterials[jIndices->mValueInt];
+
+					if (auto jAttributes = jPrimitive->GetObjectItem("attributes"))
+					{
+						if (auto jPosition = jAttributes->GetObjectItem("POSITION"))
+						{
+							auto& dataAccessor = dataAccessors[jPosition->mValueInt];
+							modelPrimitives.mVertices.Resize(dataAccessor.mCount);
+							ReadBuffer<Vector3>(dataAccessor, &modelPrimitives.mVertices[0].mPosition, sizeof(ModelVertex));
+						}
+
+						if (auto jNormal = jAttributes->GetObjectItem("NORMAL"))													
+							ReadBuffer<Vector3>(dataAccessors[jNormal->mValueInt], &modelPrimitives.mVertices[0].mNormal, sizeof(ModelVertex));
+						if (auto jTangent = jAttributes->GetObjectItem("TANGENT"))
+							ReadBuffer<Vector3>(dataAccessors[jTangent->mValueInt], &modelPrimitives.mVertices[0].mTangent, sizeof(ModelVertex), sizeof(Vector4));
+						if (auto jColor = jAttributes->GetObjectItem("COLOR_0"))
+							ReadBuffer<uint32>(dataAccessors[jColor->mValueInt], &modelPrimitives.mVertices[0].mColor, sizeof(ModelVertex));
+						if (auto jTexCoords = jAttributes->GetObjectItem("TEXCOORD_0"))
+						{
+							ReadBuffer<TexCoords>(dataAccessors[jTexCoords->mValueInt], &modelPrimitives.mVertices[0].mTexCoords, sizeof(ModelVertex));
+							for (auto& vertex : modelPrimitives.mVertices)
+							{
+								vertex.mTexCoords.mV = 1.0f - vertex.mTexCoords.mV;
+							}
+						}
+						if (auto jTexCoords = jAttributes->GetObjectItem("TEXCOORD_1"))
+						{
+							ReadBuffer<TexCoords>(dataAccessors[jTexCoords->mValueInt], &modelPrimitives.mVertices[0].mTexCoords, sizeof(ModelVertex));
+							for (auto& vertex : modelPrimitives.mVertices)
+							{
+								//vertex.mTexCoords.mU = 1.0f - vertex.mTexCoords.mU;
+								vertex.mTexCoords.mV = 1.0f - vertex.mTexCoords.mV;
+							}
+						}
+						else
+						{
+							for (auto& vertex : modelPrimitives.mVertices)
+								vertex.mBumpTexCoords = vertex.mTexCoords;
+						}
+					}
+
+					primCount++;
+				}				
+			}
+
+			mModelDef->mMeshes.Add(modelMesh);
+		}
+	}
+
+	if (auto jNodes = jRoot->GetObjectItem("nodes"))
+	{		
+		mModelDef->mNodes.Reserve(jNodes->GetArraySize());
+		for (auto jNode = jNodes->mChild; jNode != NULL; jNode = jNode->mNext)
+		{
+			ModelNode modelNode;
+			if (auto jName = jNode->GetObjectItem("name"))
+			{
+				if (jName->mValueString != NULL)
+					modelNode.mName = jName->mValueString;
+			}
+			if (auto jChildren = jNode->GetObjectItem("children"))
+			{
+				for (auto jChild = jChildren->mChild; jChild != NULL; jChild = jChild->mNext)
+				{
+					int childIdx = jChild->mValueInt;
+					modelNode.mChildren.Add(mModelDef->mNodes.mVals + childIdx);
+				}
+			}
+
+			if (auto jTranslation = jNode->GetObjectItem("translation"))
+				_GetFloat3(jTranslation, modelNode.mTranslation);
+			if (auto jTranslation = jNode->GetObjectItem("rotation"))
+				_GetFloat4(jTranslation, modelNode.mRotation);
+			if (auto jMesh = jNode->GetObjectItem("mesh"))
+				modelNode.mMesh = mModelDef->mMeshes.mVals + jMesh->mValueInt;
+
+			mModelDef->mNodes.Add(modelNode);
+		}
+	}
+
+	return true;
+}

+ 43 - 0
BeefySysLib/gfx/glTF.h

@@ -0,0 +1,43 @@
+#pragma once
+
+#pragma once
+
+#include "Common.h"
+#include "util/Quaternion.h"
+#include "util/Vector.h"
+#include "util/Array.h"
+#include "FileStream.h"
+#include <vector>
+
+NS_BF_BEGIN;
+
+class ModelDef;
+class ModelMaterialDef;
+
+class GLTFReader
+{
+public:
+	class StaticMaterial
+	{
+	public:
+		ModelMaterialDef* mMaterialDef;
+		String mMaterialSlotName;
+	};
+
+public:
+	String mBasePathName;
+	String mRootDir;
+	ModelDef* mModelDef;
+	Array<StaticMaterial> mStaticMaterials;
+
+public:
+	GLTFReader(ModelDef* modelDef);
+	~GLTFReader();
+
+	bool ParseMaterialDef(ModelMaterialDef* materialDef, const StringImpl& matText);
+	ModelMaterialDef* LoadMaterial(const StringImpl& path);
+	bool LoadModelProps(const StringImpl& relPath);
+	bool ReadFile(const StringImpl& filePath, const StringImpl& rootDir);
+};
+
+NS_BF_END;

+ 62 - 1
BeefySysLib/img/TGAData.cpp

@@ -49,7 +49,7 @@ bool TGAData::ReadData()
 	bool flipped = (hdr->mImageDescriptor & 0x20) != 0;
 
 	mWidth = hdr->mWidth;
-	mHeight = hdr->mWidth;
+	mHeight = hdr->mHeight;
     mBits = new uint32[mWidth * mHeight];
 
 	if (hdr->mDataTypeCode == 10) // RLE
@@ -148,6 +148,67 @@ readSpanHeader:
 				}
 			}
 
+			NOP;
+		}
+		else if (aMode == 3)
+		{
+			int y = 0;
+			int x = 0;
+
+			readSpanHeader3:
+			int spanLen = 0;
+			uint32 spanColor = 0;
+
+			uint8 spanHeader = *(srcPtr++);
+			spanLen = (spanHeader & 0x7F) + 1;
+			if ((spanHeader & 0x80) != 0)
+			{
+				// Repeat color								
+				int b = *(srcPtr++);
+				int g = *(srcPtr++);
+				int r = *(srcPtr++);
+				int a = 255;
+				
+				spanColor = (a << 24) | (b << 16) | (g << 8) | r;
+
+				for (; y < mHeight; y++)
+				{
+					for (; x < mWidth; x++)
+					{
+						if (spanLen == 0)
+							goto readSpanHeader3;
+						*(destPtr++) = spanColor;
+						spanLen--;
+					}
+
+					x = 0;
+					destPtr += destAdd;
+				}
+			}
+			else
+			{
+				for (; y < mHeight; y++)
+				{
+					for (; x < mWidth; x++)
+					{
+						if (spanLen == 0)
+							goto readSpanHeader3;
+
+						int b = *(srcPtr++);
+						int g = *(srcPtr++);
+						int r = *(srcPtr++);
+						int a = 255;
+						
+						*(destPtr++) = (a << 24) | (b << 16) | (g << 8) | r;
+
+						spanLen--;
+					}
+
+					x = 0;
+					destPtr += destAdd;
+				}
+			}
+
 			NOP;
 		}
 	}

+ 2 - 1
BeefySysLib/platform/PlatformInterface.h

@@ -326,7 +326,8 @@ enum BfpSysDirectoryKind
 	BfpSysDirectoryKind_AppData_LocalLow,
 	BfpSysDirectoryKind_AppData_Roaming,
 	BfpSysDirectoryKind_Programs,
-	BfpSysDirectoryKind_Programs_Common	
+	BfpSysDirectoryKind_Programs_Common,
+	BfpSysDirectoryKind_Documents
 };
 
 struct BfpFindFileData;

+ 243 - 0
BeefySysLib/platform/win/DDS.h

@@ -0,0 +1,243 @@
+//--------------------------------------------------------------------------------------
+// dds.h
+//
+// This header defines constants and structures that are useful when parsing 
+// DDS files.  DDS files were originally designed to use several structures
+// and constants that are native to DirectDraw and are defined in ddraw.h,
+// such as DDSURFACEDESC2 and DDSCAPS2.  This file defines similar 
+// (compatible) constants and structures so that one can use DDS files 
+// without needing to include ddraw.h.
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
+// PARTICULAR PURPOSE.
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// http://go.microsoft.com/fwlink/?LinkId=248926
+//--------------------------------------------------------------------------------------
+
+#pragma once
+
+#if defined(_XBOX_ONE) && defined(_TITLE)
+#include <d3d11_x.h>
+#else
+#include <dxgiformat.h>
+#endif
+
+#include <stdint.h>
+
+namespace DirectX
+{
+
+#pragma pack(push,1)
+
+const uint32_t DDS_MAGIC = 0x20534444; // "DDS "
+
+struct DDS_PIXELFORMAT
+{
+    uint32_t    dwSize;
+    uint32_t    dwFlags;
+    uint32_t    dwFourCC;
+    uint32_t    dwRGBBitCount;
+    uint32_t    dwRBitMask;
+    uint32_t    dwGBitMask;
+    uint32_t    dwBBitMask;
+    uint32_t    dwABitMask;
+};
+
+#define DDS_FOURCC      0x00000004  // DDPF_FOURCC
+#define DDS_RGB         0x00000040  // DDPF_RGB
+#define DDS_RGBA        0x00000041  // DDPF_RGB | DDPF_ALPHAPIXELS
+#define DDS_LUMINANCE   0x00020000  // DDPF_LUMINANCE
+#define DDS_LUMINANCEA  0x00020001  // DDPF_LUMINANCE | DDPF_ALPHAPIXELS
+#define DDS_ALPHA       0x00000002  // DDPF_ALPHA
+#define DDS_PAL8        0x00000020  // DDPF_PALETTEINDEXED8
+#define DDS_BUMPDUDV    0x00080000  // DDPF_BUMPDUDV
+
+#ifndef MAKEFOURCC
+    #define MAKEFOURCC(ch0, ch1, ch2, ch3)                              \
+                ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) |       \
+                ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 ))
+#endif /* defined(MAKEFOURCC) */
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT1 =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','1'), 0, 0, 0, 0, 0 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT2 =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','2'), 0, 0, 0, 0, 0 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT3 =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','3'), 0, 0, 0, 0, 0 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT4 =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','4'), 0, 0, 0, 0, 0 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT5 =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','5'), 0, 0, 0, 0, 0 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_BC4_UNORM =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','4','U'), 0, 0, 0, 0, 0 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_BC4_SNORM =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','4','S'), 0, 0, 0, 0, 0 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_BC5_UNORM =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','5','U'), 0, 0, 0, 0, 0 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_BC5_SNORM =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','5','S'), 0, 0, 0, 0, 0 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_R8G8_B8G8 =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('R','G','B','G'), 0, 0, 0, 0, 0 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_G8R8_G8B8 =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('G','R','G','B'), 0, 0, 0, 0, 0 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_YUY2 =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('Y','U','Y','2'), 0, 0, 0, 0, 0 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8R8G8B8 =
+    { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_X8R8G8B8 =
+    { sizeof(DDS_PIXELFORMAT), DDS_RGB,  0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8B8G8R8 =
+    { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_X8B8G8R8 =
+    { sizeof(DDS_PIXELFORMAT), DDS_RGB,  0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_G16R16 =
+    { sizeof(DDS_PIXELFORMAT), DDS_RGB,  0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_R5G6B5 =
+    { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A1R5G5B5 =
+    { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A4R4G4B4 =
+    { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_R8G8B8 =
+    { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_L8 =
+    { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0,  8, 0xff, 0x00, 0x00, 0x00 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_L16 =
+    { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 16, 0xffff, 0x0000, 0x0000, 0x0000 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8L8 =
+    { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 16, 0x00ff, 0x0000, 0x0000, 0xff00 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8 =
+    { sizeof(DDS_PIXELFORMAT), DDS_ALPHA, 0, 8, 0x00, 0x00, 0x00, 0xff };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_V8U8 = 
+    { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 16, 0x00ff, 0xff00, 0x0000, 0x0000 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_Q8W8V8U8 = 
+    { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 };
+
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_V16U16 = 
+    { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 };
+
+// D3DFMT_A2R10G10B10/D3DFMT_A2B10G10R10 should be written using DX10 extension to avoid D3DX 10:10:10:2 reversal issue
+
+// This indicates the DDS_HEADER_DXT10 extension is present (the format is in dxgiFormat)
+extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DX10 =
+    { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','1','0'), 0, 0, 0, 0, 0 };
+
+#define DDS_HEADER_FLAGS_TEXTURE        0x00001007  // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT 
+#define DDS_HEADER_FLAGS_MIPMAP         0x00020000  // DDSD_MIPMAPCOUNT
+#define DDS_HEADER_FLAGS_VOLUME         0x00800000  // DDSD_DEPTH
+#define DDS_HEADER_FLAGS_PITCH          0x00000008  // DDSD_PITCH
+#define DDS_HEADER_FLAGS_LINEARSIZE     0x00080000  // DDSD_LINEARSIZE
+
+#define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT
+#define DDS_WIDTH  0x00000004 // DDSD_WIDTH
+
+#define DDS_SURFACE_FLAGS_TEXTURE 0x00001000 // DDSCAPS_TEXTURE
+#define DDS_SURFACE_FLAGS_MIPMAP  0x00400008 // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP
+#define DDS_SURFACE_FLAGS_CUBEMAP 0x00000008 // DDSCAPS_COMPLEX
+
+#define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
+#define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
+#define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
+#define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
+#define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
+#define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
+
+#define DDS_CUBEMAP_ALLFACES ( DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |\
+                               DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |\
+                               DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ )
+
+#define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP
+
+#define DDS_FLAGS_VOLUME 0x00200000 // DDSCAPS2_VOLUME
+
+// Subset here matches D3D10_RESOURCE_DIMENSION and D3D11_RESOURCE_DIMENSION
+enum DDS_RESOURCE_DIMENSION
+{
+    DDS_DIMENSION_TEXTURE1D	= 2,
+    DDS_DIMENSION_TEXTURE2D	= 3,
+    DDS_DIMENSION_TEXTURE3D	= 4,
+};
+
+// Subset here matches D3D10_RESOURCE_MISC_FLAG and D3D11_RESOURCE_MISC_FLAG
+enum DDS_RESOURCE_MISC_FLAG
+{
+    DDS_RESOURCE_MISC_TEXTURECUBE = 0x4L,
+};
+
+enum DDS_MISC_FLAGS2
+{
+    DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7L,
+};
+
+enum DDS_ALPHA_MODE
+{
+    DDS_ALPHA_MODE_UNKNOWN       = 0,
+    DDS_ALPHA_MODE_STRAIGHT      = 1,
+    DDS_ALPHA_MODE_PREMULTIPLIED = 2,
+    DDS_ALPHA_MODE_OPAQUE        = 3,
+    DDS_ALPHA_MODE_CUSTOM        = 4,
+};
+
+struct DDS_HEADER
+{
+    uint32_t    dwSize;
+    uint32_t    dwFlags;
+    uint32_t    dwHeight;
+    uint32_t    dwWidth;
+    uint32_t    dwPitchOrLinearSize;
+    uint32_t    dwDepth; // only if DDS_HEADER_FLAGS_VOLUME is set in dwFlags
+    uint32_t    dwMipMapCount;
+    uint32_t    dwReserved1[11];
+    DDS_PIXELFORMAT ddspf;
+    uint32_t    dwCaps;
+    uint32_t    dwCaps2;
+    uint32_t    dwCaps3;
+    uint32_t    dwCaps4;
+    uint32_t    dwReserved2;
+};
+
+struct DDS_HEADER_DXT10
+{
+    DXGI_FORMAT dxgiFormat;
+    uint32_t    resourceDimension;
+    uint32_t    miscFlag; // see DDS_RESOURCE_MISC_FLAG
+    uint32_t    arraySize;
+    uint32_t    miscFlags2; // see DDS_MISC_FLAGS2
+};
+
+#pragma pack(pop)
+
+static_assert( sizeof(DDS_HEADER) == 124, "DDS Header size mismatch" );
+static_assert( sizeof(DDS_HEADER_DXT10) == 20, "DDS DX10 Extended Header size mismatch");
+
+}; // namespace

+ 498 - 88
BeefySysLib/platform/win/DXRenderDevice.cpp

@@ -5,6 +5,10 @@
 #include "img/ImageData.h"
 #include "util/PerfTimer.h"
 #include "util/BeefPerf.h"
+#include "FileStream.h"
+#include "DDS.h"
+
+using namespace DirectX;
 
 #include <D3Dcompiler.h>
 
@@ -37,6 +41,131 @@ USING_NS_BF;
 #define DXFAILED(check) ((hr = (check)) != 0)
 #define DXCHECK(check) if ((check) != 0) BF_FATAL(StrFormat("DirectX call failed with result 0x%X", check).c_str());
 
+static int GetBytesPerPixel(DXGI_FORMAT fmt, int& blockSize)
+{
+	blockSize = 1;
+	switch (fmt)
+	{
+	case DXGI_FORMAT_UNKNOWN: return 0;
+	case DXGI_FORMAT_R32G32B32A32_TYPELESS: return 4 + 4 + 4 + 4;
+	case DXGI_FORMAT_R32G32B32A32_FLOAT: return 4 + 4 + 4 + 4;
+	case DXGI_FORMAT_R32G32B32A32_UINT: return 4 + 4 + 4 + 4;
+	case DXGI_FORMAT_R32G32B32A32_SINT: return 4 + 4 + 4 + 4;
+	case DXGI_FORMAT_R32G32B32_TYPELESS: return 4 + 4 + 4;
+	case DXGI_FORMAT_R32G32B32_FLOAT: return 4 + 4 + 4;
+	case DXGI_FORMAT_R32G32B32_UINT: return 4 + 4 + 4;
+	case DXGI_FORMAT_R32G32B32_SINT: return 4 + 4 + 4;
+	case DXGI_FORMAT_R16G16B16A16_TYPELESS: return 2 + 2 + 2 + 2;
+	case DXGI_FORMAT_R16G16B16A16_FLOAT: return 2 + 2 + 2 + 2;
+	case DXGI_FORMAT_R16G16B16A16_UNORM: return 2 + 2 + 2 + 2;
+	case DXGI_FORMAT_R16G16B16A16_UINT: return 2 + 2 + 2 + 2;
+	case DXGI_FORMAT_R16G16B16A16_SNORM: return 2 + 2 + 2 + 2;
+	case DXGI_FORMAT_R16G16B16A16_SINT: return 2 + 2 + 2 + 2;
+	case DXGI_FORMAT_R32G32_TYPELESS: return 4 + 4;
+	case DXGI_FORMAT_R32G32_FLOAT: return 4 + 4;
+	case DXGI_FORMAT_R32G32_UINT: return 4 + 4;
+	case DXGI_FORMAT_R32G32_SINT: return 4 + 4;
+	case DXGI_FORMAT_R32G8X24_TYPELESS: return 4 + 3;
+	case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: return 4 + 1 + 3;
+	case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: return 4 + 1 + 3;
+	case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: return 4 + 1 + 1 + 3;
+	case DXGI_FORMAT_R10G10B10A2_TYPELESS: return 4;
+	case DXGI_FORMAT_R10G10B10A2_UNORM: return 4;
+	case DXGI_FORMAT_R10G10B10A2_UINT: return 4;
+	case DXGI_FORMAT_R11G11B10_FLOAT: return 4;
+	case DXGI_FORMAT_R8G8B8A8_TYPELESS: return 4;
+	case DXGI_FORMAT_R8G8B8A8_UNORM: return 4;
+	case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: return 4;
+	case DXGI_FORMAT_R8G8B8A8_UINT: return 4;
+	case DXGI_FORMAT_R8G8B8A8_SNORM: return 4;
+	case DXGI_FORMAT_R8G8B8A8_SINT: return 4;
+	case DXGI_FORMAT_R16G16_TYPELESS: return 4;
+	case DXGI_FORMAT_R16G16_FLOAT: return 4;
+	case DXGI_FORMAT_R16G16_UNORM: return 4;
+	case DXGI_FORMAT_R16G16_UINT: return 4;
+	case DXGI_FORMAT_R16G16_SNORM: return 4;
+	case DXGI_FORMAT_R16G16_SINT: return 4;
+	case DXGI_FORMAT_R32_TYPELESS: return 4;
+	case DXGI_FORMAT_D32_FLOAT: return 4;
+	case DXGI_FORMAT_R32_FLOAT: return 4;
+	case DXGI_FORMAT_R32_UINT: return 4;
+	case DXGI_FORMAT_R32_SINT: return 4;
+	case DXGI_FORMAT_R24G8_TYPELESS: return 4;
+	case DXGI_FORMAT_D24_UNORM_S8_UINT: return 4;
+	case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return 4;
+	case DXGI_FORMAT_X24_TYPELESS_G8_UINT: return 4;
+	case DXGI_FORMAT_R8G8_TYPELESS: return 2;
+	case DXGI_FORMAT_R8G8_UNORM: return 2;
+	case DXGI_FORMAT_R8G8_UINT: return 2;
+	case DXGI_FORMAT_R8G8_SNORM: return 2;
+	case DXGI_FORMAT_R8G8_SINT: return 2;
+	case DXGI_FORMAT_R16_TYPELESS: return 2;
+	case DXGI_FORMAT_R16_FLOAT: return 2;
+	case DXGI_FORMAT_D16_UNORM: return 2;
+	case DXGI_FORMAT_R16_UNORM: return 2;
+	case DXGI_FORMAT_R16_UINT: return 2;
+	case DXGI_FORMAT_R16_SNORM: return 2;
+	case DXGI_FORMAT_R16_SINT: return 2;
+	case DXGI_FORMAT_R8_TYPELESS: return 1;
+	case DXGI_FORMAT_R8_UNORM: return 1;
+	case DXGI_FORMAT_R8_UINT: return 1;
+	case DXGI_FORMAT_R8_SNORM: return 1;
+	case DXGI_FORMAT_R8_SINT: return 1;
+	case DXGI_FORMAT_A8_UNORM: return 1;
+	case DXGI_FORMAT_R1_UNORM: return 1;
+	case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: return 3;
+	case DXGI_FORMAT_R8G8_B8G8_UNORM: return 4;
+	case DXGI_FORMAT_G8R8_G8B8_UNORM: return 4;
+	case DXGI_FORMAT_BC1_TYPELESS: blockSize = 4; return 8;
+	case DXGI_FORMAT_BC1_UNORM: blockSize = 4; return 8;
+	case DXGI_FORMAT_BC1_UNORM_SRGB: blockSize = 4; return 8;
+	case DXGI_FORMAT_BC2_TYPELESS: blockSize = 4; return 16;
+	case DXGI_FORMAT_BC2_UNORM: blockSize = 4; return 16;
+	case DXGI_FORMAT_BC2_UNORM_SRGB: blockSize = 4; return 16;
+	case DXGI_FORMAT_BC3_TYPELESS: blockSize = 4; return 16;
+	case DXGI_FORMAT_BC3_UNORM: blockSize = 4; return 16;
+	case DXGI_FORMAT_BC3_UNORM_SRGB: blockSize = 4; return 16;
+	case DXGI_FORMAT_BC4_TYPELESS: blockSize = 4; return 8;
+	case DXGI_FORMAT_BC4_UNORM: blockSize = 4; return 8;
+	case DXGI_FORMAT_BC4_SNORM: blockSize = 4; return 8;
+	case DXGI_FORMAT_BC5_TYPELESS: blockSize = 4; return 16;
+	case DXGI_FORMAT_BC5_UNORM: blockSize = 4; return 16;
+	case DXGI_FORMAT_BC5_SNORM: blockSize = 4; return 16;
+	case DXGI_FORMAT_B5G6R5_UNORM: return 1;
+	case DXGI_FORMAT_B5G5R5A1_UNORM: return 2;
+	case DXGI_FORMAT_B8G8R8A8_UNORM: return 4;
+	case DXGI_FORMAT_B8G8R8X8_UNORM: return 4;
+	case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: return 4;
+	case DXGI_FORMAT_B8G8R8A8_TYPELESS: return 4;
+	case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: return 4;
+	case DXGI_FORMAT_B8G8R8X8_TYPELESS: return 4;
+	case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: return 4;
+	case DXGI_FORMAT_BC6H_TYPELESS: return 1;
+	case DXGI_FORMAT_BC6H_UF16: return 1;
+	case DXGI_FORMAT_BC6H_SF16: return 1;
+	case DXGI_FORMAT_BC7_TYPELESS: return 1;
+	case DXGI_FORMAT_BC7_UNORM: return 1;
+	case DXGI_FORMAT_BC7_UNORM_SRGB: return 1;
+	case DXGI_FORMAT_AYUV: return 1;
+	case DXGI_FORMAT_Y410: return 1;
+	case DXGI_FORMAT_Y416: return 1;
+	case DXGI_FORMAT_NV12: return 1;
+	case DXGI_FORMAT_P010: return 1;
+	case DXGI_FORMAT_P016: return 1;
+	case DXGI_FORMAT_420_OPAQUE: return 1;
+	case DXGI_FORMAT_YUY2: return 1;
+	case DXGI_FORMAT_Y210: return 1;
+	case DXGI_FORMAT_Y216: return 1;
+	case DXGI_FORMAT_NV11: return 1;
+	case DXGI_FORMAT_AI44: return 1;
+	case DXGI_FORMAT_IA44: return 1;
+	case DXGI_FORMAT_P8: return 1;
+	case DXGI_FORMAT_A8P8: return 1;
+	case DXGI_FORMAT_B4G4R4A4_UNORM: return 1;
+	default: return 1;
+	}
+}
+
 DXShaderParam::DXShaderParam()
 {
 	mD3DVariable = NULL;
@@ -383,7 +512,7 @@ void DXRenderDevice::PhysSetRenderState(RenderState* renderState)
 	
 	if ((renderState->mShader != mPhysRenderState->mShader) && (renderState->mShader != NULL))
 	{
-		mD3DDeviceContext->PSSetSamplers(0, 1, &mD3DDefaultSamplerState);
+		mD3DDeviceContext->PSSetSamplers(0, 1, mPhysRenderState->mTexWrap ? &mD3DWrapSamplerState  : &mD3DDefaultSamplerState);
 		mD3DDeviceContext->IASetInputLayout(dxShader->mD3DLayout);
 		mD3DDeviceContext->VSSetShader(dxShader->mD3DVertexShader, NULL, 0);
 		mD3DDeviceContext->PSSetShader(dxShader->mD3DPixelShader, NULL, 0);
@@ -595,6 +724,7 @@ ModelInstance* DXRenderDevice::CreateModelInstance(ModelDef* modelDef)
 
 	//renderState->mCullMode = CullMode_Front;
 
+	renderState->mTexWrap = true;
 	renderState->mDepthFunc = DepthFunc_LessEqual;
 	renderState->mWriteDepthBuffer = true;
 
@@ -602,91 +732,135 @@ ModelInstance* DXRenderDevice::CreateModelInstance(ModelDef* modelDef)
 
 	////
 
-	dxModelInstance->mD3DRenderDevice = this;
-
-	dxModelInstance->mDXModelMeshs.resize(modelDef->mMeshes.size());
+	dxModelInstance->mD3DRenderDevice = this;			
+	dxModelInstance->mDXModelMeshs.Resize(modelDef->mMeshes.size());
+	int dxMeshIdx = 0;
 
 	for (int meshIdx = 0; meshIdx < (int)modelDef->mMeshes.size(); meshIdx++)
 	{
 		ModelMesh* mesh = &modelDef->mMeshes[meshIdx];
+		DXModelMesh* dxMesh = &dxModelInstance->mDXModelMeshs[dxMeshIdx];
+		
+		dxMesh->mPrimitives.Resize(mesh->mPrimitives.size());
+
+		for (int primitivesIdx = 0 ; primitivesIdx < (int)mesh->mPrimitives.size(); primitivesIdx++)
+		{	
+			auto primitives = &mesh->mPrimitives[primitivesIdx];
+			auto dxPrimitives = &dxMesh->mPrimitives[primitivesIdx];			
+
+// 			String texPath = mesh->mTexFileName;
+// 			if (!texPath.IsEmpty())
+// 			{
+// 				if ((int)texPath.IndexOf(':') == -1)
+// 					texPath = modelDef->mLoadDir + "Textures/" + texPath;
+// 				//texPath = gBFApp->mInstallDir + L"models/Textures/" + texPath;
+// 
+// 				dxPrimitives->mTexture = (DXTexture*)((RenderDevice*)this)->LoadTexture(texPath, TextureFlag_NoPremult);
+// 			}
+
+			Array<String> texPaths = primitives->mTexPaths;
+			
 
-		DXModelMesh* dxMesh = &dxModelInstance->mDXModelMeshs[meshIdx];
-
-		String texPath = mesh->mTexFileName;
-		if ((int)texPath.IndexOf(':') == -1)
-			texPath = modelDef->mLoadDir + "Textures/" + texPath;
-			//texPath = gBFApp->mInstallDir + L"models/Textures/" + texPath;
-
-		dxMesh->mTexture = (DXTexture*)((RenderDevice*)this)->LoadTexture(texPath, TextureFlag_NoPremult);
+			if (primitives->mMaterial != NULL)
+			{
+				dxPrimitives->mMaterialName = primitives->mMaterial->mName;
+				if (primitives->mMaterial->mDef != NULL)
+				{
+					for (auto& texParamVal : primitives->mMaterial->mDef->mTextureParameterValues)
+					{
+						if (texPaths.IsEmpty())
+							texPaths.Add(texParamVal->mTexturePath);
+
+// 						if (texPath.IsEmpty())
+// 							texPath = texParamVal->mTexturePath;
+// 						if ((texParamVal->mName == "Albedo_texture") || (texParamVal->mName.EndsWith("_Color")))
+// 							texPath = texParamVal->mTexturePath;
+// 						else if ((texParamVal->mName == "NM_texture") || (texParamVal->mName.EndsWith("_NM")))
+// 							bumpTexPath = texParamVal->mTexturePath;
+					}
+				}
+			}
+						
+			for (auto& texPath : texPaths)
+			{
+				if (!modelDef->mLoadDir.IsEmpty())
+					texPath = GetAbsPath(texPath, modelDef->mLoadDir);
+				dxPrimitives->mTextures.Add((DXTexture*)((RenderDevice*)this)->LoadTexture(texPath, TextureFlag_NoPremult));
+			}
+			
+			dxPrimitives->mNumIndices = (int)primitives->mIndices.size();
+			dxPrimitives->mNumVertices = (int)primitives->mVertices.size();
 
-		dxMesh->mNumIndices = (int)mesh->mIndices.size();
-		dxMesh->mNumVertices = (int)mesh->mVertices.size();
+			D3D11_BUFFER_DESC bd;
+			bd.Usage = D3D11_USAGE_DYNAMIC;
+			bd.ByteWidth = (int)primitives->mIndices.size() * sizeof(uint16);
+			bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
+			bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+			bd.MiscFlags = 0;
+			bd.StructureByteStride = 0;
 
-		D3D11_BUFFER_DESC bd;
-		bd.Usage = D3D11_USAGE_DYNAMIC;
-		bd.ByteWidth = (int)mesh->mIndices.size() * sizeof(uint16);
-		bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
-		bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
-		bd.MiscFlags = 0;
-		bd.StructureByteStride = 0;
+			mD3DDevice->CreateBuffer(&bd, NULL, &dxPrimitives->mD3DIndexBuffer);
 
-		mD3DDevice->CreateBuffer(&bd, NULL, &dxMesh->mD3DIndexBuffer);
+			D3D11_MAPPED_SUBRESOURCE mappedSubResource;
 
-		D3D11_MAPPED_SUBRESOURCE mappedSubResource;
+			DXCHECK(mD3DDeviceContext->Map(dxPrimitives->mD3DIndexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubResource));
+			uint16* dxIdxData = (uint16*)mappedSubResource.pData;
+			for (int idxIdx = 0; idxIdx < dxPrimitives->mNumIndices; idxIdx++)
+				dxIdxData[idxIdx] = (uint16)primitives->mIndices[idxIdx];
+			mD3DDeviceContext->Unmap(dxPrimitives->mD3DIndexBuffer, 0);
 
-		DXCHECK(mD3DDeviceContext->Map(dxMesh->mD3DIndexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubResource));
-		uint16* dxIdxData = (uint16*)mappedSubResource.pData;		
-		for (int idxIdx = 0; idxIdx < dxMesh->mNumIndices; idxIdx++)		
-			dxIdxData[idxIdx] = (uint16)mesh->mIndices[idxIdx];		
-		mD3DDeviceContext->Unmap(dxMesh->mD3DIndexBuffer, 0);
+			//
 
-		//
+			bd.Usage = D3D11_USAGE_DYNAMIC;
+			bd.ByteWidth = (int)primitives->mVertices.size() * sizeof(DXModelVertex);
+			bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+			bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+			bd.MiscFlags = 0;
+			bd.StructureByteStride = 0;
 
-		bd.Usage = D3D11_USAGE_DYNAMIC;
-		bd.ByteWidth = (int)mesh->mVertices.size() * sizeof(DXModelVertex);
-		bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
-		bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
-		bd.MiscFlags = 0;
-		bd.StructureByteStride = 0;
+			mD3DDevice->CreateBuffer(&bd, NULL, &dxPrimitives->mD3DVertexBuffer);
 
-		mD3DDevice->CreateBuffer(&bd, NULL, &dxMesh->mD3DVertexBuffer);
+			DXCHECK(mD3DDeviceContext->Map(dxPrimitives->mD3DVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubResource));
+			DXModelVertex* dxVtxData = (DXModelVertex*)mappedSubResource.pData;
+			for (int vtxIdx = 0; vtxIdx < (int)primitives->mVertices.size(); vtxIdx++)
+			{
+				ModelVertex* srcVtxData = &primitives->mVertices[vtxIdx];
+				DXModelVertex* destVtx = dxVtxData + vtxIdx;
+
+				destVtx->mPosition = srcVtxData->mPosition;
+				destVtx->mTexCoords = srcVtxData->mTexCoords;
+				//destVtx->mTexCoords.mV = 1.0f - destVtx->mTexCoords.mV;
+				destVtx->mTexCoords.mV = destVtx->mTexCoords.mV;
+				destVtx->mBumpTexCoords = srcVtxData->mBumpTexCoords;
+				destVtx->mColor = srcVtxData->mColor;
+				destVtx->mTangent = srcVtxData->mTangent;
+			}
 
-		/*DXCHECK(mD3DDeviceContext->Map(dxMesh->mD3DVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubResource));
-		DXModelVertex* dxVtxData = (DXModelVertex*)mappedSubResource.pData;
-		for (int vtxIdx = 0; vtxIdx < (int)mesh->mVertexData.size(); vtxIdx++)
-		{
-			VertexData* srcVtxData = &mesh->mVertexData[vtxIdx];
-			DXModelVertex* destVtx = dxVtxData + vtxIdx;
+			mD3DDeviceContext->Unmap(dxPrimitives->mD3DVertexBuffer, 0);
 
-			destVtx->mPosition = srcVtxData->mCoords;			
-			destVtx->mTexCoords = srcVtxData->mTexCoords[0];			
-			destVtx->mTexCoords.mV = 1.0f - destVtx->mTexCoords.mV;
-			destVtx->mBumpTexCoords = srcVtxData->mTexCoords[0];			
-			destVtx->mColor = 0xFFFFFFFF;
-			destVtx->mTangent = srcVtxData->mTangent;			
+			dxMeshIdx++;
 		}
-		
-		mD3DDeviceContext->Unmap(dxMesh->mD3DVertexBuffer, 0);*/
 	}
 
 	return dxModelInstance;
 }
 
-void DXDrawLayer::SetShaderConstantData(int slotIdx, void* constData, int size)
+void DXDrawLayer::SetShaderConstantData(int usageIdx, int slotIdx, void* constData, int size)
 {
 	DXSetConstantData* dxSetConstantData = AllocRenderCmd<DXSetConstantData>(size);
 	dxSetConstantData->mRenderState = mRenderDevice->mCurRenderState;
+	dxSetConstantData->mUsageIdx = usageIdx;
 	dxSetConstantData->mSlotIdx = slotIdx;
 	dxSetConstantData->mSize = size;
 
-	if (size == 64) // Transpose for shader	
-		*((Matrix4*)dxSetConstantData->mData) = Matrix4::Transpose(*((Matrix4*)constData));	
-	else
+// 	if (size == 64) // Transpose for shader	
+// 		*((Matrix4*)dxSetConstantData->mData) = Matrix4::Transpose(*((Matrix4*)constData));	
+// 	else
 		memcpy(dxSetConstantData->mData, constData, size);
 	QueueRenderCmd(dxSetConstantData);
 }
 
-void DXDrawLayer::SetShaderConstantDataTyped(int slotIdx, void* constData, int size, int* typeData, int typeCount)
+void DXDrawLayer::SetShaderConstantDataTyped(int usageIdx, int slotIdx, void* constData, int size, int* typeData, int typeCount)
 {
 	for (int usageIdx = 0; usageIdx < 2; usageIdx++)
 	{
@@ -770,18 +944,20 @@ void DXDrawLayer::SetShaderConstantDataTyped(int slotIdx, void* constData, int s
 
 ///
 
-DXModelMesh::DXModelMesh()
+DXModelPrimitives::DXModelPrimitives()
 {
 	mD3DIndexBuffer = NULL;
-	mD3DVertexBuffer = NULL;
+	mD3DVertexBuffer = NULL;	
 }
 
-DXModelMesh::~DXModelMesh()
+DXModelPrimitives::~DXModelPrimitives()
 {
 	if (mD3DIndexBuffer != NULL)
 		mD3DIndexBuffer->Release();
 	if (mD3DVertexBuffer != NULL)
 		mD3DVertexBuffer->Release();
+	for (auto tex : mTextures)
+		tex->Release();	
 }
 
 //////////////////////////////////////////////////////////////////////////
@@ -839,6 +1015,12 @@ void DXRenderState::SetClipped(bool clipped)
 	InvalidateRasterizerState(); 
 }
 
+void DXRenderState::SetTexWrap(bool wrap)
+{
+	mTexWrap = wrap;
+	InvalidateRasterizerState();
+}
+
 void DXRenderState::SetClipRect(const Rect& rect) 
 { 
 	BF_ASSERT((rect.mWidth >= 0) && (rect.mHeight >= 0));
@@ -879,14 +1061,44 @@ void DXModelInstance::Render(RenderDevice* renderDevice, RenderWindow* renderWin
 
 		DXModelMesh* dxMesh = &mDXModelMeshs[meshIdx];
 
-		mD3DRenderDevice->mD3DDeviceContext->PSSetShaderResources(0, 1, &dxMesh->mTexture->mD3DResourceView);
-				
-		// Set vertex buffer
-		UINT stride = sizeof(DXModelVertex);
-		UINT offset = 0;
-		mD3DRenderDevice->mD3DDeviceContext->IASetVertexBuffers(0, 1, &dxMesh->mD3DVertexBuffer, &stride, &offset);
-		mD3DRenderDevice->mD3DDeviceContext->IASetIndexBuffer(dxMesh->mD3DIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
-		mD3DRenderDevice->mD3DDeviceContext->DrawIndexed(dxMesh->mNumIndices, 0, 0);
+		for (auto primIdx = 0; primIdx < (int)dxMesh->mPrimitives.size(); primIdx++)
+		{
+			auto dxPrimitives = &dxMesh->mPrimitives[primIdx];
+
+			if (dxPrimitives->mNumIndices == 11904)
+			{
+				NOP;
+			}
+
+			//TODO:
+			if (dxPrimitives->mNumIndices == 48384)
+				continue;
+
+			if (::GetAsyncKeyState('1'))
+			{
+				if (dxPrimitives->mNumIndices != 9417)
+					continue;
+			}
+			else if (::GetAsyncKeyState('2'))
+			{
+				if (dxPrimitives->mNumIndices != 3684)
+					continue;
+			}
+			
+
+			if (dxPrimitives->mTextures.IsEmpty())
+				continue;
+
+			for (int i = 0; i < (int)dxPrimitives->mTextures.mSize; i++)
+				mD3DRenderDevice->mD3DDeviceContext->PSSetShaderResources(i, 1, &dxPrimitives->mTextures[i]->mD3DResourceView);
+
+			// Set vertex buffer
+			UINT stride = sizeof(DXModelVertex);
+			UINT offset = 0;
+			mD3DRenderDevice->mD3DDeviceContext->IASetVertexBuffers(0, 1, &dxPrimitives->mD3DVertexBuffer, &stride, &offset);
+			mD3DRenderDevice->mD3DDeviceContext->IASetIndexBuffer(dxPrimitives->mD3DIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
+			mD3DRenderDevice->mD3DDeviceContext->DrawIndexed(dxPrimitives->mNumIndices, 0, 0);
+		}
 	}	
 }
 
@@ -992,46 +1204,59 @@ void DXSetTextureCmd::Render(RenderDevice* renderDevice, RenderWindow* renderWin
 
 void DXSetConstantData::Render(RenderDevice* renderDevice, RenderWindow* renderWindow)
 {
-	SetRenderState();
+	//SetRenderState();
 
 	DXShader* dxShader = (DXShader*)renderDevice->mCurRenderState->mShader;
 	DXRenderDevice* dxRenderDevice = (DXRenderDevice*)renderDevice;
 
 	HRESULT result = 0;
 
-	int numBlocks = (mSize + 16 - 1) / 16;		
-	int mtxBufferNum = mSlotIdx * 32 + (numBlocks - 1) * 2 + mUsageIdx;
-	static ID3D11Buffer* matrixBuffers[32 * 32 * 2] = {NULL};
-	
-	if (matrixBuffers[mtxBufferNum] == NULL)
-	{	
+	int bufferSize = BF_ALIGN(mSize, 16);
+
+	int id = (mSlotIdx << 24) | (bufferSize << 1) | (mUsageIdx);
+	ID3D11Buffer* buffer = NULL;
+	ID3D11Buffer** bufferPtr = NULL;
+	if (dxRenderDevice->mBufferMap.TryAdd(id, NULL, &bufferPtr))
+	{		
 		D3D11_BUFFER_DESC matrixBufferDesc;
 		matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
-		matrixBufferDesc.ByteWidth = sizeof(float[4]) * numBlocks;
+		matrixBufferDesc.ByteWidth = bufferSize;
 		matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
 		matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
 		matrixBufferDesc.MiscFlags = 0;
 		matrixBufferDesc.StructureByteStride = 0;
 
-		result = dxRenderDevice->mD3DDevice->CreateBuffer(&matrixBufferDesc, NULL, &matrixBuffers[mtxBufferNum]);
+		result = dxRenderDevice->mD3DDevice->CreateBuffer(&matrixBufferDesc, NULL, &buffer);
 		if (FAILED(result))
 			return;
+
+		//OutputDebugStrF("Created Buffer %d %p\n", bufferSize, buffer);
+
+		*bufferPtr = buffer;
 	}
+	else
+		buffer = *bufferPtr;
 	
 	D3D11_MAPPED_SUBRESOURCE mappedResource;
-	result = dxRenderDevice->mD3DDeviceContext->Map(matrixBuffers[mtxBufferNum], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
-	if (FAILED(result))	
+	result = dxRenderDevice->mD3DDeviceContext->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
+	if (FAILED(result))
 		return;
-	
-	float* dataPtr = (float*) mappedResource.pData;
-	memset(dataPtr, 0, numBlocks * 16);
+
+	float* dataPtr = (float*)mappedResource.pData;	
+	memset(dataPtr, 0, bufferSize);
 	memcpy(mappedResource.pData, mData, mSize);
-	
-	dxRenderDevice->mD3DDeviceContext->Unmap(matrixBuffers[mtxBufferNum], 0);		
+
+	dxRenderDevice->mD3DDeviceContext->Unmap(buffer, 0);
 	if (mUsageIdx == 0)
-		dxRenderDevice->mD3DDeviceContext->VSSetConstantBuffers(mSlotIdx, 1, &matrixBuffers[mtxBufferNum]);
+	{
+		//OutputDebugStrF("VSSetConstantBuffers %d %p\n", mSlotIdx, buffer);
+		dxRenderDevice->mD3DDeviceContext->VSSetConstantBuffers(mSlotIdx, 1, &buffer);
+	}
 	else
-		dxRenderDevice->mD3DDeviceContext->PSSetConstantBuffers(mSlotIdx, 1, &matrixBuffers[mtxBufferNum]);
+	{
+		//OutputDebugStrF("PSSetConstantBuffers %d %p\n", mSlotIdx, buffer);
+		dxRenderDevice->mD3DDeviceContext->PSSetConstantBuffers(mSlotIdx, 1, &buffer);
+	}
 }
 
 ///
@@ -1291,7 +1516,8 @@ bool DXRenderDevice::Init(BFApp* app)
 
 	D3D_FEATURE_LEVEL d3dFeatureLevel = (D3D_FEATURE_LEVEL)0;
 	int flags = 0;	
-	//flags = D3D11_CREATE_DEVICE_DEBUG;
+	//TODO:
+	flags = D3D11_CREATE_DEVICE_DEBUG;
 	DXCHECK(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, featureLevelArr, 6, D3D11_SDK_VERSION, &mD3DDevice, &d3dFeatureLevel, &mD3DDeviceContext));
 	OutputDebugStrF("D3D Feature Level: %X\n", d3dFeatureLevel);
 	
@@ -1363,9 +1589,18 @@ bool DXRenderDevice::Init(BFApp* app)
 	sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
 	sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
 	sampDesc.MinLOD = 0;
-	sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
-	
+	sampDesc.MaxLOD = D3D11_FLOAT32_MAX;	
 	DXCHECK(mD3DDevice->CreateSamplerState(&sampDesc, &mD3DDefaultSamplerState));
+	
+	ZeroMemory(&sampDesc, sizeof(sampDesc));
+	sampDesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+	sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
+	sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
+	sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
+	sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
+	sampDesc.MinLOD = 0;
+	sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
+	DXCHECK(mD3DDevice->CreateSamplerState(&sampDesc, &mD3DWrapSamplerState));
 		
 	D3D11_BUFFER_DESC bd;
 	bd.Usage = D3D11_USAGE_DYNAMIC;
@@ -1402,6 +1637,8 @@ void DXRenderDevice::ReleaseNative()
 	mD3DNormalBlendState = NULL;
 	mD3DDefaultSamplerState->Release();
 	mD3DDefaultSamplerState = NULL;
+	mD3DWrapSamplerState->Release();
+	mD3DWrapSamplerState = NULL;
 	mD3DDeviceContext->Release();
 	mD3DDeviceContext = NULL;
 	mD3DDevice->Release();
@@ -1468,6 +1705,178 @@ void DXRenderDevice::FrameEnd()
 	}
 }
 
+Texture* DXRenderDevice::LoadTexture(const StringImpl& fileName, int flags)
+{
+	if (fileName.StartsWith("!backbuffer:"))
+	{
+		int colon = (int)fileName.IndexOf(':');
+		String addrStr = fileName.Substring(colon + 1);		
+		void* addr = (void*)(intptr)strtoll(addrStr.c_str(), NULL, 16);
+		BFWindow* window = (BFWindow*)addr;
+		DXRenderWindow* renderWindow = (DXRenderWindow*)window->mRenderWindow;
+
+		DXTexture* aTexture = NULL;
+		aTexture->mD3DRenderTargetView = renderWindow->mD3DRenderTargetView;
+		aTexture->mD3DTexture = renderWindow->mD3DBackBuffer;
+
+		aTexture->mD3DRenderTargetView->AddRef();
+		aTexture->mD3DTexture->AddRef();
+		aTexture->AddRef();
+		return aTexture;
+	}
+
+	DXTexture* aTexture = NULL;
+	if (mTextureMap.TryGetValue(fileName, &aTexture))
+	{
+		aTexture->AddRef();
+		return aTexture;
+	}
+
+	int dotPos = (int)fileName.LastIndexOf('.');
+	String ext;
+	if (dotPos != -1)
+		ext = fileName.Substring(dotPos);
+
+	if (ext.Equals(".dds", StringImpl::CompareKind_OrdinalIgnoreCase))
+	{
+		FileStream fs;
+		if (!fs.Open(fileName, "rb"))
+			return NULL;
+
+		int header = fs.ReadInt32();
+		if (header != 0x20534444)
+			return NULL;
+
+		auto hdr = fs.ReadT<DDS_HEADER>();
+
+		DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM;
+
+		if (hdr.ddspf.dwFlags == DDS_RGBA)
+		{
+			if (hdr.ddspf.dwRGBBitCount == 32)
+			{
+				if (hdr.ddspf.dwRBitMask == 0xff)
+					format = DXGI_FORMAT_R8G8B8A8_UNORM;
+				else if (hdr.ddspf.dwRBitMask = 0xff0000)
+					format = DXGI_FORMAT_B8G8R8A8_UNORM;
+				else if (hdr.ddspf.dwRBitMask == 0xffff)
+					format = DXGI_FORMAT_R16G16_UNORM;
+				else if (hdr.ddspf.dwRBitMask == 0x3ff)
+					format = DXGI_FORMAT_R10G10B10A2_UNORM;
+			}
+			else if (hdr.ddspf.dwRGBBitCount == 16)
+			{
+				if (hdr.ddspf.dwRBitMask == 0x7c00)
+					format = DXGI_FORMAT_B5G5R5A1_UNORM;
+				else if (hdr.ddspf.dwRBitMask == 0xf800)
+					format = DXGI_FORMAT_B5G6R5_UNORM;				
+			}
+			else if (hdr.ddspf.dwRGBBitCount == 8)
+			{
+				if (hdr.ddspf.dwRBitMask == 0xff)
+					format = DXGI_FORMAT_R8_UNORM;
+				else if (hdr.ddspf.dwABitMask == 0xff)
+					format = DXGI_FORMAT_A8_UNORM;
+			}
+		}
+
+		if (hdr.ddspf.dwFourCC == '1TXD')
+			format = DXGI_FORMAT_BC1_UNORM;
+		if (hdr.ddspf.dwFourCC == '3TXD')
+			format = DXGI_FORMAT_BC2_UNORM;
+		if (hdr.ddspf.dwFourCC == '5TXD')
+			format = DXGI_FORMAT_BC3_UNORM;
+		if (hdr.ddspf.dwFourCC == 'U4CB')
+			format = DXGI_FORMAT_BC4_UNORM;
+		if (hdr.ddspf.dwFourCC == 'S4CB')
+			format = DXGI_FORMAT_BC4_SNORM;
+		if (hdr.ddspf.dwFourCC == '2ITA')
+			format = DXGI_FORMAT_BC5_UNORM;
+		if (hdr.ddspf.dwFourCC == 'S5CB')
+			format = DXGI_FORMAT_BC5_SNORM;		
+
+		if (hdr.ddspf.dwFourCC == '01XD')
+		{
+			auto hdr10 = fs.ReadT<DDS_HEADER_DXT10>();
+			format = hdr10.dxgiFormat;
+		}
+
+		int blockSize = 0;
+		int bytesPerPixel = GetBytesPerPixel(format, blockSize);		
+
+		int mipSize = ((hdr.dwWidth + blockSize - 1) / blockSize) * ((hdr.dwHeight + blockSize - 1) / blockSize) * bytesPerPixel;
+		Array<uint8> data;
+		data.Resize(mipSize);
+		fs.Read(data.mVals, data.mSize);
+
+		D3D11_SUBRESOURCE_DATA resData;
+		resData.pSysMem = data.mVals;
+		resData.SysMemPitch = ((hdr.dwWidth + blockSize - 1) / blockSize) * bytesPerPixel;
+		resData.SysMemSlicePitch = mipSize;
+
+		// Create the target texture
+		D3D11_TEXTURE2D_DESC desc;
+		ZeroMemory(&desc, sizeof(desc));
+		desc.Width = hdr.dwWidth;
+		desc.Height = hdr.dwHeight;
+		desc.MipLevels = 1;
+		desc.ArraySize = 1;
+		desc.Format = format;
+		desc.SampleDesc.Count = 1;
+		desc.Usage = D3D11_USAGE_DEFAULT;
+		desc.CPUAccessFlags = 0;
+		desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+
+		DXGI_FORMAT viewFormat = format;
+		switch (viewFormat)
+		{
+		case DXGI_FORMAT_B8G8R8A8_TYPELESS: viewFormat = DXGI_FORMAT_B8G8R8A8_UNORM; break;
+		case DXGI_FORMAT_R8G8B8A8_TYPELESS: viewFormat = DXGI_FORMAT_R8G8B8A8_UNORM; break;
+		case DXGI_FORMAT_BC1_TYPELESS: viewFormat = DXGI_FORMAT_BC1_UNORM; break;
+		case DXGI_FORMAT_BC2_TYPELESS: viewFormat = DXGI_FORMAT_BC2_UNORM; break;
+		case DXGI_FORMAT_BC3_TYPELESS: viewFormat = DXGI_FORMAT_BC3_UNORM; break;
+		case DXGI_FORMAT_BC4_TYPELESS: viewFormat = DXGI_FORMAT_BC4_UNORM; break;
+		case DXGI_FORMAT_BC5_TYPELESS: viewFormat = DXGI_FORMAT_BC5_UNORM; break;
+		}
+
+		//OutputDebugStrF("Creating texture\n");
+
+		ID3D11Texture2D* d3DTexture = NULL;
+		DXCHECK(mD3DDevice->CreateTexture2D(&desc, &resData, &d3DTexture));
+
+		D3D11_SHADER_RESOURCE_VIEW_DESC srDesc;
+		srDesc.Format = viewFormat;
+		srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+		srDesc.Texture2D.MostDetailedMip = 0;
+		srDesc.Texture2D.MipLevels = 1;
+
+		ID3D11ShaderResourceView* d3DShaderResourceView = NULL;
+		DXCHECK(mD3DDevice->CreateShaderResourceView(d3DTexture, &srDesc, &d3DShaderResourceView));
+
+		DXTexture* aTexture = new DXTexture();
+		aTexture->mPath = fileName;
+		aTexture->mRenderDevice = this;
+		aTexture->mWidth = hdr.dwWidth;
+		aTexture->mHeight = hdr.dwHeight;
+		aTexture->mD3DTexture = d3DTexture;
+		aTexture->mD3DResourceView = d3DShaderResourceView;
+		aTexture->AddRef();
+
+		mTextureMap[aTexture->mPath] = aTexture;
+		mTextures.Add(aTexture);
+		return aTexture;
+	}
+		
+	aTexture = (DXTexture*)RenderDevice::LoadTexture(fileName, flags);
+	if (aTexture != NULL)
+	{
+		aTexture->mPath = fileName;
+		mTextureMap[aTexture->mPath] = aTexture;
+	}
+	
+	return aTexture;
+}
+
 Texture* DXRenderDevice::LoadTexture(ImageData* imageData, int flags)
 {	
 	ID3D11ShaderResourceView* d3DShaderResourceView = NULL;
@@ -1809,7 +2218,7 @@ Texture* DXRenderDevice::CreateRenderTarget(int width, int height, bool destAlph
 
 	ID3D11Texture2D* d3DTexture = NULL;
 	DXCHECK(mD3DDevice->CreateTexture2D(&desc, NULL, &d3DTexture));
-
+	
 	aWidth = width;
 	aHeight = height;
 
@@ -1833,6 +2242,7 @@ Texture* DXRenderDevice::CreateRenderTarget(int width, int height, bool destAlph
 	aRenderTarget->mWidth = width;
 	aRenderTarget->mHeight = height;
 	aRenderTarget->mRenderDevice = this;	
+	aRenderTarget->mD3DTexture = d3DTexture;
 	aRenderTarget->mD3DResourceView = d3DShaderResourceView;
 	aRenderTarget->mD3DRenderTargetView = d3DRenderTargetView;
 	aRenderTarget->AddRef();

+ 23 - 9
BeefySysLib/platform/win/DXRenderDevice.h

@@ -44,6 +44,7 @@
 #include "gfx/DrawLayer.h"
 #include "gfx/ModelInstance.h"
 #include "util/HashSet.h"
+#include "util/Dictionary.h"
 #include <map>
 
 NS_BF_BEGIN;
@@ -55,6 +56,7 @@ class DXRenderDevice;
 class DXTexture : public Texture
 {
 public:
+	String					mPath;
 	DXRenderDevice*			mRenderDevice;
 	ID3D11Texture2D*		mD3DTexture;
 	ID3D11ShaderResourceView* mD3DResourceView;
@@ -124,8 +126,8 @@ class DXDrawLayer : public DrawLayer
 public:	
 	virtual DrawBatch*		CreateDrawBatch();	
 	virtual RenderCmd*		CreateSetTextureCmd(int textureIdx, Texture* texture) override;
-	virtual void			SetShaderConstantData(int slotIdx, void* constData, int size) override;
-	virtual void			SetShaderConstantDataTyped(int slotIdx, void* constData, int size, int* typeData, int typeCount) override;
+	virtual void			SetShaderConstantData(int usageIdx, int slotIdx, void* constData, int size) override;
+	virtual void			SetShaderConstantDataTyped(int usageIdx, int slotIdx, void* constData, int size, int* typeData, int typeCount) override;
 
 public:	
 	DXDrawLayer();	
@@ -203,32 +205,40 @@ public:
 	void IndalidateDepthStencilState();	
 
 	virtual void SetClipped(bool clipped);
+	virtual void SetTexWrap(bool clipped);
 	virtual void SetClipRect(const Rect& rect);
 	virtual void SetWriteDepthBuffer(bool writeDepthBuffer);
 	virtual void SetDepthFunc(DepthFunc depthFunc);
 };
 
-class DXModelMesh
+class DXModelPrimitives
 {
 public:
+	String					mMaterialName;
 	int						mNumIndices;
 	int						mNumVertices;
-	DXTexture*				mTexture;
+	Array<DXTexture*>		mTextures;	
 
 	ID3D11Buffer*			mD3DIndexBuffer;
 	//TODO: Split the vertex buffer up into static and dynamic buffers
 	ID3D11Buffer*			mD3DVertexBuffer;
 
 public:
-	DXModelMesh();
-	~DXModelMesh();
+	DXModelPrimitives();
+	~DXModelPrimitives();
+};
+
+class DXModelMesh
+{
+public:
+	Array<DXModelPrimitives> mPrimitives;
 };
 
 class DXModelInstance : public ModelInstance
 {
 public:	
 	DXRenderDevice*			mD3DRenderDevice;	
-	std::vector<DXModelMesh> mDXModelMeshs;	
+	Array<DXModelMesh>		mDXModelMeshs;	
 
 public:
 	DXModelInstance(ModelDef* modelDef);
@@ -274,6 +284,7 @@ public:
 	ID3D11DeviceContext*	mD3DDeviceContext;
 	ID3D11BlendState*		mD3DNormalBlendState;		
 	ID3D11SamplerState*		mD3DDefaultSamplerState;
+	ID3D11SamplerState*		mD3DWrapSamplerState;
 	bool					mHasVSync;
 
 	ID3D11Buffer*			mD3DVertexBuffer;
@@ -282,8 +293,10 @@ public:
 	int						mIdxByteIdx;	
 
 	HashSet<DXRenderState*>	mRenderStates;
-	HashSet<DXTexture*>		mTextures;	
-		
+	HashSet<DXTexture*>		mTextures;
+	Dictionary<String, DXTexture*> mTextureMap;
+	Dictionary<int, ID3D11Buffer*> mBufferMap;
+			
 public:		
 	virtual void			PhysSetRenderState(RenderState* renderState) override;
 	virtual void			PhysSetRenderWindow(RenderWindow* renderWindow);
@@ -302,6 +315,7 @@ public:
 	void					FrameStart() override;
 	void					FrameEnd() override;
 
+	Texture*				LoadTexture(const StringImpl& fileName, int flags) override;
 	Texture*				LoadTexture(ImageData* imageData, int flags) override;
 	Texture*				CreateDynTexture(int width, int height) override;
 	Shader*					LoadShader(const StringImpl& fileName, VertexDefinition* vertexDefinition) override;

+ 3 - 0
BeefySysLib/platform/win/Platform.cpp

@@ -2637,6 +2637,9 @@ BFP_EXPORT void BFP_CALLTYPE BfpDirectory_GetSysDirectory(BfpSysDirectoryKind sy
 	case BfpSysDirectoryKind_Programs_Common:
 		_GetKnownFolder(FOLDERID_CommonPrograms);
 		return;	
+	case BfpSysDirectoryKind_Documents:
+		_GetKnownFolder(FOLDERID_Documents);
+		return;
 	}
 
 	TryStringOut(path, outPath, inOutPathLen, (BfpResult*)outResult);

+ 4 - 4
BeefySysLib/util/Quaternion.cpp

@@ -26,11 +26,11 @@ Beefy::Quaternion Beefy::Quaternion::Slerp(float fT, const Quaternion& rkP, cons
 	if ((fabs(fCos) < 1.0f - BF_MS_EPSILON) && (false))
 	{
 		// Standard case (slerp)
-		float fSin = sqrt(1.0f - (fCos * fCos));
-		float fAngle = atan2(fSin, fCos);
+		float fSin = sqrtf(1.0f - (fCos * fCos));
+		float fAngle = atan2f(fSin, fCos);
 		float fInvSin = 1.0f / fSin;
-		float fCoeff0 = sin((1.0f - fT) * fAngle) * fInvSin;
-		float fCoeff1 = sin(fT * fAngle) * fInvSin;
+		float fCoeff0 = sinf((1.0f - fT) * fAngle) * fInvSin;
+		float fCoeff1 = sinf(fT * fAngle) * fInvSin;
 		return fCoeff0 * rkP + fCoeff1 * rkT;
 	}
 	else

+ 1 - 1
BeefySysLib/util/Quaternion.h

@@ -114,7 +114,7 @@ public:
 	static Quaternion Normalise(const Quaternion& quat)
 	{
 		float len = quat.Norm();
-		float factor = 1.0f / sqrt(len);
+		float factor = 1.0f / sqrtf(len);
 		return quat * factor;		
 	}
 };

+ 11 - 1
BeefySysLib/util/Vector.cpp

@@ -13,7 +13,7 @@ Vector3::Vector3(float x, float y, float z)
 
 float Vector3::GetMagnitude() const
 {
-	return sqrt(mX*mX + mY*mY + mZ*mZ);
+	return sqrtf(mX*mX + mY*mY + mZ*mZ);
 }
 
 Vector3 Vector3::Normalize(const Vector3& vec)
@@ -75,4 +75,14 @@ Vector3 Vector3::Transform2(const Vector3& vec, const Quaternion& quat)
 	result.mZ = vec.mZ + z * quat.mW + (quat.mX * y - quat.mY * x);
 
 	return result;
+}
+
+///
+
+Vector4::Vector4(float x, float y, float z, float w)
+{
+	mX = x;
+	mY = y;
+	mZ = z;
+	mW = w;
 }

+ 60 - 0
BeefySysLib/util/Vector.h

@@ -94,4 +94,64 @@ public:
 	}
 };
 
+class Vector4
+{
+public:
+	float mX;
+	float mY;
+	float mZ;
+	float mW;
+
+public:
+	Vector4(float x = 0, float y = 0, float z = 0, float w = 0);
+	
+	bool operator==(const Vector4& check) const
+	{
+		return (mX == check.mX) && (mY == check.mY) && (mZ == check.mZ);
+	}
+
+	bool operator!=(const Vector4& check) const
+	{
+		return (mX != check.mX) || (mY != check.mY) || (mZ != check.mZ);
+	}
+	
+	static Vector4 Scale(const Vector4& vec, float scale)
+	{
+		return Vector4(vec.mX * scale, vec.mY * scale, vec.mZ * scale, vec.mW * scale);
+	}
+
+	Vector4 operator +(const Vector4& v2) const
+	{
+		return Vector4(mX + v2.mX, mY + v2.mY, mZ + v2.mZ, mW + v2.mW);
+	}
+
+	Vector4 operator *(const Vector4& v2) const
+	{
+		return Vector4(mX * v2.mX, mY * v2.mY, mZ * v2.mZ, mW * v2.mW);
+	}
+
+	Vector4 operator *(float scale) const
+	{
+		return Vector4(mX * scale, mY * scale, mZ * scale, mW * scale);
+	}
+
+	inline Vector4& operator -= (const Vector4& vec)
+	{
+		mX -= vec.mX;
+		mY -= vec.mY;
+		mZ -= vec.mZ;
+		mW -= vec.mW;
+		return *this;
+	}
+
+	inline Vector4& operator *= (const Vector4& vec)
+	{
+		mX *= vec.mX;
+		mY *= vec.mY;
+		mZ *= vec.mZ;
+		mW *= vec.mW;
+		return *this;
+	}
+};
+
 NS_BF_END;