Browse Source

Optimized triangulation algorithm. Must more faster now.
Added loader for TERRAGEN terrains (*.ter).
ScenePreprocessor generates a default material if none is given.
AC: file extension bug
Added terragen test files.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@279 67173fc5-114c-0410-ac8e-9d2fd5bffc1f

aramis_acg 16 years ago
parent
commit
39bfb7608b

+ 1 - 4
code/ACLoader.cpp

@@ -129,10 +129,7 @@ bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) cons
 	for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
 		*it = tolower( *it);
 
-	if( extension == ".ac" || extension == "ac")
-		return true;
-
-	return false;
+	return( extension == ".ac3d" || extension == ".ac");
 }
 
 // ------------------------------------------------------------------------------------------------

+ 8 - 6
code/Importer.cpp

@@ -129,7 +129,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_BUILD_NO_COLLADA_IMPORTER
 #	include "ColladaLoader.h"
 #endif
-
+#ifndef AI_BUILD_NO_TERRAGEN_IMPORTER
+#	include "TerragenLoader.h"
+#endif
 
 // PostProcess-Steps
 #ifndef AI_BUILD_NO_CALCTANGENTS_PROCESS
@@ -193,11 +195,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #	include "TextureTransform.h"
 #endif
 
-
 using namespace Assimp;
 
-
-
 // ------------------------------------------------------------------------------------------------
 // Constructor. 
 Importer::Importer() :
@@ -205,12 +204,12 @@ Importer::Importer() :
 	mScene(NULL),
 	mErrorString("")	
 {
-	// allocate a default IO handler
+	// Allocate a default IO handler
 	mIOHandler = new DefaultIOSystem;
 	mIsDefaultHandler = true; 
 	bExtraVerbose     = false; // disable extra verbose mode by default
 
-	// add an instance of each worker class here
+	// Add an instance of each worker class here
 	// the order doesn't really care, however file formats that are
 	// used more frequently than others should be at the beginning.
 	mImporter.reserve(25);
@@ -290,6 +289,9 @@ Importer::Importer() :
 #if (!defined AI_BUILD_NO_COLLADA_IMPORTER)
 	mImporter.push_back( new ColladaLoader());
 #endif
+#if (!defined AI_BUILD_NO_TERRAGEN_IMPORTER)
+	mImporter.push_back( new TerragenImporter());
+#endif
 
 	// Add an instance of each post processing step here in the order 
 	// of sequence it is executed. steps that are added here are not validated -

+ 25 - 1
code/ScenePreprocessor.cpp

@@ -52,7 +52,6 @@ void ScenePreprocessor::ProcessScene (aiScene* _scene)
 	for (unsigned int i = 0; i < scene->mNumMeshes;++i)
 		ProcessMesh(scene->mMeshes[i]);
 
-	// - nothing to do for materials for the moment
 	// - nothing to do for nodes for the moment
 	// - nothing to do for textures for the moment
 	// - nothing to do for lights for the moment
@@ -61,6 +60,31 @@ void ScenePreprocessor::ProcessScene (aiScene* _scene)
 	// Process all animations
 	for (unsigned int i = 0; i < scene->mNumAnimations;++i)
 		ProcessAnimation(scene->mAnimations[i]);
+
+	// Generate a default material if none was specified
+	if (!scene->mNumMaterials && scene->mNumMeshes)
+	{
+		scene->mMaterials      = new aiMaterial*[scene->mNumMaterials = 1];
+		MaterialHelper* helper = new MaterialHelper();
+		scene->mMaterials[0]   = helper;
+
+		// gray
+		aiColor3D clr(0.6f,0.6f,0.6f);
+		helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+
+		// add a small ambient color value
+		clr = aiColor3D(0.05f,0.05f,0.05f);
+		helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
+
+		// setup the default name
+		aiString name(AI_DEFAULT_MATERIAL_NAME);
+		helper->AddProperty(&name,AI_MATKEY_NAME);
+
+		for (unsigned int i = 0; i < scene->mNumMeshes;++i)
+			scene->mMeshes[i]->mMaterialIndex = 0;
+
+		DefaultLogger::get()->debug("ScenePreprocessor: Added default material \'" AI_DEFAULT_MATERIAL_NAME  "\'");
+	}
 }
 
 // ---------------------------------------------------------------------------

