// ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); // Check whether we can read from the file if( file.get() == NULL) throw new ImportErrorException( "Failed to open OFF file " + pFile + "."); unsigned int fileSize = (unsigned int)file->FileSize(); // allocate storage and copy the contents of the file to a memory buffer std::vector<char> mBuffer2(fileSize+1); file->Read(&mBuffer2[0], 1, fileSize); mBuffer2[fileSize] = '\0'; const char* buffer = &mBuffer2[0]; char line[4096]; GetNextLine(buffer,line); if ('O' == line[0])GetNextLine(buffer,line); // skip the 'OFF' line const char* sz = line; SkipSpaces(&sz); const unsigned int numVertices = strtol10(sz,&sz);SkipSpaces(&sz); const unsigned int numFaces = strtol10(sz,&sz); pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = 1 ]; aiMesh* mesh = pScene->mMeshes[0] = new aiMesh(); aiFace* faces = mesh->mFaces = new aiFace [mesh->mNumFaces = numFaces]; std::vector<aiVector3D> tempPositions(numVertices); // now read all vertex lines for (unsigned int i = 0; i< numVertices;++i) { if(!GetNextLine(buffer,line)) { DefaultLogger::get()->error("OFF: The number of verts in the header is incorrect"); break; } aiVector3D& v = tempPositions[i]; sz = line; SkipSpaces(&sz); sz = fast_atof_move(sz,(float&)v.x); SkipSpaces(&sz); sz = fast_atof_move(sz,(float&)v.y); SkipSpaces(&sz); fast_atof_move(sz,(float&)v.z); } // First find out how many vertices we'll need const char* old = buffer; for (unsigned int i = 0; i< mesh->mNumFaces;++i) { if(!GetNextLine(buffer,line)) { DefaultLogger::get()->error("OFF: The number of faces in the header is incorrect"); break; } sz = line;SkipSpaces(&sz); if(!(faces->mNumIndices = strtol10(sz,&sz)) || faces->mNumIndices > 9) { DefaultLogger::get()->error("OFF: Faces with zero indices aren't allowed"); --mesh->mNumFaces; continue; } mesh->mNumVertices += faces->mNumIndices; ++faces; } if (!mesh->mNumVertices) throw new ImportErrorException("OFF: There are no valid faces"); // allocate storage for the output vertices aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; // second: now parse all face indices buffer = old;faces = mesh->mFaces; for (unsigned int i = 0, p = 0; i< mesh->mNumFaces;) { if(!GetNextLine(buffer,line))break; unsigned int idx; sz = line;SkipSpaces(&sz); if(!(idx = strtol10(sz,&sz)) || idx > 9) continue; faces->mIndices = new unsigned int [faces->mNumIndices]; for (unsigned int m = 0; m < faces->mNumIndices;++m) { SkipSpaces(&sz); if ((idx = strtol10(sz,&sz)) >= numVertices) { DefaultLogger::get()->error("OFF: Vertex index is out of range"); idx = numVertices-1; } faces->mIndices[m] = p++; *verts++ = tempPositions[idx]; } ++i; ++faces; } // generate the output node graph pScene->mRootNode = new aiNode(); pScene->mRootNode->mName.Set("<OFFRoot>"); pScene->mRootNode->mMeshes = new unsigned int [pScene->mRootNode->mNumMeshes = 1]; pScene->mRootNode->mMeshes[0] = 0; // generate a default material pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = 1]; MaterialHelper* pcMat = new MaterialHelper(); aiColor4D clr(0.6f,0.6f,0.6f,1.0f); pcMat->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE); pScene->mMaterials[0] = pcMat; const int twosided =1; pcMat->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED); }
// ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void RAWImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); // Check whether we can read from the file if( file.get() == NULL) throw new ImportErrorException( "Failed to open RAW file " + pFile + "."); unsigned int fileSize = (unsigned int)file->FileSize(); // allocate storage and copy the contents of the file to a memory buffer // (terminate it with zero) std::vector<char> mBuffer2(fileSize+1); file->Read(&mBuffer2[0], 1, fileSize); mBuffer2[fileSize] = '\0'; const char* buffer = &mBuffer2[0]; // list of groups loaded from the file std::vector< GroupInformation > outGroups(1,GroupInformation("<default>")); std::vector< GroupInformation >::iterator curGroup = outGroups.begin(); // now read all lines char line[4096]; while (GetNextLine(buffer,line)) { // if the line starts with a non-numeric identifier, it marks // the beginning of a new group const char* sz = line;SkipSpaces(&sz); if (IsLineEnd(*sz))continue; if (!IsNumeric(*sz)) { const char* sz2 = sz; while (!IsSpaceOrNewLine(*sz2))++sz2; const unsigned int length = (unsigned int)(sz2-sz); // find an existing group with this name for (std::vector< GroupInformation >::iterator it = outGroups.begin(), end = outGroups.end(); it != end;++it) { if (length == (*it).name.length() && !::strcmp(sz,(*it).name.c_str())) { curGroup = it;sz2 = NULL; break; } } if (sz2) { outGroups.push_back(GroupInformation(std::string(sz,length))); curGroup = outGroups.end()-1; } } else { // there can be maximally 12 floats plus an extra texture file name float data[12]; unsigned int num; for (num = 0; num < 12;++num) { if(!SkipSpaces(&sz) || !IsNumeric(*sz))break; sz = fast_atof_move(sz,data[num]); } if (num != 12 && num != 9) { DefaultLogger::get()->error("A line may have either 9 or 12 floats and an optional texture"); continue; } MeshInformation* output = NULL; const char* sz2 = sz; unsigned int length; if (!IsLineEnd(*sz)) { while (!IsSpaceOrNewLine(*sz2))++sz2; length = (unsigned int)(sz2-sz); } else if (9 == num) { sz = "%default%"; length = 9; } else { sz = ""; length = 0; } // search in the list of meshes whether we have one with this texture for (std::vector< MeshInformation >::iterator it = (*curGroup).meshes.begin(), end = (*curGroup).meshes.end(); it != end; ++it) { if (length == (*it).name.length() && (length ? !::strcmp(sz,(*it).name.c_str()) : true)) { output = &(*it); break; } } // if we don't have the mesh, create it if (!output) { (*curGroup).meshes.push_back(MeshInformation(std::string(sz,length))); output = &((*curGroup).meshes.back()); } if (12 == num) { aiColor4D v(data[0],data[1],data[2],1.0f); output->colors.push_back(v); output->colors.push_back(v); output->colors.push_back(v); output->vertices.push_back(aiVector3D(data[3],data[4],data[5])); output->vertices.push_back(aiVector3D(data[6],data[7],data[8])); output->vertices.push_back(aiVector3D(data[9],data[10],data[11])); } else { output->vertices.push_back(aiVector3D(data[0],data[1],data[2])); output->vertices.push_back(aiVector3D(data[3],data[4],data[5])); output->vertices.push_back(aiVector3D(data[6],data[7],data[8])); } } } pScene->mRootNode = new aiNode(); pScene->mRootNode->mName.Set("<RawRoot>"); // count the number of valid groups // (meshes can't be empty) for (std::vector< GroupInformation >::iterator it = outGroups.begin(), end = outGroups.end(); it != end;++it) { if (!(*it).meshes.empty()) { ++pScene->mRootNode->mNumChildren; pScene->mNumMeshes += (unsigned int)(*it).meshes.size(); } } if (!pScene->mNumMeshes) { throw new ImportErrorException("RAW: No meshes loaded. The file seems to be corrupt or empty."); } pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; aiNode** cc; if (1 == pScene->mRootNode->mNumChildren) { cc = &pScene->mRootNode; pScene->mRootNode->mNumChildren = 0; } else cc = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren]; pScene->mNumMaterials = pScene->mNumMeshes; aiMaterial** mats = pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; unsigned int meshIdx = 0; for (std::vector< GroupInformation >::iterator it = outGroups.begin(), end = outGroups.end(); it != end;++it) { if ((*it).meshes.empty())continue; aiNode* node; if (pScene->mRootNode->mNumChildren) { node = *cc = new aiNode(); node->mParent = pScene->mRootNode; } else node = *cc;++cc; node->mName.Set((*it).name); // add all meshes node->mNumMeshes = (unsigned int)(*it).meshes.size(); unsigned int* pi = node->mMeshes = new unsigned int[ node->mNumMeshes ]; for (std::vector< MeshInformation >::iterator it2 = (*it).meshes.begin(), end2 = (*it).meshes.end(); it2 != end2; ++it2) { ai_assert(!(*it2).vertices.empty()); // allocate the mesh *pi++ = meshIdx; aiMesh* mesh = pScene->mMeshes[meshIdx] = new aiMesh(); mesh->mMaterialIndex = meshIdx++; mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // allocate storage for the vertex components and copy them mesh->mNumVertices = (unsigned int)(*it2).vertices.size(); mesh->mVertices = new aiVector3D[ mesh->mNumVertices ]; ::memcpy(mesh->mVertices,&(*it2).vertices[0],sizeof(aiVector3D)*mesh->mNumVertices); if ((*it2).colors.size()) { ai_assert((*it2).colors.size() == mesh->mNumVertices); mesh->mColors[0] = new aiColor4D[ mesh->mNumVertices ]; ::memcpy(mesh->mColors[0],&(*it2).colors[0],sizeof(aiColor4D)*mesh->mNumVertices); } // generate triangles ai_assert(0 == mesh->mNumVertices % 3); aiFace* fc = mesh->mFaces = new aiFace[ mesh->mNumFaces = mesh->mNumVertices/3 ]; aiFace* const fcEnd = fc + mesh->mNumFaces; unsigned int n = 0; while (fc != fcEnd) { aiFace& f = *fc++; f.mIndices = new unsigned int[f.mNumIndices = 3]; for (unsigned int m = 0; m < 3;++m) f.mIndices[m] = n++; } // generate a material for the mesh MaterialHelper* mat = new MaterialHelper(); aiColor4D clr(1.0f,1.0f,1.0f,1.0f); if ("%default%" == (*it2).name) // a gray default material { clr.r = clr.g = clr.b = 0.6f; } else if ((*it2).name.length() > 0) // a texture { aiString s; s.Set((*it2).name); mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); } mat->AddProperty<aiColor4D>(&clr,1,AI_MATKEY_COLOR_DIFFUSE); *mats++ = mat; } } }
// ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void DXFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile)); // Check whether we can read from the file if( file.get() == NULL) { throw DeadlyImportError( "Failed to open DXF file " + pFile + ""); } // read the contents of the file in a buffer std::vector<char> buffer2; TextFileToBuffer(file.get(),buffer2); buffer = &buffer2[0]; bRepeat = false; mDefaultLayer = NULL; // check whether this is a binaray DXF file - we can't read binary DXF files :-( if (!strncmp(AI_DXF_BINARY_IDENT,buffer,AI_DXF_BINARY_IDENT_LEN)) throw DeadlyImportError("DXF: Binary files are not supported at the moment"); // now get all lines of the file while (GetNextToken()) { if (2 == groupCode) { // ENTITIES and BLOCKS sections - skip the whole rest, no need to waste our time with them if (!::strcmp(cursor,"ENTITIES") || !::strcmp(cursor,"BLOCKS")) { if (!ParseEntities()) break; else bRepeat = true; } // other sections - skip them to make sure there will be no name conflicts else { while ( GetNextToken()) { if (!::strcmp(cursor,"ENDSEC")) break; } } } // print comment strings else if (999 == groupCode) { DefaultLogger::get()->info(std::string( cursor )); } else if (!groupCode && !::strcmp(cursor,"EOF")) break; } // find out how many valud layers we have for (std::vector<LayerInfo>::const_iterator it = mLayers.begin(),end = mLayers.end(); it != end;++it) { if (!(*it).vPositions.empty()) ++pScene->mNumMeshes; } if (!pScene->mNumMeshes) throw DeadlyImportError("DXF: this file contains no 3d data"); pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ]; unsigned int m = 0; for (std::vector<LayerInfo>::const_iterator it = mLayers.begin(),end = mLayers.end();it != end;++it) { if ((*it).vPositions.empty()) { continue; } // generate the output mesh aiMesh* pMesh = pScene->mMeshes[m++] = new aiMesh(); const std::vector<aiVector3D>& vPositions = (*it).vPositions; const std::vector<aiColor4D>& vColors = (*it).vColors; // check whether we need vertex colors here aiColor4D* clrOut = NULL; const aiColor4D* clr = NULL; for (std::vector<aiColor4D>::const_iterator it2 = (*it).vColors.begin(), end2 = (*it).vColors.end();it2 != end2; ++it2) { if ((*it2).r == (*it2).r) /* qnan? */ { clrOut = pMesh->mColors[0] = new aiColor4D[vPositions.size()]; for (unsigned int i = 0; i < vPositions.size();++i) clrOut[i] = aiColor4D(0.6f,0.6f,0.6f,1.0f); clr = &vColors[0]; break; } } pMesh->mNumFaces = (unsigned int)vPositions.size() / 4u; pMesh->mFaces = new aiFace[pMesh->mNumFaces]; aiVector3D* vpOut = pMesh->mVertices = new aiVector3D[vPositions.size()]; const aiVector3D* vp = &vPositions[0]; for (unsigned int i = 0; i < pMesh->mNumFaces;++i) { aiFace& face = pMesh->mFaces[i]; // check whether we need four, three or two indices here if (vp[1] == vp[2]) { face.mNumIndices = 2; } else if (vp[3] == vp[2]) { face.mNumIndices = 3; } else face.mNumIndices = 4; face.mIndices = new unsigned int[face.mNumIndices]; for (unsigned int a = 0; a < face.mNumIndices;++a) { *vpOut++ = vp[a]; if (clr) { if (is_not_qnan( clr[a].r )) { *clrOut = clr[a]; } ++clrOut; } face.mIndices[a] = pMesh->mNumVertices++; } vp += 4; } } // generate the output scene graph pScene->mRootNode = new aiNode(); pScene->mRootNode->mName.Set("<DXF_ROOT>"); if (1 == pScene->mNumMeshes) { pScene->mRootNode->mMeshes = new unsigned int[ pScene->mRootNode->mNumMeshes = 1 ]; pScene->mRootNode->mMeshes[0] = 0; } else { pScene->mRootNode->mChildren = new aiNode*[ pScene->mRootNode->mNumChildren = pScene->mNumMeshes ]; for (m = 0; m < pScene->mRootNode->mNumChildren;++m) { aiNode* p = pScene->mRootNode->mChildren[m] = new aiNode(); p->mName.length = ::strlen( mLayers[m].name ); strcpy_s(p->mName.data, mLayers[m].name); p->mMeshes = new unsigned int[p->mNumMeshes = 1]; p->mMeshes[0] = m; p->mParent = pScene->mRootNode; } } // generate a default material MaterialHelper* pcMat = new MaterialHelper(); aiString s; s.Set(AI_DEFAULT_MATERIAL_NAME); pcMat->AddProperty(&s, AI_MATKEY_NAME); aiColor4D clrDiffuse(0.6f,0.6f,0.6f,1.0f); pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE); clrDiffuse = aiColor4D(1.0f,1.0f,1.0f,1.0f); pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR); clrDiffuse = aiColor4D(0.05f,0.05f,0.05f,1.0f); pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT); pScene->mNumMaterials = 1; pScene->mMaterials = new aiMaterial*[1]; pScene->mMaterials[0] = pcMat; // flip winding order to be ccw FlipWindingOrderProcess flipper; flipper.Execute(pScene); // --- everything destructs automatically --- }
// ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void MD2Importer::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile)); // Check whether we can read from the file if( file.get() == NULL) throw new ImportErrorException( "Failed to open MD2 file " + pFile + ""); // check whether the md3 file is large enough to contain // at least the file header fileSize = (unsigned int)file->FileSize(); if( fileSize < sizeof(MD2::Header)) throw new ImportErrorException( "MD2 File is too small"); std::vector<uint8_t> mBuffer2(fileSize); file->Read(&mBuffer2[0], 1, fileSize); mBuffer = &mBuffer2[0]; m_pcHeader = (BE_NCONST MD2::Header*)mBuffer; #ifdef AI_BUILD_BIG_ENDIAN ByteSwap::Swap4(&m_pcHeader->frameSize); ByteSwap::Swap4(&m_pcHeader->magic); ByteSwap::Swap4(&m_pcHeader->numFrames); ByteSwap::Swap4(&m_pcHeader->numGlCommands); ByteSwap::Swap4(&m_pcHeader->numSkins); ByteSwap::Swap4(&m_pcHeader->numTexCoords); ByteSwap::Swap4(&m_pcHeader->numTriangles); ByteSwap::Swap4(&m_pcHeader->numVertices); ByteSwap::Swap4(&m_pcHeader->offsetEnd); ByteSwap::Swap4(&m_pcHeader->offsetFrames); ByteSwap::Swap4(&m_pcHeader->offsetGlCommands); ByteSwap::Swap4(&m_pcHeader->offsetSkins); ByteSwap::Swap4(&m_pcHeader->offsetTexCoords); ByteSwap::Swap4(&m_pcHeader->offsetTriangles); ByteSwap::Swap4(&m_pcHeader->skinHeight); ByteSwap::Swap4(&m_pcHeader->skinWidth); ByteSwap::Swap4(&m_pcHeader->version); #endif ValidateHeader(); // there won't be more than one mesh inside the file pScene->mNumMaterials = 1; pScene->mRootNode = new aiNode(); pScene->mRootNode->mNumMeshes = 1; pScene->mRootNode->mMeshes = new unsigned int[1]; pScene->mRootNode->mMeshes[0] = 0; pScene->mMaterials = new aiMaterial*[1]; pScene->mMaterials[0] = new MaterialHelper(); pScene->mNumMeshes = 1; pScene->mMeshes = new aiMesh*[1]; aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh(); pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // navigate to the begin of the frame data BE_NCONST MD2::Frame* pcFrame = (BE_NCONST MD2::Frame*) ((uint8_t*) m_pcHeader + m_pcHeader->offsetFrames); pcFrame += configFrameID; // navigate to the begin of the triangle data MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*) m_pcHeader + m_pcHeader->offsetTriangles); // navigate to the begin of the tex coords data BE_NCONST MD2::TexCoord* pcTexCoords = (BE_NCONST MD2::TexCoord*) ((uint8_t*) m_pcHeader + m_pcHeader->offsetTexCoords); // navigate to the begin of the vertex data BE_NCONST MD2::Vertex* pcVerts = (BE_NCONST MD2::Vertex*) (pcFrame->vertices); #ifdef AI_BUILD_BIG_ENDIAN for (uint32_t i = 0; i< m_pcHeader->numTriangles; ++i) { for (unsigned int p = 0; p < 3;++p) { ByteSwap::Swap2(& pcTriangles[i].textureIndices[p]); ByteSwap::Swap2(& pcTriangles[i].vertexIndices[p]); } } for (uint32_t i = 0; i < m_pcHeader->offsetTexCoords;++i) { ByteSwap::Swap2(& pcTexCoords[i].s); ByteSwap::Swap2(& pcTexCoords[i].t); } ByteSwap::Swap4( & pcFrame->scale[0] ); ByteSwap::Swap4( & pcFrame->scale[1] ); ByteSwap::Swap4( & pcFrame->scale[2] ); ByteSwap::Swap4( & pcFrame->translate[0] ); ByteSwap::Swap4( & pcFrame->translate[1] ); ByteSwap::Swap4( & pcFrame->translate[2] ); #endif pcMesh->mNumFaces = m_pcHeader->numTriangles; pcMesh->mFaces = new aiFace[m_pcHeader->numTriangles]; // allocate output storage pcMesh->mNumVertices = (unsigned int)pcMesh->mNumFaces*3; pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; // Not sure whether there are MD2 files without texture coordinates // NOTE: texture coordinates can be there without a texture, // but a texture can't be there without a valid UV channel MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0]; const int iMode = (int)aiShadingMode_Gouraud; pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); if (m_pcHeader->numTexCoords && m_pcHeader->numSkins) { // navigate to the first texture associated with the mesh const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)m_pcHeader + m_pcHeader->offsetSkins); aiColor3D clr; clr.b = clr.g = clr.r = 1.0f; pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR); clr.b = clr.g = clr.r = 0.05f; pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT); if (pcSkins->name[0]) { aiString szString; const size_t iLen = ::strlen(pcSkins->name); ::memcpy(szString.data,pcSkins->name,iLen); szString.data[iLen] = '\0'; szString.length = iLen; pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0)); } else{ DefaultLogger::get()->warn("Texture file name has zero length. It will be skipped."); } } else { // apply a default material aiColor3D clr; clr.b = clr.g = clr.r = 0.6f; pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR); clr.b = clr.g = clr.r = 0.05f; pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT); aiString szName; szName.Set(AI_DEFAULT_TEXTURED_MATERIAL_NAME); pcHelper->AddProperty(&szName,AI_MATKEY_NAME); aiString sz; // TODO: Try to guess the name of the texture file from the model file name sz.Set("$texture_dummy.bmp"); pcHelper->AddProperty(&sz,AI_MATKEY_TEXTURE_DIFFUSE(0)); } // now read all triangles of the first frame, apply scaling and translation unsigned int iCurrent = 0; float fDivisorU = 1.0f,fDivisorV = 1.0f; if (m_pcHeader->numTexCoords) { // allocate storage for texture coordinates, too pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; pcMesh->mNumUVComponents[0] = 2; // check whether the skin width or height are zero (this would // cause a division through zero) if (!m_pcHeader->skinWidth) { DefaultLogger::get()->error("MD2: No valid skin width given"); } else fDivisorU = (float)m_pcHeader->skinWidth; if (!m_pcHeader->skinHeight){ DefaultLogger::get()->error("MD2: No valid skin height given"); } else fDivisorV = (float)m_pcHeader->skinHeight; } for (unsigned int i = 0; i < (unsigned int)m_pcHeader->numTriangles;++i) { // Allocate the face pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3]; pScene->mMeshes[0]->mFaces[i].mNumIndices = 3; // copy texture coordinates // check whether they are different from the previous value at this index. // In this case, create a full separate set of vertices/normals/texcoords for (unsigned int c = 0; c < 3;++c,++iCurrent) { // validate vertex indices register unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c]; if (iIndex >= m_pcHeader->numVertices) { DefaultLogger::get()->error("MD2: Vertex index is outside the allowed range"); iIndex = m_pcHeader->numVertices-1; } // read x,y, and z component of the vertex aiVector3D& vec = pcMesh->mVertices[iCurrent]; vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0]; vec.x += pcFrame->translate[0]; vec.y = (float)pcVerts[iIndex].vertex[1] * pcFrame->scale[1]; vec.y += pcFrame->translate[1]; vec.z = (float)pcVerts[iIndex].vertex[2] * pcFrame->scale[2]; vec.z += pcFrame->translate[2]; // read the normal vector from the precalculated normal table aiVector3D& vNormal = pcMesh->mNormals[iCurrent]; LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal); // flip z and y to become right-handed std::swap((float&)vNormal.z,(float&)vNormal.y); std::swap((float&)vec.z,(float&)vec.y); if (m_pcHeader->numTexCoords) { // validate texture coordinates iIndex = pcTriangles[i].textureIndices[c]; if (iIndex >= m_pcHeader->numTexCoords) { DefaultLogger::get()->error("MD2: UV index is outside the allowed range"); iIndex = m_pcHeader->numTexCoords-1; } aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent]; // the texture coordinates are absolute values but we // need relative values between 0 and 1 pcOut.x = pcTexCoords[iIndex].s / fDivisorU; pcOut.y = 1.f-pcTexCoords[iIndex].t / fDivisorV; } pScene->mMeshes[0]->mFaces[i].mIndices[c] = iCurrent; } } }
// ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void MDCImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile)); // Check whether we can read from the file if( file.get() == NULL) throw DeadlyImportError( "Failed to open MDC file " + pFile + "."); // check whether the mdc file is large enough to contain the file header fileSize = (unsigned int)file->FileSize(); if( fileSize < sizeof(MDC::Header)) throw DeadlyImportError( "MDC File is too small."); std::vector<unsigned char> mBuffer2(fileSize); file->Read( &mBuffer2[0], 1, fileSize); mBuffer = &mBuffer2[0]; // validate the file header this->pcHeader = (BE_NCONST MDC::Header*)this->mBuffer; this->ValidateHeader(); std::vector<std::string> aszShaders; // get a pointer to the frame we want to read BE_NCONST MDC::Frame* pcFrame = (BE_NCONST MDC::Frame*)(this->mBuffer+ this->pcHeader->ulOffsetBorderFrames); // no need to swap the other members, we won't need them pcFrame += configFrameID; AI_SWAP4( pcFrame->localOrigin[0] ); AI_SWAP4( pcFrame->localOrigin[1] ); AI_SWAP4( pcFrame->localOrigin[2] ); // get the number of valid surfaces BE_NCONST MDC::Surface* pcSurface, *pcSurface2; pcSurface = pcSurface2 = (BE_NCONST MDC::Surface*)(mBuffer + pcHeader->ulOffsetSurfaces); unsigned int iNumShaders = 0; for (unsigned int i = 0; i < pcHeader->ulNumSurfaces;++i) { // validate the surface header this->ValidateSurfaceHeader(pcSurface2); if (pcSurface2->ulNumVertices && pcSurface2->ulNumTriangles)++pScene->mNumMeshes; iNumShaders += pcSurface2->ulNumShaders; pcSurface2 = (BE_NCONST MDC::Surface*)((int8_t*)pcSurface2 + pcSurface2->ulOffsetEnd); } aszShaders.reserve(iNumShaders); pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; // necessary that we don't crash if an exception occurs for (unsigned int i = 0; i < pScene->mNumMeshes;++i) pScene->mMeshes[i] = NULL; // now read all surfaces unsigned int iDefaultMatIndex = 0xffffffff; for (unsigned int i = 0, iNum = 0; i < pcHeader->ulNumSurfaces;++i) { if (!pcSurface->ulNumVertices || !pcSurface->ulNumTriangles)continue; aiMesh* pcMesh = pScene->mMeshes[iNum++] = new aiMesh(); pcMesh->mNumFaces = pcSurface->ulNumTriangles; pcMesh->mNumVertices = pcMesh->mNumFaces * 3; // store the name of the surface for use as node name. // FIX: make sure there is a 0 termination const_cast<char&>(pcSurface->ucName[AI_MDC_MAXQPATH-1]) = '\0'; pcMesh->mTextureCoords[3] = (aiVector3D*)pcSurface->ucName; // go to the first shader in the file. ignore the others. if (pcSurface->ulNumShaders) { const MDC::Shader* pcShader = (const MDC::Shader*)((int8_t*)pcSurface + pcSurface->ulOffsetShaders); pcMesh->mMaterialIndex = (unsigned int)aszShaders.size(); // create a new shader aszShaders.push_back(std::string( pcShader->ucName, std::min( ::strlen(pcShader->ucName),sizeof(pcShader->ucName)) )); } // need to create a default material else if (0xffffffff == iDefaultMatIndex) { pcMesh->mMaterialIndex = iDefaultMatIndex = (unsigned int)aszShaders.size(); aszShaders.push_back(std::string()); } // otherwise assign a reference to the default material else pcMesh->mMaterialIndex = iDefaultMatIndex; // allocate output storage for the mesh aiVector3D* pcVertCur = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; aiVector3D* pcNorCur = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; aiVector3D* pcUVCur = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; aiFace* pcFaceCur = pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; // create all vertices/faces BE_NCONST MDC::Triangle* pcTriangle = (BE_NCONST MDC::Triangle*) ((int8_t*)pcSurface+pcSurface->ulOffsetTriangles); BE_NCONST MDC::TexturCoord* const pcUVs = (BE_NCONST MDC::TexturCoord*) ((int8_t*)pcSurface+pcSurface->ulOffsetTexCoords); // get a pointer to the uncompressed vertices int16_t iOfs = *((int16_t*) ((int8_t*) pcSurface + pcSurface->ulOffsetFrameBaseFrames) + this->configFrameID); AI_SWAP2(iOfs); BE_NCONST MDC::BaseVertex* const pcVerts = (BE_NCONST MDC::BaseVertex*) ((int8_t*)pcSurface+pcSurface->ulOffsetBaseVerts) + ((int)iOfs * pcSurface->ulNumVertices * 4); // do the main swapping stuff ... #if (defined AI_BUILD_BIG_ENDIAN) // swap all triangles for (unsigned int i = 0; i < pcSurface->ulNumTriangles;++i) { AI_SWAP4( pcTriangle[i].aiIndices[0] ); AI_SWAP4( pcTriangle[i].aiIndices[1] ); AI_SWAP4( pcTriangle[i].aiIndices[2] ); } // swap all vertices for (unsigned int i = 0; i < pcSurface->ulNumVertices*pcSurface->ulNumBaseFrames;++i) { AI_SWAP2( pcVerts->normal ); AI_SWAP2( pcVerts->x ); AI_SWAP2( pcVerts->y ); AI_SWAP2( pcVerts->z ); } // swap all texture coordinates for (unsigned int i = 0; i < pcSurface->ulNumVertices;++i) { AI_SWAP4( pcUVs->v ); AI_SWAP4( pcUVs->v ); } #endif const MDC::CompressedVertex* pcCVerts = NULL; int16_t* mdcCompVert = NULL; // access compressed frames for large frame numbers, but never for the first if( this->configFrameID && pcSurface->ulNumCompFrames > 0 ) { mdcCompVert = (int16_t*) ((int8_t*)pcSurface+pcSurface->ulOffsetFrameCompFrames) + this->configFrameID; AI_SWAP2P(mdcCompVert); if( *mdcCompVert >= 0 ) { pcCVerts = (const MDC::CompressedVertex*)((int8_t*)pcSurface + pcSurface->ulOffsetCompVerts) + *mdcCompVert * pcSurface->ulNumVertices; } else mdcCompVert = NULL; } // copy all faces for (unsigned int iFace = 0; iFace < pcSurface->ulNumTriangles;++iFace, ++pcTriangle,++pcFaceCur) { const unsigned int iOutIndex = iFace*3; pcFaceCur->mNumIndices = 3; pcFaceCur->mIndices = new unsigned int[3]; for (unsigned int iIndex = 0; iIndex < 3;++iIndex, ++pcVertCur,++pcUVCur,++pcNorCur) { uint32_t quak = pcTriangle->aiIndices[iIndex]; if (quak >= pcSurface->ulNumVertices) { DefaultLogger::get()->error("MDC vertex index is out of range"); quak = pcSurface->ulNumVertices-1; } // compressed vertices? if (mdcCompVert) { MDC::BuildVertex(*pcFrame,pcVerts[quak],pcCVerts[quak], *pcVertCur,*pcNorCur); } else { // copy position pcVertCur->x = pcVerts[quak].x * AI_MDC_BASE_SCALING; pcVertCur->y = pcVerts[quak].y * AI_MDC_BASE_SCALING; pcVertCur->z = pcVerts[quak].z * AI_MDC_BASE_SCALING; // copy normals MD3::LatLngNormalToVec3( pcVerts[quak].normal, &pcNorCur->x ); // copy texture coordinates pcUVCur->x = pcUVs[quak].u; pcUVCur->y = 1.0f-pcUVs[quak].v; // DX to OGL } pcVertCur->x += pcFrame->localOrigin[0] ; pcVertCur->y += pcFrame->localOrigin[1] ; pcVertCur->z += pcFrame->localOrigin[2] ; } // swap the face order - DX to OGL pcFaceCur->mIndices[0] = iOutIndex + 2; pcFaceCur->mIndices[1] = iOutIndex + 1; pcFaceCur->mIndices[2] = iOutIndex + 0; } pcSurface = (BE_NCONST MDC::Surface*)((int8_t*)pcSurface + pcSurface->ulOffsetEnd); } // create a flat node graph with a root node and one child for each surface if (!pScene->mNumMeshes) throw DeadlyImportError( "Invalid MDC file: File contains no valid mesh"); else if (1 == pScene->mNumMeshes) { pScene->mRootNode = new aiNode(); pScene->mRootNode->mName.Set(std::string((const char*)pScene->mMeshes[0]->mTextureCoords[3])); pScene->mRootNode->mNumMeshes = 1; pScene->mRootNode->mMeshes = new unsigned int[1]; pScene->mRootNode->mMeshes[0] = 0; } else { pScene->mRootNode = new aiNode(); pScene->mRootNode->mNumChildren = pScene->mNumMeshes; pScene->mRootNode->mChildren = new aiNode*[pScene->mNumMeshes]; pScene->mRootNode->mName.Set("<root>"); for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { aiNode* pcNode = pScene->mRootNode->mChildren[i] = new aiNode(); pcNode->mParent = pScene->mRootNode; pcNode->mName.Set(std::string((const char*)pScene->mMeshes[i]->mTextureCoords[3])); pcNode->mNumMeshes = 1; pcNode->mMeshes = new unsigned int[1]; pcNode->mMeshes[0] = i; } } // make sure we invalidate the pointer to the mesh name for (unsigned int i = 0; i < pScene->mNumMeshes;++i) pScene->mMeshes[i]->mTextureCoords[3] = NULL; // create materials pScene->mNumMaterials = (unsigned int)aszShaders.size(); pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { MaterialHelper* pcMat = new MaterialHelper(); pScene->mMaterials[i] = pcMat; const std::string& name = aszShaders[i]; int iMode = (int)aiShadingMode_Gouraud; pcMat->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); // add a small ambient color value - RtCW seems to have one aiColor3D clr; clr.b = clr.g = clr.r = 0.05f; pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT); if (name.length())clr.b = clr.g = clr.r = 1.0f; else clr.b = clr.g = clr.r = 0.6f; pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR); if (name.length()) { aiString path; path.Set(name); pcMat->AddProperty(&path,AI_MATKEY_TEXTURE_DIFFUSE(0)); } } }
// ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void IRRMeshImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile)); // Check whether we can read from the file if( file.get() == NULL) throw DeadlyImportError( "Failed to open IRRMESH file " + pFile + ""); // Construct the irrXML parser CIrrXML_IOStreamReader st(file.get()); reader = createIrrXMLReader((IFileReadCallBack*) &st); // final data std::vector<aiMaterial*> materials; std::vector<aiMesh*> meshes; materials.reserve (5); meshes.reserve (5); // temporary data - current mesh buffer aiMaterial* curMat = NULL; aiMesh* curMesh = NULL; unsigned int curMatFlags; std::vector<aiVector3D> curVertices,curNormals,curTangents,curBitangents; std::vector<aiColor4D> curColors; std::vector<aiVector3D> curUVs,curUV2s; // some temporary variables int textMeaning = 0; int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents bool useColors = false; // Parse the XML file while (reader->read()) { switch (reader->getNodeType()) { case EXN_ELEMENT: if (!ASSIMP_stricmp(reader->getNodeName(),"buffer") && (curMat || curMesh)) { // end of previous buffer. A material and a mesh should be there if ( !curMat || !curMesh) { DefaultLogger::get()->error("IRRMESH: A buffer must contain a mesh and a material"); delete curMat; delete curMesh; } else { materials.push_back(curMat); meshes.push_back(curMesh); } curMat = NULL; curMesh = NULL; curVertices.clear(); curColors.clear(); curNormals.clear(); curUV2s.clear(); curUVs.clear(); curTangents.clear(); curBitangents.clear(); } if (!ASSIMP_stricmp(reader->getNodeName(),"material")) { if (curMat) { DefaultLogger::get()->warn("IRRMESH: Only one material description per buffer, please"); delete curMat;curMat = NULL; } curMat = ParseMaterial(curMatFlags); } /* no else here! */ if (!ASSIMP_stricmp(reader->getNodeName(),"vertices")) { int num = reader->getAttributeValueAsInt("vertexCount"); if (!num) { // This is possible ... remove the mesh from the list and skip further reading DefaultLogger::get()->warn("IRRMESH: Found mesh with zero vertices"); delete curMat;curMat = NULL; curMesh = NULL; textMeaning = 0; continue; } curVertices.reserve (num); curNormals.reserve (num); curColors.reserve (num); curUVs.reserve (num); // Determine the file format const char* t = reader->getAttributeValueSafe("type"); if (!ASSIMP_stricmp("2tcoords", t)) { curUV2s.reserve (num); vertexFormat = 1; if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) { // ********************************************************* // We have a second texture! So use this UV channel // for it. The 2nd texture can be either a normal // texture (solid_2layer or lightmap_xxx) or a normal // map (normal_..., parallax_...) // ********************************************************* int idx = 1; MaterialHelper* mat = ( MaterialHelper* ) curMat; if (curMatFlags & AI_IRRMESH_MAT_lightmap){ mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_LIGHTMAP(0)); } else if (curMatFlags & AI_IRRMESH_MAT_normalmap_solid){ mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_NORMALS(0)); } else if (curMatFlags & AI_IRRMESH_MAT_solid_2layer) { mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_DIFFUSE(1)); } } } else if (!ASSIMP_stricmp("tangents", t)) { curTangents.reserve (num); curBitangents.reserve (num); vertexFormat = 2; } else if (ASSIMP_stricmp("standard", t)) { delete curMat; DefaultLogger::get()->warn("IRRMESH: Unknown vertex format"); } else vertexFormat = 0; textMeaning = 1; } else if (!ASSIMP_stricmp(reader->getNodeName(),"indices")) { if (curVertices.empty() && curMat) { delete curMat; throw DeadlyImportError("IRRMESH: indices must come after vertices"); } textMeaning = 2; // start a new mesh curMesh = new aiMesh(); // allocate storage for all faces curMesh->mNumVertices = reader->getAttributeValueAsInt("indexCount"); if (!curMesh->mNumVertices) { // This is possible ... remove the mesh from the list and skip further reading DefaultLogger::get()->warn("IRRMESH: Found mesh with zero indices"); // mesh - away delete curMesh; curMesh = NULL; // material - away delete curMat;curMat = NULL; textMeaning = 0; continue; } if (curMesh->mNumVertices % 3) { DefaultLogger::get()->warn("IRRMESH: Number if indices isn't divisible by 3"); } curMesh->mNumFaces = curMesh->mNumVertices / 3; curMesh->mFaces = new aiFace[curMesh->mNumFaces]; // setup some members curMesh->mMaterialIndex = (unsigned int)materials.size(); curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // allocate storage for all vertices curMesh->mVertices = new aiVector3D[curMesh->mNumVertices]; if (curNormals.size() == curVertices.size()) { curMesh->mNormals = new aiVector3D[curMesh->mNumVertices]; } if (curTangents.size() == curVertices.size()) { curMesh->mTangents = new aiVector3D[curMesh->mNumVertices]; } if (curBitangents.size() == curVertices.size()) { curMesh->mBitangents = new aiVector3D[curMesh->mNumVertices]; } if (curColors.size() == curVertices.size() && useColors) { curMesh->mColors[0] = new aiColor4D[curMesh->mNumVertices]; } if (curUVs.size() == curVertices.size()) { curMesh->mTextureCoords[0] = new aiVector3D[curMesh->mNumVertices]; } if (curUV2s.size() == curVertices.size()) { curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices]; } } break; case EXN_TEXT: { const char* sz = reader->getNodeData(); if (textMeaning == 1) { textMeaning = 0; // read vertices do { SkipSpacesAndLineEnd(&sz); aiVector3D temp;aiColor4D c; // Read the vertex position sz = fast_atof_move(sz,(float&)temp.x); SkipSpaces(&sz); sz = fast_atof_move(sz,(float&)temp.y); SkipSpaces(&sz); sz = fast_atof_move(sz,(float&)temp.z); SkipSpaces(&sz); curVertices.push_back(temp); // Read the vertex normals sz = fast_atof_move(sz,(float&)temp.x); SkipSpaces(&sz); sz = fast_atof_move(sz,(float&)temp.y); SkipSpaces(&sz); sz = fast_atof_move(sz,(float&)temp.z); SkipSpaces(&sz); curNormals.push_back(temp); // read the vertex colors uint32_t clr = strtol16(sz,&sz); ColorFromARGBPacked(clr,c); if (!curColors.empty() && c != *(curColors.end()-1)) useColors = true; curColors.push_back(c); SkipSpaces(&sz); // read the first UV coordinate set sz = fast_atof_move(sz,(float&)temp.x); SkipSpaces(&sz); sz = fast_atof_move(sz,(float&)temp.y); SkipSpaces(&sz); temp.z = 0.f; temp.y = 1.f - temp.y; // DX to OGL curUVs.push_back(temp); // read the (optional) second UV coordinate set if (vertexFormat == 1) { sz = fast_atof_move(sz,(float&)temp.x); SkipSpaces(&sz); sz = fast_atof_move(sz,(float&)temp.y); temp.y = 1.f - temp.y; // DX to OGL curUV2s.push_back(temp); } // read optional tangent and bitangent vectors else if (vertexFormat == 2) { // tangents sz = fast_atof_move(sz,(float&)temp.x); SkipSpaces(&sz); sz = fast_atof_move(sz,(float&)temp.z); SkipSpaces(&sz); sz = fast_atof_move(sz,(float&)temp.y); SkipSpaces(&sz); temp.y *= -1.0f; curTangents.push_back(temp); // bitangents sz = fast_atof_move(sz,(float&)temp.x); SkipSpaces(&sz); sz = fast_atof_move(sz,(float&)temp.z); SkipSpaces(&sz); sz = fast_atof_move(sz,(float&)temp.y); SkipSpaces(&sz); temp.y *= -1.0f; curBitangents.push_back(temp); } } /* IMPORTANT: We assume that each vertex is specified in one line. So we can skip the rest of the line - unknown vertex elements are ignored. */ while (SkipLine(&sz)); } else if (textMeaning == 2) { textMeaning = 0; // read indices aiFace* curFace = curMesh->mFaces; aiFace* const faceEnd = curMesh->mFaces + curMesh->mNumFaces; aiVector3D* pcV = curMesh->mVertices; aiVector3D* pcN = curMesh->mNormals; aiVector3D* pcT = curMesh->mTangents; aiVector3D* pcB = curMesh->mBitangents; aiColor4D* pcC0 = curMesh->mColors[0]; aiVector3D* pcT0 = curMesh->mTextureCoords[0]; aiVector3D* pcT1 = curMesh->mTextureCoords[1]; unsigned int curIdx = 0; unsigned int total = 0; while(SkipSpacesAndLineEnd(&sz)) { if (curFace >= faceEnd) { DefaultLogger::get()->error("IRRMESH: Too many indices"); break; } if (!curIdx) { curFace->mNumIndices = 3; curFace->mIndices = new unsigned int[3]; } unsigned int idx = strtol10(sz,&sz); if (idx >= curVertices.size()) { DefaultLogger::get()->error("IRRMESH: Index out of range"); idx = 0; } curFace->mIndices[curIdx] = total++; *pcV++ = curVertices[idx]; if (pcN)*pcN++ = curNormals[idx]; if (pcT)*pcT++ = curTangents[idx]; if (pcB)*pcB++ = curBitangents[idx]; if (pcC0)*pcC0++ = curColors[idx]; if (pcT0)*pcT0++ = curUVs[idx]; if (pcT1)*pcT1++ = curUV2s[idx]; if (++curIdx == 3) { ++curFace; curIdx = 0; } } if (curFace != faceEnd) DefaultLogger::get()->error("IRRMESH: Not enough indices"); // Finish processing the mesh - do some small material workarounds if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) { // Take the opacity value of the current material // from the common vertex color alpha MaterialHelper* mat = (MaterialHelper*)curMat; mat->AddProperty(&curColors[0].a,1,AI_MATKEY_OPACITY); } }} break; default: // GCC complains here ... break; }; } // End of the last buffer. A material and a mesh should be there if (curMat || curMesh) { if ( !curMat || !curMesh) { DefaultLogger::get()->error("IRRMESH: A buffer must contain a mesh and a material"); delete curMat; delete curMesh; } else { materials.push_back(curMat); meshes.push_back(curMesh); } } if (materials.empty()) throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file"); // now generate the output scene pScene->mNumMeshes = (unsigned int)meshes.size(); pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { pScene->mMeshes[i] = meshes[i]; // clean this value ... pScene->mMeshes[i]->mNumUVComponents[3] = 0; } pScene->mNumMaterials = (unsigned int)materials.size(); pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; ::memcpy(pScene->mMaterials,&materials[0],sizeof(void*)*pScene->mNumMaterials); pScene->mRootNode = new aiNode(); pScene->mRootNode->mName.Set("<IRRMesh>"); pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; for (unsigned int i = 0; i < pScene->mNumMeshes;++i) pScene->mRootNode->mMeshes[i] = i; // clean up and return delete reader; AI_DEBUG_INVALIDATE_PTR(reader); }
// ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void UnrealImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { // For any of the 3 files being passed get the three correct paths // First of all, determine file extension std::string::size_type pos = pFile.find_last_of('.'); std::string extension = GetExtension(pFile); std::string d_path,a_path,uc_path; if (extension == "3d") { // jjjj_d.3d // jjjj_a.3d pos = pFile.find_last_of('_'); if (std::string::npos == pos) { throw DeadlyImportError("UNREAL: Unexpected naming scheme"); } extension = pFile.substr(0,pos); } else { extension = pFile.substr(0,pos); } // build proper paths d_path = extension+"_d.3d"; a_path = extension+"_a.3d"; uc_path = extension+".uc"; DefaultLogger::get()->debug("UNREAL: data file is " + d_path); DefaultLogger::get()->debug("UNREAL: aniv file is " + a_path); DefaultLogger::get()->debug("UNREAL: uc file is " + uc_path); // and open the files ... we can't live without them IOStream* p = pIOHandler->Open(d_path); if (!p) throw DeadlyImportError("UNREAL: Unable to open _d file"); StreamReaderLE d_reader(pIOHandler->Open(d_path)); const uint16_t numTris = d_reader.GetI2(); const uint16_t numVert = d_reader.GetI2(); d_reader.IncPtr(44); if (!numTris || numVert < 3) throw DeadlyImportError("UNREAL: Invalid number of vertices/triangles"); // maximum texture index unsigned int maxTexIdx = 0; // collect triangles std::vector<Unreal::Triangle> triangles(numTris); for (std::vector<Unreal::Triangle>::iterator it = triangles.begin(), end = triangles.end();it != end; ++it) { Unreal::Triangle& tri = *it; for (unsigned int i = 0; i < 3;++i) { tri.mVertex[i] = d_reader.GetI2(); if (tri.mVertex[i] >= numTris) { DefaultLogger::get()->warn("UNREAL: vertex index out of range"); tri.mVertex[i] = 0; } } tri.mType = d_reader.GetI1(); // handle mesh flagss? if (configHandleFlags) tri.mType = Unreal::MF_NORMAL_OS; else { // ignore MOD and MASKED for the moment, treat them as two-sided if (tri.mType == Unreal::MF_NORMAL_MOD_TS || tri.mType == Unreal::MF_NORMAL_MASKED_TS) tri.mType = Unreal::MF_NORMAL_TS; } d_reader.IncPtr(1); for (unsigned int i = 0; i < 3;++i) for (unsigned int i2 = 0; i2 < 2;++i2) tri.mTex[i][i2] = d_reader.GetI1(); tri.mTextureNum = d_reader.GetI1(); maxTexIdx = std::max(maxTexIdx,(unsigned int)tri.mTextureNum); d_reader.IncPtr(1); } p = pIOHandler->Open(a_path); if (!p) throw DeadlyImportError("UNREAL: Unable to open _a file"); StreamReaderLE a_reader(pIOHandler->Open(a_path)); // read number of frames const uint32_t numFrames = a_reader.GetI2(); if (configFrameID >= numFrames) throw DeadlyImportError("UNREAL: The requested frame does not exist"); uint32_t st = a_reader.GetI2(); if (st != numVert*4u) throw DeadlyImportError("UNREAL: Unexpected aniv file length"); // skip to our frame a_reader.IncPtr(configFrameID *numVert*4); // collect vertices std::vector<aiVector3D> vertices(numVert); for (std::vector<aiVector3D>::iterator it = vertices.begin(), end = vertices.end(); it != end; ++it) { int32_t val = a_reader.GetI4(); Unreal::DecompressVertex(*it,val); } // list of textures. std::vector< std::pair<unsigned int, std::string> > textures; // allocate the output scene aiNode* nd = pScene->mRootNode = new aiNode(); nd->mName.Set("<UnrealRoot>"); // we can live without the uc file if necessary boost::scoped_ptr<IOStream> pb (pIOHandler->Open(uc_path)); if (pb.get()) { std::vector<char> _data; TextFileToBuffer(pb.get(),_data); const char* data = &_data[0]; std::vector< std::pair< std::string,std::string > > tempTextures; // do a quick search in the UC file for some known, usually texture-related, tags for (;*data;++data) { if (TokenMatchI(data,"#exec",5)) { SkipSpacesAndLineEnd(&data); // #exec TEXTURE IMPORT [...] NAME=jjjjj [...] FILE=jjjj.pcx [...] if (TokenMatchI(data,"TEXTURE",7)) { SkipSpacesAndLineEnd(&data); if (TokenMatchI(data,"IMPORT",6)) { tempTextures.push_back(std::pair< std::string,std::string >()); std::pair< std::string,std::string >& me = tempTextures.back(); for (;!IsLineEnd(*data);++data) { if (!::ASSIMP_strincmp(data,"NAME=",5)) { const char *d = data+=5; for (;!IsSpaceOrNewLine(*data);++data) {}; me.first = std::string(d,(size_t)(data-d)); } else if (!::ASSIMP_strincmp(data,"FILE=",5)) { const char *d = data+=5; for (;!IsSpaceOrNewLine(*data);++data) {}; me.second = std::string(d,(size_t)(data-d)); } } if (!me.first.length() || !me.second.length()) tempTextures.pop_back(); } } // #exec MESHMAP SETTEXTURE MESHMAP=box NUM=1 TEXTURE=Jtex1 // #exec MESHMAP SCALE MESHMAP=box X=0.1 Y=0.1 Z=0.2 else if (TokenMatchI(data,"MESHMAP",7)) { SkipSpacesAndLineEnd(&data); if (TokenMatchI(data,"SETTEXTURE",10)) { textures.push_back(std::pair<unsigned int, std::string>()); std::pair<unsigned int, std::string>& me = textures.back(); for (;!IsLineEnd(*data);++data) { if (!::ASSIMP_strincmp(data,"NUM=",4)) { data += 4; me.first = strtol10(data,&data); } else if (!::ASSIMP_strincmp(data,"TEXTURE=",8)) { data += 8; const char *d = data; for (;!IsSpaceOrNewLine(*data);++data) {}; me.second = std::string(d,(size_t)(data-d)); // try to find matching path names, doesn't care if we don't find them for (std::vector< std::pair< std::string,std::string > >::const_iterator it = tempTextures.begin(); it != tempTextures.end(); ++it) { if ((*it).first == me.second) { me.second = (*it).second; break; } } } } } else if (TokenMatchI(data,"SCALE",5)) { for (;!IsLineEnd(*data);++data) { if (data[0] == 'X' && data[1] == '=') { data = fast_atof_move(data+2,(float&)nd->mTransformation.a1); } else if (data[0] == 'Y' && data[1] == '=') { data = fast_atof_move(data+2,(float&)nd->mTransformation.b2); } else if (data[0] == 'Z' && data[1] == '=') { data = fast_atof_move(data+2,(float&)nd->mTransformation.c3); } } } } } } } else { DefaultLogger::get()->error("Unable to open .uc file"); } std::vector<Unreal::TempMat> materials; materials.reserve(textures.size()*2+5); // find out how many output meshes and materials we'll have and build material indices for (std::vector<Unreal::Triangle>::iterator it = triangles.begin(), end = triangles.end();it != end; ++it) { Unreal::Triangle& tri = *it; Unreal::TempMat mat(tri); std::vector<Unreal::TempMat>::iterator nt = std::find(materials.begin(),materials.end(),mat); if (nt == materials.end()) { // add material tri.matIndex = materials.size(); mat.numFaces = 1; materials.push_back(mat); ++pScene->mNumMeshes; } else { tri.matIndex = static_cast<unsigned int>(nt-materials.begin()); ++nt->numFaces; } } if (!pScene->mNumMeshes) { throw DeadlyImportError("UNREAL: Unable to find valid mesh data"); } // allocate meshes and bind them to the node graph pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = pScene->mNumMeshes]; nd->mNumMeshes = pScene->mNumMeshes; nd->mMeshes = new unsigned int[nd->mNumMeshes]; for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { aiMesh* m = pScene->mMeshes[i] = new aiMesh(); m->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; const unsigned int num = materials[i].numFaces; m->mFaces = new aiFace [num]; m->mVertices = new aiVector3D [num*3]; m->mTextureCoords[0] = new aiVector3D [num*3]; nd->mMeshes[i] = i; // create materials, too MaterialHelper* mat = new MaterialHelper(); pScene->mMaterials[i] = mat; // all white by default - texture rulez aiColor3D color(1.f,1.f,1.f); aiString s; ::sprintf(s.data,"mat%i_tx%i_",i,materials[i].tex); // set the two-sided flag if (materials[i].type == Unreal::MF_NORMAL_TS) { const int twosided = 1; mat->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED); ::strcat(s.data,"ts_"); } else ::strcat(s.data,"os_"); // make TRANS faces 90% opaque that RemRedundantMaterials won't catch us if (materials[i].type == Unreal::MF_NORMAL_TRANS_TS) { const float opac = 0.9f; mat->AddProperty(&opac,1,AI_MATKEY_OPACITY); ::strcat(s.data,"tran_"); } else ::strcat(s.data,"opaq_"); // a special name for the weapon attachment point if (materials[i].type == Unreal::MF_WEAPON_PLACEHOLDER) { s.length = ::sprintf(s.data,"$WeaponTag$"); color = aiColor3D(0.f,0.f,0.f); } // set color and name mat->AddProperty(&color,1,AI_MATKEY_COLOR_DIFFUSE); s.length = ::strlen(s.data); mat->AddProperty(&s,AI_MATKEY_NAME); // set texture, if any const unsigned int tex = materials[i].tex; for (std::vector< std::pair< unsigned int, std::string > >::const_iterator it = textures.begin();it != textures.end();++it) { if ((*it).first == tex) { s.Set((*it).second); mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); break; } } } // fill them. for (std::vector<Unreal::Triangle>::iterator it = triangles.begin(), end = triangles.end();it != end; ++it) { Unreal::Triangle& tri = *it; Unreal::TempMat mat(tri); std::vector<Unreal::TempMat>::iterator nt = std::find(materials.begin(),materials.end(),mat); aiMesh* mesh = pScene->mMeshes[nt-materials.begin()]; aiFace& f = mesh->mFaces[mesh->mNumFaces++]; f.mIndices = new unsigned int[f.mNumIndices = 3]; for (unsigned int i = 0; i < 3;++i,mesh->mNumVertices++) { f.mIndices[i] = mesh->mNumVertices; mesh->mVertices[mesh->mNumVertices] = vertices[ tri.mVertex[i] ]; mesh->mTextureCoords[0][mesh->mNumVertices] = aiVector3D( tri.mTex[i][0] / 255.f, 1.f - tri.mTex[i][1] / 255.f, 0.f); } } // convert to RH MakeLeftHandedProcess hero; hero.Execute(pScene); FlipWindingOrderProcess flipper; flipper.Execute(pScene); }
// ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void MD3Importer::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { mFile = pFile; mScene = pScene; mIOHandler = pIOHandler; // get base path and file name // todo ... move to PathConverter std::string::size_type s = mFile.find_last_of("/\\"); if (s == std::string::npos) { s = 0; } else ++s; filename = mFile.substr(s), path = mFile.substr(0,s); for( std::string::iterator it = filename .begin(); it != filename.end(); ++it) *it = tolower( *it); // Load multi-part model file, if necessary if (configHandleMP) { if (ReadMultipartFile()) return; } boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile)); // Check whether we can read from the file if( file.get() == NULL) throw DeadlyImportError( "Failed to open MD3 file " + pFile + "."); // Check whether the md3 file is large enough to contain the header fileSize = (unsigned int)file->FileSize(); if( fileSize < sizeof(MD3::Header)) throw DeadlyImportError( "MD3 File is too small."); // Allocate storage and copy the contents of the file to a memory buffer std::vector<unsigned char> mBuffer2 (fileSize); file->Read( &mBuffer2[0], 1, fileSize); mBuffer = &mBuffer2[0]; pcHeader = (BE_NCONST MD3::Header*)mBuffer; // Ensure correct endianess #ifdef AI_BUILD_BIG_ENDIAN AI_SWAP4(pcHeader->VERSION); AI_SWAP4(pcHeader->FLAGS); AI_SWAP4(pcHeader->IDENT); AI_SWAP4(pcHeader->NUM_FRAMES); AI_SWAP4(pcHeader->NUM_SKINS); AI_SWAP4(pcHeader->NUM_SURFACES); AI_SWAP4(pcHeader->NUM_TAGS); AI_SWAP4(pcHeader->OFS_EOF); AI_SWAP4(pcHeader->OFS_FRAMES); AI_SWAP4(pcHeader->OFS_SURFACES); AI_SWAP4(pcHeader->OFS_TAGS); #endif // Validate the file header ValidateHeaderOffsets(); // Navigate to the list of surfaces BE_NCONST MD3::Surface* pcSurfaces = (BE_NCONST MD3::Surface*)(mBuffer + pcHeader->OFS_SURFACES); // Navigate to the list of tags BE_NCONST MD3::Tag* pcTags = (BE_NCONST MD3::Tag*)(mBuffer + pcHeader->OFS_TAGS); // Allocate output storage pScene->mNumMeshes = pcHeader->NUM_SURFACES; pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; pScene->mNumMaterials = pcHeader->NUM_SURFACES; pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]; // Set arrays to zero to ensue proper destruction if an exception is raised ::memset(pScene->mMeshes,0,pScene->mNumMeshes*sizeof(aiMesh*)); ::memset(pScene->mMaterials,0,pScene->mNumMaterials*sizeof(aiMaterial*)); // Now read possible skins from .skin file Q3Shader::SkinData skins; ReadSkin(skins); // And check whether we can locate a shader file for this model Q3Shader::ShaderData shaders; ReadShader(shaders); // Adjust all texture paths in the shader const char* header_name = pcHeader->NAME; if (shaders.blocks.size()) { for (std::list< Q3Shader::ShaderDataBlock >::iterator dit = shaders.blocks.begin(); dit != shaders.blocks.end(); ++dit) { ConvertPath((*dit).name.c_str(),header_name,(*dit).name); for (std::list< Q3Shader::ShaderMapBlock >::iterator mit = (*dit).maps.begin(); mit != (*dit).maps.end(); ++mit) { ConvertPath((*mit).name.c_str(),header_name,(*mit).name); } } } // Read all surfaces from the file unsigned int iNum = pcHeader->NUM_SURFACES; unsigned int iNumMaterials = 0; while (iNum-- > 0) { // Ensure correct endianess #ifdef AI_BUILD_BIG_ENDIAN AI_SWAP4(pcSurfaces->FLAGS); AI_SWAP4(pcSurfaces->IDENT); AI_SWAP4(pcSurfaces->NUM_FRAMES); AI_SWAP4(pcSurfaces->NUM_SHADER); AI_SWAP4(pcSurfaces->NUM_TRIANGLES); AI_SWAP4(pcSurfaces->NUM_VERTICES); AI_SWAP4(pcSurfaces->OFS_END); AI_SWAP4(pcSurfaces->OFS_SHADERS); AI_SWAP4(pcSurfaces->OFS_ST); AI_SWAP4(pcSurfaces->OFS_TRIANGLES); AI_SWAP4(pcSurfaces->OFS_XYZNORMAL); #endif // Validate the surface header ValidateSurfaceHeaderOffsets(pcSurfaces); // Navigate to the vertex list of the surface BE_NCONST MD3::Vertex* pcVertices = (BE_NCONST MD3::Vertex*) (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL); // Navigate to the triangle list of the surface BE_NCONST MD3::Triangle* pcTriangles = (BE_NCONST MD3::Triangle*) (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_TRIANGLES); // Navigate to the texture coordinate list of the surface BE_NCONST MD3::TexCoord* pcUVs = (BE_NCONST MD3::TexCoord*) (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_ST); // Navigate to the shader list of the surface BE_NCONST MD3::Shader* pcShaders = (BE_NCONST MD3::Shader*) (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_SHADERS); // If the submesh is empty ignore it if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES) { pcSurfaces = (BE_NCONST MD3::Surface*)(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_END); pScene->mNumMeshes--; continue; } // Allocate output mesh pScene->mMeshes[iNum] = new aiMesh(); aiMesh* pcMesh = pScene->mMeshes[iNum]; std::string _texture_name; const char* texture_name = NULL; // Check whether we have a texture record for this surface in the .skin file std::list< Q3Shader::SkinData::TextureEntry >::iterator it = std::find( skins.textures.begin(), skins.textures.end(), pcSurfaces->NAME ); if (it != skins.textures.end()) { texture_name = &*( _texture_name = (*it).second).begin(); DefaultLogger::get()->debug("MD3: Assigning skin texture " + (*it).second + " to surface " + pcSurfaces->NAME); (*it).resolved = true; // mark entry as resolved } // Get the first shader (= texture?) assigned to the surface if (!texture_name && pcSurfaces->NUM_SHADER) { texture_name = pcShaders->NAME; } std::string convertedPath; if (texture_name) { ConvertPath(texture_name,header_name,convertedPath); } const Q3Shader::ShaderDataBlock* shader = NULL; // Now search the current shader for a record with this name ( // excluding texture file extension) if (shaders.blocks.size()) { std::string::size_type s = convertedPath.find_last_of('.'); if (s == std::string::npos) s = convertedPath.length(); const std::string without_ext = convertedPath.substr(0,s); std::list< Q3Shader::ShaderDataBlock >::const_iterator dit = std::find(shaders.blocks.begin(),shaders.blocks.end(),without_ext); if (dit != shaders.blocks.end()) { // Hurra, wir haben einen. Tolle Sache. shader = &*dit; DefaultLogger::get()->info("Found shader record for " +without_ext ); } else DefaultLogger::get()->warn("Unable to find shader record for " +without_ext ); } MaterialHelper* pcHelper = new MaterialHelper(); const int iMode = (int)aiShadingMode_Gouraud; pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); // Add a small ambient color value - Quake 3 seems to have one aiColor3D clr; clr.b = clr.g = clr.r = 0.05f; pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT); clr.b = clr.g = clr.r = 1.0f; pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR); // use surface name + skin_name as material name aiString name; name.Set("MD3_[" + configSkinFile + "][" + pcSurfaces->NAME + "]"); pcHelper->AddProperty(&name,AI_MATKEY_NAME); if (!shader) { // Setup dummy texture file name to ensure UV coordinates are kept during postprocessing aiString szString; if (convertedPath.length()) { szString.Set(convertedPath); } else { DefaultLogger::get()->warn("Texture file name has zero length. Using default name"); szString.Set("dummy_texture.bmp"); } pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0)); // prevent transparency by default int no_alpha = aiTextureFlags_IgnoreAlpha; pcHelper->AddProperty(&no_alpha,1,AI_MATKEY_TEXFLAGS_DIFFUSE(0)); } else { Q3Shader::ConvertShaderToMaterial(pcHelper,*shader); } pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper; pcMesh->mMaterialIndex = iNumMaterials++; // Ensure correct endianess #ifdef AI_BUILD_BIG_ENDIAN for (uint32_t i = 0; i < pcSurfaces->NUM_VERTICES;++i) { AI_SWAP2( pcVertices[i].NORMAL ); AI_SWAP2( pcVertices[i].X ); AI_SWAP2( pcVertices[i].Y ); AI_SWAP2( pcVertices[i].Z ); AI_SWAP4( pcUVs[i].U ); AI_SWAP4( pcUVs[i].U ); } for (uint32_t i = 0; i < pcSurfaces->NUM_TRIANGLES;++i) { AI_SWAP4(pcTriangles[i].INDEXES[0]); AI_SWAP4(pcTriangles[i].INDEXES[1]); AI_SWAP4(pcTriangles[i].INDEXES[2]); } #endif // Fill mesh information pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; pcMesh->mNumVertices = pcSurfaces->NUM_TRIANGLES*3; pcMesh->mNumFaces = pcSurfaces->NUM_TRIANGLES; pcMesh->mFaces = new aiFace[pcSurfaces->NUM_TRIANGLES]; pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; pcMesh->mNumUVComponents[0] = 2; // Fill in all triangles unsigned int iCurrent = 0; for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_TRIANGLES;++i) { pcMesh->mFaces[i].mIndices = new unsigned int[3]; pcMesh->mFaces[i].mNumIndices = 3; //unsigned int iTemp = iCurrent; for (unsigned int c = 0; c < 3;++c,++iCurrent) { pcMesh->mFaces[i].mIndices[c] = iCurrent; // Read vertices aiVector3D& vec = pcMesh->mVertices[iCurrent]; vec.x = pcVertices[ pcTriangles->INDEXES[c]].X*AI_MD3_XYZ_SCALE; vec.y = pcVertices[ pcTriangles->INDEXES[c]].Y*AI_MD3_XYZ_SCALE; vec.z = pcVertices[ pcTriangles->INDEXES[c]].Z*AI_MD3_XYZ_SCALE; // Convert the normal vector to uncompressed float3 format aiVector3D& nor = pcMesh->mNormals[iCurrent]; LatLngNormalToVec3(pcVertices[pcTriangles->INDEXES[c]].NORMAL,(float*)&nor); // Read texture coordinates pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U; pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V; } // Flip face order if necessary if (!shader || shader->cull == Q3Shader::CULL_CW) { std::swap(pcMesh->mFaces[i].mIndices[2],pcMesh->mFaces[i].mIndices[1]); } pcTriangles++; } // Go to the next surface pcSurfaces = (BE_NCONST MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END); } // For debugging purposes: check whether we found matches for all entries in the skins file if (!DefaultLogger::isNullLogger()) { for (std::list< Q3Shader::SkinData::TextureEntry>::const_iterator it = skins.textures.begin();it != skins.textures.end(); ++it) { if (!(*it).resolved) { DefaultLogger::get()->error("MD3: Failed to match skin " + (*it).first + " to surface " + (*it).second); } } } if (!pScene->mNumMeshes) throw DeadlyImportError( "MD3: File contains no valid mesh"); pScene->mNumMaterials = iNumMaterials; // Now we need to generate an empty node graph pScene->mRootNode = new aiNode("<MD3Root>"); pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; // Attach tiny children for all tags if (pcHeader->NUM_TAGS) { pScene->mRootNode->mNumChildren = pcHeader->NUM_TAGS; pScene->mRootNode->mChildren = new aiNode*[pcHeader->NUM_TAGS]; for (unsigned int i = 0; i < pcHeader->NUM_TAGS; ++i, ++pcTags) { aiNode* nd = pScene->mRootNode->mChildren[i] = new aiNode(); nd->mName.Set((const char*)pcTags->NAME); nd->mParent = pScene->mRootNode; AI_SWAP4(pcTags->origin.x); AI_SWAP4(pcTags->origin.y); AI_SWAP4(pcTags->origin.z); // Copy local origin, again flip z,y nd->mTransformation.a4 = pcTags->origin.x; nd->mTransformation.b4 = pcTags->origin.y; nd->mTransformation.c4 = pcTags->origin.z; // Copy rest of transformation (need to transpose to match row-order matrix) for (unsigned int a = 0; a < 3;++a) { for (unsigned int m = 0; m < 3;++m) { nd->mTransformation[m][a] = pcTags->orientation[a][m]; AI_SWAP4(nd->mTransformation[m][a]); } } } } for (unsigned int i = 0; i < pScene->mNumMeshes;++i) pScene->mRootNode->mMeshes[i] = i; // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f, 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f); }