Compare commits

..

143 Commits

Author SHA1 Message Date
4526c514e9 Reformat 2024-09-15 04:57:28 +01:00
bf3a3ea02b Reformat 2024-09-15 04:57:10 +01:00
669064e4a7 Rename depth shader to shadow shader 2024-09-15 04:55:40 +01:00
2ffd44f1a5 Reformat 2024-09-15 04:55:04 +01:00
11dee52090 Add V2 utilities 2024-09-15 04:53:42 +01:00
53e535774d Update screenshot 2024-09-14 18:48:29 +01:00
2f086fe548 Implement hard shadows 2024-09-14 18:48:07 +01:00
db42dd3d9e Use stored inverted transposed projection model view matrix 2024-09-14 18:33:39 +01:00
b0cebb67f8 Store inverted final matrix in the shader 2024-09-14 18:33:04 +01:00
bca5dafabf Fragment shader for depth returns valid u8 values 2024-09-14 18:32:00 +01:00
0aacccdf67 Add render passes 2024-09-14 18:05:09 +01:00
7a17d129a1 Clean main.c 2024-09-14 17:37:51 +01:00
7f2c4cdd0a Update .gitignore 2024-09-14 17:37:41 +01:00
56fbde696f Remove U8Image and start implementing shadow render pass 2024-09-14 02:58:48 +01:00
88e9d3550a Image type uses V4f and add U8Image type 2024-09-14 02:46:07 +01:00
66ca20ee5b Reformat 2024-09-14 02:45:34 +01:00
ef0c29de02 Update head model 2024-09-14 02:43:56 +01:00
87f6b2b87a Tweak fragment shader to take and return V4f instead of Colour 2024-09-14 01:22:43 +01:00
390ab7c3b4 Reformatting and cleanup 2024-09-14 00:59:49 +01:00
72ab9f6aa2 Reformat main 2024-09-14 00:54:32 +01:00
15a471f911 Reformat main 2024-09-14 00:51:24 +01:00
ab8343e749 More reformatting 2024-09-14 00:22:36 +01:00
09e9f611f8 Reformat 2024-09-14 00:04:31 +01:00
fb1b8457d6 Reformat 2024-09-13 23:58:31 +01:00
ab71bbc438 Add testing depth shader 2024-09-13 23:55:47 +01:00
95895750e8 Split shaders code to separate files 2024-09-13 23:55:11 +01:00
98323da649 Reformat and reorganise before writing shadow code 2024-09-13 23:54:46 +01:00
c739864489 Update variable names 2024-09-13 12:57:15 +01:00
f45e30620f Reorganise resources 2024-09-13 12:56:26 +01:00
dcbda91730 str8_substr returns copy of the string 2024-09-13 02:57:06 +01:00
80f20a06cd Reformat 2024-09-13 02:48:45 +01:00
c12c212843 Update resources 2024-09-13 02:34:18 +01:00
1223e55db1 Pass model as a CLI arg 2024-09-13 02:33:41 +01:00
351b7e9d42 Rename variable 2024-09-06 19:00:17 +01:00
b57ec11598 Ensure intensity is clamped 2024-09-01 23:02:05 +01:00
d750c49f71 Update result screenshot 2024-09-01 22:31:00 +01:00
cf8c681474 Implement tangent normals and switch to using basic diffuse shader 2024-09-01 22:30:04 +01:00
84fbb711e1 Add function to invert 3x3 matrix 2024-09-01 22:28:11 +01:00
8c74706fdc Update head model 2024-09-01 22:26:04 +01:00
d851509321 Update result screenshot 2024-09-01 02:16:18 +01:00
e87f780dae Move vertex and fragment calculations to the shaders 2024-09-01 02:15:32 +01:00
8726c833d4 Add extra vector and matrix types and operations 2024-09-01 02:14:25 +01:00
da495d9e43 Add array access to Triangle struct 2024-09-01 02:13:53 +01:00
18e9964d33 Move TRIANGLE_VERTICES constant to constants.h 2024-09-01 02:13:02 +01:00
95d474c956 Save viewport matrix in a variable 2024-08-31 22:24:04 +01:00
41c30256b7 Remove unused variable 2024-08-31 22:23:46 +01:00
4b178de784 Update vector and matrix types 2024-08-31 22:23:18 +01:00
bb6b0e3e5d Change f32x3 to union 2024-08-31 13:46:12 +01:00
97622d0bf7 Move viewport matrix multiplication out of vertex shader 2024-08-26 22:30:05 +01:00
319bad9659 Fix barycentric coordinates calculation (#2)
Reviewed-on: #2

	* Set up for debugging
	* Replace get_barycentric_coords with different calculation
	* Update commented lines
	* Add V3_ELEM_COUNT macro
	* Change loop in barycentric coordinates calculation function
	* Update debug values
	* Simplify barycentric coordinates function
	* Remove debug lines
Co-authored-by: Abdelrahman <said.abdelrahman89@gmail.com>
Co-committed-by: Abdelrahman <said.abdelrahman89@gmail.com>
2024-08-26 17:12:44 +00:00
57dcf6457c Update includes and reorder variables 2024-08-25 02:47:13 +01:00
7671f688a3 Extra resources for debugging 2024-08-25 02:45:20 +01:00
f9851967d8 Ignore python related files 2024-08-25 02:43:54 +01:00
5dac82ffb7 Pass float vectors when calculating barycentric coordinates 2024-08-25 01:33:39 +01:00
185f44252f Flip the Y texture coordinate when loading the OBJ model 2024-08-25 01:32:54 +01:00
cee57282cb Ignore png images in resources dir 2024-08-25 00:45:46 +01:00
cdf521f184 Switch back to using object level normals 2024-08-24 23:39:19 +01:00
cab1b538e6 Update result screenshot 2024-08-24 23:18:51 +01:00
afa9bc7468 Update phong weights 2024-08-24 23:16:35 +01:00
8ccc424b06 Calculate light direction from position 2024-08-24 23:16:11 +01:00
62aa2fbc3b Change directional light to point light 2024-08-24 23:15:38 +01:00
22edc89593 Update wapp submodule 2024-08-24 19:08:02 +01:00
1f5f01d73f Switch to using tangent normals 2024-08-24 00:48:46 +01:00
798100f138 Fix Phong specular term 2024-08-24 00:39:40 +01:00
aa9c443997 Update result screenshot 2024-08-24 00:29:20 +01:00
535ead0337 Change light direction 2024-08-24 00:28:31 +01:00
6f5c094f49 Use matrices to transform light and normals 2024-08-24 00:25:41 +01:00
996597684b Add transpose and inverse matrix utilities 2024-08-24 00:25:18 +01:00
4f58fc4803 Rename create_shader to register_shader 2024-08-23 22:43:52 +01:00
c91f1b9e9b Use correct default arena init function 2024-08-23 22:31:14 +01:00
3ceafe5899 Update wapp submodule 2024-08-23 22:30:32 +01:00
5eeb7d6819 Switch to using wapp_mem_arena_init_default 2024-08-23 21:58:55 +01:00
ed43a0f286 Use GB macro to define arena size 2024-08-23 21:52:12 +01:00
bf1aa69cf5 Update wapp submodule 2024-08-23 21:51:57 +01:00
8b8855d648 Reformat 2024-08-23 21:47:01 +01:00
0b9a37a9cd Update result screenshot 2024-08-19 00:07:16 +01:00
2bea7218db Test implementation of phong shader 2024-08-19 00:05:59 +01:00
afc10fb79a Add rgb elements to V3f 2024-08-18 22:29:47 +01:00
86138f838c Add default material to model 2024-08-18 22:26:01 +01:00
ddfeba238d Add PhongMaterial struct 2024-08-18 22:19:54 +01:00
29749c834a Create FragmentData struct 2024-08-18 22:19:23 +01:00
760d9b6b9c Move shaders declaration to shaders.h 2024-08-18 22:18:12 +01:00
ec98a033eb Add macro for multiplying V3f with scalar 2024-08-18 22:17:27 +01:00
caa8c416a9 Remove extra intensity calculation 2024-08-18 21:08:59 +01:00
690eb3d317 Update shaders and ensure coordinates are within buffer bounds 2024-08-18 21:04:46 +01:00
6239be219a Ensure light is normalised 2024-08-18 17:09:13 +01:00
be7b577a0e Split obj loading and model rendering code 2024-08-18 17:04:41 +01:00
2e39780272 Add support for loading normals from a map 2024-08-18 16:47:30 +01:00
1adac24148 Remove seeding prng 2024-08-18 15:49:17 +01:00
50b8c6dd0a Implement shaders (#1)
Reviewed-on: #1

	* Start implementing shaders and move vector code to dedicated files
	* Extract shader into its own header
	* Ensure pixel coordinates are within bounds
	* Refactor shader code and implement fragment shaders
	* Create shaders
	* Reorganise code
Co-authored-by: Abdelrahman <said.abdelrahman89@gmail.com>
Co-committed-by: Abdelrahman <said.abdelrahman89@gmail.com>
2024-08-18 14:28:09 +00:00
3bbab3f624 Calculate projection matrix from eye and target 2024-08-17 22:04:33 +01:00
a3f0be461b Switch to calculating z buffer from viewport vertices 2024-08-17 20:27:58 +01:00
fdce10a85c Pass vertex as pointer 2024-08-17 20:21:30 +01:00
f77fa5694b Switch to using a viewport matrix 2024-08-17 16:27:45 +01:00
3e414881d9 Fix global light direction 2024-08-17 11:49:56 +01:00
4fa553db47 Correct head normals 2024-08-17 11:49:49 +01:00
711b9d8558 Update result screenshot 2024-08-12 00:25:24 +01:00
2f126f95ff Implement camera movement 2024-08-12 00:24:12 +01:00
4143fd7b05 Fix model normals 2024-08-12 00:23:58 +01:00
643da4e58d Implement shading based on vertex normals 2024-08-11 19:53:07 +01:00
d323c0ae14 Load vertex normals indices 2024-08-11 19:39:31 +01:00
e01c1397bc Load model normals 2024-08-11 19:33:32 +01:00
46ca6bfa10 Rename NULL_MODEL macro 2024-08-11 19:31:14 +01:00
91c4721c46 Make vector types public and remove unnecessary Vertex and TexCoord 2024-08-11 19:28:17 +01:00
3a852cbab0 Update README 2024-08-11 18:03:15 +01:00
5b55092ba2 Add screenshot of the result 2024-08-11 18:02:12 +01:00
b4210da399 Implement perspective projection 2024-08-11 17:58:38 +01:00
34d0d17b76 Add perspective projection support 2024-08-11 17:57:56 +01:00
fee04607a7 Add V4f, Mat4x4f and a basic static camera matrix 2024-08-11 17:56:31 +01:00
b6e7b9c213 Rename variable 2024-08-10 18:31:58 +01:00
e472b221c0 Remove unused varaible 2024-08-10 18:28:59 +01:00
fa2857b5d3 Increase model's poly count 2024-08-10 17:50:40 +01:00
ba88c5b27c Make face region higher poly 2024-08-10 17:38:39 +01:00
1ad2ca1a47 Fix checking barycentric coordinates 2024-08-10 17:38:13 +01:00
b111058f31 Rename IS_NULL_MODEL macro 2024-08-09 23:39:56 +01:00
26c1f17a86 Correct obj name 2024-08-09 22:26:06 +00:00
29c53de86e Update resources 2024-08-04 20:06:39 +01:00
1ab8c964bf Implement rendering model with texture 2024-08-04 20:05:45 +01:00
677627d8c9 Add utility to load P6 images 2024-08-04 20:05:30 +01:00
f024d51d85 Implement Z buffer 2024-08-04 17:53:52 +01:00
bd1326d83e Swap arg order for BUF_TYPE macro 2024-08-04 16:54:11 +01:00
4eb2ae06f1 Create generic buffer type 2024-08-04 16:52:02 +01:00
bce85c5c71 Prep for adding z buffer 2024-08-04 14:38:52 +01:00
5330e2e82c Remove unused wireframe render 2024-08-04 01:05:33 +01:00
c3c0dd732e Add shading 2024-07-31 23:05:39 +01:00
9f9cc2308d Add math library 2024-07-31 23:05:15 +01:00
5f44936dcd Add support for randomising colours 2024-07-31 22:11:11 +01:00
39b40cdf5f Silence the annoying gRPC logs 2024-07-31 22:03:34 +01:00
dc09a45085 Fix filling triangles 2024-07-28 23:09:16 +01:00
98231b582b Add triangle model for debugging 2024-07-28 23:08:57 +01:00
5a24809935 Initial implementatin of barycentric coordinates 2024-07-28 22:43:00 +01:00
6537753a3a Start implementing filled rendering 2024-07-28 20:31:48 +01:00
3f04b7b5b0 Refactor to add render types 2024-07-28 20:10:59 +01:00
c2adf07a1d Load and render head.obj 2024-07-20 19:41:51 +01:00
e266879a4f Add model loading from OBJ and wireframe rendering 2024-07-20 19:41:21 +01:00
f88b74912b Add min, max and clamp utilities 2024-07-20 19:40:59 +01:00
458b32bf5f Fix include 2024-07-20 19:40:35 +01:00
fe5c116c17 Rename LIST_TYPE_NAME to LIST_TYPE 2024-07-20 18:11:45 +01:00
2095580409 Add typed_list 2024-07-20 18:07:56 +01:00
92f8430454 Add example obj model 2024-07-20 17:24:14 +01:00
4baf837256 Move Image struct and add clear image, set pixel and draw_line functions 2024-07-20 17:23:24 +01:00
52d79817ad Add swap and absolute utilities 2024-07-20 17:21:50 +01:00
e39ee53b86 Update .gitignore 2024-07-20 17:21:14 +01:00
48 changed files with 41187 additions and 58 deletions

6
.gitignore vendored
View File

@@ -1,3 +1,7 @@
.cache
compile_commands.json
tiny
/tiny
*.pam
resources/*.png
.venv
*.py

View File

@@ -1,3 +1,6 @@
# tinyrenderer
Following along the [tinyrenderer](https://github.com/ssloy/tinyrenderer/wiki) lessons
### Result so far
![Screenshot of 3D render of a male head](output.png)

3
build
View File

@@ -1,3 +1,6 @@
#!/bin/bash
# Silence annoying gRPC logs
export GRPC_VERBOSITY="ERROR"
bear -- ./compile

View File

@@ -3,8 +3,11 @@
CC=clang
WAPP_INCLUDE="$(find intern/wapp/src -type d | xargs -I{} echo -n "-I{} ")"
WAPP_SRC="$(find intern/wapp/src -type f -name "*.c" | xargs -I{} echo -n "{} ")"
CFLAGS="-g -Isrc $WAPP_INCLUDE"
SRC="src/*.c $WAPP_SRC"
TINYRENDER_INCLUDE="$(find src -type d | xargs -I{} echo -n "-I{} ")"
TINYRENDER_SRC="$(find src -type f -name "*.c" | xargs -I{} echo -n "{} ")"
CFLAGS="-g $TINYRENDER_INCLUDE $WAPP_INCLUDE"
LIBS="-lm"
SRC="$TINYRENDER_SRC $WAPP_SRC"
OUT=tiny
(set -x ; $CC $CFLAGS $SRC -o $OUT)
(set -x ; $CC $CFLAGS $LIBS $SRC -o $OUT)

BIN
output.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 KiB

44
resources/cube.obj Normal file
View File

@@ -0,0 +1,44 @@
# Blender 4.2.0
# www.blender.org
o Cube
v 0.500000 0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
v 0.500000 0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v -0.500000 0.500000 0.500000
v -0.500000 -0.500000 0.500000
vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vt 0.875000 0.500000
vt 0.625000 0.750000
vt 0.625000 0.500000
vt 0.375000 1.000000
vt 0.375000 0.750000
vt 0.625000 0.000000
vt 0.375000 0.250000
vt 0.375000 0.000000
vt 0.375000 0.500000
vt 0.125000 0.750000
vt 0.125000 0.500000
vt 0.625000 0.250000
vt 0.875000 0.750000
vt 0.625000 1.000000
s 0
f 5/1/1 3/2/1 1/3/1
f 3/2/2 8/4/2 4/5/2
f 7/6/3 6/7/3 8/8/3
f 2/9/4 8/10/4 6/11/4
f 1/3/5 4/5/5 2/9/5
f 5/12/6 2/9/6 6/7/6
f 5/1/1 7/13/1 3/2/1
f 3/2/2 7/14/2 8/4/2
f 7/6/3 5/12/3 6/7/3
f 2/9/4 4/5/4 8/10/4
f 1/3/5 3/2/5 4/5/5
f 5/12/6 1/3/6 2/9/6

13334
resources/diablo3_pose.obj Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

78
resources/grid.pnm Normal file

File diff suppressed because one or more lines are too long

172
resources/half_sphere.obj Normal file
View File

@@ -0,0 +1,172 @@
# Blender 4.2.0
# www.blender.org
o Sphere
v 0.382683 0.923880 0.000000
v 0.707107 0.707107 0.000000
v 0.923879 0.382683 0.000000
v 1.000000 0.000000 0.000000
v 0.923879 -0.382683 0.000000
v 0.707107 -0.707107 0.000000
v 0.382683 -0.923880 0.000000
v 0.270598 0.923880 0.270598
v 0.500000 0.707107 0.500000
v 0.653281 0.382683 0.653281
v 0.707107 0.000000 0.707107
v 0.653281 -0.382683 0.653281
v 0.500000 -0.707107 0.500000
v 0.270598 -0.923880 0.270598
v 0.000000 0.923880 0.382683
v 0.000000 0.707107 0.707107
v 0.000000 0.382683 0.923879
v 0.000000 0.000000 1.000000
v 0.000000 -0.382683 0.923879
v 0.000000 -0.707107 0.707107
v 0.000000 -0.923880 0.382683
v -0.270598 0.923880 0.270598
v -0.500000 0.707107 0.500000
v -0.653281 0.382683 0.653281
v -0.707107 0.000000 0.707107
v -0.653281 -0.382683 0.653281
v -0.500000 -0.707107 0.500000
v -0.270598 -0.923880 0.270598
v -0.382683 0.923880 0.000000
v -0.707107 0.707107 0.000000
v -0.923879 0.382683 0.000000
v -1.000000 0.000000 0.000000
v -0.923879 -0.382683 0.000000
v -0.707107 -0.707107 0.000000
v -0.382683 -0.923880 0.000000
v 0.000000 -1.000000 0.000000
v 0.000000 1.000000 0.000000
vn 0.5414 -0.8103 0.2243
vn 0.9087 -0.1807 0.3764
vn 0.7862 0.5253 0.3256
vn 0.1945 0.9776 0.0805
vn 0.1945 -0.9776 0.0805
vn 0.7862 -0.5253 0.3256
vn 0.9087 0.1807 0.3764
vn 0.5414 0.8103 0.2243
vn 0.0805 0.9776 0.1945
vn 0.0805 -0.9776 0.1945
vn 0.3256 -0.5253 0.7862
vn 0.3764 0.1807 0.9087
vn 0.2243 0.8103 0.5414
vn 0.2243 -0.8103 0.5414
vn 0.3764 -0.1807 0.9087
vn 0.3256 0.5253 0.7862
vn -0.3256 -0.5253 0.7862
vn -0.3764 0.1807 0.9087
vn -0.2243 0.8103 0.5414
vn -0.2243 -0.8103 0.5414
vn -0.3764 -0.1807 0.9087
vn -0.3256 0.5253 0.7862
vn -0.0805 0.9776 0.1945
vn -0.0805 -0.9776 0.1945
vn -0.5414 -0.8103 0.2243
vn -0.9087 -0.1807 0.3764
vn -0.7862 0.5253 0.3256
vn -0.1945 0.9776 0.0805
vn -0.1945 -0.9776 0.0805
vn -0.7862 -0.5253 0.3256
vn -0.9087 0.1807 0.3764
vn -0.5414 0.8103 0.2243
vt 0.500000 0.250000
vt 0.375000 0.125000
vt 0.500000 0.125000
vt 0.500000 0.375000
vt 0.375000 0.500000
vt 0.375000 0.375000
vt 0.500000 0.625000
vt 0.375000 0.750000
vt 0.375000 0.625000
vt 0.500000 0.875000
vt 0.437500 1.000000
vt 0.375000 0.875000
vt 0.437500 0.000000
vt 0.375000 0.250000
vt 0.500000 0.500000
vt 0.500000 0.750000
vt 0.312500 1.000000
vt 0.250000 0.875000
vt 0.312500 0.000000
vt 0.250000 0.125000
vt 0.250000 0.250000
vt 0.250000 0.500000
vt 0.250000 0.750000
vt 0.250000 0.375000
vt 0.250000 0.625000
vt 0.125000 0.250000
vt 0.125000 0.625000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.125000 0.125000
vt 0.125000 0.375000
vt 0.187500 1.000000
vt 0.125000 0.875000
vt 0.187500 0.000000
vt 0.000000 0.125000
vt 0.000000 0.500000
vt 0.000000 0.375000
vt 0.000000 0.625000
vt 0.062500 1.000000
vt 0.000000 0.875000
vt 0.062500 0.000000
vt 0.000000 0.250000
vt 0.000000 0.750000
s 0
f 6/1/1 14/2/1 7/3/1
f 5/4/2 11/5/2 12/6/2
f 3/7/3 9/8/3 10/9/3
f 1/10/4 37/11/4 8/12/4
f 36/13/5 7/3/5 14/2/5
f 5/4/6 13/14/6 6/1/6
f 3/7/7 11/5/7 4/15/7
f 1/10/8 9/8/8 2/16/8
f 8/12/9 37/17/9 15/18/9
f 36/19/10 14/2/10 21/20/10
f 12/6/11 20/21/11 13/14/11
f 10/9/12 18/22/12 11/5/12
f 8/12/13 16/23/13 9/8/13
f 13/14/14 21/20/14 14/2/14
f 12/6/15 18/22/15 19/24/15
f 9/8/16 17/25/16 10/9/16
f 19/24/17 27/26/17 20/21/17
f 18/22/18 24/27/18 25/28/18
f 15/18/19 23/29/19 16/23/19
f 20/21/20 28/30/20 21/20/20
f 18/22/21 26/31/21 19/24/21
f 16/23/22 24/27/22 17/25/22
f 15/18/23 37/32/23 22/33/23
f 36/34/24 21/20/24 28/30/24
f 27/26/25 35/35/25 28/30/25
f 26/31/26 32/36/26 33/37/26
f 23/29/27 31/38/27 24/27/27
f 22/33/28 37/39/28 29/40/28
f 36/41/29 28/30/29 35/35/29
f 26/31/30 34/42/30 27/26/30
f 24/27/31 32/36/31 25/28/31
f 22/33/32 30/43/32 23/29/32
f 6/1/1 13/14/1 14/2/1
f 5/4/2 4/15/2 11/5/2
f 3/7/3 2/16/3 9/8/3
f 5/4/6 12/6/6 13/14/6
f 3/7/7 10/9/7 11/5/7
f 1/10/8 8/12/8 9/8/8
f 12/6/11 19/24/11 20/21/11
f 10/9/12 17/25/12 18/22/12
f 8/12/13 15/18/13 16/23/13
f 13/14/14 20/21/14 21/20/14
f 12/6/15 11/5/15 18/22/15
f 9/8/16 16/23/16 17/25/16
f 19/24/17 26/31/17 27/26/17
f 18/22/18 17/25/18 24/27/18
f 15/18/19 22/33/19 23/29/19
f 20/21/20 27/26/20 28/30/20
f 18/22/21 25/28/21 26/31/21
f 16/23/22 23/29/22 24/27/22
f 27/26/25 34/42/25 35/35/25
f 26/31/26 25/28/26 32/36/26
f 23/29/27 30/43/27 31/38/27
f 26/31/30 33/37/30 34/42/30
f 24/27/31 31/38/31 32/36/31
f 22/33/32 29/40/32 30/43/32

25141
resources/head.obj Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

269
resources/icosphere.obj Normal file
View File

@@ -0,0 +1,269 @@
# Blender 4.2.0
# www.blender.org
o Icosphere
v 0.000000 -1.000000 0.000000
v 0.723607 -0.447220 0.525725
v -0.276388 -0.447220 0.850649
v -0.894426 -0.447216 0.000000
v -0.276388 -0.447220 -0.850649
v 0.723607 -0.447220 -0.525725
v 0.276388 0.447220 0.850649
v -0.723607 0.447220 0.525725
v -0.723607 0.447220 -0.525725
v 0.276388 0.447220 -0.850649
v 0.894426 0.447216 0.000000
v 0.000000 1.000000 0.000000
v -0.162456 -0.850654 0.499995
v 0.425323 -0.850654 0.309011
v 0.262869 -0.525738 0.809012
v 0.850648 -0.525736 0.000000
v 0.425323 -0.850654 -0.309011
v -0.525730 -0.850652 0.000000
v -0.688189 -0.525736 0.499997
v -0.162456 -0.850654 -0.499995
v -0.688189 -0.525736 -0.499997
v 0.262869 -0.525738 -0.809012
v 0.951058 0.000000 0.309013
v 0.951058 0.000000 -0.309013
v 0.000000 0.000000 1.000000
v 0.587786 0.000000 0.809017
v -0.951058 0.000000 0.309013
v -0.587786 0.000000 0.809017
v -0.587786 0.000000 -0.809017
v -0.951058 0.000000 -0.309013
v 0.587786 0.000000 -0.809017
v 0.000000 0.000000 -1.000000
v 0.688189 0.525736 0.499997
v -0.262869 0.525738 0.809012
v -0.850648 0.525736 0.000000
v -0.262869 0.525738 -0.809012
v 0.688189 0.525736 -0.499997
v 0.162456 0.850654 0.499995
v 0.525730 0.850652 0.000000
v -0.425323 0.850654 0.309011
v -0.425323 0.850654 -0.309011
v 0.162456 0.850654 -0.499995
vn 0.1024 -0.9435 0.3151
vn 0.7002 -0.6617 0.2680
vn -0.2680 -0.9435 0.1947
vn -0.2680 -0.9435 -0.1947
vn 0.1024 -0.9435 -0.3151
vn 0.9050 -0.3304 0.2680
vn 0.0247 -0.3304 0.9435
vn -0.8897 -0.3304 0.3151
vn -0.5746 -0.3304 -0.7488
vn 0.5346 -0.3304 -0.7779
vn 0.8026 -0.1256 0.5831
vn -0.3066 -0.1256 0.9435
vn -0.9921 -0.1256 -0.0000
vn -0.3066 -0.1256 -0.9435
vn 0.8026 -0.1256 -0.5831
vn 0.4089 0.6617 0.6284
vn -0.4713 0.6617 0.5831
vn -0.7002 0.6617 -0.2680
vn 0.0385 0.6617 -0.7488
vn 0.7240 0.6617 -0.1947
vn 0.2680 0.9435 -0.1947
vn 0.4911 0.7947 -0.3568
vn 0.4089 0.6617 -0.6284
vn -0.1024 0.9435 -0.3151
vn -0.1876 0.7947 -0.5773
vn -0.4713 0.6617 -0.5831
vn -0.3313 0.9435 -0.0000
vn -0.6071 0.7947 -0.0000
vn -0.7002 0.6617 0.2680
vn -0.1024 0.9435 0.3151
vn -0.1876 0.7947 0.5773
vn 0.0385 0.6617 0.7488
vn 0.2680 0.9435 0.1947
vn 0.4911 0.7947 0.3568
vn 0.7240 0.6617 0.1947
vn 0.8897 0.3304 -0.3151
vn 0.7947 0.1876 -0.5773
vn 0.5746 0.3304 -0.7488
vn -0.0247 0.3304 -0.9435
vn -0.3035 0.1876 -0.9342
vn -0.5346 0.3304 -0.7779
vn -0.9050 0.3304 -0.2680
vn -0.9822 0.1876 -0.0000
vn -0.9050 0.3304 0.2680
vn -0.5346 0.3304 0.7779
vn -0.3035 0.1876 0.9342
vn -0.0247 0.3304 0.9435
vn 0.5746 0.3304 0.7488
vn 0.7947 0.1876 0.5773
vn 0.8897 0.3304 0.3151
vn 0.3066 0.1256 -0.9435
vn 0.3035 -0.1876 -0.9342
vn 0.0247 -0.3304 -0.9435
vn -0.8026 0.1256 -0.5831
vn -0.7947 -0.1876 -0.5773
vn -0.8897 -0.3304 -0.3151
vn -0.8026 0.1256 0.5831
vn -0.7947 -0.1876 0.5773
vn -0.5746 -0.3304 0.7488
vn 0.3066 0.1256 0.9435
vn 0.3035 -0.1876 0.9342
vn 0.5346 -0.3304 0.7779
vn 0.9921 0.1256 -0.0000
vn 0.9822 -0.1876 -0.0000
vn 0.9050 -0.3304 -0.2680
vn 0.4713 -0.6617 -0.5831
vn 0.1876 -0.7947 -0.5773
vn -0.0385 -0.6617 -0.7488
vn -0.4089 -0.6617 -0.6284
vn -0.4911 -0.7947 -0.3568
vn -0.7240 -0.6617 -0.1947
vn -0.7240 -0.6617 0.1947
vn -0.4911 -0.7947 0.3568
vn -0.4089 -0.6617 0.6284
vn 0.7002 -0.6617 -0.2680
vn 0.6071 -0.7947 -0.0000
vn 0.3313 -0.9435 -0.0000
vn -0.0385 -0.6617 0.7488
vn 0.1876 -0.7947 0.5773
vn 0.4713 -0.6617 0.5831
vt 0.181819 0.000000
vt 0.227273 0.078731
vt 0.136365 0.078731
vt 0.272728 0.157461
vt 0.318182 0.078731
vt 0.363637 0.157461
vt 0.909091 0.000000
vt 0.954545 0.078731
vt 0.863636 0.078731
vt 0.727273 0.000000
vt 0.772727 0.078731
vt 0.681818 0.078731
vt 0.545455 0.000000
vt 0.590909 0.078731
vt 0.500000 0.078731
vt 0.318182 0.236191
vt 0.090910 0.157461
vt 0.181819 0.157461
vt 0.136365 0.236191
vt 0.818182 0.157461
vt 0.909091 0.157461
vt 0.863636 0.236191
vt 0.636364 0.157461
vt 0.727273 0.157461
vt 0.681818 0.236191
vt 0.454546 0.157461
vt 0.545455 0.157461
vt 0.500000 0.236191
vt 0.227273 0.236191
vt 0.045455 0.236191
vt 0.772727 0.236191
vt 0.590909 0.236191
vt 0.409092 0.236191
vt 0.181819 0.314921
vt 0.272728 0.314921
vt 0.227273 0.393651
vt 0.000000 0.314921
vt 0.090910 0.314921
vt 0.045455 0.393651
vt 0.727273 0.314921
vt 0.818182 0.314921
vt 0.772727 0.393651
vt 0.545455 0.314921
vt 0.636364 0.314921
vt 0.590909 0.393651
vt 0.363637 0.314921
vt 0.454546 0.314921
vt 0.409092 0.393651
vt 0.500000 0.393651
vt 0.454546 0.472382
vt 0.681818 0.393651
vt 0.636364 0.472382
vt 0.863636 0.393651
vt 0.818182 0.472382
vt 0.909091 0.314921
vt 0.136365 0.393651
vt 0.090910 0.472382
vt 0.318182 0.393651
vt 0.272728 0.472382
vt 0.954545 0.236191
vt 1.000000 0.157461
vt 0.409092 0.078731
vt 0.363637 0.000000
s 0
f 1/1/1 14/2/1 13/3/1
f 2/4/2 14/5/2 16/6/2
f 1/7/3 13/8/3 18/9/3
f 1/10/4 18/11/4 20/12/4
f 1/13/5 20/14/5 17/15/5
f 2/4/6 16/6/6 23/16/6
f 3/17/7 15/18/7 25/19/7
f 4/20/8 19/21/8 27/22/8
f 5/23/9 21/24/9 29/25/9
f 6/26/10 22/27/10 31/28/10
f 2/4/11 23/16/11 26/29/11
f 3/17/12 25/19/12 28/30/12
f 4/20/13 27/22/13 30/31/13
f 5/23/14 29/25/14 32/32/14
f 6/26/15 31/28/15 24/33/15
f 7/34/16 33/35/16 38/36/16
f 8/37/17 34/38/17 40/39/17
f 9/40/18 35/41/18 41/42/18
f 10/43/19 36/44/19 42/45/19
f 11/46/20 37/47/20 39/48/20
f 39/48/21 42/49/21 12/50/21
f 39/48/22 37/47/22 42/49/22
f 37/47/23 10/43/23 42/49/23
f 42/45/24 41/51/24 12/52/24
f 42/45/25 36/44/25 41/51/25
f 36/44/26 9/40/26 41/51/26
f 41/42/27 40/53/27 12/54/27
f 41/42/28 35/41/28 40/53/28
f 35/41/29 8/55/29 40/53/29
f 40/39/30 38/56/30 12/57/30
f 40/39/31 34/38/31 38/56/31
f 34/38/32 7/34/32 38/56/32
f 38/36/33 39/58/33 12/59/33
f 38/36/34 33/35/34 39/58/34
f 33/35/35 11/46/35 39/58/35
f 24/33/36 37/47/36 11/46/36
f 24/33/37 31/28/37 37/47/37
f 31/28/38 10/43/38 37/47/38
f 32/32/39 36/44/39 10/43/39
f 32/32/40 29/25/40 36/44/40
f 29/25/41 9/40/41 36/44/41
f 30/31/42 35/41/42 9/40/42
f 30/31/43 27/22/43 35/41/43
f 27/22/44 8/55/44 35/41/44
f 28/30/45 34/38/45 8/37/45
f 28/30/46 25/19/46 34/38/46
f 25/19/47 7/34/47 34/38/47
f 26/29/48 33/35/48 7/34/48
f 26/29/49 23/16/49 33/35/49
f 23/16/50 11/46/50 33/35/50
f 31/28/51 32/32/51 10/43/51
f 31/28/52 22/27/52 32/32/52
f 22/27/53 5/23/53 32/32/53
f 29/25/54 30/31/54 9/40/54
f 29/25/55 21/24/55 30/31/55
f 21/24/56 4/20/56 30/31/56
f 27/22/57 28/60/57 8/55/57
f 27/22/58 19/21/58 28/60/58
f 19/21/59 3/61/59 28/60/59
f 25/19/60 26/29/60 7/34/60
f 25/19/61 15/18/61 26/29/61
f 15/18/62 2/4/62 26/29/62
f 23/16/63 24/33/63 11/46/63
f 23/16/64 16/6/64 24/33/64
f 16/6/65 6/26/65 24/33/65
f 17/15/66 22/27/66 6/26/66
f 17/15/67 20/14/67 22/27/67
f 20/14/68 5/23/68 22/27/68
f 20/12/69 21/24/69 5/23/69
f 20/12/70 18/11/70 21/24/70
f 18/11/71 4/20/71 21/24/71
f 18/9/72 19/21/72 4/20/72
f 18/9/73 13/8/73 19/21/73
f 13/8/74 3/61/74 19/21/74
f 16/6/75 17/62/75 6/26/75
f 16/6/76 14/5/76 17/62/76
f 14/5/77 1/63/77 17/62/77
f 13/3/78 15/18/78 3/17/78
f 13/3/79 14/2/79 15/18/79
f 14/2/80 2/4/80 15/18/80

15
resources/polygon.obj Normal file
View File

@@ -0,0 +1,15 @@
# Blender 4.2.0
# www.blender.org
o Sphere
v 0.707107 0.000000 0.707107
v 0.653281 -0.382683 0.653281
v 0.000000 0.000000 1.000000
v 0.000000 -0.382683 0.923879
vn 0.3764 -0.1807 0.9087
vt 0.375000 0.375000
vt 0.250000 0.500000
vt 0.250000 0.375000
vt 0.375000 0.500000
s 0
f 2/1/1 3/2/1 4/3/1
f 2/1/1 1/4/1 3/2/1

12
resources/triangle.obj Normal file
View File

@@ -0,0 +1,12 @@
# Blender 4.2.0
# www.blender.org
o Plane
v -0.750000 -0.750000 -0.000000
v 0.750000 -0.750000 -0.000000
v 0.000000 0.750000 0.000000
vn -0.0000 -0.0000 1.0000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 0.000000 1.000000
s 0
f 1/1/1 2/2/1 3/3/1

7
src/constants.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef CONSTANTS_H
#define CONSTANTS_H
#define DEPTH_MAX 255
#define TRIANGLE_VERTICES 3
#endif // CONSTANTS_H

93
src/img/img.c Normal file
View File

@@ -0,0 +1,93 @@
#include "img.h"
#include "aliases.h"
#include "mem_arena.h"
#include "pam.h"
#include "utils.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
internal i64 calculate_pixel_index(Buffer *buffer, u64 x, u64 y, u64 base_size);
bool _init_buffer(Arena *arena, Buffer *buffer, u64 base_size) {
if (!arena || !buffer || buffer->width == 0 || buffer->height == 0) {
return false;
}
u64 size = buffer->width * buffer->height * base_size;
buffer->buf = wapp_mem_arena_alloc(arena, size);
return buffer->buf != NULL;
}
u8 *_get_pixel(Buffer *buffer, u64 x, u64 y, u64 base_size) {
i64 idx = calculate_pixel_index(buffer, x, y, base_size);
if (idx == -1) {
idx = 0;
}
return ((u8 *)(buffer->buf)) + idx;
}
void _set_pixel(Buffer *buffer, u64 x, u64 y, void *value, u64 base_size) {
i64 idx = calculate_pixel_index(buffer, x, y, base_size);
if (idx == -1) {
return;
}
memcpy(((u8 *)(buffer->buf)) + idx, value, base_size);
return;
}
void _clear_buffer(Buffer *buffer, void *value, u64 base_size) {
for (u64 y = 0; y < buffer->height; ++y) {
for (u64 x = 0; x < buffer->width; ++x) {
_set_pixel(buffer, x, y, value, base_size);
}
}
}
void draw_line(Image *img, u64 x0, u64 y0, u64 x1, u64 y1, Colour colour) {
u64 dx = absolute((i64)x1 - (i64)x0);
u64 dy = absolute((i64)y1 - (i64)y0);
f32 t = 0.0f;
i64 x = 0;
i64 y = 0;
if (dx > dy) {
if (x0 > x1) {
swap(u64, x0, x1);
swap(u64, y0, y1);
}
for (u64 x = x0; x < x1; ++x) {
t = (f32)(x - x0) / (f32)(x1 - x0);
y = (i64)y0 + ((i64)y1 - (i64)y0) * t;
set_pixel(img, x, y, &colour);
}
} else {
if (y0 > y1) {
swap(u64, x0, x1);
swap(u64, y0, y1);
}
for (u64 y = y0; y < y1; ++y) {
t = (f32)(y - y0) / (f32)(y1 - y0);
x = (i64)x0 + ((i64)x1 - (i64)x0) * t;
set_pixel(img, x, y, &colour);
}
}
}
void save_image(const Image *img, const char *filename) {
write_p7_image(img->width, img->height, (u8 *)(img->buf), filename);
}
internal i64 calculate_pixel_index(Buffer *buffer, u64 x, u64 y,
u64 base_size) {
if (x < 0 || y < 0 || x >= buffer->width || y >= buffer->height) {
return -1;
}
return (y * buffer->width + x) * base_size;
}

41
src/img/img.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef IMG_H
#define IMG_H
#include "aliases.h"
#include "mem_arena.h"
#include <stdbool.h>
#define BUF_TYPE(TYPE, NAME) \
typedef struct NAME { \
u64 width; \
u64 height; \
TYPE *buf; \
} NAME
#define BUF_SIZE(BUF) sizeof(*((BUF)->buf))
typedef struct colour Colour;
struct colour {
u8 r;
u8 g;
u8 b;
u8 a;
};
BUF_TYPE(void, Buffer);
BUF_TYPE(Colour, Image);
BUF_TYPE(f32, Depth);
#define init_buffer(ARENA, BUF) (_init_buffer(ARENA, (Buffer *)BUF, BUF_SIZE(BUF)))
#define get_pixel(TYPE, BUF, X, Y) (*((TYPE *)(_get_pixel((Buffer *)BUF, X, Y, BUF_SIZE(BUF)))))
#define set_pixel(BUF, X, Y, VAL_PTR) (_set_pixel((Buffer *)BUF, X, Y, VAL_PTR, BUF_SIZE(BUF)))
#define clear_buffer(BUF, VAL_PTR) (_clear_buffer((Buffer *)BUF, VAL_PTR, BUF_SIZE(BUF)))
bool _init_buffer(Arena *arena, Buffer *buffer, u64 base_size);
u8 *_get_pixel(Buffer *buffer, u64 x, u64 y, u64 base_size);
void _set_pixel(Buffer *buffer, u64 x, u64 y, void *value, u64 base_size);
void _clear_buffer(Buffer *buffer, void *value, u64 base_size);
void draw_line(Image *img, u64 x0, u64 y0, u64 x1, u64 y1, Colour colour);
void save_image(const Image *img, const char *filename);
#endif // IMG_H

29
src/list/typed_list.c Normal file
View File

@@ -0,0 +1,29 @@
#include "typed_list.h"
#include "mem_arena.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
list_void *_create_list(Arena *arena, u64 size, u64 count) {
list_void *list = (list_void *)_alloc_for_list(arena, sizeof(list_void));
if (!list) {
return NULL;
}
list->items = (void *)_alloc_for_list(arena, size * count);
if (!list->items) {
munmap(list, sizeof(list_void));
return NULL;
}
memset(list->items, 0, sizeof(size * count));
list->capacity = count;
list->count = 0;
return list;
}
void *_alloc_for_list(Arena *arena, u64 size) {
return wapp_mem_arena_alloc(arena, size);
}

79
src/list/typed_list.h Normal file
View File

@@ -0,0 +1,79 @@
#ifndef TYPED_LIST_H
#define TYPED_LIST_H
#include "aliases.h"
#include "mem_arena.h"
#include <assert.h>
#include <string.h>
#define BASE_LIST_CAPACITY 1024
#define CONCAT(A, B) A##B
#define LIST_TYPE(T) CONCAT(list_, T)
#define MAKE_LIST_TYPE(T) \
typedef struct { \
T *items; \
u64 capacity; \
u64 count; \
} LIST_TYPE(T)
#define list_create(T, ARENA) \
(LIST_TYPE(T) *)_create_list(ARENA, sizeof(T), BASE_LIST_CAPACITY)
#define list_create_with_capacity(T, ARENA, CAPACITY) \
(LIST_TYPE(T) *)_create_list(ARENA, sizeof(T), CAPACITY)
#define _increase_list_capacity(T, ARENA, LP, CAP) \
do { \
u64 new_capacity = LP->capacity * 2; \
T *tmp = _alloc_for_list(ARENA, sizeof(T) * new_capacity); \
assert(tmp != NULL && "Failed to increase capacity"); \
\
memcpy(tmp, LP->items, sizeof(T) * LP->count); \
\
LP->capacity = new_capacity; \
LP->items = tmp; \
} while (0)
#define list_append(T, ARENA, LP, ITEM) \
do { \
if (LP->count + 1 >= LP->capacity) { \
_increase_list_capacity(T, ARENA, LP, LP->capacity * 2); \
} \
\
LP->items[(LP->count)++] = ITEM; \
} while (0)
#define list_remove(LP, IDX) \
do { \
LP->items[IDX] = LP->items[(LP->count)--]; \
} while (0)
#define list_pop(LP) (LP->count -= 1)
#define list_set(LP, IDX, ITEM) LP->items[IDX] = ITEM
#define list_get(LP, IDX) LP->items[IDX]
#define list_merge(T, ARENA, DST, LP1, LP2) \
do { \
u64 new_count = LP1->count + LP2->count; \
u64 capacity = \
new_count < BASE_LIST_CAPACITY ? BASE_LIST_CAPACITY : new_count * 2; \
\
DST = (LIST_TYPE(T) *)_create_list(ARENA, new_count, capacity); \
assert(DST != NULL && "Failed to allocate new list"); \
\
u64 lp1_copy_size = sizeof(T) * LP1->count; \
u64 lp2_copy_size = sizeof(T) * LP2->count; \
memcpy(DST->items, LP1->items, lp1_copy_size); \
T *dst = &(DST->items[LP1->count]); \
memcpy(dst, LP2->items, lp2_copy_size); \
DST->capacity = capacity; \
DST->count = new_count; \
} while (0)
MAKE_LIST_TYPE(void);
list_void *_create_list(Arena *arena, u64 size, u64 count);
void *_alloc_for_list(Arena *arena, u64 size);
#endif // !TYPED_LIST_H

View File

@@ -1,26 +1,6 @@
#include "aliases.h"
#include "mem_arena.h"
#include "mem_utils.h"
#include "pam.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tiny.h"
typedef struct img Image;
struct img {
u64 width;
u64 height;
u8 *buf;
};
int main(void) {
Arena *arena = NULL;
if (!wapp_mem_arena_init(&arena, 10ul * 1024ul * 1024ul * 1024ul,
WAPP_MEM_ALLOC_RESERVE, false)) {
return -1;
}
wapp_mem_arena_destroy(&arena);
return 0;
i32 main(i32 argc, char *argv[]) {
return tiny_main(argc, argv);
}

87
src/model/obj.c Normal file
View File

@@ -0,0 +1,87 @@
#include "obj.h"
#include "mem_arena.h"
#include "pam.h"
#include "typed_list.h"
#include "vec.h"
#include <stdio.h>
Model load_obj_file(Arena *arena, const char *filename, const char *diffuse, const char *tangent) {
if (!arena) {
return INVALID_MODEL;
}
FILE *fp = fopen(filename, "r");
if (!fp) {
return INVALID_MODEL;
}
Model model = (Model){
.vertices = list_create(V3f, arena),
.normals = list_create(V3f, arena),
.texture_coordinates = list_create(V2f, arena),
.triangles = list_create(Triangle, arena),
};
if (!(model.vertices) || !(model.normals) || !(model.texture_coordinates) || !(model.triangles)) {
return INVALID_MODEL;
}
char line[8192];
char identifier[8];
V3f vertex;
V3f normal;
V2f coord;
Triangle triangle;
f32 vx, vy, vz;
f32 nx, ny, nz;
f32 u, v;
u64 fp0, fp1, fp2;
u64 vn0, vn1, vn2;
u64 tx0, tx1, tx2;
while (fgets(line, 8191, fp) != NULL) {
sscanf(line, "%s", identifier);
if (strncmp(identifier, "v", 8) == 0) {
sscanf(line + 2, "%f %f %f", &vx, &vy, &vz);
vertex.x = vx;
vertex.y = vy;
vertex.z = vz;
list_append(V3f, arena, model.vertices, vertex);
} else if (strncmp(identifier, "vn", 8) == 0) {
sscanf(line + 2, "%f %f %f", &nx, &ny, &nz);
normal.x = nx;
normal.y = ny;
normal.z = nz;
list_append(V3f, arena, model.normals, normal);
} else if (strncmp(identifier, "vt", 8) == 0) {
sscanf(line + 2, "%f %f", &u, &v);
coord.u = u;
coord.v = 1.0f - v;
list_append(V2f, arena, model.texture_coordinates, coord);
} else if (strncmp(identifier, "f", 8) == 0) {
sscanf(line + 2, "%lu/%lu/%lu %lu/%lu/%lu %lu/%lu/%lu",
&fp0, &tx0, &vn0,
&fp1, &tx1, &vn1,
&fp2, &tx2, &vn2);
// OBJ indices start from 1
triangle.p0 = fp0 - 1;
triangle.p1 = fp1 - 1;
triangle.p2 = fp2 - 1;
triangle.tx0 = tx0 - 1;
triangle.tx1 = tx1 - 1;
triangle.tx2 = tx2 - 1;
triangle.n0 = vn0 - 1;
triangle.n1 = vn1 - 1;
triangle.n2 = vn2 - 1;
list_append(Triangle, arena, model.triangles, triangle);
}
}
if (diffuse) {
model.texture = load_p6_image(arena, diffuse);
}
if (tangent) {
model.normal = load_p6_image(arena, tangent);
}
return model;
}

63
src/model/obj.h Normal file
View File

@@ -0,0 +1,63 @@
#ifndef OBJ_H
#define OBJ_H
#include "aliases.h"
#include "constants.h"
#include "img.h"
#include "mem_arena.h"
#include "typed_list.h"
#include "vec.h"
#define INVALID_MODEL ((Model){0})
#define IS_INVALID_MODEL(m) (m.vertices == NULL || m.triangles == NULL)
typedef struct triangle Triangle;
struct triangle {
union {
u64 positions[TRIANGLE_VERTICES];
struct {
u64 p0;
u64 p1;
u64 p2;
};
};
union {
u64 normals[TRIANGLE_VERTICES];
struct {
u64 n0;
u64 n1;
u64 n2;
};
};
union {
u64 coordinates[TRIANGLE_VERTICES];
struct {
u64 tx0;
u64 tx1;
u64 tx2;
};
};
};
MAKE_LIST_TYPE(Triangle);
typedef struct point_light PointLight;
struct point_light {
V3f diffuse_intensity;
V3f specular_intensity;
V3f position;
};
typedef struct model Model;
struct model {
LIST_TYPE(V3f) *vertices;
LIST_TYPE(V3f) *normals;
LIST_TYPE(V2f) *texture_coordinates;
LIST_TYPE(Triangle) *triangles;
Image *texture;
Image *normal;
};
Model load_obj_file(Arena *arena, const char *filename, const char *diffuse, const char *tangent);
#endif // OBJ_H

172
src/model/render.c Normal file
View File

@@ -0,0 +1,172 @@
#include "render.h"
#include "aliases.h"
#include "constants.h"
#include "img.h"
#include "shader.h"
#include "typed_list.h"
#include "utils.h"
#include "vec.h"
#include <stdint.h>
#include <math.h>
Render g_render_passes[COUNT_RENDER_PASS] = {0};
typedef struct triangle_bbox TriangleBBox;
struct triangle_bbox {
u64 x0;
u64 y0;
u64 x1;
u64 y1;
};
internal void render_triangle(const Triangle *triangle, const Model *model, ShaderID shader,
Render *render, RenderType render_type, Colour colour);
internal void fill_triangle(Render *render, ShaderID shader, VertexData vertices[TRIANGLE_VERTICES],
Colour colour, const Model *model, RenderType type);
internal TriangleBBox get_triangle_bbox(const Image *img, V2f vertices[TRIANGLE_VERTICES]);
internal V3f get_barycentric_coords(V2f a, V2f b, V2f c, V2f p);
internal V2f get_viewport_vertex(const V3f *vertex, const Image *img);
bool init_render(Arena *arena, Render *render, u64 width, u64 height) {
render->img = (Image){.width = width, .height = height};
if (!init_buffer(arena, &(render->img))) {
return false;
}
render->depth = (Depth){.width = width, .height = height};
if (!init_buffer(arena, &(render->depth))) {
return false;
}
f32 inf = -INFINITY;
clear_buffer(&(render->depth), &inf);
return true;
}
void render_model(const Model *model, Render *render, ShaderID shader, RenderType render_type, Colour colour) {
Triangle triangle;
for (u64 i = 0; i < model->triangles->count; ++i) {
triangle = list_get(model->triangles, i);
render_triangle(&triangle, model, shader, render, render_type, colour);
}
}
internal void render_triangle(const Triangle *triangle, const Model *model, ShaderID shader,
Render *render, RenderType render_type, Colour colour) {
Image *img = &(render->img);
VertexData vertices[TRIANGLE_VERTICES];
for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) {
vertices[i].position = list_get(model->vertices, triangle->positions[i]);
vertices[i].normal = list_get(model->normals, triangle->normals[i]);
vertices[i].uv = list_get(model->texture_coordinates, triangle->coordinates[i]);
vertices[i] = run_vertex_shader(shader, &vertices[i], i, model);
}
if (render_type == RENDER_TYPE_WIREFRAME) {
// V3f v0, v1;
// u64 x0, y0, x1, y1;
// for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) {
// v0 = vertices[i];
// v1 = vertices[(i + 1) % TRIANGLE_VERTICES];
//
// draw_line(img, (u64)v0.x, (u64)v0.y, (u64)v1.x, (u64)v1.y, colour);
// }
} else if (render_type == RENDER_TYPE_FILLED || render_type == RENDER_TYPE_SHADED) {
fill_triangle(render, shader, vertices, colour, model, render_type);
}
}
internal void fill_triangle(Render *render, ShaderID shader, VertexData vertices[TRIANGLE_VERTICES],
Colour colour, const Model *model, RenderType type) {
Image *img = &(render->img);
Depth *depth = &(render->depth);
V2f vp_verts[TRIANGLE_VERTICES] = {0};
for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) {
vp_verts[i] = get_viewport_vertex(&vertices[i].position, img);
}
TriangleBBox bbox = get_triangle_bbox(img, vp_verts);
V2f point;
V3f coords;
f32 z;
f32 zbuf;
V4f shader_colour;
Colour output_colour;
FragmentResult result;
f32 intensity = 1.0f;
for (u64 y = bbox.y0; y <= bbox.y1; ++y) {
for (u64 x = bbox.x0; x <= bbox.x1; ++x) {
point = (V2f){x, y};
coords = get_barycentric_coords(vp_verts[0], vp_verts[1], vp_verts[2], point);
if (coords.x < 0.0f || coords.y < 0.0f || coords.z < 0.0f) {
continue;
}
z = 0.0f;
z += vertices[0].position.z * coords.x +
vertices[1].position.z * coords.y +
vertices[2].position.z * coords.z;
zbuf = get_pixel(f32, &(render->depth), x, y);
if (z <= zbuf) {
continue;
}
shader_colour = (V4f){.r = colour.r, .g = colour.g, .b = colour.b, .a = colour.a};
result = run_fragment_shader(shader, &coords, &shader_colour, model);
if (DISCARD_FRAGMENT(result)) {
continue;
}
output_colour = (Colour){
.r = clamp(result.colour.r, 0, UINT8_MAX),
.g = clamp(result.colour.g, 0, UINT8_MAX),
.b = clamp(result.colour.b, 0, UINT8_MAX),
.a = clamp(result.colour.a, 0, UINT8_MAX),
};
set_pixel(depth, x, y, &z);
set_pixel(img, x, y, &output_colour);
}
}
}
internal TriangleBBox get_triangle_bbox(const Image *img, V2f vertices[TRIANGLE_VERTICES]) {
f32 x0 = min(vertices[0].x, min(vertices[1].x, vertices[2].x));
f32 x1 = max(vertices[0].x, max(vertices[1].x, vertices[2].x));
f32 y0 = min(vertices[0].y, min(vertices[1].y, vertices[2].y));
f32 y1 = max(vertices[0].y, max(vertices[1].y, vertices[2].y));
return (TriangleBBox){
.x0 = x0,
.y0 = y0,
.x1 = x1,
.y1 = y1,
};
}
internal V3f get_barycentric_coords(V2f a, V2f b, V2f c, V2f p) {
V3f x_vec = V3(V3f, f32, c.x, b.x, a.x, a.x, a.x, p.x);
V3f y_vec = V3(V3f, f32, c.y, b.y, a.y, a.y, a.y, p.y);
V3f u = cross_product(x_vec, y_vec);
if (fabsf(u.z) < 1e-2) {
return (V3f){-1.0f, 1.0f, 1.0f};
}
return (V3f){1.0f - (u.x + u.y) / u.z, u.y / u.z, u.x / u.z};
}
internal V2f get_viewport_vertex(const V3f *vertex, const Image *img) {
V3f output = *vertex;
output.x = clamp(output.x, 0.0f, img->width);
output.y = clamp(output.y, 0.0f, img->height);
return (V2f){output.x, output.y};
}

42
src/model/render.h Normal file
View File

@@ -0,0 +1,42 @@
#ifndef RENDER_H
#define RENDER_H
#include "aliases.h"
#include "mem_arena.h"
#include "obj.h"
#include "shader.h"
typedef enum {
RENDER_TYPE_WIREFRAME,
RENDER_TYPE_FILLED,
RENDER_TYPE_SHADED,
COUNT_RENDER_TYPE,
} RenderType;
typedef enum {
PROJECTION_TYPE_ORTHOGRAPHIC,
PROJECTION_TYPE_PERSPECTIVE,
COUNT_PROJECTION_TYPE,
} ProjectionType;
typedef struct render Render;
struct render {
Image img;
Depth depth;
};
enum render_pass {
RENDER_PASS_SHADOW,
RENDER_PASS_MAIN,
COUNT_RENDER_PASS,
};
extern Render g_render_passes[COUNT_RENDER_PASS];
bool init_render(Arena *arena, Render *render, u64 width, u64 height);
void render_model(const Model *model, Render *render, ShaderID shader, RenderType render_type, Colour colour);
#endif // RENDER_H

View File

@@ -1,30 +0,0 @@
#include "pam.h"
#include <stdio.h>
#include <string.h>
void write_p7_image(u64 width, u64 height, u8 *buf, const char *outfile) {
FILE *fp = fopen(outfile, "wb");
if (!fp) {
fprintf(stderr, "Couldn't open file: %s\n", outfile);
return;
}
char header[4096] = {0};
snprintf(header, 4095,
"P7\n"
"WIDTH %lu\n"
"HEIGHT %lu\n"
"DEPTH 4\n"
"MAXVAL 255\n"
"TUPLTYPE RGB_ALPHA\n"
"ENDHDR\n",
width, height);
u64 hdr_size = strlen(header);
u64 buf_size = width * height * 4;
fwrite(header, hdr_size, 1, fp);
fwrite(buf, buf_size, 1, fp);
fclose(fp);
}

88
src/pam/pam.c Normal file
View File

@@ -0,0 +1,88 @@
#include "pam.h"
#include "img.h"
#include "mem_arena.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
void write_p7_image(u64 width, u64 height, u8 *buf, const char *outfile) {
FILE *fp = fopen(outfile, "wb");
if (!fp) {
fprintf(stderr, "Couldn't open file: %s\n", outfile);
return;
}
char header[4096] = {0};
snprintf(header, 4095,
"P7\n"
"WIDTH %lu\n"
"HEIGHT %lu\n"
"DEPTH 4\n"
"MAXVAL 255\n"
"TUPLTYPE RGB_ALPHA\n"
"ENDHDR\n",
width, height);
u64 hdr_size = strlen(header);
u64 buf_size = width * height * 4;
fwrite(header, hdr_size, 1, fp);
fwrite(buf, buf_size, 1, fp);
fclose(fp);
}
Image *load_p6_image(Arena *arena, const char *filename) {
Image *output = NULL;
if (!arena || !filename) {
goto RETURN_VALUE;
}
FILE *fp = fopen(filename, "rb");
if (!fp) {
goto RETURN_VALUE;
}
char type[4] = {0};
fread(type, 3, 1, fp);
if (strncmp(type, "P6\n", 4) != 0) {
goto CLOSE_FILE;
}
u64 w, h;
fscanf(fp, "%lu %lu\n", &w, &h);
u64 max;
fscanf(fp, "%lu\n", &max);
if (max > UINT8_MAX) {
goto CLOSE_FILE;
}
output = wapp_mem_arena_alloc(arena, sizeof(Image));
if (!output) {
goto CLOSE_FILE;
}
output->width = w;
output->height = h;
if (!init_buffer(arena, output)) {
goto CLOSE_FILE;
}
Colour colour = {0};
for (u64 y = 0; y < h; ++y) {
for (u64 x = 0; x < w; ++x) {
fread(&colour, 3, 1, fp);
colour.a = 255;
set_pixel(output, x, y, &colour);
}
}
CLOSE_FILE:
fclose(fp);
RETURN_VALUE:
return output;
}

View File

@@ -2,7 +2,9 @@
#define PAM_H
#include "aliases.h"
#include "img.h"
void write_p7_image(u64 width, u64 height, u8 *buf, const char *outfile);
Image *load_p6_image(Arena *arena, const char *filename);
#endif // PAM_H

135
src/shader/main_shader.c Normal file
View File

@@ -0,0 +1,135 @@
#include "main_shader.h"
#include "img.h"
#include "obj.h"
#include "render.h"
#include "shader.h"
#include "utils.h"
#include "vec.h"
VertexData general_shader_vertex(void *shader, const VertexData *vert, u8 index, const Model *model) {
Shader *shdr = (Shader *)shader;
V4f vh = V3_to_V4(vert->position);
vh.y = 0.0 - vh.y;
vh = mat4x4_mul_vec4(shdr->final, vh);
shdr->vertices[index].position = project_vec4(vh);
shdr->vertices[index].uv = vert->uv;
V4f hnorm = V3_to_V4(vert->normal);
hnorm = mat4x4_mul_vec4(shdr->proj_mv_inv_t, hnorm);
shdr->vertices[index].normal = project_vec4(hnorm);
normalise_v3(shdr->vertices[index].normal);
return shdr->vertices[index];
}
FragmentResult diffuse_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model) {
Shader *shdr = (Shader *)shader;
M3x3f pos_mat = {.rows = {shdr->vertices[0].position, shdr->vertices[1].position, shdr->vertices[2].position}};
pos_mat = mat3x3_transpose(pos_mat);
M3x3f normal_mat = {.rows = {shdr->vertices[0].normal, shdr->vertices[1].normal, shdr->vertices[2].normal}};
normal_mat = mat3x3_transpose(normal_mat);
M3x2f uvs = {shdr->vertices[0].uv, shdr->vertices[1].uv, shdr->vertices[2].uv};
M2x3f uv_mat = mat3x2_transpose(uvs);
V3f position = mat3x3_mul_vec3(pos_mat, (*barycentric));
V3f normal = mat3x3_mul_vec3(normal_mat, (*barycentric));
V2f uv = mat2x3_mul_vec3(uv_mat, (*barycentric));
V4f shadow_position_h = V3_to_V4(position);
shadow_position_h = mat4x4_mul_vec4(shdr->shadow_matrix, shadow_position_h);
V3f shadow_position = project_vec4(shadow_position_h);
// Calculate shadow
const Render *shadow_pass = &(g_render_passes[RENDER_PASS_SHADOW]);
f32 shadow_z = get_pixel(f32, &(shadow_pass->depth), shadow_position.x, shadow_position.y);
f32 shadow = 0.3f + 0.7f * (shadow_z < shadow_position.z + 10.0f);
#pragma region darboux_frame_tangent_normals
/**
* Based on the following section of the tinyrenderer tutorial
* https://github.com/ssloy/tinyrenderer/wiki/Lesson-6bis:-tangent-space-normal-mapping#starting-point-phong-shading
*/
if (model->normal) {
u64 nm_x = uv.u * model->normal->width;
u64 nm_y = uv.v * model->normal->height;
Colour pixel = get_pixel(Colour, model->normal, nm_x, nm_y);
V3f tangent = (V3f){
.x = pixel.r / 255.f * 2.f - 1.f,
.y = pixel.g / 255.f * 2.f - 1.f,
.z = pixel.b / 255.f * 2.f - 1.f,
};
V3f p0p1 = sub_v3(shdr->vertices[1].position, shdr->vertices[0].position);
V3f p0p2 = sub_v3(shdr->vertices[2].position, shdr->vertices[0].position);
M3x3f A = {.rows = {p0p1, p0p2, normal}};
M3x3f A_inv = mat3x3_inv(A);
V2f uv0 = shdr->vertices[0].uv;
V2f uv1 = shdr->vertices[1].uv;
V2f uv2 = shdr->vertices[2].uv;
V3f u_vec = {uv1.u - uv0.u, uv2.u - uv0.u, 0};
V3f v_vec = {uv1.v - uv0.v, uv2.v - uv0.v, 0};
V3f i = mat3x3_mul_vec3(A_inv, u_vec);
normalise_v3(i);
V3f j = mat3x3_mul_vec3(A_inv, v_vec);
normalise_v3(j);
M3x3f B = {.rows = {i, j, normal}};
B = mat3x3_transpose(B);
normal = mat3x3_mul_vec3(B, tangent);
normalise_v3(normal);
}
#pragma endregion darboux_frame_tangent_normals
V4f output;
if (model->texture) {
u64 tx_x = uv.u * model->texture->width;
u64 tx_y = uv.v * model->texture->height;
Colour tx = get_pixel(Colour, model->texture, tx_x, tx_y);
output = (V4f){.r = tx.r, .g = tx.g, .b = tx.b, .a = tx.a};
} else {
output = *colour;
}
f32 intensity = max(0.001f, dot_v3(normal, shdr->light_dir));
f32 r = clamp(intensity + shdr->ambient.r, 0.0f, 1.0f);
f32 g = clamp(intensity + shdr->ambient.g, 0.0f, 1.0f);
f32 b = clamp(intensity + shdr->ambient.b, 0.0f, 1.0f);
output.r *= r * shadow;
output.g *= g * shadow;
output.b *= b * shadow;
return (FragmentResult){.colour = output};
}
FragmentResult albedo_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model) {
Shader *shdr = (Shader *)shader;
M3x2f uvs = {shdr->vertices[0].uv, shdr->vertices[1].uv, shdr->vertices[2].uv};
M2x3f uv_mat = mat3x2_transpose(uvs);
V2f uv = mat2x3_mul_vec3(uv_mat, (*barycentric));
V4f output;
if (model->texture) {
u64 tx_x = uv.u * model->texture->width;
u64 tx_y = uv.v * model->texture->height;
Colour tx = get_pixel(Colour, model->texture, tx_x, tx_y);
output = (V4f){.r = tx.r, .g = tx.g, .b = tx.b, .a = tx.a};
} else {
output = *colour;
}
return (FragmentResult){.colour = output};
}

