Optimized uv wrapping, no fmod, interpolating normals, Smooth shading, Multiple scenes, Options in UI

This commit is contained in:
Krzosa Karol
2022-03-03 09:23:00 +01:00
parent 29123d7ea6
commit af822c4dbe
5 changed files with 201 additions and 121 deletions

View File

@@ -19,30 +19,33 @@
- [x] Alpha blending - [x] Alpha blending
- [x] Premultiplied alpha - [x] Premultiplied alpha
- [x] Merge with base - [x] Merge with base
- [ ] Lightning - [ ] Fill convention
- [x] GLOBAL Ilumination - [ ] Antialiasing (seems like performance gets really bad with this)
- [ ] Phong
- [ ] Use all materials from OBJ
- [ ] Point light
- [x] LookAt Camera - [x] LookAt Camera
- [x] FPS Camera - [x] FPS Camera
- [ ] Quarternions for rotations
- [x] Reading OBJ models - [x] Reading OBJ models
- [ ] Reading more OBJ formats - [ ] Reading more OBJ formats
- [x] Reading OBJ .mtl files - [x] Reading OBJ .mtl files
- [x] Loading materials - [x] Loading materials
- [x] Rendering textures obj models - [x] Rendering textures obj models
- [x] Reading complex obj models (sponza) - [x] Reading complex obj models (sponza)
- [ ] Fix sponza uv coordinates - [x] Fix sponza uv coordinates - the issue was uv > 1 and uv < 0
- [ ] Reading PMX files
- [ ] Rendering multiple objects, queue renderer
- [x] Simple function to render a mesh
- [x] Clipping - [x] Clipping
- [x] Triagnle rectangle bound clipping - [x] Triagnle rectangle bound clipping
- [x] A way of culling Z out triangles - [x] A way of culling Z out triangles
- [x] Simple test z clipping - [x] Simple test z clipping
- [x] Maybe should clip a triangle on znear zfar plane? - [x] Maybe should clip a triangle on znear zfar plane?
- [x] Maybe should clip out triangles that are fully z out before draw_triangle - [x] Maybe should clip out triangles that are fully z out before draw_triangle
- [ ] Subpixel precision of triangle edges - [ ] Effects!!!
- [ ] Lightning
- [x] GLOBAL Ilumination
- [ ] Phong
- [ ] Use all materials from OBJ
- [ ] Point light
- [ ] Reading PMX files
- [ ] Rendering multiple objects, queue renderer
- [x] Simple function to render a mesh
- [x] Simple profiling tooling - [x] Simple profiling tooling
- [x] Statistics based on profiler data - [x] Statistics based on profiler data
- [x] Find cool profilers - ExtraSleepy, Vtune - [x] Find cool profilers - ExtraSleepy, Vtune
@@ -56,7 +59,11 @@
- [ ] Multithreading - [ ] Multithreading
- [x] Text rendering - [x] Text rendering
- [ ] Basic UI - [ ] UI
- [x] Labels
- [x] Settings variables
- [ ] Sliders
- [ ] Groups
- [x] Gamma correct alpha blending for rectangles and bitmaps - [x] Gamma correct alpha blending for rectangles and bitmaps
- [ ] Plotting of profile data - [ ] Plotting of profile data
- [x] Simple scatter plot - [x] Simple scatter plot
@@ -84,7 +91,7 @@ the box is clipped to the image metrics - 0, 0, width, height.
### Resources that helped me build the rasterizer (Might be helpful to you too): ### Resources that helped me build the rasterizer (Might be helpful to you too):
* Algorithm I used for triangle rasterization by Juan Pineda: https://www.cs.drexel.edu/~david/Classes/Papers/comp175-06-pineda.pdf * Algorithm I used for triangle rasterization by Juan Pineda is described in paper called "A Parallel Algorithm for Polygon Rasterization"
* Casey Muratori's series on making a game from scratch(including a 2D software rasterizer(episode ~82) and 3d gpu renderer): https://hero.handmade.network/episode/code# * Casey Muratori's series on making a game from scratch(including a 2D software rasterizer(episode ~82) and 3d gpu renderer): https://hero.handmade.network/episode/code#
* Fabian Giessen's "Optimizing Software Occlusion Culling": https://fgiesen.wordpress.com/2013/02/17/optimizing-sw-occlusion-culling-index/ * Fabian Giessen's "Optimizing Software Occlusion Culling": https://fgiesen.wordpress.com/2013/02/17/optimizing-sw-occlusion-culling-index/
* Fabian Giessen's optimized software renderer: https://github.com/rygorous/intel_occlusion_cull/tree/blog/SoftwareOcclusionCulling * Fabian Giessen's optimized software renderer: https://github.com/rygorous/intel_occlusion_cull/tree/blog/SoftwareOcclusionCulling
@@ -95,9 +102,6 @@ the box is clipped to the image metrics - 0, 0, width, height.
* A bunch of helpful notes and links to resources: https://nlguillemot.wordpress.com/2016/07/10/rasterizer-notes/ * A bunch of helpful notes and links to resources: https://nlguillemot.wordpress.com/2016/07/10/rasterizer-notes/
* Very nice paid course on making a software rasterizer using a scanline method: https://pikuma.com/courses/learn-3d-computer-graphics-programming * Very nice paid course on making a software rasterizer using a scanline method: https://pikuma.com/courses/learn-3d-computer-graphics-programming
* Reference for obj loader: https://github.com/tinyobjloader/tinyobjloader/blob/master/tiny_obj_loader.h * Reference for obj loader: https://github.com/tinyobjloader/tinyobjloader/blob/master/tiny_obj_loader.h
*
*
*
### To read ### To read

