nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file UnrealLoader.cpp nuclear@0: * @brief Implementation of the UNREAL (*.3D) importer class nuclear@0: * nuclear@0: * Sources: nuclear@0: * http://local.wasp.uwa.edu.au/~pbourke/dataformats/unreal/ nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_3D_IMPORTER nuclear@0: nuclear@0: #include "UnrealLoader.h" nuclear@0: #include "StreamReader.h" nuclear@0: #include "ParsingUtils.h" nuclear@0: #include "fast_atof.h" nuclear@0: #include "ConvertToLHProcess.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: static const aiImporterDesc desc = { nuclear@0: "Unreal Mesh Importer", nuclear@0: "", nuclear@0: "", nuclear@0: "", nuclear@0: aiImporterFlags_SupportTextFlavour, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: "3d uc" nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: UnrealImporter::UnrealImporter() nuclear@0: : configFrameID (0) nuclear@0: , configHandleFlags (true) nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: UnrealImporter::~UnrealImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the class can handle the format of the given file. nuclear@0: bool UnrealImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const nuclear@0: { nuclear@0: return SimpleExtensionCheck(pFile,"3d","uc"); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Build a string of all file extensions supported nuclear@0: const aiImporterDesc* UnrealImporter::GetInfo () const nuclear@0: { nuclear@0: return &desc; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Setup configuration properties for the loader nuclear@0: void UnrealImporter::SetupProperties(const Importer* pImp) nuclear@0: { nuclear@0: // The nuclear@0: // AI_CONFIG_IMPORT_UNREAL_KEYFRAME option overrides the nuclear@0: // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. nuclear@0: configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_UNREAL_KEYFRAME,-1); nuclear@0: if(static_cast(-1) == configFrameID) { nuclear@0: configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); nuclear@0: } nuclear@0: nuclear@0: // AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS, default is true nuclear@0: configHandleFlags = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS,1)); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Imports the given file into the given scene structure. nuclear@0: void UnrealImporter::InternReadFile( const std::string& pFile, nuclear@0: aiScene* pScene, IOSystem* pIOHandler) nuclear@0: { nuclear@0: // For any of the 3 files being passed get the three correct paths nuclear@0: // First of all, determine file extension nuclear@0: std::string::size_type pos = pFile.find_last_of('.'); nuclear@0: std::string extension = GetExtension(pFile); nuclear@0: nuclear@0: std::string d_path,a_path,uc_path; nuclear@0: if (extension == "3d") { nuclear@0: // jjjj_d.3d nuclear@0: // jjjj_a.3d nuclear@0: pos = pFile.find_last_of('_'); nuclear@0: if (std::string::npos == pos) { nuclear@0: throw DeadlyImportError("UNREAL: Unexpected naming scheme"); nuclear@0: } nuclear@0: extension = pFile.substr(0,pos); nuclear@0: } nuclear@0: else { nuclear@0: extension = pFile.substr(0,pos); nuclear@0: } nuclear@0: nuclear@0: // build proper paths nuclear@0: d_path = extension+"_d.3d"; nuclear@0: a_path = extension+"_a.3d"; nuclear@0: uc_path = extension+".uc"; nuclear@0: nuclear@0: DefaultLogger::get()->debug("UNREAL: data file is " + d_path); nuclear@0: DefaultLogger::get()->debug("UNREAL: aniv file is " + a_path); nuclear@0: DefaultLogger::get()->debug("UNREAL: uc file is " + uc_path); nuclear@0: nuclear@0: // and open the files ... we can't live without them nuclear@0: IOStream* p = pIOHandler->Open(d_path); nuclear@0: if (!p) nuclear@0: throw DeadlyImportError("UNREAL: Unable to open _d file"); nuclear@0: StreamReaderLE d_reader(pIOHandler->Open(d_path)); nuclear@0: nuclear@0: const uint16_t numTris = d_reader.GetI2(); nuclear@0: const uint16_t numVert = d_reader.GetI2(); nuclear@0: d_reader.IncPtr(44); nuclear@0: if (!numTris || numVert < 3) nuclear@0: throw DeadlyImportError("UNREAL: Invalid number of vertices/triangles"); nuclear@0: nuclear@0: // maximum texture index nuclear@0: unsigned int maxTexIdx = 0; nuclear@0: nuclear@0: // collect triangles nuclear@0: std::vector triangles(numTris); nuclear@0: for (std::vector::iterator it = triangles.begin(), end = triangles.end();it != end; ++it) { nuclear@0: Unreal::Triangle& tri = *it; nuclear@0: nuclear@0: for (unsigned int i = 0; i < 3;++i) { nuclear@0: nuclear@0: tri.mVertex[i] = d_reader.GetI2(); nuclear@0: if (tri.mVertex[i] >= numTris) { nuclear@0: DefaultLogger::get()->warn("UNREAL: vertex index out of range"); nuclear@0: tri.mVertex[i] = 0; nuclear@0: } nuclear@0: } nuclear@0: tri.mType = d_reader.GetI1(); nuclear@0: nuclear@0: // handle mesh flagss? nuclear@0: if (configHandleFlags) nuclear@0: tri.mType = Unreal::MF_NORMAL_OS; nuclear@0: else { nuclear@0: // ignore MOD and MASKED for the moment, treat them as two-sided nuclear@0: if (tri.mType == Unreal::MF_NORMAL_MOD_TS || tri.mType == Unreal::MF_NORMAL_MASKED_TS) nuclear@0: tri.mType = Unreal::MF_NORMAL_TS; nuclear@0: } nuclear@0: d_reader.IncPtr(1); nuclear@0: nuclear@0: for (unsigned int i = 0; i < 3;++i) nuclear@0: for (unsigned int i2 = 0; i2 < 2;++i2) nuclear@0: tri.mTex[i][i2] = d_reader.GetI1(); nuclear@0: nuclear@0: tri.mTextureNum = d_reader.GetI1(); nuclear@0: maxTexIdx = std::max(maxTexIdx,(unsigned int)tri.mTextureNum); nuclear@0: d_reader.IncPtr(1); nuclear@0: } nuclear@0: nuclear@0: p = pIOHandler->Open(a_path); nuclear@0: if (!p) nuclear@0: throw DeadlyImportError("UNREAL: Unable to open _a file"); nuclear@0: StreamReaderLE a_reader(pIOHandler->Open(a_path)); nuclear@0: nuclear@0: // read number of frames nuclear@0: const uint32_t numFrames = a_reader.GetI2(); nuclear@0: if (configFrameID >= numFrames) nuclear@0: throw DeadlyImportError("UNREAL: The requested frame does not exist"); nuclear@0: nuclear@0: uint32_t st = a_reader.GetI2(); nuclear@0: if (st != numVert*4) nuclear@0: throw DeadlyImportError("UNREAL: Unexpected aniv file length"); nuclear@0: nuclear@0: // skip to our frame nuclear@0: a_reader.IncPtr(configFrameID *numVert*4); nuclear@0: nuclear@0: // collect vertices nuclear@0: std::vector vertices(numVert); nuclear@0: for (std::vector::iterator it = vertices.begin(), end = vertices.end(); it != end; ++it) { nuclear@0: int32_t val = a_reader.GetI4(); nuclear@0: Unreal::DecompressVertex(*it,val); nuclear@0: } nuclear@0: nuclear@0: // list of textures. nuclear@0: std::vector< std::pair > textures; nuclear@0: nuclear@0: // allocate the output scene nuclear@0: aiNode* nd = pScene->mRootNode = new aiNode(); nuclear@0: nd->mName.Set(""); nuclear@0: nuclear@0: // we can live without the uc file if necessary nuclear@0: boost::scoped_ptr pb (pIOHandler->Open(uc_path)); nuclear@0: if (pb.get()) { nuclear@0: nuclear@0: std::vector _data; nuclear@0: TextFileToBuffer(pb.get(),_data); nuclear@0: const char* data = &_data[0]; nuclear@0: nuclear@0: std::vector< std::pair< std::string,std::string > > tempTextures; nuclear@0: nuclear@0: // do a quick search in the UC file for some known, usually texture-related, tags nuclear@0: for (;*data;++data) { nuclear@0: if (TokenMatchI(data,"#exec",5)) { nuclear@0: SkipSpacesAndLineEnd(&data); nuclear@0: nuclear@0: // #exec TEXTURE IMPORT [...] NAME=jjjjj [...] FILE=jjjj.pcx [...] nuclear@0: if (TokenMatchI(data,"TEXTURE",7)) { nuclear@0: SkipSpacesAndLineEnd(&data); nuclear@0: nuclear@0: if (TokenMatchI(data,"IMPORT",6)) { nuclear@0: tempTextures.push_back(std::pair< std::string,std::string >()); nuclear@0: std::pair< std::string,std::string >& me = tempTextures.back(); nuclear@0: for (;!IsLineEnd(*data);++data) { nuclear@0: if (!::ASSIMP_strincmp(data,"NAME=",5)) { nuclear@0: const char *d = data+=5; nuclear@0: for (;!IsSpaceOrNewLine(*data);++data); nuclear@0: me.first = std::string(d,(size_t)(data-d)); nuclear@0: } nuclear@0: else if (!::ASSIMP_strincmp(data,"FILE=",5)) { nuclear@0: const char *d = data+=5; nuclear@0: for (;!IsSpaceOrNewLine(*data);++data); nuclear@0: me.second = std::string(d,(size_t)(data-d)); nuclear@0: } nuclear@0: } nuclear@0: if (!me.first.length() || !me.second.length()) nuclear@0: tempTextures.pop_back(); nuclear@0: } nuclear@0: } nuclear@0: // #exec MESHMAP SETTEXTURE MESHMAP=box NUM=1 TEXTURE=Jtex1 nuclear@0: // #exec MESHMAP SCALE MESHMAP=box X=0.1 Y=0.1 Z=0.2 nuclear@0: else if (TokenMatchI(data,"MESHMAP",7)) { nuclear@0: SkipSpacesAndLineEnd(&data); nuclear@0: nuclear@0: if (TokenMatchI(data,"SETTEXTURE",10)) { nuclear@0: nuclear@0: textures.push_back(std::pair()); nuclear@0: std::pair& me = textures.back(); nuclear@0: nuclear@0: for (;!IsLineEnd(*data);++data) { nuclear@0: if (!::ASSIMP_strincmp(data,"NUM=",4)) { nuclear@0: data += 4; nuclear@0: me.first = strtoul10(data,&data); nuclear@0: } nuclear@0: else if (!::ASSIMP_strincmp(data,"TEXTURE=",8)) { nuclear@0: data += 8; nuclear@0: const char *d = data; nuclear@0: for (;!IsSpaceOrNewLine(*data);++data); nuclear@0: me.second = std::string(d,(size_t)(data-d)); nuclear@0: nuclear@0: // try to find matching path names, doesn't care if we don't find them nuclear@0: for (std::vector< std::pair< std::string,std::string > >::const_iterator it = tempTextures.begin(); nuclear@0: it != tempTextures.end(); ++it) { nuclear@0: if ((*it).first == me.second) { nuclear@0: me.second = (*it).second; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: else if (TokenMatchI(data,"SCALE",5)) { nuclear@0: nuclear@0: for (;!IsLineEnd(*data);++data) { nuclear@0: if (data[0] == 'X' && data[1] == '=') { nuclear@0: data = fast_atoreal_move(data+2,(float&)nd->mTransformation.a1); nuclear@0: } nuclear@0: else if (data[0] == 'Y' && data[1] == '=') { nuclear@0: data = fast_atoreal_move(data+2,(float&)nd->mTransformation.b2); nuclear@0: } nuclear@0: else if (data[0] == 'Z' && data[1] == '=') { nuclear@0: data = fast_atoreal_move(data+2,(float&)nd->mTransformation.c3); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: else { nuclear@0: DefaultLogger::get()->error("Unable to open .uc file"); nuclear@0: } nuclear@0: nuclear@0: std::vector materials; nuclear@0: materials.reserve(textures.size()*2+5); nuclear@0: nuclear@0: // find out how many output meshes and materials we'll have and build material indices nuclear@0: for (std::vector::iterator it = triangles.begin(), end = triangles.end();it != end; ++it) { nuclear@0: Unreal::Triangle& tri = *it; nuclear@0: Unreal::TempMat mat(tri); nuclear@0: std::vector::iterator nt = std::find(materials.begin(),materials.end(),mat); nuclear@0: if (nt == materials.end()) { nuclear@0: // add material nuclear@0: tri.matIndex = materials.size(); nuclear@0: mat.numFaces = 1; nuclear@0: materials.push_back(mat); nuclear@0: nuclear@0: ++pScene->mNumMeshes; nuclear@0: } nuclear@0: else { nuclear@0: tri.matIndex = static_cast(nt-materials.begin()); nuclear@0: ++nt->numFaces; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (!pScene->mNumMeshes) { nuclear@0: throw DeadlyImportError("UNREAL: Unable to find valid mesh data"); nuclear@0: } nuclear@0: nuclear@0: // allocate meshes and bind them to the node graph nuclear@0: pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; nuclear@0: pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = pScene->mNumMeshes]; nuclear@0: nuclear@0: nd->mNumMeshes = pScene->mNumMeshes; nuclear@0: nd->mMeshes = new unsigned int[nd->mNumMeshes]; nuclear@0: for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { nuclear@0: aiMesh* m = pScene->mMeshes[i] = new aiMesh(); nuclear@0: m->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; nuclear@0: nuclear@0: const unsigned int num = materials[i].numFaces; nuclear@0: m->mFaces = new aiFace [num]; nuclear@0: m->mVertices = new aiVector3D [num*3]; nuclear@0: m->mTextureCoords[0] = new aiVector3D [num*3]; nuclear@0: nuclear@0: nd->mMeshes[i] = i; nuclear@0: nuclear@0: // create materials, too nuclear@0: aiMaterial* mat = new aiMaterial(); nuclear@0: pScene->mMaterials[i] = mat; nuclear@0: nuclear@0: // all white by default - texture rulez nuclear@0: aiColor3D color(1.f,1.f,1.f); nuclear@0: nuclear@0: aiString s; nuclear@0: ::sprintf(s.data,"mat%i_tx%i_",i,materials[i].tex); nuclear@0: nuclear@0: // set the two-sided flag nuclear@0: if (materials[i].type == Unreal::MF_NORMAL_TS) { nuclear@0: const int twosided = 1; nuclear@0: mat->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED); nuclear@0: ::strcat(s.data,"ts_"); nuclear@0: } nuclear@0: else ::strcat(s.data,"os_"); nuclear@0: nuclear@0: // make TRANS faces 90% opaque that RemRedundantMaterials won't catch us nuclear@0: if (materials[i].type == Unreal::MF_NORMAL_TRANS_TS) { nuclear@0: const float opac = 0.9f; nuclear@0: mat->AddProperty(&opac,1,AI_MATKEY_OPACITY); nuclear@0: ::strcat(s.data,"tran_"); nuclear@0: } nuclear@0: else ::strcat(s.data,"opaq_"); nuclear@0: nuclear@0: // a special name for the weapon attachment point nuclear@0: if (materials[i].type == Unreal::MF_WEAPON_PLACEHOLDER) { nuclear@0: s.length = ::sprintf(s.data,"$WeaponTag$"); nuclear@0: color = aiColor3D(0.f,0.f,0.f); nuclear@0: } nuclear@0: nuclear@0: // set color and name nuclear@0: mat->AddProperty(&color,1,AI_MATKEY_COLOR_DIFFUSE); nuclear@0: s.length = ::strlen(s.data); nuclear@0: mat->AddProperty(&s,AI_MATKEY_NAME); nuclear@0: nuclear@0: // set texture, if any nuclear@0: const unsigned int tex = materials[i].tex; nuclear@0: for (std::vector< std::pair< unsigned int, std::string > >::const_iterator it = textures.begin();it != textures.end();++it) { nuclear@0: if ((*it).first == tex) { nuclear@0: s.Set((*it).second); nuclear@0: mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // fill them. nuclear@0: for (std::vector::iterator it = triangles.begin(), end = triangles.end();it != end; ++it) { nuclear@0: Unreal::Triangle& tri = *it; nuclear@0: Unreal::TempMat mat(tri); nuclear@0: std::vector::iterator nt = std::find(materials.begin(),materials.end(),mat); nuclear@0: nuclear@0: aiMesh* mesh = pScene->mMeshes[nt-materials.begin()]; nuclear@0: aiFace& f = mesh->mFaces[mesh->mNumFaces++]; nuclear@0: f.mIndices = new unsigned int[f.mNumIndices = 3]; nuclear@0: nuclear@0: for (unsigned int i = 0; i < 3;++i,mesh->mNumVertices++) { nuclear@0: f.mIndices[i] = mesh->mNumVertices; nuclear@0: nuclear@0: mesh->mVertices[mesh->mNumVertices] = vertices[ tri.mVertex[i] ]; nuclear@0: mesh->mTextureCoords[0][mesh->mNumVertices] = aiVector3D( tri.mTex[i][0] / 255.f, 1.f - tri.mTex[i][1] / 255.f, 0.f); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // convert to RH nuclear@0: MakeLeftHandedProcess hero; nuclear@0: hero.Execute(pScene); nuclear@0: nuclear@0: FlipWindingOrderProcess flipper; nuclear@0: flipper.Execute(pScene); nuclear@0: } nuclear@0: nuclear@0: #endif // !! ASSIMP_BUILD_NO_3D_IMPORTER