Working on voxels

This commit is contained in:
Chewico 2026-02-17 09:33:57 +01:00
parent 041853a4a0
commit 5d0741635c
19 changed files with 1576 additions and 47 deletions

View File

@ -0,0 +1,92 @@
#pragma once
#include "DeerCore/Tools/Memory.h"
#include "DeerCore/Tools/TypeDefs.h"
#include <string>
#include <unordered_map>
#include <vector>
#define CHUNK_VOXEL_SIZE 16
#ifdef DEER_RENDER
#include "DeerRender/Resource.h"
namespace Deer {
class VoxelBuilder;
class GPUMesh;
} // namespace Deer
#endif
namespace Deer {
struct VoxelType {
std::string id;
};
struct Chunk {
Chunk() {
Voxel voxel = {.voxelTypeId = 0};
voxels_list.push_back(voxel);
voxels_reference_map[voxel] = 0;
}
struct Voxel {
uint32_t voxelTypeId;
bool operator==(const Voxel& other) const noexcept {
return voxelTypeId == other.voxelTypeId;
}
};
struct VoxelHasher {
std::size_t operator()(const Voxel& v) const noexcept {
return std::hash<uint32_t>{}(v.voxelTypeId);
}
};
std::vector<Chunk::Voxel> voxels_list;
std::unordered_map<Chunk::Voxel, uint16_t, VoxelHasher> voxels_reference_map;
uint16_t reference_matrix[CHUNK_VOXEL_SIZE][CHUNK_VOXEL_SIZE][CHUNK_VOXEL_SIZE];
uint16_t getOrCreateVoxelReference(const Voxel& voxel);
Voxel getVoxel(uint16_t voxelReference);
};
class VoxelEnvironment {
public:
VoxelEnvironment();
void modifyVoxel(uint32_t voxelId, int x, int y, int z);
uint32_t getVoxel(int x, int y, int z);
#ifdef DEER_RENDER
public:
Resource<GPUMesh> buildChunk(int x, int y, int z);
Scope<VoxelBuilder> voxelBuilder;
#endif
private:
struct ChunkID {
int x, y, z;
bool operator==(const ChunkID& other) const noexcept {
return x == other.x &&
y == other.y &&
z == other.z;
}
};
struct ChunkIDHasher {
std::size_t operator()(const ChunkID& c) const noexcept {
std::size_t h = 0;
h ^= std::hash<int>{}(c.x) + 0x9e3779b9 + (h << 6) + (h >> 2);
h ^= std::hash<int>{}(c.y) + 0x9e3779b9 + (h << 6) + (h >> 2);
h ^= std::hash<int>{}(c.z) + 0x9e3779b9 + (h << 6) + (h >> 2);
return h;
}
};
Chunk* getOrCreateChunk(int x, int y, int z);
Chunk* tryGetChunk(int x, int y, int z);
std::vector<Scope<Chunk>> chunk_list;
std::unordered_map<ChunkID, Chunk*, ChunkIDHasher> chunk_map;
};
} // namespace Deer

View File

