Licar/map.h
2024-12-02 23:05:41 +01:00

885 lines
26 KiB
C

#ifndef _LCR_MAP
#define _LCR_MAP
#include <stdint.h>
#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 TEXT format serves for editing maps in human readable format, it more or
less corresponds to the binary storage format (below) with some exceptions.
It has the following structure:
- Magic number string: "LM;".
- Until next ';': name.
- Until next ';': description.
- Until next ';': tags. Currently this may contain the following:
- digit N: set the map environment to N.
- Then a series of block strings follows. Blocks may be separated by
characters that aren't '#' (comments may be added this way). Block format
is following:
#BXYZMT
where:
- B is block type
- X, Y and Z are block coordinates, each one a single character. The
following are characters signifying numbers 0 to 63:
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$@
- M is block material ('0' to '3').
- T is an optinal transform string (for more detail see the binary format)
consisting from 0 to 3 characters, which may be:
- '|': flip horizontally
- '-': flip vertically
- 'L': rotate 90 degrees
- 'I': rotate 180 degrees
- 'J': rotate 270 degrees
Here the block order matters, latter blocks rewrite previously placed ones.
The internal BINARY map format is made from the text string. It can't contain
special blocks and blocks are ordered by their coordinate number (for fast
lookup). The format consists of blocks, each of format:
- 1 byte type: says the type of block, is the same value as B in the text
format.
- 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
*/
#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_SEPARATOR ';'
#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
// normal blocks:
#define LCR_BLOCK_FULL '=' ///< completely filled block
#define LCR_BLOCK_BOTTOM '-' ///< filled bottom half
#define LCR_BLOCK_LEFT ';' ///< filled left half
#define LCR_BLOCK_BOTTOM_LEFT ',' ///< filled bottom left quarter
#define LCR_BLOCK_BOTTOM_LEFT_FRONT '.' ///< filled bottom left front eigth
#define LCR_BLOCK_RAMP '^' ///< plain ramp
#define LCR_BLOCK_RAMP_34 '/' ///< plain ramp, 3/4 size
#define LCR_BLOCK_RAMP_12 '<' ///< plain ramp, 1/2 size
#define LCR_BLOCK_RAMP_14 '_' ///< plain ramp, 1/4 size
#define LCR_BLOCK_RAMP_CURVED_PLAT ']' ///< curved ramp with top platgform
#define LCR_BLOCK_RAMP_CURVED ')' ///< curv. ramp without top platf.
#define LCR_BLOCK_RAMP_CURVED_WALL '}' ///< curved ramp plus small wall
#define LCR_BLOCK_RAMP_STEEP '|' ///< extremely steep ramp
#define LCR_BLOCK_CORNER 'A' ///< diagonal corner
#define LCR_BLOCK_CORNER_12 '\\' ///< diagonal corner (1/2 wide)
#define LCR_BLOCK_HILL '(' ///< curved "hill"
#define LCR_BLOCK_FULL_ACCEL '>'
#define LCR_BLOCK_FULL_FAN 'o'
#define LCR_BLOCK_BUMP '~' ///< small bump on the road
#define LCR_BLOCK_CHECKPOINT_0 '+' ///< checkpoint, not taken
#define LCR_BLOCK_CHECKPOINT_1 '\'' ///< checkpoint, taken
#define LCR_BLOCK_FINISH '!' ///< finish
// special blocks:
#define LCR_BLOCK_NONE 'x' ///< no block, e.g to make holes
#define LCR_BLOCK_CUBOID_FILL 'f' /**< makes a cuboid from the
previously specified block, the
size is given by block coords */
#define LCR_BLOCK_CUBOID_HOLLOW 'h' /**< same as cuboid special block,
but makes a hollow one */
#define LCR_BLOCK_START '*' ///< specifies start block position
/*
TODO:
- ramp corner???
- curved corner?
- curved out corner?
- curved "hill"
- bumpy road
- bigger structures like a loop, sloped road etc?
*/
#define LCR_MAP_BLOCK_CACHE_SIZE (8 * 2) /// do not change
/**
Cache for accelerating LCR_mapGetBlockAtFast, consists of 8 2-item records,
the first record item stores block coord number, the second one stores the
value returned by LCR_mapGetBlockAtFast (-1 is 0xffffffff). The record index
depends on the block coords: lowest bit is x % 2, middle bit y % 2, highest
one z % 2.
*/
uint32_t _LCR_mapBlockCache[LCR_MAP_BLOCK_CACHE_SIZE];
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;
uint8_t checkpointCount;
// TODO: name, desc? possibly as a single '\n' separated string?
} LCR_currentMap;
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 pointer to the block 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)
{
LCR_LOG0("couldn't add block");
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++;
uint8_t *result = LCR_currentMap.blocks + insertAt * LCR_BLOCK_SIZE;
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 result;
}
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 result;
}
/**
Resets the map to start a run (including e.g. unmarking checkpoints etc.).
*/
void LCR_mapReset(void)
{
LCR_LOG0("resetting map");
for (int i = 0; i < LCR_currentMap.blockCount; ++i)
if (LCR_currentMap.blocks[i * LCR_BLOCK_SIZE] == LCR_BLOCK_CHECKPOINT_1)
LCR_currentMap.blocks[i * LCR_BLOCK_SIZE] = LCR_BLOCK_CHECKPOINT_0;
}
int _LCR_mapCharToCoord(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'z')
return c - 'a' + 10;
if (c >= 'A' && c <= 'Z')
return c - 'A' + 36;
if (c == '@')
return 62;
if (c == '&')
return 63;
return -1;
}
uint8_t LCR_mapLoadFromStr(const char *mapStr)
{
LCR_LOG0("loading map string");
const uint8_t *prevBlock = 0;
for (int i = 0; i < 4; ++i)
LCR_currentMap.startPos[i] = 0;
LCR_currentMap.checkpointCount = 0;
LCR_currentMap.blockCount = 0;
LCR_currentMap.environment = 0;
if (mapStr[0] != LCR_MAP_MAGIC_NUMBER1 || mapStr[1] != LCR_MAP_MAGIC_NUMBER2
|| mapStr[2] != LCR_MAP_SEPARATOR)
{
LCR_LOG0("bad magic number");
return 0;
}
mapStr += 3;
while (*mapStr != LCR_MAP_SEPARATOR) // read map name
{
if (mapStr[0] == 0)
return 0;
// TODO
mapStr++;
}
mapStr++;
while (*mapStr != LCR_MAP_SEPARATOR) // read map description
{
if (mapStr[0] == 0)
return 0;
// TODO
mapStr++;
}
mapStr++;
while (*mapStr != LCR_MAP_SEPARATOR) // read map tags
{
if (mapStr[0] >= '0' && mapStr[0] <= '9')
LCR_currentMap.environment = mapStr[0] - '0';
else if (mapStr[0] == 0)
return 0;
// TODO
mapStr++;
}
mapStr++;
while (*mapStr)
{
if (*mapStr == '#')
{
mapStr++;
uint8_t block = *mapStr;
uint8_t trans = 0;
uint8_t mat = 0;
int coords[3];
for (int i = 0; i < 3; ++i)
{
mapStr++;
coords[i] = _LCR_mapCharToCoord(*mapStr);
if (coords[i] < 0)
{
LCR_LOG0("bad coord");
return 0;
}
}
mapStr++;
if (*mapStr >= '0' && *mapStr <= '3')
mat = *mapStr - '0';
else
{
LCR_LOG0("bad material");
return 0;
}
mapStr++;
while (1)
{
if (*mapStr == '|')
trans |= LCR_BLOCK_TRANSFORM_FLIP_H;
else if (*mapStr == '-')
trans |= LCR_BLOCK_TRANSFORM_FLIP_V;
else if (*mapStr == 'L')
trans |= LCR_BLOCK_TRANSFORM_ROT_90;
else if (*mapStr == 'I')
trans |= LCR_BLOCK_TRANSFORM_ROT_180;
else if (*mapStr == 'J')
trans |= LCR_BLOCK_TRANSFORM_ROT_270;
else
break;
mapStr++;
}
switch (block)
{
case LCR_BLOCK_CUBOID_FILL:
case LCR_BLOCK_CUBOID_HOLLOW:
{
uint8_t x, y, z, mat, transform;
uint8_t tmpBlock[LCR_BLOCK_SIZE];
if (prevBlock == 0 || LCR_currentMap.blockCount == 0)
{
LCR_LOG0("no previous block");
return 0;
}
mat = LCR_mapBlockGetMaterial(prevBlock);
transform = LCR_mapBlockGetTransform(prevBlock);
LCR_mapBlockGetCoords(prevBlock,&x,&y,&z);
for (uint8_t k = 0; k < coords[2]; ++k)
for (uint8_t j = 0; j < coords[1]; ++j)
for (uint8_t i = 0; i < coords[0]; ++i)
if ((block == LCR_BLOCK_CUBOID_FILL ||
k == 0 || k == coords[2] - 1 ||
j == 0 || j == coords[1] - 1 ||
i == 0 || i == coords[0] - 1) &&
(x + i < LCR_MAP_SIZE_BLOCKS &&
y + j < LCR_MAP_SIZE_BLOCKS &&
z + k < LCR_MAP_SIZE_BLOCKS))
{
LCR_makeMapBlock(prevBlock[0],x + i,y + j,z + k,mat,transform,
tmpBlock);
prevBlock = _LCR_mapAddBlock(tmpBlock);
if (!prevBlock)
return 0;
}
break;
}
case LCR_BLOCK_START:
LCR_currentMap.startPos[0] = coords[0];
LCR_currentMap.startPos[1] = coords[1];
LCR_currentMap.startPos[2] = coords[2];
LCR_currentMap.startPos[3] = trans & 0x60;
break;
case LCR_BLOCK_CHECKPOINT_0:
LCR_currentMap.checkpointCount++;
// fall through
default: // normal block
{
uint8_t tmpBlock[LCR_BLOCK_SIZE];
LCR_makeMapBlock(block,coords[0],coords[1],coords[2],mat,trans,
tmpBlock);
prevBlock = _LCR_mapAddBlock(tmpBlock);
if (prevBlock == 0)
return 0;
break;
}
// TODO: check for invalid blocks?
}
}
if (*mapStr != '#')
mapStr++;
}
LCR_LOG2("clearing map block cache")
for (int i = 0; i < LCR_MAP_BLOCK_CACHE_SIZE; ++i)
_LCR_mapBlockCache[i] = 0xffffffff;
LCR_LOG2("map loaded")
LCR_mapReset();
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);
uint8_t cacheIndex = 2 * ((x % 2) | ((y % 2) << 1) | ((z % 2) << 2));
if (_LCR_mapBlockCache[cacheIndex] == n)
return
(_LCR_mapBlockCache[cacheIndex + 1] != 0xffffffff) ?
((int) _LCR_mapBlockCache[cacheIndex + 1]) : -1;
_LCR_mapBlockCache[cacheIndex] = n;
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
{
_LCR_mapBlockCache[cacheIndex + 1] = m;
return m;
}
}
_LCR_mapBlockCache[cacheIndex + 1] = 0xffffffff;
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_CHECKPOINT_0:
case LCR_BLOCK_CHECKPOINT_1:
case LCR_BLOCK_FINISH:
ADD(3,0,3) ADD(0,2,6) ADD(6,2,6)
ADD(3,0,3) ADD(0,2,0) ADD(0,2,6)
ADD(3,0,3) ADD(6,2,0) ADD(0,2,0)
ADD(3,0,3) ADD(6,2,6) ADD(6,2,0)
ADD(3,4,3) ADD(0,2,6) ADD(0,2,0)
ADD(3,4,3) ADD(0,2,0) ADD(6,2,0)
ADD(3,4,3) ADD(6,2,0) ADD(6,2,6)
ADD(3,4,3) ADD(6,2,6) ADD(0,2,6)
break;
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_PLAT:
ADD(0,0,6) ADD(0,4,5) ADD(0,4,6) // left
ADD(6,0,6) ADD(6,4,6) ADD(6,4,5) // right
ADD(0,4,5) ADD(6,4,5) ADD(0,4,6) // top
ADD(0,4,6) ADD(6,4,5) ADD(6,4,6)
// fall through
case LCR_BLOCK_RAMP_CURVED:
{
uint8_t plusZ = blockType == LCR_BLOCK_RAMP_CURVED;
ADD(0,0,0) ADD(6,0,0) ADD(0,1,3 + plusZ) // ramp
ADD(0,1,3 + plusZ) ADD(6,0,0) ADD(6,1,3 + plusZ)
ADD(0,1,3 + plusZ) ADD(6,1,3 + plusZ) ADD(0,2,4 + plusZ) // ramp
ADD(0,2,4 + plusZ) ADD(6,1,3 + plusZ) ADD(6,2,4 + plusZ)
ADD(0,2,4 + plusZ) ADD(6,2,4 + plusZ) ADD(0,4,5 + plusZ) // ramp
ADD(0,4,5 + plusZ) ADD(6,2,4 + plusZ) ADD(6,4,5 + plusZ)
ADD(0,0,0) ADD(0,1,3 + plusZ) ADD(0,0,6) // left
ADD(0,0,6) ADD(0,1,3 + plusZ) ADD(0,2,4 + plusZ)
ADD(0,0,6) ADD(0,2,4 + plusZ) ADD(0,4,5 + plusZ)
ADD(6,0,0) ADD(6,0,6) ADD(6,1,3 + plusZ) // right
ADD(6,0,6) ADD(6,2,4 + plusZ) ADD(6,1,3 + plusZ)
ADD(6,0,6) ADD(6,4,5 + plusZ) ADD(6,2,4 + plusZ)
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;
}
case LCR_BLOCK_BUMP:
ADD(3,0,0) ADD(6,0,3) ADD(3,1,3) // top
ADD(6,0,3) ADD(3,0,6) ADD(3,1,3)
ADD(3,0,6) ADD(0,0,3) ADD(3,1,3)
ADD(0,0,3) ADD(3,0,0) ADD(3,1,3)
ADD(3,0,0) ADD(3,0,6) ADD(6,0,3) // bottom
ADD(3,0,6) ADD(3,0,0) ADD(0,0,3) // bottom
break;
case LCR_BLOCK_HILL:
ADD(0,0,0) ADD(6,0,0) ADD(0,2,1) // front
ADD(6,0,0) ADD(6,2,1) ADD(0,2,1)
ADD(0,2,1) ADD(6,2,1) ADD(0,3,2) // front 2
ADD(6,2,1) ADD(6,3,2) ADD(0,3,2)
ADD(0,3,2) ADD(6,3,2) ADD(0,4,4) // front 3
ADD(6,3,2) ADD(6,4,4) ADD(0,4,4)
ADD(0,4,4) ADD(6,4,4) ADD(0,4,6) // top
ADD(6,4,4) ADD(6,4,6) ADD(0,4,6)
ADD(0,0,0) ADD(0,0,6) ADD(6,0,0) // bottom
ADD(6,0,0) ADD(0,0,6) ADD(6,0,6)
ADD(0,0,6) ADD(0,4,6) ADD(6,4,6) // back
ADD(0,0,6) ADD(6,4,6) ADD(6,0,6)
ADD(0,0,0) ADD(0,2,1) ADD(0,0,6) // left
ADD(0,2,1) ADD(0,3,2) ADD(0,0,6)
ADD(0,3,2) ADD(0,4,4) ADD(0,0,6)
ADD(0,4,4) ADD(0,4,6) ADD(0,0,6)
ADD(6,0,0) ADD(6,0,6) ADD(6,2,1) // right
ADD(6,2,1) ADD(6,0,6) ADD(6,3,2)
ADD(6,3,2) ADD(6,0,6) ADD(6,4,4)
ADD(6,4,4) ADD(6,0,6) ADD(6,4,6)
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