DirectX11의 Particle Editor는 DirectX11 응용 프로그램에서 사용할 입자 효과를 만들고 관리하는 데 사용되는 소프트웨어 도구이다. 그것은 개발자들이 개별 입자의 행동과 모양을 제어함으로써 연기, 화재, 물, 폭발과 같은 복잡하고 역동적인 효과를 만들 수 있게 한다.

 

목차

     

     


     

     

     

     

    Particle Editor

     

    파티클 편집기에는 일반적으로 개발자가 파티클 시스템을 시각적으로 만들고 수정할 수 있는 시각적 편집기가 포함되어 있다. 일반적으로 크기, 색상, 불투명도 및 수명과 같은 입자 속성에 대한 제어를 제공하며 개발자가 입자 시스템의 동작 및 모양을 실시간으로 조정할 수 있다. 또한 DirectX11의 Particle Editor는 개발자가 입자에 이미지를 적용할 수 있는 텍스처 매핑 및 고급 물리 시뮬레이션과 같은 기능을 제공하여 보다 현실적인 입자 효과를 생성할 수 있다.

    파티클 시스템이 생성되면 XML 또는 JSON과 같은 다양한 파일 형식으로 내보낼 수 있으며, DirectX11 응용 프로그램에서 쉽게 로드하고 사용할 수 있다. 내보낸 입자 시스템은 개발자가 고급 입자 효과를 효율적으로 구현할 수 있는 Direct3D 11 Compute Shader와 같은 DirectX11의 입자 시스템 API를 사용하여 애플리케이션에 통합할 수 있다.

     

     

    Shaders
      Particle.fx
    Framework
      Particle
      ParticleData.h
    ParticleSystem.h .cpp
    ParticleEditor
      Editor.h .cpp 생성
    Viewer.h. cpp
    Main.h .cpp
       

     

     


     

    ParticleSystem

     

    ParticleSystem.h

    더보기
    #pragma once
    class ParticleSystem : public Renderer
    {
    public:
    ParticleSystem(wstring file);
    ~ParticleSystem();
    void Reset(); //초기화 함수
    void Add(Vector3& position); //외부에서 추가
    public:
    void Update();//업데이트 함수. 외부에서 Update를 콜할때 사용
    private:
    void MapVertices();
    void Activate();
    void Deactivate();
    public:
    void Render();
    ParticleData& GetData() { return data; }
    void SetTexture(wstring file);
    private:
    void ReadFile(wstring file);
    private:
    struct VertexParticle
    {
    Vector3 Position;
    Vector2 Corner; //(-1 ~ +1)
    Vector3 Velocity; //파티클이 움직이는 속도
    Vector4 Random; //x:주기, y:크기, z:회전, w:색상
    float Time; //현재 속도
    };
    private:
    struct Desc
    {
    Color MinColor;
    Color MaxColor;
    Vector3 Gravity;
    float EndVelocity;
    Vector2 StartSize;
    Vector2 EndSize;
    Vector2 RotateSpeed;
    float ReadyTime;
    float ReadyRandomTime;
    float ColorAmount;
    float CurrentTime;
    float Padding[2];
    } desc;
    private:
    ParticleData data; //파티클 정보
    Texture* map = NULL;
    ID3DX11EffectShaderResourceVariable* sMap;
    ConstantBuffer* buffer;
    ID3DX11EffectConstantBuffer* sBuffer;
    VertexParticle* vertices = NULL;
    UINT* indices = NULL;
    float currentTime = 0.0f; //현재 시간
    float lastAddTime = 0.0f; //마지막 파티클이 추가된 시간
    UINT leadCount = 0;
    UINT gpuCount = 0;
    UINT activeCount = 0;
    UINT deactiveCount = 0;
    };

     

     

     

    ParticleSystem.cpp

    더보기
    #include "Framework.h"
    #include "ParticleSystem.h"
    #include "Utilities/Xml.h"
    ParticleSystem::ParticleSystem(wstring file)
    : Renderer(L"89_Particle.fxo")
    {
    ReadFile(L"../../_Textures/Particles/" + file + L".xml");//.xml파일을 읽는다.
    buffer = new ConstantBuffer(&desc, sizeof(Desc));
    sBuffer = shader->AsConstantBuffer("CB_Particle");
    sMap = shader->AsSRV("ParticleMap");
    Reset();
    }
    ParticleSystem::~ParticleSystem()
    {
    SafeDelete(map);
    SafeDelete(buffer);
    SafeDeleteArray(vertices);
    SafeDeleteArray(indices);
    }
    void ParticleSystem::Reset()
    {
    currentTime = 0.0f;
    lastAddTime = Time::Get()->Running();
    gpuCount = leadCount = activeCount = deactiveCount = 0;
    SafeDeleteArray(vertices);
    SafeDeleteArray(indices);
    SafeDelete(vertexBuffer);
    SafeDelete(indexBuffer);
    vertices = new VertexParticle[data.MaxParticles * 4];
    for (UINT i = 0; i < data.MaxParticles; i++)
    {
    vertices[i * 4 + 0].Corner = Vector2(-1, -1);
    vertices[i * 4 + 1].Corner = Vector2(-1, +1);
    vertices[i * 4 + 2].Corner = Vector2(+1, -1);
    vertices[i * 4 + 3].Corner = Vector2(+1, +1);
    }
    indices = new UINT[data.MaxParticles * 6];
    for (UINT i = 0; i < data.MaxParticles; i++)
    {
    indices[i * 6 + 0] = i * 4 + 0;
    indices[i * 6 + 1] = i * 4 + 1;
    indices[i * 6 + 2] = i * 4 + 2;
    indices[i * 6 + 3] = i * 4 + 2;
    indices[i * 6 + 4] = i * 4 + 1;
    indices[i * 6 + 5] = i * 4 + 3;
    }
    vertexBuffer = new VertexBuffer(vertices, data.MaxParticles * 4, sizeof(VertexParticle), 0, true); //정점 버퍼
    indexBuffer = new IndexBuffer(indices, data.MaxParticles * 6); //인덱스 버퍼
    }
    void ParticleSystem::Add(Vector3 & position)
    {
    if (Time::Get()->Running() - lastAddTime < 60.0f / 1000.0f) //60프레임 제한
    return;
    lastAddTime = Time::Get()->Running();
    UINT count = leadCount + 1; //시작해서 갈 카운트
    if (count >= data.MaxParticles)
    {
    if (data.bLoop == true)
    {
    count = 0;
    }
    else
    {
    count = data.MaxParticles;
    return;
    }
    }
    if (count == deactiveCount)
    return;
    Vector3 velocity = Vector3(1, 1, 1); //기본 속도이자 방향
    velocity *= data.StartVelocity;
    float horizontalVelocity = Math::Lerp(data.MinHorizontalVelocity, data.MaxHorizontalVelocity, Math::Random(0.0f, 1.0f));//수평이동 속도를 선형보간으로 만든다.
    float horizontalAngle = Math::PI * 2.0f * Math::Random(0.0f, 1.0f);//z방향 회전
    velocity.x += horizontalVelocity * cosf(horizontalAngle);//x속도
    velocity.y += horizontalVelocity * sinf(horizontalAngle);//y속도
    velocity.z += Math::Lerp(data.MinHorizontalVelocity, data.MaxHorizontalVelocity, Math::Random(0.0f, 1.0f));//z속도
    Vector4 random = Math::RandomVec4(0.0f, 1.0f);
    for (UINT i = 0; i < 4; i++)
    {
    vertices[leadCount * 4 + i].Position = position;
    vertices[leadCount * 4 + i].Velocity = velocity;
    vertices[leadCount * 4 + i].Random = random;
    vertices[leadCount * 4 + i].Time = currentTime;
    }
    leadCount = count;
    }
    void ParticleSystem::Update()
    {
    Super::Update();
    currentTime += Time::Delta();
    MapVertices();
    Activate();
    Deactivate();
    if (activeCount == leadCount)
    currentTime = 0.0f;
    desc.MinColor = data.MinColor;
    desc.MaxColor = data.MaxColor;
    desc.ColorAmount = data.ColorAmount;
    desc.Gravity = data.Gravity;
    desc.EndVelocity = data.EndVelocity;
    desc.RotateSpeed = Vector2(data.MinRotateSpeed, data.MaxRotateSpeed);
    desc.StartSize = Vector2(data.MinStartSize, data.MaxStartSize);
    desc.EndSize = Vector2(data.MinEndSize, data.MaxEndSize);
    desc.ReadyTime = data.ReadyTime;
    desc.ReadyRandomTime = data.ReadyRandomTime;
    }
    void ParticleSystem::MapVertices()
    {
    if (gpuCount == leadCount) return;
    D3D11_MAPPED_SUBRESOURCE subResource;
    if (leadCount > gpuCount)
    {
    D3D::GetDC()->Map(vertexBuffer->Buffer(), 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &subResource);
    {
    UINT start = gpuCount * 4;
    UINT size = (leadCount - gpuCount) * sizeof(VertexParticle) * 4;
    UINT offset = gpuCount * sizeof(VertexParticle) * 4;
    BYTE* p = (BYTE *)subResource.pData + offset;
    memcpy(p, vertices + start, size);
    }
    D3D::GetDC()->Unmap(vertexBuffer->Buffer(), 0);
    }
    else //(leadCount < gpuCount)인 경우
    {
    D3D::GetDC()->Map(vertexBuffer->Buffer(), 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &subResource);
    {
    UINT start = gpuCount * 4;
    UINT size = (data.MaxParticles - gpuCount) * sizeof(VertexParticle) * 4;
    UINT offset = gpuCount * sizeof(VertexParticle) * 4;
    BYTE* p = (BYTE *)subResource.pData + offset;
    memcpy(p, vertices + start, size);
    }
    if (leadCount > 0)
    {
    UINT size = leadCount * sizeof(VertexParticle) * 4;
    memcpy(subResource.pData, vertices, size);
    }
    D3D::GetDC()->Unmap(vertexBuffer->Buffer(), 0);
    }
    gpuCount = leadCount;
    }
    void ParticleSystem::Activate()
    {
    while (activeCount != gpuCount)
    {
    float age = currentTime - vertices[activeCount * 4].Time;
    if (age < data.ReadyTime)
    return;
    vertices[activeCount * 4].Time = currentTime;
    activeCount++;
    if (activeCount >= data.MaxParticles)
    activeCount = (data.bLoop == true) ? 0 : data.MaxParticles;
    }
    }
    void ParticleSystem::Deactivate()
    {
    while (activeCount != deactiveCount)
    {
    float age = currentTime - vertices[deactiveCount * 4].Time;
    if (age > data.ReadyTime)
    return;
    deactiveCount++;
    if (deactiveCount >= data.MaxParticles)
    deactiveCount = (data.bLoop == true) ? 0 : data.MaxParticles;
    }
    }
    void ParticleSystem::Render()
    {
    Super::Render();
    desc.CurrentTime = currentTime;
    buffer->Render();
    sBuffer->SetConstantBuffer(buffer->Buffer());
    sMap->SetResource(map->SRV());
    if (leadCount == activeCount)
    return;
    UINT pass = (UINT)data.Type;
    if (leadCount > activeCount)
    {
    shader->DrawIndexed(0, pass, (leadCount - activeCount) * 6, activeCount * 6);
    }
    else
    {
    shader->DrawIndexed(0, pass, (data.MaxParticles - activeCount) * 6, activeCount * 6);
    if (leadCount > 0)
    shader->DrawIndexed(0, pass, leadCount * 6);
    }
    }
    void ParticleSystem::SetTexture(wstring file)
    {
    SafeDelete(map);
    map = new Texture(file);
    }
    void ParticleSystem::ReadFile(wstring file)
    {
    Xml::XMLDocument* document = new Xml::XMLDocument();
    Xml::XMLError error = document->LoadFile(String::ToString(file).c_str());
    assert(error == Xml::XML_SUCCESS);
    Xml::XMLElement* root = document->FirstChildElement();
    Xml::XMLElement* node = root->FirstChildElement();
    data.Type = (ParticleData::BlendType)node->IntText();
    node = node->NextSiblingElement();
    data.bLoop = node->BoolText();
    node = node->NextSiblingElement();
    wstring textureFile = String::ToWString(node->GetText());
    data.TextureFile = L"Particles/" + textureFile;
    map = new Texture(data.TextureFile);
    node = node->NextSiblingElement();
    data.MaxParticles = node->IntText();
    node = node->NextSiblingElement();
    data.ReadyTime = node->FloatText();
    node = node->NextSiblingElement();
    data.ReadyRandomTime = node->FloatText();
    node = node->NextSiblingElement();
    data.StartVelocity = node->FloatText();
    node = node->NextSiblingElement();
    data.EndVelocity = node->FloatText();
    node = node->NextSiblingElement();
    data.MinHorizontalVelocity = node->FloatText();
    node = node->NextSiblingElement();
    data.MaxHorizontalVelocity = node->FloatText();
    node = node->NextSiblingElement();
    data.MinVerticalVelocity = node->FloatText();
    node = node->NextSiblingElement();
    data.MaxVerticalVelocity = node->FloatText();
    node = node->NextSiblingElement();
    data.Gravity.x = node->FloatAttribute("X");
    data.Gravity.y = node->FloatAttribute("Y");
    data.Gravity.z = node->FloatAttribute("Z");
    node = node->NextSiblingElement();
    data.ColorAmount = node->FloatText();
    node = node->NextSiblingElement();
    data.MinColor.r = node->FloatAttribute("R");
    data.MinColor.g = node->FloatAttribute("G");
    data.MinColor.b = node->FloatAttribute("B");
    data.MinColor.a = node->FloatAttribute("A");
    node = node->NextSiblingElement();
    data.MaxColor.r = node->FloatAttribute("R");
    data.MaxColor.g = node->FloatAttribute("G");
    data.MaxColor.b = node->FloatAttribute("B");
    data.MaxColor.a = node->FloatAttribute("A");
    node = node->NextSiblingElement();
    data.MinRotateSpeed = node->FloatText();
    node = node->NextSiblingElement();
    data.MaxRotateSpeed = node->FloatText();
    node = node->NextSiblingElement();
    data.MinStartSize = node->FloatText();
    node = node->NextSiblingElement();
    data.MaxStartSize = node->FloatText();
    node = node->NextSiblingElement();
    data.MinEndSize = node->FloatText();
    node = node->NextSiblingElement();
    data.MaxEndSize = node->FloatText();
    SafeDelete(document);
    }

     

     


     

     

    Editor

     

    Editor.h

    더보기
    #pragma once
    #include "Systems/IExecute.h"
    class Editor : public IExecute
    {
    public:
    virtual void Initialize() override;
    virtual void Ready() override {}
    virtual void Destroy() override;
    virtual void Update() override;
    virtual void PreRender() override {}
    virtual void Render() override;
    virtual void PostRender() override {}
    virtual void ResizeScreen() override {}
    private:
    void Mesh();
    private:
    void UpdateParticleList();
    void UpdateTextureList();
    void OnGUI();
    void OnGUI_List();
    void OnGUI_Settings();
    void OnGUI_Write();
    void WriteFile(wstring file);
    private:
    Shader* shader;
    CubeSky* sky;
    float windowWidth = 500; //윈도우 창 너비
    bool bLoop = false; //루프
    UINT maxParticle = 0;
    vector<wstring> particleList;
    vector<wstring> textureList; //particle 폴더 안의 textureList
    wstring file = L""; //선택한 파일명을 저장하는 변수
    ParticleSystem* particleSystem = NULL;
    Material* floor;
    Material* stone;
    MeshRender* sphere;
    MeshRender* grid;
    };

     

     

    Editor.cpp

    더보기
    #include "stdafx.h"
    #include "Editor.h"
    #include "Utilities/Xml.h"
    void Editor::Initialize()
    {
    Context::Get()->GetCamera()->RotationDegree(20, 0, 0);
    Context::Get()->GetCamera()->Position(1, 36, -85);
    ((Freedom *)Context::Get()->GetCamera())->Speed(50, 2);
    shader = new Shader(L"82_NormalMapping.fxo");
    sky = new CubeSky(L"Environment/GrassCube1024.dds");
    Mesh();
    UpdateParticleList(); //ParticleList 갱신
    UpdateTextureList(); //TextureList 갱신
    }
    void Editor::Destroy()
    {
    }
    void Editor::Update()
    {
    OnGUI();
    sky->Update();
    sphere->Update();
    grid->Update();
    Vector3 position;
    sphere->GetTransform(0)->Position(&position);
    if (Keyboard::Get()->Press('L'))
    position.x += 20 * Time::Delta();
    else if (Keyboard::Get()->Press('J'))
    position.x -= 20 * Time::Delta();
    if (Keyboard::Get()->Press('I'))
    position.z += 20 * Time::Delta();
    else if (Keyboard::Get()->Press('K'))
    position.z -= 20 * Time::Delta();
    if (Keyboard::Get()->Press('O'))
    position.y += 20 * Time::Delta();
    else if (Keyboard::Get()->Press('U'))
    position.y -= 20 * Time::Delta();
    sphere->GetTransform(0)->Position(position);
    sphere->UpdateTransforms();
    if (particleSystem != NULL)
    {
    particleSystem->Add(position);
    particleSystem->Update();
    }
    }
    void Editor::Render()
    {
    sky->Render();
    stone->Render();
    sphere->Render();
    floor->Render();
    grid->Render();
    if (particleSystem != NULL)
    particleSystem->Render();
    }
    void Editor::Mesh()
    {
    floor = new Material(shader);
    floor->DiffuseMap("Floor.png");
    floor->Specular(1, 1, 1, 20);
    floor->SpecularMap("Floor_Specular.png");
    floor->NormalMap("Floor_Normal.png");
    stone = new Material(shader);
    stone->DiffuseMap("Stones.png");
    stone->Specular(1, 1, 1, 20);
    stone->SpecularMap("Stones_Specular.png");
    stone->Emissive(0.15f, 0.15f, 0.15f, 0.3f);
    stone->NormalMap("Stones_Normal.png");
    Transform* transform = NULL;
    grid = new MeshRender(shader, new MeshGrid(5, 5));
    transform = grid->AddTransform();
    transform->Position(0, 0, 0);
    transform->Scale(12, 1, 12);
    grid->UpdateTransforms();
    sphere = new MeshRender(shader, new MeshSphere(0.5f, 20, 20));
    transform = sphere->AddTransform();
    transform->Position(0, 5, 0);
    transform->Scale(5, 5, 5);
    sphere->UpdateTransforms();
    }
    void Editor::UpdateParticleList()
    {
    particleList.clear();
    Path::GetFiles(&particleList, L"../../_Textures/Particles/", L"*.xml", false); //Particles폴더 내의 .xml 파일들을 가져온다. 이때 자식폴더는 검색하지 않는다.
    for (wstring& file : particleList)
    file = Path::GetFileNameWithoutExtension(file); //파일을 확장자 제거하여 가져온다.
    }
    void Editor::UpdateTextureList()
    {
    textureList.clear();
    vector<wstring> files;
    Path::GetFiles(&files, L"../../_Textures/Particles/", L"*.*", false);
    for (wstring file : files)
    {
    wstring ext = Path::GetExtension(file);
    transform(ext.begin(), ext.end(), ext.begin(), toupper);
    file = Path::GetFileName(file);
    if (ext == L"PNG" || ext == L"TGA" || ext == L"JPG")
    textureList.push_back(file); //png, tga, jpg이면 가져와서 넣어준다.
    }
    }
    void Editor::OnGUI()
    {
    float width = D3D::Width();
    float height = D3D::Height();
    bool bOpen = true;
    bOpen = ImGui::Begin("Particle", &bOpen);
    ImGui::SetWindowPos(ImVec2(width - windowWidth, 0));
    ImGui::SetWindowSize(ImVec2(windowWidth, height));
    {
    OnGUI_List();
    OnGUI_Settings();
    }
    ImGui::End();
    }
    void Editor::OnGUI_List()
    {
    if (ImGui::CollapsingHeader("Particle List", ImGuiTreeNodeFlags_DefaultOpen))//CollapsingHeader는 눌러서 펼치게 해주는 역할
    {
    for (UINT i = 0; i < particleList.size(); i++)
    {
    if (ImGui::Button(String::ToString(particleList[i]).c_str(), ImVec2(200, 0)))
    {
    SafeDelete(particleSystem);//파티클 지움
    file = particleList[i];//선택한 파일 저장
    particleSystem = new ParticleSystem(particleList[i]);
    bLoop = particleSystem->GetData().bLoop;
    maxParticle = particleSystem->GetData().MaxParticles;
    }
    }//for(i)
    }//ImGui::CollapsingHeader
    }
    void Editor::OnGUI_Settings()
    {
    if (particleSystem == NULL) return;
    ImGui::Spacing();
    if (ImGui::CollapsingHeader("Particle Settings", ImGuiTreeNodeFlags_DefaultOpen))
    {
    ImGui::Separator();
    ImGui::SliderInt("MaxParticles", (int *)&maxParticle, 1, 1000);
    ImGui::Checkbox("Loop", &bLoop);
    if (ImGui::Button("Apply"))
    {
    particleSystem->GetData().bLoop = bLoop;
    particleSystem->GetData().MaxParticles = maxParticle;
    particleSystem->Reset();
    }
    ImGui::Separator();
    const char* types[] = { "Opaque", "Additive", "AlphaBlend" };
    ImGui::Combo("BlendType", (int *)&particleSystem->GetData().Type, types, 3);
    ImGui::SliderFloat("ReadyTime", &particleSystem->GetData().ReadyTime, 0.1f, 10.0f);
    ImGui::SliderFloat("ReadyRandomTime", &particleSystem->GetData().ReadyRandomTime, 0.0f, 100.0f);
    ImGui::SliderFloat("StartVelocity", &particleSystem->GetData().StartVelocity, 0.0f, 10.0f);
    ImGui::SliderFloat("EndVelocity", &particleSystem->GetData().EndVelocity, -100.0f, 100.0f);
    ImGui::SliderFloat("MinHorizontalVelocity", &particleSystem->GetData().MinHorizontalVelocity, -100.0f, 100.0f);
    ImGui::SliderFloat("MaxHorizontalVelocity", &particleSystem->GetData().MaxHorizontalVelocity, -100.0f, 100.0f);
    ImGui::SliderFloat("MinVerticalVelocity", &particleSystem->GetData().MinVerticalVelocity, -100.0f, 100.0f);
    ImGui::SliderFloat("MaxVerticalVelocity", &particleSystem->GetData().MaxVerticalVelocity, -100.0f, 100.0f);
    ImGui::SliderFloat3("Gravity", particleSystem->GetData().Gravity, -100, 100);
    ImGui::SliderFloat("Color Amount", &particleSystem->GetData().ColorAmount, 0.1f, 5.0f);
    ImGui::ColorEdit4("MinColor", particleSystem->GetData().MinColor);
    ImGui::ColorEdit4("MaxColor", particleSystem->GetData().MaxColor);
    ImGui::SliderFloat("MinRotateSpeed", &particleSystem->GetData().MinRotateSpeed, -10, 10);
    ImGui::SliderFloat("MaxRotateSpeed", &particleSystem->GetData().MaxRotateSpeed, -10, 10);
    ImGui::SliderFloat("MinStartSize", &particleSystem->GetData().MinStartSize, 0, 500);
    ImGui::SliderFloat("MaxStartSize", &particleSystem->GetData().MaxStartSize, 0, 500);
    ImGui::SliderFloat("MinEndSize", &particleSystem->GetData().MinEndSize, 0, 500);
    ImGui::SliderFloat("MaxEndSize", &particleSystem->GetData().MaxEndSize, 0, 500);
    ImGui::Spacing();
    OnGUI_Write();
    ImGui::Spacing();
    ImGui::Separator();
    if (ImGui::CollapsingHeader("TextureList", ImGuiTreeNodeFlags_DefaultOpen))
    {
    for (wstring textureFile : textureList)
    {
    if (ImGui::Button(String::ToString(textureFile).c_str(), ImVec2(200, 0)))
    {
    particleSystem->GetData().TextureFile = textureFile;
    particleSystem->SetTexture(L"Particles/" + textureFile);
    }
    }//for(i)
    }
    }
    }
    void Editor::OnGUI_Write()
    {
    ImGui::Separator();
    if (ImGui::Button("WriteParticle"))
    {
    D3DDesc desc = D3D::GetDesc();
    Path::SaveFileDialog
    (
    file, L"Particle file\0*.xml", L"../../_Textures/Particles",
    bind(&Editor::WriteFile, this, placeholders::_1),
    desc.Handle
    );
    }
    }
    void Editor::WriteFile(wstring file)
    {
    Xml::XMLDocument* document = new Xml::XMLDocument();
    Xml::XMLDeclaration* decl = document->NewDeclaration();
    document->LinkEndChild(decl);
    Xml::XMLElement* root = document->NewElement("Particle");
    root->SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
    root->SetAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
    document->LinkEndChild(root);
    Xml::XMLElement* node = NULL;
    node = document->NewElement("BlendState");
    node->SetText((int)particleSystem->GetData().Type);
    root->LinkEndChild(node);
    string textureFile = String::ToString(particleSystem->GetData().TextureFile);
    String::Replace(&textureFile, "Particles/", "");
    node = document->NewElement("Loop");
    node->SetText(particleSystem->GetData().bLoop);
    root->LinkEndChild(node);
    node = document->NewElement("TextureFile");
    node->SetText(textureFile.c_str());
    root->LinkEndChild(node);
    node = document->NewElement("MaxParticles");
    node->SetText(particleSystem->GetData().MaxParticles);
    root->LinkEndChild(node);
    node = document->NewElement("ReadyTime");
    node->SetText(particleSystem->GetData().ReadyTime);
    root->LinkEndChild(node);
    node = document->NewElement("ReadyRandomTime");
    node->SetText(particleSystem->GetData().ReadyRandomTime);
    root->LinkEndChild(node);
    node = document->NewElement("StartVelocity");
    node->SetText(particleSystem->GetData().StartVelocity);
    root->LinkEndChild(node);
    node = document->NewElement("EndVelocity");
    node->SetText(particleSystem->GetData().EndVelocity);
    root->LinkEndChild(node);
    node = document->NewElement("MinHorizontalVelocity");
    node->SetText(particleSystem->GetData().MinHorizontalVelocity);
    root->LinkEndChild(node);
    node = document->NewElement("MaxHorizontalVelocity");
    node->SetText(particleSystem->GetData().MaxHorizontalVelocity);
    root->LinkEndChild(node);
    node = document->NewElement("MinVerticalVelocity");
    node->SetText(particleSystem->GetData().MinVerticalVelocity);
    root->LinkEndChild(node);
    node = document->NewElement("MaxVerticalVelocity");
    node->SetText(particleSystem->GetData().MaxVerticalVelocity);
    root->LinkEndChild(node);
    node = document->NewElement("Gravity");
    node->SetAttribute("X", particleSystem->GetData().Gravity.x);
    node->SetAttribute("Y", particleSystem->GetData().Gravity.y);
    node->SetAttribute("Z", particleSystem->GetData().Gravity.z);
    root->LinkEndChild(node);
    node = document->NewElement("ColorAmount");
    node->SetText(particleSystem->GetData().ColorAmount);
    root->LinkEndChild(node);
    node = document->NewElement("MinColor");
    node->SetAttribute("R", particleSystem->GetData().MinColor.r);
    node->SetAttribute("G", particleSystem->GetData().MinColor.g);
    node->SetAttribute("B", particleSystem->GetData().MinColor.b);
    node->SetAttribute("A", particleSystem->GetData().MinColor.a);
    root->LinkEndChild(node);
    node = document->NewElement("MaxColor");
    node->SetAttribute("R", particleSystem->GetData().MaxColor.r);
    node->SetAttribute("G", particleSystem->GetData().MaxColor.g);
    node->SetAttribute("B", particleSystem->GetData().MaxColor.b);
    node->SetAttribute("A", particleSystem->GetData().MaxColor.a);
    root->LinkEndChild(node);
    node = document->NewElement("MinRotateSpeed");
    node->SetText(particleSystem->GetData().MinRotateSpeed);
    root->LinkEndChild(node);
    node = document->NewElement("MaxRotateSpeed");
    node->SetText(particleSystem->GetData().MaxRotateSpeed);
    root->LinkEndChild(node);
    node = document->NewElement("MinStartSize");
    node->SetText((int)particleSystem->GetData().MinStartSize);
    root->LinkEndChild(node);
    node = document->NewElement("MaxStartSize");
    node->SetText((int)particleSystem->GetData().MaxStartSize);
    root->LinkEndChild(node);
    node = document->NewElement("MinEndSize");
    node->SetText((int)particleSystem->GetData().MinEndSize);
    root->LinkEndChild(node);
    node = document->NewElement("MaxEndSize");
    node->SetText((int)particleSystem->GetData().MaxEndSize);
    root->LinkEndChild(node);
    wstring folder = Path::GetDirectoryName(file);
    wstring fileName = Path::GetFileNameWithoutExtension(file);
    document->SaveFile(String::ToString(folder + fileName + L".xml").c_str());
    SafeDelete(document);
    UpdateParticleList();
    }

     


     

     

    실행화면