18
src/shader/main_shader.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef MAIN_SHADER_H
#define MAIN_SHADER_H
#include "shader.h"
#include "vec.h"
typedef struct shader Shader;
struct shader {
#include "shader_base.inc"
M4x4f shadow_matrix;
};
VertexData general_shader_vertex(void *shader, const VertexData *vert, u8 index, const Model *model);
FragmentResult diffuse_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model);
FragmentResult albedo_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model);
#endif // !MAIN_SHADER_H

58
src/shader/shader.c Normal file
View File

@@ -0,0 +1,58 @@
#include "shader.h"
#include "aliases.h"
#define MAX_SHADER_COUNT 2048
typedef struct shader_repo ShaderRepo;
struct shader_repo {
void *shaders[MAX_SHADER_COUNT];
VertexShader *vertex_funcs[MAX_SHADER_COUNT];
FragmentShader *fragment_funcs[MAX_SHADER_COUNT];
u64 count;
};
internal ShaderRepo g_repository = {0};
ShaderID register_shader(void *shader, VertexShader *vertex, FragmentShader *fragment) {
if (g_repository.count + 1 >= MAX_SHADER_COUNT) {
return INVALID_SHADER;
}
++(g_repository.count);
g_repository.shaders[g_repository.count] = shader;
g_repository.vertex_funcs[g_repository.count] = vertex;
g_repository.fragment_funcs[g_repository.count] = fragment;
return (ShaderID){g_repository.count};
}
VertexData run_vertex_shader(ShaderID shader, const VertexData *vert, u8 index, const Model *model) {
if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !vert || !model) {
return (VertexData){0};
}
void *shader_obj = g_repository.shaders[shader.id];
VertexShader *vertex_func = g_repository.vertex_funcs[shader.id];
if (!shader_obj || !vertex_func) {
return (VertexData){0};
}
return vertex_func(shader_obj, vert, index, model);
}
FragmentResult run_fragment_shader(ShaderID shader, const V3f *barycentric, const V4f *colour,
const Model *model) {
if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !colour) {
return DISCARDED_FRAGMENT;
}
void *shader_obj = g_repository.shaders[shader.id];
FragmentShader *fragment_func = g_repository.fragment_funcs[shader.id];
if (!shader_obj || !fragment_func) {
return DISCARDED_FRAGMENT;
}
return fragment_func(shader_obj, barycentric, colour, model);
}

