From 2b9461fbf77cf3d2a2661c3bcbfab6db786d8aaa Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Fri, 13 Feb 2009 22:03:57 +0000 Subject: [PATCH] MD3 - finished support for multi-part player models - skin files are now read - shaders are parsed, but not yet processed yet DefaultIOSystem - file size is now cached over multiple calls to FileSize() MaterialSystem - added AI_MATKEY_BLEND_FUNC property and the aiBlendMode enum to allow MD3 and Collada to pass transparency information correctly. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@346 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/DefaultIOStream.cpp | 41 ++-- code/DefaultIOStream.h | 21 +- code/MD3FileData.h | 78 +++--- code/MD3Loader.cpp | 518 +++++++++++++++++++++++++++------------ code/MD3Loader.h | 151 +++++++++++- code/MD5Parser.h | 4 +- code/ParsingUtils.h | 46 +++- code/SMDLoader.h | 4 +- include/aiConfig.h | 15 +- include/aiMaterial.h | 117 +++++++-- 10 files changed, 729 insertions(+), 266 deletions(-) diff --git a/code/DefaultIOStream.cpp b/code/DefaultIOStream.cpp index e4f2616b0..ca0e02af9 100644 --- a/code/DefaultIOStream.cpp +++ b/code/DefaultIOStream.cpp @@ -38,7 +38,9 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file Default File I/O implementation for #Importer */ +/** @file DefaultIOStream.cpp + * @brief Default File I/O implementation for #Importer + */ #include "AssimpPCH.h" @@ -80,11 +82,12 @@ size_t DefaultIOStream::Write(const void* pvBuffer, aiReturn DefaultIOStream::Seek(size_t pOffset, aiOrigin pOrigin) { - if (!mFile)return AI_FAILURE; + if (!mFile) + return AI_FAILURE; // Just to check whether our enum maps one to one with the CRT constants - ai_assert(aiOrigin_CUR == SEEK_CUR && aiOrigin_END == SEEK_END - && aiOrigin_SET == SEEK_SET); + BOOST_STATIC_ASSERT(aiOrigin_CUR == SEEK_CUR && + aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET); // do the seek return (0 == ::fseek(mFile, (long)pOffset,(int)pOrigin) ? AI_SUCCESS : AI_FAILURE); @@ -102,25 +105,27 @@ size_t DefaultIOStream::Tell() const // ---------------------------------------------------------------------------------- size_t DefaultIOStream::FileSize() const { - ai_assert (!mFilename.empty()); - - if (! mFile) + if (! mFile || mFilename.empty()) return 0; + + if (0xffffffff == cachedSize) { - // TODO: Is that really faster if we have already opened the file? + // TODO: Is that really faster if we're already owning a handle to the file? #if defined _WIN32 && !defined __GNUC__ - struct __stat64 fileStat; - int err = _stat64( mFilename.c_str(), &fileStat ); - if (0 != err) - return 0; - return (size_t) (fileStat.st_size); + struct __stat64 fileStat; + int err = _stat64( mFilename.c_str(), &fileStat ); + if (0 != err) + return 0; + cachedSize = (size_t) (fileStat.st_size); #else - struct stat fileStat; - int err = stat(mFilename.c_str(), &fileStat ); - if (0 != err) - return 0; - return (size_t) (fileStat.st_size); + struct stat fileStat; + int err = stat(mFilename.c_str(), &fileStat ); + if (0 != err) + return 0; + cachedSize = (size_t) (fileStat.st_size); #endif + } + return cachedSize; } // ---------------------------------------------------------------------------------- diff --git a/code/DefaultIOStream.h b/code/DefaultIOStream.h index 2d469bcb9..0f7b711d0 100644 --- a/code/DefaultIOStream.h +++ b/code/DefaultIOStream.h @@ -50,6 +50,9 @@ namespace Assimp { // ---------------------------------------------------------------------------------- //! @class DefaultIOStream //! @brief Default IO implementation, use standard IO operations +//! @note An instance of this class can exist without a valid file handle +//! attached to it. All calls fail, but the instance can nevertheless be +//! used with no restrictions. class DefaultIOStream : public IOStream { friend class DefaultIOSystem; @@ -63,28 +66,33 @@ public: ~DefaultIOStream (); // ------------------------------------------------------------------- + // Read from stream size_t Read(void* pvBuffer, size_t pSize, size_t pCount); // ------------------------------------------------------------------- + // Write to stream size_t Write(const void* pvBuffer, size_t pSize, size_t pCount); - // ------------------------------------------------------------------- + // Seek specific position aiReturn Seek(size_t pOffset, aiOrigin pOrigin); // ------------------------------------------------------------------- + // Get current seek position size_t Tell() const; // ------------------------------------------------------------------- + // Get size of file size_t FileSize() const; // ------------------------------------------------------------------- + // Flush file contents void Flush(); private: @@ -92,13 +100,17 @@ private: FILE* mFile; //! Filename std::string mFilename; + + //! Cached file size + mutable size_t cachedSize; }; // ---------------------------------------------------------------------------------- inline DefaultIOStream::DefaultIOStream () : - mFile(NULL), - mFilename("") + mFile (NULL), + mFilename (""), + cachedSize (0xffffffff) { // empty } @@ -108,7 +120,8 @@ inline DefaultIOStream::DefaultIOStream () : inline DefaultIOStream::DefaultIOStream (FILE* pFile, const std::string &strFilename) : mFile(pFile), - mFilename(strFilename) + mFilename(strFilename), + cachedSize (0xffffffff) { // empty } diff --git a/code/MD3FileData.h b/code/MD3FileData.h index 71042c74d..50a62bf7b 100644 --- a/code/MD3FileData.h +++ b/code/MD3FileData.h @@ -38,9 +38,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** @file Defines the helper data structures for importing MD3 files. - http://linux.ucla.edu/~phaethon/q3/formats/md3format.html -*/ +/** @file Md3FileData.h + * + * @brief Defines helper data structures for importing MD3 files. + * http://linux.ucla.edu/~phaethon/q3/formats/md3format.html + */ #ifndef AI_MD3FILEHELPER_H_INC #define AI_MD3FILEHELPER_H_INC @@ -77,10 +79,9 @@ namespace MD3 { // master scale factor for all vertices in a MD3 model #define AI_MD3_XYZ_SCALE (1.0f/64.0f) -// --------------------------------------------------------------------------- -/** \brief Data structure for the MD3 main header +// ------------------------------------------------------------------------------- +/** @brief Data structure for the MD3 main header */ -// --------------------------------------------------------------------------- struct Header { //! magic number @@ -90,7 +91,7 @@ struct Header uint32_t VERSION; //! original name in .pak archive - unsigned char NAME[ AI_MD3_MAXQPATH ]; + char NAME[ AI_MD3_MAXQPATH ]; //! unknown int32_t FLAGS; @@ -121,10 +122,9 @@ struct Header } PACK_STRUCT; -// --------------------------------------------------------------------------- -/** \brief Data structure for the frame header +// ------------------------------------------------------------------------------- +/** @brief Data structure for the frame header */ -// --------------------------------------------------------------------------- struct Frame { //! minimum bounds @@ -145,14 +145,13 @@ struct Frame } PACK_STRUCT; -// --------------------------------------------------------------------------- -/** \brief Data structure for the tag header +// ------------------------------------------------------------------------------- +/** @brief Data structure for the tag header */ -// --------------------------------------------------------------------------- struct Tag { //! name of the tag - unsigned char NAME[ AI_MD3_MAXQPATH ]; + char NAME[ AI_MD3_MAXQPATH ]; //! Local tag origin and orientation aiVector3D origin; @@ -161,17 +160,16 @@ struct Tag } PACK_STRUCT; -// --------------------------------------------------------------------------- -/** \brief Data structure for the surface header +// ------------------------------------------------------------------------------- +/** @brief Data structure for the surface header */ -// --------------------------------------------------------------------------- struct Surface { //! magic number int32_t IDENT; //! original name of the surface - unsigned char NAME[ AI_MD3_MAXQPATH ]; + char NAME[ AI_MD3_MAXQPATH ]; //! unknown int32_t FLAGS; @@ -205,24 +203,22 @@ struct Surface int32_t OFS_END; } PACK_STRUCT; -// --------------------------------------------------------------------------- -/** \brief Data structure for a shader +// ------------------------------------------------------------------------------- +/** @brief Data structure for a shader defined in there */ -// --------------------------------------------------------------------------- struct Shader { //! filename of the shader - unsigned char NAME[ AI_MD3_MAXQPATH ]; + char NAME[ AI_MD3_MAXQPATH ]; //! index of the shader uint32_t SHADER_INDEX; } PACK_STRUCT; -// --------------------------------------------------------------------------- -/** \brief Data structure for a triangle +// ------------------------------------------------------------------------------- +/** @brief Data structure for a triangle */ -// --------------------------------------------------------------------------- struct Triangle { //! triangle indices @@ -230,10 +226,9 @@ struct Triangle } PACK_STRUCT; -// --------------------------------------------------------------------------- -/** \brief Data structure for an UV coord +// ------------------------------------------------------------------------------- +/** @brief Data structure for an UV coord */ -// --------------------------------------------------------------------------- struct TexCoord { //! UV coordinates @@ -241,10 +236,9 @@ struct TexCoord } PACK_STRUCT; -// --------------------------------------------------------------------------- -/** \brief Data structure for a vertex +// ------------------------------------------------------------------------------- +/** @brief Data structure for a vertex */ -// --------------------------------------------------------------------------- struct Vertex { //! X/Y/Z coordinates @@ -256,15 +250,14 @@ struct Vertex #include "./../include/Compiler/poppack1.h" -// --------------------------------------------------------------------------- -/** \brief Unpack a Q3 16 bit vector to its full float3 representation +// ------------------------------------------------------------------------------- +/** @brief Unpack a Q3 16 bit vector to its full float3 representation * - * \param p_iNormal Input normal vector in latitude/longitude form - * \param p_afOut Pointer to an array of three floats to receive the result + * @param p_iNormal Input normal vector in latitude/longitude form + * @param p_afOut Pointer to an array of three floats to receive the result * - * \note This has been taken from q3 source (misc_model.c) + * @note This has been taken from q3 source (misc_model.c) */ -// --------------------------------------------------------------------------- inline void LatLngNormalToVec3(uint16_t p_iNormal, float* p_afOut) { float lat = (float)(( p_iNormal >> 8u ) & 0xff); @@ -279,14 +272,13 @@ inline void LatLngNormalToVec3(uint16_t p_iNormal, float* p_afOut) } -// --------------------------------------------------------------------------- -/** \brief Pack a Q3 normal into 16bit latitute/longitude representation - * \param p_vIn Input vector - * \param p_iOut Output normal +// ------------------------------------------------------------------------------- +/** @brief Pack a Q3 normal into 16bit latitute/longitude representation + * @param p_vIn Input vector + * @param p_iOut Output normal * - * \note This has been taken from q3 source (mathlib.c) + * @note This has been taken from q3 source (mathlib.c) */ -// --------------------------------------------------------------------------- inline void Vec3NormalToLatLng( const aiVector3D& p_vIn, uint16_t& p_iOut ) { // check for singularities diff --git a/code/MD3Loader.cpp b/code/MD3Loader.cpp index 786eabac8..20b813e4f 100644 --- a/code/MD3Loader.cpp +++ b/code/MD3Loader.cpp @@ -39,20 +39,155 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file Implementation of the MD3 importer class */ +/** @file MD3Loader.cpp + * @brief Implementation of the MD3 importer class + * + * Sources: + * http://www.gamers.org/dEngine/quake3/UQ3S + * http://linux.ucla.edu/~phaethon/q3/formats/md3format.html + * http://www.heppler.com/shader/shader/ + */ #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_MD3_IMPORTER #include "MD3Loader.h" -#include "MaterialSystem.h" -#include "StringComparison.h" #include "ByteSwap.h" #include "SceneCombiner.h" #include "GenericProperty.h" +#include "RemoveComments.h" +#include "ParsingUtils.h" using namespace Assimp; +// ------------------------------------------------------------------------------------------------ +// Load a Quake 3 shader +void Q3Shader::LoadShader(ShaderData& fill, const std::string& pFile,IOSystem* io) +{ + boost::scoped_ptr file( io->Open( pFile, "rt")); + if (!file.get()) + return; // if we can't access the file, don't worry and return + + DefaultLogger::get()->info("Loading Quake3 shader file " + pFile); + + // read file in memory + const size_t s = file->FileSize(); + std::vector _buff(s+1); + file->Read(&_buff[0],s,1); + _buff[s] = 0; + + // remove comments from it (C++ style) + CommentRemover::RemoveLineComments("//",&_buff[0]); + const char* buff = &_buff[0]; + + Q3Shader::ShaderDataBlock* curData = NULL; + Q3Shader::ShaderMapBlock* curMap = NULL; + + // read line per line + for (;;SkipLine(&buff)) { + + if(!SkipSpacesAndLineEnd(&buff)) + break; + + if (*buff == '{') { + // append to last section, if any + if (!curData) { + DefaultLogger::get()->error("Q3Shader: Unexpected shader section token \'{\'"); + return; + } + + // read this map section + for (;;SkipLine(&buff)) { + if(!SkipSpacesAndLineEnd(&buff)) + break; + + if (*buff == '{') { + // add new map section + curData->maps.push_back(Q3Shader::ShaderMapBlock()); + curMap = &curData->maps.back(); + + } + else if (*buff == '}') { + // close this map section + if (curMap) + curMap = NULL; + else { + curData = NULL; + break; + } + } + // 'map' - Specifies texture file name + else if (TokenMatchI(buff,"map",3) || TokenMatchI(buff,"clampmap",8)) { + curMap->name = GetNextToken(buff); + } + // 'blendfunc' - Alpha blending mode + else if (TokenMatchI(buff,"blendfunc",9)) { + // fixme + } + } + } + + // 'cull' specifies culling behaviour for the model + else if (TokenMatch(buff,"cull",4)) { + SkipSpaces(&buff); + if (!ASSIMP_strincmp(buff,"back",4)) { + curData->cull = Q3Shader::CULL_CCW; + } + else if (!ASSIMP_strincmp(buff,"front",5)) { + curData->cull = Q3Shader::CULL_CW; + } + //else curData->cull = Q3Shader::CULL_NONE; + } + + else { + // add new section + fill.blocks.push_back(Q3Shader::ShaderDataBlock()); + curData = &fill.blocks.back(); + + // get the name of this section + curData->name = GetNextToken(buff); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Load a Quake 3 skin +void Q3Shader::LoadSkin(SkinData& fill, const std::string& pFile,IOSystem* io) +{ + boost::scoped_ptr file( io->Open( pFile, "rt")); + if (!file.get()) + return; // if we can't access the file, don't worry and return + + DefaultLogger::get()->info("Loading Quake3 skin file " + pFile); + + // read file in memory + const size_t s = file->FileSize(); + std::vector _buff(s+1);const char* buff = &_buff[0]; + file->Read(&_buff[0],s,1); + _buff[s] = 0; + + // remove commas + std::replace(_buff.begin(),_buff.end(),',',' '); + + // read token by token and fill output table + for (;*buff;) { + SkipSpacesAndLineEnd(&buff); + + // get first identifier + std::string ss = GetNextToken(buff); + + // ignore tokens starting with tag_ + if (!::strncmp(&ss[0],"_tag",std::min((size_t)4, ss.length()))) + continue; + + fill.textures.push_back(SkinData::TextureEntry()); + SkinData::TextureEntry& s = fill.textures.back(); + + s.first = ss; + s.second = GetNextToken(buff); + } +} + // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer MD3Importer::MD3Importer() @@ -84,16 +219,16 @@ bool MD3Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler) const // ------------------------------------------------------------------------------------------------ void MD3Importer::ValidateHeaderOffsets() { - // check magic number + // Check magic number if (pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE && pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE) throw new ImportErrorException( "Invalid MD3 file: Magic bytes not found"); - // check file format version + // Check file format version if (pcHeader->VERSION > 15) DefaultLogger::get()->warn( "Unsupported MD3 file version. Continuing happily ..."); - // check some values whether they are valid + // Check some offset values whether they are valid if (!pcHeader->NUM_SURFACES) throw new ImportErrorException( "Invalid md3 file: NUM_SURFACES is 0"); @@ -109,24 +244,28 @@ void MD3Importer::ValidateHeaderOffsets() // ------------------------------------------------------------------------------------------------ void MD3Importer::ValidateSurfaceHeaderOffsets(const MD3::Surface* pcSurf) { - // calculate the relative offset of the surface + // Calculate the relative offset of the surface const int32_t ofs = int32_t((const unsigned char*)pcSurf-this->mBuffer); - if (pcSurf->OFS_TRIANGLES + ofs + pcSurf->NUM_TRIANGLES * sizeof(MD3::Triangle) > fileSize || - pcSurf->OFS_SHADERS + ofs + pcSurf->NUM_SHADER * sizeof(MD3::Shader) > fileSize || - pcSurf->OFS_ST + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::TexCoord) > fileSize || - pcSurf->OFS_XYZNORMAL + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::Vertex) > fileSize) { + // Check whether all data chunks are inside the valid range + if (pcSurf->OFS_TRIANGLES + ofs + pcSurf->NUM_TRIANGLES * sizeof(MD3::Triangle) > fileSize || + pcSurf->OFS_SHADERS + ofs + pcSurf->NUM_SHADER * sizeof(MD3::Shader) > fileSize || + pcSurf->OFS_ST + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::TexCoord) > fileSize || + pcSurf->OFS_XYZNORMAL + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::Vertex) > fileSize) { + throw new ImportErrorException("Invalid MD3 surface header: some offsets are outside the file"); } + // Check whether all requirements for Q3 files are met. We don't + // care, but probably someone does. if (pcSurf->NUM_TRIANGLES > AI_MD3_MAX_TRIANGLES) - DefaultLogger::get()->warn("The model contains more triangles than Quake 3 supports"); + DefaultLogger::get()->warn("MD3: Quake III triangle limit exceeded"); if (pcSurf->NUM_SHADER > AI_MD3_MAX_SHADERS) - DefaultLogger::get()->warn("The model contains more shaders than Quake 3 supports"); + DefaultLogger::get()->warn("MD3: Quake III shader limit exceeded"); if (pcSurf->NUM_VERTICES > AI_MD3_MAX_VERTS) - DefaultLogger::get()->warn("The model contains more vertices than Quake 3 supports"); + DefaultLogger::get()->warn("MD3: Quake III vertex limit exceeded"); if (pcSurf->NUM_FRAMES > AI_MD3_MAX_FRAMES) - DefaultLogger::get()->warn("The model contains more frames than Quake 3 supports"); + DefaultLogger::get()->warn("MD3: Quake III frame limit exceeded"); } // ------------------------------------------------------------------------------------------------ @@ -149,28 +288,53 @@ void MD3Importer::SetupProperties(const Importer* pImp) // AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART configHandleMP = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART,1)); + + // AI_CONFIG_IMPORT_MD3_SKIN_NAME + configSkinFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SKIN_NAME,"default")); +} + +// ------------------------------------------------------------------------------------------------ +// Try to read the skin for a MD3 file +void MD3Importer::ReadSkin(Q3Shader::SkinData& fill) +{ + // skip any postfixes (e.g. lower_1.md3) + std::string::size_type s = filename.find_last_of('_'); + if (s == std::string::npos) { + s = filename.find_last_of('.'); + } + ai_assert(s != std::string::npos); + + const std::string skin_file = path + filename.substr(0,s) + "_" + configSkinFile + ".skin"; + Q3Shader::LoadSkin(fill,skin_file,mIOHandler); } // ------------------------------------------------------------------------------------------------ // Read a multi-part Q3 player model bool MD3Importer::ReadMultipartFile() { - std::string::size_type s = mFile.find_last_of('/'); - if (s == std::string::npos) { - s = mFile.find_last_of('\\'); - } - if (s == std::string::npos) { - s = 0; - } - else ++s; - std::string filename = mFile.substr(s), path = mFile.substr(0,s); - for( std::string::iterator it = filename .begin(); it != filename.end(); ++it) - *it = tolower( *it); + // check whether the file name contains a common postfix, e.g lower_2.md3 + std::string::size_type s = filename.find_last_of('_'), t = filename.find_last_of('.'); + ai_assert(t != std::string::npos); + if (s == std::string::npos) + s = t; - if (filename == "lower.md3" || filename == "upper.md3" || filename == "head.md3"){ - std::string lower = path + "lower.md3"; - std::string upper = path + "upper.md3"; - std::string head = path + "head.md3"; + const std::string mod_filename = filename.substr(0,s); + const std::string suffix = filename.substr(s,t-s); + + if (mod_filename == "lower" || mod_filename == "upper" || mod_filename == "head"){ + const std::string lower = path + "lower" + suffix + ".md3"; + const std::string upper = path + "upper" + suffix + ".md3"; + const std::string head = path + "head" + suffix + ".md3"; + + aiScene* scene_upper = NULL; + aiScene* scene_lower = NULL; + aiScene* scene_head = NULL; + std::string failure; + + aiNode* tag_torso, *tag_head; + std::vector attach; + + DefaultLogger::get()->info("Multi-part MD3 player model: lower, upper and head parts are joined"); // ensure we won't try to load ourselves recursively BatchLoader::PropertyMap props; @@ -189,35 +353,45 @@ bool MD3Importer::ReadMultipartFile() nd->mName.Set(""); // ... and get them. We need all of them. - aiScene* scene_lower = batch.GetImport(lower); - if (!scene_lower) - throw new ImportErrorException("M3D: Failed to read multipart model, lower.md3 fails to load"); + scene_lower = batch.GetImport(lower); + if (!scene_lower) { + DefaultLogger::get()->error("M3D: Failed to read multipart model, lower.md3 fails to load"); + failure = "lower"; + goto error_cleanup; + } - aiScene* scene_upper = batch.GetImport(upper); - if (!scene_upper) - throw new ImportErrorException("M3D: Failed to read multipart model, upper.md3 fails to load"); + scene_upper = batch.GetImport(upper); + if (!scene_upper) { + DefaultLogger::get()->error("M3D: Failed to read multipart model, upper.md3 fails to load"); + failure = "upper"; + goto error_cleanup; + } - aiScene* scene_head = batch.GetImport(head); - if (!scene_head) - throw new ImportErrorException("M3D: Failed to read multipart model, head.md3 fails to load"); + scene_head = batch.GetImport(head); + if (!scene_head) { + DefaultLogger::get()->error("M3D: Failed to read multipart model, head.md3 fails to load"); + failure = "head"; + goto error_cleanup; + } // build attachment infos. search for typical Q3 tags - std::vector attach; // original root attach.push_back(AttachmentInfo(scene_lower, nd)); // tag_torso - aiNode* tag_torso = scene_lower->mRootNode->FindNode("tag_torso"); + tag_torso = scene_lower->mRootNode->FindNode("tag_torso"); if (!tag_torso) { - throw new ImportErrorException("M3D: Unable to find attachment tag: tag_torso expected"); + DefaultLogger::get()->error("M3D: Failed to find attachment tag for multipart model: tag_torso expected"); + goto error_cleanup; } attach.push_back(AttachmentInfo(scene_upper,tag_torso)); // tag_head - aiNode* tag_head = scene_upper->mRootNode->FindNode("tag_head"); + tag_head = scene_upper->mRootNode->FindNode("tag_head"); if (!tag_head) { - throw new ImportErrorException("M3D: Unable to find attachment tag: tag_head expected"); + DefaultLogger::get()->error("M3D: Failed to find attachment tag for multipart model: tag_head expected"); + goto error_cleanup; } attach.push_back(AttachmentInfo(scene_head,tag_head)); @@ -228,6 +402,16 @@ bool MD3Importer::ReadMultipartFile() AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS); return true; + +error_cleanup: + delete scene_upper; + delete scene_lower; + delete scene_head; + delete master; + + if (failure == mod_filename) { + throw new ImportErrorException("MD3: failure to read multipart host file"); + } } return false; } @@ -241,6 +425,20 @@ void MD3Importer::InternReadFile( const std::string& pFile, mScene = pScene; mIOHandler = pIOHandler; + // get base path and file name + // todo ... move to PathConverter + std::string::size_type s = mFile.find_last_of('/'); + if (s == std::string::npos) { + s = mFile.find_last_of('\\'); + } + if (s == std::string::npos) { + s = 0; + } + else ++s; + filename = mFile.substr(s), path = mFile.substr(0,s); + for( std::string::iterator it = filename .begin(); it != filename.end(); ++it) + *it = tolower( *it); + // Load multi-part model file, if necessary if (configHandleMP) { if (ReadMultipartFile()) @@ -253,19 +451,19 @@ void MD3Importer::InternReadFile( const std::string& pFile, if( file.get() == NULL) throw new ImportErrorException( "Failed to open MD3 file " + pFile + "."); - // check whether the md3 file is large enough to contain - // at least the file header + // Check whether the md3 file is large enough to contain the header fileSize = (unsigned int)file->FileSize(); if( fileSize < sizeof(MD3::Header)) throw new ImportErrorException( "MD3 File is too small."); - // allocate storage and copy the contents of the file to a memory buffer + // Allocate storage and copy the contents of the file to a memory buffer std::vector mBuffer2 (fileSize); file->Read( &mBuffer2[0], 1, fileSize); mBuffer = &mBuffer2[0]; pcHeader = (BE_NCONST MD3::Header*)mBuffer; + // Ensure correct endianess #ifdef AI_BUILD_BIG_ENDIAN AI_SWAP4(pcHeader->VERSION); @@ -282,33 +480,38 @@ void MD3Importer::InternReadFile( const std::string& pFile, #endif - // validate the header + // Validate the file header ValidateHeaderOffsets(); - // navigate to the list of surfaces + // Navigate to the list of surfaces BE_NCONST MD3::Surface* pcSurfaces = (BE_NCONST MD3::Surface*)(mBuffer + pcHeader->OFS_SURFACES); - // navigate to the list of tags + // Navigate to the list of tags BE_NCONST MD3::Tag* pcTags = (BE_NCONST MD3::Tag*)(mBuffer + pcHeader->OFS_TAGS); - // allocate output storage + // Allocate output storage pScene->mNumMeshes = pcHeader->NUM_SURFACES; pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; pScene->mNumMaterials = pcHeader->NUM_SURFACES; pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]; - // if an exception is thrown before the meshes are allocated -> - // otherwise the pointer value would be invalid and delete would crash + // Set arrays to zero to ensue proper destruction if an exception is raised ::memset(pScene->mMeshes,0,pScene->mNumMeshes*sizeof(aiMesh*)); ::memset(pScene->mMaterials,0,pScene->mNumMaterials*sizeof(aiMaterial*)); + // Now read possible skins from .skin file + Q3Shader::SkinData skins; + ReadSkin(skins); + + // Read all surfaces from the file unsigned int iNum = pcHeader->NUM_SURFACES; unsigned int iNumMaterials = 0; unsigned int iDefaultMatIndex = 0xFFFFFFFF; while (iNum-- > 0) { + // Ensure correct endianess #ifdef AI_BUILD_BIG_ENDIAN AI_SWAP4(pcSurfaces->FLAGS); @@ -325,26 +528,26 @@ void MD3Importer::InternReadFile( const std::string& pFile, #endif - // validate the surface + // Validate the surface header ValidateSurfaceHeaderOffsets(pcSurfaces); - // navigate to the vertex list of the surface + // Navigate to the vertex list of the surface BE_NCONST MD3::Vertex* pcVertices = (BE_NCONST MD3::Vertex*) (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL); - // navigate to the triangle list of the surface + // Navigate to the triangle list of the surface BE_NCONST MD3::Triangle* pcTriangles = (BE_NCONST MD3::Triangle*) (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_TRIANGLES); - // navigate to the texture coordinate list of the surface + // Navigate to the texture coordinate list of the surface BE_NCONST MD3::TexCoord* pcUVs = (BE_NCONST MD3::TexCoord*) (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_ST); - // navigate to the shader list of the surface + // Navigate to the shader list of the surface BE_NCONST MD3::Shader* pcShaders = (BE_NCONST MD3::Shader*) (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_SHADERS); - // if the submesh is empty ignore it + // If the submesh is empty ignore it if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES) { pcSurfaces = (BE_NCONST MD3::Surface*)(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_END); @@ -352,6 +555,7 @@ void MD3Importer::InternReadFile( const std::string& pFile, continue; } + // Ensure correct endianess #ifdef AI_BUILD_BIG_ENDIAN for (uint32_t i = 0; i < pcSurfaces->NUM_VERTICES;++i) @@ -373,7 +577,7 @@ void MD3Importer::InternReadFile( const std::string& pFile, #endif - // allocate the output mesh + // Allocate the output mesh pScene->mMeshes[iNum] = new aiMesh(); aiMesh* pcMesh = pScene->mMeshes[iNum]; pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; @@ -386,7 +590,7 @@ void MD3Importer::InternReadFile( const std::string& pFile, pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; pcMesh->mNumUVComponents[0] = 2; - // fill in all triangles + // Fill in all triangles unsigned int iCurrent = 0; for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_TRIANGLES;++i) { @@ -396,16 +600,16 @@ void MD3Importer::InternReadFile( const std::string& pFile, unsigned int iTemp = iCurrent; for (unsigned int c = 0; c < 3;++c,++iCurrent) { - // read vertices + // Read vertices pcMesh->mVertices[iCurrent].x = pcVertices[ pcTriangles->INDEXES[c]].X*AI_MD3_XYZ_SCALE; pcMesh->mVertices[iCurrent].y = pcVertices[ pcTriangles->INDEXES[c]].Y*AI_MD3_XYZ_SCALE; pcMesh->mVertices[iCurrent].z = pcVertices[ pcTriangles->INDEXES[c]].Z*AI_MD3_XYZ_SCALE; - // convert the normal vector to uncompressed float3 format + // Convert the normal vector to uncompressed float3 format LatLngNormalToVec3(pcVertices[pcTriangles->INDEXES[c]].NORMAL, (float*)&pcMesh->mNormals[iCurrent]); - // read texture coordinates + // Read texture coordinates pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U; pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V; } @@ -416,111 +620,109 @@ void MD3Importer::InternReadFile( const std::string& pFile, pcTriangles++; } - // get the first shader (= texture?) assigned to the surface - if (pcSurfaces->NUM_SHADER) - { - // make a relative path. - // if the MD3's internal path itself and the given path are using - // the same directory remove it - const char* szEndDir1 = ::strrchr((const char*)pcHeader->NAME,'\\'); - if (!szEndDir1)szEndDir1 = ::strrchr((const char*)pcHeader->NAME,'/'); + std::string _texture_name; + const char* texture_name = NULL, *header_name = pcHeader->NAME; - const char* szEndDir2 = ::strrchr((const char*)pcShaders->NAME,'\\'); - if (!szEndDir2)szEndDir2 = ::strrchr((const char*)pcShaders->NAME,'/'); + // Check whether we have a texture record for this surface in the .skin file + std::list< Q3Shader::SkinData::TextureEntry >::iterator it = std::find( + skins.textures.begin(), skins.textures.end(), pcSurfaces->NAME ); - if (szEndDir1 && szEndDir2) - { - // both of them are valid - const unsigned int iLen1 = (unsigned int)(szEndDir1 - (const char*)pcHeader->NAME); - const unsigned int iLen2 = std::min (iLen1, (unsigned int)(szEndDir2 - (const char*)pcShaders->NAME) ); - - bool bSuccess = true; - for (unsigned int a = 0; a < iLen2;++a) - { - char sz = ::tolower ( pcShaders->NAME[a] ); - char sz2 = ::tolower ( pcHeader->NAME[a] ); - if (sz != sz2) - { - bSuccess = false; - break; - } - } - if (bSuccess) { - // use the file name only - szEndDir2++; - } - else { - // use the full path - szEndDir2 = (const char*)pcShaders->NAME; - } - } - MaterialHelper* pcHelper = new MaterialHelper(); - - if (szEndDir2) { - aiString szString; - if (szEndDir2[0]) { - const size_t iLen = ::strlen(szEndDir2); - ::memcpy(szString.data,szEndDir2,iLen); - szString.data[iLen] = '\0'; - szString.length = iLen; - } - else { - DefaultLogger::get()->warn("Texture file name has zero length. Using default name"); - szString.Set("dummy_texture.bmp"); - } - pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0)); - } - - int iMode = (int)aiShadingMode_Gouraud; - pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); - - // add a small ambient color value - Quake 3 seems to have one - aiColor3D clr; - clr.b = clr.g = clr.r = 0.05f; - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_AMBIENT); - - clr.b = clr.g = clr.r = 1.0f; - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); - - aiString szName; - szName.Set(AI_DEFAULT_MATERIAL_NAME); - pcHelper->AddProperty(&szName,AI_MATKEY_NAME); - - pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper; - pcMesh->mMaterialIndex = iNumMaterials++; + if (it != skins.textures.end()) { + texture_name = &*( _texture_name = (*it).second).begin(); + DefaultLogger::get()->debug("MD3: Assigning skin texture " + (*it).second + " to surface " + pcSurfaces->NAME); + (*it).resolved = true; // mark entry as resolved } - else - { - if (0xFFFFFFFF != iDefaultMatIndex) { - pcMesh->mMaterialIndex = iDefaultMatIndex; - } - else - { - MaterialHelper* pcHelper = new MaterialHelper(); - // fill in a default material - int iMode = (int)aiShadingMode_Gouraud; - pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); + // Get the first shader (= texture?) assigned to the surface + if (!texture_name && pcSurfaces->NUM_SHADER) { + texture_name = pcShaders->NAME; + } - aiColor3D clr; - clr.b = clr.g = clr.r = 0.6f; - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); + const char* end2 = NULL; + if (texture_name) { - clr.b = clr.g = clr.r = 0.05f; - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_AMBIENT); + // If the MD3's internal path itself and the given path are using + // the same directory, remove it completely to get right output paths. + const char* end1 = ::strrchr(header_name,'\\'); + if (!end1)end1 = ::strrchr(header_name,'/'); - pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper; - iDefaultMatIndex = pcMesh->mMaterialIndex = iNumMaterials++; + end2 = ::strrchr(texture_name,'\\'); + if (!end2)end2 = ::strrchr(texture_name,'/'); + + // HACK: If the paths starts with "models/players", ignore the + // next hierarchy level, it specifies just the model name. + // Ignored by Q3, it might be not equal to the real model location. + if (end1 && end2) { + + size_t len2; + const size_t len1 = (size_t)(end1 - header_name); + if (!ASSIMP_strincmp(header_name,"models/players/",15)) { + len2 = 15; + } + else len2 = std::min (len1, (size_t)(end2 - texture_name )); + + if (!ASSIMP_strincmp(texture_name,header_name,len2)) { + // Use the file name only + end2++; + } + else { + // Use the full path + end2 = (const char*)texture_name; + } } } - // go to the next surface + + MaterialHelper* pcHelper = new MaterialHelper(); + + // Setup dummy texture file name to ensure UV coordinates are kept during postprocessing + aiString szString; + if (end2 && end2[0]) { + const size_t iLen = ::strlen(end2); + ::memcpy(szString.data,end2,iLen); + szString.data[iLen] = '\0'; + szString.length = iLen; + } + else { + DefaultLogger::get()->warn("Texture file name has zero length. Using default name"); + szString.Set("dummy_texture.bmp"); + } + pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0)); + + const int iMode = (int)aiShadingMode_Gouraud; + pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + // Add a small ambient color value - Quake 3 seems to have one + aiColor3D clr; + clr.b = clr.g = clr.r = 0.05f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_AMBIENT); + + clr.b = clr.g = clr.r = 1.0f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); + + // use surface name + skin_name as material name + aiString name; + name.Set("MD3_[" + configSkinFile + "][" + pcSurfaces->NAME + "]"); + pcHelper->AddProperty(&name,AI_MATKEY_NAME); + + pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper; + pcMesh->mMaterialIndex = iNumMaterials++; + + // Go to the next surface pcSurfaces = (BE_NCONST MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END); } + // For debugging purposes: check whether we found matches for all entries in the skins file + if (!DefaultLogger::isNullLogger()) { + for (std::list< Q3Shader::SkinData::TextureEntry>::const_iterator it = skins.textures.begin();it != skins.textures.end(); ++it) { + if (!(*it).resolved) { + DefaultLogger::get()->error("MD3: Failed to match skin " + (*it).first + " to surface " + (*it).second); + } + } + } + if (!pScene->mNumMeshes) - throw new ImportErrorException( "Invalid MD3 file: File contains no valid mesh"); + throw new ImportErrorException( "MD3: File contains no valid mesh"); pScene->mNumMaterials = iNumMaterials; // Now we need to generate an empty node graph @@ -543,12 +745,12 @@ void MD3Importer::InternReadFile( const std::string& pFile, AI_SWAP4(pcTags->origin.y); AI_SWAP4(pcTags->origin.z); - // copy local origin + // Copy local origin nd->mTransformation.a4 = pcTags->origin.x; nd->mTransformation.b4 = pcTags->origin.y; nd->mTransformation.c4 = pcTags->origin.z; - // copy rest of transformation + // Copy rest of transformation (need to transpose to match row-order matrix) for (unsigned int a = 0; a < 3;++a) { for (unsigned int m = 0; m < 3;++m) { nd->mTransformation[m][a] = pcTags->orientation[a][m]; diff --git a/code/MD3Loader.h b/code/MD3Loader.h index 35653dac0..3aaca52e8 100644 --- a/code/MD3Loader.h +++ b/code/MD3Loader.h @@ -38,7 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** @file Definition of the .MD3 importer class. */ +/** @file Md3Loader.h + * @brief Declaration of the .MD3 importer class. + */ #ifndef AI_MD3LOADER_H_INCLUDED #define AI_MD3LOADER_H_INCLUDED @@ -47,16 +49,142 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../include/aiTypes.h" -struct aiNode; - #include "MD3FileData.h" namespace Assimp { class MaterialHelper; using namespace MD3; +namespace Q3Shader { // --------------------------------------------------------------------------- -/** Used to load MD3 files +/** @brief Tiny utility data structure to hold the data of a .skin file + */ +struct SkinData +{ + //! A single entryin texture list + struct TextureEntry : public std::pair + { + // did we resolve this texture entry? + bool resolved; + + // for std::find() + bool operator == (const std::string& f) const { + return f == first; + } + }; + + //! List of textures + std::list textures; + + // rest is ignored for the moment +}; + +// --------------------------------------------------------------------------- +/** @brief Specifies cull modi for Quake shader files. + */ +enum ShaderCullMode +{ + CULL_NONE, + CULL_CW, + CULL_CCW, +}; + +// --------------------------------------------------------------------------- +/** @brief Specifies alpha blend modi (src + dest) for Quake shader files + */ +enum BlendFunc +{ + BLEND_NONE, + BLEND_GL_ONE, + BLEND_GL_ZERO, + BLEND_GL_DST_COLOR, + BLEND_GL_ONE_MINUS_DST_COLOR, + BLEND_GL_SRC_ALPHA, + BLEND_GL_ONE_MINUS_SRC_ALPHA +}; + +// --------------------------------------------------------------------------- +/** @brief Specifies alpha test modi for Quake texture maps + */ +enum AlphaTestFunc +{ + AT_NONE, + AT_GT0, + AT_LT128, + AT_GE128 +}; + +// --------------------------------------------------------------------------- +/** @brief Tiny utility data structure to hold a .shader map data block + */ +struct ShaderMapBlock +{ + ShaderMapBlock() + : blend_src (BLEND_NONE) + , blend_dest (BLEND_NONE) + , alpha_test (AT_NONE) + {} + + //! Name of referenced map + std::string name; + + //! Blend and alpha test settings for texture + BlendFunc blend_src,blend_dest; + AlphaTestFunc alpha_test; +}; + +// --------------------------------------------------------------------------- +/** @brief Tiny utility data structure to hold a .shader data block + */ +struct ShaderDataBlock +{ + ShaderDataBlock() + : cull (CULL_CCW) + {} + + //! Name of referenced data element + std::string name; + + //! Cull mode for the element + ShaderCullMode cull; + + //! Maps defined in the shader + std::list maps; +}; + +// --------------------------------------------------------------------------- +/** @brief Tiny utility data structure to hold the data of a .shader file + */ +struct ShaderData +{ + //! Shader data blocks + std::list blocks; +}; + +// --------------------------------------------------------------------------- +/** @brief Load a shader file + * + * Generally, parsing is error tolerant. There's no failure. + * @param fill Receives output data + * @param file File to be read. + * @param io IOSystem to be used for reading + */ +void LoadShader(ShaderData& fill, const std::string& file,IOSystem* io); + +// --------------------------------------------------------------------------- +/** @brief Load a skin file + * + * Generally, parsing is error tolerant. There's no failure. + * @param fill Receives output data + * @param file File to be read. + * @param io IOSystem to be used for reading + */ +void LoadSkin(SkinData& fill, const std::string& file,IOSystem* io); + +} // ! namespace Q3SHader + +// --------------------------------------------------------------------------- +/** @brief Importer class to load MD3 files */ class MD3Importer : public BaseImporter { @@ -111,6 +239,12 @@ protected: */ bool ReadMultipartFile(); + // ------------------------------------------------------------------- + /** Try to read the skin for a MD3 file + * @param fill Receives output information + */ + void ReadSkin(Q3Shader::SkinData& fill); + protected: /** Configuration option: frame to be loaded */ @@ -119,6 +253,9 @@ protected: /** Configuration option: process multi-part files */ bool configHandleMP; + /** Configuration option: name of skin file to be read */ + std::string configSkinFile; + /** Header of the MD3 file */ BE_NCONST MD3::Header* pcHeader; @@ -131,6 +268,12 @@ protected: /** Current file name */ std::string mFile; + /** Current base directory */ + std::string path; + + /** Pure file we're currently reading */ + std::string filename; + /** Output scene to be filled */ aiScene* mScene; diff --git a/code/MD5Parser.h b/code/MD5Parser.h index 8383c5d5e..41156d7e5 100644 --- a/code/MD5Parser.h +++ b/code/MD5Parser.h @@ -370,7 +370,7 @@ private: inline bool SkipLine( const char* in, const char** out) { ++lineNumber; - return ::SkipLine(in,out); + return Assimp::SkipLine(in,out); } // ------------------------------------------------------------------- inline bool SkipLine( ) @@ -407,7 +407,7 @@ private: // ------------------------------------------------------------------- inline bool SkipSpaces( ) { - return ::SkipSpaces((const char**)&buffer); + return Assimp::SkipSpaces((const char**)&buffer); } char* buffer; diff --git a/code/ParsingUtils.h b/code/ParsingUtils.h index b06438694..87653e2a6 100644 --- a/code/ParsingUtils.h +++ b/code/ParsingUtils.h @@ -39,10 +39,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/** @file Defines helper functions for text parsing */ +/** @file ParsingUtils.h + * @brief Defines helper functions for text parsing + */ #ifndef AI_PARSING_UTILS_H_INC #define AI_PARSING_UTILS_H_INC +#include "StringComparison.h" +namespace Assimp { + // --------------------------------------------------------------------------------- template AI_FORCE_INLINE bool IsSpace( const char_t in) @@ -129,16 +134,6 @@ AI_FORCE_INLINE bool IsNumeric( char_t in) return in >= '0' && in <= '9' || '-' == in || '+' == in; } // --------------------------------------------------------------------------------- -AI_FORCE_INLINE bool TokenMatch(const char*& in, const char* token, unsigned int len) -{ - if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) - { - in += len+1; - return true; - } - return false; -} -// --------------------------------------------------------------------------------- AI_FORCE_INLINE bool TokenMatch(char*& in, const char* token, unsigned int len) { if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) @@ -149,9 +144,38 @@ AI_FORCE_INLINE bool TokenMatch(char*& in, const char* token, unsigned int len) return false; } // --------------------------------------------------------------------------------- +/** @brief Case-ignoring version of TokenMatch + * @param in Input + * @param token Token to check for + * @param len Number of characters to check + */ +AI_FORCE_INLINE bool TokenMatchI(const char*& in, const char* token, unsigned int len) +{ + if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len])) + { + in += len+1; + return true; + } + return false; +} +// --------------------------------------------------------------------------------- +AI_FORCE_INLINE bool TokenMatch(const char*& in, const char* token, unsigned int len) +{ + return TokenMatch(const_cast(in), token, len); +} +// --------------------------------------------------------------------------------- AI_FORCE_INLINE void SkipToken(const char*& in) { SkipSpaces(&in); while (!IsSpaceOrNewLine(*in))++in; } +// --------------------------------------------------------------------------------- +AI_FORCE_INLINE std::string GetNextToken(const char*& in) +{ + SkipSpacesAndLineEnd(&in); + const char* cur = in; + while (!IsSpaceOrNewLine(*in))++in; + return std::string(cur,(size_t)(in-cur)); +} +} // ! namespace Assimp #endif // ! AI_PARSING_UTILS_H_INC diff --git a/code/SMDLoader.h b/code/SMDLoader.h index b6b81ba65..b5e9f12bb 100644 --- a/code/SMDLoader.h +++ b/code/SMDLoader.h @@ -360,7 +360,7 @@ protected: // ------------------------------------------------------------------- inline bool SkipLine( const char* in, const char** out) { - ::SkipLine(in,out); + Assimp::SkipLine(in,out); ++iLineNumber; return true; } @@ -368,7 +368,7 @@ protected: inline bool SkipSpacesAndLineEnd( const char* in, const char** out) { ++iLineNumber; - return ::SkipSpacesAndLineEnd(in,out); + return Assimp::SkipSpacesAndLineEnd(in,out); } private: diff --git a/include/aiConfig.h b/include/aiConfig.h index d074bcc62..e4b8ebb38 100644 --- a/include/aiConfig.h +++ b/include/aiConfig.h @@ -144,7 +144,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * combine all three files if one of them is loaded. * Property type: integer (0: false; !0: true). Default value: true. */ -#define AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART "IMPORT_MD3_HANDLE_MULTIPART" +#define AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART \ + "IMPORT_MD3_HANDLE_MULTIPART" + +// --------------------------------------------------------------------------- +/** @brief Tells the MD3 loader which skin files to load. + * + * When loading MD3 files, Assimp checks whether a file + * _.skin is existing. These files are used by + * Quake III to be able to assign different skins (e.g. red and blue team) + * to models. 'default', 'red', 'blue' are typical skin names. + * Property type: String. Default value: "default". + */ +#define AI_CONFIG_IMPORT_MD3_SKIN_NAME \ + "IMPORT_MD3_SKIN_NAME" // --------------------------------------------------------------------------- diff --git a/include/aiMaterial.h b/include/aiMaterial.h index 0baa69db4..8b53df139 100644 --- a/include/aiMaterial.h +++ b/include/aiMaterial.h @@ -441,6 +441,54 @@ enum aiTextureFlags //! @endcond }; + +// --------------------------------------------------------------------------- +/** @brief Defines alpha-blend flags. + * + * If you're familiar with OpenGL or D3D, these flags aren't new to you. + * The define *how* the final color value of a pixel is computed, basing + * on the previous color at that pixel and the new color value from the + * material. + * The blend formula is: + * @code + * SourceColor * SourceBlend + DestColor * DestBlend + * @endcode + * where is the previous color in the framebuffer at this + * position and is the material colro before the transparency + * calculation.
+ * This corresponds to the #AI_MATKEY_BLEND_FUNC property. +*/ +enum aiBlendMode +{ + /** + * Formula: + * @code + * SourceColor*SourceAlpha + DestColor*(1-SourceAlpha) + * @endcode + */ + aiBlendMode_Default = 0x0, + + /** Additive blending + * + * Formula: + * @code + * SourceColor*1 + DestColor*1 + * @endcode + */ + aiBlendMode_Additive = 0x1, + + // we don't need more for the moment, but we might need them + // in future versions ... + + /** @cond never + * This value is not used. It forces the compiler to use at least + * 32 Bit integers to represent this enum. + */ + _aiBlendMode_Force32Bit = 0x9fffffff + //! @endcond +}; + + #include "./Compiler/pushpack1.h" // --------------------------------------------------------------------------- @@ -688,18 +736,33 @@ extern "C" { * Integer property. 1 to enable wireframe mode for rendering. * A material with this property set to 1 should appear as wireframe, even * if the scene is rendered solid. - *
- * Type: int
- * Default value: 0 + *
+ * Type: int
+ * Default value: 0 */ #define AI_MATKEY_ENABLE_WIREFRAME "$mat.wireframe",0,0 +// --------------------------------------------------------------------------- +/** @def AI_MATKEY_BLEND_FUNC + * Integer property (one of the #aiBlendMode enumerated values). Defines + * the blend function to be used to combine the material color for a specific + * pixel with the previous framebuffer color at this position. This + * corresponds to the #AI_MATKEY_OPACITY and #AI_MATKEY_COLOR_TRANSPARENT + * property. No alpha-blending needs to be turned on if the opacity for all + * color channels is 1.0 and the blend mode is set to #aiBlendMode_Default. + *
+ * Type: int (#aiBlendMode)
+ * Default value: #aiBlendMode_Default +*/ +#define AI_MATKEY_BLEND_FUNC "$mat.blend",0,0 + // --------------------------------------------------------------------------- /** @def AI_MATKEY_OPACITY - * Defines the base opacity of the material. This value defines how - * transparent the material actually is. However, in order to get absolutely - * correct results you'll also need to evaluate the - * #AI_MATKEY_COLOR_TRANSPARENT property. + * Defines the base opacity of the material. To get the opacity value for + * a particular channel, this value is multiplied with the corresponding + * channel of the #AI_MATKEY_COLOR_TRANSPARENT property. The final value + * is fed in the blend function defined by the #AI_MATKEY_BLEND_FUNC + * property. *
* Type: float
* Default value: 1.0f
@@ -755,7 +818,8 @@ extern "C" { // --------------------------------------------------------------------------- /** @def AI_MATKEY_COLOR_DIFFUSE - * Defines the diffuse base color of the material.
+ * Defines the diffuse base color of the material. + * If stored as 4-component color, the alpha channel is ignored.
* Type: color (#aiColor4D or #aiColor3D)
* Default value: 0.0f|0.0f|0.0f|1.0f */ @@ -763,39 +827,46 @@ extern "C" { /** @def AI_MATKEY_COLOR_AMBIENT * Declares the amount of ambient light emitted from - * the surface of this object.
- * Type: color (#aiColor4D or #aiColor3D)
- * Default value: 0.0f|0.0f|0.0f|1.0f + * the surface of this object. If stored as 4-component color, + * the alpha channel is ignored.

+ * Type: color (#aiColor4D or #aiColor3D)
+ * Default value: 0.0f|0.0f|0.0f|1.0f */ #define AI_MATKEY_COLOR_AMBIENT "$clr.ambient",0,0 /** @def AI_MATKEY_COLOR_SPECULAR * Declares the color of light specularly reflected from - * the surface of this object.
- * Type: color (#aiColor4D or #aiColor3D)
- * Default value: 0.0f|0.0f|0.0f|1.0f + * the surface of this object. If stored as 4-component color, the + * alpha channel is ignored.

+ * Type: color (#aiColor4D or #aiColor3D)
+ * Default value: 0.0f|0.0f|0.0f|1.0f */ #define AI_MATKEY_COLOR_SPECULAR "$clr.specular",0,0 /** @def AI_MATKEY_COLOR_EMISSIVE * Declares the amount of light emitted from the - * surface of this object.
- * Type: color (#aiColor4D or #aiColor3D)
- * Default value: 0.0f|0.0f|0.0f|1.0f + * surface of this object. If stored as 4-component color, the alpha + * channel is ignored.

+ * Type: color (#aiColor4D or #aiColor3D)
+ * Default value: 0.0f|0.0f|0.0f|1.0f */ #define AI_MATKEY_COLOR_EMISSIVE "$clr.emissive",0,0 /** @def AI_MATKEY_COLOR_TRANSPARENT - * Defines the transparent base color of the material.
- * Type: color (#aiColor4D or #aiColor3D)
- * Default value: 0.0f|0.0f|0.0f|1.0f + * Defines the transparent base color of the material. If stored as + * 4-component color, the alpha channel is ignored. This + * corresponds to the #AI_MATKEY_OPACITY and #AI_MATKEY_BLEND_FUNC + * material properties.

+ * Type: color (#aiColor4D or #aiColor3D)
+ * Default value: 0.0f|0.0f|0.0f|1.0f */ #define AI_MATKEY_COLOR_TRANSPARENT "$clr.transparent",0,0 /** @def AI_MATKEY_COLOR_REFLECTIVE - * Declares the color of a perfect mirror reflection.
- * Type: color (#aiColor4D or #aiColor3D)
- * Default value: 0.0f|0.0f|0.0f|1.0f + * Declares the color of a perfect mirror reflection. If stored as + * 4-component color, the alpha channel is ignored.

+ * Type: color (#aiColor4D or #aiColor3D)
+ * Default value: 0.0f|0.0f|0.0f|1.0f */ #define AI_MATKEY_COLOR_REFLECTIVE "$clr.reflective",0,0