diff --git a/graphics3.rdbg b/graphics3.rdbg new file mode 100644 index 0000000..b78f0b8 Binary files /dev/null and b/graphics3.rdbg differ diff --git a/main.cpp b/main.cpp index 6b25789..3fb9f72 100644 --- a/main.cpp +++ b/main.cpp @@ -5,22 +5,30 @@ OK Y up coordinate system, left handed OK Drawing a cube with perspective OK Culling triangles facing away from camera OK Texture mapping -? Basic math operations on Vec4 Mat4 - Muls, Dot, Cross etc. +? Basic math operations on Vec4 Mat4 - Muls, dot, cross etc. OK Basic linear transformations - rotation, translation, scaling OK Bilinear filtering of textures / subpixel precison OK Fix the gaps between triangles (it also improved look of triangle edges) * Perspective matrix vs simple perspective -* Perspective correct interpolation -* Depth buffer +OK Perspective correct interpolation +OK Depth buffer +KINDA_OK Gamma correct blending +* Alpha blending?? +* Premultiplied alpha??? +* Lightning +* LookAt Camera * FPS Camera -* Reading OBJ files -* Loading a model from PMX? +OK Reading OBJ files +* Reading PMX files * Rendering multiple objects, queue renderer * Clipping * Optimizations * SIMD * Multithreading * +* Text rendering +* Basic UI +* Gamma correct and alpha blending */ /* What a codebase needs: @@ -34,6 +42,7 @@ OK Fix the gaps between triangles (it also improved look of triangle edges) #define BILINEAR_BLEND 1 #define PERSPECTIVE_CORRECT_INTERPOLATION 1 +#define _CRT_SECURE_NO_WARNINGS #include "main.h" #include "platform.h" #include "math.h" @@ -41,8 +50,10 @@ OK Fix the gaps between triangles (it also improved look of triangle edges) #include "objparser.h" #include +GLOBAL OS os = {}; GLOBAL bool draw_rects = 0; GLOBAL bool draw_wireframe = 0; +#include "rasterization_feature_selection.cpp" struct Face { int p[3]; @@ -76,7 +87,7 @@ GLOBAL Face cube_faces[] = { }; FUNCTION -void DrawRect(Image* dst, float X, float Y, float w, float h, U32 color) { +void draw_rect(Image* dst, float X, float Y, float w, float h, U32 color) { int max_x = (int)(MIN(X + w, dst->x) + 0.5f); int max_y = (int)(MIN(Y + h, dst->y) + 0.5f); int min_x = (int)(MAX(0, X) + 0.5f); @@ -90,7 +101,7 @@ void DrawRect(Image* dst, float X, float Y, float w, float h, U32 color) { } FUNCTION -void DrawBitmap(Image* dst, Image* src, Vec2 pos) { +void draw_bitmap(Image* dst, Image* src, Vec2 pos) { I64 minx = (I64)(pos.x + 0.5); I64 miny = (I64)(pos.y + 0.5); I64 maxx = minx + src->x; @@ -124,48 +135,61 @@ void DrawBitmap(Image* dst, Image* src, Vec2 pos) { } FUNCTION -float EdgeFunction(Vec4 vecp0, Vec4 vecp1, Vec4 p) { +float edge_function(Vec4 vecp0, Vec4 vecp1, Vec4 p) { float result = (vecp1.y - vecp0.y) * (p.x - vecp0.x) - (vecp1.x - vecp0.x) * (p.y - vecp0.y); return result; } +FUNCTION +Vec4 srgb_to_almost_linear(Vec4 a) { + Vec4 result = {a.r*a.r, a.g*a.g, a.b*a.b, a.a}; + return result; // @Note: Linear would be to power of 2.2 +} + +FUNCTION +Vec4 almost_linear_to_srgb(Vec4 a) { + Vec4 result = { sqrt(a.r), sqrt(a.g), sqrt(a.b), a.a }; + return result; +} + FUNCTION -void DrawTriangle(Image* dst, float *depth_buffer, Image *src, Vec4 p0, Vec4 p1, Vec4 p2, +void draw_triangle(Image* dst, float *depth_buffer, Image *src, + Vec4 p0, Vec4 p1, Vec4 p2, Vec2 tex0, Vec2 tex1, Vec2 tex2) { float min_x1 = (float)(MIN(p0.x, MIN(p1.x, p2.x))); float min_y1 = (float)(MIN(p0.y, MIN(p1.y, p2.y))); float max_x1 = (float)(MAX(p0.x, MAX(p1.x, p2.x))); float max_y1 = (float)(MAX(p0.y, MAX(p1.y, p2.y))); - I64 min_x = (I64)MAX(0, Floor(min_x1)); - I64 min_y = (I64)MAX(0, Floor(min_y1)); - I64 max_x = (I64)MIN(dst->x, Ceil(max_x1)); - I64 max_y = (I64)MIN(dst->y, Ceil(max_y1)); + I64 min_x = (I64)MAX(0, floor(min_x1)); + I64 min_y = (I64)MAX(0, floor(min_y1)); + I64 max_x = (I64)MIN(dst->x, ceil(max_x1)); + I64 max_y = (I64)MIN(dst->y, ceil(max_y1)); - float area = EdgeFunction(p0, p1, p2); + float area = edge_function(p0, p1, p2); for (I64 y = min_y; y < max_y; y++) { for (I64 x = min_x; x < max_x; x++) { - float edge1 = EdgeFunction(p0, p1, { (float)x,(float)y }); - float edge2 = EdgeFunction(p1, p2, { (float)x,(float)y }); - float edge3 = EdgeFunction(p2, p0, { (float)x,(float)y }); + float edge1 = edge_function(p0, p1, { (float)x,(float)y }); + float edge2 = edge_function(p1, p2, { (float)x,(float)y }); + float edge3 = edge_function(p2, p0, { (float)x,(float)y }); if (edge1 >= 0 && edge2 >= 0 && edge3 >= 0) { float w1 = edge2 / area; float w2 = edge3 / area; float w3 = edge1 / area; + float interpolated_z = (1.f / p0.w) * w1 + (1.f / p1.w) * w2 + (1.f / p2.w) * w3; #if PERSPECTIVE_CORRECT_INTERPOLATION float u = tex0.x * (w1 / p0.w) + tex1.x * (w2 / p1.w) + tex2.x * (w3 / p2.w); float v = tex0.y * (w1 / p0.w) + tex1.y * (w2 / p1.w) + tex2.y * (w3 / p2.w); - - float interpolated_z = (1.f / p0.w) * w1 + (1.f / p1.w) * w2 + (1.f / p2.w) * w3; u /= interpolated_z; v /= interpolated_z; - interpolated_z = 1.f / interpolated_z; #else float u = tex0.x * w1 + tex1.x * w2 + tex2.x * w3; float v = tex0.y * w1 + tex1.y * w2 + tex2.y * w3; #endif + // @Note: We could do: interpolated_z = 1.f / interpolated_z to get proper depth + // but why waste an instruction, the smaller the depth value the farther the object float* depth = depth_buffer + (x + y * dst->x); - if (*depth > interpolated_z) { + if (*depth < interpolated_z) { *depth = interpolated_z; u = u * (src->x - 2); v = v * (src->y - 2); @@ -175,16 +199,20 @@ void DrawTriangle(Image* dst, float *depth_buffer, Image *src, Vec4 p0, Vec4 p1, float vdiff = v - (float)vi; // Origin UV (0,0) is in bottom left U32* pixel = src->pixels + (ui + (src->y - 1ll - vi) * src->x); -#if BILINEAR_BLEND - Vec4 pixelx1y1 = V4ABGR(*pixel); - Vec4 pixelx2y1 = V4ABGR(*(pixel + 1)); - Vec4 pixelx1y2 = V4ABGR(*(pixel - src->x)); - Vec4 pixelx2y2 = V4ABGR(*(pixel + 1 - src->x)); - Vec4 blendx1 = Lerp(pixelx1y1, pixelx2y1, udiff); - Vec4 blendx2 = Lerp(pixelx1y2, pixelx2y2, udiff); - Vec4 result_color = Lerp(blendx1, blendx2, vdiff); - U32 color32 = ColorToU32ABGR(result_color); +#if BILINEAR_BLEND + Vec4 pixelx1y1 = srgb_to_almost_linear(v4abgr(*pixel)); + Vec4 pixelx2y1 = srgb_to_almost_linear(v4abgr(*(pixel + 1))); + Vec4 pixelx1y2 = srgb_to_almost_linear(v4abgr(*(pixel - src->x))); + Vec4 pixelx2y2 = srgb_to_almost_linear(v4abgr(*(pixel + 1 - src->x))); + + + Vec4 blendx1 = lerp(pixelx1y1, pixelx2y1, udiff); + Vec4 blendx2 = lerp(pixelx1y2, pixelx2y2, udiff); + Vec4 result_color = lerp(blendx1, blendx2, vdiff); + result_color = almost_linear_to_srgb(result_color); + ASSERT(result_color.r <= 1 && result_color.g <= 1 && result_color.b <= 1); + U32 color32 = color_to_u32abgr(result_color); #else U32 color32 = *pixel; #endif @@ -195,14 +223,15 @@ void DrawTriangle(Image* dst, float *depth_buffer, Image *src, Vec4 p0, Vec4 p1, } } if (draw_rects) { - DrawRect(dst, p0.x-4, p0.y-4, 8,8, 0x00ff0000); - DrawRect(dst, p1.x-4, p1.y-4, 8,8, 0x0000ff00); - DrawRect(dst, p2.x-4, p2.y-4, 8,8, 0x000000ff); + draw_rect(dst, p0.x-4, p0.y-4, 8,8, 0x00ff0000); + draw_rect(dst, p1.x-4, p1.y-4, 8,8, 0x0000ff00); + draw_rect(dst, p2.x-4, p2.y-4, 8,8, 0x000000ff); } } +#include "raster_functions.cpp" FUNCTION -void DrawLine(Image *dst, float x0, float y0, float x1, float y1) { +void draw_line(Image *dst, float x0, float y0, float x1, float y1) { float delta_x = (x1 - x0); float delta_y = (y1 - y0); float longest_side_length = (ABS(delta_x) >= ABS(delta_y)) ? ABS(delta_x) : ABS(delta_y); @@ -226,16 +255,16 @@ struct FaceA { }; FUNCTION -Obj LoadObj(const char* file) { - char* data = OS_ReadFile(file); +Obj load_obj(const char* file) { + char* data = os.read_file(file); char* memory = (char*)malloc(100000); - Obj result = Obj_Parse(memory, 100000, data); + Obj result = obj::parse(memory, 100000, data); free(data); return result; } FUNCTION -Image LoadImage(const char* path) { +Image load_image(const char* path) { int x, y, n; unsigned char* data = stbi_load(path, &x, &y, &n, 4); Image result = { (U32*)data, x, y }; @@ -243,23 +272,25 @@ Image LoadImage(const char* path) { } int main() { - Obj_Test(); - OS_Init({ 1280,720 }); + obj::test(); + os.init({ 1280,720 }); + generate_stuff(); + float rotation = 0; Vec3 camera_pos = {0,0,-5}; - Obj obj = LoadObj("assets/f22.obj"); + Obj obj = load_obj("assets/f22.obj"); Vec3* vertices = (Vec3 *)obj.vertices; Vec2* tex_coords = (Vec2*)obj.texture; FaceA* faces = (FaceA*)obj.indices; I64 face_count = obj.indices_count; - Image img = LoadImage("assets/bricksx64.png"); + Image img = load_image("assets/bricksx64.png"); Image screen320 = {(U32 *)malloc(320*180*sizeof(U32)), 320, 180}; float* depth320 = (float *)malloc(sizeof(float) * 320 * 180); - while (OS_GameLoop()) { - Mat4 perspective = Mat4Perspective(60.f, (float)screen.x, (float)screen.y, 0.1f, 100.f); + while (os.game_loop()) { + Mat4 perspective = make_matrix_perspective(60.f, (float)os.screen.x, (float)os.screen.y, 0.1f, 100.f); U32* p = screen320.pixels; for (int y = 0; y < screen320.y; y++) { for (int x = 0; x < screen320.x; x++) { @@ -269,16 +300,16 @@ int main() { float* dp = depth320; for (int y = 0; y < screen320.y; y++) { for (int x = 0; x < screen320.x; x++) { - *dp++ = FLT_MAX; + *dp++ = -FLT_MAX; } } - DrawBitmap(&screen320, &img, {0,0}); - Mat4 transform = Mat4RotationZ(rotation); - transform = transform * Mat4RotationX(rotation); - if (keydown_a) rotation += 0.05f; - if (keydown_b) rotation -= 0.05f; - if (keydown_f1) draw_rects = !draw_rects; - if (keydown_f2) draw_wireframe = !draw_wireframe; + draw_bitmap(&screen320, &img, {0,0}); + Mat4 transform = make_matrix_rotation_z(rotation); + transform = transform * make_matrix_rotation_x(rotation); + if (os.keydown_a) rotation += 0.05f; + if (os.keydown_b) rotation -= 0.05f; + if (os.keydown_f1) draw_rects = !draw_rects; + if (os.keydown_f2) draw_wireframe = !draw_wireframe; for (int i = 0; i < face_count; i++) { FaceA* face = faces + i; Vec4 pos[3] = { @@ -300,8 +331,8 @@ int main() { Vec3 p0_to_camera = camera_pos - pos[0].xyz; Vec3 p0_to_p1 = pos[1].xyz - pos[0].xyz; Vec3 p0_to_p2 = pos[2].xyz - pos[0].xyz; - Vec3 normal = Cross(p0_to_p1, p0_to_p2); - if (Dot(normal, p0_to_camera) > 0) { + Vec3 normal = cross(p0_to_p1, p0_to_p2); + if (dot(normal, p0_to_camera) > 0) { for (int j = 0; j < 3; j++) { //@Note: Camera pos[j].xyz = pos[j].xyz - camera_pos; @@ -313,25 +344,32 @@ int main() { //@Note: To pixel space pos[j].x *= screen320.x / 2; pos[j].y *= screen320.y / 2; - pos[j].x += screen320.x / 2; - pos[j].y += screen320.y / 2; + //pos[j].x += screen320.x / 2; + //pos[j].y += screen320.y / 2; + pos[j].x += screen320.x / 4; + pos[j].y += screen320.y / 4; } - DrawTriangle(&screen320, depth320, &img, pos[0], pos[1], pos[2], tex[0], tex[1], tex[2]); + draw_triangle_PERSPECTIVE_CORRECT_INTERPOLATION_on_BILINEAR_BLEND_off(&screen320, depth320, &img, pos[0], pos[1], pos[2], tex[0], tex[1], tex[2]); + for (int j = 0; j < 3; j++) { + pos[j].x += screen320.x / 3; + pos[j].y += screen320.y / 3; + } + draw_triangle_PERSPECTIVE_CORRECT_INTERPOLATION_on_BILINEAR_BLEND_on(&screen320, depth320, &img, pos[0], pos[1], pos[2], tex[0], tex[1], tex[2]); if (draw_wireframe) { - DrawLine(&screen320, pos[0].x, pos[0].y, pos[1].x, pos[1].y); - DrawLine(&screen320, pos[1].x, pos[1].y, pos[2].x, pos[2].y); - DrawLine(&screen320, pos[2].x, pos[2].y, pos[0].x, pos[0].y); + draw_line(&screen320, pos[0].x, pos[0].y, pos[1].x, pos[1].y); + draw_line(&screen320, pos[1].x, pos[1].y, pos[2].x, pos[2].y); + draw_line(&screen320, pos[2].x, pos[2].y, pos[0].x, pos[0].y); } } } // @Note: Draw 320screen to OS screen - U32* ptr = screen.pixels; - for (int y = 0; y < screen.y; y++) { - for (int x = 0; x < screen.x; x++) { - float u = (float)x / (float)screen.x; - float v = (float)y / (float)screen.y; + U32* ptr = os.screen.pixels; + for (int y = 0; y < os.screen.y; y++) { + for (int x = 0; x < os.screen.x; x++) { + float u = (float)x / (float)os.screen.x; + float v = (float)y / (float)os.screen.y; int tx = (int)(u * screen320.x + 0.5f); int ty = (int)(v * screen320.y + 0.5f); *ptr++ = screen320.pixels[tx + ty * (screen320.x)]; diff --git a/math.h b/math.h index bb45d43..89cd233 100644 --- a/math.h +++ b/math.h @@ -1,6 +1,8 @@ #include "main.h" #include +constexpr float PI32 = 3.14159265359f; + struct Mat4 { float p[4][4]; }; @@ -20,8 +22,6 @@ union Vec4 { struct { Vec3 xyz; }; }; -constexpr float PI32 = 3.14159265359f; - FUNCTION Vec4 vec4(Vec3 a, float b) { Vec4 result = { a.x,a.y,a.z,b }; @@ -29,7 +29,7 @@ Vec4 vec4(Vec3 a, float b) { } FUNCTION -Mat4 Mat4Identity() { +Mat4 make_matrix_identity() { Mat4 result = { 1,0,0,0, 0,1,0,0, @@ -40,7 +40,7 @@ Mat4 Mat4Identity() { } FUNCTION -float Sin(float value) { +float sin(float value) { __m128 result128 = _mm_set_ps1(value); result128 = _mm_sin_ps(result128); float result = *(float *)&result128; @@ -48,7 +48,7 @@ float Sin(float value) { } FUNCTION -float Cos(float value) { +float cos(float value) { __m128 result128 = _mm_set_ps1(value); result128 = _mm_cos_ps(result128); float result = *(float*)&result128; @@ -56,7 +56,7 @@ float Cos(float value) { } FUNCTION -float Tan(float value) { +float tan(float value) { __m128 result128 = _mm_set_ps1(value); result128 = _mm_tan_ps(result128); float result = *(float*)&result128; @@ -64,7 +64,7 @@ float Tan(float value) { } FUNCTION -float Floor(float value) { +float floor(float value) { __m128 result128 = _mm_set_ps1(value); result128 = _mm_floor_ps(result128); float result = *(float*)&result128; @@ -72,7 +72,7 @@ float Floor(float value) { } FUNCTION -float Ceil(float value) { +float ceil(float value) { __m128 result128 = _mm_set_ps1(value); result128 = _mm_ceil_ps(result128); float result = *(float*)&result128; @@ -80,7 +80,7 @@ float Ceil(float value) { } FUNCTION -float Round(float value) { +float round(float value) { __m128 result128 = _mm_set_ps1(value); result128 = _mm_round_ps(result128, _MM_FROUND_TO_NEAREST_INT| _MM_FROUND_NO_EXC); float result = *(float*)&result128; @@ -88,9 +88,17 @@ float Round(float value) { } FUNCTION -Mat4 Mat4RotationZ(float rotation) { - float s = Sin(rotation); - float c = Cos(rotation); +float sqrt(float value) { + __m128 result128 = _mm_set_ps1(value); + result128 = _mm_sqrt_ps(result128); + float result = *(float*)&result128; + return result; +} + +FUNCTION +Mat4 make_matrix_rotation_x(float rotation) { + float s = sin(rotation); + float c = cos(rotation); Mat4 result = { c, s, 0, 0, -s, c, 0, 0, @@ -101,9 +109,9 @@ Mat4 Mat4RotationZ(float rotation) { } FUNCTION -Mat4 Mat4RotationY(float rotation) { - float s = Sin(rotation); - float c = Cos(rotation); +Mat4 make_matrix_rotation_y(float rotation) { + float s = sin(rotation); + float c = cos(rotation); Mat4 result = { c, 0, -s, 0, 0, 1, 0, 0, @@ -114,9 +122,9 @@ Mat4 Mat4RotationY(float rotation) { } FUNCTION -Mat4 Mat4RotationX(float rotation) { - float s = Sin(rotation); - float c = Cos(rotation); +Mat4 make_matrix_rotation_z(float rotation) { + float s = sin(rotation); + float c = cos(rotation); Mat4 result = { 1, 0, 0, 0, 0, c, s, 0, @@ -127,9 +135,9 @@ Mat4 Mat4RotationX(float rotation) { } FUNCTION -Mat4 Mat4Perspective(float fov, float window_x, float window_y, float znear, float zfar) { +Mat4 make_matrix_perspective(float fov, float window_x, float window_y, float znear, float zfar) { float aspect_ratio = window_y / window_x; - float f = (1.f / Tan((fov/2.f)*(180.f/PI32))); + float f = (1.f / tan((fov/2.f)*(180.f/PI32))); Mat4 result = { aspect_ratio*f, 0, 0, 0, 0, f, 0, 0, @@ -140,7 +148,19 @@ Mat4 Mat4Perspective(float fov, float window_x, float window_y, float znear, flo } FUNCTION -Mat4 Mat4Translate(Mat4 a, Vec3 translation) { +Mat4 transpose(Mat4 a) { + Mat4 result = a; + result.p[0][1] = result.p[1][0]; + result.p[0][2] = result.p[2][0]; + result.p[0][3] = result.p[3][0]; + result.p[2][1] = result.p[1][2]; + result.p[3][1] = result.p[1][3]; + result.p[3][2] = result.p[2][3]; + return result; +} + +FUNCTION +Mat4 translate(Mat4 a, Vec3 translation) { a.p[0][0] += translation.x; a.p[0][1] += translation.y; a.p[0][2] += translation.z; @@ -189,7 +209,6 @@ Vec3 operator+(Vec3 a, Vec3 b) { return result; } - FUNCTION Vec4 operator*(Mat4 a, Vec4 b) { Vec4 result = { @@ -225,13 +244,13 @@ Mat4 operator*(Mat4 a, Mat4 b) { } FUNCTION -float Dot(Vec3 a, Vec3 b) { +float dot(Vec3 a, Vec3 b) { float result = a.x * b.x + a.y * b.y + a.z * b.z; return result; } FUNCTION -Vec3 Cross(Vec3 a, Vec3 b) { +Vec3 cross(Vec3 a, Vec3 b) { Vec3 result = { a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, @@ -241,7 +260,7 @@ Vec3 Cross(Vec3 a, Vec3 b) { } FUNCTION -U32 ColorToU32ARGB(Vec4 a) { +U32 color_to_u32argb(Vec4 a) { uint8_t r8 = (uint8_t)(a.r * 255.f); uint8_t g8 = (uint8_t)(a.g * 255.f); uint8_t b8 = (uint8_t)(a.b * 255.f); @@ -251,7 +270,7 @@ U32 ColorToU32ARGB(Vec4 a) { } FUNCTION -U32 ColorToU32ABGR(Vec4 a) { +U32 color_to_u32abgr(Vec4 a) { uint8_t r8 = (uint8_t)(a.r * 255.f); uint8_t g8 = (uint8_t)(a.g * 255.f); uint8_t b8 = (uint8_t)(a.b * 255.f); @@ -261,7 +280,7 @@ U32 ColorToU32ABGR(Vec4 a) { } FUNCTION -Vec4 V4ARGB(U32 c) { +Vec4 v4argb(U32 c) { float a = ((c & 0xff000000) >> 24) / 255.f; float r = ((c & 0x00ff0000) >> 16) / 255.f; float g = ((c & 0x0000ff00) >> 8) / 255.f; @@ -271,7 +290,7 @@ Vec4 V4ARGB(U32 c) { } FUNCTION -Vec4 V4ABGR(U32 c) { +Vec4 v4abgr(U32 c) { float a = ((c & 0xff000000) >> 24) / 255.f; float b = ((c & 0x00ff0000) >> 16) / 255.f; float g = ((c & 0x0000ff00) >> 8) / 255.f; @@ -281,13 +300,13 @@ Vec4 V4ABGR(U32 c) { } FUNCTION -float Lerp(float a, float b, float t) { +float lerp(float a, float b, float t) { float result = (1.0f - t) * a + t * b; return result; } FUNCTION -Vec4 Lerp(Vec4 a, Vec4 b, float t) { - Vec4 result = {Lerp(a.x,b.x,t), Lerp(a.y,b.y,t), Lerp(a.z,b.z,t), Lerp(a.w,b.w,t) }; +Vec4 lerp(Vec4 a, Vec4 b, float t) { + Vec4 result = {lerp(a.x,b.x,t), lerp(a.y,b.y,t), lerp(a.z,b.z,t), lerp(a.w,b.w,t) }; return result; } \ No newline at end of file diff --git a/objparser.cpp b/objparser.cpp index 6e2a0c9..310aa60 100644 --- a/objparser.cpp +++ b/objparser.cpp @@ -2,6 +2,7 @@ #include #include #include +#define FUNCTION static enum class TokenType { none, word, number, whitespace, end @@ -18,223 +19,213 @@ struct Token { }; }; -static -bool Obj_IsAlphabetic(char w) { - bool result = (w >= 'a' && w <= 'z') || (w >= 'A' && w <= 'Z'); - return result; -} - -static -bool Obj_IsNumber(char w) { - bool result = w >= '0' && w <= '9'; - return result; -} - -static -bool Obj_IsWhitespace(char w) { - bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; - return result; -} - -static -int Obj_StringLen(char* a) { - int result = 0; - while (*a++ != 0) result++; - return result; -} - -static -bool Obj_Equals(Token a, const char* b) { - int len = Obj_StringLen((char*)b); - if (a.type != TokenType::word) return false; - if (a.len != len) return false; - for (int i = 0; i < len; i++) { - if (a.s[i] != b[i]) return false; - } - return true; -} - -static -Token Obj_NextTokenRaw(char** data) { - Token result = {}; - result.s = *data; - *data += 1; - - if (Obj_IsAlphabetic(*result.s)) { - result.type = TokenType::word; - while (!Obj_IsWhitespace(**data)) { - *data+=1; - } - result.len = (int)(*data - result.s); - } - else if (Obj_IsNumber(*result.s) || *result.s == '-') { - result.type = TokenType::number; - while (Obj_IsNumber(**data) || **data == '.') { - *data += 1; - } - result.number = atof(result.s); - } - else if (*result.s == '#') { - while (**data != '\n') *data += 1; - result = Obj_NextTokenRaw(data); - } - else if (Obj_IsWhitespace(*result.s)) { - result.type = TokenType::whitespace; - while (Obj_IsWhitespace(**data)) *data += 1; - result.len = (int)(*data - result.s); - } - else if (*result.s == 0) { - result.type = TokenType::end; - } - else if(*result.s >= '!') { - result.type = (TokenType)*result.s; - } - - return result; -} - -static -Token Obj_NextToken(char** data) { - Token result; - do { - result = Obj_NextTokenRaw(data); - } while (result.type == TokenType::whitespace); - return result; -} - -static -double Obj_ExpectNumber(char** data) { - Token t = Obj_NextToken(data); - assert(t.type == TokenType::number); // @Todo: Error handling, error flag - return t.number; -} - -static -void Obj_ExpectToken(char** data, char token) { - Token t = Obj_NextToken(data); - assert(t.type == (TokenType)token); // @Todo: Error handling, error flag -} - -static -void Obj_Debug_ExpectRaw(char** data, TokenType type) { - char* data_temp = *data; - assert(Obj_NextTokenRaw(&data_temp).type == type); -} - struct Obj_Arena { char* base; size_t size; size_t p; }; -static -char* Obj_Push(Obj_Arena *a, size_t size) { - char* ptr = a->base; - if (a->p + size < a->size) { - ptr += a->p; - a->p += size; +namespace obj { + FUNCTION bool is_alphabetic(char w) { + bool result = (w >= 'a' && w <= 'z') || (w >= 'A' && w <= 'Z'); + return result; } - else { - assert(!"Buffer is too small to hold the data!"); + + FUNCTION bool is_number(char w) { + bool result = w >= '0' && w <= '9'; + return result; } - return ptr; -} -Obj Obj_Parse(char* memory, size_t memory_size, char* data) { - Obj_Arena arena = { memory, memory_size }; - Obj result = {}; - int parsing_vertices = 0; - int parsing_normals = 0; - int parsing_textures = 0; + FUNCTION bool is_whitespace(char w) { + bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; + return result; + } - for (;;) { - Token token = Obj_NextToken(&data); - if (token.type == TokenType::end) break; - else if (token.type == TokenType::word) { - if (Obj_Equals(token, "v")) { - assert(parsing_vertices != 2); - parsing_vertices = 1; - float* ptr = (float*)Obj_Push(&arena, sizeof(float) * 3); - ptr[0] = (float)Obj_ExpectNumber(&data); - ptr[1] = (float)Obj_ExpectNumber(&data); - ptr[2] = (float)Obj_ExpectNumber(&data); - if (result.vertices == 0) result.vertices = ptr; - result.vertices_count++; - Obj_Debug_ExpectRaw(&data, TokenType::whitespace); + FUNCTION int string_len(char* a) { + int result = 0; + while (*a++ != 0) result++; + return result; + } + + FUNCTION bool equals(Token a, const char* b) { + int len = string_len((char*)b); + if (a.type != TokenType::word) return false; + if (a.len != len) return false; + for (int i = 0; i < len; i++) { + if (a.s[i] != b[i]) return false; + } + return true; + } + + FUNCTION Token next_token_raw(char** data) { + Token result = {}; + result.s = *data; + *data += 1; + + if (is_alphabetic(*result.s)) { + result.type = TokenType::word; + while (!is_whitespace(**data)) { + *data += 1; } - else if (Obj_Equals(token, "vt")) { - assert(parsing_textures != 2); - parsing_textures = 1; - parsing_vertices = 2; - - float* ptr = (float*)Obj_Push(&arena, sizeof(float) * 2); - ptr[0] = (float)Obj_ExpectNumber(&data); - ptr[1] = (float)Obj_ExpectNumber(&data); - if (result.texture == 0) result.texture = ptr; - Obj_Debug_ExpectRaw(&data, TokenType::whitespace); + result.len = (int)(*data - result.s); + } + else if (is_number(*result.s) || *result.s == '-') { + result.type = TokenType::number; + while (is_number(**data) || **data == '.') { + *data += 1; } - else if (Obj_Equals(token, "vn")) { - assert((parsing_textures == 1 || parsing_textures == 2) && parsing_vertices == 2); - parsing_textures = 2; - parsing_normals = 1; + result.number = atof(result.s); + } + else if (*result.s == '#') { + while (**data != '\n') *data += 1; + result = next_token_raw(data); + } + else if (is_whitespace(*result.s)) { + result.type = TokenType::whitespace; + while (is_whitespace(**data)) *data += 1; + result.len = (int)(*data - result.s); + } + else if (*result.s == 0) { + result.type = TokenType::end; + } + else if (*result.s >= '!') { + result.type = (TokenType)*result.s; + } - float* ptr = (float*)Obj_Push(&arena, sizeof(float) * 3); - ptr[0] = (float)Obj_ExpectNumber(&data); - ptr[1] = (float)Obj_ExpectNumber(&data); - ptr[2] = (float)Obj_ExpectNumber(&data); - if (result.normals == 0) result.normals = ptr; - Obj_Debug_ExpectRaw(&data, TokenType::whitespace); - } - else if (Obj_Equals(token, "f")) { - assert(parsing_normals == 1 && parsing_textures == 2 && parsing_vertices == 2); - int* ptr = (int*)Obj_Push(&arena, sizeof(int) * 9); - ptr[0] = (int)Obj_ExpectNumber(&data); - Obj_ExpectToken(&data, '/'); - ptr[3] = (int)Obj_ExpectNumber(&data); - Obj_ExpectToken(&data, '/'); - ptr[6] = (int)Obj_ExpectNumber(&data); + return result; + } - ptr[1] = (int)Obj_ExpectNumber(&data); - Obj_ExpectToken(&data, '/'); - ptr[4] = (int)Obj_ExpectNumber(&data); - Obj_ExpectToken(&data, '/'); - ptr[7] = (int)Obj_ExpectNumber(&data); + FUNCTION Token next_token(char** data) { + Token result; + do { + result = next_token_raw(data); + } while (result.type == TokenType::whitespace); + return result; + } - ptr[2] = (int)Obj_ExpectNumber(&data); - Obj_ExpectToken(&data, '/'); - ptr[5] = (int)Obj_ExpectNumber(&data); - Obj_ExpectToken(&data, '/'); - ptr[8] = (int)Obj_ExpectNumber(&data); - if (result.indices == 0) result.indices = ptr; - result.indices_count += 1; + FUNCTION double expect_number(char** data) { + Token t = next_token(data); + assert(t.type == TokenType::number); // @Todo: Error handling, error flag + return t.number; + } - Obj_Debug_ExpectRaw(&data, TokenType::whitespace); + FUNCTION void expect_token(char** data, char token) { + Token t = next_token(data); + assert(t.type == (TokenType)token); // @Todo: Error handling, error flag + } + + FUNCTION void debug_expect_raw(char** data, TokenType type) { + char* data_temp = *data; + assert(next_token_raw(&data_temp).type == type); + } + + FUNCTION char* push(Obj_Arena *a, size_t size) { + char* ptr = a->base; + if (a->p + size < a->size) { + ptr += a->p; + a->p += size; + } + else { + assert(!"Buffer is too small to hold the data!"); + } + return ptr; + } + + Obj parse(char* memory, size_t memory_size, char* data) { + Obj_Arena arena = { memory, memory_size }; + Obj result = {}; + int parsing_vertices = 0; + int parsing_normals = 0; + int parsing_textures = 0; + + for (; ; ) { + Token token = next_token(&data); + if (token.type == TokenType::end) break; + else if (token.type == TokenType::word) { + if (equals(token, "v")) { + assert(parsing_vertices != 2); + parsing_vertices = 1; + float* ptr = (float*)push(&arena, sizeof(float) * 3); + ptr[0] = (float)expect_number(&data); + ptr[1] = (float)expect_number(&data); + ptr[2] = (float)expect_number(&data); + if (result.vertices == 0) result.vertices = ptr; + result.vertices_count++; + debug_expect_raw(&data, TokenType::whitespace); + } + else if (equals(token, "vt")) { + assert(parsing_textures != 2); + parsing_textures = 1; + parsing_vertices = 2; + + float* ptr = (float*)push(&arena, sizeof(float) * 2); + ptr[0] = (float)expect_number(&data); + ptr[1] = (float)expect_number(&data); + if (result.texture == 0) result.texture = ptr; + debug_expect_raw(&data, TokenType::whitespace); + } + else if (equals(token, "vn")) { + assert((parsing_textures == 1 || parsing_textures == 2) && parsing_vertices == 2); + parsing_textures = 2; + parsing_normals = 1; + + float* ptr = (float*)push(&arena, sizeof(float) * 3); + ptr[0] = (float)expect_number(&data); + ptr[1] = (float)expect_number(&data); + ptr[2] = (float)expect_number(&data); + if (result.normals == 0) result.normals = ptr; + debug_expect_raw(&data, TokenType::whitespace); + } + else if (equals(token, "f")) { + assert(parsing_normals == 1 && parsing_textures == 2 && parsing_vertices == 2); + int* ptr = (int*)push(&arena, sizeof(int) * 9); + ptr[0] = (int)expect_number(&data); + expect_token(&data, '/'); + ptr[3] = (int)expect_number(&data); + expect_token(&data, '/'); + ptr[6] = (int)expect_number(&data); + + ptr[1] = (int)expect_number(&data); + expect_token(&data, '/'); + ptr[4] = (int)expect_number(&data); + expect_token(&data, '/'); + ptr[7] = (int)expect_number(&data); + + ptr[2] = (int)expect_number(&data); + expect_token(&data, '/'); + ptr[5] = (int)expect_number(&data); + expect_token(&data, '/'); + ptr[8] = (int)expect_number(&data); + if (result.indices == 0) result.indices = ptr; + result.indices_count += 1; + + debug_expect_raw(&data, TokenType::whitespace); + } } } + result.memory_taken = arena.p; + return result; } - result.memory_taken = arena.p; - return result; -} -static -void Obj_TestLex() { - const char* d = "v 0.885739 0.001910 -0.380334"; - char* dd = (char *)d; - assert(Obj_NextToken(&dd).type == TokenType::word); - Token t = Obj_NextToken(&dd); assert(t.type == TokenType::number && t.number > 0.8857); - t = Obj_NextToken(&dd); assert(t.type == TokenType::number && t.number > 0.0019); - t = Obj_NextToken(&dd); assert(t.type == TokenType::number && t.number < -0.38); - d = "# Blender v2.79 (sub 0) OBJ File: 'fighters_0.blend'\n" - "# www.blender.org\n" - "mtllib f-22.mtl\n" - "o F-22\n"; - dd = (char *)d; - t = Obj_NextToken(&dd); assert(t.type == TokenType::word && Obj_Equals(t, (char*)"mtllib")); - t = Obj_NextToken(&dd); assert(t.type == TokenType::word && Obj_Equals(t, (char*)"f-22.mtl")); - t = Obj_NextToken(&dd); assert(t.type == TokenType::word && Obj_Equals(t, (char*)"o")); - t = Obj_NextToken(&dd); assert(t.type == TokenType::word && Obj_Equals(t, (char*)"F-22")); -} + FUNCTION void test_lex() { + const char* d = "v 0.885739 0.001910 -0.380334"; + char* dd = (char *)d; + assert(next_token(&dd).type == TokenType::word); + Token t = next_token(&dd); assert(t.type == TokenType::number && t.number > 0.8857); + t = next_token(&dd); assert(t.type == TokenType::number && t.number > 0.0019); + t = next_token(&dd); assert(t.type == TokenType::number && t.number < -0.38); + d = "# Blender v2.79 (sub 0) OBJ File: 'fighters_0.blend'\n" + "# www.blender.org\n" + "mtllib f-22.mtl\n" + "o F-22\n"; + dd = (char *)d; + t = next_token(&dd); assert(t.type == TokenType::word && equals(t, (char*)"mtllib")); + t = next_token(&dd); assert(t.type == TokenType::word && equals(t, (char*)"f-22.mtl")); + t = next_token(&dd); assert(t.type == TokenType::word && equals(t, (char*)"o")); + t = next_token(&dd); assert(t.type == TokenType::word && equals(t, (char*)"F-22")); + } -void Obj_Test() { - Obj_TestLex(); + void test() { + test_lex(); + } } \ No newline at end of file diff --git a/objparser.h b/objparser.h index c2f4315..7631afc 100644 --- a/objparser.h +++ b/objparser.h @@ -10,5 +10,7 @@ struct Obj { size_t memory_taken; }; -Obj Obj_Parse(char* memory, size_t memory_size, char* data); -void Obj_Test(); +namespace obj { + Obj parse(char* memory, size_t memory_size, char* data); + void test(); +} diff --git a/platform.cpp b/platform.cpp index 9b1db9e..2c859ef 100644 --- a/platform.cpp +++ b/platform.cpp @@ -7,50 +7,41 @@ #include #include +struct OSWin32 { + HBITMAP screen_dib; + HDC screen_dc; + HDC window_dc; + HWND hwnd; + HINSTANCE hinstance; +}; typedef HRESULT tSetProcessDpiAwareness(PROCESS_DPI_AWARENESS); +GLOBAL OS *wnd_proc_os = 0; -Image screen; -bool keydown_a; -bool keydown_b; -bool keydown_f1; -bool keydown_f2; -bool keydown_f3; - -GLOBAL bool g_app_is_running = true; -GLOBAL HBITMAP g_screen_dib; -GLOBAL HDC g_screen_dc; -GLOBAL HDC g_window_dc; -GLOBAL HWND g_hwnd; -GLOBAL HINSTANCE g_hinstance; -GLOBAL int g_cmdshow; - - -FUNCTION LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT result = 0; switch (uMsg) { - case WM_CLOSE: DestroyWindow(hwnd); g_app_is_running = false; break; - case WM_DESTROY: PostQuitMessage(0); g_app_is_running = false; break; + case WM_CLOSE: DestroyWindow(hwnd); wnd_proc_os->app_is_running = false; break; + case WM_DESTROY: PostQuitMessage(0); wnd_proc_os->app_is_running = false; break; case WM_SYSKEYDOWN: case WM_KEYDOWN: { switch (wParam) { - case VK_ESCAPE: g_app_is_running = false; break; - case 0x4F: keydown_a = true; break; - case 0x50: keydown_b = true; break; - case VK_F1: keydown_f1 = true; break; - case VK_F2: keydown_f2 = true; break; - case VK_F3: keydown_f3 = true; break; + case VK_ESCAPE: wnd_proc_os->app_is_running = false; break; + case 0x4F: wnd_proc_os->keydown_a = true; break; + case 0x50: wnd_proc_os->keydown_b = true; break; + case VK_F1: wnd_proc_os->keydown_f1 = true; break; + case VK_F2: wnd_proc_os->keydown_f2 = true; break; + case VK_F3: wnd_proc_os->keydown_f3 = true; break; } } break; case WM_SYSKEYUP: case WM_KEYUP: { switch (wParam) { - case 0x4F: keydown_a = false; break; - case 0x50: keydown_b = false; break; - case VK_F1: keydown_f1 = false; break; - case VK_F2: keydown_f2 = false; break; - case VK_F3: keydown_f3 = false; break; + case 0x4F: wnd_proc_os->keydown_a = false; break; + case 0x50: wnd_proc_os->keydown_b = false; break; + case VK_F1: wnd_proc_os->keydown_f1 = false; break; + case VK_F2: wnd_proc_os->keydown_f2 = false; break; + case VK_F3: wnd_proc_os->keydown_f3 = false; break; } } break; default: result = DefWindowProc(hwnd, uMsg, wParam, lParam); @@ -59,7 +50,8 @@ LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } FUNCTION -void Win32_ScreenInit(int window_x, int window_y) { +void win32_screen_init(OS *os, int window_x, int window_y) { + OSWin32 *w32 = (OSWin32 *)os->os_internal_data; BITMAPINFO bminfo = {}; bminfo.bmiHeader.biSize = sizeof(bminfo.bmiHeader); bminfo.bmiHeader.biWidth = window_x; @@ -71,14 +63,16 @@ void Win32_ScreenInit(int window_x, int window_y) { bminfo.bmiHeader.biYPelsPerMeter = 1; void* mem = 0; - g_screen_dib = CreateDIBSection(g_window_dc, &bminfo, DIB_RGB_COLORS, (void**)&mem, 0, 0); - g_screen_dc = CreateCompatibleDC(g_window_dc); - screen.pixels = (U32*)mem; - screen.x = window_x; - screen.y = window_y; + w32->screen_dib = CreateDIBSection(w32->window_dc, &bminfo, DIB_RGB_COLORS, (void**)&mem, 0, 0); + w32->screen_dc = CreateCompatibleDC(w32->window_dc); + os->screen.pixels = (U32*)mem; + os->screen.x = window_x; + os->screen.y = window_y; } -void OS_Init(OSInitArgs i) { +void OS::init(OSInitArgs i) { + OSWin32 *w32 = (OSWin32 *)os_internal_data; + w32->hinstance = (HINSTANCE)GetModuleHandle(NULL); HMODULE shcore = LoadLibraryA("Shcore.dll"); if (shcore) { tSetProcessDpiAwareness* set_dpi_awr = (tSetProcessDpiAwareness*)GetProcAddress(shcore, "SetProcessDpiAwareness"); @@ -91,11 +85,11 @@ void OS_Init(OSInitArgs i) { const wchar_t CLASS_NAME[] = L"Hello!"; WNDCLASS wc = { }; wc.lpfnWndProc = WindowProc; - wc.hInstance = g_hinstance; + wc.hInstance = w32->hinstance; wc.lpszClassName = CLASS_NAME; ASSERT(RegisterClass(&wc)); - g_hwnd = CreateWindowEx( + w32->hwnd = CreateWindowEx( 0, // Optional window styles. CLASS_NAME, // Window class L"Have a wonderful day!", // Window text @@ -103,24 +97,28 @@ void OS_Init(OSInitArgs i) { CW_USEDEFAULT, CW_USEDEFAULT, i.window_x, i.window_y, // Size and position NULL, // Parent window NULL, // Menu - g_hinstance, // Instance handle + w32->hinstance, // Instance handle NULL // Additional application data ); - if (g_hwnd == 0) { + if (w32->hwnd == 0) { ASSERT(!"Failed to create window"); return; } - ShowWindow(g_hwnd, SW_SHOW); + ShowWindow(w32->hwnd, SW_SHOW); RECT rect; - GetWindowRect(g_hwnd, &rect); - g_window_dc = GetWindowDC(g_hwnd); - Win32_ScreenInit(rect.right - rect.left, rect.bottom - rect.top); + GetWindowRect(w32->hwnd, &rect); + w32->window_dc = GetWindowDC(w32->hwnd); + win32_screen_init(this, rect.right - rect.left, rect.bottom - rect.top); + app_is_running = true; } -bool OS_GameLoop() { +bool OS::game_loop() { + OSWin32 *w32 = (OSWin32 *)os_internal_data; + MSG msg = { }; + wnd_proc_os = this; while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); @@ -128,7 +126,7 @@ bool OS_GameLoop() { // @Note: Free the screen on window resize RECT rect; - GetWindowRect(g_hwnd, &rect); + GetWindowRect(w32->hwnd, &rect); int new_width = rect.right - rect.left; int new_height = rect.bottom - rect.top; if (new_width != screen.x || new_height != screen.y) { @@ -136,14 +134,14 @@ bool OS_GameLoop() { screen.y = new_height; if (screen.pixels) { screen.pixels = 0; - DeleteDC(g_screen_dc); - DeleteObject(g_screen_dib); + DeleteDC(w32->screen_dc); + DeleteObject(w32->screen_dib); } } // @Note: Create drawable screen if (!screen.pixels) { - Win32_ScreenInit((LONG)screen.x, (LONG)screen.y); + win32_screen_init(this, (LONG)screen.x, (LONG)screen.y); } // @Note: Draw screen to window @@ -157,33 +155,35 @@ bool OS_GameLoop() { } } - SelectObject(g_screen_dc, g_screen_dib); - BitBlt(g_window_dc, 0, 0, (int)screen.x, (int)screen.y, g_screen_dc, 0, 0, SRCCOPY); + SelectObject(w32->screen_dc, w32->screen_dib); + BitBlt(w32->window_dc, 0, 0, (int)screen.x, (int)screen.y, w32->screen_dc, 0, 0, SRCCOPY); } Sleep(16); - return g_app_is_running; + return app_is_running; } int main(); int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { - g_hinstance = hInstance; - g_cmdshow = nCmdShow; return main(); } #include -void OS_Message(const char* format, ...) { +void OS::message(int debug, const char* format, ...) { char buffer[1024]; va_list args; va_start(args, format); vsnprintf(buffer, 1024, format, args); va_end(args); - - MessageBoxA(0, buffer, "Error!", 0); + if (debug) { + OutputDebugStringA(buffer); + } + else { + MessageBoxA(0, buffer, "Error!", 0); + } } -char* OS_ReadFile(const char* path) { +char* OS::read_file(const char* path) { char* result = 0; FILE* f = fopen(path, "rb"); if (f) { diff --git a/platform.h b/platform.h index ba4d07f..79a3455 100644 --- a/platform.h +++ b/platform.h @@ -11,13 +11,22 @@ struct OSInitArgs { int window_y; }; -extern Image screen; -extern bool keydown_a; -extern bool keydown_b; -extern bool keydown_f1; -extern bool keydown_f2; -extern bool keydown_f3; +struct OS { + Image screen; + bool keydown_a; + bool keydown_b; + bool keydown_f1; + bool keydown_f2; + bool keydown_f3; + bool app_is_running; -bool OS_GameLoop(); -void OS_Init(OSInitArgs); -char* OS_ReadFile(const char* path); \ No newline at end of file + void init(OSInitArgs args); + bool game_loop(); + + void message(int debug, const char *msg, ...); + char *read_file(const char *path); + + OS() = default; + OS(OSInitArgs args) { init(args); } + char os_internal_data[1024]; +}; \ No newline at end of file diff --git a/raster_functions.cpp b/raster_functions.cpp new file mode 100644 index 0000000..34791ce --- /dev/null +++ b/raster_functions.cpp @@ -0,0 +1,255 @@ + +FUNCTION void draw_triangle_PERSPECTIVE_CORRECT_INTERPOLATION_off_BILINEAR_BLEND_off (Image* dst, float *depth_buffer, Image *src, + Vec4 p0, Vec4 p1, Vec4 p2, + Vec2 tex0, Vec2 tex1, Vec2 tex2) { + float min_x1 = (float)(MIN(p0.x, MIN(p1.x, p2.x))); + float min_y1 = (float)(MIN(p0.y, MIN(p1.y, p2.y))); + float max_x1 = (float)(MAX(p0.x, MAX(p1.x, p2.x))); + float max_y1 = (float)(MAX(p0.y, MAX(p1.y, p2.y))); + I64 min_x = (I64)MAX(0, floor(min_x1)); + I64 min_y = (I64)MAX(0, floor(min_y1)); + I64 max_x = (I64)MIN(dst->x, ceil(max_x1)); + I64 max_y = (I64)MIN(dst->y, ceil(max_y1)); + + float area = edge_function(p0, p1, p2); + for (I64 y = min_y; y < max_y; y++) { + for (I64 x = min_x; x < max_x; x++) { + float edge1 = edge_function(p0, p1, { (float)x,(float)y }); + float edge2 = edge_function(p1, p2, { (float)x,(float)y }); + float edge3 = edge_function(p2, p0, { (float)x,(float)y }); + + if (edge1 >= 0 && edge2 >= 0 && edge3 >= 0) { + float w1 = edge2 / area; + float w2 = edge3 / area; + float w3 = edge1 / area; + float interpolated_z = (1.f / p0.w) * w1 + (1.f / p1.w) * w2 + (1.f / p2.w) * w3; +// PERSPECTIVE_CORRECT_INTERPOLATION + float u = tex0.x * w1 + tex1.x * w2 + tex2.x * w3; + float v = tex0.y * w1 + tex1.y * w2 + tex2.y * w3; + + // @Note: We could do: interpolated_z = 1.f / interpolated_z to get proper depth + // but why waste an instruction, the smaller the depth value the farther the object + float* depth = depth_buffer + (x + y * dst->x); + if (*depth < interpolated_z) { + *depth = interpolated_z; + u = u * (src->x - 2); + v = v * (src->y - 2); + I64 ui = (I64)(u); + I64 vi = (I64)(v); + float udiff = u - (float)ui; + float vdiff = v - (float)vi; + // Origin UV (0,0) is in bottom left + U32* pixel = src->pixels + (ui + (src->y - 1ll - vi) * src->x); + +// BILINEAR_BLEND + U32 color32 = *pixel; + + + dst->pixels[x + y * dst->x] = color32; + } + } + } + } + if (draw_rects) { + draw_rect(dst, p0.x-4, p0.y-4, 8,8, 0x00ff0000); + draw_rect(dst, p1.x-4, p1.y-4, 8,8, 0x0000ff00); + draw_rect(dst, p2.x-4, p2.y-4, 8,8, 0x000000ff); + } +} +FUNCTION void draw_triangle_PERSPECTIVE_CORRECT_INTERPOLATION_on_BILINEAR_BLEND_on (Image* dst, float *depth_buffer, Image *src, + Vec4 p0, Vec4 p1, Vec4 p2, + Vec2 tex0, Vec2 tex1, Vec2 tex2) { + float min_x1 = (float)(MIN(p0.x, MIN(p1.x, p2.x))); + float min_y1 = (float)(MIN(p0.y, MIN(p1.y, p2.y))); + float max_x1 = (float)(MAX(p0.x, MAX(p1.x, p2.x))); + float max_y1 = (float)(MAX(p0.y, MAX(p1.y, p2.y))); + I64 min_x = (I64)MAX(0, floor(min_x1)); + I64 min_y = (I64)MAX(0, floor(min_y1)); + I64 max_x = (I64)MIN(dst->x, ceil(max_x1)); + I64 max_y = (I64)MIN(dst->y, ceil(max_y1)); + + float area = edge_function(p0, p1, p2); + for (I64 y = min_y; y < max_y; y++) { + for (I64 x = min_x; x < max_x; x++) { + float edge1 = edge_function(p0, p1, { (float)x,(float)y }); + float edge2 = edge_function(p1, p2, { (float)x,(float)y }); + float edge3 = edge_function(p2, p0, { (float)x,(float)y }); + + if (edge1 >= 0 && edge2 >= 0 && edge3 >= 0) { + float w1 = edge2 / area; + float w2 = edge3 / area; + float w3 = edge1 / area; + float interpolated_z = (1.f / p0.w) * w1 + (1.f / p1.w) * w2 + (1.f / p2.w) * w3; +// PERSPECTIVE_CORRECT_INTERPOLATION + float u = tex0.x * (w1 / p0.w) + tex1.x * (w2 / p1.w) + tex2.x * (w3 / p2.w); + float v = tex0.y * (w1 / p0.w) + tex1.y * (w2 / p1.w) + tex2.y * (w3 / p2.w); + u /= interpolated_z; + v /= interpolated_z; + + // @Note: We could do: interpolated_z = 1.f / interpolated_z to get proper depth + // but why waste an instruction, the smaller the depth value the farther the object + float* depth = depth_buffer + (x + y * dst->x); + if (*depth < interpolated_z) { + *depth = interpolated_z; + u = u * (src->x - 2); + v = v * (src->y - 2); + I64 ui = (I64)(u); + I64 vi = (I64)(v); + float udiff = u - (float)ui; + float vdiff = v - (float)vi; + // Origin UV (0,0) is in bottom left + U32* pixel = src->pixels + (ui + (src->y - 1ll - vi) * src->x); + +// BILINEAR_BLEND + Vec4 pixelx1y1 = srgb_to_almost_linear(v4abgr(*pixel)); + Vec4 pixelx2y1 = srgb_to_almost_linear(v4abgr(*(pixel + 1))); + Vec4 pixelx1y2 = srgb_to_almost_linear(v4abgr(*(pixel - src->x))); + Vec4 pixelx2y2 = srgb_to_almost_linear(v4abgr(*(pixel + 1 - src->x))); + + + Vec4 blendx1 = lerp(pixelx1y1, pixelx2y1, udiff); + Vec4 blendx2 = lerp(pixelx1y2, pixelx2y2, udiff); + Vec4 result_color = lerp(blendx1, blendx2, vdiff); + result_color = almost_linear_to_srgb(result_color); + ASSERT(result_color.r <= 1 && result_color.g <= 1 && result_color.b <= 1); + U32 color32 = color_to_u32abgr(result_color); + + + dst->pixels[x + y * dst->x] = color32; + } + } + } + } + if (draw_rects) { + draw_rect(dst, p0.x-4, p0.y-4, 8,8, 0x00ff0000); + draw_rect(dst, p1.x-4, p1.y-4, 8,8, 0x0000ff00); + draw_rect(dst, p2.x-4, p2.y-4, 8,8, 0x000000ff); + } +} +FUNCTION void draw_triangle_PERSPECTIVE_CORRECT_INTERPOLATION_off_BILINEAR_BLEND_on (Image* dst, float *depth_buffer, Image *src, + Vec4 p0, Vec4 p1, Vec4 p2, + Vec2 tex0, Vec2 tex1, Vec2 tex2) { + float min_x1 = (float)(MIN(p0.x, MIN(p1.x, p2.x))); + float min_y1 = (float)(MIN(p0.y, MIN(p1.y, p2.y))); + float max_x1 = (float)(MAX(p0.x, MAX(p1.x, p2.x))); + float max_y1 = (float)(MAX(p0.y, MAX(p1.y, p2.y))); + I64 min_x = (I64)MAX(0, floor(min_x1)); + I64 min_y = (I64)MAX(0, floor(min_y1)); + I64 max_x = (I64)MIN(dst->x, ceil(max_x1)); + I64 max_y = (I64)MIN(dst->y, ceil(max_y1)); + + float area = edge_function(p0, p1, p2); + for (I64 y = min_y; y < max_y; y++) { + for (I64 x = min_x; x < max_x; x++) { + float edge1 = edge_function(p0, p1, { (float)x,(float)y }); + float edge2 = edge_function(p1, p2, { (float)x,(float)y }); + float edge3 = edge_function(p2, p0, { (float)x,(float)y }); + + if (edge1 >= 0 && edge2 >= 0 && edge3 >= 0) { + float w1 = edge2 / area; + float w2 = edge3 / area; + float w3 = edge1 / area; + float interpolated_z = (1.f / p0.w) * w1 + (1.f / p1.w) * w2 + (1.f / p2.w) * w3; +// PERSPECTIVE_CORRECT_INTERPOLATION + float u = tex0.x * w1 + tex1.x * w2 + tex2.x * w3; + float v = tex0.y * w1 + tex1.y * w2 + tex2.y * w3; + + // @Note: We could do: interpolated_z = 1.f / interpolated_z to get proper depth + // but why waste an instruction, the smaller the depth value the farther the object + float* depth = depth_buffer + (x + y * dst->x); + if (*depth < interpolated_z) { + *depth = interpolated_z; + u = u * (src->x - 2); + v = v * (src->y - 2); + I64 ui = (I64)(u); + I64 vi = (I64)(v); + float udiff = u - (float)ui; + float vdiff = v - (float)vi; + // Origin UV (0,0) is in bottom left + U32* pixel = src->pixels + (ui + (src->y - 1ll - vi) * src->x); + +// BILINEAR_BLEND + Vec4 pixelx1y1 = srgb_to_almost_linear(v4abgr(*pixel)); + Vec4 pixelx2y1 = srgb_to_almost_linear(v4abgr(*(pixel + 1))); + Vec4 pixelx1y2 = srgb_to_almost_linear(v4abgr(*(pixel - src->x))); + Vec4 pixelx2y2 = srgb_to_almost_linear(v4abgr(*(pixel + 1 - src->x))); + + + Vec4 blendx1 = lerp(pixelx1y1, pixelx2y1, udiff); + Vec4 blendx2 = lerp(pixelx1y2, pixelx2y2, udiff); + Vec4 result_color = lerp(blendx1, blendx2, vdiff); + result_color = almost_linear_to_srgb(result_color); + ASSERT(result_color.r <= 1 && result_color.g <= 1 && result_color.b <= 1); + U32 color32 = color_to_u32abgr(result_color); + + + dst->pixels[x + y * dst->x] = color32; + } + } + } + } + if (draw_rects) { + draw_rect(dst, p0.x-4, p0.y-4, 8,8, 0x00ff0000); + draw_rect(dst, p1.x-4, p1.y-4, 8,8, 0x0000ff00); + draw_rect(dst, p2.x-4, p2.y-4, 8,8, 0x000000ff); + } +} +FUNCTION void draw_triangle_PERSPECTIVE_CORRECT_INTERPOLATION_on_BILINEAR_BLEND_off (Image* dst, float *depth_buffer, Image *src, + Vec4 p0, Vec4 p1, Vec4 p2, + Vec2 tex0, Vec2 tex1, Vec2 tex2) { + float min_x1 = (float)(MIN(p0.x, MIN(p1.x, p2.x))); + float min_y1 = (float)(MIN(p0.y, MIN(p1.y, p2.y))); + float max_x1 = (float)(MAX(p0.x, MAX(p1.x, p2.x))); + float max_y1 = (float)(MAX(p0.y, MAX(p1.y, p2.y))); + I64 min_x = (I64)MAX(0, floor(min_x1)); + I64 min_y = (I64)MAX(0, floor(min_y1)); + I64 max_x = (I64)MIN(dst->x, ceil(max_x1)); + I64 max_y = (I64)MIN(dst->y, ceil(max_y1)); + + float area = edge_function(p0, p1, p2); + for (I64 y = min_y; y < max_y; y++) { + for (I64 x = min_x; x < max_x; x++) { + float edge1 = edge_function(p0, p1, { (float)x,(float)y }); + float edge2 = edge_function(p1, p2, { (float)x,(float)y }); + float edge3 = edge_function(p2, p0, { (float)x,(float)y }); + + if (edge1 >= 0 && edge2 >= 0 && edge3 >= 0) { + float w1 = edge2 / area; + float w2 = edge3 / area; + float w3 = edge1 / area; + float interpolated_z = (1.f / p0.w) * w1 + (1.f / p1.w) * w2 + (1.f / p2.w) * w3; +// PERSPECTIVE_CORRECT_INTERPOLATION + float u = tex0.x * (w1 / p0.w) + tex1.x * (w2 / p1.w) + tex2.x * (w3 / p2.w); + float v = tex0.y * (w1 / p0.w) + tex1.y * (w2 / p1.w) + tex2.y * (w3 / p2.w); + u /= interpolated_z; + v /= interpolated_z; + + // @Note: We could do: interpolated_z = 1.f / interpolated_z to get proper depth + // but why waste an instruction, the smaller the depth value the farther the object + float* depth = depth_buffer + (x + y * dst->x); + if (*depth < interpolated_z) { + *depth = interpolated_z; + u = u * (src->x - 2); + v = v * (src->y - 2); + I64 ui = (I64)(u); + I64 vi = (I64)(v); + float udiff = u - (float)ui; + float vdiff = v - (float)vi; + // Origin UV (0,0) is in bottom left + U32* pixel = src->pixels + (ui + (src->y - 1ll - vi) * src->x); + +// BILINEAR_BLEND + U32 color32 = *pixel; + + + dst->pixels[x + y * dst->x] = color32; + } + } + } + } + if (draw_rects) { + draw_rect(dst, p0.x-4, p0.y-4, 8,8, 0x00ff0000); + draw_rect(dst, p1.x-4, p1.y-4, 8,8, 0x0000ff00); + draw_rect(dst, p2.x-4, p2.y-4, 8,8, 0x000000ff); + } +} \ No newline at end of file diff --git a/rasterization_feature_selection.cpp b/rasterization_feature_selection.cpp new file mode 100644 index 0000000..cb27ed0 --- /dev/null +++ b/rasterization_feature_selection.cpp @@ -0,0 +1,165 @@ +struct Token { + char* s; + int len; +}; + +FUNCTION bool is_alphabetic(char w) { + bool result = (w >= 'a' && w <= 'z') || (w >= 'A' && w <= 'Z'); + return result; +} + +FUNCTION bool is_number(char w) { + bool result = w >= '0' && w <= '9'; + return result; +} + +FUNCTION bool is_whitespace(char w) { + bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; + return result; +} + +FUNCTION int string_len(char* a) { + int result = 0; + while (*a++ != 0) result++; + return result; +} + +FUNCTION bool equals(Token a, const char* b) { + int len = string_len((char*)b); + if (a.len != len) return false; + for (int i = 0; i < len; i++) { + if (a.s[i] != b[i]) return false; + } + return true; +} + +bool combinations[64][8] = { { 0, 0 }, { 1, 1 }, { 0, 1 }, { 1, 0 } }; +int combinations_to_make = 4; + +struct Lexer { + char* stream; + char* data; + char output[10000]; + + Token next() { + while (is_whitespace(*stream)) stream++; + Token result = {}; + result.s = stream++; + if (is_alphabetic(*result.s)) { + while (is_alphabetic(*stream) || *stream == '_') stream++; + } + result.len = (int)(stream - result.s); + return result; + } + + bool match(const char* str, Token* out = 0) { + Lexer l = *this; + Token token = next(); + if (out) *out = token; + if (equals(token, str)) { + return true; + } + *this = l; + return false; + } +}; + +struct Section { + char* begin, * end; + char* name, * name_end; + int if_clause_i; + bool is_else; +}; + +FUNCTION void generate_stuff() { + char* data = os.read_file("main.cpp"); + FILE* f = fopen("raster_functions.cpp", "w"); + ASSERT(f); + Section sections[100] = {}; + int sections_count = 0; + int if_clause_count = 0; + Lexer lexer = { data, data }; + for (;;) { + Token token = lexer.next(); + if (*token.s == 0) break; + + if (equals(token, "FUNCTION") && + lexer.match("void") && + lexer.match("draw_triangle", &token)) { + Section* section = sections + sections_count++; + section->begin = token.s + token.len; + int indent = 1; + while (lexer.next().s[0] != '{'); + for (; ; ) { + token = lexer.next(); + if (token.s[0] == '{') indent++; + else if (token.s[0] == '}') indent--; + else if (token.s[0] == '#') { + if (lexer.match("if")) { + section->end = token.s; + + if_clause_count++; + token = lexer.next(); + section = sections + sections_count++; + section->if_clause_i = if_clause_count; + section->is_else = false; + section->name = token.s; + section->name_end = token.s + token.len; + section->begin = section->name_end; + } + else if (lexer.match("else")) { + section->end = token.s; + char* name = section->name; + char* name_end = section->name_end; + + section = sections + sections_count++; + section->is_else = true; + section->if_clause_i = if_clause_count; + section->name = name; + section->name_end = name_end; + section->begin = token.s + 5; + } + else if (lexer.match("endif")) { + section->end = token.s; + section = sections + sections_count++; + section->begin = token.s + 6; + } + } + + if (indent == 0) { + section->end = token.s + 1; + break; + } + } + + for (int ci = 0; ci < combinations_to_make; ci++) { + // @Note: Figure out function name + fprintf(f, "\nFUNCTION void draw_triangle"); + for (int i = 0; i < sections_count; i++) { + section = sections + i; + if (section->name && section->is_else == !combinations[ci][section->if_clause_i - 1]) { + fprintf(f, "_%.*s_", (int)(section->name_end - section->name), section->name); + fprintf(f, "%s", section->is_else ? "off" : "on"); + } + } + // @Note: Figure out function content + for (int i = 0; i < sections_count; i++) { + section = sections + i; + if (!section->name) { + fprintf(f, "%.*s %.*s", (int)(section->name_end - section->name), section->name, (int)(section->end - section->begin), section->begin); + } + else { + if (section->is_else == !combinations[ci][section->if_clause_i - 1]) { + fprintf(f, "// %.*s %.*s", (int)(section->name_end - section->name), section->name, (int)(section->end - section->begin), section->begin); + } + } + } + } + + + + + fclose(f); + } + } +} \ No newline at end of file diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..aa1c36b --- /dev/null +++ b/test.txt @@ -0,0 +1,249 @@ + +FUNCTION void draw_triangle_PERSPECTIVE_CORRECT_INTERPOLATION_off_BILINEAR_BLEND_off (Image* dst, float *depth_buffer, Image *src, + Vec4 p0, Vec4 p1, Vec4 p2, + Vec2 tex0, Vec2 tex1, Vec2 tex2) { + float min_x1 = (float)(MIN(p0.x, MIN(p1.x, p2.x))); + float min_y1 = (float)(MIN(p0.y, MIN(p1.y, p2.y))); + float max_x1 = (float)(MAX(p0.x, MAX(p1.x, p2.x))); + float max_y1 = (float)(MAX(p0.y, MAX(p1.y, p2.y))); + I64 min_x = (I64)MAX(0, floor(min_x1)); + I64 min_y = (I64)MAX(0, floor(min_y1)); + I64 max_x = (I64)MIN(dst->x, ceil(max_x1)); + I64 max_y = (I64)MIN(dst->y, ceil(max_y1)); + + float area = edge_function(p0, p1, p2); + for (I64 y = min_y; y < max_y; y++) { + for (I64 x = min_x; x < max_x; x++) { + float edge1 = edge_function(p0, p1, { (float)x,(float)y }); + float edge2 = edge_function(p1, p2, { (float)x,(float)y }); + float edge3 = edge_function(p2, p0, { (float)x,(float)y }); + + if (edge1 >= 0 && edge2 >= 0 && edge3 >= 0) { + float w1 = edge2 / area; + float w2 = edge3 / area; + float w3 = edge1 / area; + float interpolated_z = (1.f / p0.w) * w1 + (1.f / p1.w) * w2 + (1.f / p2.w) * w3; +// PERSPECTIVE_CORRECT_INTERPOLATION + float u = tex0.x * w1 + tex1.x * w2 + tex2.x * w3; + float v = tex0.y * w1 + tex1.y * w2 + tex2.y * w3; + + // @Note: We could do: interpolated_z = 1.f / interpolated_z to get proper depth + // but why waste an instruction, the smaller the depth value the farther the object + float* depth = depth_buffer + (x + y * dst->x); + if (*depth < interpolated_z) { + *depth = interpolated_z; + u = u * (src->x - 2); + v = v * (src->y - 2); + I64 ui = (I64)(u); + I64 vi = (I64)(v); + float udiff = u - (float)ui; + float vdiff = v - (float)vi; + // Origin UV (0,0) is in bottom left + U32* pixel = src->pixels + (ui + (src->y - 1ll - vi) * src->x); + +// BILINEAR_BLEND + U32 color32 = *pixel; + + + dst->pixels[x + y * dst->x] = color32; + } + } + } + } + if (draw_rects) { + draw_rect(dst, p0.x-4, p0.y-4, 8,8, 0x00ff0000); + draw_rect(dst, p1.x-4, p1.y-4, 8,8, 0x0000ff00); + draw_rect(dst, p2.x-4, p2.y-4, 8,8, 0x000000ff); + } +} +FUNCTION void draw_triangle_PERSPECTIVE_CORRECT_INTERPOLATION_on_BILINEAR_BLEND_on (Image* dst, float *depth_buffer, Image *src, + Vec4 p0, Vec4 p1, Vec4 p2, + Vec2 tex0, Vec2 tex1, Vec2 tex2) { + float min_x1 = (float)(MIN(p0.x, MIN(p1.x, p2.x))); + float min_y1 = (float)(MIN(p0.y, MIN(p1.y, p2.y))); + float max_x1 = (float)(MAX(p0.x, MAX(p1.x, p2.x))); + float max_y1 = (float)(MAX(p0.y, MAX(p1.y, p2.y))); + I64 min_x = (I64)MAX(0, floor(min_x1)); + I64 min_y = (I64)MAX(0, floor(min_y1)); + I64 max_x = (I64)MIN(dst->x, ceil(max_x1)); + I64 max_y = (I64)MIN(dst->y, ceil(max_y1)); + + float area = edge_function(p0, p1, p2); + for (I64 y = min_y; y < max_y; y++) { + for (I64 x = min_x; x < max_x; x++) { + float edge1 = edge_function(p0, p1, { (float)x,(float)y }); + float edge2 = edge_function(p1, p2, { (float)x,(float)y }); + float edge3 = edge_function(p2, p0, { (float)x,(float)y }); + + if (edge1 >= 0 && edge2 >= 0 && edge3 >= 0) { + float w1 = edge2 / area; + float w2 = edge3 / area; + float w3 = edge1 / area; + float interpolated_z = (1.f / p0.w) * w1 + (1.f / p1.w) * w2 + (1.f / p2.w) * w3; +// PERSPECTIVE_CORRECT_INTERPOLATION + float u = tex0.x * (w1 / p0.w) + tex1.x * (w2 / p1.w) + tex2.x * (w3 / p2.w); + float v = tex0.y * (w1 / p0.w) + tex1.y * (w2 / p1.w) + tex2.y * (w3 / p2.w); + u /= interpolated_z; + v /= interpolated_z; + + // @Note: We could do: interpolated_z = 1.f / interpolated_z to get proper depth + // but why waste an instruction, the smaller the depth value the farther the object + float* depth = depth_buffer + (x + y * dst->x); + if (*depth < interpolated_z) { + *depth = interpolated_z; + u = u * (src->x - 2); + v = v * (src->y - 2); + I64 ui = (I64)(u); + I64 vi = (I64)(v); + float udiff = u - (float)ui; + float vdiff = v - (float)vi; + // Origin UV (0,0) is in bottom left + U32* pixel = src->pixels + (ui + (src->y - 1ll - vi) * src->x); + +// BILINEAR_BLEND + Vec4 pixelx1y1 = v4abgr(*pixel); + Vec4 pixelx2y1 = v4abgr(*(pixel + 1)); + Vec4 pixelx1y2 = v4abgr(*(pixel - src->x)); + Vec4 pixelx2y2 = v4abgr(*(pixel + 1 - src->x)); + + Vec4 blendx1 = lerp(pixelx1y1, pixelx2y1, udiff); + Vec4 blendx2 = lerp(pixelx1y2, pixelx2y2, udiff); + Vec4 result_color = lerp(blendx1, blendx2, vdiff); + U32 color32 = color_to_u32abgr(result_color); + + + dst->pixels[x + y * dst->x] = color32; + } + } + } + } + if (draw_rects) { + draw_rect(dst, p0.x-4, p0.y-4, 8,8, 0x00ff0000); + draw_rect(dst, p1.x-4, p1.y-4, 8,8, 0x0000ff00); + draw_rect(dst, p2.x-4, p2.y-4, 8,8, 0x000000ff); + } +} +FUNCTION void draw_triangle_PERSPECTIVE_CORRECT_INTERPOLATION_off_BILINEAR_BLEND_on (Image* dst, float *depth_buffer, Image *src, + Vec4 p0, Vec4 p1, Vec4 p2, + Vec2 tex0, Vec2 tex1, Vec2 tex2) { + float min_x1 = (float)(MIN(p0.x, MIN(p1.x, p2.x))); + float min_y1 = (float)(MIN(p0.y, MIN(p1.y, p2.y))); + float max_x1 = (float)(MAX(p0.x, MAX(p1.x, p2.x))); + float max_y1 = (float)(MAX(p0.y, MAX(p1.y, p2.y))); + I64 min_x = (I64)MAX(0, floor(min_x1)); + I64 min_y = (I64)MAX(0, floor(min_y1)); + I64 max_x = (I64)MIN(dst->x, ceil(max_x1)); + I64 max_y = (I64)MIN(dst->y, ceil(max_y1)); + + float area = edge_function(p0, p1, p2); + for (I64 y = min_y; y < max_y; y++) { + for (I64 x = min_x; x < max_x; x++) { + float edge1 = edge_function(p0, p1, { (float)x,(float)y }); + float edge2 = edge_function(p1, p2, { (float)x,(float)y }); + float edge3 = edge_function(p2, p0, { (float)x,(float)y }); + + if (edge1 >= 0 && edge2 >= 0 && edge3 >= 0) { + float w1 = edge2 / area; + float w2 = edge3 / area; + float w3 = edge1 / area; + float interpolated_z = (1.f / p0.w) * w1 + (1.f / p1.w) * w2 + (1.f / p2.w) * w3; +// PERSPECTIVE_CORRECT_INTERPOLATION + float u = tex0.x * w1 + tex1.x * w2 + tex2.x * w3; + float v = tex0.y * w1 + tex1.y * w2 + tex2.y * w3; + + // @Note: We could do: interpolated_z = 1.f / interpolated_z to get proper depth + // but why waste an instruction, the smaller the depth value the farther the object + float* depth = depth_buffer + (x + y * dst->x); + if (*depth < interpolated_z) { + *depth = interpolated_z; + u = u * (src->x - 2); + v = v * (src->y - 2); + I64 ui = (I64)(u); + I64 vi = (I64)(v); + float udiff = u - (float)ui; + float vdiff = v - (float)vi; + // Origin UV (0,0) is in bottom left + U32* pixel = src->pixels + (ui + (src->y - 1ll - vi) * src->x); + +// BILINEAR_BLEND + Vec4 pixelx1y1 = v4abgr(*pixel); + Vec4 pixelx2y1 = v4abgr(*(pixel + 1)); + Vec4 pixelx1y2 = v4abgr(*(pixel - src->x)); + Vec4 pixelx2y2 = v4abgr(*(pixel + 1 - src->x)); + + Vec4 blendx1 = lerp(pixelx1y1, pixelx2y1, udiff); + Vec4 blendx2 = lerp(pixelx1y2, pixelx2y2, udiff); + Vec4 result_color = lerp(blendx1, blendx2, vdiff); + U32 color32 = color_to_u32abgr(result_color); + + + dst->pixels[x + y * dst->x] = color32; + } + } + } + } + if (draw_rects) { + draw_rect(dst, p0.x-4, p0.y-4, 8,8, 0x00ff0000); + draw_rect(dst, p1.x-4, p1.y-4, 8,8, 0x0000ff00); + draw_rect(dst, p2.x-4, p2.y-4, 8,8, 0x000000ff); + } +} +FUNCTION void draw_triangle_PERSPECTIVE_CORRECT_INTERPOLATION_on_BILINEAR_BLEND_off (Image* dst, float *depth_buffer, Image *src, + Vec4 p0, Vec4 p1, Vec4 p2, + Vec2 tex0, Vec2 tex1, Vec2 tex2) { + float min_x1 = (float)(MIN(p0.x, MIN(p1.x, p2.x))); + float min_y1 = (float)(MIN(p0.y, MIN(p1.y, p2.y))); + float max_x1 = (float)(MAX(p0.x, MAX(p1.x, p2.x))); + float max_y1 = (float)(MAX(p0.y, MAX(p1.y, p2.y))); + I64 min_x = (I64)MAX(0, floor(min_x1)); + I64 min_y = (I64)MAX(0, floor(min_y1)); + I64 max_x = (I64)MIN(dst->x, ceil(max_x1)); + I64 max_y = (I64)MIN(dst->y, ceil(max_y1)); + + float area = edge_function(p0, p1, p2); + for (I64 y = min_y; y < max_y; y++) { + for (I64 x = min_x; x < max_x; x++) { + float edge1 = edge_function(p0, p1, { (float)x,(float)y }); + float edge2 = edge_function(p1, p2, { (float)x,(float)y }); + float edge3 = edge_function(p2, p0, { (float)x,(float)y }); + + if (edge1 >= 0 && edge2 >= 0 && edge3 >= 0) { + float w1 = edge2 / area; + float w2 = edge3 / area; + float w3 = edge1 / area; + float interpolated_z = (1.f / p0.w) * w1 + (1.f / p1.w) * w2 + (1.f / p2.w) * w3; +// PERSPECTIVE_CORRECT_INTERPOLATION + float u = tex0.x * (w1 / p0.w) + tex1.x * (w2 / p1.w) + tex2.x * (w3 / p2.w); + float v = tex0.y * (w1 / p0.w) + tex1.y * (w2 / p1.w) + tex2.y * (w3 / p2.w); + u /= interpolated_z; + v /= interpolated_z; + + // @Note: We could do: interpolated_z = 1.f / interpolated_z to get proper depth + // but why waste an instruction, the smaller the depth value the farther the object + float* depth = depth_buffer + (x + y * dst->x); + if (*depth < interpolated_z) { + *depth = interpolated_z; + u = u * (src->x - 2); + v = v * (src->y - 2); + I64 ui = (I64)(u); + I64 vi = (I64)(v); + float udiff = u - (float)ui; + float vdiff = v - (float)vi; + // Origin UV (0,0) is in bottom left + U32* pixel = src->pixels + (ui + (src->y - 1ll - vi) * src->x); + +// BILINEAR_BLEND + U32 color32 = *pixel; + + + dst->pixels[x + y * dst->x] = color32; + } + } + } + } + if (draw_rects) { + draw_rect(dst, p0.x-4, p0.y-4, 8,8, 0x00ff0000); + draw_rect(dst, p1.x-4, p1.y-4, 8,8, 0x0000ff00); + draw_rect(dst, p2.x-4, p2.y-4, 8,8, 0x000000ff); + } +} \ No newline at end of file