39
src/shader/shader.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef SHADER_H
#define SHADER_H
#include "aliases.h"
#include "obj.h"
#include "vec.h"
typedef struct shader_id ShaderID;
struct shader_id {
u64 id;
};
typedef struct vertex_data VertexData;
struct vertex_data {
V3f position;
V3f normal;
V2f uv;
};
typedef struct fragment_result FragmentResult;
struct fragment_result {
V4f colour;
bool discard;
};
#define INVALID_SHADER ((ShaderID){0})
#define IS_INVALID_SHADER(ID) (ID.id == 0)
#define DISCARDED_FRAGMENT ((FragmentResult){.discard = true})
#define DISCARD_FRAGMENT(RESULT) (RESULT.discard)
typedef VertexData(VertexShader)(void *shader, const VertexData *vert, u8 index, const Model *model);
typedef FragmentResult(FragmentShader)(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model);
ShaderID register_shader(void *shader, VertexShader *vertex, FragmentShader *fragment);
VertexData run_vertex_shader(ShaderID shader, const VertexData *vert, u8 index, const Model *model);
FragmentResult run_fragment_shader(ShaderID shader, const V3f *barycentric, const V4f *colour, const Model *model);
#endif // SHADER_H

View File

@@ -0,0 +1,8 @@
V3f light_dir;
V3f ambient;
M4x4f proj_mv;
M4x4f proj_mv_inv_t;
M4x4f viewport;
M4x4f final;
M4x4f final_inv;
VertexData vertices[TRIANGLE_VERTICES];