+ 222 - 0
code/TerragenLoader.cpp

@@ -0,0 +1,222 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the Terragen importer class */
+
+#include "AssimpPCH.h"
+
+#ifndef AI_BUILD_NO_TERRAGEN_IMPORTER
+#include "TerragenLoader.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+TerragenImporter::TerragenImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well 
+TerragenImporter::~TerragenImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file. 
+bool TerragenImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
+{
+	// simple check of file extension is enough for the moment
+	std::string::size_type pos = pFile.find_last_of('.');
+
+	// no file extension - can't read
+	if( pos == std::string::npos)return false;
+	std::string extension = pFile.substr( pos);
+
+	for( std::string::iterator it = extension.begin(); it != extension.end(); ++it)
+		*it = tolower( *it);
+
+	return extension == ".ter";
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a string of all file extensions supported
+void TerragenImporter::GetExtensionList(std::string& append)
+{
+	append.append("*.ter;");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure. 
+void TerragenImporter::InternReadFile( const std::string& pFile, 
+	aiScene* pScene, IOSystem* pIOHandler)
+{
+	IOStream* file = pIOHandler->Open( pFile, "rb");
+
+	// Check whether we can read from the file
+	if( file == NULL)
+		throw new ImportErrorException( "Failed to open TERRAGEN TERRAIN file " + pFile + ".");
+
+	// Construct a stream reader to read all data in the correct endianess
+	StreamReaderLE reader(file);
+	if(reader.GetRemainingSize() < 16)
+		throw new ImportErrorException( "TER: file is too small" );
+
+	// Check for the existence of the two magic strings 'TERRAGEN' and 'TERRAIN '
+	if (::strncmp((const char*)reader.GetPtr(),AI_TERR_BASE_STRING,8))
+		throw new ImportErrorException( "TER: Magic string \'TERRAGEN\' not found" );
+
+	if (::strncmp((const char*)reader.GetPtr()+8,AI_TERR_TERRAIN_STRING,8))
+		throw new ImportErrorException( "TER: Magic string \'TERRAIN\' not found" );
+
+	unsigned int x = 0,y = 0,mode = 0;
+	float rad  = 6370.f;
+
+
+	aiNode* root = pScene->mRootNode = new aiNode();
+	root->mName.Set("<TERRAGEN.TERRAIN>");
+
+	// Default scaling is 30
+	root->mTransformation.a1 = root->mTransformation.b2 = root->mTransformation.c3 = 30.f;
+
+	// Now read all chunks until we're finished or an EOF marker is encountered
+	reader.IncPtr(16);
+	while (reader.GetRemainingSize() >= 4)	
+	{
+		const char* head = (const char*)reader.GetPtr();
+		reader.IncPtr(4);
+
+		// EOF, break in every case
+		if (!::strncmp(head,AI_TERR_EOF_STRING,4))
+			break;
+
+		// Number of x-data points
+		if (!::strncmp(head,AI_TERR_CHUNK_XPTS,4))
+		{
+			x = (uint16_t)reader.GetI2();
+		}
+		// Number of y-data points
+		else if (!::strncmp(head,AI_TERR_CHUNK_YPTS,4))
+		{
+			y = (uint16_t)reader.GetI2();
+		}
+		// Squared terrains width-1. 
+		else if (!::strncmp(head,AI_TERR_CHUNK_SIZE,4))
+		{
+			x = y = (uint16_t)reader.GetI2()+1;
+		}
+		// terrain scaling
+		else if (!::strncmp(head,AI_TERR_CHUNK_SCAL,4))
+		{
+			root->mTransformation.a1 = reader.GetF4();
+			root->mTransformation.b2 = reader.GetF4();
+			root->mTransformation.c3 = reader.GetF4();
+		}
+		// mapping == 1: earth radius
+		else if (!::strncmp(head,AI_TERR_CHUNK_CRAD,4))
+		{
+			rad = reader.GetF4();
+		}
+		// mapping mode
+		else if (!::strncmp(head,AI_TERR_CHUNK_CRVM,4))
+		{
+			mode = reader.GetI1();
+			if (0 != mode)
+				DefaultLogger::get()->error("TER: Unsupported mapping mode, a flat terrain is returned");
+		}
+		// actual terrain data
+		else if (!::strncmp(head,AI_TERR_CHUNK_ALTW,4))
+		{
+			float hscale  = (float)reader.GetI2()  / 65536;
+			float bheight = (float)reader.GetI2();
+
+			if (!hscale)hscale = 1;
+
+			// Ensure we have enough data
+			if (reader.GetRemainingSize() < x*y*2)
+				throw new ImportErrorException("TER: ALTW chunk is too small");
+
+			if (x <= 1 || y <= 1)
+				throw new ImportErrorException("TER: Invalid terrain size");
+
+			// Allocate the output mesh
+			pScene->mMeshes = new aiMesh*[pScene->mNumMeshes = 1];
+			aiMesh* m = pScene->mMeshes[0] = new aiMesh();
+
+			// We return quads
+			aiFace* f = m->mFaces = new aiFace[m->mNumFaces = (x-1)*(y-1)];
+			aiVector3D* pv = m->mVertices = new aiVector3D[m->mNumVertices = m->mNumFaces*4];
+
+			const int16_t* data = (const int16_t*)reader.GetPtr();
+
+			for (unsigned int yy = 0, t = 0; yy < y-1;++yy)	{
+				for (unsigned int xx = 0; xx < x-1;++xx,++f)	{
+
+					// make verts
+					const float fy = (float)yy, fx = (float)xx;
+					register unsigned tmp,tmp2;
+					*pv++ = aiVector3D(fx,fy,    (float)data[(tmp2=x*yy)    + xx] * hscale + bheight);
+					*pv++ = aiVector3D(fx,fy+1,  (float)data[(tmp=x*(yy+1)) + xx] * hscale + bheight);
+					*pv++ = aiVector3D(fx+1,fy+1,(float)data[tmp  + xx+1]         * hscale + bheight);
+					*pv++ = aiVector3D(fx+1,fy,  (float)data[tmp2 + xx+1]         * hscale + bheight);
+
+					// make indices
+					f->mIndices = new unsigned int[f->mNumIndices = 4];
+					for (unsigned int i = 0; i < 4;++i)
+						f->mIndices[i] = t++;
+				}
+			}
+
+			// Add the mesh to the root node
+			root->mMeshes = new unsigned int[root->mNumMeshes = 1];
+			root->mMeshes[0] = 0;
+		}
+
+		// Get to the next chunk (4 byte aligned)
+		unsigned dtt;
+		if ((dtt = reader.GetCurrentPos() & 0x3))
+			reader.IncPtr(4-dtt);
+	}
+
+	// Check whether we have a mesh now
+	if (pScene->mNumMeshes != 1)
+		throw new ImportErrorException("TER: Unable to load terrain");
+}
+
+#endif // !! AI_BUILD_NO_TERRAGEN_IMPORTER

+ 109 - 0
code/TerragenLoader.h

@@ -0,0 +1,109 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Declaration of the .ter importer class. */
+#ifndef INCLUDED_AI_TERRAGEN_TERRAIN_LOADER_H
+#define INCLUDED_AI_TERRAGEN_TERRAIN_LOADER_H
+
+#include "BaseImporter.h"
+namespace Assimp	{
+
+// Magic strings
+#define AI_TERR_BASE_STRING         "TERRAGEN"
+#define AI_TERR_TERRAIN_STRING      "TERRAIN "
+#define AI_TERR_EOF_STRING          "EOF "
+
+// Chunka
+#define AI_TERR_CHUNK_XPTS          "XPTS"
+#define AI_TERR_CHUNK_YPTS          "YPTS"
+#define AI_TERR_CHUNK_SIZE          "SIZE"
+#define AI_TERR_CHUNK_SCAL          "SCAL"
+#define AI_TERR_CHUNK_CRAD          "CRAD"
+#define AI_TERR_CHUNK_CRVM          "CRVM"
+#define AI_TERR_CHUNK_ALTW          "ALTW"
+
+// ---------------------------------------------------------------------------
+/** @brief Importer class to load Terragen (0.9) terrain files.
+ *
+ *  The loader is basing on the information found here:
+ *  http://www.planetside.co.uk/terragen/dev/tgterrain.html#chunks
+*/
+class TerragenImporter : public BaseImporter
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	TerragenImporter();
+
+	/** Destructor, private as well */
+	~TerragenImporter();
+
+public:
+
+	// -------------------------------------------------------------------
+	/** @brief Returns whether we can handle the format of the given file
+	 *
+	 *  See BaseImporter::CanRead() for details.	
+	 **/
+	bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
+
+protected:
+
+	// -------------------------------------------------------------------
+	/** @brief Called by Importer::GetExtensionList() 
+	 *
+	 * See BaseImporter::GetExtensionList() for details
+	 */
+	void GetExtensionList(std::string& append);
+
+	// -------------------------------------------------------------------
+	/** @brief Imports the given file into the given scene structure. 
+	 *
+	 * See BaseImporter::InternReadFile() for details
+	 */
+	void InternReadFile( const std::string& pFile, aiScene* pScene, 
+		IOSystem* pIOHandler);
+
+}; //! class TerragenImporter
+
+} // end of namespace Assimp
+
+#endif // AI_AC3DIMPORTER_H_INC

+ 35 - 25
code/TriangulateProcess.cpp

@@ -81,8 +81,8 @@ void TriangulateProcess::Execute( aiScene* pScene)
 		if(	TriangulateMesh( pScene->mMeshes[a]))
 			bHas = true;
 	}
