프로그래밍 공부
작성일
2023. 10. 26. 15:26
작성자
WDmil
728x90

머티리얼을 조정하면서 조정된 데이터를 TinyXML2로 저장하고, 로드해보자.


TinyXml2는 데이터를 Xml형태로 내보내거나, Xml형태의 데이터를 읽어오는 C++의 라이브러리이다.

 

라이브러리리의 설치는 엄청 간단한데,

 

https://github.com/leethomason/tinyxml2

 

GitHub - leethomason/tinyxml2: TinyXML2 is a simple, small, efficient, C++ XML parser that can be easily integrated into other p

TinyXML2 is a simple, small, efficient, C++ XML parser that can be easily integrated into other programs. - GitHub - leethomason/tinyxml2: TinyXML2 is a simple, small, efficient, C++ XML parser tha...

github.com

위 GitHub링크에 들어가서 TinyXml2 를 받아서. 헤더와 cpp파일을 선언해서 그대로 사용하면 된다.

위처럼 때려박아 쓴다.

이제 Material을 저장하고 불러오는 함수를 작성해보자.

#pragma once

class Material
{
private:
    enum MapType
    {
        DIFFUSE, SPECULAR, NORMAL
    };

public:
    Material(wstring shaderFile);
    ~Material();

    void GUIRender();

    void Set();

    void SetShader(wstring shaderFile);

    void SetDiffuseMap(wstring textureFile);
    void SetSpecularMap(wstring textureFile);
    void SetNormalMap(wstring textureFile);

    void Save(string file);
    void Load(string file);

    MaterialBuffer::Data* GetBuffer() { return buffer->GetData(); }

    string GetName() { return name; }
    void SetName(string name) { this->name = name; }

private:
    void SelectShader();
    void SelectMap(string name, MapType mapType);
    void UnselectMap(MapType mapType);

    void SaveDialog();
    void LoadDialog();

private:
    string name;
    string editName;
    string file;
    string shaderFile;

    VertexShader* vertexShader;
    PixelShader* pixelShader;

    Texture* diffuseMap = nullptr;
    Texture* specularMap = nullptr;
    Texture* normalMap = nullptr;

    MaterialBuffer* buffer;
};

원래 사용하던 Material에서 Save, Load, SaveDialog, LoadDialog를 추가하고,

저장할 때 사용하는 변수인 이름을 저장할 file이라는 변수를 추가한다.

void Material::Save(string file)
{
    tinyxml2::XMLDocument* document = new tinyxml2::XMLDocument();
    tinyxml2::XMLElement* material = document->NewElement("Material");
    material->SetAttribute("Name", name.c_str());
    material->SetAttribute("Shader", shaderFile.c_str());
    document->InsertFirstChild(material);

    tinyxml2::XMLElement* texture = document->NewElement("Texture");
    tinyxml2::XMLElement* map = document->NewElement("DiffsueMap");
    string textureFile = ToString(diffuseMap->GetFile());
    map->SetAttribute("DM", textureFile.c_str());
    texture->InsertEndChild(map);
    map = document->NewElement("SpecularMap");
    textureFile = ToString(specularMap->GetFile());
    map->SetAttribute("SM", textureFile.c_str());
    texture->InsertEndChild(map);
    map = document->NewElement("NormalMap");
    textureFile = ToString(normalMap->GetFile());
    map->SetAttribute("NM", textureFile.c_str());
    texture->InsertEndChild(map);
    material->InsertEndChild(texture);

    tinyxml2::XMLElement* property = document->NewElement("Property");
    tinyxml2::XMLElement* diffuse = document->NewElement("Diffuse");
    diffuse->SetAttribute("R", buffer->GetData()->diffuse.x);
    diffuse->SetAttribute("G", buffer->GetData()->diffuse.y);
    diffuse->SetAttribute("B", buffer->GetData()->diffuse.z);
    diffuse->SetAttribute("A", buffer->GetData()->diffuse.w);
    property->InsertEndChild(diffuse);

    tinyxml2::XMLElement* specular = document->NewElement("Specular");
    specular->SetAttribute("R", buffer->GetData()->specular.x);
    specular->SetAttribute("G", buffer->GetData()->specular.y);
    specular->SetAttribute("B", buffer->GetData()->specular.z);
    specular->SetAttribute("A", buffer->GetData()->specular.w);
    property->InsertEndChild(specular);

    tinyxml2::XMLElement* ambient = document->NewElement("Ambient");
    ambient->SetAttribute("R", buffer->GetData()->ambient.x);
    ambient->SetAttribute("G", buffer->GetData()->ambient.y);
    ambient->SetAttribute("B", buffer->GetData()->ambient.z);
    ambient->SetAttribute("A", buffer->GetData()->ambient.w);
    property->InsertEndChild(ambient);

    tinyxml2::XMLElement* emissive = document->NewElement("Emissive");
    emissive->SetAttribute("R", buffer->GetData()->emissive.x);
    emissive->SetAttribute("G", buffer->GetData()->emissive.y);
    emissive->SetAttribute("B", buffer->GetData()->emissive.z);
    emissive->SetAttribute("A", buffer->GetData()->emissive.w);
    property->InsertEndChild(emissive);

    property->SetAttribute("Shininess", buffer->GetData()->shininess);
    property->SetAttribute("HasNormalMap", buffer->GetData()->hasNormalMap);

    material->InsertEndChild(property);

    document->SaveFile(file.c_str());
    delete document;
}

tinyXml를 사용하여 데이터를 저장하는 방식을 지정한다.

 

Save에 지정된 경로에 현재 Material데이터를 저장하는 함수이다.

 

TinyXml의 사용법은 Git사이트의 ReadMe파일 참조.


