diff --git a/res/xrc/OutfitStudio.xrc b/res/xrc/OutfitStudio.xrc index 5684617f..37296673 100644 --- a/res/xrc/OutfitStudio.xrc +++ b/res/xrc/OutfitStudio.xrc @@ -1066,6 +1066,10 @@ To FBX... Export the current project as an FBX file. + + To TRI (Head)... + Export head morphs to a TRI file. + Export Data diff --git a/src/program/OutfitStudio.cpp b/src/program/OutfitStudio.cpp index 0aed9249..1a559a25 100644 --- a/src/program/OutfitStudio.cpp +++ b/src/program/OutfitStudio.cpp @@ -72,7 +72,8 @@ wxBEGIN_EVENT_TABLE(OutfitStudioFrame, wxFrame) EVT_MENU(XRCID("exportShapeFBX"), OutfitStudioFrame::OnExportShapeFBX) EVT_MENU(XRCID("importTRIHead"), OutfitStudioFrame::OnImportTRIHead) - EVT_MENU(XRCID("exportShapeTRIHead"), OutfitStudioFrame::OnExportTRIHead) + EVT_MENU(XRCID("exportTRIHead"), OutfitStudioFrame::OnExportTRIHead) + EVT_MENU(XRCID("exportShapeTRIHead"), OutfitStudioFrame::OnExportShapeTRIHead) EVT_MENU(XRCID("importPhysicsData"), OutfitStudioFrame::OnImportPhysicsData) EVT_MENU(XRCID("exportPhysicsData"), OutfitStudioFrame::OnExportPhysicsData) @@ -2853,18 +2854,12 @@ void OutfitStudioFrame::OnExportShapeFBX(wxCommandEvent& WXUNUSED(event)) { } void OutfitStudioFrame::OnImportTRIHead(wxCommandEvent& WXUNUSED(event)) { - wxString fn = wxFileSelector(_("Import .tri morphs"), wxEmptyString, wxEmptyString, ".tri", "*.tri", wxFD_FILE_MUST_EXIST, this); - if (fn.IsEmpty()) + wxFileDialog importDialog(this, _("Import .tri morphs"), wxEmptyString, wxEmptyString, "TRI (Head) Files (*.tri)|*.tri", wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE); + if (importDialog.ShowModal() == wxID_CANCEL) return; - wxLogMessage("Importing morphs from TRI (head) file '%s'...", fn); - - TriHeadFile tri; - if (!tri.Read(fn.ToUTF8().data())) { - wxLogError("Failed to load TRI file '%s'!", fn); - wxMessageBox(_("Failed to load TRI file!"), _("Error"), wxICON_ERROR); - return; - } + wxArrayString fileNames; + importDialog.GetPaths(fileNames); sliderScroll->Freeze(); glView->SetStrokeManager(nullptr); @@ -2872,32 +2867,52 @@ void OutfitStudioFrame::OnImportTRIHead(wxCommandEvent& WXUNUSED(event)) { sliderScroll->FitInside(); activeSlider.clear(); - std::string shapeName = wxGetTextFromUser(_("Please specify a name for the new shape"), _("New Shape Name"), wxEmptyString, this).ToUTF8(); - if (shapeName.empty()) - return; + for (auto &fn : fileNames) { + wxFileName fileName(fn); + wxLogMessage("Importing morphs from TRI (head) file '%s'...", fn); - auto shape = project->CreateNifShapeFromData(shapeName, tri.GetVertices(), tri.GetTriangles(), tri.GetUV()); - if (!shape) - return; + TriHeadFile tri; + if (!tri.Read(fn.ToUTF8().data())) { + wxLogError("Failed to load TRI file '%s'!", fn); + wxMessageBox(_("Failed to load TRI file!"), _("Error"), wxICON_ERROR); + return; + } - RefreshGUIFromProj(); + std::string shapeName = fileName.GetName().ToUTF8(); + while (project->IsValidShape(shapeName)) { + std::string result = wxGetTextFromUser(_("Please enter a new unique name for the shape."), _("Rename Shape"), shapeName, this).ToUTF8(); + if (result.empty()) + continue; - auto morphs = tri.GetMorphs(); - for (auto &morph : morphs) { - morph.morphName = morph.morphName.c_str(); - if (!project->ValidSlider(morph.morphName)) { - createSliderGUI(morph.morphName, project->SliderCount(), sliderScroll, sliderScroll->GetSizer()); - project->AddEmptySlider(morph.morphName); - ShowSliderEffect(morph.morphName); + shapeName = std::move(result); } - std::unordered_map diff; - diff.reserve(morph.vertices.size()); + auto verts = tri.GetVertices(); + auto tris = tri.GetTriangles(); + auto uvs = tri.GetUV(); + auto shape = project->CreateNifShapeFromData(shapeName, verts, tris, uvs); + if (!shape) + return; + + RefreshGUIFromProj(); + + auto morphs = tri.GetMorphs(); + for (auto &morph : morphs) { + morph.morphName = morph.morphName.c_str(); + if (!project->ValidSlider(morph.morphName)) { + createSliderGUI(morph.morphName, project->SliderCount(), sliderScroll, sliderScroll->GetSizer()); + project->AddEmptySlider(morph.morphName); + ShowSliderEffect(morph.morphName); + } - for (int i = 0; i < morph.vertices.size(); i++) - diff[i] = morph.vertices[i]; + std::unordered_map diff; + diff.reserve(morph.vertices.size()); - project->SetSliderFromDiff(morph.morphName, shape, diff); + for (int i = 0; i < morph.vertices.size(); i++) + diff[i] = morph.vertices[i]; + + project->SetSliderFromDiff(morph.morphName, shape, diff); + } } sliderScroll->FitInside(); @@ -2917,6 +2932,32 @@ void OutfitStudioFrame::OnExportTRIHead(wxCommandEvent& WXUNUSED(event)) { return; } + wxString dir = wxDirSelector(_("Export .tri morphs"), wxEmptyString, wxDD_DEFAULT_STYLE, wxDefaultPosition, this); + if (dir.IsEmpty()) + return; + + for (auto &shape : project->GetWorkNif()->GetShapes()) { + std::string fn = dir + "/" + shape->GetName() + ".tri"; + + wxLogMessage("Exporting TRI (head) morphs of '%s' to '%s'...", shape->GetName(), fn); + if (!project->WriteHeadTRI(shape, fn)) { + wxLogError("Failed to export TRI file to '%s'!", fn); + wxMessageBox(_("Failed to export TRI file!"), _("Error"), wxICON_ERROR); + } + } +} + +void OutfitStudioFrame::OnExportShapeTRIHead(wxCommandEvent& WXUNUSED(event)) { + if (!project->GetWorkNif()->IsValid()) { + wxMessageBox(_("There are no valid shapes loaded!"), _("Error")); + return; + } + + if (!activeItem) { + wxMessageBox(_("There is no shape selected!"), _("Error")); + return; + } + wxString fn = wxFileSelector(_("Export .tri morphs"), wxEmptyString, wxEmptyString, ".tri", "*.tri", wxFD_SAVE | wxFD_OVERWRITE_PROMPT, this); if (fn.IsEmpty()) return; @@ -2925,7 +2966,6 @@ void OutfitStudioFrame::OnExportTRIHead(wxCommandEvent& WXUNUSED(event)) { if (!project->WriteHeadTRI(activeItem->GetShape(), fn.ToUTF8().data())) { wxLogError("Failed to export TRI file to '%s'!", fn); wxMessageBox(_("Failed to export TRI file!"), _("Error"), wxICON_ERROR); - return; } } diff --git a/src/program/OutfitStudio.h b/src/program/OutfitStudio.h index 58a83a8b..9fd5e534 100644 --- a/src/program/OutfitStudio.h +++ b/src/program/OutfitStudio.h @@ -1018,6 +1018,7 @@ class OutfitStudioFrame : public wxFrame { void OnImportTRIHead(wxCommandEvent& event); void OnExportTRIHead(wxCommandEvent& event); + void OnExportShapeTRIHead(wxCommandEvent& event); void OnImportPhysicsData(wxCommandEvent &event); void OnExportPhysicsData(wxCommandEvent &event);