adding gfx and ui stuff

This commit is contained in:
Daniel Ledda
2026-06-04 18:22:30 +02:00
parent d676c50961
commit 4dfac3f82f
48 changed files with 26734 additions and 3 deletions

42
gfx/Color.c Normal file
View File

@@ -0,0 +1,42 @@
#include "math.h"
#include "Color.h"
real32 hueToRGB(real32 p, real32 q, real32 t) {
if (t < 0) {
t += 1;
} else if (t > 1) {
t -= 1;
}
if (t < 1.0f / 6) return p + (q - p) * 6 * t;
if (t < 1.0f / 2) return q;
if (t < 2.0f / 3) return p + (q - p) * (2.0f / 3 - t) * 6;
return p;
};
Vec4 hslToHex(real32 h, real32 s, real32 l) {
h /= 360;
s /= 100;
l /= 100;
real32 r, g, b;
if (s == 0) {
r = g = b = l;
} else {
real32 q = l < 0.5f ? l * (1 + s) : l + s - l * s;
real32 p = 2 * l - q;
r = hueToRGB(p, q, h + 1.0f / 3);
g = hueToRGB(p, q, h);
b = hueToRGB(p, q, h - 1.0f / 3);
}
return (Vec4){r, g, b, 1};
}
Vec4 colorFromIndex(int index) {
real32 color_wheel_cycle = floorf(index / 6.0f);
real32 darkness_cycle = floorf(index / 12.0f);
real32 spacing = (360.0f / 6.0f);
real32 offset = color_wheel_cycle == 0 ? 0 : spacing / (color_wheel_cycle + 2);
real32 hue = spacing * (index % 6) + offset;
real32 saturation = 100.0f;
real32 lightness = 1.0f / (2 + darkness_cycle) * 100;
return hslToHex(hue, saturation, lightness);
}

17
gfx/Color.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef COLOR_H
#define COLOR_H
#include "../core.h"
#define COLOR_BLACK (Vec4){0, 0, 0, 1}
#define COLOR_RED (Vec4){1, 0, 0, 1}
#define COLOR_GREEN (Vec4){0, 1, 0, 1}
#define COLOR_BLUE (Vec4){0, 0, 1, 1}
#define COLOR_MAGENTA (Vec4){1, 0, 1, 1}
#define COLOR_YELLOW (Vec4){1, 1, 0, 1}
#define COLOR_CYAN (Vec4){0, 1, 1, 1}
#define COLOR_WHITE (Vec4){1, 1, 1, 1}
Vec4 colorFromIndex(int index);
#endif

116
gfx/Mesh.c Normal file
View File

@@ -0,0 +1,116 @@
#include "Mesh.h"
#include "../vendor/loaders/tinyobj.h"
#include "../os.h"
static void tinyobj_get_filedata(void* ctx, const char* filename, const int is_mtl, const char* obj_filename, char** data, size_t* len) {
string file = os_readEntireFile((Arena *)ctx, s("./assets/models/cube.obj"));
*data = file.str;
*len = file.length;
}
DefineList(uint32, MeshIndex);
DefineList(real32, MeshValue);
Mesh createMesh(const char* obj_file) {
Scratch temp = scratchStart(0, 0);
Mesh result = {0};
tinyobj_attrib_t attrib;
tinyobj_shape_t* shapes = NULL;
size_t num_shapes;
tinyobj_material_t* materials = NULL;
size_t num_materials;
int success = tinyobj_parse_obj(
&attrib,
&shapes,
&num_shapes,
&materials,
&num_materials,
obj_file,
tinyobj_get_filedata,
(void *)temp.arena,
TINYOBJ_FLAG_TRIANGULATE
);
if (success != TINYOBJ_SUCCESS || num_shapes <= 0) {
print("Failed to load obj from '%s'! Success %i\n", obj_file, success);
return result;
}
MeshIndexList indices = PushFullList(temp.arena, MeshIndexList, attrib.num_faces);
MeshValueList vertices = PushFullList(temp.arena, MeshValueList, 3*attrib.num_faces);
MeshValueList normals = PushFullList(temp.arena, MeshValueList, 3*attrib.num_faces);
MeshValueList texcoords = PushFullList(temp.arena, MeshValueList, 2*attrib.num_faces);
for (int i = 0; i < attrib.num_faces; i++) {
tinyobj_vertex_index_t vertex_data = attrib.faces[i];
vertices.data[3*i] = attrib.vertices[3*vertex_data.v_idx];
vertices.data[3*i+1] = attrib.vertices[3*vertex_data.v_idx + 1];
vertices.data[3*i+2] = attrib.vertices[3*vertex_data.v_idx + 2];
normals.data[3*i] = attrib.normals[3*vertex_data.vn_idx];
normals.data[3*i+1] = attrib.normals[3*vertex_data.vn_idx + 1];
normals.data[3*i+2] = attrib.normals[3*vertex_data.vn_idx + 2];
texcoords.data[2*i] = attrib.texcoords[2*vertex_data.vt_idx];
texcoords.data[2*i+1] = attrib.texcoords[2*vertex_data.vt_idx + 1];
indices.data[i] = i;
}
result.num_indices = attrib.num_faces*3;
glGenVertexArrays(1, &result.vao);
glGenBuffers(3, result.vbos);
glBindVertexArray(result.vao);
glBindBuffer(GL_ARRAY_BUFFER, result.buffers.xyz);
glBufferData(GL_ARRAY_BUFFER, vertices.length * sizeof(real32), vertices.data, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(real32), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, result.buffers.uv);
glBufferData(GL_ARRAY_BUFFER, texcoords.length * sizeof(real32), texcoords.data, GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(real32), (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, result.buffers.normals);
glBufferData(GL_ARRAY_BUFFER, normals.length * sizeof(real32), normals.data, GL_STATIC_DRAW);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(real32), (void*)0);
glEnableVertexAttribArray(2);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
//glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
scratchEnd(temp);
return result;
}
Mesh createMeshFromShape(const Shape* shape) {
Mesh result = {0};
result.num_indices = shape->indices.length;
glGenVertexArrays(1, &result.vao);
glGenBuffers(3, result.vbos);
glBindVertexArray(result.vao);
glBindBuffer(GL_ARRAY_BUFFER, result.buffers.xyz);
glBufferData(GL_ARRAY_BUFFER, shape->xyz.length * sizeof(float), shape->xyz.data, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, result.buffers.uv);
glBufferData(GL_ARRAY_BUFFER, shape->uv.length * sizeof(real32), shape->uv.data, GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(real32), (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result.buffers.elements);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, shape->indices.length * sizeof(uint32), shape->indices.data, GL_STATIC_DRAW);
return result;
}

25
gfx/Mesh.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef LEDDA_MESH_H
#define LEDDA_MESH_H
#include "../vendor/glad/glad.h"
#include "../core.h"
#include "geometry.h"
typedef struct {
uint32 vao;
union {
struct {
uint32 xyz;
uint32 uv;
uint32 normals;
uint32 elements;
} buffers;
uint32 vbos[4];
};
uint64 num_indices;
} Mesh;
Mesh createMesh(const char* obj_file);
Mesh createMeshFromShape(const Shape* shape);
#endif

100
gfx/Shader.c Normal file
View File

@@ -0,0 +1,100 @@
#include "Shader.h"
#include "../core.h"
#include "../os.h"
#include "../vendor/glad/glad.h"
#include "../vendor/raymath.h"
typedef enum {
ShaderType_fragment=GL_FRAGMENT_SHADER,
ShaderType_vertex=GL_VERTEX_SHADER,
} ShaderType;
uint32 createGlShader(string file_path, ShaderType shader_type) {
GLuint shader;
WithScratch(scratch) {
string shader_code = os_readEntireFile(scratch.arena, file_path);
shader = glCreateShader(shader_type);
const char *shader_code_cstr = cstring(scratch.arena, shader_code);
glShaderSource(shader, 1, &shader_code_cstr, NULL);
glCompileShader(shader);
int success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
GLint info_log_length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);
string info_log = PushString(scratch.arena, (size_t)info_log_length + 1);
glGetShaderInfoLog(shader, info_log_length, NULL, info_log.str);
string shader_type_name = shader_type == ShaderType_fragment ? s("FRAGMENT") : s("VERTEX");
print("%S shader compilation error (%S):\n%S", shader_type_name, file_path, info_log);
}
}
return shader;
}
Shader createShader(string vertex_path, string fragment_path) {
Shader result = {0};
WithScratch(temp) {
uint32 vertex_shader = createGlShader(vertex_path, ShaderType_vertex);
uint32 fragment_shader = createGlShader(fragment_path, ShaderType_fragment);
result.progId = glCreateProgram();
glAttachShader(result.progId, vertex_shader);
glAttachShader(result.progId, fragment_shader);
glLinkProgram(result.progId);
int success;
glGetProgramiv(result.progId, GL_LINK_STATUS, &success);
if (!success) {
GLint info_log_length;
glGetShaderiv(result.progId, GL_INFO_LOG_LENGTH, &info_log_length);
string info_log_prog = PushString(temp.arena, (size_t)info_log_length + 1);
glGetProgramInfoLog(result.progId, info_log_length, NULL, info_log_prog.str);
print("Shader program link error:\n%S", info_log_prog);
}
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
}
return result;
}
void setUniformMat4fv(Shader *s, const char *uniformName, Matrix *matrix) {
glUniformMatrix4fv(
glGetUniformLocation(s->progId, uniformName),
1, GL_FALSE, MatrixToFloat(*matrix));
}
void setUniformMat4fvByLoc(int uniformLocation, Matrix *matrix) {
glUniformMatrix4fv(uniformLocation, 1, GL_FALSE, MatrixToFloat(*matrix));
}
void setUniform4fv(Shader *s, const char *uniformName, Vec4 *vector) {
glUniform4fv(glGetUniformLocation(s->progId, uniformName), 1, (const GLfloat *)vector);
}
void setUniform4fvByLoc(int uniformLocation, Vec4 *vector) {
glUniform4fv(uniformLocation, 1, (const GLfloat *)vector);
}
void setUniform3fv(Shader *s, const char *uniformName, Vec3 *vector) {
glUniform3fv(glGetUniformLocation(s->progId, uniformName), 1, Vector3ToFloat(*vector));
}
void setUniform3fvByLoc(int uniformLocation, Vec3 *vector) {
glUniform3fv(uniformLocation, 1, Vector3ToFloat(*vector));
}
void setUniform1i(Shader *s, const char *uniformName, int32 i) {
glUniform1i(glGetUniformLocation(s->progId, uniformName), (GLint)i);
}
void setUniform1iByLoc(int uniformLocation, int32 i) {
glUniform1i(uniformLocation, (GLint)i);
}
void setUniform2fv(Shader *s, const char *uniformName, Vec2 *vector) {
glUniform2fv(glGetUniformLocation(s->progId, uniformName), 1, (const GLfloat *)vector);
}
void setUniform2fvByLoc(int uniformLocation, Vec2 *vector) {
glUniform2fv(uniformLocation, 1, (const GLfloat *)vector);
}
int getUniformLocation(Shader *s, const char *uniformName) {
return glGetUniformLocation(s->progId, uniformName);
}

30
gfx/Shader.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef DJSTDLIB_GFX_SHADER_H
#define DJSTDLIB_GFX_SHADER_H
#include "../vendor/raymath.h"
#include "../core.h"
typedef struct {
uint32 progId;
} Shader;
Shader createShader(string vertex_path, string fragment_path);
void setUniformMat4fv(Shader *s, const char *uniformName, Matrix *matrix);
void setUniformMat4fvByLoc(int uniformLocation, Matrix *matrix);
void setUniform4fv(Shader *s, const char *uniformName, Vec4 *vector);
void setUniform4fvByLoc(int uniformLocation, Vec4 *vector);
void setUniform3fv(Shader *s, const char *uniformName, Vec3 *vector);
void setUniform3fvByLoc(int uniformLocation, Vec3 *vector);
void setUniform2fv(Shader *s, const char *uniformName, Vec2 *vector);
void setUniform2fvByLoc(int uniformLocation, Vec2 *vector);
void setUniform1i(Shader *s, const char *uniformName, int32 i);
void setUniform1iByLoc(int uniformLocation, int32 i);
int getUniformLocation(Shader *s, const char *uniformName);
#endif

137
gfx/Texture.c Normal file
View File

