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