rasterizer
C++ software rendererfeat: MTL parsing
| 3 files changed, 121 insertions(+), 6 deletions(-) | |||
|---|---|---|---|
| MOD | src/mesh.cpp | +90 | -6 |
| MOD | src/mesh.h | +12 | -0 |
| MOD | src/util_string.h | +19 | -0 |
--- a/src/mesh.cpp
+++ b/src/mesh.cpp
@@ -4,13 +4,17 @@
#include <stdlib.h>
#include <string.h>
-#include "globals.h"
-#include "util_math.h"
#include "util_string.h"
+#include "texture.h"
+
+int current_material_idx = 0;
+int current_texture_idx = 0;
Mesh parse_obj(const char *filename) {
Mesh mesh;
+ bool obj_has_material = false;
+
FILE *file = fopen(filename, "r");
if (!file) {
printf("Failed to open file: %s\n", filename);
@@ -20,6 +24,16 @@ Mesh parse_obj(const char *filename) {
char line_buffer[1024];
string line = {nullptr, 0};
+ char base_dir[512];
+ strcpy(base_dir, filename);
+
+ char *last_slash = strrchr(base_dir, '/');
+ if (last_slash) {
+ last_slash[1] = '\0';
+ } else {
+ base_dir[0] = '\0';
+ }
+
while (fgets(line_buffer, sizeof(line_buffer), file)) {
line = string_from_cstr(line_buffer);
string_trim(&line);
@@ -27,19 +41,19 @@ Mesh parse_obj(const char *filename) {
if (line.size == 0 || line.str[0] == '#') {
continue;
}
- if (strncmp(line.str, "v ", 2) == 0) {
+ if (string_starts_with(line, "v ")) {
vec3f v;
sscanf(line.str, "v %f %f %f", &v.x, &v.y, &v.z);
mesh.vertices.push_back(v);
- } else if (strncmp(line.str, "vt ", 3) == 0) {
+ } else if (string_starts_with(line, "vt ")) {
vec2f vt;
sscanf(line.str, "vt %f %f", &vt.x, &vt.y);
mesh.texcoords.push_back(vt);
- } else if (strncmp(line.str, "vn ", 3) == 0) {
+ } else if (string_starts_with(line, "vn ")) {
vec3f vn;
sscanf(line.str, "vn %f %f %f", &vn.x, &vn.y, &vn.z);
mesh.normals.push_back(vn);
- } else if (strncmp(line.str, "f ", 2) == 0) {
+ } else if (string_starts_with(line, "f ")) {
char *face_token = strtok(line.str + 2, " \n\r");
while (face_token) {
Face idx = {0, 0, 0};
@@ -62,9 +76,79 @@ Mesh parse_obj(const char *filename) {
if (v > 0) idx.vertex_idx = v - 1;
if (vt > 0) idx.vertex_texture_idx = vt - 1;
if (vn > 0) idx.vertex_normal_idx = vn - 1;
+
+ if (obj_has_material) {
+ idx.mat_idx = current_material_idx;
+ } else {
+ idx.mat_idx = 0;
+ }
+
mesh.faces.push_back(idx);
face_token = strtok(nullptr, " \n\r");
}
+ } else if (string_starts_with(line, "mtllib ")) {
+ obj_has_material = true;
+
+ char mtlfile[256];
+ sscanf(line.str + 7, "%255s", mtlfile);
+ char mtlpath[512];
+ strcpy(mtlpath, base_dir);
+ strcat(mtlpath, mtlfile);
+
+ FILE *mtl = fopen(mtlpath, "r");
+ if (!mtl) {
+ printf("Failed to open MTL: %s\n", mtlpath);
+ continue;
+ }
+
+ Material current = {};
+ char mtl_line[512];
+ while (fgets(mtl_line, sizeof(mtl_line), mtl)) {
+ string mline = string_from_cstr(mtl_line);
+ string_trim(&mline);
+
+ if (string_starts_with(mline, "newmtl ")) {
+ if (current.name[0] != '\0') {
+ materials.push_back(current);
+ }
+ memset(¤t, 0, sizeof(Material));
+ sscanf(mline.str + 7, "%127s", current.name);
+ } else if (string_starts_with(mline, "Ka ")) {
+ sscanf(mline.str + 3, "%f %f %f", ¤t.ambient.x, ¤t.ambient.y, ¤t.ambient.z);
+ } else if (string_starts_with(mline, "Kd ")) {
+ sscanf(mline.str + 3, "%f %f %f", ¤t.diffuse.x, ¤t.diffuse.y, ¤t.diffuse.z);
+ } else if (string_starts_with(mline, "Ks ")) {
+ sscanf(mline.str + 3, "%f %f %f", ¤t.specular.x, ¤t.specular.y, ¤t.specular.z);
+ } else if (string_starts_with(mline, "map_Kd ")) {
+ char texfile[256];
+ sscanf(mline.str + 7, "%255s", texfile);
+ char fulltex[512];
+ strcpy(fulltex, base_dir);
+ strcat(fulltex, texfile);
+
+ Texture t = load_texture(fulltex);
+ textures.push_back(t);
+
+ current.texture_img_idx = (int)textures.size() - 1;
+ strcpy(current.texture_path, fulltex);
+ }
+ }
+ if (current.name[0] != '\0') {
+ materials.push_back(current);
+ }
+ fclose(mtl);
+ } else if (string_starts_with(line, "usemtl ")) {
+ obj_has_material = true;
+
+ char matname[128];
+ sscanf(line.str + 7, "%127s", matname);
+
+ for (int i = 0; i < materials.size(); i++) {
+ if (strcmp(materials[i].name, matname) == 0) {
+ current_material_idx = i;
+ break;
+ }
+ }
}
}
fclose(file);MOD · src/mesh.h +12 -0--- a/src/mesh.h
+++ b/src/mesh.h
@@ -2,6 +2,7 @@
#include <vector>
+#include "globals.h"
#include "util_math.h"
using std::vector;
@@ -10,6 +11,7 @@ struct Face {
int vertex_idx;
int vertex_texture_idx;
int vertex_normal_idx;
+ u32 mat_idx;
};
struct Transform {
@@ -28,6 +30,16 @@ struct Transform {
}
};
+struct Material {
+ char name[128];
+ char texture_path[512];
+ vec3f ambient; // Ka
+ vec3f diffuse; // Kd;
+ vec3f specular; // Ks;
+ u32 texture_img_idx; // 0 sentinel for missing, populated in initialize_scene()
+};
+extern vector<Material> materials;
+
struct Mesh {
vector<vec3f> vertices;
vector<vec2f> texcoords;MOD · src/util_string.h +19 -0--- a/src/util_string.h
+++ b/src/util_string.h
@@ -37,4 +37,23 @@ inline void string_trim(string *s) {
s->str += start;
s->size = end - start;
}
+
+inline bool string_starts_with(const string &s, const char *prefix) {
+ size_t len = strlen(prefix);
+ if (s.size < len) {
+ return false;
+ }
+ return strncmp(s.str, prefix, len) == 0;
+}
+
+inline bool string_equals(string a, string b) {
+ if (a.size != b.size) {
+ return false;
+ }
+ for (uint32_t i = 0; i < a.size; ++i) {
+ if (a.str[i] != b.str[i]) {
+ return false;
+ }
+ }
+ return true;
}