diff --git a/src/bsp/Bsp.cpp b/src/bsp/Bsp.cpp index f43578bd..eacdb70c 100644 --- a/src/bsp/Bsp.cpp +++ b/src/bsp/Bsp.cpp @@ -9069,7 +9069,7 @@ void Bsp::ExportToSmdWIP(const std::string& path, bool split, bool oneRoot) if (refreshedModels.find(mdlid) == refreshedModels.end()) { - bsprend->refreshModel(mdlid, false, /* do triangulate */ false); + bsprend->refreshModel(mdlid, false, /* do triangulate */ true); refreshedModels.insert(mdlid); } @@ -9423,7 +9423,7 @@ void Bsp::ExportToSmdWIP(const std::string& path, bool split, bool oneRoot) renderer->pushModelUndoState("EXPORT .SMD EDITED", EDIT_MODEL_LUMPS | FL_ENTITIES); } -void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, bool with_mdl) +void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, bool with_mdl, bool export_csm) { if (!createDir(path)) { @@ -9474,8 +9474,8 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, int vertoffset = 1; int normoffset = 0; - std::string materialid = std::string(); - std::string lastmaterialid = std::string(); + int materialid = -1; + int lastmaterialid = 0; std::set refreshedModels; @@ -9545,7 +9545,11 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, else renderer->preRenderEnts(); - tmp.update("Export to obj...", faceCount); + if (export_csm) + tmp.update("Export to csm...", faceCount); + else + tmp.update("Export to obj...", faceCount); + g_progress = tmp; std::map group_verts; @@ -9557,6 +9561,13 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, std::vector group_list; + CSMFile* csm_export = new CSMFile(); + + csm_face tmpFace; + + int csm_groups = 0; + + for (int i = 0; i < faceCount; i++) { tmp.tick(); @@ -9569,7 +9580,7 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, if (refreshedModels.find(mdlid) == refreshedModels.end()) { - bsprend->refreshModel(mdlid, false, true); + bsprend->refreshModel(mdlid, false, export_csm ? true : false); refreshedModels.insert(mdlid); } @@ -9594,47 +9605,56 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, entIds.push_back(0); } - materialid.clear(); + materialid = -1; for (size_t m = 0; m < matnames.size(); m++) { if (matnames[m] == tex.szName) - materialid = tex.szName; - } - if (materialid.empty()) - { - materialid = tex.szName; - materials.emplace_back(""); - materials.emplace_back("newmtl " + materialid); - - materials.emplace_back("Ns 0"); - materials.emplace_back("Ka 1 1 1"); - materials.emplace_back("Ks 0 0 0"); - materials.emplace_back("Ke 0 0 0"); - materials.emplace_back("Ni 1"); - - if (toLowerCase(tex.szName) == "aaatrigger" || - toLowerCase(tex.szName) == "null" || - toLowerCase(tex.szName).starts_with("sky") || - toLowerCase(tex.szName) == "noclip" || - toLowerCase(tex.szName) == "clip" || - toLowerCase(tex.szName) == "origin" || - toLowerCase(tex.szName) == "bevel" || - toLowerCase(tex.szName) == "hint" || - toLowerCase(tex.szName) == "skip" - ) - { - materials.emplace_back("d 0.25"); - materials.emplace_back("illum 1"); - } - else + materialid = m; + } + + if (materialid == -1) + { + materialid = (int)matnames.size(); + if (!export_csm) { - materials.emplace_back("d 1"); - materials.emplace_back("illum 2"); - } + materials.emplace_back(""); + materials.emplace_back("newmtl " + materialid); - materials.emplace_back("map_Kd " + std::string("textures/") + tex.szName + std::string(".bmp")); + materials.emplace_back("Ns 0"); + materials.emplace_back("Ka 1 1 1"); + materials.emplace_back("Ks 0 0 0"); + materials.emplace_back("Ke 0 0 0"); + materials.emplace_back("Ni 1"); + + if (toLowerCase(tex.szName) == "aaatrigger" || + toLowerCase(tex.szName) == "null" || + toLowerCase(tex.szName).starts_with("sky") || + toLowerCase(tex.szName) == "noclip" || + toLowerCase(tex.szName) == "clip" || + toLowerCase(tex.szName) == "origin" || + toLowerCase(tex.szName) == "bevel" || + toLowerCase(tex.szName) == "hint" || + toLowerCase(tex.szName) == "skip" + ) + { + materials.emplace_back("d 0.25"); + materials.emplace_back("illum 1"); + } + else + { + materials.emplace_back("d 1"); + materials.emplace_back("illum 2"); + } + + materials.emplace_back("map_Kd " + std::string("textures/") + tex.szName + std::string(".bmp")); + } matnames.emplace_back(tex.szName); + + if (export_csm) + { + csm_export->materials.push_back(std::string("textures/") + tex.szName + std::string(".bmp")); + } } if (!fileExists(path + std::string("textures/") + tex.szName + std::string(".bmp"))) @@ -9723,6 +9743,8 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, if (std::find(group_list.begin(), group_list.end(), groupname) == group_list.end()) group_list.push_back(groupname); + csm_groups++; + //print_log(PRINT_RED, "Entity {} angles {}\n", tmpentid, rendEnt->angles.toKeyvalueString()); } @@ -9739,23 +9761,6 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, renderer->setRenderAngles(ent->classname, angle_mat, angles); } - for (int n = rface->vertCount - 1; n >= 0; n--) - { - lightmapVert& vert = ((lightmapVert*)rgroup->buffer->get_data())[rface->vertOffset + n]; - - vec3 org_pos = vert.pos; - - if (rendEnt->needAngles) - { - org_pos = (angle_mat * vec4(org_pos, 1.0)).xyz(); - } - - org_pos += origin_offset; - - org_pos *= scale; - - group_verts[groupname] << "v " << org_pos.toKeyvalueString() << "\n"; - } BSPPLANE tmpPlane = getPlaneFromFace(&face); vec3 org_norm = tmpPlane.vNormal; @@ -9767,10 +9772,34 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, org_norm = org_norm.flip(); - group_normals[groupname] << "vn " << org_norm.toKeyvalueString() << "\n"; + if (!export_csm) + { + for (int n = rface->vertCount - 1; n >= 0; n--) + { + lightmapVert& vert = ((lightmapVert*)rgroup->buffer->get_data())[rface->vertOffset + n]; + + vec3 org_pos = vert.pos; + + if (rendEnt->needAngles) + { + org_pos = (angle_mat * vec4(org_pos, 1.0)).xyz(); + } + + org_pos += origin_offset; + + org_pos *= scale; + + group_verts[groupname] << "v " << org_pos.toKeyvalueString() << "\n"; + + + } + + group_normals[groupname] << "vn " << org_norm.toKeyvalueString() << "\n"; - normoffset++; + normoffset++; + } + int uv_idx = 0; for (int n = rface->vertCount - 1; n >= 0; n--) { @@ -9791,7 +9820,57 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, fU /= (float)tex.nWidth; fV /= -(float)tex.nHeight; - group_textures[groupname] << "vt " << flt_to_str(fU) << " " << flt_to_str(fV) << "\n"; + if (!export_csm) + { + group_textures[groupname] << "vt " << flt_to_str(fU) << " " << flt_to_str(fV) << "\n"; + } + + if (export_csm) + { + if (uv_idx == 0) + { + unsigned int startvert = (unsigned int)csm_export->vertices.size(); + + tmpFace.edgeFlags = 0; + tmpFace.lmGroup = csm_groups; + tmpFace.matIdx = (unsigned short)(materialid); + tmpFace.vertIdx[0] = startvert; + tmpFace.vertIdx[1] = startvert + 1; + tmpFace.vertIdx[2] = startvert + 2; + + csm_export->faces.push_back(tmpFace); + + csm_export->header.lmGroups = csm_groups; + } + + + org_pos.unflipUV(); + + org_pos += origin_offset; + org_pos *= scale; + + COLOR4 rndColor; + srand(csm_groups); + rndColor.r = 50 + rand() % 206; + rndColor.g = 50 + rand() % 206; + rndColor.b = 50 + rand() % 206; + rndColor.a = 255; + + csm_export->vertices.push_back({ org_pos,org_norm,rndColor }); + + size_t cur_faceIdx = csm_export->faces.size() - 1; + + csm_export->faces[cur_faceIdx].uvs[0].uv[uv_idx].x = fU; + csm_export->faces[cur_faceIdx].uvs[0].uv[uv_idx].y = fU; + + csm_export->faces[cur_faceIdx].uvs[1].uv[uv_idx].x = fU; + csm_export->faces[cur_faceIdx].uvs[1].uv[uv_idx].y = fU; + + uv_idx++; + + if (uv_idx == 3) + uv_idx = 0; + } } if (lastmaterialid != materialid) @@ -9799,65 +9878,85 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, group_vert_groups[groupname]++; //group_objects[groupname] << "g " << groupname << "_face" << group_vert_groups[groupname] << "\n"; - - group_objects[groupname] << "usemtl " << materialid << "\n"; + if (!export_csm) + { + if (materialid >= 0) + { + group_objects[groupname] << "usemtl " << matnames[materialid] << "\n"; + } + } } lastmaterialid = materialid; + if (!export_csm) + { + group_objects[groupname] << "f"; - group_objects[groupname] << "f"; + for (int n = 0; n < rface->vertCount; n++) + { + int id = vertoffset + n; - for (int n = 0; n < rface->vertCount; n++) - { - int id = vertoffset + n; + group_objects[groupname] << " " << id << "/" << id << "/" << normoffset; + } - group_objects[groupname] << " " << id << "/" << id << "/" << normoffset; + group_objects[groupname] << "\n"; } - group_objects[groupname] << "\n"; vertoffset += rface->vertCount; } } - std::ofstream obj_file(path + bsp_name + ".obj", std::ios::binary); - if (obj_file) + if (!export_csm) { - obj_file << "# Exported using bspguy!\n"; - obj_file << "mtllib " << bsp_name << ".mtl\n"; + std::ofstream obj_file(path + bsp_name + ".obj", std::ios::binary); + if (obj_file) + { + obj_file << "# Exported using bspguy!\n"; + obj_file << "mtllib " << bsp_name << ".mtl\n"; - for (auto& group : group_list) - { - obj_file << "o " << group << "\n"; + for (auto& group : group_list) + { + obj_file << "o " << group << "\n"; - obj_file << group_verts[group].str(); + obj_file << group_verts[group].str(); - obj_file << group_normals[group].str(); + obj_file << group_normals[group].str(); - obj_file << group_textures[group].str(); - - obj_file << group_objects[group].str(); - } + obj_file << group_textures[group].str(); - obj_file.flush(); - obj_file.close(); - } + obj_file << group_objects[group].str(); + } + obj_file.flush(); + obj_file.close(); + } - std::ofstream mat_file(path + bsp_name + ".mtl", std::ios::binary); - if (mat_file) - { - mat_file << "# Exported using bspguy!\n"; + std::ofstream mat_file(path + bsp_name + ".mtl", std::ios::binary); - for (auto const& s : materials) + if (mat_file) { - mat_file << s << '\n'; - } + mat_file << "# Exported using bspguy!\n"; - mat_file.flush(); - mat_file.close(); - } + for (auto const& s : materials) + { + mat_file << s << '\n'; + } + mat_file.flush(); + mat_file.close(); + } + } + else + { + print_log(PRINT_GREEN, "VALIDATE START\n"); + csm_export->validate(); + print_log(PRINT_GREEN, "VALIDATE END\n"); + csm_export->write(path + bsp_name + ".csm");/* + csm_export->read(path + bsp_name + ".csm"); + csm_export->validate();*/ + } + delete csm_export; renderer->undo(); diff --git a/src/bsp/Bsp.h b/src/bsp/Bsp.h index 4467e3b9..9c2e678a 100644 --- a/src/bsp/Bsp.h +++ b/src/bsp/Bsp.h @@ -329,7 +329,7 @@ class Bsp void ExportToSmdWIP(const std::string& path, bool split, bool oneRoot); - void ExportToObjWIP(const std::string& path, int iscale = 1, bool lightmap_mode = false, bool with_mdl = false); + void ExportToObjWIP(const std::string& path, int iscale = 1, bool lightmap_mode = false, bool with_mdl = false, bool export_csm = false); void ExportToMapWIP(const std::string& path, bool selected, bool merge_faces, bool use_one_back_vert); diff --git a/src/editor/BspRenderer.cpp b/src/editor/BspRenderer.cpp index 24154b2a..e96c84f2 100644 --- a/src/editor/BspRenderer.cpp +++ b/src/editor/BspRenderer.cpp @@ -888,7 +888,7 @@ void BspRenderer::deleteFaceMaths() faceMaths = NULL; } -int BspRenderer::refreshModel(int modelIdx, bool refreshClipnodes, bool noTriangulate) +int BspRenderer::refreshModel(int modelIdx, bool refreshClipnodes, bool triangulate) { if (modelIdx < 0 || modelIdx >= map->modelCount) return 0; @@ -1127,7 +1127,7 @@ int BspRenderer::refreshModel(int modelIdx, bool refreshClipnodes, bool noTriang } } - if (!noTriangulate) + if (triangulate) { idx = 0; // convert TRIANGLE_FAN verts to TRIANGLES so multiple faces can be drawn in a single draw call diff --git a/src/editor/BspRenderer.h b/src/editor/BspRenderer.h index c1bda251..e9468862 100644 --- a/src/editor/BspRenderer.h +++ b/src/editor/BspRenderer.h @@ -198,7 +198,7 @@ class BspRenderer bool setRenderAngles(const std::string& classname, mat4x4& outmat, vec3& outangles); void refreshEnt(size_t entIdx); - int refreshModel(int modelIdx, bool refreshClipnodes = true, bool noTriangulate = false); + int refreshModel(int modelIdx, bool refreshClipnodes = true, bool triangulate = true); bool refreshModelClipnodes(int modelIdx); void refreshFace(int faceIdx); void updateClipnodeOpacity(unsigned char newValue); diff --git a/src/editor/Gui.cpp b/src/editor/Gui.cpp index db786441..5c19f8e5 100644 --- a/src/editor/Gui.cpp +++ b/src/editor/Gui.cpp @@ -1284,7 +1284,7 @@ void Gui::drawBspContexMenu() { BSPFACE32 face = map->faces[f]; - if (std::find(app->pickInfo.selectedFaces.begin(), app->pickInfo.selectedFaces.end(), f) != app->pickInfo.selectedFaces.end()) + if (std::find(app->pickInfo.selectedFaces.begin(), app->pickInfo.selectedFaces.end(), (size_t)f) != app->pickInfo.selectedFaces.end()) { continue; } @@ -2810,7 +2810,7 @@ void Gui::drawMenuBar() static int g_scale = 1; - if (ImGui::BeginMenu("Wavefront (.obj) [WIP]", map && !map->is_mdl_model)) + if (ImGui::BeginMenu("Wavefront(.obj)/XashNT(.csm) [WIP]", map && !map->is_mdl_model)) { if (ImGui::BeginMenu("Select scale")) @@ -2841,14 +2841,30 @@ void Gui::drawMenuBar() ImGui::EndMenu(); } - if (ImGui::MenuItem("Export only bsp")) + if (ImGui::BeginMenu("Export only bsp")) { - map->ExportToObjWIP(g_working_dir, g_scale); + if (ImGui::MenuItem("Export .obj")) + { + map->ExportToObjWIP(g_working_dir, g_scale); + } + if (ImGui::MenuItem("Export .csm")) + { + map->ExportToObjWIP(g_working_dir, g_scale, false, false, true); + } + ImGui::EndMenu(); } - if (ImGui::MenuItem("Export with models")) + if (ImGui::BeginMenu("Export with models")) { - map->ExportToObjWIP(g_working_dir, g_scale, false, true); + if (ImGui::MenuItem("Export .obj")) + { + map->ExportToObjWIP(g_working_dir, g_scale, false, true); + } + if (ImGui::MenuItem("Export .csm")) + { + map->ExportToObjWIP(g_working_dir, g_scale, false, true, true); + } + ImGui::EndMenu(); } ImGui::EndMenu(); @@ -3242,7 +3258,7 @@ void Gui::drawMenuBar() if (fileSize(g_settings_path) == 0) { print_log(PRINT_RED | PRINT_INTENSITY, get_localized_string(LANG_0359)); - } + } else { glfwTerminate(); @@ -3253,9 +3269,9 @@ void Gui::drawMenuBar() std::quick_exit(0); #endif } - } + } ImGui::EndMenu(); - } +} if (ImGui::BeginMenu(get_localized_string(LANG_0556).c_str(), (map && !map->is_mdl_model))) { @@ -8111,7 +8127,7 @@ void Gui::drawSettings() ImGui::TextUnformatted(get_localized_string(LANG_0740).c_str()); ImGui::EndTooltip(); } - } + } else if (settingsTab == 1) { for (size_t i = 0; i < g_settings.fgdPaths.size(); i++) @@ -8639,7 +8655,7 @@ void Gui::drawSettings() ImGui::EndChild(); ImGui::EndGroup(); - } + } ImGui::End(); @@ -8663,7 +8679,7 @@ void Gui::drawSettings() } oldShowSettings = showSettingsWidget = apply_settings_pressed; } -} + } void Gui::drawHelp() { diff --git a/src/main.cpp b/src/main.cpp index f88db414..ce03b07d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1051,12 +1051,15 @@ int main(int argc, char* argv[]) fs::current_path(bspguy_dir); - - std::vector vertices; + /*CSMFile tmpFile; + tmpFile.read("d:/SteamLibrary/steamapps/common/Half-Life/bspguy_work/de_dust2.smd/de_dust2_1.csm"); + tmpFile.validate(); + tmpFile.write("d:/SteamLibrary/steamapps/common/Half-Life/bspguy_work/de_dust2.smd/de_dust2_1_new.csm");*/ + /*std::vector vertices; vec3 mins(-64.0f, -64.0f, -64.0f); vec3 maxs(64.0f, 64.0f, 64.0f); - CreateSkybox(vertices, mins, maxs); + CreateSkybox(vertices, mins, maxs);*/ if (fileExists("./log.txt")) { diff --git a/src/util/util.cpp b/src/util/util.cpp index 65495d6d..7879219c 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -527,7 +527,7 @@ vec3 findBestBrushCenter(std::vector& points) for (const auto& point : points) { center += point; } - center /= points.size(); + center /= points.size() * 1.0f; if (points.size() > 2) { diff --git a/src/util/util.h b/src/util/util.h index 9daf4ef7..c94f11a2 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -374,7 +374,7 @@ class JackWriter { file.write(str.data(), size); } - void writeKeyVal(const std::string& key, const std::string& val) + void writeKeyVal(const std::string& key, const std::string& val) { writeLenStr(key); writeLenStr(val); @@ -428,6 +428,296 @@ class JackReader { std::ifstream file; }; + + + +// enumerate coordinate channels +#define FCHAN_TC 0 +#define FCHAN_LM 1 + +#define FMODEL_HAS_LMGROUPS BIT( 0 ) +#define FMODEL_BBOX_COMPUTED BIT( 1 ) +#define FMODEL_COLLAPSED BIT( 2 ) +#define FMODEL_KEEP_NORMALS BIT( 3 ) +#define FMODEL_PORTALS_PROCESSED BIT( 4 ) + +#define FFACE_FROM_PLANAR_GROUP BIT( 0 ) +#define FFACE_FROM_PURE_AXIAL_GROUP BIT( 1 ) +#define FFACE_DISSOLVE_FIRST_EDGE BIT( 2 ) +#define FFACE_DISSOLVE_SECOND_EDGE BIT( 3 ) +#define FFACE_DISSOLVE_THIRD_EDGE BIT( 4 ) +#define FFACE_HAS_SOURCE_LMCOORDS BIT( 5 ) +#define FFACE_LANDSCAPE BIT( 6 ) +#define FFACE_STRUCTURAL BIT( 7 ) // can be used for BSP-processing + +#define FFACE_IS_CHECKED BIT( 15 ) // internal flag +#define FFACE_WORLD_TARGET (FFACE_FROM_PLANAR_GROUP|FFACE_FROM_PURE_AXIAL_GROUP) + + +#pragma pack(push, 1) + +struct csm_header +{ + unsigned int ident; + unsigned int version; + unsigned int flags; + unsigned int lmGroups; + + unsigned int reserved0[4]; + vec3 model_mins; + vec3 model_maxs; + unsigned int reserved1[4]; + + unsigned int mat_ofs; + unsigned int mat_size; + + unsigned int faces_ofs; + unsigned int face_size; + unsigned int faces_count; + + unsigned int vertex_ofs; + unsigned int vertex_size; + unsigned int vertex_count; +}; + +struct csm_vertex +{ + vec3 point; + vec3 normal; + COLOR4 color; +}; + +struct csm_uv_t +{ + vec2 uv[3]; +}; + +struct csm_face +{ + unsigned short matIdx; + unsigned short edgeFlags; + unsigned int vertIdx[3]; + int lmGroup; + csm_uv_t uvs[2]; +}; + +#pragma pack(pop) + + +#define IDCSMMODHEADER (('M'<<24)+('S'<<16)+('C'<<8)+'I') // little-endian "ICSM" +#define IDCSM_VERSION 2 + + +class CSMFile { +private: + bool readed; + + void parseMaterialsFromString(const std::string& materialsStr) + { + std::istringstream ss(materialsStr); + std::string material; + + while (ss.str().find('"', (size_t)ss.tellg()) != std::string::npos && ss >> std::quoted(material)) + { + materials.push_back(material); + material.clear(); + } + } + + std::string getStringFromMaterials() + { + std::string ret{}; + for (auto& m : materials) + { + ret += "\"" + m + "\" "; + } + if (materials.size()) + ret.pop_back(); + return ret; + } + +public: + + CSMFile() + { + header = {}; + materials = {}; + vertices = {}; + faces = {}; + readed = false; + } + + CSMFile(std::string path) + { + readed = read(path); + } + + csm_header header; + + std::vector materials; + std::vector vertices; + std::vector faces; + + bool validate() + { + if (header.face_size != sizeof(csm_face)) + { + print_log(PRINT_RED, "Error: Invalid CSM header face size {}!\n", header.face_size); + //return false; + } + if (header.vertex_size != sizeof(csm_vertex)) + { + print_log(PRINT_RED, "Error: Invalid CSM header vertex size {}!\n", header.vertex_size); + //return false; + } + if (!faces.size() || !vertices.size()) + { + print_log(PRINT_RED, "Error: Empty CSM structure!\n"); + //return false; + } + for (auto& f : faces) + { + if (f.matIdx >= materials.size()) + { + print_log(PRINT_RED, "Error: Invalid matIdx in face!\n"); + // return false; + } + + for (int i = 0; i < 3; i++) + { + if (f.vertIdx[i] >= vertices.size()) + { + print_log(PRINT_RED, "Error: Invalid vertIdx[{}] in face = {}! Vertices: {}\n", i, f.vertIdx[i], vertices.size()); + // return false; + } + } + } + return true; + } + + + bool read(const std::string& filePath) + { + std::ifstream file(filePath, std::ios::binary); + + if (!file) { + print_log(PRINT_RED, "Error: Failed to open file for reading: {}\n", filePath); + return false; + } + + if (!file.read(reinterpret_cast(&header), sizeof(header))) + { + print_log(PRINT_RED, "Error: Failed to read CSM header: {}\n", filePath); + return false; + } + + if (header.ident != IDCSMMODHEADER || header.version != IDCSM_VERSION) + { + print_log(PRINT_RED, "Error: Invalid CSM header struct version: {}\n", filePath); + return false; + } + + if (header.face_size != sizeof(csm_face) || header.vertex_size != sizeof(csm_vertex)) + { + print_log(PRINT_RED, "Error: Invalid CSM header struct size: {}\n", filePath); + return false; + } + + std::string matstr; + matstr.resize(header.mat_size); + + if (!file.seekg(header.mat_ofs)) + { + print_log(PRINT_RED, "Error: Invalid CSM materials offset: {}\n", filePath); + return false; + } + + if (!file.read(reinterpret_cast(matstr.data()), header.mat_size)) + { + print_log(PRINT_RED, "Error: Failed to read CSM materials: {}\n", filePath); + return false; + } + + if (!matstr.empty()) + parseMaterialsFromString(matstr); + + vertices.resize(header.vertex_count); + + if (!file.seekg(header.vertex_ofs)) + { + print_log(PRINT_RED, "Error: Invalid CSM vertices offset: {}\n", filePath); + return false; + } + + if (!file.read(reinterpret_cast(vertices.data()), header.vertex_size * header.vertex_count)) + { + print_log(PRINT_RED, "Error: Failed to read CSM vertices: {}\n", filePath); + return false; + } + + if (!file.seekg(header.faces_ofs)) + { + print_log(PRINT_RED, "Error: Invalid CSM faces offset: {}\n", filePath); + return false; + } + + faces.resize(header.faces_count); + + if (!file.read(reinterpret_cast(faces.data()), header.face_size * header.faces_count)) + { + print_log(PRINT_RED, "Error: Failed to read CSM faces: {}\n", filePath); + return false; + } + + file.close(); + + readed = true; + return true; + } + + bool write(const std::string& filePath) { + std::ofstream file(filePath, std::ios::binary); + if (!file) { + std::cerr << "Failed to open file for writing: " << filePath << std::endl; + return false; + } + + header.vertex_count = (unsigned int)vertices.size(); + header.faces_count = (unsigned int)faces.size(); + + if (!readed) + { + header.flags = 0; + } + + header.ident = IDCSMMODHEADER; + header.version = IDCSM_VERSION; + + std::string matstr = getStringFromMaterials(); + + header.mat_size = (unsigned int)matstr.size() + 1; + header.vertex_size = sizeof(csm_vertex); + header.face_size = sizeof(csm_face); + + header.mat_ofs = sizeof(csm_header); + header.vertex_ofs = header.mat_ofs + header.mat_size ; + header.faces_ofs = header.vertex_ofs + (header.vertex_size * header.vertex_count); + + file.write(reinterpret_cast(&header), sizeof(header)); + + matstr.push_back('\0'); + + file.write(reinterpret_cast(matstr.data()), header.mat_size); + //matstr.pop_back(); -- if need do something with matstr + + file.write(reinterpret_cast(vertices.data()), header.vertex_size * header.vertex_count); + file.write(reinterpret_cast(faces.data()), header.face_size * header.faces_count); + + file.close(); + return true; + } +}; + int str_to_int(const std::string& s); float str_to_float(const std::string& s);