#include "..\Header\MshFile.h" #include // helper function to save data from file to any variable type #define F2V(variableName) reinterpret_cast(&variableName) ///////////////////////////////////////////////////////////////////////// // public constructor/destructor MshFile::MshFile(const char * path) : FileInterface(path) { import(); } MshFile::~MshFile() { } ///////////////////////////////////////////////////////////////////////// // private functions void MshFile::import() { // go to file size information m_fsMesh.seekg(4); std::uint32_t tmp_fileSize; std::list tmp_mainChunks; // get all chunks under HEDR m_fsMesh.read(F2V(tmp_fileSize), sizeof(tmp_fileSize)); loadChunks(tmp_mainChunks, m_fsMesh.tellg(), tmp_fileSize); // evaulate HEDR subchunks (= find MSH2) for (ChunkHeader* it : tmp_mainChunks) { if (!strcmp("MSH2", it->name)) { // get all subchunks std::list tmp_msh2Chunks; loadChunks(tmp_msh2Chunks, it->position, it->size); // evaluate MSH2 subchunks analyseMsh2Chunks(tmp_msh2Chunks); // clean up while (!tmp_msh2Chunks.empty()) { ChunkHeader* curs = tmp_msh2Chunks.front(); tmp_msh2Chunks.pop_front(); delete curs; } } } // clean up while (!tmp_mainChunks.empty()) { ChunkHeader* cur = tmp_mainChunks.front(); tmp_mainChunks.pop_front(); delete cur; } } void MshFile::loadChunks(std::list& destination, std::streampos start, const std::uint32_t length) { // jump to first chunk m_fsMesh.seekg(start); do { ChunkHeader* tmp_header = new ChunkHeader(); // get information m_fsMesh.read(F2V(tmp_header->name[0]), sizeof(tmp_header->name) - 1); m_fsMesh.read(F2V(tmp_header->size), sizeof(tmp_header->size)); tmp_header->position = m_fsMesh.tellg(); // store information destination.push_back(tmp_header); // jump to next header m_fsMesh.seekg(tmp_header->size, std::ios_base::cur); // out of file. Maybe a size information is corrupted if (!m_fsMesh.good()) { //TODO: different way for output std::cout << "WARNING: corrupted file. Trying to continue" << std::endl; m_fsMesh.clear(); break; } } while (m_fsMesh.tellg() - start != length); } void MshFile::analyseMsh2Chunks(std::list& chunkList) { for (auto& it : chunkList) { // scene information if (!strcmp("SINF", it->name)) { // get SINF subchunks std::list tmp_sinfChunks; loadChunks(tmp_sinfChunks, it->position, it->size); // evaluate SINF subchunks for (auto& it : tmp_sinfChunks) { if (!strcmp("BBOX", it->name)) { m_fsMesh.seekg(it->position); // read in the quaternion for (int i = 0; i < 4; i++) m_fsMesh.read(F2V(m_sceneBbox.quaternion[i]), sizeof(float)); //read in the center for (int i = 0; i < 3; i++) m_fsMesh.read(F2V(m_sceneBbox.center[i]), sizeof(float)); //read in the extents for (int i = 0; i < 3; i++) m_fsMesh.read(F2V(m_sceneBbox.extents[i]), sizeof(float)); } } // clean up SINF subchunks for (ChunkHeader* it : tmp_sinfChunks) delete it; } // material list else if (!strcmp("MATL", it->name)) { // "useless" information how many MATD follow, jump over it m_fsMesh.seekg(it->position); m_fsMesh.seekg(sizeof(std::uint32_t), std::ios_base::cur); // get all MATL subchunk std::list tmp_matlChunks; loadChunks(tmp_matlChunks, m_fsMesh.tellg(), it->size - 4); // evaluate MATL subchunks for (auto& it : tmp_matlChunks) { // This shouldn't be anything else than MATD if (!strcmp("MATD", it->name)) { // get all subchunks from MATD std::list tmp_matdChunks; loadChunks(tmp_matdChunks, it->position, it->size); m_vTextureNames.push_back(""); // analyse MATD subchunks analyseMatdChunks(tmp_matdChunks); // clean up MATD subchunks while (!tmp_matdChunks.empty()) { ChunkHeader* cur = tmp_matdChunks.front(); tmp_matdChunks.pop_front(); delete cur; } } } // clean up MATL subchunks while (!tmp_matlChunks.empty()) { ChunkHeader* cur = tmp_matlChunks.front(); tmp_matlChunks.pop_front(); delete cur; } } // model else if (!strcmp("MODL", it->name)) { Model* new_model = new Model; // get all MODL subchunks std::list tmp_chunks; loadChunks(tmp_chunks, it->position, it->size); // evaluate MODL subchunks analyseModlChunks(new_model, tmp_chunks); //clean up MODL subchunks while (!tmp_chunks.empty()) { ChunkHeader* cur = tmp_chunks.front(); tmp_chunks.pop_front(); delete cur; } // save Model data m_vModels->push_back(new_model); } } } void MshFile::analyseMatdChunks(std::list& chunkList) { for (auto& it : chunkList) { if (!strcmp("TX0D", it->name)) { m_fsMesh.seekg(it->position); char* buffer = new char[it->size + 1]; *buffer = { 0 }; m_fsMesh.read(buffer, it->size); m_vTextureNames.back() = buffer; delete[] buffer; } } } void MshFile::analyseModlChunks(Model * dataDestination, std::list& chunkList) { for (auto& it : chunkList) { // model type if (!strcmp("MTYP", it->name)) { m_fsMesh.seekg(it->position); std::uint32_t tmp_type; m_fsMesh.read(F2V(tmp_type), sizeof(tmp_type)); dataDestination->type = (ModelTyp)tmp_type; } // parent name else if (!strcmp("PRNT", it->name)) { m_fsMesh.seekg(it->position); char* buffer = new char[it->size + 1]; *buffer = { 0 }; m_fsMesh.read(buffer, it->size); dataDestination->parent = buffer; delete[] buffer; } // model name else if (!strcmp("NAME", it->name)) { m_fsMesh.seekg(it->position); char* buffer = new char[it->size + 1]; *buffer = { 0 }; m_fsMesh.read(buffer, it->size); dataDestination->name = buffer; delete[] buffer; } // render flags else if (!strcmp("FLGS", it->name)) { m_fsMesh.seekg(it->position); m_fsMesh.read(F2V(dataDestination->renderFlags), sizeof(dataDestination->renderFlags)); } // translation else if (!strcmp("TRAN", it->name)) { float tmp_scale[3]; float tmp_rotation[4]; float tmp_trans[3]; m_fsMesh.seekg(it->position); // read in the data for (int i = 0; i < 3; i++) m_fsMesh.read(F2V(tmp_scale[i]), sizeof(float)); for (int i = 0; i < 4; i++) m_fsMesh.read(F2V(tmp_rotation[i]), sizeof(float)); for (int i = 0; i < 3; i++) m_fsMesh.read(F2V(tmp_trans[i]), sizeof(float)); // modify the matrix dataDestination->m4x4Translation.scale(tmp_scale[0], tmp_scale[1], tmp_scale[2]); dataDestination->m4x4Translation.rotate(QQuaternion(tmp_rotation[3], tmp_rotation[0], tmp_rotation[1], tmp_rotation[2])); dataDestination->m4x4Translation.translate(tmp_trans[0], tmp_trans[1], tmp_trans[2]); } // geometry data else if (!strcmp("GEOM", it->name)) { // get all GEOM subchunks std::list tmp_geomChunks; loadChunks(tmp_geomChunks, it->position, it->size); // evaluate GEOM subchunks analyseGeomChunks(dataDestination, tmp_geomChunks); // clean up GEOM subchunks while (!tmp_geomChunks.empty()) { ChunkHeader* cur = tmp_geomChunks.front(); tmp_geomChunks.pop_front(); delete cur; } } } } void MshFile::analyseGeomChunks(Model * dataDestination, std::list& chunkList) { for (auto& it : chunkList) { // segment if (!strcmp("SEGM", it->name)) { // get all SEGM subchunks std::list tmp_segmChunks; loadChunks(tmp_segmChunks, it->position, it->size); // evaluate SEGM subchunks analyseSegmChunks(dataDestination, tmp_segmChunks); // clean up SEGM subchunk while (!tmp_segmChunks.empty()) { ChunkHeader* cur = tmp_segmChunks.front(); tmp_segmChunks.pop_front(); delete cur; } } // cloth else if (!strcmp("CLTH", it->name)) { // get all CLTH subchunks std::list tmp_clthChunks; loadChunks(tmp_clthChunks, it->position, it->size); // evaluate CLTH subchunks analyseClthChunks(dataDestination, tmp_clthChunks); // clean up CLTH subchunks while (!tmp_clthChunks.empty()) { ChunkHeader* cur = tmp_clthChunks.front(); tmp_clthChunks.pop_front(); delete cur; } } } } void MshFile::analyseSegmChunks(Model * dataDestination, std::list& chunkList) { Segment* new_segment = new Segment; for (auto& it : chunkList) { // material index if (!strcmp("MATI", it->name)) { m_fsMesh.seekg(it->position); m_fsMesh.read(F2V(new_segment->textureIndex), sizeof(new_segment->textureIndex)); } // position list (vertex) else if (!strcmp("POSL", it->name)) { readVertex(new_segment, it->position); } // normals /*else if (!strcmp("NRML", it->name)) { fsMesh.seekg(it->position); std::uint32_t tempSize; fsMesh.read(reinterpret_cast(&tempSize), sizeof(tempSize)); // List of normals // long int - 4 - number of normal vectores stored in this list // float[3][] - 12 each - UVW vector for each vertex }*/ // uv else if (!strcmp("UV0L", it->name)) { readUV(new_segment, it->position); } // polygons (indices into vertex/uv list) else if (!strcmp("STRP", it->name)) { // don't get null, bone, shadowMesh and hidden mesh indices if (dataDestination->type == null || dataDestination->type == bone || dataDestination->type == shadowMesh || dataDestination->renderFlags == 1) continue; // jump to the data section and read the size; std::uint32_t tmp_size; m_fsMesh.seekg(it->position); m_fsMesh.read(F2V(tmp_size), sizeof(tmp_size)); int highBitCount(0); std::vector new_poly; for (unsigned int i = 0; i < tmp_size; i++) { // ReadData std::uint16_t tmp_value; m_fsMesh.read(F2V(tmp_value), sizeof(tmp_value)); // Check if highbit is set if (tmp_value >> 15) { highBitCount++; // remove the high bit, to get the actually value tmp_value = (std::uint16_t(tmp_value << 1) >> 1); } // save data new_poly.push_back((std::uint32_t)tmp_value); // if the last 2 highBits are set, it was a new poly if (highBitCount == 2) { // reset highBitCount highBitCount = 0; // remove the last two values.. std::uint32_t temp[2]; for (int i = 0; i < 2; i++) { temp[i] = new_poly.back(); new_poly.pop_back(); } // ..save the old polygon.. new_segment->polyIndices.push_back(new_poly); // ..and move the values to a new polygon new_poly.clear(); for (int i = 1; i >= 0; i--) new_poly.push_back(temp[i]); } // if high bit set } // for all values // save the last polygon (no 2 high bit followed) new_segment->polyIndices.push_back(new_poly); // kick the first element, it's empty as a reason of the algo above; new_segment->polyIndices.erase(new_segment->polyIndices.begin()); } } dataDestination->segmList.push_back(new_segment); } void MshFile::analyseClthChunks(Model * dataDestination, std::list& chunkList) { Segment* new_segment = new Segment; for (auto& it : chunkList) { // texture name if (!strcmp("CTEX", it->name)) { // read the texture name m_fsMesh.seekg(it->position); char* buffer = new char[it->size + 1]; *buffer = { 0 }; m_fsMesh.read(buffer, it->size); // search if it is already known bool tmp_found(false); for (unsigned int i = 0; i < m_vTextureNames.size(); i++) { if (!strcmp(buffer, m_vTextureNames[i].c_str())) { // if found, save the index and stop searching new_segment->textureIndex = i; tmp_found = true; break; } } // if it was not found add the texturename to the list if (!tmp_found) { m_vTextureNames.push_back(std::string(buffer)); new_segment->textureIndex = m_vTextureNames.size() - 1; } delete[] buffer; } // position list (vertex) else if (!strcmp("CPOS", it->name)) { readVertex(new_segment, it->position); } // uv else if (!strcmp("CUV0", it->name)) { readUV(new_segment, it->position); } // triangles (indices into vertex/uv list) else if (!strcmp("CMSH", it->name)) { // jump to the data section and read the size; std::uint32_t tmp_size; m_fsMesh.seekg(it->position); m_fsMesh.read(F2V(tmp_size), sizeof(tmp_size)); std::vector new_poly; // for every triangle.. for (unsigned int i = 0; i < tmp_size * 3; i += 3) { new_poly.clear(); // ..get the 3 indices and save them for (int j = 0; j < 3; j++) { std::uint32_t tmp_value; m_fsMesh.read(F2V(tmp_value), sizeof(std::uint32_t)); new_poly.push_back(tmp_value); } new_segment->polyIndices.push_back(new_poly); } } } dataDestination->segmList.push_back(new_segment); } void MshFile::readVertex(Segment * dataDestination, std::streampos position) { std::uint32_t tmp_size; m_fsMesh.seekg(position); m_fsMesh.read(F2V(tmp_size), sizeof(tmp_size)); dataDestination->vertex = new float[tmp_size * 3]; for (unsigned int i = 0; i < tmp_size * 3; i++) m_fsMesh.read(F2V(dataDestination->vertex[i]), sizeof(float)); } void MshFile::readUV(Segment * dataDestination, std::streampos position) { std::uint32_t tmp_size; m_fsMesh.seekg(position); m_fsMesh.read(F2V(tmp_size), sizeof(tmp_size)); dataDestination->uv = new float[tmp_size * 2]; for (unsigned int i = 0; i < tmp_size * 2; i++) m_fsMesh.read(F2V(dataDestination->uv[i]), sizeof(float)); }