- 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
pull/1/head
aramis_acg 2009-02-13 22:03:57 +00:00
parent d70c092b71
commit 2b9461fbf7
10 changed files with 729 additions and 266 deletions

View File

@ -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. 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" #include "AssimpPCH.h"
@ -80,11 +82,12 @@ size_t DefaultIOStream::Write(const void* pvBuffer,
aiReturn DefaultIOStream::Seek(size_t pOffset, aiReturn DefaultIOStream::Seek(size_t pOffset,
aiOrigin pOrigin) 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 // Just to check whether our enum maps one to one with the CRT constants
ai_assert(aiOrigin_CUR == SEEK_CUR && aiOrigin_END == SEEK_END BOOST_STATIC_ASSERT(aiOrigin_CUR == SEEK_CUR &&
&& aiOrigin_SET == SEEK_SET); aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET);
// do the seek // do the seek
return (0 == ::fseek(mFile, (long)pOffset,(int)pOrigin) ? AI_SUCCESS : AI_FAILURE); 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 size_t DefaultIOStream::FileSize() const
{ {
ai_assert (!mFilename.empty()); if (! mFile || mFilename.empty())
if (! mFile)
return 0; return 0;
// TODO: Is that really faster if we have already opened the file? if (0xffffffff == cachedSize) {
// TODO: Is that really faster if we're already owning a handle to the file?
#if defined _WIN32 && !defined __GNUC__ #if defined _WIN32 && !defined __GNUC__
struct __stat64 fileStat; struct __stat64 fileStat;
int err = _stat64( mFilename.c_str(), &fileStat ); int err = _stat64( mFilename.c_str(), &fileStat );
if (0 != err) if (0 != err)
return 0; return 0;
return (size_t) (fileStat.st_size); cachedSize = (size_t) (fileStat.st_size);
#else #else
struct stat fileStat; struct stat fileStat;
int err = stat(mFilename.c_str(), &fileStat ); int err = stat(mFilename.c_str(), &fileStat );
if (0 != err) if (0 != err)
return 0; return 0;
return (size_t) (fileStat.st_size); cachedSize = (size_t) (fileStat.st_size);
#endif #endif
}
return cachedSize;
} }
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------

View File

@ -50,6 +50,9 @@ namespace Assimp {
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
//! @class DefaultIOStream //! @class DefaultIOStream
//! @brief Default IO implementation, use standard IO operations //! @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 class DefaultIOStream : public IOStream
{ {
friend class DefaultIOSystem; friend class DefaultIOSystem;
@ -63,28 +66,33 @@ public:
~DefaultIOStream (); ~DefaultIOStream ();
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Read from stream
size_t Read(void* pvBuffer, size_t Read(void* pvBuffer,
size_t pSize, size_t pSize,
size_t pCount); size_t pCount);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Write to stream
size_t Write(const void* pvBuffer, size_t Write(const void* pvBuffer,
size_t pSize, size_t pSize,
size_t pCount); size_t pCount);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Seek specific position
aiReturn Seek(size_t pOffset, aiReturn Seek(size_t pOffset,
aiOrigin pOrigin); aiOrigin pOrigin);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Get current seek position
size_t Tell() const; size_t Tell() const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Get size of file
size_t FileSize() const; size_t FileSize() const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Flush file contents
void Flush(); void Flush();
private: private:
@ -92,13 +100,17 @@ private:
FILE* mFile; FILE* mFile;
//! Filename //! Filename
std::string mFilename; std::string mFilename;
//! Cached file size
mutable size_t cachedSize;
}; };
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
inline DefaultIOStream::DefaultIOStream () : inline DefaultIOStream::DefaultIOStream () :
mFile(NULL), mFile (NULL),
mFilename("") mFilename (""),
cachedSize (0xffffffff)
{ {
// empty // empty
} }
@ -108,7 +120,8 @@ inline DefaultIOStream::DefaultIOStream () :
inline DefaultIOStream::DefaultIOStream (FILE* pFile, inline DefaultIOStream::DefaultIOStream (FILE* pFile,
const std::string &strFilename) : const std::string &strFilename) :
mFile(pFile), mFile(pFile),
mFilename(strFilename) mFilename(strFilename),
cachedSize (0xffffffff)
{ {
// empty // empty
} }

View File

@ -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. /** @file Md3FileData.h
http://linux.ucla.edu/~phaethon/q3/formats/md3format.html *
*/ * @brief Defines helper data structures for importing MD3 files.
* http://linux.ucla.edu/~phaethon/q3/formats/md3format.html
*/
#ifndef AI_MD3FILEHELPER_H_INC #ifndef AI_MD3FILEHELPER_H_INC
#define 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 // master scale factor for all vertices in a MD3 model
#define AI_MD3_XYZ_SCALE (1.0f/64.0f) #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 struct Header
{ {
//! magic number //! magic number
@ -90,7 +91,7 @@ struct Header
uint32_t VERSION; uint32_t VERSION;
//! original name in .pak archive //! original name in .pak archive
unsigned char NAME[ AI_MD3_MAXQPATH ]; char NAME[ AI_MD3_MAXQPATH ];
//! unknown //! unknown
int32_t FLAGS; int32_t FLAGS;
@ -121,10 +122,9 @@ struct Header
} PACK_STRUCT; } PACK_STRUCT;
// --------------------------------------------------------------------------- // -------------------------------------------------------------------------------
/** \brief Data structure for the frame header /** @brief Data structure for the frame header
*/ */
// ---------------------------------------------------------------------------
struct Frame struct Frame
{ {
//! minimum bounds //! minimum bounds
@ -145,14 +145,13 @@ struct Frame
} PACK_STRUCT; } PACK_STRUCT;
// --------------------------------------------------------------------------- // -------------------------------------------------------------------------------
/** \brief Data structure for the tag header /** @brief Data structure for the tag header
*/ */
// ---------------------------------------------------------------------------
struct Tag struct Tag
{ {
//! name of the tag //! name of the tag
unsigned char NAME[ AI_MD3_MAXQPATH ]; char NAME[ AI_MD3_MAXQPATH ];
//! Local tag origin and orientation //! Local tag origin and orientation
aiVector3D origin; aiVector3D origin;
@ -161,17 +160,16 @@ struct Tag
} PACK_STRUCT; } PACK_STRUCT;
// --------------------------------------------------------------------------- // -------------------------------------------------------------------------------
/** \brief Data structure for the surface header /** @brief Data structure for the surface header
*/ */
// ---------------------------------------------------------------------------
struct Surface struct Surface
{ {
//! magic number //! magic number
int32_t IDENT; int32_t IDENT;
//! original name of the surface //! original name of the surface
unsigned char NAME[ AI_MD3_MAXQPATH ]; char NAME[ AI_MD3_MAXQPATH ];
//! unknown //! unknown
int32_t FLAGS; int32_t FLAGS;
@ -205,24 +203,22 @@ struct Surface
int32_t OFS_END; int32_t OFS_END;
} PACK_STRUCT; } PACK_STRUCT;
// --------------------------------------------------------------------------- // -------------------------------------------------------------------------------
/** \brief Data structure for a shader /** @brief Data structure for a shader defined in there
*/ */
// ---------------------------------------------------------------------------
struct Shader struct Shader
{ {
//! filename of the shader //! filename of the shader
unsigned char NAME[ AI_MD3_MAXQPATH ]; char NAME[ AI_MD3_MAXQPATH ];
//! index of the shader //! index of the shader
uint32_t SHADER_INDEX; uint32_t SHADER_INDEX;
} PACK_STRUCT; } PACK_STRUCT;
// --------------------------------------------------------------------------- // -------------------------------------------------------------------------------
/** \brief Data structure for a triangle /** @brief Data structure for a triangle
*/ */
// ---------------------------------------------------------------------------
struct Triangle struct Triangle
{ {
//! triangle indices //! triangle indices
@ -230,10 +226,9 @@ struct Triangle
} PACK_STRUCT; } PACK_STRUCT;
// --------------------------------------------------------------------------- // -------------------------------------------------------------------------------
/** \brief Data structure for an UV coord /** @brief Data structure for an UV coord
*/ */
// ---------------------------------------------------------------------------
struct TexCoord struct TexCoord
{ {
//! UV coordinates //! UV coordinates
@ -241,10 +236,9 @@ struct TexCoord
} PACK_STRUCT; } PACK_STRUCT;
// --------------------------------------------------------------------------- // -------------------------------------------------------------------------------
/** \brief Data structure for a vertex /** @brief Data structure for a vertex
*/ */
// ---------------------------------------------------------------------------
struct Vertex struct Vertex
{ {
//! X/Y/Z coordinates //! X/Y/Z coordinates
@ -256,15 +250,14 @@ struct Vertex
#include "./../include/Compiler/poppack1.h" #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_iNormal Input normal vector in latitude/longitude form
* \param p_afOut Pointer to an array of three floats to receive the result * @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) inline void LatLngNormalToVec3(uint16_t p_iNormal, float* p_afOut)
{ {
float lat = (float)(( p_iNormal >> 8u ) & 0xff); 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 /** @brief Pack a Q3 normal into 16bit latitute/longitude representation
* \param p_vIn Input vector * @param p_vIn Input vector
* \param p_iOut Output normal * @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 ) inline void Vec3NormalToLatLng( const aiVector3D& p_vIn, uint16_t& p_iOut )
{ {
// check for singularities // check for singularities

View File

@ -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" #include "AssimpPCH.h"
#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER #ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
#include "MD3Loader.h" #include "MD3Loader.h"
#include "MaterialSystem.h"
#include "StringComparison.h"
#include "ByteSwap.h" #include "ByteSwap.h"
#include "SceneCombiner.h" #include "SceneCombiner.h"
#include "GenericProperty.h" #include "GenericProperty.h"
#include "RemoveComments.h"
#include "ParsingUtils.h"
using namespace Assimp; using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Load a Quake 3 shader
void Q3Shader::LoadShader(ShaderData& fill, const std::string& pFile,IOSystem* io)
{
boost::scoped_ptr<IOStream> 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<char> _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<IOStream> 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<char> _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 // Constructor to be privately used by Importer
MD3Importer::MD3Importer() MD3Importer::MD3Importer()
@ -84,16 +219,16 @@ bool MD3Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void MD3Importer::ValidateHeaderOffsets() void MD3Importer::ValidateHeaderOffsets()
{ {
// check magic number // Check magic number
if (pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE && if (pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE &&
pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE) pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE)
throw new ImportErrorException( "Invalid MD3 file: Magic bytes not found"); throw new ImportErrorException( "Invalid MD3 file: Magic bytes not found");
// check file format version // Check file format version
if (pcHeader->VERSION > 15) if (pcHeader->VERSION > 15)
DefaultLogger::get()->warn( "Unsupported MD3 file version. Continuing happily ..."); 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) if (!pcHeader->NUM_SURFACES)
throw new ImportErrorException( "Invalid md3 file: NUM_SURFACES is 0"); throw new ImportErrorException( "Invalid md3 file: NUM_SURFACES is 0");
@ -109,24 +244,28 @@ void MD3Importer::ValidateHeaderOffsets()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void MD3Importer::ValidateSurfaceHeaderOffsets(const MD3::Surface* pcSurf) 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); const int32_t ofs = int32_t((const unsigned char*)pcSurf-this->mBuffer);
// Check whether all data chunks are inside the valid range
if (pcSurf->OFS_TRIANGLES + ofs + pcSurf->NUM_TRIANGLES * sizeof(MD3::Triangle) > fileSize || 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_SHADERS + ofs + pcSurf->NUM_SHADER * sizeof(MD3::Shader) > fileSize ||
pcSurf->OFS_ST + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::TexCoord) > fileSize || pcSurf->OFS_ST + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::TexCoord) > fileSize ||
pcSurf->OFS_XYZNORMAL + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::Vertex) > 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"); 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) 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) 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) 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) 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 // AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART
configHandleMP = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART,1)); 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 // Read a multi-part Q3 player model
bool MD3Importer::ReadMultipartFile() bool MD3Importer::ReadMultipartFile()
{ {
std::string::size_type s = mFile.find_last_of('/'); // check whether the file name contains a common postfix, e.g lower_2.md3
if (s == std::string::npos) { std::string::size_type s = filename.find_last_of('_'), t = filename.find_last_of('.');
s = mFile.find_last_of('\\'); ai_assert(t != std::string::npos);
} if (s == std::string::npos)
if (s == std::string::npos) { s = t;
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);
if (filename == "lower.md3" || filename == "upper.md3" || filename == "head.md3"){ const std::string mod_filename = filename.substr(0,s);
std::string lower = path + "lower.md3"; const std::string suffix = filename.substr(s,t-s);
std::string upper = path + "upper.md3";
std::string head = path + "head.md3"; 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<AttachmentInfo> 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 // ensure we won't try to load ourselves recursively
BatchLoader::PropertyMap props; BatchLoader::PropertyMap props;
@ -189,35 +353,45 @@ bool MD3Importer::ReadMultipartFile()
nd->mName.Set("<M3D_Player>"); nd->mName.Set("<M3D_Player>");
// ... and get them. We need all of them. // ... and get them. We need all of them.
aiScene* scene_lower = batch.GetImport(lower); scene_lower = batch.GetImport(lower);
if (!scene_lower) if (!scene_lower) {
throw new ImportErrorException("M3D: Failed to read multipart model, lower.md3 fails to load"); 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); scene_upper = batch.GetImport(upper);
if (!scene_upper) if (!scene_upper) {
throw new ImportErrorException("M3D: Failed to read multipart model, upper.md3 fails to load"); 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); scene_head = batch.GetImport(head);
if (!scene_head) if (!scene_head) {
throw new ImportErrorException("M3D: Failed to read multipart model, head.md3 fails to load"); 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 // build attachment infos. search for typical Q3 tags
std::vector<AttachmentInfo> attach;
// original root // original root
attach.push_back(AttachmentInfo(scene_lower, nd)); attach.push_back(AttachmentInfo(scene_lower, nd));
// tag_torso // tag_torso
aiNode* tag_torso = scene_lower->mRootNode->FindNode("tag_torso"); tag_torso = scene_lower->mRootNode->FindNode("tag_torso");
if (!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)); attach.push_back(AttachmentInfo(scene_upper,tag_torso));
// tag_head // tag_head
aiNode* tag_head = scene_upper->mRootNode->FindNode("tag_head"); tag_head = scene_upper->mRootNode->FindNode("tag_head");
if (!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)); attach.push_back(AttachmentInfo(scene_head,tag_head));
@ -228,6 +402,16 @@ bool MD3Importer::ReadMultipartFile()
AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS); AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS);
return true; 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; return false;
} }
@ -241,6 +425,20 @@ void MD3Importer::InternReadFile( const std::string& pFile,
mScene = pScene; mScene = pScene;
mIOHandler = pIOHandler; 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 // Load multi-part model file, if necessary
if (configHandleMP) { if (configHandleMP) {
if (ReadMultipartFile()) if (ReadMultipartFile())
@ -253,19 +451,19 @@ void MD3Importer::InternReadFile( const std::string& pFile,
if( file.get() == NULL) if( file.get() == NULL)
throw new ImportErrorException( "Failed to open MD3 file " + pFile + "."); throw new ImportErrorException( "Failed to open MD3 file " + pFile + ".");
// check whether the md3 file is large enough to contain // Check whether the md3 file is large enough to contain the header
// at least the file header
fileSize = (unsigned int)file->FileSize(); fileSize = (unsigned int)file->FileSize();
if( fileSize < sizeof(MD3::Header)) if( fileSize < sizeof(MD3::Header))
throw new ImportErrorException( "MD3 File is too small."); 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<unsigned char> mBuffer2 (fileSize); std::vector<unsigned char> mBuffer2 (fileSize);
file->Read( &mBuffer2[0], 1, fileSize); file->Read( &mBuffer2[0], 1, fileSize);
mBuffer = &mBuffer2[0]; mBuffer = &mBuffer2[0];
pcHeader = (BE_NCONST MD3::Header*)mBuffer; pcHeader = (BE_NCONST MD3::Header*)mBuffer;
// Ensure correct endianess
#ifdef AI_BUILD_BIG_ENDIAN #ifdef AI_BUILD_BIG_ENDIAN
AI_SWAP4(pcHeader->VERSION); AI_SWAP4(pcHeader->VERSION);
@ -282,33 +480,38 @@ void MD3Importer::InternReadFile( const std::string& pFile,
#endif #endif
// validate the header // Validate the file header
ValidateHeaderOffsets(); 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); 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); 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->mNumMeshes = pcHeader->NUM_SURFACES;
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
pScene->mNumMaterials = pcHeader->NUM_SURFACES; pScene->mNumMaterials = pcHeader->NUM_SURFACES;
pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]; pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
// if an exception is thrown before the meshes are allocated -> // Set arrays to zero to ensue proper destruction if an exception is raised
// otherwise the pointer value would be invalid and delete would crash
::memset(pScene->mMeshes,0,pScene->mNumMeshes*sizeof(aiMesh*)); ::memset(pScene->mMeshes,0,pScene->mNumMeshes*sizeof(aiMesh*));
::memset(pScene->mMaterials,0,pScene->mNumMaterials*sizeof(aiMaterial*)); ::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 iNum = pcHeader->NUM_SURFACES;
unsigned int iNumMaterials = 0; unsigned int iNumMaterials = 0;
unsigned int iDefaultMatIndex = 0xFFFFFFFF; unsigned int iDefaultMatIndex = 0xFFFFFFFF;
while (iNum-- > 0) while (iNum-- > 0)
{ {
// Ensure correct endianess
#ifdef AI_BUILD_BIG_ENDIAN #ifdef AI_BUILD_BIG_ENDIAN
AI_SWAP4(pcSurfaces->FLAGS); AI_SWAP4(pcSurfaces->FLAGS);
@ -325,26 +528,26 @@ void MD3Importer::InternReadFile( const std::string& pFile,
#endif #endif
// validate the surface // Validate the surface header
ValidateSurfaceHeaderOffsets(pcSurfaces); 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*) BE_NCONST MD3::Vertex* pcVertices = (BE_NCONST MD3::Vertex*)
(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL); (((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*) BE_NCONST MD3::Triangle* pcTriangles = (BE_NCONST MD3::Triangle*)
(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_TRIANGLES); (((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*) BE_NCONST MD3::TexCoord* pcUVs = (BE_NCONST MD3::TexCoord*)
(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_ST); (((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*) BE_NCONST MD3::Shader* pcShaders = (BE_NCONST MD3::Shader*)
(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_SHADERS); (((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) if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES)
{ {
pcSurfaces = (BE_NCONST MD3::Surface*)(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_END); pcSurfaces = (BE_NCONST MD3::Surface*)(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_END);
@ -352,6 +555,7 @@ void MD3Importer::InternReadFile( const std::string& pFile,
continue; continue;
} }
// Ensure correct endianess
#ifdef AI_BUILD_BIG_ENDIAN #ifdef AI_BUILD_BIG_ENDIAN
for (uint32_t i = 0; i < pcSurfaces->NUM_VERTICES;++i) for (uint32_t i = 0; i < pcSurfaces->NUM_VERTICES;++i)
@ -373,7 +577,7 @@ void MD3Importer::InternReadFile( const std::string& pFile,
#endif #endif
// allocate the output mesh // Allocate the output mesh
pScene->mMeshes[iNum] = new aiMesh(); pScene->mMeshes[iNum] = new aiMesh();
aiMesh* pcMesh = pScene->mMeshes[iNum]; aiMesh* pcMesh = pScene->mMeshes[iNum];
pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
@ -386,7 +590,7 @@ void MD3Importer::InternReadFile( const std::string& pFile,
pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNumUVComponents[0] = 2; pcMesh->mNumUVComponents[0] = 2;
// fill in all triangles // Fill in all triangles
unsigned int iCurrent = 0; unsigned int iCurrent = 0;
for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_TRIANGLES;++i) 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; unsigned int iTemp = iCurrent;
for (unsigned int c = 0; c < 3;++c,++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].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].y = pcVertices[ pcTriangles->INDEXES[c]].Y*AI_MD3_XYZ_SCALE;
pcMesh->mVertices[iCurrent].z = pcVertices[ pcTriangles->INDEXES[c]].Z*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, LatLngNormalToVec3(pcVertices[pcTriangles->INDEXES[c]].NORMAL,
(float*)&pcMesh->mNormals[iCurrent]); (float*)&pcMesh->mNormals[iCurrent]);
// read texture coordinates // Read texture coordinates
pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U; pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U;
pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V; pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V;
} }
@ -416,51 +620,65 @@ void MD3Importer::InternReadFile( const std::string& pFile,
pcTriangles++; pcTriangles++;
} }
// get the first shader (= texture?) assigned to the surface std::string _texture_name;
if (pcSurfaces->NUM_SHADER) const char* texture_name = NULL, *header_name = pcHeader->NAME;
{
// 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,'/');
const char* szEndDir2 = ::strrchr((const char*)pcShaders->NAME,'\\'); // Check whether we have a texture record for this surface in the .skin file
if (!szEndDir2)szEndDir2 = ::strrchr((const char*)pcShaders->NAME,'/'); std::list< Q3Shader::SkinData::TextureEntry >::iterator it = std::find(
skins.textures.begin(), skins.textures.end(), pcSurfaces->NAME );
if (szEndDir1 && szEndDir2) if (it != skins.textures.end()) {
{ texture_name = &*( _texture_name = (*it).second).begin();
// both of them are valid DefaultLogger::get()->debug("MD3: Assigning skin texture " + (*it).second + " to surface " + pcSurfaces->NAME);
const unsigned int iLen1 = (unsigned int)(szEndDir1 - (const char*)pcHeader->NAME); (*it).resolved = true; // mark entry as resolved
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;
} }
// Get the first shader (= texture?) assigned to the surface
if (!texture_name && pcSurfaces->NUM_SHADER) {
texture_name = pcShaders->NAME;
} }
if (bSuccess) {
// use the file name only const char* end2 = NULL;
szEndDir2++; if (texture_name) {
// 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,'/');
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 { else {
// use the full path // Use the full path
szEndDir2 = (const char*)pcShaders->NAME; end2 = (const char*)texture_name;
} }
} }
}
MaterialHelper* pcHelper = new MaterialHelper(); MaterialHelper* pcHelper = new MaterialHelper();
if (szEndDir2) { // Setup dummy texture file name to ensure UV coordinates are kept during postprocessing
aiString szString; aiString szString;
if (szEndDir2[0]) { if (end2 && end2[0]) {
const size_t iLen = ::strlen(szEndDir2); const size_t iLen = ::strlen(end2);
::memcpy(szString.data,szEndDir2,iLen); ::memcpy(szString.data,end2,iLen);
szString.data[iLen] = '\0'; szString.data[iLen] = '\0';
szString.length = iLen; szString.length = iLen;
} }
@ -469,12 +687,11 @@ void MD3Importer::InternReadFile( const std::string& pFile,
szString.Set("dummy_texture.bmp"); szString.Set("dummy_texture.bmp");
} }
pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0)); pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
}
int iMode = (int)aiShadingMode_Gouraud; const int iMode = (int)aiShadingMode_Gouraud;
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
// add a small ambient color value - Quake 3 seems to have one // Add a small ambient color value - Quake 3 seems to have one
aiColor3D clr; aiColor3D clr;
clr.b = clr.g = clr.r = 0.05f; clr.b = clr.g = clr.r = 0.05f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT); pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
@ -483,44 +700,29 @@ void MD3Importer::InternReadFile( const std::string& pFile,
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR); pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
aiString szName; // use surface name + skin_name as material name
szName.Set(AI_DEFAULT_MATERIAL_NAME); aiString name;
pcHelper->AddProperty(&szName,AI_MATKEY_NAME); name.Set("MD3_[" + configSkinFile + "][" + pcSurfaces->NAME + "]");
pcHelper->AddProperty(&name,AI_MATKEY_NAME);
pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper; pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
pcMesh->mMaterialIndex = iNumMaterials++; pcMesh->mMaterialIndex = iNumMaterials++;
}
else
{
if (0xFFFFFFFF != iDefaultMatIndex) {
pcMesh->mMaterialIndex = iDefaultMatIndex;
}
else
{
MaterialHelper* pcHelper = new MaterialHelper();
// fill in a default material // Go to the next surface
int iMode = (int)aiShadingMode_Gouraud;
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
aiColor3D clr;
clr.b = clr.g = clr.r = 0.6f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
clr.b = clr.g = clr.r = 0.05f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
iDefaultMatIndex = pcMesh->mMaterialIndex = iNumMaterials++;
}
}
// go to the next surface
pcSurfaces = (BE_NCONST MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END); 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) 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; pScene->mNumMaterials = iNumMaterials;
// Now we need to generate an empty node graph // 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.y);
AI_SWAP4(pcTags->origin.z); AI_SWAP4(pcTags->origin.z);
// copy local origin // Copy local origin
nd->mTransformation.a4 = pcTags->origin.x; nd->mTransformation.a4 = pcTags->origin.x;
nd->mTransformation.b4 = pcTags->origin.y; nd->mTransformation.b4 = pcTags->origin.y;
nd->mTransformation.c4 = pcTags->origin.z; 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 a = 0; a < 3;++a) {
for (unsigned int m = 0; m < 3;++m) { for (unsigned int m = 0; m < 3;++m) {
nd->mTransformation[m][a] = pcTags->orientation[a][m]; nd->mTransformation[m][a] = pcTags->orientation[a][m];

View File

@ -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 #ifndef AI_MD3LOADER_H_INCLUDED
#define 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" #include "../include/aiTypes.h"
struct aiNode;
#include "MD3FileData.h" #include "MD3FileData.h"
namespace Assimp { namespace Assimp {
class MaterialHelper; class MaterialHelper;
using namespace MD3; 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<std::string,std::string>
{
// 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<TextureEntry> 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<ShaderMapBlock> maps;
};
// ---------------------------------------------------------------------------
/** @brief Tiny utility data structure to hold the data of a .shader file
*/
struct ShaderData
{
//! Shader data blocks
std::list<ShaderDataBlock> 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 class MD3Importer : public BaseImporter
{ {
@ -111,6 +239,12 @@ protected:
*/ */
bool ReadMultipartFile(); bool ReadMultipartFile();
// -------------------------------------------------------------------
/** Try to read the skin for a MD3 file
* @param fill Receives output information
*/
void ReadSkin(Q3Shader::SkinData& fill);
protected: protected:
/** Configuration option: frame to be loaded */ /** Configuration option: frame to be loaded */
@ -119,6 +253,9 @@ protected:
/** Configuration option: process multi-part files */ /** Configuration option: process multi-part files */
bool configHandleMP; bool configHandleMP;
/** Configuration option: name of skin file to be read */
std::string configSkinFile;
/** Header of the MD3 file */ /** Header of the MD3 file */
BE_NCONST MD3::Header* pcHeader; BE_NCONST MD3::Header* pcHeader;
@ -131,6 +268,12 @@ protected:
/** Current file name */ /** Current file name */
std::string mFile; std::string mFile;
/** Current base directory */
std::string path;
/** Pure file we're currently reading */
std::string filename;
/** Output scene to be filled */ /** Output scene to be filled */
aiScene* mScene; aiScene* mScene;

View File

@ -370,7 +370,7 @@ private:
inline bool SkipLine( const char* in, const char** out) inline bool SkipLine( const char* in, const char** out)
{ {
++lineNumber; ++lineNumber;
return ::SkipLine(in,out); return Assimp::SkipLine(in,out);
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
inline bool SkipLine( ) inline bool SkipLine( )
@ -407,7 +407,7 @@ private:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
inline bool SkipSpaces( ) inline bool SkipSpaces( )
{ {
return ::SkipSpaces((const char**)&buffer); return Assimp::SkipSpaces((const char**)&buffer);
} }
char* buffer; char* buffer;

View File

@ -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 #ifndef AI_PARSING_UTILS_H_INC
#define AI_PARSING_UTILS_H_INC #define AI_PARSING_UTILS_H_INC
#include "StringComparison.h"
namespace Assimp {
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
template <class char_t> template <class char_t>
AI_FORCE_INLINE bool IsSpace( const char_t in) 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; 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) AI_FORCE_INLINE bool TokenMatch(char*& in, const char* token, unsigned int len)
{ {
if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[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; 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<char*&>(in), token, len);
}
// ---------------------------------------------------------------------------------
AI_FORCE_INLINE void SkipToken(const char*& in) AI_FORCE_INLINE void SkipToken(const char*& in)
{ {
SkipSpaces(&in); SkipSpaces(&in);
while (!IsSpaceOrNewLine(*in))++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 #endif // ! AI_PARSING_UTILS_H_INC

View File

@ -360,7 +360,7 @@ protected:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
inline bool SkipLine( const char* in, const char** out) inline bool SkipLine( const char* in, const char** out)
{ {
::SkipLine(in,out); Assimp::SkipLine(in,out);
++iLineNumber; ++iLineNumber;
return true; return true;
} }
@ -368,7 +368,7 @@ protected:
inline bool SkipSpacesAndLineEnd( const char* in, const char** out) inline bool SkipSpacesAndLineEnd( const char* in, const char** out)
{ {
++iLineNumber; ++iLineNumber;
return ::SkipSpacesAndLineEnd(in,out); return Assimp::SkipSpacesAndLineEnd(in,out);
} }
private: private:

View File

@ -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. * combine all three files if one of them is loaded.
* Property type: integer (0: false; !0: true). Default value: true. * 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
* <md3_file_name>_<skin_name>.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"
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -441,6 +441,54 @@ enum aiTextureFlags
//! @endcond //! @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 <DestColor> is the previous color in the framebuffer at this
* position and <SourceColor> is the material colro before the transparency
* calculation.<br>
* 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" #include "./Compiler/pushpack1.h"
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -694,12 +742,27 @@ extern "C" {
*/ */
#define AI_MATKEY_ENABLE_WIREFRAME "$mat.wireframe",0,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.
* <br>
* <b>Type:</b> int (#aiBlendMode)<br>
* <b>Default value:</b> <tt>#aiBlendMode_Default</tt>
*/
#define AI_MATKEY_BLEND_FUNC "$mat.blend",0,0
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** @def AI_MATKEY_OPACITY /** @def AI_MATKEY_OPACITY
* Defines the base opacity of the material. This value defines how * Defines the base opacity of the material. To get the opacity value for
* transparent the material actually is. However, in order to get absolutely * a particular channel, this value is multiplied with the corresponding
* correct results you'll also need to evaluate the * channel of the #AI_MATKEY_COLOR_TRANSPARENT property. The final value
* #AI_MATKEY_COLOR_TRANSPARENT property. * is fed in the blend function defined by the #AI_MATKEY_BLEND_FUNC
* property.
* <br> * <br>
* <b>Type:</b> float<br> * <b>Type:</b> float<br>
* <b>Default value:</b> <tt>1.0f</tt><br> * <b>Default value:</b> <tt>1.0f</tt><br>
@ -755,7 +818,8 @@ extern "C" {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** @def AI_MATKEY_COLOR_DIFFUSE /** @def AI_MATKEY_COLOR_DIFFUSE
* Defines the diffuse base color of the material. <br> * Defines the diffuse base color of the material.
* If stored as 4-component color, the alpha channel is ignored.<br>
* <b>Type:</b> color (#aiColor4D or #aiColor3D) <br> * <b>Type:</b> color (#aiColor4D or #aiColor3D) <br>
* <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f </tt> * <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f </tt>
*/ */
@ -763,7 +827,8 @@ extern "C" {
/** @def AI_MATKEY_COLOR_AMBIENT /** @def AI_MATKEY_COLOR_AMBIENT
* Declares the amount of ambient light emitted from * Declares the amount of ambient light emitted from
* the surface of this object. <br> * the surface of this object. If stored as 4-component color,
* the alpha channel is ignored.<br><br>
* <b>Type:</b> color (#aiColor4D or #aiColor3D) <br> * <b>Type:</b> color (#aiColor4D or #aiColor3D) <br>
* <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f </tt> * <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f </tt>
*/ */
@ -771,7 +836,8 @@ extern "C" {
/** @def AI_MATKEY_COLOR_SPECULAR /** @def AI_MATKEY_COLOR_SPECULAR
* Declares the color of light specularly reflected from * Declares the color of light specularly reflected from
* the surface of this object. <br> * the surface of this object. If stored as 4-component color, the
* alpha channel is ignored.<br><br>
* <b>Type:</b> color (#aiColor4D or #aiColor3D) <br> * <b>Type:</b> color (#aiColor4D or #aiColor3D) <br>
* <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f </tt> * <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f </tt>
*/ */
@ -779,21 +845,26 @@ extern "C" {
/** @def AI_MATKEY_COLOR_EMISSIVE /** @def AI_MATKEY_COLOR_EMISSIVE
* Declares the amount of light emitted from the * Declares the amount of light emitted from the
* surface of this object. <br> * surface of this object. If stored as 4-component color, the alpha
* channel is ignored.<br><br>
* <b>Type:</b> color (#aiColor4D or #aiColor3D) <br> * <b>Type:</b> color (#aiColor4D or #aiColor3D) <br>
* <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f </tt> * <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f </tt>
*/ */
#define AI_MATKEY_COLOR_EMISSIVE "$clr.emissive",0,0 #define AI_MATKEY_COLOR_EMISSIVE "$clr.emissive",0,0
/** @def AI_MATKEY_COLOR_TRANSPARENT /** @def AI_MATKEY_COLOR_TRANSPARENT
* Defines the transparent base color of the material. <br> * 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.<br> <br>
* <b>Type:</b> color (#aiColor4D or #aiColor3D) <br> * <b>Type:</b> color (#aiColor4D or #aiColor3D) <br>
* <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f </tt> * <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f </tt>
*/ */
#define AI_MATKEY_COLOR_TRANSPARENT "$clr.transparent",0,0 #define AI_MATKEY_COLOR_TRANSPARENT "$clr.transparent",0,0
/** @def AI_MATKEY_COLOR_REFLECTIVE /** @def AI_MATKEY_COLOR_REFLECTIVE
* Declares the color of a perfect mirror reflection. <br> * Declares the color of a perfect mirror reflection. If stored as
* 4-component color, the alpha channel is ignored.<br> <br>
* <b>Type:</b> color (#aiColor4D or #aiColor3D) <br> * <b>Type:</b> color (#aiColor4D or #aiColor3D) <br>
* <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f </tt> * <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f </tt>
*/ */