Browse Source

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

aramis_acg 16 years ago
parent
commit
2b9461fbf7
10 changed files with 716 additions and 253 deletions
  1. 23 18
      code/DefaultIOStream.cpp
  2. 17 4
      code/DefaultIOStream.h
  3. 35 43
      code/MD3FileData.h
  4. 354 152
      code/MD3Loader.cpp
  5. 147 4
      code/MD3Loader.h
  6. 2 2
      code/MD5Parser.h
  7. 28 4
      code/ParsingUtils.h
  8. 2 2
      code/SMDLoader.h
  9. 14 1
      include/aiConfig.h
  10. 94 23
      include/aiMaterial.h

+ 23 - 18
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.
 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 
-		&& aiOrigin_SET == SEEK_SET);
+	BOOST_STATIC_ASSERT(aiOrigin_CUR == SEEK_CUR && 
+		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)
+	if (! mFile || mFilename.empty())
 		return 0;
 		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__
 #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
 #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
 #endif
+	}
+	return cachedSize;
 }
 }
 
 
 // ----------------------------------------------------------------------------------
 // ----------------------------------------------------------------------------------

+ 17 - 4
code/DefaultIOStream.h

@@ -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), 
-	mFilename("") 
+	mFile		(NULL), 
+	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
 }
 }

+ 35 - 43
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
 #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_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)
 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
- *	\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 ) 
 inline void Vec3NormalToLatLng( const aiVector3D& p_vIn, uint16_t& p_iOut ) 
 {
 {
 	// check for singularities
 	// check for singularities

+ 354 - 152
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"
 #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);
 
 
-	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");
 		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"));
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-// Read a multi-part Q3 player model
-bool MD3Importer::ReadMultipartFile()
+// Try to read the skin for a MD3 file
+void MD3Importer::ReadSkin(Q3Shader::SkinData& fill)
 {
 {
-	std::string::size_type s = mFile.find_last_of('/');
-	if (s == std::string::npos) {
-		s = mFile.find_last_of('\\');
-	}
+	// skip any postfixes (e.g. lower_1.md3)
+	std::string::size_type s = filename.find_last_of('_');
 	if (s == std::string::npos) {
 	if (s == std::string::npos) {
-		s = 0;
+		s = filename.find_last_of('.');
 	}
 	}
-	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);
+	ai_assert(s != std::string::npos);
 
 
-	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 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()
+{
+	// 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;
+
+	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<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);
-		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
 		// 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
-	// at least the file header
+	// Check whether the md3 file is large enough to contain the 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 ->
-	// 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->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,111 +620,109 @@ void MD3Importer::InternReadFile( const std::string& pFile,
 			pcTriangles++;
 			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;
+		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
+		}
+
+		// Get the first shader (= texture?) assigned to the surface
+		if (!texture_name && pcSurfaces->NUM_SHADER)	{
+			texture_name = pcShaders->NAME;
+		}
+
+		const char* end2 = NULL;
+		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;
 				}
 				}
-			}
-			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 len2 = std::min (len1, (size_t)(end2 - texture_name ));
+
+				if (!ASSIMP_strincmp(texture_name,header_name,len2)) {
+					// Use the file name only
+					end2++;
 				}
 				}
-				else	{
-					DefaultLogger::get()->warn("Texture file name has zero length. Using default name");
-					szString.Set("dummy_texture.bmp");
+				else {
+					// Use the full path
+					end2 = (const char*)texture_name;
 				}
 				}
-				pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
 			}
 			}
+		}
 
 
-			int iMode = (int)aiShadingMode_Gouraud;
-			pcHelper->AddProperty<int>(&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<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+		MaterialHelper* pcHelper = new MaterialHelper();
 
 
-			clr.b = clr.g = clr.r = 1.0f;
-			pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
-			pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+		// 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));
 
 
-			aiString szName;
-			szName.Set(AI_DEFAULT_MATERIAL_NAME);
-			pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+		const int iMode = (int)aiShadingMode_Gouraud;
+		pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
 
 
-			pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
-			pcMesh->mMaterialIndex = iNumMaterials++;
-		}
-		else
-		{
-			if (0xFFFFFFFF != iDefaultMatIndex)	{
-				pcMesh->mMaterialIndex = iDefaultMatIndex;
-			}
-			else
-			{
-				MaterialHelper* pcHelper = new MaterialHelper();
+		// Add a small ambient color value - Quake 3 seems to have one
+		aiColor3D clr;
+		clr.b = clr.g = clr.r = 0.05f;
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
 
 
-				// fill in a default material
-				int iMode = (int)aiShadingMode_Gouraud;
-				pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+		clr.b = clr.g = clr.r = 1.0f;
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
 
 
-				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);
+		// use surface name + skin_name as material name
+		aiString name;
+		name.Set("MD3_[" + configSkinFile + "][" + pcSurfaces->NAME + "]");
+		pcHelper->AddProperty(&name,AI_MATKEY_NAME);
 
 
-				clr.b = clr.g = clr.r = 0.05f;
-				pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+		pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
+		pcMesh->mMaterialIndex = iNumMaterials++;
+	
+		// Go to the next surface
+		pcSurfaces = (BE_NCONST MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
+	}
 
 
-				pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
-				iDefaultMatIndex = pcMesh->mMaterialIndex = iNumMaterials++;
+	// 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);
 			}
 			}
 		}
 		}
