Working frame buffer render

This commit is contained in:
Arnau Alier Torres 2025-05-11 23:00:29 +02:00
parent 55b6c671a7
commit 2675fb006c
29 changed files with 380 additions and 35 deletions

View File

@ -16,6 +16,7 @@
#endif
#define ENVIRONMENT_MAX_ENTITIES 2048
#define MAX_ENVIRONMENT_COUNT 64
namespace Deer {
class Entity;
@ -162,4 +163,14 @@ namespace Deer {
friend class Environment;
};
namespace EnvironmentManager {
uint16_t createEnvironment(const std::string& name);
uint16_t getEnvironmentId(const std::string& name);
Environment& getEnvironment(uint16_t);
const std::string& getEnvironmentName(uint16_t);
void clearAllEnvironments();
}
} // namespace Deer

View File

@ -15,6 +15,7 @@ namespace Deer {
const std::string& getFrameBufferName(uint16_t);
uint16_t getFrameBufferId(std::string& name);
FrameBuffer& getFrameBuffer(uint16_t);
void unloadAllFrameBuffer();
}

View File

@ -0,0 +1,62 @@
#include "Deer/Enviroment.h"
#include "Deer/Scene.h"
#include <unordered_map>
namespace Deer {
namespace EnvironmentManager {
struct EnvironmentContainer {
Environment* env_data = nullptr;
std::string env_name;
};
std::unordered_map<std::string, uint16_t> environment_name_id;
EnvironmentContainer environments[MAX_ENVIRONMENT_COUNT]{};
uint16_t maxEnvironmentId = 1;
}
uint16_t EnvironmentManager::createEnvironment(const std::string& name) {
uint16_t envId = maxEnvironmentId;
maxEnvironmentId++;
environments[envId].env_data = new Environment();
environments[envId].env_name = name;
environment_name_id[name] = envId;
return envId;
}
uint16_t EnvironmentManager::getEnvironmentId(const std::string& name) {
return environment_name_id[name];
}
Environment& EnvironmentManager::getEnvironment(uint16_t id) {
if (id == 0)
return Scene::environment;
return *environments[id].env_data;
}
const std::string& EnvironmentManager::getEnvironmentName(uint16_t id) {
const static std::string main_env_name("Main Environment");
if (id == 0)
return main_env_name;
return environments[id].env_name;
}
void EnvironmentManager::clearAllEnvironments() {
for (int i = 1; i < maxEnvironmentId; i++) {
delete environments[i].env_data;
environments[i].env_data = nullptr;
}
environments[0].env_data = &Scene::environment;
environments[0].env_name = "Main Environment";
environment_name_id.clear();
environment_name_id["Main Environment"] = 0;
maxEnvironmentId = 1;
}
}

View File

