Added a simple scene system

This commit is contained in:
OusmBlueNinja 2025-04-01 19:19:53 -05:00
parent 557f6b2386
commit 073288fc63
17 changed files with 410 additions and 82 deletions

View File

@ -12,6 +12,8 @@
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include "stb_image.h" #include "stb_image.h"
#include <fstream>
#include <yaml-cpp/yaml.h>
using std::vector; using std::vector;
@ -46,7 +48,6 @@ bool LoadIconTexture(const char* filepath, GLuint &texOut, ImTextureID &imTexOut
return true; return true;
} }
// Editor camera. // Editor camera.
struct EditorCamera { struct EditorCamera {
glm::vec3 position = glm::vec3(0.0f, 0.0f, 8.0f); glm::vec3 position = glm::vec3(0.0f, 0.0f, 8.0f);
@ -166,8 +167,6 @@ int main() {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame(); ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame(); ImGui::NewFrame();
@ -213,6 +212,59 @@ int main() {
} }
ImGui::End(); ImGui::End();
// New Panel: File Operations (Save/Load).
ImGui::Begin("Scene File");
if (ImGui::Button("Save")) {
YAML::Emitter out;
out << YAML::BeginMap;
// Save an array of entities.
out << YAML::Key << "entities" << YAML::Value << YAML::BeginSeq;
for (auto e : entities) {
out << YAML::BeginMap;
// Save the type.
out << YAML::Key << "type" << YAML::Value << (e->GetType() == EntityType::CUBE ? "cube" : "light");
// Save transform.
out << YAML::Key << "transform" << YAML::Value << e->transform.Save();
// Save model component if cube.
if (e->GetType() == EntityType::CUBE && e->modelComponent) {
out << YAML::Key << "model" << YAML::Value << e->modelComponent->Save();
}
// Save light component if light.
if (e->GetType() == EntityType::LIGHT && e->lightComponent) {
out << YAML::Key << "light" << YAML::Value << e->lightComponent->Save();
}
out << YAML::EndMap;
}
out << YAML::EndSeq;
out << YAML::EndMap;
std::ofstream fout("default.yaml");
fout << out.c_str();
fout.close();
}
if (ImGui::Button("Load")) {
YAML::Node node = YAML::LoadFile("default.yaml");
if (node["entities"]) {
YAML::Node entitiesNode = node["entities"];
int idx = 0;
for (auto entityNode : entitiesNode) {
if (idx < entities.size()) {
// Load transform.
if (entityNode["transform"])
entities[idx]->transform.Load(entityNode["transform"]);
// Load model for cubes.
if (entities[idx]->GetType() == EntityType::CUBE && entityNode["model"])
entities[idx]->modelComponent->Load(entityNode["model"]);
// Load light for lights.
if (entities[idx]->GetType() == EntityType::LIGHT && entityNode["light"])
entities[idx]->lightComponent->Load(entityNode["light"]);
}
idx++;
}
}
}
ImGui::End();
// Bottom Panel: Rendered Output. // Bottom Panel: Rendered Output.
ImGui::Begin("Rendered Output"); ImGui::Begin("Rendered Output");
ImVec2 viewportSize = ImGui::GetContentRegionAvail(); ImVec2 viewportSize = ImGui::GetContentRegionAvail();

View File

@ -4,6 +4,7 @@
#include "Component.h" #include "Component.h"
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <yaml-cpp/yaml.h>
// A simple Camera component that inherits from Component. // A simple Camera component that inherits from Component.
class Camera : public Component { class Camera : public Component {
@ -32,6 +33,46 @@ public:
float GetYaw() const; float GetYaw() const;
float GetPitch() const; float GetPitch() const;
// Save the camera state to a YAML node.
virtual YAML::Node Save() const override {
YAML::Node node;
node["position"] = YAML::Node();
node["position"].push_back(position.x);
node["position"].push_back(position.y);
node["position"].push_back(position.z);
node["yaw"] = yaw;
node["pitch"] = pitch;
node["fov"] = fov;
node["aspect"] = aspect;
node["nearPlane"] = nearPlane;
node["farPlane"] = farPlane;
return node;
}
// Load the camera state from a YAML node.
virtual void Load(const YAML::Node &node) override {
if (node["position"]) {
position.x = node["position"][0].as<float>();
position.y = node["position"][1].as<float>();
position.z = node["position"][2].as<float>();
}
if (node["yaw"])
yaw = node["yaw"].as<float>();
if (node["pitch"])
pitch = node["pitch"].as<float>();
if (node["fov"])
fov = node["fov"].as<float>();
if (node["aspect"])
aspect = node["aspect"].as<float>();
if (node["nearPlane"])
nearPlane = node["nearPlane"].as<float>();
if (node["farPlane"])
farPlane = node["farPlane"].as<float>();
}
// (Other Camera methods remain unchanged.)
private: private:
glm::vec3 position; glm::vec3 position;
float yaw; float yaw;

View File

@ -1,12 +1,19 @@
#ifndef COMPONENT_H #ifndef COMPONENT_H
#define COMPONENT_H #define COMPONENT_H
// Base Component class for all components. #include <yaml-cpp/yaml.h>
class Component { class Component {
public: public:
virtual ~Component() {} virtual ~Component() {}
// Update the component each frame. // Update the component each frame.
virtual void Update(float deltaTime) = 0; virtual void Update(float deltaTime) = 0;
// Save the component's state to a YAML node.
virtual YAML::Node Save() const = 0;
// Load the component's state from a YAML node.
virtual void Load(const YAML::Node &node) = 0;
}; };
#endif // COMPONENT_H #endif // COMPONENT_H

View File

@ -2,6 +2,7 @@
#define LIGHT_COMPONENT_H #define LIGHT_COMPONENT_H
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <yaml-cpp/yaml.h>
class LightComponent { class LightComponent {
public: public:
@ -11,6 +12,28 @@ public:
// Light properties. // Light properties.
glm::vec3 color; glm::vec3 color;
float intensity; float intensity;
// Save this light to a YAML node.
YAML::Node Save() const {
YAML::Node node;
node["color"] = YAML::Node();
node["color"].push_back(color.x);
node["color"].push_back(color.y);
node["color"].push_back(color.z);
node["intensity"] = intensity;
return node;
}
// Load this light from a YAML node.
void Load(const YAML::Node &node) {
if (node["color"]) {
color.x = node["color"][0].as<float>();
color.y = node["color"][1].as<float>();
color.z = node["color"][2].as<float>();
}
if (node["intensity"])
intensity = node["intensity"].as<float>();
}
}; };
#endif // LIGHT_COMPONENT_H #endif // LIGHT_COMPONENT_H

View File

@ -86,6 +86,8 @@ bool ModelComponent::LoadNormalTexture(const std::string &filepath) {
// Loads a model using tiny_obj_loader and groups faces by material. // Loads a model using tiny_obj_loader and groups faces by material.
bool ModelComponent::LoadModel(const std::string &filepath) { bool ModelComponent::LoadModel(const std::string &filepath) {
std::cout << "[Info] Loading Model \'" << filepath << "\' " << std::endl;
modelPath = filepath; modelPath = filepath;
tinyobj::attrib_t attrib; tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;
@ -121,7 +123,6 @@ bool ModelComponent::LoadModel(const std::string &filepath) {
size_t index_offset = 0; size_t index_offset = 0;
// Iterate over each face. // Iterate over each face.
for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) { for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) {
std::cout << "[Info] AssetManager: Loading Mesh: "<< i << std::endl;
int fv = shapes[s].mesh.num_face_vertices[f]; int fv = shapes[s].mesh.num_face_vertices[f];
// Get material ID for this face. // Get material ID for this face.
int matID = -1; int matID = -1;
@ -192,6 +193,7 @@ bool ModelComponent::LoadModel(const std::string &filepath) {
meshes.push_back(subMesh); meshes.push_back(subMesh);
} }
} }
std::cout << "[Done] Loaded Model \'" << filepath << "\' " << std::endl;
return true; return true;
} }

View File

@ -1,10 +1,11 @@
#ifndef MODEL_COMPONENT_H #ifndef MODEL_COMPONENT_H
#define MODEL_COMPONENT_H #define MODEL_COMPONENT_H
#include <glm/glm.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
#include <GL/glew.h> #include <GL/glew.h>
#include <glm/glm.hpp>
#include <yaml-cpp/yaml.h>
// Structure representing a single vertex. // Structure representing a single vertex.
struct Vertex { struct Vertex {
@ -22,6 +23,8 @@ struct SubMesh {
glm::vec3 specularColor; glm::vec3 specularColor;
float shininess; float shininess;
GLuint diffuseTexture; GLuint diffuseTexture;
// (Optional) If you add a normal texture per submesh.
// GLuint normalTexture;
GLuint VAO, VBO, EBO; GLuint VAO, VBO, EBO;
SubMesh() SubMesh()
@ -38,11 +41,16 @@ public:
ModelComponent(); ModelComponent();
~ModelComponent(); ~ModelComponent();
// Base model path. // Base model file path.
std::string modelPath; std::string modelPath;
// Submeshes // Submeshes.
std::vector<SubMesh> meshes; std::vector<SubMesh> meshes;
// Global material overrides (if any).
glm::vec3 diffuseColor;
glm::vec3 specularColor;
float shininess;
// Functions. // Functions.
bool LoadModel(const std::string &filepath); bool LoadModel(const std::string &filepath);
void Draw(); void Draw();
@ -53,11 +61,49 @@ public:
// Setup mesh for a submesh. // Setup mesh for a submesh.
void SetupMesh(SubMesh &mesh); void SetupMesh(SubMesh &mesh);
// (Optional) Global material properties if needed. // --- YAML Serialization Functions (Globals Only) ---
glm::vec3 diffuseColor; // Save the global ModelComponent state to a YAML node.
glm::vec3 specularColor; YAML::Node Save() const {
float shininess; YAML::Node node;
node["modelPath"] = modelPath;
node["globalDiffuseColor"] = YAML::Node();
node["globalDiffuseColor"].push_back(diffuseColor.x);
node["globalDiffuseColor"].push_back(diffuseColor.y);
node["globalDiffuseColor"].push_back(diffuseColor.z);
node["globalSpecularColor"] = YAML::Node();
node["globalSpecularColor"].push_back(specularColor.x);
node["globalSpecularColor"].push_back(specularColor.y);
node["globalSpecularColor"].push_back(specularColor.z);
node["globalShininess"] = shininess;
return node;
}
// Load the global ModelComponent state from a YAML node.
void Load(const YAML::Node &node) {
if (node["modelPath"]) {
modelPath = node["modelPath"].as<std::string>();
}
if (node["globalDiffuseColor"]) {
diffuseColor.x = node["globalDiffuseColor"][0].as<float>();
diffuseColor.y = node["globalDiffuseColor"][1].as<float>();
diffuseColor.z = node["globalDiffuseColor"][2].as<float>();
}
if (node["globalSpecularColor"]) {
specularColor.x = node["globalSpecularColor"][0].as<float>();
specularColor.y = node["globalSpecularColor"][1].as<float>();
specularColor.z = node["globalSpecularColor"][2].as<float>();
}
if (node["globalShininess"]) {
shininess = node["globalShininess"].as<float>();
}
// Reload the model (populates meshes based on the modelPath).
LoadModel(modelPath);
}
}; };
#endif // MODEL_COMPONENT_H #endif // MODEL_COMPONENT_H

View File

@ -3,6 +3,7 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <yaml-cpp/yaml.h>
class TransformComponent { class TransformComponent {
public: public:
@ -15,6 +16,46 @@ public:
// Returns the world transform matrix. // Returns the world transform matrix.
glm::mat4 GetMatrix() const; glm::mat4 GetMatrix() const;
// Save this transform to a YAML node.
YAML::Node Save() const {
YAML::Node node;
node["position"] = YAML::Node();
node["position"].push_back(position.x);
node["position"].push_back(position.y);
node["position"].push_back(position.z);
node["rotation"] = YAML::Node();
node["rotation"].push_back(rotation.x);
node["rotation"].push_back(rotation.y);
node["rotation"].push_back(rotation.z);
node["scale"] = YAML::Node();
node["scale"].push_back(scale.x);
node["scale"].push_back(scale.y);
node["scale"].push_back(scale.z);
return node;
}
// Load this transform from a YAML node.
void Load(const YAML::Node &node) {
if (node["position"]) {
position.x = node["position"][0].as<float>();
position.y = node["position"][1].as<float>();
position.z = node["position"][2].as<float>();
}
if (node["rotation"]) {
rotation.x = node["rotation"][0].as<float>();
rotation.y = node["rotation"][1].as<float>();
rotation.z = node["rotation"][2].as<float>();
}
if (node["scale"]) {
scale.x = node["scale"][0].as<float>();
scale.y = node["scale"][1].as<float>();
scale.z = node["scale"][2].as<float>();
}
}
}; };
#endif // TRANSFORM_COMPONENT_H #endif // TRANSFORM_COMPONENT_H

View File

@ -185,55 +185,62 @@ bool Engine::SetupScene() {
// Fragment shader: uses a normal map and material properties. // Fragment shader: uses a normal map and material properties.
const char* fragmentShaderSrc = R"( const char* fragmentShaderSrc = R"(
#version 330 core // Fragment shader:
out vec4 FragColor; #version 330 core
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;
in vec3 Tangent;
uniform vec3 lightPositions[2];
uniform vec3 lightColors[2];
uniform int numLights;
uniform vec3 viewPos;
uniform sampler2D diffuseTexture;
uniform sampler2D normalMap;
uniform bool useNormalMap; // NEW uniform to control normal mapping
// Material properties.
uniform vec3 materialDiffuse;
uniform vec3 materialSpecular;
uniform float materialShininess;
void main() {
vec3 perturbedNormal = normalize(Normal);
if(useNormalMap) {
// Sample normal map.
vec3 normMap = texture(normalMap, TexCoords).rgb;
normMap = normalize(normMap * 2.0 - 1.0);
// Flip Z if needed.
normMap.z = -normMap.z;
// Calculate tangent space basis.
vec3 T = normalize(Tangent);
vec3 B = normalize(cross(Normal, T));
mat3 TBN = mat3(T, B, normalize(Normal));
perturbedNormal = normalize(TBN * normMap);
}
in vec3 FragPos; vec3 diffuseTex = texture(diffuseTexture, TexCoords).rgb;
in vec3 Normal;
in vec2 TexCoords; vec3 ambient = 0.1 * materialDiffuse * diffuseTex;
in vec3 Tangent; vec3 lighting = ambient;
for(int i = 0; i < numLights; i++) {
uniform vec3 lightPositions[2]; vec3 lightDir = normalize(lightPositions[i] - FragPos);
uniform vec3 lightColors[2]; float diff = max(dot(perturbedNormal, lightDir), 0.0);
uniform int numLights; vec3 diffuse = diff * materialDiffuse * diffuseTex * lightColors[i];
uniform vec3 viewPos;
uniform sampler2D diffuseTexture; vec3 viewDir = normalize(viewPos - FragPos);
uniform sampler2D normalMap; vec3 reflectDir = reflect(-lightDir, perturbedNormal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), materialShininess);
// Material properties. vec3 specular = materialSpecular * spec * lightColors[i];
uniform vec3 materialDiffuse;
uniform vec3 materialSpecular; lighting += diffuse + specular;
uniform float materialShininess; }
FragColor = vec4(lighting, 1.0);
void main() { }
// Sample normal map.
vec3 normMap = texture(normalMap, TexCoords).rgb;
normMap = normalize(normMap * 2.0 - 1.0);
normMap.z = -normMap.z;
vec3 N = normalize(Normal);
vec3 T = normalize(Tangent);
vec3 B = normalize(cross(N, T));
mat3 TBN = mat3(T, B, N);
vec3 perturbedNormal = normalize(TBN * normMap);
vec3 diffuseTex = texture(diffuseTexture, TexCoords).rgb;
vec3 ambient = 0.1 * materialDiffuse * diffuseTex;
vec3 lighting = ambient;
for(int i = 0; i < numLights; i++) {
vec3 lightDir = normalize(lightPositions[i] - FragPos);
float diff = max(dot(perturbedNormal, lightDir), 0.0);
vec3 diffuse = diff * materialDiffuse * diffuseTex * lightColors[i];
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, perturbedNormal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), materialShininess);
vec3 specular = materialSpecular * spec * lightColors[i];
lighting += diffuse + specular;
}
FragColor = vec4(lighting, 1.0);
}
)"; )";
shaderProgram = CompileShader(vertexShaderSrc, fragmentShaderSrc); shaderProgram = CompileShader(vertexShaderSrc, fragmentShaderSrc);
@ -278,26 +285,36 @@ ImTextureID Engine::RenderScene(const glm::mat4 &view, const glm::mat4 &projecti
} }
// Render each cube entity using its ModelComponent. // Render each cube entity using its ModelComponent.
for (auto e : entities) { for (auto e : entities) {
if (e->GetType() == EntityType::CUBE && e->modelComponent) { if (e->GetType() == EntityType::CUBE && e->modelComponent) {
glm::mat4 modelMatrix = e->transform.GetMatrix(); glm::mat4 modelMatrix = e->transform.GetMatrix();
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(modelMatrix)); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(modelMatrix));
glUniform3fv(glGetUniformLocation(shaderProgram, "materialDiffuse"), 1, glm::value_ptr(e->modelComponent->diffuseColor));
glUniform3fv(glGetUniformLocation(shaderProgram, "materialSpecular"), 1, glm::value_ptr(e->modelComponent->specularColor)); // Loop through all submeshes in the model component.
glUniform1f(glGetUniformLocation(shaderProgram, "materialShininess"), e->modelComponent->shininess); for (const auto &mesh : e->modelComponent->meshes) {
// Bind the diffuse texture to texture unit 0. // Set material properties for the current submesh.
glUniform3fv(glGetUniformLocation(shaderProgram, "materialDiffuse"), 1, glm::value_ptr(mesh.diffuseColor));
glUniform3fv(glGetUniformLocation(shaderProgram, "materialSpecular"), 1, glm::value_ptr(mesh.specularColor));
glUniform1f(glGetUniformLocation(shaderProgram, "materialShininess"), mesh.shininess);
// Bind the diffuse texture.
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, e->modelComponent->diffuseTexture); glBindTexture(GL_TEXTURE_2D, mesh.diffuseTexture);
glUniform1i(glGetUniformLocation(shaderProgram, "diffuseTexture"), 0); glUniform1i(glGetUniformLocation(shaderProgram, "diffuseTexture"), 0);
// Bind the normal texture to texture unit 1.
glActiveTexture(GL_TEXTURE1); // If you have a normal texture, bind it similarly (adjust as needed).
glBindTexture(GL_TEXTURE_2D, e->modelComponent->normalTexture); // glActiveTexture(GL_TEXTURE1);
glUniform1i(glGetUniformLocation(shaderProgram, "normalMap"), 1); // glBindTexture(GL_TEXTURE_2D, mesh.normalTexture);
// glUniform1i(glGetUniformLocation(shaderProgram, "normalMap"), 1);
// Draw the model.
e->modelComponent->Draw(); // Bind the submesh's VAO and draw its elements.
glBindVertexArray(mesh.VAO);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mesh.indices.size()), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
} }
} }
}
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
return (ImTextureID)(intptr_t)colorTexture; return (ImTextureID)(intptr_t)colorTexture;

