diff --git a/programs/pokitto/carModel.h b/programs/pokitto/carModel.h new file mode 100644 index 0000000..4c4008e --- /dev/null +++ b/programs/pokitto/carModel.h @@ -0,0 +1,104 @@ +#ifndef CAR_MODEL_H +#define CAR_MODEL_H + +#define CAR_VERTEX_COUNT 12 +const S3L_Unit carVertices[CAR_VERTEX_COUNT * 3] = { + -51, 14, -108, // 0 + -31, 103, -92, // 3 + -31, 103, -3, // 6 + 51, 14, -108, // 9 + 31, 103, -92, // 12 + 31, 103, -3, // 15 + -48, 59, 31, // 18 + 48, 59, 31, // 21 + -40, 52, 86, // 24 + -44, 14, 86, // 27 + 44, 14, 86, // 30 + 40, 52, 86 // 33 +}; // carVertices + +#define CAR_TRIANGLE_COUNT 18 +const S3L_Index carTriangleIndices[CAR_TRIANGLE_COUNT * 3] = { + 4, 3, 5, // 0 + 2, 7, 6, // 3 + 1, 0, 4, // 6 + 7, 5, 3, // 9 + 2, 4, 5, // 12 + 2, 0, 1, // 15 + 9, 6, 8, // 18 + 7, 8, 6, // 21 + 3, 4, 0, // 24 + 9, 11, 10, // 27 + 7, 3, 10, // 30 + 0, 6, 9, // 33 + 6, 0, 2, // 36 + 10, 11, 7, // 39 + 2, 5, 7, // 42 + 2, 1, 4, // 45 + 7, 11, 8, // 48 + 9, 8, 11 // 51 +}; // carTriangleIndices + +#define CAR_UV_COUNT 24 +const S3L_Unit carUVs[CAR_UV_COUNT * 2] = { + 451, 476, // 0 + 459, 509, // 2 + 422, 477, // 4 + 422, 476, // 6 + 409, 451, // 8 + 409, 476, // 10 + 451, 476, // 12 + 484, 476, // 14 + 451, 451, // 16 + 409, 492, // 18 + 422, 451, // 20 + 422, 477, // 22 + 459, 509, // 24 + 451, 476, // 26 + 398, 509, // 28 + 409, 492, // 30 + 398, 493, // 32 + 397, 476, // 34 + 484, 451, // 36 + 386, 476, // 38 + 397, 451, // 40 + 386, 451, // 42 + 398, 509, // 44 + 398, 493 // 46 +}; // carUVs + +#define CAR_UV_INDEX_COUNT 18 +const S3L_Index carUVIndices[CAR_UV_INDEX_COUNT * 3] = { + 0, 1, 2, // 0 + 3, 4, 5, // 3 + 6, 7, 8, // 6 + 9, 2, 1, // 9 + 3, 8, 10, // 12 + 11, 12, 13, // 15 + 14, 15, 16, // 18 + 4, 17, 5, // 21 + 18, 8, 7, // 24 + 19, 20, 21, // 27 + 9, 1, 22, // 30 + 12, 15, 14, // 33 + 15, 12, 11, // 36 + 22, 23, 9, // 39 + 3, 10, 4, // 42 + 3, 6, 8, // 45 + 4, 20, 17, // 48 + 19, 17, 20 // 51 +}; // carUVIndices + +S3L_Model3D carModel; + +void carModelInit() +{ + S3L_initModel3D( + carVertices, + CAR_VERTEX_COUNT, + carTriangleIndices, + CAR_TRIANGLE_COUNT, + &carModel); +} + +#endif // guard diff --git a/programs/pokitto/city.bin b/programs/pokitto/city.bin new file mode 100755 index 0000000..f38e447 Binary files /dev/null and b/programs/pokitto/city.bin differ diff --git a/programs/pokitto/level.bin b/programs/pokitto/level.bin new file mode 100755 index 0000000..d8b7159 Binary files /dev/null and b/programs/pokitto/level.bin differ diff --git a/programs/pokitto/level.cpp b/programs/pokitto/level.cpp new file mode 100644 index 0000000..2488ebe --- /dev/null +++ b/programs/pokitto/level.cpp @@ -0,0 +1,243 @@ +/* + Example program of small3dlib for Pokitto -- Quake-like level. + + author: Miloslav Ciz + license: CC0 1.0 +*/ + +#include "Pokitto.h" + +#define SUBSAMPLE 3 + +#if 1 // This can switch between a textured and flat mode. + #define S3L_Z_BUFFER 2 + #define S3L_SORT 0 + #define S3L_STENCIL_BUFFER 0 + #define S3L_FLAT 0 + #define S3L_PERSPECTIVE_CORRECTION 2 +#else + #define S3L_Z_BUFFER 2 + #define S3L_SORT 0 + #define S3L_STENCIL_BUFFER 0 + #define S3L_FLAT 1 + #define S3L_MAX_TRIANGES_DRAWN 200 +#endif + +#define S3L_PIXEL_FUNCTION pixelFunc + +// Because we'll be writing pixels as 2x2, define the resolution one smaller. +#define BASE_W 109 +#define BASE_H 87 + +#define S3L_RESOLUTION_X (BASE_W - BASE_W / SUBSAMPLE) +#define S3L_RESOLUTION_Y (BASE_H - BASE_H / SUBSAMPLE) + +#define S3L_STRICT_NEAR_CULLING 0 + +#define S3L_COMPUTE_DEPTH 1 // for fog + +#define S3L_REDUCED_Z_BUFFER_GRANULARITY 6 + +#include "small3dlib.h" + +#include "levelTexture1Pal.h" +#include "levelModel.h" + +Pokitto::Core pokitto; + +#if S3L_FLAT +uint8_t triangleColors[LEVEL_TRIANGLE_COUNT]; +#endif + +static inline uint8_t texture(int32_t u, int32_t v) +{ + u = S3L_wrap(u,LEVEL1_TEXTURE_WIDTH); + v = S3L_wrap(v,LEVEL1_TEXTURE_HEIGHT); + + uint32_t index = v * LEVEL1_TEXTURE_WIDTH + u; + + return level1Texture[index]; +} + +S3L_ScreenCoord subsampleMap[BASE_W + SUBSAMPLE]; + +uint32_t previousTriangle = 100; + +static inline uint8_t addIntensity(uint8_t color, int16_t intensity) +{ + int16_t newValue = (color & 0b00001111) + intensity; // value as in HSV + + if (newValue >= 16) + newValue = 15; + + return (color & 0b11110000) | newValue; +} + +static inline uint8_t substractIntensity(uint8_t color, int16_t intensity) +{ + int16_t newValue = (color & 0b00001111) - intensity; // value as in HSV + + if (newValue <= 0) + return 0; + + return (color & 0b11110000) | newValue; +} + +uint8_t c = 0; + +S3L_Vec4 uv0, uv1, uv2; + +S3L_Index material = 0; + +void pixelFunc(S3L_PixelInfo *p) +{ + uint8_t val; + uint8_t *buf = pokitto.display.screenbuffer; + +#if S3L_FLAT + val = triangleColors[p->triangleIndex]; +#else + if (p->triangleIndex != previousTriangle) + { + material = levelMaterials[p->triangleIndex]; + + if (material == 1) + c = 135; + else if (material == 2) + c = 213; + else + S3L_getIndexedTriangleValues(p->triangleIndex,levelUVIndices,levelUVs,2,&uv0,&uv1,&uv2); + + previousTriangle = p->triangleID; + } + + S3L_Unit fog = p->depth >> 9; + + if (material == 0) + { + S3L_Unit uv[2]; + uv[0] = S3L_interpolateBarycentric(uv0.x,uv1.x,uv2.x,p->barycentric); + uv[1] = S3L_interpolateBarycentric(uv0.y,uv1.y,uv2.y,p->barycentric); + + c = texture(uv[0] / 32,uv[1] / 32); + } + + val = substractIntensity(c,fog); +#endif + + buf += subsampleMap[p->y] * 110; + buf += subsampleMap[p->x]; + *buf = val; + + buf++; + *buf = val; + buf += 109; + *buf = val; + buf++; + *buf = val; +} + +S3L_Scene scene; + +void draw() +{ + S3L_newFrame(); + S3L_drawScene(scene); +} + +unsigned short palette[256]; + +int main() +{ + for (uint16_t i = 0; i < BASE_W + SUBSAMPLE; ++i) + subsampleMap[i] = i + i / SUBSAMPLE; + +#if S3L_FLAT + S3L_Vec4 toLight; + S3L_setVec4(&toLight,10,5,7,0); + S3L_normalizeVec3(&toLight); + + for (uint16_t i = 0; i < LEVEL_TRIANGLE_COUNT; ++i) + { + uint8_t c; + + S3L_Vec4 v0, v1, v2; + + S3L_getIndexedTriangleValues( + i, + levelTriangleIndices, + levelVertices,3,&v0,&v1,&v2); + + material = levelMaterials[i]; + + if (material == 1) + c = 38; + else if (material == 2) + c = 53; + else + c = 24; + + S3L_Vec4 normal; + + S3L_triangleNormal(v0,v1,v2,&normal); + + triangleColors[i] = addIntensity(c, + S3L_max(0,(S3L_dotProductVec3(normal,toLight) + S3L_FRACTIONS_PER_UNIT) / 64)); + } + +#endif + + pokitto.begin(); + + pokitto.setFrameRate(60); + + pokitto.display.load565Palette(level1Palette); + + S3L_initCamera(&scene.camera); + + levelModelInit(); + + S3L_initScene(&levelModel,1,&scene); + + while (pokitto.isRunning()) + { + if (pokitto.update()) + { + S3L_Vec4 camF, camR, camU; + int step = 300; + int step2 = 8; + + S3L_rotationToDirections( + scene.camera.transform.rotation, + step, + &camF, + &camR, + &camU); + + if (pokitto.aBtn()) + { + if (pokitto.upBtn()) + scene.camera.transform.rotation.x += 8; + else if (pokitto.downBtn()) + scene.camera.transform.rotation.x -= 8; + else if (pokitto.rightBtn()) + scene.camera.transform.rotation.y += 8; + else if (pokitto.leftBtn()) + scene.camera.transform.rotation.y -= 8; + } + else + { + if (pokitto.upBtn()) + S3L_vec3Add(&(scene.camera.transform.translation),camF); + else if (pokitto.downBtn()) + S3L_vec3Sub(&scene.camera.transform.translation,camF); + else if (pokitto.rightBtn()) + S3L_vec3Add(&scene.camera.transform.translation,camR); + else if (pokitto.leftBtn()) + S3L_vec3Sub(&scene.camera.transform.translation,camR); + } + + draw(); + } + } +} diff --git a/programs/pokitto/levelModel.h b/programs/pokitto/levelModel.h new file mode 100644 index 0000000..bbfb0b1 --- /dev/null +++ b/programs/pokitto/levelModel.h @@ -0,0 +1,1401 @@ +#ifndef LEVEL_MODEL_H +#define LEVEL_MODEL_H + +#define LEVEL_VERTEX_COUNT 143 +const S3L_Unit levelVertices[LEVEL_VERTEX_COUNT * 3] = { + -1291, -25, -517, // 0 + -676, -25, -1131, // 3 + -1905, -25, -517, // 6 + -62, -25, -2360, // 9 + -62, -25, -1131, // 12 + -676, -25, 96, // 15 + -62, -25, -517, // 18 + -1905, -25, 96, // 21 + -1444, -25, -671, // 24 + 552, -639, -2360, // 27 + -1444, 1203, -671, // 30 + 552, -639, -1131, // 33 + -2059, -25, 2400, // 36 + -1291, -25, 711, // 39 + 91, 1203, 1940, // 42 + -676, -25, 711, // 45 + 1780, -639, -1131, // 48 + 1934, -639, -671, // 51 + 2548, 1203, -671, // 54 + 2548, -639, -671, // 57 + 1780, -639, -517, // 60 + 1934, 1203, -2360, // 63 + 91, -25, 711, // 66 + -62, -1868, -1131, // 69 + -62, -1868, -517, // 72 + 1166, -1868, -1131, // 75 + -676, -1868, 96, // 78 + 91, -25, 1940, // 81 + -1291, -1868, 96, // 84 + 1166, -1868, -517, // 87 + 2395, -639, -517, // 90 + 1780, -1868, 96, // 93 + 2395, -1868, 96, // 96 + -2827, -178, 864, // 99 + -2827, -178, 96, // 102 + 3624, -639, -671, // 105 + 1780, -1868, -517, // 108 + -2059, -25, -671, // 111 + -2059, -25, 250, // 114 + -1444, -25, 864, // 117 + -1444, -25, 1786, // 120 + 2395, -25, 711, // 123 + 1934, 1203, -671, // 126 + -1291, 1203, 711, // 129 + 91, 1203, 711, // 132 + -2827, 1203, -517, // 135 + -2827, -25, -1131, // 138 + -62, 1203, 1786, // 141 + -1444, 1203, 1786, // 144 + -1444, 1203, 864, // 147 + -1291, 1203, -517, // 150 + -1905, 1203, 96, // 153 + -676, 1203, -1131, // 156 + -676, 1203, -2360, // 159 + -1905, 1203, -517, // 162 + 2395, 1203, 711, // 165 + 2395, 1203, -517, // 168 + 2548, -25, 711, // 171 + 2548, 1203, 711, // 174 + 1780, 1203, -2360, // 177 + 1780, 1203, -517, // 180 + 3624, -25, 711, // 183 + 3624, -25, 1940, // 186 + -676, -178, 711, // 189 + 3624, 1203, 1940, // 192 + 3009, -639, -2360, // 195 + 3624, -639, -1746, // 198 + 3009, 1203, -2360, // 201 + 3624, 1203, -1746, // 204 + -1905, -1254, 1325, // 207 + -676, -1868, 1325, // 210 + -1291, -1868, 1325, // 213 + 1780, -1868, 1325, // 216 + 2395, -1868, 1325, // 219 + 1780, -639, 1940, // 222 + 2395, -639, 1940, // 225 + 1780, -1868, 1940, // 228 + 2395, -1868, 1940, // 231 + 2395, -1868, -517, // 234 + -2059, 1203, 250, // 237 + -2059, 1203, -671, // 240 + -2827, 1203, -1131, // 243 + -4055, -25, -1131, // 246 + -1905, -1254, 96, // 249 + -2827, -1254, 96, // 252 + -4055, -1254, 96, // 255 + -4055, -1254, 1325, // 258 + -2059, 1203, 2400, // 261 + -676, -639, 1940, // 264 + -1291, -639, 1940, // 267 + -676, -1868, 1940, // 270 + -1291, -178, 1325, // 273 + -4055, -178, 864, // 276 + -4055, 1203, -517, // 279 + -4055, -178, 1325, // 282 + -676, -178, 96, // 285 + 2395, -178, 711, // 288 + 3624, -639, -517, // 291 + 3624, -178, 711, // 294 + 3624, -1868, 1940, // 297 + -1291, -178, 1940, // 300 + 3624, -1868, -517, // 303 + -62, -25, 1786, // 306 + -62, -25, 864, // 309 + -2673, -25, 2400, // 312 + -2673, -25, -1131, // 315 + -62, 1203, 864, // 318 + -2673, 1203, 2400, // 321 + -2673, 1203, -1131, // 324 + -830, -25, -1285, // 327 + -830, 1203, -1285, // 330 + -830, 1203, -2360, // 333 + -4055, -25, -2360, // 336 + -4055, 1203, -2360, // 339 + 3624, -178, 1940, // 342 + -62, -25, 1671, // 345 + -62, -25, 1026, // 348 + -62, 683, 1671, // 351 + -62, 860, 1484, // 354 + -62, 860, 1224, // 357 + -62, 683, 1026, // 360 + 91, -25, 1026, // 363 + 91, -25, 1671, // 366 + 91, 683, 1026, // 369 + 91, 860, 1224, // 372 + 91, 860, 1484, // 375 + 91, 683, 1671, // 378 + -676, 905, -2360, // 381 + -676, -25, -1658, // 384 + -676, 905, -1910, // 387 + -676, 685, -1658, // 390 + -830, 905, -2360, // 393 + -830, -25, -1658, // 396 + -830, 905, -1910, // 399 + -830, 685, -1658, // 402 + 1780, 453, -2360, // 405 + 1780, -639, -1417, // 408 + 1780, 207, -1417, // 411 + 1780, 453, -1586, // 414 + 1934, -639, -1417, // 417 + 1934, 453, -2360, // 420 + 1934, 207, -1417, // 423 + 1934, 453, -1586 // 426 +}; // levelVertices + +#define LEVEL_TRIANGLE_COUNT 293 +const S3L_Index levelTriangleIndices[LEVEL_TRIANGLE_COUNT * 3] = { + 7, 54, 2, // 0 + 6, 0, 1, // 3 + 4, 6, 1, // 6 + 13, 5, 15, // 9 + 3, 59, 9, // 12 + 4, 1, 128, // 15 + 63, 5, 95, // 18 + 26, 6, 24, // 21 + 6, 26, 95, // 24 + 6, 95, 5, // 27 + 43, 15, 44, // 30 + 13, 0, 5, // 33 + 3, 11, 4, // 36 + 109, 10, 8, // 39 + 23, 29, 24, // 42 + 22, 44, 15, // 45 + 3, 53, 59, // 48 + 29, 16, 20, // 51 + 32, 20, 30, // 54 + 29, 20, 36, // 57 + 31, 36, 20, // 60 + 41, 96, 30, // 63 + 60, 137, 138, // 66 + 22, 96, 41, // 69 + 124, 125, 14, // 72 + 13, 51, 7, // 75 + 82, 93, 113, // 78 + 57, 18, 19, // 81 + 13, 15, 43, // 84 + 40, 87, 12, // 87 + 104, 108, 105, // 90 + 2, 0, 7, // 93 + 3, 127, 53, // 96 + 54, 0, 2, // 99 + 41, 56, 55, // 102 + 50, 1, 0, // 105 + 19, 42, 17, // 108 + 56, 20, 60, // 111 + 27, 64, 14, // 114 + 8, 80, 37, // 117 + 110, 134, 133, // 120 + 122, 121, 41, // 123 + 61, 68, 64, // 126 + 62, 57, 61, // 129 + 65, 68, 66, // 132 + 35, 66, 68, // 135 + 140, 21, 67, // 138 + 35, 68, 61, // 141 + 58, 41, 55, // 144 + 61, 64, 62, // 147 + 16, 25, 11, // 150 + 11, 23, 4, // 153 + 23, 11, 25, // 156 + 98, 99, 97, // 159 + 70, 31, 72, // 162 + 69, 84, 83, // 165 + 99, 101, 97, // 168 + 85, 84, 86, // 171 + 77, 73, 99, // 174 + 32, 99, 73, // 177 + 70, 28, 26, // 180 + 75, 72, 73, // 183 + 74, 76, 72, // 186 + 73, 77, 75, // 189 + 30, 78, 32, // 192 + 80, 38, 37, // 195 + 12, 107, 104, // 198 + 46, 108, 81, // 201 + 69, 71, 91, // 204 + 71, 83, 28, // 207 + 34, 63, 95, // 210 + 84, 69, 86, // 213 + 81, 45, 34, // 216 + 34, 45, 33, // 219 + 34, 84, 46, // 222 + 49, 103, 39, // 225 + 79, 39, 38, // 228 + 119, 106, 47, // 231 + 102, 48, 40, // 234 + 88, 71, 70, // 237 + 70, 90, 88, // 240 + 89, 91, 71, // 243 + 26, 28, 95, // 246 + 84, 34, 83, // 249 + 95, 83, 34, // 252 + 28, 83, 95, // 255 + 94, 92, 86, // 258 + 91, 94, 69, // 261 + 112, 82, 113, // 264 + 34, 46, 81, // 267 + 86, 69, 94, // 270 + 86, 92, 85, // 273 + 93, 33, 45, // 276 + 85, 46, 84, // 279 + 115, 102, 40, // 282 + 52, 129, 130, // 285 + 92, 82, 85, // 288 + 57, 62, 41, // 291 + 72, 90, 70, // 294 + 74, 90, 76, // 297 + 97, 78, 30, // 300 + 73, 31, 32, // 303 + 78, 101, 32, // 306 + 98, 30, 96, // 309 + 77, 114, 75, // 312 + 99, 114, 77, // 315 + 100, 91, 89, // 318 + 100, 89, 88, // 321 + 98, 114, 99, // 324 + 61, 19, 35, // 327 + 38, 40, 12, // 330 + 104, 38, 12, // 333 + 105, 38, 104, // 336 + 38, 105, 37, // 339 + 37, 109, 8, // 342 + 105, 132, 109, // 345 + 112, 46, 82, // 348 + 22, 15, 63, // 351 + 94, 33, 92, // 354 + 91, 33, 94, // 357 + 74, 100, 88, // 360 + 36, 31, 29, // 363 + 29, 26, 24, // 366 + 91, 100, 63, // 369 + 35, 19, 66, // 372 + 66, 19, 65, // 375 + 45, 81, 93, // 378 + 93, 81, 113, // 381 + 111, 108, 110, // 384 + 110, 108, 10, // 387 + 10, 108, 80, // 390 + 108, 79, 80, // 393 + 107, 87, 79, // 396 + 87, 49, 79, // 399 + 47, 49, 48, // 402 + 51, 50, 54, // 405 + 43, 44, 50, // 408 + 50, 44, 52, // 411 + 59, 52, 44, // 414 + 59, 53, 52, // 417 + 60, 59, 44, // 420 + 56, 60, 55, // 423 + 60, 44, 55, // 426 + 44, 14, 55, // 429 + 64, 55, 14, // 432 + 55, 64, 58, // 435 + 58, 64, 18, // 438 + 64, 68, 18, // 441 + 68, 67, 18, // 444 + 67, 21, 18, // 447 + 42, 18, 21, // 450 + 46, 112, 105, // 453 + 124, 120, 119, // 456 + 125, 119, 118, // 459 + 126, 118, 117, // 462 + 122, 117, 115, // 465 + 123, 116, 120, // 468 + 116, 122, 115, // 471 + 130, 132, 128, // 474 + 129, 134, 130, // 477 + 127, 133, 129, // 480 + 131, 127, 3, // 483 + 16, 60, 20, // 486 + 141, 17, 42, // 489 + 136, 141, 137, // 492 + 137, 142, 138, // 495 + 138, 140, 135, // 498 + 7, 51, 54, // 501 + 6, 5, 0, // 504 + 3, 4, 128, // 507 + 63, 15, 5, // 510 + 13, 7, 0, // 513 + 3, 9, 11, // 516 + 16, 11, 136, // 519 + 11, 9, 136, // 522 + 109, 110, 10, // 525 + 23, 25, 29, // 528 + 23, 24, 6, // 531 + 6, 4, 23, // 534 + 29, 25, 16, // 537 + 32, 31, 20, // 540 + 60, 16, 137, // 543 + 16, 136, 137, // 546 + 135, 59, 138, // 549 + 59, 60, 138, // 552 + 22, 63, 96, // 555 + 44, 22, 123, // 558 + 22, 121, 123, // 561 + 122, 27, 126, // 564 + 27, 14, 126, // 567 + 44, 123, 124, // 570 + 125, 126, 14, // 573 + 44, 124, 14, // 576 + 13, 43, 51, // 579 + 57, 58, 18, // 582 + 40, 48, 87, // 585 + 104, 107, 108, // 588 + 54, 50, 0, // 591 + 41, 30, 56, // 594 + 50, 52, 1, // 597 + 19, 18, 42, // 600 + 56, 30, 20, // 603 + 27, 62, 64, // 606 + 8, 10, 80, // 609 + 110, 109, 134, // 612 + 109, 132, 134, // 615 + 131, 111, 133, // 618 + 111, 110, 133, // 621 + 62, 27, 41, // 624 + 27, 122, 41, // 627 + 121, 22, 41, // 630 + 65, 67, 68, // 633 + 9, 59, 135, // 636 + 67, 65, 140, // 639 + 58, 57, 41, // 642 + 70, 26, 31, // 645 + 32, 101, 99, // 648 + 70, 71, 28, // 651 + 75, 74, 72, // 654 + 80, 79, 38, // 657 + 12, 87, 107, // 660 + 46, 105, 108, // 663 + 71, 69, 83, // 666 + 34, 33, 63, // 669 + 49, 106, 103, // 672 + 79, 49, 39, // 675 + 116, 103, 120, // 678 + 103, 106, 120, // 681 + 47, 102, 117, // 684 + 102, 115, 117, // 687 + 119, 120, 106, // 690 + 47, 117, 118, // 693 + 118, 119, 47, // 696 + 102, 47, 48, // 699 + 88, 89, 71, // 702 + 93, 92, 33, // 705 + 85, 82, 46, // 708 + 40, 39, 116, // 711 + 39, 103, 116, // 714 + 116, 115, 40, // 717 + 128, 1, 130, // 720 + 1, 52, 130, // 723 + 52, 53, 129, // 726 + 53, 127, 129, // 729 + 92, 93, 82, // 732 + 72, 76, 90, // 735 + 74, 88, 90, // 738 + 97, 101, 78, // 741 + 73, 72, 31, // 744 + 98, 97, 30, // 747 + 61, 57, 19, // 750 + 38, 39, 40, // 753 + 37, 105, 109, // 756 + 3, 105, 112, // 759 + 91, 63, 33, // 762 + 29, 31, 26, // 765 + 65, 19, 139, // 768 + 19, 17, 139, // 771 + 108, 107, 79, // 774 + 87, 48, 49, // 777 + 47, 106, 49, // 780 + 51, 43, 50, // 783 + 124, 123, 120, // 786 + 125, 124, 119, // 789 + 126, 125, 118, // 792 + 122, 126, 117, // 795 + 123, 121, 116, // 798 + 116, 121, 122, // 801 + 130, 134, 132, // 804 + 129, 133, 134, // 807 + 127, 131, 133, // 810 + 128, 132, 3, // 813 + 21, 140, 142, // 816 + 141, 139, 17, // 819 + 42, 21, 142, // 822 + 142, 141, 42, // 825 + 136, 139, 141, // 828 + 137, 141, 142, // 831 + 138, 142, 140, // 834 + 136, 65, 139, // 837 + 136, 9, 65, // 840 + 140, 65, 135, // 843 + 9, 135, 65, // 846 + 105, 3, 132, // 849 + 3, 112, 131, // 852 + 112, 113, 131, // 855 + 81, 111, 113, // 858 + 108, 111, 81, // 861 + 131, 113, 111, // 864 + 75, 114, 74, // 867 + 74, 114, 100, // 870 + 63, 100, 114, // 873 + 114, 98, 63 // 876 +}; // levelTriangleIndices + +const uint8_t levelMaterials[LEVEL_TRIANGLE_COUNT] = { + 0, // 0 + 1, // 1 + 1, // 2 + 1, // 3 + 0, // 4 + 1, // 5 + 0, // 6 + 0, // 7 + 0, // 8 + 0, // 9 + 0, // 10 + 1, // 11 + 1, // 12 + 0, // 13 + 1, // 14 + 0, // 15 + 0, // 16 + 0, // 17 + 2, // 18 + 0, // 19 + 0, // 20 + 0, // 21 + 0, // 22 + 0, // 23 + 0, // 24 + 0, // 25 + 0, // 26 + 0, // 27 + 0, // 28 + 0, // 29 + 0, // 30 + 1, // 31 + 0, // 32 + 0, // 33 + 0, // 34 + 0, // 35 + 0, // 36 + 0, // 37 + 0, // 38 + 0, // 39 + 0, // 40 + 1, // 41 + 0, // 42 + 1, // 43 + 0, // 44 + 0, // 45 + 0, // 46 + 0, // 47 + 0, // 48 + 0, // 49 + 0, // 50 + 0, // 51 + 0, // 52 + 0, // 53 + 1, // 54 + 1, // 55 + 0, // 56 + 1, // 57 + 1, // 58 + 1, // 59 + 1, // 60 + 2, // 61 + 0, // 62 + 0, // 63 + 0, // 64 + 0, // 65 + 0, // 66 + 0, // 67 + 0, // 68 + 1, // 69 + 2, // 70 + 1, // 71 + 0, // 72 + 0, // 73 + 0, // 74 + 0, // 75 + 0, // 76 + 0, // 77 + 0, // 78 + 2, // 79 + 0, // 80 + 0, // 81 + 0, // 82 + 0, // 83 + 0, // 84 + 0, // 85 + 0, // 86 + 0, // 87 + 0, // 88 + 0, // 89 + 0, // 90 + 0, // 91 + 2, // 92 + 1, // 93 + 1, // 94 + 0, // 95 + 0, // 96 + 1, // 97 + 1, // 98 + 0, // 99 + 0, // 100 + 1, // 101 + 1, // 102 + 2, // 103 + 0, // 104 + 0, // 105 + 0, // 106 + 0, // 107 + 0, // 108 + 1, // 109 + 1, // 110 + 1, // 111 + 1, // 112 + 1, // 113 + 1, // 114 + 1, // 115 + 1, // 116 + 0, // 117 + 2, // 118 + 2, // 119 + 0, // 120 + 1, // 121 + 1, // 122 + 2, // 123 + 1, // 124 + 1, // 125 + 2, // 126 + 2, // 127 + 2, // 128 + 2, // 129 + 2, // 130 + 2, // 131 + 2, // 132 + 2, // 133 + 2, // 134 + 2, // 135 + 2, // 136 + 2, // 137 + 2, // 138 + 2, // 139 + 2, // 140 + 2, // 141 + 2, // 142 + 2, // 143 + 2, // 144 + 2, // 145 + 2, // 146 + 2, // 147 + 2, // 148 + 2, // 149 + 2, // 150 + 1, // 151 + 0, // 152 + 0, // 153 + 0, // 154 + 0, // 155 + 0, // 156 + 1, // 157 + 0, // 158 + 0, // 159 + 0, // 160 + 0, // 161 + 0, // 162 + 0, // 163 + 0, // 164 + 0, // 165 + 0, // 166 + 0, // 167 + 1, // 168 + 1, // 169 + 0, // 170 + 1, // 171 + 1, // 172 + 1, // 173 + 1, // 174 + 0, // 175 + 1, // 176 + 0, // 177 + 0, // 178 + 0, // 179 + 2, // 180 + 0, // 181 + 0, // 182 + 0, // 183 + 0, // 184 + 0, // 185 + 0, // 186 + 0, // 187 + 0, // 188 + 0, // 189 + 0, // 190 + 0, // 191 + 0, // 192 + 0, // 193 + 0, // 194 + 0, // 195 + 0, // 196 + 0, // 197 + 0, // 198 + 0, // 199 + 0, // 200 + 0, // 201 + 0, // 202 + 0, // 203 + 0, // 204 + 0, // 205 + 0, // 206 + 0, // 207 + 1, // 208 + 1, // 209 + 1, // 210 + 0, // 211 + 0, // 212 + 0, // 213 + 0, // 214 + 1, // 215 + 1, // 216 + 1, // 217 + 2, // 218 + 0, // 219 + 0, // 220 + 0, // 221 + 1, // 222 + 2, // 223 + 0, // 224 + 0, // 225 + 0, // 226 + 0, // 227 + 0, // 228 + 0, // 229 + 0, // 230 + 0, // 231 + 0, // 232 + 0, // 233 + 2, // 234 + 2, // 235 + 1, // 236 + 1, // 237 + 1, // 238 + 1, // 239 + 0, // 240 + 0, // 241 + 0, // 242 + 0, // 243 + 0, // 244 + 1, // 245 + 0, // 246 + 0, // 247 + 1, // 248 + 2, // 249 + 1, // 250 + 1, // 251 + 1, // 252 + 1, // 253 + 2, // 254 + 1, // 255 + 1, // 256 + 1, // 257 + 2, // 258 + 2, // 259 + 2, // 260 + 2, // 261 + 0, // 262 + 0, // 263 + 0, // 264 + 0, // 265 + 0, // 266 + 1, // 267 + 0, // 268 + 0, // 269 + 0, // 270 + 1, // 271 + 0, // 272 + 0, // 273 + 0, // 274 + 0, // 275 + 0, // 276 + 0, // 277 + 0, // 278 + 1, // 279 + 1, // 280 + 0, // 281 + 0, // 282 + 1, // 283 + 0, // 284 + 0, // 285 + 2, // 286 + 2, // 287 + 0, // 288 + 0, // 289 + 0, // 290 + 2, // 291 + 2 // 292 +}; // levelMaterials + +#define LEVEL_UV_COUNT 344 +const S3L_Unit levelUVs[LEVEL_UV_COUNT * 2] = { + -1655, -1007, // 0 + -2184, -2065, // 2 + -2184, -1007, // 4 + 402, 699, // 6 + -690, 699, // 8 + -144, 1246, // 10 + 402, 1246, // 12 + -690, -393, // 14 + -144, 152, // 16 + -144, -393, // 18 + -3482, -1000, // 20 + -5047, -2047, // 22 + -4005, -478, // 24 + -144, 1714, // 26 + -10129, -912, // 28 + -10653, -1045, // 30 + -10653, -914, // 32 + -10659, 525, // 34 + -11394, -1048, // 36 + -11400, 522, // 38 + -907, -2065, // 40 + -378, -1007, // 42 + 282, -2065, // 44 + 402, 2339, // 46 + 948, 1246, // 48 + 324, -1016, // 50 + -423, -2074, // 52 + -423, -1016, // 54 + 402, 1246, // 56 + 1495, 699, // 58 + 402, 699, // 60 + 282, -1007, // 62 + -2958, -2044, // 64 + 553, -430, // 66 + -60, 942, // 68 + 553, 942, // 70 + 460, -253, // 72 + 21, 729, // 74 + 460, 729, // 76 + -98, -1023, // 78 + -624, -2074, // 80 + -624, -1023, // 82 + -1150, -1023, // 84 + -1838, -992, // 86 + -1838, -857, // 88 + -762, -454, // 90 + -224, -2068, // 92 + 563, -1196, // 94 + 711, -1411, // 96 + -9474, -1040, // 98 + -7510, -901, // 100 + -7509, -1032, // 102 + 724, -1770, // 104 + 948, -1770, // 106 + 1340, -2065, // 108 + -907, -1007, // 110 + -1655, -2065, // 112 + 958, -993, // 114 + 1482, -2037, // 116 + -84, -2039, // 118 + 148, -1013, // 120 + -1037, -2067, // 122 + -1037, -486, // 124 + -3364, -1016, // 126 + -4100, -2066, // 128 + -4104, -1019, // 130 + -4628, -1021, // 132 + -7636, -2081, // 134 + -7640, -1033, // 136 + -1237, 699, // 138 + -1237, 152, // 140 + -2958, -1791, // 142 + -2713, -1007, // 144 + -762, -2068, // 146 + -1838, -2068, // 148 + -2713, -2065, // 150 + -3461, -1007, // 152 + -1564, -2067, // 154 + -1564, -486, // 156 + -224, -454, // 158 + -12971, -1015, // 160 + -9965, -2055, // 162 + -12969, -2059, // 164 + -952, -2074, // 166 + -952, -1016, // 168 + 324, -2074, // 170 + 645, -1628, // 172 + 861, -1818, // 174 + 538, -1247, // 176 + 538, -673, // 178 + 2588, -393, // 180 + -8922, -1008, // 182 + -6831, -2050, // 184 + 3681, -1486, // 186 + 2724, -393, // 188 + 3681, -393, // 190 + -6095, -481, // 192 + -6833, -483, // 194 + -7748, -484, // 196 + -5179, -1409, // 198 + -5178, -2047, // 200 + -6092, -2049, // 202 + 148, -2067, // 204 + 280, -1013, // 206 + 280, -2067, // 208 + -9967, -1010, // 210 + -13495, -520, // 212 + -12975, 519, // 214 + -12455, -520, // 216 + -11935, 519, // 218 + -11935, -1040, // 220 + 11145, -846, // 222 + 10098, 588, // 224 + 12189, -452, // 226 + -144, -940, // 228 + 2041, 152, // 230 + 2041, -940, // 232 + -1237, -940, // 234 + -2057, 152, // 236 + -1237, 152, // 238 + 12187, 591, // 240 + -3150, 152, // 242 + -3150, -940, // 244 + 2588, -1486, // 246 + 2588, -940, // 248 + 3681, -1486, // 250 + 2588, 152, // 252 + -690, 152, // 254 + -144, 152, // 256 + 467, -256, // 258 + 22, 740, // 260 + 467, 740, // 262 + -626, -2067, // 264 + -626, -1015, // 266 + -100, -1015, // 268 + -1140, -1010, // 270 + -614, -1010, // 272 + -614, -2062, // 274 + -614, -2054, // 276 + -607, -1002, // 278 + -81, -1005, // 280 + -1746, -1016, // 282 + -4624, -2068, // 284 + -7771, -1034, // 286 + -7767, -2081, // 288 + 4874, 57, // 290 + 5396, 581, // 292 + 5398, -855, // 294 + -690, -940, // 296 + 1, 2256, // 298 + -500, 501, // 300 + 1, 501, // 302 + -8291, -2083, // 304 + -8820, -907, // 306 + -9474, -909, // 308 + -8823, 9, // 310 + -2494, -2074, // 312 + -3684, -1016, // 314 + -2494, -1016, // 316 + -1746, -2074, // 318 + -3994, -1779, // 320 + -3684, -2074, // 322 + -4477, -2074, // 324 + -2185, -1011, // 326 + -3359, -2063, // 328 + 466, -246, // 330 + 26, 736, // 332 + 466, 736, // 334 + -1153, -1015, // 336 + -627, -1015, // 338 + -627, -2067, // 340 + 5920, -462, // 342 + -10135, 528, // 344 + -9609, 6, // 346 + 3047, -859, // 348 + 2656, -859, // 350 + 3046, 55, // 352 + -86, -994, // 354 + 2001, 53, // 356 + 502, 3259, // 358 + -625, 2256, // 360 + 502, 2256, // 362 + -2057, 1246, // 364 + 402, -1247, // 366 + 402, -1349, // 368 + -827, -1349, // 370 + -3461, -2065, // 372 + -4131, -1808, // 374 + -3914, -1619, // 376 + -144, -1486, // 378 + 8532, -458, // 380 + 6441, 582, // 382 + 8530, 586, // 384 + 13232, 593, // 386 + 13234, -451, // 388 + 2588, 699, // 390 + 3681, 699, // 392 + -500, -3008, // 394 + 502, -2005, // 396 + -500, -2005, // 398 + 9053, 586, // 400 + 10100, -847, // 402 + 9054, -457, // 404 + 5921, -854, // 406 + 6442, -461, // 408 + 2724, 836, // 410 + 3681, 836, // 412 + -1374, 16, // 414 + -1374, -1896, // 416 + -1920, -1896, // 418 + -1920, 1246, // 420 + -1374, 836, // 422 + -280, 1382, // 424 + -827, 836, // 426 + -280, 1714, // 428 + -3150, 2339, // 430 + -3150, 1246, // 432 + -10129, -1043, // 434 + -1001, 3259, // 436 + -625, 3259, // 438 + -1001, 1003, // 440 + 2041, 699, // 442 + -1503, 1003, // 444 + 3681, 1792, // 446 + 3134, 2339, // 448 + 1004, 2256, // 450 + 2007, 3259, // 452 + 2007, 627, // 454 + 1004, 2131, // 456 + 1129, 627, // 458 + 628, 1128, // 460 + 628, 1629, // 462 + -123, 1629, // 464 + -1879, 2131, // 466 + -1879, 1629, // 468 + -625, 1128, // 470 + -1377, 0, // 472 + -1377, 1128, // 474 + 1, 1504, // 476 + 502, 1003, // 478 + 502, 1504, // 480 + -500, 1003, // 482 + -500, -125, // 484 + 1004, 501, // 486 + 2007, -1504, // 488 + 2007, 501, // 490 + 502, -1504, // 492 + 502, -2005, // 494 + -500, -2005, // 496 + -1503, -125, // 498 + -1503, -3008, // 500 + -500, -2131, // 502 + 628, -2131, // 504 + 1505, -3008, // 506 + 2007, -2507, // 508 + 2007, -1629, // 510 + 628, -1629, // 512 + 374, 350, // 514 + 669, 180, // 516 + 374, 180, // 518 + 87, 350, // 520 + 87, 180, // 522 + -197, 350, // 524 + -197, 180, // 526 + -606, -1009, // 528 + -736, -1610, // 530 + -736, -1009, // 532 + -606, -1610, // 534 + -475, -1009, // 536 + -475, -1610, // 538 + 402, -673, // 540 + -345, -1610, // 542 + -475, -1007, // 544 + -345, -1007, // 546 + 185, 515, // 548 + -150, 360, // 550 + -150, 515, // 552 + 638, 515, // 554 + 185, 360, // 556 + -2827, -1790, // 558 + 313, -454, // 560 + -2204, -1213, // 562 + -313, -1003, // 564 + -182, -1722, // 566 + -313, -1722, // 568 + 842, 351, // 570 + 500, 176, // 572 + 500, 351, // 574 + -385, 176, // 576 + -385, 351, // 578 + 948, 2339, // 580 + 2041, 1246, // 582 + 2041, 1499, // 584 + 1495, 1246, // 586 + -11924, 520, // 588 + -11917, -1050, // 590 + -60, -430, // 592 + 21, -253, // 594 + 563, -454, // 596 + 1389, -1411, // 598 + 1389, -2068, // 600 + 553, -1617, // 602 + 553, -1007, // 604 + 1109, -1007, // 606 + 1340, -1007, // 608 + 1109, -1617, // 610 + 645, -1016, // 612 + 1249, -1818, // 614 + 1249, -2074, // 616 + 538, -1486, // 618 + 538, -393, // 620 + -5048, -1409, // 622 + 22, -256, // 624 + -3823, -1016, // 626 + -3823, -1626, // 628 + -4477, -1016, // 630 + -4378, -1626, // 632 + -4378, -1016, // 634 + -4217, -1779, // 636 + -2181, -2059, // 638 + 26, -246, // 640 + -827, -530, // 642 + 402, -530, // 644 + -3914, -1007, // 646 + -4519, -2065, // 648 + -4519, -1808, // 650 + 2041, -1486, // 652 + 502, -3008, // 654 + 2178, 1499, // 656 + 2178, 836, // 658 + -625, 0, // 660 + 669, 350, // 662 + -606, -1610, // 664 + -606, -1009, // 666 + -475, -1610, // 668 + 638, 360, // 670 + -3013, -2067, // 672 + -3013, -1423, // 674 + -2349, -1423, // 676 + -2204, -486, // 678 + -182, -1003, // 680 + 842, 176, // 682 + -2827, -2043, // 684 + -1503, -3008 // 686 +}; // levelUVs + +#define LEVEL_UV_INDEX_COUNT 293 +const S3L_Index levelUVIndices[LEVEL_UV_INDEX_COUNT * 3] = { + 0, 1, 2, // 0 + 3, 4, 5, // 3 + 6, 3, 5, // 6 + 7, 8, 9, // 9 + 10, 11, 12, // 12 + 6, 5, 13, // 15 + 14, 15, 16, // 18 + 17, 18, 19, // 21 + 18, 17, 16, // 24 + 18, 16, 15, // 27 + 20, 21, 22, // 30 + 7, 4, 8, // 33 + 23, 24, 6, // 36 + 25, 26, 27, // 39 + 28, 29, 30, // 42 + 31, 22, 21, // 45 + 10, 32, 11, // 48 + 33, 34, 35, // 51 + 36, 37, 38, // 54 + 39, 40, 41, // 57 + 42, 41, 40, // 60 + 43, 44, 45, // 63 + 46, 47, 48, // 66 + 49, 50, 51, // 69 + 52, 53, 54, // 72 + 55, 56, 0, // 75 + 57, 58, 59, // 78 + 60, 61, 62, // 81 + 55, 21, 20, // 84 + 63, 64, 65, // 87 + 66, 67, 68, // 90 + 69, 4, 70, // 93 + 10, 71, 32, // 96 + 1, 72, 2, // 99 + 43, 73, 74, // 102 + 75, 76, 72, // 105 + 62, 77, 78, // 108 + 73, 79, 46, // 111 + 80, 81, 82, // 114 + 27, 83, 84, // 117 + 85, 86, 87, // 120 + 88, 89, 90, // 123 + 91, 92, 81, // 126 + 93, 94, 95, // 129 + 96, 92, 97, // 132 + 98, 97, 92, // 135 + 99, 100, 101, // 138 + 98, 92, 91, // 141 + 102, 103, 104, // 144 + 91, 81, 105, // 147 + 106, 107, 108, // 150 + 108, 109, 110, // 153 + 109, 108, 107, // 156 + 111, 112, 113, // 159 + 114, 115, 116, // 162 + 117, 118, 119, // 165 + 112, 120, 113, // 168 + 121, 118, 122, // 171 + 123, 124, 125, // 174 + 126, 125, 124, // 177 + 114, 127, 128, // 180 + 129, 130, 131, // 183 + 132, 133, 134, // 186 + 135, 136, 137, // 189 + 138, 139, 140, // 192 + 83, 141, 84, // 195 + 65, 142, 66, // 198 + 143, 67, 144, // 201 + 145, 146, 147, // 204 + 148, 119, 127, // 207 + 149, 150, 151, // 210 + 118, 117, 122, // 213 + 144, 152, 153, // 216 + 153, 152, 154, // 219 + 153, 155, 143, // 222 + 156, 157, 158, // 225 + 159, 158, 141, // 228 + 160, 161, 162, // 231 + 163, 164, 63, // 234 + 165, 166, 167, // 237 + 168, 169, 170, // 240 + 171, 147, 146, // 243 + 17, 172, 16, // 246 + 155, 153, 173, // 249 + 16, 173, 153, // 252 + 172, 173, 16, // 255 + 174, 175, 176, // 258 + 147, 174, 145, // 261 + 177, 57, 59, // 264 + 153, 143, 144, // 267 + 176, 145, 174, // 270 + 176, 175, 178, // 273 + 179, 180, 181, // 276 + 121, 182, 118, // 279 + 183, 184, 185, // 282 + 186, 187, 188, // 285 + 175, 57, 178, // 288 + 94, 93, 90, // 291 + 116, 189, 114, // 294 + 190, 191, 192, // 297 + 113, 193, 194, // 300 + 124, 115, 126, // 303 + 195, 196, 126, // 306 + 197, 198, 199, // 309 + 200, 201, 202, // 312 + 112, 201, 200, // 315 + 203, 147, 171, // 318 + 203, 171, 204, // 321 + 111, 201, 112, // 324 + 95, 205, 206, // 327 + 207, 185, 208, // 330 + 209, 207, 208, // 333 + 210, 207, 209, // 336 + 207, 210, 211, // 339 + 211, 212, 213, // 342 + 210, 214, 212, // 345 + 215, 182, 216, // 348 + 49, 217, 14, // 351 + 218, 180, 219, // 354 + 220, 180, 218, // 357 + 190, 203, 204, // 360 + 221, 115, 29, // 363 + 29, 128, 30, // 366 + 220, 222, 150, // 369 + 206, 205, 223, // 372 + 223, 205, 224, // 375 + 181, 225, 179, // 378 + 179, 225, 226, // 381 + 227, 228, 229, // 384 + 229, 228, 230, // 387 + 230, 228, 231, // 390 + 228, 232, 231, // 393 + 233, 234, 232, // 396 + 234, 235, 232, // 399 + 236, 235, 237, // 402 + 238, 239, 240, // 405 + 241, 242, 239, // 408 + 239, 242, 243, // 411 + 244, 243, 242, // 414 + 244, 245, 243, // 417 + 246, 244, 242, // 420 + 247, 246, 248, // 423 + 246, 242, 248, // 426 + 242, 249, 248, // 429 + 250, 248, 249, // 432 + 248, 250, 251, // 435 + 251, 250, 252, // 438 + 250, 253, 252, // 441 + 253, 254, 252, // 444 + 254, 255, 252, // 447 + 256, 252, 255, // 450 + 182, 215, 210, // 453 + 257, 258, 259, // 456 + 260, 259, 261, // 459 + 262, 261, 263, // 462 + 264, 265, 266, // 465 + 267, 268, 269, // 468 + 270, 88, 183, // 471 + 271, 272, 273, // 474 + 274, 275, 276, // 477 + 277, 278, 274, // 480 + 279, 71, 10, // 483 + 280, 46, 79, // 486 + 281, 78, 77, // 489 + 282, 283, 284, // 492 + 285, 286, 287, // 495 + 287, 288, 289, // 498 + 0, 56, 1, // 501 + 3, 8, 4, // 504 + 23, 6, 13, // 507 + 14, 217, 15, // 510 + 7, 70, 4, // 513 + 23, 290, 24, // 516 + 291, 24, 292, // 519 + 24, 290, 292, // 522 + 25, 85, 26, // 525 + 28, 293, 29, // 528 + 294, 19, 18, // 531 + 18, 295, 294, // 534 + 33, 296, 34, // 537 + 36, 297, 37, // 540 + 46, 280, 47, // 543 + 280, 298, 47, // 546 + 299, 300, 48, // 549 + 300, 46, 48, // 552 + 49, 14, 50, // 555 + 22, 31, 301, // 558 + 31, 302, 301, // 561 + 303, 304, 305, // 564 + 304, 54, 305, // 567 + 22, 301, 52, // 570 + 53, 305, 54, // 573 + 22, 52, 54, // 576 + 55, 20, 56, // 579 + 60, 102, 61, // 582 + 63, 164, 64, // 585 + 66, 142, 67, // 588 + 1, 75, 72, // 591 + 43, 45, 73, // 594 + 75, 186, 76, // 597 + 62, 61, 77, // 600 + 73, 45, 79, // 603 + 80, 105, 81, // 606 + 27, 26, 83, // 609 + 85, 25, 86, // 612 + 25, 306, 86, // 615 + 307, 308, 87, // 618 + 308, 85, 87, // 621 + 93, 309, 90, // 624 + 309, 88, 90, // 627 + 89, 310, 90, // 630 + 96, 101, 92, // 633 + 12, 11, 311, // 636 + 101, 96, 99, // 639 + 102, 60, 103, // 642 + 114, 128, 115, // 645 + 126, 196, 125, // 648 + 114, 148, 127, // 651 + 129, 312, 130, // 654 + 83, 159, 141, // 657 + 65, 64, 142, // 660 + 143, 68, 67, // 663 + 148, 117, 119, // 666 + 149, 180, 150, // 669 + 156, 161, 157, // 672 + 159, 156, 158, // 675 + 313, 157, 314, // 678 + 157, 161, 314, // 681 + 162, 315, 316, // 684 + 315, 317, 316, // 687 + 160, 314, 161, // 690 + 162, 316, 318, // 693 + 318, 160, 162, // 696 + 163, 319, 164, // 699 + 165, 320, 166, // 702 + 179, 219, 180, // 705 + 121, 216, 182, // 708 + 185, 321, 270, // 711 + 321, 322, 270, // 714 + 270, 183, 185, // 717 + 323, 76, 188, // 720 + 76, 186, 188, // 723 + 186, 324, 187, // 726 + 324, 325, 187, // 729 + 175, 58, 57, // 732 + 116, 326, 189, // 735 + 190, 204, 191, // 738 + 113, 120, 193, // 741 + 124, 116, 115, // 744 + 197, 327, 198, // 747 + 95, 94, 205, // 750 + 207, 321, 185, // 753 + 211, 210, 212, // 756 + 23, 210, 215, // 759 + 220, 150, 180, // 762 + 29, 115, 128, // 765 + 224, 205, 328, // 768 + 205, 329, 328, // 771 + 228, 233, 232, // 774 + 234, 237, 235, // 777 + 236, 330, 235, // 780 + 238, 241, 239, // 783 + 257, 331, 258, // 786 + 260, 257, 259, // 789 + 262, 260, 261, // 792 + 264, 332, 265, // 795 + 267, 333, 268, // 798 + 270, 89, 88, // 801 + 271, 334, 272, // 804 + 274, 278, 275, // 807 + 277, 335, 278, // 810 + 13, 214, 23, // 813 + 336, 337, 338, // 816 + 281, 339, 78, // 819 + 77, 336, 338, // 822 + 338, 281, 77, // 825 + 282, 340, 283, // 828 + 285, 341, 286, // 831 + 287, 286, 288, // 834 + 292, 224, 328, // 837 + 292, 290, 224, // 840 + 99, 96, 311, // 843 + 12, 311, 96, // 846 + 210, 23, 214, // 849 + 10, 177, 279, // 852 + 177, 59, 279, // 855 + 225, 227, 226, // 858 + 228, 227, 225, // 861 + 279, 59, 342, // 864 + 202, 201, 190, // 867 + 190, 201, 203, // 870 + 150, 222, 343, // 873 + 343, 197, 150 // 876 +}; // levelUVIndices + +S3L_Model3D levelModel; + +void levelModelInit() +{ + S3L_initModel3D( + levelVertices, + LEVEL_VERTEX_COUNT, + levelTriangleIndices, + LEVEL_TRIANGLE_COUNT, + &levelModel); +} + +#endif // guard diff --git a/programs/pokitto/levelTexture1Pal.h b/programs/pokitto/levelTexture1Pal.h new file mode 100644 index 0000000..ad770d4 --- /dev/null +++ b/programs/pokitto/levelTexture1Pal.h @@ -0,0 +1,72 @@ +#ifndef LEVEL1_TEXTURE_H +#define LEVEL1_TEXTURE_H + +uint16_t level1Palette[256] = { +0,4226,8452,12678,16936,21162,25388,29614,35921,40147,44373,48599,52857,57083, +61309,65535,4193,8418,12611,16836,21029,25254,29447,33672,37865,42090,46283, +50508,54701,58926,63119,65264,2177,6402,8579,12804,14981,19206,21383,25608, +27785,32010,34187,38412,40589,44814,46991,51184,2177,4354,6531,8709,10886,13063, +15240,17418,19595,21772,23949,26127,28304,30481,32658,34804,2178,4356,6534,8712, +10890,13068,15246,17424,19602,21780,23958,26136,28314,30492,32670,34815,2114, +4260,6374,8520,10634,12780,14894,17040,19154,21300,23414,25560,27674,29820, +31934,34079,2114,6276,8390,12552,14666,18828,20942,25104,29266,31380,35542, +37656,41818,43932,48094,50207,4161,8323,12485,16646,20808,24970,29131,33293, +37455,41616,45778,49940,54101,58263,62425,64538,4096,8192,12288,16384,20480, +24576,28672,32768,36864,40960,45056,49152,53248,57344,61440,63488,4192,8384, +12576,16768,20960,25152,29344,33536,37728,41920,46112,50304,54496,58688,62880, +64992,2176,4352,6528,8704,10880,13056,15232,17408,19584,21760,23936,26112,28288, +30464,32640,34784,128,257,385,514,642,771,899,1028,1156,1285,1413,1542,1670, +1799,1927,2024,130,260,390,520,650,780,910,1040,1170,1300,1430,1560,1690,1820, +1950,2047,34,68,102,136,170,204,238,272,306,340,374,408,442,476,510,543,2050, +4100,6150,8200,10250,12300,14350,18448,20498,22548,24598,26648,28698,30748, +32798,36895,4097,8194,12292,16389,20486,24584,28681,32779,36876,40973,45071, +49168,53265,57363,61460,63509 +}; // level1Palette + +#define LEVEL1_TEXTURE_WIDTH 32 +#define LEVEL1_TEXTURE_HEIGHT 32 + +uint8_t level1Texture[1024] = { +20,20,21,20,20,20,20,20,20,20,20,21,21,22,22,21,21,20,21,21,21,21,21,20,21,21, +21,21,21,20,20,20,26,26,24,20,25,25,25,25,25,25,25,24,25,25,24,25,25,21,21,25, +26,25,24,24,25,25,26,26,26,26,26,25,22,22,22,20,25,25,22,22,22,22,22,22,22,22, +23,24,25,23,20,23,23,22,22,21,22,22,22,22,22,22,22,22,23,23,22,20,25,23,23,23, +23,23,23,23,23,22,23,22,24,23,21,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24, +22,19,25,24,23,23,23,23,24,24,23,23,23,23,24,23,21,23,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,22,20,24,24,24,24,23,24,24,24,24,24,24,24,25,22,20,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,22,20,24,24,24,24,24,24,24,24,24,24,24,24, +25,22,21,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,23,20,24,24,24,23,24,24, +24,24,24,24,24,24,25,22,20,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,22,21, +23,24,24,24,24,24,24,24,24,24,24,24,25,22,21,25,24,24,24,24,24,24,24,24,24,24, +24,24,23,23,22,21,22,25,25,24,24,24,24,24,24,25,25,24,23,22,20,24,24,24,24,24, +24,24,24,24,24,23,23,23,20,20,20,20,20,21,21,21,21,20,20,21,21,20,21,21,21,20, +20,22,23,22,22,22,21,21,21,21,21,20,20,20,20,22,24,23,23,23,25,24,23,23,21,22, +20,20,21,25,24,24,23,23,22,23,23,23,23,23,23,22,23,23,22,21,20,24,24,24,24,23, +23,24,22,23,24,23,26,20,25,8,8,25,24,24,24,24,24,25,24,24,25,24,25,8,9,26,20,22, +22,23,23,23,23,23,23,23,22,22,23,20,24,24,23,23,23,23,22,23,22,22,22,22,22,22, +22,23,23,23,20,23,23,23,23,23,23,23,23,23,23,23,22,21,23,24,24,24,24,24,23,24, +23,23,23,22,22,23,23,23,22,22,21,23,24,24,23,23,24,24,24,24,24,24,23,21,23,23, +23,24,24,23,23,24,24,23,23,23,24,24,23,23,22,23,20,23,24,24,24,24,24,24,24,24, +24,24,23,21,23,23,23,24,24,24,24,24,24,24,24,24,23,24,23,23,24,24,20,23,24,24, +24,24,24,24,24,24,24,24,22,21,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,21,23,24,24,24,24,24,24,24,24,24,24,23,21,23,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,20,24,24,24,24,24,24,24,23,24,24,24,23,20,22,24,24,24, +24,24,24,24,24,24,24,24,24,23,23,24,24,24,20,24,24,24,24,24,24,24,24,24,24,24, +23,20,22,25,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,20,21,22,21,22,24, +24,24,24,24,24,24,23,21,21,23,24,23,23,24,24,24,24,24,24,23,23,23,23,23,24,24, +19,19,20,19,19,19,20,20,20,21,21,21,21,20,21,21,21,21,20,21,21,21,21,22,22,21, +21,21,20,20,20,20,24,24,24,24,24,23,22,20,24,24,24,25,25,25,25,25,25,25,25,25, +25,25,23,20,22,24,24,25,25,25,24,24,23,23,23,23,23,23,22,20,24,22,23,23,23,23, +23,23,23,23,22,22,22,22,23,20,23,23,24,23,22,22,21,22,21,22,22,21,21,22,23,20, +22,23,23,24,23,23,23,23,23,23,22,22,22,22,23,20,22,24,23,23,23,22,20,21,23,23, +23,23,22,22,23,20,23,23,23,24,24,23,23,23,23,23,23,23,23,23,23,20,22,24,23,23, +23,23,23,23,24,24,24,24,24,24,23,20,23,24,24,24,24,24,24,24,24,24,24,24,24,24, +23,20,22,24,24,24,24,24,24,24,24,24,24,24,24,24,23,20,23,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,20,22,24,24,24,24,24,24,24,24,24,24,24,24,24,23,20,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,20,22,24,24,24,24,24,24,24,24,24,24,24, +24,24,23,21,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,20,22,24,24,24,24,24, +24,24,24,24,24,23,23,23,23,20,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,19, +22,24,24,24,24,24,24,24 +}; // level1Texture + +#endif // guard diff --git a/programs/pokitto/modelViewer.bin b/programs/pokitto/modelViewer.bin new file mode 100755 index 0000000..3c8e676 Binary files /dev/null and b/programs/pokitto/modelViewer.bin differ diff --git a/programs/pokitto/small3dlib.h b/programs/pokitto/small3dlib.h new file mode 100644 index 0000000..7ff3730 --- /dev/null +++ b/programs/pokitto/small3dlib.h @@ -0,0 +1,2652 @@ +#ifndef SMALL3DLIB_H +#define SMALL3DLIB_H + +/* + Simple realtime 3D software rasterization renderer. It is fast, focused on + resource-limited computers, located in a single C header file, with no + dependencies, using only 32bit integer arithmetics. + + author: Miloslav Ciz + license: CC0 1.0 + additional waiver of all IP + version: TODO + + Before including the library, define S3L_PIXEL_FUNCTION to the name of the + function you'll be using to draw single pixels (this function will be called + by the library to render the frames). Also define S3L_RESOLUTION_X and + S3L_RESOLUTION_Y. + + You'll also need to decide what rendering strategy and other settings you + want to use, depending on your specific usecase. You may want to use a + z-buffer (full or reduced, S3L_Z_BUFFER), sorted-drawing (S3L_SORT), or even + none of these. See the description of the options in this file. + + -------------------- + + This work's goal is to never be encumbered by any exclusive intellectual + property rights. The work is therefore provided under CC0 1.0 + additional + WAIVER OF ALL INTELLECTUAL PROPERTY RIGHTS that waives the rest of + intellectual property rights not already waived by CC0 1.0. The WAIVER OF ALL + INTELLECTUAL PROPERTY RGHTS is as follows: + + Each contributor to this work agrees that they waive any exclusive rights, + including but not limited to copyright, patents, trademark, trade dress, + industrial design, plant varieties and trade secrets, to any and all ideas, + concepts, processes, discoveries, improvements and inventions conceived, + discovered, made, designed, researched or developed by the contributor either + solely or jointly with others, which relate to this work or result from this + work. Should any waiver of such right be judged legally invalid or + ineffective under applicable law, the contributor hereby grants to each + affected person a royalty-free, non transferable, non sublicensable, non + exclusive, irrevocable and unconditional license to this right. + + -------------------- + + CONVENTIONS: + + This library should never draw pixels outside the specified screen + boundaries, so you don't have to check this (that would cost CPU time)! + + You can safely assume that triangles are rasterized one by one and from top + down, left to right (so you can utilize e.g. various caches), and if sorting + is disabled the order of rasterization will be that specified in the scene + structure and model arrays (of course, some triangles and models may be + skipped due to culling etc.). + + Angles are in S3L_Units, a full angle (2 pi) is S3L_FRACTIONS_PER_UNITs. + + We use row vectors. + + In 3D space, a left-handed coord. system is used. One spatial unit is split + into S3L_FRACTIONS_PER_UNIT fractions (fixed point arithmetic). + + y ^ + | _ + | /| z + | / + | / + [0,0,0]-------> x + + Untransformed camera is placed at [0,0,0], looking forward along +z axis. The + projection plane is centered at [0,0,0], stretrinch from + -S3L_FRACTIONS_PER_UNIT to S3L_FRACTIONS_PER_UNIT horizontally (x), + vertical size (y) depends on the aspect ratio (S3L_RESOLUTION_X and + S3L_RESOLUTION_Y). Camera FOV is defined by focal length in S3L_Units. + + y ^ + | _ + | /| z + ____|_/__ + | |/ | + -----[0,0,0]-|-----> x + |____|____| + | + | + + Rotations use Euler angles and are generally in the extinsic Euler angles in + ZXY order (by Z, then by X, then by Y). Positive rotation about an axis + rotates CW (clock-wise) when looking in the direction of the axis. + + Coordinates of pixels on the screen start at the top left, from [0,0]. + + There is NO subpixel accuracy (screen coordinates are only integer). + + Triangle rasterization rules are these (mostly same as OpenGL, D3D etc.): + + - Let's define: + - left side: + - not exactly horizontal, and on the left side of triangle + - exactly horizontal and above the topmost + (in other words: its normal points at least a little to the left or + completely up) + - right side: not left side + - Pixel centers are at integer coordinates and triangle for drawing are + specified with integer coordinates of pixel centers. + - A pixel is rasterized: + - if its center is inside the triangle OR + - if its center is exactly on the triangle side which is left and at the + same time is not on the side that's right (case of a triangle that's on + a single line) OR + - if its center is exactly on the triangle corner of sides neither of which + is right. + + These rules imply among others: + + - Adjacent triangles don't have any overlapping pixels, nor gaps between. + - Triangles of points that lie on a single line are NOT rasterized. + - A single "long" triangle CAN be rasterized as isolated islands of pixels. + - Transforming (e.g. mirroring, rotating by 90 degrees etc.) a result of + rasterizing triangle A is NOT generally equal to applying the same + transformation to triangle A first and then rasterizing it. Even the number + of rasterized pixels is usually different. + - If specifying a triangle with integer coordinates (which we are), then: + - The bottom-most corner (or side) of a triangle is never rasterized + (because it is connected to a right side). + - The top-most corner can only be rasterized on completely horizontal side + (otherwise it is connected to a right side). + - Vertically middle corner is rasterized if and only if it is on the left + of the triangle and at the same time is also not the bottom-most corner. +*/ + +#include + +#ifndef S3L_RESOLUTION_X +#define S3L_RESOLUTION_X 640 ///< Redefine to screen x resolution. +#endif + +#ifndef S3L_RESOLUTION_Y +#define S3L_RESOLUTION_Y 480 ///< Redefine to screen y resolution. +#endif + +/** Units of measurement in 3D space. There is S3L_FRACTIONS_PER_UNIT in one +spatial unit. By dividing the unit into fractions we effectively achieve a +fixed point arithmetic. The number of fractions is a constant that serves as +1.0 in floating point arithmetic (normalization etc.). */ + +typedef int32_t S3L_Unit; + +/** How many fractions a spatial unit is split into. This is NOT SUPPOSED TO +BE REDEFINED, so rather don't do it (otherwise things may overflow etc.). */ + +#define S3L_FRACTIONS_PER_UNIT 512 + +typedef int16_t S3L_ScreenCoord; +typedef uint16_t S3L_Index; + +#ifndef S3L_STRICT_NEAR_CULLING + /** If on, any triangle that only partially intersects the near plane will be + culled. This can prevent errorneous rendering and artifacts, but also makes + triangles close to the camera disappear. */ + + #define S3L_STRICT_NEAR_CULLING 1 +#endif + +#ifndef S3L_FLAT + /** If on, disables computation of per-pixel values such as barycentric + coordinates and depth -- these will still be available but will be the same + for the whole triangle. This can be used to create flat-shaded renders and + will be a lot faster. With this option on you will probably want to use + sorting instead of z-buffer. */ + + #define S3L_FLAT 0 +#endif + +#if S3L_FLAT + #define S3L_COMPUTE_DEPTH 0 + #define S3L_PERSPECTIVE_CORRECTION 0 + // don't disable z-buffer, it makes sense to use it with no sorting +#endif + +#ifndef S3L_PERSPECTIVE_CORRECTION + /** Specifies what type of perspective correction (PC) to use. Remember this + is an expensive operation! Possible values: + + - 0: No perspective correction. Fastest, inaccurate from most angles. + - 1: Per-pixel perspective correction, accurate but very expensive. + - 2: Approximation (computing only at every S3L_PC_APPROX_LENGTHth pixel). + Quake-style approximation is used, which only computes the PC after + S3L_PC_APPROX_LENGTH pixels. This is reasonably accurate and fast. */ + + #define S3L_PERSPECTIVE_CORRECTION 0 +#endif + +#ifndef S3L_PC_APPROX_LENGTH + /** For S3L_PERSPECTIVE_CORRECTION == 2, this specifies after how many pixels + PC is recomputed. Should be a power of two to keep up the performance. + Smaller is nicer but slower. */ + + #define S3L_PC_APPROX_LENGTH 32 +#endif + +#if S3L_PERSPECTIVE_CORRECTION +#define S3L_COMPUTE_DEPTH 1 // PC inevitably computes depth, so enable it +#endif + +#ifndef S3L_COMPUTE_DEPTH + /** Whether to compute depth for each pixel (fragment). Some other options + may turn this on automatically. If you don't need depth information, turning + this off can save performance. Depth will still be accessible in + S3L_PixelInfo, but will be constant -- equal to center point depth -- over + the whole triangle. */ + #define S3L_COMPUTE_DEPTH 1 +#endif + +#ifndef S3L_Z_BUFFER + /** What type of z-buffer (depth buffer) to use for visibility determination. + Possible values: + + - 0: Don't use z-buffer. This saves a lot of memory, but visibility checking + won't be pixel-accurate and has to mostly be done by other means + (typically sorting). + - 1: Use full z-buffer (of S3L_Units) for visibiltiy determination. This is + the most accurate option (and also a fast one), but requires a big + amount of memory. + - 2: Use reduced-size z-buffer (of bytes). This is fast and somewhat + accurate, but inaccuracies can occur and a considerable amount of memory + is needed. */ + + #define S3L_Z_BUFFER 0 +#endif + +#ifndef S3L_REDUCED_Z_BUFFER_GRANULARITY + /** For S3L_Z_BUFFER == 2 this sets the reduced z-buffer granularity. */ + + #define S3L_REDUCED_Z_BUFFER_GRANULARITY 5 +#endif + +#ifndef S3L_STENCIL_BUFFER + /** Whether to use stencil buffer for drawing -- with this a pixel that would + be resterized over an already rasterized pixel (within a frame) will be + discarded. This is mostly for front-to-back sorted drawing. */ + + #define S3L_STENCIL_BUFFER 0 +#endif + +#ifndef S3L_SORT + /** Defines how to sort triangles before drawing a frame. This can be used to + solve visibility in case z-buffer is not used, to prevent overwriting already + rasterized pixels, implement transparency etc. Note that for simplicity and + performance a relatively simple sorting is used which doesn't work completely + correctly, so mistakes can occur (even the best sorting wouldn't be able to + solve e.g. intersecting triangles). Note that sorting requires a bit of extra + memory -- an array of the triangles to sort -- the size of this array limits + the maximum number of triangles that can be drawn in a single frame + (S3L_MAX_TRIANGES_DRAWN). Possible values: + + - 0: Don't sort triangles. This is fastest and doesn't use extra memory. + - 1: Sort triangles from back to front. This can in most cases solve + visibility without requiring almost any extra memory compared to + z-buffer. + - 2: Sort triangles from front to back. This can be faster than back to + front, because we prevent computing pixels that will be overwritten by + nearer ones, but we need a 1b stencil buffer for this (enable + S3L_STENCIL_BUFFER), so a bit more memory is needed. */ + + #define S3L_SORT 0 +#endif + +#ifndef S3L_MAX_TRIANGES_DRAWN + /** Maximum number of triangles that can be drawn in sorted modes. This + affects the size of the cache used for triangle sorting. */ + + #define S3L_MAX_TRIANGES_DRAWN 128 +#endif + +#ifndef S3L_NEAR + /** Distance of the near clipping plane. Points in front or EXATLY ON this + plane are considered outside the frustum. This must be >= 0. */ + + #define S3L_NEAR (S3L_FRACTIONS_PER_UNIT / 4) +#endif + +#if S3L_NEAR <= 0 +#define S3L_NEAR 1 // Can't be <= 0. +#endif + +#ifndef S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE + /** Affects the S3L_computeModelNormals function. See its description for + details. */ + + #define S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE 6 +#endif + +#ifndef S3L_FAST_LERP_QUALITY + /** Quality (scaling) of SOME (stepped) linear interpolations. 0 will most + likely be a tiny bit faster, but artifacts can occur for bigger tris, while + higher values can fix this -- in theory all higher values will have the same + speed (it is a shift value), but it mustn't be too high to prevent + overflow. */ + + #define S3L_FAST_LERP_QUALITY 11 +#endif + +/** Vector that consists of four scalars and can represent homogenous + coordinates, but is generally also used as Vec3 and Vec2 for various + purposes. */ +typedef struct +{ + S3L_Unit x; + S3L_Unit y; + S3L_Unit z; + S3L_Unit w; +} S3L_Vec4; + +#define S3L_logVec4(v)\ + printf("Vec4: %d %d %d %d\n",((v).x),((v).y),((v).z),((v).w)) + +static inline void S3L_initVec4(S3L_Vec4 *v); +static inline void S3L_setVec4(S3L_Vec4 *v, S3L_Unit x, S3L_Unit y, + S3L_Unit z, S3L_Unit w); +static inline void S3L_vec3Add(S3L_Vec4 *result, S3L_Vec4 added); +static inline void S3L_vec3Sub(S3L_Vec4 *result, S3L_Vec4 substracted); +S3L_Unit S3L_vec3Length(S3L_Vec4 v); + +/** Normalizes Vec3. Note that this function tries to normalize correctly + rather than quickly! If you need to normalize quickly, do it yourself in a + way that best fits your case. */ +void S3L_normalizeVec3(S3L_Vec4 *v); + +/** Like S3L_normalizeVec3, but doesn't perform any checks on the input vector, + which is faster, but can be very innacurate or overflowing. You are supposed + to provide a "nice" vector (not too big or small). */ +static inline void S3L_normalizeVec3Fast(S3L_Vec4 *v); + +S3L_Unit S3L_vec2Length(S3L_Vec4 v); +void S3L_crossProduct(S3L_Vec4 a, S3L_Vec4 b, S3L_Vec4 *result); +static inline S3L_Unit S3L_dotProductVec3(S3L_Vec4 a, S3L_Vec4 b); + +/** Computes a reflection direction (typically used e.g. for specular component + in Phong illumination). The input vectors must be normalized. The result will + be normalized as well. */ +void S3L_reflect(S3L_Vec4 toLight, S3L_Vec4 normal, S3L_Vec4 *result); + +/** Determines the winding of a triangle, returns 1 (CW, clockwise), -1 (CCW, + counterclockwise) or 0 (points lie on a single line). */ +static inline int8_t S3L_triangleWinding( + S3L_ScreenCoord x0, + S3L_ScreenCoord y0, + S3L_ScreenCoord x1, + S3L_ScreenCoord y1, + S3L_ScreenCoord x2, + S3L_ScreenCoord y2); + +typedef struct +{ + S3L_Vec4 translation; + S3L_Vec4 rotation; /**< Euler angles. Rortation is applied in this order: + 1. z = by z (roll) CW looking along z+ + 2. x = by x (pitch) CW looking along x+ + 3. y = by y (yaw) CW looking along y+ */ + S3L_Vec4 scale; +} S3L_Transform3D; + +#define S3L_logTransform3D(t)\ + printf("Transform3D: T = [%d %d %d], R = [%d %d %d], S = [%d %d %d]\n",\ + (t).translation.x,(t).translation.y,(t).translation.z,\ + (t).rotation.x,(t).rotation.y,(t).rotation.z,\ + (t).scale.x,(t).scale.y,(t).scale.z) + +static inline void S3L_initTransoform3D(S3L_Transform3D *t); + +void S3L_lookAt(S3L_Vec4 pointTo, S3L_Transform3D *t); + +void S3L_setTransform3D( + S3L_Unit tx, + S3L_Unit ty, + S3L_Unit tz, + S3L_Unit rx, + S3L_Unit ry, + S3L_Unit rz, + S3L_Unit sx, + S3L_Unit sy, + S3L_Unit sz, + S3L_Transform3D *t); + +/** Converts rotation transformation to three direction vectors of given length + (any one can be NULL, in which case it won't be computed). */ +void S3L_rotationToDirections( + S3L_Vec4 rotation, + S3L_Unit length, + S3L_Vec4 *forw, + S3L_Vec4 *right, + S3L_Vec4 *up); + +/** 4x4 matrix, used mostly for 3D transforms. The indexing is this: + matrix[column][row]. */ +typedef S3L_Unit S3L_Mat4[4][4]; + +#define S3L_logMat4(m)\ + printf("Mat4:\n %d %d %d %d\n %d %d %d %d\n %d %d %d %d\n %d %d %d %d\n"\ + ,(m)[0][0],(m)[1][0],(m)[2][0],(m)[3][0],\ + (m)[0][1],(m)[1][1],(m)[2][1],(m)[3][1],\ + (m)[0][2],(m)[1][2],(m)[2][2],(m)[3][2],\ + (m)[0][3],(m)[1][3],(m)[2][3],(m)[3][3]) + +/** Initializes a 4x4 matrix to identity. */ +static inline void S3L_initMat4(S3L_Mat4 *m); + +void S3L_transposeMat4(S3L_Mat4 *m); + +void S3L_makeTranslationMat( + S3L_Unit offsetX, + S3L_Unit offsetY, + S3L_Unit offsetZ, + S3L_Mat4 *m); + +/** Makes a scaling matrix. DON'T FORGET: scale of 1.0 is set with + S3L_FRACTIONS_PER_UNIT! */ +void S3L_makeScaleMatrix( + S3L_Unit scaleX, + S3L_Unit scaleY, + S3L_Unit scaleZ, + S3L_Mat4 *m); + +/** Makes a matrix for rotation in the ZXY order. */ +void S3L_makeRotationMatrixZXY( + S3L_Unit byX, + S3L_Unit byY, + S3L_Unit byZ, + S3L_Mat4 *m); + +void S3L_makeWorldMatrix(S3L_Transform3D worldTransform, S3L_Mat4 *m); +void S3L_makeCameraMatrix(S3L_Transform3D cameraTransform, S3L_Mat4 *m); + +/** Multiplies a vector by a matrix with normalization by + S3L_FRACTIONS_PER_UNIT. Result is stored in the input vector. */ +void S3L_vec4Xmat4(S3L_Vec4 *v, S3L_Mat4 *m); + +/** Same as S3L_vec4Xmat4 but faster, because this version doesn't compute the + W component of the result, which is usually not needed. */ +void S3L_vec3Xmat4(S3L_Vec4 *v, S3L_Mat4 *m); + +/** Multiplies two matrices with normalization by S3L_FRACTIONS_PER_UNIT. + Result is stored in the first matrix. The result represents a transformation + that has the same effect as applying the transformation represented by m1 and + then m2 (in that order). */ +void S3L_mat4Xmat4(S3L_Mat4 *m1, S3L_Mat4 *m2); + +typedef struct +{ + S3L_Unit focalLength; ///< Defines the field of view (FOV). + S3L_Transform3D transform; +} S3L_Camera; + +void S3L_initCamera(S3L_Camera *camera); + +typedef struct +{ + uint8_t backfaceCulling; /**< What backface culling to use. Possible + values: + - 0 none + - 1 clock-wise + - 2 counter clock-wise */ + int8_t visible; /**< Can be used to easily hide the model. */ +} S3L_DrawConfig; + +void S3L_initDrawConfig(S3L_DrawConfig *config); + +typedef struct +{ + const S3L_Unit *vertices; + S3L_Index vertexCount; + const S3L_Index *triangles; + S3L_Index triangleCount; + S3L_Transform3D transform; + S3L_Mat4 *customTransformMatrix; /**< This can be used to override the + transform (if != 0) with a custom + transform matrix, which is more + general. */ + S3L_DrawConfig config; +} S3L_Model3D; ///< Represents a 3D model. + +void S3L_initModel3D( + const S3L_Unit *vertices, + S3L_Unit vertexCount, + const S3L_Index *triangles, + S3L_Index triangleCount, + S3L_Model3D *model); + +typedef struct +{ + S3L_Model3D *models; + S3L_Index modelCount; + S3L_Camera camera; +} S3L_Scene; ///< Represent the 3D scene to be rendered. + +void S3L_initScene( + S3L_Model3D *models, + S3L_Index modelCount, + S3L_Scene *scene); + +typedef struct +{ + S3L_ScreenCoord x; ///< Screen X coordinate. + S3L_ScreenCoord y; ///< Screen Y coordinate. + + S3L_Unit barycentric[3]; /**< Barycentric coords corresponds to the three + vertices. These serve to locate the pixel on a + triangle and interpolate values between it's + three points. Each one goes from 0 to + S3L_FRACTIONS_PER_UNIT (including), but due to + rounding error may fall outside this range (you + can use S3L_correctBarycentricCoords to fix this + for the price of some performance). The sum of + the three coordinates will always be exactly + S3L_FRACTIONS_PER_UNIT. */ + S3L_Index modelIndex; ///< Model index within the scene. + S3L_Index triangleIndex; ///< Triangle index within the model. + uint32_t triangleID; /**< Unique ID of the triangle withing the whole + scene. This can be used e.g. by a cache to + quickly find out if a triangle has changed. */ + S3L_Unit depth; ///< Depth (only if depth is turned on). + S3L_Unit previousZ; /**< Z-buffer value (not necessarily world depth in + S3L_Units!) that was in the z-buffer on the + pixels position before this pixel was + rasterized. This can be used to set the value + back, e.g. for transparency. */ + S3L_ScreenCoord triangleSize[2]; /**< Rasterized triangle width and height, + can be used e.g. for MIP mapping. */ +} S3L_PixelInfo; /**< Used to pass the info about a rasterized pixel + (fragment) to the user-defined drawing func. */ + +static inline void S3L_initPixelInfo(S3L_PixelInfo *p); + +/** Corrects barycentric coordinates so that they exactly meet the defined + conditions (each fall into <0,S3L_FRACTIONS_PER_UNIT>, sum = + S3L_FRACTIONS_PER_UNIT). Note that doing this per-pixel can slow the program + down significantly. */ +static inline void S3L_correctBarycentricCoords(S3L_Unit barycentric[3]); + +// general helper functions +static inline S3L_Unit S3L_abs(S3L_Unit value); +static inline S3L_Unit S3L_min(S3L_Unit v1, S3L_Unit v2); +static inline S3L_Unit S3L_max(S3L_Unit v1, S3L_Unit v2); +static inline S3L_Unit S3L_clamp(S3L_Unit v, S3L_Unit v1, S3L_Unit v2); +static inline S3L_Unit S3L_wrap(S3L_Unit value, S3L_Unit mod); +static inline S3L_Unit S3L_nonZero(S3L_Unit value); + +S3L_Unit S3L_sin(S3L_Unit x); +S3L_Unit S3L_asin(S3L_Unit x); +static inline S3L_Unit S3L_cos(S3L_Unit x); + +S3L_Unit S3L_vec3Length(S3L_Vec4 v); +S3L_Unit S3L_sqrt(S3L_Unit value); + +/** Projects a single point from 3D space to the screen space (pixels), which + can be useful e.g. for drawing sprites. The w component of input and result + holds the point size. If this size is 0 in the result, the sprite is outside + the view. */ +void project3DPointToScreen( + S3L_Vec4 point, + S3L_Camera camera, + S3L_Vec4 *result); + +/** Computes a normalized normal of given triangle. */ +void S3L_triangleNormal(S3L_Vec4 t0, S3L_Vec4 t1, S3L_Vec4 t2, + S3L_Vec4 *n); + +/** Helper function for retrieving per-vertex indexed values from an array, + e.g. texturing (UV) coordinates. The 'indices' array contains three indices + for each triangle, each index pointing into 'values' array, which contains + the values, each one consisting of 'numComponents' components (e.g. 2 for + UV coordinates). The three values are retrieved into 'v0', 'v1' and 'v2' + vectors (into x, y, z and w, depending on 'numComponents'). This function is + meant to be used per-triangle (typically from a cache), NOT per-pixel, as it + is not as fast as possible! */ +void S3L_getIndexedTriangleValues( + S3L_Index triangleIndex, + const S3L_Index *indices, + const S3L_Unit *values, + uint8_t numComponents, + S3L_Vec4 *v0, + S3L_Vec4 *v1, + S3L_Vec4 *v2); + +/** Computes a normalized normal for every vertex of given model (this is + relatively slow and SHOUDN'T be done each frame). The dst array must have a + sufficient size preallocated! The size is: number of model vertices * 3 * + sizeof(S3L_Unit). Note that for advanced allowing sharp edges it is not + sufficient to have per-vertex normals, but must be per-triangle. This + function doesn't support this. + + The function computes a normal for each vertex by averaging normals of + the triangles containing the vertex. The maximum number of these triangle + normals that will be averaged is set with + S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE. */ +void S3L_computeModelNormals(S3L_Model3D model, S3L_Unit *dst, + int8_t transformNormals); + +/** Interpolated between two values, v1 and v2, in the same ratio as t is to + tMax. Does NOT prevent zero division. */ +static inline S3L_Unit S3L_interpolate( + S3L_Unit v1, + S3L_Unit v2, + S3L_Unit t, + S3L_Unit tMax); + +/** Same as S3L_interpolate but with v1 == 0. Should be faster. */ +static inline S3L_Unit S3L_interpolateFrom0( + S3L_Unit v2, + S3L_Unit t, + S3L_Unit tMax); + +/** Like S3L_interpolate, but uses a parameter that goes from 0 to + S3L_FRACTIONS_PER_UNIT - 1, which can be faster. */ +static inline S3L_Unit S3L_interpolateByUnit( + S3L_Unit v1, + S3L_Unit v2, + S3L_Unit t); + +/** Same as S3L_interpolateByUnit but with v1 == 0. Should be faster. */ +static inline S3L_Unit S3L_interpolateByUnitFrom0( + S3L_Unit v2, + S3L_Unit t); + +static inline S3L_Unit S3L_distanceManhattan(S3L_Vec4 a, S3L_Vec4 b); + +/** Returns a value interpolated between the three triangle vertices based on + barycentric coordinates. */ +static inline S3L_Unit S3L_interpolateBarycentric( + S3L_Unit value0, + S3L_Unit value1, + S3L_Unit value2, + S3L_Unit barycentric[3]); + +static inline void S3L_mapProjectionPlaneToScreen( + S3L_Vec4 point, + S3L_ScreenCoord *screenX, + S3L_ScreenCoord *screenY); + +/** Draws a triangle according to given config. The vertices are specified in + Screen Space space (pixels). If perspective correction is enabled, each + vertex has to have a depth (Z position in camera space) specified in the Z + component. */ +void S3L_drawTriangle( + S3L_Vec4 point0, + S3L_Vec4 point1, + S3L_Vec4 point2, + S3L_Index modelIndex, + S3L_Index triangleIndex); + +/** This should be called before rendering each frame. The function clears + buffers and does potentially other things needed for the frame. */ +void S3L_newFrame(); + +void S3L_zBufferClear(); +void S3L_stencilBufferClear(); + +/** Writes a value (not necessarily depth! depends on the format of z-buffer) + to z-buffer (if enabled). Does NOT check boundaries! */ +void S3L_zBufferWrite(S3L_ScreenCoord x, S3L_ScreenCoord y, S3L_Unit value); + +/** Reads a value (not necessarily depth! depends on the format of z-buffer) + from z-buffer (if enabled). Does NOT check boundaries! */ +S3L_Unit S3L_zBufferRead(S3L_ScreenCoord x, S3L_ScreenCoord y); + +static inline void S3L_rotate2DPoint(S3L_Unit *x, S3L_Unit *y, S3L_Unit angle); + +/** Predefined vertices of a cube to simply insert in an array. These come with + S3L_CUBE_TRIANGLES and S3L_CUBE_TEXCOORDS. */ +#define S3L_CUBE_VERTICES(m)\ + /* 0 front, bottom, right */\ + m/2, -m/2, -m/2,\ + /* 1 front, bottom, left */\ +-m/2, -m/2, -m/2,\ + /* 2 front, top, right */\ + m/2, m/2, -m/2,\ + /* 3 front, top, left */\ +-m/2, m/2, -m/2,\ + /* 4 back, bottom, right */\ + m/2, -m/2, m/2,\ + /* 5 back, bottom, left */\ +-m/2, -m/2, m/2,\ + /* 6 back, top, right */\ + m/2, m/2, m/2,\ + /* 7 back, top, left */\ +-m/2, m/2, m/2 + +#define S3L_CUBE_VERTEX_COUNT 8 + +/** Predefined triangle indices of a cube, to be used with S3L_CUBE_VERTICES + and S3L_CUBE_TEXCOORDS. */ +#define S3L_CUBE_TRIANGLES\ + 3, 0, 2, /* front */\ + 1, 0, 3,\ + 0, 4, 2, /* right */\ + 2, 4, 6,\ + 4, 5, 6, /* back */\ + 7, 6, 5,\ + 3, 7, 1, /* left */\ + 1, 7, 5,\ + 6, 3, 2, /* top */\ + 7, 3, 6,\ + 1, 4, 0, /* bottom */\ + 5, 4, 1 + +#define S3L_CUBE_TRIANGLE_COUNT 12 + +/** Predefined texture coordinates of a cube, corresponding to triangles (NOT + vertices), to be used with S3L_CUBE_VERTICES and S3L_CUBE_TRIANGLES. */ +#define S3L_CUBE_TEXCOORDS(m)\ + 0,0, m,m, m,0,\ + 0,m, m,m, 0,0,\ + m,m, m,0, 0,m,\ + 0,m, m,0, 0,0,\ + m,0, 0,0, m,m,\ + 0,m, m,m, 0,0,\ + 0,0, 0,m, m,0,\ + m,0, 0,m, m,m,\ + 0,0, m,m, m,0,\ + 0,m, m,m, 0,0,\ + m,0, 0,m, m,m,\ + 0,0, 0,m, m,0 + +//============================================================================= +// privates + +#define S3L_UNUSED(what) (void)(what) ///< helper macro for unused vars + +#define S3L_HALF_RESOLUTION_X (S3L_RESOLUTION_X >> 1) +#define S3L_HALF_RESOLUTION_Y (S3L_RESOLUTION_Y >> 1) + +#define S3L_PROJECTION_PLANE_HEIGHT\ + ((S3L_RESOLUTION_Y * S3L_FRACTIONS_PER_UNIT * 2) / S3L_RESOLUTION_X) + +#if S3L_Z_BUFFER == 1 + #define S3L_MAX_DEPTH 2147483647 + S3L_Unit S3L_zBuffer[S3L_RESOLUTION_X * S3L_RESOLUTION_Y]; + #define S3L_zBufferFormat(depth) (depth) +#elif S3L_Z_BUFFER == 2 + #define S3L_MAX_DEPTH 255 + uint8_t S3L_zBuffer[S3L_RESOLUTION_X * S3L_RESOLUTION_Y]; + #define S3L_zBufferFormat(depth)\ + S3L_min(255,(depth) >> S3L_REDUCED_Z_BUFFER_GRANULARITY) +#endif + +#if S3L_Z_BUFFER +static inline int8_t S3L_zTest( + S3L_ScreenCoord x, + S3L_ScreenCoord y, + S3L_Unit depth) +{ + uint32_t index = y * S3L_RESOLUTION_X + x; + + depth = S3L_zBufferFormat(depth); + +#if S3L_Z_BUFFER == 2 + #define cmp <= /* For reduced z-buffer we need equality test, because + otherwise pixels at the maximum depth (255) would never be + drawn over the background (which also has the depth of + 255). */ +#else + #define cmp < /* For normal z-buffer we leave out equality test to not waste + time by drawing over already drawn pixls. */ +#endif + + if (depth cmp S3L_zBuffer[index]) + { + S3L_zBuffer[index] = depth; + return 1; + } + +#undef cmp + + return 0; +} +#endif + +S3L_Unit S3L_zBufferRead(S3L_ScreenCoord x, S3L_ScreenCoord y) +{ +#if S3L_Z_BUFFER + return S3L_zBuffer[y * S3L_RESOLUTION_X + x]; +#else + S3L_UNUSED(x); + S3L_UNUSED(y); + + return 0; +#endif +} + +void S3L_zBufferWrite(S3L_ScreenCoord x, S3L_ScreenCoord y, S3L_Unit value) +{ +#if S3L_Z_BUFFER + S3L_zBuffer[y * S3L_RESOLUTION_X + x] = value; +#else + S3L_UNUSED(x); + S3L_UNUSED(y); + S3L_UNUSED(value); +#endif +} + +#if S3L_STENCIL_BUFFER + #define S3L_STENCIL_BUFFER_SIZE\ + ((S3L_RESOLUTION_X * S3L_RESOLUTION_Y - 1) / 8 + 1) + +uint8_t S3L_stencilBuffer[S3L_STENCIL_BUFFER_SIZE]; + +static inline int8_t S3L_stencilTest( + S3L_ScreenCoord x, + S3L_ScreenCoord y) +{ + uint32_t index = y * S3L_RESOLUTION_X + x; + uint32_t bit = (index & 0x00000007); + index = index >> 3; + + uint8_t val = S3L_stencilBuffer[index]; + + if ((val >> bit) & 0x1) + return 0; + + S3L_stencilBuffer[index] = val | (0x1 << bit); + + return 1; +} +#endif + +#define S3L_COMPUTE_LERP_DEPTH\ + (S3L_COMPUTE_DEPTH && (S3L_PERSPECTIVE_CORRECTION == 0)) + +#define S3L_SIN_TABLE_LENGTH 128 + +static const S3L_Unit S3L_sinTable[S3L_SIN_TABLE_LENGTH] = +{ + /* 511 was chosen here as a highest number that doesn't overflow during + compilation for S3L_FRACTIONS_PER_UNIT == 1024 */ + + (0*S3L_FRACTIONS_PER_UNIT)/511, (6*S3L_FRACTIONS_PER_UNIT)/511, + (12*S3L_FRACTIONS_PER_UNIT)/511, (18*S3L_FRACTIONS_PER_UNIT)/511, + (25*S3L_FRACTIONS_PER_UNIT)/511, (31*S3L_FRACTIONS_PER_UNIT)/511, + (37*S3L_FRACTIONS_PER_UNIT)/511, (43*S3L_FRACTIONS_PER_UNIT)/511, + (50*S3L_FRACTIONS_PER_UNIT)/511, (56*S3L_FRACTIONS_PER_UNIT)/511, + (62*S3L_FRACTIONS_PER_UNIT)/511, (68*S3L_FRACTIONS_PER_UNIT)/511, + (74*S3L_FRACTIONS_PER_UNIT)/511, (81*S3L_FRACTIONS_PER_UNIT)/511, + (87*S3L_FRACTIONS_PER_UNIT)/511, (93*S3L_FRACTIONS_PER_UNIT)/511, + (99*S3L_FRACTIONS_PER_UNIT)/511, (105*S3L_FRACTIONS_PER_UNIT)/511, + (111*S3L_FRACTIONS_PER_UNIT)/511, (118*S3L_FRACTIONS_PER_UNIT)/511, + (124*S3L_FRACTIONS_PER_UNIT)/511, (130*S3L_FRACTIONS_PER_UNIT)/511, + (136*S3L_FRACTIONS_PER_UNIT)/511, (142*S3L_FRACTIONS_PER_UNIT)/511, + (148*S3L_FRACTIONS_PER_UNIT)/511, (154*S3L_FRACTIONS_PER_UNIT)/511, + (160*S3L_FRACTIONS_PER_UNIT)/511, (166*S3L_FRACTIONS_PER_UNIT)/511, + (172*S3L_FRACTIONS_PER_UNIT)/511, (178*S3L_FRACTIONS_PER_UNIT)/511, + (183*S3L_FRACTIONS_PER_UNIT)/511, (189*S3L_FRACTIONS_PER_UNIT)/511, + (195*S3L_FRACTIONS_PER_UNIT)/511, (201*S3L_FRACTIONS_PER_UNIT)/511, + (207*S3L_FRACTIONS_PER_UNIT)/511, (212*S3L_FRACTIONS_PER_UNIT)/511, + (218*S3L_FRACTIONS_PER_UNIT)/511, (224*S3L_FRACTIONS_PER_UNIT)/511, + (229*S3L_FRACTIONS_PER_UNIT)/511, (235*S3L_FRACTIONS_PER_UNIT)/511, + (240*S3L_FRACTIONS_PER_UNIT)/511, (246*S3L_FRACTIONS_PER_UNIT)/511, + (251*S3L_FRACTIONS_PER_UNIT)/511, (257*S3L_FRACTIONS_PER_UNIT)/511, + (262*S3L_FRACTIONS_PER_UNIT)/511, (268*S3L_FRACTIONS_PER_UNIT)/511, + (273*S3L_FRACTIONS_PER_UNIT)/511, (278*S3L_FRACTIONS_PER_UNIT)/511, + (283*S3L_FRACTIONS_PER_UNIT)/511, (289*S3L_FRACTIONS_PER_UNIT)/511, + (294*S3L_FRACTIONS_PER_UNIT)/511, (299*S3L_FRACTIONS_PER_UNIT)/511, + (304*S3L_FRACTIONS_PER_UNIT)/511, (309*S3L_FRACTIONS_PER_UNIT)/511, + (314*S3L_FRACTIONS_PER_UNIT)/511, (319*S3L_FRACTIONS_PER_UNIT)/511, + (324*S3L_FRACTIONS_PER_UNIT)/511, (328*S3L_FRACTIONS_PER_UNIT)/511, + (333*S3L_FRACTIONS_PER_UNIT)/511, (338*S3L_FRACTIONS_PER_UNIT)/511, + (343*S3L_FRACTIONS_PER_UNIT)/511, (347*S3L_FRACTIONS_PER_UNIT)/511, + (352*S3L_FRACTIONS_PER_UNIT)/511, (356*S3L_FRACTIONS_PER_UNIT)/511, + (361*S3L_FRACTIONS_PER_UNIT)/511, (365*S3L_FRACTIONS_PER_UNIT)/511, + (370*S3L_FRACTIONS_PER_UNIT)/511, (374*S3L_FRACTIONS_PER_UNIT)/511, + (378*S3L_FRACTIONS_PER_UNIT)/511, (382*S3L_FRACTIONS_PER_UNIT)/511, + (386*S3L_FRACTIONS_PER_UNIT)/511, (391*S3L_FRACTIONS_PER_UNIT)/511, + (395*S3L_FRACTIONS_PER_UNIT)/511, (398*S3L_FRACTIONS_PER_UNIT)/511, + (402*S3L_FRACTIONS_PER_UNIT)/511, (406*S3L_FRACTIONS_PER_UNIT)/511, + (410*S3L_FRACTIONS_PER_UNIT)/511, (414*S3L_FRACTIONS_PER_UNIT)/511, + (417*S3L_FRACTIONS_PER_UNIT)/511, (421*S3L_FRACTIONS_PER_UNIT)/511, + (424*S3L_FRACTIONS_PER_UNIT)/511, (428*S3L_FRACTIONS_PER_UNIT)/511, + (431*S3L_FRACTIONS_PER_UNIT)/511, (435*S3L_FRACTIONS_PER_UNIT)/511, + (438*S3L_FRACTIONS_PER_UNIT)/511, (441*S3L_FRACTIONS_PER_UNIT)/511, + (444*S3L_FRACTIONS_PER_UNIT)/511, (447*S3L_FRACTIONS_PER_UNIT)/511, + (450*S3L_FRACTIONS_PER_UNIT)/511, (453*S3L_FRACTIONS_PER_UNIT)/511, + (456*S3L_FRACTIONS_PER_UNIT)/511, (459*S3L_FRACTIONS_PER_UNIT)/511, + (461*S3L_FRACTIONS_PER_UNIT)/511, (464*S3L_FRACTIONS_PER_UNIT)/511, + (467*S3L_FRACTIONS_PER_UNIT)/511, (469*S3L_FRACTIONS_PER_UNIT)/511, + (472*S3L_FRACTIONS_PER_UNIT)/511, (474*S3L_FRACTIONS_PER_UNIT)/511, + (476*S3L_FRACTIONS_PER_UNIT)/511, (478*S3L_FRACTIONS_PER_UNIT)/511, + (481*S3L_FRACTIONS_PER_UNIT)/511, (483*S3L_FRACTIONS_PER_UNIT)/511, + (485*S3L_FRACTIONS_PER_UNIT)/511, (487*S3L_FRACTIONS_PER_UNIT)/511, + (488*S3L_FRACTIONS_PER_UNIT)/511, (490*S3L_FRACTIONS_PER_UNIT)/511, + (492*S3L_FRACTIONS_PER_UNIT)/511, (494*S3L_FRACTIONS_PER_UNIT)/511, + (495*S3L_FRACTIONS_PER_UNIT)/511, (497*S3L_FRACTIONS_PER_UNIT)/511, + (498*S3L_FRACTIONS_PER_UNIT)/511, (499*S3L_FRACTIONS_PER_UNIT)/511, + (501*S3L_FRACTIONS_PER_UNIT)/511, (502*S3L_FRACTIONS_PER_UNIT)/511, + (503*S3L_FRACTIONS_PER_UNIT)/511, (504*S3L_FRACTIONS_PER_UNIT)/511, + (505*S3L_FRACTIONS_PER_UNIT)/511, (506*S3L_FRACTIONS_PER_UNIT)/511, + (507*S3L_FRACTIONS_PER_UNIT)/511, (507*S3L_FRACTIONS_PER_UNIT)/511, + (508*S3L_FRACTIONS_PER_UNIT)/511, (509*S3L_FRACTIONS_PER_UNIT)/511, + (509*S3L_FRACTIONS_PER_UNIT)/511, (510*S3L_FRACTIONS_PER_UNIT)/511, + (510*S3L_FRACTIONS_PER_UNIT)/511, (510*S3L_FRACTIONS_PER_UNIT)/511, + (510*S3L_FRACTIONS_PER_UNIT)/511, (510*S3L_FRACTIONS_PER_UNIT)/511 +}; + +#define S3L_SIN_TABLE_UNIT_STEP\ + (S3L_FRACTIONS_PER_UNIT / (S3L_SIN_TABLE_LENGTH * 4)) + +void S3L_initVec4(S3L_Vec4 *v) +{ + v->x = 0; v->y = 0; v->z = 0; v->w = S3L_FRACTIONS_PER_UNIT; +} + +void S3L_setVec4(S3L_Vec4 *v, S3L_Unit x, S3L_Unit y, S3L_Unit z, S3L_Unit w) +{ + v->x = x; + v->y = y; + v->z = z; + v->w = w; +} + +void S3L_vec3Add(S3L_Vec4 *result, S3L_Vec4 added) +{ + result->x += added.x; + result->y += added.y; + result->z += added.z; +} + +void S3L_vec3Sub(S3L_Vec4 *result, S3L_Vec4 substracted) +{ + result->x -= substracted.x; + result->y -= substracted.y; + result->z -= substracted.z; +} + +void S3L_initMat4(S3L_Mat4 *m) +{ + #define M(x,y) (*m)[x][y] + #define S S3L_FRACTIONS_PER_UNIT + + M(0,0) = S; M(1,0) = 0; M(2,0) = 0; M(3,0) = 0; + M(0,1) = 0; M(1,1) = S; M(2,1) = 0; M(3,1) = 0; + M(0,2) = 0; M(1,2) = 0; M(2,2) = S; M(3,2) = 0; + M(0,3) = 0; M(1,3) = 0; M(2,3) = 0; M(3,3) = S; + + #undef M + #undef S +} + +S3L_Unit S3L_dotProductVec3(S3L_Vec4 a, S3L_Vec4 b) +{ + return (a.x * b.x + a.y * b.y + a.z * b.z) / S3L_FRACTIONS_PER_UNIT; +} + +void S3L_reflect(S3L_Vec4 toLight, S3L_Vec4 normal, S3L_Vec4 *result) +{ + S3L_Unit d = 2 * S3L_dotProductVec3(toLight,normal); + + result->x = (normal.x * d) / S3L_FRACTIONS_PER_UNIT - toLight.x; + result->y = (normal.y * d) / S3L_FRACTIONS_PER_UNIT - toLight.y; + result->z = (normal.z * d) / S3L_FRACTIONS_PER_UNIT - toLight.z; +} + +void S3L_crossProduct(S3L_Vec4 a, S3L_Vec4 b, S3L_Vec4 *result) +{ + result->x = a.y * b.z - a.z * b.y; + result->y = a.z * b.x - a.x * b.z; + result->z = a.x * b.y - a.y * b.x; +} + +void S3L_triangleNormal(S3L_Vec4 t0, S3L_Vec4 t1, S3L_Vec4 t2, S3L_Vec4 *n) +{ + #define ANTI_OVERFLOW 32 + + t1.x = (t1.x - t0.x) / ANTI_OVERFLOW; + t1.y = (t1.y - t0.y) / ANTI_OVERFLOW; + t1.z = (t1.z - t0.z) / ANTI_OVERFLOW; + + t2.x = (t2.x - t0.x) / ANTI_OVERFLOW; + t2.y = (t2.y - t0.y) / ANTI_OVERFLOW; + t2.z = (t2.z - t0.z) / ANTI_OVERFLOW; + + #undef ANTI_OVERFLOW + + S3L_crossProduct(t1,t2,n); + + S3L_normalizeVec3(n); +} + +void S3L_getIndexedTriangleValues( + S3L_Index triangleIndex, + const S3L_Index *indices, + const S3L_Unit *values, + uint8_t numComponents, + S3L_Vec4 *v0, + S3L_Vec4 *v1, + S3L_Vec4 *v2) +{ + uint32_t i0, i1; + S3L_Unit *value; + + i0 = triangleIndex * 3; + i1 = indices[i0] * numComponents; + value = (S3L_Unit *) v0; + + if (numComponents > 4) + numComponents = 4; + + for (uint8_t j = 0; j < numComponents; ++j) + { + *value = values[i1]; + i1++; + value++; + } + + i0++; + i1 = indices[i0] * numComponents; + value = (S3L_Unit *) v1; + + for (uint8_t j = 0; j < numComponents; ++j) + { + *value = values[i1]; + i1++; + value++; + } + + i0++; + i1 = indices[i0] * numComponents; + value = (S3L_Unit *) v2; + + for (uint8_t j = 0; j < numComponents; ++j) + { + *value = values[i1]; + i1++; + value++; + } +} + +void S3L_computeModelNormals(S3L_Model3D model, S3L_Unit *dst, + int8_t transformNormals) +{ + S3L_Index vPos = 0; + + S3L_Vec4 n; + + n.w = 0; + + S3L_Vec4 ns[S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE]; + S3L_Index normalCount; + + for (uint32_t i = 0; i < model.vertexCount; ++i) + { + normalCount = 0; + + for (uint32_t j = 0; j < model.triangleCount * 3; j += 3) + { + if ( + (model.triangles[j] == i) || + (model.triangles[j + 1] == i) || + (model.triangles[j + 2] == i)) + { + S3L_Vec4 t0, t1, t2; + uint32_t vIndex; + + #define getVertex(n)\ + vIndex = model.triangles[j + n] * 3;\ + t##n.x = model.vertices[vIndex];\ + vIndex++;\ + t##n.y = model.vertices[vIndex];\ + vIndex++;\ + t##n.z = model.vertices[vIndex]; + + getVertex(0) + getVertex(1) + getVertex(2) + + #undef getVertex + + S3L_triangleNormal(t0,t1,t2,&(ns[normalCount])); + + normalCount++; + + if (normalCount >= S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE) + break; + } + } + + n.x = S3L_FRACTIONS_PER_UNIT; + n.y = 0; + n.z = 0; + + if (normalCount != 0) + { + // compute average + + n.x = 0; + + for (uint8_t i = 0; i < normalCount; ++i) + { + n.x += ns[i].x; + n.y += ns[i].y; + n.z += ns[i].z; + } + + n.x /= normalCount; + n.y /= normalCount; + n.z /= normalCount; + + S3L_normalizeVec3(&n); + } + + dst[vPos] = n.x; + vPos++; + + dst[vPos] = n.y; + vPos++; + + dst[vPos] = n.z; + vPos++; + } + + S3L_Mat4 m; + + S3L_makeWorldMatrix(model.transform,&m); + + if (transformNormals) + for (S3L_Index i = 0; i < model.vertexCount * 3; i += 3) + { + n.x = dst[i]; + n.y = dst[i + 1]; + n.z = dst[i + 2]; + + S3L_vec4Xmat4(&n,&m); + + dst[i] = n.x; + dst[i + 1] = n.y; + dst[i + 2] = n.z; + } +} + +void S3L_vec4Xmat4(S3L_Vec4 *v, S3L_Mat4 *m) +{ + S3L_Vec4 vBackup; + + vBackup.x = v->x; + vBackup.y = v->y; + vBackup.z = v->z; + vBackup.w = v->w; + + #define dotCol(col)\ + ((vBackup.x * (*m)[col][0]) +\ + (vBackup.y * (*m)[col][1]) +\ + (vBackup.z * (*m)[col][2]) +\ + (vBackup.w * (*m)[col][3])) / S3L_FRACTIONS_PER_UNIT + + v->x = dotCol(0); + v->y = dotCol(1); + v->z = dotCol(2); + v->w = dotCol(3); +} + +void S3L_vec3Xmat4(S3L_Vec4 *v, S3L_Mat4 *m) +{ + S3L_Vec4 vBackup; + + #undef dotCol + #define dotCol(col)\ + (vBackup.x * (*m)[col][0]) / S3L_FRACTIONS_PER_UNIT +\ + (vBackup.y * (*m)[col][1]) / S3L_FRACTIONS_PER_UNIT +\ + (vBackup.z * (*m)[col][2]) / S3L_FRACTIONS_PER_UNIT +\ + (*m)[col][3] + + vBackup.x = v->x; + vBackup.y = v->y; + vBackup.z = v->z; + vBackup.w = v->w; + + v->x = dotCol(0); + v->y = dotCol(1); + v->z = dotCol(2); + v->w = S3L_FRACTIONS_PER_UNIT; +} + +#undef dotCol + +S3L_Unit S3L_abs(S3L_Unit value) +{ + return value >= 0 ? value : -1 * value; +} + +S3L_Unit S3L_min(S3L_Unit v1, S3L_Unit v2) +{ + return v1 >= v2 ? v2 : v1; +} + +S3L_Unit S3L_max(S3L_Unit v1, S3L_Unit v2) +{ + return v1 >= v2 ? v1 : v2; +} + +S3L_Unit S3L_clamp(S3L_Unit v, S3L_Unit v1, S3L_Unit v2) +{ + return v >= v1 ? (v <= v2 ? v : v2) : v1; +} + +S3L_Unit S3L_wrap(S3L_Unit value, S3L_Unit mod) +{ + return value >= 0 ? (value % mod) : (mod + (value % mod) - 1); +} + +S3L_Unit S3L_nonZero(S3L_Unit value) +{ + return value != 0 ? value : 1; +} + +S3L_Unit S3L_interpolate(S3L_Unit v1, S3L_Unit v2, S3L_Unit t, S3L_Unit tMax) +{ + return v1 + ((v2 - v1) * t) / tMax; +} + +S3L_Unit S3L_interpolateByUnit(S3L_Unit v1, S3L_Unit v2, S3L_Unit t) +{ + return v1 + ((v2 - v1) * t) / S3L_FRACTIONS_PER_UNIT; +} + +S3L_Unit S3L_interpolateByUnitFrom0(S3L_Unit v2, S3L_Unit t) +{ + return (v2 * t) / S3L_FRACTIONS_PER_UNIT; +} + +S3L_Unit S3L_interpolateFrom0(S3L_Unit v2, S3L_Unit t, S3L_Unit tMax) +{ + return (v2 * t) / tMax; +} + +S3L_Unit S3L_distanceManhattan(S3L_Vec4 a, S3L_Vec4 b) +{ + return + S3L_abs(a.x - b.x) + + S3L_abs(a.y - b.y) + + S3L_abs(a.z - b.z); +} + +void S3L_mat4Xmat4(S3L_Mat4 *m1, S3L_Mat4 *m2) +{ + S3L_Mat4 mat1; + + for (uint16_t row = 0; row < 4; ++row) + for (uint16_t col = 0; col < 4; ++col) + mat1[col][row] = (*m1)[col][row]; + + for (uint16_t row = 0; row < 4; ++row) + for (uint16_t col = 0; col < 4; ++col) + { + (*m1)[col][row] = 0; + + for (uint16_t i = 0; i < 4; ++i) + (*m1)[col][row] += + (mat1[i][row] * (*m2)[col][i]) / S3L_FRACTIONS_PER_UNIT; + } +} + +S3L_Unit S3L_sin(S3L_Unit x) +{ + x = S3L_wrap(x / S3L_SIN_TABLE_UNIT_STEP,S3L_SIN_TABLE_LENGTH * 4); + int8_t positive = 1; + + if (x < S3L_SIN_TABLE_LENGTH) + x = x; + else if (x < S3L_SIN_TABLE_LENGTH * 2) + x = S3L_SIN_TABLE_LENGTH * 2 - x - 1; + else if (x < S3L_SIN_TABLE_LENGTH * 3) + { + x = x - S3L_SIN_TABLE_LENGTH * 2; + positive = 0; + } + else + { + x = S3L_SIN_TABLE_LENGTH - (x - S3L_SIN_TABLE_LENGTH * 3) - 1; + positive = 0; + } + + return positive ? S3L_sinTable[x] : -1 * S3L_sinTable[x]; +} + +S3L_Unit S3L_asin(S3L_Unit x) +{ + x = S3L_clamp(x,-S3L_FRACTIONS_PER_UNIT,S3L_FRACTIONS_PER_UNIT); + + int8_t sign = 1; + + if (x < 0) + { + sign = -1; + x *= -1; + } + + int16_t low = 0; + int16_t high = S3L_SIN_TABLE_LENGTH -1; + int16_t middle; + + while (low <= high) // binary search + { + middle = (low + high) / 2; + + S3L_Unit v = S3L_sinTable[middle]; + + if (v > x) + high = middle - 1; + else if (v < x) + low = middle + 1; + else + break; + } + + middle *= S3L_SIN_TABLE_UNIT_STEP; + + return sign * middle; +} + +S3L_Unit S3L_cos(S3L_Unit x) +{ + return S3L_sin(x + S3L_FRACTIONS_PER_UNIT / 4); +} + +void S3L_correctBarycentricCoords(S3L_Unit barycentric[3]) +{ + barycentric[0] = S3L_clamp(barycentric[0],0,S3L_FRACTIONS_PER_UNIT); + barycentric[1] = S3L_clamp(barycentric[1],0,S3L_FRACTIONS_PER_UNIT); + + S3L_Unit d = S3L_FRACTIONS_PER_UNIT - barycentric[0] - barycentric[1]; + + if (d < 0) + { + barycentric[0] += d; + barycentric[2] = 0; + } + else + barycentric[2] = d; +} + +void S3L_makeTranslationMat( + S3L_Unit offsetX, + S3L_Unit offsetY, + S3L_Unit offsetZ, + S3L_Mat4 *m) +{ + #define M(x,y) (*m)[x][y] + #define S S3L_FRACTIONS_PER_UNIT + + M(0,0) = S; M(1,0) = 0; M(2,0) = 0; M(3,0) = 0; + M(0,1) = 0; M(1,1) = S; M(2,1) = 0; M(3,1) = 0; + M(0,2) = 0; M(1,2) = 0; M(2,2) = S; M(3,2) = 0; + M(0,3) = offsetX; M(1,3) = offsetY; M(2,3) = offsetZ; M(3,3) = S; + + #undef M + #undef S +} + +void S3L_makeScaleMatrix( + S3L_Unit scaleX, + S3L_Unit scaleY, + S3L_Unit scaleZ, + S3L_Mat4 *m) +{ + #define M(x,y) (*m)[x][y] + + M(0,0) = scaleX; M(1,0) = 0; M(2,0) = 0; M(3,0) = 0; + M(0,1) = 0; M(1,1) = scaleY; M(2,1) = 0; M(3,1) = 0; + M(0,2) = 0; M(1,2) = 0; M(2,2) = scaleZ; M(3,2) = 0; + M(0,3) = 0; M(1,3) = 0; M(2,3) = 0; M(3,3) = S3L_FRACTIONS_PER_UNIT; + + #undef M +} + +void S3L_makeRotationMatrixZXY( + S3L_Unit byX, + S3L_Unit byY, + S3L_Unit byZ, + S3L_Mat4 *m) +{ + byX *= -1; + byY *= -1; + byZ *= -1; + + S3L_Unit sx = S3L_sin(byX); + S3L_Unit sy = S3L_sin(byY); + S3L_Unit sz = S3L_sin(byZ); + + S3L_Unit cx = S3L_cos(byX); + S3L_Unit cy = S3L_cos(byY); + S3L_Unit cz = S3L_cos(byZ); + + #define M(x,y) (*m)[x][y] + #define S S3L_FRACTIONS_PER_UNIT + + M(0,0) = (cy * cz) / S + (sy * sx * sz) / (S * S); + M(1,0) = (cx * sz) / S; + M(2,0) = (cy * sx * sz) / (S * S) - (cz * sy) / S; + M(3,0) = 0; + + M(0,1) = (cz * sy * sx) / (S * S) - (cy * sz) / S; + M(1,1) = (cx * cz) / S; + M(2,1) = (cy * cz * sx) / (S * S) + (sy * sz) / S; + M(3,1) = 0; + + M(0,2) = (cx * sy) / S; + M(1,2) = -1 * sx; + M(2,2) = (cy * cx) / S; + M(3,2) = 0; + + M(0,3) = 0; + M(1,3) = 0; + M(2,3) = 0; + M(3,3) = S3L_FRACTIONS_PER_UNIT; + + #undef M + #undef S +} + +S3L_Unit S3L_sqrt(S3L_Unit value) +{ + int8_t sign = 1; + + if (value < 0) + { + sign = -1; + value *= -1; + } + + uint32_t result = 0; + uint32_t a = value; + uint32_t b = 1u << 30; + + while (b > a) + b >>= 2; + + while (b != 0) + { + if (a >= result + b) + { + a -= result + b; + result = result + 2 * b; + } + + b >>= 2; + result >>= 1; + } + + return result * sign; +} + +S3L_Unit S3L_vec3Length(S3L_Vec4 v) +{ + return S3L_sqrt(v.x * v.x + v.y * v.y + v.z * v.z); +} + +S3L_Unit S3L_vec2Length(S3L_Vec4 v) +{ + return S3L_sqrt(v.x * v.x + v.y * v.y); +} + +void S3L_normalizeVec3(S3L_Vec4 *v) +{ + #define SCALE 16 + #define BOTTOM_LIMIT 16 + #define UPPER_LIMIT 900 + + /* Here we try to decide if the vector is too small and would cause + inaccurate result due to very its inaccurate length. If so, we scale + it up. We can't scale up everything as big vectors overflow in length + calculations. */ + + if ( + S3L_abs(v->x) <= BOTTOM_LIMIT && + S3L_abs(v->y) <= BOTTOM_LIMIT && + S3L_abs(v->z) <= BOTTOM_LIMIT) + { + v->x *= SCALE; + v->y *= SCALE; + v->z *= SCALE; + } + else if ( + S3L_abs(v->x) > UPPER_LIMIT || + S3L_abs(v->y) > UPPER_LIMIT || + S3L_abs(v->z) > UPPER_LIMIT) + { + v->x /= SCALE; + v->y /= SCALE; + v->z /= SCALE; + } + + #undef SCALE + #undef BOTTOM_LIMIT + #undef UPPER_LIMIT + + S3L_Unit l = S3L_vec3Length(*v); + + if (l == 0) + return; + + v->x = (v->x * S3L_FRACTIONS_PER_UNIT) / l; + v->y = (v->y * S3L_FRACTIONS_PER_UNIT) / l; + v->z = (v->z * S3L_FRACTIONS_PER_UNIT) / l; +} + +void S3L_normalizeVec3Fast(S3L_Vec4 *v) +{ + S3L_Unit l = S3L_vec3Length(*v); + + if (l == 0) + return; + + v->x = (v->x * S3L_FRACTIONS_PER_UNIT) / l; + v->y = (v->y * S3L_FRACTIONS_PER_UNIT) / l; + v->z = (v->z * S3L_FRACTIONS_PER_UNIT) / l; +} + +void S3L_initTransoform3D(S3L_Transform3D *t) +{ + S3L_initVec4(&(t->translation)); + S3L_initVec4(&(t->rotation)); + t->scale.x = S3L_FRACTIONS_PER_UNIT; + t->scale.y = S3L_FRACTIONS_PER_UNIT; + t->scale.z = S3L_FRACTIONS_PER_UNIT; + t->scale.w = 0; +} + +/** Performs perspecive division (z-divide). Does NOT check for division by + zero. */ +static inline void S3L_perspectiveDivide(S3L_Vec4 *vector, + S3L_Unit focalLength) +{ + vector->x = (vector->x * focalLength) / vector->z; + vector->y = (vector->y * focalLength) / vector->z; +} + +void project3DPointToScreen( + S3L_Vec4 point, + S3L_Camera camera, + S3L_Vec4 *result) +{ + S3L_Mat4 m; + S3L_makeCameraMatrix(camera.transform,&m); + + S3L_Unit s = point.w; + + point.w = S3L_FRACTIONS_PER_UNIT; + + S3L_vec3Xmat4(&point,&m); + + point.z = S3L_nonZero(point.z); + + S3L_perspectiveDivide(&point,camera.focalLength); + + S3L_ScreenCoord x, y; + + S3L_mapProjectionPlaneToScreen(point,&x,&y); + + result->x = x; + result->y = y; + result->z = point.z; + + result->w = + (point.z <= 0) ? 0 : + ( + (s * camera.focalLength * S3L_RESOLUTION_X) / + (point.z * S3L_FRACTIONS_PER_UNIT) + ); +} + +void S3L_lookAt(S3L_Vec4 pointTo, S3L_Transform3D *t) +{ + S3L_Vec4 v; + + v.x = pointTo.x - t->translation.x; + v.y = pointTo.z - t->translation.z; + + S3L_Unit dx = v.x; + S3L_Unit l = S3L_vec2Length(v); + + dx = (v.x * S3L_FRACTIONS_PER_UNIT) / S3L_nonZero(l); // normalize + + t->rotation.y = -1 * S3L_asin(dx); + + if (v.y < 0) + t->rotation.y = S3L_FRACTIONS_PER_UNIT / 2 - t->rotation.y; + + v.x = pointTo.y - t->translation.y; + v.y = l; + + l = S3L_vec2Length(v); + + dx = (v.x * S3L_FRACTIONS_PER_UNIT) / S3L_nonZero(l); + + t->rotation.x = S3L_asin(dx); +} + +void S3L_setTransform3D( + S3L_Unit tx, + S3L_Unit ty, + S3L_Unit tz, + S3L_Unit rx, + S3L_Unit ry, + S3L_Unit rz, + S3L_Unit sx, + S3L_Unit sy, + S3L_Unit sz, + S3L_Transform3D *t) +{ + t->translation.x = tx; + t->translation.y = ty; + t->translation.z = tz; + + t->rotation.x = rx; + t->rotation.y = ry; + t->rotation.z = rz; + + t->scale.x = sx; + t->scale.y = sy; + t->scale.z = sz; +} + +void S3L_initCamera(S3L_Camera *camera) +{ + camera->focalLength = S3L_FRACTIONS_PER_UNIT; + S3L_initTransoform3D(&(camera->transform)); +} + +void S3L_rotationToDirections( + S3L_Vec4 rotation, + S3L_Unit length, + S3L_Vec4 *forw, + S3L_Vec4 *right, + S3L_Vec4 *up) +{ + S3L_Mat4 m; + + S3L_makeRotationMatrixZXY(rotation.x,rotation.y,rotation.z,&m); + + if (forw != 0) + { + forw->x = 0; + forw->y = 0; + forw->z = length; + S3L_vec3Xmat4(forw,&m); + } + + if (right != 0) + { + right->x = length; + right->y = 0; + right->z = 0; + S3L_vec3Xmat4(right,&m); + } + + if (up != 0) + { + up->x = 0; + up->y = length; + up->z = 0; + S3L_vec3Xmat4(up,&m); + } +} + +void S3L_initPixelInfo(S3L_PixelInfo *p) +{ + p->x = 0; + p->y = 0; + p->barycentric[0] = S3L_FRACTIONS_PER_UNIT; + p->barycentric[1] = 0; + p->barycentric[2] = 0; + p->modelIndex = 0; + p->triangleIndex = 0; + p->triangleID = 0; + p->depth = 0; + p->previousZ = 0; +} + +void S3L_initModel3D( + const S3L_Unit *vertices, + S3L_Unit vertexCount, + const S3L_Index *triangles, + S3L_Index triangleCount, + S3L_Model3D *model) +{ + model->vertices = vertices; + model->vertexCount = vertexCount; + model->triangles = triangles; + model->triangleCount = triangleCount; + model->customTransformMatrix = 0; + + S3L_initTransoform3D(&(model->transform)); + S3L_initDrawConfig(&(model->config)); +} + +void S3L_initScene( + S3L_Model3D *models, + S3L_Index modelCount, + S3L_Scene *scene) +{ + scene->models = models; + scene->modelCount = modelCount; + S3L_initCamera(&(scene->camera)); +} + +void S3L_initDrawConfig(S3L_DrawConfig *config) +{ + config->backfaceCulling = 2; + config->visible = 1; +} + +static inline void S3L_PIXEL_FUNCTION(S3L_PixelInfo *pixel); // forward decl + +/** Serves to accelerate linear interpolation for performance-critical + code. Functions such as S3L_interpolate require division to compute each + interpolated value, while S3L_FastLerpState only requires a division for + the initiation and a shift for retrieving each interpolated value. + + S3L_FastLerpState stores a value and a step, both scaled (shifted by + S3L_FAST_LERP_QUALITY) to increase precision. The step is being added to the + value, which achieves the interpolation. This will only be useful for + interpolations in which we need to get the interpolated value in every step. + + BEWARE! Shifting a negative value is undefined, so handling shifting of + negative values has to be done cleverly. */ +typedef struct +{ + S3L_Unit valueScaled; + S3L_Unit stepScaled; +} S3L_FastLerpState; + +#define S3L_getFastLerpValue(state)\ + (state.valueScaled >> S3L_FAST_LERP_QUALITY) + +#define S3L_stepFastLerp(state)\ + state.valueScaled += state.stepScaled + +static inline S3L_Unit S3L_interpolateBarycentric( + S3L_Unit value0, + S3L_Unit value1, + S3L_Unit value2, + S3L_Unit barycentric[3]) +{ + return + ( + (value0 * barycentric[0]) + + (value1 * barycentric[1]) + + (value2 * barycentric[2]) + ) / S3L_FRACTIONS_PER_UNIT; +} + +void S3L_mapProjectionPlaneToScreen( + S3L_Vec4 point, + S3L_ScreenCoord *screenX, + S3L_ScreenCoord *screenY) +{ + *screenX = + S3L_HALF_RESOLUTION_X + + (point.x * S3L_HALF_RESOLUTION_X) / S3L_FRACTIONS_PER_UNIT; + + *screenY = + S3L_HALF_RESOLUTION_Y - + (point.y * S3L_HALF_RESOLUTION_X) / S3L_FRACTIONS_PER_UNIT; +} + +void S3L_zBufferClear() +{ +#if S3L_Z_BUFFER + for (uint32_t i = 0; i < S3L_RESOLUTION_X * S3L_RESOLUTION_Y; ++i) + S3L_zBuffer[i] = S3L_MAX_DEPTH; +#endif +} + +void S3L_stencilBufferClear() +{ +#if S3L_STENCIL_BUFFER + for (uint32_t i = 0; i < S3L_STENCIL_BUFFER_SIZE; ++i) + S3L_stencilBuffer[i] = 0; +#endif +} + +void S3L_newFrame() +{ + S3L_zBufferClear(); + S3L_stencilBufferClear(); +} + +void S3L_drawTriangle( + S3L_Vec4 point0, + S3L_Vec4 point1, + S3L_Vec4 point2, + S3L_Index modelIndex, + S3L_Index triangleIndex) +{ + S3L_PixelInfo p; + S3L_initPixelInfo(&p); + p.modelIndex = modelIndex; + p.triangleIndex = triangleIndex; + p.triangleID = (modelIndex << 16) | triangleIndex; + + S3L_Vec4 *tPointSS, *lPointSS, *rPointSS; /* points in Screen Space (in + S3L_Units, normalized by + S3L_FRACTIONS_PER_UNIT) */ + + S3L_Unit *barycentric0; // bar. coord that gets higher from L to R + S3L_Unit *barycentric1; // bar. coord that gets higher from R to L + S3L_Unit *barycentric2; // bar. coord that gets higher from bottom up + + // sort the vertices: + + #define assignPoints(t,a,b)\ + {\ + tPointSS = &point##t;\ + barycentric2 = &(p.barycentric[t]);\ + if (S3L_triangleWinding(point##t.x,point##t.y,point##a.x,point##a.y,\ + point##b.x,point##b.y) >= 0)\ + {\ + lPointSS = &point##a; rPointSS = &point##b;\ + barycentric0 = &(p.barycentric[b]);\ + barycentric1 = &(p.barycentric[a]);\ + }\ + else\ + {\ + lPointSS = &point##b; rPointSS = &point##a;\ + barycentric0 = &(p.barycentric[a]);\ + barycentric1 = &(p.barycentric[b]);\ + }\ + } + + if (point0.y <= point1.y) + { + if (point0.y <= point2.y) + assignPoints(0,1,2) + else + assignPoints(2,0,1) + } + else + { + if (point1.y <= point2.y) + assignPoints(1,0,2) + else + assignPoints(2,0,1) + } + + #undef assignPoints + +#if S3L_FLAT + *barycentric0 = S3L_FRACTIONS_PER_UNIT / 3; + *barycentric1 = S3L_FRACTIONS_PER_UNIT / 3; + *barycentric2 = S3L_FRACTIONS_PER_UNIT - 2 * (S3L_FRACTIONS_PER_UNIT / 3); +#endif + + p.triangleSize[0] = rPointSS->x - lPointSS->x; + p.triangleSize[1] = + (rPointSS->y > lPointSS->y ? rPointSS->y : lPointSS->y) - tPointSS->y; + + // now draw the triangle line by line: + + S3L_ScreenCoord splitY; // Y of the vertically middle point of the triangle + S3L_ScreenCoord endY; // bottom Y of the whole triangle + int splitOnLeft; /* whether splitY is the y coord. of left or right + point */ + + if (rPointSS->y <= lPointSS->y) + { + splitY = rPointSS->y; + splitOnLeft = 0; + endY = lPointSS->y; + } + else + { + splitY = lPointSS->y; + splitOnLeft = 1; + endY = rPointSS->y; + } + + S3L_ScreenCoord currentY = tPointSS->y; + + /* We'll be using an algorithm similar to Bresenham line algorithm. The + specifics of this algorithm are among others: + + - drawing possibly NON-CONTINUOUS line + - NOT tracing the line exactly, but rather rasterizing one the right + side of it, according to the pixel CENTERS, INCLUDING the pixel + centers + + The principle is this: + + - Move vertically by pixels and accumulate the error (abs(dx/dy)). + - If the error is greater than one (crossed the next pixel center), keep + moving horizontally and substracting 1 from the error until it is less + than 1 again. + - To make this INTEGER ONLY, scale the case so that distance between + pixels is equal to dy (instead of 1). This way the error becomes + dx/dy * dy == dx, and we're comparing the error to (and potentially + substracting) 1 * dy == dy. */ + + int16_t + /* triangle side: + left right */ + lX, rX, // current x position on the screen + lDx, rDx, // dx (end point - start point) + lDy, rDy, // dy (end point - start point) + lInc, rInc, // direction in which to increment (1 or -1) + lErr, rErr, // current error (Bresenham) + lErrCmp, rErrCmp, // helper for deciding comparison (> vs >=) + lErrAdd, rErrAdd, // error value to add in each Bresenham cycle + lErrSub, rErrSub; // error value to substract when moving in x direction + + S3L_FastLerpState lSideFLS, rSideFLS; + +#if S3L_COMPUTE_LERP_DEPTH + S3L_FastLerpState lDepthFLS, rDepthFLS; + + #define initDepthFLS(s,p1,p2)\ + s##DepthFLS.valueScaled = p1##PointSS->z << S3L_FAST_LERP_QUALITY;\ + s##DepthFLS.stepScaled = ((p2##PointSS->z << S3L_FAST_LERP_QUALITY) -\ + s##DepthFLS.valueScaled) / (s##Dy != 0 ? s##Dy : 1); +#else + #define initDepthFLS(s,p1,p2) ; +#endif + + /* init side for the algorithm, params: + s - which side (l or r) + p1 - point from (t, l or r) + p2 - point to (t, l or r) + down - whether the side coordinate goes top-down or vice versa */ + #define initSide(s,p1,p2,down)\ + s##X = p1##PointSS->x;\ + s##Dx = p2##PointSS->x - p1##PointSS->x;\ + s##Dy = p2##PointSS->y - p1##PointSS->y;\ + initDepthFLS(s,p1,p2)\ + s##SideFLS.stepScaled = (S3L_FRACTIONS_PER_UNIT << S3L_FAST_LERP_QUALITY)\ + / (s##Dy != 0 ? s##Dy : 1);\ + s##SideFLS.valueScaled = 0;\ + if (!down)\ + {\ + s##SideFLS.valueScaled =\ + S3L_FRACTIONS_PER_UNIT << S3L_FAST_LERP_QUALITY;\ + s##SideFLS.stepScaled *= -1;\ + }\ + s##Inc = s##Dx >= 0 ? 1 : -1;\ + if (s##Dx < 0)\ + {s##Err = 0; s##ErrCmp = 0;}\ + else\ + {s##Err = s##Dy; s##ErrCmp = 1;}\ + s##ErrAdd = S3L_abs(s##Dx);\ + s##ErrSub = s##Dy != 0 ? s##Dy : 1; /* don't allow 0, could lead to an + infinite substracting loop */ + + #define stepSide(s)\ + while (s##Err - s##Dy >= s##ErrCmp)\ + {\ + s##X += s##Inc;\ + s##Err -= s##ErrSub;\ + }\ + s##Err += s##ErrAdd; + + initSide(r,t,r,1) + initSide(l,t,l,1) + +#if S3L_PERSPECTIVE_CORRECTION + /* PC is done by linearly interpolating reciprocals from which the corrected + velues can be computed. See + http://www.lysator.liu.se/~mikaelk/doc/perspectivetexture/ */ + + #if S3L_PERSPECTIVE_CORRECTION == 1 + #define Z_RECIP_NUMERATOR\ + (S3L_FRACTIONS_PER_UNIT * S3L_FRACTIONS_PER_UNIT * S3L_FRACTIONS_PER_UNIT) + #elif S3L_PERSPECTIVE_CORRECTION == 2 + #define Z_RECIP_NUMERATOR\ + (S3L_FRACTIONS_PER_UNIT * S3L_FRACTIONS_PER_UNIT) + #endif + /* ^ This numerator is a number by which we divide values for the + reciprocals. For PC == 2 it has to be lower because linear interpolation + scaling would make it overflow -- this results in lower depth precision + in bigger distance for PC == 2. */ + + S3L_Unit + tPointRecipZ, lPointRecipZ, rPointRecipZ, /* Reciprocals of the depth of + each triangle point. */ + lRecip0, lRecip1, rRecip0, rRecip1; /* Helper variables for swapping + the above after split. */ + + tPointRecipZ = Z_RECIP_NUMERATOR / S3L_nonZero(tPointSS->z); + lPointRecipZ = Z_RECIP_NUMERATOR / S3L_nonZero(lPointSS->z); + rPointRecipZ = Z_RECIP_NUMERATOR / S3L_nonZero(rPointSS->z); + + lRecip0 = tPointRecipZ; + lRecip1 = lPointRecipZ; + rRecip0 = tPointRecipZ; + rRecip1 = rPointRecipZ; + + #define manageSplitPerspective(b0,b1)\ + b1##Recip0 = b0##PointRecipZ;\ + b1##Recip1 = b1##PointRecipZ;\ + b0##Recip0 = b0##PointRecipZ;\ + b0##Recip1 = tPointRecipZ; +#else + #define manageSplitPerspective(b0,b1) ; +#endif + + // clip to the screen in y dimension: + + endY = S3L_min(endY,S3L_RESOLUTION_Y); + + /* Clipping above the screen (y < 0) can't be easily done here, will be + handled inside the loop. */ + + while (currentY < endY) /* draw the triangle from top to bottom -- the + bottom-most row is left out because, following + from the rasterization rules (see start of the + file), it is to never be rasterized. */ + { + if (currentY == splitY) // reached a vertical split of the triangle? + { + #define manageSplit(b0,b1,s0,s1)\ + S3L_Unit *tmp = barycentric##b0;\ + barycentric##b0 = barycentric##b1;\ + barycentric##b1 = tmp;\ + s0##SideFLS.valueScaled = (S3L_FRACTIONS_PER_UNIT\ + << S3L_FAST_LERP_QUALITY) - s0##SideFLS.valueScaled;\ + s0##SideFLS.stepScaled *= -1;\ + manageSplitPerspective(s0,s1) + + if (splitOnLeft) + { + initSide(l,l,r,0); + manageSplit(0,2,r,l) + } + else + { + initSide(r,r,l,0); + manageSplit(1,2,l,r) + } + } + + stepSide(r) + stepSide(l) + + if (currentY >= 0) /* clipping of pixels whose y < 0 (can't be easily done + outside the loop because of the Bresenham-like + algorithm steps) */ + { + p.y = currentY; + + // draw the horizontal line + +#if !S3L_FLAT + S3L_Unit rowLength = S3L_nonZero(rX - lX - 1); // prevent zero div + + #if S3L_PERSPECTIVE_CORRECTION + S3L_Unit lOverZ, lRecipZ, rOverZ, rRecipZ, lT, rT; + + lT = S3L_getFastLerpValue(lSideFLS); + rT = S3L_getFastLerpValue(rSideFLS); + + lOverZ = S3L_interpolateByUnitFrom0(lRecip1,lT); + lRecipZ = S3L_interpolateByUnit(lRecip0,lRecip1,lT); + + rOverZ = S3L_interpolateByUnitFrom0(rRecip1,rT); + rRecipZ = S3L_interpolateByUnit(rRecip0,rRecip1,rT); + #else + S3L_FastLerpState b0FLS, b1FLS; + + #if S3L_COMPUTE_LERP_DEPTH + S3L_FastLerpState depthFLS; + + depthFLS.valueScaled = lDepthFLS.valueScaled; + depthFLS.stepScaled = + (rDepthFLS.valueScaled - lDepthFLS.valueScaled) / rowLength; + #endif + + b0FLS.valueScaled = 0; + b1FLS.valueScaled = lSideFLS.valueScaled; + + b0FLS.stepScaled = rSideFLS.valueScaled / rowLength; + b1FLS.stepScaled = -1 * lSideFLS.valueScaled / rowLength; + #endif +#endif + + // clip to the screen in x dimension: + + S3L_ScreenCoord rXClipped = S3L_min(rX,S3L_RESOLUTION_X), + lXClipped = lX; + + if (lXClipped < 0) + { + lXClipped = 0; + +#if !S3L_PERSPECTIVE_CORRECTION && !S3L_FLAT + b0FLS.valueScaled -= lX * b0FLS.stepScaled; + b1FLS.valueScaled -= lX * b1FLS.stepScaled; + + #if S3L_COMPUTE_LERP_DEPTH + depthFLS.valueScaled -= lX * depthFLS.stepScaled; + #endif +#endif + } + +#if S3L_PERSPECTIVE_CORRECTION + S3L_ScreenCoord i = lXClipped - lX; /* helper var to save one + substraction in the inner + loop */ +#endif + +#if S3L_PERSPECTIVE_CORRECTION == 2 + S3L_FastLerpState + depthPC, // interpolates depth between row segments + b0PC, // interpolates barycentric0 between row segments + b1PC; // interpolates barycentric1 between row segments + + /* ^ These interpolate values between row segments (lines of pixels + of S3L_PC_APPROX_LENGTH length). After each row segment perspective + correction is recomputed. */ + + depthPC.valueScaled = + (Z_RECIP_NUMERATOR / + S3L_nonZero(S3L_interpolate(lRecipZ,rRecipZ,i,rowLength))) + << S3L_FAST_LERP_QUALITY; + + b0PC.valueScaled = + ( + S3L_interpolateFrom0(rOverZ,i,rowLength) + * depthPC.valueScaled + ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT); + + b1PC.valueScaled = + ( + (lOverZ - S3L_interpolateFrom0(lOverZ,i,rowLength)) + * depthPC.valueScaled + ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT); + + int8_t rowCount = S3L_PC_APPROX_LENGTH; +#endif + +#if S3L_Z_BUFFER + uint32_t zBufferIndex = p.y * S3L_RESOLUTION_X + lXClipped; +#endif + + // draw the row -- inner loop: + + for (S3L_ScreenCoord x = lXClipped; x < rXClipped; ++x) + { + int8_t testsPassed = 1; + +#if S3L_STENCIL_BUFFER + if (!S3L_stencilTest(x,p.y)) + testsPassed = 0; +#endif + p.x = x; + +#if S3L_COMPUTE_DEPTH + #if S3L_PERSPECTIVE_CORRECTION == 1 + p.depth = Z_RECIP_NUMERATOR / + S3L_nonZero(S3L_interpolate(lRecipZ,rRecipZ,i,rowLength)); + #elif S3L_PERSPECTIVE_CORRECTION == 2 + if (rowCount >= S3L_PC_APPROX_LENGTH) + { + // init the linear interpolation to the next PC correct value + + rowCount = 0; + + S3L_Unit nextI = i + S3L_PC_APPROX_LENGTH; + + if (nextI < rowLength) + { + S3L_Unit nextDepthScaled = + ( + Z_RECIP_NUMERATOR / + S3L_nonZero(S3L_interpolate(lRecipZ,rRecipZ,nextI,rowLength)) + ) << S3L_FAST_LERP_QUALITY; + + depthPC.stepScaled = + (nextDepthScaled - depthPC.valueScaled) / S3L_PC_APPROX_LENGTH; + + S3L_Unit nextValue = + ( + S3L_interpolateFrom0(rOverZ,nextI,rowLength) + * nextDepthScaled + ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT); + + b0PC.stepScaled = + (nextValue - b0PC.valueScaled) / S3L_PC_APPROX_LENGTH; + + nextValue = + ( + (lOverZ - S3L_interpolateFrom0(lOverZ,nextI,rowLength)) + * nextDepthScaled + ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT); + + b1PC.stepScaled = + (nextValue - b1PC.valueScaled) / S3L_PC_APPROX_LENGTH; + } + else + { + /* A special case where we'd be interpolating outside the triangle. + It seems like a valid approach at first, but it creates a bug + in a case when the rasaterized triangle is near screen 0 and can + actually never reach the extrapolated screen position. So we + have to clamp to the actual end of the triangle here. */ + + S3L_Unit maxI = S3L_nonZero(rowLength - i); + + S3L_Unit nextDepthScaled = + ( + Z_RECIP_NUMERATOR / + S3L_nonZero(rRecipZ) + ) << S3L_FAST_LERP_QUALITY; + + depthPC.stepScaled = + (nextDepthScaled - depthPC.valueScaled) / maxI; + + S3L_Unit nextValue = + ( + rOverZ + * nextDepthScaled + ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT); + + b0PC.stepScaled = + (nextValue - b0PC.valueScaled) / maxI; + + b1PC.stepScaled = + -1 * b1PC.valueScaled / maxI; + } + } + + p.depth = S3L_getFastLerpValue(depthPC); + #else + p.depth = S3L_getFastLerpValue(depthFLS); + S3L_stepFastLerp(depthFLS); + #endif +#else // !S3L_COMPUTE_DEPTH + p.depth = (tPointSS->z + lPointSS->z + rPointSS->z) / 3; +#endif + +#if S3L_Z_BUFFER + p.previousZ = S3L_zBuffer[zBufferIndex]; + + zBufferIndex++; + + if (!S3L_zTest(p.x,p.y,p.depth)) + testsPassed = 0; +#endif + + if (testsPassed) + { +#if !S3L_FLAT + #if S3L_PERSPECTIVE_CORRECTION == 0 + *barycentric0 = S3L_getFastLerpValue(b0FLS); + *barycentric1 = S3L_getFastLerpValue(b1FLS); + #elif S3L_PERSPECTIVE_CORRECTION == 1 + *barycentric0 = + ( + S3L_interpolateFrom0(rOverZ,i,rowLength) + * p.depth + ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT); + + *barycentric1 = + ( + (lOverZ - S3L_interpolateFrom0(lOverZ,i,rowLength)) + * p.depth + ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT); + #elif S3L_PERSPECTIVE_CORRECTION == 2 + *barycentric0 = S3L_getFastLerpValue(b0PC); + *barycentric1 = S3L_getFastLerpValue(b1PC); + #endif + + *barycentric2 = + S3L_FRACTIONS_PER_UNIT - *barycentric0 - *barycentric1; +#endif + S3L_PIXEL_FUNCTION(&p); + } // tests passed + +#if !S3L_FLAT + #if S3L_PERSPECTIVE_CORRECTION + i++; + #if S3L_PERSPECTIVE_CORRECTION == 2 + rowCount++; + + S3L_stepFastLerp(depthPC); + S3L_stepFastLerp(b0PC); + S3L_stepFastLerp(b1PC); + #endif + #else + S3L_stepFastLerp(b0FLS); + S3L_stepFastLerp(b1FLS); + #endif +#endif + } // inner loop + } // y clipping + +#if !S3L_FLAT + S3L_stepFastLerp(lSideFLS); + S3L_stepFastLerp(rSideFLS); + + #if S3L_COMPUTE_LERP_DEPTH + S3L_stepFastLerp(lDepthFLS); + S3L_stepFastLerp(rDepthFLS); + #endif +#endif + + ++currentY; + } // row drawing + + #undef manageSplit + #undef initPC + #undef initSide + #undef stepSide + #undef Z_RECIP_NUMERATOR +} + +void S3L_rotate2DPoint(S3L_Unit *x, S3L_Unit *y, S3L_Unit angle) +{ + if (angle < S3L_SIN_TABLE_UNIT_STEP) + return; // no visible rotation + + S3L_Unit angleSin = S3L_sin(angle); + S3L_Unit angleCos = S3L_cos(angle); + + S3L_Unit xBackup = *x; + + *x = + (angleCos * (*x)) / S3L_FRACTIONS_PER_UNIT - + (angleSin * (*y)) / S3L_FRACTIONS_PER_UNIT; + + *y = + (angleSin * xBackup) / S3L_FRACTIONS_PER_UNIT + + (angleCos * (*y)) / S3L_FRACTIONS_PER_UNIT; +} + +void S3L_makeWorldMatrix(S3L_Transform3D worldTransform, S3L_Mat4 *m) +{ + S3L_makeScaleMatrix( + worldTransform.scale.x, + worldTransform.scale.y, + worldTransform.scale.z, + m + ); + + S3L_Mat4 t; + + S3L_makeRotationMatrixZXY( + worldTransform.rotation.x, + worldTransform.rotation.y, + worldTransform.rotation.z, + &t); + + S3L_mat4Xmat4(m,&t); + + S3L_makeTranslationMat( + worldTransform.translation.x, + worldTransform.translation.y, + worldTransform.translation.z, + &t); + + S3L_mat4Xmat4(m,&t); +} + +void S3L_transposeMat4(S3L_Mat4 *m) +{ + S3L_Unit tmp; + + for (uint8_t y = 0; y < 3; ++y) + for (uint8_t x = 1 + y; x < 4; ++x) + { + tmp = (*m)[x][y]; + (*m)[x][y] = (*m)[y][x]; + (*m)[y][x] = tmp; + } +} + +void S3L_makeCameraMatrix(S3L_Transform3D cameraTransform, S3L_Mat4 *m) +{ + S3L_makeTranslationMat( + -1 * cameraTransform.translation.x, + -1 * cameraTransform.translation.y, + -1 * cameraTransform.translation.z, + m); + + S3L_Mat4 r; + + S3L_makeRotationMatrixZXY( + cameraTransform.rotation.x, + cameraTransform.rotation.y, + cameraTransform.rotation.z, + &r); + + S3L_transposeMat4(&r); // transposing creates an inverse transform + + S3L_mat4Xmat4(m,&r); +} + +int8_t S3L_triangleWinding( + S3L_ScreenCoord x0, + S3L_ScreenCoord y0, + S3L_ScreenCoord x1, + S3L_ScreenCoord y1, + S3L_ScreenCoord x2, + S3L_ScreenCoord y2) +{ + int32_t winding = + (y1 - y0) * (x2 - x1) - (x1 - x0) * (y2 - y1); + // ^ cross product for points with z == 0 + + return winding > 0 ? 1 : (winding < 0 ? -1 : 0); +} + +/** + Checks if given triangle (in Screen Space) is at least partially visible, + i.e. returns false if the triangle is either completely outside the frustum + (left, right, top, bottom, near) or is invisible due to backface culling. +*/ +static inline int8_t S3L_triangleIsVisible( + S3L_Vec4 p0, + S3L_Vec4 p1, + S3L_Vec4 p2, + uint8_t backfaceCulling) +{ + #define clipTest(c,cmp,v)\ + (p0.c cmp (v) && p1.c cmp (v) && p2.c cmp (v)) + + if ( // outside frustum? +#if S3L_STRICT_NEAR_CULLING + p0.z <= S3L_NEAR || p1.z <= S3L_NEAR || p2.z <= S3L_NEAR || + // ^ partially in front of NEAR? +#else + clipTest(z,<=,S3L_NEAR) || // completely in front of NEAR? +#endif + clipTest(x,<,0) || + clipTest(x,>=,S3L_RESOLUTION_X) || + clipTest(y,<,0) || + clipTest(y,>,S3L_RESOLUTION_Y) + ) + return 0; + + #undef clipTest + + if (backfaceCulling != 0) + { + int8_t winding = + S3L_triangleWinding(p0.x,p0.y,p1.x,p1.y,p2.x,p2.y); + + if ((backfaceCulling == 1 && winding > 0) || + (backfaceCulling == 2 && winding < 0)) + return 0; + } + + return 1; +} + +#if S3L_SORT != 0 +typedef struct +{ + uint8_t modelIndex; + S3L_Index triangleIndex; + uint16_t sortValue; +} _S3L_TriangleToSort; + +_S3L_TriangleToSort S3L_sortArray[S3L_MAX_TRIANGES_DRAWN]; +uint16_t S3L_sortArrayLength; +#endif + +void _S3L_projectVertex( + const S3L_Model3D *model, + S3L_Index triangleIndex, + uint8_t vertex, + S3L_Mat4 *projectionMatrix, + S3L_Vec4 *result, + S3L_Unit focalLength) +{ + uint32_t vertexIndex = model->triangles[triangleIndex * 3 + vertex] * 3; + + result->x = model->vertices[vertexIndex]; + result->y = model->vertices[vertexIndex + 1]; + result->z = model->vertices[vertexIndex + 2]; + result->w = S3L_FRACTIONS_PER_UNIT; // for translation + + S3L_vec3Xmat4(result,projectionMatrix); + + result->w = result->z; + /* We'll keep the non-clamped z in w for sorting. */ + + result->z = result->z >= S3L_NEAR ? result->z : S3L_NEAR; + /* ^ This firstly prevents zero division in the follwoing z-divide and + secondly "pushes" vertices that are in front of near a little bit forward, + which makes them behave a bit better. If all three vertices end up exactly + on NEAR, the triangle will be culled. */ + + S3L_perspectiveDivide(result,focalLength); + + S3L_ScreenCoord sX, sY; + + S3L_mapProjectionPlaneToScreen(*result,&sX,&sY); + + result->x = sX; + result->y = sY; +} + +void S3L_drawScene(S3L_Scene scene) +{ + S3L_Mat4 matFinal, matCamera; + S3L_Vec4 transformed0, transformed1, transformed2; + const S3L_Model3D *model; + S3L_Index modelIndex, triangleIndex; + + S3L_makeCameraMatrix(scene.camera.transform,&matCamera); + +#if S3L_SORT != 0 + uint16_t previousModel = 0; + S3L_sortArrayLength = 0; +#endif + + for (modelIndex = 0; modelIndex < scene.modelCount; ++modelIndex) + { + if (!scene.models[modelIndex].config.visible) + continue; + +#if S3L_SORT != 0 + if (S3L_sortArrayLength >= S3L_MAX_TRIANGES_DRAWN) + break; + + previousModel = modelIndex; +#endif + + if (scene.models[modelIndex].customTransformMatrix == 0) + S3L_makeWorldMatrix(scene.models[modelIndex].transform,&matFinal); + else + { + S3L_Mat4 *m = scene.models[modelIndex].customTransformMatrix; + + for (int8_t j = 0; j < 4; ++j) + for (int8_t i = 0; i < 4; ++i) + matFinal[i][j] = (*m)[i][j]; + } + + S3L_mat4Xmat4(&matFinal,&matCamera); + + S3L_Index triangleCount = scene.models[modelIndex].triangleCount; + + triangleIndex = 0; + + while (triangleIndex < triangleCount) + { + model = &(scene.models[modelIndex]); + + /* Some kind of cache could be used in theory to not project perviously + already projected vertices, but after some testing this was abandoned, + no gain was seen. */ + + _S3L_projectVertex(model,triangleIndex,0,&matFinal, + &transformed0,scene.camera.focalLength); + + _S3L_projectVertex(model,triangleIndex,1,&matFinal, + &transformed1,scene.camera.focalLength); + + _S3L_projectVertex(model,triangleIndex,2,&matFinal, + &transformed2,scene.camera.focalLength); + + if (S3L_triangleIsVisible(transformed0,transformed1,transformed2, + model->config.backfaceCulling)) + { +#if S3L_SORT == 0 + // without sorting draw right away + S3L_drawTriangle(transformed0,transformed1,transformed2,modelIndex, + triangleIndex); +#else + if (S3L_sortArrayLength >= S3L_MAX_TRIANGES_DRAWN) + break; + + // with sorting add to a sort list + S3L_sortArray[S3L_sortArrayLength].modelIndex = modelIndex; + S3L_sortArray[S3L_sortArrayLength].triangleIndex = triangleIndex; + S3L_sortArray[S3L_sortArrayLength].sortValue = + S3L_max(0,(transformed0.w + transformed1.w + transformed2.w)) >> 2; + /* ^ + The w component here stores non-clamped z. + + As a simple approximation we sort by the triangle center point, + which is a mean coordinate -- we don't actually have to divide by 3 + (or anything), that is unnecessary for sorting! We shift by 2 just + as a fast operation to prevent overflow of the sum over uint_16t. */ + + S3L_sortArrayLength++; +#endif + } + + triangleIndex++; + } + } + +#if S3L_SORT != 0 + + #if S3L_SORT == 1 + #define cmp < + #else + #define cmp > + #endif + + /* Sort the triangles. We use insertion sort, because it has many advantages, + especially for smaller arrays (better than bubble sort, in-place, stable, + simple, ...). */ + + for (int16_t i = 1; i < S3L_sortArrayLength; ++i) + { + _S3L_TriangleToSort tmp = S3L_sortArray[i]; + + int16_t j = i - 1; + + while (j >= 0 && S3L_sortArray[j].sortValue cmp tmp.sortValue) + { + S3L_sortArray[j + 1] = S3L_sortArray[j]; + j--; + } + + S3L_sortArray[j + 1] = tmp; + } + + #undef cmp + + for (S3L_Index i = 0; i < S3L_sortArrayLength; ++i) + { + modelIndex = S3L_sortArray[i].modelIndex; + triangleIndex = S3L_sortArray[i].triangleIndex; + + model = &(scene.models[modelIndex]); + + if (modelIndex != previousModel) + { + // only recompute the matrix when the model has changed + S3L_makeWorldMatrix(model->transform,&matFinal); + S3L_mat4Xmat4(&matFinal,&matCamera); + previousModel = modelIndex; + } + + /* Here we project the points again, which is redundant and slow as they've + already been projected above, but saving the projected points would + require a lot of memory, which for small resolutions could be even + worse than z-bufer. So this seems to be the best way memory-wise. */ + + _S3L_projectVertex(model,triangleIndex,0,&matFinal, + &transformed0,scene.camera.focalLength); + + _S3L_projectVertex(model,triangleIndex,1,&matFinal, + &transformed1,scene.camera.focalLength); + + _S3L_projectVertex(model,triangleIndex,2,&matFinal, + &transformed2,scene.camera.focalLength); + + S3L_drawTriangle(transformed0,transformed1,transformed2,modelIndex, + triangleIndex); + } +#endif +} + +#endif // guard