working rotation and zoom. pan bad

This commit is contained in:
2025-08-18 13:41:15 -05:00
parent 2a1654e80c
commit a00bac0b3f
15 changed files with 320 additions and 204 deletions

120
src/body.cpp Normal file
View File

@@ -0,0 +1,120 @@
#include <cassert>
#include <glad/glad.h>
#include "util.hpp"
#include "body.hpp"
#include "shaders.hpp"
enum class ParserState {
PREFIX,
VERTEX,
FACE,
FACE_SKIP,
};
bool load_body(body* out_body, const char* obj_filepath) {
array<char> source;
if (!read_file(&source, obj_filepath)) {
return false;
}
size_t num_verts = 0;
size_t num_faces = 0;
for (int i = 1; i < source.len; i++) {
if (source[i] == ' ' && source[i - 1] == 'v') {
num_verts++;
} else if (source[i] == ' ' && source[i - 1] == 'f') {
num_faces++;
}
}
array<float> verts = { (float*)malloc(sizeof(float) * num_verts * 3), num_verts * 3 };
array<int> faces = { (int*)malloc(sizeof(int) * num_faces * 3), num_faces*3 };
// Get ready for the parsing loop
ParserState state = ParserState::PREFIX;
int vert_i = 0;
int face_i = 0;
int start = 0;
for (int i = 1; i < source.len; i++) {
if (source[i] == '\n') {
state = ParserState::PREFIX;
continue;
}
switch (state) {
case ParserState::PREFIX:
if (source[i - 1] == 'v' && source[i] == ' ') {
state = ParserState::VERTEX;
start = i + 1;
} else if (i > 0 && source[i - 1] == 'f' && source[i] == ' ') {
state = ParserState::FACE;
start = i + 1;
}
break;
case ParserState::VERTEX:
if (source[i] == ' ' || source[i] == '\r' || source[i] == '\n') {
verts[vert_i] = atof(&source[start])/1.5;
vert_i++;
start = i + 1;
}
break;
case ParserState::FACE:
if (source[i] == '/') {
state = ParserState::FACE_SKIP;
faces[face_i] = atoi(&source[start]) - 1;
face_i++;
}
break;
case ParserState::FACE_SKIP:
if (source[i] == ' ') {
state = ParserState::FACE;
start = i + 1;
}
}
}
// Need a good validation check here. This is a stand in.
if (verts.len == 0 || faces.len == 0) {
printf(
"Obj file appears incomplete or corrupted. Num verts %zu. Num Faces %zu.\n", verts.len, faces.len);
return false;
}
uint vao, vbo, ebo;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, verts.len * sizeof(float), verts.data, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, faces.len * sizeof(int), faces.data, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
*out_body = { .pose = glm::mat4(1),
.ebo = ebo,
.vao = vao,
.vbo = vbo,
.shader = 0,
.verts = verts,
.faces = faces,
.color = glm::vec4(0.5, 0.5, 0.5, 1)
};
return true;
}
void draw_body(const body& b) {
use_shader(b.shader);
set_uniform(b.shader, "global_t", b.pose);
set_uniform(b.shader, "color", b.color);
glBindVertexArray(b.vao);
glDrawElements(GL_TRIANGLES, b.faces.len, GL_UNSIGNED_INT, 0);
//glDisableVertexAttribArray(0);
}

View File

