Added comments

This commit is contained in:
Muaz Ahmad 2023-08-03 14:43:11 +05:00
parent 509e94ac58
commit e58ad18fb1
5 changed files with 82 additions and 14 deletions

View file

@ -1,14 +1,20 @@
// not good at using namespaces, bear with me
#include <vector>
#include <string>
using namespace std;
// struct to hold vertex position
struct Vertex {
float pos[3];
};
// struct to hold normal vectors
struct Normal {
float dir[3];
};
// struct to hold material information
struct Material {
int idx;
string name;
@ -17,12 +23,15 @@ struct Material {
float diffuse[4];
};
// meta struct for each individual face, pointers to its set of
// vertices, normals and material structs
struct Face {
vector<Vertex*> vertices;
vector<Normal*> normals;
Material* mtl;
};
// class to load and handle .obj and .mtl parsing and face data construction
class ObjectLoader
{
public:

View file

@ -10,26 +10,28 @@ public:
OPGLWidget(QWidget *parent) : QOpenGLWidget(parent) {}
ObjectLoader obj;
public slots:
void paintGL();
void update_camera();
void paintGL(); // for repaint loop
void update_camera(); // for mouse movement
protected:
void initializeGL();
void resizeGL(int w, int h);
void reorient();
// camera controls
void keyPressEvent(QKeyEvent *event);
void keyReleaseEvent(QKeyEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
int mtl_idx = -1;
float loc[3] = {0, 0, 10};
float f[3] = {0, 0, -1};
float up[3] = {0, 1, 0};
float right[3] = {1, 0, 0};
float vel[3] = {0, 0, 0};
float speed = 0;
bool key_states[6] = {false, false, false, false, false, false};
float mouse_loc_old[2];
// init values
int mtl_idx = -1; // index to store current material to use
float loc[3] = {0, 0, 10}; // initial camera position
float f[3] = {0, 0, -1}; // initial camera direction
float up[3] = {0, 1, 0}; // initial camera up vector
float right[3] = {1, 0, 0}; // initial camera right vector
float vel[3] = {0, 0, 0}; // initial camera velocity
float speed = 0; // placeholder init for abs speed (for limit and slowing mostly)
bool key_states[6] = {false, false, false, false, false, false}; // camera control states
float mouse_loc_old[2]; // for calculating camera direction changes with mouse movement
};

View file

@ -5,9 +5,11 @@
#include <QCommandLineParser>
#include <QCommandLineOption>
// main loop
int main(int argc, char **argv) {
QApplication app(argc, argv);
QCommandLineParser parser;
// pass in files as -m and -o
QCommandLineOption mtl_filepath_opt({"m", "material"}, "Material file path, required", "path");
QCommandLineOption obj_filepath_opt({"o", "object"}, "Object file path, required", "path");
parser.addHelpOption();
@ -19,16 +21,21 @@ int main(int argc, char **argv) {
if (mtl_filepath.isEmpty() || obj_filepath.isEmpty()) {
return 1;
}
QMainWindow w;
Ui::MainWindow ui;
ui.setupUi(&w);
// parse obj and mtl files
ui.openGLWidget->obj.load_mtl(mtl_filepath.toStdString());
ui.openGLWidget->obj.load_obj(obj_filepath.toStdString());
// timers for drawing and movement loops
QTimer timer_draw;
QTimer timer_cam;
w.connect(&timer_draw, SIGNAL(timeout()), ui.openGLWidget, SLOT(paintGL()));
w.connect(&timer_cam, SIGNAL(timeout()), ui.openGLWidget, SLOT(update_camera()));
// update every 16ms
timer_draw.start(16);
timer_cam.start(16);
w.show();

View file

@ -5,11 +5,16 @@
#include <cstring>
#include <regex>
// .mtl file load and parse
void ObjectLoader::load_mtl(string filename) {
string tmp;
ifstream f(filename);
int mtl_idx = -1;
ifstream f(filename); // open file
int mtl_idx = -1; // init index outside of valid range
while (getline(f, tmp)) {
// if line defines a new material
// create new and set its name
// following lines will define ambient, spec, diffuse info for
// the newly defined material
if (tmp.rfind("newmtl", 0) == 0) {
mtl_idx += 1;
mtls.push_back(*(new Material));
@ -29,23 +34,30 @@ void ObjectLoader::load_mtl(string filename) {
default:
continue;
};
// parse syntax
// eg; Kd 0.2 0.2 0.2
// delim by space, split and parse value
char delim = ' ';
char *token = strtok(const_cast<char*>(tmp.c_str()), &delim);
for (int i = 0; i < 3; i++) {
token = strtok(NULL, &delim);
target_list[i] = atof(token);
};
// set alpha val since blender doesn't output it
target_list[3] = 1.0f;
};
};
};
// .obj file load and parsing
// only supports single object files
void ObjectLoader::load_obj(string filename) {
string tmp;
ifstream f(filename);
while (getline(f, tmp)) {
Material* mtl_ptr;
if (tmp[0] == 'v') {
// if vertex (v) or vertex normal (vn), create new vertex struct
float* target_list;
switch (tmp[1]) {
case ' ':
@ -59,6 +71,11 @@ void ObjectLoader::load_obj(string filename) {
default:
continue;
};
// same parsing as material values above
// something breaks here somehow on first run i think
// no clue if its pointer wierdness or something with
// the files but vector floats get borked
// works perfectly fine on next runs so... eh?
char delim = ' ';
char *token = strtok(const_cast<char*>(tmp.c_str()), &delim);
for (int i = 0; i < 3; i++) {
@ -67,8 +84,14 @@ void ObjectLoader::load_obj(string filename) {
};
} else if (tmp.rfind("usemtl", 0) == 0) {
// if line declares material scope, point the pointer to said material
mtl_ptr = get_mtl_ptr(tmp.substr(tmp.find(' ') + 1));
} else if (tmp[0] == 'f') {
// face declared as set of v/vt/vn indexes
// currently ignoring vt and only using files without any vt exported
// (like generic meshes with materials)
// also only handles triangle mesh objects
// assign it a metal and its corresponding vertexes and normals
faces.push_back(*(new Face));
faces.back().mtl = mtl_ptr;
regex re("f (\\d*)//(\\d*) (\\d*)//(\\d*) (\\d*)//(\\d*)");
@ -85,6 +108,7 @@ void ObjectLoader::load_obj(string filename) {
};
};
// just finds the pointer to the given material from its string
Material* ObjectLoader::get_mtl_ptr(string mtl_name) {
for (size_t i = 0; i < mtls.size(); i++) {
if (mtls[i].name == mtl_name) {

View file

@ -4,6 +4,9 @@
#include <math.h>
#include <QKeyEvent>
// probably should have learned OpenGL and rendering
// first, not even going to pretend to know what the first half
// does
void OPGLWidget::initializeGL() {
glClearDepth(1.0f);
glDepthFunc(GL_LESS);
@ -14,6 +17,7 @@ void OPGLWidget::initializeGL() {
glMatrixMode(GL_MODELVIEW);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
// values here largely just copied from default light object in blender
GLfloat light_ambient[] = {0.0, 0.0, 0.0, 1.0};
GLfloat light_diffuse[] = {1.0, 1.0, 1.0, 1.0};
GLfloat light_specular[] = {0.0, 0.5, 0.5, 0.2};
@ -25,13 +29,18 @@ void OPGLWidget::initializeGL() {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
// paint loop
void OPGLWidget::paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
reorient();
reorient(); // implementation of gluPerspective to shift camera perspective
// mostly just because I could not figure out how to get GLU included properly
glShadeModel(GL_SMOOTH);
glPolygonMode(GL_FRONT, GL_FILL);
glBegin(GL_TRIANGLES);
// for each face, check if its material is different from the one
// currently selected in mtl_idx. if different, populate new values and move idx
// then draw each vertex and normal
for (size_t i = 0; i < obj.faces.size(); i++) {
Material* mtl_ptr = obj.faces[i].mtl;
if (mtl_idx != mtl_ptr->idx) {
@ -51,6 +60,7 @@ void OPGLWidget::paintGL() {
update();
}
// cursed implementation of gluPerscpective here: https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml
void OPGLWidget::reorient() {
GLfloat s[] = {-f[2], 0, f[0]};
float s_mag = pow(pow(s[0], 2) + pow(s[1], 2) + pow(s[2], 2), 0.5);
@ -62,6 +72,7 @@ void OPGLWidget::reorient() {
glTranslatef(-loc[0], -loc[1], -loc[2]);
}
// window resize
void OPGLWidget::resizeGL(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
@ -73,10 +84,13 @@ void OPGLWidget::resizeGL(int w, int h) {
glMatrixMode(GL_MODELVIEW);
}
// camera update, movement simulation
void OPGLWidget::update_camera() {
// update location in direction of velocity by 1/100 of speed each tick
for (int i = 0; i < 3; i++) {
loc[i] += vel[i] * speed/100.0f;
};
// cursed, but its here, update velocity by corresponding key
if (key_states[0]) {
for (int i = 0; i < 3; i++) {
vel[i] += f[i];
@ -108,11 +122,14 @@ void OPGLWidget::update_camera() {
}
};
speed = pow(pow(vel[0], 2) + pow(vel[1], 2) + pow(vel[2], 2), 0.5);
// speed limit, update vel if over
if (speed > 2) {
for (int i = 0; i < 3; i++) {
vel[i] = vel[i] / speed;
}
}
// if no key pressed, decay speed until threshold then stop
bool allFalse = true;
for (int i = 0; i < 6; i++) {
if (key_states[i]) {
@ -131,6 +148,7 @@ void OPGLWidget::update_camera() {
}
}
// key mappings
void OPGLWidget::keyPressEvent(QKeyEvent *event) {
switch (event -> key()) {
case Qt::Key_W:
@ -177,29 +195,37 @@ void OPGLWidget::keyReleaseEvent(QKeyEvent *event) {
};
}
// camera panning only happens on drag, also makes it so I don't
// have to bother with mouse capture and other wierdness from i3 tiling
void OPGLWidget::mousePressEvent(QMouseEvent *event) {
setMouseTracking(true);
mouse_loc_old[0] = event -> x();
mouse_loc_old[1] = event -> y();
}
// stop tracking on release
void OPGLWidget::mouseReleaseEvent(QMouseEvent *event) {
setMouseTracking(false);
mouse_loc_old[0] = event -> x();
mouse_loc_old[1] = event -> y();
}
// actual camera panning implementation
void OPGLWidget::mouseMoveEvent(QMouseEvent *event) {
// calc a delta movement from old, then new -> old
float delta_x = event->x() - mouse_loc_old[0];
float delta_y = event->y() - mouse_loc_old[1];
mouse_loc_old[0] = event->x();
mouse_loc_old[1] = event->y();
// shift forward vector appropriately with movement
for (int i = 0; i < 3; i++) {
f[i] += right[i] * delta_x / 1000.0f;
f[i] -= up[i] * delta_y / 1000.0f;
}
// since up is static, cross product to find right, which is simply:
right[0] = -f[2];
right[2] = f[0];
// normalize forward and right
float F_mag = pow(pow(f[0], 2) + pow(f[1], 2) + pow(f[2], 2), 0.5);
float right_mag = pow(pow(right[0], 2) + pow(right[1], 2) + pow(right[2], 2), 0.5);
for (int i = 0; i < 3; i++) {