소스 검색

Added support for debug bitmap texture saving

Marko Pintera 12 년 전
부모
커밋
a25ea2ab4f

+ 3 - 0
CamelotClient/CamelotClient.cpp

@@ -60,6 +60,9 @@ int CALLBACK WinMain(
 	GameObjectPtr testModelGO = GameObject::create("TestMesh");
 	GameObjectPtr testModelGO = GameObject::create("TestMesh");
 	RenderablePtr testRenderable = testModelGO->addComponent<Renderable>();
 	RenderablePtr testRenderable = testModelGO->addComponent<Renderable>();
 
 
+	// Debug test fonts
+	FontHandle font = Importer::instance().import("C:\\arial.ttf");
+
 #if defined DX9
 #if defined DX9
 	///////////////// HLSL 9 SHADERS //////////////////////////
 	///////////////// HLSL 9 SHADERS //////////////////////////
 	String dx9psLoc = "C:\\Projects\\CamelotEngine\\Data\\hlsl9_ps.gpuprog";
 	String dx9psLoc = "C:\\Projects\\CamelotEngine\\Data\\hlsl9_ps.gpuprog";

+ 1 - 0
CamelotCore/Source/CmApplication.cpp

@@ -83,6 +83,7 @@ namespace CamelotEngine
 		Importer::startUp(new Importer());
 		Importer::startUp(new Importer());
 		loadPlugin("CamelotFreeImgImporter"); // TODO - Load this automatically somehow
 		loadPlugin("CamelotFreeImgImporter"); // TODO - Load this automatically somehow
 		loadPlugin("CamelotFBXImporter"); // TODO - Load this automatically somehow
 		loadPlugin("CamelotFBXImporter"); // TODO - Load this automatically somehow
+		loadPlugin("CamelotFontImporter"); // TODO - Load this automatically somehow
 
 
 		loadPlugin("CamelotOISInput"); // TODO - Load this automatically somehow
 		loadPlugin("CamelotOISInput"); // TODO - Load this automatically somehow
 	}
 	}

+ 5 - 0
CamelotFontImporter/Include/CmFontImporter.h

@@ -33,6 +33,11 @@ namespace CamelotEngine
 
 
 		/** Inherited from SpecificImporter */
 		/** Inherited from SpecificImporter */
 		virtual BaseResourceHandle import(const String& filePath, ConstImportOptionsPtr importOptions);
 		virtual BaseResourceHandle import(const String& filePath, ConstImportOptionsPtr importOptions);
+
+		/**
+		 * @copydoc SpecificImporter::createImportOptions().
+		 */
+		virtual ImportOptionsPtr createImportOptions() const;
 	private:
 	private:
 		vector<String>::type mExtensions;
 		vector<String>::type mExtensions;
 	};
 	};

+ 27 - 7
CamelotFontImporter/Source/CmFontImporter.cpp

@@ -3,6 +3,7 @@
 #include "CmPixelData.h"
 #include "CmPixelData.h"
 #include "CmTexture.h"
 #include "CmTexture.h"
 #include "CmResources.h"
 #include "CmResources.h"
+#include "CmDebug.h"
 
 
 #include <ft2build.h>
 #include <ft2build.h>
 #include <freetype/freetype.h>
 #include <freetype/freetype.h>
@@ -36,6 +37,11 @@ namespace CamelotEngine
 		return false;
 		return false;
 	}
 	}
 
 
