From af18307f95805ffd3c2b2fe1c1a719e179639b39 Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Mon, 8 Sep 2008 16:48:21 +0000 Subject: [PATCH] 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 --- code/LWOBLoader.cpp | 308 ++++++++++++++++++++++++++++ code/LWOFileData.h | 126 +++++++++++- code/LWOLoader.cpp | 379 ++++++++++++++++++++++++----------- code/LWOLoader.h | 59 +++++- code/LWOMaterial.cpp | 141 +------------ code/NFFLoader.cpp | 9 +- code/SmoothingGroups.inl | 3 - test/NFF/spheres.nff | 24 +++ workspaces/vc8/assimp.vcproj | 12 ++ 9 files changed, 790 insertions(+), 271 deletions(-) create mode 100644 code/LWOBLoader.cpp create mode 100644 test/NFF/spheres.nff diff --git a/code/LWOBLoader.cpp b/code/LWOBLoader.cpp new file mode 100644 index 000000000..eca5c0988 --- /dev/null +++ b/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; + } +} \ No newline at end of file diff --git a/code/LWOFileData.h b/code/LWOFileData.h index 30cde324c..b4ae07c8f 100644 --- a/code/LWOFileData.h +++ b/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 #include + +// 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 PointList; -typedef std::vector FaceList; -typedef std::vector SurfaceList; -typedef std::vector TagList; -typedef std::vector 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; diff --git a/code/LWOLoader.cpp b/code/LWOLoader.cpp index 69be2f329..952c98964 100644 --- a/code/LWOLoader.cpp +++ b/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& 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) - { - // 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; - } + uint16_t numIndices = *cursor++; + numIndices &= 0x03FF; + verts += numIndices;++faces; - // surface chunk - case AI_LWO_SURF: - { - if (!mSurfaces->empty()) - DefaultLogger::get()->warn("LWO: SURF chunk encountered twice"); - else LoadLWOBSurface(head->length); - break; - } - } - mFileBuffer = next; + 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) + { + face.mIndices = new unsigned int[face.mNumIndices]; + + for(unsigned int i = 0; i < face.mNumIndices; i++) + { + 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"); + } +} + + +// ------------------------------------------------------------------------------------------------ +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: + 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"); + } + 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; } } diff --git a/code/LWOLoader.h b/code/LWOLoader.h index 84f752813..dd8002ecf 100644 --- a/code/LWOLoader.h +++ b/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& 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*/ diff --git a/code/LWOMaterial.cpp b/code/LWOMaterial.cpp index 982a292c3..85ad1f0d1 100644 --- a/code/LWOMaterial.cpp +++ b/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; + out[0] = 0xffffffff; +} - 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; - } +// ------------------------------------------------------------------------------------------------ +void LWOImporter::FindVCChannels(const LWO::Surface& surf, const LWO::Layer& layer, + unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS]) +{ + out[0] = 0xffffffff; } // ------------------------------------------------------------------------------------------------ diff --git a/code/NFFLoader.cpp b/code/NFFLoader.cpp index 22f075ab8..9c01c435a 100644 --- a/code/NFFLoader.cpp +++ b/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); } } diff --git a/code/SmoothingGroups.inl b/code/SmoothingGroups.inl index 1cb089c65..a86e4fe28 100644 --- a/code/SmoothingGroups.inl +++ b/code/SmoothingGroups.inl @@ -117,7 +117,6 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) posEpsilon,poResult); aiVector3D vNormals; - float fDiv = 0.0f; for (std::vector::const_iterator a = poResult.begin(); a != poResult.end();++a) @@ -125,10 +124,8 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& 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; diff --git a/test/NFF/spheres.nff b/test/NFF/spheres.nff new file mode 100644 index 000000000..b432d140c --- /dev/null +++ b/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 \ No newline at end of file diff --git a/workspaces/vc8/assimp.vcproj b/workspaces/vc8/assimp.vcproj index 03e27353e..313e5a70c 100644 --- a/workspaces/vc8/assimp.vcproj +++ b/workspaces/vc8/assimp.vcproj @@ -1101,6 +1101,14 @@ > + + + + + +