-	if (bHas)DefaultLogger::get()->info("TriangulateProcess finished. Found polygons to triangulate");
-	else DefaultLogger::get()->debug("TriangulateProcess finished. There was nothing to do.");
+	if (bHas)DefaultLogger::get()->info ("TriangulateProcess finished. All polygons have been triangulated");
+	else     DefaultLogger::get()->debug("TriangulateProcess finished. There was nothing to do.");
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -111,8 +111,21 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 	pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
 	pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON;
 
-	std::vector<aiFace> newFaces;
-	newFaces.reserve( pMesh->mNumFaces*2);
+	// Find out how many output faces we'll have
+	unsigned int numOut = 0;
+	for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+	{
+		aiFace& face = pMesh->mFaces[a];
+		if( face.mNumIndices <= 3)
+			numOut++;
+
+		else numOut += face.mNumIndices-2;
+	}
+
+	// Just another check whether aiMesh::mPrimitiveTypes is correct
+	assert(numOut != pMesh->mNumFaces);
+	
+	aiFace* out = new aiFace[numOut], *curOut = out;
 	for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
 	{
 		aiFace& face = pMesh->mFaces[a];
@@ -120,41 +133,38 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 		// if it's a simple primitive, just copy it
 		if( face.mNumIndices <= 3)
 		{
-			newFaces.push_back( aiFace());
-			aiFace& nface = newFaces.back();
+			aiFace& nface = *curOut++;
 			nface.mNumIndices = face.mNumIndices;
-			nface.mIndices = face.mIndices;
-			face.mIndices = NULL;
+			nface.mIndices    = face.mIndices;
 		} 
 		else
 		{
-			assert( face.mNumIndices > 3);
-			for( unsigned int b = 0; b < face.mNumIndices - 2; b++)
+			for( unsigned int b = 0, end = face.mNumIndices - 2; b < end; b++)
 			{
-				newFaces.push_back( aiFace());
-				aiFace& nface = newFaces.back();
+				aiFace& nface = *curOut++;
 				nface.mNumIndices = 3;
-				nface.mIndices = new unsigned int[3];
-				nface.mIndices[0] = face.mIndices[0];
+				
+				// Reuse the buffer for the very last element to save another allocation
+				if (b == end-1)
+					nface.mIndices = face.mIndices;
+				else
+				{
+					nface.mIndices = new unsigned int[3];
+					nface.mIndices[0] = face.mIndices[0];
+				}
+
 				nface.mIndices[1] = face.mIndices[b+1];
 				nface.mIndices[2] = face.mIndices[b+2];
 			}
 		}
+		face.mIndices = NULL;
 	}
 
 	// kill the old faces
 	delete [] pMesh->mFaces;
 