@@ -0,0 +1,137 @@
#include "Texture.h"
#include "../os.h"
#include <math.h>
Texture createTexture(const char* bitmap, int32 width, int32 height) {
Texture result = {0};
result.width = width;
result.height = height;
glGenTextures(1, &result.tex_id);
glBindTexture(GL_TEXTURE_2D, result.tex_id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, result.width, result.height, 0, GL_RGB, GL_UNSIGNED_BYTE, bitmap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
return result;
}
DefineList(stbtt_bakedchar, STBBakedChar);
static void saveGrayscaleTarga(string location, const char *bytes, int width, int height) {
char tgaHeader[18] = {
// Image ID length
0x00,
// Color map type
0x00,
// Image type
0x03,
// Color map info (unused)
0x0, 0x0, 0x0, 0x0, 0x0,
// Image spec
0x0, 0x0, // x origin
0x0, 0x0, // y origin
width & 0xFF, (width >> 8) & 0xFF, // width
height & 0xFF, (height >> 8) & 0xFF, // width
0x8, // bits per px (depth)
0b00000000, // image descriptor
};
os_writeEntireFile(location, tgaHeader, ArrayCount(tgaHeader));
os_fileAppend(location, bytes, width * height);
}
Font createFont(Arena *arena, string ttfLocation, real32 lineHeight) {
const int CODEPOINT_START = 32;
const int CODEPOINT_END = 126;
int32 atlasWidth = ((int32)lineHeight)*(int32)sqrt(CODEPOINT_END - CODEPOINT_START);
int32 atlasHeight = ((int32)lineHeight)*(int32)sqrt(CODEPOINT_END - CODEPOINT_START);
STBBakedCharList bakedCharlist = PushFullListZero(arena, STBBakedCharList, CODEPOINT_END - CODEPOINT_START);
string fontFile = os_readEntireFile(arena, ttfLocation);
stbtt_fontinfo info;
stbtt_InitFont(&info, (unsigned char *)fontFile.str, stbtt_GetFontOffsetForIndex((unsigned char *)fontFile.str, 0));
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&info, &ascent, &descent, &lineGap);
real32 scale = stbtt_ScaleForPixelHeight(&info, lineHeight);
char *bakedFontBitmap = PushArrayZero(arena, char, atlasWidth*atlasHeight);
int32 bake_result = stbtt_BakeFontBitmap(
(unsigned char *)fontFile.str, 0,
lineHeight,
(unsigned char *)bakedFontBitmap, atlasWidth, atlasHeight,
CODEPOINT_START, CODEPOINT_END - CODEPOINT_START,
bakedCharlist.data);
GlyphMetaList glyphMeta = PushFullListZero(arena, GlyphMetaList, bakedCharlist.length);
real32 x, y;
real32 lastX, lastY;
for (EachIn(bakedCharlist, i)) {
stbtt_aligned_quad q;
stbtt_GetBakedQuad(bakedCharlist.data, atlasWidth, atlasHeight, i, &x, &y, &q, 1);
glyphMeta.data[i] = (GlyphMeta){
.uv0=(Vec2){q.s0, q.t1},
.uv1=(Vec2){q.s1, q.t0},
.xOffset=q.x0 - lastX,
.yOffset=q.y0 + lineHeight + scale * descent,
.width=q.x1-q.x0,
.height=q.y1-q.y0,
};
lastX = x;
lastY = y;
}
Font result = {0};
result.glyphMeta = glyphMeta;
result.lineHeight = lineHeight;
result.charWidth = bakedCharlist.data[0].xadvance;
//saveGrayscaleTarga(s("atlas.tga"), bakedFontBitmap, atlasWidth, atlasHeight);
glGenTextures(1, &result.texId);
glBindTexture(GL_TEXTURE_2D, result.texId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, atlasWidth, atlasHeight, 0, GL_RED, GL_UNSIGNED_BYTE, bakedFontBitmap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
glGenBuffers(1, &result.glyphTableBufId);
glBindBuffer(GL_TEXTURE_BUFFER, result.glyphTableBufId);
glBufferData(GL_TEXTURE_BUFFER, sizeof(result.glyphMeta.data[0])*result.glyphMeta.length, result.glyphMeta.data, GL_STATIC_DRAW);
glGenTextures(1, &result.glyphTableTexId);
glBindTexture(GL_TEXTURE_BUFFER, result.glyphTableTexId);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, result.glyphTableBufId);
return result;
}
Texture createTextureFromFile(const char* source_path) {
Texture result = {0};
glGenTextures(1, &result.tex_id);
glBindTexture(GL_TEXTURE_2D, result.tex_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
int nr_channels;
stbi_uc *data = stbi_load(source_path, &result.width, &result.height, &nr_channels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, result.width, result.height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
} else {
print("Failed to load texture.");
}
stbi_image_free(data);
return result;
}

42
gfx/Texture.h Normal file
View File

@@ -0,0 +1,42 @@
#ifndef LEDDA_TEXTURE_H
#define LEDDA_TEXTURE_H
#include "../core.h"
#include "../vendor/loaders/stb_truetype.h"
#include "../vendor/loaders/stb_image.h"
#include "../vendor/glad/glad.h"
typedef struct Texture Texture;
struct Texture {
uint32 tex_id;
int32 width;
int32 height;
};
typedef struct GlyphMeta GlyphMeta;
struct GlyphMeta {
Vec2 uv0;
Vec2 uv1;
real32 xOffset;
real32 yOffset;
real32 width;
real32 height;
};
DefineList(GlyphMeta, GlyphMeta);
typedef struct Font Font;
struct Font {
GlyphMetaList glyphMeta;
real32 lineHeight;
real32 charWidth;
uint32 texId;
uint32 glyphTableTexId;
uint32 glyphTableBufId;
};
Texture createTexture(const char* bitmap, int32 width, int32 height);
Texture createTextureFromFile(const char* source_path);
Font createFont(Arena *arena, string ttfLocation, real32 lineHeight);
#endif

Binary file not shown.

674
gfx/assets/models/cube.obj Normal file
View File

@@ -0,0 +1,674 @@
# Blender v2.82 (sub 7) OBJ File: ''
# www.blender.org
o Cube_Cube.001
v 0.399961 0.500000 -0.399961
v 0.500000 0.399961 -0.399961
v 0.399961 0.399961 -0.500000
v 0.438244 0.492385 -0.399961
v 0.470699 0.470699 -0.399961
v 0.399961 0.492385 -0.438244
v 0.439203 0.483194 -0.439203
v 0.465612 0.465612 -0.437212
v 0.457718 0.457718 -0.457718
v 0.492385 0.399961 -0.438244
v 0.470699 0.399961 -0.470699
v 0.492385 0.438244 -0.399961
v 0.483194 0.439203 -0.439203
v 0.465612 0.437212 -0.465612
v 0.399961 0.438244 -0.492385
v 0.399961 0.470699 -0.470699
v 0.438244 0.399961 -0.492385
v 0.439203 0.439203 -0.483194
v 0.437212 0.465612 -0.465612
v 0.500000 -0.399961 -0.399961
v 0.399961 -0.500000 -0.399961
v 0.399961 -0.399961 -0.500000
v 0.492385 -0.438244 -0.399961
v 0.470699 -0.470699 -0.399961
v 0.492385 -0.399961 -0.438244
v 0.483194 -0.439203 -0.439203
v 0.465612 -0.465612 -0.437212
v 0.457718 -0.457718 -0.457718
v 0.399961 -0.492385 -0.438244
v 0.399961 -0.470699 -0.470699
v 0.438244 -0.492385 -0.399961
v 0.439203 -0.483194 -0.439203
v 0.437212 -0.465612 -0.465612
v 0.438244 -0.399961 -0.492385
v 0.470699 -0.399961 -0.470699
v 0.399961 -0.438244 -0.492385
v 0.439203 -0.439203 -0.483194
v 0.465612 -0.437212 -0.465612
v 0.500000 0.399961 0.399961
v 0.399961 0.500000 0.399961
v 0.399961 0.399961 0.500000
v 0.492385 0.438244 0.399961
v 0.470699 0.470699 0.399961
v 0.492385 0.399961 0.438244
v 0.483194 0.439203 0.439203
v 0.465612 0.465612 0.437212
v 0.457718 0.457718 0.457718
v 0.399961 0.492385 0.438244
v 0.399961 0.470699 0.470699
v 0.438244 0.492385 0.399961
v 0.439203 0.483194 0.439203
v 0.437212 0.465612 0.465612
v 0.438244 0.399961 0.492385
v 0.470699 0.399961 0.470699
v 0.399961 0.438244 0.492385
v 0.439203 0.439203 0.483194
v 0.465612 0.437212 0.465612
v 0.399961 -0.399961 0.500000
v 0.399961 -0.500000 0.399961
v 0.500000 -0.399961 0.399961
v 0.399961 -0.438244 0.492385
v 0.399961 -0.470699 0.470699
v 0.438244 -0.399961 0.492385
v 0.439203 -0.439203 0.483194
v 0.437212 -0.465612 0.465612
v 0.457718 -0.457718 0.457718
v 0.438244 -0.492385 0.399961
v 0.470699 -0.470699 0.399961
v 0.399961 -0.492385 0.438244
v 0.439203 -0.483194 0.439203
v 0.465612 -0.465612 0.437212
v 0.492385 -0.399961 0.438244
v 0.470699 -0.399961 0.470699
v 0.492385 -0.438244 0.399961
v 0.483194 -0.439203 0.439203
v 0.465612 -0.437212 0.465612
v -0.500000 0.399961 -0.399961
v -0.399961 0.500000 -0.399961
v -0.399961 0.399961 -0.500000
v -0.492385 0.438244 -0.399961
v -0.470699 0.470699 -0.399961
v -0.492385 0.399961 -0.438244
v -0.483194 0.439203 -0.439203
v -0.465612 0.465612 -0.437212
v -0.457718 0.457718 -0.457718
v -0.399961 0.492385 -0.438244
v -0.399961 0.470699 -0.470699
v -0.438244 0.492385 -0.399961
v -0.439203 0.483194 -0.439203
v -0.437212 0.465612 -0.465612
v -0.438244 0.399961 -0.492385
v -0.470699 0.399961 -0.470699
v -0.399961 0.438244 -0.492385
v -0.439203 0.439203 -0.483194
v -0.465612 0.437212 -0.465612
v -0.399961 -0.399961 -0.500000
v -0.399961 -0.500000 -0.399961
v -0.500000 -0.399961 -0.399961
v -0.399961 -0.438244 -0.492385
v -0.399961 -0.470699 -0.470699
v -0.438244 -0.399961 -0.492385
v -0.439203 -0.439203 -0.483194
v -0.437212 -0.465612 -0.465612
v -0.457718 -0.457718 -0.457718
v -0.438244 -0.492385 -0.399961
v -0.470699 -0.470699 -0.399961
v -0.399961 -0.492385 -0.438244
v -0.439203 -0.483194 -0.439203
v -0.465612 -0.465612 -0.437212
v -0.492385 -0.399961 -0.438244
v -0.470699 -0.399961 -0.470699
v -0.492385 -0.438244 -0.399961
v -0.483194 -0.439203 -0.439203
v -0.465612 -0.437212 -0.465612
v -0.500000 0.399961 0.399961
v -0.399961 0.399961 0.500000
v -0.399961 0.500000 0.399961
v -0.492385 0.399961 0.438244
v -0.470699 0.399961 0.470699
v -0.492385 0.438244 0.399961
v -0.483194 0.439203 0.439203
v -0.465612 0.437212 0.465612
v -0.457718 0.457718 0.457718
v -0.399961 0.438244 0.492385
v -0.399961 0.470699 0.470699
v -0.438244 0.399961 0.492385
v -0.439203 0.439203 0.483194
v -0.437212 0.465612 0.465612
v -0.438244 0.492385 0.399961
v -0.470699 0.470699 0.399961
v -0.399961 0.492385 0.438244
v -0.439203 0.483194 0.439203
v -0.465612 0.465612 0.437212
v -0.399961 -0.399961 0.500000
v -0.500000 -0.399961 0.399961
v -0.399961 -0.500000 0.399961
v -0.438244 -0.399961 0.492385
v -0.470699 -0.399961 0.470699
v -0.399961 -0.438244 0.492385
v -0.439203 -0.439203 0.483194
v -0.465612 -0.437212 0.465612
v -0.457718 -0.457718 0.457718
v -0.492385 -0.438244 0.399961
v -0.470699 -0.470699 0.399961
v -0.492385 -0.399961 0.438244
v -0.483194 -0.439203 0.439203
v -0.465612 -0.465612 0.437212
v -0.399961 -0.492385 0.438244
v -0.399961 -0.470699 0.470699
v -0.438244 -0.492385 0.399961
v -0.439203 -0.483194 0.439203
v -0.437212 -0.465612 0.465612
vt 0.150010 0.525010
vt 0.349990 0.525010
vt 0.349990 0.724990
vt 0.150010 0.724990
vt 0.650010 0.525010
vt 0.849990 0.525010
vt 0.849990 0.724990
vt 0.650010 0.724990
vt 0.400010 0.275010
vt 0.599990 0.275010
vt 0.599990 0.474990
vt 0.400010 0.474990
vt 0.400010 0.775010
vt 0.599990 0.775010
vt 0.599990 0.974990
vt 0.400010 0.974990
vt 0.400010 0.025010
vt 0.599990 0.025010
vt 0.599990 0.224990
vt 0.400010 0.224990
vt 0.400010 0.525010
vt 0.390439 0.525010
vt 0.390199 0.515199
vt 0.400010 0.515439
vt 0.375000 0.525010
vt 0.375000 0.515697
vt 0.390697 0.500000
vt 0.400010 0.500000
vt 0.375000 0.510570
vt 0.599990 0.265439
vt 0.609801 0.265199
vt 0.609561 0.275010
vt 0.599990 0.250000
vt 0.609303 0.250000
vt 0.625000 0.265697
vt 0.625000 0.275010
vt 0.614430 0.250000
vt 0.859561 0.724990
vt 0.859801 0.734801
vt 0.849990 0.734561
vt 0.875000 0.724990
vt 0.875000 0.734303
vt 0.859303 0.750000
vt 0.849990 0.750000
vt 0.875000 0.739430
vt 0.390439 0.275010
vt 0.390199 0.265199
vt 0.400010 0.265439
vt 0.375000 0.275010
vt 0.375000 0.265697
vt 0.390697 0.250000
vt 0.400010 0.250000
vt 0.375000 0.260570
vt 0.599990 0.015439
vt 0.609801 0.015199
vt 0.609561 0.025010
vt 0.599990 0.000000
vt 0.609303 0.000000
vt 0.625000 0.015697
vt 0.625000 0.025010
vt 0.614430 0.000000
vt 0.650010 0.734561
vt 0.640199 0.734801
vt 0.640439 0.724990
vt 0.650010 0.750000
vt 0.640697 0.750000
vt 0.625000 0.734303
vt 0.625000 0.724990
vt 0.635570 0.750000
vt 0.609561 0.474990
vt 0.609801 0.484801
vt 0.599990 0.484561
vt 0.625000 0.474990
vt 0.625000 0.484303
vt 0.609303 0.500000
vt 0.599990 0.500000
vt 0.625000 0.489430
vt 0.400010 0.724990
vt 0.400010 0.734561
vt 0.390199 0.734801
vt 0.390439 0.724990
vt 0.400010 0.750000
vt 0.390697 0.750000
vt 0.375000 0.734303
vt 0.375000 0.724990
vt 0.385570 0.750000
vt 0.349990 0.515439
vt 0.359801 0.515199
vt 0.359561 0.525010
vt 0.349990 0.500000
vt 0.359303 0.500000
vt 0.375000 0.515697
vt 0.375000 0.525010
vt 0.364430 0.500000
vt 0.140439 0.525010
vt 0.140199 0.515199
vt 0.150010 0.515439
vt 0.125000 0.525010
vt 0.125000 0.515697
vt 0.140697 0.500000
vt 0.150010 0.500000
vt 0.125000 0.510570
vt 0.609561 0.224990
vt 0.609801 0.234801
vt 0.599990 0.234561
vt 0.625000 0.224990
vt 0.625000 0.234303
vt 0.609303 0.250000
vt 0.599990 0.250000
vt 0.625000 0.239430
vt 0.400010 0.484561
vt 0.390199 0.484801
vt 0.390439 0.474990
vt 0.400010 0.500000
vt 0.390697 0.500000
vt 0.375000 0.484303
vt 0.375000 0.474990
vt 0.385570 0.500000
vt 0.609561 0.974990
vt 0.609801 0.984801
vt 0.599990 0.984561
vt 0.625000 0.974990
vt 0.625000 0.984303
vt 0.609303 1.000000
vt 0.599990 1.000000
vt 0.625000 0.989430
vt 0.599990 0.525010
vt 0.599990 0.515439
vt 0.609801 0.515199
vt 0.609561 0.525010
vt 0.599990 0.500000
vt 0.609303 0.500000
vt 0.625000 0.515697
vt 0.625000 0.525010
vt 0.614430 0.500000
vt 0.849990 0.515439
vt 0.859801 0.515199
vt 0.859561 0.525010
vt 0.849990 0.500000
vt 0.859303 0.500000
vt 0.875000 0.515697
vt 0.875000 0.525010
vt 0.864430 0.500000
vt 0.640439 0.525010
vt 0.640199 0.515199
vt 0.650010 0.515439
vt 0.625000 0.525010
vt 0.625000 0.515697
vt 0.640697 0.500000
vt 0.650010 0.500000
vt 0.625000 0.510570
vt 0.390439 0.025010
vt 0.390199 0.015199
vt 0.400010 0.015439
vt 0.375000 0.025010
vt 0.375000 0.015697
vt 0.390697 0.000000
vt 0.400010 0.000000
vt 0.375000 0.010570
vt 0.400010 0.984561
vt 0.390199 0.984801
vt 0.390439 0.974990
vt 0.400010 1.000000
vt 0.390697 1.000000
vt 0.375000 0.984303
vt 0.375000 0.974990
vt 0.385570 1.000000
vt 0.599990 0.765439
vt 0.609801 0.765199
vt 0.609561 0.775010
vt 0.599990 0.750000
vt 0.609303 0.750000
vt 0.625000 0.765697
vt 0.625000 0.775010
vt 0.614430 0.750000
vt 0.359561 0.724990
vt 0.359801 0.734801
vt 0.349990 0.734561
vt 0.375000 0.724990
vt 0.375000 0.734303
vt 0.359303 0.750000
vt 0.349990 0.750000
vt 0.375000 0.739430
vt 0.599990 0.724990
vt 0.609561 0.724990
vt 0.609801 0.734801
vt 0.599990 0.734561
vt 0.625000 0.724990
vt 0.625000 0.734303
vt 0.609303 0.750000
vt 0.599990 0.750000
vt 0.625000 0.739430
vt 0.150010 0.734561
vt 0.140199 0.734801
vt 0.140439 0.724990
vt 0.150010 0.750000
vt 0.140697 0.750000
vt 0.125000 0.734303
vt 0.125000 0.724990
vt 0.135570 0.750000
vt 0.390439 0.775010
vt 0.390199 0.765199
vt 0.400010 0.765439
vt 0.375000 0.775010
vt 0.375000 0.765697
vt 0.390697 0.750000
vt 0.400010 0.750000
vt 0.375000 0.760570
vt 0.400010 0.234561
vt 0.390199 0.234801
vt 0.390439 0.224990
vt 0.400010 0.250000
vt 0.390697 0.250000
vt 0.375000 0.234303
vt 0.375000 0.224990
vt 0.385570 0.250000
vn 0.1004 -0.1004 0.9899
vn 0.1004 0.1004 0.9899
vn -0.1004 0.1004 0.9899
vn -0.1004 -0.1004 0.9899
vn -0.1004 -0.1004 -0.9899
vn -0.1004 0.1004 -0.9899
vn 0.1004 0.1004 -0.9899
vn 0.1004 -0.1004 -0.9899
vn 0.9899 -0.1004 -0.1004
vn 0.9899 0.1004 -0.1004
vn 0.9899 0.1004 0.1004
vn 0.9899 -0.1004 0.1004
vn -0.9899 -0.1004 0.1004
vn -0.9899 0.1004 0.1004
vn -0.9899 0.1004 -0.1004
vn -0.9899 -0.1004 -0.1004
vn -0.1004 -0.9899 -0.1004
vn 0.1004 -0.9899 -0.1004
vn 0.1004 -0.9899 0.1004
vn -0.1004 -0.9899 0.1004
vn 0.1004 0.9899 -0.1004
vn 0.3792 0.9201 -0.0981
vn 0.3673 0.8545 -0.3673
vn 0.0981 0.9201 -0.3792
vn 0.7041 0.7041 -0.0919
vn 0.6663 0.6663 -0.3347
vn 0.3347 0.6663 -0.6663
vn 0.0919 0.7041 -0.7041
vn 0.5774 0.5774 -0.5774
vn 0.9201 0.0981 -0.3792
vn 0.8545 0.3673 -0.3673
vn 0.9201 0.3792 -0.0981
vn 0.7041 0.0919 -0.7041
vn 0.6663 0.3347 -0.6663
vn 0.0981 0.3792 -0.9201
vn 0.3673 0.3673 -0.8545
vn 0.3792 0.0981 -0.9201
vn 0.9201 -0.3792 -0.0981
vn 0.8545 -0.3673 -0.3673
vn 0.9201 -0.0981 -0.3792
vn 0.7041 -0.7041 -0.0919
vn 0.6663 -0.6663 -0.3347
vn 0.6663 -0.3347 -0.6663
vn 0.7041 -0.0919 -0.7041
vn 0.5774 -0.5774 -0.5773
vn 0.0981 -0.9201 -0.3792
vn 0.3673 -0.8545 -0.3673
vn 0.3792 -0.9201 -0.0981
vn 0.0919 -0.7041 -0.7041
vn 0.3347 -0.6663 -0.6663
vn 0.3792 -0.0981 -0.9201
vn 0.3673 -0.3673 -0.8545
vn 0.0981 -0.3792 -0.9201
vn 0.9201 0.3792 0.0981
vn 0.8545 0.3673 0.3673
vn 0.9201 0.0981 0.3792
vn 0.7041 0.7041 0.0919
vn 0.6663 0.6663 0.3347
vn 0.6663 0.3347 0.6663
vn 0.7041 0.0919 0.7041
vn 0.5774 0.5774 0.5773
vn 0.1004 0.9899 0.1004
vn 0.0981 0.9201 0.3792
vn 0.3673 0.8545 0.3673
vn 0.3792 0.9201 0.0981
vn 0.0919 0.7041 0.7041
vn 0.3347 0.6663 0.6663
vn 0.3792 0.0981 0.9201
vn 0.3673 0.3673 0.8545
vn 0.0981 0.3792 0.9201
vn 0.0981 -0.3792 0.9201
vn 0.3673 -0.3673 0.8545
vn 0.3792 -0.0981 0.9201
vn 0.0919 -0.7041 0.7041
vn 0.3347 -0.6663 0.6663
vn 0.6663 -0.3347 0.6663
vn 0.7041 -0.0919 0.7041
vn 0.5773 -0.5774 0.5774
vn 0.3792 -0.9201 0.0981
vn 0.3673 -0.8545 0.3673
vn 0.0981 -0.9201 0.3792
vn 0.7041 -0.7041 0.0919
vn 0.6663 -0.6663 0.3347
vn 0.9201 -0.0981 0.3792
vn 0.8545 -0.3673 0.3673
vn 0.9201 -0.3792 0.0981
vn -0.9201 0.3792 -0.0981
vn -0.8545 0.3673 -0.3673
vn -0.9201 0.0981 -0.3792
vn -0.7041 0.7041 -0.0919
vn -0.6663 0.6663 -0.3347
vn -0.6663 0.3347 -0.6663
vn -0.7041 0.0919 -0.7041
vn -0.5774 0.5774 -0.5773
vn -0.1004 0.9899 -0.1004
vn -0.0981 0.9201 -0.3792
vn -0.3673 0.8545 -0.3673
vn -0.3792 0.9201 -0.0981
vn -0.0919 0.7041 -0.7041
vn -0.3347 0.6663 -0.6663
vn -0.3792 0.0981 -0.9201
vn -0.3673 0.3673 -0.8545
vn -0.0981 0.3792 -0.9201
vn -0.0981 -0.3792 -0.9201
vn -0.3673 -0.3673 -0.8545
vn -0.3792 -0.0981 -0.9201
vn -0.0919 -0.7041 -0.7041
vn -0.3347 -0.6663 -0.6663
vn -0.6663 -0.3347 -0.6663
vn -0.7041 -0.0919 -0.7041
vn -0.5773 -0.5774 -0.5774
vn -0.3792 -0.9201 -0.0981
vn -0.3673 -0.8545 -0.3673
vn -0.0981 -0.9201 -0.3792
vn -0.7041 -0.7041 -0.0919
vn -0.6663 -0.6663 -0.3347
vn -0.9201 -0.0981 -0.3792
vn -0.8545 -0.3673 -0.3673
vn -0.9201 -0.3792 -0.0981
vn -0.9201 0.0981 0.3792
vn -0.8545 0.3673 0.3673
vn -0.9201 0.3792 0.0981
vn -0.7041 0.0919 0.7041
vn -0.6663 0.3347 0.6663
vn -0.6663 0.6663 0.3347
vn -0.7041 0.7041 0.0919
vn -0.5774 0.5774 0.5773
vn -0.0981 0.3792 0.9201
vn -0.3673 0.3673 0.8545
vn -0.3792 0.0981 0.9201
vn -0.0919 0.7041 0.7041
vn -0.3347 0.6663 0.6663
vn -0.1004 0.9899 0.1004
vn -0.3792 0.9201 0.0981
vn -0.3673 0.8545 0.3673
vn -0.0981 0.9201 0.3792
vn -0.3792 -0.0981 0.9201
vn -0.3673 -0.3673 0.8545
vn -0.0981 -0.3792 0.9201
vn -0.7041 -0.0919 0.7041
vn -0.6663 -0.3347 0.6663
vn -0.3347 -0.6663 0.6663
vn -0.0919 -0.7041 0.7041
vn -0.5774 -0.5774 0.5773
vn -0.9201 -0.3792 0.0981
vn -0.8545 -0.3673 0.3673
vn -0.9201 -0.0981 0.3792
vn -0.7041 -0.7041 0.0919
vn -0.6663 -0.6663 0.3347
vn -0.0981 -0.9201 0.3792
vn -0.3673 -0.8545 0.3673
vn -0.3792 -0.9201 0.0981
s 1
f 58/1/1 41/2/2 116/3/3 134/4/4
f 96/5/5 79/6/6 3/7/7 22/8/8
f 20/9/9 2/10/10 39/11/11 60/12/12
f 135/13/13 115/14/14 77/15/15 98/16/16
f 97/17/17 21/18/18 59/19/19 136/20/20
f 1/21/21 4/22/22 7/23/23 6/24/24
f 4/22/22 5/25/25 8/26/26 7/23/23
f 6/24/24 7/23/23 19/27/27 16/28/28
f 7/23/23 8/26/26 9/29/29 19/27/27
f 2/10/10 10/30/30 13/31/31 12/32/32
f 10/30/30 11/33/33 14/34/34 13/31/31
f 12/32/32 13/31/31 8/35/26 5/36/25
f 13/31/31 14/34/34 9/37/29 8/35/26
f 3/7/7 15/38/35 18/39/36 17/40/37
f 15/38/35 16/41/28 19/42/27 18/39/36
f 17/40/37 18/39/36 14/43/34 11/44/33
f 18/39/36 19/42/27 9/45/29 14/43/34
f 20/9/9 23/46/38 26/47/39 25/48/40
f 23/46/38 24/49/41 27/50/42 26/47/39
f 25/48/40 26/47/39 38/51/43 35/52/44
f 26/47/39 27/50/42 28/53/45 38/51/43
f 21/18/18 29/54/46 32/55/47 31/56/48
f 29/54/46 30/57/49 33/58/50 32/55/47
f 31/56/48 32/55/47 27/59/42 24/60/41
f 32/55/47 33/58/50 28/61/45 27/59/42
f 22/8/8 34/62/51 37/63/52 36/64/53
f 34/62/51 35/65/44 38/66/43 37/63/52
f 36/64/53 37/63/52 33/67/50 30/68/49
f 37/63/52 38/66/43 28/69/45 33/67/50
f 39/11/11 42/70/54 45/71/55 44/72/56
f 42/70/54 43/73/57 46/74/58 45/71/55
f 44/72/56 45/71/55 57/75/59 54/76/60
f 45/71/55 46/74/58 47/77/61 57/75/59
f 40/78/62 48/79/63 51/80/64 50/81/65
f 48/79/63 49/82/66 52/83/67 51/80/64
f 50/81/65 51/80/64 46/84/58 43/85/57
f 51/80/64 52/83/67 47/86/61 46/84/58
f 41/2/2 53/87/68 56/88/69 55/89/70
f 53/87/68 54/90/60 57/91/59 56/88/69
f 55/89/70 56/88/69 52/92/67 49/93/66
f 56/88/69 57/91/59 47/94/61 52/92/67
f 58/1/1 61/95/71 64/96/72 63/97/73
f 61/95/71 62/98/74 65/99/75 64/96/72
f 63/97/73 64/96/72 76/100/76 73/101/77
f 64/96/72 65/99/75 66/102/78 76/100/76
f 59/19/19 67/103/79 70/104/80 69/105/81
f 67/103/79 68/106/82 71/107/83 70/104/80
f 69/105/81 70/104/80 65/108/75 62/109/74
f 70/104/80 71/107/83 66/110/78 65/108/75
f 60/12/12 72/111/84 75/112/85 74/113/86
f 72/111/84 73/114/77 76/115/76 75/112/85
f 74/113/86 75/112/85 71/116/83 68/117/82
f 75/112/85 76/115/76 66/118/78 71/116/83
f 77/15/15 80/119/87 83/120/88 82/121/89
f 80/119/87 81/122/90 84/123/91 83/120/88
f 82/121/89 83/120/88 95/124/92 92/125/93
f 83/120/88 84/123/91 85/126/94 95/124/92
f 78/127/95 86/128/96 89/129/97 88/130/98
f 86/128/96 87/131/99 90/132/100 89/129/97
f 88/130/98 89/129/97 84/133/91 81/134/90
f 89/129/97 90/132/100 85/135/94 84/133/91
f 79/6/6 91/136/101 94/137/102 93/138/103
f 91/136/101 92/139/93 95/140/92 94/137/102
f 93/138/103 94/137/102 90/141/100 87/142/99
f 94/137/102 95/140/92 85/143/94 90/141/100
f 96/5/5 99/144/104 102/145/105 101/146/106
f 99/144/104 100/147/107 103/148/108 102/145/105
f 101/146/106 102/145/105 114/149/109 111/150/110
f 102/145/105 103/148/108 104/151/111 114/149/109
f 97/17/17 105/152/112 108/153/113 107/154/114
f 105/152/112 106/155/115 109/156/116 108/153/113
f 107/154/114 108/153/113 103/157/108 100/158/107
f 108/153/113 109/156/116 104/159/111 103/157/108
f 98/16/16 110/160/117 113/161/118 112/162/119
f 110/160/117 111/163/110 114/164/109 113/161/118
f 112/162/119 113/161/118 109/165/116 106/166/115
f 113/161/118 114/164/109 104/167/111 109/165/116
f 115/14/14 118/168/120 121/169/121 120/170/122
f 118/168/120 119/171/123 122/172/124 121/169/121
f 120/170/122 121/169/121 133/173/125 130/174/126
f 121/169/121 122/172/124 123/175/127 133/173/125
f 116/3/3 124/176/128 127/177/129 126/178/130
f 124/176/128 125/179/131 128/180/132 127/177/129
f 126/178/130 127/177/129 122/181/124 119/182/123
f 127/177/129 128/180/132 123/183/127 122/181/124
f 117/184/133 129/185/134 132/186/135 131/187/136
f 129/185/134 130/188/126 133/189/125 132/186/135
f 131/187/136 132/186/135 128/190/132 125/191/131
f 132/186/135 133/189/125 123/192/127 128/190/132
f 134/4/4 137/193/137 140/194/138 139/195/139
f 137/193/137 138/196/140 141/197/141 140/194/138
f 139/195/139 140/194/138 152/198/142 149/199/143
f 140/194/138 141/197/141 142/200/144 152/198/142
f 135/13/13 143/201/145 146/202/146 145/203/147
f 143/201/145 144/204/148 147/205/149 146/202/146
f 145/203/147 146/202/146 141/206/141 138/207/140
f 146/202/146 147/205/149 142/208/144 141/206/141
f 136/20/20 148/209/150 151/210/151 150/211/152
f 148/209/150 149/212/143 152/213/142 151/210/151
f 150/211/152 151/210/151 147/214/149 144/215/148
f 151/210/151 152/213/142 142/216/144 147/214/149
f 136/20/20 59/19/19 69/105/81 148/209/150
f 148/209/150 69/105/81 62/109/74 149/212/143
f 149/199/143 62/98/74 61/95/71 139/195/139
f 139/195/139 61/95/71 58/1/1 134/4/4
f 59/19/19 21/18/18 31/56/48 67/103/79
f 67/103/79 31/56/48 24/60/41 68/106/82
f 68/117/82 24/49/41 23/46/38 74/113/86
f 74/113/86 23/46/38 20/9/9 60/12/12
f 1/21/21 40/78/62 50/81/65 4/22/22
f 4/22/22 50/81/65 43/85/57 5/25/25
f 5/36/25 43/73/57 42/70/54 12/32/32
f 12/32/32 42/70/54 39/11/11 2/10/10
f 21/18/18 97/17/17 107/154/114 29/54/46
f 29/54/46 107/154/114 100/158/107 30/57/49
f 30/68/49 100/147/107 99/144/104 36/64/53
f 36/64/53 99/144/104 96/5/5 22/8/8
f 117/184/133 78/127/95 88/130/98 129/185/134
f 129/185/134 88/130/98 81/134/90 130/188/126
f 130/174/126 81/122/90 80/119/87 120/170/122
f 120/170/122 80/119/87 77/15/15 115/14/14
f 97/17/17 136/20/20 150/211/152 105/152/112
f 105/152/112 150/211/152 144/215/148 106/155/115
f 106/166/115 144/204/148 143/201/145 112/162/119
f 112/162/119 143/201/145 135/13/13 98/16/16
f 40/78/62 117/184/133 131/187/136 48/79/63
f 48/79/63 131/187/136 125/191/131 49/82/66
f 49/93/66 125/179/131 124/176/128 55/89/70
f 55/89/70 124/176/128 116/3/3 41/2/2
f 41/2/2 58/1/1 63/97/73 53/87/68
f 53/87/68 63/97/73 73/101/77 54/90/60
f 54/76/60 73/114/77 72/111/84 44/72/56
f 44/72/56 72/111/84 60/12/12 39/11/11
f 2/10/10 20/9/9 25/48/40 10/30/30
f 10/30/30 25/48/40 35/52/44 11/33/33
f 11/44/33 35/65/44 34/62/51 17/40/37
f 17/40/37 34/62/51 22/8/8 3/7/7
f 134/4/4 116/3/3 126/178/130 137/193/137
f 137/193/137 126/178/130 119/182/123 138/196/140
f 138/207/140 119/171/123 118/168/120 145/203/147
f 145/203/147 118/168/120 115/14/14 135/13/13
f 78/127/95 1/21/21 6/24/24 86/128/96
f 86/128/96 6/24/24 16/28/28 87/131/99
f 87/142/99 16/41/28 15/38/35 93/138/103
f 93/138/103 15/38/35 3/7/7 79/6/6
f 79/6/6 96/5/5 101/146/106 91/136/101
f 91/136/101 101/146/106 111/150/110 92/139/93
f 92/125/93 111/163/110 110/160/117 82/121/89
f 82/121/89 110/160/117 98/16/16 77/15/15
f 1/21/21 78/127/95 117/184/133 40/78/62

View File

@@ -0,0 +1,29 @@
#version 330 core
layout(location = 0) out vec4 frag_color;
uniform vec3 light_pos;
uniform vec4 solid_color;
uniform vec3 camera;
in vec3 normal;
in vec3 frag_position;
void main() {
vec4 light_color = vec4(1, 1, 1, 1);
vec3 normal_norm = normalize(normal);
vec3 light_direction_norm = normalize(light_pos - frag_position);
float ambient_strength = 0.15;
vec4 ambient = ambient_strength * light_color;
float diffuse_strength = max(dot(normal_norm, light_direction_norm), 0.0);
vec4 diffuse = diffuse_strength * light_color;
float specular_strength = 0.9;
vec3 view_direction_norm = normalize(camera - frag_position);
vec3 reflect_dir = reflect(-light_direction_norm, normal_norm);
float spec = pow(max(dot(view_direction_norm, reflect_dir), 0.0), 32);
vec4 specular = specular_strength * spec * light_color;
frag_color = specular + (ambient + diffuse) * solid_color;
}

View File

@@ -0,0 +1,19 @@
#version 330 core
layout (location = 0) in vec3 a_xyz;
layout (location = 1) in vec2 a_uv;
layout (location = 2) in vec3 a_normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 normal;
out vec3 frag_position;
void main() {
vec4 a_xyz_vec4 = vec4(a_xyz, 1);
frag_position = (model * a_xyz_vec4).xyz;
normal = mat3(transpose(inverse(model))) * a_normal;
gl_Position = projection * view * model * a_xyz_vec4;
}

View File

@@ -0,0 +1,61 @@
#version 330 core
out vec4 pixel_color;
in vec4 frag_color;
in vec2 frag_dest_position;
in vec2 frag_dest_center;
in vec2 frag_dest_half_size;
in float frag_softness;
in float frag_border_radius;
in float frag_border_thickness;
in vec4 frag_border_color;
in vec2 uv;
uniform sampler2D rect_tex;
float roundedRectSDF(vec2 sample_pos, vec2 rect_center, vec2 rect_half_size, float r) {
vec2 d2 = (abs(rect_center - sample_pos) - rect_half_size + vec2(r, r));
return min(max(d2.x, d2.y), 0.0) + length(max(d2, 0.0)) - r;
}
void main() {
vec2 softness_padding = vec2(
max(0, frag_softness*2-1),
max(0, frag_softness*2-1));
float border_factor = 1.0f;
if (frag_border_thickness != 0) {
vec2 interior_half_size = frag_dest_half_size - vec2(frag_border_thickness);
float interior_radius_reduce_f = min(
interior_half_size.x / frag_dest_half_size.x,
interior_half_size.y / frag_dest_half_size.y);
float interior_corner_radius = frag_border_radius * interior_radius_reduce_f * interior_radius_reduce_f;
float inside_d = roundedRectSDF(
frag_dest_position,
frag_dest_center,
interior_half_size - softness_padding,
interior_corner_radius);
float inside_f = smoothstep(0, 2*frag_softness, inside_d);
border_factor = inside_f;
}
float dist = roundedRectSDF(
frag_dest_position,
frag_dest_center,
frag_dest_half_size - softness_padding,
frag_border_radius);
// For texturing later
float sample = 1;
float sdf_factor = 1 - smoothstep(0, 2*frag_softness, dist);
vec4 out_color = frag_color; //* texture(rect_tex, uv);
pixel_color = frag_border_color * sample * sdf_factor * border_factor
+ out_color * sample * sdf_factor;
};

View File

@@ -0,0 +1,56 @@
#version 330 core
layout (location = 0) in vec2 p0;
layout (location = 1) in vec2 p1;
layout (location = 2) in vec4 color;
layout (location = 3) in float border_radius;
layout (location = 4) in float border_thickness;
layout (location = 5) in vec4 border_color;
layout (location = 6) in float edge_softness;
uniform mat4 projection;
out vec4 frag_color;
out vec2 frag_dest_position;
out vec2 frag_dest_center;
out vec2 frag_dest_half_size;
out float frag_softness;
out float frag_border_radius;
out float frag_border_thickness;
out vec4 frag_border_color;
out vec2 uv;
const vec2 rectangle_uv[4] = vec2[](
vec2(0, 1),
vec2(0, 0),
vec2(1, 1),
vec2(1, 0)
);
const vec2 rectangle_vertices[4] = vec2[](
vec2(-1, -1),
vec2(-1, 1),
vec2( 1, -1),
vec2( 1, 1)
);
void main() {
vec2 dest_half_size = (p1 - p0) / 2;
vec2 dest_center = (p1 + p0) / 2;
vec2 dest_position = rectangle_vertices[gl_VertexID] * dest_half_size + dest_center;
gl_Position = projection * vec4(
dest_position,
0,
1
);
frag_color = color;
frag_dest_position = dest_position;
frag_dest_center = dest_center;
frag_dest_half_size = dest_half_size;
frag_border_radius = border_radius;
frag_border_thickness = border_thickness;
frag_border_color = border_color;
frag_softness = edge_softness;
uv = rectangle_uv[gl_VertexID];
}

View File

@@ -0,0 +1,11 @@
#version 330 core
out vec4 pixel_color;
in vec4 frag_color;
in vec2 frag_uv_position;
uniform sampler2D glyph_atlas;
void main() {
pixel_color = vec4(frag_color.xyz,texture(glyph_atlas, frag_uv_position).r);
};

View File

@@ -0,0 +1,58 @@
#version 330 core
layout (location = 0) in vec2 begin;
layout (location = 1) in int glyph;
layout (location = 2) in float fontSize;
layout (location = 3) in vec4 color;
uniform samplerBuffer glyph_table;
uniform mat4 projection;
uniform sampler2D font;
out vec4 frag_color;
out vec2 frag_uv_position;
const vec2 rectangle_vertices[4] = vec2[](
vec2(-1, -1), // bl
vec2(-1, 1), // tl
vec2( 1, -1), // br
vec2( 1, 1) // tr
);
const vec2 uv0_vertices[4] = vec2[](
vec2(1, 0), // bl
vec2(1, 1), // tl
vec2(0, 0), // br
vec2(0, 1) // tr
);
const vec2 uv1_vertices[4] = vec2[](
vec2(0, 1),
vec2(0, 0),
vec2(1, 1),
vec2(1, 0)
);
void main() {
vec4 chunk1 = texelFetch(glyph_table, glyph * 2 + 0);
vec4 chunk2 = texelFetch(glyph_table, glyph * 2 + 1);
vec2 uv0 = chunk1.xy;
vec2 uv1 = chunk1.zw;
vec2 offset = chunk2.xy;
vec2 dims = chunk2.zw;
vec2 p0 = begin + offset*fontSize;
vec2 p1 = begin + (offset + dims)*fontSize;
vec2 dest_half_size = (p1 - p0) / 2;
vec2 dest_center = (p1 + p0) / 2;
vec2 dest_position = rectangle_vertices[gl_VertexID] * dest_half_size + dest_center;
vec2 uv_position = uv0 * uv0_vertices[gl_VertexID] + uv1 * uv1_vertices[gl_VertexID];
gl_Position = projection * vec4(dest_position, 0, 1);
frag_color = color;
frag_uv_position = vec2(uv_position.x, uv_position.y);
}

24
gfx/debug.c Normal file
View File

@@ -0,0 +1,24 @@
#include "debug.h"
void printVec3(Vec3 vec) {
print(
"┌ ┐\n"
"│%7.2f%, %7.2f, %7.2f │\n"
"└ ┘\n",
vec.x, vec.y, vec.z);
}
void printMatrix(Matrix mat) {
print(
"┌ ┐\n"
"│%7.2f%, %7.2f, %7.2f, %7.2f │\n"
"│%7.2f%, %7.2f, %7.2f, %7.2f │\n"
"│%7.2f%, %7.2f, %7.2f, %7.2f │\n"
"│%7.2f%, %7.2f, %7.2f, %7.2f │\n"
"└ ┘\n",
mat.m0, mat.m1, mat.m2, mat.m3,
mat.m4, mat.m5, mat.m6, mat.m7,
mat.m8, mat.m9, mat.m10, mat.m11,
mat.m12, mat.m13, mat.m14, mat.m15);
}

10
gfx/debug.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef DEBUG_H
#define DEBUG_H
#include "../core.h"
#include "../vendor/raymath.h"
void printRLVec3(Vec3 vector);
void printMatrix(Matrix matrix);
#endif

102
gfx/geometry.c Normal file
View File

@@ -0,0 +1,102 @@
#include "geometry.h"
#include "gfx-common.h"
#include "../core.h"
// Buffer layout:
// X, Y, Z, U, V
const FloatList triangle_vertices = AsList(FloatList, {
-0.5f, -0.5f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.0f, 0.5f, 0.5f,
0.0f, 0.5f, 0.0f, 0.0f, 0.0f,
});
const UInt32List triangle_indices = AsList(UInt32List, {
0, 1, 2
});
const FloatList cube_vertices = AsList(FloatList, {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
});
const UInt32List cube_indices = AsList(UInt32List, {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
});
const FloatList square_xyz = AsList(FloatList, {
200.0f, 200.0f, 0.0f,
200.0f, -200.0f, 0.0f,
-200.0f, -200.0f, 0.0f,
-200.0f, 200.0f, 0.0f,
});
const FloatList square_uv = AsList(FloatList, {
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f,
});
const UInt32List square_indices = AsList(UInt32List, {
0, 1, 3,
1, 2, 3,
});
const Shape TRIANGLE = {
triangle_indices,
triangle_vertices,
triangle_vertices,
};
const Shape SQUARE = {
square_indices,
square_uv,
square_xyz,
};
const Shape CUBE = {
cube_indices,
triangle_vertices,
triangle_vertices,
};

16
gfx/geometry.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef DJSTDLIB_GEOMETRY_H
#define DJSTDLIB_GEOMETRY_H
#include "gfx-common.h"
typedef struct {
UInt32List indices;
FloatList uv;
FloatList xyz;
} Shape;
extern const Shape TRIANGLE;
extern const Shape SQUARE;
extern const Shape CUBE;
#endif

11
gfx/gfx-common.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef DJSTDLIB_GFX_COMMON_H
#define DJSTDLIB_GFX_COMMON_H
#include "../core.h"
DefineList(Vec2, Vec2);
DefineList(Vec4, Vec4);
DefineList(real32, Float);
DefineList(uint32, UInt32);
#endif

15
gfx/gfx.c Normal file
View File

@@ -0,0 +1,15 @@
#define RAYMATH_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STB_TRUETYPE_IMPLEMENTATION
#define TINYOBJ_LOADER_C_IMPLEMENTATION
#include "platform.c"
#include "render.c"
#include "ui.c"
#include "debug.c"
#include "geometry.c"
#include "Mesh.c"
#include "Shader.c"
#include "Texture.c"
#include "Color.c"
#include "world/world.c"

14
gfx/gfx.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef DJSTDLIB_GFX_H
#define DJSTDLIB_GFX_H
#include "platform.h"
#include "render.h"
#include "ui.h"
#include "debug.h"
#include "geometry.h"
#include "Mesh.h"
#include "Shader.h"
#include "Texture.h"
#include "Color.h"
#endif

1
gfx/lib_include.txt Normal file
View File

@@ -0,0 +1 @@
-lglfw -lgl -lm

211
gfx/main.c Normal file
View File

@@ -0,0 +1,211 @@
// Library initialisation
#define RAYMATH_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STB_TRUETYPE_IMPLEMENTATION
#define TINYOBJ_LOADER_C_IMPLEMENTATION
// Project
#include "lib/djstdlib/core.c"
#include "gfx/gfx.c"
#include "world/world.c"
#include "ui.c"
#include "render.c"
#include "debug.c"
// Graphics bindings and libs
#include "lib/raymath.h"
#include "lib/glad/glad.c"
#include "GLFW/glfw3.h"
typedef struct Frame Frame;
struct Frame {
uint32 width;
uint32 height;
int32 x;
int32 y;
Camera cam;
};
typedef struct GlobalState GlobalState;
struct GlobalState {
struct {
GLFWwindow *handle;
int32 width;
int32 height;
struct {
GLFWcursor *pointer;
GLFWcursor *arrow;
GLFWcursor *forbidden;
} cursors;
} window;
UI_Context *ui;
};
void framebufferSizeCallback(GLFWwindow *window, int width, int height) {
Soma *soma = (Soma *)glfwGetWindowUserPointer(window);
soma->renderer->width = width;
soma->renderer->height = height;
}
GLFWwindow *initWindowAndGL(uint32 windowWidth, uint32 windowHeight) {
if (!glfwInit()) {
return NULL;
}
#ifdef GLFW_WAYLAND_APP_ID
glfwWindowHintString(GLFW_WAYLAND_APP_ID, "Somaesque");
#endif
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_SAMPLES, 4);
GLFWwindow *window = glfwCreateWindow(
windowWidth,
windowHeight,
#ifdef DJSTDLIB_DEBUG
"Somaesque (djstdlib_debug)",
#else
"Somaesque",
#endif
NULL,
NULL);
if (window == NULL) {
print("Failed to create GLFW window\n");
glfwTerminate();
return NULL;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
print("Failed to initilaize GLAD\n");
return NULL;
}
glViewport(0, 0, windowWidth, windowHeight);
glfwSwapInterval(1);
glfwSetInputMode(window, GLFW_CURSOR | GLFW_RAW_MOUSE_MOTION, GLFW_CURSOR_NORMAL);
glfwSetWindowSize(window, windowWidth, windowHeight);
glEnable(GL_DEPTH_TEST);
glEnable(GL_MULTISAMPLE);
return window;
}
static void onMouseScroll(GLFWwindow *window, real64 deltaX, real64 deltaY) {
Soma *soma = (Soma *)glfwGetWindowUserPointer(window);
soma->ui->input->mouse.scroll.dX += deltaX;
soma->ui->input->mouse.scroll.dY += deltaY;
}
static void updateWindow(Soma *soma) {
glfwGetWindowSize(soma->window.handle, &soma->window.width, &soma->window.height);
soma->renderer->width = soma->window.width;
soma->renderer->height = soma->window.height;
}
const int32 TARGET_FPS = 144;
void APIENTRY glDebugCallback(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length,
const GLchar* message, const void* userParam)
{
fprintf(stderr, "GL: %s\n", message);
}
int mainGfx() {
Arena *arena = arenaAlloc(Megabytes(128));
Arena *solutionsArena = arenaAlloc(Megabytes(128));
int32 winWidth = 800;
int32 winHeight = 600;
GLFWwindow *windowHandle = initWindowAndGL(winWidth, winHeight);
if (!windowHandle) {
return 1;
}
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(glDebugCallback, NULL);
Scene mainScene = createScene(arena);
Camera cam = createCamera(winWidth, winHeight);
Renderer renderer = createRenderer(arena, &mainScene, &cam, createSceneGraphNode(&mainScene));
renderer.solidShader = &solidColorShader;
renderer.phongShader = &phongShader;
renderer.cubeMesh = &cubeMesh;
Arena *uiArena = arenaAlloc(Megabytes(64));
UI_Context ui = ui_initContext(uiArena, &renderer);
glfwSetWindowUserPointer(windowHandle, &soma);
glfwSetFramebufferSizeCallback(windowHandle, framebufferSizeCallback);
glfwSetScrollCallback(windowHandle, onMouseScroll);
// Render loop
real64 lastFrame = glfwGetTime();
real64 timeDelta = 1.0f / TARGET_FPS;
real64 frameStart = lastFrame;
Input lastInput = {0};
Input currInput = {0};
while (!glfwWindowShouldClose(soma.window.handle)) {
glfwPollEvents();
lastInput = currInput;
currInput = getCurrentInput(soma.window.handle);
ui.prevInput = &lastInput;
ui.input = &currInput;
updateWindow(&soma);
processInput(&soma);
Render(&renderer) {
UI_Pass(&ui) {
ui_Soma(&ui, &soma);
}
switch (ui.cursorType) {
case UI_Cursor_Arrow:
glfwSetCursor(soma.window.handle, soma.window.cursors.arrow);
break;
case UI_Cursor_Pointer:
glfwSetCursor(soma.window.handle, soma.window.cursors.pointer);
break;
case UI_Cursor_Forbidden:
glfwSetCursor(soma.window.handle, soma.window.cursors.forbidden);
break;
default:
glfwSetCursor(soma.window.handle, soma.window.cursors.arrow);
break;
}
}
updatePolycubeDisplay(&soma);
recalcScene(soma.scene);
glfwSwapBuffers(soma.window.handle);
real64 frameEnd = glfwGetTime();
real64 frameTime = frameEnd - frameStart;
lastFrame = frameStart;
//println("FPS: %.7f", 1/(frameTime));
frameStart = glfwGetTime();
soma.prevState = soma.state;
soma.state.isInitialState = false;
}
glfwTerminate();
return 0;
}

