Skip to content

Commit

Permalink
Save named poses to UI and XML files #345
Browse files Browse the repository at this point in the history
  • Loading branch information
ousnius committed Jul 16, 2021
1 parent 8436a81 commit a5c8ca7
Show file tree
Hide file tree
Showing 8 changed files with 511 additions and 3 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ set(commonsources
)
set(OSsources
${commonsources}
src/components/PoseData.cpp
src/components/RefTemplates.cpp
src/components/TweakBrush.cpp
src/components/UndoHistory.cpp
Expand Down
2 changes: 2 additions & 0 deletions OutfitStudio.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,7 @@
<ClInclude Include="src\components\DiffData.h" />
<ClInclude Include="src\components\Mesh.h" />
<ClInclude Include="src\components\NormalGenLayers.h" />
<ClInclude Include="src\components\PoseData.h" />
<ClInclude Include="src\components\RefTemplates.h" />
<ClInclude Include="src\components\SliderCategories.h" />
<ClInclude Include="src\components\SliderData.h" />
Expand Down Expand Up @@ -798,6 +799,7 @@
<ClCompile Include="src\components\DiffData.cpp" />
<ClCompile Include="src\components\Mesh.cpp" />
<ClCompile Include="src\components\NormalGenLayers.cpp" />
<ClCompile Include="src\components\PoseData.cpp" />
<ClCompile Include="src\components\RefTemplates.cpp" />
<ClCompile Include="src\components\SliderCategories.cpp" />
<ClCompile Include="src\components\SliderData.cpp" />
Expand Down
6 changes: 6 additions & 0 deletions OutfitStudio.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,9 @@
<ClInclude Include="src\components\RefTemplates.h">
<Filter>Components</Filter>
</ClInclude>
<ClInclude Include="src\components\PoseData.h">
<Filter>Components</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="lib\TinyXML-2\tinyxml2.cpp">
Expand Down Expand Up @@ -1941,6 +1944,9 @@
<ClCompile Include="lib\nifly\src\Skin.cpp">
<Filter>Libraries\nifly\src</Filter>
</ClCompile>
<ClCompile Include="src\components\PoseData.cpp">
<Filter>Components</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Xml Include="Config.xml">
Expand Down
43 changes: 43 additions & 0 deletions res/xrc/OutfitStudio.xrc
Original file line number Diff line number Diff line change
Expand Up @@ -1362,6 +1362,49 @@
<object class="panewindow">
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<option>0</option>
<flag>wxLEFT|wxRIGHT|wxEXPAND</flag>
<object class="wxBoxSizer">
<orient>wxHORIZONTAL</orient>
<object class="sizeritem">
<option>1</option>
<flag>wxLEFT|wxRIGHT|wxBOTTOM|wxEXPAND</flag>
<border>5</border>
<object class="wxChoice" name="cPoseName">
<selection>0</selection>
<content />
</object>
</object>
<object class="sizeritem">
<option>0</option>
<flag>wxBOTTOM|wxEXPAND</flag>
<border>5</border>
<object class="wxButton" name="savePose">
<size>-1,25</size>
<label>Save</label>
</object>
</object>
<object class="sizeritem">
<option>0</option>
<flag>wxBOTTOM|wxEXPAND</flag>
<border>5</border>
<object class="wxButton" name="saveAsPose">
<size>-1,25</size>
<label>Save As...</label>
</object>
</object>
<object class="sizeritem">
<option>0</option>
<flag>wxRIGHT|wxBOTTOM|wxEXPAND</flag>
<border>5</border>
<object class="wxButton" name="deletePose">
<size>-1,25</size>
<label>Delete</label>
</object>
</object>
</object>
</object>
<object class="sizeritem">
<option>0</option>
<flag>wxLEFT|wxRIGHT|wxEXPAND</flag>
Expand Down
194 changes: 194 additions & 0 deletions src/components/PoseData.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
BodySlide and Outfit Studio
See the included LICENSE file
*/

#include "PoseData.h"
#include "../utils/PlatformUtil.h"

bool PoseData::LoadElement(XMLElement* srcElement) {
if (srcElement == nullptr)
return false;

name = srcElement->Attribute("name");

XMLElement* boneElement = srcElement->FirstChildElement("Bone");
while (boneElement) {
PoseBoneData poseBoneData{};
poseBoneData.name = boneElement->Attribute("name");
poseBoneData.rotation.x = boneElement->FloatAttribute("rotX");
poseBoneData.rotation.y = boneElement->FloatAttribute("rotY");
poseBoneData.rotation.z = boneElement->FloatAttribute("rotZ");
poseBoneData.translation.x = boneElement->FloatAttribute("transX");
poseBoneData.translation.y = boneElement->FloatAttribute("transY");
poseBoneData.translation.z = boneElement->FloatAttribute("transZ");
boneData.push_back(poseBoneData);

boneElement = boneElement->NextSiblingElement("Bone");
}

return true;
}

void PoseData::WriteElement(XMLElement* element, bool append) const {
if (!append)
element->DeleteChildren();

for (auto &bone : boneData) {
XMLElement* newElement = element->GetDocument()->NewElement("Bone");
newElement = element->InsertEndChild(newElement)->ToElement();
newElement->SetAttribute("name", bone.name.c_str());
newElement->SetAttribute("rotX", bone.rotation.x);
newElement->SetAttribute("rotY", bone.rotation.y);
newElement->SetAttribute("rotZ", bone.rotation.z);
newElement->SetAttribute("transX", bone.translation.x);
newElement->SetAttribute("transY", bone.translation.y);
newElement->SetAttribute("transZ", bone.translation.z);
}
}

int PoseDataCollection::LoadData(const std::string& basePath) {
poseData.clear();

wxArrayString files;
wxDir::GetAllFiles(basePath, &files, "*.xml");

for (auto &file : files) {
PoseDataFile poseDataFile(file.ToUTF8().data());

PoseData poseDataEntry;
poseDataFile.GetData(poseDataEntry);

poseData.push_back(poseDataEntry);
}

return 0;
}


PoseDataFile::PoseDataFile(const std::string& srcFileName) {
root = nullptr;
error = 0;
Open(srcFileName);
}

void PoseDataFile::Open(const std::string& srcFileName) {
fileName = srcFileName;

FILE* fp = nullptr;

#ifdef _WINDOWS
std::wstring winFileName = PlatformUtil::MultiByteToWideUTF8(srcFileName);
error = _wfopen_s(&fp, winFileName.c_str(), L"rb");
if (error)
return;
#else
fp = fopen(srcFileName.c_str(), "rb");
if (!fp) {
error = errno;
return;
}
#endif

error = doc.LoadFile(fp);
fclose(fp);

if (error)
return;

doc.SetUserData(&fileName);
root = doc.FirstChildElement("PoseData");
if (!root) {
error = 2;
return;
}

XMLElement* element = root->FirstChildElement("Pose");
while (element) {
poseElement = element;
break;
}

if (!poseElement) {
error = 3;
return;
}

error = 0;
}

void PoseDataFile::New(const std::string& newFileName) {
if (root)
return;

Clear();

XMLElement* newElement = doc.NewElement("PoseData");
root = doc.InsertEndChild(newElement)->ToElement();

fileName = newFileName;
doc.SetUserData(&fileName);

error = 0;
}

void PoseDataFile::Clear() {
doc.Clear();
root = nullptr;
poseElement = nullptr;
error = 0;
}

void PoseDataFile::Rename(const std::string& newFileName) {
fileName = newFileName;
}

int PoseDataFile::SetData(const PoseData& data) {
if (poseElement) {
data.WriteElement(poseElement);
}
else {
XMLElement* newElement = doc.NewElement("Pose");
XMLElement* element = root->InsertEndChild(newElement)->ToElement();
element->SetAttribute("name", data.name.c_str());
data.WriteElement(element);
poseElement = element;
}

return 0;
}

bool PoseDataFile::Save() {
FILE* fp = nullptr;

#ifdef _WINDOWS
std::wstring winFileName = PlatformUtil::MultiByteToWideUTF8(fileName);
error = _wfopen_s(&fp, winFileName.c_str(), L"w");
if (error)
return false;
#else
fp = fopen(fileName.c_str(), "w");
if (!fp) {
error = errno;
return false;
}
#endif

doc.SetBOM(true);

const tinyxml2::XMLNode* firstChild = doc.FirstChild();
if (!firstChild || !firstChild->ToDeclaration())
doc.InsertFirstChild(doc.NewDeclaration());

error = doc.SaveFile(fp);
fclose(fp);
if (error)
return false;

return true;
}

int PoseDataFile::GetData(PoseData& outData) {
outData.LoadElement(poseElement);
return 0;
}
98 changes: 98 additions & 0 deletions src/components/PoseData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
BodySlide and Outfit Studio
See the included LICENSE file
*/

#pragma once

#include "Object3d.hpp"
#include "../TinyXML-2/tinyxml2.h"

#include <wx/dir.h>
#include <vector>
#include <map>
#include <set>
#include <unordered_set>

using namespace tinyxml2;

struct PoseBoneData {
std::string name;
nifly::Vector3 rotation;
nifly::Vector3 translation;
};

class PoseData {
public:
std::string name;
std::vector<PoseBoneData> boneData;

PoseData() {}

PoseData(const std::string& name) {
this->name = name;
}

PoseData(const std::string& name, const std::vector<PoseBoneData>& boneData) {
this->name = name;
this->boneData = boneData;
}

PoseData(XMLElement* srcElement) {
LoadElement(srcElement);
}

bool LoadElement(XMLElement* srcElement);
void WriteElement(XMLElement* element, bool append = false) const;
};

class PoseDataCollection {
public:
std::vector<PoseData> poseData;

// Loads all pose data in the specified folder.
int LoadData(const std::string& basePath);
};

class PoseDataFile {
XMLDocument doc;
XMLElement* root = nullptr;
XMLElement* poseElement = nullptr;
int error = 0;

public:
std::string fileName;

PoseDataFile() { }
PoseDataFile(const std::string& srcFileName);
~PoseDataFile() { }

bool fail() {
return error != 0;
}
int GetError() {
return error;
}

// Loads the XML document and identifies included pose. On a failure, sets the internal error value.
void Open(const std::string& srcFileName);

// Creates a new empty pose document structure, ready to add new pose to.
void New(const std::string& newFileName);

// Clears all data of the file.
void Clear();

// Changes the internal file name. The XML file isn't saved until the Save() function is used.
// Note the original file name is not changed. This method allows you to save a pose as a new file without altering the original.
void Rename(const std::string& newFileName);

// Updates data in the XML document with the provided information.
int SetData(const PoseData& data);

// Writes the xml file using the internal fileName (use Rename() to change the name).
bool Save();

// Reads data of the pose element.
int GetData(PoseData& outData);
};
Loading

0 comments on commit a5c8ca7

Please sign in to comment.