Optimized uv wrapping, no fmod, interpolating normals, Smooth shading, Multiple scenes, Options in UI
This commit is contained in:
34
README.md
34
README.md
@@ -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
217
main.cpp
@@ -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
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
39
ui.cpp
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user