From a7b1c5a117ba3183f675fbfe2d107fff37dde06b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miloslav=20=C4=8C=C3=AD=C5=BE?= Date: Tue, 2 Jul 2019 01:02:37 +0200 Subject: [PATCH] Add Pokitto files --- programs/pokitto/carModel.h | 104 ++ programs/pokitto/city.bin | Bin 0 -> 82804 bytes programs/pokitto/level.bin | Bin 0 -> 68096 bytes programs/pokitto/level.cpp | 243 +++ programs/pokitto/levelModel.h | 1401 ++++++++++++++ programs/pokitto/levelTexture1Pal.h | 72 + programs/pokitto/modelViewer.bin | Bin 0 -> 88332 bytes programs/pokitto/small3dlib.h | 2652 +++++++++++++++++++++++++++ 8 files changed, 4472 insertions(+) create mode 100644 programs/pokitto/carModel.h create mode 100755 programs/pokitto/city.bin create mode 100755 programs/pokitto/level.bin create mode 100644 programs/pokitto/level.cpp create mode 100644 programs/pokitto/levelModel.h create mode 100644 programs/pokitto/levelTexture1Pal.h create mode 100755 programs/pokitto/modelViewer.bin create mode 100644 programs/pokitto/small3dlib.h 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 0000000000000000000000000000000000000000..f38e44709a4699b3e204579e0f4d9672e1d89ef7 GIT binary patch literal 82804 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

b1Uh_p&U8OoU#|XB`VxBv z{TD6j9T+;0thqke6;>`@j23lT z|9ck>B@gTz8XipU-+9~c&I3clo|esx$(wC~ejVvIK5NWh`~iRS2iz0jo8h^<$^_!6{3)=5{XtvT{_gYIE3Yzv+AHtc3J(IG z`#j&Z25|oujJXk7a2(p@MT@8WLpksW^!Vpbt{s0W@zmpM2iJWDSNP6yW3IWPXGV_ literal 0 HcmV?d00001 diff --git a/programs/pokitto/level.bin b/programs/pokitto/level.bin new file mode 100755 index 0000000000000000000000000000000000000000..d8b715983067791be09953f7df61216b300b55be GIT binary patch literal 68096 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 I= 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 0000000000000000000000000000000000000000..3c8e676ff01741d9aeb633c662c2c8fab6b7b4d1 GIT binary patch literal 88332 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 literal 0 HcmV?d00001 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