86
src/shader/shaders.c Normal file
View File

@@ -0,0 +1,86 @@
#include "shaders.h"
#include "aliases.h"
#include "shadow_shader.h"
#include "main_shader.h"
#include "render.h"
#include "shader.h"
#include "vec.h"
ShaderID depth = {0};
ShaderID perspective_diffuse = {0};
ShaderID perspective_albedo = {0};
ShaderID orthographic_diffuse = {0};
ShaderID orthographic_albedo = {0};
internal ShadowShader depth_shader = {0};
internal Shader perspective = {0};
internal Shader orthographic = {0};
internal V3f g_ambient_light = {0.1f, 0.1f, 0.1f};
internal V3f g_eye = {0.2f, -0.1f, 0.5f};
internal V3f g_target = {0};
internal V3f g_up = {0.0f, 1.0f, 0.0f};
internal V3f g_light_dir = {1.0f, -1.0f, 1.0f};
internal M4x4f get_projection_matrix(ProjectionType projection_type);
void load_shaders(M4x4f vp) {
// Set up depth shader matrices
M4x4f depth_model_view = lookat(g_light_dir, g_target, g_up);
M4x4f depth_projection = projection(0.0f);
// Set up depth shader
depth_shader.proj_mv = mat4x4_mul(depth_projection, depth_model_view);
depth_shader.proj_mv_inv_t = mat4x4_inv(mat4x4_transpose(depth_shader.proj_mv));
depth_shader.viewport = vp;
depth_shader.final = mat4x4_mul(depth_shader.viewport, depth_shader.proj_mv);
depth_shader.light_dir = mat3x3_mul_vec3(depth_shader.proj_mv, g_light_dir);
normalise_v3(depth_shader.light_dir);
// Set up main shader matrices
M4x4f model_view = lookat(g_eye, g_target, g_up);
M4x4f orthographic_projection = get_projection_matrix(PROJECTION_TYPE_ORTHOGRAPHIC);
M4x4f perspective_projection = get_projection_matrix(PROJECTION_TYPE_PERSPECTIVE);
// Set up perspective shader
perspective.proj_mv = mat4x4_mul(perspective_projection, model_view);
perspective.proj_mv_inv_t = mat4x4_inv(mat4x4_transpose(perspective.proj_mv));
perspective.viewport = vp;
perspective.final = mat4x4_mul(perspective.viewport, perspective.proj_mv);
perspective.final_inv = mat4x4_inv(perspective.final);
perspective.ambient = g_ambient_light;
perspective.light_dir = mat3x3_mul_vec3(perspective.proj_mv, g_light_dir);
normalise_v3(perspective.light_dir);
perspective.shadow_matrix = mat4x4_mul(depth_shader.final, perspective.final_inv);
// Set up orthographic shader
orthographic.proj_mv = mat4x4_mul(orthographic_projection, model_view);
orthographic.proj_mv_inv_t = mat4x4_inv(mat4x4_transpose(orthographic.proj_mv));
orthographic.viewport = vp;
orthographic.final = mat4x4_mul(orthographic.viewport, orthographic.proj_mv);
orthographic.final_inv = mat4x4_inv(perspective.final);
orthographic.shadow_matrix = mat4x4_mul(depth_shader.final, orthographic.final_inv);
orthographic.light_dir = mat3x3_mul_vec3(orthographic.proj_mv, g_light_dir);
normalise_v3(orthographic.light_dir);
orthographic.ambient = g_ambient_light;
// Register shaders
depth = register_shader(&depth_shader, shadow_shader_vertex, shadow_shader_fragment);
perspective_diffuse = register_shader(&perspective, general_shader_vertex, diffuse_shader_fragment);
perspective_albedo = register_shader(&perspective, general_shader_vertex, albedo_shader_fragment);
orthographic_diffuse = register_shader(&orthographic, general_shader_vertex, diffuse_shader_fragment);
orthographic_albedo = register_shader(&orthographic, general_shader_vertex, albedo_shader_fragment);
}
internal M4x4f get_projection_matrix(ProjectionType projection_type) {
if (projection_type == PROJECTION_TYPE_PERSPECTIVE) {
V3f cam = V3(V3f, f32, g_target.x, g_target.y, g_target.z, g_eye.x, g_eye.y, g_eye.z);
normalise_v3(cam);
f32 coeff = -1.0f / magnitude_v3(cam) * 0.5f;
return projection(coeff);
}
return mat4x4_identity;
}

