Quellcode durchsuchen

Next WIP version of the LWO loader. LWO2 is still unfinished and crashes every time.
Added NFF test model.

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

aramis_acg vor 17 Jahren
Ursprung
Commit
af18307f95
9 geänderte Dateien mit 785 neuen und 266 gelöschten Zeilen
  1. 308 0
      code/LWOBLoader.cpp
  2. 117 9
      code/LWOFileData.h
  3. 253 116
      code/LWOLoader.cpp
  4. 56 3
      code/LWOLoader.h
  5. 9 132
      code/LWOMaterial.cpp
  6. 6 3
      code/NFFLoader.cpp
  7. 0 3
      code/SmoothingGroups.inl
  8. 24 0
      test/NFF/spheres.nff
  9. 12 0
      workspaces/vc8/assimp.vcproj

+ 308 - 0
code/LWOBLoader.cpp

@@ -0,0 +1,308 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the LWO importer class for the older LWOB 
+    file formats, including materials */
+
+// internal headers
+#include "LWOLoader.h"
+#include "MaterialSystem.h"
+#include "ByteSwap.h"
+
+// public assimp headers
+#include "../include/aiScene.h"
+#include "../include/aiAssert.h"
+#include "../include/DefaultLogger.h"
+#include "../include/assimp.hpp"
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWOBFile()
+{
+	LE_NCONST uint8_t* const end = mFileBuffer + fileSize;
+	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);
+		if (mFileBuffer + head->length > end)
+		{
+			throw new ImportErrorException("LWOB: Invalid file, the size attribute of "
+				"a chunk points behind the end of the file");
+			break;
+		}
+		LE_NCONST uint8_t* const next = mFileBuffer+head->length;
+		switch (head->type)
+		{
+			// vertex list
+		case AI_LWO_PNTS:
+			{
+				if (!mCurLayer->mTempPoints.empty())
+					DefaultLogger::get()->warn("LWO: PNTS chunk encountered twice");
+				else LoadLWOPoints(head->length);
+				break;
+			}
+			// face list
+		case AI_LWO_POLS:
+			{
+				if (!mCurLayer->mFaces.empty())
+					DefaultLogger::get()->warn("LWO: POLS chunk encountered twice");
+				else LoadLWOPolygons(head->length);
+				break;
+			}
+			// list of tags
+		case AI_LWO_SRFS:
+			{
+				if (!mTags->empty())
+					DefaultLogger::get()->warn("LWO: SRFS chunk encountered twice");
+				else LoadLWOTags(head->length);
+				break;
+			}
+
+			// surface chunk
+		case AI_LWO_SURF:
+			{
+				if (!mSurfaces->empty())
+					DefaultLogger::get()->warn("LWO: SURF chunk encountered twice");
+				else LoadLWOBSurface(head->length);
+				break;
+			}
+		}
+		mFileBuffer = next;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::CountVertsAndFacesLWOB(unsigned int& verts, unsigned int& faces,
+	LE_NCONST uint16_t*& cursor, const uint16_t* const end, unsigned int max)
+{
+	while (cursor < end && max--)
+	{
+		uint16_t numIndices = *cursor++;
+		verts += numIndices;faces++;
+		cursor += numIndices;
+		int16_t surface = *cursor++;
+		if (surface < 0)
+		{
+			// there are detail polygons
+			numIndices = *cursor++;
+			CountVertsAndFacesLWOB(verts,faces,cursor,end,numIndices);
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::CopyFaceIndicesLWOB(FaceList::iterator& it,
+	LE_NCONST uint16_t*& cursor, 
+	const uint16_t* const end,
+	unsigned int max)
+{
+	while (cursor < end && max--)
+	{
+		LWO::Face& face = *it;++it;
+		if(face.mNumIndices = *cursor++)
+		{
+			if (cursor + face.mNumIndices >= end)break;
+			face.mIndices = new unsigned int[face.mNumIndices];
+			for (unsigned int i = 0; i < face.mNumIndices;++i)
+			{
+				unsigned int & mi = face.mIndices[i] = *cursor++;
+				if (mi > mCurLayer->mTempPoints.size())
+				{
+					DefaultLogger::get()->warn("LWOB: face index is out of range");
+					mi = (unsigned int)mCurLayer->mTempPoints.size()-1;
+				}
+			}
+		}
+		else DefaultLogger::get()->warn("LWOB: Face has 0 indices");
+		int16_t surface = *cursor++;
+		if (surface < 0)
+		{
+			surface = -surface;
+
+			// there are detail polygons
+			uint16_t numPolygons = *cursor++;
+			if (cursor < end)CopyFaceIndicesLWOB(it,cursor,end,numPolygons);
+		}
+		face.surfaceIndex = surface-1;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWOBSurface(unsigned int size)
+{
+	LE_NCONST uint8_t* const end = mFileBuffer + size;
+
+	uint32_t iCursor = 0;
+	mSurfaces->push_back( LWO::Surface () );
+	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; 
+	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)
+		{
+		// 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;
+				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;
+				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;
+				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;
+				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;
+				break;
+			}
+		// glossiness
+		case AI_LWO_GLOS:
+			{
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,GLOS,2);
+				AI_LSWAP2P(mFileBuffer);
+				surf.mGlossiness = float(*((int16_t*)mFileBuffer));
+				break;
+			}
+		// color texture
+		case AI_LWO_CTEX:
+			{
+				pTex = &surf.mColorTexture;
+				break;
+			}
+		// diffuse texture
+		case AI_LWO_DTEX:
+			{
+				pTex = &surf.mDiffuseTexture;
+				break;
+			}
+		// specular texture
+		case AI_LWO_STEX:
+			{
+				pTex = &surf.mSpecularTexture;
+				break;
+			}
+		// bump texture
+		case AI_LWO_BTEX:
+			{
+				pTex = &surf.mBumpTexture;
+				break;
+			}
+		// transparency texture
+		case AI_LWO_TTEX:
+			{
+				pTex = &surf.mTransparencyTexture;
+				break;
+			}
+			// texture path
+		case AI_LWO_TIMG:
+			{
+				if (pTex)
+				{
+					ParseString(pTex->mFileName,head_length);	
+					AdjustTexturePath(pTex->mFileName);
+					mFileBuffer += pTex->mFileName.length();
+				}
+				else DefaultLogger::get()->warn("LWOB: TIMG tag was encuntered although "
+					"there was no xTEX tag before");
+				break;
+			}
+		// texture strength
+		case AI_LWO_TVAL:
+			{
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,TVAL,1);
+				if (pTex)pTex->mStrength = *mFileBuffer / 255.0f;
+				else DefaultLogger::get()->warn("LWOB: TVAL tag was encuntered "
+					"although there was no xTEX tag before");
+				break;
+			}
+		}
+		mFileBuffer = next;
+	}
+}