View File

@ -3,7 +3,7 @@ CXX := g++
CXXFLAGS := -std=c++20 -Wall -Wextra -O2 -I/c/msys64/mingw64/include -Ivendor/imgui-docking -Ivendor/stb -Ivendor/tini_obj CXXFLAGS := -std=c++20 -Wall -Wextra -O2 -I/c/msys64/mingw64/include -Ivendor/imgui-docking -Ivendor/stb -Ivendor/tini_obj
# Use this to link against the correct import lib # Use this to link against the correct import lib
LDFLAGS := -Llib -lglfw3 -lopengl32 -lglew32 -lglu32 LDFLAGS := -Llib -lglfw3 -lopengl32 -lglew32 -lglu32 -lyaml-cpp
# Source and build directories (including vendor folder) # Source and build directories (including vendor folder)
SRC_DIRS := . Editor Engine vendor/imgui-docking Engine/Components Engine/Entity Engine/utils SRC_DIRS := . Editor Engine vendor/imgui-docking Engine/Components Engine/Entity Engine/utils

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

91
default.yaml Normal file
View File

@ -0,0 +1,91 @@
entities:
- type: cube
transform:
position:
- -2
- 0
- 0
rotation:
- 0
- 0
- 0
scale:
- 1
- 1
- 1
model:
modelPath: ./assets/models/sponza.obj
globalDiffuseColor:
- 0.800000012
- 0.200000003
- 0.200000003
globalSpecularColor:
- 1
- 1
- 1
globalShininess: 32
- type: cube
transform:
position:
- 2
- 0
- 0
rotation:
- 0
- 0
- 0
scale:
- 0.00999999978
- 0.00999999978
- 0.00999999978
model:
modelPath: ./assets/models/sponza.obj
globalDiffuseColor:
- 0
- 1
- 0
globalSpecularColor:
- 1
- 1
- 1
globalShininess: 16
- type: light
transform:
position:
- 0
- 10
- 0
rotation:
- 0
- 0
- 0
scale:
- 1
- 1
- 1
light:
color:
- 1
- 1
- 1
intensity: 1.5
- type: light
transform:
position:
- 0
- -10
- 0
rotation:
- 0
- 0
- 0
scale:
- 1
- 1
- 1
light:
color:
- 0.200000003
- 0.200000003
- 1
intensity: 1

