├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── SlimRaster_logo.png └── src ├── SlimRaster.h ├── SlimRaster ├── app.h ├── core │ ├── base.h │ ├── init.h │ ├── string.h │ ├── text.h │ ├── time.h │ └── types.h ├── math │ ├── mat2.h │ ├── mat3.h │ ├── mat4.h │ ├── quat.h │ ├── vec2.h │ ├── vec3.h │ └── vec4.h ├── platforms │ └── win32.h ├── renderer │ ├── common.h │ ├── mesh_shaders.h │ ├── pixel_shaders.h │ └── rasterizer.h ├── scene │ ├── box.h │ ├── cube.h │ ├── curve.h │ ├── grid.h │ ├── io.h │ ├── mesh.h │ ├── primitive.h │ ├── texture.h │ └── xform.h ├── shapes │ ├── circle.h │ ├── edge.h │ ├── line.h │ ├── rect.h │ └── triangle.h └── viewport │ ├── hud.h │ ├── manipulation.h │ ├── navigation.h │ └── viewport.h ├── bmp2texture.c ├── examples ├── 1_clipping_interpolation.c ├── 1_clipping_interpolation.gif ├── 2_normal_maps.c ├── 2_normal_maps.gif ├── 3_anti_aliasing.c ├── SlimRaster.gif ├── dog.mesh ├── dog.obj ├── dog_albedo.texture ├── dog_normal.texture ├── dragon.mesh ├── floor_albedo.texture ├── floor_normal.texture ├── suzanne.mesh └── suzanne.obj └── obj2mesh.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | /cmake-build-debug/ 55 | /cmake-build-release/ 56 | /bin/ 57 | /x64/ 58 | /out/ 59 | /build/ 60 | /.idea/ 61 | /.vs/ 62 | /.vscode/ 63 | /SlimRaster.sln 64 | /SlimRaster.vcxproj 65 | /SlimRaster.vcxproj.user 66 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | 3 | project(SlimRaster_1_clipping_interpolatio) 4 | add_executable(SlimRaster_1_clipping_interpolatio WIN32 src/examples/1_clipping_interpolation.c) 5 | project(SlimRaster_2_normal_maps) 6 | add_executable(SlimRaster_2_normal_maps WIN32 src/examples/2_normal_maps.c) 7 | #project(SlimRaster_3_debug_shaders) 8 | #add_executable(SlimRaster_3_debug_shaders WIN32 src/examples/1_clipping_interpolation.c) 9 | 10 | project(obj2mesh) 11 | add_executable(obj2mesh src/obj2mesh.c) 12 | 13 | project(bmp2texture) 14 | add_executable(bmp2texture src/bmp2texture.c) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Arnon Marcus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SlimRaster_logo
2 | 3 | SlimRaster
4 | 5 | A minimalist, platform agnostic interactive real time rasterizer.
6 | Strong emphasis on simplicity, ease of use and almost no setup to get started with
7 | Written in plain C and can be complied in either C or C++
8 | 9 | This project extends [SlimEngine](https://github.com/HardCoreCodin/SlimEngine). 10 | 11 | Features: 12 | - 13 | All features of SlimEngine are available here as well.
14 | Additional features include rasterization facilities:
15 | - Pixel Shaders that feed interpolated vertex attributes 16 | - Mesh shaders for object culling using axis-aligned bounding boxes 17 | - Perspective corrected barycentric coordinates 18 | - Tangent space derivatives for adaptive texture mip-level selection 19 | - Bi-linear filtered texture sampling with auto-selected mip levels 20 | - Anti aliasing (optional SSAA) 21 | - Frustum and back face triangle culling 22 | - Frustum triangle clipping with interpolates vertex attributes 23 | SlimRaster
24 | - Normal maps with controllable strength 25 | SlimRaster
26 | 27 | * obj2mesh: Also privided is a separate CLI tool for converting `.obj` files to `.mesh` files.
28 | It is also written in plain C (so is compatible with C++)
29 | Usage: `./obj2mesh src.obj trg.mesh`
30 | - invert_winding_order : Reverses the vertex ordering (for objs exported with clockwise order)
31 | 32 | * bmp2texture: Also provided is a separate CLI tool for converting `.bmp` files to `.texture` files.
33 | It is also written in plain C (so is compatible with C++)
34 | Usage: `./bmp2texture src.bmp trg.texture`
35 | - m : Generate mip-maps
36 | - w : Wrap-around
37 | - f : Filter
38 | 39 | Architecture: 40 | - 41 | The platform layer only uses operating-system headers (no standard library used).
42 | The application layer itself has no dependencies, apart from the standard math header.
43 | It is just a library that the platform layer uses - it has no knowledge of the platform.
44 | 45 | More details on this architecture [here](https://youtu.be/Ev_TeQmus68). 46 | 47 | 48 | Usage: 49 | - 50 | The single header file variant includes everything.
51 | Otherwise, specific headers can be included from the directory of headers.
52 | The main entry point for the app needs to be defined explicitly (see [SlimApp](https://github.com/HardCoreCodin/SlimApp)).
53 | 54 | SlimRaster comes with pre-configured CMake targets for all examples.
55 | For manual builds on Windows, the typical system libraries need to be linked
56 | (winmm.lib, gdi32.lib, shell32.lib, user32.lib) and the SUBSYSTEM needs to be set to WINDOWS
57 | 58 | SlimRaster does not come with any GUI functionality at this point.
59 | Some example apps have an optional HUD (heads up display) that shows additional information.
60 | It can be toggled on or off using the`tab` key.
61 | 62 | All examples are interactive using SlimRaster's facilities having 2 interaction modes: 63 | 1. FPS navigation (WASD + mouse look + zooming)
64 | 2. DCC application (default)
65 | 66 | Double clicking the `left mouse button` anywhere within the window toggles between these 2 modes. 67 | 68 | Entering FPS mode captures the mouse movement for the window and hides the cursor.
69 | Navigation is then as in a typical first-person game (plus lateral movement and zooming):
70 | 71 | Move the `mouse` to freely look around (even if the cursor would leave the window border)
72 | Scroll the `mouse wheel` to zoom in and out (changes the field of view of the perspective)
73 | Hold `W` to move forward
74 | Hold `S` to move backward
75 | Hold `A` to move left
76 | Hold `D` to move right
77 | Hold `R` to move up
78 | Hold `F` to move down
79 | 80 | Exit this mode by double clicking the `left mouse button`. 81 | 82 | The default interaction mode is similar to a typical DCC application (i.e: Maya):
83 | The mouse is not captured to the window and the cursor is visible.
84 | Holding the `right mouse button` and dragging the mouse orbits the camera around a target.
85 | Holding the `middle mouse button` and dragging the mouse pans the camera (left, right, up and down).
86 | Scrolling the `mouse wheel` dollys the camera forward and backward.
87 | 88 | Clicking the `left mouse button` selects an object in the scene that is under the cursor.
89 | Holding the `left mouse button` while hovering an object and then dragging the mouse,
90 | moves the object parallel to the screen.
91 | 92 | Holding `alt` highlights the currently selecte object by drawing a bounding box around it.
93 | While `alt` is still held, if the cursor hovers the selected object's bounding box,
94 | mouse interaction transforms the object along the plane of the bounding box that the cursor hovers on:
95 | Holding the `left mouse button` and dragging the mouse moves the object.
96 | Holding the `right mouse button` and dragging the mouse rotates the object.
97 | Holding the `middle mouse button` and dragging the mouse scales the object.
98 | (`mouse wheel` interaction is disabled while `alt` is held)
-------------------------------------------------------------------------------- /SlimRaster_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HardCoreCodin/SlimRaster/3718a02b52844f5a66376314ff63b76eff886e56/SlimRaster_logo.png -------------------------------------------------------------------------------- /src/SlimRaster/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "./core/init.h" 4 | #include "./scene/io.h" 5 | 6 | App *app; 7 | 8 | void initApp(Defaults *defaults); 9 | 10 | void _windowRedraw() { 11 | if (!app->is_running) return; 12 | if (app->on.windowRedraw) app->on.windowRedraw(); 13 | } 14 | 15 | void _windowResize(u16 width, u16 height) { 16 | if (!app->is_running) return; 17 | updateDimensions(&app->viewport.dimensions, width, height, width); 18 | 19 | if (app->on.windowResize) app->on.windowResize(width, height); 20 | if (app->on.windowRedraw) app->on.windowRedraw(); 21 | } 22 | 23 | void _keyChanged(u8 key, bool pressed) { 24 | if (key == app->controls.key_map.ctrl) app->controls.is_pressed.ctrl = pressed; 25 | else if (key == app->controls.key_map.alt) app->controls.is_pressed.alt = pressed; 26 | else if (key == app->controls.key_map.shift) app->controls.is_pressed.shift = pressed; 27 | else if (key == app->controls.key_map.space) app->controls.is_pressed.space = pressed; 28 | else if (key == app->controls.key_map.tab) app->controls.is_pressed.tab = pressed; 29 | 30 | if (app->on.keyChanged) app->on.keyChanged(key, pressed); 31 | } 32 | 33 | void _mouseButtonDown(MouseButton *mouse_button, i32 x, i32 y) { 34 | mouse_button->is_pressed = true; 35 | mouse_button->is_handled = false; 36 | 37 | mouse_button->down_pos.x = x; 38 | mouse_button->down_pos.y = y; 39 | 40 | if (app->on.mouseButtonDown) app->on.mouseButtonDown(mouse_button); 41 | } 42 | 43 | void _mouseButtonUp(MouseButton *mouse_button, i32 x, i32 y) { 44 | mouse_button->is_pressed = false; 45 | mouse_button->is_handled = false; 46 | 47 | mouse_button->up_pos.x = x; 48 | mouse_button->up_pos.y = y; 49 | 50 | if (app->on.mouseButtonUp) app->on.mouseButtonUp(mouse_button); 51 | } 52 | 53 | void _mouseButtonDoubleClicked(MouseButton *mouse_button, i32 x, i32 y) { 54 | app->controls.mouse.double_clicked = true; 55 | mouse_button->double_click_pos.x = x; 56 | mouse_button->double_click_pos.y = y; 57 | if (app->on.mouseButtonDoubleClicked) app->on.mouseButtonDoubleClicked(mouse_button); 58 | } 59 | 60 | void _mouseWheelScrolled(f32 amount) { 61 | app->controls.mouse.wheel_scroll_amount += amount * 100; 62 | app->controls.mouse.wheel_scrolled = true; 63 | 64 | if (app->on.mouseWheelScrolled) app->on.mouseWheelScrolled(amount); 65 | } 66 | 67 | void _mousePositionSet(i32 x, i32 y) { 68 | app->controls.mouse.pos.x = x; 69 | app->controls.mouse.pos.y = y; 70 | 71 | if (app->on.mousePositionSet) app->on.mousePositionSet(x, y); 72 | } 73 | 74 | void _mouseMovementSet(i32 x, i32 y) { 75 | app->controls.mouse.movement.x = x - app->controls.mouse.pos.x; 76 | app->controls.mouse.movement.y = y - app->controls.mouse.pos.y; 77 | app->controls.mouse.moved = true; 78 | 79 | if (app->on.mouseMovementSet) app->on.mouseMovementSet(x, y); 80 | } 81 | 82 | void _mouseRawMovementSet(i32 x, i32 y) { 83 | app->controls.mouse.pos_raw_diff.x += x; 84 | app->controls.mouse.pos_raw_diff.y += y; 85 | app->controls.mouse.moved = true; 86 | 87 | if (app->on.mouseRawMovementSet) app->on.mouseRawMovementSet(x, y); 88 | } 89 | 90 | bool initAppMemory(u64 size) { 91 | if (app->memory.address) return false; 92 | 93 | void* memory_address = app->platform.getMemory(size); 94 | if (!memory_address) { 95 | app->is_running = false; 96 | return false; 97 | } 98 | 99 | initMemory(&app->memory, (u8*)memory_address, size); 100 | return true; 101 | } 102 | 103 | void* allocateAppMemory(u64 size) { 104 | void *new_memory = allocateMemory(&app->memory, size); 105 | if (new_memory) return new_memory; 106 | 107 | app->is_running = false; 108 | return null; 109 | } 110 | 111 | void initScene(Scene *scene, SceneSettings *settings, Memory *memory, Platform *platform) { 112 | scene->settings = *settings; 113 | scene->primitives = null; 114 | scene->cameras = null; 115 | scene->curves = null; 116 | scene->boxes = null; 117 | scene->grids = null; 118 | scene->meshes = null; 119 | scene->textures = null; 120 | 121 | scene->selection = (Selection*)allocateMemory(memory, sizeof(Selection)); 122 | scene->selection->object_type = scene->selection->object_id = 0; 123 | scene->selection->changed = false; 124 | 125 | if (settings->meshes && settings->mesh_files) { 126 | scene->meshes = (Mesh*)allocateMemory(memory, sizeof(Mesh) * settings->meshes); 127 | for (u32 i = 0; i < settings->meshes; i++) 128 | loadMeshFromFile(&scene->meshes[i], settings->mesh_files[i].char_ptr, platform, memory); 129 | } 130 | 131 | if (settings->cameras) { 132 | scene->cameras = (Camera*)allocateMemory(memory, sizeof(Camera) * settings->cameras); 133 | if (scene->cameras) 134 | for (u32 i = 0; i < settings->cameras; i++) 135 | initCamera(scene->cameras + i); 136 | } 137 | 138 | if (settings->primitives) { 139 | scene->primitives = (Primitive*)allocateMemory(memory, sizeof(Primitive) * settings->primitives); 140 | if (scene->primitives) 141 | for (u32 i = 0; i < settings->primitives; i++) { 142 | initPrimitive(scene->primitives + i); 143 | scene->primitives[i].id = i; 144 | } 145 | } 146 | 147 | if (settings->curves) { 148 | scene->curves = (Curve*)allocateMemory(memory, sizeof(Curve) * settings->curves); 149 | if (scene->curves) 150 | for (u32 i = 0; i < settings->curves; i++) 151 | initCurve(scene->curves + i); 152 | } 153 | 154 | if (settings->boxes) { 155 | scene->boxes = (Box*)allocateMemory(memory, sizeof(Box) * settings->boxes); 156 | if (scene->boxes) 157 | for (u32 i = 0; i < settings->boxes; i++) 158 | initBox(scene->boxes + i); 159 | } 160 | 161 | if (settings->grids) { 162 | scene->grids = (Grid*)allocateMemory(memory, sizeof(Grid) * settings->grids); 163 | if (scene->grids) 164 | for (u32 i = 0; i < settings->grids; i++) 165 | initGrid(scene->grids + i, 3, 3); 166 | } 167 | 168 | if (settings->lights) { 169 | Light *light = scene->lights = (Light*)allocateMemory(memory, sizeof(Light) * settings->lights); 170 | for (u32 i = 0; i < settings->lights; i++, light++) { 171 | for (u8 c = 0; c < 3; c++) { 172 | light->position_or_direction.components[c] = 0; 173 | light->color.components[c] = 1; 174 | light->attenuation.components[c] = 1; 175 | } 176 | light->intensity = 1; 177 | light->is_directional = false; 178 | } 179 | } 180 | 181 | if (settings->materials) { 182 | Material *material = scene->materials = (Material*)allocateMemory(memory, sizeof(Material) * settings->materials); 183 | for (u32 i = 0; i < settings->materials; i++, material++) 184 | initMaterial(material); 185 | } 186 | 187 | if (settings->textures && settings->texture_files) { 188 | scene->textures = (Texture*)allocateMemory(memory, sizeof(Texture) * settings->textures); 189 | for (u32 i = 0; i < settings->textures; i++) 190 | loadTextureFromFile(&scene->textures[i], settings->texture_files[i].char_ptr, platform, memory); 191 | } 192 | 193 | scene->last_io_ticks = 0; 194 | scene->last_io_is_save = false; 195 | } 196 | 197 | 198 | void _initApp(Defaults *defaults, u32* window_content) { 199 | app->window_content = window_content; 200 | 201 | app->is_running = true; 202 | app->user_data = null; 203 | app->memory.address = null; 204 | 205 | app->on.sceneReady = null; 206 | app->on.viewportReady = null; 207 | app->on.windowRedraw = null; 208 | app->on.keyChanged = null; 209 | app->on.mouseButtonUp = null; 210 | app->on.mouseButtonDown = null; 211 | app->on.mouseButtonDoubleClicked = null; 212 | app->on.mouseWheelScrolled = null; 213 | app->on.mousePositionSet = null; 214 | app->on.mouseMovementSet = null; 215 | app->on.mouseRawMovementSet = null; 216 | 217 | defaults->title = (char*)""; 218 | defaults->width = 480; 219 | defaults->height = 360; 220 | defaults->additional_memory_size = 0; 221 | 222 | SceneSettings *scene_settings = &defaults->settings.scene; 223 | ViewportSettings *viewport_settings = &defaults->settings.viewport; 224 | NavigationSettings *navigation_settings = &defaults->settings.navigation; 225 | 226 | setDefaultSceneSettings(scene_settings); 227 | setDefaultViewportSettings(viewport_settings); 228 | setDefaultNavigationSettings(navigation_settings); 229 | 230 | initTime(&app->time, app->platform.getTicks, app->platform.ticks_per_second); 231 | initMouse(&app->controls.mouse); 232 | initApp(defaults); 233 | 234 | u64 memory_size = sizeof(Selection) + defaults->additional_memory_size; 235 | memory_size += scene_settings->primitives * sizeof(Primitive); 236 | memory_size += scene_settings->textures * sizeof(Texture); 237 | memory_size += scene_settings->meshes * sizeof(Mesh); 238 | memory_size += scene_settings->curves * sizeof(Curve); 239 | memory_size += scene_settings->boxes * sizeof(Box); 240 | memory_size += scene_settings->grids * sizeof(Grid); 241 | memory_size += scene_settings->cameras * sizeof(Camera); 242 | memory_size += scene_settings->materials * sizeof(Material); 243 | memory_size += scene_settings->lights * sizeof(Light); 244 | memory_size += viewport_settings->hud_line_count * sizeof(HUDLine); 245 | 246 | if (scene_settings->textures && 247 | scene_settings->texture_files) 248 | for (u32 i = 0; i < scene_settings->textures; i++) 249 | memory_size += getTextureMemorySize(scene_settings->texture_files[i].char_ptr, &app->platform); 250 | 251 | u32 max_triangle_count = CUBE__TRIANGLE_COUNT; 252 | u32 max_vertex_count = CUBE__VERTEX_COUNT; 253 | u32 max_normal_count = CUBE__NORMAL_COUNT; 254 | if (scene_settings->meshes && scene_settings->mesh_files) { 255 | Mesh mesh; 256 | for (u32 i = 0; i < scene_settings->meshes; i++) { 257 | memory_size += getMeshMemorySize(&mesh, scene_settings->mesh_files[i].char_ptr, &app->platform); 258 | if (mesh.triangle_count > max_triangle_count) max_triangle_count = mesh.triangle_count; 259 | if (mesh.vertex_count > max_vertex_count) max_vertex_count = mesh.vertex_count; 260 | if (mesh.normals_count > max_normal_count) max_normal_count = mesh.normals_count; 261 | } 262 | } 263 | 264 | memory_size += max_vertex_count * (sizeof(vec3) + sizeof(vec4) + 1); 265 | memory_size += max_normal_count * sizeof(vec3); 266 | memory_size += FRAME_BUFFER_MEMORY_SIZE; 267 | 268 | initAppMemory(memory_size); 269 | 270 | PixelQuad *pixels = (PixelQuad*)allocateAppMemory(FRAME_BUFFER_MEMORY_SIZE); 271 | initRasterizer(&app->rasterizer, max_vertex_count, max_normal_count, &app->memory); 272 | initScene(&app->scene, scene_settings, &app->memory, &app->platform); 273 | if (app->on.sceneReady) app->on.sceneReady(&app->scene); 274 | 275 | if (viewport_settings->hud_line_count) 276 | viewport_settings->hud_lines = (HUDLine*)allocateAppMemory(viewport_settings->hud_line_count * sizeof(HUDLine)); 277 | 278 | initViewport(&app->viewport, viewport_settings, navigation_settings, app->scene.cameras, pixels); 279 | if (app->on.viewportReady) app->on.viewportReady(&app->viewport); 280 | } 281 | 282 | #ifdef __linux__ 283 | //linux code goes here 284 | #elif _WIN32 285 | #include "./platforms/win32.h" 286 | #endif -------------------------------------------------------------------------------- /src/SlimRaster/core/init.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "./types.h" 4 | #include "../renderer/mesh_shaders.h" 5 | #include "../renderer/pixel_shaders.h" 6 | #include "../scene/cube.h" 7 | 8 | void initNumberString(NumberString *number_string) { 9 | number_string->string.char_ptr = number_string->_buffer; 10 | number_string->string.length = 1; 11 | number_string->_buffer[12] = 0; 12 | for (u8 i = 0; i < 12; i++) 13 | number_string->_buffer[i] = ' '; 14 | } 15 | 16 | void initMouse(Mouse *mouse) { 17 | mouse->is_captured = false; 18 | 19 | mouse->moved = false; 20 | mouse->move_handled = false; 21 | 22 | mouse->double_clicked = false; 23 | mouse->double_clicked_handled = false; 24 | 25 | mouse->wheel_scrolled = false; 26 | mouse->wheel_scroll_amount = 0; 27 | mouse->wheel_scroll_handled = false; 28 | 29 | mouse->pos.x = 0; 30 | mouse->pos.y = 0; 31 | mouse->pos_raw_diff.x = 0; 32 | mouse->pos_raw_diff.y = 0; 33 | mouse->raw_movement_handled = false; 34 | 35 | mouse->middle_button.is_pressed = false; 36 | mouse->middle_button.is_handled = false; 37 | mouse->middle_button.up_pos.x = 0; 38 | mouse->middle_button.down_pos.x = 0; 39 | 40 | mouse->right_button.is_pressed = false; 41 | mouse->right_button.is_handled = false; 42 | mouse->right_button.up_pos.x = 0; 43 | mouse->right_button.down_pos.x = 0; 44 | 45 | mouse->left_button.is_pressed = false; 46 | mouse->left_button.is_handled = false; 47 | mouse->left_button.up_pos.x = 0; 48 | mouse->left_button.down_pos.x = 0; 49 | } 50 | 51 | void initTimer(Timer *timer, GetTicks getTicks, Ticks *ticks) { 52 | timer->getTicks = getTicks; 53 | timer->ticks = ticks; 54 | 55 | timer->delta_time = 0; 56 | timer->ticks_before = 0; 57 | timer->ticks_after = 0; 58 | timer->ticks_diff = 0; 59 | 60 | timer->accumulated_ticks = 0; 61 | timer->accumulated_frame_count = 0; 62 | 63 | timer->ticks_of_last_report = 0; 64 | 65 | timer->seconds = 0; 66 | timer->milliseconds = 0; 67 | timer->microseconds = 0; 68 | timer->nanoseconds = 0; 69 | 70 | timer->average_frames_per_tick = 0; 71 | timer->average_ticks_per_frame = 0; 72 | timer->average_frames_per_second = 0; 73 | timer->average_milliseconds_per_frame = 0; 74 | timer->average_microseconds_per_frame = 0; 75 | timer->average_nanoseconds_per_frame = 0; 76 | } 77 | 78 | void initTime(Time *time, GetTicks getTicks, u64 ticks_per_second) { 79 | time->getTicks = getTicks; 80 | time->ticks.per_second = ticks_per_second; 81 | 82 | time->ticks.per_tick.seconds = 1 / (f64)(time->ticks.per_second); 83 | time->ticks.per_tick.milliseconds = 1000 / (f64)(time->ticks.per_second); 84 | time->ticks.per_tick.microseconds = 1000000 / (f64)(time->ticks.per_second); 85 | time->ticks.per_tick.nanoseconds = 1000000000 / (f64)(time->ticks.per_second); 86 | 87 | initTimer(&time->timers.update, getTicks, &time->ticks); 88 | initTimer(&time->timers.render, getTicks, &time->ticks); 89 | initTimer(&time->timers.aux, getTicks, &time->ticks); 90 | 91 | time->timers.update.ticks_before = time->timers.update.ticks_of_last_report = getTicks(); 92 | } 93 | 94 | void initXform3(xform3 *xform) { 95 | mat3 I; 96 | I.X.x = 1; I.Y.x = 0; I.Z.x = 0; 97 | I.X.y = 0; I.Y.y = 1; I.Z.y = 0; 98 | I.X.z = 0; I.Y.z = 0; I.Z.z = 1; 99 | xform->matrix = xform->yaw_matrix = xform->pitch_matrix = xform->roll_matrix = xform->rotation_matrix = xform->rotation_matrix_inverted = I; 100 | xform->right_direction = &xform->rotation_matrix.X; 101 | xform->up_direction = &xform->rotation_matrix.Y; 102 | xform->forward_direction = &xform->rotation_matrix.Z; 103 | xform->scale.x = 1; 104 | xform->scale.y = 1; 105 | xform->scale.z = 1; 106 | xform->position.x = 0; 107 | xform->position.y = 0; 108 | xform->position.z = 0; 109 | xform->rotation.axis.x = 0; 110 | xform->rotation.axis.y = 0; 111 | xform->rotation.axis.z = 0; 112 | xform->rotation.amount = 1; 113 | xform->rotation_inverted = xform->rotation; 114 | } 115 | 116 | void initCamera(Camera* camera) { 117 | camera->focal_length = camera->zoom = CAMERA_DEFAULT__FOCAL_LENGTH; 118 | camera->target_distance = CAMERA_DEFAULT__TARGET_DISTANCE; 119 | camera->dolly = 0; 120 | camera->current_velocity.x = 0; 121 | camera->current_velocity.y = 0; 122 | camera->current_velocity.z = 0; 123 | initXform3(&camera->transform); 124 | } 125 | 126 | void initBox(Box *box) { 127 | box->vertices.corners.front_top_left.x = -1; 128 | box->vertices.corners.back_top_left.x = -1; 129 | box->vertices.corners.front_bottom_left.x = -1; 130 | box->vertices.corners.back_bottom_left.x = -1; 131 | 132 | box->vertices.corners.front_top_right.x = 1; 133 | box->vertices.corners.back_top_right.x = 1; 134 | box->vertices.corners.front_bottom_right.x = 1; 135 | box->vertices.corners.back_bottom_right.x = 1; 136 | 137 | 138 | box->vertices.corners.front_bottom_left.y = -1; 139 | box->vertices.corners.front_bottom_right.y = -1; 140 | box->vertices.corners.back_bottom_left.y = -1; 141 | box->vertices.corners.back_bottom_right.y = -1; 142 | 143 | box->vertices.corners.front_top_left.y = 1; 144 | box->vertices.corners.front_top_right.y = 1; 145 | box->vertices.corners.back_top_left.y = 1; 146 | box->vertices.corners.back_top_right.y = 1; 147 | 148 | 149 | box->vertices.corners.front_top_left.z = 1; 150 | box->vertices.corners.front_top_right.z = 1; 151 | box->vertices.corners.front_bottom_left.z = 1; 152 | box->vertices.corners.front_bottom_right.z = 1; 153 | 154 | box->vertices.corners.back_top_left.z = -1; 155 | box->vertices.corners.back_top_right.z = -1; 156 | box->vertices.corners.back_bottom_left.z = -1; 157 | box->vertices.corners.back_bottom_right.z = -1; 158 | 159 | setBoxEdgesFromVertices(&box->edges, &box->vertices); 160 | } 161 | 162 | void initHUD(HUD *hud, HUDLine *lines, u32 line_count, f32 line_height, enum ColorID default_color, i32 position_x, i32 position_y) { 163 | hud->lines = lines; 164 | hud->line_count = line_count; 165 | hud->line_height = line_height; 166 | hud->position.x = position_x; 167 | hud->position.y = position_y; 168 | 169 | if (lines) { 170 | HUDLine *line = lines; 171 | for (u32 i = 0; i < line_count; i++, line++) { 172 | line->use_alternate = null; 173 | line->invert_alternate_use = false; 174 | line->title_color = line->value_color = line->alternate_value_color = default_color; 175 | initNumberString(&line->value); 176 | line->title.char_ptr = line->alternate_value.char_ptr = (char*)(""); 177 | line->title.length = line->alternate_value.length = 0; 178 | } 179 | } 180 | } 181 | 182 | void setDefaultNavigationSettings(NavigationSettings *settings) { 183 | settings->max_velocity = NAVIGATION_DEFAULT__MAX_VELOCITY; 184 | settings->acceleration = NAVIGATION_DEFAULT__ACCELERATION; 185 | settings->speeds.turn = NAVIGATION_SPEED_DEFAULT__TURN; 186 | settings->speeds.orient = NAVIGATION_SPEED_DEFAULT__ORIENT; 187 | settings->speeds.orbit = NAVIGATION_SPEED_DEFAULT__ORBIT; 188 | settings->speeds.zoom = NAVIGATION_SPEED_DEFAULT__ZOOM; 189 | settings->speeds.dolly = NAVIGATION_SPEED_DEFAULT__DOLLY; 190 | settings->speeds.pan = NAVIGATION_SPEED_DEFAULT__PAN; 191 | } 192 | 193 | void initNavigation(Navigation *navigation, NavigationSettings *navigation_settings) { 194 | navigation->settings = *navigation_settings; 195 | 196 | navigation->turned = false; 197 | navigation->moved = false; 198 | navigation->zoomed = false; 199 | 200 | navigation->move.right = false; 201 | navigation->move.left = false; 202 | navigation->move.up = false; 203 | navigation->move.down = false; 204 | navigation->move.forward = false; 205 | navigation->move.backward = false; 206 | 207 | navigation->turn.right = false; 208 | navigation->turn.left = false; 209 | } 210 | void setDefaultViewportSettings(ViewportSettings *settings) { 211 | settings->near_clipping_plane_distance = VIEWPORT_DEFAULT__NEAR_CLIPPING_PLANE_DISTANCE; 212 | settings->far_clipping_plane_distance = VIEWPORT_DEFAULT__FAR_CLIPPING_PLANE_DISTANCE; 213 | settings->hud_default_color = White; 214 | settings->hud_line_count = 0; 215 | settings->hud_lines = null; 216 | settings->show_hud = false; 217 | settings->use_cube_NDC = false; 218 | settings->flip_z = false; 219 | settings->antialias = false; 220 | settings->cull_back_faces = true; 221 | settings->show_wire_frame = false; 222 | settings->background.color = Color(Black); 223 | settings->background.opacity = 0; 224 | settings->background.depth = INFINITY; 225 | } 226 | 227 | void setProjectionMatrix(Viewport *viewport) { 228 | const f32 n = viewport->settings.near_clipping_plane_distance; 229 | const f32 f = viewport->settings.far_clipping_plane_distance; 230 | const f32 fl = viewport->camera->focal_length; 231 | const f32 ar = viewport->dimensions.height_over_width; 232 | const f32 gl = viewport->settings.use_cube_NDC; 233 | mat4 M; 234 | 235 | M.X.y = M.X.z = M.X.w = M.Y.x = M.Y.z = M.Y.w = M.W.x = M.W.y = M.W.w = M.Z.x = M.Z.y = 0; 236 | M.Z.w = 1.0f; 237 | M.X.x = fl * ar; 238 | M.Y.y = fl; 239 | M.Z.z = M.W.z = 1.0f / (f - n); 240 | M.Z.z *= gl ? (f + n) : f; 241 | M.W.z *= gl ? (-2 * f * n) : (-n * f); 242 | 243 | viewport->projection_matrix = M; 244 | } 245 | 246 | void initViewport(Viewport *viewport, ViewportSettings *viewport_settings, NavigationSettings *navigation_settings, Camera *camera, PixelQuad *pixels) { 247 | viewport->pixels = pixels; 248 | viewport->camera = camera; 249 | viewport->settings = *viewport_settings; 250 | viewport->position.x = 0; 251 | viewport->position.y = 0; 252 | initBox(&viewport->default_box); 253 | initHUD(&viewport->hud, viewport_settings->hud_lines, viewport_settings->hud_line_count, 1, viewport_settings->hud_default_color, 0, 0); 254 | initNavigation(&viewport->navigation, navigation_settings); 255 | setProjectionMatrix(viewport); 256 | updateDimensions(&viewport->dimensions, MAX_WIDTH, MAX_HEIGHT, MAX_WIDTH); 257 | } 258 | 259 | void setDefaultSceneSettings(SceneSettings *settings) { 260 | settings->cameras = 1; 261 | settings->primitives = 0; 262 | settings->materials = 0; 263 | settings->lights = 0; 264 | settings->curves = 0; 265 | settings->boxes = 0; 266 | settings->grids = 0; 267 | settings->textures = 0; 268 | settings->meshes = 0; 269 | settings->mesh_files = null; 270 | settings->texture_files = null; 271 | settings->file.char_ptr = null; 272 | settings->file.length = 0; 273 | } 274 | void initCurve(Curve *curve) { 275 | curve->thickness = 0.1f; 276 | curve->revolution_count = 1; 277 | } 278 | 279 | void initPrimitive(Primitive *primitive) { 280 | primitive->id = 0; 281 | primitive->type = PrimitiveType_None; 282 | primitive->color = White; 283 | primitive->flags = ALL_FLAGS; 284 | primitive->scale.x = 1; 285 | primitive->scale.y = 1; 286 | primitive->scale.z = 1; 287 | primitive->position.x = 0; 288 | primitive->position.y = 0; 289 | primitive->position.z = 0; 290 | primitive->rotation.axis.x = 0; 291 | primitive->rotation.axis.y = 0; 292 | primitive->rotation.axis.z = 0; 293 | primitive->rotation.amount = 1; 294 | } 295 | 296 | bool initGrid(Grid *grid, u8 u_segments, u8 v_segments) { 297 | if (!u_segments || u_segments > GRID__MAX_SEGMENTS || 298 | !v_segments || v_segments > GRID__MAX_SEGMENTS) 299 | return false; 300 | 301 | grid->u_segments = u_segments; 302 | grid->v_segments = v_segments; 303 | 304 | f32 u_step = u_segments > 1 ? (2.0f / (u_segments - 1)) : 0; 305 | f32 v_step = v_segments > 1 ? (2.0f / (v_segments - 1)) : 0; 306 | 307 | for (u8 u = 0; u < grid->u_segments; u++) { 308 | grid->vertices.uv.u.from[u].y = grid->vertices.uv.u.to[u].y = 0; 309 | grid->vertices.uv.u.from[u].x = grid->vertices.uv.u.to[u].x = -1 + u * u_step; 310 | grid->vertices.uv.u.from[u].z = -1; 311 | grid->vertices.uv.u.to[ u].z = +1; 312 | } 313 | for (u8 v = 0; v < grid->v_segments; v++) { 314 | grid->vertices.uv.v.from[v].y = grid->vertices.uv.v.to[v].y = 0; 315 | grid->vertices.uv.v.from[v].z = grid->vertices.uv.v.to[v].z = -1 + v * v_step; 316 | grid->vertices.uv.v.from[v].x = -1; 317 | grid->vertices.uv.v.to[ v].x = +1; 318 | } 319 | 320 | setGridEdgesFromVertices(grid->edges.uv.u, grid->u_segments, grid->vertices.uv.u.from, grid->vertices.uv.u.to); 321 | setGridEdgesFromVertices(grid->edges.uv.v, grid->v_segments, grid->vertices.uv.v.from, grid->vertices.uv.v.to); 322 | 323 | return true; 324 | } 325 | 326 | void initMaterial(Material *material) { 327 | for (u8 i = 0; i < 3; i++) { 328 | material->specular.components[i] = 1; 329 | material->diffuse.components[i] = 1; 330 | material->ambient.components[i] = 1; 331 | } 332 | material->brdf = phong; 333 | material->flags = LAMBERT; 334 | material->pixel_shader = shadePixelDepth; 335 | material->mesh_shader = shadeMesh; 336 | material->roughness = 1; 337 | material->shininess = 1; 338 | material->normal_magnitude = 1; 339 | material->texture_count = 0; 340 | for (u8 i = 0; i < 16; i++) material->texture_ids[i] = 0; 341 | } 342 | 343 | void initRasterizer(Rasterizer *rasterizer, u32 max_vertex_count, u32 max_normal_count, Memory *memory) { 344 | rasterizer->vertex_flags = (u8*)allocateMemory(memory, max_vertex_count); 345 | rasterizer->clip_space_vertex_positions = (vec4*)allocateMemory(memory, sizeof(vec4) * max_vertex_count); 346 | rasterizer->world_space_vertex_positions = (vec3*)allocateMemory(memory, sizeof(vec3) * max_vertex_count); 347 | rasterizer->world_space_vertex_normals = (vec3*)allocateMemory(memory, sizeof(vec3) * max_normal_count); 348 | setMeshToCube(&rasterizer->default_cube_mesh); 349 | } -------------------------------------------------------------------------------- /src/SlimRaster/core/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void setString(String *string, char *char_ptr) { 4 | string->char_ptr = char_ptr; 5 | string->length = 0; 6 | if (char_ptr) 7 | while (char_ptr[string->length]) 8 | string->length++; 9 | } 10 | 11 | u32 getStringLength(char *string) { 12 | char *char_ptr = string; 13 | u32 length = 0; 14 | if (char_ptr) while (char_ptr[length]) length++; 15 | return length; 16 | } 17 | 18 | u32 getDirectoryLength(char *path) { 19 | u32 path_len = getStringLength(path); 20 | u32 dir_len = path_len; 21 | while (path[dir_len] != '/' && path[dir_len] != '\\') dir_len--; 22 | return dir_len + 1; 23 | } 24 | 25 | void copyToString(String *string, char* char_ptr, u32 offset) { 26 | string->length = offset; 27 | char *source_char = char_ptr; 28 | char *string_char = string->char_ptr + offset; 29 | while (source_char[0]) { 30 | *string_char = *source_char; 31 | string_char++; 32 | source_char++; 33 | string->length++; 34 | } 35 | *string_char = 0; 36 | } 37 | 38 | void mergeString(String *string, char* first, char* second, u32 offset) { 39 | copyToString(string, first, 0); 40 | copyToString(string, second, offset); 41 | } 42 | 43 | void printNumberIntoString(i32 number, NumberString *number_string) { 44 | initNumberString(number_string); 45 | char *buffer = number_string->_buffer; 46 | buffer[12] = 0; 47 | 48 | bool is_negative = number < 0; 49 | if (is_negative) number = -number; 50 | 51 | if (number) { 52 | u32 temp; 53 | buffer += 11; 54 | number_string->string.char_ptr = buffer; 55 | number_string->string.length = 0; 56 | 57 | for (u8 i = 0; i < 11; i++) { 58 | temp = number; 59 | number /= 10; 60 | number_string->string.length++; 61 | *buffer-- = (char)('0' + temp - number * 10); 62 | if (!number) { 63 | if (is_negative) { 64 | *buffer = '-'; 65 | number_string->string.char_ptr--; 66 | number_string->string.length++; 67 | } 68 | 69 | break; 70 | } 71 | number_string->string.char_ptr--; 72 | } 73 | } else { 74 | buffer[11] = '0'; 75 | number_string->string.length = 1; 76 | number_string->string.char_ptr = buffer + 11; 77 | } 78 | } 79 | 80 | void printFloatIntoString(f32 number, NumberString *number_string, u8 float_digits_count) { 81 | f32 factor = 1; 82 | for (u8 i = 0; i < float_digits_count; i++) factor *= 10; 83 | i32 int_num = (i32)(number * factor); 84 | if (int_num == 0) { 85 | printNumberIntoString((i32)factor, number_string); 86 | number_string->string.length++; 87 | number_string->string.char_ptr[0] = '.'; 88 | number_string->string.char_ptr--; 89 | number_string->string.char_ptr[0] = '0'; 90 | return; 91 | } 92 | 93 | bool is_negative = number < 0; 94 | bool is_fraction = is_negative ? number > -1 : number < 1; 95 | 96 | printNumberIntoString(int_num, number_string); 97 | 98 | if (is_fraction) { 99 | u32 len = number_string->string.length; 100 | number_string->string.length++; 101 | number_string->string.char_ptr--; 102 | if (is_negative) { 103 | number_string->string.char_ptr[0] = '-'; 104 | number_string->string.char_ptr[1] = '0'; 105 | } else { 106 | number_string->string.char_ptr[0] = '0'; 107 | } 108 | if (len < float_digits_count) { 109 | for (u32 i = 0; i < (float_digits_count - len); i++) { 110 | number_string->string.length++; 111 | number_string->string.char_ptr--; 112 | number_string->string.char_ptr[0] = '0'; 113 | } 114 | } 115 | } 116 | 117 | static char tmp[13]; 118 | tmp[number_string->string.length + 1] = 0; 119 | for (u8 i = 0; i < (u8)number_string->string.length; i++) { 120 | u8 char_count_from_right_to_left = (u8)number_string->string.length - i - 1; 121 | if (char_count_from_right_to_left >= float_digits_count) tmp[i] = number_string->string.char_ptr[i]; 122 | else tmp[i + 1] = number_string->string.char_ptr[i]; 123 | } 124 | tmp[number_string->string.length - float_digits_count] = '.'; 125 | copyToString(&number_string->string, tmp, 0); 126 | if (is_negative) number_string->string.length++; 127 | } -------------------------------------------------------------------------------- /src/SlimRaster/core/time.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "./base.h" 4 | 5 | void accumulateTimer(Timer* timer) { 6 | timer->ticks_diff = timer->ticks_after - timer->ticks_before; 7 | timer->accumulated_ticks += timer->ticks_diff; 8 | timer->accumulated_frame_count++; 9 | 10 | timer->seconds = (u64)(timer->ticks->per_tick.seconds * (f64)(timer->ticks_diff)); 11 | timer->milliseconds = (u64)(timer->ticks->per_tick.milliseconds * (f64)(timer->ticks_diff)); 12 | timer->microseconds = (u64)(timer->ticks->per_tick.microseconds * (f64)(timer->ticks_diff)); 13 | timer->nanoseconds = (u64)(timer->ticks->per_tick.nanoseconds * (f64)(timer->ticks_diff)); 14 | } 15 | 16 | void averageTimer(Timer *timer) { 17 | timer->average_frames_per_tick = (f64)timer->accumulated_frame_count / (f64)timer->accumulated_ticks; 18 | timer->average_ticks_per_frame = (f64)timer->accumulated_ticks / (f64)timer->accumulated_frame_count; 19 | timer->average_frames_per_second = (u16)(timer->average_frames_per_tick * (f64)timer->ticks->per_second); 20 | timer->average_milliseconds_per_frame = (u16)(timer->average_ticks_per_frame * timer->ticks->per_tick.milliseconds); 21 | timer->average_microseconds_per_frame = (u16)(timer->average_ticks_per_frame * timer->ticks->per_tick.microseconds); 22 | timer->average_nanoseconds_per_frame = (u16)(timer->average_ticks_per_frame * timer->ticks->per_tick.nanoseconds); 23 | timer->accumulated_ticks = timer->accumulated_frame_count = 0; 24 | } 25 | 26 | INLINE void beginFrameTimer(Timer *timer) { 27 | timer->ticks_after = timer->ticks_before; 28 | timer->ticks_before = timer->getTicks(); 29 | timer->ticks_diff = timer->ticks_before - timer->ticks_after; 30 | timer->delta_time = (f32)((f64)timer->ticks_diff * timer->ticks->per_tick.seconds); 31 | } 32 | 33 | INLINE void endFrameTimer(Timer *timer) { 34 | timer->ticks_after = timer->getTicks(); 35 | accumulateTimer(timer); 36 | if (timer->accumulated_ticks >= timer->ticks->per_second / 4) 37 | averageTimer(timer); 38 | } 39 | 40 | void beginFrame(Timer *timer) { 41 | beginFrameTimer(timer); 42 | } 43 | 44 | void endFrame(Timer *timer, Mouse *mouse) { 45 | resetMouseChanges(mouse); 46 | endFrameTimer(timer); 47 | } -------------------------------------------------------------------------------- /src/SlimRaster/math/mat2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | 5 | INLINE mat2 getMat2Identity() { 6 | mat2 out; 7 | 8 | out.X.x = 1; out.X.y = 0; 9 | out.Y.x = 0; out.Y.y = 1; 10 | 11 | return out; 12 | } 13 | 14 | INLINE mat2 addMat2(mat2 a, mat2 b) { 15 | mat2 out; 16 | 17 | out.X.x = a.X.x + b.X.x; 18 | out.X.y = a.X.y + b.X.y; 19 | 20 | out.Y.x = a.Y.x + b.Y.x; 21 | out.Y.y = a.Y.y + b.Y.y; 22 | 23 | return out; 24 | } 25 | 26 | INLINE mat2 subMat2(mat2 a, mat2 b) { 27 | mat2 out; 28 | 29 | out.X.x = a.X.x - b.X.x; 30 | out.X.y = a.X.y - b.X.y; 31 | 32 | out.Y.x = a.Y.x - b.Y.x; 33 | out.Y.y = a.Y.y - b.Y.y; 34 | 35 | return out; 36 | } 37 | 38 | INLINE mat2 scaleMat2(mat2 m, f32 factor) { 39 | mat2 out; 40 | 41 | out.X.x = m.X.x * factor; 42 | out.X.y = m.X.y * factor; 43 | 44 | out.Y.x = m.Y.x * factor; 45 | out.Y.y = m.Y.y * factor; 46 | 47 | return out; 48 | } 49 | 50 | INLINE mat2 transposedMat2(mat2 m) { 51 | mat2 out; 52 | 53 | out.X.x = m.X.x; out.X.y = m.Y.x; 54 | out.Y.x = m.X.y; out.Y.y = m.Y.y; 55 | 56 | return out; 57 | } 58 | 59 | INLINE mat2 mulMat2(mat2 a, mat2 b) { 60 | mat2 out; 61 | 62 | out.X.x = a.X.x*b.X.x + a.X.y*b.Y.x; // Row 1 | Column 1 63 | out.X.y = a.X.x*b.X.y + a.X.y*b.Y.y; // Row 1 | Column 2 64 | 65 | out.Y.x = a.Y.x*b.X.x + a.Y.y*b.Y.x; // Row 2 | Column 1 66 | out.Y.y = a.Y.x*b.X.y + a.Y.y*b.Y.y; // Row 2 | Column 2 67 | 68 | return out; 69 | } 70 | 71 | INLINE mat2 invMat2(mat2 m) { 72 | mat2 out; 73 | 74 | f32 a = m.X.x, b = m.X.y, 75 | c = m.Y.x, d = m.Y.y; 76 | 77 | f32 det = a*d - b*c; 78 | f32 one_over_det = 1.0f / det; 79 | 80 | out.X.x = +d * one_over_det; 81 | out.X.y = -b * one_over_det; 82 | out.Y.x = -c * one_over_det; 83 | out.Y.y = +a * one_over_det; 84 | 85 | return out; 86 | } 87 | 88 | INLINE bool safeInvertMat2(mat2 *m) { 89 | f32 a = m->X.x, b = m->X.y, 90 | c = m->Y.x, d = m->Y.y; 91 | 92 | f32 det = a*d - b*c; 93 | if (!det) return false; 94 | f32 one_over_det = 1.0f / det; 95 | 96 | m->X.x = +d * one_over_det; 97 | m->X.y = -b * one_over_det; 98 | m->Y.x = -c * one_over_det; 99 | m->Y.y = +a * one_over_det; 100 | 101 | return true; 102 | } 103 | 104 | INLINE void rotateMat2(f32 amount, mat2* out) { 105 | vec2 xy = getPointOnUnitCircle(amount); 106 | 107 | vec2 X = out->X; 108 | vec2 Y = out->Y; 109 | 110 | out->X.x = xy.x * X.x + xy.y * X.y; 111 | out->Y.x = xy.x * Y.x + xy.y * Y.y; 112 | 113 | out->X.y = xy.x * X.y - xy.y * X.x; 114 | out->Y.y = xy.x * Y.y - xy.y * Y.x; 115 | } 116 | 117 | INLINE void setRotationMat2(f32 roll, mat2* roll_matrix) { 118 | vec2 xy = getPointOnUnitCircle(roll); 119 | 120 | roll_matrix->X.x = roll_matrix->Y.y = xy.x; 121 | roll_matrix->X.y = -xy.y; 122 | roll_matrix->Y.x = +xy.y; 123 | } -------------------------------------------------------------------------------- /src/SlimRaster/math/mat3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | 5 | INLINE mat3 getMat3Identity() { 6 | mat3 out; 7 | 8 | out.X.x = 1; out.X.y = 0; out.X.z = 0; 9 | out.Y.x = 0; out.Y.y = 1; out.Y.z = 0; 10 | out.Z.x = 0; out.Z.y = 0; out.Z.z = 1; 11 | 12 | return out; 13 | } 14 | 15 | INLINE mat3 addMat3(mat3 a, mat3 b) { 16 | mat3 out; 17 | 18 | out.X.x = a.X.x + b.X.x; 19 | out.X.y = a.X.y + b.X.y; 20 | out.X.z = a.X.z + b.X.z; 21 | 22 | out.Y.x = a.Y.x + b.Y.x; 23 | out.Y.y = a.Y.y + b.Y.y; 24 | out.Y.z = a.Y.z + b.Y.z; 25 | 26 | out.Z.x = a.Z.x + b.Z.x; 27 | out.Z.y = a.Z.y + b.Z.y; 28 | out.Z.z = a.Z.x + b.Z.x; 29 | 30 | return out; 31 | } 32 | 33 | INLINE mat3 subMat3(mat3 a, mat3 b) { 34 | mat3 out; 35 | 36 | out.X.x = a.X.x - b.X.x; 37 | out.X.y = a.X.y - b.X.y; 38 | out.X.z = a.X.z - b.X.z; 39 | 40 | out.Y.x = a.Y.x - b.Y.x; 41 | out.Y.y = a.Y.y - b.Y.y; 42 | out.Y.z = a.Y.z - b.Y.z; 43 | 44 | out.Z.x = a.Z.x - b.Z.x; 45 | out.Z.y = a.Z.y - b.Z.y; 46 | out.Z.z = a.Z.x - b.Z.x; 47 | 48 | return out; 49 | } 50 | 51 | INLINE mat3 scaleMat3(mat3 m, f32 factor) { 52 | mat3 out; 53 | 54 | out.X.x = m.X.x * factor; 55 | out.X.y = m.X.y * factor; 56 | out.X.z = m.X.z * factor; 57 | 58 | out.Y.x = m.Y.x * factor; 59 | out.Y.y = m.Y.y * factor; 60 | out.Y.z = m.Y.z * factor; 61 | 62 | out.Z.x = m.Z.x * factor; 63 | out.Z.y = m.Z.y * factor; 64 | out.Z.z = m.Z.z * factor; 65 | 66 | return out; 67 | } 68 | 69 | INLINE mat3 transposedMat3(mat3 m) { 70 | mat3 out; 71 | 72 | out.X.x = m.X.x; out.X.y = m.Y.x; out.X.z = m.Z.x; 73 | out.Y.x = m.X.y; out.Y.y = m.Y.y; out.Y.z = m.Z.y; 74 | out.Z.x = m.X.z; out.Z.y = m.Y.z; out.Z.z = m.Z.z; 75 | 76 | return out; 77 | } 78 | 79 | INLINE mat3 mulMat3(mat3 a, mat3 b) { 80 | mat3 out; 81 | 82 | out.X.x = a.X.x*b.X.x + a.X.y*b.Y.x + a.X.z*b.Z.x; // Row 1 | Column 1 83 | out.X.y = a.X.x*b.X.y + a.X.y*b.Y.y + a.X.z*b.Z.y; // Row 1 | Column 2 84 | out.X.z = a.X.x*b.X.z + a.X.y*b.Y.z + a.X.z*b.Z.z; // Row 1 | Column 3 85 | 86 | out.Y.x = a.Y.x*b.X.x + a.Y.y*b.Y.x + a.Y.z*b.Z.x; // Row 2 | Column 1 87 | out.Y.y = a.Y.x*b.X.y + a.Y.y*b.Y.y + a.Y.z*b.Z.y; // Row 2 | Column 2 88 | out.Y.z = a.Y.x*b.X.z + a.Y.y*b.Y.z + a.Y.z*b.Z.z; // Row 2 | Column 3 89 | 90 | out.Z.x = a.Z.x*b.X.x + a.Z.y*b.Y.x + a.Z.z*b.Z.x; // Row 3 | Column 1 91 | out.Z.y = a.Z.x*b.X.y + a.Z.y*b.Y.y + a.Z.z*b.Z.y; // Row 3 | Column 2 92 | out.Z.z = a.Z.x*b.X.z + a.Z.y*b.Y.z + a.Z.z*b.Z.z; // Row 3 | Column 3 93 | 94 | return out; 95 | } 96 | 97 | INLINE mat3 invMat3(mat3 m) { 98 | mat3 out; 99 | 100 | f32 one_over_determinant = 1.0f / ( 101 | + m.X.x * (m.Y.y * m.Z.z - m.Z.y * m.Y.z) 102 | - m.Y.x * (m.X.y * m.Z.z - m.Z.y * m.X.z) 103 | + m.Z.x * (m.X.y * m.Y.z - m.Y.y * m.X.z) 104 | ); 105 | 106 | out.X.x = + (m.Y.y * m.Z.z - m.Z.y * m.Y.z) * one_over_determinant; 107 | out.Y.x = - (m.Y.x * m.Z.z - m.Z.x * m.Y.z) * one_over_determinant; 108 | out.Z.x = + (m.Y.x * m.Z.y - m.Z.x * m.Y.y) * one_over_determinant; 109 | out.X.y = - (m.X.y * m.Z.z - m.Z.y * m.X.z) * one_over_determinant; 110 | out.Y.y = + (m.X.x * m.Z.z - m.Z.x * m.X.z) * one_over_determinant; 111 | out.Z.y = - (m.X.x * m.Z.y - m.Z.x * m.X.y) * one_over_determinant; 112 | out.X.z = + (m.X.y * m.Y.z - m.Y.y * m.X.z) * one_over_determinant; 113 | out.Y.z = - (m.X.x * m.Y.z - m.Y.x * m.X.z) * one_over_determinant; 114 | out.Z.z = + (m.X.x * m.Y.y - m.Y.x * m.X.y) * one_over_determinant; 115 | 116 | return out; 117 | } 118 | 119 | INLINE bool safeInvertMat3(mat3 *m) { 120 | f32 m11 = m->X.x, m12 = m->X.y, m13 = m->X.z, 121 | m21 = m->Y.x, m22 = m->Y.y, m23 = m->Y.z, 122 | m31 = m->Z.x, m32 = m->Z.y, m33 = m->Z.z, 123 | 124 | c11 = m22*m33 - 125 | m23*m32, 126 | 127 | c12 = m13*m32 - 128 | m12*m33, 129 | 130 | c13 = m12*m23 - 131 | m13*m22, 132 | 133 | 134 | c21 = m23*m31 - 135 | m21*m33, 136 | 137 | c22 = m11*m33 - 138 | m13*m31, 139 | 140 | c23 = m13*m21 - 141 | m11*m23, 142 | 143 | 144 | c31 = m21*m32 - 145 | m22*m31, 146 | 147 | c32 = m12*m31 - 148 | m11*m32, 149 | 150 | c33 = m11*m22 - 151 | m12*m21, 152 | 153 | d = c11 + c12 + c13 + 154 | c21 + c22 + c23 + 155 | c31 + c32 + c33; 156 | 157 | if (!d) return false; 158 | 159 | d = 1 / d; 160 | 161 | m->X.x = d * c11; m->X.y = d * c12; m->X.z = d * c13; 162 | m->Y.x = d * c21; m->Y.y = d * c22; m->Y.z = d * c23; 163 | m->Z.x = d * c31; m->Z.y = d * c32; m->Z.z = d * c33; 164 | 165 | return true; 166 | } 167 | 168 | INLINE void yawMat3(f32 amount, mat3* out) { 169 | vec2 xy = getPointOnUnitCircle(amount); 170 | 171 | vec3 X = out->X; 172 | vec3 Y = out->Y; 173 | vec3 Z = out->Z; 174 | 175 | out->X.x = xy.x * X.x - xy.y * X.z; 176 | out->Y.x = xy.x * Y.x - xy.y * Y.z; 177 | out->Z.x = xy.x * Z.x - xy.y * Z.z; 178 | 179 | out->X.z = xy.x * X.z + xy.y * X.x; 180 | out->Y.z = xy.x * Y.z + xy.y * Y.x; 181 | out->Z.z = xy.x * Z.z + xy.y * Z.x; 182 | } 183 | 184 | INLINE void pitchMat3(f32 amount, mat3* out) { 185 | vec2 xy = getPointOnUnitCircle(amount); 186 | 187 | vec3 X = out->X; 188 | vec3 Y = out->Y; 189 | vec3 Z = out->Z; 190 | 191 | out->X.y = xy.x * X.y + xy.y * X.z; 192 | out->Y.y = xy.x * Y.y + xy.y * Y.z; 193 | out->Z.y = xy.x * Z.y + xy.y * Z.z; 194 | 195 | out->X.z = xy.x * X.z - xy.y * X.y; 196 | out->Y.z = xy.x * Y.z - xy.y * Y.y; 197 | out->Z.z = xy.x * Z.z - xy.y * Z.y; 198 | } 199 | 200 | INLINE void rollMat3(f32 amount, mat3* out) { 201 | vec2 xy = getPointOnUnitCircle(amount); 202 | 203 | vec3 X = out->X; 204 | vec3 Y = out->Y; 205 | vec3 Z = out->Z; 206 | 207 | out->X.x = xy.x * X.x + xy.y * X.y; 208 | out->Y.x = xy.x * Y.x + xy.y * Y.y; 209 | out->Z.x = xy.x * Z.x + xy.y * Z.y; 210 | 211 | out->X.y = xy.x * X.y - xy.y * X.x; 212 | out->Y.y = xy.x * Y.y - xy.y * Y.x; 213 | out->Z.y = xy.x * Z.y - xy.y * Z.x; 214 | } 215 | 216 | INLINE void setYawMat3(f32 yaw, mat3* yaw_matrix) { 217 | vec2 xy = getPointOnUnitCircle(yaw); 218 | 219 | yaw_matrix->X.x = yaw_matrix->Z.z = xy.x; 220 | yaw_matrix->X.z = +xy.y; 221 | yaw_matrix->Z.x = -xy.y; 222 | } 223 | 224 | INLINE void setPitchMat3(f32 pitch, mat3* pitch_matrix) { 225 | vec2 xy = getPointOnUnitCircle(pitch); 226 | 227 | pitch_matrix->Z.z = pitch_matrix->Y.y = xy.x; 228 | pitch_matrix->Y.z = -xy.y; 229 | pitch_matrix->Z.y = +xy.y; 230 | } 231 | 232 | INLINE void setRollMat3(f32 roll, mat3* roll_matrix) { 233 | vec2 xy = getPointOnUnitCircle(roll); 234 | 235 | roll_matrix->X.x = roll_matrix->Y.y = xy.x; 236 | roll_matrix->X.y = -xy.y; 237 | roll_matrix->Y.x = +xy.y; 238 | } -------------------------------------------------------------------------------- /src/SlimRaster/math/mat4.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | 5 | INLINE mat4 getMat4Identity() { 6 | mat4 out; 7 | 8 | out.X.x = 1; out.X.y = 0; out.X.z = 0; out.X.w = 0; 9 | out.Y.x = 0; out.Y.y = 1; out.Y.z = 0; out.Y.w = 0; 10 | out.Z.x = 0; out.Z.y = 0; out.Z.z = 1; out.Z.w = 0; 11 | out.W.x = 0; out.W.y = 0; out.W.z = 0; out.W.w = 1; 12 | 13 | return out; 14 | } 15 | 16 | INLINE mat4 transposeMat4(mat4 m) { 17 | mat4 out; 18 | 19 | out.X.x = m.X.x; out.X.y = m.Y.x; out.X.z = m.Z.x; out.X.w = m.W.x; 20 | out.Y.x = m.X.y; out.Y.y = m.Y.y; out.Y.z = m.Z.y; out.Y.w = m.W.y; 21 | out.Z.x = m.X.z; out.Z.y = m.Y.z; out.Z.z = m.Z.z; out.Z.w = m.W.z; 22 | out.W.x = m.X.w; out.W.y = m.Y.w; out.W.z = m.Z.w; out.W.w = m.W.w; 23 | 24 | return out; 25 | } 26 | 27 | INLINE mat4 addMat4(mat4 a, mat4 b) { 28 | mat4 out; 29 | 30 | out.X.x = a.X.x + b.X.x; 31 | out.X.y = a.X.y + b.X.y; 32 | out.X.z = a.X.z + b.X.z; 33 | out.X.w = a.X.w + b.X.w; 34 | 35 | out.Y.x = a.Y.x + b.Y.x; 36 | out.Y.y = a.Y.y + b.Y.y; 37 | out.Y.z = a.Y.z + b.Y.z; 38 | out.Y.w = a.Y.w + b.Y.w; 39 | 40 | out.Z.x = a.Z.x + b.Z.x; 41 | out.Z.y = a.Z.y + b.Z.y; 42 | out.Z.z = a.Z.x + b.Z.x; 43 | out.Z.w = a.Z.w + b.Z.w; 44 | 45 | out.W.x = a.W.x + b.W.x; 46 | out.W.y = a.W.y + b.W.y; 47 | out.W.z = a.W.x + b.W.x; 48 | out.W.w = a.W.w + b.W.w; 49 | 50 | return out; 51 | } 52 | 53 | INLINE mat4 subMat4(mat4 a, mat4 b) { 54 | mat4 out; 55 | 56 | out.X.x = a.X.x - b.X.x; 57 | out.X.y = a.X.y - b.X.y; 58 | out.X.z = a.X.z - b.X.z; 59 | out.X.w = a.X.w - b.X.w; 60 | 61 | out.Y.x = a.Y.x - b.Y.x; 62 | out.Y.y = a.Y.y - b.Y.y; 63 | out.Y.z = a.Y.z - b.Y.z; 64 | out.Y.w = a.Y.w - b.Y.w; 65 | 66 | out.Z.x = a.Z.x - b.Z.x; 67 | out.Z.y = a.Z.y - b.Z.y; 68 | out.Z.z = a.Z.x - b.Z.x; 69 | out.Z.w = a.Z.w - b.Z.w; 70 | 71 | out.W.x = a.W.x - b.W.x; 72 | out.W.y = a.W.y - b.W.y; 73 | out.W.z = a.W.x - b.W.x; 74 | out.W.w = a.W.w - b.W.w; 75 | 76 | return out; 77 | } 78 | 79 | INLINE mat4 scaleMat4(mat4 m, f32 factor) { 80 | mat4 out; 81 | 82 | out.X.x = m.X.x * factor; 83 | out.X.y = m.X.y * factor; 84 | out.X.z = m.X.z * factor; 85 | out.X.w = m.X.w * factor; 86 | 87 | out.Y.x = m.Y.x * factor; 88 | out.Y.y = m.Y.y * factor; 89 | out.Y.z = m.Y.z * factor; 90 | out.Y.w = m.Y.w * factor; 91 | 92 | out.Z.x = m.Z.x * factor; 93 | out.Z.y = m.Z.y * factor; 94 | out.Z.z = m.Z.z * factor; 95 | out.Z.w = m.Z.w * factor; 96 | 97 | out.W.x = m.W.x * factor; 98 | out.W.y = m.W.y * factor; 99 | out.W.z = m.W.z * factor; 100 | out.W.w = m.W.w * factor; 101 | 102 | return out; 103 | } 104 | 105 | INLINE mat4 mulMat4(mat4 a, mat4 b) { 106 | mat4 out; 107 | 108 | out.X.x = a.X.x*b.X.x + a.X.y*b.Y.x + a.X.z*b.Z.x + a.X.w*b.W.x; // Row 1 | Column 1 109 | out.X.y = a.X.x*b.X.y + a.X.y*b.Y.y + a.X.z*b.Z.y + a.X.w*b.W.y; // Row 1 | Column 2 110 | out.X.z = a.X.x*b.X.z + a.X.y*b.Y.z + a.X.z*b.Z.z + a.X.w*b.W.z; // Row 1 | Column 3 111 | out.X.w = a.X.x*b.X.w + a.X.y*b.Y.w + a.X.z*b.Z.w + a.X.w*b.W.w; // Row 1 | Column 4 112 | 113 | out.Y.x = a.Y.x*b.X.x + a.Y.y*b.Y.x + a.Y.z*b.Z.x + a.Y.w*b.W.x; // Row 2 | Column 1 114 | out.Y.y = a.Y.x*b.X.y + a.Y.y*b.Y.y + a.Y.z*b.Z.y + a.Y.w*b.W.y; // Row 2 | Column 2 115 | out.Y.z = a.Y.x*b.X.z + a.Y.y*b.Y.z + a.Y.z*b.Z.z + a.Y.w*b.W.z; // Row 2 | Column 3 116 | out.Y.w = a.Y.x*b.X.w + a.Y.y*b.Y.w + a.Y.z*b.Z.w + a.Y.w*b.W.w; // Row 2 | Column 4 117 | 118 | out.Z.x = a.Z.x*b.X.x + a.Z.y*b.Y.x + a.Z.z*b.Z.x + a.Z.w*b.W.x; // Row 3 | Column 1 119 | out.Z.y = a.Z.x*b.X.y + a.Z.y*b.Y.y + a.Z.z*b.Z.y + a.Z.w*b.W.y; // Row 3 | Column 2 120 | out.Z.z = a.Z.x*b.X.z + a.Z.y*b.Y.z + a.Z.z*b.Z.z + a.Z.w*b.W.z; // Row 3 | Column 3 121 | out.Z.w = a.Z.x*b.X.w + a.Z.y*b.Y.w + a.Z.z*b.Z.w + a.Z.w*b.W.w; // Row 3 | Column 4 122 | 123 | out.W.x = a.W.x*b.X.x + a.W.y*b.Y.x + a.W.z*b.Z.x + a.W.w*b.W.x; // Row 4 | Column 1 124 | out.W.y = a.W.x*b.X.y + a.W.y*b.Y.y + a.W.z*b.Z.y + a.W.w*b.W.y; // Row 4 | Column 2 125 | out.W.z = a.W.x*b.X.z + a.W.y*b.Y.z + a.W.z*b.Z.z + a.W.w*b.W.z; // Row 4 | Column 3 126 | out.W.w = a.W.x*b.X.w + a.W.y*b.Y.w + a.W.z*b.Z.w + a.W.w*b.W.w; // Row 4 | Column 4 127 | 128 | return out; 129 | } 130 | 131 | INLINE mat4 invMat4(mat4 m) { 132 | mat4 out; 133 | 134 | f32 m11 = m.X.x, m12 = m.X.y, m13 = m.X.z, m14 = m.X.w, 135 | m21 = m.Y.x, m22 = m.Y.y, m23 = m.Y.z, m24 = m.Y.w, 136 | m31 = m.Z.x, m32 = m.Z.y, m33 = m.Z.z, m34 = m.Z.w, 137 | m41 = m.W.x, m42 = m.W.y, m43 = m.W.z, m44 = m.W.w; 138 | 139 | out.X.x = +m22*m33*m44 - m22*m34*m43 - m32*m23*m44 + m32*m24*m43 + m42*m23*m34 - m42*m24*m33; 140 | out.X.y = -m12*m33*m44 + m12*m34*m43 + m32*m13*m44 - m32*m14*m43 - m42*m13*m34 + m42*m14*m33; 141 | out.X.z = +m12*m23*m44 - m12*m24*m43 - m22*m13*m44 + m22*m14*m43 + m42*m13*m24 - m42*m14*m23; 142 | out.X.w = -m12*m23*m34 + m12*m24*m33 + m22*m13*m34 - m22*m14*m33 - m32*m13*m24 + m32*m14*m23; 143 | 144 | out.Y.x = -m21*m33*m44 + m21*m34*m43 + m31*m23*m44 - m31*m24*m43 - m41*m23*m34 + m41*m24*m33; 145 | out.Y.y = +m11*m33*m44 - m11*m34*m43 - m31*m13*m44 + m31*m14*m43 + m41*m13*m34 - m41*m14*m33; 146 | out.Y.z = -m11*m23*m44 + m11*m24*m43 + m21*m13*m44 - m21*m14*m43 - m41*m13*m24 + m41*m14*m23; 147 | out.Y.w = +m11*m23*m34 - m11*m24*m33 - m21*m13*m34 + m21*m14*m33 + m31*m13*m24 - m31*m14*m23; 148 | 149 | out.Z.x = +m21*m32*m44 - m21*m34*m42 - m31*m22*m44 + m31*m24*m42 + m41*m22*m34 - m41*m24*m32; 150 | out.Z.y = -m11*m32*m44 + m11*m34*m42 + m31*m12*m44 - m31*m14*m42 - m41*m12*m34 + m41*m14*m32; 151 | out.Z.z = +m11*m22*m44 - m11*m24*m42 - m21*m12*m44 + m21*m14*m42 + m41*m12*m24 - m41*m14*m22; 152 | out.Z.w = -m11*m22*m34 + m11*m24*m32 + m21*m12*m34 - m21*m14*m32 - m31*m12*m24 + m31*m14*m22; 153 | 154 | out.W.x = -m21*m32*m43 + m21*m33*m42 + m31*m22*m43 - m31*m23*m42 - m41*m22*m33 + m41*m23*m32; 155 | out.W.y = +m11*m32*m43 - m11*m33*m42 - m31*m12*m43 + m31*m13*m42 + m41*m12*m33 - m41*m13*m32; 156 | out.W.z = -m11*m22*m43 + m11*m23*m42 + m21*m12*m43 - m21*m13*m42 - m41*m12*m23 + m41*m13*m22; 157 | out.W.w = +m11*m22*m33 - m11*m23*m32 - m21*m12*m33 + m21*m13*m32 + m31*m12*m23 - m31*m13*m22; 158 | 159 | f32 det = m11*out.X.x + m12*out.Y.x + m13*out.Z.x + m14*out.W.x; 160 | if (!det) return m; 161 | 162 | out = scaleMat4(out, 1.0f / det); 163 | 164 | return out; 165 | } 166 | 167 | INLINE void yawMat4(f32 amount, mat4 *out) { 168 | vec2 xy = getPointOnUnitCircle(amount); 169 | 170 | vec4 X = out->X; 171 | vec4 Y = out->Y; 172 | vec4 Z = out->Z; 173 | 174 | out->X.x = xy.x * X.x - xy.y * X.z; 175 | out->Y.x = xy.x * Y.x - xy.y * Y.z; 176 | out->Z.x = xy.x * Z.x - xy.y * Z.z; 177 | 178 | out->X.z = xy.x * X.z + xy.y * X.x; 179 | out->Y.z = xy.x * Y.z + xy.y * Y.x; 180 | out->Z.z = xy.x * Z.z + xy.y * Z.x; 181 | } 182 | 183 | INLINE void pitchMat4(f32 amount, mat4 *out) { 184 | vec2 xy = getPointOnUnitCircle(amount); 185 | 186 | vec4 X = out->X; 187 | vec4 Y = out->Y; 188 | vec4 Z = out->Z; 189 | 190 | out->X.y = xy.x * X.y + xy.y * X.z; 191 | out->Y.y = xy.x * Y.y + xy.y * Y.z; 192 | out->Z.y = xy.x * Z.y + xy.y * Z.z; 193 | 194 | out->X.z = xy.x * X.z - xy.y * X.y; 195 | out->Y.z = xy.x * Y.z - xy.y * Y.y; 196 | out->Z.z = xy.x * Z.z - xy.y * Z.y; 197 | } 198 | 199 | INLINE void rollMat4(f32 amount, mat4 *out) { 200 | vec2 xy = getPointOnUnitCircle(amount); 201 | 202 | vec4 X = out->X; 203 | vec4 Y = out->Y; 204 | vec4 Z = out->Z; 205 | 206 | out->X.x = xy.x * X.x + xy.y * X.y; 207 | out->Y.x = xy.x * Y.x + xy.y * Y.y; 208 | out->Z.x = xy.x * Z.x + xy.y * Z.y; 209 | 210 | out->X.y = xy.x * X.y - xy.y * X.x; 211 | out->Y.y = xy.x * Y.y - xy.y * Y.x; 212 | out->Z.y = xy.x * Z.y - xy.y * Z.x; 213 | } 214 | 215 | INLINE void setYawMat4(f32 yaw, mat4 *yaw_matrix) { 216 | vec2 xy = getPointOnUnitCircle(yaw); 217 | 218 | yaw_matrix->X.x = yaw_matrix->Z.z = xy.x; 219 | yaw_matrix->X.z = +xy.y; 220 | yaw_matrix->Z.x = -xy.y; 221 | } 222 | 223 | INLINE void setPitchMat4(f32 pitch, mat4 *pitch_matrix) { 224 | vec2 xy = getPointOnUnitCircle(pitch); 225 | 226 | pitch_matrix->Z.z = pitch_matrix->Y.y = xy.x; 227 | pitch_matrix->Y.z = -xy.y; 228 | pitch_matrix->Z.y = +xy.y; 229 | } 230 | 231 | INLINE void setRollMat4(f32 roll, mat4 *roll_matrix) { 232 | vec2 xy = getPointOnUnitCircle(roll); 233 | 234 | roll_matrix->X.x = roll_matrix->Y.y = xy.x; 235 | roll_matrix->X.y = -xy.y; 236 | roll_matrix->Y.x = +xy.y; 237 | } 238 | 239 | INLINE mat4 mat4fromMat3(mat3 m3) { 240 | mat4 out = getMat4Identity(); 241 | for (u8 row = 0; row < 3; row++) 242 | for (u8 col = 0; col < 3; col++) 243 | out.axis[row].components[col] = m3.axis[row].components[col]; 244 | 245 | return out; 246 | } -------------------------------------------------------------------------------- /src/SlimRaster/math/quat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | #include "./vec3.h" 5 | 6 | INLINE quat getIdentityQuaternion() { 7 | quat out; 8 | 9 | out.axis = getVec3Of(0); 10 | out.amount = 1; 11 | 12 | return out; 13 | } 14 | 15 | INLINE quat normQuat(quat q) { 16 | quat out; 17 | 18 | f32 factor = 1.0f / sqrtf(q.axis.x * q.axis.x + q.axis.y * q.axis.y + q.axis.z * q.axis.z + q.amount * q.amount); 19 | out.axis = scaleVec3(q.axis, factor); 20 | out.amount = q.amount * factor; 21 | 22 | return out; 23 | } 24 | 25 | INLINE vec3 mulVec3Quat(const vec3 v, quat q) { 26 | vec3 out = crossVec3(q.axis, v); 27 | vec3 qqv = crossVec3(q.axis, out); 28 | out = scaleAddVec3(out, q.amount, qqv); 29 | out = scaleAddVec3(out, 2, v); 30 | return out; 31 | } 32 | 33 | INLINE quat mulQuat(quat a, quat b) { 34 | quat out; 35 | 36 | out.amount = a.amount * b.amount - a.axis.x * b.axis.x - a.axis.y * b.axis.y - a.axis.z * b.axis.z; 37 | out.axis.x = a.amount * b.axis.x + a.axis.x * b.amount + a.axis.y * b.axis.z - a.axis.z * b.axis.y; 38 | out.axis.y = a.amount * b.axis.y - a.axis.x * b.axis.z + a.axis.y * b.amount + a.axis.z * b.axis.x; 39 | out.axis.z = a.amount * b.axis.z + a.axis.x * b.axis.y - a.axis.y * b.axis.x + a.axis.z * b.amount; 40 | 41 | return out; 42 | } 43 | 44 | INLINE quat conjugate(quat q) { 45 | quat out; 46 | 47 | out.amount = q.amount; 48 | out.axis = invertedVec3(q.axis); 49 | 50 | return out; 51 | } 52 | 53 | INLINE quat convertRotationMatrixToQuaternion(mat3 rotation_matrix) { 54 | quat out; 55 | 56 | f32 fourXSquaredMinus1 = rotation_matrix.X.x - rotation_matrix.Y.y - rotation_matrix.Z.z; 57 | f32 fourYSquaredMinus1 = rotation_matrix.Y.y - rotation_matrix.X.x - rotation_matrix.Z.z; 58 | f32 fourZSquaredMinus1 = rotation_matrix.Z.z - rotation_matrix.X.x - rotation_matrix.Y.y; 59 | f32 fourWSquaredMinus1 = rotation_matrix.X.x + rotation_matrix.Y.y + rotation_matrix.Z.z; 60 | 61 | int biggestIndex = 0; 62 | f32 fourBiggestSquaredMinus1 = fourWSquaredMinus1; 63 | if (fourXSquaredMinus1 > fourBiggestSquaredMinus1) { 64 | fourBiggestSquaredMinus1 = fourXSquaredMinus1; 65 | biggestIndex = 1; 66 | } 67 | if (fourYSquaredMinus1 > fourBiggestSquaredMinus1) { 68 | fourBiggestSquaredMinus1 = fourYSquaredMinus1; 69 | biggestIndex = 2; 70 | } 71 | if (fourZSquaredMinus1 > fourBiggestSquaredMinus1) { 72 | fourBiggestSquaredMinus1 = fourZSquaredMinus1; 73 | biggestIndex = 3; 74 | } 75 | 76 | f32 biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f) * 0.5f; 77 | f32 mult = 0.25f / biggestVal; 78 | 79 | switch(biggestIndex) { 80 | case 0: 81 | out.amount = biggestVal; 82 | out.axis.x = (rotation_matrix.Y.z - rotation_matrix.Z.y) * mult; 83 | out.axis.y = (rotation_matrix.Z.x - rotation_matrix.X.z) * mult; 84 | out.axis.z = (rotation_matrix.X.y - rotation_matrix.Y.x) * mult; 85 | break; 86 | case 1: 87 | out.amount = (rotation_matrix.Y.z - rotation_matrix.Z.y) * mult; 88 | out.axis.x = biggestVal; 89 | out.axis.y = (rotation_matrix.X.y + rotation_matrix.Y.x) * mult; 90 | out.axis.z = (rotation_matrix.Z.x + rotation_matrix.X.z) * mult; 91 | break; 92 | case 2: 93 | out.amount = (rotation_matrix.Z.x - rotation_matrix.X.z) * mult; 94 | out.axis.x = (rotation_matrix.X.y + rotation_matrix.Y.x) * mult; 95 | out.axis.y = biggestVal; 96 | out.axis.z = (rotation_matrix.Y.z + rotation_matrix.Z.y) * mult; 97 | break; 98 | case 3: 99 | out.amount = (rotation_matrix.X.y - rotation_matrix.Y.x) * mult; 100 | out.axis.x = (rotation_matrix.Z.x + rotation_matrix.X.z) * mult; 101 | out.axis.y = (rotation_matrix.Y.z + rotation_matrix.Z.y) * mult; 102 | out.axis.z = biggestVal; 103 | break; 104 | } 105 | 106 | return out; 107 | } 108 | 109 | INLINE mat3 convertQuaternionToRotationMatrix(quat q) { 110 | mat3 out; 111 | 112 | f32 q0 = q.amount; 113 | f32 q1 = q.axis.x; 114 | f32 q2 = q.axis.y; 115 | f32 q3 = q.axis.z; 116 | 117 | out.X.x = 2 * (q0 * q0 + q1 * q1) - 1; 118 | out.X.y = 2 * (q1 * q2 - q0 * q3); 119 | out.X.z = 2 * (q1 * q3 + q0 * q2); 120 | 121 | out.Y.x = 2 * (q1 * q2 + q0 * q3); 122 | out.Y.y = 2 * (q0 * q0 + q2 * q2) - 1; 123 | out.Y.z = 2 * (q2 * q3 - q0 * q1); 124 | 125 | out.Z.x = 2 * (q1 * q3 - q0 * q2); 126 | out.Z.y = 2 * (q2 * q3 + q0 * q1); 127 | out.Z.z = 2 * (q0 * q0 + q3 * q3) - 1; 128 | 129 | return out; 130 | } 131 | 132 | INLINE quat getRotationAroundAxis(vec3 axis, f32 amount) { 133 | vec2 sin_cos = getPointOnUnitCircle(amount); 134 | quat out; 135 | out.axis = scaleVec3(axis, sin_cos.y); 136 | out.amount = sin_cos.x; 137 | 138 | return normQuat(out); 139 | } 140 | 141 | INLINE quat getRotationAroundAxisBySinCon(vec3 axis, vec2 sin_cos) { 142 | quat out; 143 | out.axis = scaleVec3(axis, sin_cos.y); 144 | out.amount = sin_cos.x; 145 | 146 | return normQuat(out); 147 | } 148 | 149 | INLINE quat rotateAroundAxisBySinCos(quat q, vec3 axis, vec2 sin_cos) { 150 | quat rotation = getRotationAroundAxisBySinCon(axis, sin_cos); 151 | return mulQuat(q, rotation); 152 | } 153 | 154 | INLINE quat rotateAroundAxis(quat q, vec3 axis, f32 amount) { 155 | quat rotation = getRotationAroundAxis(axis, amount); 156 | return mulQuat(q, rotation); 157 | } -------------------------------------------------------------------------------- /src/SlimRaster/math/vec2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | 5 | INLINE vec2 getVec2Of(f32 value) { 6 | vec2 out; 7 | 8 | out.x = out.y = value; 9 | 10 | return out; 11 | } 12 | INLINE bool isEqualVec2(vec2 a, vec2 b) { 13 | return a.x == b.x && a.y == b.y; 14 | } 15 | 16 | INLINE vec2 clampVec2ToZero(vec2 v) { 17 | v.x = v.x > 0.0f ? v.x : 0.0f; 18 | v.y = v.y > 0.0f ? v.y : 0.0f; 19 | return v; 20 | } 21 | 22 | INLINE vec2 clampVec2ToUpper(vec2 v, vec2 upper) { 23 | v.x = v.x < upper.x ? v.x : upper.x; 24 | v.y = v.y < upper.y ? v.y : upper.y; 25 | return v; 26 | } 27 | 28 | INLINE vec2 clampVec2(vec2 v) { 29 | v.x = v.x > 0.0f ? v.x : 0.0f; 30 | v.y = v.y > 0.0f ? v.y : 0.0f; 31 | 32 | v.x = v.x < 1.0f ? v.x : 1.0f; 33 | v.y = v.y < 1.0f ? v.y : 1.0f; 34 | 35 | return v; 36 | } 37 | 38 | INLINE vec2 clampVec2To(vec2 v, const f32 min_value, const f32 max_value) { 39 | v.x = v.x > min_value ? v.x : min_value; 40 | v.y = v.y > min_value ? v.y : min_value; 41 | 42 | v.x = v.x < max_value ? v.x : max_value; 43 | v.y = v.y < max_value ? v.y : max_value; 44 | 45 | return v; 46 | } 47 | 48 | INLINE vec2 invertedVec2(vec2 in) { 49 | vec2 out; 50 | 51 | out.x = -in.x; 52 | out.y = -in.y; 53 | 54 | return out; 55 | } 56 | 57 | INLINE vec2 oneOverVec2(vec2 v) { 58 | vec2 out; 59 | 60 | out.x = 1.0f / v.x; 61 | out.y = 1.0f / v.y; 62 | 63 | return out; 64 | } 65 | 66 | INLINE vec2 approachVec2(vec2 src, vec2 trg, f32 diff) { 67 | vec2 out; 68 | 69 | out.x = approach(src.x, trg.x, diff); 70 | out.y = approach(src.y, trg.y, diff); 71 | 72 | return out; 73 | } 74 | 75 | INLINE bool nonZeroVec2(vec2 v) { 76 | return v.x != 0 || 77 | v.y != 0; 78 | } 79 | 80 | INLINE vec2 minVec2(vec2 a, vec2 b) { 81 | vec2 out; 82 | 83 | out.x = a.x < b.x ? a.x : b.x; 84 | out.y = a.y < b.y ? a.y : b.y; 85 | 86 | return out; 87 | } 88 | 89 | INLINE vec2 maxVec2(vec2 a, vec2 b) { 90 | vec2 out; 91 | 92 | out.x = a.x > b.x ? a.x : b.x; 93 | out.y = a.y > b.y ? a.y : b.y; 94 | 95 | return out; 96 | } 97 | 98 | INLINE f32 minCoordVec2(vec2 v) { 99 | f32 out = v.x; 100 | if (v.y < out) out = v.y; 101 | return out; 102 | } 103 | 104 | INLINE f32 maxCoordVec2(vec2 v) { 105 | f32 out = v.x; 106 | if (v.y > out) out = v.y; 107 | return out; 108 | } 109 | 110 | INLINE vec2 subVec2(vec2 a, vec2 b) { 111 | vec2 out; 112 | 113 | out.x = a.x - b.x; 114 | out.y = a.y - b.y; 115 | 116 | return out; 117 | } 118 | 119 | INLINE vec2 addVec2(vec2 a, vec2 b) { 120 | vec2 out; 121 | 122 | out.x = a.x + b.x; 123 | out.y = a.y + b.y; 124 | 125 | return out; 126 | } 127 | 128 | INLINE vec2 mulVec2(vec2 a, vec2 b) { 129 | vec2 out; 130 | 131 | out.x = a.x * b.x; 132 | out.y = a.y * b.y; 133 | 134 | return out; 135 | } 136 | 137 | INLINE vec2 mulAddVec2(vec2 v, vec2 factors, vec2 to_be_added) { 138 | vec2 out; 139 | 140 | out.x = fast_mul_add(v.x, factors.x, to_be_added.x); 141 | out.y = fast_mul_add(v.y, factors.y, to_be_added.y); 142 | 143 | return out; 144 | } 145 | 146 | INLINE vec2 scaleAddVec2(vec2 v, f32 factor, vec2 to_be_added) { 147 | vec2 out; 148 | 149 | out.x = fast_mul_add(v.x, factor, to_be_added.x); 150 | out.y = fast_mul_add(v.y, factor, to_be_added.y); 151 | 152 | return out; 153 | } 154 | 155 | INLINE vec2 scaleVec2(vec2 a, f32 factor) { 156 | vec2 out; 157 | 158 | out.x = a.x * factor; 159 | out.y = a.y * factor; 160 | 161 | return out; 162 | } 163 | 164 | INLINE vec2 mulVec2Mat2(vec2 in, mat2 m) { 165 | vec2 out; 166 | 167 | out.x = in.x * m.X.x + in.y * m.Y.x; 168 | out.y = in.x * m.X.y + in.y * m.Y.y; 169 | 170 | return out; 171 | } 172 | 173 | INLINE f32 dotVec2(vec2 a, vec2 b) { 174 | return ( 175 | (a.x * b.x) + 176 | (a.y * b.y) 177 | ); 178 | } 179 | 180 | INLINE f32 squaredLengthVec2(vec2 v) { 181 | return ( 182 | (v.x * v.x) + 183 | (v.y * v.y) 184 | ); 185 | } 186 | 187 | INLINE f32 lengthVec2(vec2 v) { 188 | return sqrtf(squaredLengthVec2(v)); 189 | } 190 | 191 | INLINE vec2 normVec2(vec2 v) { 192 | return scaleVec2(v, 1.0f / lengthVec2(v)); 193 | } 194 | 195 | INLINE f32 DotVec2(vec2 a, vec2 b) { return clampValue(dotVec2(a, b)); } 196 | 197 | INLINE mat2 outerVec2(vec2 a, vec2 b) { 198 | mat2 out; 199 | 200 | out.X = scaleVec2(a, b.x); 201 | out.Y = scaleVec2(a, b.y); 202 | 203 | return out; 204 | } 205 | 206 | INLINE vec2 reflectVec2(vec2 V, vec2 N) { 207 | vec2 out = scaleVec2(N, -2 * dotVec2(N, V)); 208 | out = addVec2(out, V); 209 | return out; 210 | } -------------------------------------------------------------------------------- /src/SlimRaster/math/vec3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | 5 | INLINE bool isEqualVec3(vec3 a, vec3 b) { 6 | return a.x == b.x && a.y == b.y && a.z == b.z; 7 | } 8 | 9 | INLINE vec3 clampVec3ToZero(vec3 v) { 10 | v.x = v.x > 0.0f ? v.x : 0.0f; 11 | v.y = v.y > 0.0f ? v.y : 0.0f; 12 | v.z = v.z > 0.0f ? v.z : 0.0f; 13 | return v; 14 | } 15 | 16 | INLINE vec3 clampVec3ToUpper(vec3 v, vec3 upper) { 17 | v.x = v.x < upper.x ? v.x : upper.x; 18 | v.y = v.y < upper.y ? v.y : upper.y; 19 | v.z = v.z < upper.z ? v.z : upper.z; 20 | return v; 21 | } 22 | 23 | INLINE vec3 clampVec3(vec3 v) { 24 | v.x = v.x > 0.0f ? v.x : 0.0f; 25 | v.y = v.y > 0.0f ? v.y : 0.0f; 26 | v.z = v.z > 0.0f ? v.z : 0.0f; 27 | 28 | v.x = v.x < 1.0f ? v.x : 1.0f; 29 | v.y = v.y < 1.0f ? v.y : 1.0f; 30 | v.z = v.z < 1.0f ? v.z : 1.0f; 31 | 32 | return v; 33 | } 34 | 35 | INLINE vec3 clampVec3To(vec3 v, const f32 min_value, const f32 max_value) { 36 | v.x = v.x > min_value ? v.x : min_value; 37 | v.y = v.y > min_value ? v.y : min_value; 38 | v.z = v.z > min_value ? v.z : min_value; 39 | 40 | v.x = v.x < max_value ? v.x : max_value; 41 | v.y = v.y < max_value ? v.y : max_value; 42 | v.z = v.z < max_value ? v.z : max_value; 43 | 44 | return v; 45 | } 46 | 47 | INLINE vec3 getVec3Of(f32 value) { 48 | vec3 out; 49 | 50 | out.x = out.y = out.z = value; 51 | 52 | return out; 53 | } 54 | 55 | INLINE vec3 invertedVec3(vec3 in) { 56 | vec3 out; 57 | 58 | out.x = -in.x; 59 | out.y = -in.y; 60 | out.z = -in.z; 61 | 62 | return out; 63 | } 64 | 65 | INLINE vec3 oneOverVec3(vec3 v) { 66 | vec3 out; 67 | 68 | out.x = 1.0f / v.x; 69 | out.y = 1.0f / v.y; 70 | out.z = 1.0f / v.z; 71 | 72 | return out; 73 | } 74 | 75 | INLINE vec3 approachVec3(vec3 src, vec3 trg, f32 diff) { 76 | vec3 out; 77 | 78 | out.x = approach(src.x, trg.x, diff); 79 | out.y = approach(src.y, trg.y, diff); 80 | out.z = approach(src.z, trg.z, diff); 81 | 82 | return out; 83 | } 84 | 85 | INLINE bool nonZeroVec3(vec3 v) { 86 | return v.x != 0 || 87 | v.y != 0 || 88 | v.z != 0; 89 | } 90 | 91 | INLINE vec3 minVec3(vec3 a, vec3 b) { 92 | vec3 out; 93 | 94 | out.x = a.x < b.x ? a.x : b.x; 95 | out.y = a.y < b.y ? a.y : b.y; 96 | out.z = a.z < b.z ? a.z : b.z; 97 | 98 | return out; 99 | } 100 | 101 | INLINE vec3 maxVec3(vec3 a, vec3 b) { 102 | vec3 out; 103 | 104 | out.x = a.x > b.x ? a.x : b.x; 105 | out.y = a.y > b.y ? a.y : b.y; 106 | out.z = a.z > b.z ? a.z : b.z; 107 | 108 | return out; 109 | } 110 | 111 | INLINE f32 minCoordVec3(vec3 v) { 112 | f32 out = v.x; 113 | if (v.y < out) out = v.y; 114 | if (v.z < out) out = v.z; 115 | return out; 116 | } 117 | 118 | INLINE f32 maxCoordVec3(vec3 v) { 119 | f32 out = v.x; 120 | if (v.y > out) out = v.y; 121 | if (v.z > out) out = v.z; 122 | return out; 123 | } 124 | 125 | INLINE vec3 setPointOnUnitSphere(f32 s, f32 t) { 126 | vec3 out; 127 | 128 | f32 t_squared = t * t; 129 | f32 s_squared = s * s; 130 | f32 factor = 1 / ( t_squared + s_squared + 1); 131 | 132 | out.x = 2*s * factor; 133 | out.y = 2*t * factor; 134 | out.z = (t_squared + s_squared - 1) * t_squared; 135 | 136 | return out; 137 | } 138 | 139 | INLINE vec3 subVec3(vec3 a, vec3 b) { 140 | vec3 out; 141 | 142 | out.x = a.x - b.x; 143 | out.y = a.y - b.y; 144 | out.z = a.z - b.z; 145 | 146 | return out; 147 | } 148 | 149 | INLINE vec3 addVec3(vec3 a, vec3 b) { 150 | vec3 out; 151 | 152 | out.x = a.x + b.x; 153 | out.y = a.y + b.y; 154 | out.z = a.z + b.z; 155 | 156 | return out; 157 | } 158 | 159 | INLINE vec3 mulVec3(vec3 a, vec3 b) { 160 | vec3 out; 161 | 162 | out.x = a.x * b.x; 163 | out.y = a.y * b.y; 164 | out.z = a.z * b.z; 165 | 166 | return out; 167 | } 168 | 169 | INLINE vec3 mulAddVec3(vec3 v, vec3 factors, vec3 to_be_added) { 170 | vec3 out; 171 | 172 | out.x = fast_mul_add(v.x, factors.x, to_be_added.x); 173 | out.y = fast_mul_add(v.y, factors.y, to_be_added.y); 174 | out.z = fast_mul_add(v.z, factors.z, to_be_added.z); 175 | 176 | return out; 177 | } 178 | 179 | INLINE vec3 scaleAddVec3(vec3 v, f32 factor, vec3 to_be_added) { 180 | vec3 out; 181 | 182 | out.x = fast_mul_add(v.x, factor, to_be_added.x); 183 | out.y = fast_mul_add(v.y, factor, to_be_added.y); 184 | out.z = fast_mul_add(v.z, factor, to_be_added.z); 185 | 186 | return out; 187 | } 188 | 189 | INLINE vec3 scaleVec3(vec3 a, f32 factor) { 190 | vec3 out; 191 | 192 | out.x = a.x * factor; 193 | out.y = a.y * factor; 194 | out.z = a.z * factor; 195 | 196 | return out; 197 | } 198 | 199 | INLINE vec3 mulVec3Mat3(vec3 in, mat3 m) { 200 | vec3 out; 201 | 202 | out.x = in.x * m.X.x + in.y * m.Y.x + in.z * m.Z.x; 203 | out.y = in.x * m.X.y + in.y * m.Y.y + in.z * m.Z.y; 204 | out.z = in.x * m.X.z + in.y * m.Y.z + in.z * m.Z.z; 205 | 206 | return out; 207 | } 208 | 209 | INLINE f32 dotVec3(vec3 a, vec3 b) { 210 | return ( 211 | (a.x * b.x) + 212 | (a.y * b.y) + 213 | (a.z * b.z) 214 | ); 215 | } 216 | 217 | INLINE vec3 crossVec3(vec3 a, vec3 b) { 218 | vec3 out; 219 | 220 | out.x = (a.y * b.z) - (a.z * b.y); 221 | out.y = (a.z * b.x) - (a.x * b.z); 222 | out.z = (a.x * b.y) - (a.y * b.x); 223 | 224 | return out; 225 | } 226 | 227 | INLINE f32 squaredLengthVec3(vec3 v) { 228 | return ( 229 | (v.x * v.x) + 230 | (v.y * v.y) + 231 | (v.z * v.z) 232 | ); 233 | } 234 | 235 | INLINE f32 lengthVec3(vec3 v) { 236 | return sqrtf(squaredLengthVec3(v)); 237 | } 238 | 239 | INLINE vec3 normVec3(vec3 v) { 240 | return scaleVec3(v, 1.0f / lengthVec3(v)); 241 | } 242 | 243 | INLINE f32 DotVec3(vec3 a, vec3 b) { return clampValue(dotVec3(a, b)); } 244 | 245 | INLINE mat3 outerVec3(vec3 a, vec3 b) { 246 | mat3 out; 247 | 248 | out.X = scaleVec3(a, b.x); 249 | out.Y = scaleVec3(a, b.y); 250 | out.Z = scaleVec3(a, b.z); 251 | 252 | return out; 253 | } 254 | 255 | INLINE vec3 reflectVec3(vec3 V, vec3 N) { 256 | vec3 out = scaleVec3(N, -2 * dotVec3(N, V)); 257 | out = addVec3(out, V); 258 | return out; 259 | } 260 | 261 | INLINE vec3 lerpVec3(vec3 from, vec3 to, f32 by) { 262 | return scaleAddVec3(subVec3(to, from), by, from); 263 | } -------------------------------------------------------------------------------- /src/SlimRaster/math/vec4.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | 5 | INLINE vec4 getVec4Of(f32 value) { 6 | vec4 out; 7 | 8 | out.x = out.y = out.z = out.w = value; 9 | 10 | return out; 11 | } 12 | 13 | INLINE vec4 invertedVec4(vec4 in) { 14 | vec4 out; 15 | 16 | out.x = -in.x; 17 | out.y = -in.y; 18 | out.z = -in.z; 19 | out.w = -in.w; 20 | 21 | return out; 22 | } 23 | 24 | INLINE vec4 approachVec4(vec4 src, vec4 trg, f32 diff) { 25 | vec4 out; 26 | 27 | out.x = approach(src.x, trg.x, diff); 28 | out.y = approach(src.y, trg.y, diff); 29 | out.z = approach(src.z, trg.z, diff); 30 | out.w = approach(src.w, trg.w, diff); 31 | 32 | return out; 33 | } 34 | 35 | INLINE bool nonZeroVec4(vec4 v) { 36 | return v.x != 0 || 37 | v.y != 0 || 38 | v.z != 0 || 39 | v.w != 0; 40 | } 41 | 42 | INLINE vec4 subVec4(vec4 a, vec4 b) { 43 | vec4 out; 44 | 45 | out.x = a.x - b.x; 46 | out.y = a.y - b.y; 47 | out.z = a.z - b.z; 48 | out.w = a.w - b.w; 49 | 50 | return out; 51 | } 52 | 53 | INLINE vec4 addVec4(vec4 a, vec4 b) { 54 | vec4 out; 55 | 56 | out.x = a.x + b.x; 57 | out.y = a.y + b.y; 58 | out.z = a.z + b.z; 59 | out.w = a.w + b.w; 60 | 61 | return out; 62 | } 63 | 64 | INLINE vec4 mulVec4(vec4 a, vec4 b) { 65 | vec4 out; 66 | 67 | out.x = a.x * b.x; 68 | out.y = a.y * b.y; 69 | out.z = a.z * b.z; 70 | out.w = a.w * b.w; 71 | 72 | return out; 73 | } 74 | 75 | INLINE vec4 scaleVec4(vec4 a, f32 factor) { 76 | vec4 out; 77 | 78 | out.x = a.x * factor; 79 | out.y = a.y * factor; 80 | out.z = a.z * factor; 81 | out.w = a.w * factor; 82 | 83 | return out; 84 | } 85 | INLINE vec4 scaleAddVec4(vec4 v, f32 factor, vec4 to_be_added) { 86 | vec4 out; 87 | 88 | out.x = fast_mul_add(v.x, factor, to_be_added.x); 89 | out.y = fast_mul_add(v.y, factor, to_be_added.y); 90 | out.z = fast_mul_add(v.z, factor, to_be_added.z); 91 | out.w = fast_mul_add(v.w, factor, to_be_added.w); 92 | 93 | return out; 94 | } 95 | 96 | INLINE vec4 mulVec4Mat4(vec4 in, mat4 m) { 97 | vec4 out; 98 | 99 | out.x = in.x * m.X.x + in.y * m.Y.x + in.z * m.Z.x + in.w * m.W.x; 100 | out.y = in.x * m.X.y + in.y * m.Y.y + in.z * m.Z.y + in.w * m.W.y; 101 | out.z = in.x * m.X.z + in.y * m.Y.z + in.z * m.Z.z + in.w * m.W.z; 102 | out.w = in.x * m.X.w + in.y * m.Y.w + in.z * m.Z.w + in.w * m.W.w; 103 | 104 | return out; 105 | } 106 | 107 | INLINE f32 dotVec4(vec4 a, vec4 b) { 108 | return ( 109 | (a.x * b.x) + 110 | (a.y * b.y) + 111 | (a.z * b.z) + 112 | (a.w * b.w) 113 | ); 114 | } 115 | 116 | INLINE f32 squaredLengthVec4(vec4 v) { 117 | return ( 118 | (v.x * v.x) + 119 | (v.y * v.y) + 120 | (v.z * v.z) + 121 | (v.w * v.w) 122 | ); 123 | } 124 | 125 | INLINE f32 lengthVec4(vec4 v) { 126 | return sqrtf(squaredLengthVec4(v)); 127 | } 128 | 129 | INLINE vec4 norm4(vec4 v) { 130 | return scaleVec4(v, 1.0f / lengthVec4(v)); 131 | } 132 | 133 | INLINE f32 mulVec3Mat4(vec3 in, f32 w, mat4 M, vec3 *out) { 134 | vec4 v4 = mulVec4Mat4(Vec4fromVec3(in, w), M); 135 | *out = Vec3fromVec4(v4); 136 | return v4.w; 137 | } 138 | 139 | INLINE vec4 lerpVec4(vec4 from, vec4 to, f32 by) { 140 | return scaleAddVec4(subVec4(to, from), by, from); 141 | } -------------------------------------------------------------------------------- /src/SlimRaster/platforms/win32.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include 3 | 4 | #ifndef NDEBUG 5 | #include 6 | #include 7 | #include 8 | 9 | void DisplayError(LPTSTR lpszFunction) { 10 | LPVOID lpMsgBuf; 11 | LPVOID lpDisplayBuf; 12 | unsigned int last_error = GetLastError(); 13 | 14 | FormatMessage( 15 | FORMAT_MESSAGE_ALLOCATE_BUFFER | 16 | FORMAT_MESSAGE_FROM_SYSTEM | 17 | FORMAT_MESSAGE_IGNORE_INSERTS, 18 | null, last_error, 19 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 20 | (LPTSTR) &lpMsgBuf, 0, null); 21 | 22 | lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); 23 | 24 | if (FAILED( StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR), 25 | TEXT("%s failed with error code %d as follows:\n%s"), lpszFunction, last_error, lpMsgBuf))) 26 | printf("FATAL ERROR: Unable to output error code.\n"); 27 | 28 | _tprintf(TEXT((LPTSTR)"ERROR: %s\n"), (LPCTSTR)lpDisplayBuf); 29 | 30 | LocalFree(lpMsgBuf); 31 | LocalFree(lpDisplayBuf); 32 | } 33 | #endif 34 | 35 | #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) 36 | #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) 37 | 38 | WNDCLASSA window_class; 39 | HWND window; 40 | HDC win_dc; 41 | BITMAPINFO info; 42 | RECT win_rect; 43 | RAWINPUT raw_inputs; 44 | HRAWINPUT raw_input_handle; 45 | RAWINPUTDEVICE raw_input_device; 46 | UINT raw_input_size; 47 | PUINT raw_input_size_ptr = (PUINT)(&raw_input_size); 48 | UINT raw_input_header_size = sizeof(RAWINPUTHEADER); 49 | 50 | u64 Win32_ticksPerSecond; 51 | LARGE_INTEGER performance_counter; 52 | 53 | void Win32_setWindowTitle(char* str) { SetWindowTextA(window, str); } 54 | void Win32_setCursorVisibility(bool on) { ShowCursor(on); } 55 | void Win32_setWindowCapture(bool on) { if (on) SetCapture(window); else ReleaseCapture(); } 56 | u64 Win32_getTicks() { 57 | QueryPerformanceCounter(&performance_counter); 58 | return (u64)performance_counter.QuadPart; 59 | } 60 | void* Win32_getMemory(u64 size) { 61 | return VirtualAlloc((LPVOID)MEMORY_BASE, (SIZE_T)size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); 62 | } 63 | 64 | inline UINT getRawInput(LPVOID data) { 65 | return GetRawInputData(raw_input_handle, RID_INPUT, data, raw_input_size_ptr, raw_input_header_size); 66 | } 67 | inline bool hasRawInput() { 68 | return getRawInput(0) == 0 && raw_input_size != 0; 69 | } 70 | inline bool hasRawMouseInput(LPARAM lParam) { 71 | raw_input_handle = (HRAWINPUT)(lParam); 72 | return ( 73 | hasRawInput() && 74 | getRawInput((LPVOID)&raw_inputs) == raw_input_size && 75 | raw_inputs.header.dwType == RIM_TYPEMOUSE 76 | ); 77 | } 78 | 79 | void Win32_closeFile(void *handle) { CloseHandle(handle); } 80 | void* Win32_openFileForReading(const char* path) { 81 | HANDLE handle = CreateFile(path, // file to open 82 | GENERIC_READ, // open for reading 83 | FILE_SHARE_READ, // share for reading 84 | null, // default security 85 | OPEN_EXISTING, // existing file only 86 | FILE_ATTRIBUTE_NORMAL, // normal file 87 | null); // no attr. template 88 | #ifndef NDEBUG 89 | if (handle == INVALID_HANDLE_VALUE) { 90 | DisplayError(TEXT((LPTSTR)"CreateFile")); 91 | _tprintf(TEXT("Terminal failure: unable to open file \"%s\" for read.\n"), path); 92 | return null; 93 | } 94 | #endif 95 | return handle; 96 | } 97 | void* Win32_openFileForWriting(const char* path) { 98 | HANDLE handle = CreateFile(path, // file to open 99 | GENERIC_WRITE, // open for writing 100 | 0, // do not share 101 | null, // default security 102 | OPEN_ALWAYS, // create new or open existing 103 | FILE_ATTRIBUTE_NORMAL, // normal file 104 | null); 105 | #ifndef NDEBUG 106 | if (handle == INVALID_HANDLE_VALUE) { 107 | DisplayError(TEXT((LPTSTR)"CreateFile")); 108 | _tprintf(TEXT("Terminal failure: unable to open file \"%s\" for write.\n"), path); 109 | return null; 110 | } 111 | #endif 112 | return handle; 113 | } 114 | bool Win32_readFromFile(LPVOID out, DWORD size, HANDLE handle) { 115 | DWORD bytes_read = 0; 116 | BOOL result = ReadFile(handle, out, size, &bytes_read, null); 117 | #ifndef NDEBUG 118 | if (result == FALSE) { 119 | DisplayError(TEXT((LPTSTR)"ReadFile")); 120 | printf("Terminal failure: Unable to read from file.\n GetLastError=%08x\n", (unsigned int)GetLastError()); 121 | CloseHandle(handle); 122 | } 123 | #endif 124 | return result != FALSE; 125 | } 126 | 127 | bool Win32_writeToFile(LPVOID out, DWORD size, HANDLE handle) { 128 | DWORD bytes_written = 0; 129 | BOOL result = WriteFile(handle, out, size, &bytes_written, null); 130 | #ifndef NDEBUG 131 | if (result == FALSE) { 132 | DisplayError(TEXT((LPTSTR)"WriteFile")); 133 | printf("Terminal failure: Unable to write from file.\n GetLastError=%08x\n", (unsigned int)GetLastError()); 134 | CloseHandle(handle); 135 | } 136 | #endif 137 | return result != FALSE; 138 | } 139 | 140 | LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { 141 | switch (message) { 142 | case WM_DESTROY: 143 | app->is_running = false; 144 | PostQuitMessage(0); 145 | break; 146 | 147 | case WM_SIZE: 148 | GetClientRect(window, &win_rect); 149 | 150 | info.bmiHeader.biWidth = win_rect.right - win_rect.left; 151 | info.bmiHeader.biHeight = win_rect.top - win_rect.bottom; 152 | 153 | _windowResize((u16)info.bmiHeader.biWidth, (u16)-info.bmiHeader.biHeight); 154 | 155 | break; 156 | 157 | case WM_PAINT: 158 | SetDIBitsToDevice(win_dc, 159 | 0, 0, app->viewport.dimensions.width, app->viewport.dimensions.height, 160 | 0, 0, 0, app->viewport.dimensions.height, 161 | app->window_content, &info, DIB_RGB_COLORS); 162 | 163 | ValidateRgn(window, null); 164 | break; 165 | 166 | case WM_SYSKEYDOWN: 167 | case WM_KEYDOWN: 168 | _keyChanged((u8)wParam, true); 169 | break; 170 | 171 | case WM_SYSKEYUP: 172 | case WM_KEYUP: _keyChanged((u8)wParam, false); break; 173 | 174 | case WM_MBUTTONUP: _mouseButtonUp( &app->controls.mouse.middle_button, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; 175 | case WM_MBUTTONDOWN: _mouseButtonDown(&app->controls.mouse.middle_button, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; 176 | case WM_LBUTTONDOWN: _mouseButtonDown(&app->controls.mouse.left_button, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; 177 | case WM_LBUTTONUP : _mouseButtonUp( &app->controls.mouse.left_button, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; 178 | case WM_RBUTTONDOWN: _mouseButtonDown(&app->controls.mouse.right_button, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; 179 | case WM_RBUTTONUP: _mouseButtonUp( &app->controls.mouse.right_button, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; 180 | case WM_LBUTTONDBLCLK: _mouseButtonDoubleClicked(&app->controls.mouse.left_button, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; 181 | case WM_RBUTTONDBLCLK: _mouseButtonDoubleClicked(&app->controls.mouse.right_button, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; 182 | case WM_MBUTTONDBLCLK: _mouseButtonDoubleClicked(&app->controls.mouse.middle_button, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; 183 | case WM_MOUSEWHEEL: _mouseWheelScrolled((f32)(GET_WHEEL_DELTA_WPARAM(wParam)) / (f32)(WHEEL_DELTA)); break; 184 | case WM_MOUSEMOVE: 185 | _mouseMovementSet(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 186 | _mousePositionSet(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 187 | break; 188 | 189 | case WM_INPUT: 190 | if ((hasRawMouseInput(lParam)) && ( 191 | raw_inputs.data.mouse.lLastX != 0 || 192 | raw_inputs.data.mouse.lLastY != 0)) 193 | _mouseRawMovementSet( 194 | raw_inputs.data.mouse.lLastX, 195 | raw_inputs.data.mouse.lLastY 196 | ); 197 | 198 | default: 199 | return DefWindowProc(hWnd, message, wParam, lParam); 200 | } 201 | 202 | return 0; 203 | } 204 | 205 | int APIENTRY WinMain(HINSTANCE hInstance, 206 | HINSTANCE hPrevInstance, 207 | LPSTR lpCmdLine, 208 | int nCmdShow) { 209 | 210 | void* app_memory = GlobalAlloc(GPTR, sizeof(App)); 211 | if (!app_memory) 212 | return -1; 213 | 214 | app = (App*)app_memory; 215 | 216 | void* window_content_memory = GlobalAlloc(GPTR, sizeof(u32) * MAX_WIDTH * MAX_HEIGHT); 217 | if (!window_content_memory) 218 | return -1; 219 | 220 | LARGE_INTEGER performance_frequency; 221 | QueryPerformanceFrequency(&performance_frequency); 222 | Win32_ticksPerSecond = (u64)performance_frequency.QuadPart; 223 | 224 | app->controls.key_map.space = VK_SPACE; 225 | app->controls.key_map.shift = VK_SHIFT; 226 | app->controls.key_map.ctrl = VK_CONTROL; 227 | app->controls.key_map.alt = VK_MENU; 228 | app->controls.key_map.tab = VK_TAB; 229 | 230 | app->platform.ticks_per_second = Win32_ticksPerSecond; 231 | app->platform.getTicks = Win32_getTicks; 232 | app->platform.getMemory = Win32_getMemory; 233 | app->platform.setWindowTitle = Win32_setWindowTitle; 234 | app->platform.setWindowCapture = Win32_setWindowCapture; 235 | app->platform.setCursorVisibility = Win32_setCursorVisibility; 236 | app->platform.closeFile = Win32_closeFile; 237 | app->platform.openFileForReading = Win32_openFileForReading; 238 | app->platform.openFileForWriting = Win32_openFileForWriting; 239 | app->platform.readFromFile = Win32_readFromFile; 240 | app->platform.writeToFile = Win32_writeToFile; 241 | 242 | Defaults defaults; 243 | _initApp(&defaults, (u32*)window_content_memory); 244 | 245 | info.bmiHeader.biSize = sizeof(info.bmiHeader); 246 | info.bmiHeader.biCompression = BI_RGB; 247 | info.bmiHeader.biBitCount = 32; 248 | info.bmiHeader.biPlanes = 1; 249 | 250 | window_class.lpszClassName = "RnDer"; 251 | window_class.hInstance = hInstance; 252 | window_class.lpfnWndProc = WndProc; 253 | window_class.style = CS_OWNDC|CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS; 254 | window_class.hCursor = LoadCursorA(null, IDC_ARROW); 255 | 256 | if (!RegisterClassA(&window_class)) return -1; 257 | 258 | win_rect.top = 0; 259 | win_rect.left = 0; 260 | win_rect.right = defaults.width; 261 | win_rect.bottom = defaults.height; 262 | AdjustWindowRect(&win_rect, WS_OVERLAPPEDWINDOW, false); 263 | 264 | window = CreateWindowA( 265 | window_class.lpszClassName, 266 | defaults.title, 267 | WS_OVERLAPPEDWINDOW, 268 | 269 | CW_USEDEFAULT, 270 | CW_USEDEFAULT, 271 | win_rect.right - win_rect.left, 272 | win_rect.bottom - win_rect.top, 273 | 274 | null, 275 | null, 276 | hInstance, 277 | null 278 | ); 279 | if (!window) 280 | return -1; 281 | 282 | raw_input_device.usUsagePage = 0x01; 283 | raw_input_device.usUsage = 0x02; 284 | if (!RegisterRawInputDevices(&raw_input_device, 1, sizeof(raw_input_device))) 285 | return -1; 286 | 287 | win_dc = GetDC(window); 288 | 289 | SetICMMode(win_dc, ICM_OFF); 290 | 291 | 292 | 293 | ShowWindow(window, nCmdShow); 294 | 295 | MSG message; 296 | while (app->is_running) { 297 | while (PeekMessageA(&message, null, 0, 0, PM_REMOVE)) { 298 | TranslateMessage(&message); 299 | DispatchMessageA(&message); 300 | } 301 | _windowRedraw(); 302 | InvalidateRgn(window, null, false); 303 | } 304 | 305 | return 0; 306 | } -------------------------------------------------------------------------------- /src/SlimRaster/renderer/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../math/vec3.h" 4 | #include "../math/quat.h" 5 | 6 | INLINE f32 toneMappedBaked(f32 LinearColor) { 7 | f32 x = LinearColor - 0.004f; 8 | if (x < 0.0f) x = 0.0f; 9 | f32 x2_times_sholder_strength = x * x * 6.2f; 10 | return (x2_times_sholder_strength + x*0.5f)/(x2_times_sholder_strength + x*1.7f + 0.06f); 11 | } 12 | 13 | INLINE vec3 reflectWithDot(vec3 V, vec3 N, f32 NdotV) { 14 | return scaleAddVec3(N, -2 * NdotV, V); 15 | } 16 | 17 | INLINE vec3 shadePointOnSurface(Shaded *shaded, f32 NdotL) { 18 | if (shaded->has.specular) { 19 | vec3 half_vector, color; 20 | if (shaded->uses.blinn) { 21 | half_vector = normVec3(subVec3(shaded->light_direction, shaded->viewing_direction)); 22 | color = scaleVec3(shaded->material->specular, powf(DotVec3(shaded->normal, half_vector), 16.0f * shaded->material->shininess)); 23 | } else 24 | color = scaleVec3(shaded->material->specular, powf(DotVec3(shaded->reflected_direction, shaded->light_direction), 4.0f * shaded->material->shininess)); 25 | 26 | if (shaded->has.diffuse) 27 | return scaleAddVec3(shaded->diffuse, clampValue(NdotL), color); 28 | else 29 | return color; 30 | } else 31 | return scaleVec3(shaded->diffuse, clampValue(NdotL)); 32 | } -------------------------------------------------------------------------------- /src/SlimRaster/renderer/mesh_shaders.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../math/vec4.h" 4 | 5 | 6 | u8 shadeMesh(Mesh *mesh, Rasterizer *rasterizer) { 7 | 8 | // Transform the mesh's vertex positions into clip space and world space: 9 | vec4 world_space; 10 | for (u32 i = 0; i < mesh->vertex_count; i++) { 11 | world_space = Vec4fromVec3(mesh->vertex_positions[i], 1.0f); 12 | world_space = mulVec4Mat4(world_space, rasterizer->model_to_world); 13 | rasterizer->clip_space_vertex_positions[i] = mulVec4Mat4(world_space, rasterizer->world_to_clip); 14 | rasterizer->world_space_vertex_positions[i] = world_space.v3; 15 | } 16 | 17 | // Transform the mesh's vertex normals into world space: 18 | for (u32 i = 0; i < mesh->normals_count; i++) { 19 | world_space = Vec4fromVec3(mesh->vertex_normals[i], 0.0f); 20 | world_space = mulVec4Mat4(world_space, rasterizer->model_to_world_inverted_transposed); 21 | rasterizer->world_space_vertex_normals[i] = world_space.v3; 22 | } 23 | 24 | return INSIDE; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/SlimRaster/renderer/pixel_shaders.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../math/vec3.h" 4 | #include "../scene/texture.h" 5 | #include "./common.h" 6 | 7 | 8 | INLINE vec3 sampleNormal(Texture *texture, vec2 uv, vec2 dUV) { 9 | vec3 normal = sampleTexture(texture, uv, dUV).v3; 10 | f32 y = normal.z; 11 | normal.z = normal.y; 12 | normal.y = y; 13 | return normVec3(scaleAddVec3(normal, 2.0f, getVec3Of(-1.0f))); 14 | } 15 | INLINE quat getNormalRotation(vec3 normal, f32 magnitude) { 16 | quat q; 17 | normal = scaleVec3(normal, 0.25f); 18 | q.axis.x = normal.z; 19 | q.axis.y = 0; 20 | q.axis.z = normal.x; 21 | q.amount = normal.y / magnitude; 22 | return normQuat(q); 23 | } 24 | 25 | INLINE u8 getCheckerBoardPixelValueByUV(vec2 UV, f32 half_step_count) { 26 | f32 s = UV.u * half_step_count; 27 | f32 t = UV.v * half_step_count; 28 | s -= floorf(s); 29 | t -= floorf(t); 30 | return (s > 0.5f ? (u8)1 : (u8)0) ^ (t < 0.5f ? (u8)1 : (u8)0); 31 | } 32 | 33 | void shadePixelTextured(PixelShaderInputs *inputs, Scene *scene, Shaded *shaded, PixelShaderOutputs *outputs) { 34 | outputs->color = sampleTexture(scene->textures + shaded->material->texture_ids[0], inputs->UV, inputs->dUV).v3; 35 | } 36 | 37 | void shadePixelDepth(PixelShaderInputs *inputs, Scene *scene, Shaded *shaded, PixelShaderOutputs *outputs) { 38 | outputs->color = getVec3Of(inputs->depth > 10 ? 1 : inputs->depth * 0.1f); 39 | } 40 | 41 | void shadePixelUV(PixelShaderInputs *inputs, Scene *scene, Shaded *shaded, PixelShaderOutputs *outputs) { 42 | outputs->color.v2 = inputs->UV; 43 | } 44 | 45 | void shadePixelPosition(PixelShaderInputs *inputs, Scene *scene, Shaded *shaded, PixelShaderOutputs *outputs) { 46 | outputs->color = scaleVec3(addVec3(shaded->position, getVec3Of(2.0f)), 0.5f); 47 | } 48 | 49 | void shadePixelNormal(PixelShaderInputs *inputs, Scene *scene, Shaded *shaded, PixelShaderOutputs *outputs) { 50 | outputs->color = scaleAddVec3(shaded->normal, 0.5f, getVec3Of(0.5f)); 51 | } 52 | 53 | void shadePixelCheckerboard(PixelShaderInputs *inputs, Scene *scene, Shaded *shaded, PixelShaderOutputs *outputs) { 54 | outputs->color = getVec3Of(getCheckerBoardPixelValueByUV(inputs->UV, 4)); 55 | } 56 | 57 | void shadePixelClassic(PixelShaderInputs *inputs, Scene *scene, Shaded *shaded, PixelShaderOutputs *outputs) { 58 | f32 NdotL, NdotRd, squared_distance; 59 | decodeMaterialSpec(shaded->material->flags, &shaded->has, &shaded->uses); 60 | 61 | shaded->diffuse = shaded->material->diffuse; 62 | 63 | if (shaded->material->texture_count) { 64 | Texture *texture = &scene->textures[shaded->material->texture_ids[0]]; 65 | vec4 texture_sample = sampleTexture(texture, inputs->UV, inputs->dUV); 66 | shaded->diffuse = mulVec3(shaded->diffuse, texture_sample.v3); 67 | if (shaded->material->texture_count > 1) { 68 | texture = &scene->textures[shaded->material->texture_ids[1]]; 69 | texture_sample.v3 = sampleNormal(texture, inputs->UV, inputs->dUV); 70 | if (shaded->material->normal_magnitude) { 71 | quat normal_rotation = getNormalRotation(texture_sample.v3, shaded->material->normal_magnitude); 72 | shaded->normal = mulVec3Quat(shaded->normal, normal_rotation); 73 | } 74 | } 75 | } 76 | 77 | outputs->color = scene->ambient_light.color; 78 | shaded->viewing_direction = normVec3(subVec3(shaded->position, shaded->viewing_origin)); 79 | if (shaded->uses.phong) { 80 | NdotRd = DotVec3(shaded->normal, shaded->viewing_direction); 81 | shaded->reflected_direction = reflectWithDot(shaded->viewing_direction, shaded->normal, NdotRd); 82 | } 83 | 84 | Light *light = scene->lights; 85 | for (u32 i = 0; i < scene->settings.lights; i++, light++) { 86 | shaded->light_direction = subVec3(light->position_or_direction, shaded->position); 87 | squared_distance = squaredLengthVec3(shaded->light_direction); 88 | shaded->light_direction = scaleVec3(shaded->light_direction, 1.0f / sqrtf(squared_distance)); 89 | NdotL = dotVec3(shaded->normal, shaded->light_direction); 90 | if (NdotL > 0) 91 | outputs->color = mulAddVec3( 92 | shadePointOnSurface(shaded, NdotL), 93 | scaleVec3(light->color, light->intensity / squared_distance), 94 | outputs->color); 95 | } 96 | 97 | outputs->color.x = toneMappedBaked(outputs->color.x); 98 | outputs->color.y = toneMappedBaked(outputs->color.y); 99 | outputs->color.z = toneMappedBaked(outputs->color.z); 100 | } 101 | 102 | void shadePixelClassicCheckerboard(PixelShaderInputs *inputs, Scene *scene, Shaded *shaded, PixelShaderOutputs *outputs) { 103 | shadePixelClassic(inputs, scene, shaded, outputs); 104 | 105 | if (!getCheckerBoardPixelValueByUV(inputs->UV, 4)) 106 | outputs->color = scaleVec3(outputs->color, 0.5f); 107 | } -------------------------------------------------------------------------------- /src/SlimRaster/scene/box.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/init.h" 4 | #include "../math/quat.h" 5 | #include "../math/vec3.h" 6 | #include "../shapes/edge.h" 7 | #include "./primitive.h" 8 | 9 | void transformBoxVerticesFromObjectToViewSpace(BoxVertices *vertices, BoxVertices *transformed_vertices, Primitive *primitive, Viewport *viewport) { 10 | vec3 position; 11 | for (u8 i = 0; i < BOX__VERTEX_COUNT; i++) { 12 | position = vertices->buffer[i]; 13 | position = convertPositionToWorldSpace(position, primitive); 14 | position = subVec3( position, viewport->camera->transform.position); 15 | position = mulVec3Quat(position, viewport->camera->transform.rotation_inverted); 16 | transformed_vertices->buffer[i] = position; 17 | } 18 | } 19 | 20 | void drawBox(Box *box, u8 sides, Primitive *primitive, vec3 color, f32 opacity, u8 line_width, Viewport *viewport) { 21 | // Transform vertices positions from local-space to world-space and then to view-space: 22 | static BoxVertices vertices; 23 | transformBoxVerticesFromObjectToViewSpace(&box->vertices, &vertices, primitive, viewport); 24 | 25 | // Distribute transformed vertices positions to edges: 26 | static BoxEdges edges; 27 | setBoxEdgesFromVertices(&edges, &vertices); 28 | 29 | if (sides == BOX__ALL_SIDES) { 30 | for (u8 i = 0; i < BOX__EDGE_COUNT; i++) 31 | drawEdge(edges.buffer + i, color, opacity, line_width, viewport); 32 | } else { 33 | if (sides & Front | sides & Top ) drawEdge(&edges.sides.front_top, color, opacity, line_width, viewport); 34 | if (sides & Front | sides & Bottom) drawEdge(&edges.sides.front_bottom, color, opacity, line_width, viewport); 35 | if (sides & Front | sides & Left ) drawEdge(&edges.sides.front_left, color, opacity, line_width, viewport); 36 | if (sides & Front | sides & Right ) drawEdge(&edges.sides.front_right, color, opacity, line_width, viewport); 37 | if (sides & Back | sides & Top ) drawEdge(&edges.sides.back_top, color, opacity, line_width, viewport); 38 | if (sides & Back | sides & Bottom) drawEdge(&edges.sides.back_bottom, color, opacity, line_width, viewport); 39 | if (sides & Back | sides & Left ) drawEdge(&edges.sides.back_left, color, opacity, line_width, viewport); 40 | if (sides & Back | sides & Right ) drawEdge(&edges.sides.back_right, color, opacity, line_width, viewport); 41 | if (sides & Left | sides & Top ) drawEdge(&edges.sides.left_top, color, opacity, line_width, viewport); 42 | if (sides & Left | sides & Bottom) drawEdge(&edges.sides.left_bottom, color, opacity, line_width, viewport); 43 | if (sides & Right | sides & Top ) drawEdge(&edges.sides.right_top, color, opacity, line_width, viewport); 44 | if (sides & Right | sides & Bottom) drawEdge(&edges.sides.right_bottom, color, opacity, line_width, viewport); 45 | } 46 | } 47 | 48 | void drawCamera(Camera *camera, vec3 color, f32 opacity, u8 line_width, Viewport *viewport) { 49 | static Box box; 50 | static Primitive primitive; 51 | initBox(&box); 52 | primitive.flags = ALL_FLAGS; 53 | primitive.rotation = camera->transform.rotation; 54 | primitive.position = camera->transform.position; 55 | primitive.scale.x = primitive.scale.y = primitive.scale.z = 1; 56 | drawBox(&box, BOX__ALL_SIDES, &primitive, color, opacity, line_width, viewport); 57 | box.vertices.corners.back_bottom_left = scaleVec3(box.vertices.corners.back_bottom_left, 0.5f); 58 | box.vertices.corners.back_bottom_right = scaleVec3(box.vertices.corners.back_bottom_right, 0.5f); 59 | box.vertices.corners.back_top_left = scaleVec3(box.vertices.corners.back_top_left, 0.5f); 60 | box.vertices.corners.back_top_right = scaleVec3(box.vertices.corners.back_top_right, 0.5f); 61 | box.vertices.corners.front_bottom_left = scaleVec3(box.vertices.corners.front_bottom_left, 2); 62 | box.vertices.corners.front_bottom_right = scaleVec3(box.vertices.corners.front_bottom_right, 2); 63 | box.vertices.corners.front_top_left = scaleVec3(box.vertices.corners.front_top_left, 2); 64 | box.vertices.corners.front_top_right = scaleVec3(box.vertices.corners.front_top_right, 2); 65 | for (u8 i = 0; i < BOX__VERTEX_COUNT; i++) 66 | box.vertices.buffer[i].z += 1.5f; 67 | drawBox(&box, BOX__ALL_SIDES, &primitive, color, opacity, line_width, viewport); 68 | } -------------------------------------------------------------------------------- /src/SlimRaster/scene/cube.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | 5 | 6 | #define CUBE__UV_COUNT 4 7 | #define CUBE__NORMAL_COUNT 6 8 | #define CUBE__VERTEX_COUNT 8 9 | #define CUBE__TRIANGLE_COUNT 12 10 | 11 | static const vec3 CUBE__VERTEX_POSITIONS[CUBE__VERTEX_COUNT] = { 12 | {-1, -1, -1}, 13 | {1, -1, -1}, 14 | {1, 1, -1}, 15 | {-1, 1, -1}, 16 | {-1, -1, 1}, 17 | {1, -1, 1}, 18 | {1, 1, 1}, 19 | {-1, 1, 1} 20 | }; 21 | 22 | static const TriangleVertexIndices CUBE__VERTEX_POSITION_INDICES[CUBE__TRIANGLE_COUNT] = { 23 | {0, 1, 2}, 24 | {1, 5, 6}, 25 | {5, 4, 7}, 26 | {4, 0, 3}, 27 | {3, 2, 6}, 28 | {1, 0, 4}, 29 | {0, 2, 3}, 30 | {1, 6, 2}, 31 | {5, 7, 6}, 32 | {4, 3, 7}, 33 | {3, 6, 7}, 34 | {1, 4, 5} 35 | }; 36 | 37 | static const vec3 CUBE__VERTEX_NORMALS[CUBE__NORMAL_COUNT] = { 38 | {0, 0, -1}, 39 | {1, 0, 0}, 40 | {0, 0, 1}, 41 | {-1, 0, 0}, 42 | {0, 1, 0}, 43 | {0, -1, 0} 44 | }; 45 | static const TriangleVertexIndices CUBE__VERTEX_NORMAL_INDICES[CUBE__TRIANGLE_COUNT] = { 46 | {0, 0, 0}, 47 | {1, 1, 1}, 48 | {2, 2, 2}, 49 | {3, 3, 3}, 50 | {4, 4, 4}, 51 | {5, 5, 5}, 52 | {0, 0, 0}, 53 | {1, 1, 1}, 54 | {2, 2, 2}, 55 | {3, 3, 3}, 56 | {4, 4, 4}, 57 | {5, 5, 5} 58 | }; 59 | 60 | static const vec2 CUBE__VERTEX_UVS[CUBE__UV_COUNT] = { 61 | {0, 0}, 62 | {0, 1}, 63 | {1, 1}, 64 | {1, 0}, 65 | }; 66 | static const TriangleVertexIndices CUBE__VERTEX_UV_INDICES[CUBE__TRIANGLE_COUNT] = { 67 | {0, 1, 2}, 68 | {0, 1, 2}, 69 | {0, 1, 2}, 70 | {0, 1, 2}, 71 | {0, 1, 2}, 72 | {0, 1, 2}, 73 | {0, 2, 3}, 74 | {0, 2, 3}, 75 | {0, 2, 3}, 76 | {0, 2, 3}, 77 | {0, 2, 3}, 78 | {0, 2, 3} 79 | }; 80 | 81 | void setMeshToCube(Mesh *mesh) { 82 | mesh->triangle_count = CUBE__TRIANGLE_COUNT; 83 | mesh->vertex_count = CUBE__VERTEX_COUNT; 84 | mesh->normals_count = CUBE__NORMAL_COUNT; 85 | mesh->uvs_count = CUBE__UV_COUNT; 86 | 87 | mesh->vertex_uvs = (vec2*)CUBE__VERTEX_UVS; 88 | mesh->vertex_normals = (vec3*)CUBE__VERTEX_NORMALS; 89 | mesh->vertex_positions = (vec3*)CUBE__VERTEX_POSITIONS; 90 | 91 | mesh->vertex_uvs_indices = (TriangleVertexIndices*)CUBE__VERTEX_UV_INDICES; 92 | mesh->vertex_normal_indices = (TriangleVertexIndices*)CUBE__VERTEX_NORMAL_INDICES; 93 | mesh->vertex_position_indices = (TriangleVertexIndices*)CUBE__VERTEX_POSITION_INDICES; 94 | } -------------------------------------------------------------------------------- /src/SlimRaster/scene/curve.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/types.h" 4 | #include "../math/vec3.h" 5 | #include "../math/mat3.h" 6 | #include "../math/quat.h" 7 | #include "../shapes/edge.h" 8 | #include "./primitive.h" 9 | 10 | #define CURVE_STEPS 3600 11 | 12 | void drawCurve(Curve *curve, u32 step_count, Primitive *primitive, vec3 color, f32 opacity, u8 line_width, Viewport *viewport) { 13 | f32 one_over_step_count = 1.0f / (f32)step_count; 14 | f32 rotation_step = one_over_step_count * TAU; 15 | f32 rotation_step_times_rev_count = rotation_step * (f32)curve->revolution_count; 16 | 17 | if (primitive->type == PrimitiveType_Helix) 18 | rotation_step = rotation_step_times_rev_count; 19 | 20 | vec3 center_to_orbit; 21 | center_to_orbit.x = 1; 22 | center_to_orbit.y = center_to_orbit.z = 0; 23 | 24 | vec3 orbit_to_curve; 25 | orbit_to_curve.x = curve->thickness; 26 | orbit_to_curve.y = orbit_to_curve.z = 0; 27 | 28 | mat3 rotation; 29 | rotation.X.x = rotation.Z.z = cosf(rotation_step); 30 | rotation.X.z = sinf(rotation_step); 31 | rotation.Z.x = -rotation.X.z; 32 | rotation.X.y = rotation.Z.y = rotation.Y.x = rotation.Y.z = 0; 33 | rotation.Y.y = 1; 34 | 35 | mat3 orbit_to_curve_rotation; 36 | if (primitive->type == PrimitiveType_Coil) { 37 | orbit_to_curve_rotation.X.x = orbit_to_curve_rotation.Y.y = cosf(rotation_step_times_rev_count); 38 | orbit_to_curve_rotation.X.y = sinf(rotation_step_times_rev_count); 39 | orbit_to_curve_rotation.Y.x = -orbit_to_curve_rotation.X.y; 40 | orbit_to_curve_rotation.X.z = orbit_to_curve_rotation.Y.z = orbit_to_curve_rotation.Z.x = orbit_to_curve_rotation.Z.y = 0; 41 | orbit_to_curve_rotation.Z.z = 1; 42 | } 43 | 44 | // Transform vertices positions of edges from view-space to screen-space (w/ culling and clipping): 45 | mat3 accumulated_orbit_rotation = rotation; 46 | vec3 current_position, previous_position; 47 | Edge edge; 48 | 49 | for (u32 i = 0; i < step_count; i++) { 50 | center_to_orbit = mulVec3Mat3(center_to_orbit, rotation); 51 | 52 | switch (primitive->type) { 53 | case PrimitiveType_Helix: 54 | current_position = center_to_orbit; 55 | current_position.y -= 1; 56 | break; 57 | case PrimitiveType_Coil: 58 | orbit_to_curve = mulVec3Mat3(orbit_to_curve, orbit_to_curve_rotation); 59 | current_position = mulVec3Mat3(orbit_to_curve, accumulated_orbit_rotation); 60 | current_position = addVec3(center_to_orbit, current_position); 61 | break; 62 | default: 63 | break; 64 | } 65 | 66 | current_position = convertPositionToWorldSpace(current_position, primitive); 67 | current_position = subVec3( current_position, viewport->camera->transform.position); 68 | current_position = mulVec3Quat(current_position, viewport->camera->transform.rotation_inverted); 69 | 70 | if (i) { 71 | edge.from = previous_position; 72 | edge.to = current_position; 73 | drawEdge(&edge, color, opacity, line_width, viewport); 74 | } 75 | 76 | switch (primitive->type) { 77 | case PrimitiveType_Helix: 78 | center_to_orbit.y += 2 * one_over_step_count; 79 | break; 80 | case PrimitiveType_Coil: 81 | accumulated_orbit_rotation = mulMat3(accumulated_orbit_rotation, rotation); 82 | break; 83 | default: 84 | break; 85 | } 86 | 87 | previous_position = current_position; 88 | } 89 | } -------------------------------------------------------------------------------- /src/SlimRaster/scene/grid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/types.h" 4 | #include "../math/vec3.h" 5 | #include "../shapes/edge.h" 6 | #include "./primitive.h" 7 | 8 | void transformGridVerticesFromObjectToViewSpace(Grid *grid, GridVertices *transformed_vertices, Primitive *primitive, Viewport *viewport) { 9 | vec3 position; 10 | for (u8 side = 0; side < 2; side++) { 11 | for (u8 axis = 0; axis < 2; axis++) { 12 | u8 segment_count = axis ? grid->v_segments : grid->u_segments; 13 | for (u8 segment = 0; segment < segment_count; segment++) { 14 | position = grid->vertices.buffer[axis][side][segment]; 15 | position = convertPositionToWorldSpace(position, primitive); 16 | position = subVec3( position, viewport->camera->transform.position); 17 | position = mulVec3Quat(position, viewport->camera->transform.rotation_inverted); 18 | transformed_vertices->buffer[axis][side][segment] = position; 19 | } 20 | } 21 | } 22 | } 23 | 24 | void drawGrid(Grid *grid, Primitive *primitive, vec3 color, f32 opacity, u8 line_width, Viewport *viewport) { 25 | // Transform vertices positions from local-space to world-space and then to view-space: 26 | static GridVertices vertices; 27 | 28 | transformGridVerticesFromObjectToViewSpace(grid, &vertices, primitive, viewport); 29 | 30 | // Distribute transformed vertices positions to edges: 31 | static GridEdges edges; 32 | setGridEdgesFromVertices(edges.uv.u, grid->u_segments, vertices.uv.u.from, vertices.uv.u.to); 33 | setGridEdgesFromVertices(edges.uv.v, grid->v_segments, vertices.uv.v.from, vertices.uv.v.to); 34 | 35 | for (u8 u = 0; u < grid->u_segments; u++) drawEdge(edges.uv.u + u, color, opacity, line_width, viewport); 36 | for (u8 v = 0; v < grid->v_segments; v++) drawEdge(edges.uv.v + v, color, opacity, line_width, viewport); 37 | } -------------------------------------------------------------------------------- /src/SlimRaster/scene/mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | #include "../core/types.h" 5 | #include "../shapes/edge.h" 6 | #include "./primitive.h" 7 | 8 | void drawMesh(Mesh *mesh, bool draw_normals, Primitive *primitive, vec3 color, f32 opacity, u8 line_width, Viewport *viewport) { 9 | EdgeVertexIndices *edge_vertex_indices = mesh->edge_vertex_indices; 10 | quat cam_rot = viewport->camera->transform.rotation_inverted; 11 | vec3 cam_pos = viewport->camera->transform.position; 12 | vec3 *positions = mesh->vertex_positions; 13 | vec3 position; 14 | Edge edge; 15 | for (u32 i = 0; i < mesh->edge_count; i++, edge_vertex_indices++) { 16 | position = positions[edge_vertex_indices->from]; 17 | position = convertPositionToWorldSpace(position, primitive); 18 | position = subVec3(position, cam_pos); 19 | position = mulVec3Quat(position, cam_rot); 20 | edge.from = position; 21 | 22 | position = positions[edge_vertex_indices->to]; 23 | position = convertPositionToWorldSpace(position, primitive); 24 | position = subVec3(position, cam_pos); 25 | position = mulVec3Quat(position, cam_rot); 26 | edge.to = position; 27 | 28 | drawEdge(&edge, color, opacity, line_width, viewport); 29 | } 30 | 31 | if (draw_normals && mesh->normals_count && mesh->vertex_normals && mesh->vertex_normal_indices) { 32 | TriangleVertexIndices *vertex_normal_indices = mesh->vertex_normal_indices; 33 | TriangleVertexIndices *vertex_position_indices = mesh->vertex_position_indices; 34 | vec3 *normals = mesh->vertex_normals; 35 | for (u32 t = 0; t < mesh->triangle_count; t++, vertex_normal_indices++, vertex_position_indices++) { 36 | for (u8 i = 0; i < 3; i++) { 37 | position = positions[vertex_position_indices->ids[i]]; 38 | edge.from = convertPositionToWorldSpace(position, primitive); 39 | edge.from = subVec3(edge.from, cam_pos); 40 | edge.from = mulVec3Quat(edge.from, cam_rot); 41 | 42 | edge.to = addVec3(position, scaleVec3(normals[vertex_normal_indices->ids[i]], 0.1f)); 43 | edge.to = convertPositionToWorldSpace(edge.to, primitive); 44 | edge.to = subVec3(edge.to, cam_pos); 45 | edge.to = mulVec3Quat(edge.to, cam_rot); 46 | 47 | drawEdge(&edge, Color(Red), opacity * 0.5f, line_width, viewport); 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/SlimRaster/scene/primitive.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/types.h" 4 | #include "../math/vec3.h" 5 | #include "../math/mat3.h" 6 | #include "../math/mat4.h" 7 | #include "../math/quat.h" 8 | 9 | INLINE void convertPositionAndDirectionToObjectSpace( 10 | vec3 position, 11 | vec3 dir, 12 | Primitive *primitive, 13 | vec3 *out_position, 14 | vec3 *out_direction 15 | ) { 16 | *out_position = primitive->flags & IS_TRANSLATED ? 17 | subVec3(position, primitive->position) : 18 | position; 19 | 20 | if (primitive->flags & IS_ROTATED) { 21 | quat inv_rotation = conjugate(primitive->rotation); 22 | *out_position = mulVec3Quat(*out_position, inv_rotation); 23 | *out_direction = mulVec3Quat(dir, inv_rotation); 24 | } else 25 | *out_direction = dir; 26 | 27 | if (primitive->flags & IS_SCALED) { 28 | vec3 inv_scale = oneOverVec3(primitive->scale); 29 | *out_position = mulVec3(*out_position, inv_scale); 30 | if (primitive->flags & IS_SCALED_NON_UNIFORMLY) 31 | *out_direction = normVec3(mulVec3(*out_direction, inv_scale)); 32 | } 33 | } 34 | INLINE mat4 getPrimitiveTransformationMatrix(Primitive *primitive) { 35 | mat3 rotation_matrix = transposedMat3(convertQuaternionToRotationMatrix(primitive->rotation)); 36 | 37 | rotation_matrix.X = scaleVec3(rotation_matrix.X, primitive->scale.x); 38 | rotation_matrix.Y = scaleVec3(rotation_matrix.Y, primitive->scale.y); 39 | rotation_matrix.Z = scaleVec3(rotation_matrix.Z, primitive->scale.z); 40 | 41 | mat4 matrix = mat4fromMat3(rotation_matrix); 42 | matrix.W = Vec4fromVec3(primitive->position, 1); 43 | 44 | return matrix; 45 | } 46 | 47 | INLINE vec3 convertPositionToWorldSpace(vec3 position, Primitive *primitive) { 48 | if (primitive->flags & IS_SCALED) position = mulVec3( position, primitive->scale); 49 | if (primitive->flags & IS_ROTATED) position = mulVec3Quat(position, primitive->rotation); 50 | if (primitive->flags & IS_TRANSLATED) position = addVec3( position, primitive->position); 51 | return position; 52 | } 53 | INLINE vec3 convertPositionToObjectSpace(vec3 position, Primitive *primitive) { 54 | if (primitive->flags & IS_TRANSLATED) position = subVec3( position, primitive->position); 55 | if (primitive->flags & IS_ROTATED) position = mulVec3Quat(position, conjugate(primitive->rotation)); 56 | if (primitive->flags & IS_SCALED) position = mulVec3(position, oneOverVec3(primitive->scale)); 57 | return position; 58 | } 59 | 60 | INLINE vec3 convertDirectionToWorldSpace(vec3 direction, Primitive *primitive) { 61 | if (primitive->flags & IS_SCALED_NON_UNIFORMLY) direction = mulVec3(direction, oneOverVec3(primitive->scale)); 62 | if (primitive->flags & IS_ROTATED) direction = mulVec3Quat(direction, primitive->rotation); 63 | return direction; 64 | } -------------------------------------------------------------------------------- /src/SlimRaster/scene/texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/types.h" 4 | 5 | INLINE vec4 sampleTextureMip(TextureMip *mip, vec2 UV) { 6 | f32 u = UV.u; 7 | f32 v = UV.v; 8 | if (u > 1) u -= (f32)((u32)u); 9 | if (v > 1) v -= (f32)((u32)v); 10 | 11 | const f32 U = u * (f32)mip->width + 0.5f; 12 | const f32 V = v * (f32)mip->height + 0.5f; 13 | const u32 x = (u32)U; 14 | const u32 y = (u32)V; 15 | const f32 r = U - (f32)x; 16 | const f32 b = V - (f32)y; 17 | const f32 l = 1 - r; 18 | const f32 t = 1 - b; 19 | const f32 tl = t * l * COLOR_COMPONENT_TO_FLOAT; 20 | const f32 tr = t * r * COLOR_COMPONENT_TO_FLOAT; 21 | const f32 bl = b * l * COLOR_COMPONENT_TO_FLOAT; 22 | const f32 br = b * r * COLOR_COMPONENT_TO_FLOAT; 23 | 24 | const TexelQuad texel_quad = mip->texel_quads[y * (mip->width + 1) + x]; 25 | return Vec4( 26 | fast_mul_add((f32)texel_quad.R.BR, br, fast_mul_add((f32)texel_quad.R.BL, bl, fast_mul_add((f32)texel_quad.R.TR, tr, (f32)texel_quad.R.TL * tl))), 27 | fast_mul_add((f32)texel_quad.G.BR, br, fast_mul_add((f32)texel_quad.G.BL, bl, fast_mul_add((f32)texel_quad.G.TR, tr, (f32)texel_quad.G.TL * tl))), 28 | fast_mul_add((f32)texel_quad.B.BR, br, fast_mul_add((f32)texel_quad.B.BL, bl, fast_mul_add((f32)texel_quad.B.TR, tr, (f32)texel_quad.B.TL * tl))), 29 | 1.0f); 30 | } 31 | 32 | INLINE vec4 sampleTexture(Texture *texture, vec2 UV, vec2 dUV) { 33 | u8 mip_level = 0; 34 | if (texture->mipmap) { 35 | const f32 pixel_width = dUV.u * (f32)texture->width; 36 | const f32 pixel_height = dUV.v * (f32)texture->height; 37 | f32 pixel_size = (f32)(pixel_width + pixel_height) * 0.5f; 38 | const u8 last_mip = texture->mip_count - 1; 39 | 40 | while (pixel_size > 1 && mip_level < last_mip) { 41 | pixel_size /= 2; 42 | mip_level += 1; 43 | } 44 | } 45 | 46 | return sampleTextureMip(texture->mips + mip_level, UV); 47 | } -------------------------------------------------------------------------------- /src/SlimRaster/scene/xform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | #include "../math/vec3.h" 5 | #include "../math/mat3.h" 6 | #include "../math/quat.h" 7 | 8 | INLINE void rotateXform3(xform3 *xform, f32 yaw, f32 pitch, f32 roll) { 9 | if (yaw) yawMat3( yaw, &xform->yaw_matrix); 10 | if (pitch) pitchMat3(pitch, &xform->pitch_matrix); 11 | if (roll) rollMat3( roll, &xform->roll_matrix); 12 | 13 | xform->rotation_matrix = mulMat3(mulMat3(xform->pitch_matrix, xform->yaw_matrix), xform->roll_matrix); 14 | xform->rotation_matrix_inverted = transposedMat3(xform->rotation_matrix); 15 | 16 | xform->rotation = convertRotationMatrixToQuaternion(xform->rotation_matrix); 17 | xform->rotation_inverted = convertRotationMatrixToQuaternion(xform->rotation_matrix_inverted); 18 | 19 | xform->matrix = mulMat3(xform->matrix, xform->rotation_matrix); 20 | } -------------------------------------------------------------------------------- /src/SlimRaster/shapes/circle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | // 5 | //void drawCircle(Viewport *viewport, vec3 color, f32 opacity, i32 center_x, i32 center_y, i32 radius) { 6 | // if (radius <= 1) { 7 | // if (inRange(0, center_x, viewport->dimensions.width - 1) && 8 | // inRange(0, center_y, viewport->dimensions.height - 1)) 9 | // setPixel(viewport, center_y, center_x, color, opacity, 0); 10 | // 11 | // return; 12 | // } 13 | // 14 | // i32 width = viewport->dimensions.width; 15 | // i32 size = viewport->dimensions.width_times_height; 16 | // 17 | // i32 x = radius, y = 0, y2 = 0; 18 | // i32 r2 = radius * radius; 19 | // i32 x2 = r2; 20 | // 21 | // i32 Sx1 = center_x - radius; 22 | // i32 Ex1 = center_x + radius; 23 | // i32 Sy1 = center_y * width; 24 | // i32 Ey1 = Sy1; 25 | // 26 | // i32 Sx2 = center_x; 27 | // i32 Ex2 = center_x; 28 | // i32 Sy2 = (center_y - radius) * width; 29 | // i32 Ey2 = (center_y + radius) * width; 30 | // 31 | // while (y <= x) { 32 | // if (Sy1 >= 0 && Sy1 < size) { 33 | // if (Sx1 >= 0 && Sx1 < width) setPixel(canvas->float_pixels + (Sy1 + Sx1), color, opacity, 0); 34 | // if (Ex1 >= 0 && Ex1 < width) setPixel(canvas->float_pixels + (Sy1 + Ex1), color, opacity, 0); 35 | // } 36 | // if (Ey1 >= 0 && Ey1 < size) { 37 | // if (Sx1 >= 0 && Sx1 < width) setPixel(canvas->float_pixels + (Ey1 + Sx1), color, opacity, 0); 38 | // if (Ex1 >= 0 && Ex1 < width) setPixel(canvas->float_pixels + (Ey1 + Ex1), color, opacity, 0); 39 | // } 40 | // 41 | // if (Sy2 >= 0 && Sy2 < size) { 42 | // if (Sx2 >= 0 && Sx2 < width) setPixel(canvas->float_pixels + (Sy2 + Sx2), color, opacity, 0); 43 | // if (Ex2 >= 0 && Ex2 < width) setPixel(canvas->float_pixels + (Sy2 + Ex2), color, opacity, 0); 44 | // } 45 | // if (Ey2 >= 0 && Ey2 < size) { 46 | // if (Sx2 >= 0 && Sx2 < width) setPixel(canvas->float_pixels + (Ey2 + Sx2), color, opacity, 0); 47 | // if (Ex2 >= 0 && Ex2 < width) setPixel(canvas->float_pixels + (Ey2 + Ex2), color, opacity, 0); 48 | // } 49 | // 50 | // if ((x2 + y2) > r2) { 51 | // x -= 1; 52 | // x2 = x * x; 53 | // 54 | // Sx1 += 1; 55 | // Ex1 -= 1; 56 | // 57 | // Sy2 += width; 58 | // Ey2 -= width; 59 | // } 60 | // 61 | // y += 1; 62 | // y2 = y * y; 63 | // 64 | // Sy1 -= width; 65 | // Ey1 += width; 66 | // 67 | // Sx2 -= 1; 68 | // Ex2 += 1; 69 | // } 70 | //} 71 | // 72 | //void fillCircle2D(PixelGrid *canvas, RGBA color, i32 center_x, i32 center_y, i32 radius) { 73 | // if (radius <= 1) { 74 | // if (inRange(0, center_x, canvas->dimensions.width - 1) && 75 | // inRange(0, center_y, canvas->dimensions.height - 1)) 76 | // setPixel(canvas->float_pixels + (canvas->dimensions.width * center_y + center_x), color, opacity, 0); 77 | // 78 | // return; 79 | // } 80 | // 81 | // i32 width = canvas->dimensions.width; 82 | // i32 size = canvas->dimensions.width_times_height; 83 | // 84 | // i32 x = radius, y = 0, y2 = 0; 85 | // i32 r2 = radius * radius; 86 | // i32 x2 = r2; 87 | // 88 | // i32 Sx1 = center_x - radius; 89 | // i32 Ex1 = center_x + radius; 90 | // i32 Sy1 = center_y * width; 91 | // i32 Ey1 = Sy1; 92 | // 93 | // i32 Sx2 = center_x; 94 | // i32 Ex2 = center_x; 95 | // i32 Sy2 = (center_y - radius) * width; 96 | // i32 Ey2 = (center_y + radius) * width; 97 | // 98 | // i32 i, start, end; 99 | // 100 | // while (y <= x) { 101 | // start = Sx1 > 0 ? Sx1 : 0; 102 | // end = Ex1 < (width - 1) ? Ex1 : (width - 1); 103 | // if (Sy1 >= 0 && Sy1 < size) for (i = start; i <= end; i++) setPixel(canvas->float_pixels + (Sy1 + i), color, opacity, 0); 104 | // if (Ey1 >= 0 && Ey1 < size) for (i = start; i <= end; i++) setPixel(canvas->float_pixels + (Ey1 + i), color, opacity, 0); 105 | // 106 | // start = Sx2 > 0 ? Sx2 : 0; 107 | // end = Ex2 < (width - 1) ? Ex2 : (width - 1); 108 | // if (Sy2 >= 0 && Sy2 < size) for (i = start; i <= end; i++) setPixel(canvas->float_pixels + (Sy2 + i), color, opacity, 0); 109 | // if (Ey2 >= 0 && Ey2 < size) for (i = start; i <= end; i++) setPixel(canvas->float_pixels + (Ey2 + i), color, opacity, 0); 110 | // 111 | // if ((x2 + y2) > r2) { 112 | // x -= 1; 113 | // x2 = x * x; 114 | // 115 | // Sx1 += 1; 116 | // Ex1 -= 1; 117 | // 118 | // Sy2 += width; 119 | // Ey2 -= width; 120 | // } 121 | // 122 | // y += 1; 123 | // y2 = y * y; 124 | // 125 | // Sy1 -= width; 126 | // Ey1 += width; 127 | // 128 | // Sx2 -= 1; 129 | // Ex2 += 1; 130 | // } 131 | //} -------------------------------------------------------------------------------- /src/SlimRaster/shapes/edge.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/types.h" 4 | #include "../math/vec3.h" 5 | #include "../math/vec4.h" 6 | #include "./line.h" 7 | 8 | bool cullAndClipEdge(Edge *edge, Viewport *viewport) { 9 | f32 distance = viewport->settings.near_clipping_plane_distance; 10 | vec3 A = edge->from; 11 | vec3 B = edge->to; 12 | 13 | u8 out = (A.z < distance) | ((B.z < distance) << 1); 14 | if (out) { 15 | if (out == 3) return false; 16 | if (out & 1) A = lerpVec3(A, B, (distance - A.z) / (B.z - A.z)); 17 | else B = lerpVec3(B, A, (distance - B.z) / (A.z - B.z)); 18 | } 19 | 20 | distance = viewport->settings.far_clipping_plane_distance; 21 | out = (A.z > distance) | ((B.z > distance) << 1); 22 | if (out) { 23 | if (out == 3) return false; 24 | if (out & 1) A = lerpVec3(A, B, (A.z - distance) / (A.z - B.z)); 25 | else B = lerpVec3(B, A, (B.z - distance) / (B.z - A.z)); 26 | } 27 | 28 | // Left plane (facing to the right): 29 | vec3 N = Vec3(viewport->camera->focal_length, 0, viewport->dimensions.width_over_height); 30 | f32 NdotA = dotVec3(N, A); 31 | f32 NdotB = dotVec3(N, B); 32 | 33 | out = (NdotA < 0) | ((NdotB < 0) << 1); 34 | if (out) { 35 | if (out == 3) return false; 36 | if (out & 1) A = lerpVec3(A, B, NdotA / (NdotA - NdotB)); 37 | else B = lerpVec3(B, A, NdotB / (NdotB - NdotA)); 38 | } 39 | 40 | // Right plane (facing to the left): 41 | N.x = -N.x; 42 | NdotA = dotVec3(N, A); 43 | NdotB = dotVec3(N, B); 44 | 45 | out = (NdotA < 0) | ((NdotB < 0) << 1); 46 | if (out) { 47 | if (out == 3) return false; 48 | if (out & 1) A = lerpVec3(A, B, NdotA / (NdotA - NdotB)); 49 | else B = lerpVec3(B, A, NdotB / (NdotB - NdotA)); 50 | } 51 | 52 | // Bottom plane (facing up): 53 | N = Vec3(0, viewport->camera->focal_length, 1); 54 | NdotA = dotVec3(N, A); 55 | NdotB = dotVec3(N, B); 56 | 57 | out = (NdotA < 0) | ((NdotB < 0) << 1); 58 | if (out) { 59 | if (out == 3) return false; 60 | if (out & 1) A = lerpVec3(A, B, NdotA / (NdotA - NdotB)); 61 | else B = lerpVec3(B, A, NdotB / (NdotB - NdotA)); 62 | } 63 | 64 | // Top plane (facing down): 65 | N.y = -N.y; 66 | NdotA = dotVec3(N, A); 67 | NdotB = dotVec3(N, B); 68 | 69 | out = (NdotA < 0) | ((NdotB < 0) << 1); 70 | if (out) { 71 | if (out == 3) return false; 72 | if (out & 1) A = lerpVec3(A, B, NdotA / (NdotA - NdotB)); 73 | else B = lerpVec3(B, A, NdotB / (NdotB - NdotA)); 74 | } 75 | 76 | edge->from = A; 77 | edge->to = B; 78 | 79 | return true; 80 | } 81 | 82 | INLINE bool projectEdge(Edge *edge, Viewport *viewport) { 83 | if (!cullAndClipEdge(edge, viewport)) 84 | return false; 85 | 86 | vec4 A = mulVec4Mat4(Vec4fromVec3(edge->from, 1.0f), viewport->projection_matrix); 87 | vec4 B = mulVec4Mat4(Vec4fromVec3(edge->to, 1.0f), viewport->projection_matrix); 88 | 89 | // Project: 90 | edge->from = scaleVec3(Vec3fromVec4(A), 1.0f / A.w); 91 | edge->to = scaleVec3(Vec3fromVec4(B), 1.0f / B.w); 92 | 93 | // NDC->screen: 94 | edge->from.x += 1; edge->from.x *= viewport->dimensions.h_width; 95 | edge->to.x += 1; edge->to.x *= viewport->dimensions.h_width; 96 | edge->from.y += 1; edge->from.y *= viewport->dimensions.h_height; 97 | edge->to.y += 1; edge->to.y *= viewport->dimensions.h_height; 98 | 99 | // Flip Y: 100 | edge->from.y = viewport->dimensions.f_height - edge->from.y; 101 | edge->to.y = viewport->dimensions.f_height - edge->to.y; 102 | 103 | return true; 104 | } 105 | 106 | INLINE void drawEdge(Edge *edge, vec3 color, f32 opacity, u8 line_width, Viewport *viewport) { 107 | if (projectEdge(edge, viewport)) 108 | drawLine(edge->from.x, edge->from.y, edge->from.z, 109 | edge->to.x, edge->to.y, edge->to.z, 110 | color, opacity, line_width, viewport); 111 | } 112 | 113 | // 114 | //INLINE f32 clipFactor(f32 Aw, f32 Bw, f32 a, f32 b, bool negative_w) { 115 | // if (negative_w) 116 | // b = -b; 117 | // else 118 | // a = -a; 119 | // 120 | // f32 numerator = Aw + a; 121 | // return numerator / (numerator - Bw + b); 122 | //} 123 | // 124 | //INLINE bool cullAndClipEdgeInClipSpace(vec4 *A, vec4 *B) { 125 | // u8 A_flags = (A->x > A->w) | ((A->x < -A->w) * 2) | ((A->y > A->w) * 4) | ((A->y < -A->w) * 8) | ((A->z > A->w) * 16) | ((A->z < -A->w) * 32); 126 | // u8 B_flags = (B->x > B->w) | ((B->x < -B->w) * 2) | ((B->y > B->w) * 4) | ((B->y < -B->w) * 8) | ((B->z > B->w) * 16) | ((B->z < -B->w) * 32); 127 | // 128 | // // Cull: 129 | // if (A_flags & B_flags) 130 | // return false; 131 | // 132 | // // Clip: 133 | // if (A_flags | B_flags) { 134 | // if ( A_flags & 3) *A = lerpVec4(*B, *A, clipFactor(B->w, A->w, B->x, A->x, A_flags & 2)); 135 | // else if (B_flags & 3) *B = lerpVec4(*A, *B, clipFactor(A->w, B->w, A->x, B->x, B_flags & 2)); 136 | // 137 | // if ( A_flags & 12) *A = lerpVec4(*B, *A, clipFactor(B->w, A->w, B->y, A->y, A_flags & 8)); 138 | // else if (B_flags & 12) *B = lerpVec4(*A, *B, clipFactor(A->w, B->w, A->y, B->y, B_flags & 8)); 139 | // 140 | // if ( A_flags & 48) *A = lerpVec4(*B, *A, clipFactor(B->w, A->w, B->z, A->z, A_flags & 32)); 141 | // else if (B_flags & 48) *B = lerpVec4(*A, *B, clipFactor(A->w, B->w, A->z, B->z, B_flags & 32)); 142 | // } 143 | // 144 | // return true; 145 | //} 146 | // 147 | //INLINE bool cullAndClipEdge2(Edge *edge, Viewport *viewport) { 148 | // vec4 A = mulVec4Mat4(Vec4fromVec3(edge->from, 1.0f), viewport->projection_matrix); 149 | // vec4 B = mulVec4Mat4(Vec4fromVec3(edge->to, 1.0f), viewport->projection_matrix); 150 | // 151 | // if (cullAndClipEdgeInClipSpace(&A, &B)) { 152 | // edge->from = Vec3fromVec4(mulVec4Mat4(A, viewport->pre_projection_matrix_inverted)); 153 | // edge->to = Vec3fromVec4(mulVec4Mat4(B, viewport->pre_projection_matrix_inverted)); 154 | // return true; 155 | // } else 156 | // return false; 157 | //} -------------------------------------------------------------------------------- /src/SlimRaster/shapes/line.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../math/vec3.h" 4 | 5 | void drawHLine(i32 x_start, i32 x_end, i32 y, vec3 color, f32 opacity, Viewport *viewport) { 6 | x_start += viewport->position.x; 7 | x_end += viewport->position.x; 8 | y += viewport->position.y; 9 | 10 | if (!inRange(y, viewport->dimensions.height + viewport->position.y, viewport->position.y)) return; 11 | 12 | i32 first, last, step = 1; 13 | subRange(x_start, x_end, viewport->dimensions.width + viewport->position.x, viewport->position.x, &first, &last); 14 | 15 | color = mulVec3(color, color); 16 | if (viewport->settings.antialias) { 17 | y <<= 1; 18 | first <<= 1; 19 | last <<= 1; 20 | step = 2; 21 | for (i32 x = first; x <= last; x += step) { 22 | setPixel(x+0, y+0, 0, color, opacity, viewport); 23 | setPixel(x+1, y+0, 0, color, opacity, viewport); 24 | setPixel(x+0, y+1, 0, color, opacity, viewport); 25 | setPixel(x+1, y+1, 0, color, opacity, viewport); 26 | } 27 | } else 28 | for (i32 x = first; x <= last; x += step) 29 | setPixel(x, y, 0, color, opacity, viewport); 30 | } 31 | 32 | void drawVLine(i32 y_start, i32 y_end, i32 x, vec3 color, f32 opacity, Viewport *viewport) { 33 | y_start += viewport->position.y; 34 | y_end += viewport->position.y; 35 | x += viewport->position.x; 36 | 37 | if (!inRange(x, viewport->dimensions.width + viewport->position.x, viewport->position.x)) return; 38 | i32 first, last, step = 1; 39 | 40 | subRange(y_start, y_end, viewport->dimensions.height + viewport->position.y, viewport->position.y, &first, &last); 41 | color = mulVec3(color, color); 42 | if (viewport->settings.antialias) { 43 | x <<= 1; 44 | first <<= 1; 45 | last <<= 1; 46 | step = 2; 47 | for (i32 y = first; y <= last; y += step) { 48 | setPixel(x+0, y+0, 0, color, opacity, viewport); 49 | setPixel(x+1, y+0, 0, color, opacity, viewport); 50 | setPixel(x+0, y+1, 0, color, opacity, viewport); 51 | setPixel(x+1, y+1, 0, color, opacity, viewport); 52 | } 53 | } else 54 | for (i32 y = first; y <= last; y += step) 55 | setPixel(x, y, 0, color, opacity, viewport); 56 | } 57 | 58 | INLINE f32 fractionOf(f32 x) { 59 | return x - floorf(x); 60 | } 61 | 62 | INLINE f32 oneMinusFractionOf(f32 x) { 63 | return 1 - fractionOf(x); 64 | } 65 | 66 | INLINE void drawLine(f32 x1, f32 y1, f64 z1, 67 | f32 x2, f32 y2, f64 z2, 68 | vec3 color, f32 opacity, u8 line_width, Viewport *viewport) { 69 | if (x1 < 0 && 70 | y1 < 0 && 71 | x2 < 0 && 72 | y2 < 0) 73 | return; 74 | 75 | i32 x_left = viewport->position.x; 76 | i32 y_top = viewport->position.y; 77 | x1 += (f32)x_left; 78 | x2 += (f32)x_left; 79 | y1 += (f32)y_top; 80 | y2 += (f32)y_top; 81 | 82 | i32 w = viewport->dimensions.width + x_left; 83 | i32 h = viewport->dimensions.height + y_top; 84 | 85 | color = mulVec3(color, color); 86 | i32 x, y; 87 | if (viewport->settings.antialias) { 88 | x1 += x1; 89 | x2 += x2; 90 | y1 += y1; 91 | y2 += y2; 92 | w <<= 1; 93 | h <<= 1; 94 | line_width <<= 1; 95 | line_width++; 96 | } 97 | f64 tmp, z_range, range_remap; 98 | f32 dx = x2 - x1; 99 | f32 dy = y2 - y1; 100 | f32 gap, grad, first_offset, last_offset; 101 | f64 z, z_curr, z_step; 102 | vec3 first, last; 103 | vec2i start, end; 104 | bool has_depth = z1 || z2; 105 | if (fabsf(dx) > fabsf(dy)) { // Shallow: 106 | if (x2 < x1) { // Left to right: 107 | tmp = x2; x2 = x1; x1 = (f32)tmp; 108 | tmp = y2; y2 = y1; y1 = (f32)tmp; 109 | tmp = z2; z2 = z1; z1 = tmp; 110 | } 111 | 112 | grad = dy / dx; 113 | 114 | first.x = roundf(x1); 115 | last.x = roundf(x2); 116 | first_offset = first.x - x1; 117 | last_offset = last.x - x2; 118 | 119 | first.y = y1 + grad * first_offset; 120 | last.y = y2 + grad * last_offset; 121 | 122 | start.x = (i32)first.x; 123 | start.y = (i32)first.y; 124 | end.x = (i32)last.x; 125 | end.y = (i32)last.y; 126 | 127 | x = start.x; 128 | y = start.y; 129 | gap = oneMinusFractionOf(x1 + 0.5f); 130 | 131 | if (inRange(x, w, x_left)) { 132 | if (inRange(y, h, y_top)) setPixel(x, y, z1, color, oneMinusFractionOf(first.y) * gap * opacity, viewport); 133 | 134 | for (u8 i = 0; i < line_width; i++) { 135 | y++; 136 | if (inRange(y, h, y_top)) setPixel(x, y, z1, color, opacity, viewport); 137 | } 138 | 139 | y++; 140 | if (inRange(y, h, y_top)) setPixel(x, y, z1, color, fractionOf(first.y) * gap * opacity, viewport); 141 | } 142 | 143 | x = end.x; 144 | y = end.y; 145 | gap = fractionOf(x2 + 0.5f); 146 | 147 | if (inRange(x, w, x_left)) { 148 | if (inRange(y, h, y_top)) setPixel(x, y, z2, color, oneMinusFractionOf(last.y) * gap * opacity, viewport); 149 | 150 | for (u8 i = 0; i < line_width; i++) { 151 | y++; 152 | if (inRange(y, h, y_top)) setPixel(x, y, z2, color, opacity, viewport); 153 | } 154 | 155 | y++; 156 | if (inRange(y, h, y_top)) setPixel(x, y, z2, color, fractionOf(last.y) * gap * opacity, viewport); 157 | } 158 | 159 | if (has_depth) { // Compute one-over-z start and step 160 | z1 = 1.0 / z1; 161 | z2 = 1.0 / z2; 162 | z_range = z2 - z1; 163 | range_remap = z_range / (f64)(x2 - x1); 164 | z1 += range_remap * (f64)(first_offset + 1); 165 | z2 += range_remap * (f64)(last_offset - 1); 166 | z_range = z2 - z1; 167 | z_step = z_range / (f64)(last.x - first.x - 1); 168 | z_curr = z1; 169 | } else z = 0; 170 | 171 | gap = first.y + grad; 172 | for (x = start.x + 1; x < end.x; x++) { 173 | if (inRange(x, w, x_left)) { 174 | if (has_depth) z = 1.0 / z_curr; 175 | y = (i32) gap; 176 | if (inRange(y, h, y_top)) setPixel(x, y, z, color, oneMinusFractionOf(gap) * opacity, viewport); 177 | 178 | for (u8 i = 0; i < line_width; i++) { 179 | y++; 180 | if (inRange(y, h, y_top)) setPixel(x, y, z, color, opacity, viewport); 181 | } 182 | 183 | y++; 184 | if (inRange(y, h, y_top)) setPixel(x, y, z, color, fractionOf(gap) * opacity, viewport); 185 | } 186 | 187 | gap += grad; 188 | if (has_depth) z_curr += z_step; 189 | } 190 | } else { // Steep: 191 | if (y2 < y1) { // Bottom up: 192 | tmp = x2; x2 = x1; x1 = (f32)tmp; 193 | tmp = y2; y2 = y1; y1 = (f32)tmp; 194 | tmp = z2; z2 = z1; z1 = tmp; 195 | } 196 | 197 | grad = dx / dy; 198 | 199 | first.y = roundf(y1); 200 | last.y = roundf(y2); 201 | 202 | first_offset = y1 - first.y; 203 | last_offset = last.y - y2; 204 | 205 | first.x = x1 + grad * first_offset; 206 | last.x = x2 + grad * last_offset; 207 | 208 | start.y = (i32)first.y; 209 | start.x = (i32)first.x; 210 | 211 | end.y = (i32)last.y; 212 | end.x = (i32)last.x; 213 | 214 | x = start.x; 215 | y = start.y; 216 | gap = oneMinusFractionOf(y1 + 0.5f); 217 | 218 | if (inRange(y, h, y_top)) { 219 | if (inRange(x, w, x_left)) setPixel(x, y, z1, color, oneMinusFractionOf(first.x) * gap * opacity, viewport); 220 | 221 | for (u8 i = 0; i < line_width; i++) { 222 | x++; 223 | if (inRange(x, w, x_left)) setPixel(x, y, z1, color, opacity, viewport); 224 | } 225 | 226 | x++; 227 | if (inRange(x, w, x_left)) setPixel(x, y, z1, color, fractionOf(first.x) * gap * opacity, viewport); 228 | } 229 | 230 | x = end.x; 231 | y = end.y; 232 | gap = fractionOf(y2 + 0.5f); 233 | 234 | if (inRange(y, h, y_top)) { 235 | if (inRange(x, w, x_left)) setPixel(x, y, z2, color, oneMinusFractionOf(last.x) * gap * opacity, viewport); 236 | 237 | for (u8 i = 0; i < line_width; i++) { 238 | x++; 239 | if (inRange(x, w, x_left)) setPixel(x, y, z2, color, opacity, viewport); 240 | } 241 | 242 | x++; 243 | if (inRange(x, w, x_left)) setPixel(x, y, z2, color, fractionOf(last.x) * gap * opacity, viewport); 244 | } 245 | 246 | if (has_depth) { // Compute one-over-z start and step 247 | z1 = 1.0 / z1; 248 | z2 = 1.0 / z2; 249 | z_range = z2 - z1; 250 | range_remap = z_range / (f64)(y2 - y1); 251 | z1 += range_remap * (f64)(first_offset + 1); 252 | z2 += range_remap * (f64)(last_offset - 1); 253 | z_range = z2 - z1; 254 | z_step = z_range / (f64)(last.y - first.y - 1); 255 | z_curr = z1; 256 | } else z = 0; 257 | 258 | gap = first.x + grad; 259 | for (y = start.y + 1; y < end.y; y++) { 260 | if (inRange(y, h, y_top)) { 261 | if (has_depth) z = 1.0 / z_curr; 262 | x = (i32)gap; 263 | 264 | if (inRange(x, w, x_left)) setPixel(x, y, z, color, oneMinusFractionOf(gap) * opacity, viewport); 265 | 266 | for (u8 i = 0; i < line_width; i++) { 267 | x++; 268 | if (inRange(x, w, x_left)) setPixel(x, y, z, color, opacity, viewport); 269 | } 270 | 271 | x++; 272 | if (inRange(x, w, x_left)) setPixel(x, y, z, color, fractionOf(gap) * opacity, viewport); 273 | } 274 | 275 | gap += grad; 276 | if (has_depth) z_curr += z_step; 277 | } 278 | } 279 | } 280 | 281 | //INLINE void swapf(f32 *a, f32 *b) { 282 | // f32 t = *a; 283 | // *a = *b; 284 | // *b = t; 285 | //} 286 | // 287 | //f32 fpart(f32 x) { 288 | // return x - floorf(x); 289 | //} 290 | // 291 | //f32 rfpart(f32 x) { 292 | // return 1.0f - fpart(x); 293 | //} 294 | // 295 | //#define getPixel(x, y, canvas) ((canvas)->pixels + (canvas)->dimensions.width * (y) + (x)) 296 | //#define plot(x, y, opacity, color, canvas) (setPixel(getPixel(x, y, canvas), (color), opacity, 0, (canvas)->settings.gamma_corrected_blending)) 297 | // 298 | // 299 | //INLINE void drawLinehh(PixelGrid *canvas, vec3 color, f32 opacity, 300 | // f32 x1, f32 y1, f64 z1, 301 | // f32 x2, f32 y2, f64 z2, 302 | // u8 line_width) { 303 | // bool steep = fabsf(y2 - y1) > fabsf(x2 - x1); 304 | // 305 | // if (steep) { 306 | // swapf(&x1, &y1); 307 | // swapf(&x2, &y2); 308 | // } 309 | // 310 | // if (x1 > x2) { 311 | // swapf(&x1, &x2); 312 | // swapf(&y1, &y2); 313 | // } 314 | // 315 | // f32 dx = x2 - x1; 316 | // f32 dy = y2 - y1; 317 | // f32 gradient = dx ? (dy / dx) : 1.0f; 318 | // 319 | // // handle first endpoint 320 | // f32 xend = roundf(x1); 321 | // f32 yend = y1 + gradient * (xend - x1); 322 | // f32 xgap = rfpart(x1 + 0.5f); 323 | // i32 xpxl1 = (i32)xend; // this will be used in the main loop 324 | // i32 ypxl1 = (i32)yend; 325 | // if (steep) { 326 | // plot( ypxl1 , xpxl1, rfpart(yend) * xgap, color, canvas); 327 | // plot(ypxl1 + 1, xpxl1, fpart(yend) * xgap, color, canvas); 328 | // } else { 329 | // plot(xpxl1, ypxl1 , rfpart(yend) * xgap, color, canvas); 330 | // plot(xpxl1, ypxl1 + 1, fpart(yend) * xgap, color, canvas); 331 | // } 332 | // 333 | // f32 intery = yend + gradient; // first y-intersection for the main loop 334 | // 335 | // // handle second endpoint 336 | // xend = roundf(x2); 337 | // yend = y2 + gradient * (xend - x2); 338 | // xgap = fpart(x2 + 0.5f); 339 | // i32 xpxl2 = (i32)xend; //this will be used in the main loop 340 | // i32 ypxl2 = (i32)yend; 341 | // if (steep) { 342 | // plot( ypxl2 , xpxl2, rfpart(yend) * xgap, color, canvas); 343 | // plot(ypxl2 + 1, xpxl2, fpart(yend) * xgap, color, canvas); 344 | // } else { 345 | // plot(xpxl2, ypxl2 , rfpart(yend) * xgap, color, canvas); 346 | // plot(xpxl2, ypxl2 + 1, fpart(yend) * xgap, color, canvas); 347 | // } 348 | // 349 | // // main loop 350 | // if (steep) { 351 | // for (i32 x = xpxl1 + 1; x < xpxl2 - 1; x++, intery += gradient) { 352 | // plot((i32)intery , x, rfpart(intery), color, canvas); 353 | // plot((i32)intery + 1, x, fpart(intery), color, canvas); 354 | // } 355 | // } else { 356 | // for (i32 x = xpxl1 + 1; x < xpxl2 - 1; x++, intery += gradient) { 357 | // plot(x, (i32)intery , rfpart(intery), color, canvas); 358 | // plot(x, (i32)intery + 1, fpart(intery), color, canvas); 359 | // } 360 | // } 361 | //} -------------------------------------------------------------------------------- /src/SlimRaster/shapes/rect.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | #include "./line.h" 5 | 6 | INLINE void drawRect(Rect rect, vec3 color, f32 opacity, Viewport *viewport) { 7 | if (rect.max.x < 0 || rect.min.x >= viewport->dimensions.width || 8 | rect.max.y < 0 || rect.min.y >= viewport->dimensions.height) 9 | return; 10 | 11 | drawHLine(rect.min.x, rect.max.x, rect.min.y, color, opacity, viewport); 12 | drawHLine(rect.min.x, rect.max.x, rect.max.y, color, opacity, viewport); 13 | drawVLine(rect.min.y, rect.max.y, rect.min.x, color, opacity, viewport); 14 | drawVLine(rect.min.y, rect.max.y, rect.max.x, color, opacity, viewport); 15 | } 16 | 17 | INLINE void fillRect(Rect rect, vec3 color, f32 opacity, Viewport *viewport) { 18 | if (rect.max.x < 0 || rect.min.x >= viewport->dimensions.width || 19 | rect.max.y < 0 || rect.min.y >= viewport->dimensions.height) 20 | return; 21 | 22 | i32 min_x, min_y, max_x, max_y; 23 | subRange(rect.min.x, rect.max.x, viewport->dimensions.width, 0, &min_x, &max_x); 24 | subRange(rect.min.y, rect.max.y, viewport->dimensions.height, 0, &min_y, &max_y); 25 | for (i32 y = min_y; y <= max_y; y++) 26 | drawHLine(min_x, max_x, y, color, opacity, viewport); 27 | } -------------------------------------------------------------------------------- /src/SlimRaster/shapes/triangle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | // 5 | //void fillTriangle(PixelGrid *canvas, vec3 color, f32 opacity, f32 *X, f32 *Y) { 6 | // u16 W = canvas->dimensions.width; 7 | // u16 H = canvas->dimensions.height; 8 | // f32 dx1, x1, y1, xs, 9 | // dx2, x2, y2, xe, 10 | // dx3, x3, y3, dy; 11 | // i32 offset, 12 | // x, x1i, y1i, x2i, xsi, ysi = 0, 13 | // y, y2i, x3i, y3i, xei, yei = 0; 14 | // for (u8 i = 1; i <= 2; i++) { 15 | // if (Y[i] < Y[ysi]) ysi = i; 16 | // if (Y[i] > Y[yei]) yei = i; 17 | // } 18 | // u8 id[3]; 19 | // if (ysi) { 20 | // if (ysi == 1) { 21 | // id[0] = 1; 22 | // id[1] = 2; 23 | // id[2] = 0; 24 | // } else { 25 | // id[0] = 2; 26 | // id[1] = 0; 27 | // id[2] = 1; 28 | // } 29 | // } else { 30 | // id[0] = 0; 31 | // id[1] = 1; 32 | // id[2] = 2; 33 | // } 34 | // x1 = X[id[0]]; y1 = Y[id[0]]; x1i = (i32)x1; y1i = (i32)y1; 35 | // x2 = X[id[1]]; y2 = Y[id[1]]; x2i = (i32)x2; y2i = (i32)y2; 36 | // x3 = X[id[2]]; y3 = Y[id[2]]; x3i = (i32)x3; y3i = (i32)y3; 37 | // dx1 = x1i == x2i || y1i == y2i ? 0 : (x2 - x1) / (y2 - y1); 38 | // dx2 = x2i == x3i || y2i == y3i ? 0 : (x3 - x2) / (y3 - y2); 39 | // dx3 = x1i == x3i || y1i == y3i ? 0 : (x3 - x1) / (y3 - y1); 40 | // dy = 1 - (y1 - (f32)y1); 41 | // xs = dx3 ? x1 + dx3 * dy : x1; ysi = (i32)Y[ysi]; 42 | // xe = dx1 ? x1 + dx1 * dy : x1; yei = (i32)Y[yei]; 43 | // offset = W * y1i; 44 | // for (y = ysi; y < yei; y++) { 45 | // if (y == y3i) xs = dx2 ? (x3 + dx2 * (1 - (y3 - (f32)y3i))) : x3; 46 | // if (y == y2i) xe = dx2 ? (x2 + dx2 * (1 - (y2 - (f32)y2i))) : x2; 47 | // xsi = (i32)xs; 48 | // xei = (i32)xe; 49 | // for (x = xsi; x < xei; x++) { 50 | // if (x > 0 && x < W && y > 0 && y < H) 51 | // setPixel(canvas->float_pixels + (offset + x), color, opacity, 0); 52 | // } 53 | // offset += W; 54 | // xs += y < y3i ? dx3 : dx2; 55 | // xe += y < y2i ? dx1 : dx2; 56 | // } 57 | //} -------------------------------------------------------------------------------- /src/SlimRaster/viewport/hud.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/base.h" 4 | #include "../core/text.h" 5 | 6 | void drawHUD(Viewport *viewport, HUD *hud) { 7 | u16 x = (u16)hud->position.x; 8 | u16 y = (u16)hud->position.y; 9 | 10 | HUDLine *line = hud->lines; 11 | bool alt; 12 | for (u32 i = 0; i < hud->line_count; i++, line++) { 13 | if (line->use_alternate) { 14 | alt = *line->use_alternate; 15 | if (line->invert_alternate_use) 16 | alt = !alt; 17 | } else 18 | alt = false; 19 | 20 | drawText(line->title.char_ptr, x, y, Color(line->title_color), 1, viewport); 21 | drawText(alt ? line->alternate_value.char_ptr : line->value.string.char_ptr, 22 | x + (u16)line->title.length * FONT_WIDTH, y, 23 | Color(alt ? line->alternate_value_color : line->value_color), 1, viewport); 24 | y += (u16)(hud->line_height * (f32)FONT_HEIGHT); 25 | } 26 | } -------------------------------------------------------------------------------- /src/SlimRaster/viewport/manipulation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/types.h" 4 | #include "../math/vec3.h" 5 | #include "../math/quat.h" 6 | #include "../scene/primitive.h" 7 | #include "../scene/box.h" 8 | 9 | void setViewportProjectionPlane(Viewport *viewport) { 10 | Camera *camera = viewport->camera; 11 | ProjectionPlane *projection_plane = &viewport->projection_plane; 12 | Dimensions *dimensions = &viewport->dimensions; 13 | 14 | projection_plane->start = scaleVec3(*camera->transform.forward_direction, dimensions->f_height * camera->focal_length); 15 | projection_plane->right = scaleVec3(*camera->transform.right_direction, 1.0f - (f32)dimensions->width); 16 | projection_plane->down = scaleVec3(*camera->transform.up_direction, dimensions->f_height - 1.0f); 17 | projection_plane->start = addVec3(projection_plane->start, projection_plane->right); 18 | projection_plane->start = addVec3(projection_plane->start, projection_plane->down); 19 | 20 | projection_plane->right = scaleVec3(*camera->transform.right_direction, 2); 21 | projection_plane->down = scaleVec3(*camera->transform.up_direction, -2); 22 | } 23 | 24 | INLINE void setRayFromCoords(Ray *ray, vec2i coords, Viewport *viewport) { 25 | ray->origin = viewport->camera->transform.position; 26 | ray->direction = scaleAddVec3(viewport->projection_plane.right, (f32)coords.x, viewport->projection_plane.start); 27 | ray->direction = scaleAddVec3(viewport->projection_plane.down, (f32)coords.y, ray->direction); 28 | ray->direction = normVec3(ray->direction); 29 | } 30 | 31 | INLINE bool hitPlane(vec3 plane_origin, vec3 plane_normal, vec3 *ray_origin, vec3 *ray_direction, RayHit *hit) { 32 | f32 Rd_dot_n = dotVec3(*ray_direction, plane_normal); 33 | if (Rd_dot_n == 0) // The ray is parallel to the plane 34 | return false; 35 | 36 | bool ray_is_facing_the_plane = Rd_dot_n < 0; 37 | 38 | vec3 RtoP = subVec3(*ray_origin, plane_origin); 39 | f32 Po_to_Ro_dot_n = dotVec3(RtoP, plane_normal); 40 | hit->from_behind = Po_to_Ro_dot_n < 0; 41 | if (hit->from_behind == ray_is_facing_the_plane) // The ray can't hit the plane 42 | return false; 43 | 44 | hit->distance = fabsf(Po_to_Ro_dot_n / Rd_dot_n); 45 | hit->position = scaleVec3(*ray_direction, hit->distance); 46 | hit->position = addVec3(*ray_origin, hit->position); 47 | hit->normal = plane_normal; 48 | 49 | return true; 50 | } 51 | 52 | INLINE BoxSide getBoxSide(vec3 octant, u8 axis) { 53 | switch (axis) { 54 | case 0 : return octant.x > 0 ? Right : Left; 55 | case 3 : return octant.x > 0 ? Left : Right; 56 | case 1 : return octant.y > 0 ? Top : Bottom; 57 | case 4 : return octant.y > 0 ? Bottom : Top; 58 | case 2 : return octant.z > 0 ? Front : Back; 59 | default: return octant.z > 0 ? Back : Front; 60 | } 61 | } 62 | 63 | INLINE BoxSide hitCube(RayHit *hit, vec3 *Ro, vec3 *Rd) { 64 | vec3 octant, RD_rcp = oneOverVec3(*Rd); 65 | octant.x = signbit(Rd->x) ? 1.0f : -1.0f; 66 | octant.y = signbit(Rd->y) ? 1.0f : -1.0f; 67 | octant.z = signbit(Rd->z) ? 1.0f : -1.0f; 68 | 69 | f32 t[6]; 70 | t[0] = ( octant.x - Ro->x) * RD_rcp.x; 71 | t[1] = ( octant.y - Ro->y) * RD_rcp.y; 72 | t[2] = ( octant.z - Ro->z) * RD_rcp.z; 73 | t[3] = (-octant.x - Ro->x) * RD_rcp.x; 74 | t[4] = (-octant.y - Ro->y) * RD_rcp.y; 75 | t[5] = (-octant.z - Ro->z) * RD_rcp.z; 76 | 77 | u8 max_axis = t[3] < t[4] ? 3 : 4; if (t[5] < t[max_axis]) max_axis = 5; 78 | f32 maxt = t[max_axis]; 79 | if (maxt < 0) // Further-away hit is behind the ray - intersection can not occur. 80 | return NoSide; 81 | 82 | u8 min_axis = t[0] > t[1] ? 0 : 1; if (t[2] > t[min_axis]) min_axis = 2; 83 | f32 mint = t[min_axis]; 84 | if (maxt < (mint > 0 ? mint : 0)) 85 | return NoSide; 86 | 87 | hit->from_behind = mint < 0; // Further-away hit is in front of the ray, closer one is behind it. 88 | if (hit->from_behind) { 89 | mint = maxt; 90 | min_axis = max_axis; 91 | } 92 | 93 | BoxSide side = getBoxSide(octant, min_axis); 94 | hit->position = scaleAddVec3(*Rd, mint, *Ro); 95 | hit->normal = getVec3Of(0); 96 | switch (side) { 97 | case Left: hit->normal.x = hit->from_behind ? +1.0f : -1.0f; break; 98 | case Right: hit->normal.x = hit->from_behind ? -1.0f : +1.0f; break; 99 | case Bottom: hit->normal.y = hit->from_behind ? +1.0f : -1.0f; break; 100 | case Top: hit->normal.y = hit->from_behind ? -1.0f : +1.0f; break; 101 | case Back: hit->normal.z = hit->from_behind ? +1.0f : -1.0f; break; 102 | case Front: hit->normal.z = hit->from_behind ? -1.0f : +1.0f; break; 103 | default: return NoSide; 104 | } 105 | 106 | return side; 107 | } 108 | 109 | INLINE bool rayHitScene(Ray *ray, RayHit *local_hit, RayHit *hit, Scene *scene) { 110 | bool current_found, found = false; 111 | vec3 Ro, Rd; 112 | Primitive hit_primitive, primitive; 113 | for (u32 i = 0; i < scene->settings.primitives; i++) { 114 | primitive = scene->primitives[i]; 115 | if (primitive.type == PrimitiveType_Mesh) 116 | primitive.scale = mulVec3(primitive.scale, scene->meshes[primitive.id].aabb.max); 117 | 118 | convertPositionAndDirectionToObjectSpace(ray->origin, ray->direction, &primitive, &Ro, &Rd); 119 | 120 | current_found = hitCube(local_hit, &Ro, &Rd); 121 | if (current_found) { 122 | local_hit->position = convertPositionToWorldSpace(local_hit->position, &primitive); 123 | local_hit->distance_squared = squaredLengthVec3(subVec3(local_hit->position, ray->origin)); 124 | if (local_hit->distance_squared < hit->distance_squared) { 125 | *hit = *local_hit; 126 | hit->object_type = primitive.type; 127 | hit->object_id = i; 128 | 129 | hit_primitive = primitive; 130 | found = true; 131 | } 132 | } 133 | } 134 | 135 | if (found) { 136 | hit->distance = sqrtf(hit->distance_squared); 137 | hit->normal = normVec3(convertDirectionToWorldSpace(hit->normal, &hit_primitive)); 138 | } 139 | 140 | return found; 141 | } 142 | 143 | void manipulateSelection(Scene *scene, Viewport *viewport, Controls *controls) { 144 | Mouse *mouse = &controls->mouse; 145 | Camera *camera = viewport->camera; 146 | Dimensions *dimensions = &viewport->dimensions; 147 | Selection *selection = scene->selection; 148 | 149 | setProjectionMatrix(viewport); 150 | setViewportProjectionPlane(viewport); 151 | 152 | vec3 position; 153 | vec3 *cam_pos = &camera->transform.position; 154 | mat3 *rot = &camera->transform.rotation_matrix; 155 | mat3 *inv_rot = &camera->transform.rotation_matrix_inverted; 156 | RayHit *hit = &selection->hit; 157 | Ray ray, *local_ray = &selection->local_ray; 158 | Primitive primitive; 159 | vec2i mouse_pos = Vec2i(mouse->pos.x - viewport->position.x, 160 | mouse->pos.y - viewport->position.y); 161 | 162 | if (mouse->left_button.is_pressed) { 163 | if (!mouse->left_button.is_handled) { // This is the first frame after the left mouse button went down: 164 | mouse->left_button.is_handled = true; 165 | 166 | // Cast a ray onto the scene to find the closest object behind the hovered pixel: 167 | setRayFromCoords(&ray, mouse_pos, viewport); 168 | 169 | hit->distance_squared = INFINITY; 170 | if (rayHitScene(&ray, &selection->local_hit, hit, scene)) { 171 | // Detect if object scene->selection has changed: 172 | selection->changed = ( 173 | selection->object_type != hit->object_type || 174 | selection->object_id != hit->object_id 175 | ); 176 | 177 | // Track the object that is now selected: 178 | selection->object_type = hit->object_type; 179 | selection->object_id = hit->object_id; 180 | selection->primitive = null; 181 | 182 | // Capture a pointer to the selected object's position for later use in transformations: 183 | selection->primitive = scene->primitives + selection->object_id; 184 | selection->world_position = &selection->primitive->position; 185 | selection->transformation_plane_origin = hit->position; 186 | 187 | selection->world_offset = subVec3(hit->position, *selection->world_position); 188 | 189 | // Track how far away the hit position is from the camera along the z axis: 190 | position = mulVec3Mat3(subVec3(hit->position, ray.origin), *inv_rot); 191 | selection->object_distance = position.z; 192 | } else { 193 | if (selection->object_type) 194 | selection->changed = true; 195 | selection->object_type = 0; 196 | } 197 | } 198 | } 199 | 200 | if (selection->object_type) { 201 | if (controls->is_pressed.alt) { 202 | bool any_mouse_button_is_pressed = ( 203 | mouse->left_button.is_pressed || 204 | mouse->middle_button.is_pressed || 205 | mouse->right_button.is_pressed); 206 | if (selection->primitive && !any_mouse_button_is_pressed) { 207 | // Cast a ray onto the bounding box of the currently selected object: 208 | setRayFromCoords(&ray, mouse_pos, viewport); 209 | primitive = *selection->primitive; 210 | if (primitive.type == PrimitiveType_Mesh) 211 | primitive.scale = mulVec3(primitive.scale, scene->meshes[primitive.id].aabb.max); 212 | 213 | convertPositionAndDirectionToObjectSpace(ray.origin, ray.direction, &primitive, &local_ray->origin, &local_ray->direction); 214 | 215 | selection->box_side = hitCube(hit, &local_ray->origin, &local_ray->direction); 216 | if (selection->box_side) { 217 | selection->transformation_plane_center = convertPositionToWorldSpace(hit->normal, &primitive); 218 | selection->transformation_plane_origin = convertPositionToWorldSpace(hit->position, &primitive); 219 | selection->transformation_plane_normal = convertDirectionToWorldSpace(hit->normal, &primitive); 220 | selection->transformation_plane_normal = normVec3(selection->transformation_plane_normal); 221 | selection->world_offset = subVec3(selection->transformation_plane_origin, *selection->world_position); 222 | selection->object_scale = selection->primitive->scale; 223 | selection->object_rotation = selection->primitive->rotation; 224 | } 225 | } 226 | 227 | if (selection->box_side) { 228 | if (selection->primitive) { 229 | if (any_mouse_button_is_pressed) { 230 | setRayFromCoords(&ray, mouse_pos, viewport); 231 | if (hitPlane(selection->transformation_plane_origin, 232 | selection->transformation_plane_normal, 233 | &ray.origin, 234 | &ray.direction, 235 | hit)) { 236 | 237 | primitive = *selection->primitive; 238 | if (primitive.type == PrimitiveType_Mesh) 239 | primitive.scale = mulVec3(primitive.scale, scene->meshes[primitive.id].aabb.max); 240 | if (mouse->left_button.is_pressed) { 241 | position = subVec3(hit->position, selection->world_offset); 242 | *selection->world_position = position; 243 | if (selection->primitive) 244 | selection->primitive->flags |= IS_TRANSLATED; 245 | } else if (mouse->middle_button.is_pressed) { 246 | position = selection->transformation_plane_origin; 247 | position = convertPositionToObjectSpace( position, &primitive); 248 | hit->position = convertPositionToObjectSpace(hit->position, &primitive); 249 | 250 | selection->primitive->scale = mulVec3(selection->object_scale, 251 | mulVec3(hit->position, oneOverVec3(position))); 252 | selection->primitive->flags |= IS_SCALED | IS_SCALED_NON_UNIFORMLY; 253 | } else if (mouse->right_button.is_pressed) { 254 | vec3 v1 = subVec3(hit->position, 255 | selection->transformation_plane_center); 256 | vec3 v2 = subVec3(selection->transformation_plane_origin, 257 | selection->transformation_plane_center); 258 | quat q; 259 | q.axis = crossVec3(v2, v1); 260 | q.amount = sqrtf(squaredLengthVec3(v1) * squaredLengthVec3(v2)) + dotVec3(v1, v2); 261 | q = normQuat(q); 262 | selection->primitive->rotation = mulQuat(q, selection->object_rotation); 263 | selection->primitive->rotation = normQuat(selection->primitive->rotation); 264 | selection->primitive->flags |= IS_ROTATED; 265 | } 266 | } 267 | } 268 | } 269 | } 270 | } else { 271 | selection->box_side = NoSide; 272 | if (mouse->left_button.is_pressed && mouse->moved) { 273 | // Back-project the new mouse position onto a quad at a distance of the selected-object away from the camera 274 | position.z = selection->object_distance; 275 | 276 | // Screen -> NDC: 277 | position.x = ((f32)mouse_pos.x + 0.5f) / dimensions->h_width - 1; 278 | position.y = ((f32)mouse_pos.y + 0.5f) / dimensions->h_height - 1; 279 | position.y = -position.y; 280 | 281 | // NDC -> View: 282 | position.x *= position.z / (camera->focal_length * dimensions->height_over_width); 283 | position.y *= position.z / camera->focal_length; 284 | 285 | // View -> World: 286 | position = addVec3(mulVec3Mat3(position, *rot), *cam_pos); 287 | 288 | // Back-track by the world offset (from the hit position back to the selected-object's center): 289 | position = subVec3(position, selection->world_offset); 290 | *selection->world_position = position; 291 | if (selection->primitive) 292 | selection->primitive->flags |= IS_TRANSLATED; 293 | } 294 | } 295 | } 296 | } 297 | 298 | void drawSelection(Scene *scene, Viewport *viewport, Controls *controls) { 299 | Mouse *mouse = &controls->mouse; 300 | Selection *selection = scene->selection; 301 | Box *box = &selection->box; 302 | 303 | if (controls->is_pressed.alt && 304 | !mouse->is_captured && 305 | selection->object_type && 306 | selection->primitive) { 307 | Primitive primitive = *selection->primitive; 308 | if (primitive.type == PrimitiveType_Mesh) 309 | primitive.scale = mulVec3(primitive.scale, scene->meshes[primitive.id].aabb.max); 310 | 311 | initBox(box); 312 | drawBox(box, BOX__ALL_SIDES, &primitive, Color(Yellow), 0.5f, 0, viewport); 313 | if (selection->box_side) { 314 | vec3 color = Color(White); 315 | switch (selection->box_side) { 316 | case Left: case Right: color = Color(Red); break; 317 | case Top: case Bottom: color = Color(Green); break; 318 | case Front: case Back: color = Color(Blue); break; 319 | case NoSide: break; 320 | } 321 | drawBox(box, selection->box_side, &primitive, color, 0.5f, 1, viewport); 322 | } 323 | } 324 | } -------------------------------------------------------------------------------- /src/SlimRaster/viewport/navigation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/types.h" 4 | #include "../math/vec3.h" 5 | #include "../scene/xform.h" 6 | 7 | void zoomCamera(Camera *camera, f32 zoom) { 8 | f32 new_zoom = camera->zoom + zoom; 9 | camera->focal_length = new_zoom > 1 ? new_zoom : (new_zoom < -1 ? (-1 / new_zoom) : 1); 10 | camera->zoom = new_zoom; 11 | } 12 | 13 | void dollyCamera(Camera *camera, f32 dolly) { 14 | vec3 target_position = scaleVec3(*camera->transform.forward_direction, camera->target_distance); 15 | target_position = addVec3(camera->transform.position, target_position); 16 | 17 | // Compute new target distance: 18 | camera->dolly += dolly; 19 | camera->target_distance = powf(2, camera->dolly / -200) * CAMERA_DEFAULT__TARGET_DISTANCE; 20 | 21 | // Back-track from target position to new current position: 22 | camera->transform.position = scaleVec3(*camera->transform.forward_direction, camera->target_distance); 23 | camera->transform.position = subVec3(target_position,camera->transform.position); 24 | } 25 | 26 | void orbitCamera(Camera *camera, f32 azimuth, f32 altitude) { 27 | // Move the camera forward to the position of it's target: 28 | vec3 movement = scaleVec3(*camera->transform.forward_direction, camera->target_distance); 29 | camera->transform.position = addVec3(camera->transform.position, movement); 30 | 31 | // Reorient the camera while it is at the position of it's target: 32 | rotateXform3(&camera->transform, azimuth, altitude, 0); 33 | 34 | // Back the camera away from it's target position using the updated forward direction: 35 | movement = scaleVec3(*camera->transform.forward_direction, camera->target_distance); 36 | camera->transform.position = subVec3(camera->transform.position, movement); 37 | } 38 | 39 | void panCamera(Camera *camera, f32 right, f32 up) { 40 | vec3 up_movement = scaleVec3(*camera->transform.up_direction, up); 41 | vec3 right_movement = scaleVec3(*camera->transform.right_direction, right); 42 | camera->transform.position = addVec3(camera->transform.position, up_movement); 43 | camera->transform.position = addVec3(camera->transform.position, right_movement); 44 | } 45 | 46 | void panViewport(Viewport *viewport, Mouse *mouse) { 47 | f32 x = viewport->navigation.settings.speeds.pan * -(f32)mouse->pos_raw_diff.x; 48 | f32 y = viewport->navigation.settings.speeds.pan * +(f32)mouse->pos_raw_diff.y; 49 | panCamera(viewport->camera, x, y); 50 | 51 | viewport->navigation.moved = true; 52 | mouse->raw_movement_handled = true; 53 | mouse->moved = false; 54 | } 55 | 56 | void zoomViewport(Viewport *viewport, Mouse *mouse) { 57 | f32 z = viewport->navigation.settings.speeds.zoom * mouse->wheel_scroll_amount; 58 | zoomCamera(viewport->camera, z); 59 | 60 | viewport->navigation.zoomed = true; 61 | mouse->wheel_scroll_handled = true; 62 | } 63 | 64 | void dollyViewport(Viewport *viewport, Mouse *mouse) { 65 | f32 z = viewport->navigation.settings.speeds.dolly * mouse->wheel_scroll_amount; 66 | dollyCamera(viewport->camera, z); 67 | viewport->navigation.moved = true; 68 | mouse->wheel_scroll_handled = true; 69 | } 70 | 71 | void orientViewport(Viewport *viewport, Mouse *mouse) { 72 | f32 x = viewport->navigation.settings.speeds.orient * -(f32)mouse->pos_raw_diff.x; 73 | f32 y = viewport->navigation.settings.speeds.orient * -(f32)mouse->pos_raw_diff.y; 74 | rotateXform3(&viewport->camera->transform, x, y, 0); 75 | 76 | mouse->moved = false; 77 | mouse->raw_movement_handled = true; 78 | viewport->navigation.turned = true; 79 | } 80 | 81 | void orbitViewport(Viewport *viewport, Mouse *mouse) { 82 | f32 x = viewport->navigation.settings.speeds.orbit * -(f32)mouse->pos_raw_diff.x; 83 | f32 y = viewport->navigation.settings.speeds.orbit * -(f32)mouse->pos_raw_diff.y; 84 | orbitCamera(viewport->camera, x, y); 85 | 86 | viewport->navigation.turned = true; 87 | viewport->navigation.moved = true; 88 | mouse->raw_movement_handled = true; 89 | mouse->moved = false; 90 | } 91 | 92 | void navigateViewport(Viewport *viewport, f32 delta_time) { 93 | vec3 target_velocity = getVec3Of(0); 94 | Navigation *navigation = &viewport->navigation; 95 | Camera *camera = viewport->camera; 96 | 97 | if (navigation->move.right) target_velocity.x += navigation->settings.max_velocity; 98 | if (navigation->move.left) target_velocity.x -= navigation->settings.max_velocity; 99 | if (navigation->move.up) target_velocity.y += navigation->settings.max_velocity; 100 | if (navigation->move.down) target_velocity.y -= navigation->settings.max_velocity; 101 | if (navigation->move.forward) target_velocity.z += navigation->settings.max_velocity; 102 | if (navigation->move.backward) target_velocity.z -= navigation->settings.max_velocity; 103 | if (navigation->turn.left) { 104 | rotateXform3(&camera->transform, delta_time * +navigation->settings.speeds.turn, 0, 0); 105 | navigation->turned = true; 106 | } 107 | if (navigation->turn.right) { 108 | rotateXform3(&camera->transform, delta_time * -navigation->settings.speeds.turn, 0, 0); 109 | navigation->turned = true; 110 | } 111 | 112 | // Update the current velocity: 113 | f32 velocity_difference = navigation->settings.acceleration * delta_time; 114 | camera->current_velocity = approachVec3(camera->current_velocity, target_velocity, velocity_difference); 115 | 116 | navigation->moved = nonZeroVec3(camera->current_velocity); 117 | if (navigation->moved) { // Update the current position: 118 | vec3 movement = scaleVec3(camera->current_velocity, delta_time); 119 | movement = mulVec3Mat3(movement, camera->transform.rotation_matrix); 120 | camera->transform.position = addVec3(camera->transform.position, movement); 121 | } 122 | } -------------------------------------------------------------------------------- /src/SlimRaster/viewport/viewport.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../math/vec3.h" 4 | #include "./hud.h" 5 | 6 | typedef union RGBA2u32 { 7 | RGBA rgba; 8 | u32 value; 9 | } RGBA2u32; 10 | 11 | void drawViewportToWindowContent(Viewport *viewport) { 12 | PixelQuad *src_pixel = viewport->pixels; 13 | u32 *trg_value = app->window_content; 14 | vec3 color; 15 | RGBA2u32 background, trg_pixel; 16 | background.rgba.R = (u8)(viewport->settings.background.color.r * FLOAT_TO_COLOR_COMPONENT); 17 | background.rgba.G = (u8)(viewport->settings.background.color.g * FLOAT_TO_COLOR_COMPONENT); 18 | background.rgba.B = (u8)(viewport->settings.background.color.b * FLOAT_TO_COLOR_COMPONENT); 19 | background.rgba.A = (u8)(viewport->settings.background.opacity * FLOAT_TO_COLOR_COMPONENT); 20 | if (viewport->settings.antialias) { 21 | for (u16 y = 0; y < viewport->dimensions.height; y++) { 22 | for (u16 x = 0; x < viewport->dimensions.width; x++, src_pixel++, trg_value++) { 23 | if (src_pixel->TL.opacity || src_pixel->TR.opacity || src_pixel->BL.opacity || src_pixel->BR.opacity) { 24 | color = scaleVec3(src_pixel->TL.color, src_pixel->TL.opacity * 0.25f); 25 | color = scaleAddVec3(src_pixel->TR.color, src_pixel->TR.opacity * 0.25f, color); 26 | color = scaleAddVec3(src_pixel->BL.color, src_pixel->BL.opacity * 0.25f, color); 27 | color = scaleAddVec3(src_pixel->BR.color, src_pixel->BR.opacity * 0.25f, color); 28 | trg_pixel.rgba.R = (u8)(color.r > (MAX_COLOR_VALUE * MAX_COLOR_VALUE) ? MAX_COLOR_VALUE : sqrt(color.r)); 29 | trg_pixel.rgba.G = (u8)(color.g > (MAX_COLOR_VALUE * MAX_COLOR_VALUE) ? MAX_COLOR_VALUE : sqrt(color.g)); 30 | trg_pixel.rgba.B = (u8)(color.b > (MAX_COLOR_VALUE * MAX_COLOR_VALUE) ? MAX_COLOR_VALUE : sqrt(color.b)); 31 | trg_pixel.rgba.A = (u8)(clampValue(src_pixel->TL.opacity) * FLOAT_TO_COLOR_COMPONENT); 32 | } else trg_pixel = background; 33 | *trg_value = trg_pixel.value; 34 | } 35 | } 36 | } else { 37 | for (u16 y = 0; y < viewport->dimensions.height; y++) { 38 | for (u16 x = 0; x < viewport->dimensions.width; x++, src_pixel++, trg_value++) { 39 | if (src_pixel->TL.depth == INFINITY) 40 | trg_pixel = background; 41 | else { 42 | color = scaleVec3(src_pixel->TL.color, src_pixel->TL.opacity); 43 | trg_pixel.rgba.R = (u8)(color.r > (MAX_COLOR_VALUE * MAX_COLOR_VALUE) ? MAX_COLOR_VALUE : sqrt(color.r)); 44 | trg_pixel.rgba.G = (u8)(color.g > (MAX_COLOR_VALUE * MAX_COLOR_VALUE) ? MAX_COLOR_VALUE : sqrt(color.g)); 45 | trg_pixel.rgba.B = (u8)(color.b > (MAX_COLOR_VALUE * MAX_COLOR_VALUE) ? MAX_COLOR_VALUE : sqrt(color.b)); 46 | trg_pixel.rgba.A = (u8)(clampValue(src_pixel->TL.opacity) * FLOAT_TO_COLOR_COMPONENT); 47 | } 48 | *trg_value = trg_pixel.value; 49 | } 50 | } 51 | } 52 | } 53 | 54 | void fillViewport(Viewport *viewport, vec3 color, f32 opacity, f64 depth) { 55 | PixelQuad fill_pixel; 56 | Pixel fill_sub_pixel; 57 | fill_sub_pixel.color = color; 58 | fill_sub_pixel.opacity = opacity; 59 | fill_sub_pixel.depth = depth; 60 | fill_pixel.TL = fill_pixel.TR = fill_pixel.BL = fill_pixel.BR = fill_sub_pixel; 61 | for (i32 y = viewport->position.y; y < (viewport->position.y + viewport->dimensions.height); y++) 62 | for (i32 x = viewport->position.x; x < (viewport->position.x + viewport->dimensions.width); x++) 63 | viewport->pixels[viewport->dimensions.stride * y + x] = fill_pixel; 64 | } 65 | 66 | void clearViewportToBackground(Viewport *viewport) { 67 | fillViewport(viewport, 68 | viewport->settings.background.color, 69 | viewport->settings.background.opacity, 70 | viewport->settings.background.depth); 71 | } 72 | 73 | void beginDrawing(Viewport *viewport) { 74 | clearViewportToBackground(viewport); 75 | setProjectionMatrix(viewport); 76 | } 77 | 78 | void endDrawing(Viewport *viewport) { 79 | if (viewport->settings.show_hud) 80 | drawHUD(viewport, &viewport->hud); 81 | drawViewportToWindowContent(viewport); 82 | } -------------------------------------------------------------------------------- /src/examples/1_clipping_interpolation.c: -------------------------------------------------------------------------------- 1 | #include "../SlimRaster/app.h" 2 | #include "../SlimRaster/core/time.h" 3 | #include "../SlimRaster/viewport/viewport.h" 4 | #include "../SlimRaster/viewport/navigation.h" 5 | #include "../SlimRaster/viewport/manipulation.h" 6 | #include "../SlimRaster/renderer/rasterizer.h" 7 | // Or using the single-header file: 8 | // #include "../SlimRaster.h" 9 | 10 | 11 | void onMouseButtonDown(MouseButton *mouse_button) { 12 | app->controls.mouse.pos_raw_diff = Vec2i(0, 0); 13 | } 14 | void onMouseDoubleClicked(MouseButton *mouse_button) { 15 | if (mouse_button == &app->controls.mouse.left_button) { 16 | app->controls.mouse.is_captured = !app->controls.mouse.is_captured; 17 | app->platform.setCursorVisibility(!app->controls.mouse.is_captured); 18 | app->platform.setWindowCapture( app->controls.mouse.is_captured); 19 | onMouseButtonDown(mouse_button); 20 | } 21 | } 22 | void onKeyChanged(u8 key, bool is_pressed) { 23 | if (!is_pressed) { 24 | Viewport *viewport = &app->viewport; 25 | ViewportSettings *settings = &viewport->settings; 26 | u8 tab = app->controls.key_map.tab; 27 | if (key == tab) settings->show_hud = !settings->show_hud; 28 | if (key == '1') { 29 | settings->show_wire_frame = !settings->show_wire_frame; 30 | setString(&viewport->hud.lines[1].value.string,settings->show_wire_frame ? (char*)"On" : (char*)"Off"); 31 | viewport->hud.lines[1].value_color = settings->show_wire_frame ? White : Grey; 32 | } 33 | if (key == '2') { 34 | settings->antialias = !settings->antialias; 35 | setString(&viewport->hud.lines[2].value.string,settings->antialias ? (char*)"On" : (char*)"Off"); 36 | viewport->hud.lines[2].value_color = settings->antialias ? White : Grey; 37 | } 38 | } 39 | 40 | NavigationMove *move = &app->viewport.navigation.move; 41 | if (key == 'R') move->up = is_pressed; 42 | if (key == 'F') move->down = is_pressed; 43 | if (key == 'W') move->forward = is_pressed; 44 | if (key == 'A') move->left = is_pressed; 45 | if (key == 'S') move->backward = is_pressed; 46 | if (key == 'D') move->right = is_pressed; 47 | } 48 | void updateViewport(Viewport *viewport, Mouse *mouse) { 49 | if (mouse->is_captured) { 50 | navigateViewport(viewport, app->time.timers.update.delta_time); 51 | if (mouse->moved) orientViewport(viewport, mouse); 52 | if (mouse->wheel_scrolled) zoomViewport(viewport, mouse); 53 | } else { 54 | if (mouse->wheel_scrolled) dollyViewport(viewport, mouse); 55 | if (mouse->moved) { 56 | if (mouse->middle_button.is_pressed) panViewport(viewport, mouse); 57 | if (mouse->right_button.is_pressed) orbitViewport(viewport, mouse); 58 | } 59 | } 60 | } 61 | void updateAndRender() { 62 | Timer *timer = &app->time.timers.update; 63 | Controls *controls = &app->controls; 64 | Viewport *viewport = &app->viewport; 65 | Mouse *mouse = &controls->mouse; 66 | Scene *scene = &app->scene; 67 | 68 | beginFrame(timer); 69 | if (!mouse->is_captured) manipulateSelection(scene, viewport, controls); 70 | if (!controls->is_pressed.alt) updateViewport(viewport, mouse); 71 | beginDrawing(viewport); 72 | rasterize(scene, viewport, &app->rasterizer); 73 | drawSelection(scene, viewport, controls); 74 | printNumberIntoString((i16)app->time.timers.update.average_frames_per_second, &viewport->hud.lines->value); 75 | endDrawing(viewport); 76 | endFrame(timer, mouse); 77 | } 78 | void setupViewport(Viewport *viewport) { 79 | xform3 *cam_xform = &viewport->camera->transform; 80 | cam_xform->position = Vec3(0, 15, -15); 81 | rotateXform3(cam_xform, 0, -0.25f, 0); 82 | 83 | i32 average_fps = app->time.timers.update.average_frames_per_second; 84 | 85 | HUDLine *fps = viewport->hud.lines; 86 | HUDLine *wireframe = fps + 1; 87 | HUDLine *msaa = wireframe + 1; 88 | HUDLine *mode = msaa + 1; 89 | wireframe->value_color = msaa->value_color = mode->value_color = Grey; 90 | 91 | printNumberIntoString(average_fps, &fps->value); 92 | 93 | setString(&fps->title, (char*)"Fps: "); 94 | setString(&wireframe->title, (char*)"Wireframe: "); 95 | setString(&wireframe->value.string, (char*)"Off"); 96 | setString(&msaa->title, (char*)"MSAA: "); 97 | setString(&msaa->value.string, (char*)"Off"); 98 | setString(&mode->title, (char*)"Mode: "); 99 | setString(&mode->value.string, (char*)"Beauty"); 100 | } 101 | void setupScene(Scene *scene) { 102 | Material *floor_material = scene->materials + 0; 103 | Material *monkey_material1 = scene->materials + 1; 104 | Material *monkey_material2 = scene->materials + 2; 105 | Material *monkey_material3 = scene->materials + 3; 106 | Primitive *floor = scene->primitives + 0; 107 | Primitive *monkey1 = scene->primitives + 1; 108 | Primitive *monkey2 = scene->primitives + 2; 109 | Primitive *monkey3 = scene->primitives + 3; 110 | 111 | monkey_material1->pixel_shader = shadePixelPosition; 112 | monkey_material2->pixel_shader = shadePixelUV; 113 | monkey_material3->pixel_shader = shadePixelNormal; 114 | floor_material->pixel_shader = shadePixelCheckerboard; 115 | 116 | floor->type = PrimitiveType_Box; 117 | floor->scale = Vec3(16, 1, 16); 118 | floor->position = Vec3(-6, -3, 0); 119 | floor->material_id = 0; 120 | 121 | monkey1->type = monkey2->type = monkey3->type = PrimitiveType_Mesh; 122 | monkey1->id = monkey2->id = monkey3->id = 0; 123 | monkey1->material_id = 1; 124 | monkey2->material_id = 2; 125 | monkey3->material_id = 3; 126 | monkey2->position = Vec3(2, 2, 3); 127 | monkey3->position = Vec3(-2, 2, -3); 128 | monkey1->rotation = getRotationAroundAxisBySinCon(Vec3(0, 1, 0), Vec2(sinf(0.5f), cosf(0.5f))); 129 | 130 | floor_material->texture_count = 2; 131 | floor_material->texture_ids[0] = 0; 132 | floor_material->texture_ids[1] = 1; 133 | floor_material->normal_magnitude = 0.4f; 134 | } 135 | 136 | void initApp(Defaults *defaults) { 137 | static char string_buffer[100]; 138 | static String file; 139 | file.char_ptr = string_buffer; 140 | char* this_file = __FILE__; 141 | char* monkey_mesh = "suzanne.mesh"; 142 | u32 dir_len = getDirectoryLength(this_file); 143 | mergeString(&file, this_file, monkey_mesh, dir_len); 144 | defaults->settings.scene.meshes = 1; 145 | defaults->settings.scene.mesh_files = &file; 146 | defaults->settings.scene.primitives = 4; 147 | defaults->settings.scene.materials = 4; 148 | defaults->settings.viewport.near_clipping_plane_distance = 1; 149 | defaults->settings.viewport.hud_line_count = 4; 150 | app->on.sceneReady = setupScene; 151 | app->on.viewportReady = setupViewport; 152 | app->on.windowRedraw = updateAndRender; 153 | app->on.keyChanged = onKeyChanged; 154 | app->on.mouseButtonDown = onMouseButtonDown; 155 | app->on.mouseButtonDoubleClicked = onMouseDoubleClicked; 156 | } -------------------------------------------------------------------------------- /src/examples/1_clipping_interpolation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HardCoreCodin/SlimRaster/3718a02b52844f5a66376314ff63b76eff886e56/src/examples/1_clipping_interpolation.gif -------------------------------------------------------------------------------- /src/examples/2_normal_maps.c: -------------------------------------------------------------------------------- 1 | #include "../SlimRaster/app.h" 2 | #include "../SlimRaster/core/time.h" 3 | #include "../SlimRaster/viewport/viewport.h" 4 | #include "../SlimRaster/viewport/navigation.h" 5 | #include "../SlimRaster/viewport/manipulation.h" 6 | #include "../SlimRaster/renderer/rasterizer.h" 7 | // Or using the single-header file: 8 | // #include "../SlimRaster.h" 9 | 10 | 11 | void onMouseButtonDown(MouseButton *mouse_button) { 12 | app->controls.mouse.pos_raw_diff = Vec2i(0, 0); 13 | } 14 | void onMouseDoubleClicked(MouseButton *mouse_button) { 15 | if (mouse_button == &app->controls.mouse.left_button) { 16 | app->controls.mouse.is_captured = !app->controls.mouse.is_captured; 17 | app->platform.setCursorVisibility(!app->controls.mouse.is_captured); 18 | app->platform.setWindowCapture( app->controls.mouse.is_captured); 19 | onMouseButtonDown(mouse_button); 20 | } 21 | } 22 | void onKeyChanged(u8 key, bool is_pressed) { 23 | if (!is_pressed) { 24 | Viewport *viewport = &app->viewport; 25 | ViewportSettings *settings = &viewport->settings; 26 | u8 tab = app->controls.key_map.tab; 27 | if (key == tab) settings->show_hud = !settings->show_hud; 28 | } 29 | 30 | NavigationMove *move = &app->viewport.navigation.move; 31 | if (key == 'R') move->up = is_pressed; 32 | if (key == 'F') move->down = is_pressed; 33 | if (key == 'W') move->forward = is_pressed; 34 | if (key == 'A') move->left = is_pressed; 35 | if (key == 'S') move->backward = is_pressed; 36 | if (key == 'D') move->right = is_pressed; 37 | } 38 | void updateViewport(Viewport *viewport, Mouse *mouse) { 39 | if (mouse->is_captured) { 40 | navigateViewport(viewport, app->time.timers.update.delta_time); 41 | if (mouse->moved) orientViewport(viewport, mouse); 42 | if (mouse->wheel_scrolled) zoomViewport(viewport, mouse); 43 | } else { 44 | if (mouse->wheel_scrolled) dollyViewport(viewport, mouse); 45 | if (mouse->moved) { 46 | if (mouse->middle_button.is_pressed) panViewport(viewport, mouse); 47 | if (mouse->right_button.is_pressed) orbitViewport(viewport, mouse); 48 | } 49 | } 50 | } 51 | void updateAndRender() { 52 | Timer *timer = &app->time.timers.update; 53 | Controls *controls = &app->controls; 54 | Viewport *viewport = &app->viewport; 55 | Mouse *mouse = &controls->mouse; 56 | Scene *scene = &app->scene; 57 | f32 dt = app->time.timers.update.delta_time; 58 | static float elapsed = 0; 59 | elapsed += dt; 60 | 61 | vec2 sincos = Vec2(sinf(elapsed), cosf(elapsed)); 62 | vec3 *prim_pos = &scene->primitives[0].position; 63 | vec3 *light_pos = &scene->lights[0].position_or_direction; 64 | 65 | light_pos->x = prim_pos->x - 3.0f + sincos.x * 0.6f; 66 | light_pos->z = prim_pos->z + 3.0f + sincos.y * 0.6f; 67 | light_pos->y = 2 + sinf(elapsed * 2.0f); 68 | 69 | prim_pos = &scene->primitives[1].position; 70 | light_pos = &scene->lights[2].position_or_direction; 71 | light_pos->x = prim_pos->x + 3.0f + sinf(elapsed * 0.5f) * 0.6f; 72 | light_pos->z = prim_pos->z + 3.0f + cosf(elapsed * 0.5f) * 0.6f; 73 | light_pos->y = 2 + cosf(elapsed * 2.0f); 74 | 75 | beginFrame(timer); 76 | if (!mouse->is_captured) manipulateSelection(scene, viewport, controls); 77 | Material *material = scene->materials + scene->selection->object_id; 78 | if (controls->is_pressed.ctrl) { 79 | if (mouse->wheel_scrolled) { 80 | mouse->wheel_scrolled = false; 81 | mouse->wheel_scroll_handled = true; 82 | material->normal_magnitude += mouse->wheel_scroll_amount * 0.001f; 83 | material->normal_magnitude = clampValueToBetween(material->normal_magnitude, 0, 4); 84 | printFloatIntoString(material->normal_magnitude, &viewport->hud.lines->value, 1); 85 | } 86 | } else if (!controls->is_pressed.alt) updateViewport(viewport, mouse); 87 | if (scene->selection->changed) { 88 | scene->selection->changed = false; 89 | if (!mouse->wheel_scroll_handled) 90 | printFloatIntoString(material->normal_magnitude, &viewport->hud.lines->value, 1); 91 | } 92 | beginDrawing(viewport); 93 | rasterize(scene, viewport, &app->rasterizer); 94 | drawSelection(scene, viewport, controls); 95 | endDrawing(viewport); 96 | endFrame(timer, mouse); 97 | } 98 | void setupViewport(Viewport *viewport) { 99 | xform3 *cam_xform = &viewport->camera->transform; 100 | cam_xform->position = Vec3(0, 15, -15); 101 | rotateXform3(cam_xform, 0, -0.25f, 0); 102 | setString(&viewport->hud.lines->title, (char*)"Normal Magnitude: "); 103 | } 104 | void setupScene(Scene *scene) { 105 | scene->ambient_light.color = Vec3(0.008f, 0.008f, 0.014f); 106 | 107 | Light *light1 = scene->lights + 0; 108 | Light *light2 = scene->lights + 1; 109 | Light *light3 = scene->lights + 2; 110 | Material *floor_material = scene->materials + 0; 111 | Material *dog_material = scene->materials + 1; 112 | Primitive *floor = scene->primitives + 0; 113 | Primitive *dog = scene->primitives + 1; 114 | 115 | floor_material->flags |= PHONG; 116 | floor_material->diffuse = Vec3(0.7f, 0.7f, 0.7f); 117 | floor_material->pixel_shader = shadePixelClassic; 118 | floor_material->texture_count = 2; 119 | floor_material->texture_ids[0] = 0; 120 | floor_material->texture_ids[1] = 1; 121 | floor_material->normal_magnitude = 0.4f; 122 | 123 | dog_material->flags |= PHONG; 124 | dog_material->diffuse = Vec3(0.4f, 0.4f, 0.4f); 125 | dog_material->pixel_shader = shadePixelClassic; 126 | dog_material->texture_count = 2; 127 | dog_material->texture_ids[0] = 2; 128 | dog_material->texture_ids[1] = 3; 129 | dog_material->normal_magnitude = 3.0f; 130 | 131 | floor->type = PrimitiveType_Box; 132 | floor->scale = Vec3(16, 1, 16); 133 | floor->position = Vec3(-6, -3, 0); 134 | floor->material_id = 0; 135 | 136 | dog->type = PrimitiveType_Mesh; 137 | dog->id = 0; 138 | dog->material_id = 1; 139 | dog->position = Vec3(2, 2, 9); 140 | dog->rotation = getRotationAroundAxisBySinCon(Vec3(0, 1, 0), Vec2(sinf(0.5f), cosf(0.5f))); 141 | 142 | vec3 mesh1_position = Vec3(0, 0, 5); 143 | vec3 mesh2_position = Vec3(5, 0, 5); 144 | 145 | light1->intensity = 20; 146 | light1->color.r = 0.8f; 147 | light1->color.g = 0.3f; 148 | light1->color.b = 0.2f; 149 | light1->position_or_direction.x = mesh1_position.x - 3; 150 | light1->position_or_direction.z = mesh1_position.z + 3; 151 | light1->position_or_direction.y = 5; 152 | 153 | light2->intensity = 20; 154 | light2->color.r = 0.2f; 155 | light2->color.g = 0.3f; 156 | light2->color.b = 0.8f; 157 | light2->position_or_direction.x = mesh2_position.x + 3; 158 | light2->position_or_direction.z = mesh2_position.z + 3; 159 | light2->position_or_direction.y = 4; 160 | 161 | light3->intensity = 16; 162 | light3->color.r = 0.2f; 163 | light3->color.g = 0.9f; 164 | light3->color.b = 0.3f; 165 | light3->position_or_direction.x = (mesh1_position.x + mesh2_position.x) / 2; 166 | light3->position_or_direction.z = -1; 167 | light3->position_or_direction.y = 3; 168 | } 169 | 170 | void initApp(Defaults *defaults) { 171 | static char string_buffers[5][100]; 172 | static String files[5]; 173 | files[0].char_ptr = string_buffers[0]; 174 | files[1].char_ptr = string_buffers[1]; 175 | files[2].char_ptr = string_buffers[2]; 176 | files[3].char_ptr = string_buffers[3]; 177 | files[4].char_ptr = string_buffers[4]; 178 | 179 | char* this_file = __FILE__; 180 | char* dog_mesh = "dog.mesh"; 181 | char* dog_albedo = "dog_albedo.texture"; 182 | char* dog_normal = "dog_normal.texture"; 183 | char* floor_albedo = "floor_albedo.texture"; 184 | char* floor_normal = "floor_normal.texture"; 185 | u32 dir_len = getDirectoryLength(this_file); 186 | mergeString(files, this_file, dog_mesh, dir_len); 187 | mergeString(files + 1, this_file, floor_albedo, dir_len); 188 | mergeString(files + 2, this_file, floor_normal, dir_len); 189 | mergeString(files + 3, this_file, dog_albedo, dir_len); 190 | mergeString(files + 4, this_file, dog_normal, dir_len); 191 | defaults->settings.scene.textures = 4; 192 | defaults->settings.scene.meshes = 1; 193 | defaults->settings.scene.mesh_files = files; 194 | defaults->settings.scene.texture_files = files + 1; 195 | defaults->settings.scene.lights = 3; 196 | defaults->settings.scene.primitives = 2; 197 | defaults->settings.scene.materials = 2; 198 | defaults->settings.viewport.near_clipping_plane_distance = 1; 199 | defaults->settings.viewport.hud_line_count = 1; 200 | app->on.sceneReady = setupScene; 201 | app->on.viewportReady = setupViewport; 202 | app->on.windowRedraw = updateAndRender; 203 | app->on.keyChanged = onKeyChanged; 204 | app->on.mouseButtonDown = onMouseButtonDown; 205 | app->on.mouseButtonDoubleClicked = onMouseDoubleClicked; 206 | } -------------------------------------------------------------------------------- /src/examples/2_normal_maps.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HardCoreCodin/SlimRaster/3718a02b52844f5a66376314ff63b76eff886e56/src/examples/2_normal_maps.gif -------------------------------------------------------------------------------- /src/examples/3_anti_aliasing.c: -------------------------------------------------------------------------------- 1 | #include "../SlimRaster/app.h" 2 | #include "../SlimRaster/core/time.h" 3 | #include "../SlimRaster/viewport/viewport.h" 4 | #include "../SlimRaster/viewport/navigation.h" 5 | #include "../SlimRaster/viewport/manipulation.h" 6 | #include "../SlimRaster/renderer/rasterizer.h" 7 | // Or using the single-header file: 8 | // #include "../SlimRaster.h" 9 | 10 | 11 | void onMouseButtonDown(MouseButton *mouse_button) { 12 | app->controls.mouse.pos_raw_diff = Vec2i(0, 0); 13 | } 14 | void onMouseDoubleClicked(MouseButton *mouse_button) { 15 | if (mouse_button == &app->controls.mouse.left_button) { 16 | app->controls.mouse.is_captured = !app->controls.mouse.is_captured; 17 | app->platform.setCursorVisibility(!app->controls.mouse.is_captured); 18 | app->platform.setWindowCapture( app->controls.mouse.is_captured); 19 | onMouseButtonDown(mouse_button); 20 | } 21 | } 22 | void onKeyChanged(u8 key, bool is_pressed) { 23 | if (!is_pressed) { 24 | Viewport *viewport = &app->viewport; 25 | ViewportSettings *settings = &viewport->settings; 26 | u8 tab = app->controls.key_map.tab; 27 | if (key == tab) settings->show_hud = !settings->show_hud; 28 | if (key == '1') { 29 | settings->show_wire_frame = !settings->show_wire_frame; 30 | setString(&viewport->hud.lines[1].value.string,settings->show_wire_frame ? (char*)"On" : (char*)"Off"); 31 | viewport->hud.lines[1].value_color = settings->show_wire_frame ? White : Grey; 32 | } 33 | if (key == '2') { 34 | settings->antialias = !settings->antialias; 35 | setString(&viewport->hud.lines[2].value.string,settings->antialias ? (char*)"On" : (char*)"Off"); 36 | viewport->hud.lines[2].value_color = settings->antialias ? White : Grey; 37 | } 38 | } 39 | 40 | NavigationMove *move = &app->viewport.navigation.move; 41 | if (key == 'R') move->up = is_pressed; 42 | if (key == 'F') move->down = is_pressed; 43 | if (key == 'W') move->forward = is_pressed; 44 | if (key == 'A') move->left = is_pressed; 45 | if (key == 'S') move->backward = is_pressed; 46 | if (key == 'D') move->right = is_pressed; 47 | } 48 | void updateViewport(Viewport *viewport, Mouse *mouse) { 49 | if (mouse->is_captured) { 50 | navigateViewport(viewport, app->time.timers.update.delta_time); 51 | if (mouse->moved) orientViewport(viewport, mouse); 52 | if (mouse->wheel_scrolled) zoomViewport(viewport, mouse); 53 | } else { 54 | if (mouse->wheel_scrolled) dollyViewport(viewport, mouse); 55 | if (mouse->moved) { 56 | if (mouse->middle_button.is_pressed) panViewport(viewport, mouse); 57 | if (mouse->right_button.is_pressed) orbitViewport(viewport, mouse); 58 | } 59 | } 60 | } 61 | void updateAndRender() { 62 | Timer *timer = &app->time.timers.update; 63 | Controls *controls = &app->controls; 64 | Viewport *viewport = &app->viewport; 65 | Mouse *mouse = &controls->mouse; 66 | Scene *scene = &app->scene; 67 | f32 dt = app->time.timers.update.delta_time; 68 | static float elapsed = 0; 69 | elapsed += dt; 70 | 71 | vec2 sincos = Vec2(sinf(elapsed), cosf(elapsed)); 72 | vec3 *prim_pos = &scene->primitives[0].position; 73 | vec3 *light_pos = &scene->lights[0].position_or_direction; 74 | 75 | light_pos->x = prim_pos->x - 3.0f + sincos.x * 0.6f; 76 | light_pos->z = prim_pos->z + 3.0f + sincos.y * 0.6f; 77 | light_pos->y = 2 + sinf(elapsed * 2.0f); 78 | 79 | prim_pos = &scene->primitives[1].position; 80 | light_pos = &scene->lights[2].position_or_direction; 81 | light_pos->x = prim_pos->x + 3.0f + sinf(elapsed * 0.5f) * 0.6f; 82 | light_pos->z = prim_pos->z + 3.0f + cosf(elapsed * 0.5f) * 0.6f; 83 | light_pos->y = 2 + cosf(elapsed * 2.0f); 84 | 85 | beginFrame(timer); 86 | if (!mouse->is_captured) manipulateSelection(scene, viewport, controls); 87 | if (!controls->is_pressed.alt) updateViewport(viewport, mouse); 88 | beginDrawing(viewport); 89 | rasterize(scene, viewport, &app->rasterizer); 90 | drawSelection(scene, viewport, controls); 91 | printNumberIntoString((i16)app->time.timers.update.average_frames_per_second, &viewport->hud.lines->value); 92 | endDrawing(viewport); 93 | endFrame(timer, mouse); 94 | } 95 | void setupViewport(Viewport *viewport) { 96 | xform3 *cam_xform = &viewport->camera->transform; 97 | cam_xform->position = Vec3(0, 15, -15); 98 | rotateXform3(cam_xform, 0, -0.25f, 0); 99 | 100 | Mesh *mesh = app->scene.meshes; 101 | i32 triangle_count = (i32)mesh->triangle_count + 12; 102 | i32 average_fps = app->time.timers.update.average_frames_per_second; 103 | 104 | HUDLine *fps = viewport->hud.lines; 105 | HUDLine *wireframe = fps + 1; 106 | HUDLine *msaa = wireframe + 1; 107 | HUDLine *triangles = msaa + 1; 108 | wireframe->value_color = msaa->value_color = triangles->value_color = Grey; 109 | 110 | printNumberIntoString(average_fps, &fps->value); 111 | printNumberIntoString(triangle_count, &triangles->value); 112 | 113 | setString(&fps->title, (char*)"Fps: "); 114 | setString(&wireframe->title, (char*)"Wireframe: "); 115 | setString(&wireframe->value.string, (char*)"Off"); 116 | setString(&msaa->title, (char*)"MSAA: "); 117 | setString(&msaa->value.string, (char*)"Off"); 118 | setString(&triangles->title, (char*)"Triangles: "); 119 | } 120 | void setupScene(Scene *scene) { 121 | Light *light1 = scene->lights + 0; 122 | Light *light2 = scene->lights + 1; 123 | Light *light3 = scene->lights + 2; 124 | Material *dog_material = scene->materials + 0; 125 | Material *floor_material = scene->materials + 1; 126 | Primitive *floor = scene->primitives + 0; 127 | Primitive *dog = scene->primitives + 1; 128 | 129 | dog_material->flags |= BLINN; 130 | dog_material->diffuse = Vec3(0.4f, 0.4f, 0.4f); 131 | floor_material->diffuse = Vec3(0.7f, 0.7f, 0.7f); 132 | 133 | dog_material->pixel_shader = floor_material->pixel_shader = shadePixelClassic; 134 | 135 | floor->type = PrimitiveType_Box; 136 | floor->scale = Vec3(16, 1, 16); 137 | floor->position = Vec3(-6, -3, 0); 138 | floor->material_id = 0; 139 | 140 | dog->type = PrimitiveType_Mesh; 141 | dog->id = 0; 142 | dog->material_id = 1; 143 | dog->position = Vec3(2, 2, 9); 144 | dog->rotation = getRotationAroundAxisBySinCon(Vec3(0, 1, 0), Vec2(sinf(0.5f), cosf(0.5f))); 145 | 146 | scene->ambient_light.color = Vec3(0.008f, 0.008f, 0.014f); 147 | 148 | vec3 mesh1_position = Vec3(0, 0, 5); 149 | vec3 mesh2_position = Vec3(5, 0, 5); 150 | 151 | light1->intensity = 20; 152 | light1->color.r = 0.8f; 153 | light1->color.g = 0.3f; 154 | light1->color.b = 0.2f; 155 | light1->position_or_direction.x = mesh1_position.x - 3; 156 | light1->position_or_direction.z = mesh1_position.z + 3; 157 | light1->position_or_direction.y = 5; 158 | 159 | light2->intensity = 20; 160 | light2->color.r = 0.2f; 161 | light2->color.g = 0.3f; 162 | light2->color.b = 0.8f; 163 | light2->position_or_direction.x = mesh2_position.x + 3; 164 | light2->position_or_direction.z = mesh2_position.z + 3; 165 | light2->position_or_direction.y = 4; 166 | 167 | light3->intensity = 16; 168 | light3->color.r = 0.2f; 169 | light3->color.g = 0.9f; 170 | light3->color.b = 0.3f; 171 | light3->position_or_direction.x = (mesh1_position.x + mesh2_position.x) / 2; 172 | light3->position_or_direction.z = -1; 173 | light3->position_or_direction.y = 3; 174 | 175 | dog_material->texture_count = 2; 176 | dog_material->texture_ids[0] = 0; 177 | dog_material->texture_ids[1] = 1; 178 | dog_material->normal_magnitude = 4.0f; 179 | 180 | floor_material->texture_count = 2; 181 | floor_material->texture_ids[0] = 2; 182 | floor_material->texture_ids[1] = 3; 183 | floor_material->normal_magnitude = 0.4f; 184 | } 185 | 186 | void initApp(Defaults *defaults) { 187 | static char string_buffers[5][100]; 188 | static String files[5]; 189 | files[0].char_ptr = string_buffers[0]; 190 | files[1].char_ptr = string_buffers[1]; 191 | files[2].char_ptr = string_buffers[2]; 192 | files[3].char_ptr = string_buffers[3]; 193 | files[4].char_ptr = string_buffers[4]; 194 | 195 | char* this_file = __FILE__; 196 | char* dog_mesh = "dog.mesh"; 197 | char* dog_texture = "dog_albedo.texture"; 198 | char* dog_normal = "dog_normal.texture"; 199 | char* rock_texture = "floor_albedo.texture"; 200 | char* rock_normal = "floor_normal.texture"; 201 | u32 dir_len = getDirectoryLength(this_file); 202 | mergeString(files, this_file, dog_mesh, dir_len); 203 | mergeString(files + 1, this_file, rock_texture, dir_len); 204 | mergeString(files + 2, this_file, rock_normal, dir_len); 205 | mergeString(files + 3, this_file, dog_texture, dir_len); 206 | mergeString(files + 4, this_file, dog_normal, dir_len); 207 | defaults->settings.scene.textures = 4; 208 | defaults->settings.scene.meshes = 1; 209 | defaults->settings.scene.mesh_files = files; 210 | defaults->settings.scene.texture_files = files + 1; 211 | defaults->settings.scene.lights = 3; 212 | defaults->settings.scene.primitives = 2; 213 | defaults->settings.scene.materials = 2; 214 | defaults->settings.viewport.near_clipping_plane_distance = 1; 215 | defaults->settings.viewport.hud_line_count = 4; 216 | app->on.sceneReady = setupScene; 217 | app->on.viewportReady = setupViewport; 218 | app->on.windowRedraw = updateAndRender; 219 | app->on.keyChanged = onKeyChanged; 220 | app->on.mouseButtonDown = onMouseButtonDown; 221 | app->on.mouseButtonDoubleClicked = onMouseDoubleClicked; 222 | } -------------------------------------------------------------------------------- /src/examples/SlimRaster.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HardCoreCodin/SlimRaster/3718a02b52844f5a66376314ff63b76eff886e56/src/examples/SlimRaster.gif -------------------------------------------------------------------------------- /src/examples/dog.mesh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HardCoreCodin/SlimRaster/3718a02b52844f5a66376314ff63b76eff886e56/src/examples/dog.mesh -------------------------------------------------------------------------------- /src/examples/dog_albedo.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HardCoreCodin/SlimRaster/3718a02b52844f5a66376314ff63b76eff886e56/src/examples/dog_albedo.texture -------------------------------------------------------------------------------- /src/examples/dog_normal.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HardCoreCodin/SlimRaster/3718a02b52844f5a66376314ff63b76eff886e56/src/examples/dog_normal.texture -------------------------------------------------------------------------------- /src/examples/dragon.mesh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HardCoreCodin/SlimRaster/3718a02b52844f5a66376314ff63b76eff886e56/src/examples/dragon.mesh -------------------------------------------------------------------------------- /src/examples/floor_albedo.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HardCoreCodin/SlimRaster/3718a02b52844f5a66376314ff63b76eff886e56/src/examples/floor_albedo.texture -------------------------------------------------------------------------------- /src/examples/floor_normal.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HardCoreCodin/SlimRaster/3718a02b52844f5a66376314ff63b76eff886e56/src/examples/floor_normal.texture -------------------------------------------------------------------------------- /src/examples/suzanne.mesh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HardCoreCodin/SlimRaster/3718a02b52844f5a66376314ff63b76eff886e56/src/examples/suzanne.mesh -------------------------------------------------------------------------------- /src/obj2mesh.c: -------------------------------------------------------------------------------- 1 | #ifdef COMPILER_CLANG 2 | #pragma clang diagnostic push 3 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 4 | #else 5 | #define _CRT_SECURE_NO_DEPRECATE 6 | #define _CRT_SECURE_NO_WARNINGS 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "./SlimRaster/core/types.h" 14 | #include "./SlimRaster/math/vec3.h" 15 | #include "./SlimRaster/math/mat3.h" 16 | 17 | enum VertexAttributes { 18 | VertexAttributes_None, 19 | VertexAttributes_Positions, 20 | VertexAttributes_PositionsAndUVs, 21 | VertexAttributes_PositionsUVsAndNormals 22 | }; 23 | 24 | int obj2mesh(char* obj_file_path, char* mesh_file_path, bool invert_winding_order) { 25 | Mesh mesh; 26 | mesh.aabb.min.x = mesh.aabb.min.y = mesh.aabb.min.z = 0; 27 | mesh.aabb.max.x = mesh.aabb.max.y = mesh.aabb.max.z = 0; 28 | mesh.triangle_count = 0; 29 | mesh.normals_count = 0; 30 | mesh.vertex_count = 0; 31 | mesh.edge_count = 0; 32 | mesh.uvs_count = 0; 33 | mesh.vertex_normals = null; 34 | mesh.vertex_normal_indices = null; 35 | mesh.vertex_uvs = null; 36 | mesh.vertex_uvs_indices = null; 37 | 38 | FILE* file; 39 | file = fopen(obj_file_path, "r"); 40 | char line[1024]; 41 | 42 | enum VertexAttributes vertex_attributes = VertexAttributes_None; 43 | while (fgets(line, 1024, file)) { 44 | if (strncmp(line, (char*)"vn ", 2) == 0) mesh.normals_count++; 45 | if (strncmp(line, (char*)"vt ", 2) == 0) mesh.uvs_count++; 46 | if (strncmp(line, (char*)"v ", 2) == 0) mesh.vertex_count++; 47 | if (strncmp(line, (char*)"f ", 2) == 0) { 48 | mesh.triangle_count++; 49 | if (vertex_attributes == VertexAttributes_None) { 50 | int forward_slash_count = 0; 51 | char *character = line; 52 | while (*character) { 53 | if ((*character) == '/') forward_slash_count++; 54 | character++; 55 | } 56 | switch (forward_slash_count) { 57 | case 0: vertex_attributes = VertexAttributes_Positions; break; 58 | case 3: vertex_attributes = VertexAttributes_PositionsAndUVs; break; 59 | case 6: vertex_attributes = VertexAttributes_PositionsUVsAndNormals; break; 60 | default: break; 61 | } 62 | } 63 | } 64 | } 65 | fclose(file); 66 | 67 | mesh.vertex_position_indices = (TriangleVertexIndices*)malloc(sizeof(TriangleVertexIndices) * mesh.triangle_count); 68 | mesh.vertex_positions = ( vec3*)malloc(sizeof(vec3 ) * mesh.vertex_count); 69 | mesh.edge_vertex_indices = ( EdgeVertexIndices*)malloc(sizeof(EdgeVertexIndices ) * mesh.triangle_count * 3); 70 | 71 | if (vertex_attributes == VertexAttributes_PositionsUVsAndNormals) { 72 | mesh.vertex_normals = ( vec3*)malloc(sizeof(vec3 ) * mesh.normals_count); 73 | mesh.vertex_normal_indices = (TriangleVertexIndices*)malloc(sizeof(TriangleVertexIndices) * mesh.triangle_count); 74 | mesh.vertex_uvs = ( vec2*)malloc(sizeof(vec2 ) * mesh.uvs_count); 75 | mesh.vertex_uvs_indices = (TriangleVertexIndices*)malloc(sizeof(TriangleVertexIndices) * mesh.triangle_count); 76 | } else if (vertex_attributes == VertexAttributes_PositionsAndUVs) { 77 | mesh.vertex_uvs = ( vec2*)malloc(sizeof(vec2 ) * mesh.uvs_count); 78 | mesh.vertex_uvs_indices = (TriangleVertexIndices*)malloc(sizeof(TriangleVertexIndices) * mesh.triangle_count); 79 | } 80 | 81 | vec3 *vertex_position = mesh.vertex_positions; 82 | vec3 *vertex_normal = mesh.vertex_normals; 83 | vec2 *vertex_uvs = mesh.vertex_uvs; 84 | TriangleVertexIndices *vertex_position_indices = mesh.vertex_position_indices; 85 | TriangleVertexIndices *vertex_normal_indices = mesh.vertex_normal_indices; 86 | TriangleVertexIndices *vertex_uvs_indices = mesh.vertex_uvs_indices; 87 | 88 | u8 v1_id = 0; 89 | u8 v2_id = invert_winding_order ? 2 : 1; 90 | u8 v3_id = invert_winding_order ? 1 : 2; 91 | 92 | file = fopen(obj_file_path, (char*)"r"); 93 | while (fgets(line, 1024, file)) { 94 | // Vertex information 95 | if (strncmp(line, (char*)"v ", 2) == 0) { 96 | sscanf(line, (char*)"v %f %f %f", &vertex_position->x, &vertex_position->y, &vertex_position->z); 97 | vertex_position++; 98 | } else if (strncmp(line, (char*)"vn ", 2) == 0) { 99 | sscanf(line, (char*)"vn %f %f %f", &vertex_normal->x, &vertex_normal->y, &vertex_normal->z); 100 | vertex_normal++; 101 | } else if (strncmp(line, (char*)"vt ", 2) == 0) { 102 | sscanf(line, (char*)"vt %f %f", &vertex_uvs->x, &vertex_uvs->y); 103 | vertex_uvs++; 104 | } else if (strncmp(line, (char*)"f ", 2) == 0) { 105 | int vertex_indices[3]; 106 | int uvs_indices[3]; 107 | int normal_indices[3]; 108 | 109 | switch (vertex_attributes) { 110 | case VertexAttributes_Positions: 111 | sscanf( 112 | line, (char*)"f %d %d %d", 113 | &vertex_indices[v1_id], 114 | &vertex_indices[v2_id], 115 | &vertex_indices[v3_id] 116 | ); 117 | break; 118 | case VertexAttributes_PositionsAndUVs: 119 | sscanf( 120 | line, (char*)"f %d/%d %d/%d %d/%d", 121 | &vertex_indices[v1_id], &uvs_indices[v1_id], 122 | &vertex_indices[v2_id], &uvs_indices[v2_id], 123 | &vertex_indices[v3_id], &uvs_indices[v3_id] 124 | ); 125 | vertex_uvs_indices->ids[0] = uvs_indices[0] - 1; 126 | vertex_uvs_indices->ids[1] = uvs_indices[1] - 1; 127 | vertex_uvs_indices->ids[2] = uvs_indices[2] - 1; 128 | vertex_uvs_indices++; 129 | break; 130 | case VertexAttributes_PositionsUVsAndNormals: 131 | sscanf( 132 | line, (char*)"f %d/%d/%d %d/%d/%d %d/%d/%d", 133 | &vertex_indices[v1_id], &uvs_indices[v1_id], &normal_indices[v1_id], 134 | &vertex_indices[v2_id], &uvs_indices[v2_id], &normal_indices[v2_id], 135 | &vertex_indices[v3_id], &uvs_indices[v3_id], &normal_indices[v3_id] 136 | ); 137 | vertex_uvs_indices->ids[0] = uvs_indices[0] - 1; 138 | vertex_uvs_indices->ids[1] = uvs_indices[1] - 1; 139 | vertex_uvs_indices->ids[2] = uvs_indices[2] - 1; 140 | vertex_normal_indices->ids[0] = normal_indices[0] - 1; 141 | vertex_normal_indices->ids[1] = normal_indices[1] - 1; 142 | vertex_normal_indices->ids[2] = normal_indices[2] - 1; 143 | vertex_normal_indices++; 144 | vertex_uvs_indices++; 145 | break; 146 | default: 147 | return 1; 148 | } 149 | vertex_position_indices->ids[0] = vertex_indices[0] - 1; 150 | vertex_position_indices->ids[1] = vertex_indices[1] - 1; 151 | vertex_position_indices->ids[2] = vertex_indices[2] - 1; 152 | vertex_position_indices++; 153 | } 154 | } 155 | fclose(file); 156 | 157 | // Dog/Monkey > 158 | // mat3 rot45 = getMat3Identity(); 159 | // rot45.X.x = 0.70710678118f; 160 | // rot45.X.z = 0.70710678118f; 161 | // rot45.Z.x = -0.70710678118f; 162 | // rot45.Z.z = 0.70710678118f; 163 | // mat3 rot90 = mulMat3(rot45, rot45); 164 | // mat3 rot = mulMat3(rot45, rot90); // Dog 165 | // mat3 rot = rot90; // Monkey 166 | // 167 | // vertex_position = mesh.vertex_normals; 168 | // for (u32 i = 0; i < mesh.normals_count; i++, vertex_position++) 169 | // *vertex_position = mulVec3Mat3(*vertex_position, rot); 170 | // Dog/Monkey < 171 | 172 | vertex_position = mesh.vertex_positions; 173 | for (u32 i = 0; i < mesh.vertex_count; i++, vertex_position++) { 174 | // *vertex_position = mulVec3Mat3(*vertex_position, rot); // Dog/Monkey 175 | mesh.aabb.min.x = mesh.aabb.min.x < vertex_position->x ? mesh.aabb.min.x : vertex_position->x; 176 | mesh.aabb.min.y = mesh.aabb.min.y < vertex_position->y ? mesh.aabb.min.y : vertex_position->y; 177 | mesh.aabb.min.z = mesh.aabb.min.z < vertex_position->z ? mesh.aabb.min.z : vertex_position->z; 178 | mesh.aabb.max.x = mesh.aabb.max.x > vertex_position->x ? mesh.aabb.max.x : vertex_position->x; 179 | mesh.aabb.max.y = mesh.aabb.max.y > vertex_position->y ? mesh.aabb.max.y : vertex_position->y; 180 | mesh.aabb.max.z = mesh.aabb.max.z > vertex_position->z ? mesh.aabb.max.z : vertex_position->z; 181 | } 182 | 183 | vec3 centroid = scaleVec3(addVec3(mesh.aabb.min, mesh.aabb.max), 0.5f); 184 | if (nonZeroVec3(centroid)) { 185 | mesh.aabb.min = subVec3(mesh.aabb.min, centroid); 186 | mesh.aabb.max = subVec3(mesh.aabb.max, centroid); 187 | vertex_position = mesh.vertex_positions; 188 | for (u32 i = 0; i < mesh.vertex_count; i++, vertex_position++) 189 | *vertex_position = subVec3(*vertex_position, centroid); 190 | } 191 | 192 | // Dog/Monkey > 193 | // f32 scale = 0.1f; // dog 194 | // f32 scale = 4.0f; // dog 195 | // vertex_position = mesh.vertex_positions; 196 | // for (u32 i = 0; i < mesh.vertex_count; i++, vertex_position++) 197 | // *vertex_position = scaleVec3(*vertex_position, scale); 198 | // mesh.aabb.min = scaleVec3(mesh.aabb.min, scale); 199 | // mesh.aabb.max = scaleVec3(mesh.aabb.max, scale); 200 | // Dog/Monkey < 201 | 202 | EdgeVertexIndices current_edge_vertex_indices, *edge_vertex_indices; 203 | vertex_position_indices = mesh.vertex_position_indices; 204 | for (u32 i = 0; i < mesh.triangle_count; i++, vertex_position_indices++) { 205 | for (u8 from = 0, to = 1; from < 3; from++, to = (to + 1) % 3) { 206 | current_edge_vertex_indices.from = vertex_position_indices->ids[from]; 207 | current_edge_vertex_indices.to = vertex_position_indices->ids[to]; 208 | if (current_edge_vertex_indices.from > current_edge_vertex_indices.to) { 209 | u32 temp = current_edge_vertex_indices.from; 210 | current_edge_vertex_indices.from = current_edge_vertex_indices.to; 211 | current_edge_vertex_indices.to = temp; 212 | } 213 | 214 | bool found = false; 215 | edge_vertex_indices = mesh.edge_vertex_indices; 216 | for (u32 e = 0; e < mesh.edge_count; e++, edge_vertex_indices++) { 217 | if (edge_vertex_indices->from == current_edge_vertex_indices.from && 218 | edge_vertex_indices->to == current_edge_vertex_indices.to) { 219 | found = true; 220 | break; 221 | } 222 | } 223 | 224 | if (!found) { 225 | mesh.edge_vertex_indices[mesh.edge_count] = current_edge_vertex_indices; 226 | mesh.edge_count++; 227 | } 228 | } 229 | } 230 | 231 | file = fopen(mesh_file_path, (char*)"wb"); 232 | 233 | fwrite(&mesh.aabb, sizeof(AABB), 1, file); 234 | fwrite(&mesh.vertex_count, sizeof(u32), 1, file); 235 | fwrite(&mesh.triangle_count, sizeof(u32), 1, file); 236 | fwrite(&mesh.edge_count, sizeof(u32), 1, file); 237 | fwrite(&mesh.uvs_count, sizeof(u32), 1, file); 238 | fwrite(&mesh.normals_count, sizeof(u32), 1, file); 239 | fwrite( mesh.vertex_positions, sizeof(vec3) , mesh.vertex_count, file); 240 | fwrite( mesh.vertex_position_indices, sizeof(TriangleVertexIndices), mesh.triangle_count, file); 241 | fwrite( mesh.edge_vertex_indices, sizeof(EdgeVertexIndices) , mesh.edge_count, file); 242 | if (mesh.uvs_count) { 243 | fwrite(mesh.vertex_uvs, sizeof(vec2) , mesh.uvs_count, file); 244 | fwrite(mesh.vertex_uvs_indices, sizeof(TriangleVertexIndices) , mesh.triangle_count, file); 245 | } 246 | if (mesh.normals_count) { 247 | fwrite(mesh.vertex_normals, sizeof(vec3) , mesh.normals_count, file); 248 | fwrite(mesh.vertex_normal_indices, sizeof(TriangleVertexIndices) , mesh.triangle_count, file); 249 | } 250 | 251 | fclose(file); 252 | 253 | return 0; 254 | } 255 | 256 | int main(int argc, char *argv[]) { 257 | if (argc == 2 && !strcmp(argv[1], (char*)"--help")) { 258 | printf((char*)("Exactly 2 file paths need to be provided: " 259 | "An '.obj' file (input) then a '.mesh' file (output), " 260 | "and an optional flag '-invert_winding_order' for inverting winding order")); 261 | return 0; 262 | } else if (argc == 3 || // 2 arguments 263 | argc == 4 // 3 arguments 264 | ) { 265 | char *obj_file_path = argv[1]; 266 | char *mesh_file_path = argv[2]; 267 | bool invert_winding_order = argc == 4 ? !strcmp(argv[3], (char*)"-invert_winding_order") : false; 268 | return obj2mesh(obj_file_path, mesh_file_path, invert_winding_order); 269 | } 270 | 271 | printf((char*)("Exactly 2 file paths need to be provided: " 272 | "An '.obj' file (input) then a '.mesh' file (output), " 273 | "and an optional flag '-invert_winding_order' for inverting winding order")); 274 | return 1; 275 | } --------------------------------------------------------------------------------