Added comments
This commit is contained in:
parent
509e94ac58
commit
e58ad18fb1
5 changed files with 82 additions and 14 deletions
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
|
|
28
src/obj.cpp
28
src/obj.cpp
|
@ -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) {
|
||||||
|
|
|
@ -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++) {
|
||||||
|
|
Loading…
Reference in a new issue