From 2e05866542b49b50912ce73da9a1ab1a7c345cc3 Mon Sep 17 00:00:00 2001 From: ousnius Date: Sat, 21 Mar 2020 20:14:23 +0100 Subject: [PATCH] Save and load masks #229 --- lang/BodySlide.pot | 9 +++ lang/de/BodySlide.mo | Bin 78932 -> 79151 bytes lang/de/BodySlide.po | 9 +++ res/xrc/OutfitStudio.xrc | 66 ++++++++++++++++++++-- src/program/OutfitStudio.cpp | 104 ++++++++++++++++++++++++++++++----- src/program/OutfitStudio.h | 26 +++++++-- src/render/GLSurface.h | 2 +- 7 files changed, 188 insertions(+), 28 deletions(-) diff --git a/lang/BodySlide.pot b/lang/BodySlide.pot index 9de9119a..bd7ea43f 100644 --- a/lang/BodySlide.pot +++ b/lang/BodySlide.pot @@ -2570,3 +2570,12 @@ msgstr "" msgid "Rotation" msgstr "" + +msgid "Masks" +msgstr "" + +msgid "New Mask" +msgstr "" + +msgid "Please enter a new unique name for the mask." +msgstr "" diff --git a/lang/de/BodySlide.mo b/lang/de/BodySlide.mo index 82baa2400162dd12b85bef2293c680b20f4171a6..a7f56e4c286a5f1985fad088d6e3a2f1d09104f9 100644 GIT binary patch delta 18138 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 ACIA2c delta 18045 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 + + + wxALL|wxEXPAND|wxLEFT + 2 + + + 1 + + wxSYS_COLOUR_MENU + 0 + + + wxVERTICAL + + + wxALL|wxEXPAND + + wxHORIZONTAL + + + wxLEFT|wxRIGHT|wxBOTTOM|wxEXPAND + 5 + + 0 + + + + + + wxBOTTOM|wxEXPAND + 5 + + -1,25 + + + + + + wxBOTTOM|wxEXPAND + 5 + + -1,25 + + + + + + wxBOTTOM|wxEXPAND + 5 + + -1,25 + + + + + + + + + wxALL|wxEXPAND|wxLEFT @@ -1857,12 +1917,6 @@ Clear Mask - - - Shade meshes to indicate areas that are currently masked. - 1 - 1 - diff --git a/src/program/OutfitStudio.cpp b/src/program/OutfitStudio.cpp index f1016da2..ff14bf36 100644 --- a/src/program/OutfitStudio.cpp +++ b/src/program/OutfitStudio.cpp @@ -54,7 +54,13 @@ wxBEGIN_EVENT_TABLE(OutfitStudioFrame, wxFrame) EVT_COMMAND_SCROLL(XRCID("brushFocus"), OutfitStudioFrame::OnBrushSettingsSlider) EVT_COMMAND_SCROLL(XRCID("brushSpace"), OutfitStudioFrame::OnBrushSettingsSlider) - EVT_COLLAPSIBLEPANE_CHANGED(XRCID("posePane"), OutfitStudioFrame::OnPosePaneCollapse) + EVT_COLLAPSIBLEPANE_CHANGED(XRCID("masksPane"), OutfitStudioFrame::OnPaneCollapse) + EVT_CHOICE(XRCID("cMaskName"), OutfitStudioFrame::OnSelectMask) + EVT_BUTTON(XRCID("saveMask"), OutfitStudioFrame::OnSaveMask) + EVT_BUTTON(XRCID("saveAsMask"), OutfitStudioFrame::OnSaveAsMask) + EVT_BUTTON(XRCID("deleteMask"), OutfitStudioFrame::OnDeleteMask) + + EVT_COLLAPSIBLEPANE_CHANGED(XRCID("posePane"), OutfitStudioFrame::OnPaneCollapse) EVT_CHOICE(XRCID("cPoseBone"), OutfitStudioFrame::OnPoseBoneChanged) EVT_COMMAND_SCROLL(XRCID("rxPoseSlider"), OutfitStudioFrame::OnRXPoseSlider) EVT_COMMAND_SCROLL(XRCID("ryPoseSlider"), OutfitStudioFrame::OnRYPoseSlider) @@ -158,7 +164,6 @@ wxBEGIN_EVENT_TABLE(OutfitStudioFrame, wxFrame) EVT_MENU(XRCID("btnMaskMore"), OutfitStudioFrame::OnMaskMore) EVT_MENU(XRCID("btnClearMask"), OutfitStudioFrame::OnClearMask) EVT_MENU(XRCID("btnInvertMask"), OutfitStudioFrame::OnInvertMask) - EVT_MENU(XRCID("btnShowMask"), OutfitStudioFrame::OnShowMask) EVT_MENU(XRCID("btnRecalcNormals"), OutfitStudioFrame::OnRecalcNormals) EVT_MENU(XRCID("btnSmoothSeams"), OutfitStudioFrame::OnSmoothNormalSeams) @@ -2888,6 +2893,10 @@ void OutfitStudioFrame::ClearProject() { glView->DestroyOverlays(); activePartition.Unset(); + activeSegment.Unset(); + + wxChoice* cMaskName = (wxChoice*)FindWindowByName("cMaskName"); + cMaskName->Clear(); SetTitle("Outfit Studio"); } @@ -5531,6 +5540,11 @@ void OutfitStudioFrame::OnReadoutChange(wxCommandEvent& event) { void OutfitStudioFrame::OnTabButtonClick(wxCommandEvent& event) { int id = event.GetId(); + if (id != XRCID("meshTabButton")) { + wxCollapsiblePane* masksPane = dynamic_cast(FindWindowByName("masksPane")); + masksPane->Hide(); + } + if (id != XRCID("segmentTabButton")) { wxStaticText* segmentTypeLabel = (wxStaticText*)FindWindowByName("segmentTypeLabel"); wxChoice* segmentType = (wxChoice*)FindWindowByName("segmentType"); @@ -5559,12 +5573,10 @@ void OutfitStudioFrame::OnTabButtonClick(wxCommandEvent& event) { glView->SetGlobalBrushCollision(); GetMenuBar()->Check(XRCID("btnBrushCollision"), true); - GetMenuBar()->Check(XRCID("btnShowMask"), true); GetMenuBar()->Enable(XRCID("btnBrushCollision"), true); GetMenuBar()->Enable(XRCID("btnSelect"), true); GetMenuBar()->Enable(XRCID("btnClearMask"), true); GetMenuBar()->Enable(XRCID("btnInvertMask"), true); - GetMenuBar()->Enable(XRCID("btnShowMask"), true); GetMenuBar()->Enable(XRCID("deleteVerts"), true); GetToolBar()->ToggleTool(XRCID("btnBrushCollision"), true); @@ -5592,12 +5604,10 @@ void OutfitStudioFrame::OnTabButtonClick(wxCommandEvent& event) { glView->SetGlobalBrushCollision(); GetMenuBar()->Check(XRCID("btnBrushCollision"), true); - GetMenuBar()->Check(XRCID("btnShowMask"), true); GetMenuBar()->Enable(XRCID("btnBrushCollision"), true); GetMenuBar()->Enable(XRCID("btnSelect"), true); GetMenuBar()->Enable(XRCID("btnClearMask"), true); GetMenuBar()->Enable(XRCID("btnInvertMask"), true); - GetMenuBar()->Enable(XRCID("btnShowMask"), true); GetMenuBar()->Enable(XRCID("deleteVerts"), true); GetToolBar()->ToggleTool(XRCID("btnBrushCollision"), true); @@ -5705,12 +5715,15 @@ void OutfitStudioFrame::OnTabButtonClick(wxCommandEvent& event) { wxStateButton* segmentTabButton = (wxStateButton*)FindWindowByName("segmentTabButton"); wxStateButton* partitionTabButton = (wxStateButton*)FindWindowByName("partitionTabButton"); wxStateButton* lightsTabButton = (wxStateButton*)FindWindowByName("lightsTabButton"); + wxCollapsiblePane* masksPane = dynamic_cast(FindWindowByName("masksPane")); boneTabButton->SetCheck(false); colorsTabButton->SetCheck(false); segmentTabButton->SetCheck(false); partitionTabButton->SetCheck(false); lightsTabButton->SetCheck(false); + + masksPane->Show(); } else if (id == XRCID("boneTabButton")) { outfitShapes->Hide(); @@ -5892,7 +5905,6 @@ void OutfitStudioFrame::OnTabButtonClick(wxCommandEvent& event) { GetMenuBar()->Check(XRCID("btnMaskBrush"), true); GetMenuBar()->Check(XRCID("btnXMirror"), false); GetMenuBar()->Check(XRCID("btnBrushCollision"), false); - GetMenuBar()->Check(XRCID("btnShowMask"), false); GetMenuBar()->Enable(XRCID("btnSelect"), false); GetMenuBar()->Enable(XRCID("btnTransform"), false); GetMenuBar()->Enable(XRCID("btnPivot"), false); @@ -5904,7 +5916,6 @@ void OutfitStudioFrame::OnTabButtonClick(wxCommandEvent& event) { GetMenuBar()->Enable(XRCID("btnBrushCollision"), false); GetMenuBar()->Enable(XRCID("btnClearMask"), false); GetMenuBar()->Enable(XRCID("btnInvertMask"), false); - GetMenuBar()->Enable(XRCID("btnShowMask"), false); GetMenuBar()->Enable(XRCID("deleteVerts"), false); GetToolBar()->ToggleTool(XRCID("btnMaskBrush"), true); @@ -5964,7 +5975,6 @@ void OutfitStudioFrame::OnTabButtonClick(wxCommandEvent& event) { GetMenuBar()->Check(XRCID("btnMaskBrush"), true); GetMenuBar()->Check(XRCID("btnXMirror"), false); GetMenuBar()->Check(XRCID("btnBrushCollision"), false); - GetMenuBar()->Check(XRCID("btnShowMask"), false); GetMenuBar()->Enable(XRCID("btnSelect"), false); GetMenuBar()->Enable(XRCID("btnTransform"), false); GetMenuBar()->Enable(XRCID("btnPivot"), false); @@ -5976,7 +5986,6 @@ void OutfitStudioFrame::OnTabButtonClick(wxCommandEvent& event) { GetMenuBar()->Enable(XRCID("btnBrushCollision"), false); GetMenuBar()->Enable(XRCID("btnClearMask"), false); GetMenuBar()->Enable(XRCID("btnInvertMask"), false); - GetMenuBar()->Enable(XRCID("btnShowMask"), false); GetMenuBar()->Enable(XRCID("deleteVerts"), false); GetToolBar()->ToggleTool(XRCID("btnMaskBrush"), true); @@ -8513,6 +8522,76 @@ void OutfitStudioFrame::OnEditUV(wxCommandEvent& WXUNUSED(event)) { } } +void OutfitStudioFrame::OnSelectMask(wxCommandEvent& WXUNUSED(event)) { + wxChoice* cMaskName = (wxChoice*)FindWindowByName("cMaskName"); + int maskSel = cMaskName->GetSelection(); + if (maskSel != wxNOT_FOUND) { + auto maskData = (std::map>*)cMaskName->GetClientData(maskSel); + for (auto mask : (*maskData)) { + glView->SetShapeMask(mask.second, mask.first); + } + } + + glView->Render(); +} + +void OutfitStudioFrame::OnSaveMask(wxCommandEvent& WXUNUSED(event)) { + wxChoice* cMaskName = (wxChoice*)FindWindowByName("cMaskName"); + int maskSel = cMaskName->GetSelection(); + if (maskSel != wxNOT_FOUND) { + auto maskData = new std::map>(); + + std::vector shapes = GetShapeList(); + for (auto &s : shapes) { + std::unordered_map mask; + glView->GetShapeMask(mask, s); + (*maskData)[s] = std::move(mask); + } + + cMaskName->SetClientData(maskSel, maskData); + } + else { + wxCommandEvent evt; + OnSaveAsMask(evt); + } +} + +void OutfitStudioFrame::OnSaveAsMask(wxCommandEvent& WXUNUSED(event)) { + wxChoice* cMaskName = (wxChoice*)FindWindowByName("cMaskName"); + + wxString maskName; + do { + maskName = wxGetTextFromUser(_("Please enter a new unique name for the mask."), _("New Mask")); + if (maskName.empty()) + return; + + } while (cMaskName->FindString(maskName) != wxNOT_FOUND); + + auto maskData = new std::map>(); + + std::vector shapes = GetShapeList(); + for (auto &s : shapes) { + std::unordered_map mask; + glView->GetShapeMask(mask, s); + (*maskData)[s] = std::move(mask); + } + + cMaskName->Append(maskName, maskData); +} + +void OutfitStudioFrame::OnDeleteMask(wxCommandEvent& WXUNUSED(event)) { + wxChoice* cMaskName = (wxChoice*)FindWindowByName("cMaskName"); + int maskSel = cMaskName->GetSelection(); + if (maskSel != wxNOT_FOUND) { + cMaskName->Delete(maskSel); + } +} + +void OutfitStudioFrame::OnPaneCollapse(wxCollapsiblePaneEvent& WXUNUSED(event)) { + wxWindow* parentPanel = FindWindowByName("bottomSplitPanel"); + parentPanel->Layout(); +} + void OutfitStudioFrame::ApplyPose() { for (auto &shape : project->GetWorkNif()->GetShapes()) { std::vector verts; @@ -8527,11 +8606,6 @@ void OutfitStudioFrame::ApplyPose() { glView->Render(); } -void OutfitStudioFrame::OnPosePaneCollapse(wxCollapsiblePaneEvent& WXUNUSED(event)) { - wxWindow* parentPanel = FindWindowByName("bottomSplitPanel"); - parentPanel->Layout(); -} - AnimBone *OutfitStudioFrame::GetPoseBonePtr() { int selind = cPoseBone->GetSelection(); if (selind == wxNOT_FOUND) return nullptr; diff --git a/src/program/OutfitStudio.h b/src/program/OutfitStudio.h index ead213d0..5031384a 100644 --- a/src/program/OutfitStudio.h +++ b/src/program/OutfitStudio.h @@ -437,6 +437,21 @@ class wxGLPanel : public wxGLCanvas { mask[i] = m->vcolors[i].x; } + void SetShapeMask(std::unordered_map& mask, const std::string& shapeName) { + mesh* m = gls.GetMesh(shapeName); + if (!m) + return; + + for (int i = 0; i < m->nVerts; i++) { + if (mask.find(i) != mask.end()) + m->vcolors[i].x = mask[i]; + else + m->vcolors[i].x = 0.0f; + } + + m->QueueUpdate(mesh::UpdateType::VertexColors); + } + void MaskLess() { for (auto &m : gls.GetActiveMeshes()) { std::set unmaskPoints; @@ -1273,11 +1288,6 @@ class OutfitStudioFrame : public wxFrame { glView->Render(); } - void OnShowMask(wxCommandEvent& event) { - glView->SetMaskVisible(event.IsChecked()); - glView->Render(); - } - void OnIncBrush(wxCommandEvent& WXUNUSED(event)) { if (glView->GetActiveBrush() && glView->GetBrushSize() < 1.0f) { float v = glView->IncBrush() / 3.0f; @@ -1353,8 +1363,12 @@ class OutfitStudioFrame : public wxFrame { wxLaunchDefaultBrowser(url); } + void OnSelectMask(wxCommandEvent& event); + void OnSaveMask(wxCommandEvent& event); + void OnSaveAsMask(wxCommandEvent& event); + void OnDeleteMask(wxCommandEvent& event); + void OnPaneCollapse(wxCollapsiblePaneEvent& event); void ApplyPose(); - void OnPosePaneCollapse(wxCollapsiblePaneEvent &event); AnimBone *GetPoseBonePtr(); void PoseToGUI(); void OnPoseBoneChanged(wxCommandEvent& event); diff --git a/src/render/GLSurface.h b/src/render/GLSurface.h index 8c93297e..f1f79601 100644 --- a/src/render/GLSurface.h +++ b/src/render/GLSurface.h @@ -29,7 +29,7 @@ class GLSurface { bool bWireframe = false; bool bLighting = true; bool bTextured = true; - bool bMaskVisible = false; + bool bMaskVisible = true; bool bWeightColors = false; bool bVertexColors = false; bool bSegmentColors = false;