Browse Source

Added vertex color support to the DXF loader, fixed a minor bugs. Coordinate system issue still unsolved.
Modified GenVertexNormals to take always the cross product of the first and the last normal of a face as face normal.
Material implementation for LWO - seems to work, but there are some issues with highly complex models.
Added some LWO test models
Cleaned up the ./test dir - converted some BMPs to JPG to save space,
Finished the matrial property list in jAssimp, some other changes, too. The Java part of the API should be working now.

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

aramis_acg 17 years ago
parent
commit
3e46a0860e

+ 11 - 9
CREDITS

@@ -7,21 +7,29 @@ The following is the list of all constributors.
 Thanks for your help!
 
 - Alexander Gessler,
-3DS-, ASE-, DXF-, HMP-, MDL-, MD2-, MD3-, MD5-, MDC-, NFF-, PLY-, STL- and LWO-Loader, 
+3DS-, ASE-, DXF-, HMP-, MDL-, MD2-, MD3-, MD5-, MDC-, NFF-, PLY-, STL-, RAW-, OFF- and LWO-Loader, 
 Configuration-Interface, AssImp-Viewer (Win32), Website (Admin and Design), admin.
 
 -Thomas Schulze,
-X-Loader, Preprocessing framework.
+X-Loader, Preprocessing framework. Data structure & Interface design, documentation.
 
 -R.Schmidt,
 Linux build, eclipse support.
 
 - Kim Kulling:
-Obj-Loader, Logging, Scons-build enviroment.
+Obj-Loader, Logging, Scons-build environment.
 
 - Matthias Gubisch,
+Assimp.net
 Visual Studio 9 support, bugfixes.
 
+- Sebastian Hempel,
+PyAssimp
+Compile-Bugfixes for mingw, add enviroment for static library support in make.
+
+- Jonathan Pokrass
+supplied a bugfix concerning the scaling in the md3 loader.
+
 - Andrew Galante,
 submitted patches to make Assimp compile with GCC-4, a makefile and the xcode3 workspace.
 
@@ -31,11 +39,5 @@ tested Assimp under Windows Vista 64 Bit.
 - Marius Schröder
 allowed us to use many of his models for screenshots and testing.
 
-- Sebastian Hempel,
-Compile-Bugfixes for mingw, add enviroment for static library support in make.
-
-- Jonathan Pokrass
-supplied a bugfix concerning the scaling in the md3 loader.
-
 - Christian Schubert
 supplied various XFiles for testing purposes.

+ 66 - 5
code/DXFLoader.cpp

@@ -63,6 +63,31 @@ using namespace Assimp;
 #define AI_DXF_BINARY_IDENT_LEN (24)
 
 
+// color indices for DXF - 16 are supported
+static aiColor4D g_aclrDxfIndexColors[] =
+{
+	aiColor4D (0.6f, 0.6f, 0.6f, 1.0f),
+	aiColor4D (1.0f, 0.0f, 0.0f, 1.0f), // red
+	aiColor4D (0.0f, 1.0f, 0.0f, 1.0f), // green
+	aiColor4D (0.0f, 0.0f, 1.0f, 1.0f), // blue
+	aiColor4D (0.3f, 1.0f, 0.3f, 1.0f), // light green
+	aiColor4D (0.3f, 0.3f, 1.0f, 1.0f), // light blue
+	aiColor4D (1.0f, 0.3f, 0.3f, 1.0f), // light red
+	aiColor4D (1.0f, 0.0f, 1.0f, 1.0f), // pink
+	aiColor4D (1.0f, 0.6f, 0.0f, 1.0f), // orange
+	aiColor4D (0.6f, 0.3f, 0.0f, 1.0f), // dark orange
+	aiColor4D (1.0f, 1.0f, 0.0f, 1.0f), // yellow
+	aiColor4D (0.3f, 0.3f, 0.3f, 1.0f), // dark gray
+	aiColor4D (0.8f, 0.8f, 0.8f, 1.0f), // light gray
+	aiColor4D (0.0f, 00.f, 0.0f, 1.0f), // black
+	aiColor4D (1.0f, 1.0f, 1.0f, 1.0f), // white
+	aiColor4D (0.6f, 0.0f, 1.0f, 1.0f)  // violet
+};
+#define AI_DXF_NUM_INDEX_COLORS (sizeof(g_aclrDxfIndexColors)/sizeof(g_aclrDxfIndexColors[0]))
+
+// invalid/unassigned color value
+aiColor4D g_clrInvalid = aiColor4D(std::numeric_limits<float>::quiet_NaN(),0.f,0.f,1.f);
+
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 DXFImporter::DXFImporter()
@@ -157,7 +182,7 @@ void DXFImporter::InternReadFile( const std::string& pFile,
 		{
 			// ENTITIES and BLOCKS sections - skip the whole rest, no need to waste our time with them
 			if (!::strcmp(cursor,"ENTITIES") || !::strcmp(cursor,"BLOCKS"))
-				if (!ParseEntities())break;
+				if (!ParseEntities())break; else bRepeat = true;
 
 			// other sections - skip them to make sure there will be no name conflicts
 			else
@@ -200,6 +225,21 @@ void DXFImporter::InternReadFile( const std::string& pFile,
 		// generate the output mesh
 		aiMesh* pMesh = pScene->mMeshes[m++] = new aiMesh();
 		const std::vector<aiVector3D>& vPositions = (*it).vPositions;
+		const std::vector<aiColor4D>& vColors = (*it).vColors;
+
+		// check whether we need vertex colors here
+		aiColor4D* clrOut;
+		const aiColor4D* clr = NULL;
+		for (std::vector<aiColor4D>::const_iterator it2 = (*it).vColors.begin(), end2 = (*it).vColors.end();
+			 it2 != end2; ++it2)
+		{
+			if (std::numeric_limits<float>::quiet_NaN() != (*it2).r)
+			{
+				clrOut = pMesh->mColors[0] = new aiColor4D[vPositions.size()];
+				clr = &vColors[0];
+				break;
+			}
+		}
 
 		pMesh->mNumFaces = (unsigned int)vPositions.size() / 4u;
 		pMesh->mFaces = new aiFace[pMesh->mNumFaces];
@@ -222,6 +262,7 @@ void DXFImporter::InternReadFile( const std::string& pFile,
 			for (unsigned int a = 0; a < face.mNumIndices;++a)
 			{
 				*vpOut++ = vp[a];
+				if (clr)*clrOut++ = clr[a];
 				face.mIndices[a] = pMesh->mNumVertices++;
 			}
 			vp += 4;
@@ -333,6 +374,7 @@ bool DXFImporter::ParsePolyLine()
 	LayerInfo* out = NULL;
 
 	std::vector<aiVector3D> positions;
+	std::vector<aiColor4D>  colors;
 	std::vector<unsigned int> indices;
 	unsigned int flags = 0;
 
@@ -344,10 +386,14 @@ bool DXFImporter::ParsePolyLine()
 			{
 				if (!::strcmp(cursor,"VERTEX"))
 				{
-					aiVector3D v;
+					aiVector3D v;aiColor4D clr(g_clrInvalid);
 					unsigned int idx[4] = {0xffffffff,0xffffffff,0xffffffff,0xffffffff};
-					ParsePolyLineVertex(v, idx);
-					if (0xffffffff == idx[0])positions.push_back(v);
+					ParsePolyLineVertex(v, clr, idx);
+					if (0xffffffff == idx[0])
+					{
+						positions.push_back(v);
+						colors.push_back(clr);
+					}
 					else
 					{
 						// check whether we have a fourth coordinate
@@ -413,6 +459,10 @@ bool DXFImporter::ParsePolyLine()
 	// use a default layer if necessary
 	if (!out)SetDefaultLayer(out);
 
+	flags = (unsigned int)(out->vPositions.size()+indices.size());
+	out->vPositions.reserve(flags);
+	out->vColors.reserve(flags);
+
 	// generate unique vertices
 	for (std::vector<unsigned int>::const_iterator it = indices.begin(), end = indices.end();
 		it != end; ++it)
@@ -424,13 +474,14 @@ bool DXFImporter::ParsePolyLine()
 			idx = (unsigned int) positions.size();
 		}
 		out->vPositions.push_back(positions[idx-1]); // indices are one-based.
+		out->vColors.push_back(colors[idx-1]); // indices are one-based.
 	}
 
 	return ret;
 }
 
 // ------------------------------------------------------------------------------------------------
-bool DXFImporter::ParsePolyLineVertex(aiVector3D& out,unsigned int* outIdx)
+bool DXFImporter::ParsePolyLineVertex(aiVector3D& out,aiColor4D& clr, unsigned int* outIdx)
 {
 	bool ret = false;
 	while (GetNextToken())
@@ -455,6 +506,9 @@ bool DXFImporter::ParsePolyLineVertex(aiVector3D& out,unsigned int* outIdx)
 		case 72: outIdx[1] = strtol10(cursor);break;
 		case 73: outIdx[2] = strtol10(cursor);break;
 		case 74: outIdx[3] = strtol10(cursor);break;
+
+		// color
+		case 62: clr = g_aclrDxfIndexColors[strtol10(cursor) % AI_DXF_NUM_INDEX_COLORS]; break;
 		};
 		if (ret)break;
 	}
@@ -468,6 +522,7 @@ bool DXFImporter::Parse3DFace()
 	LayerInfo* out = NULL;
 
 	aiVector3D vip[4]; // -- vectors are initialized to zero
+	aiColor4D  clr(g_clrInvalid);
 	while (GetNextToken())
 	{
 		switch (groupCode)
@@ -516,6 +571,9 @@ bool DXFImporter::Parse3DFace()
 
 		// z position of the fourth corner
 		case 33: vip[3].z = fast_atof(cursor);break;
+
+		// color
+		case 62: clr = g_aclrDxfIndexColors[strtol10(cursor) % AI_DXF_NUM_INDEX_COLORS]; break;
 		};
 		if (ret)break;
 	}
@@ -528,5 +586,8 @@ bool DXFImporter::Parse3DFace()
 	out->vPositions.push_back(vip[1]);
 	out->vPositions.push_back(vip[2]);
 	out->vPositions.push_back(vip[3]); // might be equal to the third
+
+	for (unsigned int i = 0; i < 4;++i)
+		out->vColors.push_back(clr);
 	return ret;
 }

+ 4 - 1
code/DXFLoader.h

@@ -76,6 +76,7 @@ protected:
 
 		// face buffer - order is x,y,z,w v1,v2,v3 (w is equal to z if unused)
 		std::vector<aiVector3D> vPositions;
+		std::vector<aiColor4D>  vColors;
 	};
 
 
@@ -149,11 +150,13 @@ protected:
 	// -------------------------------------------------------------------
 	/** Parses a VERTEX element in a POLYLINE/POLYFACE
 	 *  @param out Receives the output vertex. 
+	 *  @param clr Receives the output vertex color - won't be modified
+	 *    if it is not existing. 
 	 *  @param outIdx Receives the output vertex indices, if present.
 	 *    Wont't be modified otherwise. Size must be at least 4.
 	 *  @return false if the end of the file was reached
 	 */
-	bool ParsePolyLineVertex(aiVector3D& out,unsigned int* outIdx);
+	bool ParsePolyLineVertex(aiVector3D& out, aiColor4D& clr, unsigned int* outIdx);
 
 private:
 

+ 2 - 11
code/GenFaceNormalsProcess.cpp

@@ -106,20 +106,11 @@ bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
 
 		aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
 		aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
-		aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[2]];
-
-		aiVector3D pDelta1 = *pV2 - *pV1;
-		aiVector3D pDelta2 = *pV3 - *pV1;
-		aiVector3D vNor = pDelta1 ^ pDelta2;
-		vNor.Normalize();
-
-		//if (face.mIndices[1] > face.mIndices[2])
-		//	vNor *= -1.0f;
+		aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]];
+		aiVector3D vNor = (*pV2 - *pV1) ^ (*pV3 - *pV1).Normalize();
 
 		for (unsigned int i = 0;i < face.mNumIndices;++i)
-		{
 			pMesh->mNormals[face.mIndices[i]] = vNor;
-		}
 	}
 	return true;
 }

+ 3 - 9
code/GenVertexNormalsProcess.cpp

@@ -116,12 +116,8 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh)
 
 		aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
 		aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
-		aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[2]];
-
-		aiVector3D pDelta1 = *pV2 - *pV1;
-		aiVector3D pDelta2 = *pV3 - *pV1;
-		aiVector3D vNor = pDelta1 ^ pDelta2;
-		vNor.Normalize();
+		aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]];
+		aiVector3D vNor = (*pV2 - *pV1) ^ (*pV3 - *pV1).Normalize();
 
 		for (unsigned int i = 0;i < face.mNumIndices;++i)
 			pMesh->mNormals[face.mIndices[i]] = vNor;
@@ -146,7 +142,7 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh)
 	SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
 	std::vector<unsigned int> verticesFound;
 
-	const float fLimit = cosf(this->configMaxAngle); 
+	const float fLimit = cos(this->configMaxAngle); 
 
 	aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices];
 	for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
@@ -157,7 +153,6 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh)
 		vertexFinder.FindPositions( posThis, posEpsilon, verticesFound);
 
 		aiVector3D pcNor; 
-		//unsigned int div = 0;
 		for (unsigned int a = 0; a < verticesFound.size(); ++a)
 		{
 			unsigned int vidx = verticesFound[a];
@@ -167,7 +162,6 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh)
 				continue;
 
 			pcNor += pMesh->mNormals[vidx];
-			//++div;
 		}
 		pcNor.Normalize();
 		pcNew[i] = pcNor;

+ 53 - 8
code/IFF.h

@@ -2,17 +2,21 @@
 
 // Definitions for the Interchange File Format (IFF)
 // Alexander Gessler, 2006
-// Adapted for Assimp August 2008
+// Adapted to Assimp August 2008
 
 #ifndef AI_IFF_H_INCLUDED
 #define AI_IFF_H_INCLUDED
 
 #include "ByteSwap.h"
 