14
src/shader/shaders.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef SHADERS_H
#define SHADERS_H
#include "shader.h"
extern ShaderID depth;
extern ShaderID perspective_diffuse;
extern ShaderID perspective_albedo;
extern ShaderID orthographic_diffuse;
extern ShaderID orthographic_albedo;
void load_shaders(M4x4f vp);
#endif // SHADERS_H

View File

@@ -0,0 +1,30 @@
#include "shadow_shader.h"
#include "constants.h"
#include "shader.h"
#include "utils.h"
VertexData shadow_shader_vertex(void *shader, const VertexData *vert, u8 index, const Model *model) {
ShadowShader *shdr = (ShadowShader *)shader;
V4f vh = V3_to_V4(vert->position);
vh.y = 0.0 - vh.y;
vh = mat4x4_mul_vec4(shdr->final, vh);
shdr->vertices[index].position = project_vec4(vh);
return shdr->vertices[index];
}
FragmentResult shadow_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model) {
ShadowShader *shdr = (ShadowShader *)shader;
M3x3f pos_mat = {.rows = {shdr->vertices[0].position, shdr->vertices[1].position, shdr->vertices[2].position}};
pos_mat = mat3x3_transpose(pos_mat);
V3f position = mat3x3_mul_vec3(pos_mat, (*barycentric));
f32 channel = clamp(position.z + DEPTH_MAX, 0.0f, DEPTH_MAX);
V4f output = {.r = channel, .g = channel, .b = channel, .a = 255};
return (FragmentResult){.colour = output};
}