void Material::Load(string file)
{
    this->file = file;

    tinyxml2::XMLDocument* document = new tinyxml2::XMLDocument();
    document->LoadFile(file.c_str());
    tinyxml2::XMLElement* material = document->FirstChildElement();
    name = material->Attribute("Name");
    shaderFile = material->Attribute("Shader");
    SetShader(ToWString(shaderFile));
    
    tinyxml2::XMLElement* texture = material->FirstChildElement();
    tinyxml2::XMLElement* dm = texture->FirstChildElement();
    string textureFile = dm->Attribute("DM");
    SetDiffuseMap(ToWString(textureFile));
    tinyxml2::XMLElement* sm = dm->NextSiblingElement();
    textureFile = sm->Attribute("SM");
    SetSpecularMap(ToWString(textureFile));
    tinyxml2::XMLElement* nm = sm->NextSiblingElement();
    textureFile = nm->Attribute("NM");
    SetNormalMap(ToWString(textureFile));

    tinyxml2::XMLElement* property = texture->NextSiblingElement();
    buffer->GetData()->shininess = property->FloatAttribute("Shininess");
    buffer->GetData()->hasNormalMap = property->IntAttribute("HasNormalMap");

    tinyxml2::XMLElement* diffuse = property->FirstChildElement();
    buffer->GetData()->diffuse.x = diffuse->FloatAttribute("R");
    buffer->GetData()->diffuse.y = diffuse->FloatAttribute("G");
    buffer->GetData()->diffuse.z = diffuse->FloatAttribute("B");
    buffer->GetData()->diffuse.w = diffuse->FloatAttribute("A");

    tinyxml2::XMLElement* specular = diffuse->NextSiblingElement();
    buffer->GetData()->specular.x = specular->FloatAttribute("R");
    buffer->GetData()->specular.y = specular->FloatAttribute("G");
    buffer->GetData()->specular.z = specular->FloatAttribute("B");
    buffer->GetData()->specular.w = specular->FloatAttribute("A");

    tinyxml2::XMLElement* ambient = specular->NextSiblingElement();
    buffer->GetData()->ambient.x = ambient->FloatAttribute("R");
    buffer->GetData()->ambient.y = ambient->FloatAttribute("G");
    buffer->GetData()->ambient.z = ambient->FloatAttribute("B");
    buffer->GetData()->ambient.w = ambient->FloatAttribute("A");

    tinyxml2::XMLElement* emissive = ambient->NextSiblingElement();
    buffer->GetData()->emissive.x = emissive->FloatAttribute("R");
    buffer->GetData()->emissive.y = emissive->FloatAttribute("G");
    buffer->GetData()->emissive.z = emissive->FloatAttribute("B");
    buffer->GetData()->emissive.w = emissive->FloatAttribute("A");

    delete document;
}

데이터를 Load하는 함수이다.

 

지정된 file경로의 파일을 열어서

순서대로 지정된 이름의 항목들을 가져오는 함수이다.

 

데이터를 가져와서 지정된 데이터를 원하는 항목에 기입하는 형태의 함수이다.


void Material::SaveDialog()
{
    string key = "Save";

    if (ImGui::Button(key.c_str()))
    {
        if (file.empty())
            Save("TextData/Materials/" + name + ".mat");
        else
            Save(file);
    }

    ImGui::SameLine();

    key = "SaveAs";

    if(ImGui::Button(key.c_str()))
        DIALOG->OpenDialog(key.c_str(), key.c_str(), ".mat", ".");

    if (DIALOG->Display(key.c_str()))
    {
        if (DIALOG->IsOk())
        {
            string file = DIALOG->GetFilePathName();            

            Save(file);
        }

        DIALOG->Close();
    }
}

void Material::LoadDialog()
{
    string key = "Load";

    if (ImGui::Button(key.c_str()))
        DIALOG->OpenDialog(key.c_str(), key.c_str(), ".mat", ".");

    if (DIALOG->Display(key.c_str()))
    {
        if (DIALOG->IsOk())
        {
            string file = DIALOG->GetFilePathName();

            Load(file);
        }

        DIALOG->Close();
    }
}

Save와 Load Dialog이다.

 

각각 ImGui의 OpenDisplay함수를 사용하여 원하는 데이터를 지정하고 저장할 수 있게 하였다.

void Material::GUIRender()
{
    string title = name + "_Material";

    if (ImGui::TreeNode(title.c_str()))
    {
        char str[128];
        strcpy_s(str, 128, editName.c_str());        
        ImGui::InputText("Name", str, 128);
        editName = str;

        ImGui::SameLine();
        if (ImGui::Button("Edit"))
            name = editName;

        SelectShader();
        ImGui::Text(shaderFile.c_str());

        ImGui::ColorEdit3("Diffuse", (float*)&buffer->GetData()->diffuse);
        ImGui::ColorEdit3("Specular", (float*)&buffer->GetData()->specular);
        ImGui::ColorEdit3("Ambient", (float*)&buffer->GetData()->ambient);
        ImGui::ColorEdit3("Emissive", (float*)&buffer->GetData()->emissive);

        ImGui::SliderFloat("Shininess", (float*)&buffer->GetData()->shininess, 1, 50);

        SelectMap("DM", DIFFUSE);
        ImGui::SameLine();
        UnselectMap(DIFFUSE);

        SelectMap("SM", SPECULAR);
        ImGui::SameLine();
        UnselectMap(SPECULAR);

        SelectMap("NM", NORMAL);
        ImGui::SameLine();
        UnselectMap(NORMAL);

        SaveDialog();
        LoadDialog();

        ImGui::TreePop();
    }
}

그 이후에 GUIRneder()의 항목 안에 집어넣으면 끝난다.

요렇게 사용하면 끝.

728x90