+ 117 - 9
code/LWOFileData.h

@@ -49,12 +49,17 @@ Original copyright notice: "Ernie Wright  17 Sep 00"
 #ifndef AI_LWO_FILEDATA_INCLUDED
 #define AI_LWO_FILEDATA_INCLUDED
 
-
-#include "IFF.h"
+// STL headers
 #include <vector>
 #include <list>
+
+// public ASSIMP headers
 #include "../include/aiMesh.h"
 
+// internal headers
+#include "IFF.h"
+#include "SmoothingGroups.h"
+
 namespace Assimp {
 namespace LWO {
 
@@ -232,15 +237,94 @@ namespace LWO {
 #define AI_LWO_DATA  AI_IFF_FOURCC('D','A','T','A')
 
 
+/* VMAP types */
+#define AI_LWO_TXUV  AI_IFF_FOURCC('T','X','U','V')
+#define AI_LWO_RGB   AI_IFF_FOURCC(' ','R','G','B')
+#define AI_LWO_RGBA  AI_IFF_FOURCC('R','G','B','A')
+#define AI_LWO_WGHT  AI_IFF_FOURCC('W','G','H','T')
+
+
 // ---------------------------------------------------------------------------
 /** \brief Data structure for a face in a LWO file
+ *
+ * \note We can't use the code in SmoothingGroups.inl here - the mesh
+ *   structures of 3DS/ASE and LWO are too different. 
  */
 struct Face : public aiFace
 {
-	Face() : surfaceIndex(0) {}
+	Face() 
+		: surfaceIndex(0)
+		, smoothGroup(0)
+	{}
+
 	unsigned int surfaceIndex;
+	unsigned int smoothGroup;
+};
+
+
+// ---------------------------------------------------------------------------
+/** \brief Base structure for all vertex map representations
+ */
+struct VMapEntry
+{
+	VMapEntry(unsigned int _dims)
+		:  dims(_dims)
+	{}
+
+	~VMapEntry() {delete[] rawData;}
+
+	std::string name;
+	float* rawData;
+	unsigned int dims;
 };
 
+// ---------------------------------------------------------------------------
+/** \brief Represents an extra vertex color channel
+ */
+struct VColorChannel : public VMapEntry
+{
+	VColorChannel(unsigned int num)
+		: VMapEntry(4)
+	{
+		data = new aiColor4D[num];
+		for (unsigned int i = 0; i < num;++i)
+			data[i].a = 1.0f;
+		rawData = (float*)data;
+	}
+
+	aiColor4D* data;
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Represents an extra vertex UV channel
+ */
+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;
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Represents a weight map 
+ */
+struct WeightChannel : public VMapEntry
+{
+	WeightChannel(unsigned int num)
+		: VMapEntry(1)
+	{
+		rawData = new float[num];
+		for (unsigned int m = 0; m < num;++m)
+			rawData[m] = 0.f;
+	}
+};
+
+
 // ---------------------------------------------------------------------------
 /** \brief LWO2 gradient keyframe
  */
@@ -272,7 +356,8 @@ struct GradientInfo
 struct Texture
 {
 	Texture()
-		: mStrength(1.0f)
+		: mStrength			(1.0f)
+		, iUVChannelIndex	(0)
 	{}
 
 	//! File name of the texture
@@ -285,6 +370,8 @@ struct Texture
 	/*************** SPECIFIC TO LWO2 *********************/
 	uint32_t type; // type of the texture
 
+	//! Index of the corresponding UV channel
+	unsigned int iUVChannelIndex;
 	
 	GradientInfo mGradient;
 	// todo ... maybe support for procedurals?
@@ -336,11 +423,15 @@ struct Surface
 	} \
 
 
-typedef std::vector<aiVector3D>		PointList;
-typedef std::vector<LWO::Face>		FaceList;
-typedef std::vector<LWO::Surface>	SurfaceList;
-typedef std::vector<std::string>	TagList;
-typedef std::vector<unsigned int>	TagMappingTable;
+// some typedefs ... to make life with loader monsters like this easier
+typedef std::vector	<	aiVector3D		>	PointList;
+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	<	WeightChannel	>	WeightChannelList;
+typedef std::vector	<	VColorChannel	>	VColorChannelList;
+typedef std::vector	<	UVChannel		>	UVChannelList;
 
 
 // ---------------------------------------------------------------------------
@@ -350,14 +441,31 @@ struct Layer
 {
 	Layer()
 		: mParent (0xffff)
+		, mFaceIDXOfs(0)
+		, mPointIDXOfs(0)
 	{}
 
 	/** Temporary point list from the file */
 	PointList mTempPoints;
 
+	/** Weight channel list from the file */
+	WeightChannelList mWeightChannels;
+
+	/** Vertex color list from the file */
+	VColorChannelList mVColorChannels;
+
+	/** UV channel list from the file */
+	UVChannelList mUVChannels;
+
 	/** Temporary face list from the file*/
 	FaceList mFaces;
 
+	/** Current face indexing offset from the beginning of the buffers*/
+	unsigned int mFaceIDXOfs;
+
+	/** Current point indexing offset from the beginning of the buffers*/
+	unsigned int mPointIDXOfs;
+
 	/** Parent index */
 	uint16_t mParent;
 

+ 253 - 116
code/LWOLoader.cpp

@@ -153,7 +153,6 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 	// new lightwave format
 	else if (AI_LWO_FOURCC_LWO2 == fileType)
 	{
-		throw new ImportErrorException("LWO2 is under development and currently disabled.");
 		mIsLWO2 = true;
 		this->LoadLWO2File();
 	}
@@ -252,6 +251,39 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 				aiFace* pf = mesh->mFaces = new aiFace[mesh->mNumFaces];
 				mesh->mMaterialIndex = i;
 
+				// find out which vertex color channels and which texture coordinate
+				// channels are really required by the material attached to this mesh
+				unsigned int vUVChannelIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+				unsigned int vVColorIndices[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+#if _DEBUG
+				for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_TEXTURECOORDS;++mui )
+					vUVChannelIndices[mui] = 0xffffffff;
+				for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_COLOR_SETS;++mui )
+					vVColorIndices[mui] = 0xffffffff;
+#endif
+
+				FindUVChannels(_mSurfaces[i],layer,vUVChannelIndices);
+				FindVCChannels(_mSurfaces[i],layer,vVColorIndices);
+
+				// allocate storage for UV and CV channels
+				aiVector3D* pvUV[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+				for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_TEXTURECOORDS;++mui )
+				{
+					if (0xffffffff == vUVChannelIndices[mui])break;
+					pvUV[mui] = mesh->mTextureCoords[mui] = new aiVector3D[mesh->mNumVertices];
+
+					// LightWave doesn't support more than 2 UV components
+					mesh->mNumUVComponents[0] = 2;
+				}
+		
+				aiColor4D* pvVC[AI_MAX_NUMBER_OF_COLOR_SETS];
+				for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_COLOR_SETS;++mui)	
+				{
+					if (0xffffffff == vVColorIndices[mui])break;
+					pvVC[mui] = mesh->mColors[mui] = new aiColor4D[mesh->mNumVertices];
+				}
+
 				// now convert all faces
 				unsigned int vert = 0;
 				for (SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
@@ -262,7 +294,29 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 					// copy all vertices
 					for (unsigned int q = 0; q  < face.mNumIndices;++q)
 					{
-						*pv++ = layer.mTempPoints[face.mIndices[q]];
+						register unsigned int idx = face.mIndices[q];
+						*pv++ = layer.mTempPoints[idx];
+
+						// 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];
+						}
+
+						// 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];
+						}
+
+#if 0
+						// process vertex weights - not yet supported
+						for (unsigned int w = 0; w < layer.mWeightChannels.size();++w)
+						{
+						}
+#endif
 						face.mIndices[q] = vert++;
 					}
 
@@ -376,65 +430,10 @@ void LWOImporter::GenerateNodeGraph(std::vector<aiNode*>& apcNodes)
 	if (!pScene->mRootNode)throw new ImportErrorException("LWO: Unable to build a valid node graph");
 }
 
-// ------------------------------------------------------------------------------------------------
-void LWOImporter::CountVertsAndFaces(unsigned int& verts, unsigned int& faces,
-	LE_NCONST uint16_t*& cursor, const uint16_t* const end, unsigned int max)
-{
-	while (cursor < end && max--)
-	{
-		uint16_t numIndices = *cursor++;
-		verts += numIndices;faces++;
-		cursor += numIndices;
-		int16_t surface = *cursor++;
-		if (surface < 0)
-		{
-			// there are detail polygons
-			numIndices = *cursor++;
-			CountVertsAndFaces(verts,faces,cursor,end,numIndices);
-		}
-	}
-}
-
-// ------------------------------------------------------------------------------------------------
-void LWOImporter::CopyFaceIndices(FaceList::iterator& it,
-	LE_NCONST uint16_t*& cursor, 
-	const uint16_t* const end,
-	unsigned int max)
-{
-	while (cursor < end && max--)
-	{
-		LWO::Face& face = *it;++it;
-		if(face.mNumIndices = *cursor++)
-		{
-			if (cursor + face.mNumIndices >= end)break;
-			face.mIndices = new unsigned int[face.mNumIndices];
-			for (unsigned int i = 0; i < face.mNumIndices;++i)
-			{
-				unsigned int & mi = face.mIndices[i] = *cursor++;
-				if (mi > mCurLayer->mTempPoints.size())
-				{
-					DefaultLogger::get()->warn("LWO: face index is out of range");
-					mi = (unsigned int)mCurLayer->mTempPoints.size()-1;
-				}
-			}
-		}
-		else DefaultLogger::get()->warn("LWO: Face has 0 indices");
-		int16_t surface = *cursor++;
-		if (surface < 0)
-		{
-			surface = -surface;
-
-			// there are detail polygons
-			uint16_t numPolygons = *cursor++;
-			if (cursor < end)CopyFaceIndices(it,cursor,end,numPolygons);
-		}
-		face.surfaceIndex = surface-1;
-	}
-}
-
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::ResolveTags()
 {
+	// --- this function is used for both LWO2 and LWOB
 	mMapping->resize(mTags->size(),0xffffffff);
 	for (unsigned int a = 0; a  < mTags->size();++a)
 	{
@@ -454,6 +453,7 @@ void LWOImporter::ResolveTags()
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::ParseString(std::string& out,unsigned int max)
 {
+	// --- this function is used for both LWO2 and LWOB
 	unsigned int iCursor = 0;
 	const char* in = (const char*)mFileBuffer,*sz = in;
 	while (*in)
@@ -472,6 +472,7 @@ void LWOImporter::ParseString(std::string& out,unsigned int max)
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::AdjustTexturePath(std::string& out)
 {
+	// --- this function is used for both LWO2 and LWOB
 	if (::strstr(out.c_str(), "(sequence)"))
 	{
 		// remove the (sequence) and append 000
@@ -480,9 +481,34 @@ void LWOImporter::AdjustTexturePath(std::string& out)
 	}
 }
 
+// ------------------------------------------------------------------------------------------------
+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;
+}
+
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWOTags(unsigned int size)
 {
+	// --- this function is used for both LWO2 and LWOB
+
 	const char* szCur = (const char*)mFileBuffer, *szLast = szCur;
 	const char* const szEnd = szLast+size;
 	while (szCur < szEnd)
@@ -501,6 +527,7 @@ 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 );
 
 	// perform endianess conversions
@@ -514,6 +541,17 @@ void LWOImporter::LoadLWOPoints(unsigned int length)
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWOPolygons(unsigned int length)
 {
+	// --- this function is used for both LWO2 and LWOB
+	if (mIsLWO2)
+	{
+		uint32_t type = *((LE_NCONST uint32_t*)mFileBuffer);mFileBuffer += 4;
+		if (type != AI_LWO_FACE)
+		{
+			DefaultLogger::get()->warn("LWO2: Only POLS.FACE chunsk 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;
@@ -525,76 +563,158 @@ void LWOImporter::LoadLWOPolygons(unsigned int length)
 #endif
 
 	unsigned int iNumFaces = 0,iNumVertices = 0;
-	CountVertsAndFaces(iNumVertices,iNumFaces,cursor,end);
+	if (mIsLWO2)CountVertsAndFacesLWO2(iNumVertices,iNumFaces,cursor,end);
+	else CountVertsAndFacesLWOB(iNumVertices,iNumFaces,cursor,end);
 
 	// allocate the output array and copy face indices
 	if (iNumFaces)
 	{
 		cursor = (LE_NCONST uint16_t*)mFileBuffer;
-		// this->mTempPoints->resize(iNumVertices);
+
 		mCurLayer->mFaces.resize(iNumFaces);
 		FaceList::iterator it = mCurLayer->mFaces.begin();
-		CopyFaceIndices(it,cursor,end);
+		if (mIsLWO2)CopyFaceIndicesLWO2(it,cursor,end);
+		else CopyFaceIndicesLWOB(it,cursor,end);
 	}
 }
 
 // ------------------------------------------------------------------------------------------------
-void LWOImporter::LoadLWOBFile()
+void LWOImporter::CountVertsAndFacesLWO2(unsigned int& verts, unsigned int& faces,
+	LE_NCONST uint16_t*& cursor, const uint16_t* const end, unsigned int max)
 {
-	LE_NCONST uint8_t* const end = mFileBuffer + fileSize;
-	while (true)
+	while (cursor < end && max--)
 	{
-		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);
-		if (mFileBuffer + head->length > end)
-		{
-			throw new ImportErrorException("LWOB: Invalid file, the size attribute of "
-				"a chunk points behind the end of the file");
-			break;
-		}
-		LE_NCONST uint8_t* const next = mFileBuffer+head->length;
-		switch (head->type)
+		uint16_t numIndices = *cursor++;
+		numIndices &= 0x03FF;
+		verts += numIndices;++faces;
+
+		for(uint16_t i = 0; i < numIndices; i++)
+			ReadVSizedIntLWO2((uint8_t*&)cursor);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::CopyFaceIndicesLWO2(FaceList::iterator& it,
+	LE_NCONST uint16_t*& cursor, 
+	const uint16_t* const end,
+	unsigned int max)
+{
+	while (cursor < end && max--)
+	{
+		LWO::Face& face = *it;++it;
+		if(face.mNumIndices = (*cursor++) & 0x03FF)
 		{
-			// vertex list
-		case AI_LWO_PNTS:
-			{
-				if (!mCurLayer->mTempPoints.empty())
-					DefaultLogger::get()->warn("LWO: PNTS chunk encountered twice");
-				else LoadLWOPoints(head->length);
-				break;
-			}
-			// face list
-		case AI_LWO_POLS:
-			{
-				if (!mCurLayer->mFaces.empty())
-					DefaultLogger::get()->warn("LWO: POLS chunk encountered twice");
-				else LoadLWOPolygons(head->length);
-				break;
-			}
-			// list of tags
-		case AI_LWO_SRFS:
+			face.mIndices = new unsigned int[face.mNumIndices];
+			
+			for(unsigned int i = 0; i < face.mNumIndices; i++)
 			{
-				if (!mTags->empty())
-					DefaultLogger::get()->warn("LWO: SRFS chunk encountered twice");
-				else LoadLWOTags(head->length);
-				break;
+				face.mIndices[i] = ReadVSizedIntLWO2((uint8_t*&)cursor) + mCurLayer->mPointIDXOfs;
+				if(face.mIndices[i] > mCurLayer->mTempPoints.size())
+				{
+					DefaultLogger::get()->warn("LWO2: face index is out of range");
+					face.mIndices[i] = (unsigned int)mCurLayer->mTempPoints.size()-1;
+				}
 			}
+		}
+		else DefaultLogger::get()->warn("LWO2: face has 0 indices");
+	}
+}
 
-			// surface chunk
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2PolygonTags(unsigned int length)
+{
+	uint32_t type = *((LE_NCONST uint32_t*)mFileBuffer);mFileBuffer+=4;
+	AI_LSWAP4(type);
+
+	if (type != AI_LWO_SURF && type != AI_LWO_SMGP)
+		return;
+
+	LE_NCONST uint8_t* const end = mFileBuffer+length;
+	while (mFileBuffer < end)
+	{
+		unsigned int i = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
+		unsigned int j = ReadVSizedIntLWO2(mFileBuffer);
+
+		if (i > mCurLayer->mFaces.size())
+		{
+			DefaultLogger::get()->warn("LWO2: face index in ptag list is out of range");
+			continue;
+		}
+
+		switch (type)
+		{
 		case AI_LWO_SURF:
-			{
-				if (!mSurfaces->empty())
-					DefaultLogger::get()->warn("LWO: SURF chunk encountered twice");
-				else LoadLWOBSurface(head->length);
-				break;
-			}
+			mCurLayer->mFaces[i].surfaceIndex = j;
+			break;
+		case AI_LWO_SMGP:
+			mCurLayer->mFaces[i].smoothGroup = j;
+			break;
+		};
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+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;
+
+	VMapEntry* base;
+
+	switch (type)
+	{
+	case AI_LWO_TXUV:
+		if (dims != 2)
+		{
+			DefaultLogger::get()->warn("LWO2: Found UV channel with != 2 components"); 
 		}
-		mFileBuffer = next;
+		mCurLayer->mUVChannels.push_back(UVChannel(mCurLayer->mTempPoints.size()));
+		base = &mCurLayer->mUVChannels.back();
+	case AI_LWO_WGHT:
+		if (dims != 1)
+		{
+			DefaultLogger::get()->warn("LWO2: found vertex weight map with != 1 components"); 
+		}
+		mCurLayer->mWeightChannels.push_back(WeightChannel(mCurLayer->mTempPoints.size()));
+		base = &mCurLayer->mWeightChannels.back();
+	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(mCurLayer->mTempPoints.size()));
+		base = &mCurLayer->mVColorChannels.back();
+	default: return;
+	};
+
+	// read the name of the vertex map 
+	ParseString(base->name,length);
+
+	// 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;
+	while (mFileBuffer < end)
+	{
+		unsigned int idx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mPointIDXOfs;
+		if (idx > mCurLayer->mTempPoints.size())
+		{
+			DefaultLogger::get()->warn("LWO2: vertex index in vmap/vmad is out of range");
+			continue;
+		}
+		for (unsigned int i = 0; i < type;++i)
+		{
+			base->rawData[idx*dims+i]= *((float*)mFileBuffer);
+			mFileBuffer += 4;
+		}
+		mFileBuffer += diff;
 	}
 }
+
+
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWO2File()
 {
@@ -647,24 +767,41 @@ void LWOImporter::LoadLWO2File()
 			// vertex list
 		case AI_LWO_PNTS:
 			{
-				if (!mCurLayer->mTempPoints.empty())
-					DefaultLogger::get()->warn("LWO: PNTS chunk encountered twice");
-				else LoadLWOPoints(head->length);
+				unsigned int old = (unsigned int)mCurLayer->mTempPoints.size();
+				LoadLWOPoints(head->length);
+				mCurLayer->mPointIDXOfs = old;
+				break;
+			}
+			// vertex tags
+		//case AI_LWO_VMAD:
+		case AI_LWO_VMAP:
+			{
+				if (mCurLayer->mTempPoints.empty())
+					DefaultLogger::get()->warn("LWO2: Unexpected VMAD/VMAP chunk");
+				else LoadLWO2VertexMap(head->length,head->type == AI_LWO_VMAD);
 				break;
 			}
 			// face list
 		case AI_LWO_POLS:
 			{
-				if (!mCurLayer->mFaces.empty())
-					DefaultLogger::get()->warn("LWO: POLS chunk encountered twice");
-				else LoadLWOPolygons(head->length);
+				unsigned int old = (unsigned int)mCurLayer->mFaces.size();
+				LoadLWOPolygons(head->length);
+				mCurLayer->mFaceIDXOfs = old;
+				break;
+			}
+			// polygon tags 
+		case AI_LWO_PTAG:
+			{
+				if (mCurLayer->mFaces.empty())
+					DefaultLogger::get()->warn("LWO2: Unexpected PTAG");
+				else LoadLWO2PolygonTags(head->length);
 				break;
 			}
 			// list of tags
 		case AI_LWO_SRFS:
 			{
 				if (!mTags->empty())
-					DefaultLogger::get()->warn("LWO: SRFS chunk encountered twice");
+					DefaultLogger::get()->warn("LWO2: SRFS chunk encountered twice");
 				else LoadLWOTags(head->length);
 				break;
 			}
@@ -673,8 +810,8 @@ void LWOImporter::LoadLWO2File()
 		case AI_LWO_SURF:
 			{
 				if (!mSurfaces->empty())
-					DefaultLogger::get()->warn("LWO: SURF chunk encountered twice");
-				else LoadLWOBSurface(head->length);
+					DefaultLogger::get()->warn("LWO2: SURF chunk encountered twice");
+				else LoadLWO2Surface(head->length);
 				break;
 			}
 		}

+ 56 - 3
code/LWOLoader.h

@@ -55,7 +55,13 @@ namespace Assimp	{
 using namespace LWO;
 
 // ---------------------------------------------------------------------------
-/** Clas to load LWO files
+/** Class to load LWO files.
+ *
+ *  @note  Methods named "xxxLWO2[xxx]" are used with the newer LWO2 format.
+ *         Methods named "xxxLWOB[xxx]" are used with the older LWOB format.
+ *         Methods named "xxxLWO[xxx]" are used with both formats.
+ *         Methods named "xxx" are used to preprocess the loaded data -
+ *         they aren't specific to one format version, either
 */
 class LWOImporter : public BaseImporter
 {
@@ -157,6 +163,19 @@ private:
 	*/
 	void LoadLWOPolygons(unsigned int length);
 
+	// -------------------------------------------------------------------
+	/** Load polygon tags from a PTAG chunk
+	 *  @param length Size of the chunk
+	*/
+	void LoadLWO2PolygonTags(unsigned int length);
+
+	// -------------------------------------------------------------------
+	/** Load a vertex map from a VMAP/VMAD chunk
+	 *  @param length Size of the chunk
+	 *  @param perPoly Operate on per-polygon base?
+	*/
+	void LoadLWO2VertexMap(unsigned int length, bool perPoly);
+
 	// -------------------------------------------------------------------
 	/** Load polygons from a PNTS chunk
 	 *  @param length Size of the chunk
@@ -167,7 +186,13 @@ private:
 	// -------------------------------------------------------------------
 	/** Count vertices and faces in a LWOB/LWO2 file
 	*/
-	void CountVertsAndFaces(unsigned int& verts, 
+	void CountVertsAndFacesLWO2(unsigned int& verts, 
+		unsigned int& faces,
+		LE_NCONST uint16_t*& cursor, 
+		const uint16_t* const end,
+		unsigned int max = 0xffffffff);
+
+	void CountVertsAndFacesLWOB(unsigned int& verts, 
 		unsigned int& faces,
 		LE_NCONST uint16_t*& cursor, 
 		const uint16_t* const end,
@@ -176,7 +201,12 @@ private:
 	// -------------------------------------------------------------------
 	/** Read vertices and faces in a LWOB/LWO2 file
 	*/
-	void CopyFaceIndices(LWO::FaceList::iterator& it,
+	void CopyFaceIndicesLWO2(LWO::FaceList::iterator& it,
+		LE_NCONST uint16_t*& cursor, 
+		const uint16_t* const end, 
+		unsigned int max = 0xffffffff);
+
+	void CopyFaceIndicesLWOB(LWO::FaceList::iterator& it,
 		LE_NCONST uint16_t*& cursor, 
 		const uint16_t* const end, 
 		unsigned int max = 0xffffffff);
@@ -213,6 +243,23 @@ private:
 	*/
 	void ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat);
 
+	
+	// -------------------------------------------------------------------
+	/** Get a list of all UV/VC channels required by a specific surface.
+	 *
+	 *  @param surf Working surface
+	 *  @param layer Working layer
+	 *  @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,
+		unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS]);
+
+	void FindVCChannels(const LWO::Surface& surf, 
+		const LWO::Layer& layer,
+		unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS]);
+
 	// -------------------------------------------------------------------
 	/** Generate the final node graph
 	 *  Unused nodes are deleted.
@@ -220,6 +267,12 @@ private:
 	*/
 	void GenerateNodeGraph(std::vector<aiNode*>& apcNodes);
 
+	// -------------------------------------------------------------------
+	/** Read a variable sized integer
+	 *  @param inout Input and output buffer
+	*/
+	int ReadVSizedIntLWO2(uint8_t*& inout);
+
 protected:
 
 	/** true if the file is a LWO2 file*/

+ 9 - 132
code/LWOMaterial.cpp

@@ -113,140 +113,17 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat
 }
 
 // ------------------------------------------------------------------------------------------------
-void LWOImporter::LoadLWOBSurface(unsigned int size)
+void LWOImporter::FindUVChannels(const LWO::Surface& surf, const LWO::Layer& layer,
+	unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS])
 {
-	LE_NCONST uint8_t* const end = mFileBuffer + size;
-
-	uint32_t iCursor = 0;
-	mSurfaces->push_back( LWO::Surface () );
-	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; 
-	while (true)
-	{
-		if (mFileBuffer + 6 > end)break;
+	out[0] = 0xffffffff;
+}
 
-		// 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)
-		{
-		// 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;
-				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;
-				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;
-				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;
-				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;
-				break;
-			}
-		// glossiness
-		case AI_LWO_GLOS:
-			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,GLOS,2);
-				AI_LSWAP2P(mFileBuffer);
-				surf.mGlossiness = float(*((int16_t*)mFileBuffer));
-				break;
-			}
-		// color texture
-		case AI_LWO_CTEX:
-			{
-				pTex = &surf.mColorTexture;
-				break;
-			}
-		// diffuse texture
-		case AI_LWO_DTEX:
-			{
-				pTex = &surf.mDiffuseTexture;
-				break;
-			}
-		// specular texture
-		case AI_LWO_STEX:
-			{
-				pTex = &surf.mSpecularTexture;
-				break;
-			}
-		// bump texture
-		case AI_LWO_BTEX:
-			{
-				pTex = &surf.mBumpTexture;
-				break;
-			}
-		// transparency texture
-		case AI_LWO_TTEX:
-			{
-				pTex = &surf.mTransparencyTexture;
-				break;
-			}
-			// texture path
-		case AI_LWO_TIMG:
-			{
-				if (pTex)
-				{
-					ParseString(pTex->mFileName,head_length);	
-					AdjustTexturePath(pTex->mFileName);
-					mFileBuffer += pTex->mFileName.length();
-				}
-				else DefaultLogger::get()->warn("LWOB: TIMG tag was encuntered although "
-					"there was no xTEX tag before");
-				break;
-			}
-		// texture strength
-		case AI_LWO_TVAL:
-			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head_length,TVAL,1);
-				if (pTex)pTex->mStrength = *mFileBuffer / 255.0f;
-				else DefaultLogger::get()->warn("LWOB: TVAL tag was encuntered "
-					"although there was no xTEX tag before");
-				break;
-			}
-		}
-		mFileBuffer = next;
-	}
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::FindVCChannels(const LWO::Surface& surf, const LWO::Layer& layer,
+	unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS])
+{
+	out[0] = 0xffffffff;
 }
 
 // ------------------------------------------------------------------------------------------------

+ 6 - 3
code/NFFLoader.cpp

@@ -140,6 +140,8 @@ void NFFImporter::InternReadFile( const std::string& pFile,
 	MeshInfo* currentMeshWithNormals = NULL;
 	MeshInfo* currentMesh = NULL;
 
+	ShadingInfo s; // current material info
+
 	char line[4096];
 	const char* sz;
 	unsigned int sphere = 0,cylinder = 0,cone = 0,numNamed = 0;
@@ -197,8 +199,6 @@ void NFFImporter::InternReadFile( const std::string& pFile,
 		{
 			SkipSpaces(&line[1],&sz);
 
-			ShadingInfo s; // color;
-
 			// read just the RGB colors, the rest is ignored for the moment
 			sz = fast_atof_move(sz, s.color.r);
 			SkipSpaces(&sz);
@@ -241,6 +241,7 @@ void NFFImporter::InternReadFile( const std::string& pFile,
 		{
 			meshesLocked.push_back(MeshInfo(false,true));
 			MeshInfo& currentMesh = meshesLocked.back();
+			currentMesh.shader = s;
 
 			sz = &line[1];
 			aiVector3D center; float radius;
@@ -261,6 +262,7 @@ void NFFImporter::InternReadFile( const std::string& pFile,
 		{
 			meshesLocked.push_back(MeshInfo(false,true));
 			MeshInfo& currentMesh = meshes.back();
+			currentMesh.shader = s;
 
 			sz = &line[1];
 			aiVector3D center1, center2; float radius1, radius2;
@@ -286,7 +288,8 @@ void NFFImporter::InternReadFile( const std::string& pFile,
 		// '#' - comment
 		else if ('#' == line[0])
 		{
-			DefaultLogger::get()->info(line);
+			const char* sz;SkipSpaces(&line[1],&sz);
+			if (!IsLineEnd(*sz))DefaultLogger::get()->info(sz);
 		}
 	}
 

+ 0 - 3
code/SmoothingGroups.inl

@@ -117,7 +117,6 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh)
 				posEpsilon,poResult);
 
 			aiVector3D vNormals;
-			float fDiv = 0.0f;
 			for (std::vector<unsigned int>::const_iterator
 				a =  poResult.begin();
 				a != poResult.end();++a)
@@ -125,10 +124,8 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh)
 				vNormals += sMesh.mNormals[(*a)];
 				//fDiv += 1.0f;
 			}
-			//vNormals.x /= fDiv;vNormals.y /= fDiv;vNormals.z /= fDiv;
 			vNormals.Normalize();
 			avNormals[(*i).mIndices[c]] = vNormals;
-			//poResult.clear();
 		}
 	}
 	sMesh.mNormals = avNormals;

+ 24 - 0
test/NFF/spheres.nff

@@ -0,0 +1,24 @@
+
+#default color
+
+# A simple sphere
+s 5.0 5.0 5.0 10.0
+
+#blue
+f 0.0 0.0 1.0 0 1 1
+
+# Another simple sphere
+s 5.0 4.0 8.0 3.0
+
+#green
+f 0.0 1.0 0.0 0 1 1
+
+# And another one
+s 1.0 -4.0 2.0 4.0
+
+#red
+f 1.0 0.0 0.0 0 1 1
+
+# a simple cone
+c 10 10 5 3
+c 14 14 3 6

+ 12 - 0
workspaces/vc8/assimp.vcproj

@@ -1101,6 +1101,14 @@
 						>
 					</File>
 				</Filter>
+				<Filter
+					Name="FBX"
+					>
+				</Filter>
+				<Filter
+					Name="Collada"
+					>
+				</Filter>
 			</Filter>
 		</Filter>
 		<Filter
@@ -1276,6 +1284,10 @@
 				<Filter
 					Name="LWO"
 					>
+					<File
+						RelativePath="..\..\code\LWOBLoader.cpp"
+						>
+					</File>
 					<File
 						RelativePath="..\..\code\LWOLoader.cpp"
 						>