Loading mtl textures!
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@@ -4,6 +4,12 @@ assets/
|
|||||||
stats/
|
stats/
|
||||||
|
|
||||||
data.txt
|
data.txt
|
||||||
|
*.ilk
|
||||||
|
*.exp
|
||||||
|
*.exe
|
||||||
|
*.lib
|
||||||
|
*.pdb
|
||||||
*.sln
|
*.sln
|
||||||
*.vcxproj*
|
*.vcxproj*
|
||||||
*.rdbg
|
*.rdbg
|
||||||
|
*.sublime*
|
||||||
54
main.cpp
54
main.cpp
@@ -25,8 +25,11 @@
|
|||||||
/// - [x] FPS Camera
|
/// - [x] FPS Camera
|
||||||
/// - [x] Reading OBJ models
|
/// - [x] Reading OBJ models
|
||||||
/// - [ ] Reading more OBJ formats
|
/// - [ ] Reading more OBJ formats
|
||||||
/// - [ ] Reading OBJ .mtl files
|
/// - [x] Reading OBJ .mtl files
|
||||||
|
/// - [x] Loading materials
|
||||||
|
/// - [x] Rendering textures obj models
|
||||||
/// - [x] Reading complex obj models (sponza)
|
/// - [x] Reading complex obj models (sponza)
|
||||||
|
/// - [ ] Fix sponza uv coordinates
|
||||||
/// - [ ] Reading PMX files
|
/// - [ ] Reading PMX files
|
||||||
/// - [ ] Rendering multiple objects, queue renderer
|
/// - [ ] Rendering multiple objects, queue renderer
|
||||||
/// - [x] Simple function to render a mesh
|
/// - [x] Simple function to render a mesh
|
||||||
@@ -92,8 +95,8 @@ struct R_Render {
|
|||||||
F32 *depth320;
|
F32 *depth320;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "obj_parser.cpp"
|
|
||||||
#include "stb_image.h"
|
#include "stb_image.h"
|
||||||
|
#include "obj_parser.cpp"
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
|
||||||
GLOBAL B32 draw_rects = 0;
|
GLOBAL B32 draw_rects = 0;
|
||||||
@@ -291,6 +294,9 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 ligh
|
|||||||
F32 v = tex0.y * (w1 / p0.w) + tex1.y * (w2 / p1.w) + tex2.y * (w3 / p2.w);
|
F32 v = tex0.y * (w1 / p0.w) + tex1.y * (w2 / p1.w) + tex2.y * (w3 / p2.w);
|
||||||
u /= interpolated_w;
|
u /= interpolated_w;
|
||||||
v /= interpolated_w;
|
v /= interpolated_w;
|
||||||
|
u = CLAMP(0, u, 1);
|
||||||
|
v = CLAMP(0, v, 1);
|
||||||
|
if(u > 1 || v > 1 || u < 0 || v < 0) continue;
|
||||||
// @Note: We could do: interpolated_w = 1.f / interpolated_w to get proper depth
|
// @Note: We could do: interpolated_w = 1.f / interpolated_w to get proper depth
|
||||||
// but why waste an instruction, the smaller the depth value the farther the object
|
// but why waste an instruction, the smaller the depth value the farther the object
|
||||||
F32* depth = depth_buffer + (x + y * dst->x);
|
F32* depth = depth_buffer + (x + y * dst->x);
|
||||||
@@ -411,26 +417,6 @@ FUNCTION
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FUNCTION
|
|
||||||
Bitmap load_image(const char* path) {
|
|
||||||
int x, y, n;
|
|
||||||
unsigned char* data = stbi_load(path, &x, &y, &n, 4);
|
|
||||||
Bitmap result = { (U32*)data, x, y };
|
|
||||||
#if PREMULTIPLIED_ALPHA_BLENDING
|
|
||||||
U32 *p = result.pixels;
|
|
||||||
for (int Y = 0; Y < y; Y++) {
|
|
||||||
for (int X = 0; X < x; X++) {
|
|
||||||
Vec4 color = vec4abgr(*p);
|
|
||||||
color.r *= color.a;
|
|
||||||
color.g *= color.a;
|
|
||||||
color.b *= color.a;
|
|
||||||
*p++ = vec4_to_u32abgr(color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
FN void r_scatter_plot(Bitmap *dst, F64 *data, I64 data_len) {
|
FN void r_scatter_plot(Bitmap *dst, F64 *data, I64 data_len) {
|
||||||
F64 min = F32MAX;
|
F64 min = F32MAX;
|
||||||
F64 max = FLT_MIN;
|
F64 max = FLT_MIN;
|
||||||
@@ -453,9 +439,14 @@ FN void r_scatter_plot(Bitmap *dst, F64 *data, I64 data_len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
S8 scenario_name = string_null;
|
S8 scenario_name = string_null;
|
||||||
FN void r_draw_mesh(R_Render *r, ObjMesh *mesh, Vec3 *vertices, Vec2 *tex_coords, Vec3 *normals) {
|
FN void r_draw_mesh(R_Render *r, OBJMaterial *materials, ObjMesh *mesh, Vec3 *vertices, Vec2 *tex_coords, Vec3 *normals) {
|
||||||
for (int i = 0; i < mesh->indices.len; i++) {
|
for (int i = 0; i < mesh->indices.len; i++) {
|
||||||
ObjIndex *index = mesh->indices.e + i;
|
ObjIndex *index = mesh->indices.e + i;
|
||||||
|
OBJMaterial *material = materials + index->material_id;
|
||||||
|
Bitmap *image = &material->texture_ambient;
|
||||||
|
if(image->pixels == 0) {
|
||||||
|
image = &r->img;
|
||||||
|
}
|
||||||
R_Vertex vert[] = {
|
R_Vertex vert[] = {
|
||||||
{
|
{
|
||||||
vertices[index->vertex[0] - 1],
|
vertices[index->vertex[0] - 1],
|
||||||
@@ -572,10 +563,9 @@ FN void r_draw_mesh(R_Render *r, ObjMesh *mesh, Vec3 *vertices, Vec2 *tex_coords
|
|||||||
in[j].pos.y += r->screen320.y / 2;
|
in[j].pos.y += r->screen320.y / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
draw_triangle_nearest(&r->screen320, r->depth320, image, light, in[0].pos, in[1].pos, in[2].pos, in[0].tex, in[1].tex, in[2].tex);
|
||||||
draw_triangle_nearest(&r->screen320, r->depth320, &r->img, light, in[0].pos, in[1].pos, in[2].pos, in[0].tex, in[1].tex, in[2].tex);
|
|
||||||
if (in_count > 3) {
|
if (in_count > 3) {
|
||||||
draw_triangle_nearest(&r->screen320, r->depth320, &r->img, light, in[0].pos, in[2].pos, in[3].pos, in[0].tex, in[2].tex, in[3].tex);
|
draw_triangle_nearest(&r->screen320, r->depth320, image, light, in[0].pos, in[2].pos, in[3].pos, in[0].tex, in[2].tex, in[3].tex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -618,17 +608,17 @@ int main() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario_name = LIT("assets/f22.obj");
|
// scenario_name = LIT("assets/f22.obj");
|
||||||
//scenario_name = LIT("assets/cube.obj");
|
//scenario_name = LIT("assets/cube.obj");
|
||||||
//scenario_name = LIT("assets/AnyConv.com__White.obj");
|
//scenario_name = LIT("assets/AnyConv.com__White.obj");
|
||||||
//scenario_name = LIT("assets/sponza/sponza.obj");
|
scenario_name = LIT("assets/sponza/sponza.obj");
|
||||||
Obj obj = load_obj(scenario_name);
|
Obj obj = load_obj(os.perm_arena, scenario_name);
|
||||||
Vec3* vertices = (Vec3 *)obj.vertices.e;
|
Vec3* vertices = (Vec3 *)obj.vertices.e;
|
||||||
Vec2* tex_coords = (Vec2*)obj.texture_coordinates.e;
|
Vec2* tex_coords = (Vec2*)obj.texture_coordinates.e;
|
||||||
Vec3 *normals = (Vec3 *)obj.normals.e;
|
Vec3 *normals = (Vec3 *)obj.normals.e;
|
||||||
ObjMesh *mesh = obj.mesh.e;
|
ObjMesh *mesh = obj.mesh.e;
|
||||||
|
|
||||||
F32 speed = 5.f;
|
F32 speed = 100.f;
|
||||||
F32 rotation = 0;
|
F32 rotation = 0;
|
||||||
|
|
||||||
int screen_x = 320;
|
int screen_x = 320;
|
||||||
@@ -638,7 +628,7 @@ int main() {
|
|||||||
r.screen320 = {(U32 *)PUSH_SIZE(os.perm_arena, screen_x*screen_y*sizeof(U32)), screen_x, screen_y};
|
r.screen320 = {(U32 *)PUSH_SIZE(os.perm_arena, screen_x*screen_y*sizeof(U32)), screen_x, screen_y};
|
||||||
r.plot = {(U32 *)PUSH_SIZE(os.perm_arena, 1280*720*sizeof(U32)), 1280, 720};
|
r.plot = {(U32 *)PUSH_SIZE(os.perm_arena, 1280*720*sizeof(U32)), 1280, 720};
|
||||||
r.depth320 = (F32 *)PUSH_SIZE(os.perm_arena, sizeof(F32) * screen_x * screen_y);
|
r.depth320 = (F32 *)PUSH_SIZE(os.perm_arena, sizeof(F32) * screen_x * screen_y);
|
||||||
r.img = load_image("assets/cat.png");
|
r.img = load_image("assets/bricksx64.png");
|
||||||
|
|
||||||
/* @Note: Transparent texture */ {
|
/* @Note: Transparent texture */ {
|
||||||
#if 0
|
#if 0
|
||||||
@@ -712,7 +702,7 @@ int main() {
|
|||||||
r.transform = mat4_rotation_z(rotation);
|
r.transform = mat4_rotation_z(rotation);
|
||||||
r.transform = r.transform * mat4_rotation_y(rotation);
|
r.transform = r.transform * mat4_rotation_y(rotation);
|
||||||
for (int i = 0; i < obj.mesh.len; i++) {
|
for (int i = 0; i < obj.mesh.len; i++) {
|
||||||
r_draw_mesh(&r, mesh+i, vertices, tex_coords, normals);
|
r_draw_mesh(&r, obj.materials.e, mesh+i, vertices, tex_coords, normals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
142
obj_parser.cpp
142
obj_parser.cpp
@@ -38,13 +38,53 @@ struct ObjMesh {
|
|||||||
DynamicArray<ObjIndex> indices;
|
DynamicArray<ObjIndex> indices;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct OBJMaterial {
|
||||||
|
char name[64];
|
||||||
|
U32 name_len;
|
||||||
|
Bitmap texture_ambient; // map_Ka
|
||||||
|
Bitmap texture_diffuse; // map_Kd
|
||||||
|
Bitmap texture_dissolve; // map_d
|
||||||
|
Bitmap texture_displacment; // map_Disp
|
||||||
|
F32 non_transparency; // d
|
||||||
|
F32 transparency; // Tr
|
||||||
|
F32 optical_density; // Ni
|
||||||
|
F32 shininess; // Ns
|
||||||
|
I32 illumination_model; // illum
|
||||||
|
Vec3 ambient_color; // Ka
|
||||||
|
Vec3 diffuse_color; // Kd
|
||||||
|
Vec3 specular_color; // Ks
|
||||||
|
};
|
||||||
|
|
||||||
struct Obj {
|
struct Obj {
|
||||||
DynamicArray<Vec3> vertices;
|
DynamicArray<Vec3> vertices;
|
||||||
DynamicArray<Vec2> texture_coordinates;
|
DynamicArray<Vec2> texture_coordinates;
|
||||||
DynamicArray<Vec3> normals;
|
DynamicArray<Vec3> normals;
|
||||||
DynamicArray<ObjMesh> mesh;
|
DynamicArray<ObjMesh> mesh;
|
||||||
|
DynamicArray<OBJMaterial> materials;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FUNCTION
|
||||||
|
Bitmap load_image(const char* path) {
|
||||||
|
int x, y, n;
|
||||||
|
unsigned char* data = stbi_load(path, &x, &y, &n, 4);
|
||||||
|
Bitmap result = { (U32*)data, x, y };
|
||||||
|
#if PREMULTIPLIED_ALPHA_BLENDING
|
||||||
|
if(data) {
|
||||||
|
U32 *p = result.pixels;
|
||||||
|
for (int Y = 0; Y < y; Y++) {
|
||||||
|
for (int X = 0; X < x; X++) {
|
||||||
|
Vec4 color = vec4abgr(*p);
|
||||||
|
color.r *= color.a;
|
||||||
|
color.g *= color.a;
|
||||||
|
color.b *= color.a;
|
||||||
|
*p++ = vec4_to_u32abgr(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
namespace obj {
|
namespace obj {
|
||||||
enum class TokenType {
|
enum class TokenType {
|
||||||
none, word, number, whitespace, end
|
none, word, number, whitespace, end
|
||||||
@@ -62,7 +102,7 @@ namespace obj {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
FN Token next_token_raw(char** data) {
|
FUNCTION Token next_token_raw(char** data) {
|
||||||
Token result = {};
|
Token result = {};
|
||||||
result.s = *data;
|
result.s = *data;
|
||||||
*data += 1;
|
*data += 1;
|
||||||
@@ -101,7 +141,7 @@ namespace obj {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
FN Token next_token(char** data) {
|
FUNCTION Token next_token(char** data) {
|
||||||
Token result;
|
Token result;
|
||||||
do {
|
do {
|
||||||
result = next_token_raw(data);
|
result = next_token_raw(data);
|
||||||
@@ -109,25 +149,91 @@ namespace obj {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
FN double expect_number(char** data) {
|
FUNCTION double expect_number(char** data) {
|
||||||
Token t = next_token(data);
|
Token t = next_token(data);
|
||||||
TASSERT(t.type == TokenType::number); // @Todo: Error handling, error flag
|
TASSERT(t.type == TokenType::number); // @Todo: Error handling, error flag
|
||||||
return t.number;
|
return t.number;
|
||||||
}
|
}
|
||||||
|
|
||||||
FN void expect_token(char** data, char token) {
|
FUNCTION void expect_token(char** data, char token) {
|
||||||
Token t = next_token(data);
|
Token t = next_token(data);
|
||||||
TASSERT(t.type == (TokenType)token); // @Todo: Error handling, error flag
|
TASSERT(t.type == (TokenType)token); // @Todo: Error handling, error flag
|
||||||
}
|
}
|
||||||
|
|
||||||
FN void debug_expect_raw(char** data, TokenType type) {
|
FUNCTION void debug_expect_raw(char** data, TokenType type) {
|
||||||
char* data_temp = *data;
|
char* data_temp = *data;
|
||||||
Token t = next_token_raw(&data_temp);
|
Token t = next_token_raw(&data_temp);
|
||||||
TASSERT(t.type == type);
|
TASSERT(t.type == type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each face needs to have own material_id, group_id, smoothing ..
|
FUNCTION void parse_mtl(Arena *arena, Obj* obj, S8 path_obj_folder, S8 mtl_file) {
|
||||||
Obj parse(char* data) {
|
Scratch scratch;
|
||||||
|
char *data = (char *)mtl_file.str;
|
||||||
|
OBJMaterial *m = 0;
|
||||||
|
for (;;) {
|
||||||
|
Token token = next_token(&data);
|
||||||
|
if (token.type == TokenType::end) break;
|
||||||
|
else if (token.type == TokenType::word) {
|
||||||
|
if (string_compare(token.s8, LIT("newmtl"))) {
|
||||||
|
token = next_token(&data);
|
||||||
|
m = obj->materials.push_empty();
|
||||||
|
ZERO_STRUCT(m);
|
||||||
|
m->name_len = CLAMP_TOP(token.len, 64);
|
||||||
|
memory_copy(token.s8.str, m->name, m->name_len);
|
||||||
|
}
|
||||||
|
else if (string_compare(token.s8, LIT("Ns"))) {
|
||||||
|
m->shininess = expect_number(&data);
|
||||||
|
}
|
||||||
|
else if (string_compare(token.s8, LIT("Ka"))) {
|
||||||
|
m->ambient_color.x = expect_number(&data);
|
||||||
|
m->ambient_color.y = expect_number(&data);
|
||||||
|
m->ambient_color.z = expect_number(&data);
|
||||||
|
}
|
||||||
|
else if (string_compare(token.s8, LIT("Kd"))) {
|
||||||
|
m->diffuse_color.x = expect_number(&data);
|
||||||
|
m->diffuse_color.y = expect_number(&data);
|
||||||
|
m->diffuse_color.z = expect_number(&data);
|
||||||
|
}
|
||||||
|
else if (string_compare(token.s8, LIT("Ks"))) {
|
||||||
|
m->specular_color.x = expect_number(&data);
|
||||||
|
m->specular_color.y = expect_number(&data);
|
||||||
|
m->specular_color.z = expect_number(&data);
|
||||||
|
}
|
||||||
|
else if (string_compare(token.s8, LIT("Ni"))) {
|
||||||
|
m->optical_density = expect_number(&data);
|
||||||
|
}
|
||||||
|
else if (string_compare(token.s8, LIT("d"))) {
|
||||||
|
m->non_transparency = expect_number(&data);
|
||||||
|
}
|
||||||
|
else if (string_compare(token.s8, LIT("illum"))) {
|
||||||
|
m->illumination_model = (I32)expect_number(&data);
|
||||||
|
}
|
||||||
|
else if (string_compare(token.s8, LIT("map_Kd"))) {
|
||||||
|
Token t = next_token(&data);
|
||||||
|
S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8);
|
||||||
|
m->texture_diffuse = load_image((const char *)path.str);
|
||||||
|
}
|
||||||
|
else if (string_compare(token.s8, LIT("map_Ka"))) {
|
||||||
|
Token t = next_token(&data);
|
||||||
|
S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8);
|
||||||
|
m->texture_ambient = load_image((const char *)path.str);
|
||||||
|
}
|
||||||
|
else if (string_compare(token.s8, LIT("map_d"))) {
|
||||||
|
Token t = next_token(&data);
|
||||||
|
S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8);
|
||||||
|
m->texture_dissolve = load_image((const char *)path.str);
|
||||||
|
}
|
||||||
|
else if (string_compare(token.s8, LIT("map_Disp"))) {
|
||||||
|
Token t = next_token(&data);
|
||||||
|
S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8);
|
||||||
|
m->texture_displacment = load_image((const char *)path.str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNCTION Obj parse(Arena *arena, char* data, S8 path_obj_folder) {
|
||||||
|
Scratch mtl_scratch;
|
||||||
Obj result = {};
|
Obj result = {};
|
||||||
int smoothing = 0;
|
int smoothing = 0;
|
||||||
ObjMesh *mesh = result.mesh.push_empty();
|
ObjMesh *mesh = result.mesh.push_empty();
|
||||||
@@ -158,13 +264,23 @@ namespace obj {
|
|||||||
norm->z = (float)expect_number(&data);
|
norm->z = (float)expect_number(&data);
|
||||||
debug_expect_raw(&data, TokenType::whitespace);
|
debug_expect_raw(&data, TokenType::whitespace);
|
||||||
}
|
}
|
||||||
else if (string_compare(token.s8, LIT("mtlib"))) {
|
else if (string_compare(token.s8, LIT("mtllib"))) {
|
||||||
Token t = next_token(&data);
|
Token t = next_token(&data);
|
||||||
TASSERT(t.type == TokenType::word);
|
S8 path = string_format(mtl_scratch, "%s/%s", path_obj_folder, t.s8);
|
||||||
|
S8 mtl_file = os_read_file(mtl_scratch, path).error_is_fatal();
|
||||||
|
PUSH_SIZE(mtl_scratch, 1);
|
||||||
|
parse_mtl(arena, &result, path_obj_folder, mtl_file);
|
||||||
}
|
}
|
||||||
else if (string_compare(token.s8, LIT("usemtl"))) {
|
else if (string_compare(token.s8, LIT("usemtl"))) {
|
||||||
Token t = next_token(&data);
|
Token t = next_token(&data);
|
||||||
TASSERT(t.type == TokenType::word);
|
TASSERT(t.type == TokenType::word);
|
||||||
|
for(U64 i = 0; i < result.materials.len; i++) {
|
||||||
|
OBJMaterial *m = result.materials.e + i;
|
||||||
|
if(string_compare(string_make((U8 *)m->name, m->name_len), t.s8)) {
|
||||||
|
material_id = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (string_compare(token.s8, LIT("o"))) {
|
else if (string_compare(token.s8, LIT("o"))) {
|
||||||
Token t = next_token(&data);
|
Token t = next_token(&data);
|
||||||
@@ -227,7 +343,7 @@ namespace obj {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
FN void test_lex() {
|
FUNCTION void test_lex() {
|
||||||
const char* d = "v 0.885739 0.001910 -0.380334";
|
const char* d = "v 0.885739 0.001910 -0.380334";
|
||||||
char* dd = (char *)d;
|
char* dd = (char *)d;
|
||||||
TASSERT(next_token(&dd).type == TokenType::word);
|
TASSERT(next_token(&dd).type == TokenType::word);
|
||||||
@@ -250,11 +366,11 @@ namespace obj {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FUNCTION
|
FUNCTION Obj load_obj(Arena *arena, S8 file) {
|
||||||
Obj load_obj(S8 file) {
|
|
||||||
Scratch scratch;
|
Scratch scratch;
|
||||||
S8 data = os_read_file(scratch, file).error_is_fatal();
|
S8 data = os_read_file(scratch, file).error_is_fatal();
|
||||||
PUSH_SIZE(scratch, 1);
|
PUSH_SIZE(scratch, 1);
|
||||||
Obj result = obj::parse((char *)data.str);
|
S8 path = string_chop_last_slash(file);
|
||||||
|
Obj result = obj::parse(arena, (char *)data.str, path);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user