View File

@@ -0,0 +1,17 @@
#ifndef SHADOW_SHADER_H
#define SHADOW_SHADER_H
#include "constants.h"
#include "shader.h"
#include "vec.h"
typedef struct shadow_shader ShadowShader;
struct shadow_shader {
#include "shader_base.inc"
};
VertexData shadow_shader_vertex(void *shader, const VertexData *vert, u8 index, const Model *model);
FragmentResult shadow_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model);
#endif // !SHADOW_SHADER_H

97
src/str/str.c Normal file
View File

@@ -0,0 +1,97 @@
#include "str.h"
#include "aliases.h"
#include "mem_arena.h"
#include <stddef.h>
#include <string.h>
#define CAPACITY_SCALAR 4
Str8 str8(Arena *arena, char *str) {
if (!str) {
return (Str8){0};
}
u64 length = strlen(str);
Str8 output = {
.str = str,
.length = length,
.capacity = length,
};
if (arena) {
output.capacity *= CAPACITY_SCALAR;
output.str = wapp_mem_arena_alloc(arena, output.capacity);
if (!output.str) {
return (Str8){0};
}
strncpy(output.str, str, output.length);
}
return output;
}
Str8 str8_copy(Arena *arena, const Str8 *str) {
if (!arena) {
return str8_lit(str->str);
}
char *tmp = wapp_mem_arena_alloc(arena, str->capacity);
if (!tmp) {
return str8_lit(str->str);
}
strncpy(tmp, str->str, str->length);
return (Str8){
.str = tmp,
.length = str->length,
.capacity = str->capacity,
};
}
i32 str8_concat(Arena *arena, Str8 *dst, char *src) {
if (!src || !dst) {
return STR_OP_FAILED;
}
u64 src_length = strlen(src);
if (src_length + dst->length > dst->capacity) {
if (!arena) {
return STR_OP_FAILED;
}
u64 capacity = dst->capacity * CAPACITY_SCALAR + src_length;
char *str = wapp_mem_arena_alloc(arena, capacity);
if (!str) {
return STR_OP_FAILED;
}
strncpy(str, dst->str, dst->length);
strncpy(str + dst->length, src, src_length);
dst->str = str;
dst->length = dst->length + src_length;
dst->capacity = capacity;
return STR_OP_SUCEEDED;
}
strncpy(dst->str + dst->length, src, src_length);
return STR_OP_SUCEEDED;
}
Str8 str8_substr(Arena *arena, const Str8 *str, u64 start, u64 count) {
if (start > str->length || start + count > str->length) {
return (Str8){0};
}
Str8 tmp = {
.str = str->str + start,
.length = count,
.capacity = count,
};
return str8_copy(arena, &tmp);
}

