vrshoot

annotate 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
rev   line source
nuclear@0 1 /*
nuclear@0 2 ---------------------------------------------------------------------------
nuclear@0 3 Open Asset Import Library (assimp)
nuclear@0 4 ---------------------------------------------------------------------------
nuclear@0 5
nuclear@0 6 Copyright (c) 2006-2012, assimp team
nuclear@0 7
nuclear@0 8 All rights reserved.
nuclear@0 9
nuclear@0 10 Redistribution and use of this software in source and binary forms,
nuclear@0 11 with or without modification, are permitted provided that the following
nuclear@0 12 conditions are met:
nuclear@0 13
nuclear@0 14 * Redistributions of source code must retain the above
nuclear@0 15 copyright notice, this list of conditions and the
nuclear@0 16 following disclaimer.
nuclear@0 17
nuclear@0 18 * Redistributions in binary form must reproduce the above
nuclear@0 19 copyright notice, this list of conditions and the
nuclear@0 20 following disclaimer in the documentation and/or other
nuclear@0 21 materials provided with the distribution.
nuclear@0 22
nuclear@0 23 * Neither the name of the assimp team, nor the names of its
nuclear@0 24 contributors may be used to endorse or promote products
nuclear@0 25 derived from this software without specific prior
nuclear@0 26 written permission of the assimp team.
nuclear@0 27
nuclear@0 28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
nuclear@0 29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
nuclear@0 30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
nuclear@0 31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
nuclear@0 32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
nuclear@0 33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
nuclear@0 34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
nuclear@0 35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
nuclear@0 36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
nuclear@0 37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
nuclear@0 38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nuclear@0 39 ---------------------------------------------------------------------------
nuclear@0 40 */
nuclear@0 41
nuclear@0 42 /** @file MD3Loader.cpp
nuclear@0 43 * @brief Implementation of the MD3 importer class
nuclear@0 44 *
nuclear@0 45 * Sources:
nuclear@0 46 * http://www.gamers.org/dEngine/quake3/UQ3S
nuclear@0 47 * http://linux.ucla.edu/~phaethon/q3/formats/md3format.html
nuclear@0 48 * http://www.heppler.com/shader/shader/
nuclear@0 49 */
nuclear@0 50
nuclear@0 51 #include "AssimpPCH.h"
nuclear@0 52 #ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
nuclear@0 53
nuclear@0 54 #include "MD3Loader.h"
nuclear@0 55 #include "ByteSwap.h"
nuclear@0 56 #include "SceneCombiner.h"
nuclear@0 57 #include "GenericProperty.h"
nuclear@0 58 #include "RemoveComments.h"
nuclear@0 59 #include "ParsingUtils.h"
nuclear@0 60 #include "Importer.h"
nuclear@0 61
nuclear@0 62 using namespace Assimp;
nuclear@0 63
nuclear@0 64 static const aiImporterDesc desc = {
nuclear@0 65 "Quake III Mesh Importer",
nuclear@0 66 "",
nuclear@0 67 "",
nuclear@0 68 "",
nuclear@0 69 aiImporterFlags_SupportBinaryFlavour,
nuclear@0 70 0,
nuclear@0 71 0,
nuclear@0 72 0,
nuclear@0 73 0,
nuclear@0 74 "md3"
nuclear@0 75 };
nuclear@0 76
nuclear@0 77 // ------------------------------------------------------------------------------------------------
nuclear@0 78 // Convert a Q3 shader blend function to the appropriate enum value
nuclear@0 79 Q3Shader::BlendFunc StringToBlendFunc(const std::string& m)
nuclear@0 80 {
nuclear@0 81 if (m == "GL_ONE") {
nuclear@0 82 return Q3Shader::BLEND_GL_ONE;
nuclear@0 83 }
nuclear@0 84 if (m == "GL_ZERO") {
nuclear@0 85 return Q3Shader::BLEND_GL_ZERO;
nuclear@0 86 }
nuclear@0 87 if (m == "GL_SRC_ALPHA") {
nuclear@0 88 return Q3Shader::BLEND_GL_SRC_ALPHA;
nuclear@0 89 }
nuclear@0 90 if (m == "GL_ONE_MINUS_SRC_ALPHA") {
nuclear@0 91 return Q3Shader::BLEND_GL_ONE_MINUS_SRC_ALPHA;
nuclear@0 92 }
nuclear@0 93 if (m == "GL_ONE_MINUS_DST_COLOR") {
nuclear@0 94 return Q3Shader::BLEND_GL_ONE_MINUS_DST_COLOR;
nuclear@0 95 }
nuclear@0 96 DefaultLogger::get()->error("Q3Shader: Unknown blend function: " + m);
nuclear@0 97 return Q3Shader::BLEND_NONE;
nuclear@0 98 }
nuclear@0 99
nuclear@0 100 // ------------------------------------------------------------------------------------------------
nuclear@0 101 // Load a Quake 3 shader
nuclear@0 102 bool Q3Shader::LoadShader(ShaderData& fill, const std::string& pFile,IOSystem* io)
nuclear@0 103 {
nuclear@0 104 boost::scoped_ptr<IOStream> file( io->Open( pFile, "rt"));
nuclear@0 105 if (!file.get())
nuclear@0 106 return false; // if we can't access the file, don't worry and return
nuclear@0 107
nuclear@0 108 DefaultLogger::get()->info("Loading Quake3 shader file " + pFile);
nuclear@0 109
nuclear@0 110 // read file in memory
nuclear@0 111 const size_t s = file->FileSize();
nuclear@0 112 std::vector<char> _buff(s+1);
nuclear@0 113 file->Read(&_buff[0],s,1);
nuclear@0 114 _buff[s] = 0;
nuclear@0 115
nuclear@0 116 // remove comments from it (C++ style)
nuclear@0 117 CommentRemover::RemoveLineComments("//",&_buff[0]);
nuclear@0 118 const char* buff = &_buff[0];
nuclear@0 119
nuclear@0 120 Q3Shader::ShaderDataBlock* curData = NULL;
nuclear@0 121 Q3Shader::ShaderMapBlock* curMap = NULL;
nuclear@0 122
nuclear@0 123 // read line per line
nuclear@0 124 for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) {
nuclear@0 125
nuclear@0 126 if (*buff == '{') {
nuclear@0 127 ++buff;
nuclear@0 128
nuclear@0 129 // append to last section, if any
nuclear@0 130 if (!curData) {
nuclear@0 131 DefaultLogger::get()->error("Q3Shader: Unexpected shader section token \'{\'");
nuclear@0 132 return true; // still no failure, the file is there
nuclear@0 133 }
nuclear@0 134
nuclear@0 135 // read this data section
nuclear@0 136 for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) {
nuclear@0 137 if (*buff == '{') {
nuclear@0 138 ++buff;
nuclear@0 139 // add new map section
nuclear@0 140 curData->maps.push_back(Q3Shader::ShaderMapBlock());
nuclear@0 141 curMap = &curData->maps.back();
nuclear@0 142
nuclear@0 143 for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) {
nuclear@0 144 // 'map' - Specifies texture file name
nuclear@0 145 if (TokenMatchI(buff,"map",3) || TokenMatchI(buff,"clampmap",8)) {
nuclear@0 146 curMap->name = GetNextToken(buff);
nuclear@0 147 }
nuclear@0 148 // 'blendfunc' - Alpha blending mode
nuclear@0 149 else if (TokenMatchI(buff,"blendfunc",9)) {
nuclear@0 150 const std::string blend_src = GetNextToken(buff);
nuclear@0 151 if (blend_src == "add") {
nuclear@0 152 curMap->blend_src = Q3Shader::BLEND_GL_ONE;
nuclear@0 153 curMap->blend_dest = Q3Shader::BLEND_GL_ONE;
nuclear@0 154 }
nuclear@0 155 else if (blend_src == "filter") {
nuclear@0 156 curMap->blend_src = Q3Shader::BLEND_GL_DST_COLOR;
nuclear@0 157 curMap->blend_dest = Q3Shader::BLEND_GL_ZERO;
nuclear@0 158 }
nuclear@0 159 else if (blend_src == "blend") {
nuclear@0 160 curMap->blend_src = Q3Shader::BLEND_GL_SRC_ALPHA;
nuclear@0 161 curMap->blend_dest = Q3Shader::BLEND_GL_ONE_MINUS_SRC_ALPHA;
nuclear@0 162 }
nuclear@0 163 else {
nuclear@0 164 curMap->blend_src = StringToBlendFunc(blend_src);
nuclear@0 165 curMap->blend_dest = StringToBlendFunc(GetNextToken(buff));
nuclear@0 166 }
nuclear@0 167 }
nuclear@0 168 // 'alphafunc' - Alpha testing mode
nuclear@0 169 else if (TokenMatchI(buff,"alphafunc",9)) {
nuclear@0 170 const std::string at = GetNextToken(buff);
nuclear@0 171 if (at == "GT0") {
nuclear@0 172 curMap->alpha_test = Q3Shader::AT_GT0;
nuclear@0 173 }
nuclear@0 174 else if (at == "LT128") {
nuclear@0 175 curMap->alpha_test = Q3Shader::AT_LT128;
nuclear@0 176 }
nuclear@0 177 else if (at == "GE128") {
nuclear@0 178 curMap->alpha_test = Q3Shader::AT_GE128;
nuclear@0 179 }
nuclear@0 180 }
nuclear@0 181 else if (*buff == '}') {
nuclear@0 182 ++buff;
nuclear@0 183 // close this map section
nuclear@0 184 curMap = NULL;
nuclear@0 185 break;
nuclear@0 186 }
nuclear@0 187 }
nuclear@0 188
nuclear@0 189 }
nuclear@0 190 else if (*buff == '}') {
nuclear@0 191 ++buff;
nuclear@0 192 curData = NULL;
nuclear@0 193 break;
nuclear@0 194 }
nuclear@0 195
nuclear@0 196 // 'cull' specifies culling behaviour for the model
nuclear@0 197 else if (TokenMatchI(buff,"cull",4)) {
nuclear@0 198 SkipSpaces(&buff);
nuclear@0 199 if (!ASSIMP_strincmp(buff,"back",4)) {
nuclear@0 200 curData->cull = Q3Shader::CULL_CCW;
nuclear@0 201 }
nuclear@0 202 else if (!ASSIMP_strincmp(buff,"front",5)) {
nuclear@0 203 curData->cull = Q3Shader::CULL_CW;
nuclear@0 204 }
nuclear@0 205 else if (!ASSIMP_strincmp(buff,"none",4) || !ASSIMP_strincmp(buff,"disable",7)) {
nuclear@0 206 curData->cull = Q3Shader::CULL_NONE;
nuclear@0 207 }
nuclear@0 208 else DefaultLogger::get()->error("Q3Shader: Unrecognized cull mode");
nuclear@0 209 }
nuclear@0 210 }
nuclear@0 211 }
nuclear@0 212
nuclear@0 213 else {
nuclear@0 214 // add new section
nuclear@0 215 fill.blocks.push_back(Q3Shader::ShaderDataBlock());
nuclear@0 216 curData = &fill.blocks.back();
nuclear@0 217
nuclear@0 218 // get the name of this section
nuclear@0 219 curData->name = GetNextToken(buff);
nuclear@0 220 }
nuclear@0 221 }
nuclear@0 222 return true;
nuclear@0 223 }
nuclear@0 224
nuclear@0 225 // ------------------------------------------------------------------------------------------------
nuclear@0 226 // Load a Quake 3 skin
nuclear@0 227 bool Q3Shader::LoadSkin(SkinData& fill, const std::string& pFile,IOSystem* io)
nuclear@0 228 {
nuclear@0 229 boost::scoped_ptr<IOStream> file( io->Open( pFile, "rt"));
nuclear@0 230 if (!file.get())
nuclear@0 231 return false; // if we can't access the file, don't worry and return
nuclear@0 232
nuclear@0 233 DefaultLogger::get()->info("Loading Quake3 skin file " + pFile);
nuclear@0 234
nuclear@0 235 // read file in memory
nuclear@0 236 const size_t s = file->FileSize();
nuclear@0 237 std::vector<char> _buff(s+1);const char* buff = &_buff[0];
nuclear@0 238 file->Read(&_buff[0],s,1);
nuclear@0 239 _buff[s] = 0;
nuclear@0 240
nuclear@0 241 // remove commas
nuclear@0 242 std::replace(_buff.begin(),_buff.end(),',',' ');
nuclear@0 243
nuclear@0 244 // read token by token and fill output table
nuclear@0 245 for (;*buff;) {
nuclear@0 246 SkipSpacesAndLineEnd(&buff);
nuclear@0 247
nuclear@0 248 // get first identifier
nuclear@0 249 std::string ss = GetNextToken(buff);
nuclear@0 250
nuclear@0 251 // ignore tokens starting with tag_
nuclear@0 252 if (!::strncmp(&ss[0],"tag_",std::min((size_t)4, ss.length())))
nuclear@0 253 continue;
nuclear@0 254
nuclear@0 255 fill.textures.push_back(SkinData::TextureEntry());
nuclear@0 256 SkinData::TextureEntry& s = fill.textures.back();
nuclear@0 257
nuclear@0 258 s.first = ss;
nuclear@0 259 s.second = GetNextToken(buff);
nuclear@0 260 }
nuclear@0 261 return true;
nuclear@0 262 }
nuclear@0 263
nuclear@0 264 // ------------------------------------------------------------------------------------------------
nuclear@0 265 // Convert Q3Shader to material
nuclear@0 266 void Q3Shader::ConvertShaderToMaterial(aiMaterial* out, const ShaderDataBlock& shader)
nuclear@0 267 {
nuclear@0 268 ai_assert(NULL != out);
nuclear@0 269
nuclear@0 270 /* IMPORTANT: This is not a real conversion. Actually we're just guessing and
nuclear@0 271 * hacking around to build an aiMaterial that looks nearly equal to the
nuclear@0 272 * original Quake 3 shader. We're missing some important features like
nuclear@0 273 * animatable material properties in our material system, but at least
nuclear@0 274 * multiple textures should be handled correctly.
nuclear@0 275 */
nuclear@0 276
nuclear@0 277 // Two-sided material?
nuclear@0 278 if (shader.cull == Q3Shader::CULL_NONE) {
nuclear@0 279 const int twosided = 1;
nuclear@0 280 out->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED);
nuclear@0 281 }
nuclear@0 282
nuclear@0 283 unsigned int cur_emissive = 0, cur_diffuse = 0, cur_lm =0;
nuclear@0 284
nuclear@0 285 // Iterate through all textures
nuclear@0 286 for (std::list< Q3Shader::ShaderMapBlock >::const_iterator it = shader.maps.begin(); it != shader.maps.end();++it) {
nuclear@0 287
nuclear@0 288 // CONVERSION BEHAVIOUR:
nuclear@0 289 //
nuclear@0 290 //
nuclear@0 291 // If the texture is additive
nuclear@0 292 // - if it is the first texture, assume additive blending for the whole material
nuclear@0 293 // - otherwise register it as emissive texture.
nuclear@0 294 //
nuclear@0 295 // If the texture is using standard blend (or if the blend mode is unknown)
nuclear@0 296 // - if first texture: assume default blending for material
nuclear@0 297 // - in any case: set it as diffuse texture
nuclear@0 298 //
nuclear@0 299 // If the texture is using 'filter' blending
nuclear@0 300 // - take as lightmap
nuclear@0 301 //
nuclear@0 302 // Textures with alpha funcs
nuclear@0 303 // - aiTextureFlags_UseAlpha is set (otherwise aiTextureFlags_NoAlpha is explicitly set)
nuclear@0 304 aiString s((*it).name);
nuclear@0 305 aiTextureType type; unsigned int index;
nuclear@0 306
nuclear@0 307 if ((*it).blend_src == Q3Shader::BLEND_GL_ONE && (*it).blend_dest == Q3Shader::BLEND_GL_ONE) {
nuclear@0 308 if (it == shader.maps.begin()) {
nuclear@0 309 const int additive = aiBlendMode_Additive;
nuclear@0 310 out->AddProperty(&additive,1,AI_MATKEY_BLEND_FUNC);
nuclear@0 311
nuclear@0 312 index = cur_diffuse++;
nuclear@0 313 type = aiTextureType_DIFFUSE;
nuclear@0 314 }
nuclear@0 315 else {
nuclear@0 316 index = cur_emissive++;
nuclear@0 317 type = aiTextureType_EMISSIVE;
nuclear@0 318 }
nuclear@0 319 }
nuclear@0 320 else if ((*it).blend_src == Q3Shader::BLEND_GL_DST_COLOR && (*it).blend_dest == Q3Shader::BLEND_GL_ZERO) {
nuclear@0 321 index = cur_lm++;
nuclear@0 322 type = aiTextureType_LIGHTMAP;
nuclear@0 323 }
nuclear@0 324 else {
nuclear@0 325 const int blend = aiBlendMode_Default;
nuclear@0 326 out->AddProperty(&blend,1,AI_MATKEY_BLEND_FUNC);
nuclear@0 327
nuclear@0 328 index = cur_diffuse++;
nuclear@0 329 type = aiTextureType_DIFFUSE;
nuclear@0 330 }
nuclear@0 331
nuclear@0 332 // setup texture
nuclear@0 333 out->AddProperty(&s,AI_MATKEY_TEXTURE(type,index));
nuclear@0 334
nuclear@0 335 // setup texture flags
nuclear@0 336 const int use_alpha = ((*it).alpha_test != Q3Shader::AT_NONE ? aiTextureFlags_UseAlpha : aiTextureFlags_IgnoreAlpha);
nuclear@0 337 out->AddProperty(&use_alpha,1,AI_MATKEY_TEXFLAGS(type,index));
nuclear@0 338 }
nuclear@0 339 // If at least one emissive texture was set, set the emissive base color to 1 to ensure
nuclear@0 340 // the texture is actually displayed.
nuclear@0 341 if (0 != cur_emissive) {
nuclear@0 342 aiColor3D one(1.f,1.f,1.f);
nuclear@0 343 out->AddProperty(&one,1,AI_MATKEY_COLOR_EMISSIVE);
nuclear@0 344 }
nuclear@0 345 }
nuclear@0 346
nuclear@0 347 // ------------------------------------------------------------------------------------------------
nuclear@0 348 // Constructor to be privately used by Importer
nuclear@0 349 MD3Importer::MD3Importer()
nuclear@0 350 : configFrameID (0)
nuclear@0 351 , configHandleMP (true)
nuclear@0 352 {}
nuclear@0 353
nuclear@0 354 // ------------------------------------------------------------------------------------------------
nuclear@0 355 // Destructor, private as well
nuclear@0 356 MD3Importer::~MD3Importer()
nuclear@0 357 {}
nuclear@0 358
nuclear@0 359 // ------------------------------------------------------------------------------------------------
nuclear@0 360 // Returns whether the class can handle the format of the given file.
nuclear@0 361 bool MD3Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
nuclear@0 362 {
nuclear@0 363 const std::string extension = GetExtension(pFile);
nuclear@0 364 if (extension == "md3")
nuclear@0 365 return true;
nuclear@0 366
nuclear@0 367 // if check for extension is not enough, check for the magic tokens
nuclear@0 368 if (!extension.length() || checkSig) {
nuclear@0 369 uint32_t tokens[1];
nuclear@0 370 tokens[0] = AI_MD3_MAGIC_NUMBER_LE;
nuclear@0 371 return CheckMagicToken(pIOHandler,pFile,tokens,1);
nuclear@0 372 }
nuclear@0 373 return false;
nuclear@0 374 }
nuclear@0 375
nuclear@0 376 // ------------------------------------------------------------------------------------------------
nuclear@0 377 void MD3Importer::ValidateHeaderOffsets()
nuclear@0 378 {
nuclear@0 379 // Check magic number
nuclear@0 380 if (pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE &&
nuclear@0 381 pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE)
nuclear@0 382 throw DeadlyImportError( "Invalid MD3 file: Magic bytes not found");
nuclear@0 383
nuclear@0 384 // Check file format version
nuclear@0 385 if (pcHeader->VERSION > 15)
nuclear@0 386 DefaultLogger::get()->warn( "Unsupported MD3 file version. Continuing happily ...");
nuclear@0 387
nuclear@0 388 // Check some offset values whether they are valid
nuclear@0 389 if (!pcHeader->NUM_SURFACES)
nuclear@0 390 throw DeadlyImportError( "Invalid md3 file: NUM_SURFACES is 0");
nuclear@0 391
nuclear@0 392 if (pcHeader->OFS_FRAMES >= fileSize || pcHeader->OFS_SURFACES >= fileSize ||
nuclear@0 393 pcHeader->OFS_EOF > fileSize) {
nuclear@0 394 throw DeadlyImportError("Invalid MD3 header: some offsets are outside the file");
nuclear@0 395 }
nuclear@0 396
nuclear@0 397 if (pcHeader->NUM_FRAMES <= configFrameID )
nuclear@0 398 throw DeadlyImportError("The requested frame is not existing the file");
nuclear@0 399 }
nuclear@0 400
nuclear@0 401 // ------------------------------------------------------------------------------------------------
nuclear@0 402 void MD3Importer::ValidateSurfaceHeaderOffsets(const MD3::Surface* pcSurf)
nuclear@0 403 {
nuclear@0 404 // Calculate the relative offset of the surface
nuclear@0 405 const int32_t ofs = int32_t((const unsigned char*)pcSurf-this->mBuffer);
nuclear@0 406
nuclear@0 407 // Check whether all data chunks are inside the valid range
nuclear@0 408 if (pcSurf->OFS_TRIANGLES + ofs + pcSurf->NUM_TRIANGLES * sizeof(MD3::Triangle) > fileSize ||
nuclear@0 409 pcSurf->OFS_SHADERS + ofs + pcSurf->NUM_SHADER * sizeof(MD3::Shader) > fileSize ||
nuclear@0 410 pcSurf->OFS_ST + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::TexCoord) > fileSize ||
nuclear@0 411 pcSurf->OFS_XYZNORMAL + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::Vertex) > fileSize) {
nuclear@0 412
nuclear@0 413 throw DeadlyImportError("Invalid MD3 surface header: some offsets are outside the file");
nuclear@0 414 }
nuclear@0 415
nuclear@0 416 // Check whether all requirements for Q3 files are met. We don't
nuclear@0 417 // care, but probably someone does.
nuclear@0 418 if (pcSurf->NUM_TRIANGLES > AI_MD3_MAX_TRIANGLES) {
nuclear@0 419 DefaultLogger::get()->warn("MD3: Quake III triangle limit exceeded");
nuclear@0 420 }
nuclear@0 421
nuclear@0 422 if (pcSurf->NUM_SHADER > AI_MD3_MAX_SHADERS) {
nuclear@0 423 DefaultLogger::get()->warn("MD3: Quake III shader limit exceeded");
nuclear@0 424 }
nuclear@0 425
nuclear@0 426 if (pcSurf->NUM_VERTICES > AI_MD3_MAX_VERTS) {
nuclear@0 427 DefaultLogger::get()->warn("MD3: Quake III vertex limit exceeded");
nuclear@0 428 }
nuclear@0 429
nuclear@0 430 if (pcSurf->NUM_FRAMES > AI_MD3_MAX_FRAMES) {
nuclear@0 431 DefaultLogger::get()->warn("MD3: Quake III frame limit exceeded");
nuclear@0 432 }
nuclear@0 433 }
nuclear@0 434
nuclear@0 435 // ------------------------------------------------------------------------------------------------
nuclear@0 436 const aiImporterDesc* MD3Importer::GetInfo () const
nuclear@0 437 {
nuclear@0 438 return &desc;
nuclear@0 439 }
nuclear@0 440
nuclear@0 441 // ------------------------------------------------------------------------------------------------
nuclear@0 442 // Setup configuration properties
nuclear@0 443 void MD3Importer::SetupProperties(const Importer* pImp)
nuclear@0 444 {
nuclear@0 445 // The
nuclear@0 446 // AI_CONFIG_IMPORT_MD3_KEYFRAME option overrides the
nuclear@0 447 // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
nuclear@0 448 configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD3_KEYFRAME,-1);
nuclear@0 449 if(static_cast<unsigned int>(-1) == configFrameID) {
nuclear@0 450 configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
nuclear@0 451 }
nuclear@0 452
nuclear@0 453 // AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART
nuclear@0 454 configHandleMP = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART,1));
nuclear@0 455
nuclear@0 456 // AI_CONFIG_IMPORT_MD3_SKIN_NAME
nuclear@0 457 configSkinFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SKIN_NAME,"default"));
nuclear@0 458
nuclear@0 459 // AI_CONFIG_IMPORT_MD3_SHADER_SRC
nuclear@0 460 configShaderFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SHADER_SRC,""));
nuclear@0 461
nuclear@0 462 // AI_CONFIG_FAVOUR_SPEED
nuclear@0 463 configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
nuclear@0 464 }
nuclear@0 465
nuclear@0 466 // ------------------------------------------------------------------------------------------------
nuclear@0 467 // Try to read the skin for a MD3 file
nuclear@0 468 void MD3Importer::ReadSkin(Q3Shader::SkinData& fill) const
nuclear@0 469 {
nuclear@0 470 // skip any postfixes (e.g. lower_1.md3)
nuclear@0 471 std::string::size_type s = filename.find_last_of('_');
nuclear@0 472 if (s == std::string::npos) {
nuclear@0 473 s = filename.find_last_of('.');
nuclear@0 474 }
nuclear@0 475 ai_assert(s != std::string::npos);
nuclear@0 476
nuclear@0 477 const std::string skin_file = path + filename.substr(0,s) + "_" + configSkinFile + ".skin";
nuclear@0 478 Q3Shader::LoadSkin(fill,skin_file,mIOHandler);
nuclear@0 479 }
nuclear@0 480
nuclear@0 481 // ------------------------------------------------------------------------------------------------
nuclear@0 482 // Try to read the shader for a MD3 file
nuclear@0 483 void MD3Importer::ReadShader(Q3Shader::ShaderData& fill) const
nuclear@0 484 {
nuclear@0 485 // Determine Q3 model name from given path
nuclear@0 486 const std::string::size_type s = path.find_last_of("\\/",path.length()-2);
nuclear@0 487 const std::string model_file = path.substr(s+1,path.length()-(s+2));
nuclear@0 488
nuclear@0 489 // If no specific dir or file is given, use our default search behaviour
nuclear@0 490 if (!configShaderFile.length()) {
nuclear@0 491 if(!Q3Shader::LoadShader(fill,path + "..\\..\\..\\scripts\\" + model_file + ".shader",mIOHandler)) {
nuclear@0 492 Q3Shader::LoadShader(fill,path + "..\\..\\..\\scripts\\" + filename + ".shader",mIOHandler);
nuclear@0 493 }
nuclear@0 494 }
nuclear@0 495 else {
nuclear@0 496 // If the given string specifies a file, load this file.
nuclear@0 497 // Otherwise it's a directory.
nuclear@0 498 const std::string::size_type st = configShaderFile.find_last_of('.');
nuclear@0 499 if (st == std::string::npos) {
nuclear@0 500
nuclear@0 501 if(!Q3Shader::LoadShader(fill,configShaderFile + model_file + ".shader",mIOHandler)) {
nuclear@0 502 Q3Shader::LoadShader(fill,configShaderFile + filename + ".shader",mIOHandler);
nuclear@0 503 }
nuclear@0 504 }
nuclear@0 505 else {
nuclear@0 506 Q3Shader::LoadShader(fill,configShaderFile,mIOHandler);
nuclear@0 507 }
nuclear@0 508 }
nuclear@0 509 }
nuclear@0 510
nuclear@0 511 // ------------------------------------------------------------------------------------------------
nuclear@0 512 // Tiny helper to remove a single node from its parent' list
nuclear@0 513 void RemoveSingleNodeFromList(aiNode* nd)
nuclear@0 514 {
nuclear@0 515 if (!nd || nd->mNumChildren || !nd->mParent)return;
nuclear@0 516 aiNode* par = nd->mParent;
nuclear@0 517 for (unsigned int i = 0; i < par->mNumChildren;++i) {
nuclear@0 518 if (par->mChildren[i] == nd) {
nuclear@0 519 --par->mNumChildren;
nuclear@0 520 for (;i < par->mNumChildren;++i) {
nuclear@0 521 par->mChildren[i] = par->mChildren[i+1];
nuclear@0 522 }
nuclear@0 523 delete nd;
nuclear@0 524 break;
nuclear@0 525 }
nuclear@0 526 }
nuclear@0 527 }
nuclear@0 528
nuclear@0 529 // ------------------------------------------------------------------------------------------------
nuclear@0 530 // Read a multi-part Q3 player model
nuclear@0 531 bool MD3Importer::ReadMultipartFile()
nuclear@0 532 {
nuclear@0 533 // check whether the file name contains a common postfix, e.g lower_2.md3
nuclear@0 534 std::string::size_type s = filename.find_last_of('_'), t = filename.find_last_of('.');
nuclear@0 535 ai_assert(t != std::string::npos);
nuclear@0 536 if (s == std::string::npos)
nuclear@0 537 s = t;
nuclear@0 538
nuclear@0 539 const std::string mod_filename = filename.substr(0,s);
nuclear@0 540 const std::string suffix = filename.substr(s,t-s);
nuclear@0 541
nuclear@0 542 if (mod_filename == "lower" || mod_filename == "upper" || mod_filename == "head"){
nuclear@0 543 const std::string lower = path + "lower" + suffix + ".md3";
nuclear@0 544 const std::string upper = path + "upper" + suffix + ".md3";
nuclear@0 545 const std::string head = path + "head" + suffix + ".md3";
nuclear@0 546
nuclear@0 547 aiScene* scene_upper = NULL;
nuclear@0 548 aiScene* scene_lower = NULL;
nuclear@0 549 aiScene* scene_head = NULL;
nuclear@0 550 std::string failure;
nuclear@0 551
nuclear@0 552 aiNode* tag_torso, *tag_head;
nuclear@0 553 std::vector<AttachmentInfo> attach;
nuclear@0 554
nuclear@0 555 DefaultLogger::get()->info("Multi part MD3 player model: lower, upper and head parts are joined");
nuclear@0 556
nuclear@0 557 // ensure we won't try to load ourselves recursively
nuclear@0 558 BatchLoader::PropertyMap props;
nuclear@0 559 SetGenericProperty( props.ints, AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART, 0, NULL);
nuclear@0 560
nuclear@0 561 // now read these three files
nuclear@0 562 BatchLoader batch(mIOHandler);
nuclear@0 563 const unsigned int _lower = batch.AddLoadRequest(lower,0,&props);
nuclear@0 564 const unsigned int _upper = batch.AddLoadRequest(upper,0,&props);
nuclear@0 565 const unsigned int _head = batch.AddLoadRequest(head,0,&props);
nuclear@0 566 batch.LoadAll();
nuclear@0 567
nuclear@0 568 // now construct a dummy scene to place these three parts in
nuclear@0 569 aiScene* master = new aiScene();
nuclear@0 570 aiNode* nd = master->mRootNode = new aiNode();
nuclear@0 571 nd->mName.Set("<MD3_Player>");
nuclear@0 572
nuclear@0 573 // ... and get them. We need all of them.
nuclear@0 574 scene_lower = batch.GetImport(_lower);
nuclear@0 575 if (!scene_lower) {
nuclear@0 576 DefaultLogger::get()->error("M3D: Failed to read multi part model, lower.md3 fails to load");
nuclear@0 577 failure = "lower";
nuclear@0 578 goto error_cleanup;
nuclear@0 579 }
nuclear@0 580
nuclear@0 581 scene_upper = batch.GetImport(_upper);
nuclear@0 582 if (!scene_upper) {
nuclear@0 583 DefaultLogger::get()->error("M3D: Failed to read multi part model, upper.md3 fails to load");
nuclear@0 584 failure = "upper";
nuclear@0 585 goto error_cleanup;
nuclear@0 586 }
nuclear@0 587
nuclear@0 588 scene_head = batch.GetImport(_head);
nuclear@0 589 if (!scene_head) {
nuclear@0 590 DefaultLogger::get()->error("M3D: Failed to read multi part model, head.md3 fails to load");
nuclear@0 591 failure = "head";
nuclear@0 592 goto error_cleanup;
nuclear@0 593 }
nuclear@0 594
nuclear@0 595 // build attachment infos. search for typical Q3 tags
nuclear@0 596
nuclear@0 597 // original root
nuclear@0 598 scene_lower->mRootNode->mName.Set("lower");
nuclear@0 599 attach.push_back(AttachmentInfo(scene_lower, nd));
nuclear@0 600
nuclear@0 601 // tag_torso
nuclear@0 602 tag_torso = scene_lower->mRootNode->FindNode("tag_torso");
nuclear@0 603 if (!tag_torso) {
nuclear@0 604 DefaultLogger::get()->error("M3D: Failed to find attachment tag for multi part model: tag_torso expected");
nuclear@0 605 goto error_cleanup;
nuclear@0 606 }
nuclear@0 607 scene_upper->mRootNode->mName.Set("upper");
nuclear@0 608 attach.push_back(AttachmentInfo(scene_upper,tag_torso));
nuclear@0 609
nuclear@0 610 // tag_head
nuclear@0 611 tag_head = scene_upper->mRootNode->FindNode("tag_head");
nuclear@0 612 if (!tag_head) {
nuclear@0 613 DefaultLogger::get()->error("M3D: Failed to find attachment tag for multi part model: tag_head expected");
nuclear@0 614 goto error_cleanup;
nuclear@0 615 }
nuclear@0 616 scene_head->mRootNode->mName.Set("head");
nuclear@0 617 attach.push_back(AttachmentInfo(scene_head,tag_head));
nuclear@0 618
nuclear@0 619 // Remove tag_head and tag_torso from all other model parts ...
nuclear@0 620 // this ensures (together with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY)
nuclear@0 621 // that tag_torso/tag_head is also the name of the (unique) output node
nuclear@0 622 RemoveSingleNodeFromList (scene_upper->mRootNode->FindNode("tag_torso"));
nuclear@0 623 RemoveSingleNodeFromList (scene_head-> mRootNode->FindNode("tag_head" ));
nuclear@0 624
nuclear@0 625 // Undo the rotations which we applied to the coordinate systems. We're
nuclear@0 626 // working in global Quake space here
nuclear@0 627 scene_head->mRootNode->mTransformation = aiMatrix4x4();
nuclear@0 628 scene_lower->mRootNode->mTransformation = aiMatrix4x4();
nuclear@0 629 scene_upper->mRootNode->mTransformation = aiMatrix4x4();
nuclear@0 630
nuclear@0 631 // and merge the scenes
nuclear@0 632 SceneCombiner::MergeScenes(&mScene,master, attach,
nuclear@0 633 AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES |
nuclear@0 634 AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES |
nuclear@0 635 AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS |
nuclear@0 636 (!configSpeedFlag ? AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY : 0));
nuclear@0 637
nuclear@0 638 // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
nuclear@0 639 mScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
nuclear@0 640 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
nuclear@0 641
nuclear@0 642 return true;
nuclear@0 643
nuclear@0 644 error_cleanup:
nuclear@0 645 delete scene_upper;
nuclear@0 646 delete scene_lower;
nuclear@0 647 delete scene_head;
nuclear@0 648 delete master;
nuclear@0 649
nuclear@0 650 if (failure == mod_filename) {
nuclear@0 651 throw DeadlyImportError("MD3: failure to read multipart host file");
nuclear@0 652 }
nuclear@0 653 }
nuclear@0 654 return false;
nuclear@0 655 }
nuclear@0 656
nuclear@0 657 // ------------------------------------------------------------------------------------------------
nuclear@0 658 // Convert a MD3 path to a proper value
nuclear@0 659 void MD3Importer::ConvertPath(const char* texture_name, const char* header_name, std::string& out) const
nuclear@0 660 {
nuclear@0 661 // If the MD3's internal path itself and the given path are using
nuclear@0 662 // the same directory, remove it completely to get right output paths.
nuclear@0 663 const char* end1 = ::strrchr(header_name,'\\');
nuclear@0 664 if (!end1)end1 = ::strrchr(header_name,'/');
nuclear@0 665
nuclear@0 666 const char* end2 = ::strrchr(texture_name,'\\');
nuclear@0 667 if (!end2)end2 = ::strrchr(texture_name,'/');
nuclear@0 668
nuclear@0 669 // HACK: If the paths starts with "models", ignore the
nuclear@0 670 // next two hierarchy levels, it specifies just the model name.
nuclear@0 671 // Ignored by Q3, it might be not equal to the real model location.
nuclear@0 672 if (end2) {
nuclear@0 673
nuclear@0 674 size_t len2;
nuclear@0 675 const size_t len1 = (size_t)(end1 - header_name);
nuclear@0 676 if (!ASSIMP_strincmp(texture_name,"models",6) && (texture_name[6] == '/' || texture_name[6] == '\\')) {
nuclear@0 677 len2 = 6; // ignore the seventh - could be slash or backslash
nuclear@0 678
nuclear@0 679 if (!header_name[0]) {
nuclear@0 680 // Use the file name only
nuclear@0 681 out = end2+1;
nuclear@0 682 return;
nuclear@0 683 }
nuclear@0 684 }
nuclear@0 685 else len2 = std::min (len1, (size_t)(end2 - texture_name ));
nuclear@0 686 if (!ASSIMP_strincmp(texture_name,header_name,len2)) {
nuclear@0 687 // Use the file name only
nuclear@0 688 out = end2+1;
nuclear@0 689 return;
nuclear@0 690 }
nuclear@0 691 }
nuclear@0 692 // Use the full path
nuclear@0 693 out = texture_name;
nuclear@0 694 }
nuclear@0 695
nuclear@0 696 // ------------------------------------------------------------------------------------------------
nuclear@0 697 // Imports the given file into the given scene structure.
nuclear@0 698 void MD3Importer::InternReadFile( const std::string& pFile,
nuclear@0 699 aiScene* pScene, IOSystem* pIOHandler)
nuclear@0 700 {
nuclear@0 701 mFile = pFile;
nuclear@0 702 mScene = pScene;
nuclear@0 703 mIOHandler = pIOHandler;
nuclear@0 704
nuclear@0 705 // get base path and file name
nuclear@0 706 // todo ... move to PathConverter
nuclear@0 707 std::string::size_type s = mFile.find_last_of("/\\");
nuclear@0 708 if (s == std::string::npos) {
nuclear@0 709 s = 0;
nuclear@0 710 }
nuclear@0 711 else ++s;
nuclear@0 712 filename = mFile.substr(s), path = mFile.substr(0,s);
nuclear@0 713 for( std::string::iterator it = filename .begin(); it != filename.end(); ++it)
nuclear@0 714 *it = tolower( *it);
nuclear@0 715
nuclear@0 716 // Load multi-part model file, if necessary
nuclear@0 717 if (configHandleMP) {
nuclear@0 718 if (ReadMultipartFile())
nuclear@0 719 return;
nuclear@0 720 }
nuclear@0 721
nuclear@0 722 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
nuclear@0 723
nuclear@0 724 // Check whether we can read from the file
nuclear@0 725 if( file.get() == NULL)
nuclear@0 726 throw DeadlyImportError( "Failed to open MD3 file " + pFile + ".");
nuclear@0 727
nuclear@0 728 // Check whether the md3 file is large enough to contain the header
nuclear@0 729 fileSize = (unsigned int)file->FileSize();
nuclear@0 730 if( fileSize < sizeof(MD3::Header))
nuclear@0 731 throw DeadlyImportError( "MD3 File is too small.");
nuclear@0 732
nuclear@0 733 // Allocate storage and copy the contents of the file to a memory buffer
nuclear@0 734 std::vector<unsigned char> mBuffer2 (fileSize);
nuclear@0 735 file->Read( &mBuffer2[0], 1, fileSize);
nuclear@0 736 mBuffer = &mBuffer2[0];
nuclear@0 737
nuclear@0 738 pcHeader = (BE_NCONST MD3::Header*)mBuffer;
nuclear@0 739
nuclear@0 740 // Ensure correct endianess
nuclear@0 741 #ifdef AI_BUILD_BIG_ENDIAN
nuclear@0 742
nuclear@0 743 AI_SWAP4(pcHeader->VERSION);
nuclear@0 744 AI_SWAP4(pcHeader->FLAGS);
nuclear@0 745 AI_SWAP4(pcHeader->IDENT);
nuclear@0 746 AI_SWAP4(pcHeader->NUM_FRAMES);
nuclear@0 747 AI_SWAP4(pcHeader->NUM_SKINS);
nuclear@0 748 AI_SWAP4(pcHeader->NUM_SURFACES);
nuclear@0 749 AI_SWAP4(pcHeader->NUM_TAGS);
nuclear@0 750 AI_SWAP4(pcHeader->OFS_EOF);
nuclear@0 751 AI_SWAP4(pcHeader->OFS_FRAMES);
nuclear@0 752 AI_SWAP4(pcHeader->OFS_SURFACES);
nuclear@0 753 AI_SWAP4(pcHeader->OFS_TAGS);
nuclear@0 754
nuclear@0 755 #endif
nuclear@0 756
nuclear@0 757 // Validate the file header
nuclear@0 758 ValidateHeaderOffsets();
nuclear@0 759
nuclear@0 760 // Navigate to the list of surfaces
nuclear@0 761 BE_NCONST MD3::Surface* pcSurfaces = (BE_NCONST MD3::Surface*)(mBuffer + pcHeader->OFS_SURFACES);
nuclear@0 762
nuclear@0 763 // Navigate to the list of tags
nuclear@0 764 BE_NCONST MD3::Tag* pcTags = (BE_NCONST MD3::Tag*)(mBuffer + pcHeader->OFS_TAGS);
nuclear@0 765
nuclear@0 766 // Allocate output storage
nuclear@0 767 pScene->mNumMeshes = pcHeader->NUM_SURFACES;
nuclear@0 768 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
nuclear@0 769
nuclear@0 770 pScene->mNumMaterials = pcHeader->NUM_SURFACES;
nuclear@0 771 pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
nuclear@0 772
nuclear@0 773 // Set arrays to zero to ensue proper destruction if an exception is raised
nuclear@0 774 ::memset(pScene->mMeshes,0,pScene->mNumMeshes*sizeof(aiMesh*));
nuclear@0 775 ::memset(pScene->mMaterials,0,pScene->mNumMaterials*sizeof(aiMaterial*));
nuclear@0 776
nuclear@0 777 // Now read possible skins from .skin file
nuclear@0 778 Q3Shader::SkinData skins;
nuclear@0 779 ReadSkin(skins);
nuclear@0 780
nuclear@0 781 // And check whether we can locate a shader file for this model
nuclear@0 782 Q3Shader::ShaderData shaders;
nuclear@0 783 ReadShader(shaders);
nuclear@0 784
nuclear@0 785 // Adjust all texture paths in the shader
nuclear@0 786 const char* header_name = pcHeader->NAME;
nuclear@0 787 if (shaders.blocks.size()) {
nuclear@0 788 for (std::list< Q3Shader::ShaderDataBlock >::iterator dit = shaders.blocks.begin(); dit != shaders.blocks.end(); ++dit) {
nuclear@0 789 ConvertPath((*dit).name.c_str(),header_name,(*dit).name);
nuclear@0 790
nuclear@0 791 for (std::list< Q3Shader::ShaderMapBlock >::iterator mit = (*dit).maps.begin(); mit != (*dit).maps.end(); ++mit) {
nuclear@0 792 ConvertPath((*mit).name.c_str(),header_name,(*mit).name);
nuclear@0 793 }
nuclear@0 794 }
nuclear@0 795 }
nuclear@0 796
nuclear@0 797 // Read all surfaces from the file
nuclear@0 798 unsigned int iNum = pcHeader->NUM_SURFACES;
nuclear@0 799 unsigned int iNumMaterials = 0;
nuclear@0 800 while (iNum-- > 0) {
nuclear@0 801
nuclear@0 802 // Ensure correct endianess
nuclear@0 803 #ifdef AI_BUILD_BIG_ENDIAN
nuclear@0 804
nuclear@0 805 AI_SWAP4(pcSurfaces->FLAGS);
nuclear@0 806 AI_SWAP4(pcSurfaces->IDENT);
nuclear@0 807 AI_SWAP4(pcSurfaces->NUM_FRAMES);
nuclear@0 808 AI_SWAP4(pcSurfaces->NUM_SHADER);
nuclear@0 809 AI_SWAP4(pcSurfaces->NUM_TRIANGLES);
nuclear@0 810 AI_SWAP4(pcSurfaces->NUM_VERTICES);
nuclear@0 811 AI_SWAP4(pcSurfaces->OFS_END);
nuclear@0 812 AI_SWAP4(pcSurfaces->OFS_SHADERS);
nuclear@0 813 AI_SWAP4(pcSurfaces->OFS_ST);
nuclear@0 814 AI_SWAP4(pcSurfaces->OFS_TRIANGLES);
nuclear@0 815 AI_SWAP4(pcSurfaces->OFS_XYZNORMAL);
nuclear@0 816
nuclear@0 817 #endif
nuclear@0 818
nuclear@0 819 // Validate the surface header
nuclear@0 820 ValidateSurfaceHeaderOffsets(pcSurfaces);
nuclear@0 821
nuclear@0 822 // Navigate to the vertex list of the surface
nuclear@0 823 BE_NCONST MD3::Vertex* pcVertices = (BE_NCONST MD3::Vertex*)
nuclear@0 824 (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL);
nuclear@0 825
nuclear@0 826 // Navigate to the triangle list of the surface
nuclear@0 827 BE_NCONST MD3::Triangle* pcTriangles = (BE_NCONST MD3::Triangle*)
nuclear@0 828 (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_TRIANGLES);
nuclear@0 829
nuclear@0 830 // Navigate to the texture coordinate list of the surface
nuclear@0 831 BE_NCONST MD3::TexCoord* pcUVs = (BE_NCONST MD3::TexCoord*)
nuclear@0 832 (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_ST);
nuclear@0 833
nuclear@0 834 // Navigate to the shader list of the surface
nuclear@0 835 BE_NCONST MD3::Shader* pcShaders = (BE_NCONST MD3::Shader*)
nuclear@0 836 (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_SHADERS);
nuclear@0 837
nuclear@0 838 // If the submesh is empty ignore it
nuclear@0 839 if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES)
nuclear@0 840 {
nuclear@0 841 pcSurfaces = (BE_NCONST MD3::Surface*)(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_END);
nuclear@0 842 pScene->mNumMeshes--;
nuclear@0 843 continue;
nuclear@0 844 }
nuclear@0 845
nuclear@0 846 // Allocate output mesh
nuclear@0 847 pScene->mMeshes[iNum] = new aiMesh();
nuclear@0 848 aiMesh* pcMesh = pScene->mMeshes[iNum];
nuclear@0 849
nuclear@0 850 std::string _texture_name;
nuclear@0 851 const char* texture_name = NULL;
nuclear@0 852
nuclear@0 853 // Check whether we have a texture record for this surface in the .skin file
nuclear@0 854 std::list< Q3Shader::SkinData::TextureEntry >::iterator it = std::find(
nuclear@0 855 skins.textures.begin(), skins.textures.end(), pcSurfaces->NAME );
nuclear@0 856
nuclear@0 857 if (it != skins.textures.end()) {
nuclear@0 858 texture_name = &*( _texture_name = (*it).second).begin();
nuclear@0 859 DefaultLogger::get()->debug("MD3: Assigning skin texture " + (*it).second + " to surface " + pcSurfaces->NAME);
nuclear@0 860 (*it).resolved = true; // mark entry as resolved
nuclear@0 861 }
nuclear@0 862
nuclear@0 863 // Get the first shader (= texture?) assigned to the surface
nuclear@0 864 if (!texture_name && pcSurfaces->NUM_SHADER) {
nuclear@0 865 texture_name = pcShaders->NAME;
nuclear@0 866 }
nuclear@0 867
nuclear@0 868 std::string convertedPath;
nuclear@0 869 if (texture_name) {
nuclear@0 870 ConvertPath(texture_name,header_name,convertedPath);
nuclear@0 871 }
nuclear@0 872
nuclear@0 873 const Q3Shader::ShaderDataBlock* shader = NULL;
nuclear@0 874
nuclear@0 875 // Now search the current shader for a record with this name (
nuclear@0 876 // excluding texture file extension)
nuclear@0 877 if (shaders.blocks.size()) {
nuclear@0 878
nuclear@0 879 std::string::size_type s = convertedPath.find_last_of('.');
nuclear@0 880 if (s == std::string::npos)
nuclear@0 881 s = convertedPath.length();
nuclear@0 882
nuclear@0 883 const std::string without_ext = convertedPath.substr(0,s);
nuclear@0 884 std::list< Q3Shader::ShaderDataBlock >::const_iterator dit = std::find(shaders.blocks.begin(),shaders.blocks.end(),without_ext);
nuclear@0 885 if (dit != shaders.blocks.end()) {
nuclear@0 886 // Hurra, wir haben einen. Tolle Sache.
nuclear@0 887 shader = &*dit;
nuclear@0 888 DefaultLogger::get()->info("Found shader record for " +without_ext );
nuclear@0 889 }
nuclear@0 890 else DefaultLogger::get()->warn("Unable to find shader record for " +without_ext );
nuclear@0 891 }
nuclear@0 892
nuclear@0 893 aiMaterial* pcHelper = new aiMaterial();
nuclear@0 894
nuclear@0 895 const int iMode = (int)aiShadingMode_Gouraud;
nuclear@0 896 pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
nuclear@0 897
nuclear@0 898 // Add a small ambient color value - Quake 3 seems to have one
nuclear@0 899 aiColor3D clr;
nuclear@0 900 clr.b = clr.g = clr.r = 0.05f;
nuclear@0 901 pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
nuclear@0 902
nuclear@0 903 clr.b = clr.g = clr.r = 1.0f;
nuclear@0 904 pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
nuclear@0 905 pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
nuclear@0 906
nuclear@0 907 // use surface name + skin_name as material name
nuclear@0 908 aiString name;
nuclear@0 909 name.Set("MD3_[" + configSkinFile + "][" + pcSurfaces->NAME + "]");
nuclear@0 910 pcHelper->AddProperty(&name,AI_MATKEY_NAME);
nuclear@0 911
nuclear@0 912 if (!shader) {
nuclear@0 913 // Setup dummy texture file name to ensure UV coordinates are kept during postprocessing
nuclear@0 914 aiString szString;
nuclear@0 915 if (convertedPath.length()) {
nuclear@0 916 szString.Set(convertedPath);
nuclear@0 917 }
nuclear@0 918 else {
nuclear@0 919 DefaultLogger::get()->warn("Texture file name has zero length. Using default name");
nuclear@0 920 szString.Set("dummy_texture.bmp");
nuclear@0 921 }
nuclear@0 922 pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
nuclear@0 923
nuclear@0 924 // prevent transparency by default
nuclear@0 925 int no_alpha = aiTextureFlags_IgnoreAlpha;
nuclear@0 926 pcHelper->AddProperty(&no_alpha,1,AI_MATKEY_TEXFLAGS_DIFFUSE(0));
nuclear@0 927 }
nuclear@0 928 else {
nuclear@0 929 Q3Shader::ConvertShaderToMaterial(pcHelper,*shader);
nuclear@0 930 }
nuclear@0 931
nuclear@0 932 pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
nuclear@0 933 pcMesh->mMaterialIndex = iNumMaterials++;
nuclear@0 934
nuclear@0 935 // Ensure correct endianess
nuclear@0 936 #ifdef AI_BUILD_BIG_ENDIAN
nuclear@0 937
nuclear@0 938 for (uint32_t i = 0; i < pcSurfaces->NUM_VERTICES;++i) {
nuclear@0 939 AI_SWAP2( pcVertices[i].NORMAL );
nuclear@0 940 AI_SWAP2( pcVertices[i].X );
nuclear@0 941 AI_SWAP2( pcVertices[i].Y );
nuclear@0 942 AI_SWAP2( pcVertices[i].Z );
nuclear@0 943
nuclear@0 944 AI_SWAP4( pcUVs[i].U );
nuclear@0 945 AI_SWAP4( pcUVs[i].U );
nuclear@0 946 }
nuclear@0 947 for (uint32_t i = 0; i < pcSurfaces->NUM_TRIANGLES;++i) {
nuclear@0 948 AI_SWAP4(pcTriangles[i].INDEXES[0]);
nuclear@0 949 AI_SWAP4(pcTriangles[i].INDEXES[1]);
nuclear@0 950 AI_SWAP4(pcTriangles[i].INDEXES[2]);
nuclear@0 951 }
nuclear@0 952
nuclear@0 953 #endif
nuclear@0 954
nuclear@0 955 // Fill mesh information
nuclear@0 956 pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
nuclear@0 957
nuclear@0 958 pcMesh->mNumVertices = pcSurfaces->NUM_TRIANGLES*3;
nuclear@0 959 pcMesh->mNumFaces = pcSurfaces->NUM_TRIANGLES;
nuclear@0 960 pcMesh->mFaces = new aiFace[pcSurfaces->NUM_TRIANGLES];
nuclear@0 961 pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
nuclear@0 962 pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
nuclear@0 963 pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
nuclear@0 964 pcMesh->mNumUVComponents[0] = 2;
nuclear@0 965
nuclear@0 966 // Fill in all triangles
nuclear@0 967 unsigned int iCurrent = 0;
nuclear@0 968 for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_TRIANGLES;++i) {
nuclear@0 969 pcMesh->mFaces[i].mIndices = new unsigned int[3];
nuclear@0 970 pcMesh->mFaces[i].mNumIndices = 3;
nuclear@0 971
nuclear@0 972 //unsigned int iTemp = iCurrent;
nuclear@0 973 for (unsigned int c = 0; c < 3;++c,++iCurrent) {
nuclear@0 974 pcMesh->mFaces[i].mIndices[c] = iCurrent;
nuclear@0 975
nuclear@0 976 // Read vertices
nuclear@0 977 aiVector3D& vec = pcMesh->mVertices[iCurrent];
nuclear@0 978 vec.x = pcVertices[ pcTriangles->INDEXES[c]].X*AI_MD3_XYZ_SCALE;
nuclear@0 979 vec.y = pcVertices[ pcTriangles->INDEXES[c]].Y*AI_MD3_XYZ_SCALE;
nuclear@0 980 vec.z = pcVertices[ pcTriangles->INDEXES[c]].Z*AI_MD3_XYZ_SCALE;
nuclear@0 981
nuclear@0 982 // Convert the normal vector to uncompressed float3 format
nuclear@0 983 aiVector3D& nor = pcMesh->mNormals[iCurrent];
nuclear@0 984 LatLngNormalToVec3(pcVertices[pcTriangles->INDEXES[c]].NORMAL,(float*)&nor);
nuclear@0 985
nuclear@0 986 // Read texture coordinates
nuclear@0 987 pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U;
nuclear@0 988 pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V;
nuclear@0 989 }
nuclear@0 990 // Flip face order if necessary
nuclear@0 991 if (!shader || shader->cull == Q3Shader::CULL_CW) {
nuclear@0 992 std::swap(pcMesh->mFaces[i].mIndices[2],pcMesh->mFaces[i].mIndices[1]);
nuclear@0 993 }
nuclear@0 994 pcTriangles++;
nuclear@0 995 }
nuclear@0 996
nuclear@0 997 // Go to the next surface
nuclear@0 998 pcSurfaces = (BE_NCONST MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
nuclear@0 999 }
nuclear@0 1000
nuclear@0 1001 // For debugging purposes: check whether we found matches for all entries in the skins file
nuclear@0 1002 if (!DefaultLogger::isNullLogger()) {
nuclear@0 1003 for (std::list< Q3Shader::SkinData::TextureEntry>::const_iterator it = skins.textures.begin();it != skins.textures.end(); ++it) {
nuclear@0 1004 if (!(*it).resolved) {
nuclear@0 1005 DefaultLogger::get()->error("MD3: Failed to match skin " + (*it).first + " to surface " + (*it).second);
nuclear@0 1006 }
nuclear@0 1007 }
nuclear@0 1008 }
nuclear@0 1009
nuclear@0 1010 if (!pScene->mNumMeshes)
nuclear@0 1011 throw DeadlyImportError( "MD3: File contains no valid mesh");
nuclear@0 1012 pScene->mNumMaterials = iNumMaterials;
nuclear@0 1013
nuclear@0 1014 // Now we need to generate an empty node graph
nuclear@0 1015 pScene->mRootNode = new aiNode("<MD3Root>");
nuclear@0 1016 pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
nuclear@0 1017 pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
nuclear@0 1018
nuclear@0 1019 // Attach tiny children for all tags
nuclear@0 1020 if (pcHeader->NUM_TAGS) {
nuclear@0 1021 pScene->mRootNode->mNumChildren = pcHeader->NUM_TAGS;
nuclear@0 1022 pScene->mRootNode->mChildren = new aiNode*[pcHeader->NUM_TAGS];
nuclear@0 1023
nuclear@0 1024 for (unsigned int i = 0; i < pcHeader->NUM_TAGS; ++i, ++pcTags) {
nuclear@0 1025
nuclear@0 1026 aiNode* nd = pScene->mRootNode->mChildren[i] = new aiNode();
nuclear@0 1027 nd->mName.Set((const char*)pcTags->NAME);
nuclear@0 1028 nd->mParent = pScene->mRootNode;
nuclear@0 1029
nuclear@0 1030 AI_SWAP4(pcTags->origin.x);
nuclear@0 1031 AI_SWAP4(pcTags->origin.y);
nuclear@0 1032 AI_SWAP4(pcTags->origin.z);
nuclear@0 1033
nuclear@0 1034 // Copy local origin, again flip z,y
nuclear@0 1035 nd->mTransformation.a4 = pcTags->origin.x;
nuclear@0 1036 nd->mTransformation.b4 = pcTags->origin.y;
nuclear@0 1037 nd->mTransformation.c4 = pcTags->origin.z;
nuclear@0 1038
nuclear@0 1039 // Copy rest of transformation (need to transpose to match row-order matrix)
nuclear@0 1040 for (unsigned int a = 0; a < 3;++a) {
nuclear@0 1041 for (unsigned int m = 0; m < 3;++m) {
nuclear@0 1042 nd->mTransformation[m][a] = pcTags->orientation[a][m];
nuclear@0 1043 AI_SWAP4(nd->mTransformation[m][a]);
nuclear@0 1044 }
nuclear@0 1045 }
nuclear@0 1046 }
nuclear@0 1047 }
nuclear@0 1048
nuclear@0 1049 for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
nuclear@0 1050 pScene->mRootNode->mMeshes[i] = i;
nuclear@0 1051
nuclear@0 1052 // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
nuclear@0 1053 pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
nuclear@0 1054 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
nuclear@0 1055 }
nuclear@0 1056
nuclear@0 1057 #endif // !! ASSIMP_BUILD_NO_MD3_IMPORTER