+	ImportOptionsPtr FontImporter::createImportOptions() const
+	{
+		return ImportOptionsPtr(new FontImportOptions());
+	}
+
 	BaseResourceHandle FontImporter::import(const String& filePath, ConstImportOptionsPtr importOptions)
 	BaseResourceHandle FontImporter::import(const String& filePath, ConstImportOptionsPtr importOptions)
 	{
 	{
 		const FontImportOptions* gpuProgImportOptions = static_cast<const FontImportOptions*>(importOptions.get());
 		const FontImportOptions* gpuProgImportOptions = static_cast<const FontImportOptions*>(importOptions.get());
@@ -81,7 +87,12 @@ namespace CamelotEngine
 		INT32 maxHeight = 0;
 		INT32 maxHeight = 0;
 		for(UINT32 i = 33; i < 166; i++)
 		for(UINT32 i = 33; i < 166; i++)
 		{
 		{
-			FT_Load_Char(face, i, FT_LOAD_RENDER);
+			error = FT_Load_Char(face, i, FT_LOAD_RENDER);
+
+			if(error)
+			{
+				CM_EXCEPT(InternalErrorException, "Failed to load a character");
+			}
 
 
 			FT_GlyphSlot slot = face->glyph;
 			FT_GlyphSlot slot = face->glyph;
 
 
@@ -95,7 +106,8 @@ namespace CamelotEngine
 		UINT32 texWidth = 1024;
 		UINT32 texWidth = 1024;
 		UINT32 texHeight = 1024;
 		UINT32 texHeight = 1024;
 
 
-		UINT8* pixelBuffer = new UINT8[texWidth * texHeight];
+		UINT8* pixelBuffer = new UINT8[texWidth * texHeight * 2];
+		memset(pixelBuffer, 0, texWidth * texHeight * 2);
 		PixelData pixelData(texWidth, texHeight, 1, PF_R8G8, pixelBuffer, true);
 		PixelData pixelData(texWidth, texHeight, 1, PF_R8G8, pixelBuffer, true);
 
 
 		UINT32 curX = 0;
 		UINT32 curX = 0;
@@ -111,10 +123,15 @@ namespace CamelotEngine
 					CM_EXCEPT(InternalErrorException, "Texture to small to fit all glyphs.");
 					CM_EXCEPT(InternalErrorException, "Texture to small to fit all glyphs.");
 			}
 			}
 
 
-			FT_Load_Char(face, i, FT_LOAD_RENDER);
+			error = FT_Load_Char(face, i, FT_LOAD_RENDER);
+			if(error)
+			{
+				CM_EXCEPT(InternalErrorException, "Failed to load a character");
+			}
+
 			FT_GlyphSlot slot = face->glyph;
 			FT_GlyphSlot slot = face->glyph;
 
 
-			if(slot->bitmap.buffer == nullptr)
+			if(slot->bitmap.buffer == nullptr && slot->bitmap.rows > 0 && slot->bitmap.width > 0)
 			{
 			{
 				CM_EXCEPT(InternalErrorException, "Failed to render glyph bitmap");
 				CM_EXCEPT(InternalErrorException, "Failed to render glyph bitmap");
 			}
 			}
@@ -136,10 +153,13 @@ namespace CamelotEngine
 			curX += maxWidth;
 			curX += maxWidth;
 		}
 		}
 
 
-		TextureHandle newTex = Texture::create(TEX_TYPE_2D, texWidth, texHeight, 0, PF_R8G8);
-		newTex->setRawPixels(pixelData);
+		gDebug().writeAsBMP(pixelData, "C:\\FontTex.bmp");
+
+		//TextureHandle newTex = Texture::create(TEX_TYPE_2D, texWidth, texHeight, 0, PF_R8G8);
+		//newTex->waitUntilInitialized();
+		//newTex->setRawPixels(pixelData);
 
 
-		Resources::instance().create(newTex, "C:\\FontTex.tex", true);
+		//Resources::instance().create(newTex, "C:\\FontTex.tex", true);
 
 
 		/************************************************************************/
 		/************************************************************************/
 		/* 								END DEBUG	                     		*/
 		/* 								END DEBUG	                     		*/

+ 2 - 0
CamelotUtility/CamelotUtility.vcxproj

@@ -160,6 +160,7 @@
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemGroup>
   <ItemGroup>
+    <ClCompile Include="Source\CmBitmapWriter.cpp" />
     <ClCompile Include="Source\CmInt2.cpp" />
     <ClCompile Include="Source\CmInt2.cpp" />
     <ClCompile Include="Source\CmManagedDataBlock.cpp" />
     <ClCompile Include="Source\CmManagedDataBlock.cpp" />
     <ClCompile Include="Source\CmPixelData.cpp" />
     <ClCompile Include="Source\CmPixelData.cpp" />
@@ -168,6 +169,7 @@
     <ClCompile Include="Source\Win32\CmTimer.cpp" />
     <ClCompile Include="Source\Win32\CmTimer.cpp" />
     <ClInclude Include="Include\CmAsyncOp.h" />
     <ClInclude Include="Include\CmAsyncOp.h" />
     <ClInclude Include="Include\CmBinarySerializer.h" />
     <ClInclude Include="Include\CmBinarySerializer.h" />