View File

@ -31,14 +31,22 @@ Collapsed=0
DockId=0x00000004,0 DockId=0x00000004,0
[Window][Entity List] [Window][Entity List]
Pos=0,0 Pos=0,78
Size=245,800 Size=245,722
Collapsed=0 Collapsed=0
DockId=0x00000005,0 DockId=0x00000008,0
[Window][Scene File]
Pos=0,0
Size=245,76
Collapsed=0
DockId=0x00000007,0
[Docking][Data] [Docking][Data]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,0 Size=1280,800 Split=X Selected=0xB6999AB4 DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,0 Size=1280,800 Split=X Selected=0xB6999AB4
DockNode ID=0x00000005 Parent=0x08BD597D SizeRef=245,800 Selected=0x5A1EAB5B DockNode ID=0x00000005 Parent=0x08BD597D SizeRef=245,800 Split=Y Selected=0x5A1EAB5B
DockNode ID=0x00000007 Parent=0x00000005 SizeRef=245,76 Selected=0xE1A4FD08
DockNode ID=0x00000008 Parent=0x00000005 SizeRef=245,722 Selected=0x5A1EAB5B
DockNode ID=0x00000006 Parent=0x08BD597D SizeRef=1033,800 Split=X DockNode ID=0x00000006 Parent=0x08BD597D SizeRef=1033,800 Split=X
DockNode ID=0x00000003 Parent=0x00000006 SizeRef=772,800 Split=X DockNode ID=0x00000003 Parent=0x00000006 SizeRef=772,800 Split=X
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=272,800 Selected=0x5098C5B2 DockNode ID=0x00000001 Parent=0x00000003 SizeRef=272,800 Selected=0x5098C5B2