From 2241b4d6350f7840a19c310b3cc754aded08e640 Mon Sep 17 00:00:00 2001 From: Miloslav Ciz Date: Thu, 1 Aug 2024 21:41:21 +0200 Subject: [PATCH] Improve renderer, handle textures --- TODO.txt | 1 + assets.h | 33 +++++++-- game.h | 3 +- map.h | 22 ++++-- renderer.h | 191 ++++++++++++++++++++++++++++++++++++++++------------- 5 files changed, 191 insertions(+), 59 deletions(-) diff --git a/TODO.txt b/TODO.txt index 047249e..40affe4 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,3 +1,4 @@ +- maybe change sticker to fan? could me more fun: TEST and see - based viewing distance idea: limit number of rendered map triangles to N, keep sorting the triangle array by distance continually! e.g. in each frame handle one of the N visible triangles like this -- in the non-visible triangles diff --git a/assets.h b/assets.h index d1a86e7..336068c 100644 --- a/assets.h +++ b/assets.h @@ -25,14 +25,24 @@ static const uint8_t map1[] = 10, 10, 0, - LCR_MAP_BLOCK(LCR_BLOCK_FULL,32,0,32,LCR_BLOCK_MATERIAL_CONCRETE,0), - LCR_MAP_BLOCK(LCR_BLOCK_FULL,33,63,32,LCR_BLOCK_MATERIAL_CONCRETE,0), - LCR_MAP_BLOCK(LCR_BLOCK_RAMP_CURVED_WALL,34,32,32,LCR_BLOCK_MATERIAL_CONCRETE,/*LCR_BLOCK_TRANSFORM_ROT_90*/0), + + LCR_MAP_BLOCK(LCR_BLOCK_FULL_ACCEL,32,32,32,LCR_BLOCK_MATERIAL_CONCRETE,0), + LCR_MAP_BLOCK(LCR_BLOCK_FULL_ACCEL,33,32,32,LCR_BLOCK_MATERIAL_GRASS,0), + LCR_MAP_BLOCK(LCR_BLOCK_FULL_ACCEL,34,32,32,LCR_BLOCK_MATERIAL_GRASS,LCR_BLOCK_TRANSFORM_ROT_90), + LCR_MAP_BLOCK(LCR_BLOCK_FULL_ACCEL,35,32,32,LCR_BLOCK_MATERIAL_CONCRETE,LCR_BLOCK_TRANSFORM_ROT_90), + LCR_MAP_BLOCK(LCR_BLOCK_FULL_ACCEL,36,32,32,LCR_BLOCK_MATERIAL_ICE,0), + LCR_MAP_BLOCK(LCR_BLOCK_FULL_ACCEL,37,32,32,LCR_BLOCK_MATERIAL_CONCRETE,0), + LCR_MAP_BLOCK(LCR_BLOCK_FULL_ACCEL,38,32,32,LCR_BLOCK_MATERIAL_ICE,0), + +/* + LCR_MAP_BLOCK(LCR_BLOCK_FULL,32,32,32,LCR_BLOCK_MATERIAL_CONCRETE,0), + LCR_MAP_BLOCK(LCR_BLOCK_FULL,33,32,32,LCR_BLOCK_MATERIAL_GRASS,0), + LCR_MAP_BLOCK(LCR_BLOCK_RAMP_CURVED_WALL,34,32,32,LCR_BLOCK_MATERIAL_GRASS,LCR_BLOCK_TRANSFORM_ROT_90), LCR_MAP_BLOCK(LCR_BLOCK_RAMP_CURVED_WALL,35,32,32,LCR_BLOCK_MATERIAL_CONCRETE,LCR_BLOCK_TRANSFORM_ROT_90), - LCR_MAP_BLOCK(LCR_BLOCK_FULL,38,35,32,LCR_BLOCK_MATERIAL_CONCRETE,0), - LCR_MAP_BLOCK(LCR_BLOCK_FULL,39,35,32,LCR_BLOCK_MATERIAL_CONCRETE,0), - LCR_MAP_BLOCK(LCR_BLOCK_FULL,40,35,32,LCR_BLOCK_MATERIAL_CONCRETE,0), + LCR_MAP_BLOCK(LCR_BLOCK_FULL,63,35,32,LCR_BLOCK_MATERIAL_CONCRETE,0), + LCR_MAP_BLOCK(LCR_BLOCK_FULL,0,35,32,LCR_BLOCK_MATERIAL_ICE,0), + LCR_MAP_BLOCK(LCR_BLOCK_FULL,40,35,32,LCR_BLOCK_MATERIAL_DIRT,0), LCR_MAP_BLOCK(LCR_BLOCK_FULL,41,35,32,LCR_BLOCK_MATERIAL_CONCRETE,0), LCR_MAP_BLOCK(LCR_BLOCK_FULL,42,35,32,LCR_BLOCK_MATERIAL_CONCRETE,0), LCR_MAP_BLOCK(LCR_BLOCK_FULL,43,35,32,LCR_BLOCK_MATERIAL_CONCRETE,0), @@ -46,6 +56,8 @@ static const uint8_t map1[] = LCR_MAP_BLOCK(LCR_BLOCK_FULL,33,33,33,LCR_BLOCK_MATERIAL_CONCRETE,0), LCR_MAP_BLOCK(LCR_BLOCK_FULL,32,32,33,LCR_BLOCK_MATERIAL_CONCRETE,0), LCR_MAP_BLOCK(LCR_BLOCK_FULL,33,32,33,LCR_BLOCK_MATERIAL_CONCRETE,0), +*/ + LCR_MAP_TERMINATOR }; @@ -57,6 +69,15 @@ uint16_t _LCR_currentImagePalette[256]; const uint8_t *_LCR_currentImagePixel; const uint8_t *_LCR_currentImage; +#define LCR_IMAGE_WALL_CONCRETE 0 +#define LCR_IMAGE_WALL_WOOD 1 +#define LCR_IMAGE_GROUND_CONCRETE 2 +#define LCR_IMAGE_GROUND_GRASS 3 +#define LCR_IMAGE_GROUND_DIRT 4 +#define LCR_IMAGE_GROUND_ICE 5 +#define LCR_IMAGE_GROUND_ACCEL 6 +#define LCR_IMAGE_GROUND_STICKER 7 + static const uint8_t LCR_images[] = { // 0: wall concrete diff --git a/game.h b/game.h index c87d992..44c5334 100644 --- a/game.h +++ b/game.h @@ -113,8 +113,7 @@ void LCR_gameInit(void) LCR_rendererInit(); - -LCR_loadImage(0); +LCR_loadImage(0); // ??? } void LCR_gameEnd(void) diff --git a/map.h b/map.h index bd51607..68681d6 100644 --- a/map.h +++ b/map.h @@ -64,7 +64,9 @@ #define LCR_BLOCK_SIZE 4 ///< size of map block, in bytes #define LCR_BLOCK_MATERIAL_CONCRETE 0x00 -#define LCR_BLOCK_MATERIAL_DIRT 0x01 +#define LCR_BLOCK_MATERIAL_GRASS 0x01 +#define LCR_BLOCK_MATERIAL_DIRT 0x02 +#define LCR_BLOCK_MATERIAL_ICE 0x03 #define LCR_MAP_COUNT 1 @@ -80,9 +82,12 @@ #define LCR_BLOCK_RAMP_CURVED_SHORT 0x08 #define LCR_BLOCK_RAMP_CURVED_WALL 0x09 -#define LCR_BLOCK_CHECKPOINT_0 0x10 ///< checkpoint, not taken -#define LCR_BLOCK_CHECKPOINT_1 0x11 ///< checkpoint, taken -#define LCR_BLOCK_FINISH 0x12 ///< finish +#define LCR_BLOCK_FULL_ACCEL 0x40 +#define LCR_BLOCK_FULL_STICKER 0x60 + +#define LCR_BLOCK_CHECKPOINT_0 0x10 ///< checkpoint, not taken +#define LCR_BLOCK_CHECKPOINT_1 0x11 ///< checkpoint, taken +#define LCR_BLOCK_FINISH 0x12 ///< finish // special blocks: #define LCR_BLOCK_NONE 0x80 /**< no block, can be used e.g to make @@ -138,6 +143,11 @@ uint8_t LCR_mapBlockGetTransform(const uint8_t block[LCR_BLOCK_SIZE]) return block[3] & 0xf0; } +uint8_t LCR_mapBlockGetMaterial(const uint8_t block[LCR_BLOCK_SIZE]) +{ + return (block[3] >> 2) & 0x03; +} + uint32_t LCR_mapBlockGetCoordNumber(const uint8_t block[LCR_BLOCK_SIZE]) { return block[1] | (((uint32_t) block[2]) << 8) | @@ -301,6 +311,8 @@ void _LCR_decodeMapBlockCoords(uint8_t byte, uint8_t *x, uint8_t *y, uint8_t *z) *z = (byte / 35); } +#define LCR_BLOCK_SHAPE_COORD_MAX 12 + /** Decodes XYZ coordinates encoded in a byte returned by LCR_mapGetBlockShape. Each coordinate will be in range 0 to 12 (including both). This unusual range @@ -357,6 +369,8 @@ void LCR_mapGetBlockShape(uint8_t blockType, uint8_t transform, case LCR_BLOCK_LEFT: case LCR_BLOCK_BOTTOM_LEFT: case LCR_BLOCK_BOTTOM_LEFT_FRONT: + case LCR_BLOCK_FULL_ACCEL: + case LCR_BLOCK_FULL_STICKER: { uint8_t xRight = 6, yTop = 4, zBack = 6 >> (blockType == LCR_BLOCK_BOTTOM_LEFT_FRONT); diff --git a/renderer.h b/renderer.h index a223a3b..4817ca1 100644 --- a/renderer.h +++ b/renderer.h @@ -28,6 +28,13 @@ struct S3L_Unit mapVertices[LCR_SETTING_MAX_MAP_VERTICES * 3]; S3L_Index mapTriangles[LCR_SETTING_MAX_MAP_TRIANGLES * 3]; + /** + Additional data for triangles. 4 higher bits hold direction (for lighting): + 0 is floor, 1 is wall, 2 is wall (90 degrees). 4 lower bits hold the + texture index. + */ + uint8_t mapTriangleData[LCR_SETTING_MAX_MAP_TRIANGLES]; + // pixel function precomputed values: int previousTriangleID; int triangleUVs[6]; @@ -45,16 +52,15 @@ void LCR_pixelFunc3D(S3L_PixelInfo *pixel) for (int i = 0; i < 3; ++i) v[i] = LCR_renderer.mapVertices + 3 * t[i]; - uint8_t type = // 0: floor, 1: wall, 2: wall 90 degrees - (v[0][0] == v[1][0] && v[1][0] == v[2][0]) + - 2 * (v[0][2] == v[1][2] && v[1][2] == v[2][2]); + uint8_t type = LCR_renderer.mapTriangleData[pixel->triangleID] >> 4; - if (type == 0) + LCR_loadImage(LCR_renderer.mapTriangleData[pixel->triangleID] & 0x0f); + + if (type == 0) // floor? { - LCR_loadImage(3); - - if (v[0][1] != v[1][1] || v[1][1] != v[2][1]) + if (v[0][1] != v[1][1] || v[1][1] != v[2][1]) // angled floor? LCR_imageChangeBrightness(1); + for (int i = 0; i < 6; ++i) LCR_renderer.triangleUVs[i] = (( (v[i / 2][(i % 2) * 2]) * @@ -62,8 +68,6 @@ void LCR_pixelFunc3D(S3L_PixelInfo *pixel) } else { - LCR_loadImage(1); - if (type == 1) LCR_imageChangeBrightness(0); @@ -79,6 +83,27 @@ void LCR_pixelFunc3D(S3L_PixelInfo *pixel) } } + // shift the UVs to the origin (prevent high values of UV coords) + for (int i = 0; i < 2; ++i) + { + uint8_t minCoord = LCR_renderer.triangleUVs[i] < + LCR_renderer.triangleUVs[2 + i] ? (0 + i) : (2 + i); + + if (LCR_renderer.triangleUVs[4 + i] < LCR_renderer.triangleUVs[minCoord]) + minCoord = 4 + i; + + S3L_Unit shiftBy = LCR_renderer.triangleUVs[minCoord] % LCR_IMAGE_SIZE; + + if (shiftBy < 0) + shiftBy += LCR_IMAGE_SIZE; + + shiftBy -= LCR_renderer.triangleUVs[minCoord]; + + LCR_renderer.triangleUVs[i] += shiftBy; + LCR_renderer.triangleUVs[2 + i] += shiftBy; + LCR_renderer.triangleUVs[4 + i] += shiftBy; + } + LCR_renderer.previousTriangleID = pixel->triangleIndex; } @@ -119,10 +144,8 @@ S3L_Index _LCR_addMapVertex(S3L_Unit x, S3L_Unit y, S3L_Unit z) // if it doesn't exist, add it if (LCR_renderer.mapModel->vertexCount < LCR_SETTING_MAX_MAP_VERTICES) { - *vertices = x; - vertices++; - *vertices = y; - vertices++; + *vertices = x; vertices++; + *vertices = y; vertices++; *vertices = z; LCR_renderer.mapModel->vertexCount++; return LCR_renderer.mapModel->vertexCount - 1; @@ -132,30 +155,42 @@ S3L_Index _LCR_addMapVertex(S3L_Unit x, S3L_Unit y, S3L_Unit z) return 0; } -void _LCR_addMapTriangle(S3L_Index a, S3L_Index b, S3L_Index c) +void _LCR_addMapTriangle(S3L_Index a, S3L_Index b, S3L_Index c, uint8_t mat) { if (LCR_renderer.mapModel->triangleCount < LCR_SETTING_MAX_MAP_TRIANGLES) { - S3L_Index *t = &(LCR_renderer.mapTriangles[LCR_renderer.mapModel->triangleCount * 3]); + S3L_Index *t = + &(LCR_renderer.mapTriangles[LCR_renderer.mapModel->triangleCount * 3]); *t = a; t++; *t = b; t++; *t = c; + LCR_renderer.mapTriangleData[LCR_renderer.mapModel->triangleCount] = + mat; + LCR_renderer.mapModel->triangleCount++; } } -void _LCR_rendererSwapTriangles(S3L_Index *t1, S3L_Index *t2) +void _LCR_rendererSwapMapTriangles(unsigned int index1, unsigned int index2) { - S3L_Index tmp; + uint8_t tmpMat; + S3L_Index tmpIndex, + *t1 = LCR_renderer.mapTriangles + index1 * 3, + *t2 = LCR_renderer.mapTriangles + index2 * 3; for (int i = 0; i < 3; ++i) { - tmp = t1[i]; + tmpIndex = t1[i]; t1[i] = t2[i]; - t2[i] = tmp; + t2[i] = tmpIndex; } + + tmpMat = LCR_renderer.mapTriangleData[index1]; + LCR_renderer.mapTriangleData[index1] = + LCR_renderer.mapTriangleData[index2]; + LCR_renderer.mapTriangleData[index2] = tmpMat; } int _LCR_quadCoversTriangle(const S3L_Unit quad[8], const S3L_Unit tri[6]) @@ -288,7 +323,6 @@ uint8_t _LCR_rendererCheckMapTriangleCover(const S3L_Index *t1, t3 += 3; } - } const S3L_Index *tmp = t1; @@ -332,8 +366,9 @@ void _LCR_rendererCullHiddenMapTriangles(void) { if (j < LCR_renderer.mapModel->triangleCount - n) { - _LCR_rendererSwapTriangles(t2,LCR_renderer.mapTriangles + - (LCR_renderer.mapModel->triangleCount - 1 - n) * 3); + + _LCR_rendererSwapMapTriangles(j, + LCR_renderer.mapModel->triangleCount - 1 - n); n++; } @@ -344,8 +379,9 @@ void _LCR_rendererCullHiddenMapTriangles(void) if (t1Covered) { - _LCR_rendererSwapTriangles(t1,LCR_renderer.mapTriangles + - (LCR_renderer.mapModel->triangleCount - 1 - n) * 3); + _LCR_rendererSwapMapTriangles(i, + LCR_renderer.mapModel->triangleCount - 1 - n); + n++; // we stay at this position because we've swapped the triangle here } @@ -403,45 +439,106 @@ uint8_t _LCR_rendererBuildMapModel(void) for (int j = 0; j < LCR_currentMap.blockCount; ++j) { - uint8_t bx, by, bz; - LCR_mapBlockGetCoords(LCR_currentMap.blocks + j * LCR_BLOCK_SIZE, - &bx,&by,&bz); - - LCR_mapGetBlockShape(LCR_currentMap.blocks[j * LCR_BLOCK_SIZE], - LCR_mapBlockGetTransform(LCR_currentMap.blocks + j * LCR_BLOCK_SIZE), - blockShapeBytes,&blockShapeByteCount); - - uint8_t vx, vy, vz, vi = 0, - isAtFloor = by == 0, - isAtCeiling = by == LCR_MAP_SIZE_BLOCKS - 1; - S3L_Index triangleIndices[3]; - S3L_Unit originOffset = -1 * LCR_MAP_SIZE_BLOCKS / 2 * LCR_RENDERER_UNIT; + S3L_Index triangleIndices[3]; + const uint8_t *block = LCR_currentMap.blocks + j * LCR_BLOCK_SIZE; + uint8_t + blockType = block[0], + edgeBits, // bottom, top, left, right, front, bottom + bx, by, bz, // block coords + vx, vy, vz, // vertex coords + vi = 0; // vertex index (0, 1 or 2) + + LCR_mapBlockGetCoords(block,&bx,&by,&bz); + + LCR_mapGetBlockShape(blockType,LCR_mapBlockGetTransform(block), + blockShapeBytes,&blockShapeByteCount); for (int i = 0; i < blockShapeByteCount; ++i) { + if (vi == 0) + edgeBits = (by == 0) | + ((by == LCR_MAP_SIZE_BLOCKS - 1) << 1) | + ((bx == 0) << 2) | + ((bx == LCR_MAP_SIZE_BLOCKS - 1) << 3) | + ((bz == 0) << 4) | + ((bz == LCR_MAP_SIZE_BLOCKS - 1) << 5); + LCR_decodeMapBlockCoords(blockShapeBytes[i],&vx,&vy,&vz); - isAtFloor &= vy == 0; - isAtCeiling &= vy == 12; + edgeBits &= (vy == 0) | ((vy == LCR_BLOCK_SHAPE_COORD_MAX) << 1) | + ((vx == 0) << 2) | ((vx == LCR_BLOCK_SHAPE_COORD_MAX) << 3) | + ((vz == 0) << 4) | ((vz == LCR_BLOCK_SHAPE_COORD_MAX) << 5); triangleIndices[vi] = _LCR_addMapVertex( - originOffset + (((S3L_Unit) bx) * LCR_RENDERER_UNIT) + (LCR_RENDERER_UNIT * ((S3L_Unit) vx)) / 12, - (originOffset + (((S3L_Unit) by) * LCR_RENDERER_UNIT)) / 2 + (LCR_RENDERER_UNIT / 2 * ((S3L_Unit) vy)) / 12, - originOffset + (((S3L_Unit) bz) * LCR_RENDERER_UNIT) + (LCR_RENDERER_UNIT * ((S3L_Unit) vz)) / 12); + originOffset + (((S3L_Unit) bx) * LCR_RENDERER_UNIT) + + (LCR_RENDERER_UNIT * ((S3L_Unit) vx)) / LCR_BLOCK_SHAPE_COORD_MAX, + (originOffset + (((S3L_Unit) by) * LCR_RENDERER_UNIT)) / 2 + + (LCR_RENDERER_UNIT / 2 * ((S3L_Unit) vy)) / LCR_BLOCK_SHAPE_COORD_MAX, + originOffset + (((S3L_Unit) bz) * LCR_RENDERER_UNIT) + + (LCR_RENDERER_UNIT * ((S3L_Unit) vz)) / LCR_BLOCK_SHAPE_COORD_MAX); if (vi < 2) vi++; else { // don't add triangles completely at the floor or ceiling of the map - if (!isAtFloor && !isAtCeiling) - _LCR_addMapTriangle( - triangleIndices[0],triangleIndices[1],triangleIndices[2]); + if (!edgeBits) + { +#define VERT(n,c) LCR_renderer.mapVertices[3 * n + c] + uint8_t blockMat = LCR_mapBlockGetMaterial(block); + uint8_t triangleData = + (((VERT(triangleIndices[0],0) == VERT(triangleIndices[1],0)) && + (VERT(triangleIndices[1],0) == VERT(triangleIndices[2],0))) << 4) | + (((VERT(triangleIndices[0],2) == VERT(triangleIndices[1],2)) && + (VERT(triangleIndices[1],2) == VERT(triangleIndices[2],2))) << 5); +#undef VERT + if (triangleData & 0xf0) // wall? + { + triangleData |= + ((blockMat == LCR_BLOCK_MATERIAL_CONCRETE) || + (blockMat == LCR_BLOCK_MATERIAL_ICE) || + (blockType == LCR_BLOCK_FULL_ACCEL) || + (blockType == LCR_BLOCK_FULL_STICKER)) ? + LCR_IMAGE_WALL_CONCRETE : LCR_IMAGE_WALL_WOOD; + } + else + { // TODO: tidy this mess? + if (blockType == LCR_BLOCK_FULL_ACCEL) + triangleData |= LCR_IMAGE_GROUND_ACCEL; + else if (blockType == LCR_BLOCK_FULL_STICKER) + triangleData |= LCR_IMAGE_GROUND_STICKER; + else + switch (blockMat) + { + case LCR_BLOCK_MATERIAL_CONCRETE: + triangleData |= LCR_IMAGE_GROUND_CONCRETE; + break; + + case LCR_BLOCK_MATERIAL_GRASS: + triangleData |= LCR_IMAGE_GROUND_GRASS; + break; + + case LCR_BLOCK_MATERIAL_DIRT: + triangleData |= LCR_IMAGE_GROUND_DIRT; + break; + + case LCR_BLOCK_MATERIAL_ICE: + triangleData |= LCR_IMAGE_GROUND_ICE; + break; + + default: + break; + } + } + + _LCR_addMapTriangle( + triangleIndices[0],triangleIndices[1],triangleIndices[2], + triangleData); + } + vi = 0; - isAtFloor = by == 0; - isAtCeiling = by == LCR_MAP_SIZE_BLOCKS - 1; } } }