163
gfx/platform.c Normal file
View File

@@ -0,0 +1,163 @@
#include "platform.h"
#define RAYMATH_IMPLEMENTATION
#include "../vendor/glad/glad.c"
#include "GLFW/glfw3.h"
typedef struct OS_WindowPlatformData OS_WindowPlatformData;
struct OS_WindowPlatformData {
OS_WindowEventCallback eventHandler;
real64 mouseWheelFrameDX;
real64 mouseWheelFrameDY;
};
static void os_glfw_onFramebufferResize(GLFWwindow *window, int32 width, int32 height) {
OS_Window *osWindow = (OS_Window *)glfwGetWindowUserPointer(window);
osWindow->width = width;
osWindow->height = height;
OS_WindowEvent event = (OS_WindowEvent){.kind=OS_WindowEventKind_Resize, .resize={width, height}};
((OS_WindowPlatformData *)osWindow->platformData)->eventHandler(&event, osWindow);
}
static void os_glfw_onMouseScroll(GLFWwindow *window, real64 x, real64 y) {
OS_Window *osWindow = (OS_Window *)glfwGetWindowUserPointer(window);
}
OS_WindowInitResult os_windowInit(Arena *arena, OS_WindowInitParams params) {
Scratch scratch = scratchStart(NULL, 0);
OS_WindowInitResult result = {.result=NULL, .valid=true};
if (glfwInit()) {
const char *windowNameCStr = cstring(scratch.arena, params.name);
#ifdef GLFW_WAYLAND_APP_ID
glfwWindowHintString(GLFW_WAYLAND_APP_ID, windowNameCStr);
#endif
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_SAMPLES, 4);
GLFWwindow *glfwWindow = glfwCreateWindow(
params.width,
params.height,
#ifdef DJSTDLIB_DEBUG
cstring(scratch.arena, print("%s (djstdlib_debug)", windowNameCStr)),
#else
windowNameCStr,
#endif
NULL,
NULL);
if (glfwWindow != NULL) {
glfwMakeContextCurrent(glfwWindow);
if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
glViewport(0, 0, params.width, params.height);
glfwSwapInterval(1);
glfwSetInputMode(glfwWindow, GLFW_CURSOR | GLFW_RAW_MOUSE_MOTION, GLFW_CURSOR_NORMAL);
glfwSetWindowSize(glfwWindow, params.width, params.height);
glEnable(GL_DEPTH_TEST);
glEnable(GL_MULTISAMPLE);
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
//glDebugMessageCallback(glDebugCallback, NULL);
OS_Window *osWindow = PushStructZero(arena, OS_Window);
OS_WindowPlatformData *platformData = PushStructZero(arena, OS_WindowPlatformData);
osWindow->platformData = platformData;
platformData->eventHandler = params.eventHandler;
platformData->eventHandler = params.eventHandler;
osWindow->userData = params.data;
osWindow->width = params.width;
osWindow->height = params.height;
osWindow->handle = glfwWindow;
glfwSetWindowUserPointer(glfwWindow, osWindow);
glfwSetFramebufferSizeCallback(glfwWindow, os_glfw_onFramebufferResize);
glfwSetScrollCallback(glfwWindow, os_glfw_onMouseScroll);
result.result = osWindow;
} else {
print("os_windowInit::Failed to initilaize GLAD\n");
result.valid = false;
}
} else {
print("os_windowInit::Failed to create GLFW window\n");
glfwTerminate();
result.valid = false;
}
}
scratchEnd(scratch);
return result;
}
bool os_windowShouldClose(OS_Window *window) {
return (bool)glfwWindowShouldClose((GLFWwindow*)window->handle);
}
static bool glfwMouse(GLFWwindow *window, int mouseBtnCode) {
switch (glfwGetMouseButton(window, mouseBtnCode)) {
case GLFW_RELEASE: return false;
case GLFW_PRESS: return true;
default: return false;
}
}
static bool glfwKey(GLFWwindow *window, int keyCode) {
switch (glfwGetKey(window, keyCode)) {
case GLFW_RELEASE: return false;
case GLFW_PRESS: return true;
default: return false;
}
}
void os_windowGetInput(OS_Window *window, OS_Input *input) {
*input = (OS_Input){0};
GLFWwindow *handle = (GLFWwindow*)window->handle;
input->keyboard.escape = glfwKey(handle, GLFW_KEY_ESCAPE);
input->keyboard.enter = glfwKey(handle, GLFW_KEY_ENTER);
input->keyboard.space = glfwKey(handle, GLFW_KEY_SPACE);
input->keyboard.lshift = glfwKey(handle, GLFW_KEY_LEFT_SHIFT);
input->keyboard.w = glfwKey(handle, GLFW_KEY_W);
input->keyboard.x = glfwKey(handle, GLFW_KEY_X);
input->keyboard.y = glfwKey(handle, GLFW_KEY_Y);
input->keyboard.z = glfwKey(handle, GLFW_KEY_Z);
input->mouse.btnLeft = glfwMouse(handle, GLFW_MOUSE_BUTTON_LEFT);
input->mouse.btnRight = glfwMouse(handle, GLFW_MOUSE_BUTTON_RIGHT);
input->mouse.btnMiddle = glfwMouse(handle, GLFW_MOUSE_BUTTON_MIDDLE);
real64 mouseX;
real64 mouseY;
glfwGetCursorPos(handle, &mouseX, &mouseY);
input->mouse.point = (Vec2){(real32)mouseX, (real32)mouseY};
input->mouse.scroll.dX = ((OS_WindowPlatformData *)window->platformData)->mouseWheelFrameDX;
input->mouse.scroll.dY = ((OS_WindowPlatformData *)window->platformData)->mouseWheelFrameDY;
}
void os_windowSwapBuffers(OS_Window *window) {
glfwSwapBuffers((GLFWwindow*)window->handle);
}
void os_windowFrameBegin(OS_Window *window) {
OS_WindowPlatformData *platformData = (OS_WindowPlatformData*)window->platformData;
platformData->mouseWheelFrameDX = 0;
platformData->mouseWheelFrameDY = 0;
glfwPollEvents();
}
void os_windowFrameEnd(OS_Window *window) {
real64 end = glfwGetTime();
real64 frameTime = end - window->frame.start;
window->frame.fps = 1.0f/frameTime;
window->frame.start = glfwGetTime();
}

