#ifndef _LCR_MAP #define _LCR_MAP #include #include "constants.h" #include "settings.h" /** The map (track) module for Licar. Map coordinates/size: - map size is 64x64x64 blocks - [0,0,0] is is bottom-left-front-most - x goes right, y goes up, z goes forward - coordinate number is a single number obtained as x + 64 * y + 64 * 64 * z The STORAGE map format is binary and consists of the following values: - 76, 77 (for "LM", magic number) - one byte recording the map environment - ASCII map name - 10 (separator) - ASCII comment - 10 (separator) - block values, each one in the format: - 1 byte type: says the type of block. If the highest bit is 0, the block is normal, otherwise it is a special block (will be preprocessed) - 3 bytes: A, B, C, such that: - A, B and lowest 2 bits of C form the block coordinate number (A being the lowest part etc.) - bits C2 and C3 say the block material - highest 4 bits of C (C4, C5, C6, C7) say the block's transform: - first if C4 is set, the block is flipped in the X direction - then the block is rotated around vertical axis by 0, 90, 180 or 270 degrees if C5C6 is 00, 01, 10 or 11. - last if C7 is set, the block is flipped vertically - 255 (terminator) In this format order of blocks matters, latter blocks will replace previous blocks placed on the same coordinate. Internally the map will be preprocessed to RAM when loaded so that thing like the magic number and special blocks are removed and the remaining blocks will be sorted for fast block searching. The PREPROCESSED map format is similar, but only consists of block values but there are only normal blocks (no special blocks) and they are sorted by their coordinate number. */ #define LCR_BLOCK_TRANSFORM_FLIP_H 0x10 #define LCR_BLOCK_TRANSFORM_ROT_90 0x20 #define LCR_BLOCK_TRANSFORM_ROT_180 0x40 #define LCR_BLOCK_TRANSFORM_ROT_270 0x60 #define LCR_BLOCK_TRANSFORM_FLIP_V 0x80 #define LCR_BLOCK_XYZ_TO_COORD(x,y,z) // ??? #define LCR_MAP_MAGIC_NUMBER1 'L' #define LCR_MAP_MAGIC_NUMBER2 'M' #define LCR_MAP_MAGIC_NUMBER LCR_MAP_MAGIC_NUMBER1, LCR_MAP_MAGIC_NUMBER2 #define LCR_MAP_TERMINATOR 0xff #define LCR_MAP_BLOCK(t,x,y,z,m,r) t,(uint8_t) (x | (y << 6)), \ (uint8_t) ((y >> 2) | (z << 4)), \ (uint8_t) ((z >> 4) | (m << 2) | (r)) #define LCR_BLOCK_SIZE 4 ///< size of map block, in bytes #define LCR_BLOCK_MATERIAL_CONCRETE 0x00 #define LCR_BLOCK_MATERIAL_GRASS 0x01 #define LCR_BLOCK_MATERIAL_DIRT 0x02 #define LCR_BLOCK_MATERIAL_ICE 0x03 #define LCR_MAP_COUNT 1 // normal blocks: #define LCR_BLOCK_FULL 0x00 ///< completely filled block #define LCR_BLOCK_BOTTOM 0x01 ///< filled bottom half #define LCR_BLOCK_LEFT 0x02 ///< filled left half #define LCR_BLOCK_BOTTOM_LEFT 0x03 ///< filled bottom left quarter #define LCR_BLOCK_BOTTOM_LEFT_FRONT 0x04 ///< filled bottom left front eigth #define LCR_BLOCK_RAMP 0x05 ///< plain ramp #define LCR_BLOCK_RAMP_34 0x06 ///< plain ramp, 3/4 size #define LCR_BLOCK_RAMP_12 0x07 ///< plain ramp, 1/2 size #define LCR_BLOCK_RAMP_14 0x08 ///< plain ramp, 1/4 size #define LCR_BLOCK_RAMP_CURVED_PLAT 0x09 ///< curved ramp with top platgform #define LCR_BLOCK_RAMP_CURVED 0x0a ///< curv. ramp without top platf. #define LCR_BLOCK_RAMP_CURVED_WALL 0x0b ///< curved ramp plus small wall #define LCR_BLOCK_RAMP_STEEP 0x0c ///< extremely steep ramp #define LCR_BLOCK_CORNER 0x0d ///< diagonal corner #define LCR_BLOCK_CORNER_12 0x0e ///< diagonal corner (1/2 wide) /* TODO: - ramp corner??? - curved corner? - curved out corner? - curved "hill" - bumpy road */ #define LCR_BLOCK_FULL_ACCEL 0x20 #define LCR_BLOCK_FULL_FAN 0x30 #define LCR_BLOCK_CHECKPOINT_0 0x40 ///< checkpoint, not taken #define LCR_BLOCK_CHECKPOINT_1 0x41 ///< checkpoint, taken #define LCR_BLOCK_FINISH 0x42 ///< finish // special blocks: #define LCR_BLOCK_NONE 0x80 ///< no block, e.g to make holes #define LCR_BLOCK_CUBOID_FILL 0x81 /**< makes a cuboid from the previously specified block, the size is given by block coords */ #define LCR_BLOCK_CUBOID_HOLLOW 0x82 /**< same as cuboid special block, but makes a hollow one */ #define LCR_BLOCK_START 0x83 ///< specifies start block position struct { uint16_t blockCount; uint8_t blocks[LCR_SETTING_MAP_MAX_BLOCKS * LCR_BLOCK_SIZE]; uint8_t startPos[4]; ///< Initial position and rotation. uint8_t environment; // TODO: name, desc? possibly as a single '\n' separated string? } LCR_currentMap; static const uint8_t LCR_map0[] = { LCR_MAP_MAGIC_NUMBER, 77, 48, 10, // map name: M0 10, // map comment: LCR_MAP_BLOCK( LCR_BLOCK_NONE, 3, 0, 0, LCR_BLOCK_MATERIAL_CONCRETE, 0), LCR_MAP_BLOCK( LCR_BLOCK_FULL, 3, 0, 0, LCR_BLOCK_MATERIAL_CONCRETE, 0), LCR_MAP_BLOCK( LCR_BLOCK_FULL, 0, 1, 0, LCR_BLOCK_MATERIAL_CONCRETE, 0), LCR_MAP_BLOCK( LCR_BLOCK_FULL, 2, 0, 0, LCR_BLOCK_MATERIAL_CONCRETE, 0), LCR_MAP_BLOCK( LCR_BLOCK_FULL, 0, 1, 0, LCR_BLOCK_MATERIAL_CONCRETE, 0), LCR_MAP_BLOCK( LCR_BLOCK_FULL, 3, 0, 0, LCR_BLOCK_MATERIAL_CONCRETE, 0), LCR_MAP_TERMINATOR }; static const uint8_t *LCR_maps[LCR_MAP_COUNT] = { LCR_map0 }; void LCR_makeMapBlock(uint8_t type, uint8_t x, uint8_t y, uint8_t z, uint8_t material, uint8_t transform, uint8_t block[LCR_BLOCK_SIZE]) { block[0] = type; block[1] = x | (y << 6); block[2] = (y >> 2) | (z << 4); block[3] = (z >> 4) | (material << 2) | transform; } void LCR_mapBlockGetCoords(const uint8_t block[LCR_BLOCK_SIZE], uint8_t *x, uint8_t *y, uint8_t *z) { *x = block[1] & 0x3f; *y = (block[1] >> 6) | ((block[2] & 0x0f) << 2); *z = (block[2] >> 4) | ((block[3] & 0x03) << 4); } uint8_t LCR_mapBlockOppositeTransform(uint8_t transform) { if (!(transform & LCR_BLOCK_TRANSFORM_FLIP_H)) { if ((transform & 0x60) == LCR_BLOCK_TRANSFORM_ROT_90) return ((transform & (~0x60)) | LCR_BLOCK_TRANSFORM_ROT_270); if ((transform & 0x60) == LCR_BLOCK_TRANSFORM_ROT_270) return ((transform & (~0x60)) | LCR_BLOCK_TRANSFORM_ROT_90); } return transform; } 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) | ((((uint32_t) block[3]) & 0x3) << 16); } uint32_t LCR_mapBlockCoordsToCoordNumber(uint8_t x, uint8_t y, uint8_t z) { uint8_t b[LCR_BLOCK_SIZE]; LCR_makeMapBlock(0,x,y,z,0,0,b); return LCR_mapBlockGetCoordNumber(b); } /** Gets dimensions of a non-curved ramp. */ void LCR_rampGetDimensions(uint8_t rampType, uint8_t *height4ths, uint8_t *length6ths) { *height4ths = (rampType == LCR_BLOCK_RAMP_14) + (rampType == LCR_BLOCK_RAMP || rampType == LCR_BLOCK_RAMP_STEEP) * 4 + (rampType == LCR_BLOCK_RAMP_12 || rampType == LCR_BLOCK_RAMP_34) * 2 + (rampType == LCR_BLOCK_RAMP_34); *length6ths = rampType != LCR_BLOCK_RAMP_STEEP ? 6 : 1; } uint8_t *LCR_getMapBlockAtCoordNumber(uint32_t coord) { // binary search the block: uint16_t a = 0, b = LCR_currentMap.blockCount - 1; while (b >= a) { uint16_t mid = (a + b) / 2; uint8_t *block = LCR_currentMap.blocks + mid * LCR_BLOCK_SIZE; uint32_t coord2 = LCR_mapBlockGetCoordNumber(block); if (coord2 == coord) return block; else if (coord2 > coord) b = mid - 1; else a = mid + 1; } return 0; } /** Adds given block to current map, including possibly deleting a block by adding LCR_BLOCK_NONE. The function handles sorting the block to the right position. Returns 1 on success, else 0. */ uint8_t _LCR_mapAddBlock(const uint8_t block[LCR_BLOCK_SIZE]) { LCR_LOG2("adding map block"); if (LCR_currentMap.blockCount >= LCR_SETTING_MAP_MAX_BLOCKS) return 0; uint32_t coord = LCR_mapBlockGetCoordNumber(block); uint16_t insertAt = 0; while (insertAt < LCR_currentMap.blockCount && coord > LCR_mapBlockGetCoordNumber(LCR_currentMap.blocks + insertAt * LCR_BLOCK_SIZE)) insertAt++; if (block[0] == LCR_BLOCK_NONE) { if (insertAt < LCR_currentMap.blockCount && coord == LCR_mapBlockGetCoordNumber(LCR_currentMap.blocks + insertAt * LCR_BLOCK_SIZE)) { // shift all left (remove the block): for (uint16_t i = insertAt * LCR_BLOCK_SIZE; i < LCR_currentMap.blockCount * LCR_BLOCK_SIZE - 1; ++i) LCR_currentMap.blocks[i] = LCR_currentMap.blocks[i + 1]; LCR_currentMap.blockCount--; } return 1; } if (insertAt == LCR_currentMap.blockCount || coord != LCR_mapBlockGetCoordNumber(LCR_currentMap.blocks + insertAt * LCR_BLOCK_SIZE)) { // shift from here to the right, make room for the new block LCR_currentMap.blockCount++; for (int16_t i = ((int16_t) LCR_currentMap.blockCount) - 1; i > insertAt; i--) for (uint8_t j = 0; j < LCR_BLOCK_SIZE; ++j) LCR_currentMap.blocks[i * LCR_BLOCK_SIZE + j] = LCR_currentMap.blocks[(i - 1) * LCR_BLOCK_SIZE + j]; } insertAt *= LCR_BLOCK_SIZE; for (uint8_t j = 0; j < LCR_BLOCK_SIZE; ++j) LCR_currentMap.blocks[insertAt + j] = block[j]; return 1; } /** Loads and preprocesses given map. Returns 1 on success, otherwise 0. */ uint8_t LCR_mapLoad(const uint8_t *map) { LCR_LOG0("loading map") for (int i = 0; i < 4; ++i) LCR_currentMap.startPos[i] = 0; LCR_currentMap.blockCount = 0; if (map[0] != LCR_MAP_MAGIC_NUMBER1 || map[1] != LCR_MAP_MAGIC_NUMBER2) return 1; map += 2; while (*map != 10) // read map name { // TODO map++; } map++; while (*map != 10) // read map description { // TODO map++; } map++; LCR_currentMap.environment = *map; map++; while (*map != LCR_MAP_TERMINATOR) { switch (*map) { case LCR_BLOCK_CUBOID_FILL: case LCR_BLOCK_CUBOID_HOLLOW: { const uint8_t *prevBlock = map - LCR_BLOCK_SIZE; uint8_t x, y, z, w, h, d, mat, transform; uint8_t tmpBlock[LCR_BLOCK_SIZE]; if (LCR_currentMap.blockCount == 0 || (prevBlock[0] & 0x80)) return 0; mat = LCR_mapBlockGetMaterial(prevBlock); transform = LCR_mapBlockGetTransform(prevBlock); LCR_mapBlockGetCoords(prevBlock,&x,&y,&z); LCR_mapBlockGetCoords(map,&w,&h,&d); for (uint8_t k = 0; k < d; ++k) for (uint8_t j = 0; j < h; ++j) for (uint8_t i = 0; i < w; ++i) if (*map == LCR_BLOCK_CUBOID_FILL || k == 0 || k == d - 1 || j == 0 || j == h - 1 || i == 0 || i == w - 1) { LCR_makeMapBlock(prevBlock[0],x + i,y + j,z + k,mat,transform, tmpBlock); if (!_LCR_mapAddBlock(tmpBlock)) return 0; } break; } case LCR_BLOCK_START: LCR_mapBlockGetCoords(map, LCR_currentMap.startPos, LCR_currentMap.startPos + 1, LCR_currentMap.startPos + 2); LCR_currentMap.startPos[3] = LCR_mapBlockGetTransform(map) & 0x60; break; default: if (!_LCR_mapAddBlock(map)) // normal block return 0; break; } map += LCR_BLOCK_SIZE; } LCR_LOG2("map loaded") return 1; } /** Same as LCR_mapGetBlockAt, but allows to specify start and end block of the of the search to make it faster. */ int LCR_mapGetBlockAtFast(uint8_t x, uint8_t y, uint8_t z, int start, int end) { // binary search (the blocks are sorted) uint32_t n = LCR_mapBlockCoordsToCoordNumber(x,y,z); while (start <= end) { int m = (start + end) / 2; uint32_t n2 = LCR_mapBlockGetCoordNumber( LCR_currentMap.blocks + m * LCR_BLOCK_SIZE); if (n2 < n) start = m + 1; else if (n2 > n) end = m - 1; else return m; } return -1; } /** Gets an index to a map block of the currently loaded map at given coordinates. If there is no block at given coordinates, -1 is returned. */ int LCR_mapGetBlockAt(uint8_t x, uint8_t y, uint8_t z) { if (LCR_currentMap.blockCount == 0) return -1; return LCR_mapGetBlockAtFast(x,y,z,0,LCR_currentMap.blockCount - 1); } uint8_t _LCR_encodeMapBlockCoords(uint8_t x, uint8_t y, uint8_t z) { return (5 * 7) * z + 7 * y + x; } void _LCR_decodeMapBlockCoords(uint8_t byte, uint8_t *x, uint8_t *y, uint8_t *z) { *x = (byte % 7); *y = ((byte / 7) % 5); *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 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) { _LCR_decodeMapBlockCoords(byte,x,y,z); *x *= 2; *y *= 3; *z *= 2; } 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] = _LCR_encodeMapBlockCoords(x,y,z); *byteCount += 1; } /** Macro that transforms coordinates according to block transformation. */ #define LCR_TRANSFORM_COORDS(trans,cx,cy,cz,maxXZ,maxY)\ if (trans & LCR_BLOCK_TRANSFORM_FLIP_H) cx = maxXZ - cx;\ if (trans & 0x20) { /* for both 90 and 270 */ \ cx ^= cz; cz ^= cx; cx ^= cz; /* swap */ \ cx = maxXZ - cx; } \ if (trans & 0x40) { /* for both 180 and 270 */ \ cx = maxXZ - cx; \ cz = maxXZ - cz; } \ if (trans & LCR_BLOCK_TRANSFORM_FLIP_V) \ cy = maxY - cy; /** Gets a shape of given map block type as a 3D model composed of triangles. The 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, 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. Helper side view grid: 4 . . . . . . . ^ 3 . . . . . . . | y 2 . . . . . . . 1 . . . . . . . 0 . . . . . . . 0 1 2 3 4 5 6 -> x/z */ *byteCount = 0; #define ADD(a,b,c) _LCR_addBlockShapeByte(bytes,byteCount,a,b,c); switch (blockType) { case LCR_BLOCK_FULL: case LCR_BLOCK_BOTTOM: case LCR_BLOCK_LEFT: case LCR_BLOCK_BOTTOM_LEFT: case LCR_BLOCK_BOTTOM_LEFT_FRONT: case LCR_BLOCK_FULL_ACCEL: case LCR_BLOCK_FULL_FAN: { uint8_t xRight = 6, yTop = 4, zBack = 6 >> (blockType == LCR_BLOCK_BOTTOM_LEFT_FRONT); if (blockType == LCR_BLOCK_BOTTOM || blockType == LCR_BLOCK_BOTTOM_LEFT || blockType == LCR_BLOCK_BOTTOM_LEFT_FRONT) yTop /= 2; if (blockType == LCR_BLOCK_LEFT || blockType == LCR_BLOCK_BOTTOM_LEFT || blockType == LCR_BLOCK_BOTTOM_LEFT_FRONT) xRight /= 2; ADD(0,0,0) ADD(xRight,0,0) ADD(xRight,yTop,0) // front ADD(0,0,0) ADD(xRight,yTop,0) ADD(0,yTop,0) ADD(xRight,0,0) ADD(xRight,0,zBack) ADD(xRight,yTop,zBack) // right ADD(xRight,0,0) ADD(xRight,yTop,zBack) ADD(xRight,yTop,0) ADD(0,0,0) ADD(0,yTop,0) ADD(0,yTop,zBack) // left ADD(0,0,0) ADD(0,yTop,zBack) ADD(0,0,zBack) ADD(0,0,zBack) ADD(0,yTop,zBack) ADD(xRight,yTop,zBack) // back ADD(0,0,zBack) ADD(xRight,yTop,zBack) ADD(xRight,0,zBack) ADD(0,yTop,0) ADD(xRight,yTop,0) ADD(xRight,yTop,zBack) // top ADD(0,yTop,0) ADD(xRight,yTop,zBack) ADD(0,yTop,zBack) ADD(0,0,0) ADD(xRight,0,zBack) ADD(xRight,0,0) // bottom ADD(0,0,0) ADD(0,0,zBack) ADD(xRight,0,zBack) break; } case LCR_BLOCK_RAMP_CURVED: ADD(0,0,0) ADD(6,0,0) ADD(0,1,4) // ramp ADD(0,1,4) ADD(6,0,0) ADD(6,1,4) ADD(0,1,4) ADD(6,1,4) ADD(0,2,5) // ramp ADD(0,2,5) ADD(6,1,4) ADD(6,2,5) ADD(0,2,5) ADD(6,2,5) ADD(0,4,6) // ramp ADD(0,4,6) ADD(6,2,5) ADD(6,4,6) ADD(0,0,0) ADD(0,1,4) ADD(0,0,6) // left ADD(0,0,6) ADD(0,1,4) ADD(0,2,5) ADD(0,0,6) ADD(0,2,5) ADD(0,4,6) ADD(6,0,0) ADD(6,0,6) ADD(6,1,4) // right ADD(6,0,6) ADD(6,2,5) ADD(6,1,4) ADD(6,0,6) ADD(6,4,6) ADD(6,2,5) ADD(0,0,6) ADD(0,4,6) ADD(6,0,6) // back ADD(6,0,6) ADD(0,4,6) ADD(6,4,6) ADD(0,0,0) ADD(6,0,6) ADD(6,0,0) // bottom ADD(0,0,0) ADD(0,0,6) ADD(6,0,6) break; case LCR_BLOCK_RAMP_CURVED_WALL: ADD(0,0,0) ADD(5,0,0) ADD(0,1,3) // ramp ADD(0,1,3) ADD(5,0,0) ADD(5,1,3) ADD(0,1,3) ADD(5,1,3) ADD(0,2,4) // ramp ADD(0,2,4) ADD(5,1,3) ADD(5,2,4) ADD(0,2,4) ADD(5,2,4) ADD(0,4,5) // ramp ADD(0,4,5) ADD(5,2,4) ADD(5,4,5) ADD(0,4,5) ADD(5,4,5) ADD(0,4,6) // top ADD(0,4,6) ADD(5,4,5) ADD(6,4,6) ADD(5,4,5) ADD(6,4,0) ADD(6,4,6) // top ADD(5,4,5) ADD(5,4,0) ADD(6,4,0) ADD(5,4,0) ADD(5,4,5) ADD(5,2,4) // inner side ADD(5,4,0) ADD(5,2,4) ADD(5,1,3) ADD(5,4,0) ADD(5,1,3) ADD(5,0,0) ADD(5,4,0) ADD(5,0,0) ADD(6,4,0) // front ADD(6,4,0) ADD(5,0,0) ADD(6,0,0) ADD(6,4,0) ADD(6,0,0) ADD(6,4,6) // right ADD(6,4,6) ADD(6,0,0) ADD(6,0,6) ADD(0,0,6) ADD(0,4,5) ADD(0,4,6) // left ADD(0,0,6) ADD(0,2,4) ADD(0,4,5) ADD(0,0,6) ADD(0,1,3) ADD(0,2,4) ADD(0,0,6) ADD(0,0,0) ADD(0,1,3) ADD(0,0,6) ADD(0,4,6) ADD(6,0,6) // back ADD(6,0,6) ADD(0,4,6) ADD(6,4,6) ADD(0,0,0) ADD(6,0,6) ADD(6,0,0) // bottom ADD(0,0,0) ADD(0,0,6) ADD(6,0,6) break; case LCR_BLOCK_RAMP: case LCR_BLOCK_RAMP_12: case LCR_BLOCK_RAMP_14: case LCR_BLOCK_RAMP_34: case LCR_BLOCK_RAMP_STEEP: { uint8_t front, top; LCR_rampGetDimensions(blockType,&top,&front); front = 6 - front; ADD(0,0,front) ADD(0,top,6) ADD(0,0,6) // side ADD(6,0,front) ADD(6,0,6) ADD(6,top,6) // side ADD(0,0,front) ADD(6,0,front) ADD(0,top,6) // top ADD(6,0,front) ADD(6,top,6) ADD(0,top,6) // top ADD(0,0,6) ADD(6,top,6) ADD(6,0,6) // back ADD(0,0,6) ADD(0,top,6) ADD(6,top,6) // back ADD(0,0,front) ADD(0,0,6) ADD(6,0,6) // bottom ADD(0,0,front) ADD(6,0,6) ADD(6,0,front) // bottom break; } case LCR_BLOCK_CORNER: case LCR_BLOCK_CORNER_12: { uint8_t right = blockType == LCR_BLOCK_CORNER ? 6 : 3; ADD(0,0,0) ADD(right,0,6) ADD(right,4,6) // front/right ADD(0,0,0) ADD(right,4,6) ADD(0,4,0) // front/right ADD(0,0,0) ADD(0,4,6) ADD(0,0,6) // left ADD(0,0,0) ADD(0,4,0) ADD(0,4,6) // left ADD(right,0,6) ADD(0,0,6) ADD(0,4,6) // back ADD(0,4,6) ADD(right,4,6) ADD(right,0,6) // back ADD(0,4,0) ADD(right,4,6) ADD(0,4,6) // top ADD(0,0,6) ADD(right,0,6) ADD(0,0,0) // bottom break; } default: break; } if (transform) { for (int i = 0; i < *byteCount; ++i) { uint8_t x, y, z, tmp; _LCR_decodeMapBlockCoords(bytes[i],&x,&y,&z); LCR_TRANSFORM_COORDS(transform,x,y,z,6,4) bytes[i] = _LCR_encodeMapBlockCoords(x,y,z); } if (((transform & LCR_BLOCK_TRANSFORM_FLIP_H) == 0) != ((transform & LCR_BLOCK_TRANSFORM_FLIP_V) == 0)) // flip triangles for (int i = 0; i < *byteCount; i += 3) { uint8_t tmp = bytes[i]; bytes[i] = bytes[i + 1]; bytes[i + 1] = tmp; } } #undef ADD } #endif // guard