added files
This commit is contained in:
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user