commit 9fd0111

shrub  ·  2026-07-30 13:13:44 +0000 UTC
parent da175b4
add small3dlib renderer
9 files changed,  +4022, -423
+6, -2
 1@@ -7,8 +7,13 @@
 2 
 3 typedef SDL_Window MGWindow;
 4 
 5-typedef struct { /* video backend !only OpenGL for now */
 6+typedef struct {
 7+#ifdef MG_RENDERER_SMALL3DLIB
 8+	SDL_Renderer* renderer;
 9+#endif
10+#ifdef MG_RENDERER_SOKOL
11 	SDL_GLContext gl;
12+#endif
13 } MGVidBE;
14 
15 typedef struct {
16@@ -41,4 +46,3 @@ void platform_shutdown(MGContext* ctx);
17 void platform_window_create(MGContext* ctx);
18 
19 #endif /* PLATFORM_H */
20-
+39, -20
 1@@ -9,6 +9,8 @@ project(
 2 
 3 cc = meson.get_compiler('c')
 4 math_dep = cc.find_library('m', required: false)
 5+sdl_dep = dependency('sdl2')
 6+renderer_backend = get_option('renderer')
 7 
 8 sources = [
 9 	'source/main.c',
10@@ -41,41 +43,57 @@ mgmath_dep = declare_dependency(
11 	dependencies: math_dep,
12 )
13 
14-opengl_dep = dependency('opengl', required: false)
15-if not opengl_dep.found() # fallback (at least for alpine)
16-	opengl_dep = dependency('gl')
17-endif
18+renderer_c_args = []
19+renderer_sources = [
20+	'source/renderer/renderer.c',
21+]
22+if renderer_backend == 'sokol'
23+	opengl_dep = dependency('opengl', required: false)
24+	if not opengl_dep.found() # fallback (at least for alpine)
25+		opengl_dep = dependency('gl')
26+	endif
27 
28-sokol_gfx_lib = static_library(
29-	'sokol_gfx',
30-	'vendor/sokol_gfx.c',
31-	include_directories: vendor_inc,
32-	dependencies: opengl_dep,
33-	override_options: [ 'c_std=c99' ],
34-)
35+	sokol_gfx_lib = static_library(
36+		'sokol_gfx',
37+		'vendor/sokol_gfx.c',
38+		include_directories: vendor_inc,
39+		dependencies: opengl_dep,
40+		override_options: [ 'c_std=c99' ],
41+	)
42 
43-sokol_gfx_dep = declare_dependency(
44-	include_directories: vendor_inc,
45-	link_with: sokol_gfx_lib,
46-	dependencies: opengl_dep,
47-)
48+	renderer_backend_dep = declare_dependency(
49+		include_directories: vendor_inc,
50+		link_with: sokol_gfx_lib,
51+		dependencies: [ opengl_dep, sdl_dep ],
52+	)
53+	renderer_sources += [ 'source/renderer/renderer_sokol.c' ]
54+	renderer_c_args += [ '-DMG_RENDERER_SOKOL' ]
55+else
56+	renderer_backend_dep = declare_dependency(
57+		include_directories: vendor_inc,
58+		dependencies: sdl_dep,
59+	)
60+	renderer_sources += [ 'source/renderer/renderer_small3dlib.c' ]
61+	renderer_c_args += [ '-DMG_RENDERER_SMALL3DLIB' ]
62+endif
63 
64 renderer_lib = static_library(
65 	'renderer',
66-	'source/renderer/renderer.c',
67+	renderer_sources,
68 	include_directories: include_dirs,
69-	dependencies: sokol_gfx_dep,
70+	dependencies: renderer_backend_dep,
71+	c_args: renderer_c_args,
72 	override_options: [ 'c_std=c99' ],
73 )
74 
75 renderer_dep = declare_dependency(
76 	include_directories: include_dirs,
77 	link_with: renderer_lib,
78-	dependencies: sokol_gfx_dep,
79+	dependencies: renderer_backend_dep,
80 )
81 
82 deps = [
83-	dependency('sdl2'),
84+	sdl_dep,
85 	mgmath_dep,
86 	renderer_dep,
87 ]
88@@ -85,4 +103,5 @@ executable(
89 	sources,
90 	include_directories: include_dirs,
91 	dependencies: deps,
92+	c_args: renderer_c_args,
93 )
+7, -0
1@@ -0,0 +1,7 @@
2+option(
3+	'renderer',
4+	type: 'combo',
5+	choices: [ 'sokol', 'small3dlib' ],
6+	value: 'sokol',
7+	description: '3D renderer backend',
8+)
+35, -2
 1@@ -5,7 +5,11 @@
 2 
 3 void platform_get_drawable_size(MGContext* ctx, i32* width, i32* height)
 4 {
 5+#ifdef MG_RENDERER_SMALL3DLIB
 6+	SDL_GetWindowSize(ctx->win, width, height);
 7+#else
 8 	SDL_GL_GetDrawableSize(ctx->win, width, height);
 9+#endif
10 }
11 
12 void platform_init(MGContext* ctx)
13@@ -14,12 +18,14 @@ void platform_init(MGContext* ctx)
14 	if (SDL_Init(SDL_INIT_VIDEO) != 0)
15 		mg_die(MG_ERR_SDL_INIT, "SDL_Init failed: %s", SDL_GetError());
16 
17+#ifdef MG_RENDERER_SOKOL
18 	/* TODO: OpenGL specifis */
19 	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
20 	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
21 	SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
22 	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
23 	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
24+#endif
25 }
26 
27 /* TODO: platform independant design MGEvent, MGPoll */
28@@ -44,7 +50,11 @@ i32 platform_poll(MGContext* ctx)
29 
30 void platform_present(MGContext* ctx)
31 {
32+#ifdef MG_RENDERER_SMALL3DLIB
33+	SDL_RenderPresent(ctx->vbe.renderer);
34+#else
35 	SDL_GL_SwapWindow(ctx->win);
36+#endif
37 }
38 
39 void platform_shutdown(MGContext* ctx)
40@@ -52,10 +62,19 @@ void platform_shutdown(MGContext* ctx)
41 	if (!ctx)
42 		return;
43 
44+#ifdef MG_RENDERER_SMALL3DLIB
45+	if (ctx->vbe.renderer) {
46+		SDL_DestroyRenderer(ctx->vbe.renderer);
47+		ctx->vbe.renderer = NULL;
48+	}
49+#endif
50+
51+#ifdef MG_RENDERER_SOKOL
52 	if (ctx->vbe.gl) {
53 		SDL_GL_DeleteContext(ctx->vbe.gl);
54 		ctx->vbe.gl = NULL;
55 	}
56+#endif
57 
58 	if (ctx->win) {
59 		SDL_DestroyWindow(ctx->win);
60@@ -67,14 +86,28 @@ void platform_shutdown(MGContext* ctx)
61 
62 void platform_window_create(MGContext* ctx)
63 {
64+	u32 flags = SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI;
65+#ifdef MG_RENDERER_SOKOL
66+	flags |= SDL_WINDOW_OPENGL;
67+#endif
68+
69 	ctx->win = SDL_CreateWindow(
70 		ctx->title, ctx->x, ctx->y, ctx->width, ctx->height,
71-		SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI|SDL_WINDOW_OPENGL
72+		flags
73 	);
74 
75 	if (!ctx->win)
76 		mg_die(MG_ERR_SDL_CREATE_WIN, "Failed to create window: %s", SDL_GetError());
77 
78+#ifdef MG_RENDERER_SMALL3DLIB
79+	ctx->vbe.renderer = SDL_CreateRenderer(ctx->win, -1, SDL_RENDERER_ACCELERATED);
80+	if (!ctx->vbe.renderer)
81+		ctx->vbe.renderer = SDL_CreateRenderer(ctx->win, -1, SDL_RENDERER_SOFTWARE);
82+	if (!ctx->vbe.renderer)
83+		mg_die(MG_ERR_REN_CREATE, "Failed to create SDL renderer: %s", SDL_GetError());
84+#endif
85+
86+#ifdef MG_RENDERER_SOKOL
87 	ctx->vbe.gl = SDL_GL_CreateContext(ctx->win);
88 	if (!ctx->vbe.gl)
89 		mg_die(MG_ERR_GL_CTX_CREATE, "Failed to initialise GL Context");
90@@ -83,5 +116,5 @@ void platform_window_create(MGContext* ctx)
91 		mg_die(MG_ERR_GL_CTX_ACTIVATE, "Failed to activate GL Context: %s", SDL_GetError());
92 
93 	SDL_GL_SetSwapInterval(1);
94+#endif
95 }
96-
+12, -399
  1@@ -1,434 +1,47 @@
  2-#include <sokol_gfx.h>
  3-#include <stb_image_c89.h>
  4-
  5-#include "util.h"
  6-#include "types.h"
  7-#include "platform/platform.h"
  8 #include "renderer/renderer.h"
  9-
 10-#define MAX_MESHES (512) /* TODO; random value ig */
 11-#define MAX_TEXTURES (512) /* TODO; random value ig */
 12-
 13-typedef struct {
 14-	sg_buffer vb; /* vertex buffer */
 15-	sg_buffer ib; /* index buffer */
 16-	u32       ic; /* index count */
 17-	i8        used;
 18-} RendererMesh;
 19-
 20-typedef struct {
 21-	sg_image image;
 22-	sg_view  view;
 23-	i8       used;
 24-} RendererTexture;
 25-
 26-typedef struct {
 27-	MGContext* ctx;
 28-	i8         pass; /* basically a bool; in
 29-	                    render pass or not */
 30-
 31-	sg_shader   shader;
 32-	sg_pipeline pipeline;
 33-	sg_sampler  sampler;
 34-
 35-	RendererMesh    meshes[MAX_MESHES];
 36-	RendererTexture textures[MAX_MESHES];
 37-} RendererState;
 38-
 39-/* create the shader and render pipeline */
 40-static void create_pipelines(void);
 41-
 42-/* create texture sampler */
 43-static void create_sampler(void);
 44-
 45-/* logging function for passing to sokol  */
 46-static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln, const char* file, void* user);
 47-
 48-static RendererState ren;
 49-/* TODO: textures */
 50-static const char* vsdr =
 51-"#version 410\n"
 52-"layout(location=0) in vec3 position;\n"
 53-"layout(location=1) in vec2 texcoord0;\n"
 54-"layout(location=2) in vec4 color0;\n"
 55-"\n"
 56-"uniform mat4 mvp;\n"
 57-"\n"
 58-"out vec2 uv;\n"
 59-"out vec4 colour;\n"
 60-"\n"
 61-"void main(void)\n"
 62-"{\n"
 63-"    gl_Position = mvp * vec4(position, 1.0);\n"
 64-"    uv = texcoord0;\n"
 65-"    colour = color0;\n"
 66-"}\n";
 67-static const char* fsdr =
 68-"#version 410\n"
 69-"in vec2 uv;\n"
 70-"in vec4 colour;\n"
 71-"\n"
 72-"uniform sampler2D tex;\n"
 73-"\n"
 74-"out vec4 frag_colour;\n"
 75-"\n"
 76-"void main(void)\n"
 77-"{\n"
 78-"    frag_colour = texture(tex, uv);\n"
 79-"}\n";
 80-
 81-static void create_pipelines(void)
 82-{
 83-	sg_shader_desc shader_desc = {0};
 84-	sg_pipeline_desc pipeline_desc = {0};
 85-
 86-	/* shader descriptor */
 87-	shader_desc.vertex_func.source = vsdr;
 88-	shader_desc.fragment_func.source = fsdr;
 89-	shader_desc.attrs[0].glsl_name = "position";
 90-	shader_desc.attrs[1].glsl_name = "texcoord0";
 91-	shader_desc.attrs[2].glsl_name = "color0";
 92-	shader_desc.uniform_blocks[0].stage = SG_SHADERSTAGE_VERTEX;
 93-	shader_desc.uniform_blocks[0].size = sizeof(float) * 16; /* size of mvp matrix */
 94-	shader_desc.uniform_blocks[0].layout = SG_UNIFORMLAYOUT_STD140;
 95-	shader_desc.uniform_blocks[0] .glsl_uniforms[0].glsl_name = "mvp";
 96-	shader_desc.uniform_blocks[0] .glsl_uniforms[0].type = SG_UNIFORMTYPE_MAT4;
 97-
 98-	/* textures */
 99-	shader_desc.views[0].texture.stage = SG_SHADERSTAGE_FRAGMENT;
100-	shader_desc.views[0].texture.image_type = SG_IMAGETYPE_2D;
101-	shader_desc.views[0].texture.sample_type = SG_IMAGESAMPLETYPE_FLOAT;
102-	shader_desc.samplers[0].stage = SG_SHADERSTAGE_FRAGMENT;
103-	shader_desc.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING;
104-	shader_desc.texture_sampler_pairs[0].stage = SG_SHADERSTAGE_FRAGMENT;
105-	shader_desc.texture_sampler_pairs[0].view_slot = 0;
106-	shader_desc.texture_sampler_pairs[0].sampler_slot = 0;
107-	shader_desc.texture_sampler_pairs[0].glsl_name = "tex";
108-
109-	ren.shader = sg_make_shader(&shader_desc);
110-	if (sg_query_shader_state(ren.shader) != SG_RESOURCESTATE_VALID)
111-		mg_die(MG_ERR_SHADER_CREATE, "Failed to create shader");
112-
113-	/* pipeline descriptor */
114-	pipeline_desc.shader = ren.shader;
115-	pipeline_desc.layout.buffers[0].stride = sizeof(MGVertex);
116-	pipeline_desc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT3;
117-	pipeline_desc.layout.attrs[1].format = SG_VERTEXFORMAT_FLOAT2;
118-	pipeline_desc.layout.attrs[2].format = SG_VERTEXFORMAT_UBYTE4N;
119-	pipeline_desc.index_type = SG_INDEXTYPE_UINT16;
120-	pipeline_desc.depth.compare = SG_COMPAREFUNC_LESS_EQUAL;
121-	pipeline_desc.depth.write_enabled = true;
122-	pipeline_desc.cull_mode = SG_CULLMODE_BACK;
123-	pipeline_desc.face_winding = SG_FACEWINDING_CCW;
124-
125-	ren.pipeline = sg_make_pipeline(&pipeline_desc);
126-	if (sg_query_pipeline_state(ren.pipeline) != SG_RESOURCESTATE_VALID)
127-		mg_die(MG_ERR_PIPELINE_CREATE, "Failed to create pipeline");
128-}
129-
130-static void create_sampler(void)
131-{
132-	sg_sampler_desc desc = {0};
133-
134-	desc.min_filter = SG_FILTER_NEAREST;
135-	desc.mag_filter = SG_FILTER_NEAREST;
136-	desc.wrap_u = SG_WRAP_REPEAT;
137-	desc.wrap_v = SG_WRAP_REPEAT;
138-
139-	ren.sampler = sg_make_sampler(&desc);
140-
141-	if (sg_query_sampler_state(ren.sampler) != SG_RESOURCESTATE_VALID)
142-		mg_die(MG_ERR_SAMPLER_CREATE, "Failed to create sampler");
143-}
144-
145-static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln, const char* file, void* user)
146-{
147-	LogType type;
148-	(void)item;
149-	(void)user;
150-
151-	type = level <= 1 ? LOG_ERR : LOG_DBG;
152-
153-	mg_log(
154-		type, "%s: %s [%s:%u]",
155-		tag ? tag : "Sokol",
156-		msg ? msg : "No message",
157-		file ? file : "Unknown",
158-		ln
159-	);
160-}
161+#include "renderer_backend.h"
162 
163 void renderer_create(MGContext* ctx)
164 {
165-	sg_desc desc = {0};
166-
167-	ren.ctx = ctx;
168-	ren.pass = 0;
169-
170-	desc.environment.defaults.color_format = SG_PIXELFORMAT_RGBA8;
171-	desc.environment.defaults.depth_format = SG_PIXELFORMAT_DEPTH;
172-	desc.environment.defaults.sample_count = 1;
173-	desc.logger.func = sokol_log;
174-
175-	sg_setup(&desc);
176-
177-	if (!sg_isvalid())
178-		mg_die(MG_ERR_REN_CREATE, "Failed to initialise renderer");
179-
180-	create_pipelines();
181-	create_sampler();
182+	renderer_backend_create(ctx);
183 }
184 
185 void renderer_begin(void)
186 {
187-	sg_pass pass = {0};
188-	i32 width;
189-	i32 height;
190-
191-	ren.pass = 0;
192-
193-	platform_get_drawable_size(ren.ctx, &width, &height);
194-	/* (minimized window?) no need to draw */
195-	if (width <= 0 || height <= 0)
196-		return;
197-
198-	pass.action.colors[0].load_action = SG_LOADACTION_CLEAR;
199-	pass.action.colors[0].clear_value.r = 0.90f;
200-	pass.action.colors[0].clear_value.g = 0.80f;
201-	pass.action.colors[0].clear_value.b = 0.70f;
202-	pass.action.colors[0].clear_value.a = 1.00f;
203-
204-	pass.action.depth.load_action = SG_LOADACTION_CLEAR;
205-	pass.action.depth.clear_value = 1.0f;
206-
207-	/* swapchain descriptor */
208-	pass.swapchain.width = width;
209-	pass.swapchain.height = height;
210-	pass.swapchain.sample_count = 1;
211-	pass.swapchain.color_format = SG_PIXELFORMAT_RGBA8;
212-	pass.swapchain.depth_format = SG_PIXELFORMAT_DEPTH;
213-
214-	pass.swapchain.gl.framebuffer = 0;
215-
216-	sg_begin_pass(&pass);
217-	ren.pass = 1;
218+	renderer_backend_begin();
219 }
220 
221 void renderer_end(void)
222 {
223-	if (!ren.pass)
224-		return;
225-
226-	sg_end_pass();
227-	sg_commit();
228-
229-	ren.pass = 0;
230+	renderer_backend_end();
231 }
232 
233 void renderer_destroy(void)
234 {
235-	u32 id;
236-
237-	/* the values could be different in future so
238-	   probably should keep them separate */
239-	for (id = 1; id < MAX_MESHES; id++)
240-		renderer_mesh_destroy(id);
241-	for (id = 1; id < MAX_TEXTURES; id++)
242-		renderer_texture_destroy(id);
243-
244-	sg_destroy_sampler(ren.sampler);
245-	sg_destroy_pipeline(ren.pipeline);
246-	sg_destroy_shader(ren.shader);
247-
248-	if (sg_isvalid())
249-		sg_shutdown();
250-
251-	ren.ctx = NULL;
252-	ren.pass = 0;
253+	renderer_backend_destroy();
254 }
255 
256 MGMesh renderer_mesh_create(const MGMeshDesc* desc)
257 {
258-	sg_buffer_desc buffer_desc = {0};
259-	RendererMesh* mesh;
260-	u32 id;
261-
262-	if (!desc || !desc->vtc || !desc->idc || desc->vtcc == 0 || desc->idcc == 0)
263-		return MGMESH_INVALID;
264-
265-	/* find free mesh slot */
266-	for (id = 1; id < MAX_MESHES; id++) {
267-		if (!ren.meshes[id].used)
268-			break;
269-	}
270-
271-	if (id == MAX_MESHES) {
272-		mg_log(LOG_ERR, "Maximum mesh limit reached");
273-		return MGMESH_INVALID;
274-	}
275-
276-	/* vertex buffer */
277-	mesh = &ren.meshes[id];
278-	buffer_desc.data.ptr = desc->vtc;
279-	buffer_desc.data.size = desc->vtcc * sizeof(MGVertex);
280-	mesh->vb = sg_make_buffer(&buffer_desc);
281-
282-	/* index buffer */
283-	buffer_desc = (sg_buffer_desc){0};
284-	buffer_desc.usage.vertex_buffer = false;
285-	buffer_desc.usage.index_buffer = true;
286-	buffer_desc.usage.immutable = true;
287-	buffer_desc.data.ptr = desc->idc;
288-	buffer_desc.data.size = desc->idcc * sizeof(u16);
289-	mesh->ib = sg_make_buffer(&buffer_desc);
290-
291-	i8 valid = sg_query_buffer_state(mesh->vb) == SG_RESOURCESTATE_VALID &&
292-	           sg_query_buffer_state(mesh->ib) == SG_RESOURCESTATE_VALID;
293-	if (!valid) {
294-		if (sg_query_buffer_state(mesh->vb) == SG_RESOURCESTATE_VALID)
295-			sg_destroy_buffer(mesh->vb);
296-		if (sg_query_buffer_state(mesh->ib) == SG_RESOURCESTATE_VALID)
297-			sg_destroy_buffer(mesh->ib);
298-
299-		*mesh = (RendererMesh){0};
300-		mg_log(LOG_ERR, "Failed to create mesh buffers");
301-		return MGMESH_INVALID;
302-	}
303-
304-	mesh->ic = desc->idcc;
305-	mesh->used = 1;
306-	return id;
307+	return renderer_backend_mesh_create(desc);
308 }
309 
310 void renderer_mesh_draw(MGMesh mesh, MGTexture texture, const MGMat4* mvp)
311 {
312-	RendererMesh* rmesh;
313-	RendererTexture* rtexture;
314-	sg_bindings bindings = {0};
315-	sg_range uniforms;
316-
317-	if (!ren.pass || !mvp)
318-		return;
319-
320-	if (mesh == MGMESH_INVALID || mesh >= MAX_MESHES)
321-		return;
322-
323-	if (texture == MGTEXTURE_INVALID || texture >= MAX_TEXTURES)
324-		return;
325-
326-	rmesh = &ren.meshes[mesh];
327-	rtexture = &ren.textures[texture];
328-	if (!rmesh->used || !rtexture->used)
329-		return;
330-
331-	bindings.vertex_buffers[0] = rmesh->vb;
332-	bindings.index_buffer = rmesh->ib;
333-	bindings.views[0] = rtexture->view;
334-	bindings.samplers[0] = ren.sampler;
335-
336-	uniforms.ptr = &mvp->Elements[0][0];
337-	uniforms.size = sizeof(float) * 16;
338-
339-	sg_apply_pipeline(ren.pipeline);
340-	sg_apply_bindings(&bindings);
341-	sg_apply_uniforms(0, &uniforms);
342-
343-	sg_draw(0, rmesh->ic, 1);
344+	renderer_backend_mesh_draw(mesh, texture, mvp);
345 }
346 
347-void renderer_mesh_destroy(MGMesh id)
348+void renderer_mesh_destroy(MGMesh mesh)
349 {
350-	RendererMesh* mesh;
351-
352-	if (id == MGMESH_INVALID || id >= MAX_MESHES)
353-		return;
354-
355-	mesh = &ren.meshes[id];
356-	if (!mesh->used)
357-		return;
358-
359-	sg_destroy_buffer(mesh->ib);
360-	sg_destroy_buffer(mesh->vb);
361-
362-	*mesh = (RendererMesh){0};
363+	renderer_backend_mesh_destroy(mesh);
364 }
365 
366 MGTexture renderer_texture_load(const char* path)
367 {
368-	sg_image_desc image_desc = {0};
369-	sg_view_desc view_desc = {0};
370-	RendererTexture* texture;
371-	stbi_uc* pixels;
372-	int width;
373-	int height;
374-	int channels;
375-	u32 id;
376-
377-	if (!path)
378-		return MGTEXTURE_INVALID;
379-
380-	/* find free texture slot */
381-	for (id = 1; id < MAX_TEXTURES; id++) {
382-		if (!ren.textures[id].used)
383-			break;
384-	}
385-
386-	if (id == MAX_TEXTURES) {
387-		mg_log(LOG_ERR, "Maximum texture limit reached");
388-		return MGTEXTURE_INVALID;
389-	}
390-
391-	texture = &ren.textures[id];
392-	pixels = stbi_load(path, &width, &height, &channels, 4);
393-	if (!pixels) {
394-		mg_log(LOG_ERR, "Failed to load texture: %s", path);
395-		return MGTEXTURE_INVALID;
396-	}
397-
398-	image_desc.width = width;
399-	image_desc.height = height;
400-	image_desc.pixel_format = SG_PIXELFORMAT_RGBA8;
401-	image_desc.data.mip_levels[0].ptr = pixels;
402-	image_desc.data.mip_levels[0].size =width * height * 4;
403-
404-	texture->image = sg_make_image(&image_desc);
405-	stbi_image_free(pixels);
406-	if (sg_query_image_state(texture->image) != SG_RESOURCESTATE_VALID) {
407-		*texture = (RendererTexture){0};
408-		mg_log(LOG_ERR, "Failed to create texture: %s", path);
409-		return MGTEXTURE_INVALID;
410-	}
411-
412-	view_desc.texture.image = texture->image;
413-	texture->view = sg_make_view(&view_desc);
414-
415-	if (sg_query_view_state(texture->view) != SG_RESOURCESTATE_VALID) {
416-		sg_destroy_image(texture->image);
417-		*texture = (RendererTexture){0};
418-		mg_log(LOG_ERR, "Failed to create texture view: %s", path);
419-		return MGTEXTURE_INVALID;
420-	}
421-
422-	texture->used = 1;
423-
424-	return id;
425+	return renderer_backend_texture_load(path);
426 }
427 
428-void renderer_texture_destroy(MGTexture id)
429+void renderer_texture_destroy(MGTexture texture)
430 {
431-	RendererTexture* texture;
432-
433-	if (id == MGTEXTURE_INVALID || id >= MAX_TEXTURES)
434-		return;
435-
436-	texture = &ren.textures[id];
437-
438-	if (!texture->used)
439-		return;
440-
441-	sg_destroy_view(texture->view);
442-	sg_destroy_image(texture->image);
443-
444-	*texture = (RendererTexture){0};
445+	renderer_backend_texture_destroy(texture);
446 }
447-
+18, -0
 1@@ -0,0 +1,18 @@
 2+#ifndef RENDERER_BACKEND_H
 3+#define RENDERER_BACKEND_H
 4+
 5+#include "renderer/renderer.h"
 6+
 7+void renderer_backend_create(MGContext* ctx);
 8+void renderer_backend_begin(void);
 9+void renderer_backend_end(void);
10+void renderer_backend_destroy(void);
11+
12+MGMesh renderer_backend_mesh_create(const MGMeshDesc* desc);
13+void renderer_backend_mesh_draw(MGMesh mesh, MGTexture texture, const MGMat4* mvp);
14+void renderer_backend_mesh_destroy(MGMesh mesh);
15+
16+MGTexture renderer_backend_texture_load(const char* path);
17+void renderer_backend_texture_destroy(MGTexture texture);
18+
19+#endif /* RENDERER_BACKEND_H */
+473, -0
  1@@ -0,0 +1,473 @@
  2+#include <SDL2/SDL.h>
  3+#include <stdlib.h>
  4+#include <string.h>
  5+#include <stb_image_c89.h>
  6+
  7+#include "util.h"
  8+#include "types.h"
  9+#include "platform/platform.h"
 10+#include "renderer/renderer.h"
 11+#include "renderer_backend.h"
 12+
 13+#define MAX_MESHES (512)
 14+#define MAX_TEXTURES (512)
 15+#define S3L_MAX_WIDTH (4096)
 16+#define S3L_MAX_HEIGHT (4096)
 17+
 18+#define S3L_MAX_PIXELS (S3L_MAX_WIDTH * S3L_MAX_HEIGHT)
 19+#define S3L_Z_BUFFER 1
 20+#define S3L_PIXEL_FUNCTION small3dlib_pixel
 21+#include <small3dlib.h>
 22+
 23+typedef struct {
 24+	MGVertex* vtc; /* vertices */
 25+	u16*      idc; /* indices */
 26+	u32       ic;  /* index count */
 27+	i8        used;
 28+} RendererMesh;
 29+
 30+typedef struct {
 31+	u8* pixels;
 32+	i32 width;
 33+	i32 height;
 34+	i8  used;
 35+} RendererTexture;
 36+
 37+typedef struct {
 38+	MGContext*  ctx;
 39+	i8         pass; /* basically a bool; in
 40+	                    render pass or not */
 41+
 42+	SDL_Texture* screen_texture;
 43+	u32*        framebuffer;
 44+	i32         width;
 45+	i32         height;
 46+
 47+	RendererMesh    meshes[MAX_MESHES];
 48+	RendererTexture textures[MAX_TEXTURES];
 49+
 50+	RendererMesh*    active_mesh;
 51+	RendererTexture* active_texture;
 52+} RendererState;
 53+
 54+typedef struct {
 55+	float x;
 56+	float y;
 57+	float z;
 58+	float w;
 59+} ClipVertex;
 60+
 61+static RendererState ren;
 62+
 63+/* create or resize the SDL texture used to present the software framebuffer */
 64+static void create_screen_texture(i32 width, i32 height);
 65+
 66+/* destroy the SDL texture and software framebuffer */
 67+static void destroy_screen_texture(void);
 68+
 69+static ClipVertex transform_vertex(const MGMat4* mvp, const MGVertex* v);
 70+static i8 triangle_outside(const ClipVertex* a, const ClipVertex* b, const ClipVertex* c);
 71+static i8 triangle_front_facing(const ClipVertex* a, const ClipVertex* b, const ClipVertex* c);
 72+static S3L_Vec4 clip_to_screen(const ClipVertex* v);
 73+static u32 sample_texture(const RendererMesh* mesh, const RendererTexture* texture, const S3L_PixelInfo* pixel);
 74+static u32 rgba_to_sdl(u32 rgba);
 75+
 76+static inline void small3dlib_pixel(S3L_PixelInfo* pixel)
 77+{
 78+	u32 index;
 79+
 80+	if (!ren.active_mesh || !ren.active_texture || !ren.framebuffer)
 81+		return;
 82+
 83+	index = (u32)pixel->y * (u32)ren.width + (u32)pixel->x;
 84+	ren.framebuffer[index] = rgba_to_sdl(sample_texture(ren.active_mesh, ren.active_texture, pixel));
 85+}
 86+
 87+static void create_screen_texture(i32 width, i32 height)
 88+{
 89+	u64 pixel_count;
 90+	u32* framebuffer;
 91+
 92+	if (width == ren.width && height == ren.height && ren.framebuffer && ren.screen_texture)
 93+		return;
 94+
 95+	if (width <= 0 || height <= 0)
 96+		return;
 97+
 98+	if (width > S3L_MAX_WIDTH || height > S3L_MAX_HEIGHT)
 99+		mg_die(MG_ERR_REN_CREATE, "small3dlib framebuffer exceeds %dx%d", S3L_MAX_WIDTH, S3L_MAX_HEIGHT);
100+
101+	pixel_count = (u64)width * (u64)height;
102+	framebuffer = malloc(pixel_count * sizeof(*framebuffer));
103+	if (!framebuffer)
104+		mg_die(MG_ERR_REN_CREATE, "Failed to allocate small3dlib framebuffer");
105+
106+	if (ren.screen_texture)
107+		SDL_DestroyTexture(ren.screen_texture);
108+
109+	ren.screen_texture = SDL_CreateTexture(
110+		ren.ctx->vbe.renderer,
111+		SDL_PIXELFORMAT_RGBA8888,
112+		SDL_TEXTUREACCESS_STREAMING,
113+		width,
114+		height
115+	);
116+
117+	if (!ren.screen_texture)
118+		mg_die(MG_ERR_REN_CREATE, "Failed to create small3dlib texture: %s", SDL_GetError());
119+
120+	free(ren.framebuffer);
121+	ren.framebuffer = framebuffer;
122+	ren.width = width;
123+	ren.height = height;
124+
125+	S3L_resolutionX = (uint16_t)width;
126+	S3L_resolutionY = (uint16_t)height;
127+}
128+
129+static void destroy_screen_texture(void)
130+{
131+	if (ren.screen_texture) {
132+		SDL_DestroyTexture(ren.screen_texture);
133+		ren.screen_texture = NULL;
134+	}
135+
136+	free(ren.framebuffer);
137+	ren.framebuffer = NULL;
138+	ren.width = 0;
139+	ren.height = 0;
140+}
141+
142+static ClipVertex transform_vertex(const MGMat4* mvp, const MGVertex* v)
143+{
144+	ClipVertex r;
145+
146+	r.x = mvp->Elements[0][0] * v->x + mvp->Elements[1][0] * v->y +
147+	      mvp->Elements[2][0] * v->z + mvp->Elements[3][0];
148+	r.y = mvp->Elements[0][1] * v->x + mvp->Elements[1][1] * v->y +
149+	      mvp->Elements[2][1] * v->z + mvp->Elements[3][1];
150+	r.z = mvp->Elements[0][2] * v->x + mvp->Elements[1][2] * v->y +
151+	      mvp->Elements[2][2] * v->z + mvp->Elements[3][2];
152+	r.w = mvp->Elements[0][3] * v->x + mvp->Elements[1][3] * v->y +
153+	      mvp->Elements[2][3] * v->z + mvp->Elements[3][3];
154+
155+	return r;
156+}
157+
158+static i8 triangle_outside(const ClipVertex* a, const ClipVertex* b, const ClipVertex* c)
159+{
160+	if (a->w <= 0.0001f || b->w <= 0.0001f || c->w <= 0.0001f)
161+		return 1;
162+
163+	if (a->x < -a->w && b->x < -b->w && c->x < -c->w)
164+		return 1;
165+	if (a->x > a->w && b->x > b->w && c->x > c->w)
166+		return 1;
167+	if (a->y < -a->w && b->y < -b->w && c->y < -c->w)
168+		return 1;
169+	if (a->y > a->w && b->y > b->w && c->y > c->w)
170+		return 1;
171+	if (a->z < -a->w || b->z < -b->w || c->z < -c->w)
172+		return 1;
173+	if (a->z > a->w && b->z > b->w && c->z > c->w)
174+		return 1;
175+
176+	return 0;
177+}
178+
179+static i8 triangle_front_facing(const ClipVertex* a, const ClipVertex* b, const ClipVertex* c)
180+{
181+	float ax = a->x / a->w;
182+	float ay = a->y / a->w;
183+	float bx = b->x / b->w;
184+	float by = b->y / b->w;
185+	float cx = c->x / c->w;
186+	float cy = c->y / c->w;
187+	float area = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
188+
189+	return area > 0.0f;
190+}
191+
192+static S3L_Vec4 clip_to_screen(const ClipVertex* v)
193+{
194+	S3L_Vec4 r;
195+	float inv_w = 1.0f / v->w;
196+	float ndc_x = v->x * inv_w;
197+	float ndc_y = v->y * inv_w;
198+	float ndc_z = v->z * inv_w;
199+
200+	r.x = (S3L_Unit)((ndc_x * 0.5f + 0.5f) * (float)(ren.width - 1));
201+	r.y = (S3L_Unit)((0.5f - ndc_y * 0.5f) * (float)(ren.height - 1));
202+	r.z = (S3L_Unit)((ndc_z * 0.5f + 0.5f) * 1048576.0f) + 1;
203+	r.w = S3L_F;
204+
205+	return r;
206+}
207+
208+static u32 sample_texture(const RendererMesh* mesh, const RendererTexture* texture, const S3L_PixelInfo* pixel)
209+{
210+	u32 tri = (u32)pixel->triangleIndex * 3;
211+	const MGVertex* a = &mesh->vtc[mesh->idc[tri + 0]];
212+	const MGVertex* b = &mesh->vtc[mesh->idc[tri + 1]];
213+	const MGVertex* c = &mesh->vtc[mesh->idc[tri + 2]];
214+	float ba = (float)pixel->barycentric[0] / (float)S3L_F;
215+	float bb = (float)pixel->barycentric[1] / (float)S3L_F;
216+	float bc = (float)pixel->barycentric[2] / (float)S3L_F;
217+	float u = a->u * ba + b->u * bb + c->u * bc;
218+	float v = a->v * ba + b->v * bb + c->v * bc;
219+	i32 x;
220+	i32 y;
221+	u8* texel;
222+
223+	if (!texture || !texture->pixels)
224+		return 0xffffffff;
225+
226+	while (u < 0.0f)
227+		u += 1.0f;
228+	while (v < 0.0f)
229+		v += 1.0f;
230+	while (u >= 1.0f)
231+		u -= 1.0f;
232+	while (v >= 1.0f)
233+		v -= 1.0f;
234+
235+	x = (i32)(u * (float)texture->width);
236+	y = (i32)(v * (float)texture->height);
237+
238+	texel = texture->pixels + ((y * texture->width + x) * 4);
239+	return ((u32)texel[0]) |
240+	       ((u32)texel[1] << 8) |
241+	       ((u32)texel[2] << 16) |
242+	       ((u32)texel[3] << 24);
243+}
244+
245+static u32 rgba_to_sdl(u32 rgba)
246+{
247+	u32 r = (rgba >> 0) & 0xff;
248+	u32 g = (rgba >> 8) & 0xff;
249+	u32 b = (rgba >> 16) & 0xff;
250+	u32 a = (rgba >> 24) & 0xff;
251+
252+	return (r << 24) | (g << 16) | (b << 8) | a;
253+}
254+
255+void renderer_backend_create(MGContext* ctx)
256+{
257+	ren.ctx = ctx;
258+	ren.pass = 0;
259+	ren.active_mesh = NULL;
260+	ren.active_texture = NULL;
261+}
262+
263+void renderer_backend_begin(void)
264+{
265+	i32 width;
266+	i32 height;
267+	u64 i;
268+	u64 pixel_count;
269+	u32 clear_colour = rgba_to_sdl(0xffb3cce6);
270+
271+	ren.pass = 0;
272+	platform_get_drawable_size(ren.ctx, &width, &height);
273+	if (width <= 0 || height <= 0)
274+		return;
275+
276+	create_screen_texture(width, height);
277+
278+	pixel_count = (u64)ren.width * (u64)ren.height;
279+	for (i = 0; i < pixel_count; i++)
280+		ren.framebuffer[i] = clear_colour;
281+
282+	S3L_newFrame();
283+	ren.pass = 1;
284+}
285+
286+void renderer_backend_end(void)
287+{
288+	if (!ren.pass)
289+		return;
290+
291+	SDL_UpdateTexture(ren.screen_texture, NULL, ren.framebuffer, ren.width * (i32)sizeof(*ren.framebuffer));
292+	SDL_RenderClear(ren.ctx->vbe.renderer);
293+	SDL_RenderCopy(ren.ctx->vbe.renderer, ren.screen_texture, NULL, NULL);
294+	ren.pass = 0;
295+}
296+
297+void renderer_backend_destroy(void)
298+{
299+	u32 id;
300+
301+	/* the values could be different in future so
302+	   probably should keep them separate */
303+	for (id = 1; id < MAX_MESHES; id++)
304+		renderer_backend_mesh_destroy(id);
305+	for (id = 1; id < MAX_TEXTURES; id++)
306+		renderer_backend_texture_destroy(id);
307+
308+	destroy_screen_texture();
309+
310+	ren.ctx = NULL;
311+	ren.active_mesh = NULL;
312+	ren.active_texture = NULL;
313+	ren.pass = 0;
314+}
315+
316+MGMesh renderer_backend_mesh_create(const MGMeshDesc* desc)
317+{
318+	RendererMesh* mesh;
319+	u32 id;
320+
321+	if (!desc || !desc->vtc || !desc->idc || desc->vtcc == 0 || desc->idcc == 0)
322+		return MGMESH_INVALID;
323+
324+	if (desc->idcc % 3 != 0)
325+		return MGMESH_INVALID;
326+
327+	/* find free mesh slot */
328+	for (id = 1; id < MAX_MESHES; id++) {
329+		if (!ren.meshes[id].used)
330+			break;
331+	}
332+
333+	if (id == MAX_MESHES) {
334+		mg_log(LOG_ERR, "Maximum mesh limit reached");
335+		return MGMESH_INVALID;
336+	}
337+
338+	/* copy vertex and index data */
339+	mesh = &ren.meshes[id];
340+	mesh->vtc = malloc(desc->vtcc * sizeof(*mesh->vtc));
341+	mesh->idc = malloc(desc->idcc * sizeof(*mesh->idc));
342+	if (!mesh->vtc || !mesh->idc) {
343+		free(mesh->vtc);
344+		free(mesh->idc);
345+		*mesh = (RendererMesh){0};
346+		mg_log(LOG_ERR, "Failed to allocate mesh data");
347+		return MGMESH_INVALID;
348+	}
349+
350+	memcpy(mesh->vtc, desc->vtc, desc->vtcc * sizeof(*mesh->vtc));
351+	memcpy(mesh->idc, desc->idc, desc->idcc * sizeof(*mesh->idc));
352+	mesh->ic = desc->idcc;
353+	mesh->used = 1;
354+
355+	return id;
356+}
357+
358+void renderer_backend_mesh_draw(MGMesh mesh, MGTexture texture, const MGMat4* mvp)
359+{
360+	RendererMesh* rmesh;
361+	RendererTexture* rtexture;
362+	u32 tri;
363+
364+	if (!ren.pass || !mvp)
365+		return;
366+
367+	if (mesh == MGMESH_INVALID || mesh >= MAX_MESHES)
368+		return;
369+
370+	if (texture == MGTEXTURE_INVALID || texture >= MAX_TEXTURES)
371+		return;
372+
373+	rmesh = &ren.meshes[mesh];
374+	rtexture = &ren.textures[texture];
375+	if (!rmesh->used || !rtexture->used)
376+		return;
377+
378+	ren.active_mesh = rmesh;
379+	ren.active_texture = rtexture;
380+
381+	for (tri = 0; tri < rmesh->ic; tri += 3) {
382+		ClipVertex ca;
383+		ClipVertex cb;
384+		ClipVertex cc;
385+		S3L_Vec4 sa;
386+		S3L_Vec4 sb;
387+		S3L_Vec4 sc;
388+
389+		ca = transform_vertex(mvp, &rmesh->vtc[rmesh->idc[tri + 0]]);
390+		cb = transform_vertex(mvp, &rmesh->vtc[rmesh->idc[tri + 1]]);
391+		cc = transform_vertex(mvp, &rmesh->vtc[rmesh->idc[tri + 2]]);
392+
393+		if (triangle_outside(&ca, &cb, &cc))
394+			continue;
395+		if (!triangle_front_facing(&ca, &cb, &cc))
396+			continue;
397+
398+		sa = clip_to_screen(&ca);
399+		sb = clip_to_screen(&cb);
400+		sc = clip_to_screen(&cc);
401+
402+		S3L_drawTriangle(sa, sb, sc, 0, (S3L_Index)(tri / 3));
403+	}
404+
405+	ren.active_mesh = NULL;
406+	ren.active_texture = NULL;
407+}
408+
409+void renderer_backend_mesh_destroy(MGMesh id)
410+{
411+	RendererMesh* mesh;
412+
413+	if (id == MGMESH_INVALID || id >= MAX_MESHES)
414+		return;
415+
416+	mesh = &ren.meshes[id];
417+	if (!mesh->used)
418+		return;
419+
420+	free(mesh->idc);
421+	free(mesh->vtc);
422+	*mesh = (RendererMesh){0};
423+}
424+
425+MGTexture renderer_backend_texture_load(const char* path)
426+{
427+	RendererTexture* texture;
428+	int width;
429+	int height;
430+	int channels;
431+	u32 id;
432+
433+	if (!path)
434+		return MGTEXTURE_INVALID;
435+
436+	/* find free texture slot */
437+	for (id = 1; id < MAX_TEXTURES; id++) {
438+		if (!ren.textures[id].used)
439+			break;
440+	}
441+
442+	if (id == MAX_TEXTURES) {
443+		mg_log(LOG_ERR, "Maximum texture limit reached");
444+		return MGTEXTURE_INVALID;
445+	}
446+
447+	texture = &ren.textures[id];
448+	texture->pixels = stbi_load(path, &width, &height, &channels, 4);
449+	if (!texture->pixels) {
450+		mg_log(LOG_ERR, "Failed to load texture: %s", path);
451+		return MGTEXTURE_INVALID;
452+	}
453+
454+	texture->width = width;
455+	texture->height = height;
456+	texture->used = 1;
457+
458+	return id;
459+}
460+
461+void renderer_backend_texture_destroy(MGTexture id)
462+{
463+	RendererTexture* texture;
464+
465+	if (id == MGTEXTURE_INVALID || id >= MAX_TEXTURES)
466+		return;
467+
468+	texture = &ren.textures[id];
469+	if (!texture->used)
470+		return;
471+
472+	stbi_image_free(texture->pixels);
473+	*texture = (RendererTexture){0};
474+}
+434, -0
  1@@ -0,0 +1,434 @@
  2+#include <sokol_gfx.h>
  3+#include <stb_image_c89.h>
  4+
  5+#include "util.h"
  6+#include "types.h"
  7+#include "platform/platform.h"
  8+#include "renderer/renderer.h"
  9+#include "renderer_backend.h"
 10+
 11+#define MAX_MESHES (512) /* TODO; random value ig */
 12+#define MAX_TEXTURES (512) /* TODO; random value ig */
 13+
 14+typedef struct {
 15+	sg_buffer vb; /* vertex buffer */
 16+	sg_buffer ib; /* index buffer */
 17+	u32       ic; /* index count */
 18+	i8        used;
 19+} RendererMesh;
 20+
 21+typedef struct {
 22+	sg_image image;
 23+	sg_view  view;
 24+	i8       used;
 25+} RendererTexture;
 26+
 27+typedef struct {
 28+	MGContext* ctx;
 29+	i8         pass; /* basically a bool; in
 30+	                    render pass or not */
 31+
 32+	sg_shader   shader;
 33+	sg_pipeline pipeline;
 34+	sg_sampler  sampler;
 35+
 36+	RendererMesh    meshes[MAX_MESHES];
 37+	RendererTexture textures[MAX_MESHES];
 38+} RendererState;
 39+
 40+/* create the shader and render pipeline */
 41+static void create_pipelines(void);
 42+
 43+/* create texture sampler */
 44+static void create_sampler(void);
 45+
 46+/* logging function for passing to sokol  */
 47+static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln, const char* file, void* user);
 48+
 49+static RendererState ren;
 50+/* TODO: textures */
 51+static const char* vsdr =
 52+"#version 410\n"
 53+"layout(location=0) in vec3 position;\n"
 54+"layout(location=1) in vec2 texcoord0;\n"
 55+"layout(location=2) in vec4 color0;\n"
 56+"\n"
 57+"uniform mat4 mvp;\n"
 58+"\n"
 59+"out vec2 uv;\n"
 60+"out vec4 colour;\n"
 61+"\n"
 62+"void main(void)\n"
 63+"{\n"
 64+"    gl_Position = mvp * vec4(position, 1.0);\n"
 65+"    uv = texcoord0;\n"
 66+"    colour = color0;\n"
 67+"}\n";
 68+static const char* fsdr =
 69+"#version 410\n"
 70+"in vec2 uv;\n"
 71+"in vec4 colour;\n"
 72+"\n"
 73+"uniform sampler2D tex;\n"
 74+"\n"
 75+"out vec4 frag_colour;\n"
 76+"\n"
 77+"void main(void)\n"
 78+"{\n"
 79+"    frag_colour = texture(tex, uv);\n"
 80+"}\n";
 81+
 82+static void create_pipelines(void)
 83+{
 84+	sg_shader_desc shader_desc = {0};
 85+	sg_pipeline_desc pipeline_desc = {0};
 86+
 87+	/* shader descriptor */
 88+	shader_desc.vertex_func.source = vsdr;
 89+	shader_desc.fragment_func.source = fsdr;
 90+	shader_desc.attrs[0].glsl_name = "position";
 91+	shader_desc.attrs[1].glsl_name = "texcoord0";
 92+	shader_desc.attrs[2].glsl_name = "color0";
 93+	shader_desc.uniform_blocks[0].stage = SG_SHADERSTAGE_VERTEX;
 94+	shader_desc.uniform_blocks[0].size = sizeof(float) * 16; /* size of mvp matrix */
 95+	shader_desc.uniform_blocks[0].layout = SG_UNIFORMLAYOUT_STD140;
 96+	shader_desc.uniform_blocks[0] .glsl_uniforms[0].glsl_name = "mvp";
 97+	shader_desc.uniform_blocks[0] .glsl_uniforms[0].type = SG_UNIFORMTYPE_MAT4;
 98+
 99+	/* textures */
100+	shader_desc.views[0].texture.stage = SG_SHADERSTAGE_FRAGMENT;
101+	shader_desc.views[0].texture.image_type = SG_IMAGETYPE_2D;
102+	shader_desc.views[0].texture.sample_type = SG_IMAGESAMPLETYPE_FLOAT;
103+	shader_desc.samplers[0].stage = SG_SHADERSTAGE_FRAGMENT;
104+	shader_desc.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING;
105+	shader_desc.texture_sampler_pairs[0].stage = SG_SHADERSTAGE_FRAGMENT;
106+	shader_desc.texture_sampler_pairs[0].view_slot = 0;
107+	shader_desc.texture_sampler_pairs[0].sampler_slot = 0;
108+	shader_desc.texture_sampler_pairs[0].glsl_name = "tex";
109+
110+	ren.shader = sg_make_shader(&shader_desc);
111+	if (sg_query_shader_state(ren.shader) != SG_RESOURCESTATE_VALID)
112+		mg_die(MG_ERR_SHADER_CREATE, "Failed to create shader");
113+
114+	/* pipeline descriptor */
115+	pipeline_desc.shader = ren.shader;
116+	pipeline_desc.layout.buffers[0].stride = sizeof(MGVertex);
117+	pipeline_desc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT3;
118+	pipeline_desc.layout.attrs[1].format = SG_VERTEXFORMAT_FLOAT2;
119+	pipeline_desc.layout.attrs[2].format = SG_VERTEXFORMAT_UBYTE4N;
120+	pipeline_desc.index_type = SG_INDEXTYPE_UINT16;
121+	pipeline_desc.depth.compare = SG_COMPAREFUNC_LESS_EQUAL;
122+	pipeline_desc.depth.write_enabled = true;
123+	pipeline_desc.cull_mode = SG_CULLMODE_BACK;
124+	pipeline_desc.face_winding = SG_FACEWINDING_CCW;
125+
126+	ren.pipeline = sg_make_pipeline(&pipeline_desc);
127+	if (sg_query_pipeline_state(ren.pipeline) != SG_RESOURCESTATE_VALID)
128+		mg_die(MG_ERR_PIPELINE_CREATE, "Failed to create pipeline");
129+}
130+
131+static void create_sampler(void)
132+{
133+	sg_sampler_desc desc = {0};
134+
135+	desc.min_filter = SG_FILTER_NEAREST;
136+	desc.mag_filter = SG_FILTER_NEAREST;
137+	desc.wrap_u = SG_WRAP_REPEAT;
138+	desc.wrap_v = SG_WRAP_REPEAT;
139+
140+	ren.sampler = sg_make_sampler(&desc);
141+
142+	if (sg_query_sampler_state(ren.sampler) != SG_RESOURCESTATE_VALID)
143+		mg_die(MG_ERR_SAMPLER_CREATE, "Failed to create sampler");
144+}
145+
146+static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln, const char* file, void* user)
147+{
148+	LogType type;
149+	(void)item;
150+	(void)user;
151+
152+	type = level <= 1 ? LOG_ERR : LOG_DBG;
153+
154+	mg_log(
155+		type, "%s: %s [%s:%u]",
156+		tag ? tag : "Sokol",
157+		msg ? msg : "No message",
158+		file ? file : "Unknown",
159+		ln
160+	);
161+}
162+
163+void renderer_backend_create(MGContext* ctx)
164+{
165+	sg_desc desc = {0};
166+
167+	ren.ctx = ctx;
168+	ren.pass = 0;
169+
170+	desc.environment.defaults.color_format = SG_PIXELFORMAT_RGBA8;
171+	desc.environment.defaults.depth_format = SG_PIXELFORMAT_DEPTH;
172+	desc.environment.defaults.sample_count = 1;
173+	desc.logger.func = sokol_log;
174+
175+	sg_setup(&desc);
176+
177+	if (!sg_isvalid())
178+		mg_die(MG_ERR_REN_CREATE, "Failed to initialise renderer");
179+
180+	create_pipelines();
181+	create_sampler();
182+}
183+
184+void renderer_backend_begin(void)
185+{
186+	sg_pass pass = {0};
187+	i32 width;
188+	i32 height;
189+
190+	ren.pass = 0;
191+
192+	platform_get_drawable_size(ren.ctx, &width, &height);
193+	/* (minimized window?) no need to draw */
194+	if (width <= 0 || height <= 0)
195+		return;
196+
197+	pass.action.colors[0].load_action = SG_LOADACTION_CLEAR;
198+	pass.action.colors[0].clear_value.r = 0.90f;
199+	pass.action.colors[0].clear_value.g = 0.80f;
200+	pass.action.colors[0].clear_value.b = 0.70f;
201+	pass.action.colors[0].clear_value.a = 1.00f;
202+
203+	pass.action.depth.load_action = SG_LOADACTION_CLEAR;
204+	pass.action.depth.clear_value = 1.0f;
205+
206+	/* swapchain descriptor */
207+	pass.swapchain.width = width;
208+	pass.swapchain.height = height;
209+	pass.swapchain.sample_count = 1;
210+	pass.swapchain.color_format = SG_PIXELFORMAT_RGBA8;
211+	pass.swapchain.depth_format = SG_PIXELFORMAT_DEPTH;
212+
213+	pass.swapchain.gl.framebuffer = 0;
214+
215+	sg_begin_pass(&pass);
216+	ren.pass = 1;
217+}
218+
219+void renderer_backend_end(void)
220+{
221+	if (!ren.pass)
222+		return;
223+
224+	sg_end_pass();
225+	sg_commit();
226+
227+	ren.pass = 0;
228+}
229+
230+void renderer_backend_destroy(void)
231+{
232+	u32 id;
233+
234+	/* the values could be different in future so
235+	   probably should keep them separate */
236+	for (id = 1; id < MAX_MESHES; id++)
237+		renderer_backend_mesh_destroy(id);
238+	for (id = 1; id < MAX_TEXTURES; id++)
239+		renderer_backend_texture_destroy(id);
240+
241+	sg_destroy_sampler(ren.sampler);
242+	sg_destroy_pipeline(ren.pipeline);
243+	sg_destroy_shader(ren.shader);
244+
245+	if (sg_isvalid())
246+		sg_shutdown();
247+
248+	ren.ctx = NULL;
249+	ren.pass = 0;
250+}
251+
252+MGMesh renderer_backend_mesh_create(const MGMeshDesc* desc)
253+{
254+	sg_buffer_desc buffer_desc = {0};
255+	RendererMesh* mesh;
256+	u32 id;
257+
258+	if (!desc || !desc->vtc || !desc->idc || desc->vtcc == 0 || desc->idcc == 0)
259+		return MGMESH_INVALID;
260+
261+	/* find free mesh slot */
262+	for (id = 1; id < MAX_MESHES; id++) {
263+		if (!ren.meshes[id].used)
264+			break;
265+	}
266+
267+	if (id == MAX_MESHES) {
268+		mg_log(LOG_ERR, "Maximum mesh limit reached");
269+		return MGMESH_INVALID;
270+	}
271+
272+	/* vertex buffer */
273+	mesh = &ren.meshes[id];
274+	buffer_desc.data.ptr = desc->vtc;
275+	buffer_desc.data.size = desc->vtcc * sizeof(MGVertex);
276+	mesh->vb = sg_make_buffer(&buffer_desc);
277+
278+	/* index buffer */
279+	buffer_desc = (sg_buffer_desc){0};
280+	buffer_desc.usage.vertex_buffer = false;
281+	buffer_desc.usage.index_buffer = true;
282+	buffer_desc.usage.immutable = true;
283+	buffer_desc.data.ptr = desc->idc;
284+	buffer_desc.data.size = desc->idcc * sizeof(u16);
285+	mesh->ib = sg_make_buffer(&buffer_desc);
286+
287+	i8 valid = sg_query_buffer_state(mesh->vb) == SG_RESOURCESTATE_VALID &&
288+	           sg_query_buffer_state(mesh->ib) == SG_RESOURCESTATE_VALID;
289+	if (!valid) {
290+		if (sg_query_buffer_state(mesh->vb) == SG_RESOURCESTATE_VALID)
291+			sg_destroy_buffer(mesh->vb);
292+		if (sg_query_buffer_state(mesh->ib) == SG_RESOURCESTATE_VALID)
293+			sg_destroy_buffer(mesh->ib);
294+
295+		*mesh = (RendererMesh){0};
296+		mg_log(LOG_ERR, "Failed to create mesh buffers");
297+		return MGMESH_INVALID;
298+	}
299+
300+	mesh->ic = desc->idcc;
301+	mesh->used = 1;
302+	return id;
303+}
304+
305+void renderer_backend_mesh_draw(MGMesh mesh, MGTexture texture, const MGMat4* mvp)
306+{
307+	RendererMesh* rmesh;
308+	RendererTexture* rtexture;
309+	sg_bindings bindings = {0};
310+	sg_range uniforms;
311+
312+	if (!ren.pass || !mvp)
313+		return;
314+
315+	if (mesh == MGMESH_INVALID || mesh >= MAX_MESHES)
316+		return;
317+
318+	if (texture == MGTEXTURE_INVALID || texture >= MAX_TEXTURES)
319+		return;
320+
321+	rmesh = &ren.meshes[mesh];
322+	rtexture = &ren.textures[texture];
323+	if (!rmesh->used || !rtexture->used)
324+		return;
325+
326+	bindings.vertex_buffers[0] = rmesh->vb;
327+	bindings.index_buffer = rmesh->ib;
328+	bindings.views[0] = rtexture->view;
329+	bindings.samplers[0] = ren.sampler;
330+
331+	uniforms.ptr = &mvp->Elements[0][0];
332+	uniforms.size = sizeof(float) * 16;
333+
334+	sg_apply_pipeline(ren.pipeline);
335+	sg_apply_bindings(&bindings);
336+	sg_apply_uniforms(0, &uniforms);
337+
338+	sg_draw(0, rmesh->ic, 1);
339+}
340+
341+void renderer_backend_mesh_destroy(MGMesh id)
342+{
343+	RendererMesh* mesh;
344+
345+	if (id == MGMESH_INVALID || id >= MAX_MESHES)
346+		return;
347+
348+	mesh = &ren.meshes[id];
349+	if (!mesh->used)
350+		return;
351+
352+	sg_destroy_buffer(mesh->ib);
353+	sg_destroy_buffer(mesh->vb);
354+
355+	*mesh = (RendererMesh){0};
356+}
357+
358+MGTexture renderer_backend_texture_load(const char* path)
359+{
360+	sg_image_desc image_desc = {0};
361+	sg_view_desc view_desc = {0};
362+	RendererTexture* texture;
363+	stbi_uc* pixels;
364+	int width;
365+	int height;
366+	int channels;
367+	u32 id;
368+
369+	if (!path)
370+		return MGTEXTURE_INVALID;
371+
372+	/* find free texture slot */
373+	for (id = 1; id < MAX_TEXTURES; id++) {
374+		if (!ren.textures[id].used)
375+			break;
376+	}
377+
378+	if (id == MAX_TEXTURES) {
379+		mg_log(LOG_ERR, "Maximum texture limit reached");
380+		return MGTEXTURE_INVALID;
381+	}
382+
383+	texture = &ren.textures[id];
384+	pixels = stbi_load(path, &width, &height, &channels, 4);
385+	if (!pixels) {
386+		mg_log(LOG_ERR, "Failed to load texture: %s", path);
387+		return MGTEXTURE_INVALID;
388+	}
389+
390+	image_desc.width = width;
391+	image_desc.height = height;
392+	image_desc.pixel_format = SG_PIXELFORMAT_RGBA8;
393+	image_desc.data.mip_levels[0].ptr = pixels;
394+	image_desc.data.mip_levels[0].size =width * height * 4;
395+
396+	texture->image = sg_make_image(&image_desc);
397+	stbi_image_free(pixels);
398+	if (sg_query_image_state(texture->image) != SG_RESOURCESTATE_VALID) {
399+		*texture = (RendererTexture){0};
400+		mg_log(LOG_ERR, "Failed to create texture: %s", path);
401+		return MGTEXTURE_INVALID;
402+	}
403+
404+	view_desc.texture.image = texture->image;
405+	texture->view = sg_make_view(&view_desc);
406+
407+	if (sg_query_view_state(texture->view) != SG_RESOURCESTATE_VALID) {
408+		sg_destroy_image(texture->image);
409+		*texture = (RendererTexture){0};
410+		mg_log(LOG_ERR, "Failed to create texture view: %s", path);
411+		return MGTEXTURE_INVALID;
412+	}
413+
414+	texture->used = 1;
415+
416+	return id;
417+}
418+
419+void renderer_backend_texture_destroy(MGTexture id)
420+{
421+	RendererTexture* texture;
422+
423+	if (id == MGTEXTURE_INVALID || id >= MAX_TEXTURES)
424+		return;
425+
426+	texture = &ren.textures[id];
427+
428+	if (!texture->used)
429+		return;
430+
431+	sg_destroy_view(texture->view);
432+	sg_destroy_image(texture->image);
433+
434+	*texture = (RendererTexture){0};
435+}
+2998, -0
   1@@ -0,0 +1,2998 @@
   2+#ifndef SMALL3DLIB_H
   3+#define SMALL3DLIB_H
   4+
   5+/*
   6+  Simple realtime 3D software rasterization renderer. It is fast, focused on
   7+  resource-limited computers, located in a single C header file, with no
   8+  dependencies, using only 32bit integer arithmetics.
   9+
  10+  author: Miloslav Ciz
  11+  license: CC0 1.0 (public domain)
  12+           found at https://creativecommons.org/publicdomain/zero/1.0/
  13+           + additional waiver of all IP
  14+  version: 0.905d
  15+
  16+  Before including the library, define S3L_PIXEL_FUNCTION to the name of the
  17+  function you'll be using to draw single pixels (this function will be called
  18+  by the library to render the frames). Also either init S3L_resolutionX and
  19+  S3L_resolutionY or define S3L_RESOLUTION_X and S3L_RESOLUTION_Y.
  20+
  21+  You'll also need to decide what rendering strategy and other settings you
  22+  want to use, depending on your specific usecase. You may want to use a
  23+  z-buffer (full or reduced, S3L_Z_BUFFER), sorted-drawing (S3L_SORT), or even
  24+  none of these. See the description of the options in this file.
  25+
  26+  The rendering itself is done with S3L_drawScene, usually preceded by
  27+  S3L_newFrame (for clearing zBuffer etc.).
  28+
  29+  The library is meant to be used in not so huge programs that use single
  30+  translation unit and so includes both declarations and implementation at once.
  31+  If you for some reason use multiple translation units (which include the
  32+  library), you'll have to handle this yourself (e.g. create a wrapper, manually
  33+  split the library into .c and .h etc.).
  34+
  35+  --------------------
  36+
  37+  This work's goal is to never be encumbered by any exclusive intellectual
  38+  property rights. The work is therefore provided under CC0 1.0 + additional
  39+  WAIVER OF ALL INTELLECTUAL PROPERTY RIGHTS that waives the rest of
  40+  intellectual property rights not already waived by CC0 1.0. The WAIVER OF ALL
  41+  INTELLECTUAL PROPERTY RGHTS is as follows:
  42+
  43+  Each contributor to this work agrees that they waive any exclusive rights,
  44+  including but not limited to copyright, patents, trademark, trade dress,
  45+  industrial design, plant varieties and trade secrets, to any and all ideas,
  46+  concepts, processes, discoveries, improvements and inventions conceived,
  47+  discovered, made, designed, researched or developed by the contributor either
  48+  solely or jointly with others, which relate to this work or result from this
  49+  work. Should any waiver of such right be judged legally invalid or
  50+  ineffective under applicable law, the contributor hereby grants to each
  51+  affected person a royalty-free, non transferable, non sublicensable, non
  52+  exclusive, irrevocable and unconditional license to this right.
  53+
  54+  --------------------
  55+
  56+  CONVENTIONS:
  57+
  58+  This library should never draw pixels outside the specified screen
  59+  boundaries, so you don't have to check this (that would cost CPU time)!
  60+
  61+  You can safely assume that triangles are rasterized one by one and from top
  62+  down, left to right (so you can utilize e.g. various caches), and if sorting
  63+  is disabled the order of rasterization will be that specified in the scene
  64+  structure and model arrays (of course, some triangles and models may be
  65+  skipped due to culling etc.).
  66+
  67+  Angles are in S3L_Units, a full angle (2 pi) is S3L_FRACTIONS_PER_UNITs.
  68+
  69+  We use row vectors.
  70+
  71+  In 3D space, a left-handed coord. system is used. One spatial unit is split
  72+  into S3L_FRACTIONS_PER_UNITs fractions (fixed point arithmetic).
  73+
  74+     y ^
  75+       |   _ 
  76+       |   /| z
  77+       |  /
  78+       | /
  79+  [0,0,0]-------> x
  80+
  81+  Untransformed camera is placed at [0,0,0], looking forward along +z axis. The
  82+  projection plane is centered at [0,0,0], stretrinch from
  83+  -S3L_FRACTIONS_PER_UNIT to S3L_FRACTIONS_PER_UNIT horizontally (x),
  84+  vertical size (y) depends on the aspect ratio (S3L_RESOLUTION_X and
  85+  S3L_RESOLUTION_Y). Camera FOV is defined by focal length in S3L_Units.
  86+
  87+  Rotations use Euler angles and are generally in the extrinsic Euler angles in
  88+  ZXY order (by Z, then by X, then by Y). Positive rotation about an axis
  89+  rotates CW (clock-wise) when looking in the direction of the axis.
  90+
  91+  Coordinates of pixels on the screen start at the top left, from [0,0].
  92+
  93+  There is NO subpixel accuracy (screen coordinates are only integer).
  94+
  95+  Triangle rasterization rules are these (mostly same as OpenGL, D3D etc.):
  96+
  97+  - Let's define:
  98+    - left side:
  99+      - not exactly horizontal, and on the left side of triangle
 100+      - exactly horizontal and above the topmost
 101+      (in other words: its normal points at least a little to the left or
 102+       completely up)
 103+    - right side: not left side
 104+  - Pixel centers are at integer coordinates and triangle for drawing are
 105+    specified with integer coordinates of pixel centers.
 106+  - A pixel is rasterized:
 107+    - if its center is inside the triangle OR
 108+    - if its center is exactly on the triangle side which is left and at the
 109+      same time is not on the side that's right (case of a triangle that's on
 110+      a single line) OR
 111+    - if its center is exactly on the triangle corner of sides neither of which
 112+      is right.
 113+
 114+  These rules imply among others:
 115+
 116+  - Adjacent triangles don't have any overlapping pixels, nor gaps between.
 117+  - Triangles of points that lie on a single line are NOT rasterized.
 118+  - A single "long" triangle CAN be rasterized as isolated islands of pixels.
 119+  - Transforming (e.g. mirroring, rotating by 90 degrees etc.) a result of
 120+    rasterizing triangle A is NOT generally equal to applying the same
 121+    transformation to triangle A first and then rasterizing it. Even the number
 122+    of rasterized pixels is usually different.
 123+  - If specifying a triangle with integer coordinates (which we are), then:
 124+    - The bottom-most corner (or side) of a triangle is never rasterized
 125+      (because it is connected to a right side).
 126+    - The top-most corner can only be rasterized on completely horizontal side
 127+      (otherwise it is connected to a right side).
 128+    - Vertically middle corner is rasterized if and only if it is on the left
 129+      of the triangle and at the same time is also not the bottom-most corner.
 130+*/
 131+
 132+#include <stdint.h>
 133+
 134+#ifdef S3L_RESOLUTION_X
 135+  #ifdef S3L_RESOLUTION_Y
 136+    #define S3L_MAX_PIXELS (S3L_RESOLUTION_X * S3L_RESOLUTION_Y)
 137+  #endif
 138+#endif
 139+
 140+#ifndef S3L_RESOLUTION_X
 141+  #ifndef S3L_MAX_PIXELS
 142+    #error Dynamic resolution set (S3L_RESOLUTION_X not defined), but\
 143+           S3L_MAX_PIXELS not defined!
 144+  #endif
 145+
 146+  uint16_t S3L_resolutionX = 512; /**< If a static resolution is not set with
 147+                                       S3L_RESOLUTION_X, this variable can be
 148+                                       used to change X resolution at runtime,
 149+                                       in which case S3L_MAX_PIXELS has to be
 150+                                       defined (to allocate zBuffer etc.)! */
 151+  #define S3L_RESOLUTION_X S3L_resolutionX
 152+#endif
 153+
 154+#ifndef S3L_RESOLUTION_Y
 155+  #ifndef S3L_MAX_PIXELS
 156+    #error Dynamic resolution set (S3L_RESOLUTION_Y not defined), but\
 157+           S3L_MAX_PIXELS not defined!
 158+  #endif
 159+
 160+  uint16_t S3L_resolutionY = 512; /**< Same as S3L_resolutionX, but for Y
 161+                                       resolution. */
 162+  #define S3L_RESOLUTION_Y S3L_resolutionY
 163+#endif
 164+
 165+#ifndef S3L_USE_WIDER_TYPES
 166+  /** If true, the library will use wider data types which will largely supress
 167+  many rendering bugs and imprecisions happening due to overflows, but this will
 168+  also consumer more RAM and may potentially be slower on computers with smaller
 169+  native integer. */
 170+  #define S3L_USE_WIDER_TYPES 0
 171+#endif
 172+
 173+#ifndef S3L_SIN_METHOD
 174+  /** Says which method should be used for computing sin/cos functions, possible
 175+  values: 0 (lookup table, takes more program memory), 1 (Bhaskara's
 176+  approximation, slower). This may cause the trigonometric functions give
 177+  slightly different results. */
 178+  #define S3L_SIN_METHOD 0
 179+#endif
 180+
 181+/** Units of measurement in 3D space. There is S3L_FRACTIONS_PER_UNIT in one
 182+spatial unit. By dividing the unit into fractions we effectively achieve a
 183+fixed point arithmetic. The number of fractions is a constant that serves as
 184+1.0 in floating point arithmetic (normalization etc.). */
 185+
 186+typedef
 187+#if S3L_USE_WIDER_TYPES
 188+  int64_t    
 189+#else
 190+  int32_t 
 191+#endif
 192+  S3L_Unit;
 193+ 
 194+/** How many fractions a spatial unit is split into, i.e. this is the fixed
 195+point scaling. This is NOT SUPPOSED TO BE REDEFINED, so rather don't do it
 196+(otherwise things may overflow etc.). */
 197+#define S3L_FRACTIONS_PER_UNIT 512
 198+#define S3L_F S3L_FRACTIONS_PER_UNIT
 199+
 200+typedef 
 201+#if S3L_USE_WIDER_TYPES
 202+  int32_t 
 203+#else
 204+  int16_t
 205+#endif
 206+  S3L_ScreenCoord;
 207+
 208+typedef 
 209+#if S3L_USE_WIDER_TYPES
 210+  uint32_t
 211+#else
 212+  uint16_t
 213+#endif
 214+  S3L_Index;
 215+
 216+#ifndef S3L_NEAR_CROSS_STRATEGY
 217+  /** Specifies how the library will handle triangles that partially cross the
 218+  near plane. These are problematic and require special handling. Possible
 219+  values:
 220+
 221+    0: Strictly cull any triangle crossing the near plane. This will make such
 222+       triangles disappear. This is good for performance or models viewed only
 223+       from at least small distance.
 224+    1: Forcefully push the vertices crossing near plane in front of it. This is
 225+       a cheap technique that can be good enough for displaying simple
 226+       environments on slow devices, but texturing and geometric artifacts/warps
 227+       will appear.
 228+    2: Geometrically correct the triangles crossing the near plane. This may
 229+       result in some triangles being subdivided into two and is a little more
 230+       expensive, but the results will be geometrically correct, even though
 231+       barycentric correction is not performed so texturing artifacts will
 232+       appear. Can be ideal with S3L_FLAT.
 233+    3: Perform both geometrical and barycentric correction of triangle crossing
 234+       the near plane. This is significantly more expensive but results in
 235+       correct rendering. */
 236+  #define S3L_NEAR_CROSS_STRATEGY 0
 237+#endif
 238+
 239+#ifndef S3L_FLAT
 240+  /** If on, disables computation of per-pixel values such as barycentric
 241+  coordinates and depth -- these will still be available but will be the same
 242+  for the whole triangle. This can be used to create flat-shaded renders and
 243+  will be a lot faster. With this option on you will probably want to use
 244+  sorting instead of z-buffer. */
 245+  #define S3L_FLAT 0
 246+#endif
 247+
 248+#if S3L_FLAT
 249+  #define S3L_COMPUTE_DEPTH 0
 250+  #define S3L_PERSPECTIVE_CORRECTION 0
 251+  // don't disable z-buffer, it makes sense to use it with no sorting
 252+#endif
 253+
 254+#ifndef S3L_PERSPECTIVE_CORRECTION
 255+  /** Specifies what type of perspective correction (PC) to use. Remember this
 256+  is an expensive operation! Possible values:
 257+ 
 258+  0: No perspective correction. Fastest, inaccurate from most angles.
 259+  1: Per-pixel perspective correction, accurate but very expensive.
 260+  2: Approximation (computing only at every S3L_PC_APPROX_LENGTHth pixel). 
 261+     Quake-style approximation is used, which only computes the PC after
 262+     S3L_PC_APPROX_LENGTH pixels. This is reasonably accurate and fast. */
 263+  #define S3L_PERSPECTIVE_CORRECTION 0 
 264+#endif
 265+
 266+#ifndef S3L_PC_APPROX_LENGTH
 267+  /** For S3L_PERSPECTIVE_CORRECTION == 2, this specifies after how many pixels
 268+  PC is recomputed. Should be a power of two to keep up the performance.
 269+  Smaller is nicer but slower. */
 270+  #define S3L_PC_APPROX_LENGTH 32
 271+#endif
 272+
 273+#if S3L_PERSPECTIVE_CORRECTION
 274+  #define S3L_COMPUTE_DEPTH 1 // PC inevitably computes depth, so enable it
 275+#endif
 276+
 277+#ifndef S3L_COMPUTE_DEPTH
 278+  /** Whether to compute depth for each pixel (fragment). Some other options
 279+  may turn this on automatically. If you don't need depth information, turning
 280+  this off can save performance. Depth will still be accessible in
 281+  S3L_PixelInfo, but will be constant -- equal to center point depth -- over
 282+  the whole triangle. */
 283+  #define S3L_COMPUTE_DEPTH 1
 284+#endif
 285+
 286+#ifndef S3L_Z_BUFFER
 287+  /** What type of z-buffer (depth buffer) to use for visibility determination.
 288+  Possible values:
 289+
 290+  0: Don't use z-buffer. This saves a lot of memory, but visibility checking
 291+     won't be pixel-accurate and has to mostly be done by other means (typically
 292+     sorting).
 293+  1: Use full z-buffer (of S3L_Units) for visibiltiy determination. This is the
 294+     most accurate option (and also a fast one), but requires a big amount of
 295+     memory.
 296+  2: Use reduced-size z-buffer (of bytes). This is fast and somewhat accurate,
 297+     but inaccuracies can occur and a considerable amount of memory is
 298+     needed. */
 299+  #define S3L_Z_BUFFER 0 
 300+#endif
 301+
 302+#ifndef S3L_REDUCED_Z_BUFFER_GRANULARITY
 303+  /** For S3L_Z_BUFFER == 2 this sets the reduced z-buffer granularity. */
 304+  #define S3L_REDUCED_Z_BUFFER_GRANULARITY 5
 305+#endif
 306+
 307+#ifndef S3L_STENCIL_BUFFER
 308+  /** Whether to use stencil buffer for drawing -- with this a pixel that would
 309+  be resterized over an already rasterized pixel (within a frame) will be
 310+  discarded. This is mostly for front-to-back sorted drawing. */
 311+  #define S3L_STENCIL_BUFFER 0 
 312+#endif
 313+
 314+#ifndef S3L_SORT
 315+  /** Defines how to sort triangles before drawing a frame. This can be used to
 316+  solve visibility in case z-buffer is not used, to prevent overwriting already
 317+  rasterized pixels, implement transparency etc. Note that for simplicity and
 318+  performance a relatively simple sorting is used which doesn't work completely
 319+  correctly, so mistakes can occur (even the best sorting wouldn't be able to
 320+  solve e.g. intersecting triangles). Note that sorting requires a bit of extra
 321+  memory -- an array of the triangles to sort -- the size of this array limits
 322+  the maximum number of triangles that can be drawn in a single frame
 323+  (S3L_MAX_TRIANGES_DRAWN). Possible values:
 324+
 325+  0: Don't sort triangles. This is fastest and doesn't use extra memory.
 326+  1: Sort triangles from back to front. This can in most cases solve visibility
 327+     without requiring almost any extra memory compared to z-buffer.
 328+  2: Sort triangles from front to back. This can be faster than back to front
 329+     because we prevent computing pixels that will be overwritten by nearer
 330+     ones, but we need a 1b stencil buffer for this (enable S3L_STENCIL_BUFFER),
 331+     so a bit more memory is needed. */
 332+  #define S3L_SORT 0
 333+#endif
 334+
 335+#ifndef S3L_MAX_TRIANGES_DRAWN
 336+  /** Maximum number of triangles that can be drawn in sorted modes. This
 337+  affects the size of the cache used for triangle sorting. */
 338+  #define S3L_MAX_TRIANGES_DRAWN 128 
 339+#endif
 340+
 341+#ifndef S3L_NEAR
 342+  /** Distance of the near clipping plane. Points in front or EXATLY ON this
 343+  plane are considered outside the frustum. This must be >= 0. */
 344+  #define S3L_NEAR (S3L_F / 4) 
 345+#endif
 346+
 347+#if S3L_NEAR <= 0
 348+#define S3L_NEAR 1 // Can't be <= 0.
 349+#endif
 350+
 351+#ifndef S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE
 352+  /** Affects the S3L_computeModelNormals function. See its description for
 353+  details. */
 354+  #define S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE 6
 355+#endif
 356+
 357+#ifndef S3L_FAST_LERP_QUALITY
 358+  /** Quality (scaling) of SOME (stepped) linear interpolations. 0 will most
 359+  likely be a tiny bit faster, but artifacts can occur for bigger tris, while
 360+  higher values can fix this -- in theory all higher values will have the same
 361+  speed (it is a shift value), but it mustn't be too high to prevent
 362+  overflow. */
 363+  #define S3L_FAST_LERP_QUALITY 11 
 364+#endif
 365+
 366+/** Vector that consists of four scalars and can represent homogenous
 367+  coordinates, but is generally also used as Vec3 and Vec2 for various
 368+  purposes. */
 369+typedef struct
 370+{
 371+  S3L_Unit x;
 372+  S3L_Unit y;
 373+  S3L_Unit z;
 374+  S3L_Unit w;
 375+} S3L_Vec4;
 376+
 377+#define S3L_logVec4(v)\
 378+  printf("Vec4: %d %d %d %d\n",((v).x),((v).y),((v).z),((v).w))
 379+
 380+static inline void S3L_vec4Init(S3L_Vec4 *v);
 381+static inline void S3L_vec4Set(S3L_Vec4 *v, S3L_Unit x, S3L_Unit y,
 382+  S3L_Unit z, S3L_Unit w);
 383+static inline void S3L_vec3Add(S3L_Vec4 *result, S3L_Vec4 added);
 384+static inline void S3L_vec3Sub(S3L_Vec4 *result, S3L_Vec4 substracted);
 385+S3L_Unit S3L_vec3Length(S3L_Vec4 v);
 386+
 387+/** Normalizes Vec3. Note that this function tries to normalize correctly
 388+  rather than quickly! If you need to normalize quickly, do it yourself in a
 389+  way that best fits your case. */
 390+void S3L_vec3Normalize(S3L_Vec4 *v);
 391+
 392+/** Like S3L_vec3Normalize, but doesn't perform any checks on the input vector,
 393+  which is faster, but can be very innacurate or overflowing. You are supposed
 394+  to provide a "nice" vector (not too big or small). */
 395+static inline void S3L_vec3NormalizeFast(S3L_Vec4 *v);
 396+
 397+S3L_Unit S3L_vec2Length(S3L_Vec4 v);
 398+void S3L_vec3Cross(S3L_Vec4 a, S3L_Vec4 b, S3L_Vec4 *result);
 399+static inline S3L_Unit S3L_vec3Dot(S3L_Vec4 a, S3L_Vec4 b);
 400+
 401+/** Computes a reflection direction (typically used e.g. for specular component
 402+  in Phong illumination). The input vectors must be normalized. The result will
 403+  be normalized as well. */
 404+void S3L_reflect(S3L_Vec4 toLight, S3L_Vec4 normal, S3L_Vec4 *result);
 405+
 406+/** Determines the winding of a triangle, returns 1 (CW, clockwise), -1 (CCW,
 407+  counterclockwise) or 0 (points lie on a single line). */
 408+static inline int8_t S3L_triangleWinding(
 409+  S3L_ScreenCoord x0,
 410+  S3L_ScreenCoord y0, 
 411+  S3L_ScreenCoord x1,
 412+  S3L_ScreenCoord y1,
 413+  S3L_ScreenCoord x2,
 414+  S3L_ScreenCoord y2);
 415+
 416+typedef struct
 417+{
 418+  S3L_Vec4 translation;
 419+  S3L_Vec4 rotation; /**< Euler angles. Rortation is applied in this order:
 420+                          1. z = by z (roll) CW looking along z+
 421+                          2. x = by x (pitch) CW looking along x+
 422+                          3. y = by y (yaw) CW looking along y+ */
 423+  S3L_Vec4 scale;
 424+} S3L_Transform3D;
 425+
 426+#define S3L_logTransform3D(t)\
 427+  printf("Transform3D: T = [%d %d %d], R = [%d %d %d], S = [%d %d %d]\n",\
 428+    (t).translation.x,(t).translation.y,(t).translation.z,\
 429+    (t).rotation.x,(t).rotation.y,(t).rotation.z,\
 430+    (t).scale.x,(t).scale.y,(t).scale.z)
 431+
 432+static inline void S3L_transform3DInit(S3L_Transform3D *t);
 433+
 434+void S3L_lookAt(S3L_Vec4 pointTo, S3L_Transform3D *t);
 435+
 436+void S3L_transform3DSet(
 437+  S3L_Unit tx,
 438+  S3L_Unit ty,
 439+  S3L_Unit tz,
 440+  S3L_Unit rx,
 441+  S3L_Unit ry,
 442+  S3L_Unit rz,
 443+  S3L_Unit sx,
 444+  S3L_Unit sy,
 445+  S3L_Unit sz,
 446+  S3L_Transform3D *t);
 447+
 448+/** Converts rotation transformation to three direction vectors of given length
 449+  (any one can be NULL, in which case it won't be computed). */
 450+void S3L_rotationToDirections(
 451+  S3L_Vec4 rotation,
 452+  S3L_Unit length,
 453+  S3L_Vec4 *forw, 
 454+  S3L_Vec4 *right,
 455+  S3L_Vec4 *up);
 456+
 457+/** 4x4 matrix, used mostly for 3D transforms. The indexing is this:
 458+    matrix[column][row]. */
 459+typedef S3L_Unit S3L_Mat4[4][4]; 
 460+
 461+#define S3L_logMat4(m)\
 462+  printf("Mat4:\n  %d %d %d %d\n  %d %d %d %d\n  %d %d %d %d\n  %d %d %d %d\n"\
 463+   ,(m)[0][0],(m)[1][0],(m)[2][0],(m)[3][0],\
 464+    (m)[0][1],(m)[1][1],(m)[2][1],(m)[3][1],\
 465+    (m)[0][2],(m)[1][2],(m)[2][2],(m)[3][2],\
 466+    (m)[0][3],(m)[1][3],(m)[2][3],(m)[3][3])
 467+
 468+/** Initializes a 4x4 matrix to identity. */
 469+static inline void S3L_mat4Init(S3L_Mat4 m);
 470+
 471+void S3L_mat4Copy(S3L_Mat4 src, S3L_Mat4 dst);
 472+
 473+void S3L_mat4Transpose(S3L_Mat4 m);
 474+
 475+void S3L_makeTranslationMat(
 476+  S3L_Unit offsetX,
 477+  S3L_Unit offsetY,
 478+  S3L_Unit offsetZ,
 479+  S3L_Mat4 m);
 480+
 481+/** Makes a scaling matrix. DON'T FORGET: scale of 1.0 is set with
 482+  S3L_FRACTIONS_PER_UNIT! */
 483+void S3L_makeScaleMatrix(
 484+  S3L_Unit scaleX,
 485+  S3L_Unit scaleY,
 486+  S3L_Unit scaleZ,
 487+  S3L_Mat4 m);
 488+
 489+/** Makes a matrix for rotation in the ZXY order. */
 490+void S3L_makeRotationMatrixZXY(
 491+  S3L_Unit byX,
 492+  S3L_Unit byY,
 493+  S3L_Unit byZ,
 494+  S3L_Mat4 m);
 495+
 496+void S3L_makeWorldMatrix(S3L_Transform3D worldTransform, S3L_Mat4 m);
 497+void S3L_makeCameraMatrix(S3L_Transform3D cameraTransform, S3L_Mat4 m);
 498+
 499+/** Multiplies a vector by a matrix with normalization by
 500+  S3L_FRACTIONS_PER_UNIT. Result is stored in the input vector. */
 501+void S3L_vec4Xmat4(S3L_Vec4 *v, S3L_Mat4 m);
 502+
 503+/** Same as S3L_vec4Xmat4 but faster, because this version doesn't compute the
 504+  W component of the result, which is usually not needed. */
 505+void S3L_vec3Xmat4(S3L_Vec4 *v, S3L_Mat4 m);
 506+
 507+/** Multiplies two matrices with normalization by S3L_FRACTIONS_PER_UNIT.
 508+  Result is stored in the first matrix. The result represents a transformation
 509+  that has the same effect as applying the transformation represented by m1 and
 510+  then m2 (in that order). */
 511+void S3L_mat4Xmat4(S3L_Mat4 m1, S3L_Mat4 m2);
 512+
 513+typedef struct
 514+{
 515+  S3L_Unit focalLength;       /**< Defines the field of view (FOV). 0 sets an
 516+                                   orthographics projection (scale is controlled
 517+                                   with camera's scale in its transform). */
 518+  S3L_Transform3D transform;
 519+} S3L_Camera;
 520+
 521+void S3L_cameraInit(S3L_Camera *camera);
 522+
 523+typedef struct
 524+{
 525+  uint8_t backfaceCulling;    /**< What backface culling to use. Possible
 526+                                   values:
 527+                                   - 0 none
 528+                                   - 1 clock-wise
 529+                                   - 2 counter clock-wise */
 530+  int8_t visible;             /**< Can be used to easily hide the model. */
 531+} S3L_DrawConfig;
 532+
 533+void S3L_drawConfigInit(S3L_DrawConfig *config);
 534+
 535+typedef struct
 536+{
 537+  const S3L_Unit *vertices;
 538+  S3L_Index vertexCount;
 539+  const S3L_Index *triangles;
 540+  S3L_Index triangleCount;
 541+  S3L_Transform3D transform;
 542+  S3L_Mat4 *customTransformMatrix; /**< This can be used to override the
 543+                                     transform (if != 0) with a custom
 544+                                     transform matrix, which is more
 545+                                     general. */
 546+  S3L_DrawConfig config;
 547+} S3L_Model3D;                ///< Represents a 3D model.
 548+
 549+void S3L_model3DInit(
 550+  const S3L_Unit *vertices,
 551+  S3L_Index vertexCount,
 552+  const S3L_Index *triangles,
 553+  S3L_Index triangleCount,
 554+  S3L_Model3D *model);
 555+
 556+typedef struct
 557+{
 558+  S3L_Model3D *models;
 559+  S3L_Index modelCount;
 560+  S3L_Camera camera;
 561+} S3L_Scene;                  ///< Represent the 3D scene to be rendered.
 562+
 563+void S3L_sceneInit(
 564+  S3L_Model3D *models,
 565+  S3L_Index modelCount,
 566+  S3L_Scene *scene);
 567+
 568+typedef struct
 569+{
 570+  S3L_ScreenCoord x;          ///< Screen X coordinate.
 571+  S3L_ScreenCoord y;          ///< Screen Y coordinate.
 572+
 573+  S3L_Unit barycentric[3]; /**< Barycentric coords correspond to the three
 574+                              vertices. These serve to locate the pixel on a
 575+                              triangle and interpolate values between its
 576+                              three points. Each one goes from 0 to
 577+                              S3L_FRACTIONS_PER_UNIT (including), but due to
 578+                              rounding error may fall outside this range (you
 579+                              can use S3L_correctBarycentricCoords to fix this
 580+                              for the price of some performance). The sum of
 581+                              the three coordinates will always be exactly
 582+                              S3L_FRACTIONS_PER_UNIT. */
 583+  S3L_Index modelIndex;    ///< Model index within the scene.
 584+  S3L_Index triangleIndex; ///< Triangle index within the model.
 585+  uint32_t triangleID;     /**< Unique ID of the triangle withing the whole
 586+                               scene. This can be used e.g. by a cache to
 587+                               quickly find out if a triangle has changed. */
 588+  S3L_Unit depth;         ///< Depth (only if depth is turned on).
 589+  S3L_Unit previousZ;     /**< Z-buffer value (not necessarily world depth in
 590+                               S3L_Units!) that was in the z-buffer on the
 591+                               pixels position before this pixel was
 592+                               rasterized. This can be used to set the value
 593+                               back, e.g. for transparency. */
 594+  S3L_ScreenCoord triangleSize[2]; /**< Rasterized triangle width and height,
 595+                              can be used e.g. for MIP mapping. */
 596+} S3L_PixelInfo;         /**< Used to pass the info about a rasterized pixel
 597+                              (fragment) to the user-defined drawing func. */
 598+
 599+static inline void S3L_pixelInfoInit(S3L_PixelInfo *p);
 600+
 601+/** Corrects barycentric coordinates so that they exactly meet the defined
 602+  conditions (each fall into <0,S3L_FRACTIONS_PER_UNIT>, sum =
 603+  S3L_FRACTIONS_PER_UNIT). Note that doing this per-pixel can slow the program
 604+  down significantly. */
 605+static inline void S3L_correctBarycentricCoords(S3L_Unit barycentric[3]);
 606+
 607+// general helper functions
 608+static inline S3L_Unit S3L_abs(S3L_Unit value);
 609+static inline S3L_Unit S3L_min(S3L_Unit v1, S3L_Unit v2);
 610+static inline S3L_Unit S3L_max(S3L_Unit v1, S3L_Unit v2);
 611+static inline S3L_Unit S3L_clamp(S3L_Unit v, S3L_Unit v1, S3L_Unit v2);
 612+static inline S3L_Unit S3L_wrap(S3L_Unit value, S3L_Unit mod);
 613+static inline S3L_Unit S3L_nonZero(S3L_Unit value);
 614+static inline S3L_Unit S3L_zeroClamp(S3L_Unit value);
 615+
 616+S3L_Unit S3L_sin(S3L_Unit x);
 617+S3L_Unit S3L_asin(S3L_Unit x);
 618+static inline S3L_Unit S3L_cos(S3L_Unit x);
 619+
 620+S3L_Unit S3L_vec3Length(S3L_Vec4 v);
 621+S3L_Unit S3L_sqrt(S3L_Unit value);
 622+
 623+/** Projects a single point from 3D space to the screen space (pixels), which
 624+  can be useful e.g. for drawing sprites. The w component of input and result
 625+  holds the point size. If this size is 0 in the result, the sprite is outside
 626+  the view. */
 627+void S3L_project3DPointToScreen(
 628+  S3L_Vec4 point,
 629+  S3L_Camera camera,
 630+  S3L_Vec4 *result);
 631+
 632+/** Computes a normalized normal of given triangle. */
 633+void S3L_triangleNormal(S3L_Vec4 t0, S3L_Vec4 t1, S3L_Vec4 t2,
 634+  S3L_Vec4 *n);
 635+
 636+/** Helper function for retrieving per-vertex indexed values from an array,
 637+  e.g. texturing (UV) coordinates. The 'indices' array contains three indices
 638+  for each triangle, each index pointing into 'values' array, which contains
 639+  the values, each one consisting of 'numComponents' components (e.g. 2 for
 640+  UV coordinates). The three values are retrieved into 'v0', 'v1' and 'v2'
 641+  vectors (into x, y, z and w, depending on 'numComponents'). This function is
 642+  meant to be used per-triangle (typically from a cache), NOT per-pixel, as it
 643+  is not as fast as possible! */
 644+void S3L_getIndexedTriangleValues(
 645+  S3L_Index triangleIndex,
 646+  const S3L_Index *indices,
 647+  const S3L_Unit *values,
 648+  uint8_t numComponents,
 649+  S3L_Vec4 *v0,
 650+  S3L_Vec4 *v1,
 651+  S3L_Vec4 *v2);
 652+
 653+/** Computes a normalized normal for every vertex of given model (this is
 654+  relatively slow and SHOUDN'T be done each frame). The dst array must have a
 655+  sufficient size preallocated! The size is: number of model vertices * 3 *
 656+  sizeof(S3L_Unit). Note that for advanced allowing sharp edges it is not
 657+  sufficient to have per-vertex normals, but must be per-triangle. This
 658+  function doesn't support this. 
 659+
 660+  The function computes a normal for each vertex by averaging normals of
 661+  the triangles containing the vertex. The maximum number of these triangle
 662+  normals that will be averaged is set with
 663+  S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE. */
 664+void S3L_computeModelNormals(S3L_Model3D model, S3L_Unit *dst,
 665+  int8_t transformNormals);
 666+
 667+/** Interpolated between two values, v1 and v2, in the same ratio as t is to
 668+  tMax. Does NOT prevent zero division. */
 669+static inline S3L_Unit S3L_interpolate(
 670+  S3L_Unit v1,
 671+  S3L_Unit v2,
 672+  S3L_Unit t,
 673+  S3L_Unit tMax);
 674+
 675+/** Same as S3L_interpolate but with v1 == 0. Should be faster. */
 676+static inline S3L_Unit S3L_interpolateFrom0(
 677+  S3L_Unit v2,
 678+  S3L_Unit t,
 679+  S3L_Unit tMax);
 680+
 681+/** Like S3L_interpolate, but uses a parameter that goes from 0 to
 682+  S3L_FRACTIONS_PER_UNIT - 1, which can be faster. */
 683+static inline S3L_Unit S3L_interpolateByUnit(
 684+  S3L_Unit v1,
 685+  S3L_Unit v2,
 686+  S3L_Unit t);
 687+
 688+/** Same as S3L_interpolateByUnit but with v1 == 0. Should be faster. */
 689+static inline S3L_Unit S3L_interpolateByUnitFrom0(
 690+  S3L_Unit v2,
 691+  S3L_Unit t);
 692+
 693+static inline S3L_Unit S3L_distanceManhattan(S3L_Vec4 a, S3L_Vec4 b);
 694+
 695+/** Returns a value interpolated between the three triangle vertices based on
 696+  barycentric coordinates. */
 697+static inline S3L_Unit S3L_interpolateBarycentric(
 698+  S3L_Unit value0,
 699+  S3L_Unit value1,
 700+  S3L_Unit value2,
 701+  S3L_Unit barycentric[3]);
 702+
 703+static inline void S3L_mapProjectionPlaneToScreen(
 704+  S3L_Vec4 point,
 705+  S3L_ScreenCoord *screenX,
 706+  S3L_ScreenCoord *screenY);
 707+
 708+/** Draws a triangle according to given config. The vertices are specified in
 709+  Screen Space space (pixels). If perspective correction is enabled, each
 710+  vertex has to have a depth (Z position in camera space) specified in the Z
 711+  component. */
 712+void S3L_drawTriangle(
 713+  S3L_Vec4 point0,
 714+  S3L_Vec4 point1,
 715+  S3L_Vec4 point2,
 716+  S3L_Index modelIndex,
 717+  S3L_Index triangleIndex);
 718+
 719+/** This should be called before rendering each frame. The function clears
 720+  buffers and does potentially other things needed for the frame. */
 721+void S3L_newFrame(void);
 722+
 723+void S3L_zBufferClear(void);
 724+void S3L_stencilBufferClear(void);
 725+
 726+/** Writes a value (not necessarily depth! depends on the format of z-buffer)
 727+  to z-buffer (if enabled). Does NOT check boundaries! */
 728+void S3L_zBufferWrite(S3L_ScreenCoord x, S3L_ScreenCoord y, S3L_Unit value);
 729+
 730+/** Reads a value (not necessarily depth! depends on the format of z-buffer)
 731+  from z-buffer (if enabled). Does NOT check boundaries! */
 732+S3L_Unit S3L_zBufferRead(S3L_ScreenCoord x, S3L_ScreenCoord y);
 733+
 734+static inline void S3L_rotate2DPoint(S3L_Unit *x, S3L_Unit *y, S3L_Unit angle);
 735+
 736+/** Predefined vertices of a cube to simply insert in an array. These come with
 737+    S3L_CUBE_TRIANGLES and S3L_CUBE_TEXCOORDS. */
 738+#define S3L_CUBE_VERTICES(m)\
 739+ /* 0 front, bottom, right */\
 740+ m/2, -m/2, -m/2,\
 741+ /* 1 front, bottom, left */\
 742+-m/2, -m/2, -m/2,\
 743+ /* 2 front, top,    right */\
 744+ m/2,  m/2, -m/2,\
 745+ /* 3 front, top,    left */\
 746+-m/2,  m/2, -m/2,\
 747+ /* 4 back,  bottom, right */\
 748+ m/2, -m/2,  m/2,\
 749+ /* 5 back,  bottom, left */\
 750+-m/2, -m/2,  m/2,\
 751+ /* 6 back,  top,    right */\
 752+ m/2,  m/2,  m/2,\
 753+ /* 7 back,  top,    left */\
 754+-m/2,  m/2,  m/2
 755+
 756+#define S3L_CUBE_VERTEX_COUNT 8
 757+
 758+/** Predefined triangle indices of a cube, to be used with S3L_CUBE_VERTICES
 759+    and S3L_CUBE_TEXCOORDS. */
 760+#define S3L_CUBE_TRIANGLES\
 761+  3, 0, 2, /* front  */\
 762+  1, 0, 3,\
 763+  0, 4, 2, /* right  */\
 764+  2, 4, 6,\
 765+  4, 5, 6, /* back   */\
 766+  7, 6, 5,\
 767+  3, 7, 1, /* left   */\
 768+  1, 7, 5,\
 769+  6, 3, 2, /* top    */\
 770+  7, 3, 6,\
 771+  1, 4, 0, /* bottom */\
 772+  5, 4, 1
 773+
 774+#define S3L_CUBE_TRIANGLE_COUNT 12
 775+
 776+/** Predefined texture coordinates of a cube, corresponding to triangles (NOT
 777+    vertices), to be used with S3L_CUBE_VERTICES and S3L_CUBE_TRIANGLES. */
 778+#define S3L_CUBE_TEXCOORDS(m)\
 779+  0,0,  m,m,  m,0,\
 780+  0,m,  m,m,  0,0,\
 781+  m,m,  m,0,  0,m,\
 782+  0,m,  m,0,  0,0,\
 783+  m,0,  0,0,  m,m,\
 784+  0,m,  m,m,  0,0,\
 785+  0,0,  0,m,  m,0,\
 786+  m,0,  0,m,  m,m,\
 787+  0,0,  m,m,  m,0,\
 788+  0,m,  m,m,  0,0,\
 789+  m,0,  0,m,  m,m,\
 790+  0,0,  0,m,  m,0
 791+
 792+//=============================================================================
 793+// privates
 794+
 795+#define S3L_UNUSED(what) (void)(what) ///< helper macro for unused vars
 796+
 797+#define S3L_HALF_RESOLUTION_X (S3L_RESOLUTION_X >> 1)
 798+#define S3L_HALF_RESOLUTION_Y (S3L_RESOLUTION_Y >> 1)
 799+
 800+#define S3L_PROJECTION_PLANE_HEIGHT\
 801+  ((S3L_RESOLUTION_Y * S3L_F * 2) / S3L_RESOLUTION_X)
 802+
 803+#if S3L_Z_BUFFER == 1
 804+  #define S3L_MAX_DEPTH 2147483647
 805+  S3L_Unit S3L_zBuffer[S3L_MAX_PIXELS];
 806+  #define S3L_zBufferFormat(depth) (depth)
 807+#elif S3L_Z_BUFFER == 2
 808+  #define S3L_MAX_DEPTH 255
 809+  uint8_t S3L_zBuffer[S3L_MAX_PIXELS];
 810+  #define S3L_zBufferFormat(depth)\
 811+    S3L_min(255,(depth) >> S3L_REDUCED_Z_BUFFER_GRANULARITY)
 812+#endif
 813+
 814+#if S3L_Z_BUFFER
 815+static inline int8_t S3L_zTest(
 816+  S3L_ScreenCoord x,
 817+  S3L_ScreenCoord y,
 818+  S3L_Unit depth)
 819+{
 820+  uint32_t index = y * S3L_RESOLUTION_X + x;
 821+
 822+  depth = S3L_zBufferFormat(depth);
 823+
 824+#if S3L_Z_BUFFER == 2
 825+  #define cmp <= /* For reduced z-buffer we need equality test, because
 826+                    otherwise pixels at the maximum depth (255) would never be
 827+                    drawn over the background (which also has the depth of
 828+                    255). */
 829+#else
 830+  #define cmp <  /* For normal z-buffer we leave out equality test to not waste
 831+                    time by drawing over already drawn pixls. */
 832+#endif
 833+
 834+  if (depth cmp S3L_zBuffer[index])
 835+  {
 836+    S3L_zBuffer[index] = depth;
 837+    return 1;
 838+  }
 839+
 840+#undef cmp
 841+
 842+  return 0;
 843+}
 844+#endif
 845+
 846+S3L_Unit S3L_zBufferRead(S3L_ScreenCoord x, S3L_ScreenCoord y)
 847+{
 848+#if S3L_Z_BUFFER
 849+  return S3L_zBuffer[y * S3L_RESOLUTION_X + x];
 850+#else
 851+  S3L_UNUSED(x);
 852+  S3L_UNUSED(y);
 853+
 854+  return 0;
 855+#endif
 856+}
 857+
 858+void S3L_zBufferWrite(S3L_ScreenCoord x, S3L_ScreenCoord y, S3L_Unit value)
 859+{
 860+#if S3L_Z_BUFFER
 861+  S3L_zBuffer[y * S3L_RESOLUTION_X + x] = value;
 862+#else
 863+  S3L_UNUSED(x);
 864+  S3L_UNUSED(y);
 865+  S3L_UNUSED(value);
 866+#endif
 867+}
 868+
 869+#if S3L_STENCIL_BUFFER
 870+  #define S3L_STENCIL_BUFFER_SIZE\
 871+    ((S3L_RESOLUTION_X * S3L_RESOLUTION_Y - 1) / 8 + 1)
 872+
 873+uint8_t S3L_stencilBuffer[S3L_STENCIL_BUFFER_SIZE];
 874+
 875+static inline int8_t S3L_stencilTest(
 876+  S3L_ScreenCoord x,
 877+  S3L_ScreenCoord y)
 878+{
 879+  uint32_t index = y * S3L_RESOLUTION_X + x;
 880+  uint32_t bit = (index & 0x00000007);
 881+  index = index >> 3;
 882+
 883+  uint8_t val = S3L_stencilBuffer[index];
 884+
 885+  if ((val >> bit) & 0x1)
 886+    return 0;
 887+  
 888+  S3L_stencilBuffer[index] = val | (0x1 << bit);
 889+
 890+  return 1;
 891+}
 892+#endif
 893+
 894+#define S3L_COMPUTE_LERP_DEPTH\
 895+  (S3L_COMPUTE_DEPTH && (S3L_PERSPECTIVE_CORRECTION == 0))
 896+
 897+#define S3L_SIN_TABLE_LENGTH 128
 898+
 899+#if S3L_SIN_METHOD == 0
 900+static const S3L_Unit S3L_sinTable[S3L_SIN_TABLE_LENGTH] =
 901+{
 902+  /* 511 was chosen here as a highest number that doesn't overflow during
 903+     compilation for S3L_F == 1024 */
 904+
 905+  (0*S3L_F)/511, (6*S3L_F)/511, 
 906+  (12*S3L_F)/511, (18*S3L_F)/511, 
 907+  (25*S3L_F)/511, (31*S3L_F)/511, 
 908+  (37*S3L_F)/511, (43*S3L_F)/511, 
 909+  (50*S3L_F)/511, (56*S3L_F)/511, 
 910+  (62*S3L_F)/511, (68*S3L_F)/511, 
 911+  (74*S3L_F)/511, (81*S3L_F)/511, 
 912+  (87*S3L_F)/511, (93*S3L_F)/511, 
 913+  (99*S3L_F)/511, (105*S3L_F)/511, 
 914+  (111*S3L_F)/511, (118*S3L_F)/511, 
 915+  (124*S3L_F)/511, (130*S3L_F)/511, 
 916+  (136*S3L_F)/511, (142*S3L_F)/511, 
 917+  (148*S3L_F)/511, (154*S3L_F)/511, 
 918+  (160*S3L_F)/511, (166*S3L_F)/511, 
 919+  (172*S3L_F)/511, (178*S3L_F)/511, 
 920+  (183*S3L_F)/511, (189*S3L_F)/511, 
 921+  (195*S3L_F)/511, (201*S3L_F)/511, 
 922+  (207*S3L_F)/511, (212*S3L_F)/511, 
 923+  (218*S3L_F)/511, (224*S3L_F)/511, 
 924+  (229*S3L_F)/511, (235*S3L_F)/511, 
 925+  (240*S3L_F)/511, (246*S3L_F)/511, 
 926+  (251*S3L_F)/511, (257*S3L_F)/511, 
 927+  (262*S3L_F)/511, (268*S3L_F)/511, 
 928+  (273*S3L_F)/511, (278*S3L_F)/511, 
 929+  (283*S3L_F)/511, (289*S3L_F)/511, 
 930+  (294*S3L_F)/511, (299*S3L_F)/511, 
 931+  (304*S3L_F)/511, (309*S3L_F)/511, 
 932+  (314*S3L_F)/511, (319*S3L_F)/511, 
 933+  (324*S3L_F)/511, (328*S3L_F)/511, 
 934+  (333*S3L_F)/511, (338*S3L_F)/511, 
 935+  (343*S3L_F)/511, (347*S3L_F)/511, 
 936+  (352*S3L_F)/511, (356*S3L_F)/511, 
 937+  (361*S3L_F)/511, (365*S3L_F)/511, 
 938+  (370*S3L_F)/511, (374*S3L_F)/511, 
 939+  (378*S3L_F)/511, (382*S3L_F)/511, 
 940+  (386*S3L_F)/511, (391*S3L_F)/511, 
 941+  (395*S3L_F)/511, (398*S3L_F)/511, 
 942+  (402*S3L_F)/511, (406*S3L_F)/511, 
 943+  (410*S3L_F)/511, (414*S3L_F)/511, 
 944+  (417*S3L_F)/511, (421*S3L_F)/511, 
 945+  (424*S3L_F)/511, (428*S3L_F)/511, 
 946+  (431*S3L_F)/511, (435*S3L_F)/511, 
 947+  (438*S3L_F)/511, (441*S3L_F)/511, 
 948+  (444*S3L_F)/511, (447*S3L_F)/511, 
 949+  (450*S3L_F)/511, (453*S3L_F)/511, 
 950+  (456*S3L_F)/511, (459*S3L_F)/511, 
 951+  (461*S3L_F)/511, (464*S3L_F)/511, 
 952+  (467*S3L_F)/511, (469*S3L_F)/511, 
 953+  (472*S3L_F)/511, (474*S3L_F)/511, 
 954+  (476*S3L_F)/511, (478*S3L_F)/511, 
 955+  (481*S3L_F)/511, (483*S3L_F)/511, 
 956+  (485*S3L_F)/511, (487*S3L_F)/511, 
 957+  (488*S3L_F)/511, (490*S3L_F)/511, 
 958+  (492*S3L_F)/511, (494*S3L_F)/511, 
 959+  (495*S3L_F)/511, (497*S3L_F)/511, 
 960+  (498*S3L_F)/511, (499*S3L_F)/511, 
 961+  (501*S3L_F)/511, (502*S3L_F)/511, 
 962+  (503*S3L_F)/511, (504*S3L_F)/511, 
 963+  (505*S3L_F)/511, (506*S3L_F)/511, 
 964+  (507*S3L_F)/511, (507*S3L_F)/511, 
 965+  (508*S3L_F)/511, (509*S3L_F)/511, 
 966+  (509*S3L_F)/511, (510*S3L_F)/511, 
 967+  (510*S3L_F)/511, (510*S3L_F)/511, 
 968+  (510*S3L_F)/511, (510*S3L_F)/511
 969+};
 970+#endif
 971+
 972+#define S3L_SIN_TABLE_UNIT_STEP\
 973+  (S3L_F / (S3L_SIN_TABLE_LENGTH * 4))
 974+
 975+void S3L_vec4Init(S3L_Vec4 *v)
 976+{
 977+  v->x = 0; v->y = 0; v->z = 0; v->w = S3L_F;
 978+}
 979+
 980+void S3L_vec4Set(S3L_Vec4 *v, S3L_Unit x, S3L_Unit y, S3L_Unit z, S3L_Unit w)
 981+{
 982+  v->x = x;
 983+  v->y = y;
 984+  v->z = z;
 985+  v->w = w;
 986+}
 987+
 988+void S3L_vec3Add(S3L_Vec4 *result, S3L_Vec4 added)
 989+{
 990+  result->x += added.x;
 991+  result->y += added.y;
 992+  result->z += added.z;
 993+}
 994+
 995+void S3L_vec3Sub(S3L_Vec4 *result, S3L_Vec4 substracted)
 996+{
 997+  result->x -= substracted.x;
 998+  result->y -= substracted.y;
 999+  result->z -= substracted.z;
1000+}
1001+
1002+void S3L_mat4Init(S3L_Mat4 m)
1003+{
1004+  #define M(x,y) m[x][y]
1005+  #define S S3L_F
1006+
1007+  M(0,0) = S; M(1,0) = 0; M(2,0) = 0; M(3,0) = 0; 
1008+  M(0,1) = 0; M(1,1) = S; M(2,1) = 0; M(3,1) = 0; 
1009+  M(0,2) = 0; M(1,2) = 0; M(2,2) = S; M(3,2) = 0; 
1010+  M(0,3) = 0; M(1,3) = 0; M(2,3) = 0; M(3,3) = S; 
1011+
1012+  #undef M
1013+  #undef S
1014+}
1015+
1016+void S3L_mat4Copy(S3L_Mat4 src, S3L_Mat4 dst)
1017+{
1018+  for (uint8_t j = 0; j < 4; ++j)
1019+    for (uint8_t i = 0; i < 4; ++i)
1020+      dst[i][j] = src[i][j];
1021+}
1022+
1023+S3L_Unit S3L_vec3Dot(S3L_Vec4 a, S3L_Vec4 b)
1024+{
1025+  return (a.x * b.x + a.y * b.y + a.z * b.z) / S3L_F;
1026+}
1027+
1028+void S3L_reflect(S3L_Vec4 toLight, S3L_Vec4 normal, S3L_Vec4 *result)
1029+{
1030+  S3L_Unit d = 2 * S3L_vec3Dot(toLight,normal);
1031+
1032+  result->x = (normal.x * d) / S3L_F - toLight.x;
1033+  result->y = (normal.y * d) / S3L_F - toLight.y;
1034+  result->z = (normal.z * d) / S3L_F - toLight.z;
1035+}
1036+
1037+void S3L_vec3Cross(S3L_Vec4 a, S3L_Vec4 b, S3L_Vec4 *result)
1038+{
1039+  result->x = a.y * b.z - a.z * b.y;
1040+  result->y = a.z * b.x - a.x * b.z;
1041+  result->z = a.x * b.y - a.y * b.x;
1042+}
1043+
1044+void S3L_triangleNormal(S3L_Vec4 t0, S3L_Vec4 t1, S3L_Vec4 t2, S3L_Vec4 *n)
1045+{
1046+  #define ANTI_OVERFLOW 32
1047+
1048+  t1.x = (t1.x - t0.x) / ANTI_OVERFLOW;
1049+  t1.y = (t1.y - t0.y) / ANTI_OVERFLOW;
1050+  t1.z = (t1.z - t0.z) / ANTI_OVERFLOW;
1051+
1052+  t2.x = (t2.x - t0.x) / ANTI_OVERFLOW;
1053+  t2.y = (t2.y - t0.y) / ANTI_OVERFLOW;
1054+  t2.z = (t2.z - t0.z) / ANTI_OVERFLOW;
1055+
1056+  #undef ANTI_OVERFLOW
1057+
1058+  S3L_vec3Cross(t1,t2,n);
1059+
1060+  S3L_vec3Normalize(n);
1061+}
1062+
1063+void S3L_getIndexedTriangleValues(
1064+  S3L_Index triangleIndex,
1065+  const S3L_Index *indices,
1066+  const S3L_Unit *values,
1067+  uint8_t numComponents,
1068+  S3L_Vec4 *v0,
1069+  S3L_Vec4 *v1,
1070+  S3L_Vec4 *v2)
1071+{
1072+  uint32_t i0, i1;
1073+  S3L_Unit *value;
1074+
1075+  i0 = triangleIndex * 3;
1076+  i1 = indices[i0] * numComponents;
1077+  value = (S3L_Unit *) v0;
1078+
1079+  if (numComponents > 4)
1080+    numComponents = 4;
1081+
1082+  for (uint8_t j = 0; j < numComponents; ++j)
1083+  {
1084+    *value = values[i1];
1085+    i1++;
1086+    value++;
1087+  }
1088+
1089+  i0++;
1090+  i1 = indices[i0] * numComponents;
1091+  value = (S3L_Unit *) v1;
1092+
1093+  for (uint8_t j = 0; j < numComponents; ++j)
1094+  {
1095+    *value = values[i1];
1096+    i1++;
1097+    value++;
1098+  }
1099+
1100+  i0++;
1101+  i1 = indices[i0] * numComponents;
1102+  value = (S3L_Unit *) v2;
1103+
1104+  for (uint8_t j = 0; j < numComponents; ++j)
1105+  {
1106+    *value = values[i1];
1107+    i1++;
1108+    value++;
1109+  }
1110+}
1111+
1112+void S3L_computeModelNormals(S3L_Model3D model, S3L_Unit *dst,
1113+  int8_t transformNormals)
1114+{
1115+  S3L_Index vPos = 0;
1116+
1117+  S3L_Vec4 n;
1118+
1119+  n.w = 0;
1120+
1121+  S3L_Vec4 ns[S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE];
1122+  S3L_Index normalCount;
1123+
1124+  for (uint32_t i = 0; i < model.vertexCount; ++i)
1125+  {
1126+    normalCount = 0;
1127+
1128+    for (uint32_t j = 0; j < model.triangleCount * 3; j += 3)
1129+    {
1130+      if (
1131+        (model.triangles[j] == i) ||
1132+        (model.triangles[j + 1] == i) ||
1133+        (model.triangles[j + 2] == i))
1134+      {    
1135+        S3L_Vec4 t0, t1, t2;
1136+        uint32_t vIndex;
1137+
1138+        #define getVertex(n)\
1139+          vIndex = model.triangles[j + n] * 3;\
1140+          t##n.x = model.vertices[vIndex];\
1141+          vIndex++;\
1142+          t##n.y = model.vertices[vIndex];\
1143+          vIndex++;\
1144+          t##n.z = model.vertices[vIndex];
1145+
1146+        getVertex(0)
1147+        getVertex(1)
1148+        getVertex(2)
1149+
1150+        #undef getVertex
1151+        
1152+        S3L_triangleNormal(t0,t1,t2,&(ns[normalCount]));    
1153+
1154+        normalCount++;
1155+
1156+        if (normalCount >= S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE)
1157+          break;
1158+      }
1159+    }
1160+      
1161+    n.x = S3L_F;
1162+    n.y = 0;
1163+    n.z = 0;
1164+
1165+    if (normalCount != 0)
1166+    {
1167+      // compute average
1168+
1169+      n.x = 0;
1170+
1171+      for (uint8_t i = 0; i < normalCount; ++i)
1172+      {
1173+        n.x += ns[i].x;
1174+        n.y += ns[i].y;
1175+        n.z += ns[i].z;
1176+      }
1177+
1178+      n.x /= normalCount;
1179+      n.y /= normalCount;
1180+      n.z /= normalCount;
1181+
1182+      S3L_vec3Normalize(&n);
1183+    }
1184+
1185+    dst[vPos] = n.x;
1186+    vPos++;
1187+
1188+    dst[vPos] = n.y;
1189+    vPos++;
1190+
1191+    dst[vPos] = n.z;
1192+    vPos++;
1193+  }
1194+    
1195+  S3L_Mat4 m;
1196+
1197+  S3L_makeWorldMatrix(model.transform,m);
1198+
1199+  if (transformNormals)
1200+    for (S3L_Index i = 0; i < model.vertexCount * 3; i += 3)
1201+    {
1202+      n.x = dst[i];
1203+      n.y = dst[i + 1];
1204+      n.z = dst[i + 2];
1205+
1206+      S3L_vec4Xmat4(&n,m);
1207+
1208+      dst[i] = n.x;
1209+      dst[i + 1] = n.y;
1210+      dst[i + 2] = n.z;
1211+    }
1212+}
1213+
1214+void S3L_vec4Xmat4(S3L_Vec4 *v, S3L_Mat4 m)
1215+{
1216+  S3L_Vec4 vBackup;
1217+
1218+  vBackup.x = v->x;  
1219+  vBackup.y = v->y;  
1220+  vBackup.z = v->z;  
1221+  vBackup.w = v->w;  
1222+
1223+  #define dotCol(col)\
1224+    ((vBackup.x * m[col][0]) +\
1225+     (vBackup.y * m[col][1]) +\
1226+     (vBackup.z * m[col][2]) +\
1227+     (vBackup.w * m[col][3])) / S3L_F
1228+
1229+  v->x = dotCol(0);
1230+  v->y = dotCol(1);
1231+  v->z = dotCol(2);
1232+  v->w = dotCol(3);
1233+}
1234+
1235+void S3L_vec3Xmat4(S3L_Vec4 *v, S3L_Mat4 m)
1236+{
1237+  S3L_Vec4 vBackup;
1238+
1239+  #undef dotCol
1240+  #define dotCol(col)\
1241+    (vBackup.x * m[col][0]) / S3L_F +\
1242+    (vBackup.y * m[col][1]) / S3L_F +\
1243+    (vBackup.z * m[col][2]) / S3L_F +\
1244+    m[col][3]
1245+
1246+  vBackup.x = v->x;  
1247+  vBackup.y = v->y;  
1248+  vBackup.z = v->z;  
1249+  vBackup.w = v->w;  
1250+
1251+  v->x = dotCol(0);
1252+  v->y = dotCol(1);
1253+  v->z = dotCol(2);
1254+  v->w = S3L_F;
1255+}
1256+
1257+#undef dotCol
1258+
1259+S3L_Unit S3L_abs(S3L_Unit value)
1260+{
1261+  return value * (((value >= 0) << 1) - 1);
1262+}
1263+
1264+S3L_Unit S3L_min(S3L_Unit v1, S3L_Unit v2)
1265+{
1266+  return v1 >= v2 ? v2 : v1;
1267+}
1268+
1269+S3L_Unit S3L_max(S3L_Unit v1, S3L_Unit v2)
1270+{
1271+  return v1 >= v2 ? v1 : v2;
1272+}
1273+
1274+S3L_Unit S3L_clamp(S3L_Unit v, S3L_Unit v1, S3L_Unit v2)
1275+{
1276+  return v >= v1 ? (v <= v2 ? v : v2) : v1;
1277+}
1278+
1279+S3L_Unit S3L_zeroClamp(S3L_Unit value)
1280+{
1281+  return (value * (value >= 0));
1282+}
1283+
1284+S3L_Unit S3L_wrap(S3L_Unit value, S3L_Unit mod)
1285+{
1286+  return value >= 0 ? (value % mod) : (mod + (value % mod) - 1);
1287+}
1288+
1289+S3L_Unit S3L_nonZero(S3L_Unit value)
1290+{
1291+  return (value + (value == 0));
1292+}
1293+
1294+S3L_Unit S3L_interpolate(S3L_Unit v1, S3L_Unit v2, S3L_Unit t, S3L_Unit tMax)
1295+{
1296+  return v1 + ((v2 - v1) * t) / tMax;
1297+}
1298+
1299+S3L_Unit S3L_interpolateByUnit(S3L_Unit v1, S3L_Unit v2, S3L_Unit t)
1300+{
1301+  return v1 + ((v2 - v1) * t) / S3L_F;
1302+}
1303+
1304+S3L_Unit S3L_interpolateByUnitFrom0(S3L_Unit v2, S3L_Unit t)
1305+{
1306+  return (v2 * t) / S3L_F;
1307+}
1308+
1309+S3L_Unit S3L_interpolateFrom0(S3L_Unit v2, S3L_Unit t, S3L_Unit tMax)
1310+{
1311+  return (v2 * t) / tMax;
1312+}
1313+
1314+S3L_Unit S3L_distanceManhattan(S3L_Vec4 a, S3L_Vec4 b)
1315+{
1316+  return
1317+    S3L_abs(a.x - b.x) +
1318+    S3L_abs(a.y - b.y) +
1319+    S3L_abs(a.z - b.z);
1320+}
1321+
1322+void S3L_mat4Xmat4(S3L_Mat4 m1, S3L_Mat4 m2)
1323+{
1324+  S3L_Mat4 mat1;
1325+
1326+  for (uint16_t row = 0; row < 4; ++row)
1327+    for (uint16_t col = 0; col < 4; ++col)
1328+      mat1[col][row] = m1[col][row];
1329+
1330+  for (uint16_t row = 0; row < 4; ++row)
1331+    for (uint16_t col = 0; col < 4; ++col)
1332+    {
1333+      m1[col][row] = 0;
1334+
1335+      for (uint16_t i = 0; i < 4; ++i)
1336+        m1[col][row] +=
1337+          (mat1[i][row] * m2[col][i]) / S3L_F;
1338+    }
1339+}
1340+
1341+S3L_Unit S3L_sin(S3L_Unit x)
1342+{
1343+#if S3L_SIN_METHOD == 0
1344+  x = S3L_wrap(x / S3L_SIN_TABLE_UNIT_STEP,S3L_SIN_TABLE_LENGTH * 4);
1345+  int8_t positive = 1;
1346+
1347+  if (x < S3L_SIN_TABLE_LENGTH)
1348+  {
1349+  }
1350+  else if (x < S3L_SIN_TABLE_LENGTH * 2)
1351+  {
1352+    x = S3L_SIN_TABLE_LENGTH * 2 - x - 1;
1353+  }
1354+  else if (x < S3L_SIN_TABLE_LENGTH * 3)
1355+  {
1356+    x = x - S3L_SIN_TABLE_LENGTH * 2;
1357+    positive = 0;
1358+  }
1359+  else
1360+  {
1361+    x = S3L_SIN_TABLE_LENGTH - (x - S3L_SIN_TABLE_LENGTH * 3) - 1;
1362+    positive = 0;
1363+  }
1364+
1365+  return positive ? S3L_sinTable[x] : -1 * S3L_sinTable[x];
1366+#else
1367+  int8_t sign = 1;
1368+    
1369+  if (x < 0) // odd function
1370+  {
1371+    x *= -1;
1372+    sign = -1;
1373+  }
1374+    
1375+  x %= S3L_F;
1376+  
1377+  if (x > S3L_F / 2)
1378+  {
1379+    x -= S3L_F / 2;
1380+    sign *= -1;
1381+  }
1382+
1383+  S3L_Unit tmp = S3L_F - 2 * x;
1384+ 
1385+  #define _PI2 ((S3L_Unit) (9.8696044 * S3L_F))
1386+  return sign * // Bhaskara's approximation
1387+    (((32 * x * _PI2) / S3L_F) * tmp) / 
1388+    ((_PI2 * (5 * S3L_F - (8 * x * tmp) / 
1389+      S3L_F)) / S3L_F);
1390+  #undef _PI2
1391+#endif
1392+}
1393+
1394+S3L_Unit S3L_asin(S3L_Unit x)
1395+{
1396+#if S3L_SIN_METHOD == 0
1397+  x = S3L_clamp(x,-S3L_F,S3L_F);
1398+
1399+  int8_t sign = 1;
1400+
1401+  if (x < 0)
1402+  {
1403+    sign = -1;
1404+    x *= -1;
1405+  }
1406+
1407+  int16_t low = 0, high = S3L_SIN_TABLE_LENGTH -1, middle = 0;
1408+
1409+  while (low <= high) // binary search
1410+  {
1411+    middle = (low + high) / 2;
1412+
1413+    S3L_Unit v = S3L_sinTable[middle];
1414+
1415+    if (v > x)
1416+      high = middle - 1;
1417+    else if (v < x)
1418+      low = middle + 1;
1419+    else
1420+      break;
1421+  }
1422+
1423+  middle *= S3L_SIN_TABLE_UNIT_STEP;
1424+
1425+  return sign * middle;
1426+#else
1427+  S3L_Unit low = -1 * S3L_F / 4,
1428+           high = S3L_F / 4,
1429+           middle;
1430+    
1431+  while (low <= high) // binary search
1432+  {
1433+    middle = (low + high) / 2;
1434+
1435+    S3L_Unit v = S3L_sin(middle);
1436+
1437+    if (v > x)
1438+      high = middle - 1;
1439+    else if (v < x)
1440+      low = middle + 1;
1441+    else
1442+      break;
1443+  }
1444+
1445+  return middle;
1446+#endif
1447+}
1448+
1449+S3L_Unit S3L_cos(S3L_Unit x)
1450+{
1451+  return S3L_sin(x + S3L_F / 4);
1452+}
1453+
1454+void S3L_correctBarycentricCoords(S3L_Unit barycentric[3])
1455+{
1456+  barycentric[0] = S3L_clamp(barycentric[0],0,S3L_F);
1457+  barycentric[1] = S3L_clamp(barycentric[1],0,S3L_F);
1458+
1459+  S3L_Unit d = S3L_F - barycentric[0] - barycentric[1];
1460+
1461+  if (d < 0)
1462+  {
1463+    barycentric[0] += d;
1464+    barycentric[2] = 0;
1465+  }
1466+  else
1467+    barycentric[2] = d;
1468+}
1469+
1470+void S3L_makeTranslationMat(
1471+  S3L_Unit offsetX,
1472+  S3L_Unit offsetY,
1473+  S3L_Unit offsetZ,
1474+  S3L_Mat4 m)
1475+{
1476+  #define M(x,y) m[x][y]
1477+  #define S S3L_F
1478+
1479+  M(0,0) = S; M(1,0) = 0; M(2,0) = 0; M(3,0) = 0; 
1480+  M(0,1) = 0; M(1,1) = S; M(2,1) = 0; M(3,1) = 0; 
1481+  M(0,2) = 0; M(1,2) = 0; M(2,2) = S; M(3,2) = 0; 
1482+  M(0,3) = offsetX; M(1,3) = offsetY; M(2,3) = offsetZ; M(3,3) = S;
1483+
1484+  #undef M
1485+  #undef S
1486+}
1487+
1488+void S3L_makeScaleMatrix(
1489+  S3L_Unit scaleX,
1490+  S3L_Unit scaleY,
1491+  S3L_Unit scaleZ,
1492+  S3L_Mat4 m)
1493+{
1494+  #define M(x,y) m[x][y]
1495+
1496+  M(0,0) = scaleX; M(1,0) = 0;      M(2,0) = 0;     M(3,0) = 0; 
1497+  M(0,1) = 0;      M(1,1) = scaleY; M(2,1) = 0; M(3,1) = 0; 
1498+  M(0,2) = 0;      M(1,2) = 0;      M(2,2) = scaleZ; M(3,2) = 0; 
1499+  M(0,3) = 0;      M(1,3) = 0;     M(2,3) = 0; M(3,3) = S3L_F; 
1500+
1501+  #undef M
1502+}
1503+
1504+void S3L_makeRotationMatrixZXY(
1505+  S3L_Unit byX,
1506+  S3L_Unit byY,
1507+  S3L_Unit byZ,
1508+  S3L_Mat4 m)
1509+{
1510+  byX *= -1;
1511+  byY *= -1;
1512+  byZ *= -1;
1513+
1514+  S3L_Unit sx = S3L_sin(byX);
1515+  S3L_Unit sy = S3L_sin(byY);
1516+  S3L_Unit sz = S3L_sin(byZ);
1517+
1518+  S3L_Unit cx = S3L_cos(byX);
1519+  S3L_Unit cy = S3L_cos(byY);
1520+  S3L_Unit cz = S3L_cos(byZ);
1521+
1522+  #define M(x,y) m[x][y]
1523+  #define S S3L_F
1524+
1525+  M(0,0) = (cy * cz) / S + (sy * sx * sz) / (S * S);
1526+  M(1,0) = (cx * sz) / S;
1527+  M(2,0) = (cy * sx * sz) / (S * S) - (cz * sy) / S;
1528+  M(3,0) = 0;
1529+
1530+  M(0,1) = (cz * sy * sx) / (S * S) - (cy * sz) / S;
1531+  M(1,1) = (cx * cz) / S;
1532+  M(2,1) = (cy * cz * sx) / (S * S) + (sy * sz) / S;
1533+  M(3,1) = 0;
1534+
1535+  M(0,2) = (cx * sy) / S;
1536+  M(1,2) = -1 * sx;
1537+  M(2,2) = (cy * cx) / S;
1538+  M(3,2) = 0;
1539+
1540+  M(0,3) = 0;
1541+  M(1,3) = 0;
1542+  M(2,3) = 0;
1543+  M(3,3) = S3L_F;
1544+
1545+  #undef M
1546+  #undef S 
1547+}
1548+
1549+S3L_Unit S3L_sqrt(S3L_Unit value)
1550+{
1551+  int8_t sign = 1;
1552+
1553+  if (value < 0)
1554+  {
1555+    sign = -1;
1556+    value *= -1;
1557+  }
1558+
1559+  uint32_t result = 0;
1560+  uint32_t a = value;
1561+  uint32_t b = 1u << 30;
1562+
1563+  while (b > a)
1564+    b >>= 2;
1565+
1566+  while (b != 0)
1567+  {
1568+    if (a >= result + b)
1569+    {
1570+      a -= result + b;
1571+      result = result +  2 * b;
1572+    }
1573+
1574+    b >>= 2;
1575+    result >>= 1;
1576+  }
1577+
1578+  return result * sign;
1579+}
1580+
1581+S3L_Unit S3L_vec3Length(S3L_Vec4 v)
1582+{
1583+  return S3L_sqrt(v.x * v.x + v.y * v.y + v.z * v.z);  
1584+}
1585+
1586+S3L_Unit S3L_vec2Length(S3L_Vec4 v)
1587+{
1588+  return S3L_sqrt(v.x * v.x + v.y * v.y);  
1589+}
1590+
1591+void S3L_vec3Normalize(S3L_Vec4 *v)
1592+{
1593+  #define SCALE 16
1594+  #define BOTTOM_LIMIT 16
1595+  #define UPPER_LIMIT 900
1596+
1597+  /* Here we try to decide if the vector is too small and would cause
1598+     inaccurate result due to very its inaccurate length. If so, we scale
1599+     it up. We can't scale up everything as big vectors overflow in length
1600+     calculations. */
1601+
1602+  if (
1603+    S3L_abs(v->x) <= BOTTOM_LIMIT &&
1604+    S3L_abs(v->y) <= BOTTOM_LIMIT &&
1605+    S3L_abs(v->z) <= BOTTOM_LIMIT)
1606+  {
1607+    v->x *= SCALE;
1608+    v->y *= SCALE;
1609+    v->z *= SCALE;
1610+  }
1611+  else if (
1612+    S3L_abs(v->x) > UPPER_LIMIT ||
1613+    S3L_abs(v->y) > UPPER_LIMIT ||
1614+    S3L_abs(v->z) > UPPER_LIMIT)
1615+  {
1616+    v->x /= SCALE;
1617+    v->y /= SCALE;
1618+    v->z /= SCALE;
1619+  }
1620+ 
1621+  #undef SCALE
1622+  #undef BOTTOM_LIMIT
1623+  #undef UPPER_LIMIT
1624+
1625+  S3L_Unit l = S3L_vec3Length(*v);
1626+
1627+  if (l == 0)
1628+    return;
1629+
1630+  v->x = (v->x * S3L_F) / l;
1631+  v->y = (v->y * S3L_F) / l;
1632+  v->z = (v->z * S3L_F) / l;
1633+}
1634+
1635+void S3L_vec3NormalizeFast(S3L_Vec4 *v)
1636+{
1637+  S3L_Unit l = S3L_vec3Length(*v);
1638+
1639+  if (l == 0)
1640+    return;
1641+
1642+  v->x = (v->x * S3L_F) / l;
1643+  v->y = (v->y * S3L_F) / l;
1644+  v->z = (v->z * S3L_F) / l;
1645+}
1646+
1647+void S3L_transform3DInit(S3L_Transform3D *t)
1648+{
1649+  S3L_vec4Init(&(t->translation));
1650+  S3L_vec4Init(&(t->rotation));
1651+  t->scale.x = S3L_F;
1652+  t->scale.y = S3L_F;
1653+  t->scale.z = S3L_F;
1654+  t->scale.w = 0;
1655+}
1656+
1657+/** Performs perspecive division (z-divide). Does NOT check for division by
1658+  zero. */
1659+static inline void S3L_perspectiveDivide(S3L_Vec4 *vector,
1660+  S3L_Unit focalLength)
1661+{
1662+  if (focalLength == 0)
1663+    return;
1664+
1665+  vector->x = (vector->x * focalLength) / vector->z;
1666+  vector->y = (vector->y * focalLength) / vector->z;
1667+}
1668+
1669+void S3L_project3DPointToScreen(
1670+  S3L_Vec4 point,
1671+  S3L_Camera camera,
1672+  S3L_Vec4 *result)
1673+{
1674+  // TODO: hotfix to prevent a mapping bug probably to overlfows
1675+  S3L_Vec4 toPoint = point, camForw;
1676+
1677+  S3L_vec3Sub(&toPoint,camera.transform.translation);
1678+
1679+  S3L_vec3Normalize(&toPoint);
1680+
1681+  S3L_rotationToDirections(camera.transform.rotation,S3L_FRACTIONS_PER_UNIT,
1682+    &camForw,0,0);
1683+
1684+  if (S3L_vec3Dot(toPoint,camForw) < S3L_FRACTIONS_PER_UNIT / 6)
1685+  {
1686+    result->z = -1;
1687+    result->w = 0;
1688+    return;
1689+  }
1690+  // end of hotfix
1691+  S3L_Mat4 m;
1692+  S3L_makeCameraMatrix(camera.transform,m);
1693+
1694+  S3L_Unit s = point.w;
1695+
1696+  point.w = S3L_F;
1697+
1698+  S3L_vec3Xmat4(&point,m);
1699+
1700+  point.z = S3L_nonZero(point.z);
1701+
1702+  S3L_perspectiveDivide(&point,camera.focalLength);
1703+
1704+  S3L_ScreenCoord x, y;
1705+
1706+  S3L_mapProjectionPlaneToScreen(point,&x,&y);
1707+
1708+  result->x = x;
1709+  result->y = y;
1710+  result->z = point.z;
1711+
1712+  result->w =
1713+    (point.z <= 0) ? 0 :
1714+    (
1715+      camera.focalLength > 0 ?(
1716+      (s * camera.focalLength * S3L_RESOLUTION_X) /
1717+        (point.z * S3L_F)) :
1718+      ((camera.transform.scale.x * S3L_RESOLUTION_X) / S3L_F)
1719+    );
1720+}
1721+
1722+void S3L_lookAt(S3L_Vec4 pointTo, S3L_Transform3D *t)
1723+{
1724+  S3L_Vec4 v;
1725+
1726+  v.x = pointTo.x - t->translation.x;
1727+  v.y = pointTo.z - t->translation.z;
1728+
1729+  S3L_Unit dx = v.x;
1730+  S3L_Unit l = S3L_vec2Length(v);
1731+
1732+  dx = (v.x * S3L_F) / S3L_nonZero(l); // normalize
1733+
1734+  t->rotation.y = -1 * S3L_asin(dx);
1735+
1736+  if (v.y < 0)
1737+    t->rotation.y = S3L_F / 2 - t->rotation.y;
1738+
1739+  v.x = pointTo.y - t->translation.y;
1740+  v.y = l;
1741+ 
1742+  l = S3L_vec2Length(v);
1743+ 
1744+  dx = (v.x * S3L_F) / S3L_nonZero(l);
1745+
1746+  t->rotation.x = S3L_asin(dx);
1747+}
1748+
1749+void S3L_transform3DSet(
1750+  S3L_Unit tx,
1751+  S3L_Unit ty,
1752+  S3L_Unit tz,
1753+  S3L_Unit rx,
1754+  S3L_Unit ry,
1755+  S3L_Unit rz,
1756+  S3L_Unit sx,
1757+  S3L_Unit sy,
1758+  S3L_Unit sz,
1759+  S3L_Transform3D *t)
1760+{
1761+  t->translation.x = tx;
1762+  t->translation.y = ty;
1763+  t->translation.z = tz;
1764+
1765+  t->rotation.x = rx;
1766+  t->rotation.y = ry;
1767+  t->rotation.z = rz;
1768+
1769+  t->scale.x = sx;
1770+  t->scale.y = sy;
1771+  t->scale.z = sz;
1772+}
1773+
1774+void S3L_cameraInit(S3L_Camera *camera)
1775+{
1776+  camera->focalLength = S3L_F;
1777+  S3L_transform3DInit(&(camera->transform));
1778+}
1779+
1780+void S3L_rotationToDirections(
1781+  S3L_Vec4 rotation,
1782+  S3L_Unit length,
1783+  S3L_Vec4 *forw, 
1784+  S3L_Vec4 *right,
1785+  S3L_Vec4 *up)
1786+{
1787+  S3L_Mat4 m;
1788+
1789+  S3L_makeRotationMatrixZXY(rotation.x,rotation.y,rotation.z,m);
1790+
1791+  if (forw != 0)
1792+  {
1793+    forw->x = 0;
1794+    forw->y = 0;
1795+    forw->z = length;
1796+    S3L_vec3Xmat4(forw,m);
1797+  }
1798+
1799+  if (right != 0)
1800+  {
1801+    right->x = length;
1802+    right->y = 0;
1803+    right->z = 0;
1804+    S3L_vec3Xmat4(right,m);
1805+  }
1806+
1807+  if (up != 0)
1808+  {
1809+    up->x = 0;
1810+    up->y = length;
1811+    up->z = 0;
1812+    S3L_vec3Xmat4(up,m);
1813+  }
1814+}
1815+
1816+void S3L_pixelInfoInit(S3L_PixelInfo *p)
1817+{
1818+  p->x = 0;
1819+  p->y = 0;
1820+  p->barycentric[0] = S3L_F;
1821+  p->barycentric[1] = 0;
1822+  p->barycentric[2] = 0;
1823+  p->modelIndex = 0;
1824+  p->triangleIndex = 0;
1825+  p->triangleID = 0;
1826+  p->depth = 0;
1827+  p->previousZ = 0;
1828+}
1829+
1830+void S3L_model3DInit(
1831+  const S3L_Unit *vertices,
1832+  S3L_Index vertexCount,
1833+  const S3L_Index *triangles,
1834+  S3L_Index triangleCount,
1835+  S3L_Model3D *model)
1836+{
1837+  model->vertices = vertices;
1838+  model->vertexCount = vertexCount;
1839+  model->triangles = triangles;
1840+  model->triangleCount = triangleCount;
1841+  model->customTransformMatrix = 0;  
1842+
1843+  S3L_transform3DInit(&(model->transform));
1844+  S3L_drawConfigInit(&(model->config));
1845+}
1846+
1847+void S3L_sceneInit(
1848+  S3L_Model3D *models,
1849+  S3L_Index modelCount,
1850+  S3L_Scene *scene)
1851+{
1852+  scene->models = models;
1853+  scene->modelCount = modelCount;
1854+  S3L_cameraInit(&(scene->camera));
1855+}
1856+
1857+void S3L_drawConfigInit(S3L_DrawConfig *config)
1858+{
1859+  config->backfaceCulling = 2;
1860+  config->visible = 1;
1861+}
1862+
1863+#ifndef S3L_PIXEL_FUNCTION
1864+  #error Pixel rendering function (S3L_PIXEL_FUNCTION) not specified!
1865+#endif
1866+
1867+static inline void S3L_PIXEL_FUNCTION(S3L_PixelInfo *pixel); // forward decl
1868+
1869+/** Serves to accelerate linear interpolation for performance-critical
1870+  code. Functions such as S3L_interpolate require division to compute each
1871+  interpolated value, while S3L_FastLerpState only requires a division for
1872+  the initiation and a shift for retrieving each interpolated value.
1873+
1874+  S3L_FastLerpState stores a value and a step, both scaled (shifted by
1875+  S3L_FAST_LERP_QUALITY) to increase precision. The step is being added to the
1876+  value, which achieves the interpolation. This will only be useful for
1877+  interpolations in which we need to get the interpolated value in every step.
1878+
1879+  BEWARE! Shifting a negative value is undefined, so handling shifting of
1880+  negative values has to be done cleverly. */
1881+typedef struct
1882+{
1883+  S3L_Unit valueScaled;
1884+  S3L_Unit stepScaled;
1885+} S3L_FastLerpState;
1886+
1887+#define S3L_getFastLerpValue(state)\
1888+  (state.valueScaled >> S3L_FAST_LERP_QUALITY)
1889+
1890+#define S3L_stepFastLerp(state)\
1891+  state.valueScaled += state.stepScaled
1892+
1893+static inline S3L_Unit S3L_interpolateBarycentric(
1894+  S3L_Unit value0,
1895+  S3L_Unit value1,
1896+  S3L_Unit value2,
1897+  S3L_Unit barycentric[3])
1898+{
1899+  return
1900+    (
1901+      (value0 * barycentric[0]) +
1902+      (value1 * barycentric[1]) +
1903+      (value2 * barycentric[2])
1904+    ) / S3L_F;
1905+}
1906+
1907+void S3L_mapProjectionPlaneToScreen(
1908+  S3L_Vec4 point,
1909+  S3L_ScreenCoord *screenX,
1910+  S3L_ScreenCoord *screenY)
1911+{
1912+  *screenX = 
1913+    S3L_HALF_RESOLUTION_X +
1914+    (point.x * S3L_HALF_RESOLUTION_X) / S3L_F;
1915+
1916+  *screenY = 
1917+    S3L_HALF_RESOLUTION_Y -
1918+    (point.y * S3L_HALF_RESOLUTION_X) / S3L_F;
1919+}
1920+
1921+void S3L_zBufferClear(void)
1922+{
1923+#if S3L_Z_BUFFER
1924+  for (uint32_t i = 0; i < S3L_RESOLUTION_X * S3L_RESOLUTION_Y; ++i)
1925+    S3L_zBuffer[i] = S3L_MAX_DEPTH;
1926+#endif
1927+}
1928+
1929+void S3L_stencilBufferClear(void)
1930+{
1931+#if S3L_STENCIL_BUFFER
1932+  for (uint32_t i = 0; i < S3L_STENCIL_BUFFER_SIZE; ++i)
1933+    S3L_stencilBuffer[i] = 0;
1934+#endif
1935+}
1936+
1937+void S3L_newFrame(void)
1938+{
1939+  S3L_zBufferClear();
1940+  S3L_stencilBufferClear();
1941+}
1942+
1943+/* the following serves to communicate info about if the triangle has been split
1944+  and how the barycentrics should be remapped. */
1945+uint8_t _S3L_projectedTriangleState = 0; // 0 = normal, 1 = cut, 2 = split
1946+
1947+#if S3L_NEAR_CROSS_STRATEGY == 3
1948+S3L_Vec4 _S3L_triangleRemapBarycentrics[6];
1949+#endif
1950+
1951+void S3L_drawTriangle(
1952+  S3L_Vec4 point0,
1953+  S3L_Vec4 point1,
1954+  S3L_Vec4 point2,
1955+  S3L_Index modelIndex,
1956+  S3L_Index triangleIndex)
1957+{
1958+  S3L_PixelInfo p;
1959+  S3L_pixelInfoInit(&p);
1960+  p.modelIndex = modelIndex;
1961+  p.triangleIndex = triangleIndex;
1962+  p.triangleID = (modelIndex << 16) | triangleIndex;
1963+
1964+  S3L_Vec4 *tPointSS, *lPointSS, *rPointSS; /* points in Screen Space (in
1965+                                               S3L_Units, normalized by
1966+                                               S3L_F) */
1967+
1968+  S3L_Unit *barycentric0; // bar. coord that gets higher from L to R
1969+  S3L_Unit *barycentric1; // bar. coord that gets higher from R to L
1970+  S3L_Unit *barycentric2; // bar. coord that gets higher from bottom up
1971+
1972+  // sort the vertices:
1973+
1974+  #define assignPoints(t,a,b)\
1975+    {\
1976+      tPointSS = &point##t;\
1977+      barycentric2 = &(p.barycentric[t]);\
1978+      if (S3L_triangleWinding(point##t.x,point##t.y,point##a.x,point##a.y,\
1979+        point##b.x,point##b.y) >= 0)\
1980+      {\
1981+        lPointSS = &point##a; rPointSS = &point##b;\
1982+        barycentric0 = &(p.barycentric[b]);\
1983+        barycentric1 = &(p.barycentric[a]);\
1984+      }\
1985+      else\
1986+      {\
1987+        lPointSS = &point##b; rPointSS = &point##a;\
1988+        barycentric0 = &(p.barycentric[a]);\
1989+        barycentric1 = &(p.barycentric[b]);\
1990+      }\
1991+    }
1992+
1993+  if (point0.y <= point1.y)
1994+  {
1995+    if (point0.y <= point2.y)
1996+      assignPoints(0,1,2)
1997+    else
1998+      assignPoints(2,0,1)
1999+  }
2000+  else
2001+  {
2002+    if (point1.y <= point2.y)
2003+      assignPoints(1,0,2)
2004+    else
2005+      assignPoints(2,0,1)
2006+  }
2007+
2008+  #undef assignPoints
2009+
2010+#if S3L_FLAT
2011+  *barycentric0 = S3L_F / 3;
2012+  *barycentric1 = S3L_F / 3;
2013+  *barycentric2 = S3L_F - 2 * (S3L_F / 3);
2014+#endif
2015+
2016+  p.triangleSize[0] = rPointSS->x - lPointSS->x;
2017+  p.triangleSize[1] =
2018+    (rPointSS->y > lPointSS->y ? rPointSS->y : lPointSS->y) - tPointSS->y;
2019+
2020+  // now draw the triangle line by line:
2021+
2022+  S3L_ScreenCoord splitY; // Y of the vertically middle point of the triangle
2023+  S3L_ScreenCoord endY;   // bottom Y of the whole triangle
2024+  int splitOnLeft;        /* whether splitY is the y coord. of left or right 
2025+                             point */
2026+
2027+  if (rPointSS->y <= lPointSS->y)
2028+  {
2029+    splitY = rPointSS->y;
2030+    splitOnLeft = 0;
2031+    endY = lPointSS->y;
2032+  }
2033+  else
2034+  {
2035+    splitY = lPointSS->y;
2036+    splitOnLeft = 1;
2037+    endY = rPointSS->y;
2038+  }
2039+
2040+  S3L_ScreenCoord currentY = tPointSS->y;
2041+
2042+  /* We'll be using an algorithm similar to Bresenham line algorithm. The
2043+     specifics of this algorithm are among others:
2044+
2045+     - drawing possibly NON-CONTINUOUS line
2046+     - NOT tracing the line exactly, but rather rasterizing one the right
2047+       side of it, according to the pixel CENTERS, INCLUDING the pixel
2048+       centers
2049+     
2050+     The principle is this:
2051+
2052+     - Move vertically by pixels and accumulate the error (abs(dx/dy)).
2053+     - If the error is greater than one (crossed the next pixel center), keep
2054+       moving horizontally and substracting 1 from the error until it is less
2055+       than 1 again.
2056+     - To make this INTEGER ONLY, scale the case so that distance between
2057+       pixels is equal to dy (instead of 1). This way the error becomes
2058+       dx/dy * dy == dx, and we're comparing the error to (and potentially
2059+       substracting) 1 * dy == dy. */
2060+
2061+  int16_t
2062+    /* triangle side:
2063+    left     right */
2064+    lX,      rX,       // current x position on the screen
2065+    lDx,     rDx,      // dx (end point - start point)
2066+    lDy,     rDy,      // dy (end point - start point)
2067+    lInc,    rInc,     // direction in which to increment (1 or -1)
2068+    lErr,    rErr,     // current error (Bresenham)
2069+    lErrCmp, rErrCmp,  // helper for deciding comparison (> vs >=)
2070+    lErrAdd, rErrAdd,  // error value to add in each Bresenham cycle
2071+    lErrSub, rErrSub;  // error value to substract when moving in x direction
2072+
2073+  S3L_FastLerpState lSideFLS, rSideFLS;
2074+
2075+#if S3L_COMPUTE_LERP_DEPTH
2076+  S3L_FastLerpState lDepthFLS, rDepthFLS;
2077+
2078+  #define initDepthFLS(s,p1,p2)\
2079+    s##DepthFLS.valueScaled = p1##PointSS->z << S3L_FAST_LERP_QUALITY;\
2080+    s##DepthFLS.stepScaled = ((p2##PointSS->z << S3L_FAST_LERP_QUALITY) -\
2081+      s##DepthFLS.valueScaled) / (s##Dy != 0 ? s##Dy : 1);
2082+#else
2083+  #define initDepthFLS(s,p1,p2) ;
2084+#endif
2085+
2086+  /* init side for the algorithm, params:
2087+     s - which side (l or r)
2088+     p1 - point from (t, l or r)
2089+     p2 - point to (t, l or r)
2090+     down - whether the side coordinate goes top-down or vice versa */
2091+  #define initSide(s,p1,p2,down)\
2092+    s##X = p1##PointSS->x;\
2093+    s##Dx = p2##PointSS->x - p1##PointSS->x;\
2094+    s##Dy = p2##PointSS->y - p1##PointSS->y;\
2095+    initDepthFLS(s,p1,p2)\
2096+    s##SideFLS.stepScaled = (S3L_F << S3L_FAST_LERP_QUALITY)\
2097+                      / (s##Dy != 0 ? s##Dy : 1);\
2098+    s##SideFLS.valueScaled = 0;\
2099+    if (!down)\
2100+    {\
2101+      s##SideFLS.valueScaled =\
2102+        S3L_F << S3L_FAST_LERP_QUALITY;\
2103+      s##SideFLS.stepScaled *= -1;\
2104+    }\
2105+    s##Inc = s##Dx >= 0 ? 1 : -1;\
2106+    if (s##Dx < 0)\
2107+      {s##Err = 0;     s##ErrCmp = 0;}\
2108+    else\
2109+      {s##Err = s##Dy; s##ErrCmp = 1;}\
2110+    s##ErrAdd = S3L_abs(s##Dx);\
2111+    s##ErrSub = s##Dy != 0 ? s##Dy : 1; /* don't allow 0, could lead to an
2112+                                           infinite substracting loop */
2113+
2114+  #define stepSide(s)\
2115+    while (s##Err - s##Dy >= s##ErrCmp)\
2116+    {\
2117+      s##X += s##Inc;\
2118+      s##Err -= s##ErrSub;\
2119+    }\
2120+    s##Err += s##ErrAdd;
2121+
2122+  initSide(r,t,r,1)
2123+  initSide(l,t,l,1)
2124+
2125+#if S3L_PERSPECTIVE_CORRECTION
2126+  /* PC is done by linearly interpolating reciprocals from which the corrected
2127+     velues can be computed. See
2128+     http://www.lysator.liu.se/~mikaelk/doc/perspectivetexture/ */
2129+
2130+  #if S3L_PERSPECTIVE_CORRECTION == 1
2131+    #define Z_RECIP_NUMERATOR\
2132+      (S3L_F * S3L_F * S3L_F)
2133+  #elif S3L_PERSPECTIVE_CORRECTION == 2
2134+    #define Z_RECIP_NUMERATOR\
2135+      (S3L_F * S3L_F)
2136+  #endif
2137+  /* ^ This numerator is a number by which we divide values for the
2138+     reciprocals. For PC == 2 it has to be lower because linear interpolation
2139+     scaling would make it overflow -- this results in lower depth precision
2140+     in bigger distance for PC == 2. */
2141+
2142+  S3L_Unit
2143+    tPointRecipZ, lPointRecipZ, rPointRecipZ, /* Reciprocals of the depth of 
2144+                                                 each triangle point. */
2145+    lRecip0, lRecip1, rRecip0, rRecip1;       /* Helper variables for swapping
2146+                                                 the above after split. */
2147+
2148+  tPointRecipZ = Z_RECIP_NUMERATOR / S3L_nonZero(tPointSS->z);
2149+  lPointRecipZ = Z_RECIP_NUMERATOR / S3L_nonZero(lPointSS->z);
2150+  rPointRecipZ = Z_RECIP_NUMERATOR / S3L_nonZero(rPointSS->z);
2151+
2152+  lRecip0 = tPointRecipZ;
2153+  lRecip1 = lPointRecipZ;
2154+  rRecip0 = tPointRecipZ;
2155+  rRecip1 = rPointRecipZ;
2156+
2157+  #define manageSplitPerspective(b0,b1)\
2158+    b1##Recip0 = b0##PointRecipZ;\
2159+    b1##Recip1 = b1##PointRecipZ;\
2160+    b0##Recip0 = b0##PointRecipZ;\
2161+    b0##Recip1 = tPointRecipZ;
2162+#else
2163+  #define manageSplitPerspective(b0,b1) ;
2164+#endif
2165+
2166+  // clip to the screen in y dimension:
2167+
2168+  endY = S3L_min(endY,S3L_RESOLUTION_Y);
2169+
2170+  /* Clipping above the screen (y < 0) can't be easily done here, will be
2171+     handled inside the loop. */
2172+
2173+  while (currentY < endY)   /* draw the triangle from top to bottom -- the
2174+                               bottom-most row is left out because, following
2175+                               from the rasterization rules (see start of the
2176+                               file), it is to never be rasterized. */
2177+  {
2178+    if (currentY == splitY) // reached a vertical split of the triangle?
2179+    {
2180+      #define manageSplit(b0,b1,s0,s1)\
2181+        S3L_Unit *tmp = barycentric##b0;\
2182+        barycentric##b0 = barycentric##b1;\
2183+        barycentric##b1 = tmp;\
2184+        s0##SideFLS.valueScaled = (S3L_F\
2185+           << S3L_FAST_LERP_QUALITY) - s0##SideFLS.valueScaled;\
2186+        s0##SideFLS.stepScaled *= -1;\
2187+        manageSplitPerspective(s0,s1)
2188+
2189+      if (splitOnLeft)
2190+      {
2191+        initSide(l,l,r,0);
2192+        manageSplit(0,2,r,l)
2193+      }
2194+      else
2195+      {
2196+        initSide(r,r,l,0);
2197+        manageSplit(1,2,l,r)
2198+      }
2199+    }
2200+
2201+    stepSide(r)
2202+    stepSide(l)
2203+
2204+    if (currentY >= 0) /* clipping of pixels whose y < 0 (can't be easily done
2205+                          outside the loop because of the Bresenham-like
2206+                          algorithm steps) */
2207+    { 
2208+      p.y = currentY;
2209+
2210+      // draw the horizontal line
2211+
2212+#if !S3L_FLAT
2213+      S3L_Unit rowLength = S3L_nonZero(rX - lX - 1); // prevent zero div
2214+
2215+  #if S3L_PERSPECTIVE_CORRECTION
2216+      S3L_Unit lOverZ, lRecipZ, rOverZ, rRecipZ, lT, rT;
2217+
2218+      lT = S3L_getFastLerpValue(lSideFLS);
2219+      rT = S3L_getFastLerpValue(rSideFLS);
2220+
2221+      lOverZ  = S3L_interpolateByUnitFrom0(lRecip1,lT);
2222+      lRecipZ = S3L_interpolateByUnit(lRecip0,lRecip1,lT);
2223+
2224+      rOverZ  = S3L_interpolateByUnitFrom0(rRecip1,rT);
2225+      rRecipZ = S3L_interpolateByUnit(rRecip0,rRecip1,rT);
2226+  #else
2227+      S3L_FastLerpState b0FLS, b1FLS;
2228+
2229+    #if S3L_COMPUTE_LERP_DEPTH
2230+      S3L_FastLerpState  depthFLS;
2231+
2232+      depthFLS.valueScaled = lDepthFLS.valueScaled;
2233+      depthFLS.stepScaled =
2234+        (rDepthFLS.valueScaled - lDepthFLS.valueScaled) / rowLength;
2235+    #endif
2236+
2237+      b0FLS.valueScaled = 0;
2238+      b1FLS.valueScaled = lSideFLS.valueScaled;
2239+
2240+      b0FLS.stepScaled = rSideFLS.valueScaled / rowLength;
2241+      b1FLS.stepScaled = -1 * lSideFLS.valueScaled / rowLength;
2242+  #endif
2243+#endif
2244+
2245+      // clip to the screen in x dimension:
2246+
2247+      S3L_ScreenCoord rXClipped = S3L_min(rX,S3L_RESOLUTION_X),
2248+                      lXClipped = lX;
2249+
2250+      if (lXClipped < 0)
2251+      {
2252+        lXClipped = 0;
2253+
2254+#if !S3L_PERSPECTIVE_CORRECTION && !S3L_FLAT
2255+        b0FLS.valueScaled -= lX * b0FLS.stepScaled;
2256+        b1FLS.valueScaled -= lX * b1FLS.stepScaled;
2257+
2258+  #if S3L_COMPUTE_LERP_DEPTH
2259+        depthFLS.valueScaled -= lX * depthFLS.stepScaled;
2260+  #endif
2261+#endif
2262+      }
2263+
2264+#if S3L_PERSPECTIVE_CORRECTION
2265+      S3L_ScreenCoord i = lXClipped - lX;  /* helper var to save one
2266+                                              substraction in the inner
2267+                                              loop */
2268+#endif
2269+
2270+#if S3L_PERSPECTIVE_CORRECTION == 2
2271+      S3L_FastLerpState
2272+        depthPC, // interpolates depth between row segments
2273+        b0PC,    // interpolates barycentric0 between row segments 
2274+        b1PC;    // interpolates barycentric1 between row segments
2275+
2276+      /* ^ These interpolate values between row segments (lines of pixels
2277+           of S3L_PC_APPROX_LENGTH length). After each row segment perspective
2278+           correction is recomputed. */
2279+
2280+      depthPC.valueScaled = 
2281+        (Z_RECIP_NUMERATOR / 
2282+        S3L_nonZero(S3L_interpolate(lRecipZ,rRecipZ,i,rowLength)))
2283+        << S3L_FAST_LERP_QUALITY;
2284+
2285+       b0PC.valueScaled = 
2286+           ( 
2287+             S3L_interpolateFrom0(rOverZ,i,rowLength)
2288+             * depthPC.valueScaled
2289+           ) / (Z_RECIP_NUMERATOR / S3L_F);
2290+
2291+       b1PC.valueScaled =
2292+           ( 
2293+             (lOverZ - S3L_interpolateFrom0(lOverZ,i,rowLength))
2294+             * depthPC.valueScaled
2295+           ) / (Z_RECIP_NUMERATOR / S3L_F);
2296+
2297+      int8_t rowCount = S3L_PC_APPROX_LENGTH;
2298+#endif
2299+
2300+#if S3L_Z_BUFFER
2301+      uint32_t zBufferIndex = p.y * S3L_RESOLUTION_X + lXClipped;
2302+#endif
2303+
2304+      // draw the row -- inner loop:
2305+      for (S3L_ScreenCoord x = lXClipped; x < rXClipped; ++x)
2306+      {
2307+        int8_t testsPassed = 1;
2308+
2309+#if S3L_STENCIL_BUFFER
2310+        if (!S3L_stencilTest(x,p.y))
2311+          testsPassed = 0;
2312+#endif
2313+        p.x = x;
2314+
2315+#if S3L_COMPUTE_DEPTH
2316+  #if S3L_PERSPECTIVE_CORRECTION == 1
2317+        p.depth = Z_RECIP_NUMERATOR /
2318+          S3L_nonZero(S3L_interpolate(lRecipZ,rRecipZ,i,rowLength));
2319+  #elif S3L_PERSPECTIVE_CORRECTION == 2
2320+        if (rowCount >= S3L_PC_APPROX_LENGTH)
2321+        {
2322+          // init the linear interpolation to the next PC correct value
2323+
2324+          rowCount = 0;
2325+
2326+          S3L_Unit nextI = i + S3L_PC_APPROX_LENGTH;
2327+
2328+          if (nextI < rowLength)
2329+          {
2330+            S3L_Unit nextDepthScaled =
2331+              (
2332+              Z_RECIP_NUMERATOR /
2333+              S3L_nonZero(S3L_interpolate(lRecipZ,rRecipZ,nextI,rowLength))
2334+              ) << S3L_FAST_LERP_QUALITY;
2335+
2336+            depthPC.stepScaled =
2337+              (nextDepthScaled - depthPC.valueScaled) / S3L_PC_APPROX_LENGTH;
2338+
2339+            S3L_Unit nextValue = 
2340+             ( 
2341+               S3L_interpolateFrom0(rOverZ,nextI,rowLength)
2342+               * nextDepthScaled
2343+             ) / (Z_RECIP_NUMERATOR / S3L_F);
2344+
2345+            b0PC.stepScaled =
2346+              (nextValue - b0PC.valueScaled) / S3L_PC_APPROX_LENGTH;
2347+
2348+            nextValue = 
2349+             ( 
2350+               (lOverZ - S3L_interpolateFrom0(lOverZ,nextI,rowLength))
2351+               * nextDepthScaled
2352+             ) / (Z_RECIP_NUMERATOR / S3L_F);
2353+
2354+            b1PC.stepScaled =
2355+              (nextValue - b1PC.valueScaled) / S3L_PC_APPROX_LENGTH;
2356+          }
2357+          else
2358+          {
2359+            /* A special case where we'd be interpolating outside the triangle.
2360+               It seems like a valid approach at first, but it creates a bug
2361+               in a case when the rasaterized triangle is near screen 0 and can
2362+               actually never reach the extrapolated screen position. So we
2363+               have to clamp to the actual end of the triangle here. */
2364+
2365+            S3L_Unit maxI = S3L_nonZero(rowLength - i);
2366+
2367+            S3L_Unit nextDepthScaled =
2368+              (
2369+              Z_RECIP_NUMERATOR /
2370+              S3L_nonZero(rRecipZ)
2371+              ) << S3L_FAST_LERP_QUALITY;
2372+
2373+            depthPC.stepScaled =
2374+              (nextDepthScaled - depthPC.valueScaled) / maxI;
2375+
2376+            S3L_Unit nextValue = 
2377+             ( 
2378+               rOverZ
2379+               * nextDepthScaled
2380+             ) / (Z_RECIP_NUMERATOR / S3L_F);
2381+
2382+            b0PC.stepScaled =
2383+              (nextValue - b0PC.valueScaled) / maxI;
2384+
2385+            b1PC.stepScaled =
2386+              -1 * b1PC.valueScaled / maxI;
2387+          }
2388+        }
2389+
2390+        p.depth = S3L_getFastLerpValue(depthPC);
2391+  #else
2392+        p.depth = S3L_getFastLerpValue(depthFLS);
2393+        S3L_stepFastLerp(depthFLS);
2394+  #endif
2395+#else   // !S3L_COMPUTE_DEPTH
2396+        p.depth = (tPointSS->z + lPointSS->z + rPointSS->z) / 3;
2397+#endif
2398+
2399+#if S3L_Z_BUFFER
2400+        p.previousZ = S3L_zBuffer[zBufferIndex];
2401+
2402+        zBufferIndex++;
2403+
2404+        if (!S3L_zTest(p.x,p.y,p.depth))
2405+          testsPassed = 0;        
2406+#endif
2407+
2408+        if (testsPassed)
2409+        {
2410+#if !S3L_FLAT
2411+  #if S3L_PERSPECTIVE_CORRECTION == 0
2412+          *barycentric0 = S3L_getFastLerpValue(b0FLS);
2413+          *barycentric1 = S3L_getFastLerpValue(b1FLS);
2414+  #elif S3L_PERSPECTIVE_CORRECTION == 1
2415+          *barycentric0 =
2416+           ( 
2417+             S3L_interpolateFrom0(rOverZ,i,rowLength)
2418+             * p.depth
2419+           ) / (Z_RECIP_NUMERATOR / S3L_F);
2420+
2421+          *barycentric1 =
2422+           ( 
2423+             (lOverZ - S3L_interpolateFrom0(lOverZ,i,rowLength))
2424+             * p.depth
2425+           ) / (Z_RECIP_NUMERATOR / S3L_F);
2426+  #elif S3L_PERSPECTIVE_CORRECTION == 2
2427+          *barycentric0 = S3L_getFastLerpValue(b0PC);
2428+          *barycentric1 = S3L_getFastLerpValue(b1PC);
2429+  #endif
2430+
2431+          *barycentric2 =
2432+            S3L_F - *barycentric0 - *barycentric1;
2433+#endif
2434+
2435+#if S3L_NEAR_CROSS_STRATEGY == 3
2436+          if (_S3L_projectedTriangleState != 0)
2437+          {
2438+            S3L_Unit newBarycentric[3];
2439+
2440+            newBarycentric[0] = S3L_interpolateBarycentric(
2441+              _S3L_triangleRemapBarycentrics[0].x,
2442+              _S3L_triangleRemapBarycentrics[1].x,
2443+              _S3L_triangleRemapBarycentrics[2].x,
2444+              p.barycentric); 
2445+
2446+            newBarycentric[1] = S3L_interpolateBarycentric(
2447+              _S3L_triangleRemapBarycentrics[0].y,
2448+              _S3L_triangleRemapBarycentrics[1].y,
2449+              _S3L_triangleRemapBarycentrics[2].y,
2450+              p.barycentric); 
2451+
2452+            newBarycentric[2] = S3L_interpolateBarycentric(
2453+              _S3L_triangleRemapBarycentrics[0].z,
2454+              _S3L_triangleRemapBarycentrics[1].z,
2455+              _S3L_triangleRemapBarycentrics[2].z,
2456+              p.barycentric); 
2457+
2458+            p.barycentric[0] = newBarycentric[0];
2459+            p.barycentric[1] = newBarycentric[1];
2460+            p.barycentric[2] = newBarycentric[2];
2461+          }
2462+#endif
2463+          S3L_PIXEL_FUNCTION(&p);
2464+        } // tests passed
2465+
2466+#if !S3L_FLAT
2467+  #if S3L_PERSPECTIVE_CORRECTION
2468+          i++;
2469+    #if S3L_PERSPECTIVE_CORRECTION == 2
2470+          rowCount++;
2471+     
2472+          S3L_stepFastLerp(depthPC);
2473+          S3L_stepFastLerp(b0PC);
2474+          S3L_stepFastLerp(b1PC);
2475+    #endif
2476+  #else
2477+          S3L_stepFastLerp(b0FLS);
2478+          S3L_stepFastLerp(b1FLS);
2479+  #endif
2480+#endif
2481+      } // inner loop
2482+    } // y clipping
2483+
2484+#if !S3L_FLAT
2485+    S3L_stepFastLerp(lSideFLS);
2486+    S3L_stepFastLerp(rSideFLS);
2487+
2488+  #if S3L_COMPUTE_LERP_DEPTH
2489+    S3L_stepFastLerp(lDepthFLS);
2490+    S3L_stepFastLerp(rDepthFLS);
2491+  #endif
2492+#endif
2493+
2494+    ++currentY;
2495+  } // row drawing
2496+
2497+  #undef manageSplit
2498+  #undef initPC
2499+  #undef initSide
2500+  #undef stepSide
2501+  #undef Z_RECIP_NUMERATOR 
2502+}
2503+
2504+void S3L_rotate2DPoint(S3L_Unit *x, S3L_Unit *y, S3L_Unit angle)
2505+{
2506+  if (angle < S3L_SIN_TABLE_UNIT_STEP)
2507+    return; // no visible rotation
2508+
2509+  S3L_Unit angleSin = S3L_sin(angle);
2510+  S3L_Unit angleCos = S3L_cos(angle);
2511+
2512+  S3L_Unit xBackup = *x;
2513+
2514+  *x =
2515+    (angleCos * (*x)) / S3L_F -
2516+    (angleSin * (*y)) / S3L_F;
2517+
2518+  *y =
2519+    (angleSin * xBackup) / S3L_F +
2520+    (angleCos * (*y)) / S3L_F;
2521+}
2522+
2523+void S3L_makeWorldMatrix(S3L_Transform3D worldTransform, S3L_Mat4 m)
2524+{
2525+  S3L_makeScaleMatrix(
2526+    worldTransform.scale.x,
2527+    worldTransform.scale.y,
2528+    worldTransform.scale.z,
2529+    m);
2530+
2531+  S3L_Mat4 t;
2532+
2533+  S3L_makeRotationMatrixZXY(
2534+    worldTransform.rotation.x,
2535+    worldTransform.rotation.y,
2536+    worldTransform.rotation.z,
2537+    t);
2538+
2539+  S3L_mat4Xmat4(m,t);
2540+
2541+  S3L_makeTranslationMat(
2542+    worldTransform.translation.x,
2543+    worldTransform.translation.y,
2544+    worldTransform.translation.z,
2545+    t);
2546+
2547+  S3L_mat4Xmat4(m,t);
2548+}
2549+
2550+void S3L_mat4Transpose(S3L_Mat4 m)
2551+{
2552+  S3L_Unit tmp;
2553+
2554+  for (uint8_t y = 0; y < 3; ++y)
2555+    for (uint8_t x = 1 + y; x < 4; ++x)
2556+    {
2557+      tmp = m[x][y];
2558+      m[x][y] = m[y][x];
2559+      m[y][x] = tmp;
2560+    }
2561+}
2562+
2563+void S3L_makeCameraMatrix(S3L_Transform3D cameraTransform, S3L_Mat4 m)
2564+{
2565+  S3L_makeTranslationMat(
2566+    -1 * cameraTransform.translation.x,
2567+    -1 * cameraTransform.translation.y,
2568+    -1 * cameraTransform.translation.z,
2569+    m);
2570+
2571+  S3L_Mat4 r;
2572+
2573+  S3L_makeRotationMatrixZXY(
2574+    cameraTransform.rotation.x,
2575+    cameraTransform.rotation.y,
2576+    cameraTransform.rotation.z,
2577+    r);
2578+
2579+  S3L_mat4Transpose(r); // transposing creates an inverse transform
2580+
2581+  S3L_Mat4 s;
2582+
2583+  S3L_makeScaleMatrix(
2584+  cameraTransform.scale.x,
2585+  cameraTransform.scale.y,
2586+  cameraTransform.scale.z,s);
2587+    
2588+  S3L_mat4Xmat4(m,r);
2589+  S3L_mat4Xmat4(m,s);
2590+}
2591+
2592+int8_t S3L_triangleWinding(
2593+  S3L_ScreenCoord x0,
2594+  S3L_ScreenCoord y0, 
2595+  S3L_ScreenCoord x1,
2596+  S3L_ScreenCoord y1,
2597+  S3L_ScreenCoord x2,
2598+  S3L_ScreenCoord y2)
2599+{
2600+  int32_t winding =
2601+    (y1 - y0) * (x2 - x1) - (x1 - x0) * (y2 - y1);
2602+    // ^ cross product for points with z == 0
2603+
2604+  return winding > 0 ? 1 : (winding < 0 ? -1 : 0);
2605+}
2606+
2607+/** Checks if given triangle (in Screen Space) is at least partially visible,
2608+  i.e. returns false if the triangle is either completely outside the frustum
2609+  (left, right, top, bottom, near) or is invisible due to backface culling. */
2610+static inline int8_t S3L_triangleIsVisible(
2611+  S3L_Vec4 p0,
2612+  S3L_Vec4 p1,
2613+  S3L_Vec4 p2,
2614+  uint8_t backfaceCulling)
2615+{
2616+  #define clipTest(c,cmp,v)\
2617+    (p0.c cmp (v) && p1.c cmp (v) && p2.c cmp (v))
2618+
2619+  if ( // outside frustum?
2620+#if S3L_NEAR_CROSS_STRATEGY == 0
2621+      p0.z <= S3L_NEAR || p1.z <= S3L_NEAR || p2.z <= S3L_NEAR ||
2622+      // ^ partially in front of NEAR?
2623+#else
2624+      clipTest(z,<=,S3L_NEAR) || // completely in front of NEAR?
2625+#endif
2626+      clipTest(x,<,0) ||
2627+      clipTest(x,>=,S3L_RESOLUTION_X) ||
2628+      clipTest(y,<,0) ||
2629+      clipTest(y,>,S3L_RESOLUTION_Y)
2630+    )
2631+    return 0;
2632+
2633+  #undef clipTest
2634+
2635+  if (backfaceCulling != 0)
2636+  {
2637+    int8_t winding =
2638+      S3L_triangleWinding(p0.x,p0.y,p1.x,p1.y,p2.x,p2.y);
2639+
2640+    if ((backfaceCulling == 1 && winding > 0) ||
2641+        (backfaceCulling == 2 && winding < 0))
2642+      return 0;
2643+  }
2644+
2645+  return 1;
2646+}
2647+
2648+#if S3L_SORT != 0
2649+typedef struct
2650+{
2651+  uint8_t modelIndex;
2652+  S3L_Index triangleIndex;
2653+  uint16_t sortValue;
2654+} _S3L_TriangleToSort;
2655+
2656+_S3L_TriangleToSort S3L_sortArray[S3L_MAX_TRIANGES_DRAWN];
2657+uint16_t S3L_sortArrayLength;
2658+#endif
2659+
2660+void _S3L_projectVertex(const S3L_Model3D *model, S3L_Index triangleIndex,
2661+  uint8_t vertex, S3L_Mat4 projectionMatrix, S3L_Vec4 *result)
2662+{
2663+  uint32_t vertexIndex = model->triangles[triangleIndex * 3 + vertex] * 3;
2664+
2665+  result->x = model->vertices[vertexIndex];
2666+  result->y = model->vertices[vertexIndex + 1];
2667+  result->z = model->vertices[vertexIndex + 2];
2668+  result->w = S3L_F; // needed for translation 
2669+ 
2670+  S3L_vec3Xmat4(result,projectionMatrix);
2671+
2672+  result->w = result->z;
2673+  /* We'll keep the non-clamped z in w for sorting. */ 
2674+}
2675+
2676+void _S3L_mapProjectedVertexToScreen(S3L_Vec4 *vertex, S3L_Unit focalLength)
2677+{
2678+  vertex->z = vertex->z >= S3L_NEAR ? vertex->z : S3L_NEAR;
2679+  /* ^ This firstly prevents zero division in the follwoing z-divide and
2680+    secondly "pushes" vertices that are in front of near a little bit forward,
2681+    which makes them behave a bit better. If all three vertices end up exactly
2682+    on NEAR, the triangle will be culled. */ 
2683+
2684+  S3L_perspectiveDivide(vertex,focalLength);
2685+      
2686+  S3L_ScreenCoord sX, sY;
2687+      
2688+  S3L_mapProjectionPlaneToScreen(*vertex,&sX,&sY);
2689+   
2690+  vertex->x = sX;
2691+  vertex->y = sY;
2692+}
2693+
2694+/** Projects a triangle to the screen. If enabled, a triangle can be potentially
2695+  subdivided into two if it crosses the near plane, in which case two projected
2696+  triangles are returned (the info about splitting or cutting the triangle is
2697+  passed in global variables, see above). */
2698+void _S3L_projectTriangle(
2699+  const S3L_Model3D *model,
2700+  S3L_Index triangleIndex,
2701+  S3L_Mat4 matrix,
2702+  uint32_t focalLength,
2703+  S3L_Vec4 transformed[6])
2704+{
2705+  _S3L_projectVertex(model,triangleIndex,0,matrix,&(transformed[0]));
2706+  _S3L_projectVertex(model,triangleIndex,1,matrix,&(transformed[1]));
2707+  _S3L_projectVertex(model,triangleIndex,2,matrix,&(transformed[2]));
2708+  _S3L_projectedTriangleState = 0;
2709+
2710+#if S3L_NEAR_CROSS_STRATEGY == 2 || S3L_NEAR_CROSS_STRATEGY == 3
2711+  uint8_t infront = 0;
2712+  uint8_t behind = 0;
2713+  uint8_t infrontI[3];
2714+  uint8_t behindI[3];
2715+
2716+  for (uint8_t i = 0; i < 3; ++i)
2717+    if (transformed[i].z < S3L_NEAR)
2718+    {
2719+      infrontI[infront] = i;
2720+      infront++;
2721+    }
2722+    else
2723+    {
2724+      behindI[behind] = i;
2725+      behind++;
2726+    }
2727+
2728+#if S3L_NEAR_CROSS_STRATEGY == 3
2729+    for (int i = 0; i < 3; ++i)
2730+      S3L_vec4Init(&(_S3L_triangleRemapBarycentrics[i]));
2731+
2732+    _S3L_triangleRemapBarycentrics[0].x = S3L_F;
2733+    _S3L_triangleRemapBarycentrics[1].y = S3L_F;
2734+    _S3L_triangleRemapBarycentrics[2].z = S3L_F;
2735+#endif
2736+
2737+#define interpolateVertex \
2738+  S3L_Unit ratio =\
2739+    ((transformed[be].z - S3L_NEAR) * S3L_F) /\
2740+    (transformed[be].z - transformed[in].z);\
2741+  transformed[in].x = transformed[be].x - \
2742+    ((transformed[be].x - transformed[in].x) * ratio) /\
2743+      S3L_F;\
2744+  transformed[in].y = transformed[be].y -\
2745+    ((transformed[be].y - transformed[in].y) * ratio) /\
2746+      S3L_F;\
2747+  transformed[in].z = S3L_NEAR;\
2748+  if (beI != 0) {\
2749+    beI->x = (beI->x * ratio) / S3L_F;\
2750+    beI->y = (beI->y * ratio) / S3L_F;\
2751+    beI->z = (beI->z * ratio) / S3L_F;\
2752+    ratio = S3L_F - ratio;\
2753+    beI->x += (beB->x * ratio) / S3L_F;\
2754+    beI->y += (beB->y * ratio) / S3L_F;\
2755+    beI->z += (beB->z * ratio) / S3L_F; }
2756+  
2757+  if (infront == 2)
2758+  {
2759+    // shift the two vertices forward along the edge
2760+    for (uint8_t i = 0; i < 2; ++i)
2761+    {
2762+      uint8_t be = behindI[0], in = infrontI[i];
2763+
2764+#if S3L_NEAR_CROSS_STRATEGY == 3
2765+      S3L_Vec4 *beI = &(_S3L_triangleRemapBarycentrics[in]),
2766+               *beB = &(_S3L_triangleRemapBarycentrics[be]);
2767+#else
2768+      S3L_Vec4 *beI = 0, *beB = 0;
2769+#endif
2770+
2771+      interpolateVertex
2772+
2773+      _S3L_projectedTriangleState = 1;
2774+    }
2775+  }
2776+  else if (infront == 1)
2777+  {
2778+    // create another triangle and do the shifts
2779+    transformed[3] = transformed[behindI[1]];
2780+    transformed[4] = transformed[infrontI[0]];
2781+    transformed[5] = transformed[infrontI[0]];
2782+
2783+#if S3L_NEAR_CROSS_STRATEGY == 3
2784+    _S3L_triangleRemapBarycentrics[3] =
2785+      _S3L_triangleRemapBarycentrics[behindI[1]];
2786+    _S3L_triangleRemapBarycentrics[4] =
2787+      _S3L_triangleRemapBarycentrics[infrontI[0]];
2788+    _S3L_triangleRemapBarycentrics[5] =
2789+      _S3L_triangleRemapBarycentrics[infrontI[0]];
2790+#endif
2791+
2792+    for (uint8_t i = 0; i < 2; ++i)
2793+    {
2794+      uint8_t be = behindI[i], in = i + 4;
2795+
2796+#if S3L_NEAR_CROSS_STRATEGY == 3
2797+      S3L_Vec4 *beI = &(_S3L_triangleRemapBarycentrics[in]),
2798+               *beB = &(_S3L_triangleRemapBarycentrics[be]);
2799+#else
2800+      S3L_Vec4 *beI = 0, *beB = 0;
2801+#endif
2802+
2803+      interpolateVertex
2804+    }
2805+
2806+#if S3L_NEAR_CROSS_STRATEGY == 3
2807+    _S3L_triangleRemapBarycentrics[infrontI[0]] = 
2808+      _S3L_triangleRemapBarycentrics[4];
2809+#endif
2810+
2811+    transformed[infrontI[0]] = transformed[4];
2812+
2813+    _S3L_mapProjectedVertexToScreen(&transformed[3],focalLength);
2814+    _S3L_mapProjectedVertexToScreen(&transformed[4],focalLength);
2815+    _S3L_mapProjectedVertexToScreen(&transformed[5],focalLength);
2816+
2817+    _S3L_projectedTriangleState = 2;
2818+  }
2819+
2820+#undef interpolateVertex
2821+#endif // S3L_NEAR_CROSS_STRATEGY == 2
2822+
2823+  _S3L_mapProjectedVertexToScreen(&transformed[0],focalLength);
2824+  _S3L_mapProjectedVertexToScreen(&transformed[1],focalLength);
2825+  _S3L_mapProjectedVertexToScreen(&transformed[2],focalLength);
2826+}
2827+
2828+void S3L_drawScene(S3L_Scene scene)
2829+{
2830+  S3L_Mat4 matFinal, matCamera;
2831+  S3L_Vec4 transformed[6]; // transformed triangle coords, for 2 triangles
2832+
2833+  const S3L_Model3D *model;
2834+  S3L_Index modelIndex, triangleIndex;
2835+
2836+  S3L_makeCameraMatrix(scene.camera.transform,matCamera);
2837+
2838+#if S3L_SORT != 0
2839+  uint16_t previousModel = 0;
2840+  S3L_sortArrayLength = 0;
2841+#endif
2842+
2843+  for (modelIndex = 0; modelIndex < scene.modelCount; ++modelIndex)
2844+  {
2845+    if (!scene.models[modelIndex].config.visible)
2846+      continue;
2847+
2848+#if S3L_SORT != 0
2849+    if (S3L_sortArrayLength >= S3L_MAX_TRIANGES_DRAWN)
2850+      break;
2851+
2852+    previousModel = modelIndex;
2853+#endif
2854+
2855+    if (scene.models[modelIndex].customTransformMatrix == 0)
2856+      S3L_makeWorldMatrix(scene.models[modelIndex].transform,matFinal);
2857+    else
2858+    {
2859+      S3L_Mat4 *m = scene.models[modelIndex].customTransformMatrix;
2860+
2861+      for (int8_t j = 0; j < 4; ++j)
2862+        for (int8_t i = 0; i < 4; ++i)
2863+           matFinal[i][j] = (*m)[i][j];
2864+    }
2865+
2866+    S3L_mat4Xmat4(matFinal,matCamera);
2867+
2868+    S3L_Index triangleCount = scene.models[modelIndex].triangleCount;
2869+
2870+    triangleIndex = 0;
2871+      
2872+    model = &(scene.models[modelIndex]);
2873+    
2874+    while (triangleIndex < triangleCount)
2875+    {
2876+      /* Some kind of cache could be used in theory to not project perviously
2877+         already projected vertices, but after some testing this was abandoned,
2878+         no gain was seen. */
2879+
2880+      _S3L_projectTriangle(model,triangleIndex,matFinal,
2881+        scene.camera.focalLength,transformed);
2882+
2883+      if (S3L_triangleIsVisible(transformed[0],transformed[1],transformed[2],
2884+         model->config.backfaceCulling))
2885+      {
2886+#if S3L_SORT == 0
2887+        // without sorting draw right away
2888+        S3L_drawTriangle(transformed[0],transformed[1],transformed[2],modelIndex,
2889+          triangleIndex);
2890+
2891+        if (_S3L_projectedTriangleState == 2) // draw potential subtriangle
2892+        {
2893+#if S3L_NEAR_CROSS_STRATEGY == 3
2894+          _S3L_triangleRemapBarycentrics[0] = _S3L_triangleRemapBarycentrics[3];
2895+          _S3L_triangleRemapBarycentrics[1] = _S3L_triangleRemapBarycentrics[4];
2896+          _S3L_triangleRemapBarycentrics[2] = _S3L_triangleRemapBarycentrics[5];
2897+#endif
2898+
2899+          S3L_drawTriangle(transformed[3],transformed[4],transformed[5],
2900+          modelIndex, triangleIndex);
2901+        }
2902+#else
2903+
2904+        if (S3L_sortArrayLength >= S3L_MAX_TRIANGES_DRAWN)
2905+          break;
2906+
2907+        // with sorting add to a sort list
2908+        S3L_sortArray[S3L_sortArrayLength].modelIndex = modelIndex;
2909+        S3L_sortArray[S3L_sortArrayLength].triangleIndex = triangleIndex;
2910+        S3L_sortArray[S3L_sortArrayLength].sortValue = S3L_zeroClamp(
2911+          transformed[0].w + transformed[1].w + transformed[2].w) >> 2;
2912+        /* ^ 
2913+           The w component here stores non-clamped z.
2914+ 
2915+           As a simple approximation we sort by the triangle center point,
2916+           which is a mean coordinate -- we don't actually have to divide by 3
2917+           (or anything), that is unnecessary for sorting! We shift by 2 just
2918+           as a fast operation to prevent overflow of the sum over uint_16t. */
2919+
2920+        S3L_sortArrayLength++;
2921+#endif
2922+      }
2923+
2924+      triangleIndex++;
2925+    }
2926+  }
2927+
2928+#if S3L_SORT != 0
2929+
2930+  #if S3L_SORT == 1
2931+    #define cmp <
2932+  #else
2933+    #define cmp >
2934+  #endif
2935+
2936+  /* Sort the triangles. We use insertion sort, because it has many advantages,
2937+  especially for smaller arrays (better than bubble sort, in-place, stable,
2938+  simple, ...). */
2939+
2940+  for (int16_t i = 1; i < S3L_sortArrayLength; ++i)
2941+  {
2942+    _S3L_TriangleToSort tmp = S3L_sortArray[i];
2943+ 
2944+    int16_t j = i - 1;
2945+
2946+    while (j >= 0 && S3L_sortArray[j].sortValue cmp tmp.sortValue)
2947+    {
2948+      S3L_sortArray[j + 1] = S3L_sortArray[j];
2949+      j--;
2950+    }
2951+
2952+    S3L_sortArray[j + 1] = tmp;
2953+  }
2954+
2955+  #undef cmp
2956+
2957+  for (S3L_Index i = 0; i < S3L_sortArrayLength; ++i) // draw sorted triangles
2958+  {
2959+    modelIndex = S3L_sortArray[i].modelIndex;
2960+    triangleIndex = S3L_sortArray[i].triangleIndex;
2961+
2962+    model = &(scene.models[modelIndex]);
2963+
2964+    if (modelIndex != previousModel)
2965+    {
2966+      // only recompute the matrix when the model has changed
2967+      S3L_makeWorldMatrix(model->transform,matFinal);
2968+      S3L_mat4Xmat4(matFinal,matCamera);
2969+      previousModel = modelIndex;
2970+    }
2971+
2972+    /* Here we project the points again, which is redundant and slow as they've
2973+       already been projected above, but saving the projected points would
2974+       require a lot of memory, which for small resolutions could be even
2975+       worse than z-bufer. So this seems to be the best way memory-wise. */
2976+
2977+    _S3L_projectTriangle(model,triangleIndex,matFinal,scene.camera.focalLength,
2978+      transformed);
2979+
2980+    S3L_drawTriangle(transformed[0],transformed[1],transformed[2],modelIndex,
2981+      triangleIndex);
2982+        
2983+    if (_S3L_projectedTriangleState == 2)
2984+    {
2985+#if S3L_NEAR_CROSS_STRATEGY == 3
2986+      _S3L_triangleRemapBarycentrics[0] = _S3L_triangleRemapBarycentrics[3];
2987+      _S3L_triangleRemapBarycentrics[1] = _S3L_triangleRemapBarycentrics[4];
2988+      _S3L_triangleRemapBarycentrics[2] = _S3L_triangleRemapBarycentrics[5];
2989+#endif
2990+
2991+      S3L_drawTriangle(transformed[3],transformed[4],transformed[5],
2992+      modelIndex, triangleIndex);
2993+    }
2994+  }
2995+#endif
2996+}
2997+
2998+#endif // guard
2999+