-		// go to the next surface
-		pcSurfaces = (BE_NCONST MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
 	}
 	}
 
 
 	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];

+ 147 - 4
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
 #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 {
+
+// ---------------------------------------------------------------------------
+/** @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;
+};
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-/** Used to load MD3 files
+/** @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;
 
 

+ 2 - 2
code/MD5Parser.h

@@ -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;

+ 28 - 4
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
 #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,7 +134,7 @@ 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)
+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]))
 	{
 	{
@@ -139,9 +144,14 @@ AI_FORCE_INLINE bool TokenMatch(const char*& in, const char* token, unsigned int
 	return false;
 	return false;
 }
 }
 // ---------------------------------------------------------------------------------
 // ---------------------------------------------------------------------------------
-AI_FORCE_INLINE bool TokenMatch(char*& in, const char* token, unsigned int len)
+/** @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 (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len]))
+	if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len]))
 	{
 	{
 		in += len+1;
 		in += len+1;
 		return true;
 		return true;
@@ -149,9 +159,23 @@ AI_FORCE_INLINE bool TokenMatch(char*& in, const char* token, unsigned int len)
 	return false;
 	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

+ 2 - 2
code/SMDLoader.h

@@ -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:

+ 14 - 1
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. 
  * 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"
 
 
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------

+ 94 - 23
include/aiMaterial.h

@@ -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"
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
@@ -688,18 +736,33 @@ extern "C" {
  *  Integer property. 1 to enable wireframe mode for rendering.
  *  Integer property. 1 to enable wireframe mode for rendering.
  *  A material with this property set to 1 should appear as wireframe, even
  *  A material with this property set to 1 should appear as wireframe, even
  *  if the scene is rendered solid.
  *  if the scene is rendered solid.
- * <br>
- * <b>Type:</b> int <br>
- * <b>Default value:</b> <tt>0</tt>
+ *  <br>
+ *  <b>Type:</b> int <br>
+ *  <b>Default value:</b> <tt>0</tt>
 */
 */
 #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
- *  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.
  * <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,39 +827,46 @@ 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>
- * <b>Type:</b> color (#aiColor4D or #aiColor3D)     <br>
- * <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f     </tt>       
+ *  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>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f     </tt>       
 */
 */
 #define AI_MATKEY_COLOR_AMBIENT "$clr.ambient",0,0
 #define AI_MATKEY_COLOR_AMBIENT "$clr.ambient",0,0
 
 
 /** @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>
- * <b>Type:</b> color (#aiColor4D or #aiColor3D)     <br>
- * <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f     </tt>
+ *  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>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f     </tt>
 */
 */
 #define AI_MATKEY_COLOR_SPECULAR "$clr.specular",0,0
 #define AI_MATKEY_COLOR_SPECULAR "$clr.specular",0,0
 
 
 /** @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>
- * <b>Type:</b> color (#aiColor4D or #aiColor3D)     <br>
- * <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f     </tt>
+ *  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>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>
- * <b>Type:</b> color (#aiColor4D or #aiColor3D)        <br>
- * <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f        </tt>
+ *  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>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>
- * <b>Type:</b> color (#aiColor4D or #aiColor3D)       <br>
- * <b>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f       </tt>
+ *  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>Default value:</b> <tt>0.0f|0.0f|0.0f|1.0f       </tt>
 */
 */
 #define AI_MATKEY_COLOR_REFLECTIVE "$clr.reflective",0,0
 #define AI_MATKEY_COLOR_REFLECTIVE "$clr.reflective",0,0