vrshoot

diff libs/assimp/MD3Loader.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/libs/assimp/MD3Loader.cpp	Sat Feb 01 19:58:19 2014 +0200
     1.3 @@ -0,0 +1,1057 @@
     1.4 +/*
     1.5 +---------------------------------------------------------------------------
     1.6 +Open Asset Import Library (assimp)
     1.7 +---------------------------------------------------------------------------
     1.8 +
     1.9 +Copyright (c) 2006-2012, assimp team
    1.10 +
    1.11 +All rights reserved.
    1.12 +
    1.13 +Redistribution and use of this software in source and binary forms, 
    1.14 +with or without modification, are permitted provided that the following 
    1.15 +conditions are met:
    1.16 +
    1.17 +* Redistributions of source code must retain the above
    1.18 +  copyright notice, this list of conditions and the
    1.19 +  following disclaimer.
    1.20 +
    1.21 +* Redistributions in binary form must reproduce the above
    1.22 +  copyright notice, this list of conditions and the
    1.23 +  following disclaimer in the documentation and/or other
    1.24 +  materials provided with the distribution.
    1.25 +
    1.26 +* Neither the name of the assimp team, nor the names of its
    1.27 +  contributors may be used to endorse or promote products
    1.28 +  derived from this software without specific prior
    1.29 +  written permission of the assimp team.
    1.30 +
    1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
    1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.42 +---------------------------------------------------------------------------
    1.43 +*/
    1.44 +
    1.45 +/** @file MD3Loader.cpp
    1.46 + *  @brief Implementation of the MD3 importer class 
    1.47 + * 
    1.48 + *  Sources: 
    1.49 + *     http://www.gamers.org/dEngine/quake3/UQ3S
    1.50 + *     http://linux.ucla.edu/~phaethon/q3/formats/md3format.html
    1.51 + *     http://www.heppler.com/shader/shader/
    1.52 + */
    1.53 +
    1.54 +#include "AssimpPCH.h"
    1.55 +#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
    1.56 +
    1.57 +#include "MD3Loader.h"
    1.58 +#include "ByteSwap.h"
    1.59 +#include "SceneCombiner.h"
    1.60 +#include "GenericProperty.h"
    1.61 +#include "RemoveComments.h"
    1.62 +#include "ParsingUtils.h"
    1.63 +#include "Importer.h"
    1.64 +
    1.65 +using namespace Assimp;
    1.66 +
    1.67 +static const aiImporterDesc desc = {
    1.68 +	"Quake III Mesh Importer",
    1.69 +	"",
    1.70 +	"",
    1.71 +	"",
    1.72 +	aiImporterFlags_SupportBinaryFlavour,
    1.73 +	0,
    1.74 +	0,
    1.75 +	0,
    1.76 +	0,
    1.77 +	"md3" 
    1.78 +};
    1.79 +
    1.80 +// ------------------------------------------------------------------------------------------------
    1.81 +// Convert a Q3 shader blend function to the appropriate enum value
    1.82 +Q3Shader::BlendFunc StringToBlendFunc(const std::string& m)
    1.83 +{
    1.84 +	if (m == "GL_ONE") {
    1.85 +		return Q3Shader::BLEND_GL_ONE;
    1.86 +	}
    1.87 +	if (m == "GL_ZERO") {
    1.88 +		return Q3Shader::BLEND_GL_ZERO;
    1.89 +	}
    1.90 +	if (m == "GL_SRC_ALPHA") {
    1.91 +		return Q3Shader::BLEND_GL_SRC_ALPHA;
    1.92 +	}
    1.93 +	if (m == "GL_ONE_MINUS_SRC_ALPHA") {
    1.94 +		return Q3Shader::BLEND_GL_ONE_MINUS_SRC_ALPHA;
    1.95 +	}
    1.96 +	if (m == "GL_ONE_MINUS_DST_COLOR") {
    1.97 +		return Q3Shader::BLEND_GL_ONE_MINUS_DST_COLOR;
    1.98 +	}
    1.99 +	DefaultLogger::get()->error("Q3Shader: Unknown blend function: " + m);
   1.100 +	return Q3Shader::BLEND_NONE;
   1.101 +}
   1.102 +
   1.103 +// ------------------------------------------------------------------------------------------------
   1.104 +// Load a Quake 3 shader
   1.105 +bool Q3Shader::LoadShader(ShaderData& fill, const std::string& pFile,IOSystem* io)
   1.106 +{
   1.107 +	boost::scoped_ptr<IOStream> file( io->Open( pFile, "rt"));
   1.108 +	if (!file.get())
   1.109 +		return false; // if we can't access the file, don't worry and return
   1.110 +
   1.111 +	DefaultLogger::get()->info("Loading Quake3 shader file " + pFile);
   1.112 +
   1.113 +	// read file in memory
   1.114 +	const size_t s = file->FileSize();
   1.115 +	std::vector<char> _buff(s+1);
   1.116 +	file->Read(&_buff[0],s,1);
   1.117 +	_buff[s] = 0;
   1.118 +
   1.119 +	// remove comments from it (C++ style)
   1.120 +	CommentRemover::RemoveLineComments("//",&_buff[0]);
   1.121 +	const char* buff = &_buff[0];
   1.122 +
   1.123 +	Q3Shader::ShaderDataBlock* curData = NULL;
   1.124 +	Q3Shader::ShaderMapBlock*  curMap  = NULL;
   1.125 +
   1.126 +	// read line per line
   1.127 +	for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) {
   1.128 +	
   1.129 +		if (*buff == '{') {
   1.130 +			++buff;
   1.131 +
   1.132 +			// append to last section, if any
   1.133 +			if (!curData) {
   1.134 +				DefaultLogger::get()->error("Q3Shader: Unexpected shader section token \'{\'");
   1.135 +				return true; // still no failure, the file is there
   1.136 +			}
   1.137 +
   1.138 +			// read this data section
   1.139 +			for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) {
   1.140 +				if (*buff == '{') {
   1.141 +					++buff;
   1.142 +					// add new map section
   1.143 +					curData->maps.push_back(Q3Shader::ShaderMapBlock());
   1.144 +					curMap = &curData->maps.back();
   1.145 +
   1.146 +					for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) {
   1.147 +						// 'map' - Specifies texture file name
   1.148 +						if (TokenMatchI(buff,"map",3) || TokenMatchI(buff,"clampmap",8)) {
   1.149 +							curMap->name = GetNextToken(buff);
   1.150 +						}	
   1.151 +						// 'blendfunc' - Alpha blending mode
   1.152 +						else if (TokenMatchI(buff,"blendfunc",9)) {	
   1.153 +							const std::string blend_src = GetNextToken(buff);
   1.154 +							if (blend_src == "add") {
   1.155 +								curMap->blend_src  = Q3Shader::BLEND_GL_ONE;
   1.156 +								curMap->blend_dest = Q3Shader::BLEND_GL_ONE;
   1.157 +							}
   1.158 +							else if (blend_src == "filter") {
   1.159 +								curMap->blend_src  = Q3Shader::BLEND_GL_DST_COLOR;
   1.160 +								curMap->blend_dest = Q3Shader::BLEND_GL_ZERO;
   1.161 +							}
   1.162 +							else if (blend_src == "blend") {
   1.163 +								curMap->blend_src  = Q3Shader::BLEND_GL_SRC_ALPHA;
   1.164 +								curMap->blend_dest = Q3Shader::BLEND_GL_ONE_MINUS_SRC_ALPHA;
   1.165 +							}
   1.166 +							else {
   1.167 +								curMap->blend_src  = StringToBlendFunc(blend_src);
   1.168 +								curMap->blend_dest = StringToBlendFunc(GetNextToken(buff));
   1.169 +							}
   1.170 +						}
   1.171 +						// 'alphafunc' - Alpha testing mode
   1.172 +						else if (TokenMatchI(buff,"alphafunc",9)) {	
   1.173 +							const std::string at = GetNextToken(buff);
   1.174 +							if (at == "GT0") {
   1.175 +								curMap->alpha_test = Q3Shader::AT_GT0;
   1.176 +							}
   1.177 +							else if (at == "LT128") {
   1.178 +								curMap->alpha_test = Q3Shader::AT_LT128;
   1.179 +							}
   1.180 +							else if (at == "GE128") {
   1.181 +								curMap->alpha_test = Q3Shader::AT_GE128;
   1.182 +							}
   1.183 +						}
   1.184 +						else if (*buff == '}') {
   1.185 +							++buff;
   1.186 +							// close this map section
   1.187 +							curMap = NULL;
   1.188 +							break;
   1.189 +						}
   1.190 +					}
   1.191 +
   1.192 +				}
   1.193 +				else if (*buff == '}') {
   1.194 +					++buff;
   1.195 +					curData = NULL;					
   1.196 +					break;
   1.197 +				}
   1.198 +
   1.199 +				// 'cull' specifies culling behaviour for the model
   1.200 +				else if (TokenMatchI(buff,"cull",4)) {
   1.201 +					SkipSpaces(&buff);
   1.202 +					if (!ASSIMP_strincmp(buff,"back",4)) {
   1.203 +						curData->cull = Q3Shader::CULL_CCW;
   1.204 +					}
   1.205 +					else if (!ASSIMP_strincmp(buff,"front",5)) {
   1.206 +						curData->cull = Q3Shader::CULL_CW;
   1.207 +					}
   1.208 +					else if (!ASSIMP_strincmp(buff,"none",4) || !ASSIMP_strincmp(buff,"disable",7)) {
   1.209 +						curData->cull = Q3Shader::CULL_NONE;
   1.210 +					}
   1.211 +					else DefaultLogger::get()->error("Q3Shader: Unrecognized cull mode");
   1.212 +				}
   1.213 +			}
   1.214 +		}
   1.215 +
   1.216 +		else {
   1.217 +			// add new section
   1.218 +			fill.blocks.push_back(Q3Shader::ShaderDataBlock());
   1.219 +			curData = &fill.blocks.back();
   1.220 +
   1.221 +			// get the name of this section
   1.222 +			curData->name = GetNextToken(buff);
   1.223 +		}
   1.224 +	}
   1.225 +	return true;
   1.226 +}
   1.227 +
   1.228 +// ------------------------------------------------------------------------------------------------
   1.229 +// Load a Quake 3 skin
   1.230 +bool Q3Shader::LoadSkin(SkinData& fill, const std::string& pFile,IOSystem* io)
   1.231 +{
   1.232 +	boost::scoped_ptr<IOStream> file( io->Open( pFile, "rt"));
   1.233 +	if (!file.get())
   1.234 +		return false; // if we can't access the file, don't worry and return
   1.235 +
   1.236 +	DefaultLogger::get()->info("Loading Quake3 skin file " + pFile);
   1.237 +
   1.238 +	// read file in memory
   1.239 +	const size_t s = file->FileSize();
   1.240 +	std::vector<char> _buff(s+1);const char* buff = &_buff[0];
   1.241 +	file->Read(&_buff[0],s,1);
   1.242 +	_buff[s] = 0;
   1.243 +
   1.244 +	// remove commas
   1.245 +	std::replace(_buff.begin(),_buff.end(),',',' ');
   1.246 +
   1.247 +	// read token by token and fill output table
   1.248 +	for (;*buff;) {
   1.249 +		SkipSpacesAndLineEnd(&buff);
   1.250 +
   1.251 +		// get first identifier
   1.252 +		std::string ss = GetNextToken(buff);
   1.253 +		
   1.254 +		// ignore tokens starting with tag_
   1.255 +		if (!::strncmp(&ss[0],"tag_",std::min((size_t)4, ss.length())))
   1.256 +			continue;
   1.257 +
   1.258 +		fill.textures.push_back(SkinData::TextureEntry());
   1.259 +		SkinData::TextureEntry& s = fill.textures.back();
   1.260 +
   1.261 +		s.first  = ss;
   1.262 +		s.second = GetNextToken(buff);
   1.263 +	}
   1.264 +	return true;
   1.265 +}
   1.266 +
   1.267 +// ------------------------------------------------------------------------------------------------
   1.268 +// Convert Q3Shader to material
   1.269 +void Q3Shader::ConvertShaderToMaterial(aiMaterial* out, const ShaderDataBlock& shader)
   1.270 +{
   1.271 +	ai_assert(NULL != out);
   1.272 +
   1.273 +	/*  IMPORTANT: This is not a real conversion. Actually we're just guessing and
   1.274 +	 *  hacking around to build an aiMaterial that looks nearly equal to the
   1.275 +	 *  original Quake 3 shader. We're missing some important features like
   1.276 +	 *  animatable material properties in our material system, but at least 
   1.277 +	 *  multiple textures should be handled correctly.
   1.278 +	 */ 
   1.279 +
   1.280 +	// Two-sided material?
   1.281 +	if (shader.cull == Q3Shader::CULL_NONE) {
   1.282 +		const int twosided = 1;
   1.283 +		out->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED);
   1.284 +	}
   1.285 +
   1.286 +	unsigned int cur_emissive = 0, cur_diffuse = 0, cur_lm =0;
   1.287 +
   1.288 +	// Iterate through all textures
   1.289 +	for (std::list< Q3Shader::ShaderMapBlock >::const_iterator it = shader.maps.begin(); it != shader.maps.end();++it) {
   1.290 +		
   1.291 +		// CONVERSION BEHAVIOUR:
   1.292 +		//
   1.293 +		//
   1.294 +		// If the texture is additive
   1.295 +		//  - if it is the first texture, assume additive blending for the whole material
   1.296 +		//  - otherwise register it as emissive texture.
   1.297 +		//
   1.298 +		// If the texture is using standard blend (or if the blend mode is unknown)
   1.299 +		//  - if first texture: assume default blending for material
   1.300 +		//  - in any case: set it as diffuse texture
   1.301 +		//
   1.302 +		// If the texture is using 'filter' blending
   1.303 +		//  - take as lightmap
   1.304 +		//
   1.305 +		// Textures with alpha funcs
   1.306 +		//  - aiTextureFlags_UseAlpha is set (otherwise aiTextureFlags_NoAlpha is explicitly set)
   1.307 +		aiString s((*it).name);
   1.308 +		aiTextureType type; unsigned int index;
   1.309 +
   1.310 +		if ((*it).blend_src == Q3Shader::BLEND_GL_ONE && (*it).blend_dest == Q3Shader::BLEND_GL_ONE) {
   1.311 +			if (it == shader.maps.begin()) {
   1.312 +				const int additive = aiBlendMode_Additive;
   1.313 +				out->AddProperty(&additive,1,AI_MATKEY_BLEND_FUNC);
   1.314 +				
   1.315 +				index = cur_diffuse++;
   1.316 +				type  = aiTextureType_DIFFUSE;
   1.317 +			}
   1.318 +			else {
   1.319 +				index = cur_emissive++;
   1.320 +				type  = aiTextureType_EMISSIVE;
   1.321 +			}
   1.322 +		}
   1.323 +		else if ((*it).blend_src == Q3Shader::BLEND_GL_DST_COLOR && (*it).blend_dest == Q3Shader::BLEND_GL_ZERO) {
   1.324 +			index = cur_lm++;
   1.325 +			type  = aiTextureType_LIGHTMAP;
   1.326 +		}
   1.327 +		else {
   1.328 +			const int blend = aiBlendMode_Default;
   1.329 +			out->AddProperty(&blend,1,AI_MATKEY_BLEND_FUNC);
   1.330 +			
   1.331 +			index = cur_diffuse++;
   1.332 +			type  = aiTextureType_DIFFUSE;
   1.333 +		}
   1.334 +
   1.335 +		// setup texture
   1.336 +		out->AddProperty(&s,AI_MATKEY_TEXTURE(type,index));
   1.337 +
   1.338 +		// setup texture flags
   1.339 +		const int use_alpha = ((*it).alpha_test != Q3Shader::AT_NONE ? aiTextureFlags_UseAlpha : aiTextureFlags_IgnoreAlpha);
   1.340 +		out->AddProperty(&use_alpha,1,AI_MATKEY_TEXFLAGS(type,index));
   1.341 +	}
   1.342 +	// If at least one emissive texture was set, set the emissive base color to 1 to ensure
   1.343 +	// the texture is actually displayed.
   1.344 +	if (0 != cur_emissive) {
   1.345 +		aiColor3D one(1.f,1.f,1.f);
   1.346 +		out->AddProperty(&one,1,AI_MATKEY_COLOR_EMISSIVE);
   1.347 +	}
   1.348 +}
   1.349 +
   1.350 +// ------------------------------------------------------------------------------------------------
   1.351 +// Constructor to be privately used by Importer
   1.352 +MD3Importer::MD3Importer()
   1.353 +: configFrameID  (0)
   1.354 +, configHandleMP (true)
   1.355 +{}
   1.356 +
   1.357 +// ------------------------------------------------------------------------------------------------
   1.358 +// Destructor, private as well 
   1.359 +MD3Importer::~MD3Importer()
   1.360 +{}
   1.361 +
   1.362 +// ------------------------------------------------------------------------------------------------
   1.363 +// Returns whether the class can handle the format of the given file. 
   1.364 +bool MD3Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
   1.365 +{
   1.366 +	const std::string extension = GetExtension(pFile);
   1.367 +	if (extension == "md3")
   1.368 +		return true;
   1.369 +
   1.370 +	// if check for extension is not enough, check for the magic tokens 
   1.371 +	if (!extension.length() || checkSig) {
   1.372 +		uint32_t tokens[1]; 
   1.373 +		tokens[0] = AI_MD3_MAGIC_NUMBER_LE;
   1.374 +		return CheckMagicToken(pIOHandler,pFile,tokens,1);
   1.375 +	}
   1.376 +	return false;
   1.377 +}
   1.378 +
   1.379 +// ------------------------------------------------------------------------------------------------
   1.380 +void MD3Importer::ValidateHeaderOffsets()
   1.381 +{
   1.382 +	// Check magic number
   1.383 +	if (pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE &&
   1.384 +		pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE)
   1.385 +			throw DeadlyImportError( "Invalid MD3 file: Magic bytes not found");
   1.386 +
   1.387 +	// Check file format version
   1.388 +	if (pcHeader->VERSION > 15)
   1.389 +		DefaultLogger::get()->warn( "Unsupported MD3 file version. Continuing happily ...");
   1.390 +
   1.391 +	// Check some offset values whether they are valid
   1.392 +	if (!pcHeader->NUM_SURFACES)
   1.393 +		throw DeadlyImportError( "Invalid md3 file: NUM_SURFACES is 0");
   1.394 +
   1.395 +	if (pcHeader->OFS_FRAMES >= fileSize || pcHeader->OFS_SURFACES >= fileSize || 
   1.396 +		pcHeader->OFS_EOF > fileSize) {
   1.397 +		throw DeadlyImportError("Invalid MD3 header: some offsets are outside the file");
   1.398 +	}
   1.399 +
   1.400 +	if (pcHeader->NUM_FRAMES <= configFrameID )
   1.401 +		throw DeadlyImportError("The requested frame is not existing the file");
   1.402 +}
   1.403 +
   1.404 +// ------------------------------------------------------------------------------------------------
   1.405 +void MD3Importer::ValidateSurfaceHeaderOffsets(const MD3::Surface* pcSurf)
   1.406 +{
   1.407 +	// Calculate the relative offset of the surface
   1.408 +	const int32_t ofs = int32_t((const unsigned char*)pcSurf-this->mBuffer);
   1.409 +
   1.410 +	// Check whether all data chunks are inside the valid range
   1.411 +	if (pcSurf->OFS_TRIANGLES + ofs + pcSurf->NUM_TRIANGLES * sizeof(MD3::Triangle)	> fileSize  ||
   1.412 +		pcSurf->OFS_SHADERS + ofs + pcSurf->NUM_SHADER * sizeof(MD3::Shader) > fileSize         ||
   1.413 +		pcSurf->OFS_ST + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::TexCoord) > fileSize          ||
   1.414 +		pcSurf->OFS_XYZNORMAL + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::Vertex) > fileSize)	{
   1.415 +
   1.416 +		throw DeadlyImportError("Invalid MD3 surface header: some offsets are outside the file");
   1.417 +	}
   1.418 +
   1.419 +	// Check whether all requirements for Q3 files are met. We don't
   1.420 +	// care, but probably someone does.
   1.421 +	if (pcSurf->NUM_TRIANGLES > AI_MD3_MAX_TRIANGLES) {
   1.422 +		DefaultLogger::get()->warn("MD3: Quake III triangle limit exceeded");
   1.423 +	}
   1.424 +
   1.425 +	if (pcSurf->NUM_SHADER > AI_MD3_MAX_SHADERS) {
   1.426 +		DefaultLogger::get()->warn("MD3: Quake III shader limit exceeded");
   1.427 +	}
   1.428 +
   1.429 +	if (pcSurf->NUM_VERTICES > AI_MD3_MAX_VERTS) {
   1.430 +		DefaultLogger::get()->warn("MD3: Quake III vertex limit exceeded");
   1.431 +	}
   1.432 +
   1.433 +	if (pcSurf->NUM_FRAMES > AI_MD3_MAX_FRAMES) {
   1.434 +		DefaultLogger::get()->warn("MD3: Quake III frame limit exceeded");
   1.435 +	}
   1.436 +}
   1.437 +
   1.438 +// ------------------------------------------------------------------------------------------------
   1.439 +const aiImporterDesc* MD3Importer::GetInfo () const
   1.440 +{
   1.441 +	return &desc;
   1.442 +}
   1.443 +
   1.444 +// ------------------------------------------------------------------------------------------------
   1.445 +// Setup configuration properties
   1.446 +void MD3Importer::SetupProperties(const Importer* pImp)
   1.447 +{
   1.448 +	// The 
   1.449 +	// AI_CONFIG_IMPORT_MD3_KEYFRAME option overrides the
   1.450 +	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
   1.451 +	configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD3_KEYFRAME,-1);
   1.452 +	if(static_cast<unsigned int>(-1) == configFrameID) {
   1.453 +		configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
   1.454 +	}
   1.455 +
   1.456 +	// AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART
   1.457 +	configHandleMP = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART,1));
   1.458 +
   1.459 +	// AI_CONFIG_IMPORT_MD3_SKIN_NAME
   1.460 +	configSkinFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SKIN_NAME,"default"));
   1.461 +
   1.462 +	// AI_CONFIG_IMPORT_MD3_SHADER_SRC
   1.463 +	configShaderFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SHADER_SRC,""));
   1.464 +
   1.465 +	// AI_CONFIG_FAVOUR_SPEED
   1.466 +	configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
   1.467 +}
   1.468 +
   1.469 +// ------------------------------------------------------------------------------------------------
   1.470 +// Try to read the skin for a MD3 file
   1.471 +void MD3Importer::ReadSkin(Q3Shader::SkinData& fill) const
   1.472 +{
   1.473 +	// skip any postfixes (e.g. lower_1.md3)
   1.474 +	std::string::size_type s = filename.find_last_of('_');
   1.475 +	if (s == std::string::npos) {
   1.476 +		s = filename.find_last_of('.');
   1.477 +	}
   1.478 +	ai_assert(s != std::string::npos);
   1.479 +
   1.480 +	const std::string skin_file = path + filename.substr(0,s) + "_" + configSkinFile + ".skin";
   1.481 +	Q3Shader::LoadSkin(fill,skin_file,mIOHandler);
   1.482 +}
   1.483 +
   1.484 +// ------------------------------------------------------------------------------------------------
   1.485 +// Try to read the shader for a MD3 file
   1.486 +void MD3Importer::ReadShader(Q3Shader::ShaderData& fill) const
   1.487 +{
   1.488 +	// Determine Q3 model name from given path
   1.489 +	const std::string::size_type s = path.find_last_of("\\/",path.length()-2);
   1.490 +	const std::string model_file = path.substr(s+1,path.length()-(s+2));
   1.491 +
   1.492 +	// If no specific dir or file is given, use our default search behaviour
   1.493 +	if (!configShaderFile.length()) {
   1.494 +		if(!Q3Shader::LoadShader(fill,path + "..\\..\\..\\scripts\\" + model_file + ".shader",mIOHandler)) {
   1.495 +			Q3Shader::LoadShader(fill,path + "..\\..\\..\\scripts\\" + filename + ".shader",mIOHandler);
   1.496 +		}
   1.497 +	}
   1.498 +	else {
   1.499 +		// If the given string specifies a file, load this file.
   1.500 +		// Otherwise it's a directory.
   1.501 +		const std::string::size_type st = configShaderFile.find_last_of('.');
   1.502 +		if (st == std::string::npos) {
   1.503 +			
   1.504 +			if(!Q3Shader::LoadShader(fill,configShaderFile + model_file + ".shader",mIOHandler)) {
   1.505 +				Q3Shader::LoadShader(fill,configShaderFile + filename + ".shader",mIOHandler);
   1.506 +			}
   1.507 +		}
   1.508 +		else {
   1.509 +			Q3Shader::LoadShader(fill,configShaderFile,mIOHandler);
   1.510 +		}
   1.511 +	}
   1.512 +}
   1.513 +
   1.514 +// ------------------------------------------------------------------------------------------------
   1.515 +// Tiny helper to remove a single node from its parent' list
   1.516 +void RemoveSingleNodeFromList(aiNode* nd)
   1.517 +{
   1.518 +	if (!nd || nd->mNumChildren || !nd->mParent)return;
   1.519 +	aiNode* par = nd->mParent;
   1.520 +	for (unsigned int i = 0; i < par->mNumChildren;++i) {
   1.521 +		if (par->mChildren[i] == nd) { 
   1.522 +			--par->mNumChildren;
   1.523 +			for (;i < par->mNumChildren;++i) {
   1.524 +				par->mChildren[i] = par->mChildren[i+1];
   1.525 +			}
   1.526 +			delete nd;
   1.527 +			break;
   1.528 +		}
   1.529 +	}
   1.530 +}
   1.531 +
   1.532 +// ------------------------------------------------------------------------------------------------
   1.533 +// Read a multi-part Q3 player model
   1.534 +bool MD3Importer::ReadMultipartFile()
   1.535 +{
   1.536 +	// check whether the file name contains a common postfix, e.g lower_2.md3
   1.537 +	std::string::size_type s = filename.find_last_of('_'), t = filename.find_last_of('.');
   1.538 +	ai_assert(t != std::string::npos);
   1.539 +	if (s == std::string::npos)
   1.540 +		s = t;
   1.541 +
   1.542 +	const std::string mod_filename = filename.substr(0,s);
   1.543 +	const std::string suffix = filename.substr(s,t-s);
   1.544 +
   1.545 +	if (mod_filename == "lower" || mod_filename == "upper" || mod_filename == "head"){
   1.546 +		const std::string lower = path + "lower" + suffix + ".md3";
   1.547 +		const std::string upper = path + "upper" + suffix + ".md3";
   1.548 +		const std::string head  = path + "head"  + suffix + ".md3";
   1.549 +
   1.550 +		aiScene* scene_upper = NULL;
   1.551 +		aiScene* scene_lower = NULL;
   1.552 +		aiScene* scene_head = NULL;
   1.553 +		std::string failure;
   1.554 +
   1.555 +		aiNode* tag_torso, *tag_head;
   1.556 +		std::vector<AttachmentInfo> attach;
   1.557 +
   1.558 +		DefaultLogger::get()->info("Multi part MD3 player model: lower, upper and head parts are joined");
   1.559 +
   1.560 +		// ensure we won't try to load ourselves recursively
   1.561 +		BatchLoader::PropertyMap props;
   1.562 +		SetGenericProperty( props.ints, AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART, 0, NULL);
   1.563 +
   1.564 +		// now read these three files
   1.565 +		BatchLoader batch(mIOHandler);
   1.566 +		const unsigned int _lower = batch.AddLoadRequest(lower,0,&props);
   1.567 +		const unsigned int _upper = batch.AddLoadRequest(upper,0,&props);
   1.568 +		const unsigned int _head  = batch.AddLoadRequest(head,0,&props);
   1.569 +		batch.LoadAll();
   1.570 +
   1.571 +		// now construct a dummy scene to place these three parts in
   1.572 +		aiScene* master   = new aiScene();
   1.573 +		aiNode* nd = master->mRootNode = new aiNode();
   1.574 +		nd->mName.Set("<MD3_Player>");
   1.575 +
   1.576 +		// ... and get them. We need all of them.
   1.577 +		scene_lower = batch.GetImport(_lower);
   1.578 +		if (!scene_lower) {
   1.579 +			DefaultLogger::get()->error("M3D: Failed to read multi part model, lower.md3 fails to load");
   1.580 +			failure = "lower";
   1.581 +			goto error_cleanup;
   1.582 +		}
   1.583 +
   1.584 +		scene_upper = batch.GetImport(_upper);
   1.585 +		if (!scene_upper) {
   1.586 +			DefaultLogger::get()->error("M3D: Failed to read multi part model, upper.md3 fails to load");
   1.587 +			failure = "upper";
   1.588 +			goto error_cleanup;
   1.589 +		}
   1.590 +
   1.591 +		scene_head  = batch.GetImport(_head);
   1.592 +		if (!scene_head) {
   1.593 +			DefaultLogger::get()->error("M3D: Failed to read multi part model, head.md3 fails to load");
   1.594 +			failure = "head";
   1.595 +			goto error_cleanup;
   1.596 +		}
   1.597 +
   1.598 +		// build attachment infos. search for typical Q3 tags
   1.599 +
   1.600 +		// original root
   1.601 +		scene_lower->mRootNode->mName.Set("lower");
   1.602 +		attach.push_back(AttachmentInfo(scene_lower, nd));
   1.603 +
   1.604 +		// tag_torso
   1.605 +		tag_torso = scene_lower->mRootNode->FindNode("tag_torso");
   1.606 +		if (!tag_torso) {
   1.607 +			DefaultLogger::get()->error("M3D: Failed to find attachment tag for multi part model: tag_torso expected");
   1.608 +			goto error_cleanup;
   1.609 +		}
   1.610 +		scene_upper->mRootNode->mName.Set("upper");
   1.611 +		attach.push_back(AttachmentInfo(scene_upper,tag_torso));
   1.612 +
   1.613 +		// tag_head
   1.614 +		tag_head = scene_upper->mRootNode->FindNode("tag_head");
   1.615 +		if (!tag_head) {
   1.616 +			DefaultLogger::get()->error("M3D: Failed to find attachment tag for multi part model: tag_head expected");
   1.617 +			goto error_cleanup;
   1.618 +		}
   1.619 +		scene_head->mRootNode->mName.Set("head");
   1.620 +		attach.push_back(AttachmentInfo(scene_head,tag_head));
   1.621 +
   1.622 +		// Remove tag_head and tag_torso from all other model parts ...
   1.623 +		// this ensures (together with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY)
   1.624 +		// that tag_torso/tag_head is also the name of the (unique) output node
   1.625 +		RemoveSingleNodeFromList (scene_upper->mRootNode->FindNode("tag_torso"));
   1.626 +		RemoveSingleNodeFromList (scene_head-> mRootNode->FindNode("tag_head" ));
   1.627 +
   1.628 +		// Undo the rotations which we applied to the coordinate systems. We're
   1.629 +		// working in global Quake space here
   1.630 +		scene_head->mRootNode->mTransformation  = aiMatrix4x4();
   1.631 +		scene_lower->mRootNode->mTransformation = aiMatrix4x4();
   1.632 +		scene_upper->mRootNode->mTransformation = aiMatrix4x4();
   1.633 +
   1.634 +		// and merge the scenes
   1.635 +		SceneCombiner::MergeScenes(&mScene,master, attach,
   1.636 +			AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES          |
   1.637 +			AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES       |
   1.638 +			AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS |
   1.639 +			(!configSpeedFlag ? AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY : 0));
   1.640 +
   1.641 +		// Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
   1.642 +		mScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
   1.643 +			0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
   1.644 +
   1.645 +		return true;
   1.646 +
   1.647 +error_cleanup:
   1.648 +		delete scene_upper;
   1.649 +		delete scene_lower;
   1.650 +		delete scene_head;
   1.651 +		delete master;
   1.652 +
   1.653 +		if (failure == mod_filename) {
   1.654 +			throw DeadlyImportError("MD3: failure to read multipart host file");
   1.655 +		}
   1.656 +	}
   1.657 +	return false;
   1.658 +}
   1.659 +
   1.660 +// ------------------------------------------------------------------------------------------------
   1.661 +// Convert a MD3 path to a proper value
   1.662 +void MD3Importer::ConvertPath(const char* texture_name, const char* header_name, std::string& out) const
   1.663 +{
   1.664 +	// If the MD3's internal path itself and the given path are using
   1.665 +	// the same directory, remove it completely to get right output paths.
   1.666 +	const char* end1 = ::strrchr(header_name,'\\');
   1.667 +	if (!end1)end1   = ::strrchr(header_name,'/');
   1.668 +
   1.669 +	const char* end2 = ::strrchr(texture_name,'\\');
   1.670 +	if (!end2)end2   = ::strrchr(texture_name,'/');
   1.671 +
   1.672 +	// HACK: If the paths starts with "models", ignore the
   1.673 +	// next two hierarchy levels, it specifies just the model name.
   1.674 +	// Ignored by Q3, it might be not equal to the real model location.
   1.675 +	if (end2)	{
   1.676 +
   1.677 +		size_t len2;
   1.678 +		const size_t len1 = (size_t)(end1 - header_name);
   1.679 +		if (!ASSIMP_strincmp(texture_name,"models",6) && (texture_name[6] == '/' || texture_name[6] == '\\')) {
   1.680 +			len2 = 6; // ignore the seventh - could be slash or backslash
   1.681 +
   1.682 +			if (!header_name[0]) {
   1.683 +				// Use the file name only
   1.684 +				out = end2+1;
   1.685 +				return;
   1.686 +			}
   1.687 +		}
   1.688 +		else len2 = std::min (len1, (size_t)(end2 - texture_name ));
   1.689 +		if (!ASSIMP_strincmp(texture_name,header_name,len2)) {
   1.690 +			// Use the file name only
   1.691 +			out = end2+1;
   1.692 +			return;
   1.693 +		}
   1.694 +	}
   1.695 +	// Use the full path
   1.696 +	out = texture_name;
   1.697 +}
   1.698 +
   1.699 +// ------------------------------------------------------------------------------------------------
   1.700 +// Imports the given file into the given scene structure. 
   1.701 +void MD3Importer::InternReadFile( const std::string& pFile, 
   1.702 +	aiScene* pScene, IOSystem* pIOHandler)
   1.703 +{
   1.704 +	mFile = pFile;
   1.705 +	mScene = pScene;
   1.706 +	mIOHandler = pIOHandler;
   1.707 +
   1.708 +	// get base path and file name
   1.709 +	// todo ... move to PathConverter
   1.710 +	std::string::size_type s = mFile.find_last_of("/\\");
   1.711 +	if (s == std::string::npos) {
   1.712 +		s = 0;
   1.713 +	}
   1.714 +	else ++s;
   1.715 +	filename = mFile.substr(s), path = mFile.substr(0,s);
   1.716 +	for( std::string::iterator it = filename .begin(); it != filename.end(); ++it)
   1.717 +		*it = tolower( *it);
   1.718 +
   1.719 +	// Load multi-part model file, if necessary
   1.720 +	if (configHandleMP) {
   1.721 +		if (ReadMultipartFile())
   1.722 +			return;
   1.723 +	}
   1.724 +
   1.725 +	boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
   1.726 +
   1.727 +	// Check whether we can read from the file
   1.728 +	if( file.get() == NULL)
   1.729 +		throw DeadlyImportError( "Failed to open MD3 file " + pFile + ".");
   1.730 +
   1.731 +	// Check whether the md3 file is large enough to contain the header
   1.732 +	fileSize = (unsigned int)file->FileSize();
   1.733 +	if( fileSize < sizeof(MD3::Header))
   1.734 +		throw DeadlyImportError( "MD3 File is too small.");
   1.735 +
   1.736 +	// Allocate storage and copy the contents of the file to a memory buffer
   1.737 +	std::vector<unsigned char> mBuffer2 (fileSize);
   1.738 +	file->Read( &mBuffer2[0], 1, fileSize);
   1.739 +	mBuffer = &mBuffer2[0];
   1.740 +
   1.741 +	pcHeader = (BE_NCONST MD3::Header*)mBuffer;
   1.742 +
   1.743 +	// Ensure correct endianess
   1.744 +#ifdef AI_BUILD_BIG_ENDIAN
   1.745 +
   1.746 +	AI_SWAP4(pcHeader->VERSION);
   1.747 +	AI_SWAP4(pcHeader->FLAGS);
   1.748 +	AI_SWAP4(pcHeader->IDENT);
   1.749 +	AI_SWAP4(pcHeader->NUM_FRAMES);
   1.750 +	AI_SWAP4(pcHeader->NUM_SKINS);
   1.751 +	AI_SWAP4(pcHeader->NUM_SURFACES);
   1.752 +	AI_SWAP4(pcHeader->NUM_TAGS);
   1.753 +	AI_SWAP4(pcHeader->OFS_EOF);
   1.754 +	AI_SWAP4(pcHeader->OFS_FRAMES);
   1.755 +	AI_SWAP4(pcHeader->OFS_SURFACES);
   1.756 +	AI_SWAP4(pcHeader->OFS_TAGS);
   1.757 +
   1.758 +#endif
   1.759 +
   1.760 +	// Validate the file header
   1.761 +	ValidateHeaderOffsets();
   1.762 +
   1.763 +	// Navigate to the list of surfaces
   1.764 +	BE_NCONST MD3::Surface* pcSurfaces = (BE_NCONST MD3::Surface*)(mBuffer + pcHeader->OFS_SURFACES);
   1.765 +
   1.766 +	// Navigate to the list of tags
   1.767 +	BE_NCONST MD3::Tag* pcTags = (BE_NCONST MD3::Tag*)(mBuffer + pcHeader->OFS_TAGS);
   1.768 +
   1.769 +	// Allocate output storage
   1.770 +	pScene->mNumMeshes = pcHeader->NUM_SURFACES;
   1.771 +	pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
   1.772 +
   1.773 +	pScene->mNumMaterials = pcHeader->NUM_SURFACES;
   1.774 +	pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
   1.775 +
   1.776 +	// Set arrays to zero to ensue proper destruction if an exception is raised
   1.777 +	::memset(pScene->mMeshes,0,pScene->mNumMeshes*sizeof(aiMesh*));
   1.778 +	::memset(pScene->mMaterials,0,pScene->mNumMaterials*sizeof(aiMaterial*));
   1.779 +
   1.780 +	// Now read possible skins from .skin file
   1.781 +	Q3Shader::SkinData skins;
   1.782 +	ReadSkin(skins);
   1.783 +
   1.784 +	// And check whether we can locate a shader file for this model
   1.785 +	Q3Shader::ShaderData shaders;
   1.786 +	ReadShader(shaders);
   1.787 +
   1.788 +	// Adjust all texture paths in the shader
   1.789 +	const char* header_name = pcHeader->NAME;
   1.790 +	if (shaders.blocks.size()) {
   1.791 +		for (std::list< Q3Shader::ShaderDataBlock >::iterator dit = shaders.blocks.begin(); dit != shaders.blocks.end(); ++dit) {
   1.792 +			ConvertPath((*dit).name.c_str(),header_name,(*dit).name);
   1.793 +
   1.794 +			for (std::list< Q3Shader::ShaderMapBlock >::iterator mit = (*dit).maps.begin(); mit != (*dit).maps.end(); ++mit) {
   1.795 +				ConvertPath((*mit).name.c_str(),header_name,(*mit).name);
   1.796 +			}
   1.797 +		}
   1.798 +	}
   1.799 +
   1.800 +	// Read all surfaces from the file
   1.801 +	unsigned int iNum = pcHeader->NUM_SURFACES;
   1.802 +	unsigned int iNumMaterials = 0;
   1.803 +	while (iNum-- > 0)	{
   1.804 +
   1.805 +		// Ensure correct endianess
   1.806 +#ifdef AI_BUILD_BIG_ENDIAN
   1.807 +
   1.808 +		AI_SWAP4(pcSurfaces->FLAGS);
   1.809 +		AI_SWAP4(pcSurfaces->IDENT);
   1.810 +		AI_SWAP4(pcSurfaces->NUM_FRAMES);
   1.811 +		AI_SWAP4(pcSurfaces->NUM_SHADER);
   1.812 +		AI_SWAP4(pcSurfaces->NUM_TRIANGLES);
   1.813 +		AI_SWAP4(pcSurfaces->NUM_VERTICES);
   1.814 +		AI_SWAP4(pcSurfaces->OFS_END);
   1.815 +		AI_SWAP4(pcSurfaces->OFS_SHADERS);
   1.816 +		AI_SWAP4(pcSurfaces->OFS_ST);
   1.817 +		AI_SWAP4(pcSurfaces->OFS_TRIANGLES);
   1.818 +		AI_SWAP4(pcSurfaces->OFS_XYZNORMAL);
   1.819 +
   1.820 +#endif
   1.821 +
   1.822 +		// Validate the surface header
   1.823 +		ValidateSurfaceHeaderOffsets(pcSurfaces);
   1.824 +
   1.825 +		// Navigate to the vertex list of the surface
   1.826 +		BE_NCONST MD3::Vertex* pcVertices = (BE_NCONST MD3::Vertex*)
   1.827 +			(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL);
   1.828 +
   1.829 +		// Navigate to the triangle list of the surface
   1.830 +		BE_NCONST MD3::Triangle* pcTriangles = (BE_NCONST MD3::Triangle*)
   1.831 +			(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_TRIANGLES);
   1.832 +
   1.833 +		// Navigate to the texture coordinate list of the surface
   1.834 +		BE_NCONST MD3::TexCoord* pcUVs = (BE_NCONST MD3::TexCoord*)
   1.835 +			(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_ST);
   1.836 +
   1.837 +		// Navigate to the shader list of the surface
   1.838 +		BE_NCONST MD3::Shader* pcShaders = (BE_NCONST MD3::Shader*)
   1.839 +			(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_SHADERS);
   1.840 +
   1.841 +		// If the submesh is empty ignore it
   1.842 +		if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES)
   1.843 +		{
   1.844 +			pcSurfaces = (BE_NCONST MD3::Surface*)(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_END);
   1.845 +			pScene->mNumMeshes--;
   1.846 +			continue;
   1.847 +		}
   1.848 +
   1.849 +		// Allocate output mesh
   1.850 +		pScene->mMeshes[iNum] = new aiMesh();
   1.851 +		aiMesh* pcMesh = pScene->mMeshes[iNum];
   1.852 +
   1.853 +		std::string _texture_name;
   1.854 +		const char* texture_name = NULL;
   1.855 +
   1.856 +		// Check whether we have a texture record for this surface in the .skin file
   1.857 +		std::list< Q3Shader::SkinData::TextureEntry >::iterator it = std::find( 
   1.858 +			skins.textures.begin(), skins.textures.end(), pcSurfaces->NAME );
   1.859 +
   1.860 +		if (it != skins.textures.end()) {
   1.861 +			texture_name = &*( _texture_name = (*it).second).begin();
   1.862 +			DefaultLogger::get()->debug("MD3: Assigning skin texture " + (*it).second + " to surface " + pcSurfaces->NAME);
   1.863 +			(*it).resolved = true; // mark entry as resolved
   1.864 +		}
   1.865 +
   1.866 +		// Get the first shader (= texture?) assigned to the surface
   1.867 +		if (!texture_name && pcSurfaces->NUM_SHADER)	{
   1.868 +			texture_name = pcShaders->NAME;
   1.869 +		}
   1.870 +
   1.871 +		std::string convertedPath;
   1.872 +		if (texture_name) {
   1.873 +			ConvertPath(texture_name,header_name,convertedPath);
   1.874 +		}
   1.875 +
   1.876 +		const Q3Shader::ShaderDataBlock* shader = NULL;
   1.877 +
   1.878 +		// Now search the current shader for a record with this name (
   1.879 +		// excluding texture file extension)
   1.880 +		if (shaders.blocks.size()) {
   1.881 +
   1.882 +			std::string::size_type s = convertedPath.find_last_of('.');
   1.883 +			if (s == std::string::npos)
   1.884 +				s = convertedPath.length();
   1.885 +
   1.886 +			const std::string without_ext = convertedPath.substr(0,s);
   1.887 +			std::list< Q3Shader::ShaderDataBlock >::const_iterator dit = std::find(shaders.blocks.begin(),shaders.blocks.end(),without_ext);
   1.888 +			if (dit != shaders.blocks.end()) {
   1.889 +				// Hurra, wir haben einen. Tolle Sache.
   1.890 +				shader = &*dit;
   1.891 +				DefaultLogger::get()->info("Found shader record for " +without_ext );
   1.892 +			}
   1.893 +			else DefaultLogger::get()->warn("Unable to find shader record for " +without_ext );
   1.894 +		}
   1.895 +
   1.896 +		aiMaterial* pcHelper = new aiMaterial();
   1.897 +
   1.898 +		const int iMode = (int)aiShadingMode_Gouraud;
   1.899 +		pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
   1.900 +
   1.901 +		// Add a small ambient color value - Quake 3 seems to have one
   1.902 +		aiColor3D clr;
   1.903 +		clr.b = clr.g = clr.r = 0.05f;
   1.904 +		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
   1.905 +
   1.906 +		clr.b = clr.g = clr.r = 1.0f;
   1.907 +		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
   1.908 +		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
   1.909 +
   1.910 +		// use surface name + skin_name as material name
   1.911 +		aiString name;
   1.912 +		name.Set("MD3_[" + configSkinFile + "][" + pcSurfaces->NAME + "]");
   1.913 +		pcHelper->AddProperty(&name,AI_MATKEY_NAME);
   1.914 +
   1.915 +		if (!shader) {
   1.916 +			// Setup dummy texture file name to ensure UV coordinates are kept during postprocessing
   1.917 +			aiString szString;
   1.918 +			if (convertedPath.length())	{
   1.919 +				szString.Set(convertedPath);
   1.920 +			}
   1.921 +			else	{
   1.922 +				DefaultLogger::get()->warn("Texture file name has zero length. Using default name");
   1.923 +				szString.Set("dummy_texture.bmp");
   1.924 +			}
   1.925 +			pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
   1.926 +
   1.927 +			// prevent transparency by default
   1.928 +			int no_alpha = aiTextureFlags_IgnoreAlpha;
   1.929 +			pcHelper->AddProperty(&no_alpha,1,AI_MATKEY_TEXFLAGS_DIFFUSE(0));
   1.930 +		}
   1.931 +		else {
   1.932 +			Q3Shader::ConvertShaderToMaterial(pcHelper,*shader);
   1.933 +		}
   1.934 +
   1.935 +		pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
   1.936 +		pcMesh->mMaterialIndex = iNumMaterials++;
   1.937 +
   1.938 +			// Ensure correct endianess
   1.939 +#ifdef AI_BUILD_BIG_ENDIAN
   1.940 +
   1.941 +		for (uint32_t i = 0; i < pcSurfaces->NUM_VERTICES;++i)	{
   1.942 +			AI_SWAP2( pcVertices[i].NORMAL );
   1.943 +			AI_SWAP2( pcVertices[i].X );
   1.944 +			AI_SWAP2( pcVertices[i].Y );
   1.945 +			AI_SWAP2( pcVertices[i].Z );
   1.946 +
   1.947 +			AI_SWAP4( pcUVs[i].U );
   1.948 +			AI_SWAP4( pcUVs[i].U );
   1.949 +		}
   1.950 +		for (uint32_t i = 0; i < pcSurfaces->NUM_TRIANGLES;++i)	{
   1.951 +			AI_SWAP4(pcTriangles[i].INDEXES[0]);
   1.952 +			AI_SWAP4(pcTriangles[i].INDEXES[1]);
   1.953 +			AI_SWAP4(pcTriangles[i].INDEXES[2]);
   1.954 +		}
   1.955 +
   1.956 +#endif
   1.957 +
   1.958 +		// Fill mesh information
   1.959 +		pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
   1.960 +
   1.961 +		pcMesh->mNumVertices		= pcSurfaces->NUM_TRIANGLES*3;
   1.962 +		pcMesh->mNumFaces			= pcSurfaces->NUM_TRIANGLES;
   1.963 +		pcMesh->mFaces				= new aiFace[pcSurfaces->NUM_TRIANGLES];
   1.964 +		pcMesh->mNormals			= new aiVector3D[pcMesh->mNumVertices];
   1.965 +		pcMesh->mVertices			= new aiVector3D[pcMesh->mNumVertices];
   1.966 +		pcMesh->mTextureCoords[0]	= new aiVector3D[pcMesh->mNumVertices];
   1.967 +		pcMesh->mNumUVComponents[0] = 2;
   1.968 +
   1.969 +		// Fill in all triangles
   1.970 +		unsigned int iCurrent = 0;
   1.971 +		for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_TRIANGLES;++i)	{
   1.972 +			pcMesh->mFaces[i].mIndices = new unsigned int[3];
   1.973 +			pcMesh->mFaces[i].mNumIndices = 3;
   1.974 +
   1.975 +			//unsigned int iTemp = iCurrent;
   1.976 +			for (unsigned int c = 0; c < 3;++c,++iCurrent)	{
   1.977 +				pcMesh->mFaces[i].mIndices[c] = iCurrent;
   1.978 +
   1.979 +				// Read vertices
   1.980 +				aiVector3D& vec = pcMesh->mVertices[iCurrent];
   1.981 +				vec.x = pcVertices[ pcTriangles->INDEXES[c]].X*AI_MD3_XYZ_SCALE;
   1.982 +				vec.y = pcVertices[ pcTriangles->INDEXES[c]].Y*AI_MD3_XYZ_SCALE;
   1.983 +				vec.z = pcVertices[ pcTriangles->INDEXES[c]].Z*AI_MD3_XYZ_SCALE;
   1.984 +
   1.985 +				// Convert the normal vector to uncompressed float3 format
   1.986 +				aiVector3D& nor = pcMesh->mNormals[iCurrent];
   1.987 +				LatLngNormalToVec3(pcVertices[pcTriangles->INDEXES[c]].NORMAL,(float*)&nor);
   1.988 +
   1.989 +				// Read texture coordinates
   1.990 +				pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U;
   1.991 +				pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V;
   1.992 +			}
   1.993 +			// Flip face order if necessary
   1.994 +			if (!shader || shader->cull == Q3Shader::CULL_CW) {
   1.995 +				std::swap(pcMesh->mFaces[i].mIndices[2],pcMesh->mFaces[i].mIndices[1]);
   1.996 +			}
   1.997 +			pcTriangles++;
   1.998 +		}
   1.999 +	
  1.1000 +		// Go to the next surface
  1.1001 +		pcSurfaces = (BE_NCONST MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
  1.1002 +	}
  1.1003 +
  1.1004 +	// For debugging purposes: check whether we found matches for all entries in the skins file
  1.1005 +	if (!DefaultLogger::isNullLogger()) {
  1.1006 +		for (std::list< Q3Shader::SkinData::TextureEntry>::const_iterator it = skins.textures.begin();it != skins.textures.end(); ++it) {
  1.1007 +			if (!(*it).resolved) {
  1.1008 +				DefaultLogger::get()->error("MD3: Failed to match skin " + (*it).first + " to surface " + (*it).second);
  1.1009 +			}
  1.1010 +		}
  1.1011 +	}
  1.1012 +
  1.1013 +	if (!pScene->mNumMeshes)
  1.1014 +		throw DeadlyImportError( "MD3: File contains no valid mesh");
  1.1015 +	pScene->mNumMaterials = iNumMaterials;
  1.1016 +
  1.1017 +	// Now we need to generate an empty node graph
  1.1018 +	pScene->mRootNode = new aiNode("<MD3Root>");
  1.1019 +	pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
  1.1020 +	pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
  1.1021 +
  1.1022 +	// Attach tiny children for all tags
  1.1023 +	if (pcHeader->NUM_TAGS) {
  1.1024 +		pScene->mRootNode->mNumChildren = pcHeader->NUM_TAGS;
  1.1025 +		pScene->mRootNode->mChildren = new aiNode*[pcHeader->NUM_TAGS];
  1.1026 +
  1.1027 +		for (unsigned int i = 0; i < pcHeader->NUM_TAGS; ++i, ++pcTags) {
  1.1028 +
  1.1029 +			aiNode* nd = pScene->mRootNode->mChildren[i] = new aiNode();
  1.1030 +			nd->mName.Set((const char*)pcTags->NAME);
  1.1031 +			nd->mParent = pScene->mRootNode;
  1.1032 +
  1.1033 +			AI_SWAP4(pcTags->origin.x);
  1.1034 +			AI_SWAP4(pcTags->origin.y);
  1.1035 +			AI_SWAP4(pcTags->origin.z);
  1.1036 +
  1.1037 +			// Copy local origin, again flip z,y
  1.1038 +			nd->mTransformation.a4 = pcTags->origin.x;
  1.1039 +			nd->mTransformation.b4 = pcTags->origin.y;
  1.1040 +			nd->mTransformation.c4 = pcTags->origin.z;
  1.1041 +
  1.1042 +			// Copy rest of transformation (need to transpose to match row-order matrix)
  1.1043 +			for (unsigned int a = 0; a < 3;++a) {
  1.1044 +				for (unsigned int m = 0; m < 3;++m) {
  1.1045 +					nd->mTransformation[m][a] = pcTags->orientation[a][m];
  1.1046 +					AI_SWAP4(nd->mTransformation[m][a]);
  1.1047 +				}
  1.1048 +			}
  1.1049 +		}
  1.1050 +	}
  1.1051 +
  1.1052 +	for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
  1.1053 +		pScene->mRootNode->mMeshes[i] = i;
  1.1054 +
  1.1055 +	// Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
  1.1056 +	pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
  1.1057 +		0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
  1.1058 +}
  1.1059 +
  1.1060 +#endif // !! ASSIMP_BUILD_NO_MD3_IMPORTER