217
main.cpp
View File

@@ -19,30 +19,33 @@
/// - [x] Alpha blending /// - [x] Alpha blending
/// - [x] Premultiplied alpha /// - [x] Premultiplied alpha
/// - [x] Merge with base /// - [x] Merge with base
/// - [ ] Lightning /// - [ ] Fill convention
/// - [x] GLOBAL Ilumination /// - [ ] Antialiasing (seems like performance gets really bad with this)
/// - [ ] Phong
/// - [ ] Use all materials from OBJ
/// - [ ] Point light
/// - [x] LookAt Camera /// - [x] LookAt Camera
/// - [x] FPS Camera /// - [x] FPS Camera
/// - [ ] Quarternions for rotations
/// - [x] Reading OBJ models /// - [x] Reading OBJ models
/// - [ ] Reading more OBJ formats /// - [ ] Reading more OBJ formats
/// - [x] Reading OBJ .mtl files /// - [x] Reading OBJ .mtl files
/// - [x] Loading materials /// - [x] Loading materials
/// - [x] Rendering textures obj models /// - [x] Rendering textures obj models
/// - [x] Reading complex obj models (sponza) /// - [x] Reading complex obj models (sponza)
/// - [ ] Fix sponza uv coordinates /// - [x] Fix sponza uv coordinates - the issue was uv > 1 and uv < 0
/// - [ ] Reading PMX files
/// - [ ] Rendering multiple objects, queue renderer
/// - [x] Simple function to render a mesh
/// - [x] Clipping /// - [x] Clipping
/// - [x] Triagnle rectangle bound clipping /// - [x] Triagnle rectangle bound clipping
/// - [x] A way of culling Z out triangles /// - [x] A way of culling Z out triangles
/// - [x] Simple test z clipping /// - [x] Simple test z clipping
/// - [x] Maybe should clip a triangle on znear zfar plane? /// - [x] Maybe should clip a triangle on znear zfar plane?
/// - [x] Maybe should clip out triangles that are fully z out before draw_triangle /// - [x] Maybe should clip out triangles that are fully z out before draw_triangle
/// - [ ] Subpixel precision of triangle edges /// - [ ] Effects!!!
/// - [ ] Lightning
/// - [x] GLOBAL Ilumination
/// - [ ] Phong
/// - [ ] Use all materials from OBJ
/// - [ ] Point light
/// - [ ] Reading PMX files
/// - [ ] Rendering multiple objects, queue renderer
/// - [x] Simple function to render a mesh
/// - [x] Simple profiling tooling /// - [x] Simple profiling tooling
/// - [x] Statistics based on profiler data /// - [x] Statistics based on profiler data
/// - [x] Find cool profilers - ExtraSleepy, Vtune /// - [x] Find cool profilers - ExtraSleepy, Vtune
@@ -56,7 +59,11 @@
/// - [ ] Multithreading /// - [ ] Multithreading
/// ///
/// - [x] Text rendering /// - [x] Text rendering
/// - [ ] Basic UI /// - [ ] UI
/// - [x] Labels
/// - [x] Settings variables
/// - [ ] Sliders
/// - [ ] Groups
/// - [x] Gamma correct alpha blending for rectangles and bitmaps /// - [x] Gamma correct alpha blending for rectangles and bitmaps
/// - [ ] Plotting of profile data /// - [ ] Plotting of profile data
/// - [x] Simple scatter plot /// - [x] Simple scatter plot
@@ -100,7 +107,14 @@ struct R_Render {
#include "obj_parser.cpp" #include "obj_parser.cpp"
#include <float.h> #include <float.h>
enum Scene {
Scene_F22,
Scene_Sponza,
Scene_Count,
};
GLOBAL B32 draw_rects = 0; GLOBAL B32 draw_rects = 0;
GLOBAL Scene scene = Scene_Sponza;
GLOBAL F32 zfar_value = 100000.f; GLOBAL F32 zfar_value = 100000.f;
GLOBAL F32 light_rotation = 0; GLOBAL F32 light_rotation = 0;
@@ -270,9 +284,10 @@ F32 edge_function(Vec4 vecp0, Vec4 vecp1, Vec4 p) {
} }
FUNCTION FUNCTION
void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 light, void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, Vec3 light_direction,
Vec4 p0, Vec4 p1, Vec4 p2, Vec4 p0, Vec4 p1, Vec4 p2,
Vec2 tex0, Vec2 tex1, Vec2 tex2) { Vec2 tex0, Vec2 tex1, Vec2 tex2,
Vec3 norm0, Vec3 norm1, Vec3 norm2) {
if(os.frame > 60) PROFILE_BEGIN(draw_triangle); if(os.frame > 60) PROFILE_BEGIN(draw_triangle);
F32 min_x1 = (F32)(MIN(p0.x, MIN(p1.x, p2.x))); F32 min_x1 = (F32)(MIN(p0.x, MIN(p1.x, p2.x)));
F32 min_y1 = (F32)(MIN(p0.y, MIN(p1.y, p2.y))); F32 min_y1 = (F32)(MIN(p0.y, MIN(p1.y, p2.y)));
@@ -306,37 +321,34 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 ligh
F32 Cx1 = Cy1; F32 Cx1 = Cy1;
F32 Cx2 = Cy2; F32 Cx2 = Cy2;
for (I64 x = min_x; x < max_x; x++) { for (I64 x = min_x; x < max_x; x++) {
F32 edge0 = Cx0;
F32 edge1 = Cx1;
F32 edge2 = Cx2;
if (Cx0 >= 0 && Cx1 >= 0 && Cx2 >= 0) { if (Cx0 >= 0 && Cx1 >= 0 && Cx2 >= 0) {
F32 w1 = Cx1 / area; F32 w1 = Cx1 / area;
F32 w2 = Cx2 / area; F32 w2 = Cx2 / area;
F32 w3 = Cx0 / area; F32 w3 = Cx0 / area;
F32 interpolated_w = (1.f / p0.w) * w1 + (1.f / p1.w) * w2 + (1.f / p2.w) * w3;
F32 u = tex0.x * (w1 / p0.w) + tex1.x * (w2 / p1.w) + tex2.x * (w3 / p2.w);
F32 v = tex0.y * (w1 / p0.w) + tex1.y * (w2 / p1.w) + tex2.y * (w3 / p2.w);
u /= interpolated_w;
v /= interpolated_w;
u = fmodf(u, 1.f);
v = fmodf(v, 1.f);
if(u < 0) {
u = 1 + u;
}
if(v < 0) {
v = 1 + v;
}
// u = CLAMP(0, u, 1);
// v = CLAMP(0, v, 1);
// @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 interpolated_w = (1.f / p0.w) * w1 + (1.f / p1.w) * w2 + (1.f / p2.w) * w3;
F32* depth = depth_buffer + (x + y * dst->x); F32* depth = depth_buffer + (x + y * dst->x);
if (*depth < interpolated_w) { if (*depth < interpolated_w) {
*depth = interpolated_w; *depth = interpolated_w;
u = u * (src->x - 2); F32 invw0 = (w1 / p0.w);
v = v * (src->y - 2); F32 invw1 = (w2 / p1.w);
F32 invw2 = (w3 / p2.w);
Vec3 norm = (norm0 * invw0 + norm1 * invw1 + norm2 * invw2) / interpolated_w;
F32 u = tex0.x * invw0 + tex1.x * invw1 + tex2.x * invw2;
F32 v = tex0.y * invw0 + tex1.y * invw1 + tex2.y * invw2;
{
u /= interpolated_w;
v /= interpolated_w;
u = u - floor(u);
v = v - floor(v);
// u = CLAMP(0, u, 1);
// v = CLAMP(0, v, 1);
u = u * (src->x - 1);
v = v * (src->y - 1);
}
I64 ui = (I64)(u); I64 ui = (I64)(u);
I64 vi = (I64)(v); I64 vi = (I64)(v);
F32 udiff = u - (F32)ui; F32 udiff = u - (F32)ui;
@@ -346,11 +358,37 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 ligh
U32 *pixel = src->pixels + (ui + (src->y - 1ll - vi) * src->x); U32 *pixel = src->pixels + (ui + (src->y - 1ll - vi) * src->x);
#if PREMULTIPLIED_ALPHA_BLENDING #if PREMULTIPLIED_ALPHA_BLENDING
Vec4 result_color = srgb_to_almost_linear(vec4abgr(*pixel)); Vec4 result_color; {
Vec4 dst_color = srgb_to_almost_linear(vec4abgr(*dst_pixel)); U32 c = *pixel;
result_color.r *= light; F32 a = ((c & 0xff000000) >> 24) / 255.f;
result_color.g *= light; F32 b = ((c & 0x00ff0000) >> 16) / 255.f;
result_color.b *= light; F32 g = ((c & 0x0000ff00) >> 8) / 255.f;
F32 r = ((c & 0x000000ff) >> 0) / 255.f;
r*=r;
g*=g;
b*=b;
result_color = { r,g,b,a };
}
Vec4 dst_color; {
U32 c = *dst_pixel;
F32 a = ((c & 0xff000000) >> 24) / 255.f;
F32 b = ((c & 0x00ff0000) >> 16) / 255.f;
F32 g = ((c & 0x0000ff00) >> 8) / 255.f;
F32 r = ((c & 0x000000ff) >> 0) / 255.f;
r*=r; g*=g; b*=b;
dst_color = { r,g,b,a };
}
F32 light = -dot(norm, light_direction);
{
light = CLAMP(0.1f, light, 1.f);
result_color.r *= light;
result_color.g *= light;
result_color.b *= light;
}
result_color = premultiplied_alpha(dst_color, result_color); result_color = premultiplied_alpha(dst_color, result_color);
result_color = almost_linear_to_srgb(result_color); result_color = almost_linear_to_srgb(result_color);
U32 color32 = vec4_to_u32abgr(result_color); U32 color32 = vec4_to_u32abgr(result_color);
@@ -379,7 +417,7 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 ligh
} }
FUNCTION FUNCTION
void draw_triangle_subpixel(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 light, void draw_triangle_bilinear(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 light,
Vec4 p0, Vec4 p1, Vec4 p2, Vec4 p0, Vec4 p1, Vec4 p2,
Vec2 tex0, Vec2 tex1, Vec2 tex2) { Vec2 tex0, Vec2 tex1, Vec2 tex2) {
F32 min_x1 = (F32)(MIN(p0.x, MIN(p1.x, p2.x))); F32 min_x1 = (F32)(MIN(p0.x, MIN(p1.x, p2.x)));
@@ -398,6 +436,7 @@ void draw_triangle_subpixel(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 lig
F32 edge1 = edge_function(p1, p2, { (F32)x,(F32)y }); F32 edge1 = edge_function(p1, p2, { (F32)x,(F32)y });
F32 edge2 = edge_function(p2, p0, { (F32)x,(F32)y }); F32 edge2 = edge_function(p2, p0, { (F32)x,(F32)y });
if (edge0 >= 0 && edge1 >= 0 && edge2 >= 0) { if (edge0 >= 0 && edge1 >= 0 && edge2 >= 0) {
F32 w1 = edge1 / area; F32 w1 = edge1 / area;
F32 w2 = edge2 / area; F32 w2 = edge2 / area;
@@ -476,34 +515,37 @@ void r_scatter_plot(Bitmap *dst, F64 *data, I64 data_len) {
r_draw_rect(dst, (F32)x-2, (F32)y-2, 4, 4, vec4(1,0,0,1)); r_draw_rect(dst, (F32)x-2, (F32)y-2, 4, 4, vec4(1,0,0,1));
//dst->pixels[xi + yi * dst->x] = 0xffff0000; //dst->pixels[xi + yi * dst->x] = 0xffff0000;
} }
} }
S8 scenario_name = string_null;
FUNCTION FUNCTION
void r_draw_mesh(R_Render *r, OBJMaterial *materials, ObjMesh *mesh, Vec3 *vertices, Vec2 *tex_coords, Vec3 *normals) { void r_draw_mesh(R_Render *r, S8 scene_name, 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 = &r->img;
Bitmap *image = &material->texture_ambient; if(index->material_id != -1) {
if(image->pixels == 0) { OBJMaterial *material = materials + index->material_id;
image = &r->img; // @Todo: No size info from OBJ things, this stuff needs a bit of refactor
// Need to figure out how to accomodate multiple possible formats of input etc.
if(material->texture_ambient.pixels) {
image = &material->texture_ambient;
}
} }
R_Vertex vert[] = { R_Vertex vert[] = {
{ {
vertices[index->vertex[0] - 1], vertices[index->vertex[0] - 1],
tex_coords[index->tex[0] - 1], tex_coords[index->tex[0] - 1],
normals[index->normal[0] - 1], normals[index->normal[0] - 1],
}, },
{ {
vertices[index->vertex[1] - 1], vertices[index->vertex[1] - 1],
tex_coords[index->tex[1] - 1], tex_coords[index->tex[1] - 1],
normals[index->normal[1] - 1], normals[index->normal[1] - 1],
}, },
{ {
vertices[index->vertex[2] - 1], vertices[index->vertex[2] - 1],
tex_coords[index->tex[2] - 1], tex_coords[index->tex[2] - 1],
normals[index->normal[2] - 1], normals[index->normal[2] - 1],
}, },
}; };
@@ -518,8 +560,7 @@ void r_draw_mesh(R_Render *r, OBJMaterial *materials, ObjMesh *mesh, Vec3 *verti
Vec3 p0_to_p2 = vert[2].pos - vert[0].pos; Vec3 p0_to_p2 = vert[2].pos - vert[0].pos;
Vec3 normal = normalize(cross(p0_to_p1, p0_to_p2)); Vec3 normal = normalize(cross(p0_to_p1, p0_to_p2));
Vec3 light_direction = mat4_rotation_x(light_rotation) * vec3(0, 0, 1); Vec3 light_direction = mat4_rotation_x(light_rotation) * vec3(0, 0, 1);
F32 light = -dot(normal, light_direction);
light = CLAMP(0.05f, light, 1.f);
if (dot(normal, p0_to_camera) > 0) { //@Note: Backface culling if (dot(normal, p0_to_camera) > 0) { //@Note: Backface culling
/// ## Clipping /// ## Clipping
/// ///
@@ -565,6 +606,7 @@ void r_draw_mesh(R_Render *r, OBJMaterial *materials, ObjMesh *mesh, Vec3 *verti
struct _R_Vertex { struct _R_Vertex {
Vec4 pos; Vec4 pos;
Vec2 tex; Vec2 tex;
Vec3 norm;
} in[4]; } in[4];
I32 in_count = 0; I32 in_count = 0;
@@ -577,11 +619,13 @@ void r_draw_mesh(R_Render *r, OBJMaterial *materials, ObjMesh *mesh, Vec3 *verti
if (curr_dot * prev_dot < 0) { if (curr_dot * prev_dot < 0) {
F32 t = prev_dot / (prev_dot - curr_dot); F32 t = prev_dot / (prev_dot - curr_dot);
in[in_count].pos = vec4(lerp(prev->pos, curr->pos, t), 1); in[in_count].pos = vec4(lerp(prev->pos, curr->pos, t), 1);
in[in_count++].tex = lerp(prev->tex, curr->tex, t); in[in_count].tex = lerp(prev->tex, curr->tex, t);
in[in_count++].norm = lerp(prev->norm, curr->norm, t);
} }
if (curr_dot > 0) { if (curr_dot > 0) {
in[in_count].pos = vec4(vert[j].pos, 1); in[in_count].pos = vec4(vert[j].pos, 1);
in[in_count++].tex = vert[j].tex; in[in_count].tex = vert[j].tex;
in[in_count++].norm = vert[j].norm;
} }
prev = curr++; prev = curr++;
prev_dot = curr_dot; prev_dot = curr_dot;
@@ -604,9 +648,9 @@ void r_draw_mesh(R_Render *r, OBJMaterial *materials, ObjMesh *mesh, Vec3 *verti
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, image, light_direction, in[0].pos, in[1].pos, in[2].pos, in[0].tex, in[1].tex, in[2].tex, in[0].norm, in[1].norm, in[2].norm);
if (in_count > 3) { if (in_count > 3) {
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); draw_triangle_nearest(&r->screen320, r->depth320, image, light_direction, in[0].pos, in[2].pos, in[3].pos, in[0].tex, in[2].tex, in[3].tex, in[0].norm, in[2].norm, in[3].norm);
} }
@@ -615,10 +659,7 @@ void r_draw_mesh(R_Render *r, OBJMaterial *materials, ObjMesh *mesh, Vec3 *verti
LOCAL_PERSIST B32 profile_flag; LOCAL_PERSIST B32 profile_flag;
if (!profile_flag && scope->i > 2000) { if (!profile_flag && scope->i > 2000) {
profile_flag = 1; profile_flag = 1;
save_profile_data(scope, scenario_name, LIT("draw_triangle")); save_profile_data(scope, scene_name, LIT("draw_triangle"));
r_scatter_plot(&r->plot, scope->samples, 2000);
r->plot_ready = true;
} }
#endif #endif
@@ -628,8 +669,8 @@ void r_draw_mesh(R_Render *r, OBJMaterial *materials, ObjMesh *mesh, Vec3 *verti
#include "ui.cpp" #include "ui.cpp"
int main() { int main() {
os.window_size.x = 1280; os.window_size.x = 320*2;
os.window_size.y = 720; os.window_size.y = 180*2;
os.window_resizable = 1; os.window_resizable = 1;
os_init().error_is_fatal(); os_init().error_is_fatal();
S8List list = {}; S8List list = {};
@@ -649,21 +690,16 @@ int main() {
} }
//scenario_name = LIT("assets/f22.obj");
//scenario_name = LIT("assets/cube.obj"); Obj f22 = load_obj(os.perm_arena, LIT("assets/f22.obj"));
//scenario_name = LIT("assets/AnyConv.com__White.obj"); Obj sponza = load_obj(os.perm_arena, LIT("assets/sponza/sponza.obj"));
scenario_name = LIT("assets/sponza/sponza.obj"); Obj *obj = 0;
Obj obj = load_obj(os.perm_arena, scenario_name);
Vec3* vertices = (Vec3 *)obj.vertices.e;
Vec2* tex_coords = (Vec2*)obj.texture_coordinates.e;
Vec3 *normals = (Vec3 *)obj.normals.e;
ObjMesh *mesh = obj.mesh.e;
F32 speed = 100.f; F32 speed = 100.f;
F32 rotation = 0; F32 rotation = 0;
int screen_x = 320*2; int screen_x = 1280/2;
int screen_y = 180*2; int screen_y = 720/2;
R_Render r = {}; R_Render r = {};
r.camera_pos = {0,0,-2}; r.camera_pos = {0,0,-2};
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};
@@ -676,7 +712,7 @@ int main() {
Vec4 testc = vec4(1, 1, 1, 0.5f); Vec4 testc = vec4(1, 1, 1, 0.5f);
testc.rgb *= testc.a; testc.rgb *= testc.a;
U32 testc32 = vec4_to_u32abgr(testc); U32 testc32 = vec4_to_u32abgr(testc);
U32 a[] = { U32 a[] = { d
testc32, testc32, testc32, testc32, testc32, testc32, testc32, testc32,
testc32, testc32, testc32, testc32, testc32, testc32, testc32, testc32,
testc32, testc32, testc32, testc32, testc32, testc32, testc32, testc32,
@@ -693,6 +729,7 @@ int main() {
S8 frame_data = {}; S8 frame_data = {};
UISetup setup[] = { UISetup setup[] = {
UI_BOOL(LIT("Draw rectangles:"), &draw_rects), UI_BOOL(LIT("Draw rectangles:"), &draw_rects),
UI_OPTION(LIT("Scene:"), &scene, Scene_Count),
UI_IMAGE(&r.plot), UI_IMAGE(&r.plot),
UI_LABEL(&frame_data), UI_LABEL(&frame_data),
}; };
@@ -700,6 +737,19 @@ int main() {
B32 ui_mouse_lock = true; B32 ui_mouse_lock = true;
while (os_game_loop()) { while (os_game_loop()) {
switch(scene) {
case Scene_F22: {
speed = 1;
obj = &f22;
} break;
case Scene_Sponza: {
speed = 100;
obj = &sponza;
} break;
case Scene_Count:
INVALID_DEFAULT_CASE;
}
if (ui_mouse_lock == false) { if (ui_mouse_lock == false) {
r.camera_yaw.x += os.delta_mouse_pos.x * 0.01f; r.camera_yaw.x += os.delta_mouse_pos.x * 0.01f;
r.camera_yaw.y -= os.delta_mouse_pos.y * 0.01f; r.camera_yaw.y -= os.delta_mouse_pos.y * 0.01f;
@@ -745,8 +795,12 @@ int main() {
r.projection = mat4_perspective(60.f, (F32)os.screen->x, (F32)os.screen->y, 1.f, zfar_value); r.projection = mat4_perspective(60.f, (F32)os.screen->x, (F32)os.screen->y, 1.f, zfar_value);
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, obj.materials.e, mesh+i, vertices, tex_coords, normals); Vec2* tex_coords = (Vec2*)obj->texture_coordinates.e;
Vec3 *normals = (Vec3 *)obj->normals.e;
ObjMesh *mesh = obj->mesh.e;
Vec3* vertices = (Vec3 *)obj->vertices.e;
r_draw_mesh(&r, obj->name, obj->materials.e, mesh+i, vertices, tex_coords, normals);
} }
@@ -769,7 +823,7 @@ int main() {
///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////
/// ### Resources that helped me build the rasterizer (Might be helpful to you too): /// ### Resources that helped me build the rasterizer (Might be helpful to you too):
/// ///
/// * Algorithm I used for triangle rasterization by Juan Pineda: https://www.cs.drexel.edu/~david/Classes/Papers/comp175-06-pineda.pdf /// * Algorithm I used for triangle rasterization by Juan Pineda is described in paper called "A Parallel Algorithm for Polygon Rasterization"
/// * Casey Muratori's series on making a game from scratch(including a 2D software rasterizer(episode ~82) and 3d gpu renderer): https://hero.handmade.network/episode/code# /// * Casey Muratori's series on making a game from scratch(including a 2D software rasterizer(episode ~82) and 3d gpu renderer): https://hero.handmade.network/episode/code#
/// * Fabian Giessen's "Optimizing Software Occlusion Culling": https://fgiesen.wordpress.com/2013/02/17/optimizing-sw-occlusion-culling-index/ /// * Fabian Giessen's "Optimizing Software Occlusion Culling": https://fgiesen.wordpress.com/2013/02/17/optimizing-sw-occlusion-culling-index/
/// * Fabian Giessen's optimized software renderer: https://github.com/rygorous/intel_occlusion_cull/tree/blog/SoftwareOcclusionCulling /// * Fabian Giessen's optimized software renderer: https://github.com/rygorous/intel_occlusion_cull/tree/blog/SoftwareOcclusionCulling
@@ -780,9 +834,6 @@ int main() {
/// * A bunch of helpful notes and links to resources: https://nlguillemot.wordpress.com/2016/07/10/rasterizer-notes/ /// * A bunch of helpful notes and links to resources: https://nlguillemot.wordpress.com/2016/07/10/rasterizer-notes/
/// * Very nice paid course on making a software rasterizer using a scanline method: https://pikuma.com/courses/learn-3d-computer-graphics-programming /// * Very nice paid course on making a software rasterizer using a scanline method: https://pikuma.com/courses/learn-3d-computer-graphics-programming
/// * Reference for obj loader: https://github.com/tinyobjloader/tinyobjloader/blob/master/tiny_obj_loader.h /// * Reference for obj loader: https://github.com/tinyobjloader/tinyobjloader/blob/master/tiny_obj_loader.h
/// *
/// *
/// *
/// ///
/// ### To read /// ### To read
/// ///

View File

@@ -56,6 +56,7 @@ struct OBJMaterial {
}; };
struct Obj { struct Obj {
S8 name;
DynamicArray<Vec3> vertices; DynamicArray<Vec3> vertices;
DynamicArray<Vec2> texture_coordinates; DynamicArray<Vec2> texture_coordinates;
DynamicArray<Vec3> normals; DynamicArray<Vec3> normals;
@@ -255,20 +256,6 @@ namespace obj {
Vec2 *tex = result.texture_coordinates.push_empty(); Vec2 *tex = result.texture_coordinates.push_empty();
tex->x = (float)expect_number(&data); tex->x = (float)expect_number(&data);
tex->y = (float)expect_number(&data); tex->y = (float)expect_number(&data);
// tex->y = fmodf(tex->y, 1.f);
// if(tex->x < 0) {
// tex->x = 1 - fmodf(ABS(tex->x), 1.f);
// }
// else {
// tex->x = fmodf(tex->x, 1.f);
// }
// if(tex->y < 0) {
// tex->y = 1 - fmodf(ABS(tex->y), 1.f);
// }
// else {
// tex->y = fmodf(tex->y, 1.f);
// }
debug_expect_raw(&data, TokenType::whitespace); debug_expect_raw(&data, TokenType::whitespace);
} }
else if (string_compare(token.s8, LIT("vn"))) { else if (string_compare(token.s8, LIT("vn"))) {
@@ -281,9 +268,11 @@ namespace obj {
else if (string_compare(token.s8, LIT("mtllib"))) { else if (string_compare(token.s8, LIT("mtllib"))) {
Token t = next_token(&data); Token t = next_token(&data);
S8 path = string_format(mtl_scratch, "%s/%s", path_obj_folder, t.s8); 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(); Result<S8> mtl_file = os_read_file(mtl_scratch, path);
PUSH_SIZE(mtl_scratch, 1); if(mtl_file.no_error()) {
parse_mtl(arena, &result, path_obj_folder, mtl_file); PUSH_SIZE(mtl_scratch, 1);
parse_mtl(arena, &result, path_obj_folder, mtl_file.result);
}
} }
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);
@@ -386,5 +375,6 @@ FUNCTION Obj load_obj(Arena *arena, S8 file) {
PUSH_SIZE(scratch, 1); PUSH_SIZE(scratch, 1);
S8 path = string_chop_last_slash(file); S8 path = string_chop_last_slash(file);
Obj result = obj::parse(arena, (char *)data.str, path); Obj result = obj::parse(arena, (char *)data.str, path);
result.name = file;
return result; return result;
} }

View File

@@ -4,7 +4,7 @@ enum ProfileScopeName {
}; };
struct ProfileScope { struct ProfileScope {
F64 samples[5096]; U64 samples[5096];
I64 i; I64 i;
}; };
@@ -12,12 +12,12 @@ GLOBAL ProfileScope profile_scopes[ProfileScopeName_Count];
#define PROFILE_BEGIN(name) do { \ #define PROFILE_BEGIN(name) do { \
ProfileScope *__profile_scope = profile_scopes + ProfileScopeName_##name; \ ProfileScope *__profile_scope = profile_scopes + ProfileScopeName_##name; \
__profile_scope->samples[__profile_scope->i] = os_time()*1000; \ __profile_scope->samples[__profile_scope->i] = __rdtsc(); \
} while (0) } while (0)
#define PROFILE_END(name) do { \ #define PROFILE_END(name) do { \
ProfileScope *_profile_scope = profile_scopes + ProfileScopeName_##name; \ ProfileScope *_profile_scope = profile_scopes + ProfileScopeName_##name; \
_profile_scope->samples[_profile_scope->i] = os_time()*1000 - _profile_scope->samples[_profile_scope->i]; \ _profile_scope->samples[_profile_scope->i] = __rdtsc() - _profile_scope->samples[_profile_scope->i]; \
_profile_scope->i = (_profile_scope->i + 1) % 5096; \ _profile_scope->i = (_profile_scope->i + 1) % 5096; \
}while (0) }while (0)
@@ -41,7 +41,7 @@ FN void save_profile_data(ProfileScope *scope, S8 scenario_name, S8 scope_name)
string_format(scratch, "%s %s\n", build_name, scenario_name); string_format(scratch, "%s %s\n", build_name, scenario_name);
I64 one_past_last = scope->i; I64 one_past_last = scope->i;
for (I64 si = 0; si < one_past_last; si++) { for (I64 si = 0; si < one_past_last; si++) {
string_format(scratch, "%f\n", scope->samples[si]); string_format(scratch, "%u\n", scope->samples[si]);
} }
S8 data = string_end(scratch, string_pointer); S8 data = string_end(scratch, string_pointer);

39
ui.cpp
View File

@@ -3,6 +3,7 @@ enum UIWidgetKind {
UIWidgetKind_Boolean, UIWidgetKind_Boolean,
UIWidgetKind_Image, UIWidgetKind_Image,
UIWidgetKind_Label, UIWidgetKind_Label,
UIWidgetKind_Option,
UIWidgetKind_Group, UIWidgetKind_Group,
}; };
@@ -14,11 +15,14 @@ struct UISetup {
Bitmap *image; Bitmap *image;
B32 *b32; B32 *b32;
S8 *label; S8 *label;
I32 *option;
}; };
I32 option_max;
}; };
#define UI_BOOL(text, x) {UIWidgetKind_Boolean,text,(void*)(x)} #define UI_BOOL(text, x) {UIWidgetKind_Boolean,text,(void*)(x)}
#define UI_IMAGE(x) {UIWidgetKind_Image,string_null,(void*)(x)} #define UI_IMAGE(x) {UIWidgetKind_Image,string_null,(void*)(x)}
#define UI_LABEL(x) {UIWidgetKind_Label,string_null,(void*)(x)} #define UI_LABEL(x) {UIWidgetKind_Label,string_null,(void*)(x)}
#define UI_OPTION(text,x,option_max){UIWidgetKind_Option,text,(void*)(x),(option_max)}
struct UIWidget { struct UIWidget {
UIWidgetKind kind; UIWidgetKind kind;
@@ -29,10 +33,12 @@ struct UIWidget {
S8 text; S8 text;
Vec2 size; Vec2 size;
I32 option_max;
union { union {
Bitmap *image; Bitmap *image;
B32 *b32; B32 *b32;
S8 *label; S8 *label;
I32 *option;
} ptr; } ptr;
}; };
@@ -69,8 +75,9 @@ FUNCTION UIWidget *ui_push_image(Arena *arena, UIWidget *widget, Bitmap *img) {
return result; return result;
} }
FUNCTION UIWidget *ui_push_bool(Arena *arena, UIWidget *widget, B32 *b32) { FUNCTION UIWidget *ui_push_bool(Arena *arena, UIWidget *widget, S8 string, B32 *b32) {
UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Boolean); UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Boolean);
result->text = string;
result->ptr.b32 = b32; result->ptr.b32 = b32;
return result; return result;
} }
@@ -81,6 +88,14 @@ FUNCTION UIWidget *ui_push_string(Arena *arena, UIWidget *widget, S8 *string) {
return result; return result;
} }
FUNCTION UIWidget *ui_push_option(Arena *arena, UIWidget *widget, S8 string, I32 *option, I32 option_max) {
UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Option);
result->text = string;
result->ptr.option = option;
result->option_max = option_max;
return result;
}
FUNCTION UI ui_make(Arena *arena, UISetup *setup, U64 len) { FUNCTION UI ui_make(Arena *arena, UISetup *setup, U64 len) {
UI result = {}; UI result = {};
result.arena = arena_sub(arena, MiB(16)); result.arena = arena_sub(arena, MiB(16));
@@ -91,11 +106,14 @@ FUNCTION UI ui_make(Arena *arena, UISetup *setup, U64 len) {
ui_push_image(&result.arena, parent, s->image); ui_push_image(&result.arena, parent, s->image);
} break; } break;
case UIWidgetKind_Boolean: { case UIWidgetKind_Boolean: {
ui_push_bool(&result.arena, parent, s->b32)->text = s->text; ui_push_bool(&result.arena, parent, s->text, s->b32);
} break; } break;
case UIWidgetKind_Label: { case UIWidgetKind_Label: {
ui_push_string(&result.arena, parent, s->label); ui_push_string(&result.arena, parent, s->label);
} break; } break;
case UIWidgetKind_Option: {
ui_push_option(&result.arena, parent, s->text, s->option, s->option_max);
} break;
INVALID_DEFAULT_CASE; INVALID_DEFAULT_CASE;
} }
} }
@@ -168,6 +186,23 @@ FUNCTION void ui_end_frame(Bitmap *dst, UI *ui, Font *font) {
rect = r_draw_string(dst, font, *w->ptr.label, pos); rect = r_draw_string(dst, font, *w->ptr.label, pos);
pos.y -= rect.height - font->height; pos.y -= rect.height - font->height;
} break; } break;
case UIWidgetKind_Option: {
pos.y -= font->height;
Vec4 color = vec4(0, 0, 0, 1);
S8 string = string_format(scratch, "%s %d", w->text, *w->ptr.option);
rect = r_get_string_rect(font, string, pos);
B32 clicked = ui_mouse_test(ui, w, rect);
if (clicked) {
*w->ptr.b32 = (*w->ptr.b32+1) % w->option_max;
}
if (ui->hot == w) {
color = vec4(0.4f, 0.4f, 0.4f, 1.f);
}
rect.y = rect.y-font->line_advance / 5;
r_draw_rect(dst, rect.x, rect.y, rect.width, rect.height, color);
rect = r_draw_string(dst, font, string, pos);
pos.y -= rect.height - font->height;
} break;
INVALID_DEFAULT_CASE; INVALID_DEFAULT_CASE;
} }
} }