@ -24,6 +24,12 @@ namespace Deer {
VertexUV(float _u, float _v) : u(_u), v(_v) {} VertexUV(float _u, float _v) : u(_u), v(_v) {}
}; };
struct VertexLight {
uint8_t light;
VertexLight() = default;
VertexLight(int8_t _u) : light(_u) {}
};
// Vertex normal is represented with a number fromn [-64,64], and then its // Vertex normal is represented with a number fromn [-64,64], and then its
// divided by 64 to know the decimal number // divided by 64 to know the decimal number
struct VertexNormal { struct VertexNormal {
@ -43,6 +49,7 @@ namespace Deer {
vertexCount = count; vertexCount = count;
} }
void createUVData() { vertexUVData = MakeScope<VertexUV[]>(vertexCount); } void createUVData() { vertexUVData = MakeScope<VertexUV[]>(vertexCount); }
void createLightData() { vertexLightData = MakeScope<VertexLight[]>(vertexCount); }
void createIndices(uint32_t count) { void createIndices(uint32_t count) {
indexData = MakeScope<uint32_t[]>(count); indexData = MakeScope<uint32_t[]>(count);
indexCount = count; indexCount = count;
@ -51,6 +58,7 @@ namespace Deer {
inline VertexPosition* getVertexPosition() const { return vertexPositionsData.get(); } inline VertexPosition* getVertexPosition() const { return vertexPositionsData.get(); }
inline VertexNormal* getVertexNormal() const { return vertexNormalData.get(); } inline VertexNormal* getVertexNormal() const { return vertexNormalData.get(); }
inline VertexUV* getVertexUV() const { return vertexUVData.get(); } inline VertexUV* getVertexUV() const { return vertexUVData.get(); }
inline VertexLight* getVertexLight() const { return vertexLightData.get(); }
inline uint32_t* getIndexData() const { return indexData.get(); } inline uint32_t* getIndexData() const { return indexData.get(); }
inline uint32_t getVertexCount() const { return vertexCount; } inline uint32_t getVertexCount() const { return vertexCount; }
@ -61,6 +69,7 @@ namespace Deer {
Scope<VertexPosition[]> vertexPositionsData; Scope<VertexPosition[]> vertexPositionsData;
Scope<VertexNormal[]> vertexNormalData; Scope<VertexNormal[]> vertexNormalData;
Scope<VertexUV[]> vertexUVData; Scope<VertexUV[]> vertexUVData;
Scope<VertexLight[]> vertexLightData;
uint32_t indexCount = 0; uint32_t indexCount = 0;
Scope<uint32_t[]> indexData; Scope<uint32_t[]> indexData;

View File

@ -1,23 +0,0 @@
#pragma once
#include "DeerRender/EntityEnviroment.h"
#include "DeerRender/Render/FrameBuffer.h"
#include "DeerRender/Tools/Memory.h"
namespace Deer {
class RenderPiperline {
public:
RenderPiperline(RenderPiperline&) = delete;
RenderPiperline(PiperlineOptions);
void render(const EntityEnvironment&);
private:
Scope<FrameBuffer> resultImage;
PiperlineOptions options;
};
struct PiperlineOptions {
int width = 100;
int height = 100;
};
} // namespace Deer

View File

@ -0,0 +1,78 @@
#pragma once
#include "DeerCore/Voxel.h"
#include "DeerRender/Mesh.h"
#include "glm/glm.hpp"
#include <array>
#include <vector>
namespace Deer {
struct VoxelVertex {
VoxelVertex() = default;
VoxelVertex(glm::vec3 _position) : position(_position) {}
glm::vec3 position;
};
struct VoxelFaceData {
// Basic construction
std::vector<VoxelVertex> vertices;
std::vector<uint32_t> triangles;
std::array<std::vector<uint32_t>, 4> connections;
std::array<uint32_t, 4> edges;
};
class VoxelBuilder {
public:
Resource<GPUMesh> buildChunk(int x, int y, int z);
VoxelBuilder(VoxelEnvironment* env) : environment(env) {}
private:
struct VoxelData {
int16_t vertexIndexFace[6] = {-1, -1, -1, -1, -1, -1};
};
struct VertexData {
glm::vec3 position;
glm::vec3 normal;
float extrussion;
float light;
};
private:
void addFace(VoxelFaceData& data, glm::vec3 origin, glm::vec3 up, glm::vec3 right);
void calculateNormals();
void buildVertices();
void buildFaceVertices(int x, int y, int z, int face);
void buildConnections();
void buildAxisConnections(int x, int y, int z);
void connectVertices(VoxelFaceData& face1, int edgeIndex1, int face1VertexOffset, VoxelFaceData& face2, int edgeIndex2, int face2VertexOffset);
void buildMarchingCubesCorners();
void buildMarchingVoxel(int x, int y, int z, uint8_t marchingCubeId);
int getVertexIdCorner(int x, int y, int z, int edge);
VoxelData& getVoxelData(int x, int y, int z);
void clearVoxelData();
bool hasBlock(int x, int y, int z);
float calculateVertexSunLight(int x, int y, int z);
std::vector<VertexData> vertices;
std::vector<u_int32_t> indices;
int voxelXOffset;
int voxelYOffset;
int voxelZOffset;
VoxelEnvironment* environment;
VoxelData voxelData[CHUNK_VOXEL_SIZE + 2][CHUNK_VOXEL_SIZE + 2][CHUNK_VOXEL_SIZE + 2];
friend VoxelEnvironment;
};
} // namespace Deer

View File

@ -0,0 +1,16 @@
#include "DeerCore/Voxel.h"
namespace Deer {
uint16_t Chunk::getOrCreateVoxelReference(const Voxel& voxel) {
if (voxels_reference_map.contains(voxel))
return voxels_reference_map[voxel];
voxels_list.push_back(voxel);
voxels_reference_map[voxel] = voxels_list.size() - 1;
return voxels_list.size() - 1;
}
Chunk::Voxel Chunk::getVoxel(uint16_t voxelReference) {
return voxels_list[voxelReference];
}
} // namespace Deer

View File

@ -0,0 +1,91 @@
#include "DeerCore/Voxel.h"
namespace Deer {
namespace Voxel {
int getChunk(int id) {
int chunk = id / CHUNK_VOXEL_SIZE;
// Fix truncation toward zero for negatives
if (id < 0 && id % CHUNK_VOXEL_SIZE != 0)
--chunk;
return chunk;
}
int getBlock(int id) {
int block = id % CHUNK_VOXEL_SIZE;
// Make remainder positive
if (block < 0)
block += CHUNK_VOXEL_SIZE;
return block;
}
} // namespace Voxel
Chunk* VoxelEnvironment::getOrCreateChunk(int x, int y, int z) {
ChunkID chunkId = {
.x = Voxel::getChunk(x),
.y = Voxel::getChunk(y),
.z = Voxel::getChunk(z)};
if (chunk_map.contains(chunkId))
return chunk_map[chunkId];
// Should do stuff but we relax for the moment
chunk_list.push_back(MakeScope<Chunk>());
Chunk* chunk = chunk_list.back().get();
chunk_map[chunkId] = chunk;
for (int x = 0; x < CHUNK_VOXEL_SIZE; x++) {
for (int y = 0; y < CHUNK_VOXEL_SIZE; y++) {
for (int z = 0; z < CHUNK_VOXEL_SIZE; z++) {
chunk->reference_matrix[x][y][z] = 0;
}
}
}
return chunk;
}
Chunk* VoxelEnvironment::tryGetChunk(int x, int y, int z) {
ChunkID chunkId = {
.x = Voxel::getChunk(x),
.y = Voxel::getChunk(y),
.z = Voxel::getChunk(z)};
if (chunk_map.contains(chunkId))
return chunk_map[chunkId];
return nullptr;
}
void VoxelEnvironment::modifyVoxel(uint32_t voxelId, int x, int y, int z) {
Chunk* chunk = getOrCreateChunk(x, y, z);
Chunk::Voxel voxel = {.voxelTypeId = voxelId};
uint16_t voxelRef = chunk->getOrCreateVoxelReference(voxel);
int posX = Voxel::getBlock(x);
int posY = Voxel::getBlock(y);
int posZ = Voxel::getBlock(z);
chunk->reference_matrix[posX][posY][posZ] = voxelRef;
}
uint32_t VoxelEnvironment::getVoxel(int x, int y, int z) {
Chunk* chunk = tryGetChunk(x, y, z);
if (!chunk)
return 0;
int posX = Voxel::getBlock(x);
int posY = Voxel::getBlock(y);
int posZ = Voxel::getBlock(z);
uint16_t voxelRef = chunk->reference_matrix[posX][posY][posZ];
return chunk->getVoxel(voxelRef).voxelTypeId;
}
} // namespace Deer

View File

@ -34,7 +34,15 @@ namespace Deer {
vertexNormalBuffer->setLayout(normalLayout); vertexNormalBuffer->setLayout(normalLayout);
vertexArray->addVertexBuffer(vertexNormalBuffer); vertexArray->addVertexBuffer(vertexNormalBuffer);
if (data.getVertexPosition()) { if (data.getVertexLight()) {
BufferLayout lightLayout({{"v_Light", DataType::Unsigned_Byte, ShaderDataType::FloatingPoint}}, sizeof(VertexLight));
Ref<VertexBuffer> vertexLightBuffer = VertexBuffer::create(data.getVertexLight(), data.getVertexCount() * sizeof(VertexLight));
vertexLightBuffer->bind();
vertexLightBuffer->setLayout(lightLayout);
vertexArray->addVertexBuffer(vertexLightBuffer);
}
if (data.getVertexUV()) {
BufferLayout uvLayout({{"v_UV", DataType::Float2, ShaderDataType::FloatingPoint}}, sizeof(VertexUV)); BufferLayout uvLayout({{"v_UV", DataType::Float2, ShaderDataType::FloatingPoint}}, sizeof(VertexUV));
Ref<VertexBuffer> vertexUVBuffer = VertexBuffer::create(data.getVertexUV(), data.getVertexCount() * sizeof(VertexUV)); Ref<VertexBuffer> vertexUVBuffer = VertexBuffer::create(data.getVertexUV(), data.getVertexCount() * sizeof(VertexUV));
vertexUVBuffer->bind(); vertexUVBuffer->bind();

View File

@ -0,0 +1,428 @@
#include "DeerCore/Log.h"
#include "DeerRender/Voxel.h"
#include "DeerRender/Voxel/VoxelData.h"
#include <random>
namespace Deer {
VoxelFaceData testFaceData = {
.vertices = {
VoxelVertex(glm::vec3(0.1f, 0.90f, 0.1f)), // 0
VoxelVertex(glm::vec3(0.5f, 0.90f, 0.1f)), // 1
VoxelVertex(glm::vec3(0.9f, 0.90f, 0.1f)), // 2
VoxelVertex(glm::vec3(0.1f, 0.6f, 0.2f)), // 3
VoxelVertex(glm::vec3(0.5f, 0.35f, 0.2f)), // 4
VoxelVertex(glm::vec3(0.9f, 0.6f, 0.2f)), // 5
VoxelVertex(glm::vec3(0.1f, 0.55f, 0)), // 3
VoxelVertex(glm::vec3(0.5f, 0.3f, 0)), // 4
VoxelVertex(glm::vec3(0.9f, 0.55f, 0)), // 5
VoxelVertex(glm::vec3(0.1f, 0.1f, 0)), // 6
VoxelVertex(glm::vec3(0.5f, 0.1f, 0)), // 7
VoxelVertex(glm::vec3(0.9f, 0.1f, 0)), // 8
},
.triangles = {0, 1, 3, 3, 1, 4, 5, 1, 2, 1, 5, 4, 3, 4, 7, 3, 7, 6, 5, 8, 4, 4, 8, 7, 6, 7, 9, 7, 10, 9, 8, 11, 7, 7, 11, 10},
.connections = {
std::vector<uint32_t>{9, 6, 3, 0}, // [0]
std::vector<uint32_t>{2, 5, 8, 11}, // [1]
std::vector<uint32_t>{11, 10, 9}, // [2]
std::vector<uint32_t>{0, 1, 2} // [3]
},
.edges = {9, 11, 0, 2}};
VoxelFaceData flatFaceData = {
.vertices = {
VoxelVertex(glm::vec3(0.3f, 0.3f, 0)),
VoxelVertex(glm::vec3(0.7f, 0.3f, 0)),
VoxelVertex(glm::vec3(0.3f, 0.7f, 0)),
VoxelVertex(glm::vec3(0.7f, 0.7f, 0)),
},
.triangles = {0, 2, 1, 1, 2, 3},
.connections = {
std::vector<uint32_t>{0, 2}, // [0]
std::vector<uint32_t>{3, 1}, // [1]
std::vector<uint32_t>{1, 0}, // [2]
std::vector<uint32_t>{2, 3} // [3]
},
.edges = {0, 1, 2, 3}};
/*
VoxelFaceData testFaceData = {
.vertices = {
VoxelVertex(glm::vec2(0.1f, 0.95f)), // 0
VoxelVertex(glm::vec2(0.4f, 0.8f)), // 1
VoxelVertex(glm::vec2(0.9f, 0.95f)), // 2
VoxelVertex(glm::vec2(0.2f, 0.5f)), // 3
VoxelVertex(glm::vec2(0.5f, 0.6f)), // 4
VoxelVertex(glm::vec2(0.8f, 0.4f)), // 5
VoxelVertex(glm::vec2(0.1f, 0.1f)), // 6
VoxelVertex(glm::vec2(0.6f, 0.1f)), // 7
VoxelVertex(glm::vec2(0.9f, 0.1f)), // 8
},
.triangles = {0, 1, 3, 1, 4, 3, 1, 2, 5, 1, 5, 4, 6, 3, 4, 6, 4, 7, 4, 5, 7, 7, 5, 8},
.connections = {
std::vector<uint32_t>{6, 3, 0}, // [0]
std::vector<uint32_t>{2, 5, 8}, // [1]
std::vector<uint32_t>{8, 7, 6}, // [2]
std::vector<uint32_t>{0, 1, 2} // [3]
},
.edges = {6, 8, 0, 2}};
VoxelFaceData testFaceData = {
.vertices = {
VoxelVertex(glm::vec2(0.1f, 0.1f)),
VoxelVertex(glm::vec2(0.9f, 0.1f)),
VoxelVertex(glm::vec2(0.1f, 0.9f)),
VoxelVertex(glm::vec2(0.9f, 0.9f)),
},
.triangles = {0, 2, 1, 1, 2, 3},
.connections = {
std::vector<uint32_t>{0, 2}, // [0]
std::vector<uint32_t>{3, 1}, // [1]
std::vector<uint32_t>{1, 0}, // [2]
std::vector<uint32_t>{2, 3} // [3]
},
.edges = {0, 1, 2, 3}};
*/
void VoxelBuilder::calculateNormals() {
for (uint32_t indexId = 0; indexId < indices.size(); indexId += 3) {
glm::vec3 startIndex = vertices[indices[indexId + 0]].position;
glm::vec3 direction1 = vertices[indices[indexId + 1]].position - startIndex;
glm::vec3 direction2 = vertices[indices[indexId + 2]].position - startIndex;
glm::vec3 normal = glm::cross(direction1, direction2);
vertices[indices[indexId + 0]].normal += normal;
vertices[indices[indexId + 1]].normal += normal;
vertices[indices[indexId + 2]].normal += normal;
}
}
Resource<GPUMesh> VoxelBuilder::buildChunk(int x, int y, int z) {
voxelXOffset = x;
voxelYOffset = y;
voxelZOffset = z;
vertices.clear();
indices.clear();
clearVoxelData();
buildVertices();
buildConnections();
buildMarchingCubesCorners();
calculateNormals();
for (size_t i = 0; i < vertices.size(); i++) {
float randomValue = (float)(rand() % 1000) / 10000.0f - 0.05f;
randomValue = 0;
glm::vec3 normal = glm::normalize(vertices[i].normal);
vertices[i].position += normal * randomValue + normal * vertices[i].extrussion;
}
calculateNormals();
MeshData meshData;
meshData.createVertices(vertices.size());
meshData.createIndices(indices.size());
meshData.createLightData();
DEER_CORE_TRACE("{} , {}", meshData.getVertexCount(), meshData.getIndexCount());
for (size_t i = 0; i < meshData.getVertexCount(); i++) {
meshData.getVertexPosition()[i].x = vertices[i].position.x;
meshData.getVertexPosition()[i].y = vertices[i].position.y;
meshData.getVertexPosition()[i].z = vertices[i].position.z;
glm::vec3 normal = glm::normalize(vertices[i].normal);
meshData.getVertexNormal()[i].x = (uint8_t)(normal.x * 64);
meshData.getVertexNormal()[i].y = (uint8_t)(normal.y * 64);
meshData.getVertexNormal()[i].z = (uint8_t)(normal.z * 64);
meshData.getVertexLight()[i].light = (uint8_t)(vertices[i].light * 255.0f);
}
for (size_t i = 0; i < meshData.getIndexCount(); i++) {
meshData.getIndexData()[i] = indices[i];
}
return ResourceManager<GPUMesh>::loadResourceFromData(meshData, "tmp&&");
}
VoxelBuilder::VoxelData& VoxelBuilder::getVoxelData(int x, int y, int z) {
return voxelData[x + 1][y + 1][z + 1];
}
void VoxelBuilder::clearVoxelData() {
for (int x = 0; x < CHUNK_VOXEL_SIZE + 2; x++) {
for (int y = 0; y < CHUNK_VOXEL_SIZE + 2; y++) {
for (int z = 0; z < CHUNK_VOXEL_SIZE + 2; z++) {
voxelData[x][y][z] = VoxelData();
}
}
}
}
bool VoxelBuilder::hasBlock(int x, int y, int z) {
uint32_t voxelId = environment->getVoxel(x, y, z);
return voxelId > 0;
}
float VoxelBuilder::calculateVertexSunLight(int x, int y, int z) {
float acumulatedLight = 0;
for (int i = 0; i < 8; i++) {
if (!hasBlock(x + Voxel::voxelCheckOrder[i][0],
y + Voxel::voxelCheckOrder[i][1],
z + Voxel::voxelCheckOrder[i][2]))
acumulatedLight += 1.0f / 4.0f;
}
if (acumulatedLight < 1)
return acumulatedLight;
return 1;
}
void VoxelBuilder::buildVertices() {
for (int x = 0; x < CHUNK_VOXEL_SIZE; x++) {
for (int y = 0; y < CHUNK_VOXEL_SIZE; y++) {
for (int z = 0; z < CHUNK_VOXEL_SIZE; z++) {
if (!hasBlock(x, y, z))
continue;
for (int i = 0; i < 6; i++) {
if (!hasBlock(
x + Voxel::faceNeighborOffset[i][0],
y + Voxel::faceNeighborOffset[i][1],
z + Voxel::faceNeighborOffset[i][2])) {
buildFaceVertices(x, y, z, i);
}
}
}
}
}
}
void VoxelBuilder::buildFaceVertices(int x, int y, int z, int face) {
uint32_t startIndex = vertices.size();
getVoxelData(x, y, z).vertexIndexFace[face] = startIndex;
VoxelFaceData* faceData = &testFaceData;
if (face == 2 || face == 3)
faceData = &flatFaceData;
float cornersLight[4];
cornersLight[0] = calculateVertexSunLight(x + Voxel::faceOriginVectorInt[face][0],
y + Voxel::faceOriginVectorInt[face][1],
z + Voxel::faceOriginVectorInt[face][2]);
cornersLight[1] = calculateVertexSunLight(x + Voxel::faceOriginVectorInt[face][0] + Voxel::faceRightVector[face][0],
y + Voxel::faceOriginVectorInt[face][1] + Voxel::faceRightVector[face][1],
z + Voxel::faceOriginVectorInt[face][2] + Voxel::faceRightVector[face][2]);
cornersLight[2] = calculateVertexSunLight(x + Voxel::faceOriginVectorInt[face][0] + Voxel::faceUpVectorInt[face][0],
y + Voxel::faceOriginVectorInt[face][1] + Voxel::faceUpVectorInt[face][1],
z + Voxel::faceOriginVectorInt[face][2] + Voxel::faceUpVectorInt[face][2]);
cornersLight[3] = calculateVertexSunLight(x + Voxel::faceOriginVectorInt[face][0] + Voxel::faceUpVectorInt[face][0] + Voxel::faceRightVector[face][0],
y + Voxel::faceOriginVectorInt[face][1] + Voxel::faceUpVectorInt[face][1] + Voxel::faceRightVector[face][1],
z + Voxel::faceOriginVectorInt[face][2] + Voxel::faceUpVectorInt[face][2] + Voxel::faceRightVector[face][2]);
for (VoxelVertex& voxelVertex : faceData->vertices) {
glm::vec3 vertexPosition = glm::vec3(x, y, z) + Voxel::faceOriginVector[face];
glm::vec3 up = Voxel::faceUpVector[face];
glm::vec3 right = Voxel::faceRightVector[face];
vertexPosition += up * voxelVertex.position.y;
vertexPosition += right * voxelVertex.position.x;
glm::vec3 perpendicular = glm::cross(up, right);
float topLight = Voxel::lerp(voxelVertex.position.x, cornersLight[3], cornersLight[2]);
float bottomLight = Voxel::lerp(voxelVertex.position.x, cornersLight[1], cornersLight[0]);
vertices.push_back({});
VertexData& vv = vertices.back();
vv.position = vertexPosition;
vv.extrussion = voxelVertex.position.z;
vv.light = Voxel::lerp(voxelVertex.position.y, topLight, bottomLight);
}
for (uint32_t index : faceData->triangles) {
indices.push_back(index + startIndex);
}
}
// This function goes foreach edge, from (0, 0, 0) to (33, 33, 33) then check each edge
void VoxelBuilder::buildConnections() {
for (int x = 0; x < CHUNK_VOXEL_SIZE + 1; x++) {
for (int y = 0; y < CHUNK_VOXEL_SIZE + 1; y++) {
for (int z = 0; z < CHUNK_VOXEL_SIZE + 1; z++) {
buildAxisConnections(x, y, z);
}
}
}
}
void VoxelBuilder::buildAxisConnections(int x, int y, int z) {
for (int axis = 0; axis < AXIS_NUMBER; axis++) {
uint8_t edgeCheckBitmask = 0;
for (int edge = 0; edge < EDGE_PER_AXIS; edge++) {
uint8_t currentEdgeBitmask = 1 << edge;
if (edgeCheckBitmask & currentEdgeBitmask)
continue;
Voxel::VoxelEdge& voxelEdge = Voxel::edgesPerAxis[axis][edge];
VoxelData& voxel = getVoxelData(x + voxelEdge.voxelOffset[0],
y + voxelEdge.voxelOffset[1],
z + voxelEdge.voxelOffset[2]);
bool existsFace = voxel.vertexIndexFace[voxelEdge.faceIndex] >= 0;
edgeCheckBitmask |= currentEdgeBitmask;
if (!existsFace)
continue;
int edgeCheckIndex = edge;
for (int safeCheck = 0; safeCheck < EDGE_PER_AXIS - 1; safeCheck++) {
edgeCheckIndex += voxelEdge.isClockwiseOrder ? 1 : -1;
if (edgeCheckIndex >= EDGE_PER_AXIS)
edgeCheckIndex -= EDGE_PER_AXIS;
if (edgeCheckIndex < 0)
edgeCheckIndex += EDGE_PER_AXIS;
uint8_t nextEdgeBitmask = 1 << edgeCheckIndex;
if (nextEdgeBitmask & edgeCheckBitmask)
continue;
edgeCheckBitmask |= nextEdgeBitmask;
Voxel::VoxelEdge& nextVoxelEdge = Voxel::edgesPerAxis[axis][edgeCheckIndex];
VoxelData& nextVoxel = getVoxelData(x + nextVoxelEdge.voxelOffset[0],
y + nextVoxelEdge.voxelOffset[1],
z + nextVoxelEdge.voxelOffset[2]);
bool nextEdgeExist = nextVoxel.vertexIndexFace[nextVoxelEdge.faceIndex] >= 0;
if (nextEdgeExist) {
VoxelFaceData* faceData1 = &testFaceData;
if (voxelEdge.faceIndex == 2 || voxelEdge.faceIndex == 3)
faceData1 = &flatFaceData;
VoxelFaceData* faceData2 = &testFaceData;
if (nextVoxelEdge.faceIndex == 2 || nextVoxelEdge.faceIndex == 3)
faceData2 = &flatFaceData;
connectVertices(*faceData1, voxelEdge.edgeIndex, voxel.vertexIndexFace[voxelEdge.faceIndex],
*faceData2, nextVoxelEdge.edgeIndex, nextVoxel.vertexIndexFace[nextVoxelEdge.faceIndex]);
break;
}
}
}
}
}
void VoxelBuilder::connectVertices(VoxelFaceData& face1, int edgeIndex1, int face1VertexOffset, VoxelFaceData& face2, int edgeIndex2, int face2VertexOffset) {
int edge1Index = 0;
int edge2Index = face2.connections[edgeIndex2].size() - 1;
int triangleCount = 0;
while (edge1Index < face1.connections[edgeIndex1].size() - 1 || edge2Index > 0) {
triangleCount++;
if (edge1Index == face1.connections[edgeIndex1].size() - 1) {
indices.push_back(face1VertexOffset + face1.connections[edgeIndex1][edge1Index]);
indices.push_back(face2VertexOffset + face2.connections[edgeIndex2][edge2Index]);
indices.push_back(face2VertexOffset + face2.connections[edgeIndex2][edge2Index - 1]);
edge2Index--;
} else if (edge2Index == 0) {
indices.push_back(face2VertexOffset + face2.connections[edgeIndex2][edge2Index]);
indices.push_back(face1VertexOffset + face1.connections[edgeIndex1][edge1Index + 1]);
indices.push_back(face1VertexOffset + face1.connections[edgeIndex1][edge1Index]);
edge1Index++;
} else {
glm::vec3 firstEdgeVertex = vertices[face1.connections[edgeIndex1][edge1Index] + face1VertexOffset].position;
glm::vec3 secondEdgeVertex = vertices[face2.connections[edgeIndex2][edge2Index] + face2VertexOffset].position;
glm::vec3 midPoint = (firstEdgeVertex + secondEdgeVertex) * 0.5f;
glm::vec3 FED = vertices[face1.connections[edgeIndex1][edge1Index + 1] + face1VertexOffset].position - midPoint;
glm::vec3 SED = vertices[face2.connections[edgeIndex2][edge2Index - 1] + face2VertexOffset].position - midPoint;
float firstDistanceNext = FED.x * FED.x + FED.y * FED.y + FED.z * FED.z;
float secondDistanceNext = SED.x * SED.x + SED.y * SED.y + SED.z * SED.z;
if (firstDistanceNext > secondDistanceNext) {
indices.push_back(face1VertexOffset + face1.connections[edgeIndex1][edge1Index]);
indices.push_back(face2VertexOffset + face2.connections[edgeIndex2][edge2Index]);
indices.push_back(face2VertexOffset + face2.connections[edgeIndex2][edge2Index - 1]);
edge2Index--;
} else {
indices.push_back(face1VertexOffset + face1.connections[edgeIndex1][edge1Index]);
indices.push_back(face2VertexOffset + face2.connections[edgeIndex2][edge2Index]);
indices.push_back(face1VertexOffset + face1.connections[edgeIndex1][edge1Index + 1]);
edge1Index++;
}
}
}
}
void VoxelBuilder::buildMarchingCubesCorners() {
for (int x = 0; x < CHUNK_VOXEL_SIZE + 1; x++) {
for (int y = 0; y < CHUNK_VOXEL_SIZE + 1; y++) {
for (int z = 0; z < CHUNK_VOXEL_SIZE + 1; z++) {
uint8_t marchingCubeId = 0;
for (int side = 0; side < CUBE_SIDES; side++) {
bool hasVoxel = hasBlock(x + Voxel::voxelCheckOrder[side][0],
y + Voxel::voxelCheckOrder[side][1],
z + Voxel::voxelCheckOrder[side][2]);
if (hasVoxel)
marchingCubeId |= 1 << side;
}
if (marchingCubeId == 0)
continue;
buildMarchingVoxel(x, y, z, marchingCubeId);
}
}
}
}
void VoxelBuilder::buildMarchingVoxel(int x, int y, int z, uint8_t marchingCubeId) {
for (int safeStop = 0; safeStop < 16; safeStop += 3) {
int nextMarchingCubesedgeIndex = Voxel::marchingCubesTriTable[marchingCubeId][safeStop];
if (nextMarchingCubesedgeIndex == -1)
return;
int vertices[3] = {
getVertexIdCorner(x, y, z, nextMarchingCubesedgeIndex),
getVertexIdCorner(x, y, z, Voxel::marchingCubesTriTable[marchingCubeId][safeStop + 1]),
getVertexIdCorner(x, y, z, Voxel::marchingCubesTriTable[marchingCubeId][safeStop + 2]),
};
if (vertices[0] != -1 && vertices[1] != -1 && vertices[2] != -1) {
indices.push_back(vertices[0]);
indices.push_back(vertices[1]);
indices.push_back(vertices[2]);
}
}
}
int VoxelBuilder::getVertexIdCorner(int x, int y, int z, int edge) {
for (int i = 0; i < 2; i++) {
Voxel::VoxelCorner& option = Voxel::marchingCubesEdgeCornerPairs[edge][i];
VoxelData& _voxelData = getVoxelData(x + option.voxelOffset[0],
y + option.voxelOffset[1],
z + option.voxelOffset[2]);
VoxelFaceData* faceData = &testFaceData;
if (option.faceIndex == 2 || option.faceIndex == 3)
faceData = &flatFaceData;
int voxelDataFaceOffset = _voxelData.vertexIndexFace[option.faceIndex];
if (voxelDataFaceOffset >= 0)
return faceData->edges[option.localCornerIndex] + voxelDataFaceOffset;
}
return -1;
}
} // namespace Deer

View File

@ -0,0 +1,50 @@
#pragma once
#include "glm/glm.hpp"
#define EDGE_PER_AXIS 8
#define AXIS_NUMBER 3
#define CUBE_SIDES 8
namespace Deer {
namespace Voxel {
// Represents an edge of a voxel along a specific axis
struct VoxelEdge {
int voxelOffset[3];
int faceIndex;
int edgeIndex;
bool isClockwiseOrder;
};
struct VoxelCorner {
int voxelOffset[3];
int faceIndex;
int localCornerIndex;
};
inline float lerp(float v, float a, float b) {
return b + (a - b) * v;
}
// Direction vectors for checking neighboring voxels along each face
extern int faceNeighborOffset[6][3];
// Local "up" vector for each face of the voxel
extern glm::vec3 faceUpVector[6];
extern int faceUpVectorInt[6][3];
// Local "right" vector for each face of the voxel
extern glm::vec3 faceRightVector[6];
extern int faceRightVectorInt[6][3];
// Offset to the voxel face position relative to voxel center
extern glm::vec3 faceOriginVector[6];
extern int faceOriginVectorInt[6][3];
extern VoxelEdge edgesPerAxis[AXIS_NUMBER][EDGE_PER_AXIS];
extern int marchingCubesTriTable[256][16];
extern VoxelCorner marchingCubesEdgeCornerPairs[12][2];
extern int voxelCheckOrder[CUBE_SIDES][3];
} // namespace Voxel
} // namespace Deer

View File

@ -0,0 +1,7 @@
#include "DeerRender/Voxel.h"
namespace Deer {
VoxelEnvironment::VoxelEnvironment() {
voxelBuilder = MakeScope<VoxelBuilder>(this);
}
} // namespace Deer

View File

@ -0,0 +1,534 @@
#include "DeerRender/Voxel/VoxelData.h"
namespace Deer {
namespace Voxel {
// Direction vectors for checking neighboring voxels along each face
int faceNeighborOffset[6][3] = {
{-1, 0, 0}, // Left
{1, 0, 0}, // Right
{0, -1, 0}, // Bottom
{0, 1, 0}, // Top
{0, 0, -1}, // Back
{0, 0, 1} // Front
};
// Local "up" vector for each face of the voxel
glm::vec3 faceUpVector[6] = {
glm::vec3(0, 1, 0), // Left
glm::vec3(0, 1, 0), // Right
glm::vec3(0, 0, -1), // Bottom
glm::vec3(0, 0, 1), // Top
glm::vec3(0, 1, 0), // Back
glm::vec3(0, 1, 0) // Front
};
int faceUpVectorInt[6][3] = {
{0, 1, 0},
{0, 1, 0}, // Right
{0, 0, -1}, // Bottom
{0, 0, 1}, // Top
{0, 1, 0}, // Back
{0, 1, 0} // Front
};
// Local "right" vector for each face of the voxel
glm::vec3 faceRightVector[6] = {
glm::vec3(0, 0, -1), // Left
glm::vec3(0, 0, 1), // Right
glm::vec3(1, 0, 0), // Bottom
glm::vec3(1, 0, 0), // Top
glm::vec3(1, 0, 0), // Back
glm::vec3(-1, 0, 0) // Front
};
int faceRightVectorInt[6][3] = {
{0, 0, -1}, // Left
{0, 0, 1}, // Right
{1, 0, 0}, // Bottom
{1, 0, 0}, // Top
{1, 0, 0}, // Back
{-1, 0, 0} // Front
};
// Offset to the voxel face position relative to voxel center
glm::vec3 faceOriginVector[6] = {
glm::vec3(-0.5f, -0.5f, 0.5f), // Left
glm::vec3(0.5f, -0.5f, -0.5f), // Right
glm::vec3(-0.5f, -0.5f, 0.5f), // Bottom
glm::vec3(-0.5f, 0.5f, -0.5f), // Top
glm::vec3(-0.5f, -0.5f, -0.5f), // Back
glm::vec3(0.5f, -0.5f, 0.5f) // Front
};
extern int faceOriginVectorInt[6][3] = {
{0, 0, 1}, // Left
{1, 0, 0}, // Right
{0, 0, 1}, // Bottom
{0, 1, 0}, // Top
{0, 0, 0}, // Back
{1, 0, 1} // Front
};
int voxelCheckOrder[CUBE_SIDES][3]{
{-1, -1, -1},
{0, -1, -1},
{-1, 0, -1},
{0, 0, -1},
{-1, -1, 0},
{0, -1, 0},
{-1, 0, 0},
{0, 0, 0}};
// For each MC case, a list of triangles, specified as triples of edge indices, terminated by -1
int marchingCubesTriTable[256][16] = {
{-1},
{0, 3, 8, -1},
{0, 9, 1, -1},
{3, 8, 1, 1, 8, 9, -1},
{2, 11, 3, -1},
{8, 0, 11, 11, 0, 2, -1},
{3, 2, 11, 1, 0, 9, -1},
{11, 1, 2, 11, 9, 1, 11, 8, 9, -1},
{1, 10, 2, -1},
{0, 3, 8, 2, 1, 10, -1},
{10, 2, 9, 9, 2, 0, -1},
{8, 2, 3, 8, 10, 2, 8, 9, 10, -1},
{11, 3, 10, 10, 3, 1, -1},
{10, 0, 1, 10, 8, 0, 10, 11, 8, -1},
{9, 3, 0, 9, 11, 3, 9, 10, 11, -1},
{8, 9, 11, 11, 9, 10, -1},
{4, 8, 7, -1},
{7, 4, 3, 3, 4, 0, -1},
{4, 8, 7, 0, 9, 1, -1},
{1, 4, 9, 1, 7, 4, 1, 3, 7, -1},
{8, 7, 4, 11, 3, 2, -1},
{4, 11, 7, 4, 2, 11, 4, 0, 2, -1},
{0, 9, 1, 8, 7, 4, 11, 3, 2, -1},
{7, 4, 11, 11, 4, 2, 2, 4, 9, 2, 9, 1, -1},
{4, 8, 7, 2, 1, 10, -1},
{7, 4, 3, 3, 4, 0, 10, 2, 1, -1},
{10, 2, 9, 9, 2, 0, 7, 4, 8, -1},
{10, 2, 3, 10, 3, 4, 3, 7, 4, 9, 10, 4, -1},
{1, 10, 3, 3, 10, 11, 4, 8, 7, -1},
{10, 11, 1, 11, 7, 4, 1, 11, 4, 1, 4, 0, -1},
{7, 4, 8, 9, 3, 0, 9, 11, 3, 9, 10, 11, -1},
{7, 4, 11, 4, 9, 11, 9, 10, 11, -1},
{9, 4, 5, -1},
{9, 4, 5, 8, 0, 3, -1},
{4, 5, 0, 0, 5, 1, -1},
{5, 8, 4, 5, 3, 8, 5, 1, 3, -1},
{9, 4, 5, 11, 3, 2, -1},
{2, 11, 0, 0, 11, 8, 5, 9, 4, -1},
{4, 5, 0, 0, 5, 1, 11, 3, 2, -1},
{5, 1, 4, 1, 2, 11, 4, 1, 11, 4, 11, 8, -1},
{1, 10, 2, 5, 9, 4, -1},
{9, 4, 5, 0, 3, 8, 2, 1, 10, -1},
{2, 5, 10, 2, 4, 5, 2, 0, 4, -1},
{10, 2, 5, 5, 2, 4, 4, 2, 3, 4, 3, 8, -1},
{11, 3, 10, 10, 3, 1, 4, 5, 9, -1},
{4, 5, 9, 10, 0, 1, 10, 8, 0, 10, 11, 8, -1},
{11, 3, 0, 11, 0, 5, 0, 4, 5, 10, 11, 5, -1},
{4, 5, 8, 5, 10, 8, 10, 11, 8, -1},
{8, 7, 9, 9, 7, 5, -1},
{3, 9, 0, 3, 5, 9, 3, 7, 5, -1},
{7, 0, 8, 7, 1, 0, 7, 5, 1, -1},
{7, 5, 3, 3, 5, 1, -1},
{5, 9, 7, 7, 9, 8, 2, 11, 3, -1},
{2, 11, 7, 2, 7, 9, 7, 5, 9, 0, 2, 9, -1},
{2, 11, 3, 7, 0, 8, 7, 1, 0, 7, 5, 1, -1},
{2, 11, 1, 11, 7, 1, 7, 5, 1, -1},
{8, 7, 9, 9, 7, 5, 2, 1, 10, -1},
{10, 2, 1, 3, 9, 0, 3, 5, 9, 3, 7, 5, -1},
{7, 5, 8, 5, 10, 2, 8, 5, 2, 8, 2, 0, -1},
{10, 2, 5, 2, 3, 5, 3, 7, 5, -1},
{8, 7, 5, 8, 5, 9, 11, 3, 10, 3, 1, 10, -1},
{5, 11, 7, 10, 11, 5, 1, 9, 0, -1},
{11, 5, 10, 7, 5, 11, 8, 3, 0, -1},
{5, 11, 7, 10, 11, 5, -1},
{6, 7, 11, -1},
{7, 11, 6, 3, 8, 0, -1},
{6, 7, 11, 0, 9, 1, -1},
{9, 1, 8, 8, 1, 3, 6, 7, 11, -1},
{3, 2, 7, 7, 2, 6, -1},
{0, 7, 8, 0, 6, 7, 0, 2, 6, -1},
{6, 7, 2, 2, 7, 3, 9, 1, 0, -1},
{6, 7, 8, 6, 8, 1, 8, 9, 1, 2, 6, 1, -1},
{11, 6, 7, 10, 2, 1, -1},
{3, 8, 0, 11, 6, 7, 10, 2, 1, -1},
{0, 9, 2, 2, 9, 10, 7, 11, 6, -1},
{6, 7, 11, 8, 2, 3, 8, 10, 2, 8, 9, 10, -1},
{7, 10, 6, 7, 1, 10, 7, 3, 1, -1},
{8, 0, 7, 7, 0, 6, 6, 0, 1, 6, 1, 10, -1},
{7, 3, 6, 3, 0, 9, 6, 3, 9, 6, 9, 10, -1},
{6, 7, 10, 7, 8, 10, 8, 9, 10, -1},
{11, 6, 8, 8, 6, 4, -1},
{6, 3, 11, 6, 0, 3, 6, 4, 0, -1},
{11, 6, 8, 8, 6, 4, 1, 0, 9, -1},
{1, 3, 9, 3, 11, 6, 9, 3, 6, 9, 6, 4, -1},
{2, 8, 3, 2, 4, 8, 2, 6, 4, -1},
{4, 0, 6, 6, 0, 2, -1},
{9, 1, 0, 2, 8, 3, 2, 4, 8, 2, 6, 4, -1},
{9, 1, 4, 1, 2, 4, 2, 6, 4, -1},
{4, 8, 6, 6, 8, 11, 1, 10, 2, -1},
{1, 10, 2, 6, 3, 11, 6, 0, 3, 6, 4, 0, -1},
{11, 6, 4, 11, 4, 8, 10, 2, 9, 2, 0, 9, -1},
{10, 4, 9, 6, 4, 10, 11, 2, 3, -1},
{4, 8, 3, 4, 3, 10, 3, 1, 10, 6, 4, 10, -1},
{1, 10, 0, 10, 6, 0, 6, 4, 0, -1},
{4, 10, 6, 9, 10, 4, 0, 8, 3, -1},
{4, 10, 6, 9, 10, 4, -1},
{6, 7, 11, 4, 5, 9, -1},
{4, 5, 9, 7, 11, 6, 3, 8, 0, -1},
{1, 0, 5, 5, 0, 4, 11, 6, 7, -1},
{11, 6, 7, 5, 8, 4, 5, 3, 8, 5, 1, 3, -1},
{3, 2, 7, 7, 2, 6, 9, 4, 5, -1},
{5, 9, 4, 0, 7, 8, 0, 6, 7, 0, 2, 6, -1},
{3, 2, 6, 3, 6, 7, 1, 0, 5, 0, 4, 5, -1},
{6, 1, 2, 5, 1, 6, 4, 7, 8, -1},
{10, 2, 1, 6, 7, 11, 4, 5, 9, -1},
{0, 3, 8, 4, 5, 9, 11, 6, 7, 10, 2, 1, -1},
{7, 11, 6, 2, 5, 10, 2, 4, 5, 2, 0, 4, -1},
{8, 4, 7, 5, 10, 6, 3, 11, 2, -1},
{9, 4, 5, 7, 10, 6, 7, 1, 10, 7, 3, 1, -1},
{10, 6, 5, 7, 8, 4, 1, 9, 0, -1},
{4, 3, 0, 7, 3, 4, 6, 5, 10, -1},
{10, 6, 5, 8, 4, 7, -1},
{9, 6, 5, 9, 11, 6, 9, 8, 11, -1},
{11, 6, 3, 3, 6, 0, 0, 6, 5, 0, 5, 9, -1},
{11, 6, 5, 11, 5, 0, 5, 1, 0, 8, 11, 0, -1},
{11, 6, 3, 6, 5, 3, 5, 1, 3, -1},
{9, 8, 5, 8, 3, 2, 5, 8, 2, 5, 2, 6, -1},
{5, 9, 6, 9, 0, 6, 0, 2, 6, -1},
{1, 6, 5, 2, 6, 1, 3, 0, 8, -1},
{1, 6, 5, 2, 6, 1, -1},
{2, 1, 10, 9, 6, 5, 9, 11, 6, 9, 8, 11, -1},
{9, 0, 1, 3, 11, 2, 5, 10, 6, -1},
{11, 0, 8, 2, 0, 11, 10, 6, 5, -1},
{3, 11, 2, 5, 10, 6, 2, 11, 6, 6, 10, 2, -1},
{1, 8, 3, 9, 8, 1, 5, 10, 6, -1},
{6, 5, 10, 0, 1, 9, -1},
{8, 3, 0, 5, 10, 6, -1},
{6, 5, 10, -1},
{10, 5, 6, -1},
{0, 3, 8, 6, 10, 5, -1},
{10, 5, 6, 9, 1, 0, -1},
{3, 8, 1, 1, 8, 9, 6, 10, 5, -1},
{2, 11, 3, 6, 10, 5, -1},
{8, 0, 11, 11, 0, 2, 5, 6, 10, -1},
{1, 0, 9, 2, 11, 3, 6, 10, 5, -1},
{5, 6, 10, 11, 1, 2, 11, 9, 1, 11, 8, 9, -1},
{5, 6, 1, 1, 6, 2, -1},
{5, 6, 1, 1, 6, 2, 8, 0, 3, -1},
{6, 9, 5, 6, 0, 9, 6, 2, 0, -1},
{6, 2, 5, 2, 3, 8, 5, 2, 8, 5, 8, 9, -1},
{3, 6, 11, 3, 5, 6, 3, 1, 5, -1},
{8, 0, 1, 8, 1, 6, 1, 5, 6, 11, 8, 6, -1},
{11, 3, 6, 6, 3, 5, 5, 3, 0, 5, 0, 9, -1},
{5, 6, 9, 6, 11, 9, 11, 8, 9, -1},
{5, 6, 10, 7, 4, 8, -1},
{0, 3, 4, 4, 3, 7, 10, 5, 6, -1},
{5, 6, 10, 4, 8, 7, 0, 9, 1, -1},
{6, 10, 5, 1, 4, 9, 1, 7, 4, 1, 3, 7, -1},
{7, 4, 8, 6, 10, 5, 2, 11, 3, -1},
{10, 5, 6, 4, 11, 7, 4, 2, 11, 4, 0, 2, -1},
{4, 8, 7, 6, 10, 5, 3, 2, 11, 1, 0, 9, -1},
{1, 2, 10, 11, 7, 6, 9, 5, 4, -1},
{2, 1, 6, 6, 1, 5, 8, 7, 4, -1},
{0, 3, 7, 0, 7, 4, 2, 1, 6, 1, 5, 6, -1},
{8, 7, 4, 6, 9, 5, 6, 0, 9, 6, 2, 0, -1},
{7, 2, 3, 6, 2, 7, 5, 4, 9, -1},
{4, 8, 7, 3, 6, 11, 3, 5, 6, 3, 1, 5, -1},
{5, 0, 1, 4, 0, 5, 7, 6, 11, -1},
{9, 5, 4, 6, 11, 7, 0, 8, 3, -1},
{11, 7, 6, 9, 5, 4, -1},
{6, 10, 4, 4, 10, 9, -1},
{6, 10, 4, 4, 10, 9, 3, 8, 0, -1},
{0, 10, 1, 0, 6, 10, 0, 4, 6, -1},
{6, 10, 1, 6, 1, 8, 1, 3, 8, 4, 6, 8, -1},
{9, 4, 10, 10, 4, 6, 3, 2, 11, -1},
{2, 11, 8, 2, 8, 0, 6, 10, 4, 10, 9, 4, -1},
{11, 3, 2, 0, 10, 1, 0, 6, 10, 0, 4, 6, -1},
{6, 8, 4, 11, 8, 6, 2, 10, 1, -1},
{4, 1, 9, 4, 2, 1, 4, 6, 2, -1},
{3, 8, 0, 4, 1, 9, 4, 2, 1, 4, 6, 2, -1},
{6, 2, 4, 4, 2, 0, -1},
{3, 8, 2, 8, 4, 2, 4, 6, 2, -1},
{4, 6, 9, 6, 11, 3, 9, 6, 3, 9, 3, 1, -1},
{8, 6, 11, 4, 6, 8, 9, 0, 1, -1},
{11, 3, 6, 3, 0, 6, 0, 4, 6, -1},
{8, 6, 11, 4, 6, 8, -1},
{10, 7, 6, 10, 8, 7, 10, 9, 8, -1},
{3, 7, 0, 7, 6, 10, 0, 7, 10, 0, 10, 9, -1},
{6, 10, 7, 7, 10, 8, 8, 10, 1, 8, 1, 0, -1},
{6, 10, 7, 10, 1, 7, 1, 3, 7, -1},
{3, 2, 11, 10, 7, 6, 10, 8, 7, 10, 9, 8, -1},
{2, 9, 0, 10, 9, 2, 6, 11, 7, -1},
{0, 8, 3, 7, 6, 11, 1, 2, 10, -1},
{7, 6, 11, 1, 2, 10, 11, 6, 2, 2, 6, 10, -1},
{2, 1, 9, 2, 9, 7, 9, 8, 7, 6, 2, 7, -1},
{2, 7, 6, 3, 7, 2, 0, 1, 9, -1},
{8, 7, 0, 7, 6, 0, 6, 2, 0, -1},
{7, 2, 3, 6, 2, 7, -1},
{8, 1, 9, 3, 1, 8, 11, 7, 6, -1},
{11, 7, 6, 1, 9, 0, -1},
{6, 11, 7, 0, 8, 3, -1},
{11, 7, 6, -1},
{7, 11, 5, 5, 11, 10, -1},
{10, 5, 11, 11, 5, 7, 0, 3, 8, -1},
{7, 11, 5, 5, 11, 10, 0, 9, 1, -1},
{7, 11, 10, 7, 10, 5, 3, 8, 1, 8, 9, 1, -1},
{5, 2, 10, 5, 3, 2, 5, 7, 3, -1},
{5, 7, 10, 7, 8, 0, 10, 7, 0, 10, 0, 2, -1},
{0, 9, 1, 5, 2, 10, 5, 3, 2, 5, 7, 3, -1},
{9, 7, 8, 5, 7, 9, 10, 1, 2, -1},
{1, 11, 2, 1, 7, 11, 1, 5, 7, -1},
{8, 0, 3, 1, 11, 2, 1, 7, 11, 1, 5, 7, -1},
{7, 11, 2, 7, 2, 9, 2, 0, 9, 5, 7, 9, -1},
{7, 9, 5, 8, 9, 7, 3, 11, 2, -1},
{3, 1, 7, 7, 1, 5, -1},
{8, 0, 7, 0, 1, 7, 1, 5, 7, -1},
{0, 9, 3, 9, 5, 3, 5, 7, 3, -1},
{9, 7, 8, 5, 7, 9, -1},
{8, 5, 4, 8, 10, 5, 8, 11, 10, -1},
{0, 3, 11, 0, 11, 5, 11, 10, 5, 4, 0, 5, -1},
{1, 0, 9, 8, 5, 4, 8, 10, 5, 8, 11, 10, -1},
{10, 3, 11, 1, 3, 10, 9, 5, 4, -1},
{3, 2, 8, 8, 2, 4, 4, 2, 10, 4, 10, 5, -1},
{10, 5, 2, 5, 4, 2, 4, 0, 2, -1},
{5, 4, 9, 8, 3, 0, 10, 1, 2, -1},
{2, 10, 1, 4, 9, 5, -1},
{8, 11, 4, 11, 2, 1, 4, 11, 1, 4, 1, 5, -1},
{0, 5, 4, 1, 5, 0, 2, 3, 11, -1},
{0, 11, 2, 8, 11, 0, 4, 9, 5, -1},
{5, 4, 9, 2, 3, 11, -1},
{4, 8, 5, 8, 3, 5, 3, 1, 5, -1},
{0, 5, 4, 1, 5, 0, -1},
{5, 4, 9, 3, 0, 8, -1},
{5, 4, 9, -1},
{11, 4, 7, 11, 9, 4, 11, 10, 9, -1},
{0, 3, 8, 11, 4, 7, 11, 9, 4, 11, 10, 9, -1},
{11, 10, 7, 10, 1, 0, 7, 10, 0, 7, 0, 4, -1},
{3, 10, 1, 11, 10, 3, 7, 8, 4, -1},
{3, 2, 10, 3, 10, 4, 10, 9, 4, 7, 3, 4, -1},
{9, 2, 10, 0, 2, 9, 8, 4, 7, -1},
{3, 4, 7, 0, 4, 3, 1, 2, 10, -1},
{7, 8, 4, 10, 1, 2, -1},
{7, 11, 4, 4, 11, 9, 9, 11, 2, 9, 2, 1, -1},
{1, 9, 0, 4, 7, 8, 2, 3, 11, -1},
{7, 11, 4, 11, 2, 4, 2, 0, 4, -1},
{4, 7, 8, 2, 3, 11, -1},
{9, 4, 1, 4, 7, 1, 7, 3, 1, -1},
{7, 8, 4, 1, 9, 0, -1},
{3, 4, 7, 0, 4, 3, -1},
{7, 8, 4, -1},
{11, 10, 8, 8, 10, 9, -1},
{0, 3, 9, 3, 11, 9, 11, 10, 9, -1},
{1, 0, 10, 0, 8, 10, 8, 11, 10, -1},
{10, 3, 11, 1, 3, 10, -1},
{3, 2, 8, 2, 10, 8, 10, 9, 8, -1},
{9, 2, 10, 0, 2, 9, -1},
{8, 3, 0, 10, 1, 2, -1},
{2, 10, 1, -1},
{2, 1, 11, 1, 9, 11, 9, 8, 11, -1},
{11, 2, 3, 9, 0, 1, -1},
{11, 0, 8, 2, 0, 11, -1},
{3, 11, 2, -1},
{1, 8, 3, 9, 8, 1, -1},
{1, 9, 0, -1},
{8, 3, 0, -1},
{-1},
};
extern VoxelCorner marchingCubesEdgeCornerPairs[12][2] = {
{// 0
{.voxelOffset = {-1, -1, -1},
.faceIndex = 1,
.localCornerIndex = 3},
{.voxelOffset = {0, -1, -1},
.faceIndex = 0,
.localCornerIndex = 2}},
{// 1
{.voxelOffset = {0, -1, -1},
.faceIndex = 3,
.localCornerIndex = 2},
{.voxelOffset = {0, 0, -1},
.faceIndex = 2,
.localCornerIndex = 0}},
{// 2
{.voxelOffset = {0, 0, -1},
.faceIndex = 0,
.localCornerIndex = 0},
{.voxelOffset = {-1, 0, -1},
.faceIndex = 1,
.localCornerIndex = 1}},
{// 3
{.voxelOffset = {-1, 0, -1},
.faceIndex = 2,
.localCornerIndex = 1},
{.voxelOffset = {-1, -1, -1},
.faceIndex = 3,
.localCornerIndex = 3}},
{// 4
{.voxelOffset = {-1, -1, 0},
.faceIndex = 1,
.localCornerIndex = 2},
{.voxelOffset = {0, -1, 0},
.faceIndex = 0,
.localCornerIndex = 3}},
{// 5
{.voxelOffset = {0, -1, 0},
.faceIndex = 3,
.localCornerIndex = 0},
{.voxelOffset = {0, 0, 0},
.faceIndex = 2,
.localCornerIndex = 2}},
{// 6
{.voxelOffset = {0, 0, 0},
.faceIndex = 0,
.localCornerIndex = 1},
{.voxelOffset = {-1, 0, 0},
.faceIndex = 1,
.localCornerIndex = 0}},
{// 7
{.voxelOffset = {-1, 0, 0},
.faceIndex = 2,
.localCornerIndex = 3},
{.voxelOffset = {-1, -1, 0},
.faceIndex = 3,
.localCornerIndex = 1}},
{// 8
{.voxelOffset = {-1, -1, -1},
.faceIndex = 5,
.localCornerIndex = 2},
{.voxelOffset = {-1, -1, 0},
.faceIndex = 4,
.localCornerIndex = 3}},
{// 9
{.voxelOffset = {0, -1, -1},
.faceIndex = 5,
.localCornerIndex = 3},
{.voxelOffset = {0, -1, 0},
.faceIndex = 4,
.localCornerIndex = 2}},
{// 10
{.voxelOffset = {0, 0, -1},
.faceIndex = 5,
.localCornerIndex = 1},
{.voxelOffset = {0, 0, 0},
.faceIndex = 4,
.localCornerIndex = 0}},
{// 11
{.voxelOffset = {-1, 0, -1},
.faceIndex = 5,
.localCornerIndex = 0},
{.voxelOffset = {-1, 0, 0},
.faceIndex = 4,
.localCornerIndex = 1}}};
// Represents the 8 edges along each of the 3 axes for a voxel, axis : (X, Y, Z)
VoxelEdge edgesPerAxis[AXIS_NUMBER][EDGE_PER_AXIS] = {
{
{.voxelOffset = {0, 0, 0},
.isClockwiseOrder = true,
.faceIndex = 4,
.edgeIndex = 2},
{.voxelOffset = {0, 0, 0},
.isClockwiseOrder = false,
.faceIndex = 2,
.edgeIndex = 3},
{.voxelOffset = {0, -1, 0},
.isClockwiseOrder = true,
.faceIndex = 3,
.edgeIndex = 2},
{.voxelOffset = {0, -1, 0},
.isClockwiseOrder = false,
.faceIndex = 4,
.edgeIndex = 3},
{.voxelOffset = {0, -1, -1},
.isClockwiseOrder = true,
.faceIndex = 5,
.edgeIndex = 3},
{.voxelOffset = {0, -1, -1},
.isClockwiseOrder = false,
.faceIndex = 3,
.edgeIndex = 3},
{.voxelOffset = {0, 0, -1},
.isClockwiseOrder = true,
.faceIndex = 2,
.edgeIndex = 2},
{.voxelOffset = {0, 0, -1},
.isClockwiseOrder = false,
.faceIndex = 5,
.edgeIndex = 2},
}, // X axis
{
{.voxelOffset = {0, 0, 0},
.isClockwiseOrder = true,
.faceIndex = 0,
.edgeIndex = 1},
{.voxelOffset = {0, 0, 0},
.isClockwiseOrder = false,
.faceIndex = 4,
.edgeIndex = 0},
{.voxelOffset = {0, 0, -1},
.isClockwiseOrder = true,
.faceIndex = 5,
.edgeIndex = 1},
{.voxelOffset = {0, 0, -1},
.isClockwiseOrder = false,
.faceIndex = 0,
.edgeIndex = 0},
{.voxelOffset = {-1, 0, -1},
.isClockwiseOrder = true,
.faceIndex = 1,
.edgeIndex = 1},
{.voxelOffset = {-1, 0, -1},
.isClockwiseOrder = false,
.faceIndex = 5,
.edgeIndex = 0},
{.voxelOffset = {-1, 0, 0},
.isClockwiseOrder = true,
.faceIndex = 4,
.edgeIndex = 1},
{.voxelOffset = {-1, 0, 0},
.isClockwiseOrder = false,
.faceIndex = 1,
.edgeIndex = 0}}, // Y axis
{
{.voxelOffset = {0, 0, 0},
.isClockwiseOrder = true,
.faceIndex = 0,
.edgeIndex = 2},
{.voxelOffset = {0, 0, 0},
.isClockwiseOrder = false,
.faceIndex = 2,
.edgeIndex = 0},
{.voxelOffset = {0, -1, 0},
.isClockwiseOrder = true,
.faceIndex = 3,
.edgeIndex = 0},
{.voxelOffset = {0, -1, 0},
.isClockwiseOrder = false,
.faceIndex = 0,
.edgeIndex = 3},
{.voxelOffset = {-1, -1, 0},
.isClockwiseOrder = true,
.faceIndex = 1,
.edgeIndex = 3},
{.voxelOffset = {-1, -1, 0},
.isClockwiseOrder = false,
.faceIndex = 3,
.edgeIndex = 1},
{.voxelOffset = {-1, 0, 0},
.isClockwiseOrder = true,
.faceIndex = 2,
.edgeIndex = 1},
{.voxelOffset = {-1, 0, 0},
.isClockwiseOrder = false,
.faceIndex = 1,
.edgeIndex = 2},
} // Z axis
};
} // namespace Voxel
} // namespace Deer

View File

@ -40,7 +40,7 @@ namespace Deer {
if (targetRenderTime > 0 && accumulatedRenderTime >= targetRenderTime) { if (targetRenderTime > 0 && accumulatedRenderTime >= targetRenderTime) {
renderDeltaTime = accumulatedRenderTime; renderDeltaTime = accumulatedRenderTime;
worldSettings.renderCallback(*this); worldSettings.renderCallback(*this);
accumulatedRenderTime = 0; accumulatedRenderTime -= targetRenderTime;
} }
std::this_thread::sleep_for(std::chrono::microseconds(1)); std::this_thread::sleep_for(std::chrono::microseconds(1));

View File

@ -8,6 +8,8 @@
#include "DeerRender/World.h" #include "DeerRender/World.h"
// TMP // TMP
#include "DeerCore/EntityEnviroment.h"
#include "DeerRender/Voxel.h"
#include "DeerStudio/Fonts.h" #include "DeerStudio/Fonts.h"
#include "DeerStudio/Style.h" #include "DeerStudio/Style.h"
// TMP // TMP
@ -17,6 +19,7 @@ namespace Deer {
namespace DeerStudio { namespace DeerStudio {
World* world; World* world;
VoxelEnvironment env;
void onUpdate(); void onUpdate();
void onRender(); void onRender();
@ -46,6 +49,53 @@ namespace Deer {
while (world) { while (world) {
StudioPanel::setWorld(world); StudioPanel::setWorld(world);
Entity& entity = world->entityEnvironment->createEntity();
MeshComponent& mesh = entity.addComponent<MeshComponent>();
ShaderComponent& shader = entity.addComponent<ShaderComponent>();
/*
env.modifyVoxel(1, 0, 1, 0);
env.modifyVoxel(1, 1, 2, 0);
env.modifyVoxel(1, 5, 1, 1);
env.modifyVoxel(1, 5, 2, 2);
env.modifyVoxel(1, 5, 1, 3);
env.modifyVoxel(1, 15, 1, 15);
env.modifyVoxel(1, 14, 1, 16);
env.modifyVoxel(1, 5, 1, 8);
env.modifyVoxel(1, 5, 1, 9);
env.modifyVoxel(1, 5, 2, 9);
env.modifyVoxel(1, 6, 1, 9);
env.modifyVoxel(1, 0, 1, 5);
env.modifyVoxel(1, 1, 2, 6);
env.modifyVoxel(1, 4, 1, 5);
env.modifyVoxel(1, 9, 1, 9);
env.modifyVoxel(1, 10, 1, 10);
*/
for (int x = 0; x < 20; x++)
for (int y = 0; y < 20; y++)
env.modifyVoxel(1, x, 0, y);
env.modifyVoxel(1, 5, 1, 5);
env.modifyVoxel(1, 5, 2, 5);
env.modifyVoxel(1, 4, 2, 5);
env.modifyVoxel(1, 4, 2, 4);
env.modifyVoxel(1, 7, 1, 5);
env.modifyVoxel(1, 6, 1, 5);
env.modifyVoxel(1, 6, 1, 4);
DEER_CORE_INFO("{}", env.getVoxel(0, 0, 0));
shader.shader = Builtin::simpleShader();
mesh.mesh = env.voxelBuilder->buildChunk(0, 0, 0);
world->execute(); world->execute();
} }

View File

@ -1,11 +0,0 @@
#include "DeerStudio/DeerStudio.h"
#include "imgui.h"
namespace Deer {
namespace DeerStudio {
void onPanelMenuBar() {
}
} // namespace DeerStudio
} // namespace Deer

View File

@ -5,10 +5,22 @@ class ViewportPanel : Panel {
float pitch = 0; float pitch = 0;
float yaw = 0; float yaw = 0;
int frameCounter = 0;
float deltaAcomulated = 0;
float latestFrameRate;
void onImGui() { void onImGui() {
if (!frameBuffer.isValid()) if (!frameBuffer.isValid())
return; return;
frameCounter++;
deltaAcomulated += World::getRenderDeltaTime();
if (frameCounter == 50) {
latestFrameRate = 50 / deltaAcomulated;
frameCounter = 0;
deltaAcomulated = 0;
}
float vel = World::getRenderDeltaTime() * 3; float vel = World::getRenderDeltaTime() * 3;
if (ImGui::isKeyDown(key::LeftShift)) if (ImGui::isKeyDown(key::LeftShift))
@ -104,7 +116,7 @@ class ViewportPanel : Panel {
} }
ImGui::simplePopup("ViewportCameraProps", SimpleFunction(this.viewportCameraProps)); ImGui::simplePopup("ViewportCameraProps", SimpleFunction(this.viewportCameraProps));
ImGui::textColor(0, 0.5f, 0,"" + ceil(1 / World::getRenderDeltaTime())); ImGui::textColor(0, 0.5f, 0,"" + latestFrameRate);
} }
void viewportCameraProps() { void viewportCameraProps() {

View File

@ -0,0 +1,12 @@
class VoxelEditor : Panel {
FrameBuffer editorBuffer;
void onInit() {
editorBuffer = Resource::createFrameBuffer("VoxelEditor", 620, 620);
}
void onImGui() {
editorBuffer.clearRGBA(60, 120, 100, 255);
ImGui::drawFrameBufferCentered(editorBuffer, 620, 620);
}
}

83
Resources/normal.glsl Normal file
View File

@ -0,0 +1,83 @@
#type vertex
#version 410 core
// Vertex attributes
layout(location = 0) in vec3 v_position;
layout(location = 1) in vec3 v_normal;
layout(location = 2) in vec2 v_UV;
// Outputs to fragment shader
out vec3 normalWS;
out vec3 normalVS;
out vec3 positionVS;
// Uniforms
uniform mat4 u_worldMatrix;
uniform mat4 u_viewMatrix;
void main()
{
gl_Position = u_viewMatrix * u_worldMatrix * vec4(v_position, 1.0);
positionVS = gl_Position.xyz;
mat3 normalMatrix = transpose(inverse(mat3(u_worldMatrix)));
normalWS = normalize(normalMatrix * v_normal);
mat3 normalMatrixView = transpose(inverse(mat3(u_viewMatrix * u_worldMatrix)));
normalVS = normalize(normalMatrixView * v_normal);
}
#type fragment
#version 410 core
// Fragment outputs
layout(location = 0) out vec4 fragColor;
layout(location = 1) out int objectID;
// Inputs from vertex shader
in vec3 normalWS;
in vec3 normalVS;
in vec3 positionVS;
// Uniforms
uniform int u_objectID;
void main()
{
// Directional light
vec3 lightDir = normalize(vec3(1.0, 7.0, 3.0));
// Base light intensity
float light = clamp(dot(normalize(normalWS), lightDir) * 0.8 + 0.2, 0.0, 1.0);
// Gradient from purple (dark) to cyan (lit)
vec3 gradientColor = mix(
vec3(0.9412, 0.4471, 0.7137),
vec3(1.0, 0.9725, 0.5255),
light
);
// Rim/fresnel effect
float rim = 0.5 - dot(normalize(normalWS), normalize(lightDir)) * 0.5;
float rimValue = rim * rim * rim * rim;
vec3 rimColor = vec3(1.0, 0.8, 0.5) * (rim * rim * rim * rim);
vec3 mixedColor = mix(
gradientColor,
vec3(1, 1, 1) ,
rimValue
);
//float fresnel = dot(, vec3(0, 0, -1));
//fresnel = fresnel * fresnel;
float fresnel = 1 + dot(normalize(positionVS), normalize(normalVS));
fresnel = fresnel * fresnel * fresnel * fresnel;
// Combine everything
fragColor = vec4(normalize(normalWS) * 0.5 + vec3(0.5, 0.5, 0.5), 1.0);
// Object ID output
objectID = u_objectID;
}

87
Resources/shad.glsl Normal file
View File

@ -0,0 +1,87 @@
#type vertex
#version 410 core
// Vertex attributes
layout(location = 0) in vec3 v_position;
layout(location = 1) in vec3 v_normal;
layout(location = 2) in float v_light;
// Outputs to fragment shader
out vec3 normalWS;
out vec3 normalVS;
out vec3 positionVS;
out float shadow;
// Uniforms
uniform mat4 u_worldMatrix;
uniform mat4 u_viewMatrix;
void main()
{
gl_Position = u_viewMatrix * u_worldMatrix * vec4(v_position, 1.0);
positionVS = gl_Position.xyz;
mat3 normalMatrix = transpose(inverse(mat3(u_worldMatrix)));
normalWS = normalize(normalMatrix * v_normal);
mat3 normalMatrixView = transpose(inverse(mat3(u_viewMatrix * u_worldMatrix)));
normalVS = normalize(normalMatrixView * v_normal);
shadow = v_light;
}
#type fragment
#version 410 core
// Fragment outputs
layout(location = 0) out vec4 fragColor;
layout(location = 1) out int objectID;
// Inputs from vertex shader
in vec3 normalWS;
in vec3 normalVS;
in vec3 positionVS;
in float shadow;
// Uniforms
uniform int u_objectID;
void main()
{
// Directional light
vec3 lightDir = normalize(vec3(1.0, 7.0, 3.0));
// Base light intensity
float light = clamp(dot(normalize(normalWS), lightDir) * 0.8 + 0.2, 0.0, 1.0);
// Gradient from purple (dark) to cyan (lit)
vec3 gradientColor = mix(
vec3(0.9412, 0.4471, 0.7137),
vec3(1.0, 0.9725, 0.5255),
light
);
// Rim/fresnel effect
float rim = 0.5 - dot(normalize(normalWS), normalize(lightDir)) * 0.5;
float rimValue = rim * rim * rim * rim;
vec3 rimColor = vec3(1.0, 0.8, 0.5) * (rim * rim * rim * rim);
vec3 mixedColor = mix(
gradientColor,
vec3(1, 1, 1) ,
rimValue
);
//float fresnel = dot(, vec3(0, 0, -1));
//fresnel = fresnel * fresnel;
float fresnel = 1 + dot(normalize(positionVS), normalize(normalVS));
fresnel = fresnel * fresnel * fresnel * fresnel;
// Combine everything
fragColor = vec4(normalize(normalWS) * 0.5 + vec3(0.5, 0.5, 0.5), 1.0);
fragColor = vec4(shadow / 255, shadow / 255, shadow / 255, 1);
// Object ID output
objectID = u_objectID;
}

View File

@ -10,34 +10,40 @@ Collapsed=0
[Window][ViewportPanel] [Window][ViewportPanel]
Pos=561,26 Pos=561,26
Size=1500,853 Size=1348,870
Collapsed=0 Collapsed=0
DockId=0x00000005,0 DockId=0x00000005,0
[Window][PropertiesPanel] [Window][PropertiesPanel]
Pos=2063,26 Pos=1911,26
Size=497,853 Size=649,870
Collapsed=0 Collapsed=0
DockId=0x00000006,0 DockId=0x00000006,0
[Window][ResourceExplorer] [Window][ResourceExplorer]
Pos=0,881 Pos=0,898
Size=2560,490 Size=2560,473
Collapsed=0 Collapsed=0
DockId=0x00000004,0 DockId=0x00000004,0
[Window][TreePanel] [Window][TreePanel]
Pos=0,26 Pos=0,26
Size=559,853 Size=559,870
Collapsed=0 Collapsed=0
DockId=0x00000001,0 DockId=0x00000001,0
[Window][VoxelEditor]
Pos=1911,26
Size=649,870
Collapsed=0
DockId=0x00000006,1
[Docking][Data] [Docking][Data]
DockSpace ID=0x0AC2E849 Window=0xD0388BC8 Pos=0,26 Size=2560,1345 Split=Y DockSpace ID=0x0AC2E849 Window=0xD0388BC8 Pos=0,26 Size=2560,1345 Split=Y
DockNode ID=0x00000003 Parent=0x0AC2E849 SizeRef=1920,493 Split=X DockNode ID=0x00000003 Parent=0x0AC2E849 SizeRef=1920,870 Split=X
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=559,985 Selected=0x16E3C1E7 DockNode ID=0x00000001 Parent=0x00000003 SizeRef=559,985 Selected=0x16E3C1E7
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1359,985 Split=X DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1359,985 Split=X
DockNode ID=0x00000005 Parent=0x00000002 SizeRef=1500,493 CentralNode=1 Selected=0x0F5FFC8C DockNode ID=0x00000005 Parent=0x00000002 SizeRef=1348,493 CentralNode=1 Selected=0x0F5FFC8C
DockNode ID=0x00000006 Parent=0x00000002 SizeRef=497,493 Selected=0x9876A79B DockNode ID=0x00000006 Parent=0x00000002 SizeRef=649,493 Selected=0x9876A79B
DockNode ID=0x00000004 Parent=0x0AC2E849 SizeRef=1920,490 Selected=0x018A0F9B DockNode ID=0x00000004 Parent=0x0AC2E849 SizeRef=1920,473 Selected=0x018A0F9B