+    <ClInclude Include="Include\CmBitmapWriter.h" />
     <ClInclude Include="Include\CmBitwise.h" />
     <ClInclude Include="Include\CmBitwise.h" />
     <ClInclude Include="Include\CmBox.h" />
     <ClInclude Include="Include\CmBox.h" />
     <ClInclude Include="Include\CmColor.h" />
     <ClInclude Include="Include\CmColor.h" />

+ 12 - 0
CamelotUtility/CamelotUtility.vcxproj.filters

@@ -40,6 +40,12 @@
     <Filter Include="Header Files\RTTI">
     <Filter Include="Header Files\RTTI">
       <UniqueIdentifier>{e58e84a8-5f39-4bbd-874e-cfa000ebd881}</UniqueIdentifier>
       <UniqueIdentifier>{e58e84a8-5f39-4bbd-874e-cfa000ebd881}</UniqueIdentifier>
     </Filter>
     </Filter>
+    <Filter Include="Header Files\Debug">
+      <UniqueIdentifier>{cdfa7307-cdee-490f-8d31-d8be0d929c70}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\Debug">
+      <UniqueIdentifier>{1f4518ad-c827-49dc-8e69-f99a37c82871}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="Include\CmTypes.h">
     <ClInclude Include="Include\CmTypes.h">
@@ -210,6 +216,9 @@
     <ClInclude Include="Include\CmRTTIPrerequisites.h">
     <ClInclude Include="Include\CmRTTIPrerequisites.h">
       <Filter>Header Files</Filter>
       <Filter>Header Files</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="Include\CmBitmapWriter.h">
+      <Filter>Header Files\Debug</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Include\CmAxisAlignedBox.cpp">
     <ClCompile Include="Include\CmAxisAlignedBox.cpp">
@@ -305,5 +314,8 @@
     <ClCompile Include="Source\CmManagedDataBlock.cpp">
     <ClCompile Include="Source\CmManagedDataBlock.cpp">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="Source\CmBitmapWriter.cpp">
+      <Filter>Source Files\Debug</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 13 - 0
CamelotUtility/Include/CmBitmapWriter.h

@@ -0,0 +1,13 @@
+#pragma once
+
+#include "CmPrerequisitesUtil.h"
+
+namespace CamelotEngine
+{
+	class CM_UTILITY_EXPORT BitmapWriter
+	{
+	public:
+		static void rawPixelsToBMP(const UINT8* input, UINT8* output, UINT32 width, UINT32 height, UINT32 bytesPerPixel);
+		static UINT32 getBMPSize(UINT32 width, UINT32 height, UINT32 bytesPerPixel);
+	};
+}

+ 2 - 0
CamelotUtility/Include/CmDebug.h

@@ -18,6 +18,8 @@ namespace CamelotEngine
 
 
 		Log& getLog() { return mLog; }
 		Log& getLog() { return mLog; }
 
 
+		void writeAsBMP(const PixelData& pixelData, const String& filePath, bool overwrite = true) const;
+
 	private:
 	private:
 		Log mLog;
 		Log mLog;
 	};
 	};

+ 2 - 0
CamelotUtility/Include/CmPixelData.h

@@ -208,6 +208,8 @@ namespace CamelotEngine
     	{
     	{
     		setConsecutive();
     		setConsecutive();
     	}
     	}
+
+		PixelData(const PixelData& copy);
     	
     	
         /// The data pointer 
         /// The data pointer 
         void *data;
         void *data;

+ 138 - 0
CamelotUtility/Source/CmBitmapWriter.cpp

