diff --git a/cfg/language.ini b/cfg/language.ini index 8028a841..7d98e34b 100644 --- a/cfg/language.ini +++ b/cfg/language.ini @@ -460,8 +460,6 @@ LANG_0460 = All Hulls LANG_0461 = Simplify Hull LANG_0462 = Redirect Hull LANG_0463 = Print Hull Tree -LANG_0464 = Duplicate BSP model -LANG_0465 = Create a copy of this BSP model and assign to this entity.\n\nThis lets you edit the model for this entity without affecting others. LANG_0466 = Export BSP model LANG_0467 = With origin LANG_0468 = With WAD @@ -1077,7 +1075,6 @@ LANG_1083 = Copy LANG_1084 = Ctrl+C LANG_1085 = Delete LANG_1086 = Del -LANG_1087 = Duplicate BSP model LANG_1088 = ALT+G LANG_1089 = Transform LANG_1090 = Ctrl+M @@ -1180,4 +1177,8 @@ LANG_SETTINGS_WADPATH = Resources path LANG_SETTINGS_OPTIMIZE = Optimizing LANG_SETTINGS_LIMITS = Limits LANG_SETTINGS_RENDER = Rendering -LANG_SETTINGS_CONTROL = Controls \ No newline at end of file +LANG_SETTINGS_CONTROL = Controls +LANG_DUPLICATE_BSP = Duplicate BSP model +LANG_CREATE_DUPLICATE_BSP = Create a new copy of BSP model and assign to this entity.\n\nThis lets you edit the model for this entity without affecting original model. +LANG_DUPLICATE_BSP_STRUCT = Unlock BSP model +LANG_CREATE_DUPLICATE_STRUCT = Create a new copy of BSP model structures.\n\nThis lets you edit the model assigned for this entity. diff --git a/cfg/language_ru.ini b/cfg/language_ru.ini index fb4968c0..c399b1e5 100644 --- a/cfg/language_ru.ini +++ b/cfg/language_ru.ini @@ -460,8 +460,6 @@ LANG_0460 = Все хуллы LANG_0461 = Упростить хулл LANG_0462 = Переназначить хулл LANG_0463 = Вывести дерево хуллов -LANG_0464 = Дублировать браш-модель -LANG_0465 = Создаёт копию браш-модели, привязывая к ней эту же сущность.\n\nЭто позволит вам изменить модель сущности, не затрагивая другие. LANG_0466 = Экспорт браш-модели LANG_0467 = С оригином LANG_0468 = Текстуры в WAD-файлах @@ -1077,7 +1075,6 @@ LANG_1083 = Копировать LANG_1084 = Ctrl+C LANG_1085 = Удалить LANG_1086 = Del -LANG_1087 = Дублировать браш-модель LANG_1088 = ALT+G LANG_1089 = Манипуляция LANG_1090 = Ctrl+M @@ -1181,3 +1178,7 @@ LANG_SETTINGS_OPTIMIZE = Оптимизация LANG_SETTINGS_LIMITS = Лимиты LANG_SETTINGS_RENDER = Рендеринг LANG_SETTINGS_CONTROL = Управление +LANG_DUPLICATE_BSP = Дублировать браш-модель +LANG_CREATE_DUPLICATE_BSP = Создаёт новую копию браш-модели, привязывает к ней эту же сущность.\n\nПозволит редактировать модель не затрагивая оригинал. +LANG_DUPLICATE_BSP_STRUCT = Дублировать браш-структуры +LANG_CREATE_DUPLICATE_STRUCT = Создает копию струтур выбранной модели.\n\nПозволяет редактировать оригинал браш-модели. \ No newline at end of file diff --git a/src/bsp/Bsp.cpp b/src/bsp/Bsp.cpp index b5954b7b..32c46ed1 100644 --- a/src/bsp/Bsp.cpp +++ b/src/bsp/Bsp.cpp @@ -6155,7 +6155,7 @@ int Bsp::create_texinfo() void Bsp::copy_bsp_model(int modelIdx, Bsp* targetMap, STRUCTREMAP& remap, std::vector& newPlanes, std::vector& newVerts, std::vector& newEdges, std::vector& newSurfedges, std::vector& newTexinfo, std::vector& newFaces, std::vector& newLightmaps, std::vector& newNodes, - std::vector& newClipnodes) + std::vector& newClipnodes, std::vector & newTextures) { STRUCTUSAGE usage(this); mark_model_structures(modelIdx, &usage, true); @@ -6303,7 +6303,56 @@ void Bsp::copy_bsp_model(int modelIdx, Bsp* targetMap, STRUCTREMAP& remap, std:: } } +void Bsp::duplicate_model_structures(int modelIdx) +{ + std::vector newPlanes; + std::vector newVerts; + std::vector newEdges; + std::vector newSurfedges; + std::vector newTexinfo; + std::vector newFaces; + std::vector newLightmaps; + std::vector newNodes; + std::vector newClipnodes; + std::vector newTextures; + + STRUCTREMAP remap(this); + copy_bsp_model(modelIdx, this, remap, newPlanes, newVerts, newEdges, newSurfedges, newTexinfo, newFaces, newLightmaps, newNodes, newClipnodes, newTextures); + + if (newClipnodes.size()) + append_lump(LUMP_CLIPNODES, &newClipnodes[0], sizeof(BSPCLIPNODE32) * newClipnodes.size()); + if (newEdges.size()) + append_lump(LUMP_EDGES, &newEdges[0], sizeof(BSPEDGE32) * newEdges.size()); + if (newFaces.size()) + { + append_lump(LUMP_FACES, &newFaces[0], sizeof(BSPFACE32) * newFaces.size()); + } + if (newNodes.size()) + append_lump(LUMP_NODES, &newNodes[0], sizeof(BSPNODE32) * newNodes.size()); + if (newPlanes.size()) + append_lump(LUMP_PLANES, &newPlanes[0], sizeof(BSPPLANE) * newPlanes.size()); + if (newSurfedges.size()) + append_lump(LUMP_SURFEDGES, &newSurfedges[0], sizeof(int) * newSurfedges.size()); + if (newTexinfo.size()) + append_lump(LUMP_TEXINFO, &newTexinfo[0], sizeof(BSPTEXTUREINFO) * newTexinfo.size()); + if (newVerts.size()) + append_lump(LUMP_VERTICES, &newVerts[0], sizeof(vec3) * newVerts.size()); + if (newLightmaps.size()) + { + append_lump(LUMP_LIGHTING, &newLightmaps[0], sizeof(COLOR3) * newLightmaps.size()); + save_undo_lightmaps(); + resize_all_lightmaps(); + renderer->loadLightmaps(); + } + BSPMODEL& oldModel = models[modelIdx]; + oldModel.iFirstFace = remap.faces[oldModel.iFirstFace]; + oldModel.iHeadnodes[0] = oldModel.iHeadnodes[0] < 0 ? -1 : remap.nodes[oldModel.iHeadnodes[0]]; + for (int i = 1; i < MAX_MAP_HULLS; i++) + { + oldModel.iHeadnodes[i] = oldModel.iHeadnodes[i] < 0 ? -1 : remap.clipnodes[oldModel.iHeadnodes[i]]; + } +} int Bsp::duplicate_model(int modelIdx) { @@ -6316,9 +6365,10 @@ int Bsp::duplicate_model(int modelIdx) std::vector newLightmaps; std::vector newNodes; std::vector newClipnodes; + std::vector newTextures; STRUCTREMAP remap(this); - copy_bsp_model(modelIdx, this, remap, newPlanes, newVerts, newEdges, newSurfedges, newTexinfo, newFaces, newLightmaps, newNodes, newClipnodes); + copy_bsp_model(modelIdx, this, remap, newPlanes, newVerts, newEdges, newSurfedges, newTexinfo, newFaces, newLightmaps, newNodes, newClipnodes, newTextures); if (newClipnodes.size()) append_lump(LUMP_CLIPNODES, &newClipnodes[0], sizeof(BSPCLIPNODE32) * newClipnodes.size()); @@ -6370,6 +6420,7 @@ int Bsp::duplicate_model(int modelIdx) newModel.iHeadnodes[i] = oldModel.iHeadnodes[i] < 0 ? -1 : remap.clipnodes[oldModel.iHeadnodes[i]]; } //newModel.nVisLeafs = 0; // techinically should match the old model, but leaves aren't duplicated yetx + // recalculate leafs return newModelIdx; } diff --git a/src/bsp/Bsp.h b/src/bsp/Bsp.h index b9e3968b..0429cf73 100644 --- a/src/bsp/Bsp.h +++ b/src/bsp/Bsp.h @@ -247,9 +247,10 @@ class Bsp void copy_bsp_model(int modelIdx, Bsp* targetMap, STRUCTREMAP& remap, std::vector& newPlanes, std::vector& newVerts, std::vector& newEdges, std::vector& newSurfedges, std::vector& newTexinfo, std::vector& newFaces, std::vector& newLightmaps, std::vector& newNodes, - std::vector& newClipnodes); + std::vector& newClipnodes, std::vector& newTextures); int duplicate_model(int modelIdx); + void duplicate_model_structures(int modelIdx); int add_model_to_worldspawn(int modelIdx); // if the face's texinfo is not unique, a new one is created and returned. Otherwise, it's current texinfo is returned diff --git a/src/bsp/bsptypes.h b/src/bsp/bsptypes.h index b915d72f..e868e13e 100644 --- a/src/bsp/bsptypes.h +++ b/src/bsp/bsptypes.h @@ -63,23 +63,22 @@ enum lump_copy_targets FL_MODELS = 16384 }; - enum clean_unused_lump { - CLEAN_LIGHTMAP = 1, CLEAN_PLANES = 2, - CLEAN_NODES = 4, - CLEAN_CLIPNODES = 8, - CLEAN_LEAVES = 16, - CLEAN_MARKSURFACES = 32, - CLEAN_FACES = 64, - CLEAN_SURFEDGES = 128, - CLEAN_TEXINFOS = 256, - CLEAN_EDGES = 512, - CLEAN_VERTICES = 1024, - CLEAN_TEXTURES = 2048, - CLEAN_VISDATA = 4096, - CLEAN_CLIPNODES_SOMETHING = 8192 + CLEAN_TEXTURES = 4, + CLEAN_VERTICES = 8, + CLEAN_VISDATA = 16, + CLEAN_NODES = 32, + CLEAN_TEXINFOS = 64, + CLEAN_FACES = 128, + CLEAN_LIGHTMAP = 256, + CLEAN_CLIPNODES = 512, + CLEAN_LEAVES = 1024, + CLEAN_MARKSURFACES = 2048, + CLEAN_EDGES = 4096, + CLEAN_SURFEDGES = 8192, + CLEAN_CLIPNODES_SOMETHING = 16384 }; #define MAX_AMBIENTS 4 diff --git a/src/editor/BspRenderer.cpp b/src/editor/BspRenderer.cpp index 93eb1143..06e1d253 100644 --- a/src/editor/BspRenderer.cpp +++ b/src/editor/BspRenderer.cpp @@ -715,8 +715,6 @@ void BspRenderer::deleteRenderModel(RenderModel* renderModel) return; } - - if (renderModel->renderGroups) { for (int k = 0; k < renderModel->groupCount; k++) @@ -847,9 +845,9 @@ int BspRenderer::refreshModel(int modelIdx, bool refreshClipnodes, bool noTriang renderModel->renderFaces = new RenderFace[model.nFaces]; - std::vector renderGroups; - std::vector> renderGroupVerts; - std::vector> renderGroupWireframeVerts; + std::vector renderGroups{}; + std::vector> renderGroupVerts{}; + std::vector> renderGroupWireframeVerts{}; for (int i = 0; i < model.nFaces; i++) { diff --git a/src/editor/Command.cpp b/src/editor/Command.cpp index 5ba5fe29..8d854b63 100644 --- a/src/editor/Command.cpp +++ b/src/editor/Command.cpp @@ -294,6 +294,9 @@ void DuplicateBspModelCommand::execute() pickCount++; vertPickCount++; + + map->remove_unused_model_structures(CLEAN_LEAVES); + g_app->gui->refresh(); /* diff --git a/src/editor/Gui.cpp b/src/editor/Gui.cpp index be6c5c96..3e1adde3 100644 --- a/src/editor/Gui.cpp +++ b/src/editor/Gui.cpp @@ -811,22 +811,55 @@ void Gui::drawBspContexMenu() } if (modelIdx > 0) { - if (ImGui::MenuItem(get_localized_string(LANG_0464).c_str(), 0, false, !app->isLoading && allowDuplicate)) + if (ImGui::MenuItem(get_localized_string("LANG_DUPLICATE_BSP").c_str(), 0, false, !app->isLoading && allowDuplicate)) { print_log(get_localized_string(LANG_0336), app->pickInfo.selectedEnts.size()); for (auto& tmpEntIdx : app->pickInfo.selectedEnts) { - DuplicateBspModelCommand* command = new DuplicateBspModelCommand("Duplicate BSP Model", tmpEntIdx); - map->getBspRender()->pushUndoCommand(command); + if (map->ents[tmpEntIdx]->isBspModel()) + { + DuplicateBspModelCommand* command = new DuplicateBspModelCommand(get_localized_string("LANG_DUPLICATE_BSP"), tmpEntIdx); + map->getBspRender()->pushUndoCommand(command); + } } } + if (ImGui::IsItemHovered() && g.HoveredIdTimer > g_tooltip_delay) { ImGui::BeginTooltip(); - ImGui::TextUnformatted(get_localized_string(LANG_0465).c_str()); + ImGui::TextUnformatted(get_localized_string("LANG_CREATE_DUPLICATE_BSP").c_str()); ImGui::EndTooltip(); } + bool disableBspDupStruct = !app->isTransformableSolid; + if (disableBspDupStruct) + { + ImGui::BeginDisabled(); + } + if (ImGui::MenuItem(get_localized_string("LANG_DUPLICATE_BSP_STRUCT").c_str(), 0, false, !app->isLoading && allowDuplicate)) + { + print_log(get_localized_string(LANG_0336), app->pickInfo.selectedEnts.size()); + for (auto& tmpEntIdx : app->pickInfo.selectedEnts) + { + if (map->ents[tmpEntIdx]->isBspModel()) + { + pickCount++; + map->duplicate_model_structures(map->ents[tmpEntIdx]->getBspModelIdx()); + map->getBspRender()->pushModelUndoState(get_localized_string("LANG_DUPLICATE_BSP_STRUCT"), EDIT_MODEL_LUMPS); + } + } + } + + if (ImGui::IsItemHovered() && g.HoveredIdTimer > g_tooltip_delay) + { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(get_localized_string("LANG_CREATE_DUPLICATE_STRUCT").c_str()); + ImGui::EndTooltip(); + } + if (disableBspDupStruct) + { + ImGui::EndDisabled(); + } /*if (ImGui::MenuItem("ADD TO WORLDSPAWN!", 0, false, !app->isLoading && allowDuplicate)) { print_log(get_localized_string(LANG_1054), app->pickInfo.selectedEnts.size()); @@ -2499,16 +2532,55 @@ void Gui::drawMenuBar() } } - if (ImGui::MenuItem(get_localized_string(LANG_1087).c_str(), 0, false, !app->isLoading && allowDuplicate)) + if (ImGui::MenuItem(get_localized_string("LANG_DUPLICATE_BSP").c_str(), 0, false, !app->isLoading && allowDuplicate)) { - print_log(get_localized_string(LANG_1054), app->pickInfo.selectedEnts.size()); - for (auto& ent : app->pickInfo.selectedEnts) + print_log(get_localized_string(LANG_0336), app->pickInfo.selectedEnts.size()); + for (auto& tmpEntIdx : app->pickInfo.selectedEnts) + { + if (map->ents[tmpEntIdx]->isBspModel()) + { + DuplicateBspModelCommand* command = new DuplicateBspModelCommand(get_localized_string("LANG_DUPLICATE_BSP"), tmpEntIdx); + map->getBspRender()->pushUndoCommand(command); + } + } + } + + if (ImGui::IsItemHovered() && g.HoveredIdTimer > g_tooltip_delay) + { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(get_localized_string("LANG_CREATE_DUPLICATE_BSP").c_str()); + ImGui::EndTooltip(); + } + bool disableBspDupStruct = !app->isTransformableSolid; + if (disableBspDupStruct) + { + ImGui::BeginDisabled(); + } + if (ImGui::MenuItem(get_localized_string("LANG_DUPLICATE_BSP_STRUCT").c_str(), 0, false, !app->isLoading && allowDuplicate)) + { + print_log(get_localized_string(LANG_0336), app->pickInfo.selectedEnts.size()); + for (auto& tmpEntIdx : app->pickInfo.selectedEnts) { - DuplicateBspModelCommand* command = new DuplicateBspModelCommand("Duplicate BSP Model", ent); - map->getBspRender()->pushUndoCommand(command); + if (map->ents[tmpEntIdx]->isBspModel()) + { + pickCount++; + map->duplicate_model_structures(map->ents[tmpEntIdx]->getBspModelIdx()); + map->getBspRender()->pushModelUndoState(get_localized_string("LANG_DUPLICATE_BSP_STRUCT"), EDIT_MODEL_LUMPS); + } } } + if (ImGui::IsItemHovered() && g.HoveredIdTimer > g_tooltip_delay) + { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(get_localized_string("LANG_CREATE_DUPLICATE_STRUCT").c_str()); + ImGui::EndTooltip(); + } + if (disableBspDupStruct) + { + ImGui::EndDisabled(); + } + /*if (ImGui::MenuItem("ADD TO WORLDSPAWN!", 0, false, !app->isLoading && allowDuplicate)) { print_log(get_localized_string(LANG_1054), app->pickInfo.selectedEnts.size()); @@ -6623,9 +6695,11 @@ void Gui::drawImportMapWidget() } else if (showImportMapWidget_Type == SHOW_IMPORT_MODEL_BSP) { + Bsp* map = app->getSelectedMap(); + Bsp* bspModel = new Bsp(mapPath); + BspRenderer* mapRenderer = new BspRenderer(bspModel, app->pointEntRenderer); - Bsp* map = app->getSelectedMap(); std::vector newPlanes; std::vector newVerts; @@ -6636,10 +6710,10 @@ void Gui::drawImportMapWidget() std::vector newLightmaps; std::vector newNodes; std::vector newClipnodes; + std::vector newTextures; - STRUCTREMAP* remap = new STRUCTREMAP(map); - - bspModel->copy_bsp_model(0, map, *remap, newPlanes, newVerts, newEdges, newSurfedges, newTexinfo, newFaces, newLightmaps, newNodes, newClipnodes); + STRUCTREMAP remap = STRUCTREMAP(map); + bspModel->copy_bsp_model(0, map, remap, newPlanes, newVerts, newEdges, newSurfedges, newTexinfo, newFaces, newLightmaps, newNodes, newClipnodes, newTextures); if (newClipnodes.size()) { @@ -6665,12 +6739,27 @@ void Gui::drawImportMapWidget() { map->append_lump(LUMP_SURFEDGES, &newSurfedges[0], sizeof(int) * newSurfedges.size()); } + + if (newTextures.size()) + { + while (newTextures.size()) + { + auto tex = newTextures[newTextures.size() - 1]; + map->add_texture(tex.szName, tex.data, tex.nWidth, tex.nHeight); + newTextures.pop_back(); + } + } + if (newTexinfo.size()) { + map->append_lump(LUMP_TEXINFO, &newTexinfo[0], sizeof(BSPTEXTUREINFO) * newTexinfo.size()); for (auto& texinfo : newTexinfo) { if (texinfo.iMiptex < 0 || texinfo.iMiptex >= map->textureCount) + { + texinfo.iMiptex = -1; continue; + } int newMiptex = -1; int texOffset = ((int*)bspModel->textures)[texinfo.iMiptex + 1]; if (texOffset < 0) @@ -6698,7 +6787,7 @@ void Gui::drawImportMapWidget() WADTEX* wadTex = s->readTexture(tex.szName); COLOR3* imageData = ConvertWadTexToRGB(wadTex); - texinfo.iMiptex = map->add_texture(tex.szName, (unsigned char*)imageData, wadTex->nWidth, wadTex->nHeight); + newMiptex = map->add_texture(tex.szName, (unsigned char*)imageData, wadTex->nWidth, wadTex->nHeight); if (texinfo.iMiptex == -1) texinfo.iMiptex = 0; @@ -6709,15 +6798,10 @@ void Gui::drawImportMapWidget() } } } - else - { - if (newMiptex == -1) - newMiptex = 0; - texinfo.iMiptex = newMiptex; - } + texinfo.iMiptex = newMiptex; } - map->append_lump(LUMP_TEXINFO, &newTexinfo[0], sizeof(BSPTEXTUREINFO) * newTexinfo.size()); } + if (newVerts.size()) { map->append_lump(LUMP_VERTICES, &newVerts[0], sizeof(vec3) * newVerts.size()); @@ -6732,12 +6816,12 @@ void Gui::drawImportMapWidget() BSPMODEL& newModel = map->models[newModelIdx]; memcpy(&newModel, &oldModel, sizeof(BSPMODEL)); - newModel.iFirstFace = (*remap).faces[oldModel.iFirstFace]; - newModel.iHeadnodes[0] = oldModel.iHeadnodes[0] < 0 ? -1 : (*remap).nodes[oldModel.iHeadnodes[0]]; + newModel.iFirstFace = remap.faces[oldModel.iFirstFace]; + newModel.iHeadnodes[0] = oldModel.iHeadnodes[0] < 0 ? -1 : remap.nodes[oldModel.iHeadnodes[0]]; for (int i = 1; i < MAX_MAP_HULLS; i++) { - newModel.iHeadnodes[i] = oldModel.iHeadnodes[i] < 0 ? -1 : (*remap).clipnodes[oldModel.iHeadnodes[i]]; + newModel.iHeadnodes[i] = oldModel.iHeadnodes[i] < 0 ? -1 : remap.clipnodes[oldModel.iHeadnodes[i]]; } newModel.nVisLeafs = 0; @@ -6749,7 +6833,7 @@ void Gui::drawImportMapWidget() map->ents[map->ents.size() - 1]->setOrAddKeyvalue("origin", "0 0 0"); map->update_ent_lump(); app->updateEnts(); - + map->update_lump_pointers(); map->getBspRender()->reload(); delete mapRenderer; }