26
src/str/str.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef STR_H
#define STR_H
#include "aliases.h"
#include "mem_arena.h"
enum {
STR_OP_SUCEEDED,
STR_OP_FAILED,
};
typedef struct str8 Str8;
struct str8 {
char *str;
u64 length;
u64 capacity;
};
#define str8_lit(STR) (str8(NULL, STR))
Str8 str8(Arena *arena, char *str);
Str8 str8_copy(Arena *arena, const Str8 *str);
i32 str8_concat(Arena *arena, Str8 *dst, char *src);
Str8 str8_substr(Arena *arena, const Str8 *str, u64 start, u64 count);
#endif // !STR_H

116
src/tiny/tiny.c Normal file
View File

@@ -0,0 +1,116 @@
#include "img.h"
#include "mem_arena.h"
#include "misc_utils.h"
#include "obj.h"
#include "render.h"
#include "shaders.h"
#include "str.h"
#include "vec.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#define IMAGE_DIMENSION 1200
enum {
TINY_EXIT_SUCCESS,
TINY_EXIT_MISSING_ARGS,
TINY_EXIT_OBJ_NOT_EXIST,
TINY_EXIT_ARENA_INIT_FAILED,
TINY_EXIT_RENDER_INIT_FAILED,
TINY_EXIT_MODEL_LOAD_FAILED,
};
typedef struct tiny_args TinyArgs;
struct tiny_args {
Str8 obj;
Str8 diffuse;
Str8 tangent;
};
internal TinyArgs parse_args(Arena *arena, int argc, char *argv[]);
internal i32 tinyrenderer(Arena *arena, TinyArgs args);
internal bool file_exists(const Str8 *path);
i32 tiny_main(i32 argc, char *argv[]) {
Arena *arena = NULL;
if (!wapp_mem_arena_init(&arena, GB(10))) {
return TINY_EXIT_ARENA_INIT_FAILED;
}
TinyArgs args = parse_args(arena, argc, argv);
i32 output = tinyrenderer(arena, args);
wapp_mem_arena_destroy(&arena);
return output;
}
internal TinyArgs parse_args(Arena *arena, int argc, char *argv[]) {
if (argc < 2) {
exit(TINY_EXIT_MISSING_ARGS);
}
TinyArgs args = {
.obj = str8_lit(argv[1]),
};
if (!file_exists(&args.obj)) {
exit(TINY_EXIT_OBJ_NOT_EXIST);
}
u64 substr_end = args.obj.length - 4;
args.diffuse = str8_substr(arena, &args.obj, 0, substr_end);
str8_concat(arena, &args.diffuse, "_diffuse.pnm");
if (!file_exists(&args.diffuse)) {
args.diffuse = (Str8){0};
}
args.tangent = str8_substr(arena, &args.obj, 0, substr_end);
str8_concat(arena, &args.tangent, "_tangent.pnm");
if (!file_exists(&args.tangent)) {
args.tangent = (Str8){0};
}
return args;
}
internal i32 tinyrenderer(Arena *arena, TinyArgs args) {
Colour bg = {.r = 42, .g = 45, .b = 52, .a = 255};
Colour main_colour = {.r = 14, .g = 156, .b = 208, .a = 255};
Render *shadowbuffer = &(g_render_passes[RENDER_PASS_SHADOW]);
Render *framebuffer = &(g_render_passes[RENDER_PASS_MAIN]);
if (!init_render(arena, shadowbuffer, IMAGE_DIMENSION, IMAGE_DIMENSION) ||
!init_render(arena, framebuffer, IMAGE_DIMENSION, IMAGE_DIMENSION)) {
return TINY_EXIT_RENDER_INIT_FAILED;
}
const char *diffuse = args.diffuse.length > 0 ? args.diffuse.str : NULL;
const char *tangent = args.tangent.length > 0 ? args.tangent.str : NULL;
Model obj = load_obj_file(arena, args.obj.str, diffuse, tangent);
if (IS_INVALID_MODEL(obj)) {
return TINY_EXIT_MODEL_LOAD_FAILED;
}
load_shaders(viewport(0, 0, IMAGE_DIMENSION, IMAGE_DIMENSION));
clear_buffer(&(framebuffer->img), &bg);
render_model(&obj, shadowbuffer, depth, RENDER_TYPE_SHADED, main_colour);
render_model(&obj, framebuffer, perspective_diffuse, RENDER_TYPE_SHADED, main_colour);
save_image(&(framebuffer->img), "result.pam");
return TINY_EXIT_SUCCESS;
}
internal bool file_exists(const Str8 *path) {
struct stat st;
return stat(path->str, &st) == 0;
}

8
src/tiny/tiny.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef TINY_H
#define TINY_H
#include "aliases.h"
i32 tiny_main(i32 argc, char *argv[]);
#endif // !TINY_H

10
src/utils/utils.c Normal file
View File

@@ -0,0 +1,10 @@
#include "utils.h"
#include "aliases.h"
i64 absolute(i64 value) {
if (value >= 0) {
return value;
}
return value * -1;
}

19
src/utils/utils.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef UTILS_H
#define UTILS_H
#include "aliases.h"
#define swap(T, v0, v1) \
do { \
T tmp = v0; \
v0 = v1; \
v1 = tmp; \
} while (0)
#define min(a, b) (a <= b ? a : b)
#define max(a, b) (a >= b ? a : b)
#define clamp(val, minimum, maximum) (min(maximum, max(minimum, val)))
i64 absolute(i64 value);
#endif // UTILS_H

165
src/vec/vec.c Normal file
View File

@@ -0,0 +1,165 @@
#include "vec.h"
#include "constants.h"
M4x4f lookat(V3f eye, V3f target, V3f up) {
V3f z = V3(V3f, f32, target.x, target.y, target.z, eye.x, eye.y, eye.z);
normalise_v3(z);
V3f x = cross_product(up, z);
normalise_v3(x);
V3f y = cross_product(z, x);
normalise_v3(y);
M4x4f rotation = mat4x4_identity;
rotation.row0.x = x.x;
rotation.row0.y = x.y;
rotation.row0.z = x.z;
rotation.row1.x = y.x;
rotation.row1.y = y.y;
rotation.row1.z = y.z;
rotation.row2.x = z.x;
rotation.row2.y = z.y;
rotation.row2.z = z.z;
M4x4f translation = mat4x4_identity;
translation.row0.w = -(eye.x);
translation.row1.w = -(eye.y);
translation.row2.w = -(eye.z);
return mat4x4_mul(rotation, translation);
}
M4x4f projection(f32 coeff) {
return (M4x4f){
.row0 = {1.0f, 0.0f, 0.0f, 0.0f},
.row1 = {0.0f, 1.0f, 0.0f, 0.0f},
.row2 = {0.0f, 0.0f, 1.0f, 0.0f},
.row3 = {0.0f, 0.0f, coeff, 1.0f},
};
}
M4x4f viewport(f32 x, f32 y, u64 w, u64 h) {
M4x4f output = mat4x4_identity;
f32 half_width = (f32)w * 0.5f;
f32 half_height = (f32)h * 0.5f;
f32 half_depth = (f32)DEPTH_MAX * 0.5f;
output.row0.x = half_width;
output.row0.w = x + half_width;
output.row1.y = half_height;
output.row1.w = y + half_height;
output.row2.z = output.row2.w = half_depth;
return output;
}
M3x3f mat3x3_inv(M3x3f matrix) {
M3x3f dest = {0};
float det;
float a = matrix.rows[0].elements[0], b = matrix.rows[0].elements[1],
c = matrix.rows[0].elements[2], d = matrix.rows[1].elements[0],
e = matrix.rows[1].elements[1], f = matrix.rows[1].elements[2],
g = matrix.rows[2].elements[0], h = matrix.rows[2].elements[1],
i = matrix.rows[2].elements[2];
dest.rows[0].elements[0] = e * i - f * h;
dest.rows[0].elements[1] = -(b * i - h * c);
dest.rows[0].elements[2] = b * f - e * c;
dest.rows[1].elements[0] = -(d * i - g * f);
dest.rows[1].elements[1] = a * i - c * g;
dest.rows[1].elements[2] = -(a * f - d * c);
dest.rows[2].elements[0] = d * h - g * e;
dest.rows[2].elements[1] = -(a * h - g * b);
dest.rows[2].elements[2] = a * e - b * d;
det = 1.0f / (a * dest.rows[0].elements[0] + b * dest.rows[1].elements[0] +
c * dest.rows[2].elements[0]);
dest.rows[0].elements[0] *= det;
dest.rows[0].elements[1] *= det;
dest.rows[0].elements[2] *= det;
dest.rows[1].elements[0] *= det;
dest.rows[1].elements[1] *= det;
dest.rows[1].elements[2] *= det;
dest.rows[2].elements[0] *= det;
dest.rows[2].elements[1] *= det;
dest.rows[2].elements[2] *= det;
return dest;
}
M4x4f mat4x4_inv(M4x4f matrix) {
f32 mat[4][4] = mat4x4_to_array(matrix);
f32 dest[4][4] = {0};
f32 t[6];
f32 det;
f32 a = mat[0][0], b = mat[0][1], c = mat[0][2], d = mat[0][3], e = mat[1][0],
f = mat[1][1], g = mat[1][2], h = mat[1][3], i = mat[2][0], j = mat[2][1],
k = mat[2][2], l = mat[2][3], m = mat[3][0], n = mat[3][1], o = mat[3][2],
p = mat[3][3];
t[0] = k * p - o * l;
t[1] = j * p - n * l;
t[2] = j * o - n * k;
t[3] = i * p - m * l;
t[4] = i * o - m * k;
t[5] = i * n - m * j;
dest[0][0] = f * t[0] - g * t[1] + h * t[2];
dest[1][0] = -(e * t[0] - g * t[3] + h * t[4]);
dest[2][0] = e * t[1] - f * t[3] + h * t[5];
dest[3][0] = -(e * t[2] - f * t[4] + g * t[5]);
dest[0][1] = -(b * t[0] - c * t[1] + d * t[2]);
dest[1][1] = a * t[0] - c * t[3] + d * t[4];
dest[2][1] = -(a * t[1] - b * t[3] + d * t[5]);
dest[3][1] = a * t[2] - b * t[4] + c * t[5];
t[0] = g * p - o * h;
t[1] = f * p - n * h;
t[2] = f * o - n * g;
t[3] = e * p - m * h;
t[4] = e * o - m * g;
t[5] = e * n - m * f;
dest[0][2] = b * t[0] - c * t[1] + d * t[2];
dest[1][2] = -(a * t[0] - c * t[3] + d * t[4]);
dest[2][2] = a * t[1] - b * t[3] + d * t[5];
dest[3][2] = -(a * t[2] - b * t[4] + c * t[5]);
t[0] = g * l - k * h;
t[1] = f * l - j * h;
t[2] = f * k - j * g;
t[3] = e * l - i * h;
t[4] = e * k - i * g;
t[5] = e * j - i * f;
dest[0][3] = -(b * t[0] - c * t[1] + d * t[2]);
dest[1][3] = a * t[0] - c * t[3] + d * t[4];
dest[2][3] = -(a * t[1] - b * t[3] + d * t[5]);
dest[3][3] = a * t[2] - b * t[4] + c * t[5];
det = 1.0f /
(a * dest[0][0] + b * dest[1][0] + c * dest[2][0] + d * dest[3][0]);
dest[0][0] *= det;
dest[0][1] *= det;
dest[0][2] *= det;
dest[0][3] *= det;
dest[1][0] *= det;
dest[1][1] *= det;
dest[1][2] *= det;
dest[1][3] *= det;
dest[2][0] *= det;
dest[2][1] *= det;
dest[2][2] *= det;
dest[2][3] *= det;
dest[3][0] *= det;
dest[3][1] *= det;
dest[3][2] *= det;
dest[3][3] *= det;
return array_to_mat4x4(dest);
}