-namespace Assimp {
-namespace IFF {
+namespace Assimp	{
+namespace IFF		{
 
+#include "./../include/Compiler/pushpack1.h"
+
+/////////////////////////////////////////////////////////////////////////////////
 //! Describes an IFF chunk header
+/////////////////////////////////////////////////////////////////////////////////
 struct ChunkHeader
 {
 	//! Type of the chunk header - FourCC
@@ -20,7 +24,22 @@ struct ChunkHeader
 
 	//! Length of the chunk data, in bytes
 	uint32_t length;
-};
+} PACK_STRUCT;
+
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Describes an IFF sub chunk header
+/////////////////////////////////////////////////////////////////////////////////
+struct SubChunkHeader
+{
+	//! Type of the chunk header - FourCC
+	uint32_t type;
+
+	//! Length of the chunk data, in bytes
+	uint16_t length;
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
 
 
 #define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \
@@ -30,6 +49,34 @@ struct ChunkHeader
 #define AI_IFF_FOURCC_FORM AI_IFF_FOURCC('F','O','R','M')
 
 
+/////////////////////////////////////////////////////////////////////////////////
+//! Load a chunk header
+//! @param outFile Pointer to the file data - points to the chunk data afterwards
+//! @return Pointer to the chunk header
+/////////////////////////////////////////////////////////////////////////////////
+inline LE_NCONST ChunkHeader* LoadChunk(LE_NCONST uint8_t*& outFile)
+{
+	LE_NCONST ChunkHeader* head = (LE_NCONST ChunkHeader*) outFile;
+	AI_LSWAP4(head->length);
+	AI_LSWAP4(head->type);
+	outFile += sizeof(ChunkHeader);
+	return head;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Load a sub chunk header
+//! @param outFile Pointer to the file data - points to the chunk data afterwards
+//! @return Pointer to the sub chunk header
+/////////////////////////////////////////////////////////////////////////////////
+inline LE_NCONST SubChunkHeader* LoadSubChunk(LE_NCONST uint8_t*& outFile)
+{
+	LE_NCONST SubChunkHeader* head = (LE_NCONST SubChunkHeader*) outFile;
+	AI_LSWAP2(head->length);
+	AI_LSWAP4(head->type);
+	outFile += sizeof(SubChunkHeader);
+	return head;
+}
+
 /////////////////////////////////////////////////////////////////////////////////
 //! Read the file header and return the type of the file and its size
 //! @param outFile Pointer to the file data. The buffer must at 
@@ -37,11 +84,9 @@ struct ChunkHeader
 //! @param fileType Receives the type of the file
 //! @return 0 if everything was OK, otherwise an error message
 /////////////////////////////////////////////////////////////////////////////////
-inline const char* ReadHeader(const uint8_t* outFile,uint32_t& fileType) 
+inline const char* ReadHeader(LE_NCONST uint8_t* outFile,uint32_t& fileType) 
 {
-	LE_NCONST ChunkHeader* head = (LE_NCONST ChunkHeader*) outFile;
-	AI_LSWAP4(head->length);
-	AI_LSWAP4(head->type);
+	LE_NCONST ChunkHeader* head = LoadChunk(outFile);
 	if(AI_IFF_FOURCC_FORM != head->type)
 	{
 		return "The file is not an IFF file: FORM chunk is missing";

+ 55 - 51
code/LWOBLoader.cpp

@@ -63,14 +63,11 @@ void LWOImporter::LoadLWOBFile()
 	while (true)
 	{
 		if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)break;
-		LE_NCONST IFF::ChunkHeader* const head = (LE_NCONST IFF::ChunkHeader*)mFileBuffer;
-		AI_LSWAP4(head->length);
-		AI_LSWAP4(head->type);
-		mFileBuffer += sizeof(IFF::ChunkHeader);
+		LE_NCONST IFF::ChunkHeader* const head = IFF::LoadChunk(mFileBuffer);
+
 		if (mFileBuffer + head->length > end)
 		{
-			throw new ImportErrorException("LWOB: Invalid file, the size attribute of "
-				"a chunk points behind the end of the file");
+			throw new ImportErrorException("LWOB: Invalid chunk length");
 			break;
 		}
 		LE_NCONST uint8_t* const next = mFileBuffer+head->length;
@@ -204,104 +201,113 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
 	LWO::Surface& surf = mSurfaces->back();
 	LWO::Texture* pTex = NULL;
 
-	ParseString(surf.mName,size);
-	mFileBuffer+=surf.mName.length()+1;
-	// skip one byte if the length of the surface name is odd
-	if (!(surf.mName.length() & 1))++mFileBuffer; 
+	GetS0(surf.mName,size);
 	while (true)
 	{
 		if (mFileBuffer + 6 > end)break;
 
-		// no proper IFF header here - the chunk length is specified as int16
-		uint32_t head_type		= *((LE_NCONST uint32_t*)mFileBuffer);mFileBuffer+=4;
-		uint16_t head_length	= *((LE_NCONST uint16_t*)mFileBuffer);mFileBuffer+=2;
-		AI_LSWAP4(head_type);
-		AI_LSWAP2(head_length);
-		if (mFileBuffer + head_length > end)
-		{
-			throw new ImportErrorException("LWOB: Invalid file, the size attribute of "
-				"a surface sub chunk points behind the end of the file");
-		}
-		LE_NCONST uint8_t* const next = mFileBuffer+head_length;
-		switch (head_type)
+		LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+		if (mFileBuffer + head->length > end)
+			throw new ImportErrorException("LWOB: Invalid surface chunk length");
+
+		LE_NCONST uint8_t* const next = mFileBuffer+head->length;
+		switch (head->type)
 		{
 		// diffuse color
 		case AI_LWO_COLR:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,COLR,3);
-				surf.mColor.r = *mFileBuffer++ / 255.0f;
-				surf.mColor.g = *mFileBuffer++ / 255.0f;
-				surf.mColor.b = *mFileBuffer   / 255.0f;
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,COLR,3);
+				surf.mColor.r = GetU1() / 255.0f;
+				surf.mColor.g = GetU1() / 255.0f;
+				surf.mColor.b = GetU1() / 255.0f;
 				break;
 			}
 		// diffuse strength ...
 		case AI_LWO_DIFF:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,DIFF,2);
-				AI_LSWAP2P(mFileBuffer);
-				surf.mDiffuseValue = *((int16_t*)mFileBuffer) / 255.0f;
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,DIFF,2);
+				surf.mDiffuseValue = GetU2() / 255.0f;
 				break;
 			}
 		// specular strength ... 
 		case AI_LWO_SPEC:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,SPEC,2);
-				AI_LSWAP2P(mFileBuffer);
-				surf.mSpecularValue = *((int16_t*)mFileBuffer) / 255.0f;
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPEC,2);
+				surf.mSpecularValue = GetU2() / 255.0f;
 				break;
 			}
 		// luminosity ... 
 		case AI_LWO_LUMI:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,LUMI,2);
-				AI_LSWAP2P(mFileBuffer);
-				surf.mLuminosity = *((int16_t*)mFileBuffer) / 255.0f;
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LUMI,2);
+				surf.mLuminosity = GetU2() / 255.0f;
 				break;
 			}
 		// transparency
 		case AI_LWO_TRAN:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,TRAN,2);
-				AI_LSWAP2P(mFileBuffer);
-				surf.mTransparency = *((int16_t*)mFileBuffer) / 255.0f;
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TRAN,2);
+				surf.mTransparency = GetU2() / 255.0f;
+				break;
+			}
+		// surface flags
+		case AI_LWO_FLAG:
+			{
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,FLAG,2);
+				uint16_t flag = GetU2();
+				if (flag & 0x4 )   surf.mMaximumSmoothAngle = 1.56207f;
+				if (flag & 0x8 )   surf.mColorHighlights = 1.f;
+				if (flag & 0x100)  surf.bDoubleSided = true;
+				break;
+			}
+		// maximum smoothing angle
+		case AI_LWO_SMAN:
+			{
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
+				surf.mMaximumSmoothAngle = GetF4();
 				break;
 			}
 		// glossiness
 		case AI_LWO_GLOS:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,GLOS,2);
-				AI_LSWAP2P(mFileBuffer);
-				surf.mGlossiness = float(*((int16_t*)mFileBuffer));
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,GLOS,2);
+				surf.mGlossiness = (float)GetU2();
 				break;
 			}
 		// color texture
 		case AI_LWO_CTEX:
 			{
-				pTex = &surf.mColorTexture;
+				surf.mColorTextures.push_back(Texture());
+				pTex = &surf.mColorTextures.back();
 				break;
 			}
 		// diffuse texture
 		case AI_LWO_DTEX:
 			{
-				pTex = &surf.mDiffuseTexture;
+				surf.mDiffuseTextures.push_back(Texture());
+				pTex = &surf.mDiffuseTextures.back();
 				break;
 			}
 		// specular texture
 		case AI_LWO_STEX:
 			{
-				pTex = &surf.mSpecularTexture;
+				surf.mSpecularTextures.push_back(Texture());
+				pTex = &surf.mSpecularTextures.back();
 				break;
 			}
 		// bump texture
 		case AI_LWO_BTEX:
 			{
-				pTex = &surf.mBumpTexture;
+				surf.mBumpTextures.push_back(Texture());
+				pTex = &surf.mBumpTextures.back();
 				break;
 			}
 		// transparency texture
 		case AI_LWO_TTEX:
 			{
-				pTex = &surf.mTransparencyTexture;
+				surf.mOpacityTextures.push_back(Texture());
+				pTex = &surf.mOpacityTextures.back();
 				break;
 			}
 			// texture path
@@ -309,9 +315,7 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
 			{
 				if (pTex)
 				{
-					ParseString(pTex->mFileName,head_length);	
-					AdjustTexturePath(pTex->mFileName);
-					mFileBuffer += pTex->mFileName.length();
+					GetS0(pTex->mFileName,head->length);	
 				}
 				else DefaultLogger::get()->warn("LWOB: TIMG tag was encuntered although "
 					"there was no xTEX tag before");
@@ -320,8 +324,8 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
 		// texture strength
 		case AI_LWO_TVAL:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,TVAL,1);
-				if (pTex)pTex->mStrength = *mFileBuffer / 255.0f;
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TVAL,1);
+				if (pTex)pTex->mStrength = (float)GetU1()/ 255.f;
 				else DefaultLogger::get()->warn("LWOB: TVAL tag was encuntered "
 					"although there was no xTEX tag before");
 				break;

+ 212 - 45
code/LWOFileData.h

@@ -40,9 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 /** @file Defines chunk constants used by the LWO file format
 
-The chunks are taken from LWO2.h, found in the sourcecode of
-a project called Nxabega (http://www.sourceforge.net/projects/nxabega).
-I assume they are from the official LightWave SDK headers.
+The chunks are taken from the official LightWave SDK headers.
 Original copyright notice: "Ernie Wright  17 Sep 00" 
  
 */
@@ -253,12 +251,25 @@ namespace LWO {
 struct Face : public aiFace
 {
 	Face() 
-		: surfaceIndex(0)
-		, smoothGroup(0)
+		: surfaceIndex	(0)
+		, smoothGroup	(0)
 	{}
 
+	Face(const Face& f)
+	{
+		*this = f;
+	}
+
 	unsigned int surfaceIndex;
 	unsigned int smoothGroup;
+
+	Face& operator=(const LWO::Face& f)
+	{
+		aiFace::operator =(f);
+		surfaceIndex	= f.surfaceIndex;
+		smoothGroup		= f.smoothGroup;
+		return *this;
+	}
 };
 
 
@@ -271,11 +282,24 @@ struct VMapEntry
 		:  dims(_dims)
 	{}
 
-	~VMapEntry() {delete[] rawData;}
+	virtual ~VMapEntry() {}
+
+	//! allocates memory for the vertex map
+	virtual void Allocate(unsigned int num)
+	{
+		if (!rawData.empty())return; // return if already allocated
+
+		register unsigned int m = num*dims;
+		rawData.reserve(m + (m>>2u)); // 25% as  extra storage for VMADs
+		rawData.resize(m,0.f);
+		abAssigned.resize(num,false);
+	}
 
 	std::string name;
-	float* rawData;
 	unsigned int dims;
+	std::vector<float> rawData;
+
+	std::vector<bool> abAssigned;
 };
 
 // ---------------------------------------------------------------------------
@@ -283,16 +307,29 @@ struct VMapEntry
  */
 struct VColorChannel : public VMapEntry
 {
-	VColorChannel(unsigned int num)
+	VColorChannel()
 		: VMapEntry(4)
+	{}
+
+	//! need to overwrite this function - the alpha channel must
+	//! be initialized to 1.0 by default
+	virtual void Allocate(unsigned int num)
 	{
-		data = new aiColor4D[num];
-		for (unsigned int i = 0; i < num;++i)
-			data[i].a = 1.0f;
-		rawData = (float*)data;
+		if (!rawData.empty())return; // return if already allocated
+
+		register unsigned int m = num*dims;
+		rawData.reserve(m + (m>>2u)); // 25% as  extra storage for VMADs
+		rawData.resize(m,0.f);
+
+		for (std::vector<float>::iterator it = rawData.begin(), end = rawData.end();
+			 it != end;++it )	
+		{
+			for (unsigned int i = 0; i< 3;++i,++it)
+				*it = 0.f;
+			*it = 1.f;
+		}
+		abAssigned.resize(num,false);
 	}
-
-	aiColor4D* data;
 };
 
 // ---------------------------------------------------------------------------
@@ -300,14 +337,9 @@ struct VColorChannel : public VMapEntry
  */
 struct UVChannel : public VMapEntry
 {
-	UVChannel(unsigned int num)
-		: VMapEntry(3)
-	{
-		data = new aiVector3D[num]; // to make the final copying easier
-		rawData = (float*)data;
-	}
-
-	aiVector3D* data;
+	UVChannel()
+		: VMapEntry(2)
+	{}
 };
 
 // ---------------------------------------------------------------------------
@@ -315,13 +347,9 @@ struct UVChannel : public VMapEntry
  */
 struct WeightChannel : public VMapEntry
 {
-	WeightChannel(unsigned int num)
+	WeightChannel()
 		: VMapEntry(1)
-	{
-		rawData = new float[num];
-		for (unsigned int m = 0; m < num;++m)
-			rawData[m] = 0.f;
-	}
+	{}
 };
 
 
@@ -330,10 +358,59 @@ struct WeightChannel : public VMapEntry
  */
 struct Texture
 {
+	// we write the enum values out here to make debugging easier ...
+	enum BlendType
+	{
+		Normal			= 0,
+		Subtractive		= 1,
+		Difference		= 2,
+		Multiply		= 3,
+		Divide			= 4,
+		Alpha			= 5,
+		TextureDispl	= 6,
+		Additive		= 7
+	};
+
+	enum MappingMode
+	{
+		Planar			= 0,
+		Cylindrical		= 1,
+		Spherical		= 2,
+		Cubic			= 3,
+		FrontProjection	= 4,
+		UV				= 5
+	};
+
+	enum Axes
+	{
+		AXIS_X			= 0,
+		AXIS_Y			= 1,
+		AXIS_Z			= 2
+	};
+
+	enum Wrap
+	{
+		RESET			= 0,
+		REPEAT			= 1,
+		MIRROR			= 2,
+		EDGE			= 3
+	};
+
 	Texture()
 		: mClipIdx(0xffffffff)
 		, mStrength			(1.0f)
 		, mUVChannelIndex	("unknown")
+		, mRealUVIndex		(0xffffffff)
+		, enabled			(true)
+		, blendType			(Additive)
+		, bCanUse			(true)
+		, mapMode			(UV)
+		, majorAxis			(AXIS_X)
+		, wrapAmountH		(1.0f)
+		, wrapAmountW		(1.0f)
+		, wrapModeWidth		(REPEAT)
+		, wrapModeHeight	(REPEAT)
+		, ordinal			("\x00")
 	{}
 
 	//! File name of the texture
@@ -342,15 +419,38 @@ struct Texture
 	//! Clip index
 	unsigned int mClipIdx;
 
-	//! Strength of the texture
+	//! Strength of the texture - blend factor
 	float mStrength;
 
-
-	/*************** SPECIFIC TO LWO2 *********************/
 	uint32_t type; // type of the texture
 
 	//! Name of the corresponding UV channel
 	std::string mUVChannelIndex;
+	unsigned int mRealUVIndex;
+
+	//! is the texture enabled?
+	bool enabled;
+
+	//! blend type
+	BlendType blendType;
+
+	//! are we able to use the texture?
+	bool bCanUse;
+
+	//! mapping mode
+	MappingMode mapMode;
+
+	//! major axis for planar, cylindrical, spherical projections
+	Axes majorAxis;
+
+	//! wrap amount for cylindrical and spherical projections
+	float wrapAmountH,wrapAmountW;
+
+	//! wrapping mode for the texture
+	Wrap wrapModeWidth,wrapModeHeight;
+
+	//! ordinal string of the texture
+	std::string ordinal;
 };
 
 // ---------------------------------------------------------------------------
@@ -358,23 +458,66 @@ struct Texture
  */
 struct Clip
 {
-	//! path to the base texture
+	enum Type
+	{
+		 STILL, SEQ, REF, UNSUPPORTED
+	} type;
+
+	Clip()
+		: type	(UNSUPPORTED)
+		, idx	(0)
+	{}
+
+	//! path to the base texture -
 	std::string path;
+
+	//! reference to another CLIP
+	unsigned int clipRef;
+
+	//! index of the clip
+	unsigned int idx;
 };
 
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO file shader
+ *
+ *  Later
+ */
+struct Shader
+{
+	Shader()
+		:	ordinal			("\x00")
+		,	functionName	("unknown")
+		,	enabled			(true)
+	{}
+
+	std::string ordinal;
+	std::string functionName;
+	bool enabled;
+};
+
+typedef std::list < Texture >		TextureList;
+typedef std::list < Shader >		ShaderList;
+
 // ---------------------------------------------------------------------------
 /** \brief Data structure for a LWO file surface (= material)
  */
 struct Surface
 {
 	Surface()
-		: bDoubleSided			(false)
-		, mDiffuseValue			(1.0f)
-		, mSpecularValue		(1.0f)
-		, mTransparency			(0.0f)
-		, mGlossiness			(0.0f)
-		, mLuminosity			(0.0f)
-		, mMaximumSmoothAngle	(0.0f) // 0 == not specified
+		: mColor				(0.78431f,0.78431f,0.78431f)
+		, bDoubleSided			(false)
+		, mDiffuseValue			(1.f)
+		, mSpecularValue		(0.f)
+		, mTransparency			(0.f)
+		, mGlossiness			(0.4f)
+		, mLuminosity			(0.f)
+		, mColorHighlights		(0.f)
+		, mMaximumSmoothAngle	(0.f) // 0 == not specified, no smoothing
+		, mVCMap				("")
+		, mIOR					(1.f) // vakuum
+		, mBumpIntensity		(1.f)
 	{}
 
 	//! Name of the surface
@@ -387,23 +530,38 @@ struct Surface
 	bool bDoubleSided;
 
 	//! Various material parameters
-	float mDiffuseValue,mSpecularValue,mTransparency,mGlossiness,mLuminosity;
+	float mDiffuseValue,mSpecularValue,mTransparency,mGlossiness,mLuminosity,mColorHighlights;
 
 	//! Maximum angle between two adjacent triangles
 	//! that they can be smoothed - in degrees
 	float mMaximumSmoothAngle;
 
-	//! Textures
-	Texture mColorTexture,mDiffuseTexture,mSpecularTexture,
-		mBumpTexture,mTransparencyTexture;
+	//! Vertex color map to be used to color the surface
+	std::string mVCMap;
+
+	//! Names of the special shaders to be applied to the surface
+	ShaderList mShaders;
+
+	//! Textures - the first entry in the list is evaluated first
+	TextureList mColorTextures, // color textures are added to both diffuse and specular texture stacks
+		mDiffuseTextures,
+		mSpecularTextures,
+		mOpacityTextures,
+		mBumpTextures,
+		mGlossinessTextures;
+
+	//! Index of refraction
+	float mIOR;
+
+	//! Bump intensity scaling
+	float mBumpIntensity;
 };
 
 // ---------------------------------------------------------------------------
 #define AI_LWO_VALIDATE_CHUNK_LENGTH(length,name,size) \
 	if (length < size) \
 	{ \
-		DefaultLogger::get()->warn("LWO: "#name" chunk is too small"); \
-		break; \
+		throw new ImportErrorException("LWO: "#name" chunk is too small"); \
 	} \
 
 
@@ -413,6 +571,7 @@ typedef std::vector	<	LWO::Face		>	FaceList;
 typedef std::vector	<	LWO::Surface	>	SurfaceList;
 typedef std::vector	<	std::string		>	TagList;
 typedef std::vector	<	unsigned int	>	TagMappingTable;
+typedef std::vector	<	unsigned int	>	ReferrerList;
 typedef std::vector	<	WeightChannel	>	WeightChannelList;
 typedef std::vector	<	VColorChannel	>	VColorChannelList;
 typedef std::vector	<	UVChannel		>	UVChannelList;
@@ -433,6 +592,11 @@ struct Layer
 	/** Temporary point list from the file */
 	PointList mTempPoints;
 
+	/** Lists for every point the index of another point
+	    that has been copied from *this* point or 0xffffffff if
+		no copy of the point has been made */
+	ReferrerList mPointReferrers;
+
 	/** Weight channel list from the file */
 	WeightChannelList mWeightChannels;
 
@@ -456,6 +620,9 @@ struct Layer
 
 	/** Name of the layer */
 	std::string mName;
+
+	/** Pivot point of the layer */
+	aiVector3D mPivot;
 };
 
 typedef std::list<LWO::Layer>		LayerList;

+ 389 - 88
code/LWOLoader.cpp

@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "LWOLoader.h"
 #include "MaterialSystem.h"
 #include "StringComparison.h"
+#include "SGSpatialSort.h"
 #include "ByteSwap.h"
 
 // public assimp headers
@@ -52,11 +53,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "../include/IOSystem.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
-#include "../include/DefaultLogger.h"
 #include "../include/assimp.hpp"
 
 // boost headers
 #include <boost/scoped_ptr.hpp>
+#include <sstream>
+#include <iomanip>
 
 using namespace Assimp;
 
@@ -91,12 +93,14 @@ bool LWOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
 
 	return true;
 }