@ -44,6 +44,7 @@ namespace Deer {
return frameBuffer->getSpecification().width;
}
int FrameBufferManager::getFrameBufferHeight(uint16_t frameBufferId) {
FrameBuffer* frameBuffer = frameBuffers[frameBufferId].frameBuffer_data;
@ -56,6 +57,12 @@ namespace Deer {
return frameBuffers[id].frameBuffer_name;
}
FrameBuffer& FrameBufferManager::getFrameBuffer(uint16_t id) {
DEER_CORE_ASSERT(id >= 0 && id < maxFrameBufferId, "Invalid frame buffer id {0}", id);
return *frameBuffers[id].frameBuffer_data;
}
uint16_t FrameBufferManager::getFrameBufferId(std::string& name) {
return frameBuffers_name_id_map[name];
}

View File

@ -23,6 +23,8 @@ namespace Deer {
virtual void drawIndex(VertexArray& vertexArray) = 0;
virtual void drawLines(VertexArray& vertexArray) = 0;
virtual void unbindFrameBuffer() = 0;
inline static API getAPI() { return s_API; }
private:
static API s_API;

View File

@ -39,6 +39,9 @@ namespace Deer {
s_renderAPI->init();
}
static inline void unbindFrameBuffer() {
s_renderAPI->unbindFrameBuffer();
}
private:
static Scope<RenderAPI> s_renderAPI;
};

View File

@ -4,6 +4,7 @@
#include "Deer/Log.h"
#include "DeerRender/Events/Event.h"
#include "DeerRender/Input.h"
#include "DeerRender/Render/RenderCommand.h"
// THIS ORDER
#include "Plattform/OpenGL/imgui_impl_opengl3.h"
@ -44,6 +45,9 @@ namespace Deer {
}
void ImGuiLayer::end() {
RenderCommand::unbindFrameBuffer();
ImGui::EndFrame();
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

View File

@ -1,6 +1,7 @@
#include "OpenGLRenderAPI.h"
#include "Plattform/OpenGL/OpenGLBuffer.h"
#include "DeerRender/Render/VertexArray.h"
#include "Deer/Application.h"
#include "glad/glad.h"
#include "GLFW/glfw3.h"
@ -22,6 +23,7 @@ namespace Deer {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable( GL_BLEND );
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
@ -57,4 +59,11 @@ namespace Deer {
const Ref<IndexBuffer>& ib = vertexArray.getIndexBuffer();
glDrawElements(GL_LINES, ib->getCount(), getOpenGLIndexDataType(ib->getIndexDataType()), nullptr);
}
void OpenGLRenderAPI::unbindFrameBuffer() {
unsigned int width = Application::s_application->m_window->getWitdth();
unsigned int height = Application::s_application->m_window->getHeight();
glViewport(0, 0, width, height);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}

View File

@ -14,6 +14,7 @@ namespace Deer {
void drawIndex(VertexArray& vertexArray) override;
void drawLines(VertexArray& vertexArray) override;
void unbindFrameBuffer() override;
};
}

View File

@ -25,16 +25,15 @@ namespace Deer {
DataStore::rootPath = projectPath;
DataStore::loadVoxelsData();
DataStore::loadVoxelsAspect();
EditorEngine::initialize();
return 0;
}
int DeerStudioApplication::onInit() {
DataStore::generateTextureAtlas();
DataStore::loadVoxelsShaders();
ScriptEngine::compileScriptEngine(DataStore::rootPath /
std::filesystem::path("scripts"));
ScriptEngine::compileScriptEngine(DataStore::rootPath / std::filesystem::path("scripts"));
EditorEngine::initialize();
Icons::setupIcons();
@ -131,9 +130,9 @@ namespace Deer {
}
// ---- PanelS -----
EditorEngine::execute();
TerrainEditor::onImGui();
viewport_onImGui();
//viewport_onImGui();
EditorEngine::execute();
// ---- PanelS -----
Scene::gizmoRenderer.refresh();
ImGui::End();

View File

@ -56,6 +56,8 @@ namespace Deer {
{TextureBufferType::RGBA8, TextureBufferType::RED_INTEGER}, 4,
false)));
}
m_frameBuffer->unbind();
return;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("Viewport");
@ -88,6 +90,7 @@ namespace Deer {
unsigned char clearColor[4]{15, 10, 10, 255};
m_frameBuffer->clearBuffer(0, &clearColor);
ImVec2 mPos = ImGui::GetMousePos();
viewport_relativeXMouse = (mPos.x - wPos.x) / windowSize.x;
@ -130,6 +133,7 @@ namespace Deer {
m_frameBuffer->unbind();
ImGui::End();
ImGui::PopStyleVar();
return;
}
void processMovment() {

View File

@ -7,4 +7,5 @@
#include "DeerStudio/EditorEngine/API/Resource.h"
#include "DeerStudio/EditorEngine/API/UI.h"
#include "DeerStudio/EditorEngine/API/Math.h"
#include "DeerStudio/EditorEngine/API/FrameBuffer.h"
#include "DeerStudio/EditorEngine/API/FrameBuffer.h"
#include "DeerStudio/EditorEngine/API/Environment.h"

View File

@ -9,11 +9,16 @@ namespace Deer {
EntityHandleStruct getRootEntity();
EntityHandleStruct getEntity(int);
std::string getName();
};
EnvironmentHandleStruct getMainEnvironment();
EnvironmentHandleStruct createEnvironment(std::string&);
EnvironmentHandleStruct getEnvironment(std::string&);
void registerEnvironmentStructs();
void registerEnvironmentFunctions();
}
}

View File

@ -11,6 +11,8 @@ namespace Deer {
int getWidth();
int getHeight();
void clearRGBA(int, int, int, int);
void resize(int, int);
std::string getName();
};

