diff --git a/CREDITS b/CREDITS index adc03c8b2..e4f9e3d9f 100644 --- a/CREDITS +++ b/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. diff --git a/code/DXFLoader.cpp b/code/DXFLoader.cpp index dd737ff4d..79b37eb26 100644 --- a/code/DXFLoader.cpp +++ b/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::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& vPositions = (*it).vPositions; + const std::vector& vColors = (*it).vColors; + + // check whether we need vertex colors here + aiColor4D* clrOut; + const aiColor4D* clr = NULL; + for (std::vector::const_iterator it2 = (*it).vColors.begin(), end2 = (*it).vColors.end(); + it2 != end2; ++it2) + { + if (std::numeric_limits::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 positions; + std::vector colors; std::vector 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::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; } diff --git a/code/DXFLoader.h b/code/DXFLoader.h index cc7940bdb..901c04d1e 100644 --- a/code/DXFLoader.h +++ b/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 vPositions; + std::vector 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: diff --git a/code/GenFaceNormalsProcess.cpp b/code/GenFaceNormalsProcess.cpp index b8d8b0c0e..8ac19d274 100644 --- a/code/GenFaceNormalsProcess.cpp +++ b/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; } diff --git a/code/GenVertexNormalsProcess.cpp b/code/GenVertexNormalsProcess.cpp index 9a1648af9..ab6f54484 100644 --- a/code/GenVertexNormalsProcess.cpp +++ b/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 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; diff --git a/code/IFF.h b/code/IFF.h index 69b9fa407..31004b414 100644 --- a/code/IFF.h +++ b/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"; diff --git a/code/LWOBLoader.cpp b/code/LWOBLoader.cpp index 100d278be..524d10465 100644 --- a/code/LWOBLoader.cpp +++ b/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; diff --git a/code/LWOFileData.h b/code/LWOFileData.h index cc0c1cc3b..e245fb9d1 100644 --- a/code/LWOFileData.h +++ b/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 rawData; + + std::vector abAssigned; }; // --------------------------------------------------------------------------- @@ -283,16 +307,29 @@ struct VMapEntry */ struct VColorChannel : public VMapEntry { - VColorChannel(unsigned int num) + VColorChannel() : VMapEntry(4) - { - data = new aiColor4D[num]; - for (unsigned int i = 0; i < num;++i) - data[i].a = 1.0f; - rawData = (float*)data; - } + {} - aiColor4D* data; + //! need to overwrite this function - the alpha channel must + //! be initialized to 1.0 by default + 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); + + for (std::vector::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); + } }; // --------------------------------------------------------------------------- @@ -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 LayerList; diff --git a/code/LWOLoader.cpp b/code/LWOLoader.cpp index a602fbff0..49bd0a096 100644 --- a/code/LWOLoader.cpp +++ b/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 +#include +#include 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 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 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& 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::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; } } @@ -352,11 +380,103 @@ void LWOImporter::InternReadFile( const std::string& pFile, GenerateNodeGraph(apcNodes); } +// ------------------------------------------------------------------------------------------------ +void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector& smoothingGroups, + const LWO::Surface& surface) +{ + // allocate output storage + mesh->mNormals = new aiVector3D[mesh->mNumVertices]; + + // First generate per-face normals + aiVector3D* out; + std::vector 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::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 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::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& apcNodes) { - unsigned int numChilds = 0; - 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) + // 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) { - i = c << 8; - c = *inout;inout++; - i |= c; + out.insert(n+1,"/"); } - else - { - c = *inout;inout++; - i = c << 16; - c = *inout;inout++; - i |= c << 8; - c = *inout;inout++; - i |= c; - } - 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 +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 +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; } diff --git a/code/LWOLoader.h b/code/LWOLoader.h index f33c19120..5fc01ee6f 100644 --- a/code/LWOLoader.h +++ b/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(uint32_t type, unsigned int size ); + 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 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& 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 diff --git a/code/LWOMaterial.cpp b/code/LWOMaterial.cpp index 3f655aaf4..cadd8dfb9 100644 --- a/code/LWOMaterial.cpp +++ b/code/LWOMaterial.cpp @@ -58,6 +58,142 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; +// ------------------------------------------------------------------------------------------------ +template +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*)&temp,1,buffer); + + // add the UV source index + sprintf(buffer,"$tex.uvw.%s[%i]",type,cur); + temp = (*it).mRealUVIndex; + pcMat->AddProperty((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*)&temp,1,buffer); + + // add the v-wrapping + sprintf(buffer,"$tex.mapmodev.%s[%i]",type,cur); + temp = (unsigned int)GetMapMode((*it).wrapModeHeight); + pcMat->AddProperty((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(&i,1,AI_MATKEY_TWOSIDED); + + // add the refraction index and the bump intensity + pcMat->AddProperty(&surf.mIOR,1,AI_MATKEY_REFRACTI); + pcMat->AddProperty(&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(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH); pcMat->AddProperty(&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(&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(&clr,1,AI_MATKEY_COLOR_SPECULAR); + pcMat->AddProperty(&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(&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(&clr,1,AI_MATKEY_COLOR_DIFFUSE); } // ------------------------------------------------------------------------------------------------ -void LWOImporter::FindUVChannels(const LWO::Surface& surf, const LWO::Layer& layer, +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(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,41 +360,240 @@ 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::LoadLWO2ShaderBlock(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::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"; + } + + // 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; + } + + // 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) + { + surf.mShaders.insert(it,shader); + return; + } + } + surf.mShaders.push_back(shader); } // ------------------------------------------------------------------------------------------------ @@ -169,88 +604,131 @@ void LWOImporter::LoadLWO2Surface(unsigned int size) mSurfaces->push_back( LWO::Surface () ); LWO::Surface& surf = mSurfaces->back(); - 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; + 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); - // 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("LWO2: 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) + 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; diff --git a/code/MDRLoader.cpp b/code/MDRLoader.cpp index 7b3bd5dad..6838d377f 100644 --- a/code/MDRLoader.cpp +++ b/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(); } \ No newline at end of file diff --git a/code/SGSpatialSort.cpp b/code/SGSpatialSort.cpp index 95bd52977..803b3b65e 100644 --- a/code/SGSpatialSort.cpp +++ b/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& poResults) const + uint32_t pSG, + float pRadius, + std::vector& 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::const_iterator it = mPositions.begin() + index; - while( it->mDistance < maxDist) + std::vector::const_iterator it = mPositions.begin() + index; + std::vector::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) { - poResults.push_back( it->mIndex); + 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) + { + 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; } } diff --git a/code/SGSpatialSort.h b/code/SGSpatialSort.h index 451516eb2..474811aa7 100644 --- a/code/SGSpatialSort.h +++ b/code/SGSpatialSort.h @@ -46,13 +46,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #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& 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& poResults) const; + float pRadius, std::vector& 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) diff --git a/code/StandardShapes.cpp b/code/StandardShapes.cpp index ffa7abc90..ba57b82f3 100644 --- a/code/StandardShapes.cpp +++ b/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& positions) { + //aiVector3D current = aiVector3D ( normal.x, } } // ! Assimp diff --git a/code/StandardShapes.h b/code/StandardShapes.h index fa13bc37b..f37b61579 100644 --- a/code/StandardShapes.h +++ b/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& positions); diff --git a/include/aiMaterial.h b/include/aiMaterial.h index d26bf0bdf..2243ca4b3 100644 --- a/include/aiMaterial.h +++ b/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 could 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 compile time.
- * 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 :-) ).
+ * of an ASSIMP loader has made the same fault :-) ).
* 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). + *
+ * Type: float
+ * Default value: 1.0f
+*/ +#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 - *
+ * 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). + *
* Type: string (aiString)
* Default value: none
* @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 *
@@ -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 diff --git a/include/aiTypes.h b/include/aiTypes.h index 1ad52d646..ea251b5a6 100644 --- a/include/aiTypes.h +++ b/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 diff --git a/port/jAssimp/README b/port/jAssimp/README new file mode 100644 index 000000000..e69de29bb diff --git a/port/jAssimp/jni_bridge/JNIMesh.cpp b/port/jAssimp/jni_bridge/JNIMesh.cpp index 5608f5f8c..08f359262 100644 --- a/port/jAssimp/jni_bridge/JNIMesh.cpp +++ b/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, diff --git a/port/jAssimp/src/assimp/BoneAnim.java b/port/jAssimp/src/assimp/BoneAnim.java index 54a3ea460..5316c77a0 100644 --- a/port/jAssimp/src/assimp/BoneAnim.java +++ b/port/jAssimp/src/assimp/BoneAnim.java @@ -71,17 +71,17 @@ public class BoneAnim { /** * Rotation keyframes */ - private KeyFrame[] mQuatKeys; + private KeyFrame[] mQuatKeys = null; /** - * Position keyframes. Component ordering is x,y,z + * Position keyframes. Component order is x,y,z */ - private KeyFrame[] mPosKeys; + private KeyFrame[] mPosKeys = null; /** - * scaling keyframes. Component ordering is x,y,z + * scaling keyframes. Component order is x,y,z */ - private KeyFrame[] mScalingKeys; + private KeyFrame[] mScalingKeys = null; /** diff --git a/port/jAssimp/src/assimp/Importer.java b/port/jAssimp/src/assimp/Importer.java index 06a115330..50bdaec49 100644 --- a/port/jAssimp/src/assimp/Importer.java +++ b/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 properties = new PropertyList(); - private PropertyList propertiesFloat = new PropertyList(); - private PropertyList propertiesString = new PropertyList(); + private PropertyList propertiesFloat = new PropertyList(); + private PropertyList propertiesString = new PropertyList(); /** @@ -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 null 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 null if none is loaded + * @return Game Over. + */ + public final String getScenePath() { + return path; + } + + /** * Implementation of java.lang.Object.equals() * @@ -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 diff --git a/port/jAssimp/src/assimp/Material.java b/port/jAssimp/src/assimp/Material.java index 4e15fdf47..69d3aed12 100644 --- a/port/jAssimp/src/assimp/Material.java +++ b/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 String 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 Object + * * @param key MATKEY_XXX key constant - * @return null if the property wasn't there or hasn't - * the desired output type. The returned Object 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 Object 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 { *
* Type: int (TextureOp)
* Default value: 0
- * Requires: MATKEY_TEXTURE_DIFFUSE(0)
+ * Requires: MATKEY_TEXTURE_DIFFUSE(N)
+ * + * @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. + *

+ *
+ * Type: float
+ * Default value: 1.0f
+ * Requires: MATKEY_TEXTURE_DIFFUSE(N)
+ * + * @param N Index of the texture + */ + public static String MATKEY_TEXBLEND_DIFFUSE(int N) { + return "$tex.blend.diffuse[" + N + "]"; + } + /** + * @see MATKEY_TEXBLEND_DIFFUSE + */ + public static String MATKEY_TEXBLEND_SPECULAR(int N) { + return "$tex.blend.specular[" + N + "]"; + } + + /** + * @see MATKEY_TEXBLEND_DIFFUSE + */ + public static String MATKEY_TEXBLEND_AMBIENT(int N) { + return "$tex.blend.ambient[" + N + "]"; + } + + /** + * @see MATKEY_TEXBLEND_DIFFUSE + */ + public static String MATKEY_TEXBLEND_EMISSIVE(int N) { + return "$tex.blend.emissive[" + N + "]"; + } + + /** + * @see MATKEY_TEXBLEND_DIFFUSE + */ + public static String MATKEY_TEXBLEND_SHININESS(int N) { + return "$tex.blend.shininess[" + N + "]"; + } + + /** + * @see MATKEY_TEXBLEND_DIFFUSE + */ + public static String MATKEY_TEXBLEND_OPACITY(int N) { + return "$tex.blend.opacity[" + N + "]"; + } + + /** + * @see MATKEY_TEXBLEND_DIFFUSE + */ + public static String MATKEY_TEXBLEND_HEIGHT(int N) { + return "$tex.blend.height[" + N + "]"; + } + + /** + * @see MATKEY_TEXBLEND_DIFFUSE + */ + 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 + *
+ * Type:int
+ * Default value:0
+ * Requires: MATKEY_TEXTURE_DIFFUSE(N)
+ * + * @param N Index of the texture + */ + public static String MATKEY_UVWSRC_DIFFUSE(int N) { + return "$tex.uvw.diffuse[" + N + "]"; + } + + /** + * @see MATKEY_UVWSRC_DIFFUSE + */ + public static String MATKEY_UVWSRC_SPECULAR(int N) { + return "$tex.uvw.specular[" + N + "]"; + } + + /** + * @see MATKEY_UVWSRC_DIFFUSE + */ + public static String MATKEY_UVWSRC_AMBIENT(int N) { + return "$tex.uvw.ambient[" + N + "]"; + } + + /** + * @see MATKEY_UVWSRC_DIFFUSE + */ + public static String MATKEY_UVWSRC_EMISSIVE(int N) { + return "$tex.uvw.emissive[" + N + "]"; + } + + /** + * @see MATKEY_UVWSRC_DIFFUSE + */ + public static String MATKEY_UVWSRC_SHININESS(int N) { + return "$tex.uvw.shininess[" + N + "]"; + } + + /** + * @see MATKEY_UVWSRC_DIFFUSE + */ + public static String MATKEY_UVWSRC_OPACITY(int N) { + return "$tex.uvw.opacity[" + N + "]"; + } + + /** + * @see MATKEY_UVWSRC_DIFFUSE + */ + public static String MATKEY_UVWSRC_HEIGHT(int N) { + return "$tex.uvw.height[" + N + "]"; + } + + /** + * @see MATKEY_UVWSRC_DIFFUSE + */ + public static String MATKEY_UVWSRC_NORMALS(int N) { + return "$tex.uvw.normals[" + N + "]"; + } + + + /** + * Specifies the texture mapping mode in the v (y) direction. + *
+ * Type:int
+ * Default value:TextureMapMode.Wrap
+ * Requires: MATKEY_TEXTURE_DIFFUSE(N)
+ * + * @param N Index of the texture + */ + public static String MATKEY_MAPPINGMODE_U_DIFFUSE(int N) { + return "$tex.mapmodeu.diffuse[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_U_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_U_SPECULAR(int N) { + return "$tex.mapmodeu.specular[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_U_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_U_AMBIENT(int N) { + return "$tex.mapmodeu.ambient[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_U_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_U_EMISSIVE(int N) { + return "$tex.mapmodeu.emissive[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_U_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_U_SHININESS(int N) { + return "$tex.mapmodeu.shininess[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_U_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_U_OPACITY(int N) { + return "$tex.mapmodeu.opacity[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_U_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_U_HEIGHT(int N) { + return "$tex.mapmodeu.height[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_U_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_U_NORMALS(int N) { + return "$tex.mapmodeu.normals[" + N + "]"; + } + + + /** + * Specifies the texture mapping mode in the v (y) direction. + *
+ * Type:int
+ * Default value:TextureMapMode.Wrap
+ * Requires: MATKEY_TEXTURE_DIFFUSE(N)
+ * + * @param N Index of the texture + */ + public static String MATKEY_MAPPINGMODE_V_DIFFUSE(int N) { + return "$tex.mapmodev.diffuse[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_V_SPECULAR(int N) { + return "$tex.mapmodev.specular[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_V_AMBIENT(int N) { + return "$tex.mapmodev.ambient[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_V_EMISSIVE(int N) { + return "$tex.mapmodev.emissive[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_V_SHININESS(int N) { + return "$tex.mapmodev.shininess[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_V_OPACITY(int N) { + return "$tex.mapmodev.opacity[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_V_HEIGHT(int N) { + return "$tex.mapmodev.height[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_V_NORMALS(int N) { + return "$tex.mapmodev.normals[" + N + "]"; + } + + + /** + * Specifies the texture mapping mode in the w (z) direction. + *
+ * Type:int
+ * Default value:TextureMapMode.Wrap
+ * Requires: MATKEY_TEXTURE_DIFFUSE(N)
+ * + * @param N Index of the texture + */ + public static String MATKEY_MAPPINGMODE_W_DIFFUSE(int N) { + return "$tex.mapmodew.diffuse[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_W_SPECULAR(int N) { + return "$tex.mapmodew.specular[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_W_AMBIENT(int N) { + return "$tex.mapmodew.ambient[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_W_EMISSIVE(int N) { + return "$tex.mapmodew.emissive[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_W_SHININESS(int N) { + return "$tex.mapmodew.shininess[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_W_OPACITY(int N) { + return "$tex.mapmodew.opacity[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_W_HEIGHT(int N) { + return "$tex.mapmodew.height[" + N + "]"; + } + + /** + * @see MATKEY_MAPPINGMODE_V_DIFFUSE + */ + public static String MATKEY_MAPPINGMODE_W_NORMALS(int N) { + return "$tex.mapmodew.normals[" + N + "]"; + } } diff --git a/port/jAssimp/src/assimp/PostProcessStep.java b/port/jAssimp/src/assimp/PostProcessStep.java index 7a096de28..288030f56 100644 --- a/port/jAssimp/src/assimp/PostProcessStep.java +++ b/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: - * http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf - */ - 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: + * http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf + */ + 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. + *

+ * 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:
- * 1. Remove animation nodes and data from the scene. This allows other - * steps for further optimizations.
- * 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.
- * 3. Remove hierarchy levels
- * - * 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. + *

+ * It is incompatible to the PreTransformVertices-Step. Some configuration + * options exist, see aiConfig.h for more details. + * Generally, two actions are available:
+ * 1. Remove animation nodes and data from the scene. This allows other + * steps for further optimizations.
+ * 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.
+ * 3. Remove hierarchy levels
+ *

+ * 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 diff --git a/port/jAssimp/src/assimp/TextureMapMode.java b/port/jAssimp/src/assimp/TextureMapMode.java index 91a5a1166..1b18d09e3 100644 --- a/port/jAssimp/src/assimp/TextureMapMode.java +++ b/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 aiTextureMapMode 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; } diff --git a/port/jAssimp/src/assimp/test/DumpToFile.java b/port/jAssimp/src/assimp/test/DumpToFile.java index e6f6f46a0..fd59644d0 100644 --- a/port/jAssimp/src/assimp/test/DumpToFile.java +++ b/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(); } diff --git a/test/3DSFiles/IMAGE1.bmp b/test/3DSFiles/IMAGE1.bmp index 5a88d5647..01b5253ba 100644 Binary files a/test/3DSFiles/IMAGE1.bmp and b/test/3DSFiles/IMAGE1.bmp differ diff --git a/test/3DSFiles/fels.3ds b/test/3DSFiles/fels.3ds new file mode 100644 index 000000000..81b8d1d00 Binary files /dev/null and b/test/3DSFiles/fels.3ds differ diff --git a/test/LWOFiles/LWO2/hierarchy.lwo b/test/LWOFiles/LWO2/hierarchy.lwo new file mode 100644 index 000000000..acd06840f Binary files /dev/null and b/test/LWOFiles/LWO2/hierarchy.lwo differ diff --git a/test/LWOFiles/LWO2/shader_test/CellShader.lwo b/test/LWOFiles/LWO2/shader_test/CellShader.lwo new file mode 100644 index 000000000..41250498e Binary files /dev/null and b/test/LWOFiles/LWO2/shader_test/CellShader.lwo differ diff --git a/test/LWOFiles/LWO2/shader_test/SuperCellShader.lwo b/test/LWOFiles/LWO2/shader_test/SuperCellShader.lwo new file mode 100644 index 000000000..9780dce10 Binary files /dev/null and b/test/LWOFiles/LWO2/shader_test/SuperCellShader.lwo differ diff --git a/test/LWOFiles/LWO2/shader_test/fastFresnel.lwo b/test/LWOFiles/LWO2/shader_test/fastFresnel.lwo new file mode 100644 index 000000000..a462c6ecd Binary files /dev/null and b/test/LWOFiles/LWO2/shader_test/fastFresnel.lwo differ diff --git a/test/LWOFiles/LWO2/shader_test/realFresnel.lwo b/test/LWOFiles/LWO2/shader_test/realFresnel.lwo new file mode 100644 index 000000000..eb57908b5 Binary files /dev/null and b/test/LWOFiles/LWO2/shader_test/realFresnel.lwo differ diff --git a/workspaces/jidea5.1/jAssimp.ipr b/workspaces/jidea5.1/jAssimp.ipr index d66e5dcc9..ef3eb6d69 100644 --- a/workspaces/jidea5.1/jAssimp.ipr +++ b/workspaces/jidea5.1/jAssimp.ipr @@ -36,9 +36,9 @@ - @@ -49,7 +49,7 @@ -