+
 // ------------------------------------------------------------------------------------------------
 // Setup configuration properties
 void LWOImporter::SetupProperties(const Importer* pImp)
 {
 	// -- no configuration options at the moment
 }
+
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure. 
 void LWOImporter::InternReadFile( const std::string& pFile, 
@@ -166,7 +170,10 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 		szBuff[3] = (char)(fileType);
 		throw new ImportErrorException(std::string("Unknown LWO sub format: ") + szBuff);
 	}
+
+	// now, as we have loaded all data, we can resolve cross-referenced tags and clips
 	ResolveTags();
+	ResolveClips();
 
 	// now process all layers and build meshes and nodes
 	std::vector<aiMesh*> apcMeshes;
@@ -176,10 +183,10 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 
 
 	unsigned int iDefaultSurface = 0xffffffff; // index of the default surface
-	for (LayerList::const_iterator lit = mLayers->begin(), lend = mLayers->end();
+	for (LayerList::iterator lit = mLayers->begin(), lend = mLayers->end();
 		lit != lend;++lit)
 	{
-		const LWO::Layer& layer = *lit;
+		LWO::Layer& layer = *lit;
 
 		// I don't know whether there could be dummy layers, but it would be possible
 		const unsigned int meshStart = (unsigned int)apcMeshes.size();
@@ -190,7 +197,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 			std::vector<SortedRep> pSorted(mSurfaces->size()+1);
 
 			unsigned int i = 0;
-			for (FaceList::const_iterator it = layer.mFaces.begin(), end = layer.mFaces.end();
+			for (FaceList::iterator it = layer.mFaces.begin(), end = layer.mFaces.end();
 				it != end;++it,++i)
 			{
 				unsigned int idx = (*it).surfaceIndex;
@@ -232,8 +239,9 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 				apcMeshes.push_back(mesh);
 				mesh->mNumFaces = (unsigned int)sorted.size();
 
-				for (SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
-					it != end;++it)
+				// count the number of vertices
+				SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
+				for (;it != end;++it)
 				{
 					mesh->mNumVertices += layer.mFaces[*it].mNumIndices;
 				}
@@ -275,31 +283,45 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 					pvVC[mui] = mesh->mColors[mui] = new aiColor4D[mesh->mNumVertices];
 				}
 
+				// we would not need this extra array, but the code is much cleaner if we use it
+				// FIX: we can use the referrer ID array here. invalidate its contents
+				// before we resize it to avoid a unnecessary memcpy 
+				std::vector<unsigned int>& smoothingGroups = layer.mPointReferrers;
+				smoothingGroups.erase (smoothingGroups.begin(),smoothingGroups.end());
+				smoothingGroups.resize(mesh->mNumFaces,0);
+
 				// now convert all faces
 				unsigned int vert = 0;
-				for (SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
-					it != end;++it)
+				std::vector<unsigned int>::iterator outIt = smoothingGroups.begin();
+				for (it = sorted.begin(); it != end;++it,++outIt)
 				{
 					const LWO::Face& face = layer.mFaces[*it];
+					*outIt = face.smoothGroup;
 
 					// copy all vertices
 					for (unsigned int q = 0; q  < face.mNumIndices;++q)
 					{
 						register unsigned int idx = face.mIndices[q];
-						*pv++ = layer.mTempPoints[idx];
+						*pv = layer.mTempPoints[idx] + layer.mPivot;
+						pv->z *= -1.0f; // DX to OGL
+						pv++;
 
 						// process UV coordinates
 						for (unsigned int w = 0; w < AI_MAX_NUMBER_OF_TEXTURECOORDS;++w)	
 						{
 							if (0xffffffff == vUVChannelIndices[w])break;
-							*(pvUV[w])++ = layer.mUVChannels[vUVChannelIndices[w]].data[idx];
+							aiVector3D*& pp = pvUV[w];
+							const aiVector2D& src = ((aiVector2D*)&layer.mUVChannels[vUVChannelIndices[w]].rawData[0])[idx];
+							pp->x = src.x;
+							pp->y = 1.0f - src.y; // DX to OGL
+							pp++;
 						}
 
 						// process vertex colors
 						for (unsigned int w = 0; w < AI_MAX_NUMBER_OF_COLOR_SETS;++w)	
 						{
 							if (0xffffffff == vVColorIndices[w])break;
-							*(pvVC[w])++ = layer.mVColorChannels[vVColorIndices[w]].data[idx];
+							*(pvVC[w])++ = ((aiColor4D*)&layer.mVColorChannels[vVColorIndices[w]].rawData[0])[idx];
 						}
 
 #if 0
@@ -308,14 +330,20 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 						{
 						}
 #endif
-						face.mIndices[q] = vert++;
+						face.mIndices[q] = vert + (face.mNumIndices-q-1);
 					}
+					vert += face.mNumIndices;
 
 					pf->mIndices = face.mIndices;
 					pf->mNumIndices = face.mNumIndices;
 					unsigned int** p = (unsigned int**)&face.mIndices;*p = NULL; // make sure it won't be deleted
 					pf++;
 				}
+
+				// compute normal vectors for the mesh - we can't use our GenSmoothNormal-Step here
+				// since it wouldn't handle smoothing groups correctly
+				ComputeNormals(mesh,smoothingGroups,_mSurfaces[i]);
+
 				++p;
 			}
 		}
@@ -353,10 +381,102 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 }
 
 // ------------------------------------------------------------------------------------------------
-void LWOImporter::AddChildren(aiNode* node, uintptr_t parent, std::vector<aiNode*>& apcNodes)
+void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>& smoothingGroups,
+	const LWO::Surface& surface)
 {
-	unsigned int numChilds = 0;
+	// allocate output storage
+	mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+
+	// First generate per-face normals
+	aiVector3D* out;
+	std::vector<aiVector3D> faceNormals;
+
+	if (!surface.mMaximumSmoothAngle)
+		out = mesh->mNormals;
+	else
+	{
+		faceNormals.resize(mesh->mNumVertices);
+		out = &faceNormals[0];
+	}
+
+	aiFace* begin = mesh->mFaces, *const end = mesh->mFaces+mesh->mNumFaces;
+	for (; begin != end; ++begin)
+	{
+		aiFace& face = *begin;
+
+		// LWO doc: "the normal is defined as the cross product of the first and last edges"
+		aiVector3D* pV1 = mesh->mVertices + face.mIndices[0];
+		aiVector3D* pV2 = mesh->mVertices + face.mIndices[1];
+		aiVector3D* pV3 = mesh->mVertices + face.mIndices[face.mNumIndices-1];
+
+		aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).Normalize();
+		for (unsigned int i = 0; i < face.mNumIndices;++i)
+			out[face.mIndices[i]] = vNor;
+	}
+	if (!surface.mMaximumSmoothAngle)return;
+
+	// calculate the position bounds so we have a reliable epsilon to 
+	// check position differences against 
+	aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
+	for( unsigned int a = 0; a < mesh->mNumVertices; a++)
+	{
+		minVec.x = std::min( minVec.x, mesh->mVertices[a].x);
+		minVec.y = std::min( minVec.y, mesh->mVertices[a].y);
+		minVec.z = std::min( minVec.z, mesh->mVertices[a].z);
+		maxVec.x = std::max( maxVec.x, mesh->mVertices[a].x);
+		maxVec.y = std::max( maxVec.y, mesh->mVertices[a].y);
+		maxVec.z = std::max( maxVec.z, mesh->mVertices[a].z);
+	}
+	const float posEpsilon = (maxVec - minVec).Length() * 1e-5f;
 	