View File

@ -4,6 +4,8 @@
namespace Deer {
namespace EditorEngine {
struct EntityHandleStruct {
EntityHandleStruct(int _entId = 0, int _envId = 0) : entityId(_entId), environmentId(_envId) { }
uint16_t entityId;
uint16_t environmentId;
@ -12,10 +14,14 @@ namespace Deer {
};
struct EnvironmentHandleStruct {
EnvironmentHandleStruct(int _id = 0) : environmentId(_id) { }
uint16_t environmentId;
};
struct FrameBufferHandleStruct {
FrameBufferHandleStruct(int _id = 0) : frameBufferId(_id) { }
uint16_t frameBufferId;
};
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <string>
#include "glm/glm.hpp"
#include "DeerStudio/EditorEngine/API/GenericRefStructs.h"
class asIScriptFunction;
class CScriptAny;
@ -47,6 +48,11 @@ namespace Deer {
// Renders a icon in the specified size in pixels at the center
void drawIconCentered(std::string& iconId, int size);
// Renders a icon in the specified size in pixels
void drawFrameBuffer(FrameBufferHandleStruct frameBuffer, int sizeX, int sizeY);
// Renders a icon in the specified size in pixels
void drawFrameBufferCentered(FrameBufferHandleStruct frameBuffer, int sizeX, int sizeY);
// Returns if the specified mouse button is clicked on the last element
bool isItemClicked(int mouse);
// Returns if the specified mouse button is double clicked

View File

@ -1,7 +1,50 @@
#include "DeerStudio/EditorEngine/API/Environment.h"
#include "Deer/Enviroment.h"
#include "DeerRender/FrameBuffer.h"
#include "DeerRender/SceneCamera.h"
#include "Deer/Scene.h"
namespace Deer {
namespace DeerEngine {
namespace EditorEngine {
void EnvironmentStruct::render(EntityHandleStruct cameraEntity_handle, FrameBufferHandleStruct frameBuffer_handle) {
FrameBuffer& frameBuffer = FrameBufferManager::getFrameBuffer(frameBuffer_handle.frameBufferId);
Environment& env = EnvironmentManager::getEnvironment(environmentId);
frameBuffer.bind();
Entity& camEntity = env.getEntity(cameraEntity_handle.entityId);
SceneCamera sceneCamera;
sceneCamera.camera = camEntity.getComponent<CameraComponent>();
sceneCamera.transform = camEntity.getComponent<TransformComponent>();
Scene::render(sceneCamera);
//env.render(sceneCamera);
frameBuffer.unbind();
}
EntityHandleStruct EnvironmentStruct::getRootEntity() {
return EntityHandleStruct(0, environmentId);
}
EntityHandleStruct EnvironmentStruct::getEntity(int id) {
return EntityHandleStruct(id, environmentId);
}
std::string EnvironmentStruct::getName() {
return EnvironmentManager::getEnvironmentName(environmentId);
}
EnvironmentHandleStruct getMainEnvironment() {
return EnvironmentHandleStruct(0);
}
EnvironmentHandleStruct createEnvironment(std::string& name) {
return EnvironmentHandleStruct(EnvironmentManager::createEnvironment(name));
}
EnvironmentHandleStruct getEnvironment(std::string& name) {
return EnvironmentHandleStruct(EnvironmentManager::getEnvironmentId(name));
}
}
}

View File

@ -0,0 +1,54 @@
#include "DeerStudio/EditorEngine/API/Environment.h"
#include "DeerStudio/EditorEngine/ErrorHandle.h"
#include "DeerStudio/EditorEngine.h"
#include "angelscript.h"
namespace Deer {
namespace EditorEngine {
void registerEnvironmentStructs();
void registerEnvironmentFunctions();
void registerEnvironmentStructs() {
AS_CHECK(scriptEngine->RegisterObjectType("Environment", sizeof(EnvironmentStruct),
asOBJ_VALUE | asOBJ_POD | asGetTypeTraits<EnvironmentStruct>() | asOBJ_APP_CLASS_ALLINTS));
}
void registerEnvironmentFunctions() {
AS_CHECK(scriptEngine->RegisterGlobalFunction(
"Environment getMainEnvironment()",
asFUNCTION(getMainEnvironment), asCALL_CDECL
));
AS_CHECK(scriptEngine->RegisterGlobalFunction(
"Environment createEnvironment(const string&in)",
asFUNCTION(createEnvironment), asCALL_CDECL
));
AS_CHECK(scriptEngine->RegisterGlobalFunction(
"Environment getEnvironment(const string&in)",
asFUNCTION(getEnvironment), asCALL_CDECL
));
AS_CHECK(scriptEngine->RegisterObjectMethod(
"Environment", "void render(Entity, FrameBuffer)",
asMETHOD(EnvironmentStruct, render), asCALL_THISCALL
));
AS_CHECK(scriptEngine->RegisterObjectMethod(
"Environment", "Entity getRootEntity()",
asMETHOD(EnvironmentStruct, getRootEntity), asCALL_THISCALL
));
AS_CHECK(scriptEngine->RegisterObjectMethod(
"Environment", "Entity getEntity(int)",
asMETHOD(EnvironmentStruct, getEntity), asCALL_THISCALL
));
AS_CHECK(scriptEngine->RegisterObjectMethod(
"Environment", "string get_name() const property",
asMETHOD(EnvironmentStruct, getName), asCALL_THISCALL
));
}
}
}

View File

@ -16,6 +16,16 @@ namespace Deer {
FrameBufferManager::resizeFrameBuffer(frameBufferId, x, y);
}
void FrameBufferStruct::clearRGBA(int r, int g, int b, int a) {
FrameBuffer& frameBuffer = FrameBufferManager::getFrameBuffer(frameBufferId);
frameBuffer.bind();
frameBuffer.clear();
uint8_t clearColor[4] {(uint8_t)r, (uint8_t)g, (uint8_t)b, (uint8_t)a};
frameBuffer.clearBuffer(0, &clearColor);
frameBuffer.unbind();
}
std::string FrameBufferStruct::getName() {
return FrameBufferManager::getFrameBufferName(frameBufferId);
}

View File

@ -17,6 +17,11 @@ namespace Deer {
asMETHOD(FrameBufferStruct, getWidth), asCALL_THISCALL
));
AS_CHECK(scriptEngine->RegisterObjectMethod(
"FrameBuffer", "void clearRGBA(int, int, int, int)",
asMETHOD(FrameBufferStruct, clearRGBA), asCALL_THISCALL
));
AS_CHECK(scriptEngine->RegisterObjectMethod(
"FrameBuffer", "int get_height() const property",
asMETHOD(FrameBufferStruct, getHeight), asCALL_THISCALL
@ -43,6 +48,7 @@ namespace Deer {
asFUNCTION(getFrameBuffer),
asCALL_CDECL
));
}
}
}

View File

@ -2,6 +2,7 @@
#include "DeerStudio/EditorEngine/ErrorHandle.h"
#include "DeerStudio/EditorEngine/DockPanelObject.h"
#include "DeerStudio/EditorEngine.h"
#include "DeerRender/FrameBuffer.h"
#include "Deer/Log.h"
#include "imgui.h"
@ -146,6 +147,38 @@ namespace Deer {
ImVec2(1, 0));
}
void drawFrameBuffer(FrameBufferHandleStruct frameBuffer_handle, int sizeX, int sizeY) {
FrameBuffer& frameBuffer = FrameBufferManager::getFrameBuffer(frameBuffer_handle.frameBufferId);
frameBuffer.bind();
int frameBufferId = frameBuffer.getTextureBufferID(0);
ImGui::Image((void*)(uint64_t)frameBufferId,
ImVec2(sizeX, sizeY), ImVec2(0, 1),
ImVec2(1, 0));
frameBuffer.unbind();
}
void drawFrameBufferCentered(FrameBufferHandleStruct frameBuffer_handle, int _x, int _y) {
FrameBuffer& frameBuffer = FrameBufferManager::getFrameBuffer(frameBuffer_handle.frameBufferId);
int frameBufferId = frameBuffer.getTextureBufferID();
float sizeX;
if (ImGui::GetColumnsCount() > 1)
sizeX = ImGui::GetColumnWidth(-1);
else
sizeX = ImGui::GetContentRegionAvail().x;
float iconWidth = _x;
float padding = (sizeX - iconWidth) * 0.5f;
if (padding > 0.0f)
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + padding);
ImGui::Image((void*)(uint64_t)frameBufferId,
ImVec2(_x, _y), ImVec2(0, 1),
ImVec2(1, 0));
}
void drawIconCentered(std::string& name, int size) {
int iconId = DataStore::getIconId(name);
if (iconId < 0) {

View File

@ -38,6 +38,18 @@ namespace Deer {
asCALL_CDECL
));
AS_CHECK(scriptEngine->RegisterGlobalFunction(
"void drawFrameBuffer(FrameBuffer, int, int)",
asFUNCTION(drawFrameBuffer),
asCALL_CDECL
));
AS_CHECK(scriptEngine->RegisterGlobalFunction(
"void drawFrameBufferCentered(FrameBuffer, int, int)",
asFUNCTION(drawFrameBufferCentered),
asCALL_CDECL
));
AS_CHECK(scriptEngine->RegisterGlobalFunction(
"bool isItemClicked(int)",
asFUNCTION(

View File

@ -47,6 +47,7 @@ namespace Deer {
}
menuBarFunction = type->GetMethodByDecl("void onMenuBar()");
initFunction = type->GetMethodByDecl("void onInit()");
isValid = true;
}
@ -61,6 +62,26 @@ namespace Deer {
isValid = false;
}
void EditorEngine::DockPanelObject::init() {
if (!initFunction)
return;
AS_CHECK_ADDITIONAL_INFO(
scriptContext->Prepare(initFunction),
type->GetName()
);
AS_CHECK_ADDITIONAL_INFO(
scriptContext->SetObject(object),
type->GetName()
);
AS_CHECK_ADDITIONAL_INFO(
scriptContext->Execute(),
type->GetName()
);
}
void EditorEngine::DockPanelObject::executeRender() {
if (menuBarFunction) {
ImGui::Begin(type->GetName(), (bool*)0, ImGuiWindowFlags_MenuBar);
@ -115,13 +136,14 @@ namespace Deer {
}
EditorEngine::DockPanelObject::DockPanelObject(DockPanelObject&& other) noexcept
: isValid(other.isValid), renderFunction(other.renderFunction), type(other.type), object(other.object), menuBarFunction(other.menuBarFunction) {
: isValid(other.isValid), renderFunction(other.renderFunction), type(other.type), object(other.object), menuBarFunction(other.menuBarFunction), initFunction(other.initFunction) {
other.isValid = false;
other.renderFunction = nullptr;
other.type = nullptr;
other.object = nullptr;
other.menuBarFunction = nullptr;
other.initFunction = nullptr;
}
EditorEngine::DockPanelObject& EditorEngine::DockPanelObject::operator=(EditorEngine::DockPanelObject&& other) noexcept {
@ -131,12 +153,14 @@ namespace Deer {
type = other.type;
object = other.object;
menuBarFunction = other.menuBarFunction;
initFunction = other.initFunction;
other.isValid = false;
other.renderFunction = nullptr;
other.type = nullptr;
other.object = nullptr;
other.menuBarFunction = nullptr;
other.initFunction = nullptr;
}
return *this;

View File

@ -11,6 +11,7 @@ namespace Deer {
asIScriptObject* object = nullptr;
asIScriptFunction* renderFunction = nullptr;
asIScriptFunction* menuBarFunction = nullptr;
asIScriptFunction* initFunction = nullptr;
bool isValid = false;
public:
@ -25,6 +26,7 @@ namespace Deer {
void executeRender();
void invalidate();
void init();
};
}
}

View File

@ -48,6 +48,10 @@ namespace Deer {
extractDockPanels();
active = true;
for (auto& pannel : dockPanels) {
pannel.init();
}
}
void EditorEngine::deinitialize() {

View File

@ -10,6 +10,7 @@ namespace Deer {
registerMathFunctions();
registerUIFunctions();
registerFrameBufferFunctions();
registerEnvironmentFunctions();
AS_CHECK(scriptEngine->RegisterGlobalFunction(
"void setupAutomaticColumns(int)",

View File

@ -25,6 +25,7 @@ namespace Deer {
registerEntityStructs();
registerMathStructs();
registerFrameBufferStructs();
registerEnvironmentStructs();
}
}
}

24
roe/Editor/test.as Normal file
View File

@ -0,0 +1,24 @@
class Test : DockPanel {
FrameBuffer frameBuffer;
Environment mainEnv;
void onRender() {
if (!activeEntity.hasCameraComponent())
return;
text("Works");
frameBuffer.clearRGBA(0, 0, 0, 255);
mainEnv.render(activeEntity, frameBuffer);
drawFrameBuffer(frameBuffer, 400, 400);
}
void onInit() {
print("Hi!!!");
frameBuffer = createRGBA8FrameBuffer("MainFrameBuffer", 400, 400);
mainEnv = getMainEnvironment();
}
}

View File

@ -15,10 +15,10 @@ Collapsed=0
DockId=0x00000004,1
[Window][Game Window]
Pos=412,24
Size=457,487
Pos=377,24
Size=504,520
Collapsed=0
DockId=0x00000006,0
DockId=0x00000009,0
[Window][Tree Panel]
Pos=0,24
@ -27,16 +27,16 @@ Collapsed=0
DockId=0x00000001,0
[Window][Terrain Editor]
Pos=871,24
Size=409,487
Pos=883,24
Size=397,520
Collapsed=0
DockId=0x00000004,0
[Window][Viewport]
Pos=412,24
Size=457,487
Pos=369,24
Size=471,696
Collapsed=0
DockId=0x00000006,1
DockId=0x0000000A,0
[Window][Scene Explorer]
Pos=0,389
@ -57,14 +57,14 @@ Collapsed=0
DockId=0x00000008,1
[Window][MeshExplorer]
Pos=0,513
Size=1280,207
Pos=0,546
Size=1280,174
Collapsed=0
DockId=0x00000008,0
[Window][TreePannel]
Pos=0,24
Size=410,487
Size=375,520
Collapsed=0
DockId=0x00000005,0
@ -79,8 +79,8 @@ Size=351,75
Collapsed=0
[Window][PropertiesPannel]
Pos=871,24
Size=409,487
Pos=883,24
Size=397,520
Collapsed=0
DockId=0x00000004,1
@ -91,19 +91,27 @@ Collapsed=0
DockId=0x00000004,1
[Window][ShaderExplorer]
Pos=0,513
Size=1280,207
Pos=0,546
Size=1280,174
Collapsed=0
DockId=0x00000008,1
[Docking][Data]
DockSpace ID=0xA1672E74 Window=0x4647B76E Pos=0,24 Size=1280,696 Split=Y
DockNode ID=0x00000007 Parent=0xA1672E74 SizeRef=1280,487 Split=Y
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=2560,363 Split=X Selected=0x13926F0B
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=869,338 Split=X Selected=0x13926F0B
DockNode ID=0x00000005 Parent=0x00000003 SizeRef=410,446 Selected=0xE45B9F93
DockNode ID=0x00000006 Parent=0x00000003 SizeRef=457,446 CentralNode=1 Selected=0x13926F0B
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=409,338 Selected=0x2A2C795E
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=2560,331 Selected=0xCF339702
DockNode ID=0x00000008 Parent=0xA1672E74 SizeRef=1280,207 Selected=0xD962995A
[Window][Test]
Pos=377,24
Size=504,520
Collapsed=0
DockId=0x00000009,1
[Docking][Data]
DockSpace ID=0xA1672E74 Window=0x4647B76E Pos=0,24 Size=1280,696 Split=Y
DockNode ID=0x00000007 Parent=0xA1672E74 SizeRef=1280,520 Split=Y
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=2560,363 Split=X Selected=0x13926F0B
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=881,338 Split=X Selected=0x13926F0B
DockNode ID=0x00000005 Parent=0x00000003 SizeRef=375,446 Selected=0xE45B9F93
DockNode ID=0x00000006 Parent=0x00000003 SizeRef=504,446 Split=X Selected=0x44A6A033
DockNode ID=0x00000009 Parent=0x00000006 SizeRef=367,520 CentralNode=1 Selected=0x44A6A033
DockNode ID=0x0000000A Parent=0x00000006 SizeRef=471,520 Selected=0x13926F0B
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=397,338 Selected=0xA35A27E3
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=2560,331 Selected=0xCF339702
DockNode ID=0x00000008 Parent=0xA1672E74 SizeRef=1280,174 Selected=0xD962995A