├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── RenderLibrary ├── RenderLibrary.vcxproj ├── RenderLibrary.vcxproj.filters ├── draw_line.c ├── renderlib.c └── renderlib.h ├── Samples ├── dragon.ase ├── land1.ase └── land2.ase ├── SoftwareRasterizer.sln ├── SoftwareRasterizer ├── SoftwareRasterizer.vcxproj ├── SoftwareRasterizer.vcxproj.filters ├── ase.h ├── ase_compute_normal_vector.c ├── ase_extract_faces.c ├── ase_extract_vertex.c ├── ase_get_faces_count.c ├── ase_get_vertices_count.c ├── ase_read_faces.c ├── ase_read_file.c ├── ase_read_vertices.c ├── compute_lighting_coef.c ├── create_rotation_matrix.c ├── draw_line.c ├── draw_loop.c ├── draw_model.c ├── draw_triangle.c ├── fill_triangle.c ├── get_average_depth.c ├── init_boundaries.c ├── main.c ├── malloc_error.c ├── projection.c ├── quick_sort_faces.c ├── rasterizer.h ├── read_file.c ├── skip_spaces.c ├── swap_structs.c ├── transform_normals.c ├── transform_object.c ├── transform_vertex.c └── translate.c ├── Testing ├── Testing.vcxproj ├── Testing.vcxproj.filters └── main.c ├── dragon.gif ├── land1.gif └── land2.gif /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style=space 3 | indent_size=4 4 | end_of_line=lf 5 | charset=utf-8 6 | trim_trailing_whitespace=true 7 | insert_final_newline=true 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | 3 | *.suo 4 | *.vcxproj.user 5 | 6 | Debug/ 7 | Release/ 8 | /SoftwareRasterizer/*.ase 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This project is an implementation of a software rasterizer, meaning it renders 3D objects (triangles) to a 2D space, purely on the CPU and without using libraries such as DirectX or Vulkan. 4 | The only available tools are a color buffer, and a mean to present it on screen. 5 | 6 | **Disclaimer:** Before you insult me, this is a project I made back in 2002, to explore how all this works and for fun. Many things are poorly designed and this repository doesn't even pretend to be educational, there are many good resources for that on the web. 7 | 8 | ![Dragon](./dragon.gif)
9 | *A dragon rotating on 3 axis, with a light rotating around.* 10 |

11 | 12 | ![Land 1](./land1.gif)
13 | *A terrain map rotating around the Y axis, with a light rotating around in the opposite direction.* 14 |

15 | 16 | ![Land 2](./land2.gif)
17 | *Another terrain map, with a light rotating around.* 18 | 19 | # How it works 20 | 21 | There are plenty of different techniques to render a 3D model, hereafter is an explanation about how this project is implemented. 22 | 23 | ## 1. Load a 3D model 24 | 25 | Here I used the `ASE` file format because it it very easy to parse. `ASE` stands for **AS**cii **E**xport. It is very size-inefficient, but easy to understand and parse, since it is human-readable. 26 | 27 | A model is made of triangles, and triangles are made on vertices. First the list of vertices is loaded, then the triangles are loaded, because a triangle is actually three indices pointing to the vertices it uses. For example, a square will have four vertices and only two triangles, a triangle using vertices 1, 2 and 3, the other triangle using vertices 2, 3 and 4. 28 | 29 | ![3D model](https://upload.wikimedia.org/wikipedia/commons/f/fb/Dolphin_triangle_mesh.png) 30 | 31 | ## 2. Compute normal vectors 32 | 33 | A [normal vector][1] is a vector that indicates how a triangle is oriented in space. Because the [normal vectors][1] are related to the structure of the model, and the structure of the model is never modified at runtime, the [normal vectors][1] can be pre-computed and stored in memory. Computing a [normal vector][1] can be expensive, so this is something you want to avoid doing for each rendered frame. 34 | 35 | The [normal vector][1] is computed using a [cross product][2] and will be necessary to compute the lighting coefficient of the triangle. 36 | 37 | In the current implementation, there is no [back face culling][5], so the orientation of the vector does not really matter. 38 | 39 | ![Normal vector](https://upload.wikimedia.org/wikipedia/commons/thumb/c/cc/Surface_normals.svg/1280px-Surface_normals.svg.png) 40 | 41 | ## 3. Transform the model 42 | 43 | The original structure of the model always remain unchanged. Instead, a transformed copy is created for each frame, and this is the one being rendered. 44 | It allows to apply a new transform each time based on the original unchanged model, and thus avoid to accumulate errors by keeping transforming the original model. 45 | 46 | Translation and scaling are cheap transforms, but [rotations][6] are expensive because they require a lot of cosine and sine function calls as well as many multiplications. For this reason, transforms are often done using [matrices][3] because they can store pre-computed values that will apply to all vertices, and thus saving a lot of operations. The other benefit of [matrices][3] is their capability to compose multiple transforms. 47 | 48 | Note that the model's [normal vectors][1] also have to be transformed if you want the model to be lit in world space. 49 | 50 | ![Rotation](https://upload.wikimedia.org/wikipedia/commons/thumb/7/76/Stress_transformation_3D.svg/2000px-Stress_transformation_3D.svg.png) 51 | 52 | ## 4. Handle occlusion 53 | 54 | Here by occlusion I mean, avoid rendering triangles that are behind other triangles. Usually a good rendering technique uses a [Z buffer][4], but here I used an inefficient but simple technique, which consists of drawing all the triangles, from back to front in depth order. This wastes CPU cycles, but is fairly easy to implement and does the trick. 55 | 56 | For that, triangles are [sorted](https://en.wikipedia.org/wiki/Quicksort) based on their average Z position. Normally, the camera position should be taken into account because the depth of a triangle is relative to the camera, not some random point in space. The position of the camera is not taken into account here, which is bad, but it works because the camera does not move. If it would, the render would be completely broken. 57 | 58 | ![Occluded triangles](https://answers.unrealengine.com/storage/temp/251795-occluded-faces.jpg) 59 | 60 | ## 5. Compute shading 61 | 62 | The [shading][7] here is not about shadow casting, but about how a model is lit. For each triangle of the model, its [normal vector][1] and the position of the light, both in world space, are used to compute the lighting. There are several different [shading][7] (lighting) techniques, and here the flat [shading][7] is used for its extreme simplicity. 63 | 64 | In flat [shading][7], all pixels of a triangle have the same lighting coefficient, and that coefficient is a ratio based on the angle between the triangle orientation, represented by its [normal vector][1], and the light position, represented by the lighting vector, which is the vector directed from the light toward the triangle. When lighting is parallel to the triangle, the coefficient is zero and the triangle is not lit. When lighting is perpendicular to the triangle, the coefficient is one and the triangle is fully lit. The coefficient can be adjusted to be smoothened or produce specular lighting effect with non-linear adjustment. 65 | 66 | Eventually, the model color is multiplied with this coefficient in order to produce a new color that represents the lit triangle. The major drawback with flat [shading][7] is that it is terribly ugly. 67 | 68 | ![Flat and Phong shading](https://upload.wikimedia.org/wikipedia/commons/8/84/Phong-shading-sample.jpg) 69 | 70 | ## 6. Prepare scanline 71 | 72 | For each triangle, the first step to perform is to [project][8] it from 3D space to 2D space. There are different [projection][8] techniques such as orthographic or perspective, here I used a dirty custom and cheap projection computation. 73 | 74 | Once 2D coordinates are determined for each vertex of the triangle, lines are drawn between the 2D coordinates. This line drawing function does not draw to screen, instead it fulfills a buffer keeping track of the X coordinate (min and max) for each given Y coordinate of the lines. Once the three lines of the triangle are plotted, the buffer contains a pair of minimum and maximum X coordinate for each Y coordinate. 75 | 76 | ![](https://www.codeproject.com/KB/GDI/3DSoftwareRenderingEngine/scanlinerasterizer.png) 77 | 78 | ## 7. Fill the color buffer 79 | 80 | For each Y line of the buffer fulfilled at the prepare scanline stage and the color computed at the shading phase, scan from the minimum X to the maximum X and set the color buffer at position X and Y with the final computed color. 81 | 82 | Repeat stages 5, 6 and 7 for all triangles of the model. When all this is done, the color buffer can be presented to screen. 83 | 84 | ![](https://gamasutra.com/db_area/images/scanline.png) 85 | 86 | # Build 87 | 88 | The project files are setup for Visual Studio 2019 Community Edition.
89 | Note that you must have installed the C++ workload. 90 | 91 | I've tested with Visual Studio 2017 Community Edition as well and it works, you just have to tweak `Windows SDK Version` and `Platform Toolset` parameters according to your context in the project settings to get it building. 92 | 93 | # Possible future work 94 | 95 | - Implement a [Z buffer][4] 96 | - Implement [Gouraud](https://en.wikipedia.org/wiki/Gouraud_shading) or [Phong](https://en.wikipedia.org/wiki/Phong_shading) shading 97 | - Linux / MacOS implementations 98 | 99 | [1]: https://en.wikipedia.org/wiki/Normal_(geometry) 100 | [2]: https://en.wikipedia.org/wiki/Cross_product 101 | [3]: https://en.wikipedia.org/wiki/Transformation_matrix 102 | [4]: https://en.wikipedia.org/wiki/Z-buffering 103 | [5]: https://en.wikipedia.org/wiki/Back-face_culling 104 | [6]: https://en.wikipedia.org/wiki/Rotation_matrix 105 | [7]: https://en.wikipedia.org/wiki/Shading 106 | [8]: https://en.wikipedia.org/wiki/3D_projection 107 | -------------------------------------------------------------------------------- /RenderLibrary/RenderLibrary.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | CompileAsC 24 | CompileAsC 25 | NotUsing 26 | 27 | 28 | NotUsing 29 | 30 | 31 | NotUsing 32 | 33 | 34 | NotUsing 35 | 36 | 37 | 38 | 39 | CompileAsC 40 | CompileAsC 41 | NotUsing 42 | 43 | 44 | NotUsing 45 | 46 | 47 | NotUsing 48 | 49 | 50 | NotUsing 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 16.0 60 | {C8C979B0-92F7-43EB-9900-4372FFD41447} 61 | Win32Proj 62 | RenderLibrary 63 | 10.0 64 | 65 | 66 | 67 | StaticLibrary 68 | true 69 | v142 70 | Unicode 71 | 72 | 73 | StaticLibrary 74 | false 75 | v142 76 | true 77 | Unicode 78 | 79 | 80 | StaticLibrary 81 | true 82 | v142 83 | Unicode 84 | 85 | 86 | StaticLibrary 87 | false 88 | v142 89 | true 90 | Unicode 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | true 112 | $(SolutionDir)$(Platform)\$(Configuration)\ 113 | $(Platform)\$(Configuration)\ 114 | 115 | 116 | true 117 | 118 | 119 | false 120 | $(SolutionDir)$(Platform)\$(Configuration)\ 121 | $(Platform)\$(Configuration)\ 122 | 123 | 124 | false 125 | 126 | 127 | 128 | NotUsing 129 | Level3 130 | Disabled 131 | true 132 | WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) 133 | true 134 | 135 | 136 | true 137 | 138 | CompileAsC 139 | 140 | 141 | Windows 142 | true 143 | 144 | 145 | 146 | 147 | NotUsing 148 | Level3 149 | Disabled 150 | true 151 | _DEBUG;_LIB;%(PreprocessorDefinitions) 152 | true 153 | 154 | 155 | true 156 | 157 | CompileAsC 158 | 159 | 160 | Windows 161 | true 162 | 163 | 164 | 165 | 166 | NotUsing 167 | Level3 168 | MaxSpeed 169 | true 170 | true 171 | true 172 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 173 | true 174 | 175 | 176 | true 177 | 178 | CompileAsC 179 | 180 | 181 | Windows 182 | true 183 | true 184 | true 185 | 186 | 187 | 188 | 189 | NotUsing 190 | Level3 191 | MaxSpeed 192 | true 193 | true 194 | true 195 | NDEBUG;_LIB;%(PreprocessorDefinitions) 196 | true 197 | 198 | 199 | true 200 | 201 | CompileAsC 202 | 203 | 204 | Windows 205 | true 206 | true 207 | true 208 | 209 | 210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /RenderLibrary/RenderLibrary.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | 14 | 15 | Source Files 16 | 17 | 18 | Source Files 19 | 20 | 21 | 22 | 23 | Header Files 24 | 25 | 26 | -------------------------------------------------------------------------------- /RenderLibrary/draw_line.c: -------------------------------------------------------------------------------- 1 | #include "renderlib.h" 2 | 3 | void renderlib_draw_horizontal_line(int x1, int x2, int y, COLOR color) 4 | { 5 | int x; 6 | 7 | for (x = x1; x <= x2; x++) 8 | renderlib_set_pixel(x, y, color); 9 | } 10 | 11 | void renderlib_draw_vertical_line(int x, int y1, int y2, COLOR color) 12 | { 13 | int y; 14 | 15 | for (y = y1; y <= y2; y++) 16 | renderlib_set_pixel(x, y, color); 17 | } 18 | 19 | void renderlib_draw_larger_dx(int x1, int y1, int x2, int y2, COLOR color) 20 | { 21 | int x; 22 | int y; 23 | 24 | for (x = x1; x <= x2; x++) 25 | { 26 | y = y1 + ((y2 - y1) * (x - x1)) / (x2 - x1); 27 | renderlib_set_pixel(x, y, color); 28 | } 29 | } 30 | 31 | void renderlib_draw_larger_dy(int x1, int y1, int x2, int y2, COLOR color) 32 | { 33 | int x; 34 | int y; 35 | 36 | for (y = y1; y <= y2; y++) 37 | { 38 | x = x1 + ((x2 - x1) * (y - y1)) / (y2 - y1); 39 | renderlib_set_pixel(x, y, color); 40 | } 41 | } 42 | 43 | void renderlib_draw_line(int x1, int y1, int x2, int y2, COLOR color) 44 | { 45 | int dx; 46 | int dy; 47 | 48 | dx = abs(x2 - x1); 49 | dy = abs(y2 - y1); 50 | 51 | if (dx == 0 && dy == 0) 52 | renderlib_set_pixel(x1, y1, color); 53 | else if (dx == 0) 54 | { 55 | if (y1 < y2) 56 | renderlib_draw_vertical_line(x1, y1, y2, color); 57 | else 58 | renderlib_draw_vertical_line(x1, y2, y1, color); 59 | } 60 | else if (dy == 0) 61 | { 62 | if (x1 > x2) 63 | renderlib_draw_horizontal_line(x1, x2, y1, color); 64 | else 65 | renderlib_draw_horizontal_line(x2, x1, y1, color); 66 | } 67 | else if (dx >= dy) 68 | { 69 | if (x1 <= x2) 70 | renderlib_draw_larger_dx(x1, y1, x2, y2, color); 71 | else 72 | renderlib_draw_larger_dx(x2, y2, x1, y1, color); 73 | } 74 | else // if (dy > dx) 75 | { 76 | if (y1 <= y2) 77 | renderlib_draw_larger_dy(x1, y1, x2, y2, color); 78 | else 79 | renderlib_draw_larger_dy(x2, y2, x1, y1, color); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /RenderLibrary/renderlib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "renderlib.h" 4 | 5 | static HWND _hwnd; 6 | static HDC _win_hDC; 7 | static WNDCLASS _win_class; 8 | 9 | static int _win_w = 0; 10 | static int _win_h = 0; 11 | static int _buffer_w = 0; 12 | static int _buffer_h = 0; 13 | 14 | static int _frames = -1; 15 | static FILETIME _start_frame_time = { 0 }; 16 | static FILETIME _start_total_time = { 0 }; 17 | 18 | static COLOR* _buffer = NULL; 19 | static BITMAPINFO _bmp_info; 20 | 21 | LRESULT CALLBACK _window_proc_func(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 22 | { 23 | RECT wsize; 24 | 25 | if (message == WM_PAINT) 26 | { 27 | if (_buffer != NULL) 28 | { 29 | GetClientRect(hWnd, &wsize); 30 | StretchDIBits(_win_hDC, 0, 0, wsize.right, wsize.bottom, 0, 0, _buffer_w, _buffer_h, _buffer, &_bmp_info, DIB_RGB_COLORS, SRCCOPY); 31 | ValidateRect(_hwnd, NULL); 32 | } 33 | } 34 | else if (message == WM_KEYDOWN) 35 | { 36 | if (wParam == VK_ESCAPE) 37 | { 38 | renderlib_release(); 39 | ExitProcess(0); 40 | } 41 | } 42 | else if (message == WM_CLOSE) 43 | { 44 | renderlib_release(); 45 | ExitProcess(0); 46 | } 47 | else 48 | return (int)DefWindowProc(hWnd, message, wParam, lParam); 49 | 50 | return 1; 51 | } 52 | 53 | int renderlib_create_window(WCHAR* title, int width, int height) 54 | { 55 | int x; 56 | int y; 57 | RECT rect; 58 | 59 | _win_class.style = CS_HREDRAW | CS_VREDRAW; 60 | _win_class.lpfnWndProc = (WNDPROC)_window_proc_func; 61 | _win_class.cbClsExtra = 0; 62 | _win_class.cbWndExtra = 0; 63 | _win_class.hInstance = NULL; 64 | _win_class.hbrBackground = NULL; 65 | _win_class.lpszMenuName = NULL; 66 | _win_class.lpszClassName = title; 67 | 68 | if (RegisterClass(&_win_class) == 0) 69 | return 1; 70 | 71 | x = (GetSystemMetrics(SM_CXSCREEN) - width) >> 1; 72 | y = (GetSystemMetrics(SM_CYSCREEN) - height) >> 1; 73 | 74 | _buffer_w = width; 75 | _buffer_h = height; 76 | 77 | rect.top = y; 78 | rect.left = x; 79 | rect.right = x + width; 80 | rect.bottom = y + height; 81 | 82 | AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW | WS_VISIBLE, FALSE); 83 | 84 | _win_w = rect.right - rect.left; 85 | _win_h = rect.bottom - rect.top; 86 | 87 | _hwnd = CreateWindowEx( 88 | 0, 89 | title, 90 | title, 91 | WS_OVERLAPPEDWINDOW | WS_VISIBLE, 92 | x, y, _win_w, _win_h, 93 | NULL, 94 | NULL, 95 | NULL, 96 | NULL); 97 | 98 | if (_hwnd == NULL) 99 | return 1; 100 | 101 | _buffer = malloc((size_t)_buffer_w * (size_t)_buffer_h * sizeof(COLOR)); 102 | if (_buffer == NULL) 103 | { 104 | printf("Failed to allocate color buffer.\n"); 105 | return 1; 106 | } 107 | 108 | memset(&_bmp_info, 0, sizeof(BITMAPINFO)); 109 | _bmp_info.bmiHeader.biBitCount = 32; 110 | _bmp_info.bmiHeader.biHeight = -height; 111 | _bmp_info.bmiHeader.biWidth = width; 112 | _bmp_info.bmiHeader.biPlanes = 1; 113 | _bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 114 | 115 | _win_hDC = GetDC(_hwnd); 116 | 117 | ShowWindow(_hwnd, SW_SHOW); 118 | UpdateWindow(_hwnd); 119 | 120 | return 0; 121 | } 122 | 123 | COLOR* renderlib_get_color_buffer() 124 | { 125 | return _buffer; 126 | } 127 | 128 | void renderlib_update() 129 | { 130 | MSG msg; 131 | 132 | SendMessage(_hwnd, WM_PAINT, 0, 0); 133 | 134 | while (PeekMessage(&msg, _hwnd, 0, 0, PM_REMOVE)) 135 | { 136 | TranslateMessage(&msg); 137 | DispatchMessage(&msg); 138 | } 139 | } 140 | 141 | int renderlib_get_pixel(int x, int y, COLOR* color) 142 | { 143 | if (_buffer == NULL || color == NULL || x < 0 || y < 0 || x >= _buffer_w || y >= _buffer_h) 144 | return 0; 145 | 146 | *color = _buffer[y * _buffer_w + x]; 147 | 148 | return 1; 149 | } 150 | 151 | int renderlib_set_pixel(int x, int y, COLOR color) 152 | { 153 | if (_buffer == NULL || x < 0 || y < 0 || x >= _buffer_w || y >= _buffer_h) 154 | return 0; 155 | 156 | _buffer[y * _buffer_w + x] = color; 157 | 158 | return 1; 159 | } 160 | 161 | int renderlib_write_bitmap(char* filename) 162 | { 163 | int x; 164 | int y; 165 | FILE* fd; 166 | BITMAPFILEHEADER fh; 167 | 168 | if (_buffer == NULL) 169 | return 0; 170 | 171 | if (fopen_s(&fd, filename, "wb") != 0 || fd == NULL) 172 | return 0; 173 | 174 | fh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); 175 | fh.bfReserved1 = 0; 176 | fh.bfReserved2 = 0; 177 | fh.bfType = 0x4D42; 178 | fh.bfSize = fh.bfOffBits + (_buffer_w * _buffer_h * 4); 179 | 180 | _bmp_info.bmiHeader.biHeight = _buffer_h; 181 | 182 | fwrite(&fh, sizeof(fh), 1, fd); 183 | fwrite(&_bmp_info.bmiHeader, sizeof(_bmp_info.bmiHeader), 1, fd); 184 | 185 | _bmp_info.bmiHeader.biHeight = -_buffer_h; 186 | 187 | for (y = _buffer_h - 1; y >= 0; y--) 188 | { 189 | for (x = 0; x < _buffer_w; x++) 190 | fwrite(_buffer + ((size_t)y * _buffer_w + x), sizeof(COLOR), 1, fd); 191 | } 192 | 193 | fclose(fd); 194 | 195 | return 1; 196 | } 197 | 198 | int renderlib_clear() 199 | { 200 | if (_buffer == NULL) 201 | return 0; 202 | 203 | memset(_buffer, 0, (size_t)_buffer_w * (size_t)_buffer_h * sizeof(COLOR)); 204 | 205 | return 1; 206 | } 207 | 208 | void renderlib_release() 209 | { 210 | ReleaseDC(_hwnd, _win_hDC); 211 | DestroyWindow(_hwnd); 212 | SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 1, 0, 0); 213 | } 214 | 215 | void usleep(long long usec) 216 | { 217 | HANDLE timer; 218 | LARGE_INTEGER ft; 219 | 220 | ft.QuadPart = -(10 * usec); 221 | 222 | timer = CreateWaitableTimer(NULL, TRUE, NULL); 223 | if (timer == 0) 224 | { 225 | printf("Call to CreateWaitableTimer failed.\n"); 226 | exit(1); 227 | } 228 | 229 | SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); 230 | WaitForSingleObject(timer, INFINITE); 231 | CloseHandle(timer); 232 | } 233 | 234 | void renderlib_sync_start() 235 | { 236 | GetSystemTimePreciseAsFileTime(&_start_frame_time); 237 | 238 | if (_start_total_time.dwHighDateTime == 0 && _start_total_time.dwLowDateTime == 0) 239 | _start_total_time = _start_frame_time; 240 | } 241 | 242 | void renderlib_sync_end(int hertz, int print_fps) 243 | { 244 | FILETIME ft; 245 | ULARGE_INTEGER start_frame_time; 246 | ULARGE_INTEGER current_frame_time; 247 | ULARGE_INTEGER start_total_time; 248 | 249 | GetSystemTimePreciseAsFileTime(&ft); 250 | 251 | start_frame_time.LowPart = _start_frame_time.dwLowDateTime; 252 | start_frame_time.HighPart = _start_frame_time.dwHighDateTime; 253 | 254 | current_frame_time.LowPart = ft.dwLowDateTime; 255 | current_frame_time.HighPart = ft.dwHighDateTime; 256 | 257 | start_total_time.LowPart = _start_total_time.dwLowDateTime; 258 | start_total_time.HighPart = _start_total_time.dwHighDateTime; 259 | 260 | usleep((1000 * 1000 / hertz) - ((current_frame_time.QuadPart - start_frame_time.QuadPart) / 10)); 261 | 262 | if (print_fps == 0) 263 | return; 264 | 265 | if (_frames < 0) 266 | _frames = 1; 267 | else 268 | _frames++; 269 | 270 | if (current_frame_time.QuadPart - start_total_time.QuadPart >= 10000000) 271 | { 272 | printf("fps: %d\n", _frames); 273 | _start_total_time = ft; 274 | _frames = 0; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /RenderLibrary/renderlib.h: -------------------------------------------------------------------------------- 1 | #ifndef __RENDERLIB_H__ 2 | #define __RENDERLIB_H__ 3 | 4 | #include 5 | #include 6 | 7 | typedef uint32_t COLOR; 8 | 9 | int renderlib_create_window(WCHAR* title, int w, int h); 10 | COLOR* renderlib_get_color_buffer(); 11 | void renderlib_update(); 12 | int renderlib_clear(); 13 | int renderlib_get_pixel(int x, int y, COLOR* color); 14 | int renderlib_set_pixel(int x, int y, COLOR color); 15 | void renderlib_draw_line(int x1, int y1, int x2, int y2, COLOR color); 16 | int renderlib_write_bitmap(char *filename); 17 | void renderlib_release(); 18 | void renderlib_sync_start(); 19 | void renderlib_sync_end(int hertz, int print_fps); 20 | 21 | #endif // __RENDERLIB_H__ 22 | -------------------------------------------------------------------------------- /SoftwareRasterizer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28803.156 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SoftwareRasterizer", "SoftwareRasterizer\SoftwareRasterizer.vcxproj", "{D9CBD2EE-9AE8-4FAA-B90F-55E27D0F52A2}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {C8C979B0-92F7-43EB-9900-4372FFD41447} = {C8C979B0-92F7-43EB-9900-4372FFD41447} 9 | EndProjectSection 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenderLibrary", "RenderLibrary\RenderLibrary.vcxproj", "{C8C979B0-92F7-43EB-9900-4372FFD41447}" 12 | EndProject 13 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Testing", "Testing\Testing.vcxproj", "{DBF32EBE-DF15-49BB-8DD5-1368D689DC51}" 14 | ProjectSection(ProjectDependencies) = postProject 15 | {C8C979B0-92F7-43EB-9900-4372FFD41447} = {C8C979B0-92F7-43EB-9900-4372FFD41447} 16 | EndProjectSection 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D0584555-50CC-4EBF-BA65-F7D745AFB395}" 19 | ProjectSection(SolutionItems) = preProject 20 | .editorconfig = .editorconfig 21 | EndProjectSection 22 | EndProject 23 | Global 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Debug|x64 = Debug|x64 26 | Debug|x86 = Debug|x86 27 | Release|x64 = Release|x64 28 | Release|x86 = Release|x86 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {D9CBD2EE-9AE8-4FAA-B90F-55E27D0F52A2}.Debug|x64.ActiveCfg = Debug|x64 32 | {D9CBD2EE-9AE8-4FAA-B90F-55E27D0F52A2}.Debug|x64.Build.0 = Debug|x64 33 | {D9CBD2EE-9AE8-4FAA-B90F-55E27D0F52A2}.Debug|x86.ActiveCfg = Debug|Win32 34 | {D9CBD2EE-9AE8-4FAA-B90F-55E27D0F52A2}.Debug|x86.Build.0 = Debug|Win32 35 | {D9CBD2EE-9AE8-4FAA-B90F-55E27D0F52A2}.Release|x64.ActiveCfg = Release|x64 36 | {D9CBD2EE-9AE8-4FAA-B90F-55E27D0F52A2}.Release|x64.Build.0 = Release|x64 37 | {D9CBD2EE-9AE8-4FAA-B90F-55E27D0F52A2}.Release|x86.ActiveCfg = Release|Win32 38 | {D9CBD2EE-9AE8-4FAA-B90F-55E27D0F52A2}.Release|x86.Build.0 = Release|Win32 39 | {C8C979B0-92F7-43EB-9900-4372FFD41447}.Debug|x64.ActiveCfg = Debug|x64 40 | {C8C979B0-92F7-43EB-9900-4372FFD41447}.Debug|x64.Build.0 = Debug|x64 41 | {C8C979B0-92F7-43EB-9900-4372FFD41447}.Debug|x86.ActiveCfg = Debug|Win32 42 | {C8C979B0-92F7-43EB-9900-4372FFD41447}.Debug|x86.Build.0 = Debug|Win32 43 | {C8C979B0-92F7-43EB-9900-4372FFD41447}.Release|x64.ActiveCfg = Release|x64 44 | {C8C979B0-92F7-43EB-9900-4372FFD41447}.Release|x64.Build.0 = Release|x64 45 | {C8C979B0-92F7-43EB-9900-4372FFD41447}.Release|x86.ActiveCfg = Release|Win32 46 | {C8C979B0-92F7-43EB-9900-4372FFD41447}.Release|x86.Build.0 = Release|Win32 47 | {DBF32EBE-DF15-49BB-8DD5-1368D689DC51}.Debug|x64.ActiveCfg = Debug|x64 48 | {DBF32EBE-DF15-49BB-8DD5-1368D689DC51}.Debug|x64.Build.0 = Debug|x64 49 | {DBF32EBE-DF15-49BB-8DD5-1368D689DC51}.Debug|x86.ActiveCfg = Debug|Win32 50 | {DBF32EBE-DF15-49BB-8DD5-1368D689DC51}.Debug|x86.Build.0 = Debug|Win32 51 | {DBF32EBE-DF15-49BB-8DD5-1368D689DC51}.Release|x64.ActiveCfg = Release|x64 52 | {DBF32EBE-DF15-49BB-8DD5-1368D689DC51}.Release|x64.Build.0 = Release|x64 53 | {DBF32EBE-DF15-49BB-8DD5-1368D689DC51}.Release|x86.ActiveCfg = Release|Win32 54 | {DBF32EBE-DF15-49BB-8DD5-1368D689DC51}.Release|x86.Build.0 = Release|Win32 55 | EndGlobalSection 56 | GlobalSection(SolutionProperties) = preSolution 57 | HideSolutionNode = FALSE 58 | EndGlobalSection 59 | GlobalSection(ExtensibilityGlobals) = postSolution 60 | SolutionGuid = {D7420F9B-3C9C-47EB-BEFF-0B8290353859} 61 | EndGlobalSection 62 | EndGlobal 63 | -------------------------------------------------------------------------------- /SoftwareRasterizer/SoftwareRasterizer.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | NotUsing 24 | 25 | 26 | 27 | 28 | CompileAsC 29 | NotUsing 30 | 31 | 32 | 33 | 34 | CompileAsC 35 | NotUsing 36 | 37 | 38 | 39 | 40 | CompileAsC 41 | NotUsing 42 | 43 | 44 | 45 | 46 | CompileAsC 47 | 48 | 49 | NotUsing 50 | 51 | 52 | 53 | 54 | CompileAsC 55 | NotUsing 56 | 57 | 58 | 59 | 60 | CompileAsC 61 | NotUsing 62 | 63 | 64 | 65 | 66 | CompileAsC 67 | NotUsing 68 | 69 | 70 | 71 | 72 | CompileAsC 73 | 74 | 75 | NotUsing 76 | 77 | 78 | 79 | 80 | CompileAsC 81 | NotUsing 82 | 83 | 84 | 85 | 86 | CompileAsC 87 | NotUsing 88 | 89 | 90 | 91 | 92 | CompileAsC 93 | NotUsing 94 | 95 | 96 | 97 | 98 | CompileAsC 99 | 100 | 101 | NotUsing 102 | 103 | 104 | 105 | 106 | CompileAsC 107 | NotUsing 108 | 109 | 110 | 111 | 112 | CompileAsC 113 | NotUsing 114 | 115 | 116 | 117 | 118 | CompileAsC 119 | NotUsing 120 | 121 | 122 | 123 | 124 | CompileAsC 125 | 126 | 127 | NotUsing 128 | 129 | 130 | 131 | 132 | CompileAsC 133 | NotUsing 134 | 135 | 136 | 137 | 138 | CompileAsC 139 | NotUsing 140 | 141 | 142 | 143 | 144 | CompileAsC 145 | NotUsing 146 | 147 | 148 | 149 | 150 | CompileAsC 151 | 152 | 153 | 154 | NotUsing 155 | 156 | 157 | 158 | 159 | CompileAsC 160 | NotUsing 161 | 162 | 163 | 164 | 165 | CompileAsC 166 | NotUsing 167 | 168 | 169 | 170 | 171 | CompileAsC 172 | NotUsing 173 | 174 | 175 | 176 | 177 | CompileAsC 178 | 179 | 180 | NotUsing 181 | 182 | 183 | 184 | 185 | CompileAsC 186 | NotUsing 187 | 188 | 189 | 190 | 191 | CompileAsC 192 | NotUsing 193 | 194 | 195 | 196 | 197 | CompileAsC 198 | NotUsing 199 | 200 | 201 | 202 | 203 | CompileAsC 204 | 205 | 206 | NotUsing 207 | 208 | 209 | 210 | 211 | CompileAsC 212 | NotUsing 213 | 214 | 215 | 216 | 217 | CompileAsC 218 | NotUsing 219 | 220 | 221 | 222 | 223 | CompileAsC 224 | NotUsing 225 | 226 | 227 | 228 | 229 | CompileAsC 230 | 231 | 232 | NotUsing 233 | 234 | 235 | 236 | 237 | CompileAsC 238 | NotUsing 239 | 240 | 241 | 242 | 243 | CompileAsC 244 | NotUsing 245 | 246 | 247 | 248 | 249 | CompileAsC 250 | NotUsing 251 | 252 | 253 | 254 | 255 | CompileAsC 256 | 257 | 258 | NotUsing 259 | 260 | 261 | 262 | 263 | CompileAsC 264 | NotUsing 265 | 266 | 267 | 268 | 269 | CompileAsC 270 | NotUsing 271 | 272 | 273 | 274 | 275 | CompileAsC 276 | NotUsing 277 | 278 | 279 | 280 | 281 | CompileAsC 282 | 283 | 284 | NotUsing 285 | 286 | 287 | 288 | 289 | CompileAsC 290 | NotUsing 291 | 292 | 293 | 294 | 295 | CompileAsC 296 | NotUsing 297 | 298 | 299 | 300 | 301 | CompileAsC 302 | NotUsing 303 | 304 | 305 | 306 | 307 | CompileAsC 308 | 309 | 310 | NotUsing 311 | 312 | 313 | 314 | 315 | CompileAsC 316 | NotUsing 317 | 318 | 319 | 320 | 321 | CompileAsC 322 | NotUsing 323 | 324 | 325 | 326 | 327 | CompileAsC 328 | NotUsing 329 | 330 | 331 | 332 | 333 | CompileAsC 334 | 335 | 336 | NotUsing 337 | 338 | 339 | 340 | 341 | CompileAsC 342 | NotUsing 343 | 344 | 345 | 346 | 347 | CompileAsC 348 | NotUsing 349 | 350 | 351 | 352 | 353 | CompileAsC 354 | NotUsing 355 | 356 | 357 | 358 | 359 | CompileAsC 360 | 361 | 362 | NotUsing 363 | 364 | 365 | 366 | 367 | CompileAsC 368 | NotUsing 369 | 370 | 371 | 372 | 373 | CompileAsC 374 | NotUsing 375 | 376 | 377 | 378 | 379 | CompileAsC 380 | NotUsing 381 | 382 | 383 | 384 | 385 | CompileAsC 386 | 387 | 388 | NotUsing 389 | 390 | 391 | 392 | 393 | CompileAsC 394 | NotUsing 395 | 396 | 397 | 398 | 399 | CompileAsC 400 | NotUsing 401 | 402 | 403 | 404 | 405 | CompileAsC 406 | NotUsing 407 | 408 | 409 | 410 | 411 | CompileAsC 412 | 413 | 414 | NotUsing 415 | 416 | 417 | 418 | 419 | CompileAsC 420 | NotUsing 421 | 422 | 423 | 424 | 425 | CompileAsC 426 | NotUsing 427 | 428 | 429 | 430 | 431 | CompileAsC 432 | NotUsing 433 | 434 | 435 | 436 | 437 | CompileAsC 438 | 439 | 440 | NotUsing 441 | 442 | 443 | 444 | 445 | CompileAsC 446 | NotUsing 447 | 448 | 449 | 450 | 451 | CompileAsC 452 | NotUsing 453 | 454 | 455 | 456 | 457 | CompileAsC 458 | NotUsing 459 | 460 | 461 | 462 | 463 | CompileAsC 464 | 465 | 466 | NotUsing 467 | 468 | 469 | 470 | 471 | CompileAsC 472 | NotUsing 473 | 474 | 475 | 476 | 477 | CompileAsC 478 | NotUsing 479 | 480 | 481 | 482 | 483 | CompileAsC 484 | NotUsing 485 | 486 | 487 | 488 | 489 | CompileAsC 490 | 491 | 492 | NotUsing 493 | 494 | 495 | 496 | 497 | CompileAsC 498 | NotUsing 499 | 500 | 501 | 502 | 503 | CompileAsC 504 | NotUsing 505 | 506 | 507 | 508 | 509 | CompileAsC 510 | NotUsing 511 | 512 | 513 | 514 | 515 | CompileAsC 516 | 517 | 518 | CompileAsC 519 | CompileAsC 520 | NotUsing 521 | 522 | 523 | 524 | 525 | NotUsing 526 | 527 | 528 | 529 | 530 | NotUsing 531 | 532 | 533 | 534 | 535 | CompileAsC 536 | NotUsing 537 | 538 | 539 | 540 | 541 | CompileAsC 542 | 543 | 544 | NotUsing 545 | 546 | 547 | 548 | 549 | CompileAsC 550 | NotUsing 551 | 552 | 553 | 554 | 555 | CompileAsC 556 | NotUsing 557 | 558 | 559 | 560 | 561 | CompileAsC 562 | NotUsing 563 | 564 | 565 | 566 | 567 | CompileAsC 568 | 569 | 570 | NotUsing 571 | 572 | 573 | 574 | 575 | CompileAsC 576 | NotUsing 577 | 578 | 579 | 580 | 581 | CompileAsC 582 | NotUsing 583 | 584 | 585 | 586 | 587 | CompileAsC 588 | NotUsing 589 | 590 | 591 | 592 | 593 | CompileAsC 594 | 595 | 596 | NotUsing 597 | 598 | 599 | 600 | 601 | CompileAsC 602 | NotUsing 603 | 604 | 605 | 606 | 607 | CompileAsC 608 | NotUsing 609 | 610 | 611 | 612 | 613 | CompileAsC 614 | NotUsing 615 | 616 | 617 | 618 | 619 | CompileAsC 620 | 621 | 622 | NotUsing 623 | 624 | 625 | 626 | 627 | CompileAsC 628 | NotUsing 629 | 630 | 631 | 632 | 633 | CompileAsC 634 | NotUsing 635 | 636 | 637 | 638 | 639 | CompileAsC 640 | NotUsing 641 | 642 | 643 | 644 | 645 | CompileAsC 646 | 647 | 648 | 649 | NotUsing 650 | 651 | 652 | 653 | 654 | CompileAsC 655 | NotUsing 656 | 657 | 658 | 659 | 660 | CompileAsC 661 | NotUsing 662 | 663 | 664 | 665 | 666 | CompileAsC 667 | NotUsing 668 | 669 | 670 | 671 | 672 | CompileAsC 673 | 674 | 675 | NotUsing 676 | 677 | 678 | 679 | 680 | CompileAsC 681 | NotUsing 682 | 683 | 684 | 685 | 686 | CompileAsC 687 | NotUsing 688 | 689 | 690 | 691 | 692 | CompileAsC 693 | NotUsing 694 | 695 | 696 | 697 | 698 | CompileAsC 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 16.0 707 | {D9CBD2EE-9AE8-4FAA-B90F-55E27D0F52A2} 708 | Win32Proj 709 | SoftwareRasterizer 710 | 10.0 711 | 712 | 713 | 714 | Application 715 | true 716 | v142 717 | Unicode 718 | 719 | 720 | Application 721 | false 722 | v142 723 | true 724 | Unicode 725 | 726 | 727 | Application 728 | true 729 | v142 730 | Unicode 731 | 732 | 733 | Application 734 | false 735 | v142 736 | true 737 | Unicode 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | true 759 | 760 | 761 | true 762 | 763 | 764 | false 765 | 766 | 767 | false 768 | 769 | 770 | 771 | NotUsing 772 | Level3 773 | Disabled 774 | true 775 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 776 | true 777 | true 778 | 779 | 780 | CompileAsC 781 | 782 | 783 | Console 784 | true 785 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;RenderLibrary.lib;%(AdditionalDependencies) 786 | $(SolutionDir)$(Platform)\$(Configuration)\ 787 | 788 | 789 | 790 | 791 | NotUsing 792 | Level3 793 | Disabled 794 | true 795 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 796 | true 797 | true 798 | 799 | 800 | CompileAsC 801 | 802 | 803 | Console 804 | true 805 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;RenderLibrary.lib;%(AdditionalDependencies) 806 | $(SolutionDir)$(Platform)\$(Configuration)\ 807 | 808 | 809 | 810 | 811 | NotUsing 812 | Level3 813 | MaxSpeed 814 | true 815 | true 816 | true 817 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 818 | true 819 | true 820 | 821 | 822 | CompileAsC 823 | 824 | 825 | Console 826 | true 827 | true 828 | true 829 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;RenderLibrary.lib;%(AdditionalDependencies) 830 | $(SolutionDir)$(Platform)\$(Configuration)\ 831 | 832 | 833 | 834 | 835 | NotUsing 836 | Level3 837 | MaxSpeed 838 | true 839 | true 840 | true 841 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 842 | true 843 | true 844 | 845 | 846 | CompileAsC 847 | 848 | 849 | Console 850 | true 851 | true 852 | true 853 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;RenderLibrary.lib;%(AdditionalDependencies) 854 | $(SolutionDir)$(Platform)\$(Configuration)\ 855 | 856 | 857 | 858 | 859 | 860 | -------------------------------------------------------------------------------- /SoftwareRasterizer/SoftwareRasterizer.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | 14 | 15 | Source Files 16 | 17 | 18 | Source Files 19 | 20 | 21 | Source Files 22 | 23 | 24 | Source Files 25 | 26 | 27 | Source Files 28 | 29 | 30 | Source Files 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | Source Files 40 | 41 | 42 | Source Files 43 | 44 | 45 | Source Files 46 | 47 | 48 | Source Files 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | Source Files 70 | 71 | 72 | Source Files 73 | 74 | 75 | Source Files 76 | 77 | 78 | Source Files 79 | 80 | 81 | Source Files 82 | 83 | 84 | Source Files 85 | 86 | 87 | Source Files 88 | 89 | 90 | Source Files 91 | 92 | 93 | Source Files 94 | 95 | 96 | Source Files 97 | 98 | 99 | 100 | 101 | Header Files 102 | 103 | 104 | Header Files 105 | 106 | 107 | -------------------------------------------------------------------------------- /SoftwareRasterizer/ase.h: -------------------------------------------------------------------------------- 1 | #ifndef __ASE_H__ 2 | #define __ASE_H__ 3 | 4 | #include "../RenderLibrary/renderlib.h" 5 | 6 | #define WIN_WIDTH 640 7 | #define WIN_HEIGHT 480 8 | 9 | #define X 0 10 | #define Y 1 11 | #define Z 2 12 | 13 | typedef struct s_position 14 | { 15 | double x; 16 | double y; 17 | double z; 18 | } t_position; 19 | 20 | typedef struct s_vertex 21 | { 22 | t_position object_pos; 23 | t_position world_pos; 24 | } t_vertex; 25 | 26 | typedef struct s_horizontal_boundaries 27 | { 28 | int xmin; 29 | int xmax; 30 | } t_horizontal_boundaries; 31 | 32 | typedef struct s_boundaries 33 | { 34 | int ymin; 35 | int ymax; 36 | t_horizontal_boundaries rows[WIN_HEIGHT]; 37 | } t_boundaries; 38 | 39 | typedef struct s_face 40 | { 41 | int v1; 42 | int v2; 43 | int v3; 44 | t_vertex normal; 45 | } t_face; 46 | 47 | typedef struct s_model 48 | { 49 | int vertices_count; 50 | int faces_count; 51 | t_vertex* vertices; 52 | t_face* faces; 53 | } t_model; 54 | 55 | typedef struct s_matrix3x3 56 | { 57 | double components[3][3]; 58 | } t_matrix3x3; 59 | 60 | void ase_extract_faces(char* str, t_model* model); 61 | void ase_extract_vertex(char* ptr, t_vertex* vertices); 62 | char* ase_get_faces_count(char* ptr, int* faces_count); 63 | char* ase_get_vertices_count(char* ptr, int* vertices_count); 64 | char* ase_read_vertices(char* ptr, t_model* model); 65 | char* ase_read_faces(char* ptr, t_model* model); 66 | t_model* ase_read_file(char* filename); 67 | 68 | void ase_compute_normal_vector(t_model* model, int face_index); 69 | 70 | #endif // __ASE_H__ 71 | -------------------------------------------------------------------------------- /SoftwareRasterizer/ase_compute_normal_vector.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | 3 | void ase_compute_normal_vector(t_model* model, int face_index) 4 | { 5 | double a[3]; 6 | double b[3]; 7 | t_vertex* vertices; 8 | t_face* face; 9 | t_vertex* normal; 10 | 11 | vertices = model->vertices; 12 | face = &model->faces[face_index]; 13 | normal = &model->faces[face_index].normal; 14 | 15 | a[X] = vertices[face->v1].object_pos.x - vertices[face->v2].object_pos.x; 16 | a[Y] = vertices[face->v1].object_pos.y - vertices[face->v2].object_pos.y; 17 | a[Z] = vertices[face->v1].object_pos.z - vertices[face->v2].object_pos.z; 18 | 19 | b[X] = vertices[face->v1].object_pos.x - vertices[face->v3].object_pos.x; 20 | b[Y] = vertices[face->v1].object_pos.y - vertices[face->v3].object_pos.y; 21 | b[Z] = vertices[face->v1].object_pos.z - vertices[face->v3].object_pos.z; 22 | 23 | normal->object_pos.x = a[Y] * b[Z] - a[Z] * b[Y]; 24 | normal->object_pos.y = a[Z] * b[X] - a[X] * b[Z]; 25 | normal->object_pos.z = a[X] * b[Y] - a[Y] * b[X]; 26 | 27 | normal->world_pos.x = normal->object_pos.x; 28 | normal->world_pos.y = normal->object_pos.y; 29 | normal->world_pos.z = normal->object_pos.z; 30 | } 31 | -------------------------------------------------------------------------------- /SoftwareRasterizer/ase_extract_faces.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | 3 | void ase_extract_faces(char* str, t_model* model) 4 | { 5 | int index; 6 | 7 | index = atoi(str); 8 | str = strchr(str, ':') + 1; 9 | 10 | str = strchr(str, ':') + 1; 11 | model->faces[index].v1 = atoi(str); 12 | 13 | str = strchr(str, ':') + 1; 14 | model->faces[index].v2 = atoi(str); 15 | 16 | str = strchr(str, ':') + 1; 17 | model->faces[index].v3 = atoi(str); 18 | 19 | ase_compute_normal_vector(model, index); 20 | } 21 | -------------------------------------------------------------------------------- /SoftwareRasterizer/ase_extract_vertex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ase.h" 3 | #include "rasterizer.h" 4 | 5 | void ase_extract_vertex(char* ptr, t_vertex* vertices) 6 | { 7 | int num; 8 | double coord; 9 | 10 | ptr = skip_spaces(ptr); 11 | num = atoi(ptr); 12 | ptr = skip_non_spaces(ptr); 13 | 14 | ptr = skip_spaces(ptr); 15 | coord = atof(ptr); 16 | ptr = skip_non_spaces(ptr); 17 | vertices[num].object_pos.x = coord; 18 | vertices[num].world_pos.x = coord; 19 | 20 | ptr = skip_spaces(ptr); 21 | coord = atof(ptr); 22 | ptr = skip_non_spaces(ptr); 23 | vertices[num].object_pos.z = coord; 24 | vertices[num].world_pos.z = coord; 25 | 26 | ptr = skip_spaces(ptr); 27 | coord = atof(ptr); 28 | ptr = skip_non_spaces(ptr); 29 | vertices[num].object_pos.y = coord; 30 | vertices[num].world_pos.y = coord; 31 | } 32 | -------------------------------------------------------------------------------- /SoftwareRasterizer/ase_get_faces_count.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | char* ase_get_faces_count(char* ptr, int* faces_count) 6 | { 7 | ptr = strstr(ptr, "*MESH_NUMFACES "); 8 | 9 | if (ptr == NULL) 10 | { 11 | printf("Failed to find MESH_NUMFACES marker.\n"); 12 | return NULL; 13 | } 14 | 15 | ptr += 15; 16 | 17 | *faces_count = atoi(ptr); 18 | 19 | return ptr; 20 | } 21 | -------------------------------------------------------------------------------- /SoftwareRasterizer/ase_get_vertices_count.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | char* ase_get_vertices_count(char* ptr, int* vertices_count) 6 | { 7 | ptr = strstr(ptr, "*MESH_NUMVERTEX "); 8 | 9 | if (ptr == NULL) 10 | { 11 | printf("Failed to find MESH_NUMVERTEX marker.\n"); 12 | return NULL; 13 | } 14 | 15 | ptr += 16; 16 | 17 | *vertices_count = atoi(ptr); 18 | 19 | return ptr; 20 | } 21 | -------------------------------------------------------------------------------- /SoftwareRasterizer/ase_read_faces.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | #include "rasterizer.h" 3 | 4 | char* ase_read_faces(char* ptr, t_model* model) 5 | { 6 | char* temp_ptr; 7 | 8 | while (1) 9 | { 10 | temp_ptr = strstr(ptr, "*MESH_FACE "); 11 | 12 | if (temp_ptr == NULL) 13 | return ptr; 14 | 15 | ptr = temp_ptr + 11; 16 | 17 | ase_extract_faces(ptr, model); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SoftwareRasterizer/ase_read_file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "rasterizer.h" 4 | #include "ase.h" 5 | 6 | t_model* ase_read_file(char* filename) 7 | { 8 | int vertices_count; 9 | int faces_count; 10 | char* original_file_buffer; 11 | char* file_ptr; 12 | 13 | t_vertex* vertices; 14 | t_face* faces; 15 | t_model* model; 16 | 17 | original_file_buffer = read_file(filename); 18 | if (original_file_buffer == NULL) 19 | return NULL; 20 | 21 | file_ptr = original_file_buffer; 22 | 23 | file_ptr = ase_get_vertices_count(file_ptr, &vertices_count); 24 | if (file_ptr == NULL) 25 | return NULL; 26 | 27 | if (vertices_count == 0) 28 | { 29 | printf("File '%s' contains no vertices.\n", filename); 30 | return NULL; 31 | } 32 | 33 | file_ptr = ase_get_faces_count(file_ptr, &faces_count); 34 | if (file_ptr == NULL) 35 | return NULL; 36 | 37 | if (faces_count == 0) 38 | { 39 | printf("File '%s' contains no faces.\n", filename); 40 | return NULL; 41 | } 42 | 43 | vertices = malloc(vertices_count * sizeof(t_vertex)); 44 | if (vertices == NULL) 45 | { 46 | malloc_error(vertices_count); 47 | return NULL; 48 | } 49 | 50 | faces = malloc(faces_count * sizeof(t_face)); 51 | if (faces == NULL) 52 | { 53 | malloc_error(faces_count); 54 | return NULL; 55 | } 56 | 57 | model = malloc(sizeof(t_model)); 58 | if (model == NULL) 59 | { 60 | malloc_error(sizeof(t_model)); 61 | return NULL; 62 | } 63 | 64 | model->vertices_count = vertices_count; 65 | model->faces_count = faces_count; 66 | 67 | model->vertices = vertices; 68 | model->faces = faces; 69 | 70 | file_ptr = ase_read_vertices(file_ptr, model); 71 | file_ptr = ase_read_faces(file_ptr, model); 72 | 73 | free(original_file_buffer); 74 | 75 | return model; 76 | } 77 | -------------------------------------------------------------------------------- /SoftwareRasterizer/ase_read_vertices.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | #include "rasterizer.h" 3 | 4 | char* ase_read_vertices(char* ptr, t_model* model) 5 | { 6 | char* temp_ptr; 7 | 8 | while (1) 9 | { 10 | temp_ptr = strstr(ptr, "*MESH_VERTEX "); 11 | 12 | if (temp_ptr == NULL) 13 | return ptr; 14 | 15 | ptr = temp_ptr + 13; 16 | 17 | ase_extract_vertex(ptr, model->vertices); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SoftwareRasterizer/compute_lighting_coef.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ase.h" 3 | 4 | double compute_lighting_coef(t_position* light, t_model* model, int face_index) 5 | { 6 | double k; 7 | double face_norm; 8 | double light_norm; 9 | double fn[3]; 10 | double ln[3]; 11 | double face_pos[3]; 12 | t_vertex* vertices; 13 | t_face* face; 14 | 15 | vertices = model->vertices; 16 | face = &model->faces[face_index]; 17 | 18 | face_pos[X] = (vertices[face->v1].world_pos.x + vertices[face->v2].world_pos.x + vertices[face->v3].world_pos.x) / 3.0; 19 | face_pos[Y] = (vertices[face->v1].world_pos.y + vertices[face->v2].world_pos.y + vertices[face->v3].world_pos.y) / 3.0; 20 | face_pos[Z] = (vertices[face->v1].world_pos.z + vertices[face->v2].world_pos.z + vertices[face->v3].world_pos.z) / 3.0; 21 | 22 | ln[X] = light->x - face_pos[X]; 23 | ln[Y] = light->y - face_pos[Y]; 24 | ln[Z] = light->z - face_pos[Z]; 25 | 26 | fn[X] = face->normal.world_pos.x; 27 | fn[Y] = face->normal.world_pos.y; 28 | fn[Z] = face->normal.world_pos.z; 29 | 30 | face_norm = sqrt(fn[X] * fn[X] + fn[Y] * fn[Y] + fn[Z] * fn[Z]); 31 | light_norm = sqrt(ln[X] * ln[X] + ln[Y] * ln[Y] + ln[Z] * ln[Z]); 32 | 33 | k = (fn[X] * ln[X] + fn[Y] * ln[Y] + fn[Z] * ln[Z]) / (face_norm * light_norm); 34 | 35 | if (k < 0.0) 36 | k = -k; 37 | 38 | if (k > 1.0 + 1e5) 39 | k = 1.0; 40 | 41 | return k * 0.75 + 0.25; 42 | } 43 | -------------------------------------------------------------------------------- /SoftwareRasterizer/create_rotation_matrix.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ase.h" 3 | 4 | void create_rotation_matrix(double x_angle, double y_angle, double z_angle, t_matrix3x3* matrix) 5 | { 6 | double cosxa; 7 | double cosya; 8 | double cosza; 9 | 10 | double sinxa; 11 | double sinya; 12 | double sinza; 13 | 14 | cosxa = cos(x_angle); 15 | cosya = cos(y_angle); 16 | cosza = cos(z_angle); 17 | 18 | sinxa = sin(x_angle); 19 | sinya = sin(y_angle); 20 | sinza = sin(z_angle); 21 | 22 | matrix->components[0][0] = cosya * cosza; 23 | matrix->components[1][0] = sinya * cosza; 24 | matrix->components[2][0] = -sinza; 25 | 26 | matrix->components[0][1] = cosya * sinza * sinxa - sinya * cosxa; 27 | matrix->components[1][1] = sinya * sinza * sinxa + cosxa * cosya; 28 | matrix->components[2][1] = sinxa * cosza; 29 | 30 | matrix->components[0][2] = cosya * sinza * cosxa + sinya * sinxa; 31 | matrix->components[1][2] = sinya * sinza * cosxa - cosya * sinxa; 32 | matrix->components[2][2] = cosxa * cosza; 33 | } 34 | -------------------------------------------------------------------------------- /SoftwareRasterizer/draw_line.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | 3 | void draw_dot(int x, int y, t_boundaries* boundaries) 4 | { 5 | if (boundaries->rows[y].xmin > x) 6 | boundaries->rows[y].xmin = x; 7 | if (boundaries->rows[y].xmax < x) 8 | boundaries->rows[y].xmax = x; 9 | } 10 | 11 | void draw_horizontal_line(int x1, int x2, int y, t_boundaries* boundaries) 12 | { 13 | if (boundaries->rows[y].xmin > x1) 14 | boundaries->rows[y].xmin = x1; 15 | 16 | if (boundaries->rows[y].xmax < x2) 17 | boundaries->rows[y].xmax = x2; 18 | } 19 | 20 | void draw_vertical_line(int x, int y1, int y2, t_boundaries* boundaries) 21 | { 22 | int y; 23 | 24 | y = y1; 25 | 26 | while (y <= y2) 27 | { 28 | if (boundaries->rows[y].xmin > x) 29 | boundaries->rows[y].xmin = x; 30 | if (boundaries->rows[y].xmax < x) 31 | boundaries->rows[y].xmax = x; 32 | 33 | y++; 34 | } 35 | } 36 | 37 | void draw_larger_dx(int x1, int y1, int x2, int y2, t_boundaries* boundaries) 38 | { 39 | int x; 40 | int y; 41 | 42 | x = x1; 43 | 44 | while (x <= x2) 45 | { 46 | y = y1 + ((y2 - y1) * (x - x1)) / (x2 - x1); 47 | 48 | if (boundaries->rows[y].xmin > x) 49 | boundaries->rows[y].xmin = x; 50 | 51 | if (boundaries->rows[y].xmax < x) 52 | boundaries->rows[y].xmax = x; 53 | 54 | x++; 55 | } 56 | } 57 | 58 | void draw_larger_dy(int x1, int y1, int x2, int y2, t_boundaries* boundaries) 59 | { 60 | int x; 61 | int y; 62 | 63 | y = y1; 64 | 65 | while (y <= y2) 66 | { 67 | x = x1 + ((x2 - x1) * (y - y1)) / (y2 - y1); 68 | 69 | if (boundaries->rows[y].xmin > x) 70 | boundaries->rows[y].xmin = x; 71 | if (boundaries->rows[y].xmax < x) 72 | boundaries->rows[y].xmax = x; 73 | 74 | y++; 75 | } 76 | } 77 | 78 | void draw_line(int x1, int y1, int x2, int y2, COLOR color, t_boundaries* boundaries) 79 | { 80 | int dx; 81 | int dy; 82 | 83 | dx = abs(x2 - x1); 84 | dy = abs(y2 - y1); 85 | 86 | if (dx == 0 && dy == 0) 87 | draw_dot(x1, y1, boundaries); 88 | else if (dx == 0) 89 | { 90 | if (y1 < y2) 91 | draw_vertical_line(x1, y1, y2, boundaries); 92 | else 93 | draw_vertical_line(x1, y2, y1, boundaries); 94 | } 95 | else if (dy == 0) 96 | { 97 | if (x1 > x2) 98 | draw_horizontal_line(x1, x2, y1, boundaries); 99 | else 100 | draw_horizontal_line(x2, x1, y1, boundaries); 101 | } 102 | else if (dx >= dy) 103 | { 104 | if (x1 <= x2) 105 | draw_larger_dx(x1, y1, x2, y2, boundaries); 106 | else 107 | draw_larger_dx(x2, y2, x1, y1, boundaries); 108 | } 109 | else // if (dy > dx) 110 | { 111 | if (y1 <= y2) 112 | draw_larger_dy(x1, y1, x2, y2, boundaries); 113 | else 114 | draw_larger_dy(x2, y2, x1, y1, boundaries); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /SoftwareRasterizer/draw_loop.c: -------------------------------------------------------------------------------- 1 | #define _USE_MATH_DEFINES 2 | #include 3 | #include "ase.h" 4 | #include "rasterizer.h" 5 | #include "../RenderLibrary/renderlib.h" 6 | 7 | #define M_PI2 (M_PI * 2.0) 8 | 9 | void draw_loop(t_context* params, t_model* model) 10 | { 11 | double object_angle; 12 | double light_angle; 13 | t_matrix3x3 rotation; 14 | 15 | object_angle = 0.0; 16 | light_angle = 0.0; 17 | 18 | while (1) 19 | { 20 | renderlib_sync_start(); 21 | 22 | renderlib_clear(); 23 | 24 | if (object_angle >= M_PI2) 25 | object_angle -= M_PI2; 26 | 27 | object_angle += 0.04; 28 | 29 | if (light_angle >= M_PI2) 30 | light_angle -= M_PI2; 31 | 32 | light_angle -= 0.015; 33 | 34 | create_rotation_matrix(0.5, 0.0, object_angle, &rotation); 35 | 36 | transform_object(&rotation, model); 37 | transform_normals(&rotation, model); 38 | 39 | create_rotation_matrix(0.0, light_angle, 0.0, &rotation); 40 | 41 | transform_vertex(&rotation , ¶ms->light); 42 | 43 | quick_sort_faces(0, model->faces_count - 1, model); 44 | draw_model(0x80A0FF, params, model); 45 | 46 | renderlib_update(); 47 | 48 | renderlib_sync_end(60, 1); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /SoftwareRasterizer/draw_model.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | #include "rasterizer.h" 3 | 4 | void draw_model(COLOR color, t_context* context, t_model* model) 5 | { 6 | int i; 7 | 8 | for (i = 0; i < model->faces_count; i++) 9 | draw_triangle(color, context, model, i); 10 | } 11 | -------------------------------------------------------------------------------- /SoftwareRasterizer/draw_triangle.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ase.h" 3 | #include "rasterizer.h" 4 | #include "../RenderLibrary/renderlib.h" 5 | 6 | void draw_triangle(COLOR color, t_context* context, t_model* model, int face_index) 7 | { 8 | double x[3]; 9 | double y[3]; 10 | double z[3]; 11 | int miny; 12 | int maxy; 13 | int projected_x[3]; 14 | int projected_y[3]; 15 | unsigned char r; 16 | unsigned char g; 17 | unsigned char b; 18 | double k; 19 | t_vertex* vertices; 20 | t_face* face; 21 | 22 | vertices = model->vertices; 23 | face = &model->faces[face_index]; 24 | 25 | init_boundaries(&context->boundaries); 26 | 27 | x[0] = vertices[face->v1].world_pos.x; 28 | x[1] = vertices[face->v2].world_pos.x; 29 | x[2] = vertices[face->v3].world_pos.x; 30 | 31 | y[0] = vertices[face->v1].world_pos.y; 32 | y[1] = vertices[face->v2].world_pos.y; 33 | y[2] = vertices[face->v3].world_pos.y; 34 | 35 | z[0] = vertices[face->v1].world_pos.z; 36 | z[1] = vertices[face->v2].world_pos.z; 37 | z[2] = vertices[face->v3].world_pos.z; 38 | 39 | if (y[0] < -context->camera.z || y[1] < -context->camera.z || y[2] < -context->camera.z) 40 | return; 41 | 42 | projected_x[0] = project_x(x[0], z[0], context); 43 | projected_x[1] = project_x(x[1], z[1], context); 44 | projected_x[2] = project_x(x[2], z[2], context); 45 | 46 | projected_y[0] = project_y(y[0], z[0], context); 47 | projected_y[1] = project_y(y[1], z[1], context); 48 | projected_y[2] = project_y(y[2], z[2], context); 49 | 50 | k = compute_lighting_coef(&context->light.world_pos, model, face_index); 51 | 52 | r = (unsigned char)(((color >> 16) & 0xFF) * k); 53 | g = (unsigned char)(((color >> 8) & 0xFF) * k); 54 | b = (unsigned char)((color & 0xFF) * k); 55 | 56 | color = (COLOR)(r << 16) + (g << 8) + b; 57 | 58 | miny = projected_y[0]; 59 | if (miny > projected_y[1]) 60 | miny = projected_y[1]; 61 | if (miny > projected_y[2]) 62 | miny = projected_y[2]; 63 | if (miny < 0) 64 | miny = 0; 65 | 66 | maxy = projected_y[0]; 67 | if (maxy < projected_y[1]) 68 | maxy = projected_y[1]; 69 | if (maxy < projected_y[2]) 70 | maxy = projected_y[2]; 71 | if (maxy > WIN_HEIGHT - 1) 72 | maxy = WIN_HEIGHT - 1; 73 | 74 | context->boundaries.ymin = miny; 75 | context->boundaries.ymax = maxy; 76 | 77 | draw_line(projected_x[0], projected_y[0], projected_x[1], projected_y[1], color, &context->boundaries); 78 | draw_line(projected_x[1], projected_y[1], projected_x[2], projected_y[2], color, &context->boundaries); 79 | draw_line(projected_x[2], projected_y[2], projected_x[0], projected_y[0], color, &context->boundaries); 80 | 81 | fill_triangle(color, context); 82 | } 83 | -------------------------------------------------------------------------------- /SoftwareRasterizer/fill_triangle.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | #include "rasterizer.h" 3 | #include "../RenderLibrary/renderlib.h" 4 | 5 | void fill_triangle(int color, t_context* context) 6 | { 7 | int x; 8 | int y; 9 | int xmin; 10 | int xmax; 11 | int ymin; 12 | int ymax; 13 | t_horizontal_boundaries* rows; 14 | 15 | ymin = context->boundaries.ymin; 16 | ymax = context->boundaries.ymax; 17 | rows = context->boundaries.rows; 18 | 19 | if (ymin < 0 && ymax < 0) 20 | return; 21 | 22 | for (y = ymin; y <= ymax; y++) 23 | { 24 | xmin = rows[y].xmin; 25 | xmax = rows[y].xmax; 26 | 27 | for (x = xmin; x <= xmax; x++) 28 | renderlib_set_pixel(x, y, color); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SoftwareRasterizer/get_average_depth.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | 3 | double get_average_depth(int face_index, t_model* model) 4 | { 5 | double average_z; 6 | 7 | average_z = 8 | model->vertices[model->faces[face_index].v1].world_pos.z + 9 | model->vertices[model->faces[face_index].v2].world_pos.z + 10 | model->vertices[model->faces[face_index].v3].world_pos.z; 11 | 12 | return average_z; 13 | } 14 | -------------------------------------------------------------------------------- /SoftwareRasterizer/init_boundaries.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | 3 | void init_boundaries(t_boundaries* boundaries) 4 | { 5 | int i; 6 | 7 | boundaries->ymin = -1; 8 | boundaries->ymax = -1; 9 | 10 | for (i = 0; i < WIN_HEIGHT; i++) 11 | { 12 | boundaries->rows[i].xmin = WIN_WIDTH + 1; 13 | boundaries->rows[i].xmax = -1; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SoftwareRasterizer/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ase.h" 3 | #include "rasterizer.h" 4 | #include "../RenderLibrary/renderlib.h" 5 | 6 | void main(int ac, char** av) 7 | { 8 | char* filename; 9 | COLOR* buffer; 10 | t_context context; 11 | t_model* model; 12 | 13 | context.light.object_pos.x = 1; 14 | context.light.object_pos.y = -500; 15 | context.light.object_pos.z = 0; 16 | 17 | context.light.world_pos.x = context.light.object_pos.x; 18 | context.light.world_pos.y = context.light.object_pos.y; 19 | context.light.world_pos.z = context.light.object_pos.z; 20 | 21 | context.camera.x = WIN_WIDTH / 2; 22 | context.camera.y = WIN_HEIGHT / 2; 23 | context.camera.z = 256; 24 | 25 | context.projection_coef = 250.0; 26 | 27 | renderlib_create_window(L"Software Rasterizer", WIN_WIDTH, WIN_HEIGHT); 28 | buffer = renderlib_get_color_buffer(); 29 | 30 | if (ac == 1) 31 | filename = "..\\Samples\\dragon.ase"; 32 | else 33 | filename = av[1]; 34 | 35 | model = ase_read_file(filename); 36 | if (model == NULL) 37 | return; 38 | 39 | draw_loop(&context, model); 40 | } 41 | -------------------------------------------------------------------------------- /SoftwareRasterizer/malloc_error.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void malloc_error(long byte_count) 5 | { 6 | printf("Failed to allocate %ld bytes, out of memory, u mad?\n", byte_count); 7 | exit(1); 8 | } 9 | -------------------------------------------------------------------------------- /SoftwareRasterizer/projection.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ase.h" 4 | #include "rasterizer.h" 5 | 6 | int project_x(double x, double z, t_context* context) 7 | { 8 | double coef_x; 9 | double depth; 10 | 11 | coef_x = x * context->projection_coef; 12 | depth = z + context->camera.z; 13 | 14 | if (fabs(depth) < 1e-3) 15 | return coef_x >= 0.0 ? WIN_WIDTH + 1 : -1; 16 | 17 | return (int)round((coef_x / depth) + context->camera.x); 18 | } 19 | 20 | int project_y(double y, double z, t_context* context) 21 | { 22 | double coef_y; 23 | double depth; 24 | 25 | coef_y = y * context->projection_coef; 26 | depth = z + context->camera.z; 27 | 28 | if (fabs(depth) < 1e-3) 29 | return coef_y <= 0.0 ? WIN_HEIGHT + 1 : -1; 30 | 31 | return (int)round(WIN_HEIGHT - ((coef_y / depth) + context->camera.y)); 32 | } 33 | -------------------------------------------------------------------------------- /SoftwareRasterizer/quick_sort_faces.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | #include "rasterizer.h" 3 | 4 | void quick_sort_faces(int start, int end, t_model* model) 5 | { 6 | int i; 7 | int j; 8 | double mid; 9 | 10 | i = start; 11 | j = end; 12 | mid = get_average_depth((start + end) / 2, model); 13 | 14 | while (i <= j) 15 | { 16 | while (get_average_depth(i, model) > mid) 17 | i++; 18 | 19 | while (get_average_depth(j, model) < mid) 20 | j--; 21 | 22 | if (i <= j) 23 | { 24 | swap_structs(i, j, model->faces); 25 | i++; 26 | j--; 27 | } 28 | } 29 | 30 | if (i < end) 31 | quick_sort_faces(i, end, model); 32 | 33 | if (start < j) 34 | quick_sort_faces(start, j, model); 35 | } 36 | -------------------------------------------------------------------------------- /SoftwareRasterizer/rasterizer.h: -------------------------------------------------------------------------------- 1 | #ifndef __RASTERIZER_H__ 2 | #define __RASTERIZER_H__ 3 | 4 | #include "ase.h" 5 | #include "../RenderLibrary/renderlib.h" 6 | 7 | typedef struct s_context 8 | { 9 | double projection_coef; 10 | t_vertex light; 11 | t_position camera; 12 | t_boundaries boundaries; 13 | } t_context; 14 | 15 | void draw_triangle(COLOR color, t_context* context, t_model* model, int face_index); 16 | void draw_model(COLOR color, t_context* context, t_model* model); 17 | void swap_structs(int i, int j, t_face* faces); 18 | void draw_loop(t_context* param, t_model* model); 19 | void translate(int x, int y, int z, t_model* model); 20 | double compute_lighting_coef(t_position* light, t_model* model, int face_index); 21 | int project_x(double x, double z, t_context* param); 22 | int project_y(double y, double z, t_context* param); 23 | void fill_triangle(int color, t_context* context); 24 | void init_boundaries(t_boundaries* boundaries); 25 | void draw_line(int x1, int y1, int x2, int y2, COLOR color, t_boundaries* boundaries); 26 | void create_rotation_matrix(double x_angle, double y_angle, double z_angle, t_matrix3x3* matrix); 27 | void transform_object(t_matrix3x3* matrix, t_model* model); 28 | void transform_normals(t_matrix3x3* matrix, t_model* model); 29 | void transform_vertex(t_matrix3x3* matrix, t_vertex* vertex); 30 | double get_average_depth(int numface, t_model* model); 31 | void quick_sort_faces(int start, int end, t_model* model); 32 | 33 | char* read_file(char* filename); 34 | char* skip_spaces(char* str); 35 | char* skip_non_spaces(char* str); 36 | void malloc_error(long byte_count); 37 | 38 | #endif // __RASTERIZER_H__ 39 | -------------------------------------------------------------------------------- /SoftwareRasterizer/read_file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "rasterizer.h" 4 | 5 | char* read_file(char* filename) 6 | { 7 | FILE* fd; 8 | long file_size; 9 | char* file_buffer; 10 | 11 | if (fopen_s(&fd, filename, "rb") != 0 || fd == NULL) 12 | { 13 | printf("Failed to open file '%s'.\n", filename); 14 | return NULL; 15 | } 16 | 17 | if (fseek(fd, 0, SEEK_END) != 0) 18 | { 19 | printf("Failed to seek end of file.\n"); 20 | return NULL; 21 | } 22 | 23 | file_size = ftell(fd); 24 | 25 | if (file_size < 0L) 26 | { 27 | printf("Failed to query file size.\n"); 28 | return NULL; 29 | } 30 | 31 | if (fseek(fd, 0, SEEK_SET) != 0) 32 | { 33 | printf("Failed to seek end of file.\n"); 34 | return NULL; 35 | } 36 | 37 | file_buffer = (char*)malloc((size_t)file_size + 1); 38 | 39 | if (file_buffer == NULL) 40 | { 41 | malloc_error(file_size); 42 | return NULL; 43 | } 44 | 45 | if (fread(file_buffer, sizeof(char), file_size, fd) != file_size) 46 | { 47 | free(file_buffer); 48 | printf("Failed to read %ld bytes from file '%s'.\n", file_size, filename); 49 | return NULL; 50 | } 51 | 52 | file_buffer[file_size] = 0; 53 | 54 | return file_buffer; 55 | } 56 | -------------------------------------------------------------------------------- /SoftwareRasterizer/skip_spaces.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | char* skip_spaces(char* str) 4 | { 5 | int i; 6 | 7 | if (str == NULL) 8 | return NULL; 9 | 10 | for (i = 0; str[i] == ' ' || str[i] == '\t'; i++) 11 | ; 12 | 13 | if (str[i] == 0) 14 | return NULL; 15 | 16 | return str + i; 17 | } 18 | 19 | char* skip_non_spaces(char* str) 20 | { 21 | int i; 22 | 23 | if (str == NULL) 24 | return NULL; 25 | 26 | for (i = 0; str[i] != ' ' && str[i] != '\t' && str[i] != 0; i++) 27 | ; 28 | 29 | if (str[i] == 0) 30 | return NULL; 31 | 32 | return str + i; 33 | } 34 | -------------------------------------------------------------------------------- /SoftwareRasterizer/swap_structs.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | 3 | void swap_structs(int i, int j, t_face* faces) 4 | { 5 | t_face temp; 6 | 7 | temp = faces[i]; 8 | faces[i] = faces[j]; 9 | faces[j] = temp; 10 | } 11 | -------------------------------------------------------------------------------- /SoftwareRasterizer/transform_normals.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | #include "rasterizer.h" 3 | 4 | void transform_normals(t_matrix3x3* matrix, t_model* model) 5 | { 6 | int i; 7 | 8 | for (i = 0; i < model->faces_count; i++) 9 | transform_vertex(matrix, &model->faces[i].normal); 10 | } 11 | -------------------------------------------------------------------------------- /SoftwareRasterizer/transform_object.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | #include "rasterizer.h" 3 | 4 | void transform_object(t_matrix3x3* matrix, t_model* model) 5 | { 6 | int i; 7 | 8 | for (i = 0; i < model->vertices_count; i++) 9 | transform_vertex(matrix, &model->vertices[i]); 10 | } 11 | -------------------------------------------------------------------------------- /SoftwareRasterizer/transform_vertex.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | 3 | void transform_vertex(t_matrix3x3* matrix, t_vertex* vertex) 4 | { 5 | vertex->world_pos.x = 6 | matrix->components[0][0] * vertex->object_pos.x + 7 | matrix->components[1][0] * vertex->object_pos.y + 8 | matrix->components[2][0] * vertex->object_pos.z; 9 | 10 | vertex->world_pos.y = 11 | matrix->components[0][1] * vertex->object_pos.x + 12 | matrix->components[1][1] * vertex->object_pos.y + 13 | matrix->components[2][1] * vertex->object_pos.z; 14 | 15 | vertex->world_pos.z = 16 | matrix->components[0][2] * vertex->object_pos.x + 17 | matrix->components[1][2] * vertex->object_pos.y + 18 | matrix->components[2][2] * vertex->object_pos.z; 19 | } 20 | -------------------------------------------------------------------------------- /SoftwareRasterizer/translate.c: -------------------------------------------------------------------------------- 1 | #include "ase.h" 2 | 3 | void translate(int x, int y, int z, t_model* model) 4 | { 5 | int i; 6 | 7 | for (i = 0; i < model->vertices_count; i++) 8 | { 9 | model->vertices[i].world_pos.x += x; 10 | model->vertices[i].world_pos.y += z; 11 | model->vertices[i].world_pos.z += y; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Testing/Testing.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | NotUsing 24 | 25 | 26 | 27 | 28 | CompileAsC 29 | NotUsing 30 | 31 | 32 | 33 | 34 | CompileAsC 35 | NotUsing 36 | 37 | 38 | 39 | 40 | CompileAsC 41 | NotUsing 42 | 43 | 44 | 45 | 46 | CompileAsC 47 | 48 | 49 | 50 | 16.0 51 | {DBF32EBE-DF15-49BB-8DD5-1368D689DC51} 52 | Win32Proj 53 | Testing 54 | 10.0 55 | 56 | 57 | 58 | Application 59 | true 60 | v142 61 | Unicode 62 | 63 | 64 | Application 65 | false 66 | v142 67 | true 68 | Unicode 69 | 70 | 71 | Application 72 | true 73 | v142 74 | Unicode 75 | 76 | 77 | Application 78 | false 79 | v142 80 | true 81 | Unicode 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | false 103 | 104 | 105 | true 106 | 107 | 108 | true 109 | 110 | 111 | false 112 | 113 | 114 | 115 | NotUsing 116 | Level3 117 | MaxSpeed 118 | true 119 | true 120 | true 121 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | true 124 | 125 | 126 | CompileAsC 127 | 128 | 129 | Console 130 | true 131 | true 132 | true 133 | $(SolutionDir)$(Platform)\$(Configuration)\ 134 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;RenderLibrary.lib;%(AdditionalDependencies) 135 | 136 | 137 | 138 | 139 | NotUsing 140 | Level3 141 | Disabled 142 | true 143 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 144 | true 145 | true 146 | 147 | 148 | CompileAsC 149 | 150 | 151 | Console 152 | true 153 | $(SolutionDir)$(Platform)\$(Configuration)\ 154 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;RenderLibrary.lib;%(AdditionalDependencies) 155 | 156 | 157 | 158 | 159 | NotUsing 160 | Level3 161 | Disabled 162 | true 163 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 164 | true 165 | true 166 | 167 | 168 | CompileAsC 169 | 170 | 171 | Console 172 | true 173 | $(SolutionDir)$(Platform)\$(Configuration)\ 174 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;RenderLibrary.lib;%(AdditionalDependencies) 175 | 176 | 177 | 178 | 179 | NotUsing 180 | Level3 181 | MaxSpeed 182 | true 183 | true 184 | true 185 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 186 | true 187 | true 188 | 189 | 190 | CompileAsC 191 | 192 | 193 | Console 194 | true 195 | true 196 | true 197 | $(SolutionDir)$(Platform)\$(Configuration)\ 198 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;RenderLibrary.lib;%(AdditionalDependencies) 199 | 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /Testing/Testing.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | 14 | 15 | Source Files 16 | 17 | 18 | -------------------------------------------------------------------------------- /Testing/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #define _USE_MATH_DEFINES 3 | #include 4 | #include "../RenderLibrary/renderlib.h" 5 | 6 | #define WIN_WIDTH 640 7 | #define WIN_HEIGHT 480 8 | 9 | void main() 10 | { 11 | int x; 12 | int y; 13 | double angle; 14 | COLOR* buffer; 15 | 16 | const double radius = 150.0; 17 | 18 | angle = 0.0; 19 | 20 | renderlib_create_window(L"Software Rasterizer", WIN_WIDTH, WIN_HEIGHT); 21 | buffer = renderlib_get_color_buffer(); 22 | 23 | while (1) 24 | { 25 | renderlib_sync_start(); 26 | renderlib_clear(); 27 | 28 | x = (int)round(radius * cos(angle)); 29 | y = (int)round(radius * sin(angle)); 30 | 31 | renderlib_draw_line( 32 | (WIN_WIDTH / 2) + x, 33 | (WIN_HEIGHT / 2) + y, 34 | (WIN_WIDTH / 2), 35 | (WIN_HEIGHT / 2), 36 | 0xFF0000 37 | ); 38 | 39 | renderlib_draw_line( 40 | (WIN_WIDTH / 2) - x, 41 | (WIN_HEIGHT / 2) - y, 42 | (WIN_WIDTH / 2), 43 | (WIN_HEIGHT / 2), 44 | 0xFF00 45 | ); 46 | 47 | angle += M_PI / 30.0; 48 | 49 | renderlib_update(); 50 | renderlib_sync_end(60, 1); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /dragon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanukiSharp/SoftwareRasterizer/2bd39d07092f5df2db82ae6306f4a767393b6419/dragon.gif -------------------------------------------------------------------------------- /land1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanukiSharp/SoftwareRasterizer/2bd39d07092f5df2db82ae6306f4a767393b6419/land1.gif -------------------------------------------------------------------------------- /land2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanukiSharp/SoftwareRasterizer/2bd39d07092f5df2db82ae6306f4a767393b6419/land2.gif --------------------------------------------------------------------------------