+	// now generate the spatial sort tree
+	SGSpatialSort sSort;
+	std::vector<unsigned int>::const_iterator it = smoothingGroups.begin();
+	for( begin =  mesh->mFaces; begin != end; ++begin, ++it)
+	{
+		aiFace& face = *begin;
+		for (unsigned int i = 0; i < face.mNumIndices;++i)
+		{
+			register unsigned int tt = face.mIndices[i];
+			sSort.Add(mesh->mVertices[tt],tt,*it);
+		}
+	}
+	// sort everything - this takes O(logn) time
+	sSort.Prepare();
+	std::vector<unsigned int> poResult;poResult.reserve(20);
+
+	const float fLimit = cos(surface.mMaximumSmoothAngle);
+
+	// generate vertex normals. We have O(logn) for the binary lookup, which we need
+	// for n elements, thus the EXPECTED complexity is O(nlogn)
+	for( begin =  mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it)
+	{
+		register unsigned int sg = *it;
+
+		aiFace& face = *begin;
+		unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices;
+		for (; beginIdx != endIdx; ++beginIdx)
+		{
+			sSort.FindPositions(mesh->mVertices[*beginIdx],sg,posEpsilon,poResult,true);
+
+			aiVector3D vNormals;
+			for (std::vector<unsigned int>::const_iterator
+				a =  poResult.begin(), end = poResult.end();
+				a != end;++a)
+			{
+				const aiVector3D& v = faceNormals[*a];
+				if (v * faceNormals[*beginIdx] < fLimit)continue;
+				vNormals += v;
+			}
+			vNormals.Normalize();
+			mesh->mNormals[*beginIdx] = vNormals;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::AddChildren(aiNode* node, uintptr_t parent, std::vector<aiNode*>& apcNodes)
+{
 	for (uintptr_t i  = 0; i < (uintptr_t)apcNodes.size();++i)
 	{
 		if (i == parent)continue;
@@ -454,57 +574,50 @@ void LWOImporter::ResolveTags()
 }
 
 // ------------------------------------------------------------------------------------------------
-void LWOImporter::ParseString(std::string& out,unsigned int max)
+void LWOImporter::ResolveClips()
 {
-	// --- this function is used for both LWO2 and LWOB
-	unsigned int iCursor = 0;
-	const char* in = (const char*)mFileBuffer,*sz = in;
-	while (*in)
+	for( unsigned int i = 0; i < mClips.size();++i)
 	{
-		if (++iCursor > max)
+		Clip& clip = mClips[i];
+		if (Clip::REF == clip.type)
 		{
-			DefaultLogger::get()->warn("LWOB: Invalid file, string is is too long");
-			break;
+			if (clip.clipRef >= mClips.size())
+			{
+				DefaultLogger::get()->error("LWO2: Clip referrer index is out of range");
+				clip.clipRef = 0;
+			}
+			Clip& dest = mClips[clip.clipRef];
+			if (Clip::REF == dest.type)
+			{
+				DefaultLogger::get()->error("LWO2: Clip references another clip reference");
+				clip.type = Clip::UNSUPPORTED;
+			}
+			else
+			{
+				clip.path = dest.path;
+				clip.type = dest.type;
+			}
 		}
-		++in;
 	}
-	unsigned int len = (unsigned int) (in-sz);
-	out = std::string(sz,len);
 }
 
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::AdjustTexturePath(std::string& out)
 {
 	// --- this function is used for both LWO2 and LWOB
-	if (::strstr(out.c_str(), "(sequence)"))
+	if (!mIsLWO2 && ::strstr(out.c_str(), "(sequence)"))
 	{
 		// remove the (sequence) and append 000
-		DefaultLogger::get()->info("LWO: Sequence of animated texture found. It will be ignored");
+		DefaultLogger::get()->info("LWOB: Sequence of animated texture found. It will be ignored");
 		out = out.substr(0,out.length()-10) + "000";
 	}
-}
 
-// ------------------------------------------------------------------------------------------------
-int LWOImporter::ReadVSizedIntLWO2(uint8_t*& inout)
-{
-	int i;
-	int c = *inout;inout++;
-	if(c != 0xFF)
-	{
-		i = c << 8;
-		c = *inout;inout++;
-		i |= c;
-	}
-	else
+	// format: drive:path/file - we need to insert a slash after the drive
+	std::string::size_type n = out.find_first_of(':');
+	if (std::string::npos != n)
 	{
-		c = *inout;inout++;
-		i = c << 16;
-		c = *inout;inout++;
-		i |= c << 8;
-		c = *inout;inout++;
-		i |= c;
+		out.insert(n+1,"/");
 	}
-	return i;
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -530,8 +643,20 @@ void LWOImporter::LoadLWOTags(unsigned int size)
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWOPoints(unsigned int length)
 {
-	// --- this function is used for both LWO2 and LWOB
-	mCurLayer->mTempPoints.resize( length / 12 );
+	// --- this function is used for both LWO2 and LWOB but for
+	// LWO2 we need to allocate 25% more storage - it could be we'll 
+	// need to duplicate some points later.
+	register unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12;
+	if (mIsLWO2)
+	{
+		mCurLayer->mTempPoints.reserve	( regularSize + (regularSize>>2u) );
+		mCurLayer->mTempPoints.resize	( regularSize );
+
+		// initialize all point referrers with the default values
+		mCurLayer->mPointReferrers.reserve	( regularSize + (regularSize>>2u) );
+		mCurLayer->mPointReferrers.resize	( regularSize, 0xffffffff );
+	}
+	else mCurLayer->mTempPoints.resize( regularSize );
 
 	// perform endianess conversions
 #ifndef AI_BUILD_BIG_ENDIAN
@@ -544,17 +669,16 @@ void LWOImporter::LoadLWOPoints(unsigned int length)
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWO2Polygons(unsigned int length)
 {
-	uint32_t type = *((LE_NCONST uint32_t*)mFileBuffer);mFileBuffer += 4;length-=4;
-	AI_LSWAP4(type);
+	LE_NCONST uint16_t* const end	= (LE_NCONST uint16_t*)(mFileBuffer+length);
+	uint32_t type = GetU4();
 
 	if (type != AI_LWO_FACE)
 	{
-		DefaultLogger::get()->warn("LWO2: Only POLS.FACE chunsk are supported.");
+		DefaultLogger::get()->warn("LWO2: Only POLS.FACE chunks are supported.");
 		return;
 	}
 
 	// first find out how many faces and vertices we'll finally need
-	LE_NCONST uint16_t* const end	= (LE_NCONST uint16_t*)(mFileBuffer+length);
 	LE_NCONST uint16_t* cursor		= (LE_NCONST uint16_t*)mFileBuffer;
 
 	unsigned int iNumFaces = 0,iNumVertices = 0;
@@ -616,21 +740,22 @@ void LWOImporter::CopyFaceIndicesLWO2(FaceList::iterator& it,
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWO2PolygonTags(unsigned int length)
 {
-	uint32_t type = *((LE_NCONST uint32_t*)mFileBuffer);mFileBuffer+=4;
-	AI_LSWAP4(type);
+	LE_NCONST uint8_t* const end = mFileBuffer+length;
+
+	AI_LWO_VALIDATE_CHUNK_LENGTH(length,PTAG,4);
+	uint32_t type = GetU4();
 
 	if (type != AI_LWO_SURF && type != AI_LWO_SMGP)
 		return;
 
-	LE_NCONST uint8_t* const end = mFileBuffer+length;
-	while (mFileBuffer <= end)
+	while (mFileBuffer < end)
 	{
 		unsigned int i = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
-		unsigned int j = ReadVSizedIntLWO2(mFileBuffer);
+		unsigned int j = GetU2();
 
 		if (i >= mCurLayer->mFaces.size())
 		{
-			DefaultLogger::get()->warn("LWO2: face index in ptag list is out of range");
+			DefaultLogger::get()->warn("LWO2: face index in PTAG is out of range");
 			continue;
 		}
 
@@ -646,14 +771,88 @@ void LWOImporter::LoadLWO2PolygonTags(unsigned int length)
 	}
 }
 
+// ------------------------------------------------------------------------------------------------
+template <class T>
+VMapEntry* FindEntry(std::vector< T >& list,const std::string& name, bool perPoly)
+{
+	for (typename std::vector< T >::iterator it = list.begin(), end = list.end();
+		 it != end; ++it)
+	{
+		if ((*it).name == name)
+		{
+			if (!perPoly)
+			{
+				DefaultLogger::get()->warn("LWO2: Found two VMAP sections with equal names");
+			}
+			return &(*it);
+		}
+	}
+	list.push_back( T() );
+	VMapEntry* p = &list.back();
+	p->name = name;
+	return p;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+void CreateNewEntry(std::vector< T >& list, unsigned int srcIdx)
+{
+	for (typename std::vector< T >::iterator 
+		it =  list.begin(), end = list.end();
+		it != end;++it)
+	{
+		T& chan = *it;
+		for (unsigned int a = 0; a < chan.dims;++a)
+		{
+			chan.rawData.push_back(chan.rawData[srcIdx*chan.dims+a]);
+			chan.abAssigned[srcIdx] = true;
+			chan.abAssigned.resize(chan.abAssigned.size()+1,false);
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::DoRecursiveVMAPAssignment(VMapEntry* base, unsigned int numRead, 
+	unsigned int idx, float* data)
+{
+	ai_assert(NULL != data);
+	LWO::ReferrerList& refList	= mCurLayer->mPointReferrers;
+	unsigned int i;
+
+	base->abAssigned[idx] = true;
+	for (i = 0; i < numRead;++i) 
+		base->rawData[idx*base->dims+i]= data[i];
+
+	if (0xffffffff != (i = refList[idx]))
+		DoRecursiveVMAPAssignment(base,numRead,i,data);
+}
+
+// ------------------------------------------------------------------------------------------------
+void AddToSingleLinkedList(ReferrerList& refList, unsigned int srcIdx, unsigned int destIdx)
+{
+	if(0xffffffff == refList[srcIdx])
+	{
+		refList[srcIdx] = destIdx;
+		return;
+	}
+	AddToSingleLinkedList(refList,refList[srcIdx],destIdx);
+}
+
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly)
 {
-	unsigned int type = *((LE_NCONST uint32_t*)mFileBuffer);mFileBuffer+=4;
-	unsigned int dims = *((LE_NCONST uint16_t*)mFileBuffer);mFileBuffer+=2;
+	LE_NCONST uint8_t* const end = mFileBuffer+length;
+
+	AI_LWO_VALIDATE_CHUNK_LENGTH(length,VMAP,6);
+	unsigned int type = GetU4();
+	unsigned int dims = GetU2();
 
 	VMapEntry* base;
 
+	// read the name of the vertex map 
+	std::string name;
+	GetS0(name,length);
+
 	switch (type)
 	{
 	case AI_LWO_TXUV:
@@ -661,51 +860,139 @@ void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly)
 		{
 			DefaultLogger::get()->warn("LWO2: Found UV channel with != 2 components"); 
 		}
-		mCurLayer->mUVChannels.push_back(UVChannel((unsigned int)mCurLayer->mTempPoints.size()));
-		base = &mCurLayer->mUVChannels.back();
+		base = FindEntry(mCurLayer->mUVChannels,name,perPoly);
+		break;
 	case AI_LWO_WGHT:
 		if (dims != 1)
 		{
 			DefaultLogger::get()->warn("LWO2: found vertex weight map with != 1 components"); 
 		}
-		mCurLayer->mWeightChannels.push_back(WeightChannel((unsigned int)mCurLayer->mTempPoints.size()));
-		base = &mCurLayer->mWeightChannels.back();
+		base = FindEntry(mCurLayer->mWeightChannels,name,perPoly);
+		break;
 	case AI_LWO_RGB:
 	case AI_LWO_RGBA:
 		if (dims != 3 && dims != 4)
 		{
 			DefaultLogger::get()->warn("LWO2: found vertex color map with != 3&4 components"); 
 		}
-		mCurLayer->mVColorChannels.push_back(VColorChannel((unsigned int)mCurLayer->mTempPoints.size()));
-		base = &mCurLayer->mVColorChannels.back();
+		base = FindEntry(mCurLayer->mVColorChannels,name,perPoly);
+		break;
 	default: return;
 	};
-
-	// read the name of the vertex map 
-	ParseString(base->name,length);
+	base->Allocate((unsigned int)mCurLayer->mTempPoints.size());
 
 	// now read all entries in the map
 	type = std::min(dims,base->dims); 
 	const unsigned int diff = (dims - type)<<2;
 
-	LE_NCONST uint8_t* const end = mFileBuffer+length;
+	LWO::FaceList& list	= mCurLayer->mFaces;
+	LWO::PointList& pointList = mCurLayer->mTempPoints;
+	LWO::ReferrerList& refList = mCurLayer->mPointReferrers;
+
+	float temp[4];
+
 	while (mFileBuffer < end)
 	{
 		unsigned int idx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mPointIDXOfs;
-		if (idx > mCurLayer->mTempPoints.size())
+		if (idx >= pointList.size())
 		{
 			DefaultLogger::get()->warn("LWO2: vertex index in vmap/vmad is out of range");
-			continue;
+			mFileBuffer += base->dims*4;continue;
 		}
-		for (unsigned int i = 0; i < type;++i)
+		if (perPoly)
 		{
-			base->rawData[idx*dims+i]= *((float*)mFileBuffer);
-			mFileBuffer += 4;
+			unsigned int polyIdx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
+			if (base->abAssigned[idx])
+			{
+				// we have already a VMAP entry for this vertex - thus
+				// we need to duplicate the corresponding polygon.
+				if (polyIdx >= list.size())
+				{
+					DefaultLogger::get()->warn("LWO2: VMAD polygon index is out of range");
+					mFileBuffer += base->dims*4;continue;
+				}
+
+				LWO::Face& src = list[polyIdx];
+
+				// generate new vertex positions
+				for (unsigned int i = 0; i < src.mNumIndices;++i)
+				{
+					register unsigned int srcIdx = src.mIndices[i];
+
+					// store the index of the new vertex in the old vertex
+					// so we get a single linked list we can traverse in
+					// only one direction
+					refList.push_back(0xffffffff);
+					AddToSingleLinkedList(refList,srcIdx,(src.mIndices[i] = (unsigned int)pointList.size()));
+					pointList.push_back(pointList[srcIdx]);
+
+					CreateNewEntry(mCurLayer->mVColorChannels,	srcIdx );
+					CreateNewEntry(mCurLayer->mUVChannels,		srcIdx );
+					CreateNewEntry(mCurLayer->mWeightChannels,	srcIdx );
+				}
+			}
 		}
+		for (unsigned int l = 0; l < type;++l)
+			temp[l] = GetF4();
+
+		DoRecursiveVMAPAssignment(base,type,idx, temp);
 		mFileBuffer += diff;
 	}
 }
 
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2Clip(unsigned int length)
+{
+	AI_LWO_VALIDATE_CHUNK_LENGTH(length,CLIP,10);
+
+	mClips.push_back(LWO::Clip());
+	LWO::Clip& clip = mClips.back();
+
+	// first - get the index of the clip
+	clip.idx = GetU4();
+
+	LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+	switch (head->type)
+	{
+	case AI_LWO_STIL:
+		GetS0(clip.path,head->length);
+		clip.type = Clip::STILL;
+		break;
+
+	case AI_LWO_ISEQ:
+		{
+			uint8_t digits = GetU1();  mFileBuffer++;
+			int16_t offset = GetU2();  mFileBuffer+=4;
+			int16_t start  = GetU2();  mFileBuffer+=4;
+
+			std::string s;std::stringstream ss;
+			GetS0(s,head->length);head->length -= (unsigned int)s.length()+1;
+			ss << s;
+			ss << std::setw(digits) << offset + start;
+			GetS0(s,head->length);
+			ss << s;
+			clip.path = ss.str();
+			clip.type = Clip::SEQ;
+		}
+		break;
+
+	case AI_LWO_STCC:
+		DefaultLogger::get()->warn("LWO2: Color shifted images are not supported");
+		break;
+
+	case AI_LWO_ANIM:
+		DefaultLogger::get()->warn("LWO2: Animated textures are not supported");
+		break;
+
+	case AI_LWO_XREF:
+		clip.type = Clip::REF;
+		clip.clipRef = GetU4();
+		break;
+
+	default:
+		DefaultLogger::get()->warn("LWO2: Encountered unknown CLIP subchunk");
+	}
+}
 
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWO2File()
@@ -714,14 +1001,11 @@ void LWOImporter::LoadLWO2File()
 	while (true)
 	{
 		if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)break;
-		LE_NCONST IFF::ChunkHeader* const head = (LE_NCONST IFF::ChunkHeader*)mFileBuffer;
-		AI_LSWAP4(head->length);
-		AI_LSWAP4(head->type);
-		mFileBuffer += sizeof(IFF::ChunkHeader);
+		LE_NCONST IFF::ChunkHeader* const head = IFF::LoadChunk(mFileBuffer);
+
 		if (mFileBuffer + head->length > end)
 		{
-			throw new ImportErrorException("LWOB: Invalid file, the size attribute of "
-				"a chunk points behind the end of the file");
+			throw new ImportErrorException("LWO2: Chunk length points behind the file");
 			break;
 		}
 		LE_NCONST uint8_t* const next = mFileBuffer+head->length;
@@ -738,9 +1022,13 @@ void LWOImporter::LoadLWO2File()
 
 				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LAYR,16);
 
-				// and parse its properties
-				mFileBuffer += 16;
-				ParseString(layer.mName,head->length-16);
+				// and parse its properties, e.g. the pivot point
+				mFileBuffer += 2;
+				mCurLayer->mPivot.x = GetF4();
+				mCurLayer->mPivot.y = GetF4();
+				mCurLayer->mPivot.z = GetF4();
+				mFileBuffer += 2;
+				GetS0(layer.mName,head->length-16);
 
 				// if the name is empty, generate a default name
 				if (layer.mName.empty())
@@ -751,7 +1039,7 @@ void LWOImporter::LoadLWO2File()
 				}
 
 				if (mFileBuffer + 2 <= next)
-					layer.mParent = *((uint16_t*)mFileBuffer);
+					layer.mParent = GetU2();
 
 				break;
 			}
@@ -765,11 +1053,17 @@ void LWOImporter::LoadLWO2File()
 				break;
 			}
 			// vertex tags
-		//case AI_LWO_VMAD:
+		case AI_LWO_VMAD:
+			if (mCurLayer->mFaces.empty())
+			{
+				DefaultLogger::get()->warn("LWO2: Unexpected VMAD chunk");
+				break;
+			}
+			// --- intentionally no break here
 		case AI_LWO_VMAP:
 			{
 				if (mCurLayer->mTempPoints.empty())
-					DefaultLogger::get()->warn("LWO2: Unexpected VMAD/VMAP chunk");
+					DefaultLogger::get()->warn("LWO2: Unexpected VMAP chunk");
 				else LoadLWO2VertexMap(head->length,head->type == AI_LWO_VMAD);
 				break;
 			}
@@ -804,6 +1098,13 @@ void LWOImporter::LoadLWO2File()
 				LoadLWO2Surface(head->length);
 				break;
 			}
+
+			// clip chunk
+		case AI_LWO_CLIP:
+			{
+				LoadLWO2Clip(head->length);
+				break;
+			}
 		}
 		mFileBuffer = next;
 	}

+ 151 - 8
code/LWOLoader.h

@@ -43,9 +43,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define AI_LWOLOADER_H_INCLUDED
 
 #include "../include/aiTypes.h"
+#include "../include/DefaultLogger.h"
 
-#include "BaseImporter.h"
 #include "LWOFileData.h"
+#include "BaseImporter.h"
 #include "MaterialSystem.h"
 
 struct aiTexture;
@@ -63,6 +64,7 @@ using namespace LWO;
  *         Methods named "xxx" are used to preprocess the loaded data -
  *         they aren't specific to one format version, either
 */
+// ---------------------------------------------------------------------------
 class LWOImporter : public BaseImporter
 {
 	friend class Importer;
@@ -120,6 +122,17 @@ private:
 	 */
 	void LoadLWO2File();
 
+
+	// -------------------------------------------------------------------
+	/** Parsing functions used for all file format versions
+	*/
+	inline void GetS0(std::string& out,unsigned int max);
+	inline float GetF4();
+	inline uint32_t GetU4();
+	inline uint16_t GetU2();
+	inline uint8_t  GetU1();
+
+
 	// -------------------------------------------------------------------
 	/** Loads a surface chunk from an LWOB file
 	 *  @param size Maximum size to be read, in bytes.  
@@ -135,9 +148,18 @@ private:
 	// -------------------------------------------------------------------
 	/** Loads a texture block from a LWO2 file.
 	 *  @param size Maximum size to be read, in bytes.  
-	 *  @param type Type of the texture block - PROC, GRAD or IMAP
+	 *  @param head Header of the SUF.BLOK header
+	 */
+	void LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader* head,
+		unsigned int size );
+
+	// -------------------------------------------------------------------
+	/** Loads a shader block from a LWO2 file.
+	 *  @param size Maximum size to be read, in bytes.  
+	 *  @param head Header of the SUF.BLOK header
 	 */
-	void LoadLWO2TextureBlock(uint32_t type, unsigned int size );
+	void LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader* head,
+		unsigned int size );
 
 	// -------------------------------------------------------------------
 	/** Loads an image map from a LWO2 file
@@ -183,6 +205,12 @@ private:
 	*/
 	void LoadLWOPoints(unsigned int length);
 
+	// -------------------------------------------------------------------
+	/** Load a clip from a CLIP chunk
+	 *  @param length Size of the chunk
+	*/
+	void LoadLWO2Clip(unsigned int length);
+
 
 	// -------------------------------------------------------------------
 	/** Count vertices and faces in a LWOB/LWO2 file
@@ -206,6 +234,7 @@ private:
 		LE_NCONST uint16_t*& cursor, 
 		const uint16_t* const end);
 
+	// -------------------------------------------------------------------
 	void CopyFaceIndicesLWOB(LWO::FaceList::iterator& it,
 		LE_NCONST uint16_t*& cursor, 
 		const uint16_t* const end, 
@@ -213,14 +242,27 @@ private:
 
 	// -------------------------------------------------------------------
 	/** Resolve the tag and surface lists that have been loaded.
-	* Generates the mMapping table.
+	*   Generates the mMapping table.
 	*/
 	void ResolveTags();
 
 	// -------------------------------------------------------------------
-	/** Parse a string from the current file position
+	/** Resolve the clip list that has been loaded.
+	*   Replaces clip references with real clips.
 	*/
-	void ParseString(std::string& out,unsigned int max);
+	void ResolveClips();
+
+	// -------------------------------------------------------------------
+	/** Add a texture list to an output material description.
+	 *
+	 *  @param pcMat Output material
+	 *  @param in Input texture list
+	 *  @param type Type identifier of the texture list. This is the string
+	 *    that appears in the middle of all material keys - e.g. "diffuse",
+	 *    "shininess", "glossiness" or "specular". 
+	*/
+	bool HandleTextures(MaterialHelper* pcMat, const TextureList& in,
+		const char* type);
 
 	// -------------------------------------------------------------------
 	/** Adjust a texture path
@@ -241,10 +283,15 @@ private:
 	 *  @param out Output list. The members are indices into the 
 	 *    UV/VC channel lists of the layer
 	*/
-	void FindUVChannels(const LWO::Surface& surf, 
-		const LWO::Layer& layer,
+	void FindUVChannels(/*const*/ LWO::Surface& surf, 
+		/*const*/ LWO::Layer& layer,
 		unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS]);
 
+	// -------------------------------------------------------------------
+	void FindUVChannels(LWO::TextureList& list, LWO::Layer& layer,
+		unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS], unsigned int& next);
+
+	// -------------------------------------------------------------------
 	void FindVCChannels(const LWO::Surface& surf, 
 		const LWO::Layer& layer,
 		unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS]);
@@ -271,6 +318,27 @@ private:
 	*/
 	int ReadVSizedIntLWO2(uint8_t*& inout);
 
+	// -------------------------------------------------------------------
+	/** Assign a value from a VMAP to a vertex and all vertices 
+	 *  attached to it.
+	 *  @param base VMAP destination data
+	 *  @param numRead Number of float's to be read
+	 *  @param idx Absolute index of the first vertex
+	 *  @param data Value of the VMAP to be assigned - read numRead
+	 *    floats from this array.
+	*/
+	void DoRecursiveVMAPAssignment(VMapEntry* base, unsigned int numRead, 
+		unsigned int idx, float* data);
+
+	// -------------------------------------------------------------------
+	/** Compute normal vectors for a mesh
+	 *  @param mesh Input mesh
+	 *  @param smoothingGroups Smoothing-groups-per-face array
+	 *  @param surface Surface for the mesh 
+	*/
+	void ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>& smoothingGroups,
+		const LWO::Surface& surface);
+
 protected:
 
 	/** true if the file is a LWO2 file*/
@@ -305,6 +373,81 @@ protected:
 	aiScene* pScene;
 };
 
