Files
somaesque-native/src/main.cpp
Daniel Ledda 95b781a4b9 update
2025-02-17 14:13:22 +01:00

676 lines
23 KiB
C++

// stdlib
// TODO(djledda): get rid of this
#include <vector>
// Graphics bindings and libs
#include "lib/glad/glad.c"
#include <GLFW/glfw3.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/ext/matrix_transform.hpp>
#include <glm/glm.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
// Project
#include "SomaSolve.cpp" // errors from iostream also defining the keyword `global`
#include "gfx/gfx.cpp"
#include "world/world.cpp"
#include "VoxelSpace.cpp"
#include "./tests.cpp"
#include "lib/djstdlib/core.cpp"
// Library initialisation
#define STB_IMAGE_IMPLEMENTATION
#include "lib/loaders/stb_image.h"
#define TINYOBJ_LOADER_C_IMPLEMENTATION
#include "lib/loaders/tinyobj.h"
#define PI (real32)3.14159265358979323846264338327950288
void print(glm::vec3* vector) {
glm::vec3 vec = *vector;
print(
"┌ ┐\n"
"│%7.2f%, %7.2f, %7.2f │\n"
"└ ┘\n",
vec[0], vec[1], vec[2]);
}
void print(glm::mat4* matrix) {
glm::mat4 mat = *matrix;
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[0][0], mat[0][1], mat[0][2], mat[0][3],
mat[1][0], mat[1][1], mat[1][2], mat[1][3],
mat[2][0], mat[2][1], mat[2][2], mat[2][3],
mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
}
struct Camera {
glm::mat4 view;
glm::mat4 proj;
glm::vec3 pos;
glm::vec3 up;
glm::vec3 target;
};
Camera *createCamera(Arena *arena, real32 aspect_ratio = 800.0f / 600.0f) {
Camera *result = PushStruct(arena, Camera);
result->view = glm::mat4();
result->proj = glm::perspective(glm::radians(45.0f), aspect_ratio, 0.1f, 100.0f);
result->pos = glm::vec3(0.0f);
result->up = glm::vec3(0.0f, 1.0f, 0.0f);
return result;
}
void cameraLookAt(Camera *c, float x, float y, float z) {
c->target = glm::vec3(x, y, z);
c->view = glm::lookAt(c->pos, c->target, c->up);
}
void camera_set_up(Camera *c, real32 up_x, real32 up_y, real32 up_z) {
c->up = glm::vec3(up_x, up_y, up_z);
}
struct Frame {
uint32 width;
uint32 height;
int32 x;
int32 y;
Camera* cam;
};
struct Polycube {
uint32 entityHandle;
Space repr;
Vector4<real32> color;
};
struct RenderObjects_Rectangle {
uint32 vao;
list<Vector2<real32>> p0;
uint32 p0BufferId;
list<Vector2<real32>> p1;
uint32 p1BufferId;
list<Vector4<real32>> color;
uint32 colorBufferId;
uint64 count;
};
struct Input {
struct {
bool escape;
bool enter;
bool space;
bool lshift;
bool x;
bool y;
bool z;
} keyboard;
struct {
union {
struct {
real32 x;
real32 y;
};
Vector2<real32> point;
};
bool btnLeft;
bool btnRight;
bool btnMiddle;
} mouse;
};
struct Renderer {
Scene *scene;
RenderObjects_Rectangle rects;
};
struct PolycubeInput {
Space repr;
Vector4<real32> color;
};
struct SomaState {
bool wireframe;
bool polycubeDirty;
uint32 currentPolycube;
uint32 lastPolycubeVisible;
uint32 light;
list<Polycube> polycubes;
Camera* camera;
glm::vec3 rotAxisX;
glm::vec3 rotAxisY;
list<PolycubeInput> polycubeInput;
};
struct Soma {
Scene *scene;
Renderer *renderer;
SomaState state;
Input currInput;
Input prevInput;
struct {
GLFWwindow *handle;
uint32 width;
uint32 height;
} window;
};
void showEntity(Scene *scene, uint32 entityHandle) {
SceneGraphNode *node = getSceneGraphNodeForEntity(scene, entityHandle);
for (uint32 &child : node->children) {
SceneGraphNode *subNode = getSceneGraphNode(scene, child);
if (subNode->entityHandle) {
getEntity(scene, subNode->entityHandle)->flags |= EntityFlags_Visible;
}
}
}
void hideEntity(Scene *scene, uint32 entityHandle) {
SceneGraphNode *node = getSceneGraphNodeForEntity(scene, entityHandle);
for (uint32 &child : node->children) {
SceneGraphNode *subNode = getSceneGraphNode(scene, child);
if (subNode->entityHandle) {
getEntity(scene, subNode->entityHandle)->flags &= ~EntityFlags_Visible;
}
}
}
glm::vec3 centreFromPolycube(Scene *scene, Polycube *p) {
glm::vec3 centre = glm::vec3(0.0f);
for (uint32 &child : getSceneGraphNode(scene, p->entityHandle)->children) {
centre += getSceneGraphNode(scene, child)->translation;
}
centre /= getSceneGraphNodeForEntity(scene, p->entityHandle)->children.size();
return centre;
}
Frame createFrame(Arena *arena, uint32 width, uint32 height, uint32 x, uint32 y) {
Frame result = {0};
result.width = width;
result.height = height;
result.x = x;
result.y = y;
result.cam = createCamera(arena, (real32)result.width / (real32)result.height);
return result;
}
void framebufferSizeCallback(GLFWwindow *window, int width, int height) {
glViewport(0, 0, width, height);
}
GLFWwindow *initWindowAndGL(uint32 windowWidth, uint32 windowHeight) {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow *window = glfwCreateWindow(windowWidth, windowHeight, "Somaesque", 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, 800, 600);
glfwSetFramebufferSizeCallback(window, framebufferSizeCallback);
glfwSetInputMode(window, GLFW_CURSOR | GLFW_RAW_MOUSE_MOTION, GLFW_CURSOR_NORMAL);
glEnable(GL_DEPTH_TEST);
return window;
}
void updateViewportFromFrame(uint32 windowWidth, uint32 windowHeight, Frame* frame) {
glViewport(frame->x, windowHeight - frame->y - frame->height, frame->width, frame->height);
}
struct UI_Context {
Renderer *renderer;
Input *prevInput;
Input *input;
bool cursorIsPointer;
};
struct UI_Rect {
real32 x;
real32 y;
real32 width;
real32 height;
real32 borderRadius;
Vector4<real32> color;
};
Mesh cubeMesh = {0};
Texture wallTex = {0};
Shader solidColorShader;
Shader phongShader;
inline bool glfwMouse(GLFWwindow *window, int mouseBtnCode) {
switch (glfwGetMouseButton(window, mouseBtnCode)) {
case GLFW_RELEASE: return false;
case GLFW_PRESS: return true;
default: return false;
}
}
inline bool glfwKey(GLFWwindow *window, int keyCode) {
switch (glfwGetKey(window, keyCode)) {
case GLFW_RELEASE: return false;
case GLFW_PRESS: return true;
default: return false;
}
}
Input getCurrentInput(GLFWwindow *window) {
Input input = {0};
input.keyboard.escape = glfwKey(window, GLFW_KEY_ESCAPE);
input.keyboard.enter = glfwKey(window, GLFW_KEY_ENTER);
input.keyboard.space = glfwKey(window, GLFW_KEY_SPACE);
input.keyboard.lshift = glfwKey(window, GLFW_KEY_LEFT_SHIFT);
input.keyboard.x = glfwKey(window, GLFW_KEY_X);
input.keyboard.y = glfwKey(window, GLFW_KEY_Y);
input.keyboard.z = glfwKey(window, GLFW_KEY_Z);
input.mouse.btnLeft = glfwMouse(window, GLFW_MOUSE_BUTTON_LEFT);
input.mouse.btnRight = glfwMouse(window, GLFW_MOUSE_BUTTON_RIGHT);
input.mouse.btnMiddle = glfwMouse(window, GLFW_MOUSE_BUTTON_MIDDLE);
real64 mouseX;
real64 mouseY;
glfwGetCursorPos(window, &mouseX, &mouseY);
input.mouse.point = vec2<real32>((real32)mouseX, (real32)mouseY);
return input;
}
void processInput(Soma *soma) {
Input *input = &soma->currInput;
Input *prevInput = &soma->prevInput;
if (input->keyboard.escape) {
glfwSetWindowShouldClose(soma->window.handle, true);
}
if (input->keyboard.space && !prevInput->keyboard.space) {
glPolygonMode(GL_FRONT_AND_BACK, !soma->state.wireframe ? GL_LINE : GL_FILL);
soma->state.wireframe = !soma->state.wireframe;
}
SceneGraphNode *node = getSceneGraphNode(soma->scene, getEntity(soma->scene, soma->state.light)->graphNodeHandle);
int shiftMultiplier = input->keyboard.lshift ? -1 : 1;
node->translation.x += 1.0 * input->keyboard.x * shiftMultiplier;
node->translation.y += 1.0 * input->keyboard.y * shiftMultiplier;
node->translation.z += 1.0 * input->keyboard.z * shiftMultiplier;
if (input->keyboard.enter && !prevInput->keyboard.enter) {
if (soma->state.currentPolycube == 6) {
soma->state.currentPolycube = 0;
} else {
soma->state.currentPolycube += 1;
}
}
bool dragScene = false;
if (input->mouse.btnLeft) {
Polycube *current_polycube = &soma->state.polycubes.data[soma->state.currentPolycube];
SceneGraphNode *polycubeGraphNode = getSceneGraphNodeForEntity(soma->scene, current_polycube->entityHandle);
real64 deltaX = (input->mouse.x - prevInput->mouse.x) * 0.005;
if (deltaX > 0.00000001 || deltaX < -0.00000001) {
polycubeGraphNode->rotation *= glm::angleAxis((real32)deltaX, soma->state.rotAxisY);
}
real64 deltaY = (input->mouse.y - prevInput->mouse.y) * 0.005;
if (deltaY > 0.00000001 || deltaY < -0.00000001) {
polycubeGraphNode->rotation = glm::angleAxis(-(real32)deltaY, soma->state.rotAxisX) * polycubeGraphNode->rotation;
}
}
}
Polycube createPolycubeFromRepr(Soma *soma, Space *repr, Vector4<real32> color) {
uint32 polycubeMainEntityHandle = createEntity(soma->scene);
Entity *polycubeMainEntity = getEntity(soma->scene, polycubeMainEntityHandle);
for (int x = 0; x < repr->dim_x; x++) {
for (int y = 0; y < repr->dim_y; y++) {
for (int z = 0; z < repr->dim_z; z++) {
if (filledAt(repr, x, y, z)) {
uint32 segmentEntityHandle = createEntity(soma->scene);
Entity *polycubeSegment = getEntity(soma->scene, segmentEntityHandle);
polycubeSegment->mesh = &cubeMesh;
polycubeSegment->tex = &wallTex;
SceneGraphNode *graphNode = getSceneGraphNode(soma->scene, polycubeSegment->graphNodeHandle);
graphNode->translation = glm::vec3(
-((repr->dim_z - 1)/2.0f) + z,
((repr->dim_x - 1)/2.0f) - x,
-((repr->dim_y - 1)/2.0f) + y
);
sceneNodeAddEntity(soma->scene, polycubeMainEntity->graphNodeHandle, segmentEntityHandle);
}
}
}
}
Polycube result = {};
result.entityHandle = polycubeMainEntityHandle;
result.color = color;
result.repr = *repr;
return result;
}
RenderObjects_Rectangle createRectangleObjects(Arena *arena, size_t count) {
RenderObjects_Rectangle result = {0};
result.count = count;
result.p0 = PushFullList(arena, Vector2<real32>, count);
result.p1 = PushFullList(arena, Vector2<real32>, count);
result.color = PushFullList(arena, Vector4<real32>, count);
glGenVertexArrays(1, &result.vao);
uint32 p0Buffer;
uint32 p1Buffer;
uint32 colorBuffer;
glGenBuffers(1, &result.p0BufferId);
glGenBuffers(1, &result.p1BufferId);
glGenBuffers(1, &result.colorBufferId);
glBindVertexArray(result.vao);
glBindBuffer(GL_ARRAY_BUFFER, result.p0BufferId);
glBufferData(GL_ARRAY_BUFFER, result.p0.length * sizeof(Vector2<real32>), 0, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2<real32>), (void*)0);
glVertexAttribDivisor(0, 1);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, result.p1BufferId);
glBufferData(GL_ARRAY_BUFFER, result.p1.length * sizeof(Vector2<real32>), 0, GL_DYNAMIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2<real32>), (void*)0);
glVertexAttribDivisor(1, 1);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, result.colorBufferId);
glBufferData(GL_ARRAY_BUFFER, result.color.length * sizeof(Vector4<real32>), 0, GL_DYNAMIC_DRAW);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(Vector4<real32>), (void*)0);
glVertexAttribDivisor(2, 1);
glEnableVertexAttribArray(2);
return result;
}
void reinitRectangleObjectBuffers(Renderer *r) {
glBindVertexArray(r->rects.vao);
glBindBuffer(GL_ARRAY_BUFFER, r->rects.p0BufferId);
glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.p0.head * sizeof(Vector2<real32>), r->rects.p0.data);
glBindBuffer(GL_ARRAY_BUFFER, r->rects.p1BufferId);
glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.p1.head * sizeof(Vector2<real32>), r->rects.p1.data);
glBindBuffer(GL_ARRAY_BUFFER, r->rects.colorBufferId);
glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.color.head * sizeof(Vector4<real32>), r->rects.color.data);
}
Renderer createRenderer(Arena *arena, Scene *scene) {
Renderer result = {0};
result.scene = scene;
result.scene->sceneRoot = createSceneGraphNode(scene);
result.rects = createRectangleObjects(arena, 100);
initGraphNode(getSceneGraphNode(scene, scene->sceneRoot));
return result;
}
void renderBegin(Renderer *r) {
r->rects.p0.head = 0;
r->rects.p1.head = 0;
r->rects.color.head = 0;
}
void renderEnd(Soma *soma, Renderer *renderer) {
// 3D Entities
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glUseProgram(phongShader.progId);
setUniformMat4fv(&phongShader, "projection", &soma->state.camera->proj);
setUniformMat4fv(&phongShader, "view", &soma->state.camera->view);
SceneGraphNode *lightGraphNode = getSceneGraphNode(soma->scene, getEntity(soma->scene, soma->state.light)->graphNodeHandle);
setUniform3fv(&phongShader, "light_pos", &lightGraphNode->translation);
setUniform3fv(&phongShader, "camera", &soma->state.camera->pos);
Polycube *currentPolycube = &soma->state.polycubes.data[soma->state.currentPolycube];
glBindVertexArray(cubeMesh.vao);
setUniform4fv(&phongShader, "solid_color", &currentPolycube->color);
int model_uniform = getUniformLocation(&phongShader, "model");
for (Entity &entity : renderer->scene->entities) {
if (entity.flags & EntityFlags_Render && entity.flags & EntityFlags_Visible) {
setUniformMat4fv(model_uniform, &getSceneGraphNode(renderer->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;
}
}
// 2D overlay
glUseProgram(solidColorShader.progId);
reinitRectangleObjectBuffers(renderer);
glm::mat4 ortho = glm::ortho(0.0, 800.0, 600.0, 0.0, -1.0, 1.0);
setUniformMat4fv(&solidColorShader, "projection", &ortho);
glBindVertexArray(renderer->rects.vao);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, renderer->rects.p0.head);
}
void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, Vector4<real32> color) {
appendList(&r->rects.p0, vec2<real32>(x, y));
appendList(&r->rects.p1, vec2<real32>(x + width, y + height));
appendList(&r->rects.color, color);
}
inline bool pointInRect(Vector2<real32> point, UI_Rect rect) {
return point.x > rect.x && point.y > rect.y && point.x < (rect.x + rect.width) && point.y < (rect.y + rect.height);
}
bool ui_checkboxRect(UI_Context *ui, bool *value, UI_Rect rect) {
bool clicked = false;
if (pointInRect(ui->input->mouse.point, rect)) {
ui->cursorIsPointer = true;
if (ui->prevInput->mouse.btnLeft && !ui->input->mouse.btnLeft) {
*value = !*value;
clicked = true;
}
}
rendererPlaceRectangle(ui->renderer, rect.x, rect.y, rect.width, rect.height, *value ? rect.color : vec4<real32>(1, 1, 1, 1));
return clicked;
}
void uiPass(Soma *soma, UI_Context *ui, Renderer *renderer) {
PolycubeInput *currentPolycube = &soma->state.polycubeInput.data[soma->state.currentPolycube];
real32 boxSize = 30;
real32 padding = 20;
real32 paddingBetween = 5;
real32 currY = padding;
for (int x = 0; x < currentPolycube->repr.dim_x; x++) {
for (int y = 0; y < currentPolycube->repr.dim_y; y++) {
real32 currX = padding;
for (int z = 0; z < currentPolycube->repr.dim_z; z++) {
bool cellActive = filledAt(&currentPolycube->repr, x, y, z);
UI_Rect rect = {
currX,
currY,
boxSize,
boxSize,
0,
currentPolycube->color,
};
if (ui_checkboxRect(ui, &cellActive, rect)) {
soma->state.polycubeDirty = true;
spaceSet(&currentPolycube->repr, cellActive, x, y, z);
}
currX += paddingBetween + boxSize;
}
currY += paddingBetween + boxSize;
}
currY += padding;
}
}
int main_cmd() {
interactive_cmd_line_solve_soma();
return 0;
}
int mainGfx() {
Arena *arena = arenaAlloc(Megabytes(128));
Scene mainScene = createScene();
Soma soma = {};
soma.window.width = 800;
soma.window.height = 600;
soma.window.handle = initWindowAndGL(soma.window.width, soma.window.height);
soma.scene = &mainScene;
Renderer renderer = createRenderer(arena, &mainScene);
soma.renderer = &renderer;
if (!soma.window.handle) {
return -1;
}
Frame mainFrame = createFrame(arena, soma.window.width, soma.window.height, 0, 0);
UI_Context ui = {};
GLFWcursor *pointerCursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
GLFWcursor *arrowCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
soma.state.currentPolycube = 0;
soma.state.lastPolycubeVisible = 6;
soma.state.polycubeInput = PushListZero(arena, PolycubeInput, 64);
soma.state.polycubes = PushListZero(arena, Polycube, 64);
soma.state.light = createEntity(&mainScene);
soma.state.camera = mainFrame.cam;
SceneGraphNode *light = getSceneGraphNode(&mainScene, getEntity(&mainScene, soma.state.light)->graphNodeHandle);
light->translation = glm::vec3(4.0f, 6.0f, 24.0f);
/*
Shader solid_texture_shader = createShader(
"./assets/shaders/2d.vertex.glsl"_s,
"./assets/shaders/2d-tex.fragment.glsl"_s);
*/
solidColorShader = createShader(
"./assets/shaders/2d-solid.vertex.glsl"_s,
"./assets/shaders/2d-solid.fragment.glsl"_s);
phongShader = createShader(
"./assets/shaders/phong-solid.vertex.glsl"_s,
"./assets/shaders/phong-solid.fragment.glsl"_s);
cubeMesh = createMesh("./assets/models/cube.obj");
wallTex = createTexture("./assets/textures/brick-wall.jpg");
for (int i = 0; i < ArrayCount(STD_SOMA); i++) {
Space voxelSpace = { STD_SOMA[i], 3, 3, 3 };
Vector4<real32> color = colorFromIndex(i);
appendList(&soma.state.polycubeInput, PolycubeInput{ voxelSpace, color });
cullEmptySpace(&voxelSpace);
Polycube polycube = createPolycubeFromRepr(&soma, &voxelSpace, color);
polycube.color = color;
appendList(&soma.state.polycubes, polycube);
sceneNodeAddEntity(renderer.scene, renderer.scene->sceneRoot, polycube.entityHandle);
}
soma.state.camera->pos = glm::vec3(0.0f, 0.0f, 8.0f);
cameraLookAt(soma.state.camera, 0.0f, 0.0f, 0.0f);
SceneGraphNode *reference_polycube_gn = getSceneGraphNodeForEntity(soma.scene, soma.state.polycubes.data[0].entityHandle);
soma.state.rotAxisY = glm::normalize(glm::vec4(0, 1, 0, 0) * glm::inverse(reference_polycube_gn->world));
glm::vec3 eyes = glm::normalize(soma.state.camera->pos - reference_polycube_gn->translation);
soma.state.rotAxisX = glm::normalize(glm::cross(eyes, soma.state.rotAxisY));
for (int i = 0; i < ArrayCount(STD_SOMA); i++) {
auto gn = getSceneGraphNodeForEntity(soma.scene, soma.state.polycubes.data[i].entityHandle);
gn->rotation *= glm::angleAxis(PI / 4, glm::vec3(1, 0, 0));
gn->rotation *= glm::angleAxis(PI / 4, glm::vec3(0, 1, 0));
}
real64 lastFrame = glfwGetTime();
real64 timeDelta = 1.0f/60.0f;
while (!glfwWindowShouldClose(soma.window.handle)) {
real64 currTime = glfwGetTime();
timeDelta = currTime - lastFrame;
lastFrame = currTime;
//print("%.7f\n", timeDelta);
glfwPollEvents();
soma.currInput = getCurrentInput(soma.window.handle);
processInput(&soma);
if (soma.state.lastPolycubeVisible != soma.state.currentPolycube) {
hideEntity(soma.scene, soma.state.polycubes.data[soma.state.lastPolycubeVisible].entityHandle);
showEntity(soma.scene, soma.state.polycubes.data[soma.state.currentPolycube].entityHandle);
soma.state.lastPolycubeVisible = soma.state.currentPolycube;
}
if (soma.state.polycubeDirty) {
PolycubeInput *pinput = &soma.state.polycubeInput.data[soma.state.currentPolycube];
removeEntity(soma.scene, soma.state.polycubes.data[soma.state.currentPolycube].entityHandle);
Space culledRepr = pinput->repr;
cullEmptySpace(&culledRepr);
Polycube polycube = createPolycubeFromRepr(&soma, &culledRepr, pinput->color);
soma.state.polycubes.data[soma.state.currentPolycube] = polycube;
sceneNodeAddEntity(soma.scene, soma.scene->sceneRoot, polycube.entityHandle);
soma.state.polycubeDirty = false;
}
updateViewportFromFrame(soma.window.width, soma.window.height, &mainFrame);
recalcScene(soma.scene);
renderBegin(&renderer);
ui.cursorIsPointer = false;
ui.prevInput = &soma.prevInput;
ui.input = &soma.currInput;
ui.renderer = &renderer;
uiPass(&soma, &ui, &renderer);
if (ui.cursorIsPointer) {
glfwSetCursor(soma.window.handle, pointerCursor);
} else {
glfwSetCursor(soma.window.handle, arrowCursor);
}
renderEnd(&soma, &renderer);
glfwSwapBuffers(soma.window.handle);
soma.prevInput = soma.currInput;
}
glfwTerminate();
return 0;
}
int main() {
initialiseCore();
return mainGfx();
}