diff --git a/TODO.txt b/TODO.txt index 4007b64..f65fc13 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,3 +1,10 @@ +- sky images could be just composed of 4x4 normal images? then we only need + one type of image +- map actually in ASCII format? how will humans edit it? +- make a simple rendering setting: + - will exclude images and only draw solid colors, let's say only 16, so that + memory usage is reduced, CPU rendering is relieved, executable is smaller + AND frontend doesn't have to use RGB565 (it can basically choose any mode). - background sky rendering efficiently: - first draw the solid color top and bottom - then render the 3D scene @@ -42,7 +49,7 @@ TOTAL SIZE OF TEXTURES: - possibility of simple procedural textures to save space! <-- SOUNDS NICE - possibility to turn off textures completely <-- MUST HAVE - how to map textures to blocks? - - figured out nice procedural mapping in Blender :) + - figured out nice procedural mapping in Blender :) <-- DONE - PROBABLY LIKE THIS: - background textures: 128x128 with image-specific 256 color palette, pixel access shouldn't be as time critical here (128x128/256 looks better @@ -54,6 +61,20 @@ TOTAL SIZE OF TEXTURES: CAUSE BACKGROUND WON'T BE A 3D MODEL - Track model has to be preprocessed, unseen walls must be removed else would be too slow. <-- OF COURSE + - Removing unseen triangles: + - make a routine that does the cleaning, call it several times during the + model construction (waiting till the very end could run out of memory + with too many vertices/triangles, also processing so many of them could + even make it slower), it will work like this: + - go through all triangles, mark each covered or not (we can't remove + them right away, they need to stay to see if they cover other tris) + - for each triangle go through all again, mark the checked tri covered if: + - the other tri has the same vertices (trivial case), OR + - non-trivial case (consider e.g. two covering quads but split + differently) not so easy, maybe good enough: if the tris share two + vertices AND they're in the same plane (EZ to check for axis aligned) + AND the other tri has a neighbor tri that has the non-shared vertex? + - remove all marked tris AND vertices that become unused - ====== Track/map format: list of map blocks. ======= - one block record: - type: 5 bits? diff --git a/assets.h b/assets.h index a002215..6771d80 100644 --- a/assets.h +++ b/assets.h @@ -2,6 +2,20 @@ #define _LCR_ASSETS_H #include +#include "map.h" + +// TODO: images should be 64x64 indexed, sky will be composed of 2x2 normal images + +static const uint8_t map1[] = + { + LCR_MAP_MAGIC_NUMBER, + 0, + 10, + 10, + 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_CONCRETE,0), + LCR_MAP_TERMINATOR + }; #define LCR_SKY_IMAGE_SIZE 128 diff --git a/constants.h b/constants.h index d8a50c4..d16ef5e 100644 --- a/constants.h +++ b/constants.h @@ -16,6 +16,6 @@ (LCR_SETTING_FREE_CAMERA_TURN_SPEED / LCR_SETTING_FPS) /** Maximum number of triangles of a block shape. */ -#define LCR_MAP_BLOCK_SHAPE_MAX_TRIANGLES 32 +#define LCR_MAP_BLOCK_SHAPE_MAX_BYTES 64 #endif diff --git a/frontend_sdl.c b/frontend_sdl.c index 7c76e73..947ba2e 100644 --- a/frontend_sdl.c +++ b/frontend_sdl.c @@ -45,9 +45,9 @@ void LCR_sleep(uint16_t timeMs) usleep(timeMs * 1000); } -void LCR_drawPixel(unsigned int x, unsigned int y, uint_fast16_t color) +void LCR_drawPixel(unsigned long index, uint16_t color) { - screen[y * LCR_SETTING_RESOLUTION_X + x] = color; + screen[index] = color; } int main(int argc, char *argv[]) diff --git a/game.h b/game.h index e96682b..1ac3d74 100644 --- a/game.h +++ b/game.h @@ -44,7 +44,7 @@ void LCR_sleep(uint16_t timeMs); is handled by the game internals and out-of-screen pixels will never be drawn. The color value depends on game settings but is normally an RGB565 value. */ -void LCR_drawPixel(unsigned int x, unsigned int y, uint_fast16_t color); +void LCR_drawPixel(unsigned long index, uint16_t color); /** Call this function in your frontend at the start of the program. @@ -69,52 +69,47 @@ uint8_t LCR_gameStep(uint32_t timeMs); uint32_t LCR_nextRenderFrameTime; -void LCR_drawPixelUnsafe(unsigned int x, unsigned int y, - uint_fast16_t color); +void LCR_drawPixelXYUnsafe(unsigned int x, unsigned int y, + uint16_t color); /** Internal pixel drawing function that checks for out-of-screen coordinates. Use this if the pixel can potentially lie of screen (however if you know it won't, use the normal unsafe function in sake of performance). */ -static inline void LCR_drawPixelSafe(unsigned int x, unsigned int y, +static inline void LCR_drawPixelXYSafe(unsigned int x, unsigned int y, uint_fast16_t color); #include "racing.h" #include "assets.h" #include "renderer.h" -uint8_t _LCR_keyStates[LCR_KEYS_TOTAL]; /**< Assures unchanging key states +uint8_t LCR_keyStates[LCR_KEYS_TOTAL]; /**< Assures unchanging key states during a single frame. */ -void LCR_drawPixelUnsafe(unsigned int x, unsigned int y, - uint_fast16_t color) +void LCR_drawPixelXYUnsafe(unsigned int x, unsigned int y, + uint16_t color) { #if LCR_SETTING_RESOLUTION_SUBDIVIDE == 1 - LCR_drawPixel(x,y,color); + LCR_drawPixel(y * LCR_SETTING_RESOLUTION_X + x,color); #else - x *= LCR_SETTING_RESOLUTION_SUBDIVIDE; - y *= LCR_SETTING_RESOLUTION_SUBDIVIDE; - - unsigned int x2 = x + LCR_SETTING_RESOLUTION_SUBDIVIDE; - - for (int yy = y; yy < y + LCR_SETTING_RESOLUTION_SUBDIVIDE; ++yy) - for (int xx = x; xx < x2; ++xx) - LCR_drawPixel(xx,yy,color); + // TODO #endif } -static inline void LCR_drawPixelSafe(unsigned int x, unsigned int y, +static inline void LCR_drawPixelXYSafe(unsigned int x, unsigned int y, uint_fast16_t color) { if (x < LCR_EFFECTIVE_RESOLUTION_X && y < LCR_EFFECTIVE_RESOLUTION_Y) - LCR_drawPixelUnsafe(x,y,color); + LCR_drawPixelXYUnsafe(x,y,color); } void LCR_gameInit(void) { for (int i = 0; i < LCR_KEYS_TOTAL; ++i) - _LCR_keyStates[i] = 0; + LCR_keyStates[i] = 0; + + LCR_mapLoad(map1); LCR_rendererInit(); } @@ -126,7 +121,7 @@ void LCR_gameEnd(void) uint8_t LCR_gameStep(uint32_t time) { for (int i = 0; i < LCR_KEYS_TOTAL; ++i) - _LCR_keyStates[i] = LCR_keyPressed(i); + LCR_keyStates[i] = LCR_keyPressed(i); uint32_t sleep = 0; @@ -152,28 +147,28 @@ uint8_t LCR_gameStep(uint32_t time) for (int i = 0; i < 5; ++i) offsets[i] = 0; - if (_LCR_keyStates[LCR_KEY_A]) + if (LCR_keyStates[LCR_KEY_A]) { - if (_LCR_keyStates[LCR_KEY_UP]) + if (LCR_keyStates[LCR_KEY_UP]) offsets[4] = LCR_FREE_CAMERA_TURN_STEP; - else if (_LCR_keyStates[LCR_KEY_DOWN]) + else if (LCR_keyStates[LCR_KEY_DOWN]) offsets[4] -= LCR_FREE_CAMERA_TURN_STEP; - if (_LCR_keyStates[LCR_KEY_RIGHT]) + if (LCR_keyStates[LCR_KEY_RIGHT]) offsets[3] -= LCR_FREE_CAMERA_TURN_STEP; - else if (_LCR_keyStates[LCR_KEY_LEFT]) + else if (LCR_keyStates[LCR_KEY_LEFT]) offsets[3] = LCR_FREE_CAMERA_TURN_STEP; } else { - if (_LCR_keyStates[LCR_KEY_UP]) + if (LCR_keyStates[LCR_KEY_UP]) offsets[0] = LCR_FREE_CAMERA_STEP; - else if (_LCR_keyStates[LCR_KEY_DOWN]) + else if (LCR_keyStates[LCR_KEY_DOWN]) offsets[0] -= LCR_FREE_CAMERA_STEP; - if (_LCR_keyStates[LCR_KEY_RIGHT]) + if (LCR_keyStates[LCR_KEY_RIGHT]) offsets[1] = LCR_FREE_CAMERA_STEP; - else if (_LCR_keyStates[LCR_KEY_LEFT]) + else if (LCR_keyStates[LCR_KEY_LEFT]) offsets[1] -= LCR_FREE_CAMERA_STEP; } diff --git a/map.h b/map.h index 581f249..340d19c 100644 --- a/map.h +++ b/map.h @@ -76,8 +76,8 @@ #define LCR_BLOCK_LEFT_FRONT 0x03 ///< filled left front quarter of block #define LCR_BLOCK_BOTTOM_LEFT 0x04 ///< filled bottom left quarter of block -#define LCR_BLOCK_CHECKPOINT_NOT 0x10 ///< checkpoint, not taken -#define LCR_BLOCK_CHECKPOINT_YES 0x11 ///< checkpoint, taken +#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: @@ -274,18 +274,57 @@ const uint8_t *LCR_mapGetBlockAt(uint8_t x, uint8_t y, uint8_t z) return 0; } +void _LCR_addBlockShapeByte(uint8_t *bytes, uint8_t *byteCount, + int x, int y, int z) +{ + if (*byteCount >= LCR_MAP_BLOCK_SHAPE_MAX_BYTES) + return; + + bytes[*byteCount] = (5 * 7) * z + 7 * y + x; + *byteCount += 1; +} + /** Gets a shape of given map block type as a 3D model composed of triangles. The - model is returned as an array of 16 bit ints, each of which represents - X with lowest 5 bits, Y with next 5 bits and Z with next 5 bits (the - directions of axes are same as map X, Y, Z directions). Each coordinate can - go from 0 to 16; WATCH OUT, THIS IS 17 VALUES, NOT 16, so as to allow having - a mid coord (8), mid-mid coords (4, 12) and mid-mid-mid coords. + model is returned as an array of byte triplets (triangles), with each byte + representing one coordinate. These coordinates can be decoded with + LCR_decodeMapBlockCoords function. */ void LCR_mapGetBlockShape(uint8_t blockType, uint8_t transform, - uint16_t triangles[LCR_MAP_BLOCK_SHAPE_MAX_TRIANGLES * 3], - uint8_t *triangleCount) + uint8_t bytes[LCR_MAP_BLOCK_SHAPE_MAX_BYTES], uint8_t *byteCount) { + /* + The coordinate format is following: byte B specifies coordinates X (0 to 6) + = B % 7, Y (vertical, 0 to 4) = (B / 7) % 5, Z (0 to 6) = B % 35. + */ + + *byteCount = 0; + + switch (blockType) + { + case LCR_BLOCK_FULL: + default: + _LCR_addBlockShapeByte(bytes,byteCount,0,0,1); + _LCR_addBlockShapeByte(bytes,byteCount,5,0,1); + _LCR_addBlockShapeByte(bytes,byteCount,0,1,3); + + + + + break; + } +} + +/** + 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 + is intentional as it for example has an exact mid value. +*/ +void LCR_decodeMapBlockCoords(uint8_t byte, uint8_t *x, uint8_t *y, uint8_t *z) +{ + *x = (byte % 7) * 2; + *y = ((byte / 7) % 5) * 3; + *z = (byte % 35) * 2; } #endif // guard diff --git a/renderer.h b/renderer.h index b2ee426..8d4f7dc 100644 --- a/renderer.h +++ b/renderer.h @@ -23,45 +23,116 @@ struct LCR_Renderer S3L_Scene LCR_scene3D; S3L_Model3D LCR_models3D[3]; // TODO -S3L_Unit LCR_vertices3D[LCR_SETTING_MAX_VERTICES * 3]; -S3L_Index LCR_triangles3D[LCR_SETTING_MAX_TRIANGLES * 3]; +S3L_Model3D *LCR_mapModel; +S3L_Unit LCR_mapVertices[LCR_SETTING_MAX_MAP_VERTICES * 3]; +S3L_Index LCR_mapTriangles[LCR_SETTING_MAX_MAP_TRIANGLES * 3]; void LCR_pixelFunc3D(S3L_PixelInfo *pixel) { - LCR_drawPixelSafe(pixel->x,pixel->y,0xff00); + LCR_drawPixelXYSafe(pixel->x,pixel->y,0xff00); +} + +S3L_Index _LCR_addMapVertex(S3L_Unit x, S3L_Unit y, S3L_Unit z) +{ + S3L_Index index = 0; + S3L_Unit *vertices = LCR_mapVertices; + + while (index < LCR_mapModel->vertexCount) // if exists, return vertex index + { + if (vertices[0] == x && vertices[1] == y && vertices[2] == z) + return index; + + vertices += 3; + index++; + } + + // if it doesn't exist, add it + + if (LCR_mapModel->vertexCount < LCR_SETTING_MAX_MAP_VERTICES) + { + *vertices = x; + vertices++; + *vertices = y; + vertices++; + *vertices = z; + LCR_mapModel->vertexCount++; + return LCR_mapModel->vertexCount - 1; + } + + // TODO: error print + return 0; +} + +void _LCR_addMapTriangle(S3L_Index a, S3L_Index b, S3L_Index c) +{ + if (LCR_mapModel->triangleCount < LCR_SETTING_MAX_MAP_TRIANGLES) + { + S3L_Index *t = &(LCR_mapModel->triangles[LCR_mapModel->triangleCount * 3]); + + *t = a; + t++; + *t = b; + t++; + *t = c; + + LCR_mapModel->triangleCount++; + } } /** Builds an internal 3D model of the currently loaded map. Returns 1 on success, otherwise 0 (e.g. not enough space). */ -uint8_t LCR_buildMapModel(void) +uint8_t _LCR_rendererBuildMapModel(void) { - // TODO + uint8_t blockShapeBytes[LCR_MAP_BLOCK_SHAPE_MAX_BYTES]; + uint8_t blockShapeByteCount; - LCR_vertices3D[0] = -2000; - LCR_vertices3D[1] = -100; - LCR_vertices3D[2] = 1000; + S3L_model3DInit(LCR_mapVertices,0,LCR_mapTriangles,0,LCR_mapModel); - LCR_vertices3D[3] = 400; - LCR_vertices3D[4] = -100; - LCR_vertices3D[5] = 2000; + LCR_mapGetBlockShape(0,0,blockShapeBytes,&blockShapeByteCount); - LCR_vertices3D[6] = 0; - LCR_vertices3D[7] = 400; - LCR_vertices3D[8] = 2000; +uint8_t vx, vy, vz, vi = 0; +uint8_t *bytes = blockShapeBytes; +S3L_Index triangleIndices[3]; - LCR_triangles3D[0] = 0; - LCR_triangles3D[1] = 1; - LCR_triangles3D[2] = 2; +for (int i = 0; i < blockShapeByteCount; ++i) +{ + LCR_decodeMapBlockCoords(blockShapeBytes[i],&vx,&vy,&vz); + +printf("%d: %d %d %d\n",blockShapeBytes[i],vx,vy,vz); + + triangleIndices[vi] = _LCR_addMapVertex( + (LCR_SQUARE_SIDE_LEN * ((LCR_SpaceUnit) vx)) / 12, + (LCR_SQUARE_SIDE_LEN / 2 * ((LCR_SpaceUnit) vy)) / 12, + (LCR_SQUARE_SIDE_LEN * ((LCR_SpaceUnit) vz)) / 12); + + if (vi < 2) + vi++; + else + { + _LCR_addMapTriangle( + triangleIndices[0],triangleIndices[1],triangleIndices[2]); + vi = 0; + } +} + +/* +a = _LCR_addMapVertex(-2000,-100,1000); +b = _LCR_addMapVertex(400,-100,2000); +c = _LCR_addMapVertex(0,400,2000); + +_LCR_addMapTriangle(a,b,c); +*/ return 1; } uint8_t LCR_rendererInit(void) { - if (!LCR_buildMapModel()) + LCR_mapModel = LCR_models3D; + + if (!_LCR_rendererBuildMapModel()) return 0; - S3L_model3DInit(LCR_vertices3D,3,LCR_triangles3D,1,LCR_models3D); S3L_sceneInit(LCR_models3D,1,&LCR_scene3D); return 1; @@ -108,6 +179,14 @@ void LCR_rendererDraw(void) void LCR_drawBackground(int verticalOffset) { + +// TODO + +for (int y = 0; y < LCR_EFFECTIVE_RESOLUTION_Y; ++y) + for (int x = 0; x < LCR_EFFECTIVE_RESOLUTION_X; ++x) + LCR_drawPixelXYUnsafe(x,y,0xffff); + +/* uint16_t color = LCR_skyImages[LCR_skyImages[256] & 0x00ff] ; // TODO int limit = verticalOffset - LCR_SETTING_SKY_SIZE * LCR_SKY_IMAGE_SIZE / 2; @@ -128,11 +207,13 @@ void LCR_drawBackground(int verticalOffset) for (int y = LCR_EFFECTIVE_RESOLUTION_Y - 1; y >= limit; --y) for (int x = 0; x < LCR_EFFECTIVE_RESOLUTION_X; ++x) LCR_drawPixelUnsafe(x,y,color); +*/ } void LCR_drawSkyStrip(int verticalOffset, uint8_t horizontalOffset) { #if LCR_SETTING_SKY_SIZE != 0 +/* verticalOffset -= (LCR_SETTING_SKY_SIZE * LCR_SKY_IMAGE_SIZE) / 2; int finalY = verticalOffset + LCR_SETTING_SKY_SIZE * LCR_SKY_IMAGE_SIZE; @@ -173,7 +254,7 @@ void LCR_drawSkyStrip(int verticalOffset, uint8_t horizontalOffset) if (verticalOffset % LCR_SETTING_SKY_SIZE == 0) skyLine += LCR_SKY_IMAGE_SIZE / 2; } - +*/ #endif } diff --git a/settings.h b/settings.h index 26cfc8e..7bd5f6d 100644 --- a/settings.h +++ b/settings.h @@ -35,15 +35,15 @@ #define LCR_SETTING_SKY_ROLL_MULTIPLIER_H 4 #endif -#ifndef LCR_SETTING_MAX_VERTICES +#ifndef LCR_SETTING_MAX_MAP_VERTICES /** Maximum number of vertices for 3D rendering. Lower number will decrease RAM usage but will prevent larger maps from being loaded. */ - #define LCR_SETTING_MAX_VERTICES 10000 + #define LCR_SETTING_MAX_MAP_VERTICES 10000 #endif -#ifndef LCR_SETTING_MAX_TRIANGLES - /** Like LCR_SETTING_MAX_VERTICES but for the number of triangles. */ - #define LCR_SETTING_MAX_TRIANGLES 10000 +#ifndef LCR_SETTING_MAX_MAP_TRIANGLES + /** Like LCR_SETTING_MAX_MAP_VERTICES but for the number of triangles. */ + #define LCR_SETTING_MAX_MAP_TRIANGLES 10000 #endif #ifndef LCR_SETTING_SKY_SIZE