+
+// ------------------------------------------------------------------------------------------------
+inline float LWOImporter::GetF4()
+{
+	float f = *((float*)mFileBuffer);mFileBuffer += 4;
+	AI_LSWAP4(f);
+	return f;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline uint32_t LWOImporter::GetU4()
+{
+	uint32_t f = *((uint32_t*)mFileBuffer);mFileBuffer += 4;
+	AI_LSWAP4(f);
+	return f;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline uint16_t LWOImporter::GetU2()
+{
+	uint16_t f = *((uint16_t*)mFileBuffer);mFileBuffer += 2;
+	AI_LSWAP2(f);
+	return f;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline uint8_t LWOImporter::GetU1()
+{
+	return *mFileBuffer++;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline int LWOImporter::ReadVSizedIntLWO2(uint8_t*& inout)
+{
+	int i;
+	int c = *inout;inout++;
+	if(c != 0xFF)
+	{
+		i = c << 8;
+		c = *inout;inout++;
+		i |= c;
+	}
+	else
+	{
+		c = *inout;inout++;
+		i = c << 16;
+		c = *inout;inout++;
+		i |= c << 8;
+		c = *inout;inout++;
+		i |= c;
+	}
+	return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline void LWOImporter::GetS0(std::string& out,unsigned int max)
+{
+	unsigned int iCursor = 0;
+	const char*sz = (const char*)mFileBuffer;
+	while (*mFileBuffer)
+	{
+		if (++iCursor > max)
+		{
+			DefaultLogger::get()->warn("LWO: Invalid file, string is is too long");
+			break;
+		}
+		++mFileBuffer;
+	}
+	unsigned int len = (unsigned int) ((const char*)mFileBuffer-sz);
+	out = std::string(sz,len);
+	mFileBuffer += (len&0x1 ? 1 : 2);
+}
+
+
+
 } // end of namespace Assimp
 
 #endif // AI_LWOIMPORTER_H_INCLUDED

+ 542 - 64
code/LWOMaterial.cpp

@@ -58,6 +58,142 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 using namespace Assimp;
 
+// ------------------------------------------------------------------------------------------------
+template <class T>
+T lerp(const T& one, const T& two, float val)
+{
+	return one + (two-one)*val;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline aiTextureMapMode GetMapMode(LWO::Texture::Wrap in)
+{
+	switch (in)
+	{	
+		case LWO::Texture::REPEAT:
+			return aiTextureMapMode_Wrap;
+		case LWO::Texture::MIRROR:
+			return aiTextureMapMode_Mirror;
+		case LWO::Texture::RESET:
+			DefaultLogger::get()->warn("LWO2: Unsupported texture map mode: RESET");
+		case LWO::Texture::EDGE:
+			return aiTextureMapMode_Clamp;
+	}
+	return (aiTextureMapMode)0;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool LWOImporter::HandleTextures(MaterialHelper* pcMat, const TextureList& in, const char* type)
+{
+	ai_assert(NULL != pcMat && NULL != type);
+
+	unsigned int cur = 0, temp = 0;
+	char buffer[512];
+	aiString s;
+	bool ret = false;
+
+	for (TextureList::const_iterator it = in.begin(), end = in.end();
+		 it != end;++it)
+	{
+		if (!(*it).enabled || !(*it).bCanUse || 0xffffffff == (*it).mRealUVIndex)continue;
+		ret = true;
+
+		// add the path to the texture
+		sprintf(buffer,"$tex.file.%s[%i]",type,cur);
+
+		// The older LWOB format does not use indirect references to clips.
+		// The file name of a texture is directly specified in the tex chunk.
+		if (mIsLWO2)
+		{
+			// find the corresponding clip
+			ClipList::iterator clip = mClips.begin();
+			temp = (*it).mClipIdx;
+			for (ClipList::iterator end = mClips.end(); clip != end; ++clip)
+			{
+				if ((*clip).idx == temp)
+				{
+					break;
+				}
+			}
+			if (mClips.end() == clip)
+			{
+				DefaultLogger::get()->error("LWO2: Clip index is out of bounds");
+				temp = 0;
+			}
+			if (Clip::UNSUPPORTED == (*clip).type)
+			{
+				DefaultLogger::get()->error("LWO2: Clip type is not supported");
+				continue;
+			}
+			AdjustTexturePath((*clip).path);
+			s.Set((*clip).path);
+		}
+		else 
+		{
+			std::string ss = (*it).mFileName;
+			if (!ss.length())
+			{
+				DefaultLogger::get()->error("LWOB: Empty file name");
+				continue;
+			}
+			AdjustTexturePath(ss);
+			s.Set(ss);
+		}
+		pcMat->AddProperty(&s,buffer);
+
+		// add the blend factor
+		sprintf(buffer,"$tex.blend.%s[%i]",type,cur);
+		pcMat->AddProperty(&(*it).mStrength,1,buffer);
+
+		// add the blend operation
+		sprintf(buffer,"$tex.op.%s[%i]",type,cur);
+		switch ((*it).blendType)
+		{
+			case LWO::Texture::Normal:
+			case LWO::Texture::Multiply:
+				temp = (unsigned int)aiTextureOp_Multiply;
+				break;
+
+			case LWO::Texture::Subtractive:
+			case LWO::Texture::Difference:
+				temp = (unsigned int)aiTextureOp_Subtract;
+				break;
+
+			case LWO::Texture::Divide:
+				temp = (unsigned int)aiTextureOp_Divide;
+				break;
+
+			case LWO::Texture::Additive:
+				temp = (unsigned int)aiTextureOp_Add;
+				break;
+
+			default:
+				temp = (unsigned int)aiTextureOp_Multiply;
+				DefaultLogger::get()->warn("LWO2: Unsupported texture blend mode: alpha or displacement");
+
+		}
+		pcMat->AddProperty<int>((int*)&temp,1,buffer);
+
+		// add the UV source index
+		sprintf(buffer,"$tex.uvw.%s[%i]",type,cur);
+		temp = (*it).mRealUVIndex;
+		pcMat->AddProperty<int>((int*)&temp,1,buffer);
+
+		// add the u-wrapping
+		sprintf(buffer,"$tex.mapmodeu.%s[%i]",type,cur);
+		temp = (unsigned int)GetMapMode((*it).wrapModeWidth);
+		pcMat->AddProperty<int>((int*)&temp,1,buffer);
+
+		// add the v-wrapping
+		sprintf(buffer,"$tex.mapmodev.%s[%i]",type,cur);
+		temp = (unsigned int)GetMapMode((*it).wrapModeHeight);
+		pcMat->AddProperty<int>((int*)&temp,1,buffer);
+
+		++cur;
+	}
+	return ret;
+}
+
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat)
 {
@@ -68,13 +204,19 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat
 
 	int i = surf.bDoubleSided ? 1 : 0;
 	pcMat->AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
+
+	// add the refraction index and the bump intensity
+	pcMat->AddProperty<float>(&surf.mIOR,1,AI_MATKEY_REFRACTI);
+	pcMat->AddProperty<float>(&surf.mBumpIntensity,1,AI_MATKEY_BUMPSCALING);
 	
+	aiShadingMode m;
 	if (surf.mSpecularValue && surf.mGlossiness)
 	{
-		// this is only an assumption, needs to be confirmed.
-		// the values have been tweaked by hand and seem to be correct.
 		float fGloss;
-		if (mIsLWO2)fGloss = surf.mGlossiness * 50.0f;
+		if (mIsLWO2)
+		{
+			fGloss = pow( surf.mGlossiness*10.0f+2.0f, 2.0f);
+		}
 		else
 		{
 			if (16.0f >= surf.mGlossiness)fGloss = 6.0f;
@@ -85,18 +227,14 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat
 
 		pcMat->AddProperty<float>(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
 		pcMat->AddProperty<float>(&fGloss,1,AI_MATKEY_SHININESS);
+		m = aiShadingMode_Phong;
 	}
-
-	// (the diffuse value is just a scaling factor)
-	aiColor3D clr = surf.mColor;
-	clr.r *= surf.mDiffuseValue;
-	clr.g *= surf.mDiffuseValue;
-	clr.b *= surf.mDiffuseValue;
-	pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+	else m = aiShadingMode_Gouraud;
 
 	// specular color
-	clr.b = clr.g  = clr.r = surf.mSpecularValue;
+	aiColor3D clr = lerp( aiColor3D(1.f,1.f,1.f), surf.mColor, surf.mColorHighlights );
 	pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_SPECULAR);
+	pcMat->AddProperty<float>(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
 
 	// emissive color
 	// (luminosity is not really the same but it affects the surface in 
@@ -108,15 +246,113 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat
 	float f = 1.0f-surf.mTransparency;
 	pcMat->AddProperty<float>(&f,1,AI_MATKEY_OPACITY);
 
-	// now handle all textures ...
-	// TODO
+	// ADD TEXTURES to the material
+	// TODO: find out how we can handle COLOR textures correctly...
+	bool b = HandleTextures(pcMat,surf.mColorTextures,"diffuse");
+	b = (b || HandleTextures(pcMat,surf.mDiffuseTextures,"diffuse"));
+	HandleTextures(pcMat,surf.mSpecularTextures,"specular");
+	HandleTextures(pcMat,surf.mGlossinessTextures,"shininess");
+	HandleTextures(pcMat,surf.mBumpTextures,"height");
+	HandleTextures(pcMat,surf.mOpacityTextures,"opacity");
+
+	// now we need to know which shader we must use
+	// iterate through the shader list of the surface and 
+	// search for a name we know 
+	for (ShaderList::const_iterator it = surf.mShaders.begin(), end = surf.mShaders.end();
+		 it != end;++it)
+	{
+		if (!(*it).enabled)continue;
+		if ((*it).functionName == "LW_SuperCelShader" ||
+			(*it).functionName == "AH_CelShader")
+		{
+			m = aiShadingMode_Toon;
+			break;
+		}
+		else if ((*it).functionName == "LW_RealFresnel" ||
+			(*it).functionName == "LW_FastFresnel")
+		{
+			m = aiShadingMode_Fresnel;
+			break;
+		}
+		else
+		{
+			DefaultLogger::get()->warn("LWO2: Unknown surface shader: " + (*it).functionName);
+		}
+	}
+	if (surf.mMaximumSmoothAngle <= 0.0f)m = aiShadingMode_Flat;
+	pcMat->AddProperty((int*)&m,1,AI_MATKEY_SHADING_MODEL);
+
+	// (the diffuse value is just a scaling factor)
+	// If a diffuse texture is set, we set this value to 1.0
+	clr = (b ? aiColor3D(1.f,1.f,1.f) : surf.mColor);
+	clr.r *= surf.mDiffuseValue;
+	clr.g *= surf.mDiffuseValue;
+	clr.b *= surf.mDiffuseValue;
+	pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::FindUVChannels(LWO::TextureList& list, LWO::Layer& layer,
+	unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS], unsigned int& next)
+{
+	for (TextureList::iterator it = list.begin(), end = list.end();
+		 it != end;++it)
+	{
+		if (!(*it).enabled || !(*it).bCanUse || 0xffffffff != (*it).mRealUVIndex)continue;
+		switch ((*it).mapMode)
+		{
+			// TODO: implement these special mappings ...
+		case LWO::Texture::Spherical:
+		case LWO::Texture::Cylindrical:
+		case LWO::Texture::Cubic:
+		case LWO::Texture::Planar:
+		case LWO::Texture::FrontProjection:
+
+			DefaultLogger::get()->warn("LWO2: Only UV mapping is currently supported.");
+			continue;
+
+		default: ;
+		}
+		for (unsigned int i = 0; i < layer.mUVChannels.size();++i)
+		{
+			if ((*it).mUVChannelIndex == layer.mUVChannels[i].name)
+			{
+				// check whether we have this channel already
+				for (unsigned int m = 0; m < next;++m)
+				{
+					if (i == out[m])
+					{
+						(*it).mRealUVIndex = m;
+					}
+				}
+				if (0xffffffff == (*it).mRealUVIndex)
+				{
+					(*it).mRealUVIndex = next;
+					out[next++] = i;
+					if (AI_MAX_NUMBER_OF_TEXTURECOORDS != next)
+						out[next] = 0xffffffff;
+					break;
+				}
+			}
+		}
+		if (0xffffffff == (*it).mRealUVIndex)
+			DefaultLogger::get()->error("LWO2: Unable to find matching UV channel for a texture");
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
-void LWOImporter::FindUVChannels(const LWO::Surface& surf, const LWO::Layer& layer,
+void LWOImporter::FindUVChannels(LWO::Surface& surf, LWO::Layer& layer,
 	unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS])
 {
 	out[0] = 0xffffffff;
+	unsigned int next = 0;
+
+	FindUVChannels(surf.mColorTextures,layer,out,next);
+	FindUVChannels(surf.mDiffuseTextures,layer,out,next);
+	FindUVChannels(surf.mSpecularTextures,layer,out,next);
+	FindUVChannels(surf.mGlossinessTextures,layer,out,next);
+	FindUVChannels(surf.mOpacityTextures,layer,out,next);
+	FindUVChannels(surf.mBumpTextures,layer,out,next);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -124,133 +360,375 @@ void LWOImporter::FindVCChannels(const LWO::Surface& surf, const LWO::Layer& lay
 	unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS])
 {
 	out[0] = 0xffffffff;
+	if (surf.mVCMap.length())
+	{
+		for (unsigned int i = 0; i < layer.mVColorChannels.size();++i)
+		{
+			if (surf.mVCMap == layer.mVColorChannels[i].name)
+			{
+				out[0] = i;
+				out[1] = 0xffffffff;
+				return;
+			}
+		}
+		DefaultLogger::get()->warn("LWO2: Unable to find vertex color channel: " + surf.mVCMap);
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWO2ImageMap(unsigned int size, LWO::Texture& tex )
 {
-	//LE_NCONST uint8_t* const end = mFileBuffer + size;
+	LE_NCONST uint8_t* const end = mFileBuffer + size;
+	while (true)
+	{
+		if (mFileBuffer + 6 >= end)break;
+		LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+		if (mFileBuffer + head->length > end)
+			throw new ImportErrorException("LWO2: Invalid SURF.BLOCK chunk length");
+
+		LE_NCONST uint8_t* const next = mFileBuffer+head->length;
+		switch (head->type)
+		{
+		case AI_LWO_PROJ:
+			tex.wrapModeWidth  = (Texture::Wrap)GetU2();
+			tex.wrapModeHeight = (Texture::Wrap)GetU2();
+			break;
+		case AI_LWO_AXIS:
+			tex.majorAxis = (Texture::Axes)GetU2();
+			break;
+		case AI_LWO_IMAG:
+			tex.mClipIdx = GetU2();
+			break;
+		case AI_LWO_VMAP:
+			GetS0(tex.mUVChannelIndex,head->length);
+			break;
+		case AI_LWO_WRPH:
+			tex.wrapAmountH = GetF4();
+			break;
+		case AI_LWO_WRPW:
+			tex.wrapAmountW = GetF4();
+			break;
+		}
+		mFileBuffer = next;
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWO2Procedural(unsigned int size, LWO::Texture& tex )
 {
 	// --- not supported at the moment
+	DefaultLogger::get()->error("LWO2: Found procedural texture, this is not supported");
+	tex.bCanUse = false;
 }
 
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWO2Gradient(unsigned int size, LWO::Texture& tex  )
 {
 	// --- not supported at the moment
+	DefaultLogger::get()->error("LWO2: Found gradient texture, this is not supported");
+	tex.bCanUse = false;
 }
 
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWO2TextureHeader(unsigned int size, LWO::Texture& tex )
 {
-	//LE_NCONST uint8_t* const end = mFileBuffer + size;
+	LE_NCONST uint8_t* const end = mFileBuffer + size;
+
+	// get the ordinal string
+	GetS0( tex.ordinal, size);
+
+	// we could crash later if this is an empty string ...
+	if (!tex.ordinal.length())
+	{
+		DefaultLogger::get()->error("LWO2: Ill-formed SURF.BLOK ordinal string");
+		tex.ordinal = "\x00";
+	}
+	while (true)
+	{
+		if (mFileBuffer + 6 >= end)break;
+		LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+		if (mFileBuffer + head->length > end)
+			throw new ImportErrorException("LWO2: Invalid texture header chunk length");
+
+		LE_NCONST uint8_t* const next = mFileBuffer+head->length;
+		switch (head->type)
+		{
+		case AI_LWO_CHAN:
+			tex.type = GetU4();
+			break;
+		case AI_LWO_ENAB:
+			tex.enabled = GetU2() ? true : false;
+			break;
+		case AI_LWO_OPAC:
+			tex.blendType = (Texture::BlendType)GetU2();
+			tex.mStrength = GetF4();
+			break;
+		}
+		mFileBuffer = next;
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
-void LWOImporter::LoadLWO2TextureBlock(uint32_t type, unsigned int size )
+void LWOImporter::LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader* head, unsigned int size )
 {
-	//LE_NCONST uint8_t* const end = mFileBuffer + size;
+	ai_assert(!mSurfaces->empty());
+	LWO::Surface& surf = mSurfaces->back();
+	LWO::Texture tex;
 
-	//LWO::Surface& surf = mSurfaces->back();
-	//LWO::Texture tex;
+	// load the texture header
+	LoadLWO2TextureHeader(head->length,tex);
+	size -= head->length + 6;
 
 	// now get the exact type of the texture
+	switch (head->type)
+	{
+	case AI_LWO_PROC:
+		LoadLWO2Procedural(size,tex);
+		break;
+	case AI_LWO_GRAD:
+		LoadLWO2Gradient(size,tex); 
+		break;
+	case AI_LWO_IMAP:
+		LoadLWO2ImageMap(size,tex);
+	}
+
+	// get the destination channel
+	TextureList* listRef = NULL;
+	switch (tex.type)
+	{
+	case AI_LWO_COLR:
+		listRef = &surf.mColorTextures;break;
+	case AI_LWO_DIFF:
+		listRef = &surf.mDiffuseTextures;break;
+	case AI_LWO_SPEC:
+		listRef = &surf.mSpecularTextures;break;
+	case AI_LWO_GLOS:
+		listRef = &surf.mGlossinessTextures;break;
+	case AI_LWO_BUMP:
+		listRef = &surf.mBumpTextures;break;
+	case AI_LWO_TRAN:
+		listRef = &surf.mOpacityTextures;break;
+	default:
+		DefaultLogger::get()->warn("LWO2: Encountered unknown texture type");
+		return;
+	}
+
+	// now attach the texture to the parent surface - sort by ordinal string
+	for (TextureList::iterator it = listRef->begin();
+		it != listRef->end(); ++it)
+	{
+		if (::strcmp(tex.ordinal.c_str(),(*it).ordinal.c_str()) < 0)
+		{
+			listRef->insert(it,tex);
+			return;
+		}
+	}
+	listRef->push_back(tex);
 }
 
 // ------------------------------------------------------------------------------------------------
-void LWOImporter::LoadLWO2Surface(unsigned int size)
+void LWOImporter::LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader* head, unsigned int size )
 {
 	LE_NCONST uint8_t* const end = mFileBuffer + size;
 
-	mSurfaces->push_back( LWO::Surface () );
+	ai_assert(!mSurfaces->empty());
 	LWO::Surface& surf = mSurfaces->back();
+	LWO::Shader shader;
+
+	// get the ordinal string
+	GetS0( shader.ordinal, size);
+
+	// we could crash later if this is an empty string ...
+	if (!shader.ordinal.length())
+	{
+		DefaultLogger::get()->error("LWO2: Ill-formed SURF.BLOK ordinal string");
+		shader.ordinal = "\x00";
+	}
 
-	ParseString(surf.mName,size);
-	mFileBuffer+=surf.mName.length()+1;
-	 // skip one byte if the length of the surface name is odd
-	if (!(surf.mName.length() & 1))++mFileBuffer;
-	mFileBuffer += 2;
+	// read the header
 	while (true)
 	{
 		if (mFileBuffer + 6 >= end)break;
+		LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+		if (mFileBuffer + head->length > end)
+			throw new ImportErrorException("LWO2: Invalid shader header chunk length");
+
+		LE_NCONST uint8_t* const next = mFileBuffer+head->length;
+		switch (head->type)
+		{
+		case AI_LWO_ENAB:
+			shader.enabled = GetU2() ? true : false;
+		}
+		mFileBuffer = next;
+	}
+
+	// process other subchunks ...
+	while (true)
+	{
+		if (mFileBuffer + 6 >= end)break;
+		LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+		if (mFileBuffer + head->length > end)
+			throw new ImportErrorException("LWO2: Invalid shader data chunk length");
+
+		LE_NCONST uint8_t* const next = mFileBuffer+head->length;
+		switch (head->type)
+		{
+		case AI_LWO_FUNC:
+			GetS0( shader.functionName, head->length );
+		}
+		mFileBuffer = next;
+	}
 
-		// no proper IFF header here - the chunk length is specified as int16
-		uint32_t head_type		= *((LE_NCONST uint32_t*)mFileBuffer);mFileBuffer+=4;
-		uint16_t head_length	= *((LE_NCONST uint16_t*)mFileBuffer);mFileBuffer+=2;
-		AI_LSWAP4(head_type);
-		AI_LSWAP2(head_length);
-		if (mFileBuffer + head_length > end)
+	// now attach the shader to the parent surface - sort by ordinal string
+	for (ShaderList::iterator it = surf.mShaders.begin();
+		it != surf.mShaders.end(); ++it)
+	{
+		if (::strcmp(shader.ordinal.c_str(),(*it).ordinal.c_str()) < 0)
 		{
-			throw new ImportErrorException("LWO2: Invalid file, the size attribute of "
-				"a surface sub chunk points behind the end of the file");
+			surf.mShaders.insert(it,shader);
+			return;
 		}
-		LE_NCONST uint8_t* const next = mFileBuffer+head_length;
-		switch (head_type)
+	}
+	surf.mShaders.push_back(shader);
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2Surface(unsigned int size)
+{
+	LE_NCONST uint8_t* const end = mFileBuffer + size;
+
+	mSurfaces->push_back( LWO::Surface () );
+	LWO::Surface& surf = mSurfaces->back();
+
+	GetS0(surf.mName,size);
+
+	// check whether this surface was derived from any other surface
+	std::string derived;
+	GetS0(derived,(unsigned int)(end - mFileBuffer));
+	if (derived.length())
+	{
+
+		// yes, find this surface
+		for (SurfaceList::iterator it = mSurfaces->begin(), end = mSurfaces->end()-1;
+			 it != end; ++it)
+		{
+			if ((*it).mName == derived)
+			{
+				// we have it ...
+				surf = *it;
+				derived.clear();
+			}
+		}
+		if (!derived.size())
+			DefaultLogger::get()->warn("LWO2: Unable to find source surface: " + derived);
+	}
+
+	while (true)
+	{
+		if (mFileBuffer + 6 >= end)break;
+		LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+		if (mFileBuffer + head->length > end)
+			throw new ImportErrorException("LWO2: Invalid surface chunk length");
+
+		LE_NCONST uint8_t* const next = mFileBuffer+head->length;
+		switch (head->type)
 		{
 			// diffuse color
 		case AI_LWO_COLR:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,COLR,12);
-				surf.mColor.r = ((float*)mFileBuffer)[0];
-				surf.mColor.g = ((float*)mFileBuffer)[1];
-				surf.mColor.b = ((float*)mFileBuffer)[2];
-				AI_LSWAP4(surf.mColor.r);
-				AI_LSWAP4(surf.mColor.g);
-				AI_LSWAP4(surf.mColor.b);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,COLR,12);
+				surf.mColor.r = GetF4();
+				surf.mColor.g = GetF4();
+				surf.mColor.b = GetF4();
 				break;
 			}
 			// diffuse strength ... hopefully
 		case AI_LWO_DIFF:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,DIFF,4);
-				surf.mDiffuseValue = *((float*)mFileBuffer);
-				AI_LSWAP4(surf.mDiffuseValue);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,DIFF,4);
+				surf.mDiffuseValue = GetF4();
 				break;
 			}
 			// specular strength ... hopefully
 		case AI_LWO_SPEC:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,SPEC,4);
-				surf.mSpecularValue = *((float*)mFileBuffer);
-				AI_LSWAP4(surf.mSpecularValue);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPEC,4);
+				surf.mSpecularValue = GetF4();
 				break;
 			}
 			// transparency
 		case AI_LWO_TRAN:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,TRAN,4);
-				surf.mTransparency = *((float*)mFileBuffer);
-				AI_LSWAP4(surf.mTransparency);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TRAN,4);
+				surf.mTransparency = GetF4();
 				break;
 			}
 			// glossiness
 		case AI_LWO_GLOS:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,GLOS,4);
-				surf.mGlossiness = *((float*)mFileBuffer);
-				AI_LSWAP4(surf.mGlossiness);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,GLOS,4);
+				surf.mGlossiness = GetF4();
+				break;
+			}
+			// bump intensity
+		case AI_LWO_BUMP:
+			{
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,BUMP,4);
+				surf.mBumpIntensity = GetF4();
+				break;
+			}
+			// color highlights
+		case AI_LWO_CLRH:
+			{
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,CLRH,4);
+				surf.mColorHighlights = GetF4();
+				break;
+			}
+			// index of refraction
+		case AI_LWO_RIND:
+			{
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,RIND,4);
+				surf.mIOR = GetF4();
+				break;
+			}
+			// polygon sidedness
+		case AI_LWO_SIDE:
+			{
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SIDE,2);
+				surf.bDoubleSided = (3 == GetU2());
+				break;
+			}
+			// maximum smoothing angle
+		case AI_LWO_SMAN:
+			{
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
+				surf.mMaximumSmoothAngle = GetF4();
 				break;
 			}
 			// surface bock entry
 		case AI_LWO_BLOK:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,BLOK,4);
