diff --git a/resources/languages/language.ini b/resources/languages/language.ini index 89f93408..48047f97 100644 --- a/resources/languages/language.ini +++ b/resources/languages/language.ini @@ -642,6 +642,7 @@ LANG_0636 = DebugVec1 %6.2f %6.2f %6.2f LANG_0637 = DebugVec2 %6.2f %6.2f %6.2f LANG_0638 = DebugVec3 %6.2f %6.2f %6.2f LANG_0639 = Undo Memory Usage: %.2f MB +UNDO_MEM_USAGE = Undo Memory Usage: %.2f MB (%.2f MB COMPRESSED) LANG_0640 = Show DragAxes LANG_0641 = PRESS ME TO DECAL LANG_0642 = TEST VIS DATA[CLIPNODES] diff --git a/resources/languages/language_ru.ini b/resources/languages/language_ru.ini index 95bcb70d..8171efcb 100644 --- a/resources/languages/language_ru.ini +++ b/resources/languages/language_ru.ini @@ -642,6 +642,7 @@ LANG_0636 = DebugVec1 %6.2f %6.2f %6.2f LANG_0637 = DebugVec2 %6.2f %6.2f %6.2f LANG_0638 = DebugVec3 %6.2f %6.2f %6.2f LANG_0639 = Память буфера отмены: %.2f МБ +UNDO_MEM_USAGE = Использование памяти отмены: %.2f MB (%.2f MB СЖАТОЙ) LANG_0640 = Отображать крестовину перемещения LANG_0641 = Тест деколей (WIP) LANG_0642 = Тест VIS-данных [клипноды] diff --git a/resources/languages/language_zh.ini b/resources/languages/language_zh.ini index f9b718a4..d09a6715 100644 --- a/resources/languages/language_zh.ini +++ b/resources/languages/language_zh.ini @@ -642,6 +642,7 @@ LANG_0636 = 调试向量1 %6.2f %6.2f %6.2f LANG_0637 = 调试向量2 %6.2f %6.2f %6.2f LANG_0638 = 调试向量3 %6.2f %6.2f %6.2f LANG_0639 = 撤销内存使用情况: %.2f MB +UNDO_MEM_USAGE = 撤销内存使用情况: %.2f MB (%.2f MB ZIP) LANG_0640 = 显示拖动轴 LANG_0641 = 按我喷涂贴花 LANG_0642 = 测试 VIS 数据[剪裁节点] diff --git a/src/bsp/Bsp.cpp b/src/bsp/Bsp.cpp index 9016113c..af2f590a 100644 --- a/src/bsp/Bsp.cpp +++ b/src/bsp/Bsp.cpp @@ -55,7 +55,7 @@ void Bsp::init_empty_bsp() update_lump_pointers(); - load_ents(); + reload_ents(); Entity* ent = new Entity(); ent->setOrAddKeyvalue("mapversion", "220"); @@ -272,7 +272,7 @@ Bsp::Bsp(std::string fpath) delete[] newlump; } - load_ents(); + reload_ents(); update_lump_pointers(); if (modelCount > 0) @@ -1574,7 +1574,12 @@ bool Bsp::does_model_use_shared_structures(int modelIdx) LumpState Bsp::duplicate_lumps(unsigned int targets) { - LumpState state{}; + LumpState state(this); + + if (targets & FL_ENTITIES) + { + update_ent_lump(); + } for (int i = 0; i < HEADER_LUMPS; i++) { @@ -1639,16 +1644,23 @@ int Bsp::delete_embedded_textures() void Bsp::replace_lumps(const LumpState& state) { + bool uploadEnts = false; + for (unsigned int i = 0; i < HEADER_LUMPS; i++) { if (state.lumps[i].size()) { lumps[i] = state.lumps[i]; bsp_header.lump[i].nLength = (int)lumps[i].size(); + if (i == LUMP_ENTITIES) + { + uploadEnts = true; + } } } - load_ents(); + if (uploadEnts) + reload_ents(); update_lump_pointers(); } @@ -4564,7 +4576,7 @@ void Bsp::write(const std::string& path) delete[] oldfile; } - auto backupLumps = duplicate_lumps(0xFFFFFFFF); + auto backupLumps = duplicate_lumps(); std::ofstream file(path, std::ios::trunc | std::ios::binary); if (!file.is_open()) @@ -4615,9 +4627,8 @@ void Bsp::write(const std::string& path) update_lump_pointers(); std::vector nulls(is_bsp30ext && extralumps.size() ? sizeof(BSPHEADER) + sizeof(BSPHEADER_EX) : sizeof(BSPHEADER) + (is_bsp_pathos ? 4 : 0), 0); - file.write(reinterpret_cast(nulls.data()), nulls.size()); + file.write((const char*)nulls.data(), nulls.size()); - unsigned char* oldLighting = (unsigned char*)lightdata; unsigned char* freelighting = NULL; // first process, for face restore offsets @@ -4644,9 +4655,6 @@ void Bsp::write(const std::string& path) } } - - - unsigned char* oldClipnodes = (unsigned char*)clipnodes; BSPCLIPNODE16* freeClipnodes16 = NULL; if (!is_bsp2 && (is_broken_clipnodes || !is_32bit_clipnodes || bsp_header.lump[LUMP_CLIPNODES].nLength / sizeof(BSPCLIPNODE32) < MAX_MAP_CLIPNODES_DEFAULT)) @@ -4672,7 +4680,6 @@ void Bsp::write(const std::string& path) lumps[LUMP_CLIPNODES].assign((unsigned char*)freeClipnodes16, (unsigned char*)(freeClipnodes16)+bsp_header.lump[LUMP_CLIPNODES].nLength); } - unsigned char* oldnodes = (unsigned char*)nodes; BSPNODE16* freenodes16 = NULL; BSPNODE32A* freenodes32a = NULL; @@ -4717,8 +4724,6 @@ void Bsp::write(const std::string& path) lumps[LUMP_NODES].assign((unsigned char*)freenodes32a, (unsigned char*)(freenodes32a)+bsp_header.lump[LUMP_NODES].nLength); } - - unsigned char* oldfaces = (unsigned char*)faces; BSPFACE16* freefaces16 = NULL; if (!is_bsp2) @@ -4741,7 +4746,6 @@ void Bsp::write(const std::string& path) lumps[LUMP_FACES].assign((unsigned char*)freefaces16, (unsigned char*)(freefaces16)+bsp_header.lump[LUMP_FACES].nLength); } - unsigned char* oldmarksurfs = (unsigned char*)marksurfs; unsigned short* freemarksurfs16 = NULL; if (!is_bsp2) @@ -4755,7 +4759,6 @@ void Bsp::write(const std::string& path) lumps[LUMP_MARKSURFACES].assign((unsigned char*)freemarksurfs16, (unsigned char*)(freemarksurfs16)+bsp_header.lump[LUMP_MARKSURFACES].nLength); } - unsigned char* oldleaves = (unsigned char*)leaves; BSPLEAF16* freeleaves16 = NULL; BSPLEAF32A* freeleaves32a = NULL; @@ -4804,7 +4807,6 @@ void Bsp::write(const std::string& path) lumps[LUMP_LEAVES].assign((unsigned char*)freeleaves32a, (unsigned char*)(freeleaves32a)+bsp_header.lump[LUMP_LEAVES].nLength); } - unsigned char* oldedges = (unsigned char*)edges; BSPEDGE16* freeedges16 = NULL; if (!is_bsp2) @@ -5733,121 +5735,13 @@ bool Bsp::load_lumps(const std::string& fpath) return valid; } -void Bsp::load_ents() +void Bsp::reload_ents() { for (size_t i = 0; i < ents.size(); i++) delete ents[i]; ents.clear(); - std::istringstream in(std::string((char*)lumps[LUMP_ENTITIES].data(), (char*)lumps[LUMP_ENTITIES].data() + bsp_header.lump[LUMP_ENTITIES].nLength)); - - int lineNum = 0; - int lastBracket = -1; - Entity* ent = NULL; - - std::string line; - while (std::getline(in, line)) - { - lineNum++; - - while (line[0] == ' ' || line[0] == '\t' || line[0] == '\r') - { - line.erase(line.begin()); - } - - if (line.length() < 1 || line[0] == '\n') - continue; - - if (line[0] == '{') - { - if (lastBracket == 0) - { - print_log(get_localized_string(LANG_0103), bsp_path, lineNum); - continue; - } - lastBracket = 0; - delete ent; - ent = new Entity(); - - if (line.find('}') == std::string::npos && - line.find('\"') == std::string::npos) - { - continue; - } - } - if (line[0] == '}') - { - if (lastBracket == 1) - print_log(get_localized_string(LANG_0104), bsp_path, lineNum); - lastBracket = 1; - if (!ent) - continue; - - if (ent->keyvalues.count("classname")) - ents.push_back(ent); - else - print_log(get_localized_string(LANG_0105)); - - ent = NULL; - - // you can end/start an ent on the same line, you know - if (line.find('{') != std::string::npos) - { - ent = new Entity(); - lastBracket = 0; - - if (line.find('\"') == std::string::npos) - { - continue; - } - line.erase(line.begin()); - } - } - if (lastBracket == 0 && ent) // currently defining an entity - { - Keyvalues k(line); - for (size_t i = 0; i < k.keys.size(); i++) - { - ent->addKeyvalue(k.keys[i], k.values[i], true); - } - - if (line.find('}') != std::string::npos) - { - lastBracket = 1; - - if (ent->keyvalues.count("classname")) - ents.push_back(ent); - else - print_log(get_localized_string(LANG_1022)); - - ent = NULL; - } - if (line.find('{') != std::string::npos) - { - ent = new Entity(); - lastBracket = 0; - } - } - } - - // swap worldspawn to first entity - if (ents.size() > 1) - { - if (ents[0]->keyvalues["classname"] != "worldspawn") - { - print_log(get_localized_string(LANG_0106)); - for (size_t i = 1; i < ents.size(); i++) - { - if (ents[i]->keyvalues["classname"] == "worldspawn") - { - std::swap(ents[0], ents[i]); - break; - } - } - } - } - - delete ent; + ents = load_ents(std::string((char*)lumps[LUMP_ENTITIES].data(), (char*)lumps[LUMP_ENTITIES].data() + bsp_header.lump[LUMP_ENTITIES].nLength),bsp_name); } void Bsp::print_stat(const std::string& name, unsigned int val, unsigned int max, bool isMem) @@ -11080,6 +10974,7 @@ void Bsp::ExportToSmdWIP(const std::string& path, bool split, bool oneRoot) save_undo_lightmaps(); resize_all_lightmaps(); + bsprend->reuploadTextures(); bsprend->loadLightmaps(); @@ -11455,14 +11350,13 @@ void Bsp::ExportToSmdWIP(const std::string& path, bool split, bool oneRoot) update_lump_pointers(); remove_unused_model_structures(CLEAN_MODELS); - save_undo_lightmaps(); resize_all_lightmaps(); bsprend->reloadTextures(); bsprend->loadLightmaps(); update_ent_lump(); update_lump_pointers(); - renderer->pushModelUndoState("EXPORT .SMD EDITED", EDIT_MODEL_LUMPS | FL_ENTITIES); + renderer->pushUndoState("EXPORT .SMD EDITED", EDIT_MODEL_LUMPS | FL_ENTITIES); } void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, bool with_mdl, bool export_csm, int grouping) @@ -11498,7 +11392,6 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, print_log(PRINT_RED, " Merged {} verts \n", merged); remove_unused_model_structures(CLEAN_EDGES_FORCE | CLEAN_TEXINFOS_FORCE); - save_undo_lightmaps(); resize_all_lightmaps(); bsprend->reuploadTextures(); bsprend->loadLightmaps(); @@ -11554,10 +11447,6 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, g_progress = tmp; remove_unused_model_structures(); - tmp.tick(); - g_progress = tmp; - save_undo_lightmaps(); - tmp.tick(); g_progress = tmp; resize_all_lightmaps(); @@ -11576,7 +11465,7 @@ void Bsp::ExportToObjWIP(const std::string& path, int iscale, bool lightmapmode, tmp.tick(); g_progress = tmp; - renderer->pushModelUndoState("CREATE MDL->BSP MODEL", EDIT_MODEL_LUMPS | FL_ENTITIES); + renderer->pushUndoState("CREATE MDL->BSP MODEL", EDIT_MODEL_LUMPS | FL_ENTITIES); } else renderer->preRenderEnts(); @@ -12152,7 +12041,6 @@ void Bsp::ExportToMapWIP(const std::string& path, bool selected, bool merge_face print_log(PRINT_RED, " Merged {} verts \n", merged); remove_unused_model_structures(CLEAN_EDGES_FORCE | CLEAN_TEXINFOS_FORCE); - save_undo_lightmaps(); resize_all_lightmaps(); bsprend->reuploadTextures(); bsprend->loadLightmaps(); @@ -13531,7 +13419,6 @@ void Bsp::ExportToMapWIP(const std::string& path, bool selected, bool merge_face if (!selected) { - save_undo_lightmaps(); resize_all_lightmaps(); bsprend->reloadTextures(); bsprend->loadLightmaps(); @@ -13548,7 +13435,7 @@ void Bsp::ExportToMapWIP(const std::string& path, bool selected, bool merge_face print_log(PRINT_BLUE, "Export {} wad!\n", targetMapFileName); } - renderer->pushModelUndoState("EXPORT .MAP EDITED", EDIT_MODEL_LUMPS | FL_ENTITIES); + renderer->pushUndoState("EXPORT .MAP EDITED", EDIT_MODEL_LUMPS | FL_ENTITIES); } else { @@ -15090,4 +14977,28 @@ int Bsp::CalcFaceTextureStep(int facenum) } return g_limits.textureStep; +} + +int Bsp::GetTriggerTexture() +{ + unsigned int totalTextures = ((unsigned int*)textures)[0]; + for (unsigned int i = 0; i < totalTextures; i++) + { + int texOffset = ((int*)textures)[i + 1]; + if (texOffset >= 0) + { + BSPMIPTEX& tex = *((BSPMIPTEX*)(textures + texOffset)); + if (tex.szName[0] != '\0' && strcasecmp(tex.szName, "aaatrigger") == 0) + { + return i; + } + } + } + return -1; +} + +int Bsp::AddTriggerTexture() +{ + //print_log(get_localized_string(LANG_0295)); + return add_texture("aaatrigger", aaatriggerTex->get_data(), aaatriggerTex->width, aaatriggerTex->height); } \ No newline at end of file diff --git a/src/bsp/Bsp.h b/src/bsp/Bsp.h index 0293651b..fc06c04c 100644 --- a/src/bsp/Bsp.h +++ b/src/bsp/Bsp.h @@ -210,8 +210,7 @@ class Bsp // TODO: split any planes shared with other models bool vertex_manipulation_sync(int modelIdx, const std::vector& hullVerts, bool convexCheckOnly); - void load_ents(); - + void reload_ents(); // call this after editing ents void update_ent_lump(bool stripNodes = false); @@ -402,7 +401,7 @@ class Bsp bool does_model_use_shared_structures(int modelIdx); // returns the current lump contents - LumpState duplicate_lumps(unsigned int targets); + LumpState duplicate_lumps(unsigned int targets = 0xFFFFFFFF); void replace_lumps(const LumpState& state); @@ -473,6 +472,8 @@ class Bsp bool CalcFaceExtents(lightinfo_t* l); int CalcFaceTextureStep(int facenum); + int GetTriggerTexture(); + int AddTriggerTexture(); unsigned int remove_unused_lightmaps(std::vector & usedFaces); unsigned int remove_unused_visdata(BSPLEAF32* oldLeaves, int oldWorldLeaves, int oldLeavesMemSize); // called after removing unused leaves diff --git a/src/bsp/BspMerger.cpp b/src/bsp/BspMerger.cpp index bb6622b6..8235c4aa 100644 --- a/src/bsp/BspMerger.cpp +++ b/src/bsp/BspMerger.cpp @@ -47,7 +47,7 @@ MergeResult BspMerger::merge(std::vector maps, const vec3& gap, const std: { Bsp* mapB = maps[b]; - mapB->load_ents(); + mapB->reload_ents(); mapB->save_undo_lightmaps(); g_progress.update("Merging lightstyles", (int)(maps[0]->faceCount + maps[b]->faceCount)); @@ -1100,7 +1100,7 @@ bool BspMerger::merge(Bsp& mapA, Bsp& mapB, bool modelMerge) switch (i) { case LUMP_ENTITIES: - mapA.load_ents(); break; + mapA.reload_ents(); break; } } } diff --git a/src/bsp/bsptypes.cpp b/src/bsp/bsptypes.cpp index 686b7627..ae6251e3 100644 --- a/src/bsp/bsptypes.cpp +++ b/src/bsp/bsptypes.cpp @@ -115,4 +115,48 @@ bool BSPLEAF32A::isEmpty() emptyLeaf.nContents = CONTENTS_SOLID; return memcmp(&emptyLeaf, this, sizeof(BSPLEAF32A)) == 0; +} + +std::vector getDiffModels(LumpState& oldLump, LumpState& newLump) +{ + std::vector updateModels{}; + if (newLump.lumps[LUMP_MODELS].empty()) + return updateModels; + + if (oldLump.lumps[LUMP_MODELS].empty() && newLump.lumps[LUMP_MODELS].size()) + { + int addModelCount = (int)(newLump.lumps[LUMP_MODELS].size() / sizeof(BSPMODEL)); + for (int i = 0; i < addModelCount; i++) + { + updateModels.push_back(i); + } + return updateModels; + } + + if (newLump.lumps[LUMP_MODELS].size() > oldLump.lumps[LUMP_MODELS].size()) + { + int curModelCount = (int)(oldLump.lumps[LUMP_MODELS].size() / sizeof(BSPMODEL)); + int addModelCount = (int)((newLump.lumps[LUMP_MODELS].size() - newLump.lumps[LUMP_MODELS].size()) / sizeof(BSPMODEL)); + for (int i = curModelCount; i < curModelCount + addModelCount; i++) + { + updateModels.push_back(i); + } + } + + size_t modelLumpCount = std::min(newLump.lumps[LUMP_MODELS].size(), oldLump.lumps[LUMP_MODELS].size()); + + modelLumpCount /= sizeof(BSPMODEL); + + BSPMODEL* listOld = (BSPMODEL*)oldLump.lumps[LUMP_MODELS].data(); + BSPMODEL* listNew = (BSPMODEL*)newLump.lumps[LUMP_MODELS].data(); + + for (size_t i = 0; i < modelLumpCount; i++) + { + if (memcmp(&listOld[i], &listNew[i], sizeof(BSPMODEL)) != 0) + { + updateModels.push_back((int)i); + } + } + + return updateModels; } \ No newline at end of file diff --git a/src/bsp/bsptypes.h b/src/bsp/bsptypes.h index 63268b37..142287a8 100644 --- a/src/bsp/bsptypes.h +++ b/src/bsp/bsptypes.h @@ -53,7 +53,7 @@ struct BSPFACE_INFOEX short groupid; // to determine equal landscapes from various groups, -1 - no group }; -enum lump_copy_targets +enum lump_copy_targets : unsigned int { FL_ENTITIES = 1, FL_PLANES = 2, @@ -72,7 +72,7 @@ enum lump_copy_targets FL_MODELS = 16384 }; -enum clean_unused_lump +enum clean_unused_lump : unsigned int { CLEAN_PLANES = 2, CLEAN_TEXTURES = 4, @@ -365,11 +365,6 @@ struct BSPHEADER_EX } }; -struct LumpState -{ - std::vector lumps[HEADER_LUMPS]; -}; - struct BSPFACE16 { unsigned short iPlane; // Plane the face is parallel to @@ -560,4 +555,13 @@ struct TraceResult vec3 vecPlaneNormal; // surface normal at impact //edict_t* pHit; // entity the surface is on int iHitgroup; // 0 == generic, non zero is specific body part -}; \ No newline at end of file +}; + +struct LumpState +{ + void* map; + std::vector lumps[HEADER_LUMPS]; + LumpState(void* _map) : map(_map) {} +}; + +std::vector getDiffModels(LumpState& oldLump, LumpState& newLump); \ No newline at end of file diff --git a/src/editor/BspRenderer.cpp b/src/editor/BspRenderer.cpp index 2f4a8ae6..d159b4c4 100644 --- a/src/editor/BspRenderer.cpp +++ b/src/editor/BspRenderer.cpp @@ -17,7 +17,7 @@ #include -BspRenderer::BspRenderer(Bsp* _map) +BspRenderer::BspRenderer(Bsp* _map) : undoLumpState(LumpState(_map)) { map = _map; map->setBspRender(this); @@ -232,9 +232,7 @@ BspRenderer::BspRenderer(Bsp* _map) map->ents[i]->getTargets(); } - undoLumpState = LumpState(); - undoEntityStateMap = std::map(); - + undoLumpState = LumpState(map); saveLumpState(); } @@ -1916,8 +1914,7 @@ void BspRenderer::updateClipnodeOpacity(unsigned char newValue) void BspRenderer::preRenderEnts() { - while (renderEnts.size() < map->ents.size()) - renderEnts.emplace_back(RenderEnt()); + renderEnts.resize(map->ents.size(), RenderEnt()); for (int i = 0; i < (int)map->ents.size(); i++) { @@ -1995,17 +1992,8 @@ void BspRenderer::refreshEnt(int entIdx, int refreshFlags) if (entIdx >= (int)map->ents.size()) return; - //print_log("Refresh {} ent with flags: {}\n", entIdx, refreshFlags); - - while (renderEnts.size() < map->ents.size()) - { - renderEnts.emplace_back(RenderEnt()); - } - - while (renderEnts.size() > map->ents.size()) - { - renderEnts.pop_back(); - } + if (renderEnts.size() != map->ents.size()) + renderEnts.resize(map->ents.size(), RenderEnt()); int skin = -1; int sequence = -1; @@ -2702,16 +2690,10 @@ void BspRenderer::render(bool modelVertsDraw, int clipnodeHull) renderOffset = mapOffset.flip(); localCameraOrigin = cameraOrigin - mapOffset; - if (delayEntUndoList.size()) + if (delayEntUndo) { - for (const auto& undoEnt : delayEntUndoList) - { - if (undoEnt.entIdx < (int)map->ents.size() && undoEnt.ent == map->ents[undoEnt.entIdx]) - { - pushEntityUndoState(undoEnt.description, undoEnt.entIdx); - } - } - delayEntUndoList.clear(); + delayEntUndo = false; + pushUndoState(delayEntUndoDesc, FL_ENTITIES); } g_app->matmodel.loadIdentity(); @@ -3710,81 +3692,15 @@ int BspRenderer::getBestClipnodeHull(int modelIdx) } -void BspRenderer::saveEntityState(int entIdx) -{ - undoEntityStateMap[entIdx] = *map->ents[entIdx]; -} - void BspRenderer::saveLumpState() { if (g_settings.verboseLogs) print_log("SAVE LUMP STATES TO BACKUP\n"); - map->update_ent_lump(); - for (int i = 0; i < HEADER_LUMPS; i++) - { - undoLumpState.lumps[i] = map->lumps[i]; - } -} - -void BspRenderer::pushEntityUndoStateDelay(const std::string& actionDesc, int entIdx, Entity* ent) -{ - delayEntUndoList.emplace_back(actionDesc, entIdx, ent); + undoLumpState = map->duplicate_lumps(); } -void BspRenderer::pushEntityUndoState(const std::string& actionDesc, int entIdx) -{ - if (g_settings.verboseLogs) - print_log("SAVE ENT STATES TO BACKUP\n"); - if ((size_t)entIdx >= map->ents.size()) - { - print_log(get_localized_string(LANG_0287)); - return; - } - - Entity* ent = map->ents[entIdx]; - - if (!ent) - { - print_log(get_localized_string(LANG_0288)); - return; - } - - bool anythingToUndo = false; - if (undoEntityStateMap[entIdx].keyOrder.size() == ent->keyOrder.size()) - { - for (size_t i = 0; i < undoEntityStateMap[entIdx].keyOrder.size(); i++) - { - std::string oldKey = undoEntityStateMap[entIdx].keyOrder[i]; - std::string newKey = ent->keyOrder[i]; - if (oldKey != newKey) - { - anythingToUndo = true; - break; - } - std::string oldVal = undoEntityStateMap[entIdx].keyvalues[oldKey]; - std::string newVal = ent->keyvalues[oldKey]; - if (oldVal != newVal) - { - anythingToUndo = true; - break; - } - } - } - else - { - anythingToUndo = true; - } - - if (!anythingToUndo) - { - print_log(PRINT_RED | PRINT_INTENSITY, get_localized_string(LANG_0289)); - return; // nothing to undo - } - pushUndoCommand(new EditEntityCommand(actionDesc, entIdx, undoEntityStateMap[entIdx], *ent)); - saveEntityState(entIdx); -} -void BspRenderer::pushModelUndoState(const std::string& actionDesc, unsigned int targets) +void BspRenderer::pushUndoState(const std::string& actionDesc, unsigned int targets) { if (g_settings.verboseLogs) print_log("SAVE MODEL STATES TO BACKUP\n"); @@ -3794,21 +3710,6 @@ void BspRenderer::pushModelUndoState(const std::string& actionDesc, unsigned int return; } - auto entIdx = g_app->pickInfo.selectedEnts; - if (!entIdx.size() && g_app->pickInfo.selectedFaces.size()) - { - int modelIdx = map->get_model_from_face((int)g_app->pickInfo.selectedFaces[0]); - if (modelIdx >= 0) - { - int entid = map->get_ent_from_model(modelIdx); - entIdx.push_back(entid); - } - } - if (!entIdx.size()) - { - entIdx.push_back(0); - } - LumpState newLumps = map->duplicate_lumps(targets); bool differences[HEADER_LUMPS] = { false }; @@ -3851,16 +3752,21 @@ void BspRenderer::pushModelUndoState(const std::string& actionDesc, unsigned int } } - EditBspModelCommand* editCommand = new EditBspModelCommand(actionDesc, (int)entIdx[0], undoLumpState, newLumps, undoEntityStateMap[entIdx[0]].origin, targetLumps); + EditBspCommand* editCommand = new EditBspCommand(actionDesc, undoLumpState, newLumps, targetLumps); pushUndoCommand(editCommand); - // entity origin edits also update the ent origin (TODO: this breaks when moving + scaling something) - // saveEntityState((int)entIdx[0]); + if (differences[LUMP_ENTITIES]) + { + preRenderEnts(); + } } -void BspRenderer::pushUndoCommand(Command* cmd) +void BspRenderer::pushUndoCommand(EditBspCommand* cmd) { cmd->execute(); + + undoLumpState = map->duplicate_lumps(); + undoHistory.push_back(cmd); clearRedoCommands(); @@ -3871,8 +3777,6 @@ void BspRenderer::pushUndoCommand(Command* cmd) } calcUndoMemoryUsage(); - - saveLumpState(); } void BspRenderer::undo() @@ -3882,8 +3786,8 @@ void BspRenderer::undo() return; } - Command* undoCommand = undoHistory[undoHistory.size() - 1]; - if (!undoCommand->allowedDuringLoad && g_app->isLoading) + EditBspCommand* undoCommand = undoHistory[undoHistory.size() - 1]; + if (g_app->isLoading) { print_log(PRINT_RED | PRINT_INTENSITY, get_localized_string(LANG_0293), undoCommand->desc); return; @@ -3893,6 +3797,11 @@ void BspRenderer::undo() undoHistory.pop_back(); redoHistory.push_back(undoCommand); g_app->updateEnts(); + + undoLumpState = map->duplicate_lumps(); + + + calcUndoMemoryUsage(); } void BspRenderer::redo() @@ -3902,8 +3811,8 @@ void BspRenderer::redo() return; } - Command* redoCommand = redoHistory[redoHistory.size() - 1]; - if (!redoCommand->allowedDuringLoad && g_app->isLoading) + EditBspCommand * redoCommand = redoHistory[redoHistory.size() - 1]; + if (g_app->isLoading) { print_log(PRINT_RED | PRINT_INTENSITY, get_localized_string(LANG_0294), redoCommand->desc); return; @@ -3913,6 +3822,10 @@ void BspRenderer::redo() redoHistory.pop_back(); undoHistory.push_back(redoCommand); g_app->updateEnts(); + + + calcUndoMemoryUsage(); + } void BspRenderer::clearUndoCommands() @@ -3939,15 +3852,18 @@ void BspRenderer::clearRedoCommands() void BspRenderer::calcUndoMemoryUsage() { - undoMemoryUsage = (undoHistory.size() + redoHistory.size()) * sizeof(Command*); + undoMemoryUsageZip = (undoHistory.size() + redoHistory.size()) * sizeof(EditBspCommand*); + undoMemoryUsage = undoMemoryUsageZip; for (size_t i = 0; i < undoHistory.size(); i++) { + undoMemoryUsageZip += undoHistory[i]->memoryUsageZip(); undoMemoryUsage += undoHistory[i]->memoryUsage(); } for (size_t i = 0; i < redoHistory.size(); i++) { - undoMemoryUsage += redoHistory[i]->memoryUsage(); + undoMemoryUsageZip += redoHistory[i]->memoryUsageZip(); + undoMemoryUsageZip += redoHistory[i]->memoryUsage(); } } @@ -3957,6 +3873,12 @@ void BspRenderer::clearDrawCache() drawedNodes.clear(); } +void BspRenderer::pushEntityUndoStateDelay(const std::string& desc) +{ + delayEntUndo = true; + delayEntUndoDesc = desc; +} + PickInfo::PickInfo() { selectedEnts.clear(); diff --git a/src/editor/BspRenderer.h b/src/editor/BspRenderer.h index 8d08ba14..5f153510 100644 --- a/src/editor/BspRenderer.h +++ b/src/editor/BspRenderer.h @@ -16,7 +16,7 @@ #include #include -class Command; +class EditBspCommand; enum RENDER_PASS : int { @@ -302,11 +302,12 @@ class BspRenderer void delayLoadData(); int getBestClipnodeHull(int modelIdx); + size_t undoMemoryUsageZip = 0; // approximate space used by undo+redo history (compressed) size_t undoMemoryUsage = 0; // approximate space used by undo+redo history - std::vector undoHistory; - std::vector redoHistory; - std::map undoEntityStateMap; - LumpState undoLumpState{}; + + std::vector undoHistory; + std::vector redoHistory; + LumpState undoLumpState; struct DelayEntUndo { @@ -320,18 +321,17 @@ class BspRenderer } }; - std::vector delayEntUndoList; + bool delayEntUndo = false; + std::string delayEntUndoDesc = "undo"; + + void pushUndoState(const std::string& actionDesc, unsigned int targets); + void pushEntityUndoStateDelay(const std::string& desc); - void pushModelUndoState(const std::string& actionDesc, unsigned int targets); - void pushEntityUndoState(const std::string& actionDesc, int entIdx); - void pushEntityUndoStateDelay(const std::string& actionDesc, int entIdx, Entity* ent); - void pushUndoCommand(Command* cmd); void undo(); void redo(); void clearUndoCommands(); void clearRedoCommands(); void calcUndoMemoryUsage(); - void saveEntityState(int entIdx); void saveLumpState(); void clearDrawCache(); @@ -342,6 +342,8 @@ class BspRenderer float intersectDist; private: + void pushUndoCommand(EditBspCommand* cmd); + struct nodeBuffStr { int modelIdx = 0; diff --git a/src/editor/Command.cpp b/src/editor/Command.cpp index b8813172..78931218 100644 --- a/src/editor/Command.cpp +++ b/src/editor/Command.cpp @@ -4,647 +4,156 @@ #include #include "log.h" +std::vector compressData(const std::vector& data) { + unsigned char* out = NULL; + size_t outsize = 0; + LodePNGCompressSettings settings; + lodepng_compress_settings_init(&settings); -Bsp* Command::getBsp() -{ - if (mapIdx < 0 || mapIdx >= (int)mapRenderers.size()) - { - return NULL; + unsigned error = lodepng_zlib_compress(&out, &outsize, data.data(), data.size(), &settings); + if (error) { + throw std::runtime_error("Compression failed: " + std::string(lodepng_error_text(error))); } - return mapRenderers[mapIdx]->map; + std::vector compressedData(out, out + outsize); + free(out); + return compressedData; } -BspRenderer* Command::getBspRenderer() -{ - if (mapIdx < 0 || mapIdx >= (int)mapRenderers.size()) - { - return NULL; - } - - return mapRenderers[mapIdx]; -} +std::vector decompressData(const std::vector& compressedData) { + unsigned char* out = NULL; + size_t outsize = 0; + LodePNGDecompressSettings settings; + lodepng_decompress_settings_init(&settings); -EditEntityCommand::EditEntityCommand(std::string desc, int entIdx, Entity _oldEntData, Entity _newEntData) - : Command(desc, g_app->getSelectedMapId()), oldEntData(std::move(_oldEntData)), newEntData(std::move(_newEntData)) -{ - this->entIdx = entIdx; - this->allowedDuringLoad = true; -} - -void EditEntityCommand::execute() -{ - Entity* target = getEnt(); - *target = newEntData; - refresh(); - BspRenderer* renderer = getBspRenderer(); - renderer->saveEntityState(entIdx); -} - -void EditEntityCommand::undo() -{ - Entity* target = getEnt(); - *target = oldEntData; - refresh(); - BspRenderer* renderer = getBspRenderer(); - renderer->saveEntityState(entIdx); -} - -Entity* EditEntityCommand::getEnt() -{ - Bsp* map = getBsp(); - - if (!map || entIdx < 0 || entIdx >= (int)map->ents.size()) - { - return NULL; + unsigned error = lodepng_zlib_decompress(&out, &outsize, compressedData.data(), compressedData.size(), &settings); + if (error) { + throw std::runtime_error("Decompression failed: " + std::string(lodepng_error_text(error))); } - return map->ents[entIdx]; -} - -void EditEntityCommand::refresh() -{ - BspRenderer* renderer = getBspRenderer(); - if (!renderer) - return; - Entity* ent = getEnt(); - if (!ent) - return; - renderer->refreshEnt(entIdx); - pickCount++; // force GUI update -} - -size_t EditEntityCommand::memoryUsage() -{ - return sizeof(EditEntityCommand) + oldEntData.getMemoryUsage() + newEntData.getMemoryUsage(); -} - -// -// Delete entity -// -DeleteEntityCommand::DeleteEntityCommand(std::string desc, int entIdx) - : Command(desc, g_app->getSelectedMapId()) -{ - this->entIdx = entIdx; - this->entData = new Entity(); - *this->entData = *(g_app->getSelectedMap()->ents[entIdx]); - this->allowedDuringLoad = true; + std::vector decompressedData(out, out + outsize); + free(out); + return decompressedData; } -DeleteEntityCommand::~DeleteEntityCommand() -{ - delete entData; -} -void DeleteEntityCommand::execute() +EditBspCommand::EditBspCommand(const std::string & desc, LumpState _oldLumps, LumpState _newLumps, unsigned int targetLumps) : + desc(desc), oldLumps(std::move(_oldLumps)), newLumps(std::move(_newLumps)), targetLumps(targetLumps), memoryused(0) { - Bsp* map = getBsp(); - - if (!map) - return; - - g_app->deselectObject(); - - Entity* ent = map->ents[entIdx]; - - map->ents.erase(map->ents.begin() + entIdx); - - refresh(); - - delete ent; -} - -void DeleteEntityCommand::undo() -{ - Bsp* map = getBsp(); - if (!map) - return; - - Entity* newEnt = new Entity(); - *newEnt = *entData; - map->ents.insert(map->ents.begin() + entIdx, newEnt); - - g_app->pickInfo.SetSelectedEnt(entIdx); - - refresh(); - - if (newEnt->hasKey("model") && - toLowerCase(newEnt->keyvalues["model"]).find(".bsp") != std::string::npos) - { - g_app->reloadBspModels(); - } -} - -void DeleteEntityCommand::refresh() -{ - BspRenderer* renderer = getBspRenderer(); - if (!renderer) - return; - - renderer->preRenderEnts(); - g_app->gui->refresh(); -} - -size_t DeleteEntityCommand::memoryUsage() -{ - return sizeof(DeleteEntityCommand) + entData->getMemoryUsage(); -} - - -// -// Create Entity -// -CreateEntityCommand::CreateEntityCommand(std::string desc, int mapIdx, Entity* entData) : Command(desc, mapIdx) -{ - this->entData = new Entity(); - *this->entData = *entData; - this->allowedDuringLoad = true; -} - -CreateEntityCommand::~CreateEntityCommand() -{ - delete entData; -} - -void CreateEntityCommand::execute() -{ - Bsp* map = getBsp(); - if (!map) - return; - - Entity* newEnt = new Entity(); - *newEnt = *entData; - map->ents.push_back(newEnt); - map->update_ent_lump(); - BspRenderer* renderer = getBspRenderer(); - if (!renderer || map->ents.size() == 0) - return; - renderer->refreshEnt((int)(map->ents.size()) - 1); - g_app->gui->refresh(); -} - -void CreateEntityCommand::undo() -{ - Bsp* map = getBsp(); - if (!map || !map->ents.size()) - return; - - g_app->deselectObject(); - - delete map->ents[map->ents.size() - 1]; - map->ents.pop_back(); - BspRenderer* renderer = getBspRenderer(); - if (!renderer) - return; - renderer->preRenderEnts(); - g_app->gui->refresh(); -} - -size_t CreateEntityCommand::memoryUsage() -{ - return sizeof(CreateEntityCommand) + entData->getMemoryUsage(); -} - -// -// Create Entities From Text -// -CreateEntityFromTextCommand::CreateEntityFromTextCommand(std::string desc, int mapIdx, std::string textData) : Command(desc, mapIdx) { - this->textData = std::move(textData); - this->allowedDuringLoad = true; - createdEnts = 0; -} - -CreateEntityFromTextCommand::~CreateEntityFromTextCommand() { -} - -void CreateEntityFromTextCommand::execute() { - Bsp* map = getBsp(); - - std::istringstream in(textData); - - int lineNum = 0; - int lastBracket = -1; - Entity* ent = NULL; - - std::vector ents; - - std::string line = ""; - while (std::getline(in, line)) + for (int i = 0; i < HEADER_LUMPS; i++) { - lineNum++; - if (line.length() < 1 || line[0] == '\n') - continue; + memoryused += oldLumps.lumps[i].size(); + memoryused += newLumps.lumps[i].size(); - if (line[0] == '{') + if (oldLumps.lumps[i].size()) { - if (lastBracket == 0) - { - print_log("clipboard ent text data (line {}): Unexpected '{'\n", lineNum); - continue; - } - lastBracket = 0; - - delete ent; - ent = new Entity(); + oldLumps.lumps[i] = compressData(oldLumps.lumps[i]); } - else if (line[0] == '}') - { - if (lastBracket == 1) - print_log("clipboard ent text data (line {}): Unexpected '}'\n", lineNum); - lastBracket = 1; - - if (ent == NULL) - continue; - - if (ent->keyvalues.count("classname")) - ents.push_back(ent); - else - print_log("Found unknown classname entity. Skip it.\n"); - ent = NULL; - // you can end/start an ent on the same line, you know - if (line.find('{') != std::string::npos) - { - ent = new Entity(); - lastBracket = 0; - } - } - else if (lastBracket == 0 && ent != NULL) // currently defining an entity + if (newLumps.lumps[i].size()) { - Keyvalues keyvals(line); - for (size_t k = 0; k < keyvals.keys.size();k++) - { - if (keyvals.keys[k].length() && keyvals.values[k].length()) - ent->addKeyvalue(keyvals.keys[k], keyvals.values[k]); - } + newLumps.lumps[i] = compressData(newLumps.lumps[i]); } } - - for (Entity* e : ents) { - map->ents.push_back(e); - } - createdEnts = ents.size(); - print_log("Pasted {} entities from clipboard\n", createdEnts); - - refresh(); -} - -void CreateEntityFromTextCommand::undo() { - Bsp* map = getBsp(); - - g_app->deselectObject(); - - for (size_t i = 0; i < createdEnts; i++) { - delete map->ents[map->ents.size() - 1]; - map->ents.pop_back(); - } - - refresh(); } -void CreateEntityFromTextCommand::refresh() { - BspRenderer* renderer = getBspRenderer(); - renderer->preRenderEnts(); - g_app->gui->refresh(); - g_app->updateCullBox(); -} - -size_t CreateEntityFromTextCommand::memoryUsage() { - return sizeof(CreateEntityFromTextCommand) + textData.size(); -} - -// -// Duplicate BSP Model command -// -DuplicateBspModelCommand::DuplicateBspModelCommand(std::string desc, int entIdx) - : Command(desc, g_app->getSelectedMapId()) +void EditBspCommand::execute() { - int tmpentIdx = entIdx; - int modelIdx = -1; - Bsp* map = g_app->getSelectedMap(); - if (map && tmpentIdx >= 0) + Bsp* map = NULL; + BspRenderer* renderer = NULL; + + for (auto& r : mapRenderers) { - modelIdx = map->ents[tmpentIdx]->getBspModelIdx(); - if (modelIdx < 0 && map->is_worldspawn_ent(tmpentIdx)) + if (r->map == oldLumps.map) { - modelIdx = 0; + map = r->map; + renderer = r; } } - this->oldModelIdx = modelIdx; - this->newModelIdx = -1; - this->entIdx = tmpentIdx; - this->allowedDuringLoad = false; -} - -DuplicateBspModelCommand::~DuplicateBspModelCommand() -{ -} - -void DuplicateBspModelCommand::execute() -{ - Bsp* map = getBsp(); - Entity* ent = map->ents[entIdx]; - BspRenderer* renderer = getBspRenderer(); - - //int dupLumps = FL_CLIPNODES | FL_EDGES | FL_FACES | FL_NODES | FL_PLANES | FL_SURFEDGES | FL_TEXINFO | FL_VERTICES | FL_LIGHTING | FL_MODELS; - oldLumps = map->duplicate_lumps(0xFFFFFFFF); - - newModelIdx = map->duplicate_model(oldModelIdx); - - ent->setOrAddKeyvalue("model", "*" + std::to_string(newModelIdx)); - - map->remove_unused_model_structures(CLEAN_LEAVES); - - renderer->loadLightmaps(); - renderer->refreshEnt(entIdx); - - renderer->refreshModel(newModelIdx); - - g_app->pickInfo.selectedFaces.clear(); - - if (g_app->pickInfo.selectedEnts.size()) - g_app->pickInfo.SetSelectedEnt(g_app->pickInfo.selectedEnts[0]); - - pickCount++; - vertPickCount++; - - g_app->gui->refresh(); -} - -void DuplicateBspModelCommand::undo() -{ - Bsp* map = getBsp(); - BspRenderer* renderer = getBspRenderer(); - - g_app->deselectObject(); - map->replace_lumps(oldLumps); - - Entity* ent = map->ents[entIdx]; - ent->setOrAddKeyvalue("model", "*" + std::to_string(oldModelIdx)); - - - map->update_ent_lump(); - renderer->reuploadTextures(); - renderer->loadLightmaps(); - renderer->preRenderEnts(); - g_app->gui->refresh(); -} - -size_t DuplicateBspModelCommand::memoryUsage() -{ - size_t size = sizeof(DuplicateBspModelCommand); - - for (int i = 0; i < HEADER_LUMPS; i++) - { - size += oldLumps.lumps[i].size(); - } - - return size; -} - + if (!map) + return; -// -// Create BSP model -// -CreateBspModelCommand::CreateBspModelCommand(std::string desc, int mapIdx, Entity* entData, float size, bool empty) : Command(desc, mapIdx) -{ - this->entData = new Entity(); - *this->entData = *entData; - this->mdl_size = size; - this->empty = empty; for (int i = 0; i < HEADER_LUMPS; i++) { - oldLumps.lumps[i].clear(); - } -} + if (oldLumps.lumps[i].size()) + { + oldLumps.lumps[i] = decompressData(oldLumps.lumps[i]); + } -CreateBspModelCommand::~CreateBspModelCommand() -{ - if (entData) - { - delete entData; - entData = NULL; + if (newLumps.lumps[i].size()) + { + newLumps.lumps[i] = decompressData(newLumps.lumps[i]); + } } -} - -void CreateBspModelCommand::execute() -{ - Bsp* map = getBsp(); - if (!map) - return; - BspRenderer* renderer = getBspRenderer(); - if (!renderer) - return; - - int aaatriggerIdx = getDefaultTextureIdx(); - int dupLumps = FL_MARKSURFACES | FL_EDGES | FL_FACES | FL_NODES | FL_PLANES | FL_CLIPNODES | FL_SURFEDGES | FL_TEXINFO | FL_VERTICES | FL_LIGHTING | FL_MODELS | FL_LEAVES; - if (aaatriggerIdx == -1) + if (targetLumps & FL_ENTITIES) { - dupLumps |= FL_TEXTURES; + map->update_ent_lump(); } - oldLumps = map->duplicate_lumps(dupLumps); + map->replace_lumps(newLumps); - bool NeedreloadTextures = false; - // add the aaatrigger texture if it doesn't already exist - if (aaatriggerIdx == -1) + auto mdls = getDiffModels(oldLumps, newLumps); + for (auto& mdl : mdls) { - NeedreloadTextures = true; - aaatriggerIdx = addDefaultTexture(); + renderer->refreshModel(mdl); } - vec3 mins = vec3(-mdl_size, -mdl_size, -mdl_size); - vec3 maxs = vec3(mdl_size, mdl_size, mdl_size); - int modelIdx = map->create_solid(mins, maxs, aaatriggerIdx, empty); - //BSPMODEL& model = map->models[modelIdx]; - - entData->addKeyvalue("model", "*" + std::to_string(modelIdx)); - - Entity* newEnt = new Entity(); - *newEnt = *entData; - map->ents.push_back(newEnt); - - g_app->deselectObject(); - - //renderer->updateLightmapInfos(); - //renderer->preRenderFaces(); - //renderer->preRenderEnts(); - //renderer->reloadTextures(); - //renderer->reloadLightmaps(); - //renderer->refreshModel(modelIdx); - // - - map->update_ent_lump(); - - map->save_undo_lightmaps(); - map->resize_all_lightmaps(); - - if (NeedreloadTextures) - renderer->reuploadTextures(); - renderer->loadLightmaps(); - renderer->refreshModel(modelIdx); - if (map->ents.size() > 0) - renderer->refreshEnt((int)(map->ents.size()) - 1); - renderer->calcFaceMaths(); - //g_app->reloading = true; - //renderer->reload(); - //g_app->reloading = false; - - g_app->gui->refresh(); -} - -void CreateBspModelCommand::undo() -{ - Bsp* map = getBsp(); - BspRenderer* renderer = getBspRenderer(); - - if (!map || !renderer || !map->ents.size()) - return; - - map->replace_lumps(oldLumps); - - delete map->ents[map->ents.size() - 1]; - map->ents.pop_back(); - - map->save_undo_lightmaps(); - map->resize_all_lightmaps(); - - renderer->reload(); - - g_app->gui->refresh(); - g_app->deselectObject(); -} - -size_t CreateBspModelCommand::memoryUsage() -{ - size_t size = sizeof(DuplicateBspModelCommand); - for (int i = 0; i < HEADER_LUMPS; i++) { - size += oldLumps.lumps[i].size(); - } - - return size; -} - -int CreateBspModelCommand::getDefaultTextureIdx() -{ - Bsp* map = getBsp(); - if (!map) - return -1; - - unsigned int totalTextures = ((unsigned int*)map->textures)[0]; - for (unsigned int i = 0; i < totalTextures; i++) - { - int texOffset = ((int*)map->textures)[i + 1]; - if (texOffset >= 0) + if (oldLumps.lumps[i].size()) { - BSPMIPTEX& tex = *((BSPMIPTEX*)(map->textures + texOffset)); - if (tex.szName[0] != '\0' && strcasecmp(tex.szName, "aaatrigger") == 0) - { - tex.nWidth = aaatriggerTex->width; - tex.nHeight = aaatriggerTex->height; - print_log(get_localized_string(LANG_0295)); - return i; - } + oldLumps.lumps[i] = compressData(oldLumps.lumps[i]); } - } - return -1; -} - -int CreateBspModelCommand::addDefaultTexture() -{ - Bsp* map = getBsp(); - if (!map) - return -1; - int aaatriggerIdx = map->add_texture("aaatrigger", aaatriggerTex->get_data(), aaatriggerTex->width, aaatriggerTex->height); - - return aaatriggerIdx; -} - -// -// Edit BSP model -// -EditBspModelCommand::EditBspModelCommand(std::string desc, int entIdx, LumpState oldLumps, LumpState newLumps, - vec3 oldOrigin, unsigned int targetLumps) : Command(desc, g_app->getSelectedMapId()) -{ - - this->oldLumps = oldLumps; - this->newLumps = newLumps; - this->targetLumps = targetLumps; - this->allowedDuringLoad = false; - this->oldOrigin = oldOrigin; - this->entIdx = entIdx; - - Bsp* map = g_app->getSelectedMap(); - if (map && entIdx >= 0) - { - this->modelIdx = map->ents[entIdx]->getBspModelIdx(); - this->newOrigin = map->ents[entIdx]->origin; - } - else - { - this->modelIdx = -1; - this->newOrigin = this->oldOrigin; + if (newLumps.lumps[i].size()) + { + newLumps.lumps[i] = compressData(newLumps.lumps[i]); + } } -} -EditBspModelCommand::~EditBspModelCommand() -{ - for (int i = 0; i < HEADER_LUMPS; i++) - { - oldLumps.lumps[i].clear(); - newLumps.lumps[i].clear(); - } } -void EditBspModelCommand::execute() +void EditBspCommand::undo() { - Bsp* map = getBsp(); - BspRenderer* renderer = getBspRenderer(); - if (!map || !renderer) - return; + Bsp* map = NULL; + BspRenderer* renderer = NULL; - map->replace_lumps(newLumps); - if (entIdx < (int)map->ents.size()) + for (auto& r : mapRenderers) { - map->ents[entIdx]->setOrAddKeyvalue("origin", newOrigin.toKeyvalueString()); - map->getBspRender()->undoEntityStateMap[entIdx].setOrAddKeyvalue("origin", newOrigin.toKeyvalueString()); - refresh(); + if (r->map == oldLumps.map) + { + map = r->map; + renderer = r; + } } -} -void EditBspModelCommand::undo() -{ - Bsp* map = getBsp(); if (!map) return; - map->replace_lumps(oldLumps); - map->ents[entIdx]->setOrAddKeyvalue("origin", oldOrigin.toKeyvalueString()); - map->getBspRender()->undoEntityStateMap[entIdx].setOrAddKeyvalue("origin", oldOrigin.toKeyvalueString()); - refresh(); - - BspRenderer* renderer = getBspRenderer(); - - if (targetLumps & FL_VERTICES) + for (int i = 0; i < HEADER_LUMPS; i++) { - if (renderer) + if (oldLumps.lumps[i].size()) { - renderer->preRenderFaces(); + oldLumps.lumps[i] = decompressData(oldLumps.lumps[i]); + } + + if (newLumps.lumps[i].size()) + { + newLumps.lumps[i] = decompressData(newLumps.lumps[i]); } } + map->replace_lumps(oldLumps); + if (targetLumps & FL_TEXTURES) { if (renderer) { renderer->reuploadTextures(); - renderer->preRenderFaces(); } } + if (targetLumps & FL_LIGHTING) { if (renderer) @@ -652,442 +161,57 @@ void EditBspModelCommand::undo() renderer->loadLightmaps(); } } -} -void EditBspModelCommand::refresh() -{ - Bsp* map = getBsp(); - if (!map) - return; - BspRenderer* renderer = getBspRenderer(); - - bool updateVerts = false; - - if (newLumps.lumps[LUMP_LIGHTING].size()) + if (targetLumps & FL_VERTICES || targetLumps & FL_TEXTURES) { - map->getBspRender()->loadLightmaps(); - updateVerts = true; + if (renderer) + { + renderer->preRenderFaces(); + } } - if (newLumps.lumps[LUMP_ENTITIES].size()) + + auto mdls = getDiffModels(oldLumps, newLumps); + for (auto& mdl : mdls) { - renderer->refreshEnt(entIdx); + renderer->refreshModel(mdl); } - renderer->refreshModel(modelIdx); - - g_app->gui->refresh(); - + pickCount++; vertPickCount++; -} - -size_t EditBspModelCommand::memoryUsage() -{ - size_t size = sizeof(DuplicateBspModelCommand); + for (int i = 0; i < HEADER_LUMPS; i++) { - size += oldLumps.lumps[i].size() + newLumps.lumps[i].size(); - } - - return size; -} - - -// -// Delete boxed data -// -DeleteBoxedDataCommand::DeleteBoxedDataCommand(std::string desc, int mapIdx, vec3 mins, vec3 maxs, LumpState oldLumps) : Command(desc, mapIdx) { - this->oldLumps = oldLumps; - this->allowedDuringLoad = false; - this->mins = mins; - this->maxs = maxs; -} - -DeleteBoxedDataCommand::~DeleteBoxedDataCommand() -{ - -} - -void DeleteBoxedDataCommand::execute() { - Bsp* map = getBsp(); - - map->delete_box_data(mins, maxs); - - refresh(); -} - -void DeleteBoxedDataCommand::undo() { - Bsp* map = getBsp(); - - map->replace_lumps(oldLumps); - map->load_ents(); - - refresh(); -} - -void DeleteBoxedDataCommand::refresh() { - BspRenderer* renderer = getBspRenderer(); - - renderer->reload(); - g_app->deselectObject(); - g_app->gui->refresh(); -} - -size_t DeleteBoxedDataCommand::memoryUsage() -{ - size_t size = sizeof(DeleteBoxedDataCommand); - - for (int i = 0; i < HEADER_LUMPS; i++) { - size += oldLumps.lumps[i].size(); - } - - return size; -} - - - -// -// Clean Map -// -CleanMapCommand::CleanMapCommand(std::string desc, int mapIdx, LumpState oldLumps) : Command(desc, mapIdx) -{ - this->oldLumps = oldLumps; - this->allowedDuringLoad = false; -} + if (oldLumps.lumps[i].size()) + { + oldLumps.lumps[i] = compressData(oldLumps.lumps[i]); + } -CleanMapCommand::~CleanMapCommand() -{ - for (int i = 0; i < HEADER_LUMPS; i++) - { - oldLumps.lumps[i].clear(); + if (newLumps.lumps[i].size()) + { + newLumps.lumps[i] = compressData(newLumps.lumps[i]); + } } } -void CleanMapCommand::execute() +size_t EditBspCommand::memoryUsageZip() { - Bsp* map = getBsp(); - if (!map) - return; - - BspRenderer* renderer = getBspRenderer(); - if (!renderer) - return; - print_log(get_localized_string(LANG_0296), map->bsp_name); - map->remove_unused_model_structures().print_delete_stats(1); - - refresh(); -} - -void CleanMapCommand::undo() -{ - Bsp* map = getBsp(); - if (!map) - return; - - map->replace_lumps(oldLumps); - - refresh(); -} - -void CleanMapCommand::refresh() -{ - Bsp* map = getBsp(); - if (!map) - return; - BspRenderer* renderer = getBspRenderer(); - - renderer->reload(); - g_app->deselectObject(); - g_app->gui->refresh(); -} - -size_t CleanMapCommand::memoryUsage() -{ - size_t size = sizeof(CleanMapCommand); + size_t size = sizeof(EditBspCommand); for (int i = 0; i < HEADER_LUMPS; i++) { - size += oldLumps.lumps[i].size(); - } - - return size; -} - - -// -// Delete OOB data -// -DeleteOobDataCommand::DeleteOobDataCommand(std::string desc, int mapIdx, int clipFlags, LumpState oldLumps) : Command(desc, mapIdx) { - this->oldLumps = oldLumps; - this->allowedDuringLoad = false; - this->clipFlags = clipFlags; -} - -DeleteOobDataCommand::~DeleteOobDataCommand() -{ - -} - -void DeleteOobDataCommand::execute() { - Bsp* map = getBsp(); - - map->delete_oob_data(clipFlags); - - refresh(); -} - -void DeleteOobDataCommand::undo() { - Bsp* map = getBsp(); - - map->replace_lumps(oldLumps); - map->load_ents(); - - refresh(); -} - -void DeleteOobDataCommand::refresh() { - BspRenderer* renderer = getBspRenderer(); - - renderer->reload(); - g_app->deselectObject(); - g_app->gui->refresh(); -} - -size_t DeleteOobDataCommand::memoryUsage() { - size_t size = sizeof(DeleteOobDataCommand); - - for (int i = 0; i < HEADER_LUMPS; i++) { - size += oldLumps.lumps[i].size(); + size += oldLumps.lumps[i].size() + newLumps.lumps[i].size(); } return size; } - -// -// Fix bad surface extents -// -FixSurfaceExtentsCommand::FixSurfaceExtentsCommand(std::string desc, int mapIdx, bool scaleNotSubdivide, - bool downscaleOnly, int maxTextureDim, LumpState oldLumps) : Command(desc, mapIdx) { - this->oldLumps = oldLumps; - this->allowedDuringLoad = false; - this->scaleNotSubdivide = scaleNotSubdivide; - this->downscaleOnly = downscaleOnly; - this->maxTextureDim = maxTextureDim; -} - -FixSurfaceExtentsCommand::~FixSurfaceExtentsCommand() +size_t EditBspCommand::memoryUsage() { - -} - -void FixSurfaceExtentsCommand::execute() { - Bsp* map = getBsp(); - - map->fix_bad_surface_extents(scaleNotSubdivide, downscaleOnly, maxTextureDim); - - refresh(); -} - -void FixSurfaceExtentsCommand::undo() { - Bsp* map = getBsp(); - - map->replace_lumps(oldLumps); - - refresh(); -} - -void FixSurfaceExtentsCommand::refresh() { - BspRenderer* renderer = getBspRenderer(); - - renderer->reload(); - g_app->deselectObject(); - g_app->gui->refresh(); -} - -size_t FixSurfaceExtentsCommand::memoryUsage() { - size_t size = sizeof(FixSurfaceExtentsCommand); - - for (int i = 0; i < HEADER_LUMPS; i++) { - size += oldLumps.lumps[i].size(); - } - - return size; -} - - -// -// Deduplicate models -// -DeduplicateModelsCommand::DeduplicateModelsCommand(std::string desc, int mapIdx, LumpState oldLumps) : Command(desc, mapIdx) { - this->oldLumps = oldLumps; - this->allowedDuringLoad = false; -} - -DeduplicateModelsCommand::~DeduplicateModelsCommand() { - -} - -void DeduplicateModelsCommand::execute() { - Bsp* map = getBsp(); - - map->deduplicate_models(); - - refresh(); -} - -void DeduplicateModelsCommand::undo() { - Bsp* map = getBsp(); - - map->replace_lumps(oldLumps); - map->load_ents(); - - refresh(); -} - -void DeduplicateModelsCommand::refresh() { - BspRenderer* renderer = getBspRenderer(); - - renderer->reload(); - g_app->deselectObject(); - g_app->gui->refresh(); -} - -size_t DeduplicateModelsCommand::memoryUsage() { - size_t size = sizeof(DeduplicateModelsCommand); - - for (int i = 0; i < HEADER_LUMPS; i++) { - size += oldLumps.lumps[i].size(); - } - - return size; -} - - -// -// Move the entire map -// -MoveMapCommand::MoveMapCommand(std::string desc, int mapIdx, vec3 offset, LumpState oldLumps) : Command(desc, mapIdx) { - this->oldLumps = oldLumps; - this->allowedDuringLoad = false; - this->offset = offset; -} - -MoveMapCommand::~MoveMapCommand() { - -} - -void MoveMapCommand::execute() { - Bsp* map = getBsp(); - - map->ents[0]->removeKeyvalue("origin"); - map->move(offset); - - refresh(); -} - -void MoveMapCommand::undo() { - Bsp* map = getBsp(); - - map->replace_lumps(oldLumps); - map->ents[0]->setOrAddKeyvalue("origin", offset.toKeyvalueString()); + size_t size = sizeof(EditBspCommand); - refresh(); -} - -void MoveMapCommand::refresh() { - BspRenderer* renderer = getBspRenderer(); - - renderer->reload(); - g_app->deselectObject(); - g_app->gui->refresh(); -} - -size_t MoveMapCommand::memoryUsage() { - size_t size = sizeof(MoveMapCommand); - - for (int i = 0; i < HEADER_LUMPS; i++) { - size += oldLumps.lumps[i].size(); - } + size += memoryused; return size; } - -// -// Optimize Map -// -OptimizeMapCommand::OptimizeMapCommand(std::string desc, int mapIdx, LumpState oldLumps) : Command(desc, mapIdx) -{ - this->oldLumps = oldLumps; - this->allowedDuringLoad = false; -} - -OptimizeMapCommand::~OptimizeMapCommand() -{ - for (int i = 0; i < HEADER_LUMPS; i++) - { - oldLumps.lumps[i].clear(); - } -} - -void OptimizeMapCommand::execute() -{ - Bsp* map = getBsp(); - BspRenderer* renderer = getBspRenderer(); - if (!map || !renderer) - return; - map->update_ent_lump(); - - print_log(get_localized_string(LANG_0297), map->bsp_name); - if (!map->has_hull2_ents()) - { - print_log(get_localized_string(LANG_0298)); - map->delete_hull(2, 1); - } - - bool oldVerbose = g_settings.verboseLogs; - g_settings.verboseLogs = true; - auto removestats = map->delete_unused_hulls(true); - - removestats.print_delete_stats(1); - g_settings.verboseLogs = oldVerbose; - - refresh(); -} - -void OptimizeMapCommand::undo() -{ - Bsp* map = getBsp(); - if (!map) - return; - - map->replace_lumps(oldLumps); - - refresh(); -} - -void OptimizeMapCommand::refresh() -{ - Bsp* map = getBsp(); - if (!map) - return; - - BspRenderer* renderer = getBspRenderer(); - - renderer->reload(); - g_app->deselectObject(); - g_app->gui->refresh(); -} - -size_t OptimizeMapCommand::memoryUsage() -{ - size_t size = sizeof(OptimizeMapCommand); - - for (int i = 0; i < HEADER_LUMPS; i++) - { - size += oldLumps.lumps[i].size(); - } - - return size; -} \ No newline at end of file diff --git a/src/editor/Command.h b/src/editor/Command.h index 0c8494e9..31f5f656 100644 --- a/src/editor/Command.h +++ b/src/editor/Command.h @@ -6,253 +6,22 @@ #include "Settings.h" #include "Renderer.h" -// Undoable actions following the Command Pattern -class Command -{ -public: - std::string desc; - int mapIdx; - bool allowedDuringLoad = false; - - Command(std::string _desc, int _mapIdx) : desc(std::move(_desc)) - { - this->mapIdx = _mapIdx; - } - - virtual void execute() = 0; - virtual void undo() = 0; - virtual size_t memoryUsage() = 0; - virtual ~Command() = default; - - BspRenderer* getBspRenderer(); - Bsp* getBsp(); -}; - - -class EditEntityCommand : public Command -{ -public: - int entIdx; - Entity oldEntData; - Entity newEntData; - - - EditEntityCommand(std::string desc, int entIdx, Entity _oldEntData, Entity _newEntData); - - ~EditEntityCommand() - { - } +// Undoable actions - void execute() override; - void undo() override; - Entity* getEnt(); - void refresh(); - size_t memoryUsage() override; -}; - - -class DeleteEntityCommand : public Command -{ -public: - int entIdx; - Entity* entData; - - DeleteEntityCommand(std::string desc, int entIdx); - ~DeleteEntityCommand(); - - void execute() override; - void undo() override; - void refresh(); - size_t memoryUsage() override; -}; - - -class CreateEntityCommand : public Command +class EditBspCommand { public: - Entity* entData; - - CreateEntityCommand(std::string desc, int mapIdx, Entity* entData); - ~CreateEntityCommand(); - - void execute() override; - void undo() override; - size_t memoryUsage() override; -}; - - - -class CreateEntityFromTextCommand : public Command { -public: - std::string textData; - size_t createdEnts; - - CreateEntityFromTextCommand(std::string desc, int mapIdx, std::string textData); - ~CreateEntityFromTextCommand(); + std::string desc; + EditBspCommand(const std::string & desc, LumpState oldLumps, LumpState newLumps, unsigned int targetLumps); + ~EditBspCommand() = default; void execute(); void undo(); - void refresh(); + size_t memoryused; size_t memoryUsage(); -}; - - -class DuplicateBspModelCommand : public Command -{ -public: - int oldModelIdx; - int newModelIdx; // TODO: could break redos if this is ever not deterministic - int entIdx; - LumpState oldLumps{}; - DuplicateBspModelCommand(std::string desc, int entIdx); - ~DuplicateBspModelCommand(); - - void execute() override; - void undo() override; - size_t memoryUsage() override; -}; - - -class CreateBspModelCommand : public Command -{ -public: - Entity* entData; - LumpState oldLumps{}; - float mdl_size; - bool empty = false; - - CreateBspModelCommand(std::string desc, int mapIdx, Entity* entData, float size, bool empty); - ~CreateBspModelCommand(); - - void execute() override; - void undo() override; - size_t memoryUsage() override; - + size_t memoryUsageZip(); + LumpState oldLumps; + LumpState newLumps; private: - int getDefaultTextureIdx(); - int addDefaultTexture(); -}; - - -class EditBspModelCommand : public Command -{ -public: - int modelIdx; - int entIdx; unsigned int targetLumps; - vec3 oldOrigin; - vec3 newOrigin; - LumpState oldLumps{}; - LumpState newLumps{}; - - EditBspModelCommand(std::string desc, int entIdx, LumpState oldLumps, LumpState newLumps, vec3 oldOrigin, unsigned int targetLumps); - ~EditBspModelCommand(); - - void execute() override; - void undo() override; - void refresh(); - size_t memoryUsage() override; -}; - - -class CleanMapCommand : public Command -{ -public: - LumpState oldLumps{}; - - CleanMapCommand(std::string desc, int mapIdx, LumpState oldLumps); - ~CleanMapCommand(); - - void execute() override; - void undo() override; - void refresh(); - size_t memoryUsage() override; -}; - - -class OptimizeMapCommand : public Command -{ -public: - LumpState oldLumps{}; - - OptimizeMapCommand(std::string desc, int mapIdx, LumpState oldLumps); - ~OptimizeMapCommand(); - - void execute() override; - void undo() override; - void refresh(); - size_t memoryUsage() override; -}; - - -class DeleteBoxedDataCommand : public Command { -public: - LumpState oldLumps = LumpState(); - vec3 mins, maxs; - - DeleteBoxedDataCommand(std::string desc, int mapIdx, vec3 mins, vec3 maxs, LumpState oldLumps); - ~DeleteBoxedDataCommand(); - - void execute(); - void undo(); - void refresh(); - size_t memoryUsage(); }; - -class DeleteOobDataCommand : public Command { -public: - LumpState oldLumps = LumpState(); - int clipFlags; - - DeleteOobDataCommand(std::string desc, int mapIdx, int clipFlags, LumpState oldLumps); - ~DeleteOobDataCommand(); - - void execute(); - void undo(); - void refresh(); - size_t memoryUsage(); -}; - -class FixSurfaceExtentsCommand : public Command { -public: - LumpState oldLumps = LumpState(); - bool scaleNotSubdivide; - bool downscaleOnly; - int maxTextureDim; - - FixSurfaceExtentsCommand(std::string desc, int mapIdx, bool scaleNotSubdivide, bool downscaleOnly, int maxTextureDim, LumpState oldLumps); - ~FixSurfaceExtentsCommand(); - - void execute(); - void undo(); - void refresh(); - size_t memoryUsage(); -}; - -class DeduplicateModelsCommand : public Command { -public: - LumpState oldLumps = LumpState(); - - DeduplicateModelsCommand(std::string desc, int mapIdx, LumpState oldLumps); - ~DeduplicateModelsCommand(); - - void execute(); - void undo(); - void refresh(); - size_t memoryUsage(); -}; - -class MoveMapCommand : public Command { -public: - LumpState oldLumps = LumpState(); - vec3 offset; - - MoveMapCommand(std::string desc, int mapIdx, vec3 offset, LumpState oldLumps); - ~MoveMapCommand(); - - void execute(); - void undo(); - void refresh(); - size_t memoryUsage(); -}; - diff --git a/src/editor/Gui.cpp b/src/editor/Gui.cpp index e8d5bcb7..88549f4f 100644 --- a/src/editor/Gui.cpp +++ b/src/editor/Gui.cpp @@ -583,13 +583,15 @@ int ImportModel(Bsp* map, const std::string& mdl_path, bool noclip) rend->refreshModel(newModelIdx); rend->preRenderEnts(); - map->getBspRender()->pushModelUndoState("IMPORT MODEL", EDIT_MODEL_LUMPS | FL_ENTITIES); + map->getBspRender()->pushUndoState("IMPORT MODEL", EDIT_MODEL_LUMPS | FL_ENTITIES); return newModelIdx; } void ExportModel(Bsp* src_map, const std::string& export_path, int model_id, int ExportType, bool movemodel) { + LumpState backupLumps = src_map->duplicate_lumps(); + Bsp* bspModel = new Bsp(); bspModel->setBspRender(src_map->getBspRender()); bspModel->bsp_valid = true; @@ -866,189 +868,7 @@ void ExportModel(Bsp* src_map, const std::string& export_path, int model_id, int delete bspModel; delete[] tmpCompressed; - - //print_log(get_localized_string(LANG_0315)); - //src_map->update_ent_lump(); - //src_map->update_lump_pointers(); - //src_map->validate(); - //src_map->write(src_map->bsp_path + ".tmp.bsp"); - - - //print_log(get_localized_string(LANG_0316)); - - //Bsp* tmpMap = new Bsp(src_map->bsp_path + ".tmp.bsp"); - - //tmpMap->force_skip_crc = true; - - //if (ExportType == 1) - //{ - // tmpMap->is_bsp29 = true; - // tmpMap->is_texture_has_pal = false; - // tmpMap->bsp_header.nVersion = 29; - //} - //else - //{ - // tmpMap->is_bsp29 = false; - // tmpMap->is_texture_has_pal = true; - // tmpMap->bsp_header.nVersion = 30; - //} - - //print_log(get_localized_string(LANG_0317)); - //removeFile(src_map->bsp_path + ".tmp.bsp"); - - //vec3 modelOrigin = tmpMap->get_model_center(model_id); - - //BSPMODEL tmpModel = src_map->models[model_id]; - - //if (tmpMap->modelCount < 1) - //{ - // print_log(get_localized_string(LANG_0318)); - // tmpMap->create_model(); - //} - - //print_log(get_localized_string(LANG_0319)); - //tmpMap->models[0] = tmpModel; - - //for (int i = 1; i < tmpMap->ents.size(); i++) - //{ - // delete tmpMap->ents[i]; - //} - //print_log(get_localized_string(LANG_0320)); - - //Entity* tmpEnt = new Entity("worldspawn"); - - //tmpEnt->setOrAddKeyvalue("compiler", g_version_string); - //tmpEnt->setOrAddKeyvalue("message", "bsp model"); - - //print_log(get_localized_string(LANG_0321)); - //tmpMap->modelCount = 1; - //tmpMap->lumps[LUMP_MODELS] = (unsigned char*)tmpMap->models; - //tmpMap->bsp_header.lump[LUMP_MODELS].nLength = sizeof(BSPMODEL); - - //tmpMap->ents.clear(); - //tmpMap->ents.push_back(tmpEnt); - - //tmpMap->update_ent_lump(); - //tmpMap->update_lump_pointers(); - - //print_log(get_localized_string(LANG_0322)); - //STRUCTCOUNT removed = tmpMap->remove_unused_model_structures(CLEAN_LIGHTMAP | CLEAN_PLANES | CLEAN_NODES | CLEAN_CLIPNODES | CLEAN_CLIPNODES_SOMETHING | CLEAN_LEAVES | CLEAN_FACES | CLEAN_SURFEDGES | CLEAN_TEXINFOS | - // CLEAN_EDGES | CLEAN_VERTICES | CLEAN_TEXTURES | CLEAN_VISDATA); - //if (!removed.allZero()) - // removed.print_delete_stats(1); - - - //int markid = 0; - //for (int i = 0; i < tmpMap->leafCount; i++) - //{ - // BSPLEAF32& tmpLeaf = tmpMap->leaves[i]; - // if (tmpLeaf.nMarkSurfaces > 0) - // { - // tmpLeaf.iFirstMarkSurface = markid; - // markid += tmpLeaf.nMarkSurfaces; - // } - //} - - //tmpMap->models[0].nVisLeafs = tmpMap->leafCount - 1; - - //int totalLeaves = 1; - //totalLeaves += tmpMap->models[0].nVisLeafs; - //if (totalLeaves > tmpMap->leafCount) - //{ - // while (totalLeaves > tmpMap->leafCount) - // tmpMap->create_leaf(CONTENTS_EMPTY); - //} - //else if (totalLeaves < tmpMap->leafCount) - //{ - // while (totalLeaves < tmpMap->leafCount) - // { - // tmpMap->models[0].nVisLeafs++; - // totalLeaves++; - // } - //} - - //if (movemodel) - //{ - // print_log(get_localized_string(LANG_0325)); - // tmpMap->move(-modelOrigin, 0, true, true); - //} - - //print_log(get_localized_string(LANG_0326)); - //tmpMap->update_lump_pointers(); - //update_unused_wad_files(src_map, tmpMap, ExportType); - - //if (model_id != 0) - //{ - // for (int i = 0; i < tmpMap->leafCount; i++) - // { - // tmpMap->leaves[i].nVisOffset = 0; - // } - // unsigned char* newVisLump = new unsigned char[g_limits.maxMapLeaves / 8]; - // memset(newVisLump, 255, g_limits.maxMapLeaves / 8); - // tmpMap->replace_lump(LUMP_VISIBILITY, newVisLump, g_limits.maxMapLeaves / 8); - // delete[] newVisLump; - //} - - //print_log(get_localized_string(LANG_0327)); - //removed = tmpMap->remove_unused_model_structures(CLEAN_LIGHTMAP | CLEAN_PLANES | CLEAN_NODES | CLEAN_CLIPNODES | CLEAN_CLIPNODES_SOMETHING | CLEAN_LEAVES | CLEAN_FACES | CLEAN_SURFEDGES | CLEAN_TEXINFOS | - // CLEAN_EDGES | CLEAN_VERTICES | CLEAN_TEXTURES | CLEAN_VISDATA | CLEAN_MARKSURFACES); - - //if (model_id == 0) - //{ - // int rowSize = (((tmpMap->leafCount - 1) + 63) & ~63) >> 3; - // unsigned char* tmpVisData = new unsigned char[rowSize]; - // unsigned char* tmpCompressed = new unsigned char[g_limits.maxMapLeaves / 8]; - - // for (int i = 1; i < tmpMap->leafCount; i++) - // { - // if (tmpMap->leaves[i].nVisOffset >= 0) - // { - // memset(tmpVisData, 0, rowSize); - - // if (tmpMap->visdata) - // DecompressVis(tmpMap->visdata + tmpMap->leaves[i].nVisOffset, tmpVisData, rowSize, tmpMap->leafCount - 1, tmpMap->visDataLength - tmpMap->leaves[i].nVisOffset); - // else - // memset(tmpVisData, 255, rowSize); - - // int size = CompressVis(tmpVisData, rowSize, tmpCompressed, g_limits.maxMapLeaves / 8); - - // tmpMap->leaves[i].nVisOffset = tmpMap->visDataLength; - - // unsigned char* newVisLump = new unsigned char[tmpMap->visDataLength + size]; - // if (tmpMap->visdata) - // memcpy(newVisLump, tmpMap->visdata, tmpMap->visDataLength); - // memcpy(newVisLump + tmpMap->visDataLength, tmpCompressed, size); - // tmpMap->replace_lump(LUMP_VISIBILITY, newVisLump, tmpMap->visDataLength + size); - // delete[] newVisLump; - // - // } - // } - - // delete[] tmpVisData; - // delete[] tmpCompressed; - //} - - //tmpMap->update_ent_lump(); - //tmpMap->update_lump_pointers(); - - //print_log("NODE FACES: {}\n", tmpMap->nodes[tmpMap->models[0].iHeadnodes[0]].nFaces); - //print_log("NODE MINS/MAXS: {} {} {} / {} {} {}\n", tmpMap->nodes[tmpMap->models[0].iHeadnodes[0]].nMins.x, - // tmpMap->nodes[tmpMap->models[0].iHeadnodes[0]].nMins.y, tmpMap->nodes[tmpMap->models[0].iHeadnodes[0]].nMins.z, - // tmpMap->nodes[tmpMap->models[0].iHeadnodes[0]].nMaxs.x, tmpMap->nodes[tmpMap->models[0].iHeadnodes[0]].nMaxs.y, - // tmpMap->nodes[tmpMap->models[0].iHeadnodes[0]].nMaxs.z); - - //if (tmpMap->validate()) - //{ - // createDir(g_working_dir); - // removeFile(g_working_dir + src_map->bsp_name + "_model" + std::to_string(model_id) + ".bsp"); - // tmpMap->write(g_working_dir + src_map->bsp_name + "_model" + std::to_string(model_id) + ".bsp"); - //} - //else - //{ - // print_log(PRINT_RED | PRINT_INTENSITY, get_localized_string(LANG_0341)); - //} - - //delete tmpMap; + src_map->replace_lumps(backupLumps); } @@ -1646,11 +1466,12 @@ void Gui::drawBspContexMenu() { if (map->ents[tmpentIdx]->isBspModel()) { - DuplicateBspModelCommand* command = new DuplicateBspModelCommand(get_localized_string("LANG_DUPLICATE_BSP"), (int)tmpentIdx); - rend->pushUndoCommand(command); app->modelUsesSharedStructures = false; + map->ents[tmpentIdx]->setOrAddKeyvalue("model", "*" + std::to_string(map->duplicate_model(map->ents[tmpentIdx]->getBspModelIdx()))); } } + map->remove_unused_model_structures(CLEAN_LEAVES); + rend->pushUndoState(get_localized_string("LANG_DUPLICATE_BSP"), EDIT_MODEL_LUMPS | FL_ENTITIES); } if (ImGui::IsItemHovered() && g.HoveredIdTimer > g_tooltip_delay) @@ -1676,9 +1497,8 @@ void Gui::drawBspContexMenu() app->modelUsesSharedStructures = false; } } - map->update_ent_lump(); - rend->pushModelUndoState(get_localized_string("LANG_DUPLICATE_BSP_STRUCT"), EDIT_MODEL_LUMPS); + rend->pushUndoState(get_localized_string("LANG_DUPLICATE_BSP_STRUCT"), EDIT_MODEL_LUMPS); pickCount++; } @@ -1769,7 +1589,7 @@ void Gui::drawBspContexMenu() rend->loadLightmaps(); rend->preRenderEnts(); - rend->pushModelUndoState("MERGE {} and {} SELECTED BSP ENTITIES", EDIT_MODEL_LUMPS | FL_ENTITIES); + rend->pushUndoState("MERGE {} and {} SELECTED BSP ENTITIES", EDIT_MODEL_LUMPS | FL_ENTITIES); if (merge_errors > 0) { @@ -3268,7 +3088,7 @@ void Gui::drawMenuBar() mapFixLightEnts(map); - rend->pushModelUndoState("Create lights", FL_ENTITIES); + rend->pushUndoState("Create lights", FL_ENTITIES); FlushConsoleLog(); vec3 mins{}, maxs{}; /*map->get_bounding_box(mins, maxs);*/ @@ -3828,7 +3648,7 @@ void Gui::drawMenuBar() char* newlump = loadFile(entFilePath, len); map->replace_lump(LUMP_ENTITIES, newlump, len); delete[] newlump; - map->load_ents(); + map->reload_ents(); g_app->updateEnts(); app->reloading = true; for (size_t i = 0; i < mapRenderers.size(); i++) @@ -4008,12 +3828,12 @@ void Gui::drawMenuBar() if (ImGui::BeginMenu(get_localized_string(LANG_0556).c_str(), (map && !map->is_mdl_model))) { - Command* undoCmd = !rend->undoHistory.empty() ? rend->undoHistory[rend->undoHistory.size() - 1] : NULL; - Command* redoCmd = !rend->redoHistory.empty() ? rend->redoHistory[rend->redoHistory.size() - 1] : NULL; + EditBspCommand* undoCmd = !rend->undoHistory.empty() ? rend->undoHistory[rend->undoHistory.size() - 1] : NULL; + EditBspCommand* redoCmd = !rend->redoHistory.empty() ? rend->redoHistory[rend->redoHistory.size() - 1] : NULL; std::string undoTitle = undoCmd ? "Undo " + undoCmd->desc : "Can't undo"; std::string redoTitle = redoCmd ? "Redo " + redoCmd->desc : "Can't redo"; - bool canUndo = undoCmd && (!app->isLoading || undoCmd->allowedDuringLoad); - bool canRedo = redoCmd && (!app->isLoading || redoCmd->allowedDuringLoad); + bool canUndo = undoCmd && (!app->isLoading); + bool canRedo = redoCmd && (!app->isLoading); bool entSelected = app->pickInfo.selectedEnts.size(); bool nonWorldspawnEntSelected = entSelected; @@ -4107,10 +3927,12 @@ void Gui::drawMenuBar() { if (map->ents[tmpentIdx]->isBspModel()) { - DuplicateBspModelCommand* command = new DuplicateBspModelCommand(get_localized_string("LANG_DUPLICATE_BSP"), (int)tmpentIdx); - rend->pushUndoCommand(command); + app->modelUsesSharedStructures = false; + map->ents[tmpentIdx]->setOrAddKeyvalue("model", "*" + std::to_string(map->duplicate_model(map->ents[tmpentIdx]->getBspModelIdx()))); } } + map->remove_unused_model_structures(CLEAN_LEAVES); + rend->pushUndoState(get_localized_string("LANG_DUPLICATE_BSP"), EDIT_MODEL_LUMPS | FL_ENTITIES); } if (ImGui::IsItemHovered() && g.HoveredIdTimer > g_tooltip_delay) @@ -4131,11 +3953,12 @@ void Gui::drawMenuBar() { if (map->ents[tmpentIdx]->isBspModel()) { - pickCount++; map->duplicate_model_structures(map->ents[tmpentIdx]->getBspModelIdx()); - rend->pushModelUndoState(get_localized_string("LANG_DUPLICATE_BSP_STRUCT"), EDIT_MODEL_LUMPS); + app->modelUsesSharedStructures = false; } } + + rend->pushUndoState(get_localized_string("LANG_DUPLICATE_BSP_STRUCT"), EDIT_MODEL_LUMPS); } if (ImGui::IsItemHovered() && g.HoveredIdTimer > g_tooltip_delay) @@ -4205,14 +4028,30 @@ void Gui::drawMenuBar() if (ImGui::MenuItem(get_localized_string(LANG_0564).c_str(), 0, false, !app->isLoading && map)) { - CleanMapCommand* command = new CleanMapCommand("Clean " + map->bsp_name, app->getSelectedMapId(), rend->undoLumpState); - rend->pushUndoCommand(command); + print_log(get_localized_string(LANG_0296), map->bsp_name); + map->remove_unused_model_structures().print_delete_stats(1); + rend->pushUndoState("Clean " + map->bsp_name, EDIT_MODEL_LUMPS); } if (ImGui::MenuItem(get_localized_string(LANG_0565).c_str(), 0, false, !app->isLoading && map)) { - OptimizeMapCommand* command = new OptimizeMapCommand("Optimize " + map->bsp_name, app->getSelectedMapId(), rend->undoLumpState); - rend->pushUndoCommand(command); + map->update_ent_lump(); + + print_log(get_localized_string(LANG_0297), map->bsp_name); + if (!map->has_hull2_ents()) + { + print_log(get_localized_string(LANG_0298)); + map->delete_hull(2, 1); + } + + bool oldVerbose = g_settings.verboseLogs; + g_settings.verboseLogs = true; + auto removestats = map->delete_unused_hulls(true); + + removestats.print_delete_stats(1); + g_settings.verboseLogs = oldVerbose; + + rend->pushUndoState("Optimize " + map->bsp_name, EDIT_MODEL_LUMPS | FL_ENTITIES); } if (ImGui::BeginMenu(get_localized_string(LANG_0566).c_str(), map)) @@ -4277,7 +4116,7 @@ void Gui::drawMenuBar() rend->preRenderFaces(); rend->preRenderEnts(); - rend->pushModelUndoState("CREATE MDL->BSP MODEL", EDIT_MODEL_LUMPS | FL_ENTITIES); + rend->pushUndoState("CREATE MDL->BSP MODEL", EDIT_MODEL_LUMPS | FL_ENTITIES); } ImGui::EndMenu(); } @@ -4417,7 +4256,7 @@ void Gui::drawMenuBar() map->is_protected = true; - rend->pushModelUndoState("PROTECT MAP FROM DECOMPILER", EDIT_MODEL_LUMPS); + rend->pushUndoState("PROTECT MAP FROM DECOMPILER", EDIT_MODEL_LUMPS); } if (ImGui::IsItemHovered() && g.HoveredIdTimer > g_tooltip_delay) { @@ -4464,12 +4303,9 @@ void Gui::drawMenuBar() print_log("Moved worldspawn origin by {} {} {}\n", ori.x, ori.y, ori.z); map->move(ori); map->ents[0]->removeKeyvalue("origin"); - } - - DeleteOobDataCommand* command = new DeleteOobDataCommand("Delete OOB Data", - app->getSelectedMapId(), clipFlags[i], rend->undoLumpState); - rend->pushUndoCommand(command); + map->delete_oob_data(clipFlags[i]); + rend->pushUndoState("Delete OOB Data", EDIT_MODEL_LUMPS | FL_ENTITIES); } IMGUI_TOOLTIP(g, "Deletes BSP data and entities outside of the " "max map boundary.\n\n" @@ -4483,9 +4319,8 @@ void Gui::drawMenuBar() print_log("Create at least 2 entities with \"cull\" as a classname first!\n"); } else { - DeleteBoxedDataCommand* command = new DeleteBoxedDataCommand("Delete Boxed Data", - app->getSelectedMapId(), g_app->cullMins, g_app->cullMaxs, rend->undoLumpState); - rend->pushUndoCommand(command); + map->delete_box_data(g_app->cullMins, g_app->cullMaxs); + rend->pushUndoState("Delete Boxed Data", EDIT_MODEL_LUMPS | FL_ENTITIES); } } @@ -4494,55 +4329,51 @@ void Gui::drawMenuBar() "engine with stricter map limits.\n\n" "Create 2 cull entities from the \"Create\" menu to define the culling box. " "A transparent red box will form between them."); - if (ImGui::MenuItem("Deduplicate Models", 0, false, rend && !app->isLoading && app->getSelectedMap())) { - DeduplicateModelsCommand* command = new DeduplicateModelsCommand("Deduplicate models", - app->getSelectedMapId(), rend->undoLumpState); - rend->pushUndoCommand(command); + if (ImGui::MenuItem("Deduplicate Models", 0, false, rend && !app->isLoading && app->getSelectedMap())) + { + map->deduplicate_models(); + rend->pushUndoState("Deduplicate Models", EDIT_MODEL_LUMPS | FL_ENTITIES); } IMGUI_TOOLTIP(g, "Scans for duplicated BSP models and updates entity model keys to reference only one model in set of duplicated models. " "This lowers the model count and allows more game models to be precached.\n\n" "This does not delete BSP data structures unless you run the Clean command afterward."); if (ImGui::MenuItem("Downscale Invalid Textures", "(WIP)", false, rend && !app->isLoading && app->getSelectedMap())) { map->downscale_invalid_textures(); - - rend->preRenderFaces(); - g_app->gui->refresh(); - reloadLimits(); + rend->pushUndoState("Downscale Invalid Textures", FL_TEXINFO | FL_TEXTURES); } IMGUI_TOOLTIP(g, "Shrinks textures that exceed the max texture size and adjusts texture coordinates accordingly. Does not work with WAD textures yet.\n"); if (ImGui::BeginMenu("Fix Bad Surface Extents", !app->isLoading && app->getSelectedMap())) { - if (ImGui::MenuItem("Shrink Textures (512)", 0, false, !app->isLoading && app->getSelectedMap())) { - FixSurfaceExtentsCommand* command = new FixSurfaceExtentsCommand("Shrink textures (512)", - app->getSelectedMapId(), false, true, 512, rend->undoLumpState); - rend->pushUndoCommand(command); + if (ImGui::MenuItem("Shrink Textures (512)", 0, false, !app->isLoading && app->getSelectedMap())) + { + map->fix_bad_surface_extents(false, true, 512); + rend->pushUndoState("Shrink Textures (512)", FL_TEXINFO | FL_TEXTURES | FL_FACES); } IMGUI_TOOLTIP(g, "Downscales embedded textures on bad faces to a max resolution of 512x512 pixels. " "This alone will likely not be enough to fix all faces with bad surface extents." "You may also have to apply the Subdivide or Scale methods."); - if (ImGui::MenuItem("Shrink Textures (256)", 0, false, !app->isLoading && app->getSelectedMap())) { - FixSurfaceExtentsCommand* command = new FixSurfaceExtentsCommand("Shrink textures (256)", - app->getSelectedMapId(), false, true, 256, rend->undoLumpState); - rend->pushUndoCommand(command); + if (ImGui::MenuItem("Shrink Textures (256)", 0, false, !app->isLoading && app->getSelectedMap())) + { + map->fix_bad_surface_extents(false, true, 256); + rend->pushUndoState("Shrink Textures (256)", FL_TEXINFO | FL_TEXTURES | FL_FACES); } IMGUI_TOOLTIP(g, "Downscales embedded textures on bad faces to a max resolution of 256x256 pixels. " "This alone will likely not be enough to fix all faces with bad surface extents." "You may also have to apply the Subdivide or Scale methods."); if (ImGui::MenuItem("Shrink Textures (128)", 0, false, !app->isLoading && app->getSelectedMap())) { - FixSurfaceExtentsCommand* command = new FixSurfaceExtentsCommand("Shrink textures (128)", - app->getSelectedMapId(), false, true, 128, rend->undoLumpState); - rend->pushUndoCommand(command); + map->fix_bad_surface_extents(false, true, 128); + rend->pushUndoState("Shrink Textures (128)", FL_TEXINFO | FL_TEXTURES | FL_FACES); } IMGUI_TOOLTIP(g, "Downscales embedded textures on bad faces to a max resolution of 128x128 pixels. " "This alone will likely not be enough to fix all faces with bad surface extents." "You may also have to apply the Subdivide or Scale methods."); - if (ImGui::MenuItem("Shrink Textures (64)", 0, false, !app->isLoading && app->getSelectedMap())) { - FixSurfaceExtentsCommand* command = new FixSurfaceExtentsCommand("Shrink textures (64)", - app->getSelectedMapId(), false, true, 64, rend->undoLumpState); - rend->pushUndoCommand(command); + if (ImGui::MenuItem("Shrink Textures (64)", 0, false, !app->isLoading && app->getSelectedMap())) + { + map->fix_bad_surface_extents(false, true, 512); + rend->pushUndoState("Shrink Textures (64)", FL_TEXINFO | FL_TEXTURES | FL_FACES); } IMGUI_TOOLTIP(g, "Downscales embedded textures to a max resolution of 64x64 pixels. " "This alone will likely not be enough to fix all faces with bad surface extents." @@ -4550,17 +4381,16 @@ void Gui::drawMenuBar() ImGui::Separator(); - if (ImGui::MenuItem("Scale", 0, false, !app->isLoading && app->getSelectedMap())) { - FixSurfaceExtentsCommand* command = new FixSurfaceExtentsCommand("Scale faces", - app->getSelectedMapId(), true, false, 0, rend->undoLumpState); - rend->pushUndoCommand(command); + if (ImGui::MenuItem("Scale", 0, false, !app->isLoading && app->getSelectedMap())) + { + map->fix_bad_surface_extents(true, false, 0); + rend->pushUndoState("Scale Textures", FL_TEXINFO | FL_TEXTURES | FL_FACES); } IMGUI_TOOLTIP(g, "Scales up face textures until they have valid extents. The drawback to this method is shifted texture coordinates and lower apparent texture quality."); if (ImGui::MenuItem("Subdivide", 0, false, !app->isLoading && app->getSelectedMap())) { - FixSurfaceExtentsCommand* command = new FixSurfaceExtentsCommand("Subdivide faces", - app->getSelectedMapId(), false, false, 0, rend->undoLumpState); - rend->pushUndoCommand(command); + map->fix_bad_surface_extents(false, false, 0); + rend->pushUndoState("Subdivide Textures", FL_TEXINFO | FL_TEXTURES | FL_FACES); } IMGUI_TOOLTIP(g, "Subdivides faces until they have valid extents. The drawback to this method is reduced in-game performace from higher poly counts."); @@ -4577,10 +4407,8 @@ void Gui::drawMenuBar() origin = app->snapToGrid(origin); newEnt->addKeyvalue("origin", origin.toKeyvalueString()); newEnt->addKeyvalue("classname", "cull"); - - CreateEntityCommand* createCommand = new CreateEntityCommand("Create Entity", app->getSelectedMapId(), newEnt); - delete newEnt; - rend->pushUndoCommand(createCommand); + map->ents.push_back(newEnt); + rend->pushUndoState("Cull Entity", FL_ENTITIES); } IMGUI_TOOLTIP(g, "Create a point entity for use with the culling tool. 2 of these define the bounding box for structure culling operations.\n"); @@ -5072,7 +4900,7 @@ void Gui::drawMenuBar() map->update_ent_lump(); map->update_lump_pointers(); - rend->pushModelUndoState(fmt::format("MAP SCALE TO {:2}", scale_val), EDIT_MODEL_LUMPS | FL_ENTITIES); + rend->pushUndoState(fmt::format("MAP SCALE TO {:2}", scale_val), EDIT_MODEL_LUMPS | FL_ENTITIES); } } ImGui::EndMenu(); @@ -5085,7 +4913,6 @@ void Gui::drawMenuBar() { map->remove_faces_by_content(CONTENTS_SKY); - map->save_undo_lightmaps(); map->resize_all_lightmaps(); rend->loadLightmaps(); rend->preRenderFaces(); @@ -5093,7 +4920,7 @@ void Gui::drawMenuBar() map->update_ent_lump(); map->update_lump_pointers(); - rend->pushModelUndoState("REMOVE FACES FROM SKY", EDIT_MODEL_LUMPS); + rend->pushUndoState("REMOVE FACES FROM SKY", EDIT_MODEL_LUMPS); } if (ImGui::IsItemHovered() && g.HoveredIdTimer > g_tooltip_delay) { @@ -5107,14 +4934,13 @@ void Gui::drawMenuBar() map->remove_faces_by_content(CONTENTS_SOLID); - map->save_undo_lightmaps(); map->resize_all_lightmaps(); rend->loadLightmaps(); rend->preRenderFaces(); map->update_ent_lump(); map->update_lump_pointers(); - rend->pushModelUndoState("REMOVE FACES FROM SOLID", EDIT_MODEL_LUMPS); + rend->pushUndoState("REMOVE FACES FROM SOLID", EDIT_MODEL_LUMPS); } if (ImGui::IsItemHovered() && g.HoveredIdTimer > g_tooltip_delay) { @@ -5128,7 +4954,6 @@ void Gui::drawMenuBar() { map->cull_leaf_faces(rend->curLeafIdx); - map->save_undo_lightmaps(); map->resize_all_lightmaps(); rend->loadLightmaps(); @@ -5137,7 +4962,7 @@ void Gui::drawMenuBar() map->update_ent_lump(); map->update_lump_pointers(); - rend->pushModelUndoState(fmt::format("REMOVE FACES FROM {} LEAF", rend->curLeafIdx), EDIT_MODEL_LUMPS); + rend->pushUndoState(fmt::format("REMOVE FACES FROM {} LEAF", rend->curLeafIdx), EDIT_MODEL_LUMPS); } if (ImGui::IsItemHovered() && g.HoveredIdTimer > g_tooltip_delay) { @@ -5504,10 +5329,8 @@ void Gui::drawMenuBar() newEnt->addKeyvalue("origin", origin.toKeyvalueString()); newEnt->addKeyvalue("classname", "info_player_deathmatch"); - CreateEntityCommand* createCommand = new CreateEntityCommand("Create Entity", app->getSelectedMapId(), newEnt); - rend->pushUndoCommand(createCommand); - - delete newEnt; + map->ents.push_back(newEnt); + rend->pushUndoState("Create Entity", FL_ENTITIES); } if (ImGui::MenuItem(get_localized_string(LANG_0589).c_str(), 0, false, !app->isLoading && map)) @@ -5520,15 +5343,20 @@ void Gui::drawMenuBar() newEnt->addKeyvalue("origin", origin.toKeyvalueString()); newEnt->addKeyvalue("classname", "func_illusionary"); - float snapSize = app->snapSize; - if (snapSize < 16) + float mdl_size = 64.0f; + + int aaatriggerIdx = map->GetTriggerTexture(); + unsigned int dupLumps = FL_MARKSURFACES | FL_EDGES | FL_FACES | FL_NODES | FL_PLANES | FL_CLIPNODES | FL_SURFEDGES | FL_TEXINFO | FL_VERTICES | FL_LIGHTING | FL_MODELS | FL_LEAVES | FL_ENTITIES; + if (aaatriggerIdx == -1) { - snapSize = 16; + dupLumps |= FL_TEXTURES; + aaatriggerIdx = map->AddTriggerTexture(); } - CreateBspModelCommand* command = new CreateBspModelCommand("Create Model", app->getSelectedMapId(), newEnt, snapSize, true); - rend->pushUndoCommand(command); - delete newEnt; + vec3 mins = vec3(-mdl_size, -mdl_size, -mdl_size); + vec3 maxs = vec3(mdl_size, mdl_size, mdl_size); + int modelIdx = map->create_solid(mins, maxs, aaatriggerIdx, true); + newEnt->addKeyvalue("model", "*" + std::to_string(modelIdx)); if (map->ents.size()) { @@ -5543,6 +5371,10 @@ void Gui::drawMenuBar() } } map->resize_all_lightmaps(); + + + map->ents.push_back(newEnt); + rend->pushUndoState(get_localized_string(LANG_0589), dupLumps); } if (ImGui::MenuItem(get_localized_string(LANG_0591).c_str(), 0, false, !app->isLoading && map)) @@ -5555,31 +5387,30 @@ void Gui::drawMenuBar() newEnt->addKeyvalue("origin", origin.toKeyvalueString()); newEnt->addKeyvalue("classname", "func_wall"); - float snapSize = app->snapSize; - if (snapSize < 16) + float mdl_size = 64.0f; + + int aaatriggerIdx = map->GetTriggerTexture(); + unsigned int dupLumps = FL_MARKSURFACES | FL_EDGES | FL_FACES | FL_NODES | FL_PLANES | FL_CLIPNODES | FL_SURFEDGES | FL_TEXINFO | FL_VERTICES | FL_LIGHTING | FL_MODELS | FL_LEAVES; + if (aaatriggerIdx == -1) { - snapSize = 16; + dupLumps |= FL_TEXTURES; + aaatriggerIdx = map->AddTriggerTexture(); } - CreateBspModelCommand* command = new CreateBspModelCommand("Create Model", app->getSelectedMapId(), newEnt, snapSize, false); - rend->pushUndoCommand(command); - map->save_undo_lightmaps(); + vec3 mins = vec3(-mdl_size, -mdl_size, -mdl_size); + vec3 maxs = vec3(mdl_size, mdl_size, mdl_size); + int modelIdx = map->create_solid(mins, maxs, aaatriggerIdx, false); + newEnt->addKeyvalue("model", "*" + std::to_string(modelIdx)); - delete newEnt; - - if (map->ents.size()) + BSPMODEL& model = map->models[modelIdx]; + for (int i = 0; i < model.nFaces; i++) { - newEnt = map->ents[map->ents.size() - 1]; - if (newEnt && newEnt->getBspModelIdx() >= 0) - { - BSPMODEL& model = map->models[newEnt->getBspModelIdx()]; - for (int i = 0; i < model.nFaces; i++) - { - map->faces[model.iFirstFace + i].nStyles[0] = 0; - } - } + map->faces[model.iFirstFace + i].nStyles[0] = 0; } + map->resize_all_lightmaps(); + map->ents.push_back(newEnt); + rend->pushUndoState(get_localized_string(LANG_0589), dupLumps); } if (ImGui::MenuItem(get_localized_string(LANG_0590).c_str(), 0, false, !app->isLoading && map)) @@ -5592,27 +5423,31 @@ void Gui::drawMenuBar() newEnt->addKeyvalue("origin", origin.toKeyvalueString()); newEnt->addKeyvalue("classname", "trigger_once"); - float snapSize = app->snapSize; - if (snapSize < 16) + float mdl_size = 64.0f; + + int aaatriggerIdx = map->GetTriggerTexture(); + unsigned int dupLumps = FL_MARKSURFACES | FL_EDGES | FL_FACES | FL_NODES | FL_PLANES | FL_CLIPNODES | FL_SURFEDGES | FL_TEXINFO | FL_VERTICES | FL_LIGHTING | FL_MODELS | FL_LEAVES | FL_ENTITIES; + if (aaatriggerIdx == -1) { - snapSize = 16; + dupLumps |= FL_TEXTURES; + aaatriggerIdx = map->AddTriggerTexture(); } - CreateBspModelCommand* command = new CreateBspModelCommand("Create Model", app->getSelectedMapId(), newEnt, snapSize, false); - rend->pushUndoCommand(command); + vec3 mins = vec3(-mdl_size, -mdl_size, -mdl_size); + vec3 maxs = vec3(mdl_size, mdl_size, mdl_size); + int modelIdx = map->create_solid(mins, maxs, aaatriggerIdx, true); + newEnt->addKeyvalue("model", "*" + std::to_string(modelIdx)); - delete newEnt; - if (map->ents.size()) - { - newEnt = map->ents[map->ents.size() - 1]; - if (newEnt && newEnt->getBspModelIdx() >= 0) - { - BSPMODEL& model = map->models[newEnt->getBspModelIdx()]; - model.iFirstFace = 0; - model.nFaces = 0; - } - } + BSPMODEL& model = map->models[modelIdx]; + model.iFirstFace = 0; + model.nFaces = 0; map->remove_unused_model_structures(CLEAN_FACES | CLEAN_MARKSURFACES); + map->ents.push_back(newEnt); + + + map->resize_all_lightmaps(); + map->ents.push_back(newEnt); + rend->pushUndoState(get_localized_string(LANG_0590), dupLumps); } if (ImGui::MenuItem("BSP Clip model", 0, false, !app->isLoading && map)) @@ -5625,27 +5460,31 @@ void Gui::drawMenuBar() newEnt->addKeyvalue("origin", origin.toKeyvalueString()); newEnt->addKeyvalue("classname", "func_wall"); - float snapSize = app->snapSize; - if (snapSize < 16) + float mdl_size = 64.0f; + + int aaatriggerIdx = map->GetTriggerTexture(); + unsigned int dupLumps = FL_MARKSURFACES | FL_EDGES | FL_FACES | FL_NODES | FL_PLANES | FL_CLIPNODES | FL_SURFEDGES | FL_TEXINFO | FL_VERTICES | FL_LIGHTING | FL_MODELS | FL_LEAVES | FL_ENTITIES; + if (aaatriggerIdx == -1) { - snapSize = 16; + dupLumps |= FL_TEXTURES; + aaatriggerIdx = map->AddTriggerTexture(); } - CreateBspModelCommand* command = new CreateBspModelCommand("Create Model", app->getSelectedMapId(), newEnt, snapSize, false); - rend->pushUndoCommand(command); + vec3 mins = vec3(-mdl_size, -mdl_size, -mdl_size); + vec3 maxs = vec3(mdl_size, mdl_size, mdl_size); + int modelIdx = map->create_solid(mins, maxs, aaatriggerIdx, true); + newEnt->addKeyvalue("model", "*" + std::to_string(modelIdx)); - delete newEnt; - if (map->ents.size()) - { - newEnt = map->ents[map->ents.size() - 1]; - if (newEnt && newEnt->getBspModelIdx() >= 0) - { - BSPMODEL& model = map->models[newEnt->getBspModelIdx()]; - model.iFirstFace = 0; - model.nFaces = 0; - } - } + BSPMODEL& model = map->models[modelIdx]; + model.iFirstFace = 0; + model.nFaces = 0; map->remove_unused_model_structures(CLEAN_FACES | CLEAN_MARKSURFACES); + map->ents.push_back(newEnt); + + + map->resize_all_lightmaps(); + map->ents.push_back(newEnt); + rend->pushUndoState("BSP Clip model", dupLumps); } if (DebugKeyPressed) @@ -6082,7 +5921,7 @@ void Gui::drawMenuBar() rend->reuploadTextures(); rend->preRenderFaces(); - rend->pushModelUndoState("CREATE SKYBOX", EDIT_MODEL_LUMPS | FL_ENTITIES); + rend->pushUndoState("CREATE SKYBOX", EDIT_MODEL_LUMPS | FL_ENTITIES); } @@ -6708,7 +6547,8 @@ void Gui::drawDebugWidget() ImGui::Text(get_localized_string(LANG_0638).c_str(), app->debugVec3.x, app->debugVec3.y, app->debugVec3.z); float mb = renderer->undoMemoryUsage / (1024.0f * 1024.0f); - ImGui::Text(get_localized_string(LANG_0639).c_str(), mb); + float mb_zip = renderer->undoMemoryUsageZip / (1024.0f * 1024.0f); + ImGui::Text(get_localized_string("UNDO_MEM_USAGE").c_str(), mb, mb_zip); ImGui::Text(fmt::format(fmt::runtime(get_localized_string(LANG_0388)), app->isTransformableSolid).c_str()); ImGui::Text(fmt::format(fmt::runtime(get_localized_string(LANG_0389)), app->isScalingObject).c_str()); @@ -7264,10 +7104,10 @@ void Gui::drawKeyvalueEditor() { ent->setOrAddKeyvalue("classname", group.classes[k]->name); map->getBspRender()->refreshEnt((int)entIdx[0]); - map->getBspRender()->pushEntityUndoStateDelay("Change Class", (int)entIdx[0], ent); } } + map->getBspRender()->pushEntityUndoStateDelay("Change Class"); ImGui::EndMenu(); } @@ -7504,9 +7344,9 @@ void Gui::drawKeyvalueEditor_SmartEditTab(int entIdx) map->getBspRender()->refreshEnt(selected_entId); } } - - map->getBspRender()->pushEntityUndoStateDelay("Edit Keyvalue", (int)selected_entId, selected_ent); } + + map->getBspRender()->pushEntityUndoStateDelay("Edit Keyvalue"); } } pickCount++; @@ -7637,8 +7477,8 @@ void Gui::drawKeyvalueEditor_SmartEditTab(int entIdx) map2->getBspRender()->refreshModel(ent->getBspModelIdx()); } } - map2->getBspRender()->pushEntityUndoStateDelay("Edit Keyvalue", (int)selected_entId, ent); } + map2->getBspRender()->pushEntityUndoStateDelay("Edit Keyvalue"); } } } @@ -7735,9 +7575,9 @@ void Gui::drawKeyvalueEditor_FlagsTab(int entIdx) selected_ent->setOrAddKeyvalue("spawnflags", std::to_string(spawnflags)); else selected_ent->removeKeyvalue("spawnflags"); - - map->getBspRender()->pushEntityUndoStateDelay(checkboxEnabled[i] ? "Enable Flag" : "Disable Flag", (int)selected_entId, selected_ent); } + + map->getBspRender()->pushEntityUndoStateDelay(checkboxEnabled[i] ? "Enable Flag" : "Disable Flag"); } if ((!name.empty() || !description.empty()) && ImGui::IsItemHovered()) { @@ -7781,6 +7621,7 @@ struct TextChangeCallback std::string key = map->ents[g_app->pickInfo.selectedEnts[0]]->keyOrder[inputData->idx]; if (key != data->Buf) { + bool reloadModels = false; for (auto entId : g_app->pickInfo.selectedEnts) { Entity* selent = map->ents[entId]; @@ -7789,13 +7630,17 @@ struct TextChangeCallback render->refreshEnt((int)entId); if (key == "model" || std::string(data->Buf) == "model") { - g_app->reloadBspModels(); + reloadModels = true; } - - g_app->updateEntConnections(); - map->getBspRender()->pushEntityUndoStateDelay("Rename Keyvalue", (int)entId, selent); } } + if (reloadModels) + { + g_app->reloadBspModels(); + } + g_app->updateEntConnections(); + map->getBspRender()->pushEntityUndoStateDelay("Rename Keyvalue"); + } } } @@ -7896,28 +7741,25 @@ struct TextChangeCallback selent->setOrAddKeyvalue(key, data->Buf); } render->refreshEnt((int)entId); - pickCount++; - vertPickCount++; - g_app->updateEntConnections(); if (needrefreshmodel) { if (selent->getBspModelIdx() > 0) { map2->getBspRender()->refreshModel(selent->getBspModelIdx()); - g_app->updateEntConnections(); } } - map2->getBspRender()->pushEntityUndoStateDelay("Edit Keyvalue RAW", (int)entId, selent); } } if (needreloadmodels) { - pickCount++; - vertPickCount++; - g_app->updateEntConnections(); g_app->reloadBspModels(); } + + pickCount++; + vertPickCount++; + g_app->updateEntConnections(); + map2->getBspRender()->pushEntityUndoStateDelay("Edit Keyvalue RAW"); } } } @@ -8131,7 +7973,7 @@ void Gui::drawKeyvalueEditor_RawEditTab(int entIdx) ent->removeKeyvalue(keyOrdname); map->getBspRender()->refreshEnt(entIdx); app->updateEntConnections(); - map->getBspRender()->pushEntityUndoStateDelay("Delete Keyvalue RAW", entIdx, ent); + map->getBspRender()->pushEntityUndoStateDelay("Delete Keyvalue RAW"); } ImGui::PopStyleColor(3); ImGui::NextColumn(); @@ -8141,7 +7983,7 @@ void Gui::drawKeyvalueEditor_RawEditTab(int entIdx) if (!keyDragging && wasKeyDragging) { map->getBspRender()->refreshEnt(entIdx); - map->getBspRender()->pushEntityUndoStateDelay("Move Keyvalue", entIdx, ent); + map->getBspRender()->pushEntityUndoStateDelay("Move Keyvalue"); } wasKeyDragging = keyDragging; @@ -8164,7 +8006,7 @@ void Gui::drawKeyvalueEditor_RawEditTab(int entIdx) map->getBspRender()->refreshEnt(entIdx); app->updateEntConnections(); keyName.clear(); - map->getBspRender()->pushEntityUndoStateDelay("Add Keyvalue", entIdx, ent); + map->getBspRender()->pushEntityUndoStateDelay("Add Keyvalue"); } } ImGui::SameLine(); @@ -10350,6 +10192,12 @@ void Gui::drawLimits() oldMap = map; } + if (!map) + return; + + BspRenderer* rend = map->getBspRender(); + if (!rend) + return; if (ImGui::Begin(fmt::format("{}###LIMITS_WIDGET", title).c_str(), &showLimitsWidget)) { @@ -10460,6 +10308,10 @@ void Gui::drawLimits() } } + ImGui::SeparatorText((get_localized_string(LANG_0721) + " " + std::to_string(rend->undoHistory.size())).c_str()); + float mb = rend->undoMemoryUsage / (1024.0f * 1024.0f); + float mb_zip = rend->undoMemoryUsageZip / (1024.0f * 1024.0f); + ImGui::Text(get_localized_string("UNDO_MEM_USAGE").c_str(), mb, mb_zip); ImGui::End(); } @@ -11769,7 +11621,7 @@ void Gui::drawLightMapTool() memcpy(map->lightdata + offset, currentlightMap[i]->get_data(), lightmapSz); } map->resize_all_lightmaps(true); - renderer->pushModelUndoState(get_localized_string(LANG_0599), FL_LIGHTING); + renderer->pushUndoState(get_localized_string(LANG_0599), FL_LIGHTING); } ImGui::SameLine(); @@ -12468,7 +12320,7 @@ void Gui::drawFaceEditorWidget() mergeFaceVec = updatedFaceVec = scaledX = scaledY = shiftedX = shiftedY = textureChanged = toggledFlags = updatedTexVec = stylesChanged = false; - map->getBspRender()->pushModelUndoState(targetEditName, targetLumps); + map->getBspRender()->pushUndoState(targetEditName, targetLumps); } pasteTextureNow = false; @@ -12636,7 +12488,7 @@ void Gui::drawFaceEditorWidget() map->update_ent_lump(); map->update_lump_pointers(); - mapRenderer->pushModelUndoState("DELETE FACES", EDIT_MODEL_LUMPS); + mapRenderer->pushUndoState("DELETE FACES", EDIT_MODEL_LUMPS); } if (ImGui::IsItemHovered()) { @@ -12662,7 +12514,7 @@ void Gui::drawFaceEditorWidget() map->update_ent_lump(); map->update_lump_pointers(); - mapRenderer->pushModelUndoState("REMOVE FACES FROM PVS", EDIT_MODEL_LUMPS); + mapRenderer->pushUndoState("REMOVE FACES FROM PVS", EDIT_MODEL_LUMPS); } if (ImGui::IsItemHovered()) { @@ -12685,7 +12537,7 @@ void Gui::drawFaceEditorWidget() map->update_lump_pointers(); - mapRenderer->pushModelUndoState("MAKE FACES VISIBLE IN ALL LEAFS", FL_LEAVES | FL_MARKSURFACES); + mapRenderer->pushUndoState("MAKE FACES VISIBLE IN ALL LEAFS", FL_LEAVES | FL_MARKSURFACES); } if (ImGui::IsItemHovered()) { @@ -12709,7 +12561,7 @@ void Gui::drawFaceEditorWidget() map->update_ent_lump(); map->update_lump_pointers(); - mapRenderer->pushModelUndoState("MAKE FACES INVISIBLE FOR CURRENT LEAF", FL_LEAVES | FL_MARKSURFACES); + mapRenderer->pushUndoState("MAKE FACES INVISIBLE FOR CURRENT LEAF", FL_LEAVES | FL_MARKSURFACES); } if (ImGui::IsItemHovered()) { @@ -12733,7 +12585,7 @@ void Gui::drawFaceEditorWidget() map->update_ent_lump(); map->update_lump_pointers(); - mapRenderer->pushModelUndoState("MAKE FACES VISIBLE FOR CURRENT LEAF", FL_LEAVES | FL_MARKSURFACES); + mapRenderer->pushUndoState("MAKE FACES VISIBLE FOR CURRENT LEAF", FL_LEAVES | FL_MARKSURFACES); } if (ImGui::IsItemHovered()) { @@ -12827,7 +12679,7 @@ void Gui::drawFaceEditorWidget() g_app->pointEntRenderer->genCubeBuffers(mapRenderer->nodePlaneCube);*/ updatedLeafVec = false; - mapRenderer->pushModelUndoState("UPDATE MODEL PLANE MINS/MAXS", FL_NODES); + mapRenderer->pushUndoState("UPDATE MODEL PLANE MINS/MAXS", FL_NODES); } ImGui::PopItemWidth(); } @@ -13147,7 +12999,7 @@ void Gui::drawFaceEditorWidget() if (!removed.allZero()) removed.print_delete_stats(1); - mapRenderer->pushModelUndoState("UPDATE LEAF VISIBILITY", FL_VISIBILITY); + mapRenderer->pushUndoState("UPDATE LEAF VISIBILITY", FL_VISIBILITY); } ImGui::PopStyleColor(); @@ -13192,7 +13044,7 @@ void Gui::drawFaceEditorWidget() removed.print_delete_stats(1); - mapRenderer->pushModelUndoState("UPDATE LEAF VISIBILITY", FL_VISIBILITY); + mapRenderer->pushUndoState("UPDATE LEAF VISIBILITY", FL_VISIBILITY); } ImGui::PopStyleColor(); @@ -13266,7 +13118,7 @@ void Gui::drawFaceEditorWidget() g_app->pointEntRenderer->genCubeBuffers(mapRenderer->leafCube); updatedLeafVec = false; - mapRenderer->pushModelUndoState("EDIT LEAF", FL_LEAVES); + mapRenderer->pushUndoState("EDIT LEAF", FL_LEAVES); } std::vector leafNodes{}; @@ -13343,7 +13195,7 @@ void Gui::drawFaceEditorWidget() g_app->pointEntRenderer->genCubeBuffers(mapRenderer->nodeCube); updatedLeafVec = false; - mapRenderer->pushModelUndoState("UPDATE LEAF NODE MINS/MAXS", FL_NODES); + mapRenderer->pushUndoState("UPDATE LEAF NODE MINS/MAXS", FL_NODES); } } @@ -13400,7 +13252,7 @@ void Gui::drawFaceEditorWidget() g_app->pointEntRenderer->genCubeBuffers(mapRenderer->nodePlaneCube);*/ updatedLeafVec = false; - mapRenderer->pushModelUndoState("UPDATE LEAF NODE MINS/MAXS", FL_NODES); + mapRenderer->pushUndoState("UPDATE LEAF NODE MINS/MAXS", FL_NODES); } ImGui::PopItemWidth(); } @@ -13410,7 +13262,7 @@ void Gui::drawFaceEditorWidget() last_leaf = map->clone_world_leaf(last_leaf); BSPLEAF32& leaf = map->leaves[last_leaf]; app->goToCoords(getCenter(leaf.nMins, leaf.nMaxs)); - mapRenderer->pushModelUndoState("DUPLICATE LEAF", FL_LEAVES | FL_NODES | FL_PLANES | FL_MARKSURFACES | FL_VISIBILITY); + mapRenderer->pushUndoState("DUPLICATE LEAF", FL_LEAVES | FL_NODES | FL_PLANES | FL_MARKSURFACES | FL_VISIBILITY); } if (ImGui::IsItemHovered()) @@ -13454,7 +13306,7 @@ void Gui::drawFaceEditorWidget() if (!removed.allZero()) removed.print_delete_stats(1); - mapRenderer->pushModelUndoState("UPDATE VIS LUMP", FL_LEAVES | FL_MARKSURFACES); + mapRenderer->pushUndoState("UPDATE VIS LUMP", FL_LEAVES | FL_MARKSURFACES); } } } diff --git a/src/editor/Renderer.cpp b/src/editor/Renderer.cpp index 5b376d26..01a0b215 100644 --- a/src/editor/Renderer.cpp +++ b/src/editor/Renderer.cpp @@ -2801,18 +2801,15 @@ bool Renderer::transformAxisControls() map->getBspRender()->refreshModel(modelIdx); map->getBspRender()->refreshModelClipnodes(modelIdx); applyTransform(map, true); - map->getBspRender()->pushModelUndoState("Move verts", EDIT_MODEL_LUMPS); + map->getBspRender()->pushUndoState("Move verts", EDIT_MODEL_LUMPS); } } else if (transformTarget == TRANSFORM_OBJECT) { if (moveOrigin || modelIdx < 0) { - for (int tmpentIdx : pickInfo.selectedEnts) - { - saveTranformResult = false; - map->getBspRender()->pushEntityUndoState("Move Entity", (int)tmpentIdx); - } + saveTranformResult = false; + map->getBspRender()->pushEntityUndoStateDelay("Move Entity"); } else { @@ -2832,7 +2829,7 @@ bool Renderer::transformAxisControls() map->getBspRender()->refreshModel(modelIdx); map->getBspRender()->refreshModelClipnodes(modelIdx); updateEntConnectionPositions(); - map->getBspRender()->pushModelUndoState("Move Model", EDIT_MODEL_LUMPS | FL_ENTITIES); + map->getBspRender()->pushUndoState("Move Model", EDIT_MODEL_LUMPS | FL_ENTITIES); } } else if (transformTarget == TRANSFORM_ORIGIN) @@ -2861,7 +2858,7 @@ bool Renderer::transformAxisControls() if (updateModels) { map->resize_all_lightmaps(); - map->getBspRender()->pushModelUndoState("Move model [vOrigin]", EDIT_MODEL_LUMPS | FL_ENTITIES); + map->getBspRender()->pushUndoState("Move model [vOrigin]", EDIT_MODEL_LUMPS | FL_ENTITIES); } } } @@ -2881,7 +2878,7 @@ bool Renderer::transformAxisControls() map->resize_all_lightmaps(); map->getBspRender()->refreshModel(modelIdx); map->getBspRender()->refreshModelClipnodes(modelIdx); - map->getBspRender()->pushModelUndoState("Scale Model", EDIT_MODEL_LUMPS); + map->getBspRender()->pushUndoState("Scale Model", EDIT_MODEL_LUMPS); } } } @@ -4545,7 +4542,7 @@ bool Renderer::splitModelFace() mapRenderer->refreshModel(modelIdx); pickCount++; vertPickCount++; - map->getBspRender()->pushModelUndoState("Split Face", EDIT_MODEL_LUMPS); + map->getBspRender()->pushUndoState("Split Face", EDIT_MODEL_LUMPS); return true; } @@ -4634,6 +4631,7 @@ void Renderer::cutEnt() Bsp* map = SelectedMap; if (!map) return; + BspRenderer * rend = map->getBspRender(); std::ostringstream ss; @@ -4648,11 +4646,15 @@ void Renderer::cutEnt() removeFile(g_working_dir + "copyModel" + std::to_string(map->ents[ents[i]]->getBspModelIdx()) + ".bsp"); ExportModel(map, g_working_dir + "copyModel" + std::to_string(map->ents[ents[i]]->getBspModelIdx()) + ".bsp", map->ents[ents[i]]->getBspModelIdx(), 2, true); } + } - DeleteEntityCommand* deleteCommand = new DeleteEntityCommand("Cut Entity", ents[i]); - map->getBspRender()->pushUndoCommand(deleteCommand); + for (int i = (int)ents.size() - 1; i >= 0; i--) + { + delete map->ents[i]; + map->ents.erase(map->ents.begin() + i); } + rend->pushUndoState("Cut Entity", FL_ENTITIES); ImGui::SetClipboardText(ss.str().c_str()); } @@ -4699,64 +4701,15 @@ void Renderer::pasteEnt(bool noModifyOrigin, bool copyModel) } BspRenderer* rend = map->getBspRender(); + std::vector copiedEnts{}; - std::vector copiedEnts; - std::istringstream in(clipboardText); - - int lineNum = 0; - int lastBracket = -1; - Entity* ent = NULL; - std::string line = ""; - - while (std::getline(in, line)) + try + { + copiedEnts = load_ents(clipboardText, map->bsp_name); + } + catch (...) { - lineNum++; - if (line.length() < 1 || line[0] == '\n') - continue; - - if (line[0] == '{') - { - if (lastBracket == 0) - { - print_log("clipboard ent text data (line {}): Unexpected '{'\n", lineNum); - continue; - } - lastBracket = 0; - - delete ent; - ent = new Entity(); - } - else if (line[0] == '}') - { - if (lastBracket == 1) - print_log("clipboard ent text data (line {}): Unexpected '}'\n", lineNum); - lastBracket = 1; - - if (ent == NULL) - continue; - - if (ent->keyvalues.count("classname")) - copiedEnts.push_back(ent); - else - print_log("Found unknown classname entity. Skip it.\n"); - ent = NULL; - // you can end/start an ent on the same line, you know - if (line.find('{') != std::string::npos) - { - ent = new Entity(); - lastBracket = 0; - } - } - else if (lastBracket == 0 && ent != NULL) // currently defining an entity - { - Keyvalues keyvals(line); - for (size_t k = 0; k < keyvals.keys.size(); k++) - { - if (keyvals.keys[k].length() && keyvals.values[k].length()) - ent->addKeyvalue(keyvals.keys[k], keyvals.values[k]); - } - } } clearSelection(); @@ -4789,51 +4742,60 @@ void Renderer::pasteEnt(bool noModifyOrigin, bool copyModel) vec3 rounded = gridSnappingEnabled ? snapToGrid(newOri) : newOri; copiedEnts[i]->setOrAddKeyvalue("origin", rounded.toKeyvalueString()); } - - CreateEntityCommand* createCommand = new CreateEntityCommand("Paste Entity", getSelectedMapId(), copiedEnts[i]); - map->getBspRender()->pushUndoCommand(createCommand); - selectEnt(map, map->ents.size() > 1 ? ((int)map->ents.size() - 1) : 0, true); + map->ents.push_back(copiedEnts[i]); + selectEnt(map, (int)map->ents.size() - 1, true); } + + if (copiedEnts.size()) + rend->pushUndoState("Paste Entity", FL_ENTITIES); } -void Renderer::pasteEntsFromText(std::string text) { +void Renderer::pasteEntsFromText(std::string text) +{ + auto clipboardText = ImGui::GetClipboardText(); + if (!clipboardText) + return; Bsp* map = getSelectedMap(); if (!map) { return; } BspRenderer* rend = map->getBspRender(); + std::vector copiedEnts{}; - CreateEntityFromTextCommand* createCommand = - new CreateEntityFromTextCommand("Paste entities from clipboard", getSelectedMapId(), text); - rend->pushUndoCommand(createCommand); - - if (createCommand->createdEnts == 1) { - Entity* createdEnt = map->ents[map->ents.size() - 1]; - vec3 oldOrigin = getEntOrigin(map, createdEnt); - vec3 modelOffset = getEntOffset(map, createdEnt); - vec3 mapOffset = rend->mapOffset; + try + { + copiedEnts = load_ents(clipboardText, map->bsp_name); + } + catch (...) + { - vec3 moveDist = (cameraOrigin + cameraForward * 100) - oldOrigin; - vec3 newOri = (oldOrigin + moveDist) - (modelOffset + mapOffset); - vec3 rounded = gridSnappingEnabled ? snapToGrid(newOri) : newOri; - createdEnt->setOrAddKeyvalue("origin", rounded.toKeyvalueString(!gridSnappingEnabled)); - createCommand->refresh(); } - if (map->ents.size() > 0) - selectEnt(map, (int)map->ents.size() - 1); -} + clearSelection(); + selectMap(map); -void Renderer::deleteEnt(int entIdx) -{ - Bsp* map = SelectedMap; + for (size_t i = 0; i < copiedEnts.size(); i++) + { + vec3 baseOrigin = copiedEnts[0]->origin; - if (!map || entIdx <= 0) - return; - PickInfo tmpPickInfo = pickInfo; - DeleteEntityCommand* deleteCommand = new DeleteEntityCommand("Delete Entity", entIdx); - map->getBspRender()->pushUndoCommand(deleteCommand); + vec3 entOrigin = copiedEnts[i]->origin; + vec3 offset = entOrigin - baseOrigin; + vec3 mapOffset = map->getBspRender()->mapOffset; + vec3 moveDist = (cameraOrigin + cameraForward * 100) - entOrigin; + vec3 newOri = (entOrigin + moveDist) - (entOrigin + mapOffset); + + newOri += offset; + + vec3 rounded = gridSnappingEnabled ? snapToGrid(newOri) : newOri; + copiedEnts[i]->setOrAddKeyvalue("origin", rounded.toKeyvalueString()); + + map->ents.push_back(copiedEnts[i]); + selectEnt(map, (int)map->ents.size() - 1, true); + } + + + rend->pushUndoState("Paste Ents from clipboard", FL_ENTITIES); } void Renderer::deleteEnts() @@ -4857,13 +4819,16 @@ void Renderer::deleteEnts() { reloadbspmdls = true; } - deleteEnt((int)entIdx); + delete map->ents[entIdx]; + map->ents.erase(map->ents.begin() + entIdx); } if (reloadbspmdls) { reloadBspModels(); } + + map->getBspRender()->pushUndoState("Delete ents", FL_ENTITIES); } } @@ -4966,7 +4931,6 @@ void Renderer::selectEnt(Bsp* map, int entIdx, bool add) } filterNeeded = true; updateEntConnections(); - map->getBspRender()->saveEntityState(entIdx); pickCount++; // force transform window update } } @@ -5090,8 +5054,7 @@ void Renderer::ungrabEnt() { return; } - for (auto& ent : pickEnts) - map->getBspRender()->pushEntityUndoState("Move Entity", (int)ent); + map->getBspRender()->pushEntityUndoStateDelay("Move Entity"); movingEnt = false; } diff --git a/src/editor/Renderer.h b/src/editor/Renderer.h index 6c76872e..7378bb61 100644 --- a/src/editor/Renderer.h +++ b/src/editor/Renderer.h @@ -99,14 +99,7 @@ extern std::vector mapRenderers; class Renderer { friend class Gui; - friend class EditEntityCommand; - friend class DeleteEntityCommand; - friend class CreateEntityCommand; - friend class DuplicateBspModelCommand; - friend class CreateBspModelCommand; - friend class EditBspModelCommand; - friend class CleanMapCommand; - friend class OptimizeMapCommand; + friend class EditBspCommand; public: @@ -366,7 +359,6 @@ class Renderer void copyEnt(); void pasteEnt(bool noModifyOrigin, bool copyModel = false); void pasteEntsFromText(std::string text); - void deleteEnt(int entIdx = 0); void deleteEnts(); void scaleSelectedObject(Bsp* map, int modelIdx, float x, float y, float z); void scaleSelectedObject(Bsp* map, int modelIdx, vec3 dir, const vec3& fromDir, bool logging = false); diff --git a/src/main.cpp b/src/main.cpp index 51dbb9b7..a176fe17 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,7 +24,7 @@ // Notes: (newbspguy): // ... -std::string g_version_string = "NewBSPGuy v4.40"; +std::string g_version_string = "NewBSPGuy v4.45"; #ifdef WIN32 diff --git a/src/util/util.cpp b/src/util/util.cpp index af93706c..32670042 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -2931,3 +2931,119 @@ void mapFixLightEnts(Bsp* map) map->update_ent_lump(); } + +std::vector load_ents(const std::string& entLump, const std::string& mapName) +{ + std::vector ents{}; + std::istringstream in(entLump); + + int lineNum = 0; + int lastBracket = -1; + Entity* ent = NULL; + + std::string line; + while (std::getline(in, line)) + { + lineNum++; + + while (line[0] == ' ' || line[0] == '\t' || line[0] == '\r') + { + line.erase(line.begin()); + } + + if (line.length() < 1 || line[0] == '\n') + continue; + + if (line[0] == '{') + { + if (lastBracket == 0) + { + print_log(get_localized_string(LANG_0103), mapName, lineNum); + continue; + } + lastBracket = 0; + delete ent; + ent = new Entity(); + + if (line.find('}') == std::string::npos && + line.find('\"') == std::string::npos) + { + continue; + } + } + if (line[0] == '}') + { + if (lastBracket == 1) + print_log(get_localized_string(LANG_0104), mapName, lineNum); + lastBracket = 1; + if (!ent) + continue; + + if (ent->keyvalues.count("classname")) + ents.push_back(ent); + else + print_log(get_localized_string(LANG_0105)); + + ent = NULL; + + // you can end/start an ent on the same line, you know + if (line.find('{') != std::string::npos) + { + ent = new Entity(); + lastBracket = 0; + + if (line.find('\"') == std::string::npos) + { + continue; + } + line.erase(line.begin()); + } + } + if (lastBracket == 0 && ent) // currently defining an entity + { + Keyvalues k(line); + for (size_t i = 0; i < k.keys.size(); i++) + { + ent->addKeyvalue(k.keys[i], k.values[i], true); + } + + if (line.find('}') != std::string::npos) + { + lastBracket = 1; + + if (ent->keyvalues.count("classname")) + ents.push_back(ent); + else + print_log(get_localized_string(LANG_1022)); + + ent = NULL; + } + if (line.find('{') != std::string::npos) + { + ent = new Entity(); + lastBracket = 0; + } + } + } + + // swap worldspawn to first entity + if (ents.size() > 1) + { + if (ents[0]->keyvalues["classname"] != "worldspawn") + { + print_log(get_localized_string(LANG_0106)); + for (size_t i = 1; i < ents.size(); i++) + { + if (ents[i]->keyvalues["classname"] == "worldspawn") + { + std::swap(ents[0], ents[i]); + break; + } + } + } + } + + delete ent; + + return ents; +} \ No newline at end of file diff --git a/src/util/util.h b/src/util/util.h index c39e4645..d5c8b332 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -17,6 +17,7 @@ namespace fs = std::filesystem; #include #include "primitives.h" +class Entity; class Bsp; extern std::string g_version_string; @@ -289,4 +290,6 @@ int ImportModel(Bsp* map, const std::string& mdl_path, bool noclip); unsigned char FixBounds(int i); unsigned char FixBounds(unsigned int i); unsigned char FixBounds(float i); -unsigned char FixBounds(double i); \ No newline at end of file +unsigned char FixBounds(double i); + +std::vector load_ents(const std::string& entLump, const std::string& mapName); \ No newline at end of file