@@ -0,0 +1,138 @@
+#include "CmBitmapWriter.h"
+#include "CmPixelData.h"
+
+namespace CamelotEngine
+{
+#pragma pack(push, 2) // Align to 2byte boundary so we don't get extra 2 bytes for this struct
+
+	struct BMP_HEADER
+	{
+		UINT16 BM;
+		UINT32 size_of_file;
+		UINT32 reserve;
+		UINT32 offset_of_pixel_data;
+		UINT32 size_of_header;
+		UINT32 width;
+		UINT32 hight;
+		UINT16 num_of_color_plane;
+		UINT16 num_of_bit_per_pix;
+		UINT32 compression;
+		UINT32 size_of_pix_data;
+		UINT32 h_resolution;
+		UINT32 v_resolution;
+		UINT32 num_of_color_in_palette;
+		UINT32 important_colors;
+
+	};
+
+#pragma pack(pop)
+
+	void BitmapWriter::rawPixelsToBMP(const UINT8* input, UINT8* output, UINT32 width, UINT32 height, UINT32 bytesPerPixel)
+	{
+		UINT16 bmpBytesPerPixel = 3;
+		if(bytesPerPixel >= 4)
+			bmpBytesPerPixel = 4;
+
+		UINT32 padding = (width * bmpBytesPerPixel) % 4;
+		if(padding != 0)
+			padding = 4 - padding;
+
+		UINT32 rowPitch = (width * bmpBytesPerPixel) + padding;
+		UINT32 dataSize = height * rowPitch;
+
+		BMP_HEADER header;
+		header.BM = 0x4d42;
+		header.size_of_file =  sizeof(header) + dataSize;
+		header.reserve = 0000; 
+		header.offset_of_pixel_data = 54;
+		header.size_of_header = 40;
+		header.width = width;
+		header.hight = height;
+		header.num_of_color_plane = 1;
+		header.num_of_bit_per_pix = bmpBytesPerPixel * 8;
+		header.compression = 0;
+		header.size_of_pix_data = dataSize;
+		header.h_resolution = 2835;
+		header.v_resolution = 2835;
+		header.num_of_color_in_palette = 0;
+		header.important_colors = 0;
+
+		// Write header
+		memcpy(output, &header, sizeof(header));
+		output += sizeof(header);
+
+		// Write bytes
+		UINT32 widthBytes = width * bytesPerPixel;
+		 
+		// BPP matches so we can just copy directly
+		if(bmpBytesPerPixel == bytesPerPixel)
+		{
+			for(INT32 y = height - 1; y >= 0 ; y--)
+			{
+				UINT8* outputPtr = output + y * rowPitch;
+
+				memcpy(outputPtr, input, widthBytes);
+				memset(outputPtr + widthBytes, 0, padding);
+
+				input += widthBytes;
+			}
+		}
+		else if(bmpBytesPerPixel < bytesPerPixel) // More bytes in source than supported in BMP, just truncate excess data
+		{
+			for(INT32 y = height - 1; y >= 0 ; y--)
+			{
+				UINT8* outputPtr = output + y * rowPitch;
+
+				for(UINT32 x = 0; x < width; x++)
+				{
+					memcpy(outputPtr, input, bmpBytesPerPixel);
+					outputPtr += bmpBytesPerPixel;
+					input += bytesPerPixel;
+				}
+
+				memset(outputPtr, 0, padding);
+			}
+		}
+		else // More bytes in BMP than in source (BMP must be 24bit minimum)
+		{
+			for(INT32 y = height - 1; y >= 0 ; y--)
+			{
+				UINT8* outputPtr = output + y * rowPitch;
+
+				for(UINT32 x = 0; x < width; x++)
+				{
+					memcpy(outputPtr, input, bytesPerPixel);
+
+					// Fill the empty bytes with the last available byte from input
+					UINT32 remainingBytes = bmpBytesPerPixel - bytesPerPixel;
+					while(remainingBytes > 0)
+					{
+						memcpy(outputPtr + (bmpBytesPerPixel - remainingBytes), input, 1);
+						remainingBytes--;
+					}
+
+					outputPtr += bmpBytesPerPixel;
+					input += bytesPerPixel;
+				}
+
+				memset(outputPtr, 0, padding);
+			}
+		}
+	}
+
+	UINT32 BitmapWriter::getBMPSize(UINT32 width, UINT32 height, UINT32 bytesPerPixel)
+	{
+		UINT16 bmpBytesPerPixel = 3;
+		if(bytesPerPixel >= 4)
+			bmpBytesPerPixel = 4;
+
+		UINT32 padding = (width * bmpBytesPerPixel) % 4;
+		if(padding != 0)
+			padding = 4 - padding;
+
+		UINT32 rowPitch = (width * bmpBytesPerPixel) + padding;
+		UINT32 dataSize = height * rowPitch;
+
+		return sizeof(BMP_HEADER) + dataSize;
+	}
+}