-				uint32_t type = *((uint32_t*)mFileBuffer);
-				AI_LSWAP4(type);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,BLOK,4);
+				LE_NCONST IFF::SubChunkHeader* head2 = IFF::LoadSubChunk(mFileBuffer);
 
-				switch (type)
+				switch (head2->type)
 				{
-				case AI_LWO_IMAP:
 				case AI_LWO_PROC:
-					break;
 				case AI_LWO_GRAD:
-					mFileBuffer+=4;
-					LoadLWO2TextureBlock(type,head_length-4);
+				case AI_LWO_IMAP:
+					LoadLWO2TextureBlock(head2, head->length);
+					break;
+				case AI_LWO_SHDR:
+					LoadLWO2ShaderBlock(head2, head->length);
 					break;
+
+				default:
+					DefaultLogger::get()->warn("LWO2: Found an unsupported surface BLOK");
 				};
 
 				break;

+ 1 - 1
code/MDRLoader.cpp

@@ -226,7 +226,7 @@ void MDRImporter::InternReadFile(
 	file->Read( &mBuffer2[0], 1, fileSize);
 	mBuffer = &mBuffer2[0];
 
-	// validate the file header
+	// validate the file header and do BigEndian byte swapping for all sub headers
 	this->pcHeader = (BE_NCONST MDR::Header*)this->mBuffer;
 	this->ValidateHeader();
 }

+ 39 - 11
code/SGSpatialSort.cpp