-	// and insert our newly generated faces
-	pMesh->mNumFaces = (unsigned int)newFaces.size();
-	pMesh->mFaces = new aiFace[pMesh->mNumFaces];
-	for( unsigned int a = 0; a < newFaces.size(); a++)
-	{
-		// operator= would copy the whole array which is definitely not necessary
-		pMesh->mFaces[a].mNumIndices = newFaces[a].mNumIndices;
-		pMesh->mFaces[a].mIndices    = newFaces[a].mIndices;
-		newFaces[a].mIndices = NULL;
-	}
-
+	// ... and store the new ones
+	pMesh->mFaces    = out;
+	pMesh->mNumFaces = numOut;
 	return true;
 }

+ 2 - 0
code/ValidateDataStructure.cpp

@@ -291,6 +291,8 @@ void ValidateDSProcess::Execute( aiScene* pScene)
 	}
 	else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE))
 	{
+		// NOTE: ScenePreprocessor generates a default material if none is there
+		// (and at least one mesh is found). So this should never be triggered ...
 		ReportError("aiScene::mNumMaterials is 0. At least one material must be there");
 	}
 	else if (pScene->mMaterials)

+ 2 - 1
code/makefile

@@ -76,7 +76,8 @@ SOURCES = AssimpPCH.cpp \
 	TargetAnimation.cpp \
 	ComputeUVMappingProcess.cpp  \
 	ColladaLoader.cpp \
-	ColladaParser.cpp 
+	ColladaParser.cpp \
+	TerragenLoader.cpp 
 
 OBJECTS = $(SOURCES:.cpp=.o)
 

+ 2 - 1
code/makefile.mingw

@@ -76,7 +76,8 @@ SOURCES = AssimpPCH.cpp \
 	TargetAnimation.cpp \
 	ComputeUVMappingProcess.cpp \
 	ColladaLoader.cpp \
-	ColladaParser.cpp 
+	ColladaParser.cpp \
+	TerragenLoader.cpp 
 
 OBJECTS = $(SOURCES:.cpp=.o)
 

BIN
test/models/TER/RealisticTerrain.ter


BIN
test/models/TER/RealisticTerrain_Large.ter


+ 12 - 0
workspaces/vc8/assimp.vcproj

@@ -1751,6 +1751,18 @@
 						>
 					</File>
 				</Filter>
+				<Filter
+					Name="TER"
+					>
+					<File
+						RelativePath="..\..\code\TerragenLoader.cpp"
+						>
+					</File>
+					<File
+						RelativePath="..\..\code\TerragenLoader.h"
+						>
+					</File>
+				</Filter>
 			</Filter>
 			<Filter
 				Name="PostProcess"