91
gfx/platform.h Normal file
View File

@@ -0,0 +1,91 @@
#ifndef PLATFORM_H
#define PLATFORM_H
#include "../core.h"
typedef struct OS_Input OS_Input;
struct OS_Input {
struct {
bool escape;
bool enter;
bool space;
bool lshift;
bool x;
bool y;
bool z;
bool w;
} keyboard;
struct {
union {
struct {
real32 x;
real32 y;
};
Vec2 point;
};
struct {
real64 dX;
real64 dY;
} scroll;
bool btnLeft;
bool btnRight;
bool btnMiddle;
} mouse;
};
enum OS_WindowEventKind {
OS_WindowEventKind_Resize,
// ---
OS_WindowEventKind_Count,
};
typedef struct OS_WindowEvent OS_WindowEvent;
struct OS_WindowEvent {
enum OS_WindowEventKind kind;
union {
struct {
int32 width;
int32 height;
} resize;
};
};
struct OS_Window;
typedef void (*OS_WindowEventCallback)(OS_WindowEvent *e, struct OS_Window *userData);
typedef struct OS_WindowInitParams OS_WindowInitParams;
struct OS_WindowInitParams {
string name;
int32 width;
int32 height;
void *data;
OS_WindowEventCallback eventHandler;
};
typedef struct OS_Window OS_Window;
struct OS_Window {
void *handle;
int32 width;
int32 height;
real64 fps;
void *userData;
void *platformData;
struct {
real64 start;
real64 fps;
} frame;
};
DefineResult(OS_Window*, OS_WindowInit);
OS_WindowInitResult os_windowInit(Arena *arena, OS_WindowInitParams params);
bool os_windowShouldClose(OS_Window *window);
void os_windowProcessEvents(OS_Window *window);
void os_windowGetInput(OS_Window *window, OS_Input *input);
void os_windowSwapBuffers(OS_Window *window);
void os_windowFrameBegin(OS_Window *window);
void os_windowFrameEnd(OS_Window *window);
void os_windowTerminate(OS_Window *window);
#endif

