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

View file

@ -10,26 +10,28 @@ public:
OPGLWidget(QWidget *parent) : QOpenGLWidget(parent) {} OPGLWidget(QWidget *parent) : QOpenGLWidget(parent) {}
ObjectLoader obj; ObjectLoader obj;
public slots: public slots:
void paintGL(); void paintGL(); // for repaint loop
void update_camera(); void update_camera(); // for mouse movement
protected: protected:
void initializeGL(); void initializeGL();
void resizeGL(int w, int h); void resizeGL(int w, int h);
void reorient(); void reorient();
// camera controls
void keyPressEvent(QKeyEvent *event); void keyPressEvent(QKeyEvent *event);
void keyReleaseEvent(QKeyEvent *event); void keyReleaseEvent(QKeyEvent *event);
void mousePressEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event);
int mtl_idx = -1; // init values
float loc[3] = {0, 0, 10}; int mtl_idx = -1; // index to store current material to use
float f[3] = {0, 0, -1}; float loc[3] = {0, 0, 10}; // initial camera position
float up[3] = {0, 1, 0}; float f[3] = {0, 0, -1}; // initial camera direction
float right[3] = {1, 0, 0}; float up[3] = {0, 1, 0}; // initial camera up vector
float vel[3] = {0, 0, 0}; float right[3] = {1, 0, 0}; // initial camera right vector
float speed = 0; float vel[3] = {0, 0, 0}; // initial camera velocity
bool key_states[6] = {false, false, false, false, false, false}; float speed = 0; // placeholder init for abs speed (for limit and slowing mostly)
float mouse_loc_old[2]; 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 <QCommandLineParser>
#include <QCommandLineOption> #include <QCommandLineOption>
// main loop
int main(int argc, char **argv) { int main(int argc, char **argv) {
QApplication app(argc, argv); QApplication app(argc, argv);
QCommandLineParser parser; QCommandLineParser parser;
// pass in files as -m and -o
QCommandLineOption mtl_filepath_opt({"m", "material"}, "Material file path, required", "path"); QCommandLineOption mtl_filepath_opt({"m", "material"}, "Material file path, required", "path");
QCommandLineOption obj_filepath_opt({"o", "object"}, "Object file path, required", "path"); QCommandLineOption obj_filepath_opt({"o", "object"}, "Object file path, required", "path");
parser.addHelpOption(); parser.addHelpOption();
@ -19,16 +21,21 @@ int main(int argc, char **argv) {
if (mtl_filepath.isEmpty() || obj_filepath.isEmpty()) { if (mtl_filepath.isEmpty() || obj_filepath.isEmpty()) {
return 1; return 1;
} }
QMainWindow w; QMainWindow w;
Ui::MainWindow ui; Ui::MainWindow ui;
ui.setupUi(&w); ui.setupUi(&w);
// parse obj and mtl files
ui.openGLWidget->obj.load_mtl(mtl_filepath.toStdString()); ui.openGLWidget->obj.load_mtl(mtl_filepath.toStdString());
ui.openGLWidget->obj.load_obj(obj_filepath.toStdString()); ui.openGLWidget->obj.load_obj(obj_filepath.toStdString());
// timers for drawing and movement loops
QTimer timer_draw; QTimer timer_draw;
QTimer timer_cam; QTimer timer_cam;
w.connect(&timer_draw, SIGNAL(timeout()), ui.openGLWidget, SLOT(paintGL())); w.connect(&timer_draw, SIGNAL(timeout()), ui.openGLWidget, SLOT(paintGL()));
w.connect(&timer_cam, SIGNAL(timeout()), ui.openGLWidget, SLOT(update_camera())); w.connect(&timer_cam, SIGNAL(timeout()), ui.openGLWidget, SLOT(update_camera()));
// update every 16ms
timer_draw.start(16); timer_draw.start(16);
timer_cam.start(16); timer_cam.start(16);
w.show(); w.show();

View file

@ -5,11 +5,16 @@
#include <cstring> #include <cstring>
#include <regex> #include <regex>
// .mtl file load and parse
void ObjectLoader::load_mtl(string filename) { void ObjectLoader::load_mtl(string filename) {
string tmp; string tmp;
ifstream f(filename); ifstream f(filename); // open file
int mtl_idx = -1; int mtl_idx = -1; // init index outside of valid range
while (getline(f, tmp)) { 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) { if (tmp.rfind("newmtl", 0) == 0) {
mtl_idx += 1; mtl_idx += 1;
mtls.push_back(*(new Material)); mtls.push_back(*(new Material));
@ -29,23 +34,30 @@ void ObjectLoader::load_mtl(string filename) {
default: default:
continue; continue;
}; };
// parse syntax
// eg; Kd 0.2 0.2 0.2
// delim by space, split and parse value
char delim = ' '; char delim = ' ';
char *token = strtok(const_cast<char*>(tmp.c_str()), &delim); char *token = strtok(const_cast<char*>(tmp.c_str()), &delim);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
token = strtok(NULL, &delim); token = strtok(NULL, &delim);
target_list[i] = atof(token); target_list[i] = atof(token);
}; };
// set alpha val since blender doesn't output it
target_list[3] = 1.0f; target_list[3] = 1.0f;
}; };
}; };
}; };
// .obj file load and parsing
// only supports single object files
void ObjectLoader::load_obj(string filename) { void ObjectLoader::load_obj(string filename) {
string tmp; string tmp;
ifstream f(filename); ifstream f(filename);
while (getline(f, tmp)) { while (getline(f, tmp)) {
Material* mtl_ptr; Material* mtl_ptr;
if (tmp[0] == 'v') { if (tmp[0] == 'v') {
// if vertex (v) or vertex normal (vn), create new vertex struct
float* target_list; float* target_list;
switch (tmp[1]) { switch (tmp[1]) {
case ' ': case ' ':
@ -59,6 +71,11 @@ void ObjectLoader::load_obj(string filename) {
default: default:
continue; 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 delim = ' ';
char *token = strtok(const_cast<char*>(tmp.c_str()), &delim); char *token = strtok(const_cast<char*>(tmp.c_str()), &delim);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
@ -67,8 +84,14 @@ void ObjectLoader::load_obj(string filename) {
}; };
} else if (tmp.rfind("usemtl", 0) == 0) { } 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)); mtl_ptr = get_mtl_ptr(tmp.substr(tmp.find(' ') + 1));
} else if (tmp[0] == 'f') { } 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.push_back(*(new Face));
faces.back().mtl = mtl_ptr; faces.back().mtl = mtl_ptr;
regex re("f (\\d*)//(\\d*) (\\d*)//(\\d*) (\\d*)//(\\d*)"); 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) { Material* ObjectLoader::get_mtl_ptr(string mtl_name) {
for (size_t i = 0; i < mtls.size(); i++) { for (size_t i = 0; i < mtls.size(); i++) {
if (mtls[i].name == mtl_name) { if (mtls[i].name == mtl_name) {

View file

@ -4,6 +4,9 @@
#include <math.h> #include <math.h>
#include <QKeyEvent> #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() { void OPGLWidget::initializeGL() {
glClearDepth(1.0f); glClearDepth(1.0f);
glDepthFunc(GL_LESS); glDepthFunc(GL_LESS);
@ -14,6 +17,7 @@ void OPGLWidget::initializeGL() {
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glEnable(GL_LIGHTING); glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0); 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_ambient[] = {0.0, 0.0, 0.0, 1.0};
GLfloat light_diffuse[] = {1.0, 1.0, 1.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}; 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); glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
} }
// paint loop
void OPGLWidget::paintGL() { void OPGLWidget::paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); 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); glShadeModel(GL_SMOOTH);
glPolygonMode(GL_FRONT, GL_FILL); glPolygonMode(GL_FRONT, GL_FILL);
glBegin(GL_TRIANGLES); 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++) { for (size_t i = 0; i < obj.faces.size(); i++) {
Material* mtl_ptr = obj.faces[i].mtl; Material* mtl_ptr = obj.faces[i].mtl;
if (mtl_idx != mtl_ptr->idx) { if (mtl_idx != mtl_ptr->idx) {
@ -51,6 +60,7 @@ void OPGLWidget::paintGL() {
update(); update();
} }
// cursed implementation of gluPerscpective here: https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml
void OPGLWidget::reorient() { void OPGLWidget::reorient() {
GLfloat s[] = {-f[2], 0, f[0]}; 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); 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]); glTranslatef(-loc[0], -loc[1], -loc[2]);
} }
// window resize
void OPGLWidget::resizeGL(int w, int h) { void OPGLWidget::resizeGL(int w, int h) {
glViewport(0, 0, w, h); glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
@ -73,10 +84,13 @@ void OPGLWidget::resizeGL(int w, int h) {
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
} }
// camera update, movement simulation
void OPGLWidget::update_camera() { void OPGLWidget::update_camera() {
// update location in direction of velocity by 1/100 of speed each tick
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
loc[i] += vel[i] * speed/100.0f; loc[i] += vel[i] * speed/100.0f;
}; };
// cursed, but its here, update velocity by corresponding key
if (key_states[0]) { if (key_states[0]) {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
vel[i] += f[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 = pow(pow(vel[0], 2) + pow(vel[1], 2) + pow(vel[2], 2), 0.5);
// speed limit, update vel if over
if (speed > 2) { if (speed > 2) {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
vel[i] = vel[i] / speed; vel[i] = vel[i] / speed;
} }
} }
// if no key pressed, decay speed until threshold then stop
bool allFalse = true; bool allFalse = true;
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
if (key_states[i]) { if (key_states[i]) {
@ -131,6 +148,7 @@ void OPGLWidget::update_camera() {
} }
} }
// key mappings
void OPGLWidget::keyPressEvent(QKeyEvent *event) { void OPGLWidget::keyPressEvent(QKeyEvent *event) {
switch (event -> key()) { switch (event -> key()) {
case Qt::Key_W: 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) { void OPGLWidget::mousePressEvent(QMouseEvent *event) {
setMouseTracking(true); setMouseTracking(true);
mouse_loc_old[0] = event -> x(); mouse_loc_old[0] = event -> x();
mouse_loc_old[1] = event -> y(); mouse_loc_old[1] = event -> y();
} }
// stop tracking on release
void OPGLWidget::mouseReleaseEvent(QMouseEvent *event) { void OPGLWidget::mouseReleaseEvent(QMouseEvent *event) {
setMouseTracking(false); setMouseTracking(false);
mouse_loc_old[0] = event -> x(); mouse_loc_old[0] = event -> x();
mouse_loc_old[1] = event -> y(); mouse_loc_old[1] = event -> y();
} }
// actual camera panning implementation
void OPGLWidget::mouseMoveEvent(QMouseEvent *event) { 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_x = event->x() - mouse_loc_old[0];
float delta_y = event->y() - mouse_loc_old[1]; float delta_y = event->y() - mouse_loc_old[1];
mouse_loc_old[0] = event->x(); mouse_loc_old[0] = event->x();
mouse_loc_old[1] = event->y(); mouse_loc_old[1] = event->y();
// shift forward vector appropriately with movement
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
f[i] += right[i] * delta_x / 1000.0f; f[i] += right[i] * delta_x / 1000.0f;
f[i] -= up[i] * delta_y / 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[0] = -f[2];
right[2] = f[0]; 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 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); 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++) { for (int i = 0; i < 3; i++) {