+ 31 - 0
CamelotUtility/Source/CmDebug.cpp

@@ -1,5 +1,11 @@
 #include "CmDebug.h"
 #include "CmDebug.h"
 #include "CmLog.h"
 #include "CmLog.h"
+#include "CmException.h"
+#include "CmBitmapWriter.h"
+#include "CmFileSystem.h"
+#include "CmDataStream.h"
+#include "CmPixelData.h"
+#include "CmPixelUtil.h"
 
 
 namespace CamelotEngine
 namespace CamelotEngine
 {
 {
@@ -28,6 +34,31 @@ namespace CamelotEngine
 		mLog.logMsg(msg, channel);
 		mLog.logMsg(msg, channel);
 	}
 	}
 
 
+	void Debug::writeAsBMP(const PixelData& pixelData, const String& filePath, bool overwrite) const
+	{
+		if(FileSystem::fileExists(filePath))
+		{
+			if(overwrite)
+				FileSystem::remove(filePath);
+			else
+				CM_EXCEPT(FileNotFoundException, "File already exists at specified location: " + filePath);
+		}
+
+		DataStreamPtr ds = FileSystem::create(filePath);
+
+		UINT32 bytesPerPixel = PixelUtil::getNumElemBytes(pixelData.format);
+
+		UINT32 bmpDataSize = BitmapWriter::getBMPSize(pixelData.getWidth(), pixelData.getHeight(), bytesPerPixel);
+		UINT8* bmpBuffer = new UINT8[bmpDataSize];
+
+		BitmapWriter::rawPixelsToBMP((UINT8*)pixelData.data, bmpBuffer, pixelData.getWidth(), pixelData.getHeight(), bytesPerPixel);
+
+		ds->write(bmpBuffer, bmpDataSize);
+		ds->close();
+
+		delete [] bmpBuffer;
+	}
+
 	CM_UTILITY_EXPORT Debug& gDebug()
 	CM_UTILITY_EXPORT Debug& gDebug()
 	{
 	{
 		static Debug debug;
 		static Debug debug;

+ 10 - 0
CamelotUtility/Source/CmPixelData.cpp

@@ -3,6 +3,16 @@
 
 
 namespace CamelotEngine
 namespace CamelotEngine
 {
 {
+	PixelData::PixelData(const PixelData& copy)
+		:Box(copy), IReflectable(copy)
+	{
+		data = copy.data;
+		format = copy.format;
+		rowPitch = copy.rowPitch;
+		slicePitch = copy.slicePitch;
+		ownsData = false;
+	}
+
 	/************************************************************************/
 	/************************************************************************/
 	/* 								SERIALIZATION                      		*/
 	/* 								SERIALIZATION                      		*/
 	/************************************************************************/
 	/************************************************************************/

+ 4 - 0
TODO.txt

@@ -19,6 +19,9 @@ Figure out how to store texture references in a font?
 	 - In inspector they can be expanded as children of the main resource, but cannot be directly modified?
 	 - In inspector they can be expanded as children of the main resource, but cannot be directly modified?
 	 - Deleting the main resource deletes the children too
 	 - Deleting the main resource deletes the children too
 
 
+Add Debug.SaveToBMP(PixelData)
+Add TextureAtlas.Generate() -> accepts coords and returns new coords. Doesn't deal with actual textures
+
  Names: TextMesh & SpriteMesh instead of 2DText and 2DSprite!
  Names: TextMesh & SpriteMesh instead of 2DText and 2DSprite!
 
 
 -----------------------IMMEDIATE TODO---------------------------------------------------------------
 -----------------------IMMEDIATE TODO---------------------------------------------------------------
@@ -189,6 +192,7 @@ Low priority TODO:
   - Arrays of objects aren't supported in HLSL or GLSL because of limited GLSL introspection
   - Arrays of objects aren't supported in HLSL or GLSL because of limited GLSL introspection
     - I might need to add an exception thrown if user tries to use them
     - I might need to add an exception thrown if user tries to use them
   - Structs aren't supported in GLSL for introspection reasons
   - Structs aren't supported in GLSL for introspection reasons
+ - Go through pixel formats and removed unused ones: L8, L16, A4L4, and many others
 
 
 Optional TODO:
 Optional TODO:
  - Add precompiled headers to all projects
  - Add precompiled headers to all projects