275
gfx/render.c Normal file
View File

@@ -0,0 +1,275 @@
#include "render.h"
static RenderObjects_Char createCharObjects(Arena *arena, size_t count) {
RenderObjects_Char result = {0};
result.count = count;
result.begin.buf = PushFullList(arena, Vec2List, count);
result.glyph.buf = PushFullList(arena, IntList, count);
result.fontSize.buf = PushFullList(arena, FloatList, count);
result.color.buf = PushFullList(arena, Vec4List, count);
glGenVertexArrays(1, &result.vao);
glGenBuffers(1, &result.begin.bufId);
glGenBuffers(1, &result.glyph.bufId);
glGenBuffers(1, &result.fontSize.bufId);
glGenBuffers(1, &result.color.bufId);
glBindVertexArray(result.vao);
int32 bufItemSize = sizeof(result.begin.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.begin.bufId);
glBufferData(GL_ARRAY_BUFFER, result.begin.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(0, 1);
glEnableVertexAttribArray(0);
bufItemSize = sizeof(result.glyph.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.glyph.bufId);
glBufferData(GL_ARRAY_BUFFER, result.glyph.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribIPointer(1, 1, GL_INT, bufItemSize, NULL);
glVertexAttribDivisor(1, 1);
glEnableVertexAttribArray(1);
bufItemSize = sizeof(result.fontSize.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.fontSize.bufId);
glBufferData(GL_ARRAY_BUFFER, result.fontSize.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(2, 1);
glEnableVertexAttribArray(2);
bufItemSize = sizeof(result.color.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.color.bufId);
glBufferData(GL_ARRAY_BUFFER, result.color.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(3, 1);
glEnableVertexAttribArray(3);
return result;
}
static RenderObjects_Rect createRectangleObjects(Arena *arena, size_t count) {
RenderObjects_Rect result = {0};
result.count = count;
result.p0.buf = PushFullList(arena, Vec2List, count);
result.p1.buf = PushFullList(arena, Vec2List, count);
result.color.buf = PushFullList(arena, Vec4List, count);
result.borderRadius.buf = PushFullList(arena, FloatList, count);
result.borderThickness.buf = PushFullList(arena, FloatList, count);
result.borderColor.buf = PushFullList(arena, Vec4List, count);
result.edgeSoftness.buf = PushFullList(arena, FloatList, count);
glGenVertexArrays(1, &result.vao);
glGenBuffers(1, &result.p0.bufId);
glGenBuffers(1, &result.p1.bufId);
glGenBuffers(1, &result.color.bufId);
glGenBuffers(1, &result.borderRadius.bufId);
glGenBuffers(1, &result.borderThickness.bufId);
glGenBuffers(1, &result.borderColor.bufId);
glGenBuffers(1, &result.edgeSoftness.bufId);
glBindVertexArray(result.vao);
int32 bufItemSize = sizeof(result.p0.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.p0.bufId);
glBufferData(GL_ARRAY_BUFFER, result.p0.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(0, 1);
glEnableVertexAttribArray(0);
bufItemSize = sizeof(result.p1.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.p1.bufId);
glBufferData(GL_ARRAY_BUFFER, result.p1.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(1, 1);
glEnableVertexAttribArray(1);
bufItemSize = sizeof(result.color.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.color.bufId);
glBufferData(GL_ARRAY_BUFFER, result.color.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(2, 1);
glEnableVertexAttribArray(2);
bufItemSize = sizeof(result.borderRadius.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.borderRadius.bufId);
glBufferData(GL_ARRAY_BUFFER, result.borderRadius.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(3, 1);
glEnableVertexAttribArray(3);
bufItemSize = sizeof(result.borderThickness.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.borderThickness.bufId);
glBufferData(GL_ARRAY_BUFFER, result.borderThickness.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(4, 1);
glEnableVertexAttribArray(4);
bufItemSize = sizeof(result.borderColor.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.borderColor.bufId);
glBufferData(GL_ARRAY_BUFFER, result.borderColor.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(5, 1);
glEnableVertexAttribArray(5);
bufItemSize = sizeof(result.edgeSoftness.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.edgeSoftness.bufId);
glBufferData(GL_ARRAY_BUFFER, result.edgeSoftness.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(6, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(6, 1);
glEnableVertexAttribArray(6);
return result;
}
#define GL_UpdateBuffer(buffer) \
glBindBuffer(GL_ARRAY_BUFFER, (buffer).bufId);\
glBufferSubData(GL_ARRAY_BUFFER, 0, (buffer).buf.length * sizeof((buffer).underlying), (buffer).buf.data);
static void updateCharObjectBuffers(Renderer *r) {
glBindVertexArray(r->chars.vao);
GL_UpdateBuffer(r->chars.begin);
GL_UpdateBuffer(r->chars.glyph);
GL_UpdateBuffer(r->chars.fontSize);
GL_UpdateBuffer(r->chars.color);
}
static void updateRectangleObjectBuffers(Renderer *r) {
glBindVertexArray(r->rects.vao);
GL_UpdateBuffer(r->rects.p0);
GL_UpdateBuffer(r->rects.p1);
GL_UpdateBuffer(r->rects.color);
GL_UpdateBuffer(r->rects.borderRadius);
GL_UpdateBuffer(r->rects.borderThickness);
GL_UpdateBuffer(r->rects.borderColor);
GL_UpdateBuffer(r->rects.edgeSoftness);
}
Renderer createRenderer(Arena *arena, Scene *scene, Camera *cam, int32 light) {
return (Renderer){
.scene = scene,
.light = light,
.camera = cam,
.rects = createRectangleObjects(arena, 1024),
.chars = createCharObjects(arena, 10000),
};
}
void renderBegin(Renderer *r) {
r->rects.p0.buf.length = 0;
r->rects.p1.buf.length = 0;
r->rects.color.buf.length = 0;
r->rects.borderRadius.buf.length = 0;
r->rects.borderThickness.buf.length = 0;
r->rects.borderColor.buf.length = 0;
r->rects.edgeSoftness.buf.length = 0;
r->chars.begin.buf.length = 0;
r->chars.glyph.buf.length = 0;
r->chars.fontSize.buf.length = 0;
r->chars.color.buf.length = 0;
r->sceneWidth = 0;
r->sceneHeight = 0;
r->sceneX = 0;
r->sceneY = 0;
}
#define CHECK() do{ GLenum e=glGetError(); if(e) printf("GL err 0x%x at %s:%d\n",e,__FILE__,__LINE__); }while(0)
void renderEnd(Renderer *r) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
// --- 3D Scene ---
glViewport(r->sceneX, r->height - r->sceneY - r->sceneHeight, r->sceneWidth, r->sceneHeight);
cameraSetAspect(r->camera, r->sceneWidth, r->sceneHeight);
glUseProgram(r->phongShader->progId);
setUniformMat4fv(r->phongShader, "projection", &r->camera->proj);
setUniformMat4fv(r->phongShader, "view", &r->camera->view);
SceneGraphNode *lightGraphNode = getSceneGraphNode(r->scene, r->light);
setUniform3fv(r->phongShader, "light_pos", &lightGraphNode->translation);
setUniform3fv(r->phongShader, "camera", &r->camera->pos);
//glBindVertexArray(r->cubeMesh->vao);
int model_uniform = getUniformLocation(r->phongShader, "model");
int solid_color_uniform = getUniformLocation(r->phongShader, "solid_color");
for (EachIn(r->scene->entities, i)) {
Entity *entity = &r->scene->entities.data[i];
if (entity->flags & EntityFlags_Render && entity->flags & EntityFlags_Visible) {
setUniform4fvByLoc(solid_color_uniform, &entity->color);
setUniformMat4fvByLoc(model_uniform, &getSceneGraphNode(r->scene, entity->graphNodeHandle)->world);
glBindTexture(GL_TEXTURE_2D, entity->tex->tex_id);
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)entity->mesh->num_indices);
entity->flags &= ~EntityFlags_Render;
}
}
// --- UI overlay ---
glViewport(0, 0, r->width, r->height);
glUseProgram(r->solidShader->progId);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Matrix ortho = MatrixOrtho(0.0, r->width, r->height, 0.0, -1.0, 1.0);
// - 1. Rects
updateRectangleObjectBuffers(r);
setUniformMat4fv(r->solidShader, "projection", &ortho);
glBindVertexArray(r->rects.vao);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->rects.p0.buf.length);
// - 2. Text
updateCharObjectBuffers(r);
glUseProgram(r->textShader->progId);
setUniformMat4fv(r->textShader, "projection", &ortho);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, r->activeFont->texId);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, r->activeFont->glyphTableTexId);
setUniform1i(r->textShader, "glyph_table", 1);
glBindVertexArray(r->chars.vao);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->chars.begin.buf.length);
glDisable(GL_BLEND);
glActiveTexture(GL_TEXTURE0);
}
void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, Vec4 color, real32 borderRadius, real32 borderThickness, Vec4 borderColor) {
x = roundf(x);
y = roundf(y);
width = roundf(width);
height = roundf(height);
ListAppend(r->rects.p0.buf, ((Vec2){ x, y }));
ListAppend(r->rects.p1.buf, ((Vec2){ x + width, y + height }));
ListAppend(r->rects.color.buf, color);
ListAppend(r->rects.borderRadius.buf, borderRadius);
ListAppend(r->rects.borderThickness.buf, borderThickness);
ListAppend(r->rects.borderColor.buf, borderColor);
ListAppend(r->rects.edgeSoftness.buf, 0.15f);
}
void rendererPlaceString(Renderer *r, string s, real32 x, real32 y, Vec4 color, real32 fontSize) {
real32 ratio = fontSize / r->activeFont->lineHeight;
real32 charWidth = ratio * r->activeFont->charWidth;
for (int i = 0; i < s.length; i++) {
ListAppend(r->chars.begin.buf, ((Vec2){ .x=x + i*charWidth, .y=y }));
ListAppend(r->chars.glyph.buf, s.str[i] - 32);
ListAppend(r->chars.fontSize.buf, ratio);
ListAppend(r->chars.color.buf, (color));
}
}

102
gfx/render.h Normal file
View File

@@ -0,0 +1,102 @@
#ifndef DJSTDLIB_GFX_RENDER_H
#define DJSTDLIB_GFX_RENDER_H
#include "gfx-common.h"
#include "Shader.h"
#include "Texture.h"
#include "Mesh.h"
#include "world/world.h"
typedef struct RenderObject_BufferInt32 RenderObject_BufferInt32;
struct RenderObject_BufferInt32 {
IntList buf;
uint32 bufId;
IntList_underlying underlying;
};
typedef struct RenderObject_BufferFloat RenderObject_BufferFloat;
struct RenderObject_BufferFloat {
FloatList buf;
uint32 bufId;
FloatList_underlying underlying;
};
typedef struct RenderObject_BufferVec2 RenderObject_BufferVec2;
struct RenderObject_BufferVec2 {
Vec2List buf;
uint32 bufId;
Vec2List_underlying underlying;
};
typedef struct RenderObject_BufferVec4 RenderObject_BufferVec4;
struct RenderObject_BufferVec4 {
Vec4List buf;
uint32 bufId;
Vec4List_underlying underlying;
};
typedef struct RenderObjects_Rect RenderObjects_Rect;
struct RenderObjects_Rect {
uint32 vao;
uint64 count;
RenderObject_BufferVec2 p0;
RenderObject_BufferVec2 p1;
RenderObject_BufferVec4 color;
RenderObject_BufferFloat borderRadius;
RenderObject_BufferFloat borderThickness;
RenderObject_BufferVec4 borderColor;
RenderObject_BufferFloat edgeSoftness;
};
typedef struct RenderObjects_Char RenderObjects_Char;
struct RenderObjects_Char {
uint32 vao;
uint64 count;
RenderObject_BufferVec2 begin;
RenderObject_BufferInt32 glyph;
RenderObject_BufferFloat fontSize;
RenderObject_BufferVec4 color;
};
typedef struct GlyphData GlyphData;
struct GlyphData {
real32 x0;
real32 y0;
real32 x1;
real32 y1;
};
typedef struct Renderer Renderer;
struct Renderer {
RenderObjects_Rect rects;
RenderObjects_Char chars;
Shader *phongShader;
Shader *solidShader;
Shader *textShader;
Font *activeFont;
int32 width;
int32 height;
int32 sceneWidth;
int32 sceneHeight;
int32 sceneX;
int32 sceneY;
Scene *scene;
Camera *camera;
/** SceneGraphNode handle */
int32 light;
Mesh *cubeMesh;
int32 testTexId;
};
Renderer createRenderer(Arena *arena, Scene *scene, Camera *cam, int32 light);
void renderBegin(Renderer *r);
void renderEnd(Renderer *r);
void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, Vec4 color, real32 borderRadius, real32 borderThickness, Vec4 borderColor);
void rendererPlaceString(Renderer *r, string s, real32 x, real32 y, Vec4 color, real32 fontSize);
#define Render(r) DeferLoop(renderBegin((r)), renderEnd((r)))
#endif

444
gfx/ui.c Normal file
View File

@@ -0,0 +1,444 @@
#include "ui.h"
#include "Color.h"
UI_Context *__UI_current_ctx__ = NULL;
UI_Context ui_initContext(Arena *arena, Renderer *renderer) {
UI_RectList prevList = PushListZero(arena, UI_RectList, Thousand(10));
UI_RectList list = PushListZero(arena, UI_RectList, Thousand(10));
ListAppend(list, ((UI_Rect){0})); // empty item
return (UI_Context){
.arena=arena,
.rects=list,
.prevRects=prevList,
.hotNode=0,
.prevHotNode=0,
.cursorType=UI_Cursor_Arrow,
.scene3DHandle=0,
.input=NULL,
.prevInput=NULL,
.renderer=renderer,
};
}
void ui_attachTextAttr(UI_Context *ui, UI_Rect *rect, UI_RectStr strData) {
UI_RectStr *strDataCopy = PushStruct(ui->arena, UI_RectStr);
*strDataCopy = strData;
if (strDataCopy->fontSize == -1) {
strDataCopy->fontSize = rect->inheritedTextAttr->fontSize;
if (strDataCopy->fontSize == -1) {
strDataCopy->fontSize = ui->renderer->activeFont->lineHeight;
}
}
if (strDataCopy->color.r == -1) {
strDataCopy->color = rect->inheritedTextAttr->color;
}
if (strDataCopy->lineHeight == -1) {
strDataCopy->lineHeight = rect->inheritedTextAttr->lineHeight;
if (strDataCopy->lineHeight == -1) {
strDataCopy->lineHeight = strDataCopy->fontSize;
}
if (strDataCopy->fontSize > strDataCopy->lineHeight) {
strDataCopy->lineHeight = strDataCopy->fontSize;
}
}
if (strDataCopy->alignment == -1) {
if (rect->inheritedTextAttr->alignment != -1) {
strDataCopy->alignment = rect->inheritedTextAttr->alignment;
} else {
strDataCopy->alignment = UI_TxtAlign_Left;
}
}
rect->inheritedTextAttr = strDataCopy;
if (strData.s.str != NULL) {
strDataCopy->s = PushString(ui->arena, strData.s.length);
memcpy(strDataCopy->s.str, strData.s.str, strData.s.length);
rect->stringData = strDataCopy;
} else {
// NOTE(djledda): Not real text data if empty string -> just style attributes. Ignore
}
}
void ui_placeText(UI_Context *ui, UI_RectStr strData) {
ui_attachTextAttr(ui, &ui->rects.data[ui->currRect], strData);
}
void ui_sizingPass(UI_Context *ui, bool isXAxis, int32 rectHandle) {
UI_Rect *rect = &ui->rects.data[rectHandle];
if (!rect->firstChild) return;
bool isVertical = rect->flags & UI_Flag_Vertical;
real32 remainingSpace = (isXAxis
? rect->resolvedWidth - rect->padding.left - rect->padding.right
: rect->resolvedHeight - rect->padding.top - rect->padding.bottom) + rect->childGap;
int32 growableChildrenCount = 0;
UI_Rect *child;
int32 childHandle = rect->firstChild;
if (isVertical != isXAxis) {
while (childHandle) {
child = &ui->rects.data[childHandle];
if (!(child->flags & UI_Flag_Pos_Absolute)) {
if (child->flags & UI_Flag_HeightGrow && !isXAxis || child->flags & UI_Flag_WidthGrow && isXAxis) {
growableChildrenCount++;
}
real32 childBreadth = isXAxis ? child->resolvedWidth : child->resolvedHeight;
remainingSpace -= childBreadth + rect->childGap;
}
childHandle = child->nextSibling;
}
}
real32 childBreadthInc = growableChildrenCount > 0 ? remainingSpace / (real32)growableChildrenCount : 0;
childHandle = rect->firstChild;
while (childHandle) {
child = &ui->rects.data[childHandle];
if (isXAxis) {
if (child->flags & UI_Flag_WidthGrow) {
if (isVertical) {
child->resolvedWidth = rect->resolvedWidth - rect->padding.left - rect->padding.right;
} else {
child->resolvedWidth += childBreadthInc;
}
}
} else {
if (child->flags & UI_Flag_HeightGrow) {
if (!isVertical) {
child->resolvedHeight = rect->resolvedHeight - rect->padding.top - rect->padding.bottom;
} else {
child->resolvedHeight += childBreadthInc;
}
}
}
ui_sizingPass(ui, isXAxis, childHandle);
childHandle = child->nextSibling;
}
}
void ui_calcLayout(UI_Context *ui, bool isXAxis, int32 rectHandle) {
UI_Rect *rect = &ui->rects.data[rectHandle];
if (!rect->firstChild) return;
bool isVertical = (rect->flags & UI_Flag_Vertical);
real32 coord = isXAxis
? rect->x + rect->padding.left
: rect->y + rect->padding.top;
real32 parentCoord = coord;
int32 childHandle = rect->firstChild;
UI_Rect *child;
while (childHandle) {
child = &ui->rects.data[childHandle];
if (child->flags & UI_Flag_Pos_Absolute) {
if (isXAxis) {
child->x = rect->x;
} else {
child->y = rect->y;
}
} else {
if (isXAxis) {
child->x = child->flags & UI_Flag_Center && isVertical
? coord + (rect->resolvedWidth - rect->padding.left - rect->padding.right)/2 - child->resolvedWidth/2
: coord;
} else {
child->y = child->flags & UI_Flag_Center && !isVertical
? coord + (rect->resolvedHeight - rect->padding.top - rect->padding.bottom)/2 - child->resolvedHeight/2
: coord;
}
}
if (isXAxis) {
child->x += child->xOffset;
} else {
child->y += child->yOffset;
}
ui_calcLayout(ui, isXAxis, childHandle);
if (!(child->flags & UI_Flag_Pos_Absolute) && !isVertical == isXAxis) {
coord += isXAxis ? child->resolvedWidth : child->resolvedHeight;
coord += rect->childGap;
}
childHandle = child->nextSibling;
}
}
void ui_begin(UI_Context *ui) {
__UI_current_ctx__ = ui;
arenaFreeFrom(ui->arena, 0);
ClearList(ui->prevRects);
ListAppendList(ui->prevRects, ui->rects);
ui->cursorType = UI_Cursor_Arrow;
ui->prevHotNode = ui->hotNode;
ui->prevHoveredNode = ui->hoveredNode;
ui->hotNode = 0;
ui->hoveredNode = 0;
ui->scene3DHandle = 0;
ui->rootRect = 0;
ui->currRect = 0;
ClearList(ui->rects);
ListAppend(ui->rects, (UI_Rect){0});
ui_openElement(ui, (UI_Rect){ .width=ui->renderer->width, .height=ui->renderer->height, .color=(Vec4){0,0,0,0}});
UI_RectStr *inheritedTextAttrInit = PushStruct(ui->arena, UI_RectStr);
*inheritedTextAttrInit = UI_TxtAttr();
ui->rects.data[ui->currRect].inheritedTextAttr = inheritedTextAttrInit;
}
void ui_end(UI_Context *ui) {
ui_closeElement(ui);
// 1) a. Calculate layout
ui_sizingPass(ui, true, ui->rootRect); // x
ui_sizingPass(ui, false, ui->rootRect); // y
// 1) b.
ui_calcLayout(ui, true, ui->rootRect); // x
ui_calcLayout(ui, false, ui->rootRect); // y
// 2) Create render commands:
for (EachEl(ui->rects, UI_Rect, rect)) {
rendererPlaceRectangle(
ui->renderer,
rect->x,
rect->y,
rect->resolvedWidth,
rect->resolvedHeight,
rect->color,
rect->borderRadius,
rect->borderThickness,
rect->borderColor
);
if (rect->stringData) {
UI_RectStr *str = rect->stringData;
real32 alignmentXOffset = 0;
real32 alignmentYOffset = rect->padding.top;
real32 textWidth = str->s.length * str->fontSize / ui->renderer->activeFont->lineHeight * ui->renderer->activeFont->charWidth;
enum UI_TxtAlign alignment = str->alignment == -1 ? UI_TxtAlign_Left : str->alignment;
if (alignment & UI_TxtAlign_Right) {
alignmentXOffset = rect->resolvedWidth - rect->padding.right - textWidth;
} else if (alignment & UI_TxtAlign_Center) {
alignmentXOffset = rect->resolvedWidth/2.0 - textWidth/2.0;
} else {
alignmentXOffset = rect->padding.left;
}
rendererPlaceString(
ui->renderer,
str->s,
rect->x + alignmentXOffset + str->xOffset,
rect->y + alignmentYOffset + str->yOffset,
str->color,
str->fontSize
);
}
}
if (ui->scene3DHandle) {
UI_Rect *scene3DRect = &ui->rects.data[ui->scene3DHandle];
ui->renderer->sceneX = (int32)scene3DRect->x;
ui->renderer->sceneY = (int32)scene3DRect->y;
ui->renderer->sceneWidth = (int32)scene3DRect->resolvedWidth;
ui->renderer->sceneHeight = (int32)scene3DRect->resolvedHeight;
}
__UI_current_ctx__ = NULL;
}
static bool pointInRect(real32 x, real32 y, UI_Rect rect) {
return x > rect.x && y > rect.y && x < (rect.x + rect.resolvedWidth) && y < (rect.y + rect.resolvedHeight);
}
void ui_openElement(UI_Context *ui, UI_Rect rect) {
ListAppend(ui->rects, (UI_Rect){0});
int32 newHandle = ui->rects.length - 1;
UI_Rect *newRect = &ui->rects.data[newHandle];
UI_Rect *currRect = &ui->rects.data[ui->currRect];
*newRect = rect;
newRect->parent = ui->currRect;
if (currRect->lastChild) {
ui->rects.data[currRect->lastChild].nextSibling = newHandle;
} else {
currRect->firstChild = newHandle;
}
currRect->lastChild = newHandle;
if (!ui->rootRect) {
ui->rootRect = newHandle;
}
ui->currRect = newHandle;
if (newRect->flags & UI_Flag_3DScene) {
ui->scene3DHandle = newHandle;
}
if (currRect->stringData) {
UI_RectStr *inheritedTextAttr = PushStructZero(ui->arena, UI_RectStr);
*inheritedTextAttr = *currRect->stringData;
*newRect->inheritedTextAttr = *currRect->stringData;
newRect->inheritedTextAttr->s = (string){ .str=NULL, .length=0 };
} else if (newRect->inheritedTextAttr == NULL && currRect) {
newRect->inheritedTextAttr = currRect->inheritedTextAttr;
}
}
void ui_closeElement(UI_Context *ui) {
UI_Rect *currRect = &ui->rects.data[ui->currRect];
UI_Rect *parentRect = &ui->rects.data[currRect->parent];
if (currRect->width != -1) {
currRect->resolvedWidth = currRect->width;
} else if (currRect->minWidth == -1) {
if (currRect->stringData) {
real32 charWidth = currRect->stringData->fontSize / ui->renderer->activeFont->lineHeight * ui->renderer->activeFont->charWidth;
currRect->minWidth = charWidth*currRect->stringData->s.length;
} else {
currRect->minWidth = 0;
}
}
if (currRect->height != -1) {
currRect->resolvedHeight = currRect->height;
} else if (currRect->minHeight == -1) {
if (currRect->stringData) {
currRect->minHeight = currRect->stringData->lineHeight;
} else {
currRect->minHeight = 0;
}
}
bool vertical = parentRect->flags & UI_Flag_Vertical;
real32 currBreadth = vertical ? currRect->resolvedHeight : currRect->resolvedWidth;
real32 parentBreadth = vertical ? parentRect->height : parentRect->width;
real32 gap = parentRect->childGap;
real32 breadthInc = (parentRect->firstChild == ui->currRect ? 0 : gap) + currBreadth;
if (parentBreadth == -1) {
if (vertical) {
parentRect->resolvedHeight += breadthInc;
} else {
parentRect->resolvedWidth += breadthInc;
}
}
real32 currCrossBreadth = vertical ? currRect->resolvedWidth : currRect->resolvedHeight;
real32 parentCrossBreadth = vertical ? parentRect->width : parentRect->height;
real32 currParentCrossBreadth = vertical ? parentRect->resolvedWidth : parentRect->resolvedHeight;
if (parentCrossBreadth == -1) {
real32 newCrossBreadth = currCrossBreadth > currParentCrossBreadth ? currCrossBreadth : currParentCrossBreadth;
if (vertical) {
parentRect->resolvedWidth = newCrossBreadth;
} else {
parentRect->resolvedHeight = newCrossBreadth;
}
}
ui->currRect = ui->rects.data[ui->currRect].parent;
}
/**
* Returns whether the checkbox was clicked
*/
bool ui_ButtonWithHover(UI_Context *ui, UI_Rect rect, UI_Rect hovered, UI_RectStr textAttr) {
int32 id = UI_NextID();
bool clicked = false;
bool inRect = pointInRect(ui->input->mouse.point.x, ui->input->mouse.point.y, ui->prevRects.data[id]);
if (inRect) {
ui->cursorType = UI_Cursor_Pointer;
if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) {
clicked = true;
} else if (ui->input->mouse.btnLeft && (!ui->prevInput->mouse.btnLeft || ui->prevHotNode == id)) {
ui->hotNode = id;
}
ui->hoveredNode = id;
}
bool useHover = inRect && !(hovered.flags & UI_Flag_Ignore);
UI_Rect *rectToRender = useHover ? &hovered : &rect;
if (rectToRender->borderRadius == -1) {
rectToRender->borderRadius = 5;
}
UI_FromRect(*rectToRender) ui_placeText(ui, textAttr);
return clicked;
}
UI_Rect ui_HoverRect(UI_Context *ui, UI_Rect rect, UI_Rect hovered) {
int32 id = UI_NextID();
bool inRect = pointInRect(ui->input->mouse.point.x, ui->input->mouse.point.y, ui->prevRects.data[id]);
if (inRect) {
ui->hoveredNode = id;
}
return inRect && !(hovered.flags & UI_Flag_Ignore) ? hovered : rect;
}
/**
* Returns whether the checkbox was clicked
*/
bool ui_Button(UI_Context *ui, UI_Rect rect, UI_RectStr textAttr) {
return ui_ButtonWithHover(ui, rect, UI_RectAttr(.flags=UI_Flag_Ignore), textAttr);
}
/**
* Returns whether the checkbox was clicked
*/
bool ui_CheckboxRect(UI_Context *ui, bool *value, UI_Rect rect) {
int32 id = UI_NextID();
bool clicked = false;
bool pointerInRect = pointInRect(ui->input->mouse.point.x, ui->input->mouse.point.y, ui->prevRects.data[id]);
if (pointerInRect) {
ui->cursorType = UI_Cursor_Pointer;
if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) {
*value = !*value;
clicked = true;
} else if (ui->input->mouse.btnLeft && (!ui->prevInput->mouse.btnLeft || ui->prevHotNode == id)) {
ui->hotNode = id;
}
}
if (*value) {
rect.borderRadius = 5;
rect.borderThickness = 0;
if (pointerInRect) {
rect.color.x += 0.5;
rect.color.y += 0.5;
rect.color.z += 0.5;
rect.color.x = rect.color.x <= 1.0 ? rect.color.x : 1.0;
rect.color.y = rect.color.y <= 1.0 ? rect.color.y : 1.0;
rect.color.z = rect.color.z <= 1.0 ? rect.color.z : 1.0;
}
UI_FromRect(rect);
} else {
rect.borderRadius = 5;
rect.borderThickness = 2;
rect.borderColor = COLOR_WHITE;
if (pointerInRect) {
rect.color = COLOR_WHITE;
rect.color.w = 0.2;
} else {
rect.color = (Vec4){0,0,0,0};
}
UI_FromRect(rect);
}
return clicked;
}

181
gfx/ui.h Normal file
View File

@@ -0,0 +1,181 @@
#ifndef UI_H
#define UI_H
#include "platform.h"
#include "render.h"
#include "../core.h"
#include "GLFW/glfw3.h"
enum UI_Flag {
UI_Flag_WidthGrow=1<<0, // Default is fixed
UI_Flag_HeightGrow=1<<1, // Default is fixed
UI_Flag_Vertical=1<<2, // Default is horizontal
UI_Flag_3DScene=1<<3,
UI_Flag_Center=1<<4,
UI_Flag_Ignore=1<<7, // For optional parameters
UI_Flag_Pos_Absolute=1<<8, // Default is relative
// ..
UI_Flag_COUNT,
};
typedef struct UI_Padding UI_Padding;
struct UI_Padding {
real32 top;
real32 right;
real32 bottom;
real32 left;
};
enum UI_Cursor {
UI_Cursor_Arrow=1<<0,
UI_Cursor_Pointer=1<<1,
UI_Cursor_Forbidden=1<<2,
UI_Cursor_Count=1<<3,
};
enum UI_TxtAlign {
UI_TxtAlign_Left=1<<0, // Default
UI_TxtAlign_Center=1<<1,
UI_TxtAlign_Right=1<<2,
};
typedef struct UI_RectStr UI_RectStr;
struct UI_RectStr {
string s;
real32 xOffset;
real32 yOffset;
real32 lineHeight;
real32 fontSize;
Vec4 color;
enum UI_TxtAlign alignment;
};
typedef struct UI_Rect UI_Rect;
struct UI_Rect {
enum UI_Flag flags;
int32 parent;
int32 firstChild;
int32 lastChild;
int32 nextSibling;
real32 xOffset;
real32 yOffset;
real32 width;
real32 minWidth;
real32 maxWidth;
real32 height;
real32 minHeight;
real32 maxHeight;
real32 borderRadius;
real32 borderThickness;
Vec4 color;
Vec4 borderColor;
UI_Padding padding;
real32 childGap;
real32 x;
real32 y;
real32 resolvedWidth;
real32 resolvedHeight;
UI_RectStr *stringData;
/**
* Inherited from parent to propagate attributes. Actual string is ignored, only used for filling out defaults in
* the next stringData pointer that is set.
* This pointer is always set, as the global UI context will set it on the root node if it does not present its own
* text data.
*/
UI_RectStr *inheritedTextAttr;
};
DefineList(UI_Rect, UI_Rect);
typedef struct UI_Context UI_Context;
struct UI_Context {
Arena *arena;
UI_RectList prevRects;
UI_RectList rects;
int32 hotNode;
int32 hoveredNode;
int32 prevHoveredNode;
int32 scene3DHandle;
int32 prevHotNode;
Renderer *renderer;
OS_Input *prevInput;
OS_Input *input;
enum UI_Cursor cursorType;
int32 rootRect;
int32 currRect;
};
void ui_begin(UI_Context *ui);
void ui_end(UI_Context *ui);
UI_Context ui_initContext(Arena *arena, Renderer *renderer);
void ui_resolve();
void ui_rect(UI_Context *ui, UI_Rect rect);
void ui_placeText(UI_Context *ui, UI_RectStr strData);
void ui_attachTextAttr(UI_Context *ui, UI_Rect *rect, UI_RectStr strData);
void ui_openElement(UI_Context *ui, UI_Rect rect);
void ui_closeElement(UI_Context *ui);
bool ui_CheckboxRect(UI_Context *ui, bool *value, UI_Rect rect);
bool ui_Button(UI_Context *ui, UI_Rect rect, UI_RectStr textAttr);
bool ui_ButtonWithHover(UI_Context *ui, UI_Rect rect, UI_Rect hovered, UI_RectStr textAttr);
UI_Rect ui_HoverRect(UI_Context *ui, UI_Rect rect, UI_Rect hovered);
extern UI_Context *__UI_current_ctx__;
#if 0
#define UI_DEBUG_ATTR .borderColor=COLOR_RED, .borderThickness=1,
#else
#define UI_DEBUG_ATTR
#endif
#define UI_Pass(ui) DeferLoop(ui_begin((ui)), ui_end((ui)))
#define UI_PadUniform(padding) ((UI_Padding){ (padding), (padding), (padding), (padding) })
#define UI_NextID() ((__UI_current_ctx__)->rects.length)
#define UI(...)\
/** Place a UI_Rect defined in-place in the current position in the UI tree */\
DeferLoop(ui_openElement((__UI_current_ctx__), UI_RectAttr(__VA_ARGS__)), ui_closeElement((__UI_current_ctx__)))
#define UI_RectAttr(...)\
/** Generate a UI_Rect to be passed as a value */\
((UI_Rect){UI_DEBUG_ATTR .minHeight=-1, .minWidth=-1, .width=-1, .height=-1, .inheritedTextAttr=(__UI_current_ctx__->rects.data[(__UI_current_ctx__)->currRect].inheritedTextAttr), __VA_ARGS__})
#define UI_FromRect(rect)\
/** Place a UI_Rect in the current position in the UI tree, as defined by an existing value */\
DeferLoop(ui_openElement((__UI_current_ctx__), (rect)), ui_closeElement((__UI_current_ctx__)))
#define UI_TxtAttrS(str, ...)\
/** Generate UI text to be used as a value */\
((UI_RectStr){ .color={-1,-1,-1,-1}, .alignment=-1, .xOffset=0, .yOffset=0, .fontSize=-1, .lineHeight=-1, .s=(str), __VA_ARGS__ })
#define UI_TxtAttr(...)\
/** Generate UI text to be used as a value, attributes only, no string */\
((UI_RectStr){ .color={-1,-1,-1,-1}, .alignment=-1, .xOffset=0, .yOffset=0, .fontSize=-1, .lineHeight=-1, .s=((string){.str=NULL,.length=0}), __VA_ARGS__ })
#define UI_Txt(str, ...)\
/** Place UI text in the current position in the UI tree. Attaches to the current UI_Rect */\
(ui_placeText((__UI_current_ctx__), (UI_TxtAttrS(str, __VA_ARGS__))))
#define UI_AttachTxtAttr(rect, txtAttr)\
/** Attach "phantom" text to the specified rect in order to set its heritable attributes for subsequent text in the UI subtrees */\
(ui_attachTextAttr((__UI_current_ctx__), (rect), (txtAttr)))
#define UI_SetTxtAttr(...)\
/** Attach "phantom" text to the current rect in order to set its heritable attributes for subsequent text in the UI subtrees */\
(ui_placeText((__UI_current_ctx__), (UI_TxtAttr(__VA_ARGS__))))
#define UI_TxtSeg(str, ...)\
/** Place a text fragment in its own box (useful for placing many text fragments in a row */\
UI() (UI_Txt((str), __VA_ARGS__))
#endif

26
gfx/world/camera.c Normal file
View File

@@ -0,0 +1,26 @@
#include "camera.h"
#include "../../vendor/raymath.h"
Camera createCamera(int32 width, int32 height) {
return (Camera){
.view = (Matrix){0},
.proj = MatrixPerspective(DEG2RAD * 45.0f, (real32)width/(real32)height, 0.1f, 100.0f),
.pos = (Vec3){0},
.up = (Vec3){0,1,0},
};
}
void cameraSetAspect(Camera *c, int32 width, int32 height) {
real32 aspectRatio = (real32)width/(real32)height;
c->proj = MatrixPerspective(DEG2RAD * 45.0f, aspectRatio, 0.1f, 100.0f);
}
void cameraLookAt(Camera *c, float x, float y, float z) {
c->target = (Vec3){x, y, z};
c->view = MatrixLookAt(c->pos, c->target, c->up);
}
void cameraSetUp(Camera *c, real32 up_x, real32 up_y, real32 up_z) {
c->up = (Vec3){up_x, up_y, up_z};
}

21
gfx/world/camera.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef DJSTDLIB_GFX_CAMERA_H
#define DJSTDLIB_GFX_CAMERA_H
#include "../../core.h"
#include "../../vendor/raymath.h"
typedef struct Camera Camera;
struct Camera {
Matrix view;
Matrix proj;
Vec3 pos;
Vec3 up;
Vec3 target;
};
Camera createCamera(int32 width, int32 height);
void cameraSetAspect(Camera *c, int32 width, int32 height);
void cameraLookAt(Camera *c, float x, float y, float z);
void cameraSetUp(Camera *c, real32 up_x, real32 up_y, real32 up_z);
#endif

196
gfx/world/scene.c Normal file
View File

@@ -0,0 +1,196 @@
#include "../../core.h"
#include "scene.h"
Entity *getEntity(Scene *s, int32 entityHandle) {
return &s->entities.data[entityHandle];
}
SceneGraphNode *getSceneGraphNode(Scene *s, int32 sceneGraphNodeHandle) {
return &s->graphNodes.data[sceneGraphNodeHandle];
}
SceneGraphNode *getSceneGraphNodeForEntity(Scene *s, int32 entityHandle) {
return getSceneGraphNode(s, getEntity(s, entityHandle)->graphNodeHandle);
}
int32 createSceneGraphNode(Scene *s) {
SceneGraphNode *newNode;
int32 newNodeHandle;
if (s->nextFreeNode) {
newNodeHandle = s->nextFreeNode;
newNode = getSceneGraphNode(s, newNodeHandle);
if (newNode->next) {
s->nextFreeNode = newNode->next;
} else {
s->nextFreeNode = 0;
}
newNode->next = 0;
} else {
ListAppend(s->graphNodes, (SceneGraphNode){0});
newNodeHandle = (int32)s->graphNodes.length - 1;
newNode = getSceneGraphNode(s, newNodeHandle);
newNode->nextSibling = 0;
}
initGraphNode(newNode);
return newNodeHandle;
}
int32 createEntity(Scene *s) {
Entity *newEntity;
int32 newEntityHandle;
if (s->nextFreeEntity) {
newEntityHandle = s->nextFreeEntity;
newEntity = getEntity(s, newEntityHandle);
if (newEntity->next) {
s->nextFreeEntity = newEntity->next;
} else {
s->nextFreeEntity = 0;
}
newEntity->next = 0;
} else {
ListAppend(s->entities, (Entity){0});
newEntityHandle = (int32)s->entities.length - 1;
newEntity = getEntity(s, newEntityHandle);
}
newEntity->graphNodeHandle = createSceneGraphNode(s);
getSceneGraphNodeForEntity(s, newEntityHandle)->entityHandle = newEntityHandle;
return newEntityHandle;
}
void initGraphNode(SceneGraphNode *n) {
n->scale = (Vec3){1.0f, 1.0f, 1.0f};
n->translation = (Vec3){0.0f, 0.0f, 0.0f};
n->rotation = (Quaternion){1.0f, 0.0f, 0.0f, 0.0f};
n->local = MatrixIdentity();
n->world = n->local;
}
Scene createScene(Arena *arena) {
Scene result = {
.entities = PushListZero(arena, EntityList, 100000),
.nextFreeEntity = 0,
.graphNodes = PushListZero(arena, SceneGraphNodeList, 100000),
.nextFreeNode = 0,
};
int32 handle = createEntity(&result); // Intialise the "zero" nodes
getEntity(&result, handle)->flags = EntityFlags_None | EntityFlags_Dead;
result.sceneRoot = createSceneGraphNode(&result);
return result;
}
void recalcSceneGraphNode(Scene *s, int32 parentHandle) {
if (!parentHandle) return;
SceneGraphNode *parentNode = getSceneGraphNode(s, parentHandle);
getEntity(s, parentNode->entityHandle)->flags |= EntityFlags_Render;
int32 nextChild = parentNode->firstChild;
while (nextChild) {
SceneGraphNode *childNode = getSceneGraphNode(s, nextChild);
childNode->parentHandle = parentHandle;
childNode->local = MatrixCompose(childNode->translation, childNode->rotation, childNode->scale);
childNode->world = MatrixMultiply(childNode->local, parentNode->world);
recalcSceneGraphNode(s, nextChild);
nextChild = childNode->nextSibling;
}
}
void recalcScene(Scene *s) {
recalcSceneGraphNode(s, s->sceneRoot);
}
static void removeSceneGraphNodeRecursive(Scene *s, int32 deletedNodeHandle, bool deletingParent) {
if (!deletedNodeHandle) return;
SceneGraphNode *deletedNode = getSceneGraphNode(s, deletedNodeHandle);
int32 nextChild = deletedNode->firstChild;
while (nextChild) {
int32 sibling = getSceneGraphNode(s, nextChild)->nextSibling;
removeSceneGraphNodeRecursive(s, nextChild, true);
nextChild = sibling;
}
if (deletedNode->entityHandle) {
Entity *entity = getEntity(s, deletedNode->entityHandle);
*entity = (Entity){0};
if (s->nextFreeEntity) {
entity->next = s->nextFreeEntity;
}
s->nextFreeEntity = deletedNode->entityHandle;
}
if (s->nextFreeNode) {
deletedNode->next = s->nextFreeNode;
}
s->nextFreeNode = deletedNodeHandle;
if (!deletingParent && deletedNode->parentHandle) {
SceneGraphNode *parentNode = getSceneGraphNode(s, deletedNode->parentHandle);
if (parentNode->firstChild == deletedNodeHandle) {
if (deletedNode->nextSibling) {
parentNode->firstChild = deletedNode->nextSibling;
} else {
parentNode->firstChild = 0;
}
} else {
int32 prevSibling = parentNode->firstChild;
int32 nextSibling = getSceneGraphNode(s, parentNode->firstChild)->nextSibling;
while (nextSibling) {
SceneGraphNode *siblingNode = getSceneGraphNode(s, nextSibling);
if (nextSibling == deletedNodeHandle) {
SceneGraphNode *prevSiblingNode = getSceneGraphNode(s, prevSibling);
prevSiblingNode->nextSibling = deletedNode->nextSibling;
break;
}
prevSibling = nextSibling;
nextSibling = siblingNode->nextSibling;
}
}
}
deletedNode->firstChild = 0;
deletedNode->parentHandle = 0;
deletedNode->entityHandle = 0;
deletedNode->nextSibling = 0;
}
void removeSceneGraphNode(Scene *s, int32 graphNodeHandle) {
removeSceneGraphNodeRecursive(s, graphNodeHandle, false);
}
void removeEntity(Scene *s, int32 entityHandle) {
if (!entityHandle) return;
removeSceneGraphNode(s, getEntity(s, entityHandle)->graphNodeHandle);
}
void sceneNodeAddNode(Scene *s, int32 parentHandle, int32 childHandle) {
SceneGraphNode *parentNode = getSceneGraphNode(s, parentHandle);
SceneGraphNode *childNode = getSceneGraphNode(s, childHandle);
childNode->nextSibling = parentNode->firstChild;
parentNode->firstChild = childHandle;
childNode->parentHandle = parentHandle;
}
void show(Scene *s, uint32 graphNodeHandle) {
SceneGraphNode *node = getSceneGraphNode(s, graphNodeHandle);
if (node->entityHandle) {
getEntity(s, node->entityHandle)->flags |= EntityFlags_Visible;
}
int32 next = node->firstChild;
while (next) {
show(s, next);
next = getSceneGraphNode(s, next)->nextSibling;
}
}
void hide(Scene *s, uint32 graphNodeHandle) {
SceneGraphNode *node = getSceneGraphNode(s, graphNodeHandle);
if (node->entityHandle) {
getEntity(s, node->entityHandle)->flags &= ~EntityFlags_Visible;
}
int32 next = node->firstChild;
while (next) {
hide(s, next);
next = getSceneGraphNode(s, next)->nextSibling;
}
}

75
gfx/world/scene.h Normal file
View File

@@ -0,0 +1,75 @@
#ifndef DJSTDLIB_GFX_SCENE_H
#define DJSTDLIB_GFX_SCENE_H
#include "../../core.h"
#include "../../vendor/raymath.h"
#include "../Mesh.h"
#include "../Texture.h"
DefineList(int32, Handle);
enum EntityFlags {
EntityFlags_None=0,
EntityFlags_Visible=1<<0,
EntityFlags_Dead=1<<1,
EntityFlags_Render=1<<2,
};
typedef struct Entity Entity;
struct Entity {
int32 graphNodeHandle;
uint64 flags;
Vec4 color;
Mesh *mesh;
Texture *tex;
// Free list
int32 next;
};
DefineList(Entity, Entity);
typedef struct SceneGraphNode SceneGraphNode;
struct SceneGraphNode {
Matrix local;
Matrix world;
Vec3 translation;
Quaternion rotation;
Vec3 scale;
int32 entityHandle;
int32 parentHandle;
/** Next free in the free list */
int32 next;
/** Next child in child list in scene hierarchy */
int32 nextSibling;
int32 firstChild;
};
DefineList(SceneGraphNode, SceneGraphNode);
typedef struct Scene Scene;
struct Scene {
int32 sceneRoot;
EntityList entities;
int32 nextFreeEntity;
/** @internal */
SceneGraphNodeList graphNodes;
int32 nextFreeNode;
};
int32 createEntity(Scene *s);
Entity *getEntity(Scene *s, int32 id);
SceneGraphNode *getSceneGraphNode(Scene *s, int32 id);
int32 createSceneGraphNode(Scene *s);
Scene createScene(Arena *arena);
void initGraphNode(SceneGraphNode *n);
void recalcSceneGraphNode(Scene *s, int32 parentHandle);
void recalcScene(Scene *s);
void removeEntity(Scene *s, int32 entityHandle);
void removeSceneGraphNode(Scene *s, int32 graphNodeHandle);
void sceneNodeAddNode(Scene *s, int32 recipientNodeHandle, int32 graphNodeHandle);
SceneGraphNode *getSceneGraphNodeForEntity(Scene *s, int32 entityHandle);
void show(Scene *s, uint32 graphNodeHandle);
void hide(Scene *s, uint32 graphNodeHandle);
#endif

2
gfx/world/world.c Normal file
View File

@@ -0,0 +1,2 @@
#include "scene.c"
#include "camera.c"

8
gfx/world/world.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef DJSTDLIB_GFX_WORLD_H
#define DJSTDLIB_GFX_WORLD_H
#include "scene.h"
#include "camera.h"
#endif