@@ -83,9 +83,10 @@ void SGSpatialSort::Prepare()
 // ------------------------------------------------------------------------------------------------
 // Returns an iterator for all positions close to the given position.
 void SGSpatialSort::FindPositions( const aiVector3D& pPosition, 
-									  uint32_t pSG,
-									  float pRadius,
-									  std::vector<unsigned int>& poResults) const
+	uint32_t pSG,
+	float pRadius,
+	std::vector<unsigned int>& poResults,
+	bool exactMatch /*= false*/) const
 {
 	float dist = pPosition * mPlaneNormal;
 	float minDist = dist - pRadius, maxDist = dist + pRadius;
@@ -126,17 +127,44 @@ void SGSpatialSort::FindPositions( const aiVector3D& pPosition,
 	// Add all positions inside the distance range within the given radius to the result aray
 
 	float squareEpsilon = pRadius * pRadius;
-	std::vector<Entry>::const_iterator it = mPositions.begin() + index;
-	while( it->mDistance < maxDist)
+	std::vector<Entry>::const_iterator it  = mPositions.begin() + index;
+	std::vector<Entry>::const_iterator end = mPositions.end();
+
+	if (exactMatch)
 	{
-		if((it->mPosition - pPosition).SquareLength() < squareEpsilon &&
-			(it->mSmoothGroups & pSG || 0 == it->mSmoothGroups || 0 == pSG))
+		while( it->mDistance < maxDist)
+		{
+			if((it->mPosition - pPosition).SquareLength() < squareEpsilon && it->mSmoothGroups == pSG)
+			{
+				poResults.push_back( it->mIndex);
+			}
+			++it;
+			if( end == it )break;
+		}
+	}
+	else
+	{
+		// if the given smoothing group is 0, we'll return all surrounding vertices
+		if (!pSG)
+		{
+			while( it->mDistance < maxDist)
+			{
+				if((it->mPosition - pPosition).SquareLength() < squareEpsilon)
+					poResults.push_back( it->mIndex);
+				++it;
+				if( end == it)break;
+			}
+		}
+		else while( it->mDistance < maxDist)
 		{
-			poResults.push_back( it->mIndex);
+			if((it->mPosition - pPosition).SquareLength() < squareEpsilon &&
+				(it->mSmoothGroups & pSG || !it->mSmoothGroups))
+			{
+				poResults.push_back( it->mIndex);
+			}
+			++it;
+			if( end == it)break;
 		}
-		++it;
-		if( it == mPositions.end())
-			break;
 	}
 }
 

+ 26 - 15
code/SGSpatialSort.h

@@ -46,13 +46,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <vector>
 #include "../include/aiTypes.h"
 
-namespace Assimp
-{
+namespace Assimp	{
 
-// ------------------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------
 /** Specialized version of SpatialSort to support smoothing groups
- *  This is used in the .3ds loader
+ *  This is used in by the 3DS, ASE and LWO loaders. 3DS and ASE share their 
+ *  normal computation code in SmoothingGroups.inl, the LWO loader has its own
+ *  implementation to handle all details of its file format correctly.
  */
+// ----------------------------------------------------------------------------------
 class SGSpatialSort
 {
 public:
@@ -60,7 +62,8 @@ public:
 	SGSpatialSort();
 
 	// -------------------------------------------------------------------
-	/** Construction from a given face array, handling smoothing groups properly
+	/** Construction from a given face array, handling smoothing groups
+	 *  properly
 	 */
 	SGSpatialSort(const std::vector<aiVector3D>& vPositions);
 
@@ -74,7 +77,7 @@ public:
 		unsigned int smoothingGroup);
 
 	// -------------------------------------------------------------------
-	/** Prepare the spatial sorter for use
+	/** Prepare the spatial sorter for use. This step runs in O(logn)
 	 */
 	void Prepare();
 
@@ -85,27 +88,35 @@ public:
 	/** Returns an iterator for all positions close to the given position.
 	 * @param pPosition The position to look for vertices.
 	 * @param pSG Only included vertices with at least one shared smooth group
-	 * @param pRadius Maximal distance from the position a vertex may have to be counted in.
-	 * @param poResults The container to store the indices of the found positions. Will be emptied
-	 *   by the call so it may contain anything.
+	 * @param pRadius Maximal distance from the position a vertex may have
+	 *   to be counted in.
+	 * @param poResults The container to store the indices of the found
+	 *   positions. Will be emptied by the call so it may contain anything.
+	 * @param exactMatch Specifies whether smoothing groups are bit masks 
+	 *   (false) or integral values (true). In the latter case, a vertex
+	 *   cannot belong to more than one smoothing group.
 	 * @return An iterator to iterate over all vertices in the given area.
 	 */
+	// -------------------------------------------------------------------
 	void FindPositions( const aiVector3D& pPosition, uint32_t pSG,
-		float pRadius, std::vector<unsigned int>& poResults) const;
+		float pRadius, std::vector<unsigned int>& poResults, 
+		bool exactMatch = false) const;
 
 protected:
 	/** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */
 	aiVector3D mPlaneNormal;
 
 	// -------------------------------------------------------------------
-	/** An entry in a spatially sorted position array. Consists of a vertex index,
-	 * its position and its precalculated distance from the reference plane */
+	/** An entry in a spatially sorted position array. Consists of a 
+	 *  vertex index, its position and its precalculated distance from
+	 *  the reference plane */
+	// -------------------------------------------------------------------
 	struct Entry
 	{
-		unsigned int mIndex; ///< The vertex referred by this entry
-		aiVector3D mPosition; ///< Position
+		unsigned int mIndex;	///< The vertex referred by this entry
+		aiVector3D mPosition;	///< Position
 		uint32_t mSmoothGroups;
-		float mDistance; ///< Distance of this vertex to the sorting plane
+		float mDistance;		///< Distance of this vertex to the sorting plane
 
 		Entry() { /** intentionally not initialized.*/ }
 		Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance,uint32_t pSG) 

+ 6 - 5
code/StandardShapes.cpp

@@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp	{
 
-	// note - flip the face ordering
+	// note - flip the face order
 #define ADD_TRIANGLE(n0,n1,n2) \
 	positions.push_back(n2); \
 	positions.push_back(n1); \
@@ -291,12 +291,13 @@ void StandardShapes::MakeCone(
 
 // ------------------------------------------------------------------------------------------------
 void StandardShapes::MakeCircle(
-	aiVector3D&		center, 
-	aiVector3D&		normal, 
-	float			radius,
-	unsigned int	tess,
+	const aiVector3D&	center, 
+	const aiVector3D&	normal, 
+	float				radius,
+	unsigned int		tess,
 	std::vector<aiVector3D>& positions)
 {
+	//aiVector3D current = aiVector3D ( normal.x,
 }
 
 } // ! Assimp

+ 1 - 1
code/StandardShapes.h

@@ -146,7 +146,7 @@ public:
 	 *  @param tess Number of triangles
 	 *  @param positions Receives output triangles.
 	 */
-	static void MakeCircle(aiVector3D& center, aiVector3D& normal, 
+	static void MakeCircle(const aiVector3D& center, const aiVector3D& normal, 
 		float radius, unsigned int tess,
 		std::vector<aiVector3D>& positions);
 	

+ 35 - 9
include/aiMaterial.h

@@ -197,7 +197,11 @@ enum aiShadingMode
 
     /** No shading at all
     */
-    aiShadingMode_NoShading = 0x8
+    aiShadingMode_NoShading = 0x9,
+
+	/** Fresnel shading
+    */
+    aiShadingMode_Fresnel = 0xa
 };
 
 
@@ -242,6 +246,7 @@ struct aiMaterialProperty
 } // need to end extern C block to allow template member functions
 #endif
 
+// supported texture types
 #define AI_TEXTYPE_OPACITY		0x0
 #define AI_TEXTYPE_SPECULAR		0x1
 #define AI_TEXTYPE_AMBIENT		0x2
@@ -250,6 +255,7 @@ struct aiMaterialProperty
 #define AI_TEXTYPE_NORMALS		0x5
 #define AI_TEXTYPE_SHININESS	0x6
 #define AI_TEXTYPE_DIFFUSE		0x7
+#define AI_TEXTYPE_GLOSSINESS	0x8
 
 // ---------------------------------------------------------------------------
 /** Data structure for a material
@@ -339,7 +345,7 @@ extern "C" {
 
 // ---------------------------------------------------------------------------
 /** @def AI_BUILD_KEY
- * Builds a material texture key with a dynamic index.
+ * Build a material texture key from an index that is not a compile-time constant.
  * Applications <b>could</b> do this (C-example):
  * @code
  * int i;
@@ -352,10 +358,10 @@ extern "C" {
  * @endcode 
  * However, this is wrong because AI_MATKEY_TEXTURE_DIFFUSE() builds the key 
  * string at <b>compile time</b>. <br>
- * Therefore, the dynamic indexing results in a
+ * Therefore, the indexing results in a
  * material key like this : "$tex.file.diffuse[i]" - and it is not very
  * propable that there is a key with this name ... (except the programmer
- * of an ASSIMP loader has made the same mistake :-) ).<br>
+ * of an ASSIMP loader has made the same fault :-) ).<br>
  * This is the right way:
  * @code
  * int i;
@@ -369,8 +375,8 @@ extern "C" {
  * }
  * @endcode 
  * @param base Base material key. This is the same key you'd have used
- *   normally with an underscore as suffix (e.g. AI_MATKEY_TEXTURE_DIFFUSE_)
- * @param index Index to be used. Here you may pass a variable!
+ *   normally with an extra underscore as suffix (e.g. AI_MATKEY_TEXTURE_DIFFUSE_)
+ * @param index Index to be used. 
  * @param out Array of chars to receive the output value. It must be 
  *  sufficiently large. This will be checked via a static assertion for
  *  C++0x. For MSVC8 and later versions the security enhanced version of
@@ -469,6 +475,16 @@ extern "C" {
 */
 #define AI_MATKEY_SHININESS_STRENGTH "$mat.shinpercent"
 
+/** @def AI_MATKEY_REFRACTI
+ * Index of refraction of the material. This is used by some shading models,
+ * e.g. Cook-Torrance. The value is the ratio of the speed of light in a 
+ * vacuum to the speed of light in the material (always >= 1.0 in the real world).
+ * <br>
+ * <b>Type:</b> float<br>
+ * <b>Default value:</b> 1.0f <br>
+*/
+#define AI_MATKEY_REFRACTI "$mat.refracti"
+
 // ---------------------------------------------------------------------------
 /** @def AI_MATKEY_COLOR_DIFFUSE
  *  Defines the diffuse base color of the material
@@ -586,8 +602,12 @@ extern "C" {
 #define AI_MATKEY_TEXTURE_HEIGHT_   "$tex.file.height"
 
 /** @def AI_MATKEY_TEXTURE_SHININESS
- *  Defines a specific shininess texture channel of the material
- *  <br>
+ * Defines a specific shininess texture channel of the material.
+ * The shininess corresponds to the specular exponent used in the
+ * phong lighting equation. However, it is undefined how the exponent
+ * is encoded in the texture. A possible way would be the following:
+ * pixel value c € (0,1), n = 2^(10c+2).
+ * <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
  * @note The key string is built at compile time, therefore it is not 
@@ -598,6 +618,7 @@ extern "C" {
 #define AI_MATKEY_TEXTURE_SHININESS(N) "$tex.file.shininess["#N"]"
 #define AI_MATKEY_TEXTURE_SHININESS_   "$tex.file.shininess"
 
+
 /** @def AI_MATKEY_TEXTURE_OPACITY
  *  Defines a specific opacity texture channel of the material
  *  <br>
@@ -626,6 +647,7 @@ extern "C" {
  * use the macro suffixed with '_' to build the key dynamically. The 
  * AI_BUILD_KEY()-macro can be used to do this.
  */
+// ---------------------------------------------------------------------------
 #define AI_MATKEY_TEXOP_DIFFUSE(N)	"$tex.op.diffuse["#N"]"
 /** @see AI_MATKEY_TEXOP_DIFFUSE */
 #define AI_MATKEY_TEXOP_AMBIENT(N)	"$tex.op.ambient["#N"]"
@@ -663,6 +685,7 @@ extern "C" {
  * use the macro suffixed with '_' to build the key dynamically. The 
  * AI_BUILD_KEY()-macro can be used to do this.
  */
+// ---------------------------------------------------------------------------
 #define AI_MATKEY_UVWSRC_DIFFUSE(N)		"$tex.uvw.diffuse["#N"]"
 /** @see AI_MATKEY_UVWSRC_DIFFUSE */
 #define AI_MATKEY_UVWSRC_AMBIENT(N)		"$tex.uvw.ambient["#N"]"
@@ -700,6 +723,7 @@ extern "C" {
  * use the macro suffixed with '_' to build the key dynamically. The 
  * AI_BUILD_KEY()-macro can be used to do this.
  */
+// ---------------------------------------------------------------------------
 #define AI_MATKEY_TEXBLEND_DIFFUSE(N)	"$tex.blend.diffuse["#N"]"
 /** @see AI_MATKEY_TEXBLEND_DIFFUSE */
 #define AI_MATKEY_TEXBLEND_AMBIENT(N)	"$tex.blend.ambient["#N"]"
@@ -738,6 +762,7 @@ extern "C" {
  * use the macro suffixed with '_' to build the key dynamically. The 
  * AI_BUILD_KEY()-macro can be used to do this.
  */
+// ---------------------------------------------------------------------------
 #define AI_MATKEY_MAPPINGMODE_U_DIFFUSE(N)	"$tex.mapmodeu.diffuse["#N"]"
 /** @see AI_MATKEY_MAPPINGMODE_U_DIFFUSE */
 #define AI_MATKEY_MAPPINGMODE_U_AMBIENT(N)	"$tex.mapmodeu.ambient["#N"]"
@@ -776,6 +801,7 @@ extern "C" {
  * use the macro suffixed with '_' to build the key dynamically. The 
  * AI_BUILD_KEY()-macro can be used to do this.
  */
+// ---------------------------------------------------------------------------
 #define AI_MATKEY_MAPPINGMODE_V_DIFFUSE(N)	"$tex.mapmodev.diffuse["#N"]"
 /** @see AI_MATKEY_MAPPINGMODE_V_DIFFUSE */
 #define AI_MATKEY_MAPPINGMODE_V_AMBIENT(N)	"$tex.mapmodev.ambient["#N"]"
@@ -814,6 +840,7 @@ extern "C" {
  * use the macro suffixed with '_' to build the key dynamically. The 
  * AI_BUILD_KEY()-macro can be used to do this.
  */
+// ---------------------------------------------------------------------------
 #define AI_MATKEY_MAPPINGMODE_W_DIFFUSE(N)	"$tex.mapmodew.diffuse["#N"]"
 /** @see AI_MATKEY_MAPPINGMODE_W_DIFFUSE */
 #define AI_MATKEY_MAPPINGMODE_W_AMBIENT(N)	"$tex.mapmodew.ambient["#N"]"
@@ -841,7 +868,6 @@ extern "C" {
 
 #define AI_MATKEY_ORENNAYAR_ROUGHNESS	 "$shading.orennayar.roughness"
 #define AI_MATKEY_MINNAERT_DARKNESS		 "$shading.minnaert.darkness"
-#define AI_MATKEY_COOK_TORRANCE_REFRACTI "$shading.cookt.refracti"
 #define AI_MATKEY_COOK_TORRANCE_PARAM	 "$shading.cookt.param"
 
 /** @def AI_MATKEY_GLOBAL_BACKGROUND_IMAGE

+ 10 - 0
include/aiTypes.h

@@ -107,6 +107,16 @@ struct aiColor3D
 	bool operator != (const aiColor3D& other) const
 		{return r != other.r || g != other.g || b != other.b;}
 
+	aiColor3D operator+(const aiColor3D& c) const
+		{return aiColor3D(r+c.r,g+c.g,b+c.b);}
+	aiColor3D operator-(const aiColor3D& c) const
+		{return aiColor3D(r+c.r,g+c.g,b+c.b);}
+	aiColor3D operator*(const aiColor3D& c) const
+		{return aiColor3D(r*c.r,g*c.g,b*c.b);}
+	
+	aiColor3D operator*(float f)
+		{return aiColor3D(r*f,g*f,b*f);}
+
 #endif // !__cplusplus
 
 	//! Red, green and blue color values

+ 0 - 0
port/jAssimp/README


+ 1 - 1
port/jAssimp/jni_bridge/JNIMesh.cpp

@@ -211,7 +211,7 @@ void JNIEnvironment::_assimp::_Mesh::Fill(jobject obj,const aiMesh* pcSrc)
 	}
 
 	// copy bones
-	if (0 < pcSrc->mNumBones)
+	if (pcSrc->mNumBones)
 	{
 		// allocate the array
 		jobjectArray jarr = pc->NewObjectArray(pcSrc->mNumBones,

+ 5 - 5
port/jAssimp/src/assimp/BoneAnim.java

@@ -71,17 +71,17 @@ public class BoneAnim {
     /**
      * Rotation keyframes
      */
-    private KeyFrame<Quaternion>[] mQuatKeys;
+    private KeyFrame<Quaternion>[] mQuatKeys = null;
 
     /**
-     * Position keyframes. Component ordering is x,y,z
+     * Position keyframes. Component order is x,y,z
      */
-    private KeyFrame<float[]>[] mPosKeys;
+    private KeyFrame<float[]>[] mPosKeys = null;
 
     /**
-     * scaling keyframes. Component ordering is x,y,z
+     * scaling keyframes. Component order is x,y,z
      */
-    private KeyFrame<float[]>[] mScalingKeys;
+    private KeyFrame<float[]>[] mScalingKeys = null;
 
 
     /**

+ 26 - 4
port/jAssimp/src/assimp/Importer.java

@@ -196,8 +196,8 @@ public class Importer {
      * List of config properties for all supported types: int, float and string
      */
     private PropertyList<Integer> properties = new PropertyList<Integer>();
-    private PropertyList<Float> propertiesFloat = new PropertyList<Float>();
-    private PropertyList<String> propertiesString = new PropertyList<String>();
+    private PropertyList<Float>   propertiesFloat = new PropertyList<Float>();
+    private PropertyList<String>  propertiesString = new PropertyList<String>();
 
 
     /**
@@ -221,7 +221,7 @@ public class Importer {
      * @param iVersion Version of the JNI interface to be used.
      * @throws NativeException Thrown if the jassimp library could not be loaded
      *                         or if the entry point to the module wasn't found. if this exception
-     *                         is not thrown, you can assume that jAssimp is fully available.
+     *                         is not thrown you can assume that jAssimp is fully available.
      */
     public Importer(int iVersion) throws NativeException {
 
@@ -390,12 +390,30 @@ public class Importer {
             throw new NativeException("Failed to load the mesh");
         }
         if (null == this.scene) {
-            throw new NativeException("Failed to copy the data into the Java VM");
+            throw new NativeException("Failed to copy the data to Java");
         }
         return this.scene;
     }
 
 
+    /**
+     * Get the current scene or <code>null</code> if none is loaded
+     * @return Hello Amanda, I want to play a game ...
+     */
+    public final Scene getScene()   {
+        return scene;
+    }
+
+
+    /**
+     * Get the source path of the current scene or <code>null</code> if none is loaded
+     * @return Game Over.
+     */
+    public final String getScenePath() {
+        return path;
+    }
+
+
     /**
      * Implementation of <code>java.lang.Object.equals()</code>
      *
@@ -555,6 +573,10 @@ public class Importer {
     }
 
 
+    // *********************************************************************************
+    // JNI INTERNALS
+    // *********************************************************************************
+
     /**
      * JNI bridge call. For internal use only
      * The method initializes the ASSIMP-JNI bridge for use. No native

+ 350 - 28
port/jAssimp/src/assimp/Material.java

@@ -43,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 package assimp;
 
 
-
 /**
  * Class to wrap materials. Materials are represented in ASSIMP as a list of
  * key/value pairs, the key being a <code>String</code> and the value being
@@ -80,7 +79,8 @@ public class Material {
 
         /**
          * Constructs a new exception
-         * @param message Error message
+         *
+         * @param message      Error message
          * @param property_key Name of the property that wasn't found
          */
         public PropertyNotFoundException(String message, String property_key) {
@@ -92,27 +92,29 @@ public class Material {
 
     /**
      * Get a property with a specific name as generic <code>Object</code>
+     *
      * @param key MATKEY_XXX key constant
-     * @return  null if the property wasn't there or hasn't
-     * the desired output type. The returned <code>Object</code> can be
-     * casted to the expected data type for the property. Primitive
-     * types are represented by their boxed variants.
+     * @return null if the property wasn't there or hasn't
+     *         the desired output type. The returned <code>Object</code> can be
+     *         casted to the expected data type for the property. Primitive
+     *         types are represented by their boxed variants.
      */
     public Object getProperty(String key) throws PropertyNotFoundException {
 
-        for (Property prop : properties)  {
-            if (prop.key.equals(key)){
+        for (Property prop : properties) {
+            if (prop.key.equals(key)) {
                 return prop.value;
             }
         }
-        throw new PropertyNotFoundException("Unable to find material property: ",key);
+        throw new PropertyNotFoundException("Unable to find material property: ", key);
     }
 
-   /**
+    /**
      * Get a material property as float array
+     *
      * @param key MATKEY_XXX key constant
-      * @throws PropertyNotFoundException - if the property can't be found
-      * or if it has the wrong data type.
+     * @throws PropertyNotFoundException - if the property can't be found
+     *                                   or if it has the wrong data type.
      */
     public float[] getPropertyAsFloatArray(String key) throws PropertyNotFoundException {
 
@@ -121,18 +123,19 @@ public class Material {
             return (float[]) obj;
         }
         String msg = "The data type requested (float[]) doesn't match the " +
-            "real data type of the material property";
+                "real data type of the material property";
         DefaultLogger.get().error(msg);
-       throw new PropertyNotFoundException(msg,key);
+        throw new PropertyNotFoundException(msg, key);
     }
 
 
-     /**
+    /**
      * Get a floating-point material property
+     *
      * @param key MATKEY_XXX key constant
      * @return The value of the property.
-      * @throws PropertyNotFoundException - if the property can't be found
-      * or if it has the wrong data type.
+     * @throws PropertyNotFoundException - if the property can't be found
+     *                                   or if it has the wrong data type.
      */
     public float getPropertyAsFloat(String key) throws PropertyNotFoundException {
 
@@ -141,18 +144,19 @@ public class Material {
             return (Float) obj;
         }
         String msg = "The data type requested (Float) doesn't match the " +
-            "real data type of the material property";
+                "real data type of the material property";
         DefaultLogger.get().error(msg);
-       throw new PropertyNotFoundException(msg,key);
+        throw new PropertyNotFoundException(msg, key);
     }
 
 
     /**
      * Get an integer material property
+     *
      * @param key MATKEY_XXX key constant
      * @return The value of the property.
-      * @throws PropertyNotFoundException - if the property can't be found
-      * or if it has the wrong data type.
+     * @throws PropertyNotFoundException - if the property can't be found
+     *                                   or if it has the wrong data type.
      */
     public int getPropertyAsInt(String key) throws PropertyNotFoundException {
 
@@ -161,18 +165,19 @@ public class Material {
             return (Integer) obj;
         }
         String msg = "The data type requested (Integer) doesn't match the " +
-            "real data type of the material property";
+                "real data type of the material property";
         DefaultLogger.get().error(msg);
-       throw new PropertyNotFoundException(msg,key);
+        throw new PropertyNotFoundException(msg, key);
     }
 
 
     /**
      * Get a material property string
+     *
      * @param key MATKEY_XXX key constant
      * @return The value of the property.
-      * @throws PropertyNotFoundException - if the property can't be found
-      * or if it has the wrong data type.
+     * @throws PropertyNotFoundException - if the property can't be found
+     *                                   or if it has the wrong data type.
      */
     public String getPropertyAsString(String key) throws PropertyNotFoundException {
 
@@ -181,9 +186,9 @@ public class Material {
             return (String) obj;
         }
         String msg = "The data type requested (java.lang.String) doesn't match the " +
-            "real data type of the material property";
+                "real data type of the material property";
         DefaultLogger.get().error(msg);
-       throw new PropertyNotFoundException(msg,key);
+        throw new PropertyNotFoundException(msg, key);
     }
 
 
@@ -228,7 +233,9 @@ public class Material {
      * <br>
      * <b>Type:</b> int (TextureOp)<br>
      * <b>Default value:</b> 0<br>
-     * <b>Requires:</b> MATKEY_TEXTURE_DIFFUSE(0)<br>
+     * <b>Requires:</b> MATKEY_TEXTURE_DIFFUSE(N)<br>
+     *
+     * @param N Index of the texture
      */
     public static String MATKEY_TEXOP_DIFFUSE(int N) {
         return "$tex.op.diffuse[" + N + "]";
@@ -284,5 +291,320 @@ public class Material {
     }
 
 
+    /**
+     * Specifies the blend factor to be multiplied with the value of
+     * the n'th texture layer before it is combined with the previous
+     * layer using a specific blend operation.
+     * <p/>
+     * <br>
+     * <b>Type:</b> float<br>
+     * <b>Default value:</b> 1.0f<br>
+     * <b>Requires:</b> MATKEY_TEXTURE_DIFFUSE(N)<br>
+     *
+     * @param N Index of the texture
+     */
+    public static String MATKEY_TEXBLEND_DIFFUSE(int N) {
+        return "$tex.blend.diffuse[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_TEXBLEND_DIFFUSE</code>
+     */
+    public static String MATKEY_TEXBLEND_SPECULAR(int N) {
+        return "$tex.blend.specular[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_TEXBLEND_DIFFUSE</code>
+     */
+    public static String MATKEY_TEXBLEND_AMBIENT(int N) {
+        return "$tex.blend.ambient[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_TEXBLEND_DIFFUSE</code>
+     */
+    public static String MATKEY_TEXBLEND_EMISSIVE(int N) {
+        return "$tex.blend.emissive[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_TEXBLEND_DIFFUSE</code>
+     */
+    public static String MATKEY_TEXBLEND_SHININESS(int N) {
+        return "$tex.blend.shininess[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_TEXBLEND_DIFFUSE</code>
+     */
+    public static String MATKEY_TEXBLEND_OPACITY(int N) {
+        return "$tex.blend.opacity[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_TEXBLEND_DIFFUSE</code>
+     */
+    public static String MATKEY_TEXBLEND_HEIGHT(int N) {
+        return "$tex.blend.height[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_TEXBLEND_DIFFUSE</code>
+     */
+    public static String MATKEY_TEXBLEND_NORMALS(int N) {
+        return "$tex.blend.normals[" + N + "]";
+    }
+
+
+    /**
+     * Specifies the index of the UV channel to be used for a texture
+     * <br>
+     * <b>Type:</b>int<br>
+     * <b>Default value:</b>0<br>
+     * <b>Requires:</b> MATKEY_TEXTURE_DIFFUSE(N)<br>
+     *
+     * @param N Index of the texture
+     */
+    public static String MATKEY_UVWSRC_DIFFUSE(int N) {
+        return "$tex.uvw.diffuse[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_UVWSRC_DIFFUSE</code>
+     */
+    public static String MATKEY_UVWSRC_SPECULAR(int N) {
+        return "$tex.uvw.specular[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_UVWSRC_DIFFUSE</code>
+     */
+    public static String MATKEY_UVWSRC_AMBIENT(int N) {
+        return "$tex.uvw.ambient[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_UVWSRC_DIFFUSE</code>
+     */
+    public static String MATKEY_UVWSRC_EMISSIVE(int N) {
+        return "$tex.uvw.emissive[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_UVWSRC_DIFFUSE</code>
+     */
+    public static String MATKEY_UVWSRC_SHININESS(int N) {
+        return "$tex.uvw.shininess[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_UVWSRC_DIFFUSE</code>
+     */
+    public static String MATKEY_UVWSRC_OPACITY(int N) {
+        return "$tex.uvw.opacity[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_UVWSRC_DIFFUSE</code>
+     */
+    public static String MATKEY_UVWSRC_HEIGHT(int N) {
+        return "$tex.uvw.height[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_UVWSRC_DIFFUSE</code>
+     */
+    public static String MATKEY_UVWSRC_NORMALS(int N) {
+        return "$tex.uvw.normals[" + N + "]";
+    }
+
+
+    /**
+     * Specifies the texture mapping mode in the v (y) direction.
+     * <br>
+     * <b>Type:</b>int<br>
+     * <b>Default value:</b>TextureMapMode.Wrap<br>
+     * <b>Requires:</b> MATKEY_TEXTURE_DIFFUSE(N)<br>
+     *
+     * @param N Index of the texture
+     */
+    public static String MATKEY_MAPPINGMODE_U_DIFFUSE(int N) {
+        return "$tex.mapmodeu.diffuse[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_U_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_U_SPECULAR(int N) {
+        return "$tex.mapmodeu.specular[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_U_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_U_AMBIENT(int N) {
+        return "$tex.mapmodeu.ambient[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_U_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_U_EMISSIVE(int N) {
+        return "$tex.mapmodeu.emissive[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_U_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_U_SHININESS(int N) {
+        return "$tex.mapmodeu.shininess[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_U_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_U_OPACITY(int N) {
+        return "$tex.mapmodeu.opacity[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_U_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_U_HEIGHT(int N) {
+        return "$tex.mapmodeu.height[" + N + "]";
+    }
 
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_U_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_U_NORMALS(int N) {
+        return "$tex.mapmodeu.normals[" + N + "]";
+    }
+
+
+    /**
+     * Specifies the texture mapping mode in the v (y) direction.
+     * <br>
+     * <b>Type:</b>int<br>
+     * <b>Default value:</b>TextureMapMode.Wrap<br>
+     * <b>Requires:</b> MATKEY_TEXTURE_DIFFUSE(N)<br>
+     *
+     * @param N Index of the texture
+     */
+    public static String MATKEY_MAPPINGMODE_V_DIFFUSE(int N) {
+        return "$tex.mapmodev.diffuse[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_V_SPECULAR(int N) {
+        return "$tex.mapmodev.specular[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_V_AMBIENT(int N) {
+        return "$tex.mapmodev.ambient[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_V_EMISSIVE(int N) {
+        return "$tex.mapmodev.emissive[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_V_SHININESS(int N) {
+        return "$tex.mapmodev.shininess[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_V_OPACITY(int N) {
+        return "$tex.mapmodev.opacity[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_V_HEIGHT(int N) {
+        return "$tex.mapmodev.height[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_V_NORMALS(int N) {
+        return "$tex.mapmodev.normals[" + N + "]";
+    }
+
+
+    /**
+     * Specifies the texture mapping mode in the w (z) direction.
+     * <br>
+     * <b>Type:</b>int<br>
+     * <b>Default value:</b>TextureMapMode.Wrap<br>
+     * <b>Requires:</b> MATKEY_TEXTURE_DIFFUSE(N)<br>
+     *
+     * @param N Index of the texture
+     */
+    public static String MATKEY_MAPPINGMODE_W_DIFFUSE(int N) {
+        return "$tex.mapmodew.diffuse[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_W_SPECULAR(int N) {
+        return "$tex.mapmodew.specular[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_W_AMBIENT(int N) {
+        return "$tex.mapmodew.ambient[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_W_EMISSIVE(int N) {
+        return "$tex.mapmodew.emissive[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_W_SHININESS(int N) {
+        return "$tex.mapmodew.shininess[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_W_OPACITY(int N) {
+        return "$tex.mapmodew.opacity[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_W_HEIGHT(int N) {
+        return "$tex.mapmodew.height[" + N + "]";
+    }
+
+    /**
+     * @see <code>MATKEY_MAPPINGMODE_V_DIFFUSE</code>
+     */
+    public static String MATKEY_MAPPINGMODE_W_NORMALS(int N) {
+        return "$tex.mapmodew.normals[" + N + "]";
+    }
 }

+ 46 - 42
port/jAssimp/src/assimp/PostProcessStep.java

@@ -45,7 +45,7 @@ package assimp;
 /**
  * Enumeration class that defines postprocess steps that can be executed on a model
  * after it has been loaded. All PPSteps are implemented in C++, so their performance
- * is awesome. Most steps are O(n * log(n)). ;-)
+ * is awesome :-) (*duck*)
  *
  * @author Aramis (Alexander Gessler)
  * @version 1.0
@@ -186,54 +186,58 @@ public class PostProcessStep {
             new PostProcessStep("ValidateDataStructure");
 
 
-    /** Reorders triangles for vertex cache locality and thus better performance.
-	 * The step tries to improve the ACMR (average post-transform vertex cache
-	 * miss ratio) for all meshes. The step runs in O(n) and is roughly
-	 * basing on the algorithm described in this paper:
-	 * <url>http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf</url>
-	 */
-	 public static final PostProcessStep ImproveVertexLocality =
+    /**
+     * Reorders triangles for vertex cache locality and thus better performance.
+     * The step tries to improve the ACMR (average post-transform vertex cache
+     * miss ratio) for all meshes. The step runs in O(n) and is roughly
+     * basing on the algorithm described in this paper:
+     * <url>http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf</url>
+     */
+    public static final PostProcessStep ImproveVertexLocality =
             new PostProcessStep("ImproveVertexLocality");
 
 
-    /** Searches for redundant materials and removes them.
-	 *
-	 * This is especially useful in combination with the PretransformVertices
-	 * and OptimizeGraph steps. Both steps join small meshes, but they
-	 * can't do that if two meshes have different materials.
-	 */
-	 public static final PostProcessStep RemoveRedundantMaterials =
+    /**
+     * Searches for redundant materials and removes them.
+     * <p/>
+     * This is especially useful in combination with the PretransformVertices
+     * and OptimizeGraph steps. Both steps join small meshes, but they
+     * can't do that if two meshes have different materials.
+     */
+    public static final PostProcessStep RemoveRedundantMaterials =
             new PostProcessStep("RemoveRedundantMaterials");
 
-	/** This step tries to determine which meshes have normal vectors
-	 * that are facing inwards. The algorithm is simple but effective:
-	 * the bounding box of all vertices + their normals is compared against
-	 * the volume of the bounding box of all vertices without their normals.
-	 * This works well for most objects, problems might occur with planar
-	 * surfaces. However, the step tries to filter such cases.
-	 * The step inverts all infacing normals. Generally it is recommended
-	 * to enable this step, although the result is not always correct.
-	*/
-	 public static final PostProcessStep FixInfacingNormals =
+    /**
+     * This step tries to determine which meshes have normal vectors
+     * that are facing inwards. The algorithm is simple but effective:
+     * the bounding box of all vertices + their normals is compared against
+     * the volume of the bounding box of all vertices without their normals.
+     * This works well for most objects, problems might occur with planar
+     * surfaces. However, the step tries to filter such cases.
+     * The step inverts all infacing normals. Generally it is recommended
+     * to enable this step, although the result is not always correct.
+     */
+    public static final PostProcessStep FixInfacingNormals =
             new PostProcessStep("FixInfacingNormals");
 
-	/** This step performs some optimizations on the node graph.
-	 *
-	 * It is incompatible to the PreTransformVertices-Step. Some configuration
-	 * options exist, see aiConfig.h for more details.
-	 * Generally, two actions are available:<br>
-	 *   1. Remove animation nodes and data from the scene. This allows other
-	 *      steps for further optimizations.<br>
-	 *   2. Combine very small meshes to larger ones. Only if the meshes
-	 *      are used by the same node or by nodes on the same hierarchy (with
-	 *      equal local transformations). Unlike PreTransformVertices, the
-	 *      OptimizeGraph-step doesn't transform vertices from one space
-	 *      another.<br>
-	 *   3. Remove hierarchy levels<br>
-	 *
-	 *  It is recommended to have this step run with the default configuration.
-	 */
-	 public static final PostProcessStep OptimizeGraph =
+    /**
+     * This step performs some optimizations on the node graph.
+     * <p/>
+     * It is incompatible to the PreTransformVertices-Step. Some configuration
+     * options exist, see aiConfig.h for more details.
+     * Generally, two actions are available:<br>
+     * 1. Remove animation nodes and data from the scene. This allows other
+     * steps for further optimizations.<br>
+     * 2. Combine very small meshes to larger ones. Only if the meshes
+     * are used by the same node or by nodes on the same hierarchy (with
+     * equal local transformations). Unlike PreTransformVertices, the
+     * OptimizeGraph-step doesn't transform vertices from one space
+     * another.<br>
+     * 3. Remove hierarchy levels<br>
+     * <p/>
+     * It is recommended to have this step run with the default configuration.
+     */
+    public static final PostProcessStep OptimizeGraph =
             new PostProcessStep("OptimizeGraph");
 
     private final String myName; // for debug only

+ 19 - 5
port/jAssimp/src/assimp/TextureMapMode.java

@@ -42,11 +42,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 package assimp;
 
 /**
- * Created by IntelliJ IDEA.
- * User: Alex
- * Date: 08.06.2008
- * Time: 17:27:11
- * To change this template use File | Settings | File Templates.
+ * Defines how texture coordinates ! ElemOf [0 ... 1] are handled.
+ *
+ * This corresponds to the native <code>aiTextureMapMode</code> enum.
+ *
+ * @author Alexander Gessler (Aramis)
  */
 public class TextureMapMode {
+
+    /** A texture coordinate u|v is translated to u%1|v%1
+     */
+    public static final int Wrap = 0x0;
+
+     /** Texture coordinates outside the area formed by 1|1 and 0|0
+     *  are clamped to the nearest valid value on an axis
+     */
+    public static final int Clamp = 0x1;
+   
+     /** A texture coordinate u|v becomes u%1|v%1 if (u-(u%1))%2 is zero and
+     *  1-(u%1)|1-(v%1) otherwise
+     */
+    public static final int Mirror = 0x2;
 }

+ 33 - 4
port/jAssimp/src/assimp/test/DumpToFile.java

@@ -45,8 +45,14 @@ package assimp.test;
 import assimp.*;
 
 
+import javax.imageio.ImageWriter;
+import javax.imageio.ImageIO;
+import javax.imageio.stream.ImageOutputStream;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.io.File;
+import java.awt.image.BufferedImage;
+import java.util.Iterator;
 
 
 /**
@@ -115,10 +121,34 @@ public class DumpToFile {
      *
      * @param texture Texture to be exported
      * @param path    Output path
-     * @throws IOException yes ... sometimes ... :-)
      */
-    public static void SaveTextureToTGA(Texture texture, String path) throws IOException {
+    public static void SaveTextureToTGA(Texture texture, String path) {
+        BufferedImage bImg = texture.convertToImage();
 
+        Iterator writers = ImageIO.getImageWritersBySuffix("tga");
+        if (!(writers.hasNext())) {
+            System.out.println("No writer for TGA file format available");
+            return;
+        }
+        ImageWriter w = (ImageWriter) (writers.next());
+        if (w == null) {
+            System.out.println("No writer for TGA file format available");
+            return;
+        }
+        File fo = new File(path);
+
+        try {
+
+        ImageOutputStream ios = ImageIO.createImageOutputStream(fo);
+        w.setOutput(ios);
+        w.write(bImg);
+
+        }
+        catch (IOException ex) {
+             System.out.println("Failed to write " + path);
+            return;
+        }
+        System.out.println(path + " has been written");
     }
 
 
@@ -390,7 +420,7 @@ public class DumpToFile {
 
                 String path = arguments[1].substring(0, arguments[1].length() - 4) + "_tex" + i++ + ".tga";
                 stream.write("Emb. Texture\n" +
-                    "\tExportPath: " + path + "\n\n");
+                        "\tExportPath: " + path + "\n\n");
 
                 SaveTextureToTGA(texture, path);
             }
@@ -399,7 +429,6 @@ public class DumpToFile {
         /*  Now print all materials
          */
 
-
         // close the stream again
         stream.close();
     }

BIN
test/3DSFiles/IMAGE1.bmp


BIN
test/3DSFiles/fels.3ds


BIN
test/LWOFiles/LWO2/hierarchy.lwo


BIN
test/LWOFiles/LWO2/shader_test/CellShader.lwo


BIN
test/LWOFiles/LWO2/shader_test/SuperCellShader.lwo


BIN
test/LWOFiles/LWO2/shader_test/fastFresnel.lwo


BIN
test/LWOFiles/LWO2/shader_test/realFresnel.lwo


+ 4 - 4
workspaces/jidea5.1/jAssimp.ipr

@@ -36,9 +36,9 @@
     <entry_points />
   </component>
   <component name="ExportToHTMLSettings">
-    <option name="PRINT_LINE_NUMBERS" value="false" />
-    <option name="OPEN_IN_BROWSER" value="false" />
-    <option name="OUTPUT_DIRECTORY" />
+    <option name="PRINT_LINE_NUMBERS" value="true" />
+    <option name="OPEN_IN_BROWSER" value="true" />
+    <option name="OUTPUT_DIRECTORY" value="J:\Programmieren\ASSIMP\assimp3\trunk\workspaces\jidea5.1\exportToHTML" />
   </component>
   <component name="GUI Designer component loader factory" />
   <component name="JavacSettings">
@@ -49,7 +49,7 @@
     <option name="MAXIMUM_HEAP_SIZE" value="128" />
   </component>
   <component name="JavadocGenerationManager">
-    <option name="OUTPUT_DIRECTORY" value="J:/Programmieren/ASSIMP3/assimp/doc/javadoc" />
+    <option name="OUTPUT_DIRECTORY" value="J:/Programmieren/ASSIMP/assimp3/doc/javadoc" />
     <option name="OPTION_SCOPE" value="protected" />
     <option name="OPTION_HIERARCHY" value="true" />
     <option name="OPTION_NAVIGATOR" value="true" />

+ 8 - 0
workspaces/vc8/assimp.vcproj

@@ -1132,6 +1132,10 @@
 						>
 					</File>
 				</Filter>
+				<Filter
+					Name="AC"
+					>
+				</Filter>
 			</Filter>
 		</Filter>
 		<Filter
@@ -1468,6 +1472,10 @@
 						>
 					</File>
 				</Filter>
+				<Filter
+					Name="AC"
+					>
+				</Filter>
 			</Filter>
 		</Filter>
 		<Filter