added files

This commit is contained in:
ben de roo
2026-09-06 21:15:01 +02:00
parent 64d277d9d1
commit 3a2a671325
55 changed files with 5597 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
#include "Camera.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <algorithm>
#include <cmath>
namespace planetarium {
Camera::Camera() { reset(); }
void Camera::onMouseDrag(float deltaX, float deltaY) {
const float rotateSpeed = 0.005f;
targetYaw_ -= deltaX * rotateSpeed;
targetPitch_ += deltaY * rotateSpeed;
targetPitch_ = std::clamp(targetPitch_, kMinPitch, kMaxPitch);
}
void Camera::onMousePan(float deltaX, float deltaY) {
// Pan moves the orbit focus along the camera's local right/up axes,
// scaled by distance so panning feels consistent whether zoomed in
// close to a planet or viewing the whole system.
glm::vec3 forward = glm::normalize(currentFocus_ - currentPosition_);
glm::vec3 right = glm::normalize(glm::cross(forward, glm::vec3(0.0f, 1.0f, 0.0f)));
glm::vec3 up = glm::normalize(glm::cross(right, forward));
float panSpeed = 0.0015f * currentDistance_;
targetFocus_ += (-right * deltaX + up * deltaY) * panSpeed;
}
void Camera::onScroll(float deltaY) {
float zoomFactor = std::pow(0.9f, deltaY);
targetDistance_ = std::clamp(targetDistance_ * zoomFactor, kMinDistance, kMaxDistance);
}
void Camera::reset() {
targetYaw_ = -0.6f;
targetPitch_ = 0.45f;
targetDistance_ = 18.0f;
targetFocus_ = glm::vec3(0.0f);
}
void Camera::focusOn(const glm::vec3& worldPoint, float suggestedDistance) {
targetFocus_ = worldPoint;
targetDistance_ = std::clamp(suggestedDistance, kMinDistance, kMaxDistance);
}
void Camera::update(float deltaTime) {
// Exponential ("critically damped") smoothing: each frame closes a
// fixed fraction of the remaining gap to the target, giving smooth
// deceleration without overshoot regardless of frame rate.
float t = 1.0f - std::exp(-kSmoothingSpeed * deltaTime);
currentYaw_ += (targetYaw_ - currentYaw_) * t;
currentPitch_ += (targetPitch_ - currentPitch_) * t;
currentDistance_ += (targetDistance_ - currentDistance_) * t;
currentFocus_ += (targetFocus_ - currentFocus_) * t;
glm::vec3 offset;
offset.x = currentDistance_ * std::cos(currentPitch_) * std::sin(currentYaw_);
offset.y = currentDistance_ * std::sin(currentPitch_);
offset.z = currentDistance_ * std::cos(currentPitch_) * std::cos(currentYaw_);
currentPosition_ = currentFocus_ + offset;
}
glm::mat4 Camera::viewMatrix() const {
return glm::lookAt(currentPosition_, currentFocus_, glm::vec3(0.0f, 1.0f, 0.0f));
}
} // namespace planetarium
+148
View File
@@ -0,0 +1,148 @@
#include "Engine.hpp"
#include "GLFunctions.hpp"
#include <GLFW/glfw3.h>
#include <algorithm>
#include <cstdio>
namespace planetarium {
Engine::~Engine() {
if (window_) {
glfwDestroyWindow(window_);
glfwTerminate();
}
}
bool Engine::initialize(int width, int height, const char* title) {
if (!glfwInit()) {
std::fprintf(stderr, "[Engine] glfwInit failed\n");
return false;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
#endif
glfwWindowHint(GLFW_SAMPLES, 4);
window_ = glfwCreateWindow(width, height, title, nullptr, nullptr);
if (!window_) {
std::fprintf(stderr, "[Engine] Failed to create GLFW window (is a display/X server available?)\n");
glfwTerminate();
return false;
}
glfwSetWindowUserPointer(window_, this);
glfwMakeContextCurrent(window_);
glfwSwapInterval(1);
if (!gl::loadGLFunctions()) {
std::fprintf(stderr, "[Engine] Failed to load required OpenGL 3.3 entry points\n");
return false;
}
glfwSetCursorPosCallback(window_, &Engine::cursorPosCallback);
glfwSetMouseButtonCallback(window_, &Engine::mouseButtonCallback);
glfwSetScrollCallback(window_, &Engine::scrollCallback);
glfwSetKeyCallback(window_, &Engine::keyCallback);
glfwSetFramebufferSizeCallback(window_, &Engine::framebufferSizeCallback);
glfwGetFramebufferSize(window_, &framebufferWidth_, &framebufferHeight_);
lastFrameTime_ = glfwGetTime();
return true;
}
void Engine::run() {
while (window_ && !glfwWindowShouldClose(window_)) {
double now = glfwGetTime();
float deltaTime = static_cast<float>(now - lastFrameTime_);
lastFrameTime_ = now;
deltaTime = std::min(deltaTime, 0.1f); // clamp huge stalls (e.g. window drag)
glfwPollEvents();
update(deltaTime);
render();
glfwSwapBuffers(window_);
}
}
void Engine::update(float deltaTime) {
camera_.update(deltaTime);
if (updateCallback_) updateCallback_(deltaTime);
}
void Engine::render() {
if (renderCallback_) renderCallback_(framebufferWidth_, framebufferHeight_);
}
// ---------------------------------------------------------------------
// GLFW callback trampolines
// ---------------------------------------------------------------------
void Engine::cursorPosCallback(GLFWwindow* window, double x, double y) {
auto* engine = static_cast<Engine*>(glfwGetWindowUserPointer(window));
if (!engine) return;
double dx = x - engine->lastMouseX_;
double dy = y - engine->lastMouseY_;
engine->lastMouseX_ = x;
engine->lastMouseY_ = y;
if (engine->rotating_) {
engine->camera_.onMouseDrag(static_cast<float>(dx), static_cast<float>(dy));
} else if (engine->panning_) {
engine->camera_.onMousePan(static_cast<float>(dx), static_cast<float>(dy));
}
}
void Engine::mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
auto* engine = static_cast<Engine*>(glfwGetWindowUserPointer(window));
if (!engine) return;
(void)mods;
if (button == GLFW_MOUSE_BUTTON_LEFT) {
engine->rotating_ = (action == GLFW_PRESS);
} else if (button == GLFW_MOUSE_BUTTON_MIDDLE) {
engine->panning_ = (action == GLFW_PRESS);
}
}
void Engine::scrollCallback(GLFWwindow* window, double xOffset, double yOffset) {
auto* engine = static_cast<Engine*>(glfwGetWindowUserPointer(window));
if (!engine) return;
(void)xOffset;
engine->camera_.onScroll(static_cast<float>(yOffset));
}
void Engine::keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
auto* engine = static_cast<Engine*>(glfwGetWindowUserPointer(window));
if (!engine) return;
(void)scancode;
(void)mods;
if (action != GLFW_PRESS) return;
switch (key) {
case GLFW_KEY_R:
engine->camera_.reset();
break;
case GLFW_KEY_ESCAPE:
glfwSetWindowShouldClose(window, GLFW_TRUE);
break;
default:
break;
}
}
void Engine::framebufferSizeCallback(GLFWwindow* window, int width, int height) {
auto* engine = static_cast<Engine*>(glfwGetWindowUserPointer(window));
if (!engine) return;
engine->framebufferWidth_ = width;
engine->framebufferHeight_ = height;
}
} // namespace planetarium
+92
View File
@@ -0,0 +1,92 @@
#include "GLFunctions.hpp"
#include <GLFW/glfw3.h>
#include <cstdio>
namespace planetarium::gl {
PFNGLGENVERTEXARRAYS glGenVertexArrays = nullptr;
PFNGLBINDVERTEXARRAY glBindVertexArray = nullptr;
PFNGLDELETEVERTEXARRAYS glDeleteVertexArrays = nullptr;
PFNGLGENBUFFERS glGenBuffers = nullptr;
PFNGLBINDBUFFER glBindBuffer = nullptr;
PFNGLBUFFERDATA glBufferData = nullptr;
PFNGLDELETEBUFFERS glDeleteBuffers = nullptr;
PFNGLVERTEXATTRIBPOINTER glVertexAttribPointer = nullptr;
PFNGLENABLEVERTEXATTRIBARRAY glEnableVertexAttribArray = nullptr;
PFNGLDISABLEVERTEXATTRIBARRAY glDisableVertexAttribArray = nullptr;
PFNGLCREATESHADER glCreateShader = nullptr;
PFNGLSHADERSOURCE glShaderSource = nullptr;
PFNGLCOMPILESHADER glCompileShader = nullptr;
PFNGLGETSHADERIV glGetShaderiv = nullptr;
PFNGLGETSHADERINFOLOG glGetShaderInfoLog = nullptr;
PFNGLDELETESHADER glDeleteShader = nullptr;
PFNGLCREATEPROGRAM glCreateProgram = nullptr;
PFNGLATTACHSHADER glAttachShader = nullptr;
PFNGLLINKPROGRAM glLinkProgram = nullptr;
PFNGLGETPROGRAMIV glGetProgramiv = nullptr;
PFNGLGETPROGRAMINFOLOG glGetProgramInfoLog = nullptr;
PFNGLUSEPROGRAM glUseProgram = nullptr;
PFNGLDELETEPROGRAM glDeleteProgram = nullptr;
PFNGLGETUNIFORMLOCATION glGetUniformLocation = nullptr;
PFNGLUNIFORMMATRIX4FV glUniformMatrix4fv = nullptr;
PFNGLUNIFORM3FV glUniform3fv = nullptr;
PFNGLUNIFORM4FV glUniform4fv = nullptr;
PFNGLUNIFORM1F glUniform1f = nullptr;
PFNGLUNIFORM1I glUniform1i = nullptr;
PFNGLACTIVETEXTURE glActiveTexture = nullptr;
PFNGLGENERATEMIPMAP glGenerateMipmap = nullptr;
namespace {
template <typename T>
bool resolve(T& out, const char* name) {
out = reinterpret_cast<T>(glfwGetProcAddress(name));
if (!out) {
std::fprintf(stderr, "[GLFunctions] Failed to resolve entry point: %s\n", name);
return false;
}
return true;
}
} // namespace
bool loadGLFunctions() {
bool ok = true;
ok &= resolve(glGenVertexArrays, "glGenVertexArrays");
ok &= resolve(glBindVertexArray, "glBindVertexArray");
ok &= resolve(glDeleteVertexArrays, "glDeleteVertexArrays");
ok &= resolve(glGenBuffers, "glGenBuffers");
ok &= resolve(glBindBuffer, "glBindBuffer");
ok &= resolve(glBufferData, "glBufferData");
ok &= resolve(glDeleteBuffers, "glDeleteBuffers");
ok &= resolve(glVertexAttribPointer, "glVertexAttribPointer");
ok &= resolve(glEnableVertexAttribArray, "glEnableVertexAttribArray");
ok &= resolve(glDisableVertexAttribArray, "glDisableVertexAttribArray");
ok &= resolve(glCreateShader, "glCreateShader");
ok &= resolve(glShaderSource, "glShaderSource");
ok &= resolve(glCompileShader, "glCompileShader");
ok &= resolve(glGetShaderiv, "glGetShaderiv");
ok &= resolve(glGetShaderInfoLog, "glGetShaderInfoLog");
ok &= resolve(glDeleteShader, "glDeleteShader");
ok &= resolve(glCreateProgram, "glCreateProgram");
ok &= resolve(glAttachShader, "glAttachShader");
ok &= resolve(glLinkProgram, "glLinkProgram");
ok &= resolve(glGetProgramiv, "glGetProgramiv");
ok &= resolve(glGetProgramInfoLog, "glGetProgramInfoLog");
ok &= resolve(glUseProgram, "glUseProgram");
ok &= resolve(glDeleteProgram, "glDeleteProgram");
ok &= resolve(glGetUniformLocation, "glGetUniformLocation");
ok &= resolve(glUniformMatrix4fv, "glUniformMatrix4fv");
ok &= resolve(glUniform3fv, "glUniform3fv");
ok &= resolve(glUniform4fv, "glUniform4fv");
ok &= resolve(glUniform1f, "glUniform1f");
ok &= resolve(glUniform1i, "glUniform1i");
ok &= resolve(glActiveTexture, "glActiveTexture");
ok &= resolve(glGenerateMipmap, "glGenerateMipmap");
return ok;
}
} // namespace planetarium::gl
+216
View File
@@ -0,0 +1,216 @@
#include "Mesh.hpp"
#include <cmath>
#include <cstddef>
namespace planetarium {
using namespace gl;
Mesh::~Mesh() { release(); }
Mesh::Mesh(Mesh&& other) noexcept
: vao_(other.vao_),
vbo_(other.vbo_),
ebo_(other.ebo_),
indexCount_(other.indexCount_),
vertexCount_(other.vertexCount_),
isLineStrip_(other.isLineStrip_) {
other.vao_ = other.vbo_ = other.ebo_ = 0;
other.indexCount_ = other.vertexCount_ = 0;
}
Mesh& Mesh::operator=(Mesh&& other) noexcept {
if (this != &other) {
release();
vao_ = other.vao_;
vbo_ = other.vbo_;
ebo_ = other.ebo_;
indexCount_ = other.indexCount_;
vertexCount_ = other.vertexCount_;
isLineStrip_ = other.isLineStrip_;
other.vao_ = other.vbo_ = other.ebo_ = 0;
other.indexCount_ = other.vertexCount_ = 0;
}
return *this;
}
void Mesh::release() {
if (ebo_) glDeleteBuffers(1, &ebo_);
if (vbo_) glDeleteBuffers(1, &vbo_);
if (vao_) glDeleteVertexArrays(1, &vao_);
vao_ = vbo_ = ebo_ = 0;
indexCount_ = vertexCount_ = 0;
}
void Mesh::upload(const std::vector<Vertex>& vertices, const std::vector<unsigned int>& indices) {
release();
isLineStrip_ = false;
glGenVertexArrays(1, &vao_);
glGenBuffers(1, &vbo_);
glBindVertexArray(vao_);
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(vertices.size() * sizeof(Vertex)),
vertices.data(), GL_STATIC_DRAW);
if (!indices.empty()) {
glGenBuffers(1, &ebo_);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
static_cast<GLsizeiptr>(indices.size() * sizeof(unsigned int)),
indices.data(), GL_STATIC_DRAW);
indexCount_ = static_cast<GLsizei>(indices.size());
}
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, position)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, normal)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, uv)));
glBindVertexArray(0);
vertexCount_ = static_cast<GLsizei>(vertices.size());
}
void Mesh::uploadLineStrip(const std::vector<glm::vec3>& points) {
release();
isLineStrip_ = true;
glGenVertexArrays(1, &vao_);
glGenBuffers(1, &vbo_);
glBindVertexArray(vao_);
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(points.size() * sizeof(glm::vec3)),
points.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), nullptr);
glBindVertexArray(0);
vertexCount_ = static_cast<GLsizei>(points.size());
}
void Mesh::draw() const {
if (!vao_) return;
glBindVertexArray(vao_);
if (ebo_) {
glDrawElements(GL_TRIANGLES, indexCount_, GL_UNSIGNED_INT, nullptr);
} else {
glDrawArrays(GL_TRIANGLES, 0, vertexCount_);
}
glBindVertexArray(0);
}
void Mesh::drawLineStrip() const {
if (!vao_) return;
glBindVertexArray(vao_);
glDrawArrays(GL_LINE_LOOP, 0, vertexCount_);
glBindVertexArray(0);
}
Mesh Mesh::createUVSphere(int stacks, int slices) {
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
vertices.reserve(static_cast<size_t>((stacks + 1) * (slices + 1)));
const float pi = 3.14159265358979323846f;
for (int i = 0; i <= stacks; ++i) {
float v = static_cast<float>(i) / static_cast<float>(stacks);
float phi = v * pi;
for (int j = 0; j <= slices; ++j) {
float u = static_cast<float>(j) / static_cast<float>(slices);
float theta = u * 2.0f * pi;
float x = std::sin(phi) * std::cos(theta);
float y = std::cos(phi);
float z = std::sin(phi) * std::sin(theta);
Vertex vert;
vert.position = glm::vec3(x, y, z);
vert.normal = glm::normalize(glm::vec3(x, y, z));
vert.uv = glm::vec2(u, 1.0f - v);
vertices.push_back(vert);
}
}
for (int i = 0; i < stacks; ++i) {
for (int j = 0; j < slices; ++j) {
unsigned int a = static_cast<unsigned int>(i * (slices + 1) + j);
unsigned int b = static_cast<unsigned int>(a + slices + 1);
indices.push_back(a);
indices.push_back(b);
indices.push_back(a + 1);
indices.push_back(a + 1);
indices.push_back(b);
indices.push_back(b + 1);
}
}
Mesh mesh;
mesh.upload(vertices, indices);
return mesh;
}
Mesh Mesh::createRing(float innerRadius, float outerRadius, int segments) {
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
const float pi = 3.14159265358979323846f;
for (int i = 0; i <= segments; ++i) {
float t = static_cast<float>(i) / static_cast<float>(segments);
float theta = t * 2.0f * pi;
float ct = std::cos(theta), st = std::sin(theta);
Vertex inner;
inner.position = glm::vec3(ct * innerRadius, 0.0f, st * innerRadius);
inner.normal = glm::vec3(0.0f, 1.0f, 0.0f);
inner.uv = glm::vec2(t, 0.0f);
vertices.push_back(inner);
Vertex outer;
outer.position = glm::vec3(ct * outerRadius, 0.0f, st * outerRadius);
outer.normal = glm::vec3(0.0f, 1.0f, 0.0f);
outer.uv = glm::vec2(t, 1.0f);
vertices.push_back(outer);
}
for (int i = 0; i < segments; ++i) {
unsigned int a = static_cast<unsigned int>(i * 2);
unsigned int b = a + 1;
unsigned int c = a + 2;
unsigned int d = a + 3;
indices.push_back(a);
indices.push_back(b);
indices.push_back(c);
indices.push_back(b);
indices.push_back(d);
indices.push_back(c);
}
Mesh mesh;
mesh.upload(vertices, indices);
return mesh;
}
Mesh Mesh::createOrbitCircle(int segments) {
std::vector<glm::vec3> points;
points.reserve(static_cast<size_t>(segments));
const float pi = 3.14159265358979323846f;
for (int i = 0; i < segments; ++i) {
float t = static_cast<float>(i) / static_cast<float>(segments) * 2.0f * pi;
points.emplace_back(std::cos(t), 0.0f, std::sin(t));
}
Mesh mesh;
mesh.uploadLineStrip(points);
return mesh;
}
} // namespace planetarium
+121
View File
@@ -0,0 +1,121 @@
#include "Shader.hpp"
#include <glm/gtc/type_ptr.hpp>
#include <cstdio>
#include <fstream>
#include <sstream>
namespace planetarium {
using namespace gl;
namespace {
std::string readFile(const std::string& path) {
std::ifstream file(path);
if (!file.is_open()) {
std::fprintf(stderr, "[Shader] Could not open file: %s\n", path.c_str());
return {};
}
std::ostringstream ss;
ss << file.rdbuf();
return ss.str();
}
GLuint compileStage(GLenum stage, const std::string& source, const std::string& debugName) {
GLuint shader = glCreateShader(stage);
const char* src = source.c_str();
glShaderSource(shader, 1, &src, nullptr);
glCompileShader(shader);
GLint success = GL_FALSE;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char log[1024];
glGetShaderInfoLog(shader, sizeof(log), nullptr, log);
std::fprintf(stderr, "[Shader] Compile error in %s:\n%s\n", debugName.c_str(), log);
glDeleteShader(shader);
return 0;
}
return shader;
}
} // namespace
Shader::~Shader() {
if (program_) glDeleteProgram(program_);
}
Shader::Shader(Shader&& other) noexcept : program_(other.program_) {
other.program_ = 0;
}
Shader& Shader::operator=(Shader&& other) noexcept {
if (this != &other) {
if (program_) glDeleteProgram(program_);
program_ = other.program_;
other.program_ = 0;
}
return *this;
}
bool Shader::loadFromFiles(const std::string& vertexPath, const std::string& fragmentPath) {
std::string vertSrc = readFile(vertexPath);
std::string fragSrc = readFile(fragmentPath);
if (vertSrc.empty() || fragSrc.empty()) return false;
GLuint vs = compileStage(GL_VERTEX_SHADER, vertSrc, vertexPath);
GLuint fs = compileStage(GL_FRAGMENT_SHADER, fragSrc, fragmentPath);
if (!vs || !fs) {
if (vs) glDeleteShader(vs);
if (fs) glDeleteShader(fs);
return false;
}
GLuint prog = glCreateProgram();
glAttachShader(prog, vs);
glAttachShader(prog, fs);
glLinkProgram(prog);
GLint linked = GL_FALSE;
glGetProgramiv(prog, GL_LINK_STATUS, &linked);
glDeleteShader(vs);
glDeleteShader(fs);
if (!linked) {
char log[1024];
glGetProgramInfoLog(prog, sizeof(log), nullptr, log);
std::fprintf(stderr, "[Shader] Link error (%s / %s):\n%s\n", vertexPath.c_str(),
fragmentPath.c_str(), log);
glDeleteProgram(prog);
return false;
}
if (program_) glDeleteProgram(program_);
program_ = prog;
return true;
}
void Shader::use() const {
if (program_) glUseProgram(program_);
}
GLint Shader::locationOf(const std::string& name) const {
return glGetUniformLocation(program_, name.c_str());
}
void Shader::setMat4(const std::string& name, const glm::mat4& m) const {
glUniformMatrix4fv(locationOf(name), 1, GL_FALSE, glm::value_ptr(m));
}
void Shader::setVec3(const std::string& name, const glm::vec3& v) const {
glUniform3fv(locationOf(name), 1, glm::value_ptr(v));
}
void Shader::setVec4(const std::string& name, const glm::vec4& v) const {
glUniform4fv(locationOf(name), 1, glm::value_ptr(v));
}
void Shader::setFloat(const std::string& name, float f) const {
glUniform1f(locationOf(name), f);
}
void Shader::setInt(const std::string& name, int i) const {
glUniform1i(locationOf(name), i);
}
} // namespace planetarium
+70
View File
@@ -0,0 +1,70 @@
#include "Engine.hpp"
#include "GLFunctions.hpp"
#include "Mesh.hpp"
#include "Shader.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <cstdio>
#include <sys/stat.h>
using namespace planetarium;
using namespace planetarium::gl;
namespace {
bool directoryExists(const std::string& path) {
struct stat info{};
return stat(path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR);
}
} // namespace
int main() {
Engine engine;
if (!engine.initialize(1280, 720, "Engine Demo")) {
std::fprintf(stderr, "Failed to initialize engine.\n");
return 1;
}
std::string assetDir = "assets";
if (!directoryExists(assetDir + "/shaders")) {
#ifdef ENGINE_ASSET_DIR
assetDir = ENGINE_ASSET_DIR;
#endif
}
Shader shader;
if (!shader.loadFromFiles(assetDir + "/shaders/basic.vert", assetDir + "/shaders/basic.frag")) {
std::fprintf(stderr, "Failed to load demo shaders from %s\n", assetDir.c_str());
return 1;
}
Mesh sphere = Mesh::createUVSphere(32, 32);
float spinDeg = 0.0f;
engine.setUpdateCallback([&](float dt) { spinDeg += dt * 20.0f; });
engine.setRenderCallback([&](int fbWidth, int fbHeight) {
glViewport(0, 0, fbWidth, fbHeight);
glClearColor(0.05f, 0.06f, 0.09f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
float aspect = fbHeight > 0 ? static_cast<float>(fbWidth) / static_cast<float>(fbHeight) : 1.0f;
glm::mat4 projection = glm::perspective(glm::radians(50.0f), aspect, 0.05f, 200.0f);
glm::mat4 model = glm::rotate(glm::mat4(1.0f), glm::radians(spinDeg), glm::vec3(0.0f, 1.0f, 0.0f));
shader.use();
shader.setMat4("uModel", model);
shader.setMat4("uView", engine.camera().viewMatrix());
shader.setMat4("uProjection", projection);
shader.setVec3("uBaseColor", glm::vec3(0.30f, 0.65f, 0.95f));
shader.setVec3("uLightDir", glm::vec3(-0.4f, -1.0f, -0.3f));
shader.setVec3("uViewPos", engine.camera().position());
sphere.draw();
});
engine.run();
return 0;
}