@@ -1,24 +1,91 @@
#include <array>
#include <cmath>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/ext/vector_float3.hpp>
#include <stdio.h>
#include <optional>
#include "util.hpp"
#include "shaders.hpp"
#include "body.hpp"
static GLFWwindow* window;
static float width, height;
void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); }
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
void framebuffer_size_callback(GLFWwindow* window, int w, int h) {
width = w;
height = h;
glViewport(0, 0, width, height);
}
bool glfw_window_setup() {
void process_input() {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
static bool mouse_pressed = false;
static bool scroll_pressed = false;
static double prev_cursor_x, prev_cursor_y;
static double theta = 0.0; // angle of camera trans vect wrt x-z plane
static double phi = 0.0; // angle of camera trans vect wrt x-axis
static glm::vec3 camera_loc = glm::vec3(0, 0, 2);
static glm::vec3 focal_point = glm::vec3(0, 0, 0);
static glm::vec3 up = glm::vec3(0, 1, 0);
static glm::mat4 camera_t = glm::lookAt(camera_loc, focal_point, up);
static glm::vec4 strafe_x = camera_t[0];
static glm::vec4 strafe_y = camera_t[1];
static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos) {
float dx = (xpos - prev_cursor_x);
float dy = (ypos - prev_cursor_y);
prev_cursor_x = xpos;
prev_cursor_y = ypos;
if (mouse_pressed) {
phi += dx / width * glm::radians(360.0);
theta += dy / height * glm::radians(360.0);
}
if (scroll_pressed) {
focal_point += strafe_x * (dx/300) + strafe_y * (dy/300);
}
double len = glm::length(camera_loc);
camera_loc.x = len * glm::cos(theta) * glm::cos(-phi);
camera_loc.y = len * glm::sin(theta);
camera_loc.z = -len * glm::cos(theta) * glm::sin(-phi);
camera_t = glm::lookAt(camera_loc, focal_point, up);
}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
if (button == GLFW_MOUSE_BUTTON_RIGHT) {
mouse_pressed = action == GLFW_PRESS;
}
if (button == GLFW_MOUSE_BUTTON_MIDDLE) {
scroll_pressed = action == GLFW_PRESS;
strafe_x = camera_t[0];
strafe_y = camera_t[1];
}
glfwGetCursorPos(window, &prev_cursor_x, &prev_cursor_y);
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
glm::vec4 k = camera_t[2];
float d = yoffset;
camera_loc += k * (-d * glm::length(camera_loc) / 10);
camera_t = glm::lookAt(camera_loc, focal_point, up);
}
bool glfw_setup() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
@@ -26,147 +93,72 @@ bool glfw_window_setup() {
// glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
window = glfwCreateWindow(800, 800, "LearnOpenGL", NULL, NULL);
if (window == NULL) {
std::cout << "Failed to create GLFW windoww" << std::endl;
printf("Failed to create GLFW window\n");
glfwTerminate();
return false;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, cursor_position_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
glfwSetScrollCallback(window, scroll_callback);
cursor_position_callback(window, 0, 0);
return true;
}
enum class ParserState {
PREFIX,
VERTEX,
FACE,
FACE_SKIP,
};
std::optional<std::pair<array<float>, array<unsigned int>>> load_icosphere() {
std::FILE* f = NULL;
if (fopen_s(&f, "Icosphere.obj", "r")) {
return {};
}
// MEMORY LEAK!!! 84 verts currently
float* vert_data = (float*)malloc(sizeof(float) * 128);
size_t vert_data_size = 0;
// MEMORY LEAK!!! (I think)
unsigned int* indices_data = (unsigned int*)malloc(sizeof(int) * 256);
size_t indices_data_size = 0;
ParserState state = ParserState::PREFIX;
char ln[128];
int len = 0;
int start = 0;
while ((ln[len] = std::fgetc(f)) != EOF) {
switch (state) {
case ParserState::PREFIX:
if (len > 0 && ln[len - 1] == 'v' && ln[len] == ' ') {
state = ParserState::VERTEX;
start = len + 1;
} else if (len > 0 && ln[len - 1] == 'f' && ln[len] == ' ') {
state = ParserState::FACE;
start = len + 1;
}
break;
case ParserState::VERTEX:
if (ln[len] == ' ' || ln[len] == '\n') {
vert_data[vert_data_size] = atof(&ln[start]);
vert_data_size++;
start = len + 1;
}
break;
case ParserState::FACE:
if (ln[len] == '/') {
state = ParserState::FACE_SKIP;
indices_data[indices_data_size] = atoi(&ln[start]) - 1;
indices_data_size++;
}
break;
case ParserState::FACE_SKIP:
if (ln[len] == ' ') {
state = ParserState::FACE;
start = len + 1;
}
}
if (ln[len] == '\n') {
len = 0;
state = ParserState::PREFIX;
}
len++;
}
array<float> verts = { vert_data, vert_data_size };
array<unsigned int> indices = { indices_data, indices_data_size };
return std::make_pair(verts, indices);
}
int main() {
if (!glfw_window_setup())
if (!glfw_setup())
return -1;
auto data = load_icosphere();
if (!data) {
std::cout << "ERROR loading the icosphere obj file failed" << std::endl;
return -1;
}
auto verts = data.value().first;
auto indices = data.value().second;
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cout << "Failed to initialize GLAD" << std::endl;
printf("Failed to initialize GLAD\n");
return -1;
}
glViewport(0, 0, 800, 800);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
width = 800;
height = 800;
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
uint shader;
const char* vertex_filepath = "src/shaders/vertex.glsl";
const char* fragment_filepath = "src/shaders/fragment.glsl";
if (!load_shader(&shader, vertex_filepath, fragment_filepath))
return -1;
//glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT); // Write to back buffer
glfwSwapBuffers(window); // front buffer is now back
glClear(GL_COLOR_BUFFER_BIT); // Write to back buffer again (former front buf)
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, verts.len * sizeof(float), verts.data, GL_STATIC_DRAW);
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.len * sizeof(unsigned int), indices.data, GL_STATIC_DRAW);
uint program_id;
const char* vertex_filepath = "src/shaders/vertex.glsl";
const char* fragment_filepath = "src/shaders/fragment.glsl";
if (!shader::load(&program_id, vertex_filepath, fragment_filepath))
body b;
if (!load_body(&b, "Icosphere.obj"))
return -1;
shader::use(program_id);
// Tell opengl which attribute our data is for (i.e. location)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
b.shader = shader;
b.pose = glm::translate(b.pose, glm::vec3(0, 0, 0));
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//set_uniform(shader, "color", glm::vec4 { sin(time), sin(time + glm::radians(45.0f)), sin(time + glm::radians(90.0f)), 1.0 } / 2.0f);
//time = glfwGetTime();
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
float time;
glm::mat4 projection_t
= glm::perspective(glm::radians(60.0f), (float)width / (float)height, 0.1f, 1300.0f);
while (!glfwWindowShouldClose(window)) {
float time = glfwGetTime();
shader::set_uniform(program_id, "color", vec4 { 1, sin(time), 1, 1.0 });
shader::use(program_id);
glClear(GL_COLOR_BUFFER_BIT);
glDrawElements(GL_TRIANGLES, indices.len, GL_UNSIGNED_INT, 0);
glBindVertexArray(VAO);
process_input();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
set_uniform(b.shader, "projection_t", projection_t);
set_uniform(b.shader, "camera_t", camera_t);
draw_body(b);
processInput(window);
glfwSwapBuffers(window);
glfwPollEvents();
}