340
src/vec/vec.h Normal file
View File

@@ -0,0 +1,340 @@
#ifndef VEC_H
#define VEC_H
#include "aliases.h"
#include "typed_list.h"
#include <math.h>
typedef struct i64x2 V2i;
struct i64x2 {
i64 x;
i64 y;
};
typedef struct u64x2 V2u;
struct u64x2 {
u64 x;
u64 y;
};
typedef union f32x2 V2f;
union f32x2 {
f32 elements[2];
struct {
union {
f32 x;
f32 u;
};
union {
f32 y;
f32 v;
};
};
};
typedef union f32x3 V3f;
union f32x3 {
f32 elements[3];
struct {
union {
f32 x;
f32 r;
};
union {
f32 y;
f32 g;
};
union {
f32 z;
f32 b;
};
};
};
typedef union f32x4 V4f;
union f32x4 {
f32 elements[4];
struct {
union {
f32 x;
f32 r;
};
union {
f32 y;
f32 g;
};
union {
f32 z;
f32 b;
};
union {
f32 w;
f32 a;
};
};
};
typedef struct u64x3 V3u;
struct u64x3 {
u64 x;
u64 y;
u64 z;
};
typedef union f32_3x2 M3x2f;
union f32_3x2 {
V2f rows[3];
struct {
V2f row0;
V2f row1;
V2f row2;
};
};
typedef union f32_2x3 M2x3f;
union f32_2x3 {
V3f rows[2];
struct {
V3f row0;
V3f row1;
};
};
typedef union f32_3x3 M3x3f;
union f32_3x3 {
V3f rows[3];
struct {
V3f row0;
V3f row1;
V3f row2;
};
};
typedef union f32_4x4 M4x4f;
union f32_4x4 {
V4f rows[4];
struct {
V4f row0;
V4f row1;
V4f row2;
V4f row3;
};
};
MAKE_LIST_TYPE(V3f);
MAKE_LIST_TYPE(V2f);
#define V2(T, ELEM_T, X0, Y0, X1, Y1) \
((T){(ELEM_T)X1 - (ELEM_T)X0, (ELEM_T)Y1 - (ELEM_T)Y0})
#define V3(T, ELEM_T, X0, Y0, Z0, X1, Y1, Z1) \
((T){(ELEM_T)X1 - (ELEM_T)X0, (ELEM_T)Y1 - (ELEM_T)Y0, \
(ELEM_T)Z1 - (ELEM_T)Z0})
#define V3_to_V4(V3) ((V4f){.x = V3.x, .y = V3.y, .z = V3.z, .w = 1.0f})
#define dot_v2(V1, V2) ((f32)V1.x * (f32)V2.x + (f32)V1.y * (f32)V2.y)
#define add_v2(V1, V2) \
((V2f){ \
.x = V1.x + V2.x, \
.y = V1.y + V2.y, \
})
#define sub_v2(V1, V2) \
((V2f){ \
.x = V1.x - V2.x, \
.y = V1.y - V2.y, \
})
#define mul_v2(V1, V2) \
((V2f){ \
.x = V1.x * V2.x, \
.y = V1.y * V2.y, \
})
#define num_mul_v2(V, N) ((V2f){.x = (N) * V.x, .y = (N) * V.y})
#define magnitude_v2(V) (sqrtf(dot_v2(V, V)))
#define normalise_v2(V) \
do { \
f32 magnitude = magnitude_v2(V); \
V.x /= magnitude; \
V.y /= magnitude; \
} while (0)
#define dot_v3(V1, V2) \
((f32)V1.x * (f32)V2.x + (f32)V1.y * (f32)V2.y + (f32)V1.z * (f32)V2.z)
#define add_v3(V1, V2) \
((V3f){ \
.x = V1.x + V2.x, \
.y = V1.y + V2.y, \
.z = V1.z + V2.z, \
})
#define sub_v3(V1, V2) \
((V3f){ \
.x = V1.x - V2.x, \
.y = V1.y - V2.y, \
.z = V1.z - V2.z, \
})
#define mul_v3(V1, V2) \
((V3f){ \
.x = V1.x * V2.x, \
.y = V1.y * V2.y, \
.z = V1.z * V2.z, \
})
#define num_mul_v3(V, N) ((V3f){.x = (N) * V.x, .y = (N) * V.y, .z = (N) * V.z})
#define num_div_v3(V, N) ((V3f){.x = V.x / (N), .y = V.y / (N), .z = V.z / (N)})
#define magnitude_v3(V) (sqrtf(dot_v3(V, V)))
#define normalise_v3(V) \
do { \
f32 magnitude = magnitude_v3(V); \
V.x /= magnitude; \
V.y /= magnitude; \
V.z /= magnitude; \
} while (0)
#define cross_product(V1, V2) \
((V3f){ \
.x = V1.y * V2.z - V1.z * V2.y, \
.y = V1.z * V2.x - V1.x * V2.z, \
.z = V1.x * V2.y - V1.y * V2.x, \
})
#define mat3x2_transpose(MAT) \
((M2x3f){ \
.row0 = {.x = MAT.row0.x, .y = MAT.row1.x, .z = MAT.row2.x}, \
.row1 = {.x = MAT.row0.y, .y = MAT.row1.y, .z = MAT.row2.y}, \
})
#define mat2x3_mul_vec3(MAT, V) \
((V2f){ \
.elements[0] = dot_v3(MAT.rows[0], V), \
.elements[1] = dot_v3(MAT.rows[1], V), \
})
#define mat3x3_transpose(MAT) \
((M3x3f){ \
.row0 = {.x = MAT.row0.x, .y = MAT.row1.x, .z = MAT.row2.x}, \
.row1 = {.x = MAT.row0.y, .y = MAT.row1.y, .z = MAT.row2.y}, \
.row2 = {.x = MAT.row0.z, .y = MAT.row1.z, .z = MAT.row2.z}, \
})
#define mat3x3_mul_vec3(MAT, V) \
((V3f){ \
.elements[0] = dot_v3(MAT.rows[0], V), \
.elements[1] = dot_v3(MAT.rows[1], V), \
.elements[2] = dot_v3(MAT.rows[2], V), \
})
#define mat4x4_identity \
((M4x4f){ \
.row0 = {1.0f, 0.0f, 0.0f, 0.0f}, \
.row1 = {0.0f, 1.0f, 0.0f, 0.0f}, \
.row2 = {0.0f, 0.0f, 1.0f, 0.0f}, \
.row3 = {0.0f, 0.0f, 0.0f, 1.0f}, \
})
#define mat4x4_to_array(MAT) \
((f32[4][4]){ \
{MAT.row0.x, MAT.row0.y, MAT.row0.z, MAT.row0.w}, \
{MAT.row1.x, MAT.row1.y, MAT.row1.z, MAT.row1.w}, \
{MAT.row2.x, MAT.row2.y, MAT.row2.z, MAT.row2.w}, \
{MAT.row3.x, MAT.row3.y, MAT.row3.z, MAT.row3.w}, \
})
#define array_to_mat4x4(arr) \
((M4x4f){ \
.row0 = {arr[0][0], arr[0][1], arr[0][2], arr[0][3]}, \
.row1 = {arr[1][0], arr[1][1], arr[1][2], arr[1][3]}, \
.row2 = {arr[2][0], arr[2][1], arr[2][2], arr[2][3]}, \
.row3 = {arr[3][0], arr[3][1], arr[3][2], arr[3][3]}, \
})
#define mat4x4_transpose(MAT) \
((M4x4f){ \
.row0 = {.x = MAT.row0.x, \
.y = MAT.row1.x, \
.z = MAT.row2.x, \
.w = MAT.row3.x}, \
.row1 = {.x = MAT.row0.y, \
.y = MAT.row1.y, \
.z = MAT.row2.y, \
.w = MAT.row3.y}, \
.row2 = {.x = MAT.row0.z, \
.y = MAT.row1.z, \
.z = MAT.row2.z, \
.w = MAT.row3.z}, \
.row3 = {.x = MAT.row0.w, \
.y = MAT.row1.w, \
.z = MAT.row2.w, \
.w = MAT.row3.w}, \
})
#define mat4x4_mul(MAT1, MAT2) \
((M4x4f){ \
.row0.x = MAT1.row0.x * MAT2.row0.x + MAT1.row0.y * MAT2.row1.x + \
MAT1.row0.z * MAT2.row2.x + MAT1.row0.w * MAT2.row3.x, \
.row0.y = MAT1.row0.x * MAT2.row0.y + MAT1.row0.y * MAT2.row1.y + \
MAT1.row0.z * MAT2.row2.y + MAT1.row0.w * MAT2.row3.y, \
.row0.z = MAT1.row0.x * MAT2.row0.z + MAT1.row0.y * MAT2.row1.z + \
MAT1.row0.z * MAT2.row2.z + MAT1.row0.w * MAT2.row3.z, \
.row0.w = MAT1.row0.x * MAT2.row0.w + MAT1.row0.y * MAT2.row1.w + \
MAT1.row0.z * MAT2.row2.w + MAT1.row0.w * MAT2.row3.w, \
.row1.x = MAT1.row1.x * MAT2.row0.x + MAT1.row1.y * MAT2.row1.x + \
MAT1.row1.z * MAT2.row2.x + MAT1.row1.w * MAT2.row3.x, \
.row1.y = MAT1.row1.x * MAT2.row0.y + MAT1.row1.y * MAT2.row1.y + \
MAT1.row1.z * MAT2.row2.y + MAT1.row1.w * MAT2.row3.y, \
.row1.z = MAT1.row1.x * MAT2.row0.z + MAT1.row1.y * MAT2.row1.z + \
MAT1.row1.z * MAT2.row2.z + MAT1.row1.w * MAT2.row3.z, \
.row1.w = MAT1.row1.x * MAT2.row0.w + MAT1.row1.y * MAT2.row1.w + \
MAT1.row1.z * MAT2.row2.w + MAT1.row1.w * MAT2.row3.w, \
.row2.x = MAT1.row2.x * MAT2.row0.x + MAT1.row2.y * MAT2.row1.x + \
MAT1.row2.z * MAT2.row2.x + MAT1.row2.w * MAT2.row3.x, \
.row2.y = MAT1.row2.x * MAT2.row0.y + MAT1.row2.y * MAT2.row1.y + \
MAT1.row2.z * MAT2.row2.y + MAT1.row2.w * MAT2.row3.y, \
.row2.z = MAT1.row2.x * MAT2.row0.z + MAT1.row2.y * MAT2.row1.z + \
MAT1.row2.z * MAT2.row2.z + MAT1.row2.w * MAT2.row3.z, \
.row2.w = MAT1.row2.x * MAT2.row0.w + MAT1.row2.y * MAT2.row1.w + \
MAT1.row2.z * MAT2.row2.w + MAT1.row2.w * MAT2.row3.w, \
.row3.x = MAT1.row3.x * MAT2.row0.x + MAT1.row3.y * MAT2.row1.x + \
MAT1.row3.z * MAT2.row2.x + MAT1.row3.w * MAT2.row3.x, \
.row3.y = MAT1.row3.x * MAT2.row0.y + MAT1.row3.y * MAT2.row1.y + \
MAT1.row3.z * MAT2.row2.y + MAT1.row3.w * MAT2.row3.y, \
.row3.z = MAT1.row3.x * MAT2.row0.z + MAT1.row3.y * MAT2.row1.z + \
MAT1.row3.z * MAT2.row2.z + MAT1.row3.w * MAT2.row3.z, \
.row3.w = MAT1.row3.x * MAT2.row0.w + MAT1.row3.y * MAT2.row1.w + \
MAT1.row3.z * MAT2.row2.w + MAT1.row3.w * MAT2.row3.w, \
})
#define mat4x4_mul_vec4(MAT, V) \
((V4f){ \
.x = MAT.row0.x * V.x + MAT.row0.y * V.y + MAT.row0.z * V.z + \
MAT.row0.w * V.w, \
.y = MAT.row1.x * V.x + MAT.row1.y * V.y + MAT.row1.z * V.z + \
MAT.row1.w * V.w, \
.z = MAT.row2.x * V.x + MAT.row2.y * V.y + MAT.row2.z * V.z + \
MAT.row2.w * V.w, \
.w = MAT.row3.x * V.x + MAT.row3.y * V.y + MAT.row3.z * V.z + \
MAT.row3.w * V.w, \
})
#define project_vec4(V) ((V3f){.x = V.x / V.w, .y = V.y / V.w, .z = V.z / V.w})
#define mat_access(MAT, i, j) (MAT.rows[i].elements[j])
M4x4f lookat(V3f eye, V3f target, V3f up);
M4x4f projection(f32 coeff);
M4x4f viewport(f32 x, f32 y, u64 w, u64 h);
M3x3f mat3x3_inv(M3x3f matrix);
M4x4f mat4x4_inv(M4x4f matrix);
#endif // VEC_H