#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() { //TODO: clean up } ///////////////////////////////////////////////////////////////////////// // private functions void MshFile::import() { // go to file size information m_file.seekg(4); std::uint32_t tmp_fileSize; std::list tmp_mainChunks; // get all chunks under HEDR m_file.read(F2V(tmp_fileSize), sizeof(tmp_fileSize)); loadChunks(tmp_mainChunks, m_file.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_file.seekg(start); do { ChunkHeader* tmp_header = new ChunkHeader(); // get information m_file.read(F2V(tmp_header->name[0]), sizeof(tmp_header->name) - 1); m_file.read(F2V(tmp_header->size), sizeof(tmp_header->size)); tmp_header->position = m_file.tellg(); // store information destination.push_back(tmp_header); // jump to next header m_file.seekg(tmp_header->size, std::ios_base::cur); // out of file. Maybe a size information is corrupted if (!m_file.good()) { //TODO: different way for output std::cout << "WARNING: corrupted file. Trying to continue" << std::endl; m_file.clear(); break; } } while (m_file.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_file.seekg(it->position); // read in the quaternion float tmp_quat[4]; for (int i = 0; i < 4; i++) m_file.read(F2V(tmp_quat[i]), sizeof(float)); m_sceneBbox.rotation.setX(tmp_quat[0]); m_sceneBbox.rotation.setY(tmp_quat[1]); m_sceneBbox.rotation.setZ(tmp_quat[2]); m_sceneBbox.rotation.setScalar(tmp_quat[3]); //read in the center for (int i = 0; i < 3; i++) m_file.read(F2V(m_sceneBbox.center[i]), sizeof(float)); //read in the extents for (int i = 0; i < 3; i++) m_file.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_file.seekg(it->position); m_file.seekg(sizeof(std::uint32_t), std::ios_base::cur); // get all MATL subchunk std::list tmp_matlChunks; loadChunks(tmp_matlChunks, m_file.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_textureNames->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_models->push_back(new_model); } } } void MshFile::analyseMatdChunks(std::list& chunkList) { for (auto& it : chunkList) { if (!strcmp("TX0D", it->name)) { m_file.seekg(it->position); char* buffer = new char[it->size + 1]; *buffer = { 0 }; m_file.read(buffer, it->size); m_textureNames->back() = buffer; delete[] buffer; } } } void MshFile::analyseModlChunks(Model * dataDestination, std::list& chunkList) { for (auto& it : chunkList) { // model type if (!strcmp("MTYP", it->name)) { m_file.seekg(it->position); std::uint32_t tmp_type; m_file.read(F2V(tmp_type), sizeof(tmp_type)); dataDestination->type = (ModelTyp)tmp_type; } // parent name else if (!strcmp("PRNT", it->name)) { m_file.seekg(it->position); char* buffer = new char[it->size + 1]; *buffer = { 0 }; m_file.read(buffer, it->size); dataDestination->parent = buffer; delete[] buffer; } // model name else if (!strcmp("NAME", it->name)) { m_file.seekg(it->position); char* buffer = new char[it->size + 1]; *buffer = { 0 }; m_file.read(buffer, it->size); dataDestination->name = buffer; delete[] buffer; } // render flags else if (!strcmp("FLGS", it->name)) { m_file.seekg(it->position); m_file.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_file.seekg(it->position); // read in the data for (int i = 0; i < 3; i++) m_file.read(F2V(tmp_scale[i]), sizeof(float)); for (int i = 0; i < 4; i++) m_file.read(F2V(tmp_rotation[i]), sizeof(float)); for (int i = 0; i < 3; i++) m_file.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]); dataDestination->m4x4Translation = getParentMatrix(dataDestination->parent) * dataDestination->m4x4Translation; } // 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_file.seekg(it->position); m_file.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_file.seekg(it->position); m_file.read(F2V(tmp_size), sizeof(tmp_size)); int highBitCount(0); QVector tmp_buffer; for (unsigned int i = 0; i < tmp_size; i++) { // ReadData std::uint16_t tmp_value; m_file.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 tmp_buffer.push_back((GLuint)tmp_value); // if the last 2 highBits are set, it was a new poly if (highBitCount == 2) { // reset highBitCount highBitCount = 0; if (tmp_buffer.size() == 5) { for (size_t i = 0; i < 3; i++) new_segment->indices.push_back(tmp_buffer.takeFirst()); } else if (tmp_buffer.size() > 5) { unsigned int tmp_multiPolySize = tmp_buffer.size() - 2; // for every triangle of the multi polygon.. for (unsigned int tri = 0; tri < tmp_multiPolySize - 2; tri++) // ..calculate the edge indices for (int triEdge = 0; triEdge < 3; triEdge++) new_segment->indices.push_back((GLuint)(tri + triEdge - ((tri % 2) * (triEdge - 1) * 2))); } else { //TODO: this shouldn't happen only once } } // if 2 high bits are set } // for all values // save the last polygon (no 2 high bit followed) if (tmp_buffer.size() == 3) { for (size_t i = 0; i < 3; i++) new_segment->indices.push_back(tmp_buffer.takeFirst()); } else if (tmp_buffer.size() > 3) { unsigned int tmp_multiPolySize = tmp_buffer.size(); // for every triangle of the multi polygon.. for (unsigned int tri = 0; tri < tmp_multiPolySize - 2; tri++) // ..calculate the edge indices for (int triEdge = 0; triEdge < 3; triEdge++) new_segment->indices.push_back((GLuint)(tri + triEdge - ((tri % 2) * (triEdge - 1) * 2))); } else { //TODO: this shouldn't happen std::cout << "Where am i??" << std::endl; } } } 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_file.seekg(it->position); char* buffer = new char[it->size + 1]; *buffer = { 0 }; m_file.read(buffer, it->size); // search if it is already known bool tmp_found(false); for (unsigned int i = 0; i < m_textureNames->size(); i++) { if (!strcmp(buffer, m_textureNames->at(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_textureNames->push_back(std::string(buffer)); new_segment->textureIndex = m_textureNames->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_file.seekg(it->position); m_file.read(F2V(tmp_size), sizeof(tmp_size)); // for every triangle.. for (unsigned int i = 0; i < tmp_size * 3; i++) { std::uint32_t tmp_value; m_file.read(F2V(tmp_value), sizeof(std::uint32_t)); new_segment->indices.push_back((GLuint)tmp_value); } } } dataDestination->segmList.push_back(new_segment); } void MshFile::readVertex(Segment * dataDestination, std::streampos position) { std::uint32_t tmp_size; m_file.seekg(position); m_file.read(F2V(tmp_size), sizeof(tmp_size)); for (unsigned int i = 0; i < tmp_size; i++) { float tmp[3]; for (unsigned int j = 0; j < 3; j++) m_file.read(F2V(tmp[j]), sizeof(float)); VertexData new_data; new_data.position = QVector3D(tmp[0], tmp[1], tmp[2]); dataDestination->vertices.push_back(new_data); } } void MshFile::readUV(Segment * dataDestination, std::streampos position) { std::uint32_t tmp_size; m_file.seekg(position); m_file.read(F2V(tmp_size), sizeof(tmp_size)); if (tmp_size < dataDestination->vertices.size()) { //TODO: warning std::cout << "WARNING: too less UVs " << tmp_size << " < " << dataDestination->vertices.size() << std::endl; //TODO: fill backward with default UVs } else if (tmp_size > dataDestination->vertices.size()) { //TODO: warning std::cout << "WARNING: too many UVs " << tmp_size << " > " << dataDestination->vertices.size() << std::endl; tmp_size = dataDestination->vertices.size(); } for (unsigned int i = 0; i < tmp_size; i++) { float tmp[2]; for (unsigned int j = 0; j < 2; j++) m_file.read(F2V(dataDestination->vertices[i].texCoord[j]), sizeof(float)); } } QMatrix4x4 MshFile::getParentMatrix(std::string parent) const { QMatrix4x4 matrix; for (auto& it : *m_models) { if (!strcmp(parent.c_str(), it->name.c_str())) { //TODO: the other way around?? matrix = getParentMatrix(it->parent) * it->m4x4Translation; break; } } return matrix; }