View File

@@ -1,5 +1,7 @@
#include <typeinfo>
#include <cstdlib>
#include <stdio.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "util.hpp"
#include "shaders.hpp"
@@ -14,11 +16,9 @@ struct overload : Ts ... {
};
template<class... Ts> overload(Ts...) -> overload<Ts...>;
namespace shader {
bool _compile_shader(uint *out_id, const char* filepath, int shader_type);
bool load(uint* out_id, const char* vertex_filepath, const char* fragment_filepath) {
bool load_shader(uint* out_id, const char* vertex_filepath, const char* fragment_filepath) {
uint vertex_id;
if (!_compile_shader(&vertex_id, vertex_filepath, GL_VERTEX_SHADER))
return false;
@@ -49,25 +49,24 @@ bool load(uint* out_id, const char* vertex_filepath, const char* fragment_filepa
return true;
}
void use(uint id) { glUseProgram(id); }
void use_shader(uint id) { glUseProgram(id); }
void set_uniform(uint id, const char* name, uniform_variant value) {
const auto visitor = overload {
[id, name](int i) { glUniform1i(glGetUniformLocation(id, name), i); },
[id, name](float f) { glUniform1f(glGetUniformLocation(id, name), f); },
[id, name](bool b) { glUniform1i(glGetUniformLocation(id, name), b); },
[id, name](vec4 v) { glUniform4f(glGetUniformLocation(id, name), v.x, v.y, v.z, v.w); },
[id, name](vec3 v) { glUniform3f(glGetUniformLocation(id, name), v.x, v.y, v.z); },
[id, name](vec2 v) { glUniform2f(glGetUniformLocation(id, name), v.x, v.y); },
[id, name](vec4i v) { glUniform4i(glGetUniformLocation(id, name), v.x, v.y, v.z, v.w); },
[id, name](vec3i v) { glUniform3i(glGetUniformLocation(id, name), v.x, v.y, v.z); },
[id, name](vec2i v) { glUniform2i(glGetUniformLocation(id, name), v.x, v.y); },
[id, name](glm::vec4 v) { glUniform4f(glGetUniformLocation(id, name), v.x, v.y, v.z, v.w); },
[id, name](glm::vec3 v) { glUniform3f(glGetUniformLocation(id, name), v.x, v.y, v.z); },
[id, name](glm::vec2 v) { glUniform2f(glGetUniformLocation(id, name), v.x, v.y); },
[id, name](glm::ivec4 v) { glUniform4i(glGetUniformLocation(id, name), v.x, v.y, v.z, v.w); },
[id, name](glm::ivec3 v) { glUniform3i(glGetUniformLocation(id, name), v.x, v.y, v.z); },
[id, name](glm::ivec2 v) { glUniform2i(glGetUniformLocation(id, name), v.x, v.y); },
[id, name](glm::mat4 v) { glUniformMatrix4fv(glGetUniformLocation(id, name), 1, GL_FALSE, glm::value_ptr(v)); },
};
visit(visitor, value);
}
// Privates
bool _compile_shader(uint *out_id, const char* filepath, int shader_type) {
array<char> source;
if (!read_file(&source, filepath))
@@ -77,6 +76,8 @@ bool _compile_shader(uint *out_id, const char* filepath, int shader_type) {
glShaderSource(id, 1, &source.data, (int*)&source.len);
glCompileShader(id);
free(source.data);
int status;
glGetShaderiv(id, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
@@ -89,5 +90,3 @@ bool _compile_shader(uint *out_id, const char* filepath, int shader_type) {
*out_id = id;
return status == GL_TRUE;
}
} // namespace shader

View File

@@ -2,8 +2,15 @@
uniform vec4 color;
in vec3 frag_pos;
out vec4 frag_color;
void main() {
frag_color = color;
vec3 dx = dFdx(frag_pos) * 100;
vec3 dy = dFdy(frag_pos) * 100;
vec3 N = normalize(cross(dFdx(frag_pos), dFdy(frag_pos)));
frag_color = color + vec4(N/2, 1)/3;
//frag_color = vec4(length(dx), length(dy), length(dy), 1.0);
//frag_color += color;
}

View File

@@ -1,8 +1,13 @@
#version 330 core
layout (location = 0) in vec3 pos;
uniform vec4 color;
layout (location = 0) in vec3 pos;
out vec3 frag_pos;
uniform mat4 global_t;
uniform mat4 camera_t;
uniform mat4 projection_t;
void main() {
gl_Position = vec4(pos.x, pos.y, pos.z, 1.0);
gl_Position = projection_t * camera_t * global_t * vec4(pos.x, pos.y, pos.z, 1.0);
frag_pos = pos;
}

View File

@@ -1,4 +1,5 @@
#include <cstdio>
#include <assert.h>
#include <stdlib.h>
#include "util.hpp"