From 6ef8ac28b384ee04168159ddcf4562a202f11c78 Mon Sep 17 00:00:00 2001 From: XProger Date: Tue, 2 Mar 2021 22:35:06 +0300 Subject: [PATCH] GBA animation, states & collision from fixed 30 fps branch, sound mixer, rotate90 mode (120x160), asm scanline rasterizer, move from libgba to libtonc --- src/platform/gba/Makefile | 5 +- src/platform/gba/OpenLara.vcxproj | 9 + src/platform/gba/camera.h | 179 ++- src/platform/gba/collision.h | 334 +++++ src/platform/gba/common.cpp | 565 +++++++- src/platform/gba/common.h | 722 ++++++++-- src/platform/gba/deploy.sh | 2 +- src/platform/gba/draw.h | 474 +++++++ src/platform/gba/enemy.h | 19 + src/platform/gba/game.h | 160 +++ src/platform/gba/item.h | 384 ++++++ src/platform/gba/lara.h | 2044 +++++++++++++++++++++++++++++ src/platform/gba/level.h | 856 +++++------- src/platform/gba/main.cpp | 388 +++--- src/platform/gba/rasterizer.s | 396 ++++++ src/platform/gba/sound.h | 118 ++ 16 files changed, 5767 insertions(+), 888 deletions(-) create mode 100644 src/platform/gba/collision.h create mode 100644 src/platform/gba/draw.h create mode 100644 src/platform/gba/enemy.h create mode 100644 src/platform/gba/game.h create mode 100644 src/platform/gba/item.h create mode 100644 src/platform/gba/lara.h create mode 100644 src/platform/gba/rasterizer.s create mode 100644 src/platform/gba/sound.h diff --git a/src/platform/gba/Makefile b/src/platform/gba/Makefile index 35217d8..d11b487 100644 --- a/src/platform/gba/Makefile +++ b/src/platform/gba/Makefile @@ -26,6 +26,7 @@ SOURCES := . INCLUDES := include DATA := data MUSIC := +LIBTONC := $(DEVKITPRO)/libtonc #--------------------------------------------------------------------------------- # options for code generation @@ -48,14 +49,14 @@ LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project #--------------------------------------------------------------------------------- -LIBS := -lmm -lgba +LIBS := -lmm -ltonc #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing # include and lib #--------------------------------------------------------------------------------- -LIBDIRS := $(LIBGBA) +LIBDIRS := $(LIBGBA) $(LIBTONC) #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional diff --git a/src/platform/gba/OpenLara.vcxproj b/src/platform/gba/OpenLara.vcxproj index 8f5cecc..b6e42bd 100644 --- a/src/platform/gba/OpenLara.vcxproj +++ b/src/platform/gba/OpenLara.vcxproj @@ -25,8 +25,15 @@ + + + + + + + 15.0 @@ -105,6 +112,7 @@ Console true + winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) @@ -139,6 +147,7 @@ true true true + winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) diff --git a/src/platform/gba/camera.h b/src/platform/gba/camera.h index cfbbb32..0255b41 100644 --- a/src/platform/gba/camera.h +++ b/src/platform/gba/camera.h @@ -3,64 +3,163 @@ #include "common.h" -#define CAM_SPEED (1 << 3) -#define CAM_ROT_SPEED (1 << 9) -#define CAM_ROT_X_MAX int16(85 * 0x8000 / 180) +#define CAM_SPEED (1 << 3) +#define CAM_ROT_SPEED (1 << 9) +#define CAM_ROT_X_MAX int16(85 * 0x8000 / 180) +#define CAM_DIST_FOLLOW (1024 + 512) -struct Camera { - vec3i pos; - int16 rotX, rotY; +enum CameraMode +{ + CAMERA_MODE_FREE = 0, + CAMERA_MODE_FOLLOW = 1, + CAMERA_MODE_COMBAT = 2, + CAMERA_MODE_FIXED = 3, +}; + +struct Camera +{ + vec3i viewPos; + vec3i targetPos; + + int16 targetAngleX; + int16 targetAngleY; + int16 targetDist; + + int16 angleX; + int16 angleY; int32 room; - void init() { - pos.x = 75162; - pos.y = 2048; - pos.z = 5000; + Item* item; - rotX = 0; - rotY = 16 << 8; + CameraMode mode; + bool modeSwitch; - //rotX = -0x1000; - //rotY = int16(0x8000); + void init() + { + item = NULL; + mode = CAMERA_MODE_FOLLOW; + modeSwitch = false; + + viewPos.x = 75162; + viewPos.y = 2048; + viewPos.z = 5000; + + targetPos = viewPos; + targetAngleX = 0; + targetAngleY = 0; + targetDist = CAM_DIST_FOLLOW; + + angleX = 0; + angleY = 16 << 8; + + //angleX = -0x1000; + //angleY = int16(0x8000); } - void update() { - if (keys[IK_UP]) rotX -= CAM_ROT_SPEED; - if (keys[IK_DOWN]) rotX += CAM_ROT_SPEED; - if (keys[IK_LEFT]) rotY -= CAM_ROT_SPEED; - if (keys[IK_RIGHT]) rotY += CAM_ROT_SPEED; - - rotX = clamp(rotX, -CAM_ROT_X_MAX, CAM_ROT_X_MAX); - - matrixSetView(pos.x, pos.y, pos.z, rotX, rotY); + void freeControl() + { + matrixSetView(viewPos, angleX, angleY); Matrix &m = matrixGet(); - if (keys[IK_R]) { - pos.x += m[0].x * CAM_SPEED >> 10; - pos.y += m[0].y * CAM_SPEED >> 10; - pos.z += m[0].z * CAM_SPEED >> 10; + if (keys & IK_UP) angleX -= CAM_ROT_SPEED; + if (keys & IK_DOWN) angleX += CAM_ROT_SPEED; + if (keys & IK_LEFT) angleY -= CAM_ROT_SPEED; + if (keys & IK_RIGHT) angleY += CAM_ROT_SPEED; + + angleX = X_CLAMP(angleX, -CAM_ROT_X_MAX, CAM_ROT_X_MAX); + + if (keys & IK_A) + { + viewPos.x += m[2].x * CAM_SPEED >> 10; + viewPos.y += m[2].y * CAM_SPEED >> 10; + viewPos.z += m[2].z * CAM_SPEED >> 10; } - if (keys[IK_L]) { - pos.x -= m[0].x * CAM_SPEED >> 10; - pos.y -= m[0].y * CAM_SPEED >> 10; - pos.z -= m[0].z * CAM_SPEED >> 10; + if (keys & IK_B) + { + viewPos.x -= m[2].x * CAM_SPEED >> 10; + viewPos.y -= m[2].y * CAM_SPEED >> 10; + viewPos.z -= m[2].z * CAM_SPEED >> 10; + } + } + + void updateRoom() + { + room = getRoomIndex(room, viewPos.x, viewPos.y, viewPos.z); + const RoomInfo::Sector* sector = getSector(room, viewPos.x, viewPos.z); + int32 floor = getFloor(sector, viewPos.x, viewPos.y, viewPos.z); + int32 ceiling = getCeiling(sector, viewPos.x, viewPos.y, viewPos.z) - 256; + + if (floor != WALL) { + viewPos.y = X_MIN(viewPos.y, floor - 256); } - if (keys[IK_A]) { - pos.x += m[2].x * CAM_SPEED >> 10; - pos.y += m[2].y * CAM_SPEED >> 10; - pos.z += m[2].z * CAM_SPEED >> 10; + if (ceiling != WALL) + { + viewPos.y = X_MAX(viewPos.y, ceiling + 256); + } + } + + void update() + { + if (keys & IK_START) + { + if (!modeSwitch) + { + modeSwitch = true; + if (mode != CAMERA_MODE_FREE) { + mode = CAMERA_MODE_FREE; + } else { + mode = CAMERA_MODE_FOLLOW; + } + } + } else { + modeSwitch = false; } - if (keys[IK_B]) { - pos.x -= m[2].x * CAM_SPEED >> 10; - pos.y -= m[2].y * CAM_SPEED >> 10; - pos.z -= m[2].z * CAM_SPEED >> 10; + if (mode == CAMERA_MODE_FREE) + { + freeControl(); } - room = getRoomIndex(room, &pos); + if (mode == CAMERA_MODE_FOLLOW && item) { + int32 tx = item->pos.x; + int32 ty = item->pos.y; + int32 tz = item->pos.z; + + const Box &box = getBoundingBox(item); + ty += box.maxY + ((box.minY - box.maxY) * 3 >> 2); + + targetPos.x = tx; + targetPos.y += (ty - targetPos.y) >> 2; + targetPos.z = tz; + + int16 angle = item->angleY + targetAngleY; + + int32 dy = targetDist * phd_sin(targetAngleX) >> FIXED_SHIFT; + int32 dz = targetDist * phd_cos(targetAngleX) >> FIXED_SHIFT; + + int32 cx = targetPos.x - (phd_sin(angle) * dz >> FIXED_SHIFT); + int32 cy = targetPos.y - 256 + dy; + int32 cz = targetPos.z - (phd_cos(angle) * dz >> FIXED_SHIFT); + + viewPos.x += (cx - viewPos.x) >> 2; + viewPos.y += (cy - viewPos.y) >> 2; + viewPos.z += (cz - viewPos.z) >> 2; + + updateRoom(); + + anglesFromVector(targetPos.x - viewPos.x, targetPos.y - viewPos.y, targetPos.z - viewPos.z, angleX, angleY); + } + + matrixSetView(viewPos, angleX, angleY); + + updateRoom(); + + targetAngleX = 0; + targetAngleY = 0; + targetDist = CAM_DIST_FOLLOW; } }; diff --git a/src/platform/gba/collision.h b/src/platform/gba/collision.h new file mode 100644 index 0000000..1413c54 --- /dev/null +++ b/src/platform/gba/collision.h @@ -0,0 +1,334 @@ +#ifndef H_COLLISION +#define H_COLLISION + +#include "common.h" + +enum CollisionType { + CT_NONE = 0, + CT_FRONT = (1 << 0), + CT_LEFT = (1 << 1), + CT_RIGHT = (1 << 2), + CT_CEILING = (1 << 3), + CT_FRONT_CEILING = (1 << 4), + CT_FLOOR_CEILING = (1 << 5), +}; + +struct CollisionInfo { + + enum SideType { + ST_MIDDLE, + ST_FRONT, + ST_LEFT, + ST_RIGHT, + ST_MAX + }; + + struct Side { + int32 floor; + int32 ceiling; + SlantType slantType; + }; + + int16 *trigger; + + Side m; + Side f; + Side l; + Side r; + + int32 radius; + + int32 gapPos; + int32 gapNeg; + int32 gapCeiling; + + vec3i offset; + vec3i pos; + + int16 angle; + int16 quadrant; + + CollisionType type; + + int8 slantX; + int8 slantZ; + + bool enemyPush; + bool enemyHit; + bool staticHit; + bool stopOnSlant; + bool stopOnLava; + + void setSide(SideType st, int32 floor, int32 ceiling) + { + SlantType slantType; + + if (floorSlant.slantX == 0 && floorSlant.slantZ == 0) { + slantType = SLANT_NONE; + } else if (abs(floorSlant.slantX) < 3 && abs(floorSlant.slantZ) < 3) { + slantType = SLANT_LOW; + } else { + slantType = SLANT_HIGH; + } + + if (st != ST_MIDDLE) { + if (stopOnSlant && floor < 0 && slantType == SLANT_HIGH) { + floor -= 0x7FFF; + } else if (stopOnSlant && floor > 0 && slantType == SLANT_HIGH) { + floor = 512; + }/* TODO lava else if (stopOnLava && floor > 0 && trigger && FloorData(*(uint16*)trigger).cmd.func == FloorData::LAVA) { + floor = 512; + }*/ + } + + Side *s = &m + st; + s->slantType = slantType; + s->floor = floor; + s->ceiling = ceiling; + } +}; + +CollisionInfo cinfo; + +int32 alignOffset(int32 a, int32 b) +{ + int32 ca = a / 1024; + int32 cb = b / 1024; + + if (ca == cb) { + return 0; + } + + a &= 1023; + + if (ca < cb) { + return 1025 - a; + } + + return -(a + 1); +} + +bool collideStatic(CollisionInfo &cinfo, const vec3i &p, int32 height) { + /* + cinfo.staticHit = false; + cinfo.offset = vec3i(0); + + BoxV2 objBox(v2pos - vec3i(cinfo.radius, height, cinfo.radius), + v2pos + vec3i(cinfo.radius, 0, cinfo.radius)); + +// TODO: check linked rooms + for (int j = 0; j < level->roomsCount; j++) { + const TR::Room &room = level->rooms[j]; + + for (int i = 0; i < room.meshesCount; i++) { + TR::Room::Mesh &m = room.meshes[i]; + TR::StaticMesh &sm = level->staticMeshes[m.meshIndex]; + + if (sm.flags & 1) continue; + + BoxV2 meshBox = sm.getBox(true); + meshBox.rotate90(m.rotation.value / ANGLE_90); + meshBox.translate(m.pos); + + if (!objBox.intersect(meshBox)) continue; + + cinfo.offset = meshBox.pushOut2D(objBox); + + bool flip = (cinfo.quadrant > 1); + + if (cinfo.quadrant & 1) { + if (abs(cinfo.offset.z) > cinfo.radius) { + cinfo.offset.z = cinfo.pos.z - p.z; + cinfo.type = CT_FRONT; + } else if (cinfo.offset.z != 0) { + cinfo.offset.x = 0; + cinfo.type = ((cinfo.offset.z > 0) ^ flip) ? CT_RIGHT : CT_LEFT; + } else { + cinfo.offset = vec3i(0); + } + } else { + if (abs(cinfo.offset.x) > cinfo.radius) { + cinfo.offset.x = cinfo.pos.x - p.x; + cinfo.type = CT_FRONT; + } else if (cinfo.offset.x != 0) { + cinfo.offset.z = 0; + cinfo.type = ((cinfo.offset.x > 0) ^ flip) ? CT_LEFT : CT_RIGHT; + } else { + cinfo.offset = vec3i(0); + } + } + + cinfo.staticHit = true; + + return true; + } + } + */ + return false; +} + +void collideRoom(Item* item, int32 height, int32 yOffset = 0) +{ + cinfo.type = CT_NONE; + cinfo.offset = vec3i(0, 0, 0); + + vec3i p = item->pos; + p.y += yOffset; + + int32 y = p.y - height; + + int32 cy = y - 160; + + int32 floor, ceiling; + + #define CHECK_HEIGHT(v) {\ + int32 roomIndex = getRoomIndex(item->room, v.x, cy, v.z);\ + const RoomInfo::Sector* sector = getSector(roomIndex, v.x, v.z);\ + floor = getFloor(sector, v.x, cy, v.z);\ + if (floor != WALL) floor -= p.y;\ + ceiling = getCeiling(sector, v.x, cy, v.z);\ + if (ceiling != WALL) ceiling -= y;\ + } + +// middle + CHECK_HEIGHT(p); + + //cinfo.trigger = floorTrigger; TODO + cinfo.slantX = floorSlant.slantX; + cinfo.slantZ = floorSlant.slantZ; + + cinfo.setSide(CollisionInfo::ST_MIDDLE, floor, ceiling); + + vec3i f, l, r; + int32 R = cinfo.radius; + + switch (cinfo.quadrant) { + case 0 : { + f = vec3i((R * phd_sin(cinfo.angle)) >> FIXED_SHIFT, 0, R); + l = vec3i(-R, 0, R); + r = vec3i( R, 0, R); + break; + } + case 1 : { + f = vec3i( R, 0, (R * phd_cos(cinfo.angle)) >> FIXED_SHIFT); + l = vec3i( R, 0, R); + r = vec3i( R, 0, -R); + break; + } + case 2 : { + f = vec3i((R * phd_sin(cinfo.angle)) >> FIXED_SHIFT, 0, -R); + l = vec3i( R, 0, -R); + r = vec3i(-R, 0, -R); + break; + } + case 3 : { + f = vec3i(-R, 0, (R * phd_cos(cinfo.angle)) >> FIXED_SHIFT); + l = vec3i(-R, 0, -R); + r = vec3i(-R, 0, R); + break; + } + default : { + f.x = f.y = f.z = 0; + l.x = l.y = l.z = 0; + r.x = r.y = r.z = 0; + ASSERT(false); + } + } + + f += p; + l += p; + r += p; + + vec3i delta; + delta.x = cinfo.pos.x - p.x; + delta.y = cinfo.pos.y - p.y; + delta.z = cinfo.pos.z - p.z; + +// front + CHECK_HEIGHT(f); + cinfo.setSide(CollisionInfo::ST_FRONT, floor, ceiling); + +// left + CHECK_HEIGHT(l); + cinfo.setSide(CollisionInfo::ST_LEFT, floor, ceiling); + +// right + CHECK_HEIGHT(r); + cinfo.setSide(CollisionInfo::ST_RIGHT, floor, ceiling); + +// static objects + collideStatic(cinfo, p, height); + +// check middle + if (cinfo.m.floor == WALL) + { + cinfo.offset = delta; + cinfo.type = CT_FRONT; + return; + } + + if (cinfo.m.floor <= cinfo.m.ceiling) + { + cinfo.offset = delta; + cinfo.type = CT_FLOOR_CEILING; + return; + } + + if (cinfo.m.ceiling >= 0) + { + cinfo.offset.y = cinfo.m.ceiling; + cinfo.type = CT_CEILING; + } + +// front + if (cinfo.f.floor > cinfo.gapPos || + cinfo.f.floor < cinfo.gapNeg || + cinfo.f.ceiling > cinfo.gapCeiling) + { + if (cinfo.quadrant & 1) + { + cinfo.offset.x = alignOffset(f.x, p.x); + cinfo.offset.z = delta.z; + } else { + cinfo.offset.x = delta.x; + cinfo.offset.z = alignOffset(f.z, p.z); + } + + cinfo.type = CT_FRONT; + return; + } + +// front ceiling + if (cinfo.f.ceiling >= cinfo.gapCeiling) + { + cinfo.offset = delta; + cinfo.type = CT_FRONT_CEILING; + return; + } + +// left + if (cinfo.l.floor > cinfo.gapPos || cinfo.l.floor < cinfo.gapNeg) + { + if (cinfo.quadrant & 1) { + cinfo.offset.z = alignOffset(l.z, f.z); + } else { + cinfo.offset.x = alignOffset(l.x, f.x); + } + cinfo.type = CT_LEFT; + return; + } + +// right + if (cinfo.r.floor > cinfo.gapPos || cinfo.r.floor < cinfo.gapNeg) + { + if (cinfo.quadrant & 1) { + cinfo.offset.z = alignOffset(r.z, f.z); + } else { + cinfo.offset.x = alignOffset(r.x, f.x); + } + cinfo.type = CT_RIGHT; + return; + } +} + +#endif diff --git a/src/platform/gba/common.cpp b/src/platform/gba/common.cpp index 197356a..52f4b0e 100644 --- a/src/platform/gba/common.cpp +++ b/src/platform/gba/common.cpp @@ -1,11 +1,144 @@ #include "common.h" -uint16 divTable[DIV_TABLE_SIZE]; // IWRAM 0.5 kb +Rect clip; vec3i viewPos; Matrix matrixStack[MAX_MATRICES]; int32 matrixStackIndex = 0; -const int16 sin_table[1025] = { // IWRAM 2 kb +uint32 keys; + +const uint16 divTable[DIV_TABLE_SIZE] = { + 0xFFFF, 0xFFFF, 0x8000, 0x5555, 0x4000, 0x3333, 0x2AAA, 0x2492, + 0x2000, 0x1C71, 0x1999, 0x1745, 0x1555, 0x13B1, 0x1249, 0x1111, + 0x1000, 0x0F0F, 0x0E38, 0x0D79, 0x0CCC, 0x0C30, 0x0BA2, 0x0B21, + 0x0AAA, 0x0A3D, 0x09D8, 0x097B, 0x0924, 0x08D3, 0x0888, 0x0842, + 0x0800, 0x07C1, 0x0787, 0x0750, 0x071C, 0x06EB, 0x06BC, 0x0690, + 0x0666, 0x063E, 0x0618, 0x05F4, 0x05D1, 0x05B0, 0x0590, 0x0572, + 0x0555, 0x0539, 0x051E, 0x0505, 0x04EC, 0x04D4, 0x04BD, 0x04A7, + 0x0492, 0x047D, 0x0469, 0x0456, 0x0444, 0x0432, 0x0421, 0x0410, + 0x0400, 0x03F0, 0x03E0, 0x03D2, 0x03C3, 0x03B5, 0x03A8, 0x039B, + 0x038E, 0x0381, 0x0375, 0x0369, 0x035E, 0x0353, 0x0348, 0x033D, + 0x0333, 0x0329, 0x031F, 0x0315, 0x030C, 0x0303, 0x02FA, 0x02F1, + 0x02E8, 0x02E0, 0x02D8, 0x02D0, 0x02C8, 0x02C0, 0x02B9, 0x02B1, + 0x02AA, 0x02A3, 0x029C, 0x0295, 0x028F, 0x0288, 0x0282, 0x027C, + 0x0276, 0x0270, 0x026A, 0x0264, 0x025E, 0x0259, 0x0253, 0x024E, + 0x0249, 0x0243, 0x023E, 0x0239, 0x0234, 0x0230, 0x022B, 0x0226, + 0x0222, 0x021D, 0x0219, 0x0214, 0x0210, 0x020C, 0x0208, 0x0204, + 0x0200, 0x01FC, 0x01F8, 0x01F4, 0x01F0, 0x01EC, 0x01E9, 0x01E5, + 0x01E1, 0x01DE, 0x01DA, 0x01D7, 0x01D4, 0x01D0, 0x01CD, 0x01CA, + 0x01C7, 0x01C3, 0x01C0, 0x01BD, 0x01BA, 0x01B7, 0x01B4, 0x01B2, + 0x01AF, 0x01AC, 0x01A9, 0x01A6, 0x01A4, 0x01A1, 0x019E, 0x019C, + 0x0199, 0x0197, 0x0194, 0x0192, 0x018F, 0x018D, 0x018A, 0x0188, + 0x0186, 0x0183, 0x0181, 0x017F, 0x017D, 0x017A, 0x0178, 0x0176, + 0x0174, 0x0172, 0x0170, 0x016E, 0x016C, 0x016A, 0x0168, 0x0166, + 0x0164, 0x0162, 0x0160, 0x015E, 0x015C, 0x015A, 0x0158, 0x0157, + 0x0155, 0x0153, 0x0151, 0x0150, 0x014E, 0x014C, 0x014A, 0x0149, + 0x0147, 0x0146, 0x0144, 0x0142, 0x0141, 0x013F, 0x013E, 0x013C, + 0x013B, 0x0139, 0x0138, 0x0136, 0x0135, 0x0133, 0x0132, 0x0130, + 0x012F, 0x012E, 0x012C, 0x012B, 0x0129, 0x0128, 0x0127, 0x0125, + 0x0124, 0x0123, 0x0121, 0x0120, 0x011F, 0x011E, 0x011C, 0x011B, + 0x011A, 0x0119, 0x0118, 0x0116, 0x0115, 0x0114, 0x0113, 0x0112, + 0x0111, 0x010F, 0x010E, 0x010D, 0x010C, 0x010B, 0x010A, 0x0109, + 0x0108, 0x0107, 0x0106, 0x0105, 0x0104, 0x0103, 0x0102, 0x0101, + 0x0100, 0x00FF, 0x00FE, 0x00FD, 0x00FC, 0x00FB, 0x00FA, 0x00F9, + 0x00F8, 0x00F7, 0x00F6, 0x00F5, 0x00F4, 0x00F3, 0x00F2, 0x00F1, + 0x00F0, 0x00F0, 0x00EF, 0x00EE, 0x00ED, 0x00EC, 0x00EB, 0x00EA, + 0x00EA, 0x00E9, 0x00E8, 0x00E7, 0x00E6, 0x00E5, 0x00E5, 0x00E4, + 0x00E3, 0x00E2, 0x00E1, 0x00E1, 0x00E0, 0x00DF, 0x00DE, 0x00DE, + 0x00DD, 0x00DC, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D8, + 0x00D7, 0x00D6, 0x00D6, 0x00D5, 0x00D4, 0x00D4, 0x00D3, 0x00D2, + 0x00D2, 0x00D1, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CD, + 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C9, 0x00C8, + 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C4, 0x00C3, + 0x00C3, 0x00C2, 0x00C1, 0x00C1, 0x00C0, 0x00C0, 0x00BF, 0x00BF, + 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BB, 0x00BA, + 0x00BA, 0x00B9, 0x00B9, 0x00B8, 0x00B8, 0x00B7, 0x00B7, 0x00B6, + 0x00B6, 0x00B5, 0x00B5, 0x00B4, 0x00B4, 0x00B3, 0x00B3, 0x00B2, + 0x00B2, 0x00B1, 0x00B1, 0x00B0, 0x00B0, 0x00AF, 0x00AF, 0x00AE, + 0x00AE, 0x00AD, 0x00AD, 0x00AC, 0x00AC, 0x00AC, 0x00AB, 0x00AB, + 0x00AA, 0x00AA, 0x00A9, 0x00A9, 0x00A8, 0x00A8, 0x00A8, 0x00A7, + 0x00A7, 0x00A6, 0x00A6, 0x00A5, 0x00A5, 0x00A5, 0x00A4, 0x00A4, + 0x00A3, 0x00A3, 0x00A3, 0x00A2, 0x00A2, 0x00A1, 0x00A1, 0x00A1, + 0x00A0, 0x00A0, 0x009F, 0x009F, 0x009F, 0x009E, 0x009E, 0x009D, + 0x009D, 0x009D, 0x009C, 0x009C, 0x009C, 0x009B, 0x009B, 0x009A, + 0x009A, 0x009A, 0x0099, 0x0099, 0x0099, 0x0098, 0x0098, 0x0098, + 0x0097, 0x0097, 0x0097, 0x0096, 0x0096, 0x0095, 0x0095, 0x0095, + 0x0094, 0x0094, 0x0094, 0x0093, 0x0093, 0x0093, 0x0092, 0x0092, + 0x0092, 0x0091, 0x0091, 0x0091, 0x0090, 0x0090, 0x0090, 0x0090, + 0x008F, 0x008F, 0x008F, 0x008E, 0x008E, 0x008E, 0x008D, 0x008D, + 0x008D, 0x008C, 0x008C, 0x008C, 0x008C, 0x008B, 0x008B, 0x008B, + 0x008A, 0x008A, 0x008A, 0x0089, 0x0089, 0x0089, 0x0089, 0x0088, + 0x0088, 0x0088, 0x0087, 0x0087, 0x0087, 0x0087, 0x0086, 0x0086, + 0x0086, 0x0086, 0x0085, 0x0085, 0x0085, 0x0084, 0x0084, 0x0084, + 0x0084, 0x0083, 0x0083, 0x0083, 0x0083, 0x0082, 0x0082, 0x0082, + 0x0082, 0x0081, 0x0081, 0x0081, 0x0081, 0x0080, 0x0080, 0x0080, + 0x0080, 0x007F, 0x007F, 0x007F, 0x007F, 0x007E, 0x007E, 0x007E, + 0x007E, 0x007D, 0x007D, 0x007D, 0x007D, 0x007C, 0x007C, 0x007C, + 0x007C, 0x007B, 0x007B, 0x007B, 0x007B, 0x007A, 0x007A, 0x007A, + 0x007A, 0x007A, 0x0079, 0x0079, 0x0079, 0x0079, 0x0078, 0x0078, + 0x0078, 0x0078, 0x0078, 0x0077, 0x0077, 0x0077, 0x0077, 0x0076, + 0x0076, 0x0076, 0x0076, 0x0076, 0x0075, 0x0075, 0x0075, 0x0075, + 0x0075, 0x0074, 0x0074, 0x0074, 0x0074, 0x0073, 0x0073, 0x0073, + 0x0073, 0x0073, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0071, + 0x0071, 0x0071, 0x0071, 0x0071, 0x0070, 0x0070, 0x0070, 0x0070, + 0x0070, 0x0070, 0x006F, 0x006F, 0x006F, 0x006F, 0x006F, 0x006E, + 0x006E, 0x006E, 0x006E, 0x006E, 0x006D, 0x006D, 0x006D, 0x006D, + 0x006D, 0x006D, 0x006C, 0x006C, 0x006C, 0x006C, 0x006C, 0x006B, + 0x006B, 0x006B, 0x006B, 0x006B, 0x006B, 0x006A, 0x006A, 0x006A, + 0x006A, 0x006A, 0x006A, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069, + 0x0069, 0x0068, 0x0068, 0x0068, 0x0068, 0x0068, 0x0068, 0x0067, + 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0066, 0x0066, 0x0066, + 0x0066, 0x0066, 0x0066, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, + 0x0065, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, + 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0062, 0x0062, + 0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0061, 0x0061, 0x0061, + 0x0061, 0x0061, 0x0061, 0x0061, 0x0060, 0x0060, 0x0060, 0x0060, + 0x0060, 0x0060, 0x0060, 0x005F, 0x005F, 0x005F, 0x005F, 0x005F, + 0x005F, 0x005F, 0x005E, 0x005E, 0x005E, 0x005E, 0x005E, 0x005E, + 0x005E, 0x005E, 0x005D, 0x005D, 0x005D, 0x005D, 0x005D, 0x005D, + 0x005D, 0x005C, 0x005C, 0x005C, 0x005C, 0x005C, 0x005C, 0x005C, + 0x005C, 0x005B, 0x005B, 0x005B, 0x005B, 0x005B, 0x005B, 0x005B, + 0x005B, 0x005A, 0x005A, 0x005A, 0x005A, 0x005A, 0x005A, 0x005A, + 0x005A, 0x0059, 0x0059, 0x0059, 0x0059, 0x0059, 0x0059, 0x0059, + 0x0059, 0x0058, 0x0058, 0x0058, 0x0058, 0x0058, 0x0058, 0x0058, + 0x0058, 0x0057, 0x0057, 0x0057, 0x0057, 0x0057, 0x0057, 0x0057, + 0x0057, 0x0057, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, + 0x0056, 0x0056, 0x0056, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055, + 0x0055, 0x0055, 0x0055, 0x0055, 0x0054, 0x0054, 0x0054, 0x0054, + 0x0054, 0x0054, 0x0054, 0x0054, 0x0054, 0x0053, 0x0053, 0x0053, + 0x0053, 0x0053, 0x0053, 0x0053, 0x0053, 0x0053, 0x0052, 0x0052, + 0x0052, 0x0052, 0x0052, 0x0052, 0x0052, 0x0052, 0x0052, 0x0052, + 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, + 0x0051, 0x0051, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, + 0x0050, 0x0050, 0x0050, 0x0050, 0x004F, 0x004F, 0x004F, 0x004F, + 0x004F, 0x004F, 0x004F, 0x004F, 0x004F, 0x004F, 0x004E, 0x004E, + 0x004E, 0x004E, 0x004E, 0x004E, 0x004E, 0x004E, 0x004E, 0x004E, + 0x004E, 0x004D, 0x004D, 0x004D, 0x004D, 0x004D, 0x004D, 0x004D, + 0x004D, 0x004D, 0x004D, 0x004D, 0x004C, 0x004C, 0x004C, 0x004C, + 0x004C, 0x004C, 0x004C, 0x004C, 0x004C, 0x004C, 0x004C, 0x004B, + 0x004B, 0x004B, 0x004B, 0x004B, 0x004B, 0x004B, 0x004B, 0x004B, + 0x004B, 0x004B, 0x004A, 0x004A, 0x004A, 0x004A, 0x004A, 0x004A, + 0x004A, 0x004A, 0x004A, 0x004A, 0x004A, 0x004A, 0x0049, 0x0049, + 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, + 0x0049, 0x0049, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, + 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0047, + 0x0047, 0x0047, 0x0047, 0x0047, 0x0047, 0x0047, 0x0047, 0x0047, + 0x0047, 0x0047, 0x0047, 0x0047, 0x0046, 0x0046, 0x0046, 0x0046, + 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, + 0x0046, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, + 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0044, 0x0044, + 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, + 0x0044, 0x0044, 0x0044, 0x0044, 0x0043, 0x0043, 0x0043, 0x0043, + 0x0043, 0x0043, 0x0043, 0x0043, 0x0043, 0x0043, 0x0043, 0x0043, + 0x0043, 0x0043, 0x0043, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, + 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, + 0x0042, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, + 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, + 0x0041, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, + 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040 +}; + +const int16 sinTable[1025] = { 0x0000, 0x0019, 0x0032, 0x004B, 0x0065, 0x007E, 0x0097, 0x00B0, 0x00C9, 0x00E2, 0x00FB, 0x0114, 0x012E, 0x0147, 0x0160, 0x0179, 0x0192, 0x01AB, 0x01C4, 0x01DD, 0x01F7, 0x0210, 0x0229, 0x0242, @@ -136,6 +269,271 @@ const int16 sin_table[1025] = { // IWRAM 2 kb 0x3FFF, 0x3FFF, 0x3FFF, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000 }; +const int16 atanTable[2050] = { + 0x0000, 0x0005, 0x000A, 0x000F, 0x0014, 0x0019, 0x001F, 0x0024, + 0x0029, 0x002E, 0x0033, 0x0038, 0x003D, 0x0042, 0x0047, 0x004C, + 0x0051, 0x0057, 0x005C, 0x0061, 0x0066, 0x006B, 0x0070, 0x0075, + 0x007A, 0x007F, 0x0084, 0x008A, 0x008F, 0x0094, 0x0099, 0x009E, + 0x00A3, 0x00A8, 0x00AD, 0x00B2, 0x00B7, 0x00BC, 0x00C2, 0x00C7, + 0x00CC, 0x00D1, 0x00D6, 0x00DB, 0x00E0, 0x00E5, 0x00EA, 0x00EF, + 0x00F4, 0x00FA, 0x00FF, 0x0104, 0x0109, 0x010E, 0x0113, 0x0118, + 0x011D, 0x0122, 0x0127, 0x012C, 0x0131, 0x0137, 0x013C, 0x0141, + 0x0146, 0x014B, 0x0150, 0x0155, 0x015A, 0x015F, 0x0164, 0x0169, + 0x016F, 0x0174, 0x0179, 0x017E, 0x0183, 0x0188, 0x018D, 0x0192, + 0x0197, 0x019C, 0x01A1, 0x01A6, 0x01AC, 0x01B1, 0x01B6, 0x01BB, + 0x01C0, 0x01C5, 0x01CA, 0x01CF, 0x01D4, 0x01D9, 0x01DE, 0x01E3, + 0x01E9, 0x01EE, 0x01F3, 0x01F8, 0x01FD, 0x0202, 0x0207, 0x020C, + 0x0211, 0x0216, 0x021B, 0x0220, 0x0226, 0x022B, 0x0230, 0x0235, + 0x023A, 0x023F, 0x0244, 0x0249, 0x024E, 0x0253, 0x0258, 0x025D, + 0x0262, 0x0268, 0x026D, 0x0272, 0x0277, 0x027C, 0x0281, 0x0286, + 0x028B, 0x0290, 0x0295, 0x029A, 0x029F, 0x02A4, 0x02A9, 0x02AF, + 0x02B4, 0x02B9, 0x02BE, 0x02C3, 0x02C8, 0x02CD, 0x02D2, 0x02D7, + 0x02DC, 0x02E1, 0x02E6, 0x02EB, 0x02F0, 0x02F6, 0x02FB, 0x0300, + 0x0305, 0x030A, 0x030F, 0x0314, 0x0319, 0x031E, 0x0323, 0x0328, + 0x032D, 0x0332, 0x0337, 0x033C, 0x0341, 0x0347, 0x034C, 0x0351, + 0x0356, 0x035B, 0x0360, 0x0365, 0x036A, 0x036F, 0x0374, 0x0379, + 0x037E, 0x0383, 0x0388, 0x038D, 0x0392, 0x0397, 0x039C, 0x03A2, + 0x03A7, 0x03AC, 0x03B1, 0x03B6, 0x03BB, 0x03C0, 0x03C5, 0x03CA, + 0x03CF, 0x03D4, 0x03D9, 0x03DE, 0x03E3, 0x03E8, 0x03ED, 0x03F2, + 0x03F7, 0x03FC, 0x0401, 0x0407, 0x040C, 0x0411, 0x0416, 0x041B, + 0x0420, 0x0425, 0x042A, 0x042F, 0x0434, 0x0439, 0x043E, 0x0443, + 0x0448, 0x044D, 0x0452, 0x0457, 0x045C, 0x0461, 0x0466, 0x046B, + 0x0470, 0x0475, 0x047A, 0x047F, 0x0484, 0x0489, 0x048E, 0x0494, + 0x0499, 0x049E, 0x04A3, 0x04A8, 0x04AD, 0x04B2, 0x04B7, 0x04BC, + 0x04C1, 0x04C6, 0x04CB, 0x04D0, 0x04D5, 0x04DA, 0x04DF, 0x04E4, + 0x04E9, 0x04EE, 0x04F3, 0x04F8, 0x04FD, 0x0502, 0x0507, 0x050C, + 0x0511, 0x0516, 0x051B, 0x0520, 0x0525, 0x052A, 0x052F, 0x0534, + 0x0539, 0x053E, 0x0543, 0x0548, 0x054D, 0x0552, 0x0557, 0x055C, + 0x0561, 0x0566, 0x056B, 0x0570, 0x0575, 0x057A, 0x057F, 0x0584, + 0x0589, 0x058E, 0x0593, 0x0598, 0x059D, 0x05A2, 0x05A7, 0x05AC, + 0x05B1, 0x05B6, 0x05BB, 0x05C0, 0x05C5, 0x05CA, 0x05CF, 0x05D4, + 0x05D9, 0x05DE, 0x05E3, 0x05E8, 0x05ED, 0x05F2, 0x05F7, 0x05FC, + 0x0601, 0x0606, 0x060B, 0x0610, 0x0615, 0x061A, 0x061F, 0x0624, + 0x0629, 0x062E, 0x0633, 0x0638, 0x063D, 0x0642, 0x0647, 0x064C, + 0x0651, 0x0656, 0x065B, 0x0660, 0x0665, 0x066A, 0x066E, 0x0673, + 0x0678, 0x067D, 0x0682, 0x0687, 0x068C, 0x0691, 0x0696, 0x069B, + 0x06A0, 0x06A5, 0x06AA, 0x06AF, 0x06B4, 0x06B9, 0x06BE, 0x06C3, + 0x06C8, 0x06CD, 0x06D2, 0x06D7, 0x06DC, 0x06E1, 0x06E5, 0x06EA, + 0x06EF, 0x06F4, 0x06F9, 0x06FE, 0x0703, 0x0708, 0x070D, 0x0712, + 0x0717, 0x071C, 0x0721, 0x0726, 0x072B, 0x0730, 0x0735, 0x0739, + 0x073E, 0x0743, 0x0748, 0x074D, 0x0752, 0x0757, 0x075C, 0x0761, + 0x0766, 0x076B, 0x0770, 0x0775, 0x077A, 0x077E, 0x0783, 0x0788, + 0x078D, 0x0792, 0x0797, 0x079C, 0x07A1, 0x07A6, 0x07AB, 0x07B0, + 0x07B5, 0x07B9, 0x07BE, 0x07C3, 0x07C8, 0x07CD, 0x07D2, 0x07D7, + 0x07DC, 0x07E1, 0x07E6, 0x07EB, 0x07EF, 0x07F4, 0x07F9, 0x07FE, + 0x0803, 0x0808, 0x080D, 0x0812, 0x0817, 0x081C, 0x0820, 0x0825, + 0x082A, 0x082F, 0x0834, 0x0839, 0x083E, 0x0843, 0x0848, 0x084C, + 0x0851, 0x0856, 0x085B, 0x0860, 0x0865, 0x086A, 0x086F, 0x0873, + 0x0878, 0x087D, 0x0882, 0x0887, 0x088C, 0x0891, 0x0896, 0x089A, + 0x089F, 0x08A4, 0x08A9, 0x08AE, 0x08B3, 0x08B8, 0x08BD, 0x08C1, + 0x08C6, 0x08CB, 0x08D0, 0x08D5, 0x08DA, 0x08DF, 0x08E3, 0x08E8, + 0x08ED, 0x08F2, 0x08F7, 0x08FC, 0x0901, 0x0905, 0x090A, 0x090F, + 0x0914, 0x0919, 0x091E, 0x0922, 0x0927, 0x092C, 0x0931, 0x0936, + 0x093B, 0x093F, 0x0944, 0x0949, 0x094E, 0x0953, 0x0958, 0x095C, + 0x0961, 0x0966, 0x096B, 0x0970, 0x0975, 0x0979, 0x097E, 0x0983, + 0x0988, 0x098D, 0x0992, 0x0996, 0x099B, 0x09A0, 0x09A5, 0x09AA, + 0x09AE, 0x09B3, 0x09B8, 0x09BD, 0x09C2, 0x09C6, 0x09CB, 0x09D0, + 0x09D5, 0x09DA, 0x09DE, 0x09E3, 0x09E8, 0x09ED, 0x09F2, 0x09F6, + 0x09FB, 0x0A00, 0x0A05, 0x0A0A, 0x0A0E, 0x0A13, 0x0A18, 0x0A1D, + 0x0A22, 0x0A26, 0x0A2B, 0x0A30, 0x0A35, 0x0A39, 0x0A3E, 0x0A43, + 0x0A48, 0x0A4D, 0x0A51, 0x0A56, 0x0A5B, 0x0A60, 0x0A64, 0x0A69, + 0x0A6E, 0x0A73, 0x0A77, 0x0A7C, 0x0A81, 0x0A86, 0x0A8B, 0x0A8F, + 0x0A94, 0x0A99, 0x0A9E, 0x0AA2, 0x0AA7, 0x0AAC, 0x0AB1, 0x0AB5, + 0x0ABA, 0x0ABF, 0x0AC4, 0x0AC8, 0x0ACD, 0x0AD2, 0x0AD7, 0x0ADB, + 0x0AE0, 0x0AE5, 0x0AE9, 0x0AEE, 0x0AF3, 0x0AF8, 0x0AFC, 0x0B01, + 0x0B06, 0x0B0B, 0x0B0F, 0x0B14, 0x0B19, 0x0B1E, 0x0B22, 0x0B27, + 0x0B2C, 0x0B30, 0x0B35, 0x0B3A, 0x0B3F, 0x0B43, 0x0B48, 0x0B4D, + 0x0B51, 0x0B56, 0x0B5B, 0x0B60, 0x0B64, 0x0B69, 0x0B6E, 0x0B72, + 0x0B77, 0x0B7C, 0x0B80, 0x0B85, 0x0B8A, 0x0B8F, 0x0B93, 0x0B98, + 0x0B9D, 0x0BA1, 0x0BA6, 0x0BAB, 0x0BAF, 0x0BB4, 0x0BB9, 0x0BBD, + 0x0BC2, 0x0BC7, 0x0BCB, 0x0BD0, 0x0BD5, 0x0BD9, 0x0BDE, 0x0BE3, + 0x0BE7, 0x0BEC, 0x0BF1, 0x0BF5, 0x0BFA, 0x0BFF, 0x0C03, 0x0C08, + 0x0C0D, 0x0C11, 0x0C16, 0x0C1B, 0x0C1F, 0x0C24, 0x0C29, 0x0C2D, + 0x0C32, 0x0C37, 0x0C3B, 0x0C40, 0x0C45, 0x0C49, 0x0C4E, 0x0C53, + 0x0C57, 0x0C5C, 0x0C60, 0x0C65, 0x0C6A, 0x0C6E, 0x0C73, 0x0C78, + 0x0C7C, 0x0C81, 0x0C86, 0x0C8A, 0x0C8F, 0x0C93, 0x0C98, 0x0C9D, + 0x0CA1, 0x0CA6, 0x0CAB, 0x0CAF, 0x0CB4, 0x0CB8, 0x0CBD, 0x0CC2, + 0x0CC6, 0x0CCB, 0x0CCF, 0x0CD4, 0x0CD9, 0x0CDD, 0x0CE2, 0x0CE6, + 0x0CEB, 0x0CF0, 0x0CF4, 0x0CF9, 0x0CFD, 0x0D02, 0x0D07, 0x0D0B, + 0x0D10, 0x0D14, 0x0D19, 0x0D1E, 0x0D22, 0x0D27, 0x0D2B, 0x0D30, + 0x0D34, 0x0D39, 0x0D3E, 0x0D42, 0x0D47, 0x0D4B, 0x0D50, 0x0D54, + 0x0D59, 0x0D5E, 0x0D62, 0x0D67, 0x0D6B, 0x0D70, 0x0D74, 0x0D79, + 0x0D7D, 0x0D82, 0x0D87, 0x0D8B, 0x0D90, 0x0D94, 0x0D99, 0x0D9D, + 0x0DA2, 0x0DA6, 0x0DAB, 0x0DAF, 0x0DB4, 0x0DB9, 0x0DBD, 0x0DC2, + 0x0DC6, 0x0DCB, 0x0DCF, 0x0DD4, 0x0DD8, 0x0DDD, 0x0DE1, 0x0DE6, + 0x0DEA, 0x0DEF, 0x0DF3, 0x0DF8, 0x0DFC, 0x0E01, 0x0E05, 0x0E0A, + 0x0E0F, 0x0E13, 0x0E18, 0x0E1C, 0x0E21, 0x0E25, 0x0E2A, 0x0E2E, + 0x0E33, 0x0E37, 0x0E3C, 0x0E40, 0x0E45, 0x0E49, 0x0E4E, 0x0E52, + 0x0E56, 0x0E5B, 0x0E5F, 0x0E64, 0x0E68, 0x0E6D, 0x0E71, 0x0E76, + 0x0E7A, 0x0E7F, 0x0E83, 0x0E88, 0x0E8C, 0x0E91, 0x0E95, 0x0E9A, + 0x0E9E, 0x0EA3, 0x0EA7, 0x0EAC, 0x0EB0, 0x0EB4, 0x0EB9, 0x0EBD, + 0x0EC2, 0x0EC6, 0x0ECB, 0x0ECF, 0x0ED4, 0x0ED8, 0x0EDC, 0x0EE1, + 0x0EE5, 0x0EEA, 0x0EEE, 0x0EF3, 0x0EF7, 0x0EFC, 0x0F00, 0x0F04, + 0x0F09, 0x0F0D, 0x0F12, 0x0F16, 0x0F1B, 0x0F1F, 0x0F23, 0x0F28, + 0x0F2C, 0x0F31, 0x0F35, 0x0F3A, 0x0F3E, 0x0F42, 0x0F47, 0x0F4B, + 0x0F50, 0x0F54, 0x0F58, 0x0F5D, 0x0F61, 0x0F66, 0x0F6A, 0x0F6E, + 0x0F73, 0x0F77, 0x0F7C, 0x0F80, 0x0F84, 0x0F89, 0x0F8D, 0x0F91, + 0x0F96, 0x0F9A, 0x0F9F, 0x0FA3, 0x0FA7, 0x0FAC, 0x0FB0, 0x0FB5, + 0x0FB9, 0x0FBD, 0x0FC2, 0x0FC6, 0x0FCA, 0x0FCF, 0x0FD3, 0x0FD7, + 0x0FDC, 0x0FE0, 0x0FE5, 0x0FE9, 0x0FED, 0x0FF2, 0x0FF6, 0x0FFA, + 0x0FFF, 0x1003, 0x1007, 0x100C, 0x1010, 0x1014, 0x1019, 0x101D, + 0x1021, 0x1026, 0x102A, 0x102E, 0x1033, 0x1037, 0x103B, 0x1040, + 0x1044, 0x1048, 0x104D, 0x1051, 0x1055, 0x105A, 0x105E, 0x1062, + 0x1067, 0x106B, 0x106F, 0x1073, 0x1078, 0x107C, 0x1080, 0x1085, + 0x1089, 0x108D, 0x1092, 0x1096, 0x109A, 0x109E, 0x10A3, 0x10A7, + 0x10AB, 0x10B0, 0x10B4, 0x10B8, 0x10BC, 0x10C1, 0x10C5, 0x10C9, + 0x10CE, 0x10D2, 0x10D6, 0x10DA, 0x10DF, 0x10E3, 0x10E7, 0x10EB, + 0x10F0, 0x10F4, 0x10F8, 0x10FD, 0x1101, 0x1105, 0x1109, 0x110E, + 0x1112, 0x1116, 0x111A, 0x111F, 0x1123, 0x1127, 0x112B, 0x1130, + 0x1134, 0x1138, 0x113C, 0x1140, 0x1145, 0x1149, 0x114D, 0x1151, + 0x1156, 0x115A, 0x115E, 0x1162, 0x1166, 0x116B, 0x116F, 0x1173, + 0x1177, 0x117C, 0x1180, 0x1184, 0x1188, 0x118C, 0x1191, 0x1195, + 0x1199, 0x119D, 0x11A1, 0x11A6, 0x11AA, 0x11AE, 0x11B2, 0x11B6, + 0x11BB, 0x11BF, 0x11C3, 0x11C7, 0x11CB, 0x11CF, 0x11D4, 0x11D8, + 0x11DC, 0x11E0, 0x11E4, 0x11E9, 0x11ED, 0x11F1, 0x11F5, 0x11F9, + 0x11FD, 0x1202, 0x1206, 0x120A, 0x120E, 0x1212, 0x1216, 0x121A, + 0x121F, 0x1223, 0x1227, 0x122B, 0x122F, 0x1233, 0x1237, 0x123C, + 0x1240, 0x1244, 0x1248, 0x124C, 0x1250, 0x1254, 0x1259, 0x125D, + 0x1261, 0x1265, 0x1269, 0x126D, 0x1271, 0x1275, 0x127A, 0x127E, + 0x1282, 0x1286, 0x128A, 0x128E, 0x1292, 0x1296, 0x129A, 0x129F, + 0x12A3, 0x12A7, 0x12AB, 0x12AF, 0x12B3, 0x12B7, 0x12BB, 0x12BF, + 0x12C3, 0x12C7, 0x12CC, 0x12D0, 0x12D4, 0x12D8, 0x12DC, 0x12E0, + 0x12E4, 0x12E8, 0x12EC, 0x12F0, 0x12F4, 0x12F8, 0x12FC, 0x1301, + 0x1305, 0x1309, 0x130D, 0x1311, 0x1315, 0x1319, 0x131D, 0x1321, + 0x1325, 0x1329, 0x132D, 0x1331, 0x1335, 0x1339, 0x133D, 0x1341, + 0x1345, 0x1349, 0x134D, 0x1351, 0x1355, 0x135A, 0x135E, 0x1362, + 0x1366, 0x136A, 0x136E, 0x1372, 0x1376, 0x137A, 0x137E, 0x1382, + 0x1386, 0x138A, 0x138E, 0x1392, 0x1396, 0x139A, 0x139E, 0x13A2, + 0x13A6, 0x13AA, 0x13AE, 0x13B2, 0x13B6, 0x13BA, 0x13BE, 0x13C2, + 0x13C6, 0x13CA, 0x13CE, 0x13D2, 0x13D6, 0x13DA, 0x13DE, 0x13E2, + 0x13E6, 0x13E9, 0x13ED, 0x13F1, 0x13F5, 0x13F9, 0x13FD, 0x1401, + 0x1405, 0x1409, 0x140D, 0x1411, 0x1415, 0x1419, 0x141D, 0x1421, + 0x1425, 0x1429, 0x142D, 0x1431, 0x1435, 0x1439, 0x143D, 0x1440, + 0x1444, 0x1448, 0x144C, 0x1450, 0x1454, 0x1458, 0x145C, 0x1460, + 0x1464, 0x1468, 0x146C, 0x1470, 0x1473, 0x1477, 0x147B, 0x147F, + 0x1483, 0x1487, 0x148B, 0x148F, 0x1493, 0x1497, 0x149B, 0x149E, + 0x14A2, 0x14A6, 0x14AA, 0x14AE, 0x14B2, 0x14B6, 0x14BA, 0x14BE, + 0x14C1, 0x14C5, 0x14C9, 0x14CD, 0x14D1, 0x14D5, 0x14D9, 0x14DD, + 0x14E0, 0x14E4, 0x14E8, 0x14EC, 0x14F0, 0x14F4, 0x14F8, 0x14FB, + 0x14FF, 0x1503, 0x1507, 0x150B, 0x150F, 0x1513, 0x1516, 0x151A, + 0x151E, 0x1522, 0x1526, 0x152A, 0x152D, 0x1531, 0x1535, 0x1539, + 0x153D, 0x1541, 0x1544, 0x1548, 0x154C, 0x1550, 0x1554, 0x1558, + 0x155B, 0x155F, 0x1563, 0x1567, 0x156B, 0x156E, 0x1572, 0x1576, + 0x157A, 0x157E, 0x1581, 0x1585, 0x1589, 0x158D, 0x1591, 0x1594, + 0x1598, 0x159C, 0x15A0, 0x15A4, 0x15A7, 0x15AB, 0x15AF, 0x15B3, + 0x15B7, 0x15BA, 0x15BE, 0x15C2, 0x15C6, 0x15C9, 0x15CD, 0x15D1, + 0x15D5, 0x15D8, 0x15DC, 0x15E0, 0x15E4, 0x15E8, 0x15EB, 0x15EF, + 0x15F3, 0x15F7, 0x15FA, 0x15FE, 0x1602, 0x1606, 0x1609, 0x160D, + 0x1611, 0x1614, 0x1618, 0x161C, 0x1620, 0x1623, 0x1627, 0x162B, + 0x162F, 0x1632, 0x1636, 0x163A, 0x163E, 0x1641, 0x1645, 0x1649, + 0x164C, 0x1650, 0x1654, 0x1658, 0x165B, 0x165F, 0x1663, 0x1666, + 0x166A, 0x166E, 0x1671, 0x1675, 0x1679, 0x167D, 0x1680, 0x1684, + 0x1688, 0x168B, 0x168F, 0x1693, 0x1696, 0x169A, 0x169E, 0x16A1, + 0x16A5, 0x16A9, 0x16AC, 0x16B0, 0x16B4, 0x16B7, 0x16BB, 0x16BF, + 0x16C2, 0x16C6, 0x16CA, 0x16CD, 0x16D1, 0x16D5, 0x16D8, 0x16DC, + 0x16E0, 0x16E3, 0x16E7, 0x16EB, 0x16EE, 0x16F2, 0x16F6, 0x16F9, + 0x16FD, 0x1700, 0x1704, 0x1708, 0x170B, 0x170F, 0x1713, 0x1716, + 0x171A, 0x171D, 0x1721, 0x1725, 0x1728, 0x172C, 0x1730, 0x1733, + 0x1737, 0x173A, 0x173E, 0x1742, 0x1745, 0x1749, 0x174C, 0x1750, + 0x1754, 0x1757, 0x175B, 0x175E, 0x1762, 0x1766, 0x1769, 0x176D, + 0x1770, 0x1774, 0x1778, 0x177B, 0x177F, 0x1782, 0x1786, 0x1789, + 0x178D, 0x1791, 0x1794, 0x1798, 0x179B, 0x179F, 0x17A2, 0x17A6, + 0x17AA, 0x17AD, 0x17B1, 0x17B4, 0x17B8, 0x17BB, 0x17BF, 0x17C2, + 0x17C6, 0x17C9, 0x17CD, 0x17D1, 0x17D4, 0x17D8, 0x17DB, 0x17DF, + 0x17E2, 0x17E6, 0x17E9, 0x17ED, 0x17F0, 0x17F4, 0x17F7, 0x17FB, + 0x17FE, 0x1802, 0x1806, 0x1809, 0x180D, 0x1810, 0x1814, 0x1817, + 0x181B, 0x181E, 0x1822, 0x1825, 0x1829, 0x182C, 0x1830, 0x1833, + 0x1837, 0x183A, 0x183E, 0x1841, 0x1845, 0x1848, 0x184C, 0x184F, + 0x1853, 0x1856, 0x185A, 0x185D, 0x1860, 0x1864, 0x1867, 0x186B, + 0x186E, 0x1872, 0x1875, 0x1879, 0x187C, 0x1880, 0x1883, 0x1887, + 0x188A, 0x188E, 0x1891, 0x1894, 0x1898, 0x189B, 0x189F, 0x18A2, + 0x18A6, 0x18A9, 0x18AD, 0x18B0, 0x18B3, 0x18B7, 0x18BA, 0x18BE, + 0x18C1, 0x18C5, 0x18C8, 0x18CC, 0x18CF, 0x18D2, 0x18D6, 0x18D9, + 0x18DD, 0x18E0, 0x18E3, 0x18E7, 0x18EA, 0x18EE, 0x18F1, 0x18F5, + 0x18F8, 0x18FB, 0x18FF, 0x1902, 0x1906, 0x1909, 0x190C, 0x1910, + 0x1913, 0x1917, 0x191A, 0x191D, 0x1921, 0x1924, 0x1928, 0x192B, + 0x192E, 0x1932, 0x1935, 0x1938, 0x193C, 0x193F, 0x1943, 0x1946, + 0x1949, 0x194D, 0x1950, 0x1953, 0x1957, 0x195A, 0x195D, 0x1961, + 0x1964, 0x1968, 0x196B, 0x196E, 0x1972, 0x1975, 0x1978, 0x197C, + 0x197F, 0x1982, 0x1986, 0x1989, 0x198C, 0x1990, 0x1993, 0x1996, + 0x199A, 0x199D, 0x19A0, 0x19A4, 0x19A7, 0x19AA, 0x19AE, 0x19B1, + 0x19B4, 0x19B8, 0x19BB, 0x19BE, 0x19C2, 0x19C5, 0x19C8, 0x19CC, + 0x19CF, 0x19D2, 0x19D5, 0x19D9, 0x19DC, 0x19DF, 0x19E3, 0x19E6, + 0x19E9, 0x19ED, 0x19F0, 0x19F3, 0x19F6, 0x19FA, 0x19FD, 0x1A00, + 0x1A04, 0x1A07, 0x1A0A, 0x1A0D, 0x1A11, 0x1A14, 0x1A17, 0x1A1B, + 0x1A1E, 0x1A21, 0x1A24, 0x1A28, 0x1A2B, 0x1A2E, 0x1A31, 0x1A35, + 0x1A38, 0x1A3B, 0x1A3E, 0x1A42, 0x1A45, 0x1A48, 0x1A4B, 0x1A4F, + 0x1A52, 0x1A55, 0x1A58, 0x1A5C, 0x1A5F, 0x1A62, 0x1A65, 0x1A69, + 0x1A6C, 0x1A6F, 0x1A72, 0x1A76, 0x1A79, 0x1A7C, 0x1A7F, 0x1A83, + 0x1A86, 0x1A89, 0x1A8C, 0x1A8F, 0x1A93, 0x1A96, 0x1A99, 0x1A9C, + 0x1A9F, 0x1AA3, 0x1AA6, 0x1AA9, 0x1AAC, 0x1AB0, 0x1AB3, 0x1AB6, + 0x1AB9, 0x1ABC, 0x1AC0, 0x1AC3, 0x1AC6, 0x1AC9, 0x1ACC, 0x1ACF, + 0x1AD3, 0x1AD6, 0x1AD9, 0x1ADC, 0x1ADF, 0x1AE3, 0x1AE6, 0x1AE9, + 0x1AEC, 0x1AEF, 0x1AF2, 0x1AF6, 0x1AF9, 0x1AFC, 0x1AFF, 0x1B02, + 0x1B05, 0x1B09, 0x1B0C, 0x1B0F, 0x1B12, 0x1B15, 0x1B18, 0x1B1C, + 0x1B1F, 0x1B22, 0x1B25, 0x1B28, 0x1B2B, 0x1B2E, 0x1B32, 0x1B35, + 0x1B38, 0x1B3B, 0x1B3E, 0x1B41, 0x1B44, 0x1B48, 0x1B4B, 0x1B4E, + 0x1B51, 0x1B54, 0x1B57, 0x1B5A, 0x1B5D, 0x1B61, 0x1B64, 0x1B67, + 0x1B6A, 0x1B6D, 0x1B70, 0x1B73, 0x1B76, 0x1B79, 0x1B7D, 0x1B80, + 0x1B83, 0x1B86, 0x1B89, 0x1B8C, 0x1B8F, 0x1B92, 0x1B95, 0x1B98, + 0x1B9C, 0x1B9F, 0x1BA2, 0x1BA5, 0x1BA8, 0x1BAB, 0x1BAE, 0x1BB1, + 0x1BB4, 0x1BB7, 0x1BBA, 0x1BBD, 0x1BC1, 0x1BC4, 0x1BC7, 0x1BCA, + 0x1BCD, 0x1BD0, 0x1BD3, 0x1BD6, 0x1BD9, 0x1BDC, 0x1BDF, 0x1BE2, + 0x1BE5, 0x1BE8, 0x1BEB, 0x1BEE, 0x1BF2, 0x1BF5, 0x1BF8, 0x1BFB, + 0x1BFE, 0x1C01, 0x1C04, 0x1C07, 0x1C0A, 0x1C0D, 0x1C10, 0x1C13, + 0x1C16, 0x1C19, 0x1C1C, 0x1C1F, 0x1C22, 0x1C25, 0x1C28, 0x1C2B, + 0x1C2E, 0x1C31, 0x1C34, 0x1C37, 0x1C3A, 0x1C3D, 0x1C40, 0x1C43, + 0x1C46, 0x1C49, 0x1C4C, 0x1C4F, 0x1C52, 0x1C55, 0x1C58, 0x1C5B, + 0x1C5E, 0x1C61, 0x1C64, 0x1C67, 0x1C6A, 0x1C6D, 0x1C70, 0x1C73, + 0x1C76, 0x1C79, 0x1C7C, 0x1C7F, 0x1C82, 0x1C85, 0x1C88, 0x1C8B, + 0x1C8E, 0x1C91, 0x1C94, 0x1C97, 0x1C9A, 0x1C9D, 0x1CA0, 0x1CA3, + 0x1CA6, 0x1CA9, 0x1CAC, 0x1CAF, 0x1CB2, 0x1CB5, 0x1CB8, 0x1CBB, + 0x1CBE, 0x1CC1, 0x1CC3, 0x1CC6, 0x1CC9, 0x1CCC, 0x1CCF, 0x1CD2, + 0x1CD5, 0x1CD8, 0x1CDB, 0x1CDE, 0x1CE1, 0x1CE4, 0x1CE7, 0x1CEA, + 0x1CED, 0x1CF0, 0x1CF3, 0x1CF5, 0x1CF8, 0x1CFB, 0x1CFE, 0x1D01, + 0x1D04, 0x1D07, 0x1D0A, 0x1D0D, 0x1D10, 0x1D13, 0x1D16, 0x1D18, + 0x1D1B, 0x1D1E, 0x1D21, 0x1D24, 0x1D27, 0x1D2A, 0x1D2D, 0x1D30, + 0x1D33, 0x1D35, 0x1D38, 0x1D3B, 0x1D3E, 0x1D41, 0x1D44, 0x1D47, + 0x1D4A, 0x1D4D, 0x1D4F, 0x1D52, 0x1D55, 0x1D58, 0x1D5B, 0x1D5E, + 0x1D61, 0x1D64, 0x1D66, 0x1D69, 0x1D6C, 0x1D6F, 0x1D72, 0x1D75, + 0x1D78, 0x1D7B, 0x1D7D, 0x1D80, 0x1D83, 0x1D86, 0x1D89, 0x1D8C, + 0x1D8E, 0x1D91, 0x1D94, 0x1D97, 0x1D9A, 0x1D9D, 0x1DA0, 0x1DA2, + 0x1DA5, 0x1DA8, 0x1DAB, 0x1DAE, 0x1DB1, 0x1DB3, 0x1DB6, 0x1DB9, + 0x1DBC, 0x1DBF, 0x1DC2, 0x1DC4, 0x1DC7, 0x1DCA, 0x1DCD, 0x1DD0, + 0x1DD3, 0x1DD5, 0x1DD8, 0x1DDB, 0x1DDE, 0x1DE1, 0x1DE3, 0x1DE6, + 0x1DE9, 0x1DEC, 0x1DEF, 0x1DF1, 0x1DF4, 0x1DF7, 0x1DFA, 0x1DFD, + 0x1DFF, 0x1E02, 0x1E05, 0x1E08, 0x1E0B, 0x1E0D, 0x1E10, 0x1E13, + 0x1E16, 0x1E19, 0x1E1B, 0x1E1E, 0x1E21, 0x1E24, 0x1E26, 0x1E29, + 0x1E2C, 0x1E2F, 0x1E32, 0x1E34, 0x1E37, 0x1E3A, 0x1E3D, 0x1E3F, + 0x1E42, 0x1E45, 0x1E48, 0x1E4A, 0x1E4D, 0x1E50, 0x1E53, 0x1E55, + 0x1E58, 0x1E5B, 0x1E5E, 0x1E60, 0x1E63, 0x1E66, 0x1E69, 0x1E6B, + 0x1E6E, 0x1E71, 0x1E74, 0x1E76, 0x1E79, 0x1E7C, 0x1E7F, 0x1E81, + 0x1E84, 0x1E87, 0x1E8A, 0x1E8C, 0x1E8F, 0x1E92, 0x1E94, 0x1E97, + 0x1E9A, 0x1E9D, 0x1E9F, 0x1EA2, 0x1EA5, 0x1EA8, 0x1EAA, 0x1EAD, + 0x1EB0, 0x1EB2, 0x1EB5, 0x1EB8, 0x1EBA, 0x1EBD, 0x1EC0, 0x1EC3, + 0x1EC5, 0x1EC8, 0x1ECB, 0x1ECD, 0x1ED0, 0x1ED3, 0x1ED5, 0x1ED8, + 0x1EDB, 0x1EDE, 0x1EE0, 0x1EE3, 0x1EE6, 0x1EE8, 0x1EEB, 0x1EEE, + 0x1EF0, 0x1EF3, 0x1EF6, 0x1EF8, 0x1EFB, 0x1EFE, 0x1F00, 0x1F03, + 0x1F06, 0x1F08, 0x1F0B, 0x1F0E, 0x1F10, 0x1F13, 0x1F16, 0x1F18, + 0x1F1B, 0x1F1E, 0x1F20, 0x1F23, 0x1F26, 0x1F28, 0x1F2B, 0x1F2E, + 0x1F30, 0x1F33, 0x1F36, 0x1F38, 0x1F3B, 0x1F3D, 0x1F40, 0x1F43, + 0x1F45, 0x1F48, 0x1F4B, 0x1F4D, 0x1F50, 0x1F53, 0x1F55, 0x1F58, + 0x1F5A, 0x1F5D, 0x1F60, 0x1F62, 0x1F65, 0x1F68, 0x1F6A, 0x1F6D, + 0x1F6F, 0x1F72, 0x1F75, 0x1F77, 0x1F7A, 0x1F7C, 0x1F7F, 0x1F82, + 0x1F84, 0x1F87, 0x1F8A, 0x1F8C, 0x1F8F, 0x1F91, 0x1F94, 0x1F97, + 0x1F99, 0x1F9C, 0x1F9E, 0x1FA1, 0x1FA4, 0x1FA6, 0x1FA9, 0x1FAB, + 0x1FAE, 0x1FB0, 0x1FB3, 0x1FB6, 0x1FB8, 0x1FBB, 0x1FBD, 0x1FC0, + 0x1FC3, 0x1FC5, 0x1FC8, 0x1FCA, 0x1FCD, 0x1FCF, 0x1FD2, 0x1FD5, + 0x1FD7, 0x1FDA, 0x1FDC, 0x1FDF, 0x1FE1, 0x1FE4, 0x1FE6, 0x1FE9, + 0x1FEC, 0x1FEE, 0x1FF1, 0x1FF3, 0x1FF6, 0x1FF8, 0x1FFB, 0x1FFD, + 0x2000, 0x2000, +}; + +const int32 atanOctant[] = { + 0, -16384, -65535, 49152, -32768, 16384, 32768, -49152 +}; + + int32 phd_sin(int32 x) { x &= 0xFFFF; @@ -146,7 +544,7 @@ int32 phd_sin(int32 x) x = 0x8000 - x; } - x = sin_table[x >> 4]; + x = sinTable[x >> 4]; return neg ? -x : x; } @@ -156,17 +554,86 @@ int32 phd_cos(int32 x) return phd_sin(x + 0x4000); } -int32 clamp(int32 x, int32 a, int32 b) { - return x < a ? a : (x > b ? b : x); +int32 phd_atan(int32 x, int32 y) +{ + if (x == 0 && y == 0) + return 0; + + int32 o = 0; + + if (x < 0) { + o += 4; + x = -x; + } + + if (y < 0) { + o += 2; + y = -y; + } + + if (y > x) { + o++; + swap(x, y); + } + + return abs(atanTable[(y << 11) / x] + atanOctant[o]); } -void initLUT() { +uint32 phd_sqrt(uint32 x) +{ + uint32 m = 0x40000000; + uint32 y = 0; + uint32 z = 0; + + do { + y += m; + + if (y > x) { + y = z; + } else { + x -= y; + y = m | z; + } + + z = y >> 1; + } while (m >>= 2); + + return y; +} + +void anglesFromVector(int32 x, int32 y, int32 z, int16 &angleX, int16 &angleY) +{ + angleY = phd_atan(z, x); + angleX = phd_atan(phd_sqrt(x * x + z * z), y); + + if ((y > 0 && angleX > 0) || (y < 0 && angleX < 0)) + { + angleX = -angleX; + } +} + +/* +void initDivTable() +{ + uint16 divTable[DIV_TABLE_SIZE]; + divTable[0] = 0xFFFF; divTable[1] = 0xFFFF; for (uint32 i = 2; i < DIV_TABLE_SIZE; i++) { divTable[i] = (1 << 16) / i; } + + ASSERT((DIV_TABLE_SIZE & 8) == 0) + + printf("const uint16 divTable[DIV_TABLE_SIZE] = {\n"); + for (int i = 0; i < DIV_TABLE_SIZE; i += 8) { + printf(" 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X,\n", + divTable[i + 0], divTable[i + 1], divTable[i + 2], divTable[i + 3], + divTable[i + 4], divTable[i + 5], divTable[i + 6], divTable[i + 7]); + } + printf("};\n"); } +*/ Matrix& matrixGet() { @@ -188,22 +655,17 @@ void matrixPop() matrixStackIndex--; } -void matrixTranslate(int32 x, int32 y, int32 z) +void matrixTranslate(const vec3i &pos) { Matrix &m = matrixGet(); - - vec3i offset(x, y, z); - m[0][3] += DP33(m[0], offset); - m[1][3] += DP33(m[1], offset); - m[2][3] += DP33(m[2], offset); + m[0][3] += DP33(m[0], pos); + m[1][3] += DP33(m[1], pos); + m[2][3] += DP33(m[2], pos); } -void matrixTranslateAbs(int32 x, int32 y, int32 z) +void matrixTranslateAbs(const vec3i &pos) { - vec3i d; - d.x = x - viewPos.x; - d.y = y - viewPos.y; - d.z = z - viewPos.z; + vec3i d = pos - viewPos; Matrix &m = matrixGet(); m[0][3] = DP33(m[0], d); @@ -290,17 +752,41 @@ void matrixRotateYXZ(int32 angleX, int32 angleY, int32 angleZ) if (angleZ) matrixRotateZ(angleZ); } -void matrixFrame(int32 x, int32 y, int32 z, uint16* angles) +#define LERP_FAST(a, b, mul, div) a = (a + b) >> 1 +#define LERP_SLOW(a, b, mul, div) a = a + (b - a) * mul / div + +#define LERP_ROW(lerp_func, a, b, mul, div) \ + lerp_func(a[0], b[0], mul, div); \ + lerp_func(a[1], b[1], mul, div); \ + lerp_func(a[2], b[2], mul, div); \ + lerp_func(a[3], b[3], mul, div); + +void matrixLerp(const Matrix &n, int32 multiplier, int32 divider) +{ + Matrix &m = matrixGet(); + + if (divider == 2) { + LERP_ROW(LERP_FAST, m[0], n[0], multiplier, divider); + LERP_ROW(LERP_FAST, m[1], n[1], multiplier, divider); + LERP_ROW(LERP_FAST, m[2], n[2], multiplier, divider); + } else { + LERP_ROW(LERP_SLOW, m[0], n[0], multiplier, divider); + LERP_ROW(LERP_SLOW, m[1], n[1], multiplier, divider); + LERP_ROW(LERP_SLOW, m[2], n[2], multiplier, divider); + } +} + +void matrixFrame(const vec3i &pos, uint16* angles) { int32 angleX = (angles[1] & 0x3FF0) << 2; int32 angleY = (angles[1] & 0x000F) << 12 | (angles[0] & 0xFC00) >> 4; int32 angleZ = (angles[0] & 0x03FF) << 6; - matrixTranslate(x, y, z); + matrixTranslate(pos); matrixRotateYXZ(angleX, angleY, angleZ); } -void matrixSetView(int32 x, int32 y, int32 z, int32 angleX, int32 angleY) +void matrixSetView(const vec3i &pos, int32 angleX, int32 angleY) { int32 sx = phd_sin(angleX); int32 cx = phd_cos(angleX); @@ -309,22 +795,37 @@ void matrixSetView(int32 x, int32 y, int32 z, int32 angleX, int32 angleY) Matrix &m = matrixGet(); - m[0][0] = cy; - m[0][1] = 0; - m[0][2] = -sy; - m[0][3] = x; +#ifdef ROTATE90_MODE + m[1][0] = -(cy); + m[1][1] = -(0); + m[1][2] = -(-sy); + m[1][3] = pos.x; - m[1][0] = (sx * sy) >> FIXED_SHIFT; - m[1][1] = cx; - m[1][2] = (sx * cy) >> FIXED_SHIFT; - m[1][3] = y; + m[0][0] = (sx * sy) >> FIXED_SHIFT; + m[0][1] = cx; + m[0][2] = (sx * cy) >> FIXED_SHIFT; + m[0][3] = pos.y; m[2][0] = (cx * sy) >> FIXED_SHIFT; m[2][1] = -sx; m[2][2] = (cx * cy) >> FIXED_SHIFT; - m[2][3] = z; + m[2][3] = pos.z; +#else + m[0][0] = cy; + m[0][1] = 0; + m[0][2] = -sy; + m[0][3] = pos.x; - viewPos.x = x; - viewPos.y = y; - viewPos.z = z; + m[1][0] = (sx * sy) >> FIXED_SHIFT; + m[1][1] = cx; + m[1][2] = (sx * cy) >> FIXED_SHIFT; + m[1][3] = pos.y; + + m[2][0] = (cx * sy) >> FIXED_SHIFT; + m[2][1] = -sx; + m[2][2] = (cx * cy) >> FIXED_SHIFT; + m[2][3] = pos.z; +#endif + + viewPos = pos; } \ No newline at end of file diff --git a/src/platform/gba/common.h b/src/platform/gba/common.h index ba242d0..0ab78f8 100644 --- a/src/platform/gba/common.h +++ b/src/platform/gba/common.h @@ -3,20 +3,13 @@ //#define TEST //#define PROFILE +#define ROTATE90_MODE #if defined(_WIN32) #define _CRT_SECURE_NO_WARNINGS #include #elif defined(__GBA__) - #include - #include - #include - #include - #include - #include - #include - #include - #include + #include #elif defined(__TNS__) #include #endif @@ -30,25 +23,24 @@ //#define DEBUG_FACES #if defined(__TNS__) - #define WIDTH SCREEN_WIDTH - #define HEIGHT SCREEN_HEIGHT - #define FRAME_WIDTH WIDTH - #define FRAME_HEIGHT HEIGHT - #define FOV_SHIFT 8 + #define FRAME_WIDTH SCREEN_WIDTH + #define FRAME_HEIGHT SCREEN_HEIGHT #else - #define WIDTH 160 - #define HEIGHT 128 - #define FRAME_WIDTH 160 - #define FRAME_HEIGHT 128 - #define FOV_SHIFT 7 + #ifdef ROTATE90_MODE + #define FRAME_WIDTH 160 + #define FRAME_HEIGHT 120 + #else + #define FRAME_WIDTH 160 + #define FRAME_HEIGHT 128 + #endif #endif #if defined(_WIN32) - #define INLINE inline - #define NOINLINE __declspec(noinline) + #define X_INLINE inline + #define X_NOINLINE __declspec(noinline) #elif defined(__GBA__) || defined(__TNS__) - #define INLINE __attribute__((always_inline)) inline - #define NOINLINE __attribute__((noinline)) + #define X_INLINE __attribute__((always_inline)) inline + #define X_NOINLINE __attribute__((noinline)) #endif typedef signed char int8; @@ -60,12 +52,6 @@ typedef unsigned short uint16; typedef unsigned int uint32; typedef int16 Index; -#define PI 3.14159265358979323846f -#define PIH (PI * 0.5f) -#define PI2 (PI * 2.0f) -#define DEG2RAD (PI / 180.0f) -#define RAD2DEG (180.0f / PI) - #ifdef __GBA__ #define ARM_CODE __attribute__((target("arm"))) #define THUMB_CODE __attribute__((target("thumb"))) @@ -88,10 +74,10 @@ typedef int16 Index; #if defined(_WIN32) #define ALIGN4 __declspec(align(4)) -#elif defined(__GBA__) - #define ALIGN4 __attribute__((aligned(4))) -#elif defined(__TNS__) + #define ALIGN16 __declspec(align(16)) +#elif defined(__GBA__) || defined(__TNS__) #define ALIGN4 __attribute__((aligned(4))) + #define ALIGN16 __attribute__((aligned(16))) #endif #if defined(_WIN32) @@ -100,6 +86,14 @@ typedef int16 Index; #define ASSERT(x) #endif +#if defined(_WIN32) + extern uint16 fb[FRAME_WIDTH * FRAME_HEIGHT]; +#elif defined(__GBA__) + extern uint32 fb; +#elif defined(__TNS__) + extern uint16 fb[FRAME_WIDTH * FRAME_HEIGHT]; +#endif + #ifdef PROFILE #if defined(_WIN32) @@ -124,13 +118,13 @@ typedef int16 Index; #endif #define PROFILE_START() {\ - REG_TM0CNT_L = 0;\ - REG_TM0CNT_H = (1 << 7) | TIMER_FREQ_DIV;\ + REG_TM2CNT_L = 0;\ + REG_TM2CNT_H = (1 << 7) | TIMER_FREQ_DIV;\ } #define PROFILE_STOP(value) {\ - value += REG_TM0CNT_L;\ - REG_TM0CNT_H = 0;\ + value += REG_TM2CNT_L;\ + REG_TM2CNT_H = 0;\ } #else @@ -147,34 +141,42 @@ typedef int16 Index; #endif enum InputKey { - IK_UP, - IK_RIGHT, - IK_DOWN, - IK_LEFT, - IK_A, - IK_B, - IK_L, - IK_R, - IK_MAX + IK_NONE = 0, + IK_UP = (1 << 0), + IK_RIGHT = (1 << 1), + IK_DOWN = (1 << 2), + IK_LEFT = (1 << 3), + IK_A = (1 << 4), + IK_B = (1 << 5), + IK_L = (1 << 6), + IK_R = (1 << 7), + IK_START = (1 << 8), + IK_SELECT = (1 << 9), }; -extern bool keys[IK_MAX]; +struct vec3s { + int16 x, y, z; + + vec3s() = default; + X_INLINE vec3s(int16 x, int16 y, int16 z) : x(x), y(y), z(z) {} +}; struct vec3i { int32 x, y, z; vec3i() = default; - INLINE vec3i(int32 x, int32 y, int32 z) : x(x), y(y), z(z) {} -}; - -struct vec3s { - int16 x, y, z; + X_INLINE vec3i(int32 x, int32 y, int32 z) : x(x), y(y), z(z) {} + X_INLINE vec3i(vec3s &v) : x(v.x), y(v.y), z(v.z) {} + X_INLINE vec3i operator + (const vec3i &v) const { return vec3i(x + v.x, y + v.y, z + v.z); } + X_INLINE vec3i operator - (const vec3i &v) const { return vec3i(x - v.x, y - v.y, z - v.z); } + X_INLINE vec3i& operator += (const vec3i &v) { x += v.x; y += v.y; z += v.z; return *this; } + X_INLINE vec3i& operator -= (const vec3i &v) { x -= v.x; y -= v.y; z -= v.z; return *this; } }; struct vec4i { int32 x, y, z, w; - INLINE int32& operator [] (int32 index) const { + X_INLINE int32& operator [] (int32 index) const { ASSERT(index >= 0 && index <= 3); return ((int32*)this)[index]; } @@ -192,6 +194,37 @@ struct Triangle { uint16 flags; }; +struct Rect { + int32 x0; + int32 y0; + int32 x1; + int32 y1; +}; + +struct Vertex { + int16 x, y, z; + uint8 g, clip; +}; + +union UV { + struct { uint16 v, u; }; + uint32 uv; +}; + +struct VertexUV { + Vertex v; + UV t; + VertexUV *prev; + VertexUV *next; +}; + +struct Face { + Face* next; + uint16 flags; + int16 start; + int8 indices[4]; +}; + struct Box { int16 minX; int16 maxX; @@ -227,11 +260,10 @@ struct RoomInfo { int8 ceiling; }; - struct Light { - // int32 x, y, z; - // uint16 intensity; - // uint32 radius; - uint8 dummy[18]; + struct Light { // TODO align struct + uint8 pos[12]; + int16 intensity; + uint8 radius[4]; }; struct Mesh { @@ -262,6 +294,32 @@ struct RoomInfo { */ }; +struct Room { + Rect clip; + uint8 firstItem; + bool visible; + + // TODO leave in ROM + int32 x, z; + uint16 vCount; + uint16 qCount; + uint16 tCount; + uint16 pCount; + uint16 lCount; + uint16 mCount; + uint16 zSectors; + uint16 xSectors; + uint16 ambient; + + const RoomInfo::Vertex* vertices; + const Quad* quads; + const Triangle* triangles; + const RoomInfo::Portal* portals; + const RoomInfo::Sector* sectors; + const RoomInfo::Light* lights; + const RoomInfo::Mesh* meshes; +}; + struct Node { uint32 flags; vec3i pos; @@ -296,7 +354,7 @@ struct ItemInfo { // 24 union { struct { - uint16 gravity:1; // TODO + uint16 gravity:1, shadow:1; // TODO }; uint16 value; } flags; @@ -306,12 +364,12 @@ struct ItemInfo { // 24 #define FILE_ITEM_SIZE (sizeof(ItemInfo) - 2) -struct Item : ItemInfo { // 24 + 20 = 44 +struct Item : ItemInfo { // 24 + 28 = 52 int16 angleX; int16 angleZ; - uint16 vSpeed; - uint16 hSpeed; + int16 vSpeed; + int16 hSpeed; uint16 animIndex; uint16 frameIndex; @@ -319,11 +377,28 @@ struct Item : ItemInfo { // 24 + 20 = 44 uint8 state; uint8 nextState; uint8 goalState; - uint8 timer; + uint8 waterState; uint16 health; + uint16 timer; + + int16 moveAngle; uint8 nextItem; uint8 nextActive; + + int16 turnSpeed; + int16 vSpeedHack; +}; + +struct SoundInfo +{ + uint16 index; + uint16 volume; + uint16 chance; + union { + struct { uint16 mode:2, count:4, unused:6, camera:1, pitch:1, gain:1, :1; }; + uint16 value; + } flags; }; struct Anim { @@ -343,14 +418,27 @@ struct Anim { uint16 nextAnimIndex; uint16 nextFrameIndex; - uint16 scCount; - uint16 scOffset; + uint16 statesCount; + uint16 statesStart; - uint16 acCount; - uint16 animCommand; + uint16 commandsCount; + uint16 commandsStart; }; -struct Frame { +struct AnimState { + uint16 state; + uint16 rangesCount; + uint16 rangesStart; +}; + +struct AnimRange { + uint16 frameBegin; + uint16 frameEnd; + uint16 nextAnimIndex; + uint16 nextFrameIndex; +}; + +struct AnimFrame { Box box; vec3s pos; uint16 angles[1]; @@ -379,52 +467,367 @@ struct SpriteSeq { int16 sStart; }; -struct Rect { - int32 x0; - int32 y0; - int32 x1; - int32 y1; +union FloorData { // 2 bytes + uint16 value; + + union Command { + struct { + uint16 func:5, tri:3, sub:7, end:1; + }; + struct { + int16 :5, a:5, b:5, :1; + } triangle; + } cmd; + + struct { + int8 slantX; + int8 slantZ; + }; + + struct { + uint16 a:4, b:4, c:4, d:4; + }; + + struct TriggerInfo { + uint16 timer:8, once:1, mask:5, :2; + } triggerInfo; + + union TriggerCommand { + struct { + uint16 args:10, action:5, end:1; + }; + struct { + uint16 timer:8, once:1, speed:5, :2; + }; + } triggerCmd; }; -struct Vertex { - int16 x, y, z; - uint8 g, clip; +enum FloorType { + FLOOR_TYPE_NONE, + FLOOR_TYPE_PORTAL, + FLOOR_TYPE_FLOOR, + FLOOR_TYPE_CEILING, + FLOOR_TYPE_TRIGGER, + FLOOR_TYPE_LAVA }; -union UV { - struct { uint16 v, u; }; - uint32 uv; +enum TriggerAction { + TRIGGER_ACTION_ACTIVATE, + TRIGGER_ACTION_CAMERA_SWITCH, + TRIGGER_ACTION_FLOW, + TRIGGER_ACTION_FLIP, + TRIGGER_ACTION_FLIP_ON, + TRIGGER_ACTION_FLIP_OFF, + TRIGGER_ACTION_CAMERA_TARGET, + TRIGGER_ACTION_END, + TRIGGER_ACTION_SOUNDTRACK, + TRIGGER_ACTION_EFFECT, + TRIGGER_ACTION_SECRET, + TRIGGER_ACTION_CLEAR_BODIES, + TRIGGER_ACTION_FLYBY, + TRIGGER_ACTION_CUTSCENE }; -struct VertexUV { - Vertex v; - UV t; +enum SlantType { + SLANT_NONE, + SLANT_LOW, + SLANT_HIGH }; -struct Face { - uint16 flags; - int16 depth; - int16 start; - int8 indices[4]; +enum WaterState { + WATER_STATE_ABOVE, + WATER_STATE_SURFACE, + WATER_STATE_UNDER, + WATER_STATE_WADE, }; +enum AnimCommand { + ANIM_CMD_NONE, + ANIM_CMD_OFFSET, + ANIM_CMD_JUMP, + ANIM_CMD_EMPTY, + ANIM_CMD_KILL, + ANIM_CMD_SOUND, + ANIM_CMD_EFFECT, +}; + +enum EffectType { + FX_NONE = -1, + FX_ROTATE_180 , + FX_FLOOR_SHAKE , + FX_LARA_NORMAL , + FX_LARA_BUBBLES , + FX_FINISH_LEVEL , + FX_EARTHQUAKE , + FX_FLOOD , + FX_UNK1 , + FX_STAIRS2SLOPE , + FX_UNK3 , + FX_UNK4 , + FX_EXPLOSION , + FX_LARA_HANDSFREE , + FX_FLIP_MAP , + FX_DRAW_RIGHTGUN , + FX_DRAW_LEFTGUN , + FX_SHOT_RIGHTGUN , + FX_SHOT_LEFTGUN , + FX_MESH_SWAP_1 , + FX_MESH_SWAP_2 , + FX_MESH_SWAP_3 , + FX_INV_ON , + FX_INV_OFF , + FX_DYN_ON , + FX_DYN_OFF , + FX_STATUE_FX , + FX_RESET_HAIR , + FX_BOILER_FX , + FX_ASSAULT_RESET , + FX_ASSAULT_STOP , + FX_ASSAULT_START , + FX_ASSAULT_FINISH , + FX_FOOTPRINT , +// specific + FX_TR1_FLICKER = 16, +}; + +#define ITEM_TYPES(E) \ + E( LARA ) \ + E( LARA_PISTOLS ) \ + E( LARA_SHOTGUN ) \ + E( LARA_MAGNUMS ) \ + E( LARA_UZIS ) \ + E( LARA_SPEC ) \ + E( DOPPELGANGER ) \ + E( WOLF ) \ + E( BEAR ) \ + E( BAT ) \ + E( CROCODILE_LAND ) \ + E( CROCODILE_WATER ) \ + E( LION_MALE ) \ + E( LION_FEMALE ) \ + E( PUMA ) \ + E( GORILLA ) \ + E( RAT_LAND ) \ + E( RAT_WATER ) \ + E( REX ) \ + E( RAPTOR ) \ + E( MUTANT_1 ) \ + E( MUTANT_2 ) \ + E( MUTANT_3 ) \ + E( CENTAUR ) \ + E( MUMMY ) \ + E( UNUSED_1 ) \ + E( UNUSED_2 ) \ + E( LARSON ) \ + E( PIERRE ) \ + E( SKATEBOARD ) \ + E( SKATEBOY ) \ + E( COWBOY ) \ + E( MR_T ) \ + E( NATLA ) \ + E( GIANT_MUTANT ) \ + E( TRAP_FLOOR ) \ + E( TRAP_SWING_BLADE ) \ + E( TRAP_SPIKES ) \ + E( TRAP_BOULDER ) \ + E( DART ) \ + E( TRAP_DART_EMITTER ) \ + E( DRAWBRIDGE ) \ + E( TRAP_SLAM ) \ + E( TRAP_SWORD ) \ + E( HAMMER_HANDLE ) \ + E( HAMMER_BLOCK ) \ + E( LIGHTNING ) \ + E( MOVING_OBJECT ) \ + E( BLOCK_1 ) \ + E( BLOCK_2 ) \ + E( BLOCK_3 ) \ + E( BLOCK_4 ) \ + E( MOVING_BLOCK ) \ + E( TRAP_CEILING_1 ) \ + E( TRAP_CEILING_2 ) \ + E( SWITCH ) \ + E( SWITCH_WATER ) \ + E( DOOR_1 ) \ + E( DOOR_2 ) \ + E( DOOR_3 ) \ + E( DOOR_4 ) \ + E( DOOR_5 ) \ + E( DOOR_6 ) \ + E( DOOR_7 ) \ + E( DOOR_8 ) \ + E( TRAP_DOOR_1 ) \ + E( TRAP_DOOR_2 ) \ + E( UNUSED_3 ) \ + E( BRIDGE_1 ) \ + E( BRIDGE_2 ) \ + E( BRIDGE_3 ) \ + E( INV_PASSPORT ) \ + E( INV_COMPASS ) \ + E( INV_HOME ) \ + E( GEARS_1 ) \ + E( GEARS_2 ) \ + E( GEARS_3 ) \ + E( CUT_1 ) \ + E( CUT_2 ) \ + E( CUT_3 ) \ + E( CUT_4 ) \ + E( INV_PASSPORT_CLOSED ) \ + E( INV_MAP ) \ + E( CRYSTAL ) \ + E( PISTOLS ) \ + E( SHOTGUN ) \ + E( MAGNUMS ) \ + E( UZIS ) \ + E( AMMO_PISTOLS ) \ + E( AMMO_SHOTGUN ) \ + E( AMMO_MAGNUMS ) \ + E( AMMO_UZIS ) \ + E( EXPLOSIVE ) \ + E( MEDIKIT_SMALL ) \ + E( MEDIKIT_BIG ) \ + E( INV_DETAIL ) \ + E( INV_SOUND ) \ + E( INV_CONTROLS ) \ + E( INV_GAMMA ) \ + E( INV_PISTOLS ) \ + E( INV_SHOTGUN ) \ + E( INV_MAGNUMS ) \ + E( INV_UZIS ) \ + E( INV_AMMO_PISTOLS ) \ + E( INV_AMMO_SHOTGUN ) \ + E( INV_AMMO_MAGNUMS ) \ + E( INV_AMMO_UZIS ) \ + E( INV_EXPLOSIVE ) \ + E( INV_MEDIKIT_SMALL ) \ + E( INV_MEDIKIT_BIG ) \ + E( PUZZLE_1 ) \ + E( PUZZLE_2 ) \ + E( PUZZLE_3 ) \ + E( PUZZLE_4 ) \ + E( INV_PUZZLE_1 ) \ + E( INV_PUZZLE_2 ) \ + E( INV_PUZZLE_3 ) \ + E( INV_PUZZLE_4 ) \ + E( PUZZLE_HOLE_1 ) \ + E( PUZZLE_HOLE_2 ) \ + E( PUZZLE_HOLE_3 ) \ + E( PUZZLE_HOLE_4 ) \ + E( PUZZLE_DONE_1 ) \ + E( PUZZLE_DONE_2 ) \ + E( PUZZLE_DONE_3 ) \ + E( PUZZLE_DONE_4 ) \ + E( LEADBAR ) \ + E( INV_LEADBAR ) \ + E( MIDAS_HAND ) \ + E( KEY_ITEM_1 ) \ + E( KEY_ITEM_2 ) \ + E( KEY_ITEM_3 ) \ + E( KEY_ITEM_4 ) \ + E( INV_KEY_ITEM_1 ) \ + E( INV_KEY_ITEM_2 ) \ + E( INV_KEY_ITEM_3 ) \ + E( INV_KEY_ITEM_4 ) \ + E( KEY_HOLE_1 ) \ + E( KEY_HOLE_2 ) \ + E( KEY_HOLE_3 ) \ + E( KEY_HOLE_4 ) \ + E( UNUSED_4 ) \ + E( UNUSED_5 ) \ + E( SCION_PICKUP_QUALOPEC ) \ + E( SCION_PICKUP_DROP ) \ + E( SCION_TARGET ) \ + E( SCION_PICKUP_HOLDER ) \ + E( SCION_HOLDER ) \ + E( UNUSED_6 ) \ + E( UNUSED_7 ) \ + E( INV_SCION ) \ + E( EXPLOSION ) \ + E( UNUSED_8 ) \ + E( WATER_SPLASH ) \ + E( UNUSED_9 ) \ + E( BUBBLE ) \ + E( UNUSED_10 ) \ + E( UNUSED_11 ) \ + E( BLOOD ) \ + E( UNUSED_12 ) \ + E( SMOKE ) \ + E( CENTAUR_STATUE ) \ + E( CABIN ) \ + E( MUTANT_EGG_SMALL ) \ + E( RICOCHET ) \ + E( SPARKLES ) \ + E( MUZZLE_FLASH ) \ + E( UNUSED_13 ) \ + E( UNUSED_14 ) \ + E( VIEW_TARGET ) \ + E( WATERFALL ) \ + E( NATLA_BULLET ) \ + E( MUTANT_BULLET ) \ + E( CENTAUR_BULLET ) \ + E( UNUSED_16 ) \ + E( UNUSED_17 ) \ + E( LAVA_PARTICLE ) \ + E( TRAP_LAVA_EMITTER ) \ + E( FLAME ) \ + E( TRAP_FLAME_EMITTER ) \ + E( TRAP_LAVA ) \ + E( MUTANT_EGG_BIG ) \ + E( BOAT ) \ + E( EARTHQUAKE ) \ + E( UNUSED_18 ) \ + E( UNUSED_19 ) \ + E( UNUSED_20 ) \ + E( UNUSED_21 ) \ + E( UNUSED_22 ) \ + E( LARA_BRAID ) \ + E( GLYPHS ) + +#define DECL_ENUM(v) ITEM_##v, + +enum ItemTypes { + ITEM_TYPES(DECL_ENUM) + ITEM_MAX +}; + +#undef DECL_ENUM + +extern int32 fps; + #ifdef PROFILE extern uint32 dbg_transform; extern uint32 dbg_poly; - extern uint32 dbg_sort; extern uint32 dbg_flush; extern uint32 dbg_vert_count; extern uint32 dbg_poly_count; #endif -#define DIV_TABLE_SIZE 256 -#define FixedInvS(x) ((x < 0) ? -divTable[abs(x)] : divTable[x]) -#define FixedInvU(x) divTable[x] - -extern uint16 divTable[DIV_TABLE_SIZE]; - #define FIXED_SHIFT 14 +#define SND_MAX_DIST (8 * 1024) +#define SND_SHIFT 2 +#define SND_CHANNELS (1 << SND_SHIFT) +#define SND_FIXED_SHIFT 8 +#define SND_VOL_SHIFT 15 +#define SND_PITCH_SHIFT 7 + +#if defined(_WIN32) + #define SND_SAMPLES 1024 + #define SND_OUTPUT_FREQ 22050 + #define SND_SAMPLE_FREQ 22050 + #define SND_ENCODE(x) ((x) + 128) + #define SND_DECODE(x) ((x) - 128) +#elif defined(__GBA__) + #define SND_SAMPLES 176 + #define SND_OUTPUT_FREQ 10512 + #define SND_SAMPLE_FREQ 22050 + #define SND_ENCODE(x) (x) + #define SND_DECODE(x) ((x) - 128) +#endif + +#define MAX_UPDATE_FRAMES 10 + +#define FOV_SHIFT 3 #define MAX_MATRICES 8 #define MAX_MODELS 64 #define MAX_ITEMS 256 @@ -441,53 +844,134 @@ extern uint16 divTable[DIV_TABLE_SIZE]; #define FACE_COLORED 0x4000 #define FACE_CLIPPED 0x2000 #define FACE_FLAT 0x1000 -#define FACE_TEXTURE 0x0FFF +#define FACE_SHADOW 0x0800 +#define FACE_TEXTURE 0x07FF #define NO_ROOM 0xFF #define NO_ITEM 0xFF #define NO_MODEL 0xFF +#define WALL (-127 * 256) -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#define SQR(x) ((x) * (x)) +#define DEG2SHORT (0x10000 / 360) +#define ANGLE_0 0 +#define ANGLE_1 DEG2SHORT +#define ANGLE_45 0x2000 // != 45 * DEG2SHORT !!! +#define ANGLE_90 0x4000 +#define ANGLE_180 0x8000 + +#define X_CLAMP(x, a, b) ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x))) +#define X_MIN(a,b) ((a) < (b) ? (a) : (b)) +#define X_MAX(a,b) ((a) > (b) ? (a) : (b)) +#define X_SQR(x) ((x) * (x)) #define DP43(a,b) ((a).x * (b).x + (a).y * (b).y + (a).z * (b).z + (a).w) #define DP33(a,b) ((a).x * (b).x + (a).y * (b).y + (a).z * (b).z) -#define ITEM_LARA 0 -#define ITEM_WOLF 7 -#define ITEM_BEAR 8 -#define ITEM_BAT 9 -#define ITEM_CRYSTAL 83 +#define DIV_TABLE_SIZE 1024 +#define FixedInvS(x) ((x < 0) ? -divTable[abs(x)] : divTable[x]) +#define FixedInvU(x) divTable[x] +#define OT_SHIFT 4 +#define OT_SIZE ((VIEW_MAX_F >> (FIXED_SHIFT + OT_SHIFT)) + 1) + +/* +#define PERSPECTIVE(x, y, z) {\ + x = (x / (z >> 7));\ + y = (y / (z >> 6));\ +} +*/ + +#ifdef ROTATE90_MODE + #define PERSPECTIVE(x, y, z) {\ + int32 dz = (z >> (FIXED_SHIFT + FOV_SHIFT - 1)) / 3;\ + if (dz >= DIV_TABLE_SIZE) dz = DIV_TABLE_SIZE - 1;\ + int32 d = FixedInvU(dz);\ + x = d * (x >> FIXED_SHIFT) >> 12;\ + y = d * (y >> FIXED_SHIFT) >> 13;\ + } +#else + #define PERSPECTIVE(x, y, z) {\ + int32 dz = z >> (FIXED_SHIFT + FOV_SHIFT);\ + if (dz >= DIV_TABLE_SIZE) dz = DIV_TABLE_SIZE - 1;\ + int32 d = FixedInvU(dz);\ + x = d * (x >> FIXED_SHIFT) >> 12;\ + y = d * (y >> FIXED_SHIFT) >> 12;\ + } +#endif + +extern const uint16 divTable[DIV_TABLE_SIZE]; + +// renderer internal +extern Rect clip; extern vec3i viewPos; extern Matrix matrixStack[MAX_MATRICES]; extern int32 matrixStackIndex; +extern uint32 gVerticesCount; +extern uint32 keys; + +// level data +extern const Anim* anims; +extern const AnimState* animStates; +extern const AnimRange* animRanges; +extern const uint16* animFrames; + +extern int32 modelsCount; +extern Model models[MAX_MODELS]; +extern uint8 modelsMap[MAX_ITEMS]; +extern uint8 staticMeshesMap[MAX_MESHES]; + +template +X_INLINE void swap(T &a, T &b) { + T tmp = a; + a = b; + b = tmp; +} + +X_INLINE int32 classify(const Vertex* v, const Rect &clip) { + return (v->x < clip.x0 ? 1 : 0) | + (v->x > clip.x1 ? 2 : 0) | + (v->y < clip.y0 ? 4 : 0) | + (v->y > clip.y1 ? 8 : 0); +} -int32 clamp(int32 x, int32 a, int32 b); int32 phd_sin(int32 x); int32 phd_cos(int32 x); +int32 phd_atan(int32 x, int32 y); +uint32 phd_sqrt(uint32 x); -void initLUT(); +X_INLINE int32 dot(const vec3i &a, const vec3i &b) +{ + return X_SQR(a.x) + X_SQR(a.y) + X_SQR(a.z); +} + +void anglesFromVector(int32 x, int32 y, int32 z, int16 &angleX, int16 &angleY); + +AnimFrame* getFrame(const Item* item, const Model* model); +const Box& getBoundingBox(const Item* item); Matrix& matrixGet(); void matrixPush(); void matrixPop(); -void matrixTranslate(int32 x, int32 y, int32 z); -void matrixTranslateAbs(int32 x, int32 y, int32 z); +void matrixTranslate(const vec3i &pos); +void matrixTranslateAbs(const vec3i &pos); void matrixRotateX(int32 angle); void matrixRotateY(int32 angle); void matrixRotateZ(int32 angle); void matrixRotateYXZ(int32 angleX, int32 angleY, int32 angleZ); -void matrixFrame(int32 x, int32 y, int32 z, uint16* angles); -void matrixSetView(int32 x, int32 y, int32 z, int32 angleX, int32 angleY); +void matrixFrame(const vec3i &pos, uint16* angles); +void matrixSetView(const vec3i &pos, int32 angleX, int32 angleY); void drawGlyph(const Sprite *sprite, int32 x, int32 y); void clear(); +bool rectIsVisible(const Rect* rect); bool boxIsVisible(const Box* box); +void transform(const vec3s &v, int32 vg); +bool transformBoxRect(const Box* box, Rect* rect); void transformRoom(const RoomInfo::Vertex* vertices, int32 vCount); void transformMesh(const vec3s* vertices, int32 vCount, uint16 intensity); +void faceAddQuad(uint32 flags, const Index* indices, int32 startVertex); +void faceAddTriangle(uint32 flags, const Index* indices, int32 startVertex); void faceAddRoom(const Quad* quads, int32 qCount, const Triangle* triangles, int32 tCount, int32 startVertex); void faceAddMesh(const Quad* rFaces, const Quad* crFaces, const Triangle* tFaces, const Triangle* ctFaces, int32 rCount, int32 crCount, int32 tCount, int32 ctCount, int32 startVertex); @@ -495,6 +979,26 @@ void flush(); void readLevel(const uint8 *data); const RoomInfo::Sector* getSector(int32 roomIndex, int32 x, int32 z); -int32 getRoomIndex(int32 roomIndex, const vec3i* pos); +int32 getRoomIndex(int32 roomIndex, int32 x, int32 y, int32 z); + +X_INLINE void dmaFill(void *dst, uint8 value, uint32 count) +{ + ASSERT((count & 3) == 0); +#ifdef __GBA__ + vu32 v = value; + dma3_fill(dst, v, count); +#else + memset(dst, value, count); +#endif +} + +X_INLINE int16 xRand() +{ +#ifdef __GBA__ + return qran(); +#else + return rand(); +#endif +} #endif diff --git a/src/platform/gba/deploy.sh b/src/platform/gba/deploy.sh index 0f21462..3b29030 100644 --- a/src/platform/gba/deploy.sh +++ b/src/platform/gba/deploy.sh @@ -1,2 +1,2 @@ make -./NO\$GBA.exe C:\\Projects\\OpenLara\\src\\platform\\gba\\OpenLara.elf +./mGBA.exe C:\\Projects\\OpenLara\\src\\platform\\gba\\OpenLara.gba diff --git a/src/platform/gba/draw.h b/src/platform/gba/draw.h new file mode 100644 index 0000000..452ae5e --- /dev/null +++ b/src/platform/gba/draw.h @@ -0,0 +1,474 @@ +#ifndef H_DRAW +#define H_DRAW + +#include "common.h" +#include "item.h" + +int32 seqGlyphs; + +void drawNumber(int32 number, int32 x, int32 y) +{ + const int32 widths[] = { + 12, 8, 10, 10, 10, 10, 10, 10, 10, 10 + }; + + const Sprite *glyphSprites = sprites + spritesSeq[seqGlyphs].sStart; + + while (number > 0) + { + x -= widths[number % 10]; + drawGlyph(glyphSprites + 52 + (number % 10), x, y); + number /= 10; + } +} + +void drawMesh(int16 meshIndex, uint16 intensity) +{ + int32 offset = meshOffsets[meshIndex]; + const uint8* ptr = meshData + offset; + + ptr += 2 * 5; // skip [cx, cy, cz, radius, flags] + + int16 vCount = *(int16*)ptr; ptr += 2; + const vec3s* vertices = (vec3s*)ptr; + ptr += vCount * 3 * sizeof(int16); + + int16 nCount = *(int16*)ptr; ptr += 2; + //const int16* normals = (int16*)ptr; + if (nCount > 0) { // normals + ptr += nCount * 3 * sizeof(int16); + } else { // intensity + ptr += vCount * sizeof(int16); + } + + int16 rCount = *(int16*)ptr; ptr += 2; + Quad* rFaces = (Quad*)ptr; ptr += rCount * sizeof(Quad); + + int16 tCount = *(int16*)ptr; ptr += 2; + Triangle* tFaces = (Triangle*)ptr; ptr += tCount * sizeof(Triangle); + + int16 crCount = *(int16*)ptr; ptr += 2; + Quad* crFaces = (Quad*)ptr; ptr += crCount * sizeof(Quad); + + int16 ctCount = *(int16*)ptr; ptr += 2; + Triangle* ctFaces = (Triangle*)ptr; ptr += ctCount * sizeof(Triangle); + + int32 startVertex = gVerticesCount; + + PROFILE_START(); + transformMesh(vertices, vCount, intensity); + PROFILE_STOP(dbg_transform); + + PROFILE_START(); + faceAddMesh(rFaces, crFaces, tFaces, ctFaces, rCount, crCount, tCount, ctCount, startVertex); + PROFILE_STOP(dbg_poly); +} + +void drawShadow(const Item* item) +{ + const RoomInfo::Sector* sector = getSector(item->room, item->pos.x, item->pos.z); + int32 floor = getFloor(sector, item->pos.x, item->pos.y, item->pos.z); + + if (floor == WALL) + return; + + int32 shadowSize = 160; // TODO per item + + const Box& box = getBoundingBox(item); + int32 x = (box.maxX + box.minX) >> 1; + int32 z = (box.maxZ + box.minZ) >> 1; + int32 sx = (box.maxX - box.minX) * shadowSize >> 10; + int32 sz = (box.maxZ - box.minZ) * shadowSize >> 10; + int32 sx2 = sx << 1; + int32 sz2 = sz << 1; + + int32 startVertex = gVerticesCount; + + int32 y = floor - item->pos.y; + + transform(vec3s(x - sx, y, z + sz2), 4096); + transform(vec3s(x + sx, y, z + sz2), 4096); + transform(vec3s(x + sx2, y, z + sz), 4096); + transform(vec3s(x + sx2, y, z - sz), 4096); + + transform(vec3s(x + sx, y, z - sz2), 4096); + transform(vec3s(x - sx, y, z - sz2), 4096); + transform(vec3s(x - sx2, y, z - sz), 4096); + transform(vec3s(x - sx2, y, z + sz), 4096); + + static const Index indices[] = { + 0, 1, 2, 7, + 7, 2, 3, 6, + 6, 3, 4, 5 + }; + + faceAddQuad(FACE_COLORED | FACE_FLAT | FACE_SHADOW, indices + 0, startVertex); + faceAddQuad(FACE_COLORED | FACE_FLAT | FACE_SHADOW, indices + 4, startVertex); + faceAddQuad(FACE_COLORED | FACE_FLAT | FACE_SHADOW, indices + 8, startVertex); +} + +void drawItem(const Item* item) +{ + int32 modelIndex = modelsMap[item->type]; + if (modelIndex == NO_MODEL) { + return; // TODO sprite items + } + + const Model* model = models + modelIndex; + + if (model->mCount == 1 && meshOffsets[model->mStart] == 0) + return; + + AnimFrame* frame = getFrame(item, model); + uint16* frameAngles = frame->angles + 1; + + matrixPush(); + matrixTranslateAbs(item->pos); + matrixRotateYXZ(item->angleX, item->angleY, item->angleZ); + + int32 intensity = item->intensity; + + if (intensity == 0xFFFF) { + intensity = itemCalcLighting(item, frame->box); + } + + bool isVisible = boxIsVisible(&frame->box); + if (isVisible) + { + // non-aligned access (TODO) + uint32 nodeIndex; + memcpy(&nodeIndex, &model->nodeIndex, sizeof(nodeIndex)); + Node nodes[32]; + memcpy(nodes, nodesPtr + nodeIndex, (model->mCount - 1) * sizeof(Node)); + + const Node* node = nodes; + + matrixFrame(frame->pos, frameAngles); + + drawMesh(model->mStart, intensity); + + for (int32 i = 1; i < model->mCount; i++) + { + if (node->flags & 1) matrixPop(); + if (node->flags & 2) matrixPush(); + + frameAngles += 2; + matrixFrame(node->pos, frameAngles); + + drawMesh(model->mStart + i, intensity); + + node++; + } + } + + matrixPop(); + +// shadow + if (isVisible & item->flags.shadow) { + matrixPush(); + matrixTranslateAbs(item->pos); + matrixRotateY(item->angleY); + + drawShadow(item); + + matrixPop(); + } +} + +void drawRoom(const Room* room) +{ + clip = room->clip; + + int32 startVertex = gVerticesCount; + + matrixPush(); + matrixTranslateAbs(vec3i(room->x, 0, room->z)); + + PROFILE_START(); + transformRoom(room->vertices, room->vCount); + PROFILE_STOP(dbg_transform); + + matrixPop(); + + PROFILE_START(); + faceAddRoom(room->quads, room->qCount, room->triangles, room->tCount, startVertex); + + for (int32 i = 0; i < room->mCount; i++) + { + const RoomInfo::Mesh* mesh = room->meshes + i; + const StaticMesh* staticMesh = staticMeshes + staticMeshesMap[mesh->staticMeshId]; + + if (!(staticMesh->flags & 2)) continue; // invisible + + // TODO align RoomInfo::Mesh (room relative int16?) + vec3i pos; + memcpy(&pos, &mesh->pos, sizeof(pos)); + + matrixPush(); + matrixTranslateAbs(pos); + matrixRotateY(mesh->rotation); + + if (boxIsVisible(&staticMesh->vbox)) { + drawMesh(staticMesh->meshIndex, mesh->intensity); + } + + matrixPop(); + } + + int32 itemIndex = room->firstItem; + while (itemIndex != NO_ITEM) + { + drawItem(items + itemIndex); + itemIndex = items[itemIndex].nextItem; + } + PROFILE_STOP(dbg_poly); + + flush(); +} + +bool checkPortal(int32 roomIndex, const RoomInfo::Portal* portal) +{ + Room &room = rooms[roomIndex]; + + vec3i d; + d.x = portal->v[0].x - camera.viewPos.x + room.x; + d.y = portal->v[0].y - camera.viewPos.y; + d.z = portal->v[0].z - camera.viewPos.z + room.z; + + if (DP33(portal->n, d) >= 0) { + return false; + } + + int32 x0 = room.clip.x1; + int32 y0 = room.clip.y1; + int32 x1 = room.clip.x0; + int32 y1 = room.clip.y0; + + int32 znear = 0, zfar = 0; + + Matrix &m = matrixGet(); + + vec3i pv[4]; + + for (int32 i = 0; i < 4; i++) + { + const vec3s &v = portal->v[i]; + + int32 x = DP43(m[0], v); + int32 y = DP43(m[1], v); + int32 z = DP43(m[2], v); + + pv[i].x = x; + pv[i].y = y; + pv[i].z = z; + + if (z <= VIEW_MIN_F) { + znear++; + continue; + } + + if (z >= VIEW_MAX_F) { + zfar++; + } + + if (z != 0) { + PERSPECTIVE(x, y, z); + + x += FRAME_WIDTH >> 1; + y += FRAME_HEIGHT >> 1; + } else { + x = (x > 0) ? clip.x1 : clip.x0; + y = (y > 0) ? clip.y1 : clip.y0; + } + + if (x < x0) x0 = x; + if (x > x1) x1 = x; + if (y < y0) y0 = y; + if (y > y1) y1 = y; + } + + if (znear == 4 || zfar == 4) return false; + + if (znear) { + vec3i *a = pv; + vec3i *b = pv + 3; + for (int32 i = 0; i < 4; i++) { + if ((a->z < 0) ^ (b->z < 0)) { + if (a->x < 0 && b->x < 0) { + x0 = 0; + } else if (a->x > 0 && b->x > 0) { + x1 = FRAME_WIDTH; + } else { + x0 = 0; + x1 = FRAME_WIDTH; + } + + if (a->y < 0 && b->y < 0) { + y0 = 0; + } else if (a->y > 0 && b->y > 0) { + y1 = FRAME_HEIGHT; + } else { + y0 = 0; + y1 = FRAME_HEIGHT; + } + } + b = a; + a++; + } + } + + if (x0 < room.clip.x0) x0 = room.clip.x0; + if (x1 > room.clip.x1) x1 = room.clip.x1; + if (y0 < room.clip.y0) y0 = room.clip.y0; + if (y1 > room.clip.y1) y1 = room.clip.y1; + + if (x0 >= x1 || y0 >= y1) return false; + + Room &nextRoom = rooms[portal->roomIndex]; + + if (x0 < nextRoom.clip.x0) nextRoom.clip.x0 = x0; + if (x1 > nextRoom.clip.x1) nextRoom.clip.x1 = x1; + if (y0 < nextRoom.clip.y0) nextRoom.clip.y0 = y0; + if (y1 > nextRoom.clip.y1) nextRoom.clip.y1 = y1; + + if (!nextRoom.visible) { + nextRoom.visible = true; + visRooms[visRoomsCount++] = portal->roomIndex; + } + + return true; +} + +void getVisibleRooms(int32 roomIndex) +{ + const Room* room = rooms + roomIndex; + + matrixPush(); + matrixTranslateAbs(vec3i(room->x, 0, room->z)); + + for (int32 i = 0; i < room->pCount; i++) + { + const RoomInfo::Portal* portal = room->portals + i; + + if (checkPortal(roomIndex, portal)) + { + getVisibleRooms(portal->roomIndex); + } + } + + matrixPop(); +} + +void roomReset(Room* room) +{ + room->visible = false; + room->clip = { FRAME_WIDTH, FRAME_HEIGHT, 0, 0 }; +} + +void drawRooms() +{ + rooms[camera.room].clip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT }; + visRoomsCount = 0; + visRooms[visRoomsCount++] = camera.room; + + getVisibleRooms(camera.room); + + while (visRoomsCount--) + { + Room* room = rooms + visRooms[visRoomsCount]; + drawRoom(room); + roomReset(room); + } +} + +#ifdef TEST +void faceAddQuad(uint32 flags, const Index* indices, int32 startVertex); + +extern Vertex gVertices[MAX_VERTICES]; + +void drawTest() { + static Rect testClip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT }; + static int32 testTile = 10; // 707 // 712 + +#ifdef _WIN32 + Sleep(16); +#endif + + int dx = 0; + int dy = 0; + + if (keys & IK_LEFT) dy++; + if (keys & IK_RIGHT) dy--; + if (keys & IK_UP) dx--; + if (keys & IK_DOWN) dx++; + + if (keys & IK_L) { + testClip.x0 += dx; + testClip.y0 += dy; + } + + if (keys & IK_R) { + testClip.x1 += dx; + testClip.y1 += dy; + } + + if (keys & IK_A) { + testTile++; + //Sleep(100); + } + + if (keys & IK_B) { + testTile--; + //Sleep(100); + } + + testTile = (testTile + texturesCount) % texturesCount; + + clip = testClip; + + static int vidx = 0; + + if (keys & IK_SELECT) { + vidx++; + //Sleep(100); + } + + gVertices[(vidx + 0) % 4].x = -25; + gVertices[(vidx + 0) % 4].y = -25; + + gVertices[(vidx + 1) % 4].x = 25; + gVertices[(vidx + 1) % 4].y = -25; + + gVertices[(vidx + 2) % 4].x = 50; + gVertices[(vidx + 2) % 4].y = 25; + + gVertices[(vidx + 3) % 4].x = -50; + gVertices[(vidx + 3) % 4].y = 25; + + for (int i = 0; i < 4; i++) { + gVertices[i].x += FRAME_WIDTH/2; + gVertices[i].y += FRAME_HEIGHT/2; + gVertices[i].z = 100; + gVertices[i].g = 16; + gVertices[i].clip = classify(gVertices + i, clip); + } + gVerticesCount = 4; + + Index indices[] = { 0, 1, 2, 3, 0, 2, 3 }; + + faceAddQuad(testTile, indices, 0); + +#ifdef _WIN32 + for (int y = 0; y < FRAME_HEIGHT; y++) { + for (int x = 0; x < FRAME_WIDTH; x++) { + if (x == clip.x0 || x == clip.x1 - 1 || y == clip.y0 || y == clip.y1 - 1) + ((uint16*)fb)[y * FRAME_WIDTH + x] = 255; + } + } +#endif + + flush(); +} +#endif + +#endif diff --git a/src/platform/gba/enemy.h b/src/platform/gba/enemy.h new file mode 100644 index 0000000..9c2263d --- /dev/null +++ b/src/platform/gba/enemy.h @@ -0,0 +1,19 @@ +#ifndef H_ENEMY +#define H_ENEMY + +struct Bat +{ + +}; + +struct Wolf +{ + +}; + +struct Bear +{ + +}; + +#endif diff --git a/src/platform/gba/game.h b/src/platform/gba/game.h new file mode 100644 index 0000000..80e5c48 --- /dev/null +++ b/src/platform/gba/game.h @@ -0,0 +1,160 @@ +#ifndef H_GAME +#define H_GAME + +#include "common.h" +#include "sound.h" +#include "level.h" +#include "camera.h" +#include "item.h" +#include "lara.h" +#include "draw.h" + +struct Game +{ + void init() + { + loadLevel(LEVEL1_PHD); + } + + void loadLevel(const void* data) + { + camera.init(); + + readLevel((uint8*)LEVEL1_PHD); + + // prepare rooms + for (int32 i = 0; i < roomsCount; i++) + { + roomReset(rooms + i); + } + + // prepare items + for (int32 i = 0; i < itemsCount; i++) + { + Item* item = items + i; + + itemInit(item); + + if (item->room > -1) { + roomItemAdd(item->room, i); + } + + if (item->type == ITEM_LARA) { + camera.item = item; + + // debug + //resetItem(i, 14, vec3i(20215, 6656, 52942), 0); // level 1 (bridge) + //resetItem(i, 26, vec3i(24475, 6912, 83505), 90 * DEG2SHORT); // level 1 (switch timer) + + camera.room = item->room; + } + + // TODO remove + if (item->type == ITEM_LARA || + item->type == ITEM_WOLF || + item->type == ITEM_BEAR || + item->type == ITEM_BAT || + item->type == ITEM_CRYSTAL) + { + activateItem(i); + } + } + + // prepare glyphs + for (int32 i = 0; i < spritesSeqCount; i++) { + if (spritesSeq[i].type == ITEM_GLYPHS) { + seqGlyphs = i; + break; + } + } + } + + void resetItem(int32 itemIndex, int32 roomIndex, const vec3i &pos, int32 angleY) + { + roomItemRemove(itemIndex); + + Item* item = items + itemIndex; + item->pos = vec3i(20215, 6656, 52942); + item->angleY = angleY; + item->health = LARA_MAX_HEALTH; + + roomItemAdd(roomIndex, itemIndex); + } + + void updateItems() + { + curItemIndex = firstActive; + while (curItemIndex != NO_ITEM) + { + Item* item = items + curItemIndex; + + if (item->type == ITEM_LARA) { + Lara* lara = (Lara*)item; + lara->update(); + } else { + itemControl(item); + } + + curItemIndex = item->nextActive; + } + } + + void update(int32 frames) + { + if (frames > MAX_UPDATE_FRAMES) { + frames = MAX_UPDATE_FRAMES; + } + + for (int32 i = 0; i < frames; i++) { + updateItems(); + camera.update(); + } + } + +#ifdef ROTATE90_MODE + int32 TEXT_POSX = FRAME_HEIGHT; +#else + int32 TEXT_POSX = FRAME_WIDTH; +#endif + + void render() + { + clear(); + + #ifdef TEST + #ifdef __GBA__ + VBlankIntrWait(); + #endif + + int32 cycles = 0; + PROFILE_START(); + drawTest(); + PROFILE_STOP(cycles); + + drawNumber(cycles, TEXT_POSX, 32); + #else + #ifdef PROFILE + dbg_transform = 0; + dbg_poly = 0; + dbg_flush = 0; + dbg_vert_count = 0; + dbg_poly_count = 0; + #endif + + drawRooms(); + + #ifdef PROFILE + drawNumber(dbg_transform, TEXT_POSX, 32); + drawNumber(dbg_poly, TEXT_POSX, 48); + drawNumber(dbg_flush, TEXT_POSX, 64); + drawNumber(dbg_vert_count, TEXT_POSX, 84); + drawNumber(dbg_poly_count, TEXT_POSX, 100); + #endif + + #endif + + drawNumber(fps, TEXT_POSX, 16); + } +}; + +#endif diff --git a/src/platform/gba/item.h b/src/platform/gba/item.h new file mode 100644 index 0000000..9d447d2 --- /dev/null +++ b/src/platform/gba/item.h @@ -0,0 +1,384 @@ +#ifndef H_ITEM +#define H_ITEM + +#include "common.h" +#include "sound.h" +#include "camera.h" + +int32 curItemIndex; + +#define GRAVITY 6 + +int16 angleDec(int16 angle, int32 value) { + if (angle < -value) { + return angle + value; + } else if (angle > value) { + return angle - value; + } + return 0; +} + +AnimFrame* getFrame(const Item* item, const Model* model) +{ + const Anim* anim = anims + item->animIndex; + + int32 frameSize = sizeof(AnimFrame) / 2 + model->mCount * 2; + int32 frameIndex = (item->frameIndex - anim->frameBegin) / anim->frameRate; // TODO fixed div? check the range + + return (AnimFrame*)(animFrames + anim->frameOffset / 2 + frameIndex * frameSize); +} + +const Box& getBoundingBox(const Item* item) +{ + const Model* model = models + modelsMap[item->type]; + AnimFrame* frame = getFrame(item, model); + return frame->box; +} + +Sound::Sample* soundPlay(int16 id, const vec3i &pos) +{ + int16 a = soundMap[id]; + + if (a == -1) { + return NULL; + } + + const SoundInfo* b = soundInfos + a; + + if (b->chance && b->chance < xRand()) { + return NULL; + } + + vec3i d = pos - camera.targetPos; + + if (abs(d.x) >= SND_MAX_DIST || abs(d.y) >= SND_MAX_DIST || abs(d.z) >= SND_MAX_DIST) { + return NULL; + } + + int32 volume = b->volume - (phd_sqrt(dot(d, d)) << 2); + + if (b->flags.gain) { + volume -= xRand() >> 2; + } + + if (volume <= 0) { + return NULL; + } + + volume = X_MIN(volume, 0x7FFF); + + int32 pitch = 128; + + if (b->flags.pitch) { + pitch += ((xRand() * 13) >> 14) - 13; + } + + int32 index = b->index; + if (b->flags.count > 1) { + index += (xRand() * b->flags.count) >> 15; + } + + const uint8 *data = soundData + soundOffsets[index]; + + int32 size; + memcpy(&size, data + 40, 4); // TODO preprocess and remove wave header + data += 44; + + return sound.play(data, size, volume, pitch, b->flags.mode); +} + +void move(Item* item, const Anim* anim) +{ + int32 speed = anim->speed; + + if (item->flags.gravity) + { + speed += anim->accel * (item->frameIndex - anim->frameBegin - 1); + item->hSpeed -= speed >> 16; + speed += anim->accel; + item->hSpeed += speed >> 16; + + item->vSpeed += (item->vSpeed < 128) ? GRAVITY : 1; + + item->pos.y += item->vSpeed; + } else { + speed += anim->accel * (item->frameIndex - anim->frameBegin); + + item->hSpeed = speed >> 16; + } + + item->pos.x += phd_sin(item->moveAngle) * item->hSpeed >> FIXED_SHIFT; + item->pos.z += phd_cos(item->moveAngle) * item->hSpeed >> FIXED_SHIFT; +} + +const Anim* animSet(Item* item, int32 animIndex, bool resetState, int32 frameOffset = 0) { + const Anim* anim = anims + animIndex; + + item->animIndex = animIndex; + item->frameIndex = anim->frameBegin + frameOffset; + + if (resetState) { + item->state = item->goalState = anim->state; + } + + return anim; +} + +const Anim* animChange(Item* item, const Anim* anim) +{ + if (!anim->statesCount || item->goalState == item->state) + return anim; + + const AnimState* animState = animStates + anim->statesStart; + + for (int32 i = 0; i < anim->statesCount; i++) + { + if (item->goalState == animState->state) + { + const AnimRange* animRange = animRanges + animState->rangesStart; + + for (int32 j = 0; j < animState->rangesCount; j++) + { + if ((item->frameIndex >= animRange->frameBegin) && (item->frameIndex <= animRange->frameEnd)) + { + if ((item->type != ITEM_LARA) && (item->nextState == animState->state)) { + item->nextState = 0; + } + + item->frameIndex = animRange->nextFrameIndex; + item->animIndex = animRange->nextAnimIndex; + anim = anims + animRange->nextAnimIndex; + item->state = anim->state; + return anim; + } + animRange++; + } + } + animState++; + } + + return anim; +} + +void animCmd(bool fx, Item* item, const Anim* anim) +{ + if (!anim->commandsCount) return; + + const int16 *ptr = animCommands + anim->commandsStart; + + for (int32 i = 0; i < anim->commandsCount; i++) + { + int32 cmd = *ptr++; + + switch (cmd) { + case ANIM_CMD_NONE: + break; + case ANIM_CMD_OFFSET: + { + if (!fx) + { + int32 s = phd_sin(item->moveAngle); + int32 c = phd_cos(item->moveAngle); + int32 x = ptr[0]; + int32 y = ptr[1]; + int32 z = ptr[2]; + item->pos.x += (c * x + s * z) >> FIXED_SHIFT; + item->pos.y += y; + item->pos.z += (c * z - s * x) >> FIXED_SHIFT; + } + ptr += 3; + break; + } + case ANIM_CMD_JUMP: + if (!fx) + { + if (item->vSpeedHack) { + item->vSpeed = item->vSpeedHack; + item->vSpeedHack = 0; + } else { + item->vSpeed = ptr[0]; + } + item->hSpeed = ptr[1]; + item->flags.gravity = true; + } + ptr += 2; + break; + case ANIM_CMD_EMPTY: + break; + case ANIM_CMD_KILL: + break; + case ANIM_CMD_SOUND: + if (fx && item->frameIndex == ptr[0]) + { + soundPlay(ptr[1] & 0x03FFF, item->pos); + } + ptr += 2; + break; + case ANIM_CMD_EFFECT: + if (fx && item->frameIndex == ptr[0]) { + switch (ptr[1]) { + case FX_ROTATE_180 : item->angleY -= ANGLE_180; break; + /* + case FX_FLOOR_SHAKE : ASSERT(false); + case FX_LARA_NORMAL : animation.setAnim(ANIM_STAND); break; + case FX_LARA_BUBBLES : doBubbles(); break; + case FX_LARA_HANDSFREE : break;//meshSwap(1, level->extra.weapons[wpnCurrent], BODY_LEG_L1 | BODY_LEG_R1); break; + case FX_DRAW_RIGHTGUN : drawGun(true); break; + case FX_DRAW_LEFTGUN : drawGun(false); break; + case FX_SHOT_RIGHTGUN : game->addMuzzleFlash(this, LARA_RGUN_JOINT, LARA_RGUN_OFFSET, 1 + camera->cameraIndex); break; + case FX_SHOT_LEFTGUN : game->addMuzzleFlash(this, LARA_LGUN_JOINT, LARA_LGUN_OFFSET, 1 + camera->cameraIndex); break; + case FX_MESH_SWAP_1 : + case FX_MESH_SWAP_2 : + case FX_MESH_SWAP_3 : Character::cmdEffect(fx); + case 26 : break; // TODO TR2 reset_hair + case 32 : break; // TODO TR3 footprint + default : LOG("unknown effect command %d (anim %d)\n", fx, animation.index); ASSERT(false); + */ + default : ; + } + } + ptr += 2; + break; + } + } +} + +void animUpdate(Item* item) +{ + if (modelsMap[item->type] == NO_MODEL) + return; + + const Anim* anim = anims + item->animIndex; + + item->frameIndex++; + + anim = animChange(item, anim); + + if (item->frameIndex > anim->frameEnd) + { + animCmd(false, item, anim); + + item->frameIndex = anim->nextFrameIndex; + item->animIndex = anim->nextAnimIndex; + anim = anims + anim->nextAnimIndex; + item->state = anim->state; + } + + animCmd(true, item, anim); + + move(item, anim); +} + +int32 calcLight(const vec3i &pos, int32 roomIndex) +{ + Room* room = rooms + roomIndex; + + if (!room->lCount) { + return room->ambient; + } + + int32 ambient = 8191 - room->ambient; + int32 intensity = 0; + + for (int i = 0; i < room->lCount; i++) + { + const RoomInfo::Light* light = room->lights + i; + + // TODO preprocess align + vec3i lpos; + int32 lradius; + memcpy(&lpos, &light->pos[0], sizeof(lpos)); + memcpy(&lradius, &light->radius[0], sizeof(lradius)); + + vec3i d = pos - lpos; + int32 dist = dot(d, d) >> 12; + int32 att = X_SQR(lradius) >> 12; + + int32 lum = (light->intensity * att) / (dist + att) + ambient; + + if (lum > intensity) + { + intensity = lum; + } + } + + return 8191 - ((intensity + ambient) >> 1); +} + +int32 itemCalcLighting(const Item* item, const Box& box) +{ + matrixPush(); + Matrix &m = matrixGet(); + m[0][3] = m[1][3] = m[2][3] = 0; + matrixRotateYXZ(item->angleX, item->angleY, item->angleZ); + + vec3i v((box.maxX + box.minX) >> 1, + (box.maxY + box.minY) >> 1, + (box.maxZ + box.minZ) >> 1); + + matrixTranslate(v); + + v = vec3i(m[0][3] >> FIXED_SHIFT, + m[1][3] >> FIXED_SHIFT, + m[2][3] >> FIXED_SHIFT) + item->pos; + matrixPop(); + + return calcLight(v, item->room); +} + +void itemControl(Item* item) +{ + animUpdate(item); +} + +void itemInit(Item* item) +{ + item->angleX = 0; + item->angleZ = 0; + item->vSpeed = 0; + item->hSpeed = 0; + item->nextItem = NO_ITEM; + item->nextActive = NO_ITEM; + item->animIndex = models[modelsMap[item->type]].animIndex; + item->frameIndex = anims[item->animIndex].frameBegin; + item->state = anims[item->animIndex].state; + item->nextState = item->state; + item->goalState = item->state; + item->flags.gravity = false; + item->flags.shadow = true; + + switch (item->type) { + case ITEM_LARA : item->health = 1000; break; + case ITEM_DOPPELGANGER : item->health = 1000; break; + case ITEM_WOLF : item->health = 6; break; + case ITEM_BEAR : item->health = 20; break; + case ITEM_BAT : item->health = 1; break; + case ITEM_CROCODILE_LAND : item->health = 20; break; + case ITEM_CROCODILE_WATER : item->health = 20; break; + case ITEM_LION_MALE : item->health = 30; break; + case ITEM_LION_FEMALE : item->health = 25; break; + case ITEM_PUMA : item->health = 45; break; + case ITEM_GORILLA : item->health = 22; break; + case ITEM_RAT_LAND : item->health = 5; break; + case ITEM_RAT_WATER : item->health = 5; break; + case ITEM_REX : item->health = 100; break; + case ITEM_RAPTOR : item->health = 20; break; + case ITEM_MUTANT_1 : item->health = 50; break; + case ITEM_MUTANT_2 : item->health = 50; break; + case ITEM_MUTANT_3 : item->health = 50; break; + case ITEM_CENTAUR : item->health = 120; break; + case ITEM_MUMMY : item->health = 18; break; + case ITEM_LARSON : item->health = 50; break; + case ITEM_PIERRE : item->health = 70; break; + case ITEM_SKATEBOY : item->health = 125; break; + case ITEM_COWBOY : item->health = 150; break; + case ITEM_MR_T : item->health = 200; break; + case ITEM_NATLA : item->health = 400; break; + case ITEM_GIANT_MUTANT : item->health = 500; break; + default : + item->health = 0; + item->flags.shadow = false; + } +} + +#endif diff --git a/src/platform/gba/lara.h b/src/platform/gba/lara.h new file mode 100644 index 0000000..75b239c --- /dev/null +++ b/src/platform/gba/lara.h @@ -0,0 +1,2044 @@ +#ifndef H_LARA +#define H_LARA + +#include "common.h" +#include "item.h" +#include "collision.h" +#include "camera.h" + +uint32 input; + +enum { + IN_LEFT = (1 << 1), + IN_RIGHT = (1 << 2), + IN_UP = (1 << 3), + IN_DOWN = (1 << 4), + IN_JUMP = (1 << 5), + IN_WALK = (1 << 6), + IN_ACTION = (1 << 7), + IN_WEAPON = (1 << 8), + IN_LOOK = (1 << 9), +}; + +#define LARA_STATES(E) \ + E( WALK ) \ + E( RUN ) \ + E( STOP ) \ + E( FORWARD_JUMP ) \ + E( POSE ) \ + E( FAST_BACK ) \ + E( TURN_RIGHT ) \ + E( TURN_LEFT ) \ + E( DEATH ) \ + E( FALL ) \ + E( HANG ) \ + E( REACH ) \ + E( SPLAT ) \ + E( TREAD ) \ + E( LAND ) \ + E( COMPRESS ) \ + E( BACK ) \ + E( SWIM ) \ + E( GLIDE ) \ + E( HANG_UP ) \ + E( FAST_TURN ) \ + E( STEP_RIGHT ) \ + E( STEP_LEFT ) \ + E( ROLL_END ) \ + E( SLIDE ) \ + E( BACK_JUMP ) \ + E( RIGHT_JUMP ) \ + E( LEFT_JUMP ) \ + E( UP_JUMP ) \ + E( FALL_BACK ) \ + E( HANG_LEFT ) \ + E( HANG_RIGHT ) \ + E( SLIDE_BACK ) \ + E( SURF_TREAD ) \ + E( SURF_SWIM ) \ + E( DIVE ) \ + E( PUSH_BLOCK ) \ + E( PULL_BLOCK ) \ + E( PUSH_PULL_READY ) \ + E( PICK_UP ) \ + E( SWITCH_DOWN ) \ + E( SWITCH_UP ) \ + E( USE_KEY ) \ + E( USE_PUZZLE ) \ + E( UW_DEATH ) \ + E( ROLL_START ) \ + E( SPECIAL ) \ + E( SURF_BACK ) \ + E( SURF_LEFT ) \ + E( SURF_RIGHT ) \ + E( MIDAS_USE ) \ + E( MIDAS_DEATH ) \ + E( SWAN_DIVE ) \ + E( FAST_DIVE ) \ + E( HANDSTAND ) \ + E( WATER_OUT ) + +#define DECL_ENUM(v) v, +#define DECL_S_HANDLER(v) &Lara::s_##v, +#define DECL_C_HANDLER(v) &Lara::c_##v, +#define S_HANDLER(state) void s_##state() +#define C_HANDLER(state) void c_##state() + +#define LARA_HANG_SLANT 60 +#define LARA_HANG_OFFSET 724 +#define LARA_HEIGHT 762 +#define LARA_HEIGHT_JUMP 870 // LARA_HEIGHT + hands up +#define LARA_HEIGHT_UW 400 +#define LARA_HEIGHT_SURF 700 +#define LARA_RADIUS 100 +#define LARA_RADIUS_WATER 300 +#define LARA_RADIUS_CLIMB 220 +#define LARA_MAX_HEALTH 1000 +#define LARA_MAX_OXYGEN 1800 +#define LARA_HEIGHT 762 +#define LARA_TURN_ACCEL (2 * DEG2SHORT + DEG2SHORT / 4) +#define LARA_TURN_JUMP (3 * DEG2SHORT) +#define LARA_TURN_VERY_SLOW (2 * DEG2SHORT) +#define LARA_TURN_SLOW (4 * DEG2SHORT) +#define LARA_TURN_MED (6 * DEG2SHORT) +#define LARA_TURN_FAST (8 * DEG2SHORT) +#define LARA_TILT_ACCEL (DEG2SHORT + DEG2SHORT / 2) +#define LARA_TILT_MAX (11 * DEG2SHORT) +#define LARA_STEP_HEIGHT 384 +#define LARA_SMASH_HEIGHT 640 +#define LARA_FLOAT_UP_SPEED 5 +#define LARA_SWIM_FRICTION 6 +#define LARA_SWIM_ACCEL 8 +#define LARA_SWIM_SPEED_MIN 133 +#define LARA_SWIM_SPEED_MAX 200 +#define LARA_SWIM_TIMER 10 // 1/3 sec +#define LARA_SURF_FRICTION 4 +#define LARA_SURF_ACCEL 8 +#define LARA_SURF_SPEED_MAX 60 +#define LARA_DIVE_SPEED 80 + +struct Lara : Item +{ + enum State { + LARA_STATES(DECL_ENUM) + X_MAX + }; + + enum { + ANIM_RUN = 0, + + ANIM_STAND_LEFT = 2, + ANIM_STAND_RIGHT = 3, + + ANIM_RUN_START = 6, + + ANIM_STAND = 11, + + ANIM_LANDING = 24, + + ANIM_CLIMB_JUMP = 26, + + ANIM_FALL_HANG = 28, + + ANIM_SMASH_JUMP = 32, + + ANIM_FALL_FORTH = 34, + + ANIM_BACK = 41, + ANIM_CLIMB_3 = 42, + + ANIM_CLIMB_2 = 50, + + ANIM_SMASH_RUN_LEFT = 53, + ANIM_SMASH_RUN_RIGHT = 54, + ANIM_RUN_ASCEND_LEFT = 55, + ANIM_RUN_ASCEND_RIGHT = 56, + ANIM_WALK_ASCEND_LEFT = 57, + ANIM_WALK_ASCEND_RIGHT = 58, + ANIM_WALK_DESCEND_RIGHT = 59, + ANIM_WALK_DESCEND_LEFT = 60, + ANIM_BACK_DESCEND_LEFT = 61, + ANIM_BACK_DESCEND_RIGHT = 62, + + ANIM_SLIDE_FORTH = 70, + + ANIM_UW_GLIDE = 87, + + ANIM_FALL_BACK = 93, + + ANIM_HANG = 96, + + ANIM_STAND_NORMAL = 103, + + ANIM_SLIDE_BACK = 104, + + ANIM_UNDERWATER = 108, + + ANIM_WATER_OUT = 111, + ANIM_WATER_FALL = 112, + ANIM_SURF = 114, + ANIM_SURF_SWIM = 116, + ANIM_SURF_DIVE = 119, + + ANIM_HIT_FRONT = 125, + ANIM_HIT_BACK = 126, + ANIM_HIT_LEFT = 127, + ANIM_HIT_RIGHT = 128, + + ANIM_DEATH_BOULDER = 139, + ANIM_SURF_BACK = 140, + + ANIM_SURF_LEFT = 143, + ANIM_SURF_RIGHT = 144, + + ANIM_STAND_ROLL_BEGIN = 146, + ANIM_STAND_ROLL_END = 147, + + ANIM_DEATH_SPIKES = 149, + ANIM_HANG_SWING = 150, + + ANIM_CLIMB_START = 164, + + ANIM_WADE_SWIM = 176, + ANIM_WADE = 177, + ANIM_WADE_RUN_LEFT = 178, + ANIM_WADE_RUN_RIGHT = 179, + ANIM_WADE_STAND = 186, + ANIM_WADE_ASCEND = 190, + ANIM_SURF_OUT = 191, + ANIM_SWIM_STAND = 192, + ANIM_SURF_STAND = 193, + + ANIM_SWITCH_BIG_DOWN = 195, + ANIM_SWITCH_BIG_UP = 196, + ANIM_PUSH_BUTTON = 197, + + ANIM_UW_ROLL = 203, + }; + + typedef void (Lara::*Handler)(); + + static const Handler sHandlers[X_MAX]; + static const Handler cHandlers[X_MAX]; + + void updateState() + { + (this->*sHandlers[state])(); + } + + void updateCollision() + { + (this->*cHandlers[state])(); + } + + void startScreaming() + { + // TODO + } + + void stopScreaming() + { + // TODO + } + +// common + bool alignAngle(int16 &angle, int16 threshold) + { + if (angle >= -threshold && angle <= threshold) { + angle = 0; + } else if (angle >= ANGLE_90 - threshold && angle <= ANGLE_90 + threshold) { + angle = ANGLE_90; + } else if (angle >= -ANGLE_90 - threshold && angle <= -ANGLE_90 + threshold) { + angle = -ANGLE_90; + } else if (angle >= (ANGLE_180 - 1) - threshold || angle <= -(ANGLE_180 - 1) + threshold) { + angle = -ANGLE_180; + } + + return (angle & (ANGLE_90 - 1)) > 0; + } + + void alignWall() + { + int x = (~1023) & pos.x; + int z = (~1023) & pos.z; + + switch (angleY) + { + case ANGLE_0 : pos.z = z + 1024 + LARA_RADIUS; break; + case ANGLE_90 : pos.x = x + 1024 + LARA_RADIUS; break; + case -ANGLE_90 : pos.x = x - LARA_RADIUS; break; + case -ANGLE_180 : pos.z = z - LARA_RADIUS; break; + default : ASSERT(false); + } + } + + int16 getFront(int16 angle) + { + angle += angleY; + + int32 x = pos.x + (phd_sin(angle) >> (FIXED_SHIFT - 8)); + int32 y = pos.y - LARA_HEIGHT; + int32 z = pos.z + (phd_cos(angle) >> (FIXED_SHIFT - 8)); + + int32 roomIndex = getRoomIndex(room, x, y, z); + const RoomInfo::Sector* sector = getSector(roomIndex, x, z); + int16 floor = getFloor(sector, x, y, z); + + if (floor != WALL) { + floor -= pos.y; + } + + return floor; + } + + const Box& getBounds() + { + int32 modelIndex = modelsMap[type]; + ASSERT(modelIndex != NO_MODEL); + + const Model* model = models + modelIndex; + + AnimFrame* frame = getFrame(this, model); + + return frame->box; + } + + bool checkDeath() + { + return health <= 0; + } + + void updateRoom(int32 itemIndex, int32 height) + { + int16 nextRoom = getRoomIndex(room, pos.x, pos.y + height, pos.z); + if (room != nextRoom) { + roomItemRemove(itemIndex); + roomItemAdd(nextRoom, itemIndex); + room = nextRoom; + } + } + + int32 getWaterLevel(int32 roomIndex) + { + /* + RoomInfo::Sector* sector = getWaterLevelSector(roomIndex, pos.x, pos.z); + + if (sector) { + return sector->ceiling * 256; + } + */ + return WALL; + } + + int32 getWaterDepth(int32 roomIndex) + { + /* + RoomInfo::Sector* sector = getWaterLevelSector(roomIndex, pos.x, pos.z); + + if (sector) { + return getFloor(sector, pos.x, pos.y, pos.z) - (sector->ceiling * 256); + } + */ + return WALL; + } + +// state control + bool s_checkFront(int16 angle) + { + return true; // TODO + } + + void s_ignoreEnemy() + { + cinfo.enemyPush = false; + cinfo.enemyHit = false; + } + + void s_rotate(int32 maxSpeed, int32 tilt) + { + tilt *= LARA_TILT_ACCEL; + + if (input & IN_LEFT) { + turnSpeed = X_MAX(turnSpeed - LARA_TURN_ACCEL, -maxSpeed); + angleZ = X_MAX(angleZ - tilt, -LARA_TILT_MAX); + } else if (input & IN_RIGHT) { + turnSpeed = X_MIN(turnSpeed + LARA_TURN_ACCEL, maxSpeed); + angleZ = X_MIN(angleZ + tilt, LARA_TILT_MAX); + } + } + + bool s_checkFall() + { + if (vSpeed > 131) + { + if (state == SWAN_DIVE) { + goalState = FAST_DIVE; + } else { + goalState = FALL; + } + return true; + } + return false; + } + + void s_checkWalk(int32 stopState) + { + if ((input & IN_UP) && s_checkFront(ANGLE_0)) { + if (input & IN_WALK) { + goalState = WALK; + } else { + goalState = RUN; + } + } else { + goalState = stopState; + } + } + + bool s_checkRoll() + { + if ((waterState != WATER_STATE_ABOVE) && (waterState != WATER_STATE_UNDER)) { + return false; + } + + bool roll = (input & (IN_UP | IN_DOWN)) == (IN_UP | IN_DOWN); + + if ((waterState == WATER_STATE_ABOVE) && roll) + { + if ((state == RUN) || (state == STOP)) + { + animSet(this, ANIM_STAND_ROLL_BEGIN, true, 2); + goalState = STOP; + return true; + } + } + + return false; + } + + S_HANDLER( WALK ) + { + if (checkDeath()) { + goalState = STOP; + return; + } + + s_rotate(LARA_TURN_SLOW, 0); + + s_checkWalk(STOP); + } + + S_HANDLER( RUN ) + { + if (checkDeath()) { + goalState = DEATH; + return; + } + + if (s_checkRoll()) { + return; + } + + s_rotate(LARA_TURN_FAST, 1); + + if ((input & IN_JUMP) && !flags.gravity) { + goalState = FORWARD_JUMP; + } else { + s_checkWalk(STOP); + } + } + + S_HANDLER( STOP ) + { + if (checkDeath()) + { + nextState = DEATH; + return; + } + + if (s_checkRoll()) { + return; + } + + goalState = STOP; + /* + if ((input & (IN_UP | IN_ACTION)) == (IN_UP | IN_ACTION)) { + c_angle(ANGLE_0); + cinfo.radius = LARA_RADIUS + 4; + c_default(); + cinfo.radius = LARA_RADIUS; + if (c_checkClimbUp()) { + return; + } + } + */ + if (input & IN_WALK) { + if ((input & IN_LEFT) && s_checkFront(-ANGLE_90)) { + goalState = STEP_LEFT; + } else if ((input & IN_RIGHT) && s_checkFront(ANGLE_90)) { + goalState = STEP_RIGHT; + } + } else { + if (input & IN_LEFT) { + goalState = TURN_LEFT; + } else if (input & IN_RIGHT) { + goalState = TURN_RIGHT; + } + } + + if (input & IN_JUMP) { + goalState = COMPRESS; + } else if ((input & IN_UP) && s_checkFront(ANGLE_0)) { + if (input & IN_WALK) { + s_WALK(); + } else { + s_RUN(); + } + } else if ((input & IN_DOWN) && s_checkFront(-ANGLE_180)) { + if (input & IN_WALK) { + s_BACK(); + } else { + goalState = FAST_BACK; + } + } + } + + + S_HANDLER( FORWARD_JUMP ) + { + if (goalState == SWAN_DIVE || + goalState == REACH) + { + goalState = FORWARD_JUMP; + } + + if (goalState != DEATH && + goalState != STOP && + goalState != RUN) + { + if (input & IN_ACTION) + { + goalState = REACH; + } + + if (input & IN_WALK) + { + goalState = SWAN_DIVE; + } + + s_checkRoll(); + s_checkFall(); + } + + s_rotate(LARA_TURN_JUMP, 0); + } + + S_HANDLER( POSE ) + { + // empty + } + + S_HANDLER( FAST_BACK ) + { + s_rotate(LARA_TURN_MED, 0); + goalState = STOP; + } + + S_HANDLER( TURN_RIGHT ) + { + if (checkDeath() || (input & IN_LOOK)) + { + goalState = STOP; + return; + } + + turnSpeed += LARA_TURN_ACCEL; + + if (turnSpeed > LARA_TURN_SLOW) + { + goalState = FAST_TURN; + } + + s_checkWalk((input & IN_RIGHT) ? goalState : STOP); + } + + S_HANDLER( TURN_LEFT ) + { + if (checkDeath() || (input & IN_LOOK)) + { + goalState = STOP; + return; + } + + turnSpeed -= LARA_TURN_ACCEL; + + if (turnSpeed < -LARA_TURN_SLOW) + { + goalState = FAST_TURN; + } + + s_checkWalk((input & IN_LEFT) ? goalState : STOP); + } + + S_HANDLER( DEATH ) + { + s_ignoreEnemy(); + } + + S_HANDLER( FALL ) + { + hSpeed = (hSpeed * 95) / 100; + if (vSpeed >= 154) + { + startScreaming(); + } + } + + S_HANDLER( HANG ) + { + camera.targetAngleX = -60 * DEG2SHORT; + + s_ignoreEnemy(); + if (input & IN_LEFT) { + goalState = HANG_LEFT; + } else if (input & IN_RIGHT) { + goalState = HANG_RIGHT; + } + } + + S_HANDLER( REACH ) + { + camera.targetAngleY = 85 * DEG2SHORT; + + s_checkFall(); + } + + S_HANDLER( SPLAT ) + { + // empty + } + + S_HANDLER( TREAD ) {} // TODO + + S_HANDLER( LAND ) + { + // empty + } + + S_HANDLER( COMPRESS ) + { + if ((input & IN_UP) && getFront(ANGLE_0) >= -LARA_STEP_HEIGHT) { + goalState = FORWARD_JUMP; + } else if ((input & IN_LEFT) && getFront(-ANGLE_90) >= -LARA_STEP_HEIGHT) { + goalState = LEFT_JUMP; + } else if ((input & IN_RIGHT) && getFront(ANGLE_90) >= -LARA_STEP_HEIGHT) { + goalState = RIGHT_JUMP; + } else if ((input & IN_DOWN) && getFront(-ANGLE_180) >= -LARA_STEP_HEIGHT) { + goalState = BACK_JUMP; + } + s_checkFall(); + } + + S_HANDLER( BACK ) + { + if (checkDeath()) { + goalState = STOP; + return; + } + + if ((input & (IN_WALK | IN_DOWN)) != (IN_WALK | IN_DOWN)) { + goalState = STOP; + } else { + goalState = BACK; + } + + s_rotate(LARA_TURN_SLOW, 0); + } + + S_HANDLER( SWIM ) {} // TODO + S_HANDLER( GLIDE ) {} // TODO + + S_HANDLER( HANG_UP ) + { + s_ignoreEnemy(); + } + + S_HANDLER( FAST_TURN ) + { + if (checkDeath() || (input & IN_LOOK)) + { + goalState = STOP; + return; + } + + if (turnSpeed < 0) { + turnSpeed = -LARA_TURN_FAST; + if (!(input & IN_LEFT)) { + goalState = STOP; + } + } else { + turnSpeed = LARA_TURN_FAST; + if (!(input & IN_RIGHT)) { + goalState = STOP; + } + } + } + + S_HANDLER( STEP_RIGHT ) + { + if (checkDeath() || (input & (IN_WALK | IN_LEFT)) != (IN_WALK | IN_LEFT)) + { + goalState = STOP; + } + } + + S_HANDLER( STEP_LEFT ) + { + if (checkDeath() || (input & (IN_WALK | IN_LEFT)) != (IN_WALK | IN_LEFT)) + { + goalState = STOP; + } + } + + S_HANDLER( ROLL_END ) + { + // empty + } + + S_HANDLER( SLIDE ) + { + camera.targetAngleX = -45 * DEG2SHORT; + + if (input & IN_JUMP) + { + goalState = FORWARD_JUMP; + } + } + + S_HANDLER( BACK_JUMP ) + { + camera.targetAngleY = 135 * DEG2SHORT; + + if (s_checkFall()) return; + + if (goalState == RUN) { + goalState = STOP; + } + } + + S_HANDLER( RIGHT_JUMP ) + { + s_checkFall(); + } + + S_HANDLER( LEFT_JUMP ) + { + s_checkFall(); + } + + S_HANDLER( UP_JUMP ) + { + s_checkFall(); + } + + S_HANDLER( FALL_BACK ) + { + s_checkFall(); + + if (input & IN_ACTION) + { + goalState = REACH; + } + } + + S_HANDLER( HANG_LEFT ) + { + camera.targetAngleX = -60 * DEG2SHORT; + camera.targetAngleY = -10 * DEG2SHORT; + + s_ignoreEnemy(); + + if (!(input & IN_LEFT)) + { + goalState = HANG; + } + } + + S_HANDLER( HANG_RIGHT ) + { + camera.targetAngleX = -60 * DEG2SHORT; + camera.targetAngleY = 10 * DEG2SHORT; + + s_ignoreEnemy(); + + if (!(input & IN_RIGHT)) + { + goalState = HANG; + } + } + + S_HANDLER( SLIDE_BACK ) + { + if (input & IN_JUMP) + { + goalState = BACK_JUMP; + } + } + + S_HANDLER( SURF_TREAD ) {} // TODO + S_HANDLER( SURF_SWIM ) {} // TODO + S_HANDLER( DIVE ) {} // TODO + + S_HANDLER( PUSH_BLOCK ) + { + camera.targetAngleX = -25 * DEG2SHORT; + camera.targetAngleY = 35 * DEG2SHORT; + + s_ignoreEnemy(); + } + + S_HANDLER( PULL_BLOCK ) + { + camera.targetAngleX = -25 * DEG2SHORT; + camera.targetAngleY = 35 * DEG2SHORT; + + s_ignoreEnemy(); + } + + S_HANDLER( PUSH_PULL_READY ) + { + camera.targetAngleY = 75 * DEG2SHORT; + + s_ignoreEnemy(); + + if (!(input & IN_ACTION)) + { + goalState = STOP; + } + } + + S_HANDLER( PICK_UP ) + { + camera.targetAngleX = -15 * DEG2SHORT; + camera.targetAngleY = -130 * DEG2SHORT; + camera.targetDist = 1024; + + s_ignoreEnemy(); + } + + S_HANDLER( SWITCH_DOWN ) + { + camera.targetAngleX = -25 * DEG2SHORT; + camera.targetAngleY = 80 * DEG2SHORT; + camera.targetDist = 1024; + + s_ignoreEnemy(); + } + + S_HANDLER( SWITCH_UP ) + { + camera.targetAngleX = -25 * DEG2SHORT; + camera.targetAngleY = 80 * DEG2SHORT; + camera.targetDist = 1024; + + s_ignoreEnemy(); + } + + S_HANDLER( USE_KEY ) + { + camera.targetAngleX = -25 * DEG2SHORT; + camera.targetAngleY = -80 * DEG2SHORT; + camera.targetDist = 1024; + + s_ignoreEnemy(); + } + + S_HANDLER( USE_PUZZLE ) + { + camera.targetAngleX = -25 * DEG2SHORT; + camera.targetAngleY = -80 * DEG2SHORT; + camera.targetDist = 1024; + + s_ignoreEnemy(); + } + + S_HANDLER( UW_DEATH ) + { + vSpeed = X_MAX(vSpeed - LARA_SWIM_ACCEL, 0); + angleX = angleDec(angleX, 2 * DEG2SHORT); + } + + S_HANDLER( ROLL_START ) + { + // empty + } + + S_HANDLER( SPECIAL ) + { + camera.targetAngleX = -25 * DEG2SHORT; + camera.targetAngleY = 170 * DEG2SHORT; + } + + S_HANDLER( SURF_BACK ) {} // TODO + S_HANDLER( SURF_LEFT ) {} // TODO + S_HANDLER( SURF_RIGHT ) {} // TODO + + S_HANDLER( MIDAS_USE ) + { + s_ignoreEnemy(); + } + + S_HANDLER( MIDAS_DEATH ) + { + s_ignoreEnemy(); + flags.gravity = false; + } + + S_HANDLER( SWAN_DIVE ) {} // TODO + S_HANDLER( FAST_DIVE ) {} // TODO + + S_HANDLER( HANDSTAND ) + { + s_ignoreEnemy(); + } + + S_HANDLER( WATER_OUT ) + { + s_ignoreEnemy(); + } + +// collision control + void c_applyOffset() { + pos.x += cinfo.offset.x; + pos.y += cinfo.offset.y; + pos.z += cinfo.offset.z; + cinfo.offset = vec3i(0, 0, 0); + } + + void c_angle(int32 angleDelta) { + moveAngle = angleY + angleDelta; + + cinfo.angle = moveAngle; + cinfo.quadrant = uint16(cinfo.angle + ANGLE_45) / ANGLE_90; + } + + bool c_checkCeiling() { + if (cinfo.type != CT_CEILING && cinfo.type != CT_FLOOR_CEILING) { + return false; + } + + animSet(this, ANIM_STAND, true); + goalState = state; + hSpeed = 0; + vSpeed = 0; + flags.gravity = false; + pos = cinfo.pos; + + return true; + } + + bool c_checkWall() { + if (cinfo.type == CT_FRONT || cinfo.type == CT_FRONT_CEILING) { + c_applyOffset(); + goalState = STOP; + hSpeed = 0; + flags.gravity = false; + return true; + } + + if (cinfo.type == CT_LEFT) { + c_applyOffset(); + angleY += 5 * DEG2SHORT; + angleZ = angleDec(angleZ, 2 * DEG2SHORT); + } else if (cinfo.type == CT_RIGHT) { + c_applyOffset(); + angleY -= 5 * DEG2SHORT; + angleZ = angleDec(angleZ, 2 * DEG2SHORT); + } + + return false; + } + + bool c_checkWallUW() { + if (cinfo.type == CT_FRONT) { + if (angleX > 35 * DEG2SHORT) { + angleX += 2 * DEG2SHORT; + } else if (angleX < -35 * DEG2SHORT) { + angleX -= 2 * DEG2SHORT; + } else { + vSpeed = 0; + } + } else if (cinfo.type == CT_CEILING) { + if (angleX >= -45 * DEG2SHORT) { + angleX -= 2 * DEG2SHORT; + } + } else if (cinfo.type == CT_FRONT_CEILING) { + vSpeed = 0; + } else if (cinfo.type == CT_LEFT) { + angleY += 5 * DEG2SHORT; + } else if (cinfo.type == CT_RIGHT) { + angleY -= 5 * DEG2SHORT; + } else if (cinfo.type == CT_FLOOR_CEILING) { + pos = cinfo.pos; + vSpeed = 0; + return true; + } + + if (cinfo.m.floor < 0) { + pos.y += cinfo.m.floor; + angleX += 2 * DEG2SHORT; + } + + int32 waterDepth = getWaterDepth(room); + + if (waterDepth == WALL) { + vSpeed = 0; + pos = cinfo.pos; + } else if (waterDepth <= 512) { + waterState = WATER_STATE_WADE; + + animSet(this, ANIM_SWIM_STAND, true); + goalState = STOP; + + angleX = 0; + angleZ = 0; + hSpeed = 0; + vSpeed = 0; + flags.gravity = false; + /* TODO + RoomInfo::Sector* sector = getSector(roomIndex, pos.x, pos.y, pos.z); + pos.y = getFloor(sector, pos.x, pos.y, pos.z); + */ + } + + return false; + } + + bool c_checkWallSurf() { + if ((cinfo.m.floor < 0 && cinfo.m.slantType == SLANT_HIGH) || (cinfo.type & (CT_FRONT | CT_CEILING | CT_FRONT_CEILING | CT_FLOOR_CEILING))) { + pos = cinfo.pos; + vSpeed = 0; + } else if (cinfo.type == CT_LEFT) { + angleY += 5 * DEG2SHORT; + } else if (cinfo.type == CT_RIGHT) { + angleY -= 5 * DEG2SHORT; + } + + return true; + } + + bool c_checkSlide() { + if (waterState == WATER_STATE_WADE) { + return false; + } + + if (cinfo.m.slantType != SLANT_HIGH) { + return false; + } + + c_applyOffset(); + + int16 angle; + + if (cinfo.slantX > 2) { + angle = -ANGLE_90; + } else if (cinfo.slantX < -2) { + angle = ANGLE_90; + } else if (cinfo.slantZ > 2) { + angle = -ANGLE_180; + } else { + angle = 0; + } + + if (abs(angle - angleY) <= ANGLE_90) { + if (state != SLIDE) { + animSet(this, ANIM_SLIDE_FORTH, true); + } + moveAngle = angle; + angleY = angle; + } else { + if (state != SLIDE_BACK) { + animSet(this, ANIM_SLIDE_BACK, true); + } + moveAngle = angle; + angleY = angle - ANGLE_180; + } + + return true; + } + + bool c_checkFall(int32 height, int32 fallAnimIndex = ANIM_FALL_FORTH) { + if (waterState == WATER_STATE_WADE) { + return false; + } + + if (cinfo.m.floor <= height) { + return false; + } + + animSet(this, fallAnimIndex, true); + + vSpeed = 0; + flags.gravity = true; + + return true; + } + + bool c_checkLanding() { + if ((state == FAST_DIVE /*|| state == AIR_ROLL*/) && vSpeed > 133) { + health = 0; + return true; + } + + int32 y = pos.y; + pos.y += cinfo.m.floor; + /* TODO + v2floor = pos.y; + + checkTrigger(cinfo.trigger, false); + */ + pos.y = y; + + if (vSpeed <= 140) { + return false; + } + + if (vSpeed > 154) { + health = 0; + } else { + health -= (X_SQR(vSpeed - 140) * LARA_MAX_HEALTH) / 196; + } + + return checkDeath(); + } + + bool c_checkSwing() { + int32 x = pos.x; + int32 y = pos.y; + int32 z = pos.z; + + switch (angleY) { + case ANGLE_0 : z += 256; break; + case ANGLE_90 : x += 256; break; + case -ANGLE_90 : x -= 256; break; + case -ANGLE_180 : z -= 256; break; + } + + int32 roomIndex = getRoomIndex(room, x, y, z); + const RoomInfo::Sector* sector = getSector(roomIndex, x, z); + int32 floor = getFloor(sector, x, y, z); + + if (floor != WALL) { + int32 ceiling = getCeiling(sector, x, y, z); + + floor -= y; + ceiling -= y; + + if (floor > 0 && ceiling < -400) { + return true; + } + } + + return false; + } + + bool c_checkGrab() { + return !(input & IN_ACTION) || cinfo.type != CT_FRONT || abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT; + } + + bool c_checkSpace() { + return (cinfo.f.floor < cinfo.f.ceiling || + cinfo.l.floor < cinfo.l.ceiling || + cinfo.r.floor < cinfo.r.ceiling); + } + + bool c_checkClimbStart() { + return false; + } + + bool c_checkClimbUp() { + if (c_checkGrab()) { + return false; + } + + int16 angle = angleY; + if (alignAngle(angle, 30 * DEG2SHORT)) { + return false; + } + + if (cinfo.f.floor >= -640 && cinfo.f.floor <= -384) { + if (c_checkSpace()) return false; + + animSet(this, ANIM_CLIMB_2, true); + state = HANG_UP; + + pos.y += 512 + cinfo.f.floor; + } else if (cinfo.f.floor >= -896 && cinfo.f.floor <= -640) { + if (c_checkSpace()) return false; + + animSet(this, ANIM_CLIMB_3, true); + state = HANG_UP; + + pos.y += 768 + cinfo.f.floor; + } else if (cinfo.f.floor >= -1920 && cinfo.f.floor <= -896) { + animSet(this, ANIM_STAND, true); + goalState = UP_JUMP; + vSpeedHack = -int32(phd_sqrt(-2 * GRAVITY * (cinfo.f.floor + 800)) + 3); + animUpdate(this); + } /* TODO for main branch + else if ((waterState != WATER_STATE_WADE) && (cinfo.f.floor <= -1920) && (cinfo.l.floor <= -1920) && (cinfo.r.floor <= -1920) && (cinfo.m.ceiling <= -1158)) { + animSet(this, ANIM_STAND, true); + goalState = UP_JUMP; + vSpeedHack = -116; + animUpdate(this); + } + else if (((cinfo.f.floor < -1024) && (cinfo.f.ceiling >= 506)) || (cinfo.m.ceiling <= -518) && c_checkClimbStart()) { + animSet(this, ANIM_STAND, true); + goalState = CLIMB_START; + processAnimation(); + }*/ else { + return false; + } + + angleY = angle; + c_applyOffset(); + + return true; + } + + bool c_checkHang() { + if (c_checkGrab()) { + return false; + } + + if (cinfo.f.ceiling > 0 || cinfo.m.ceiling > -LARA_STEP_HEIGHT || (cinfo.m.floor < 200 && state == REACH)) { + return false; + } + + int32 h = cinfo.f.floor - getBounds().minY; + int32 v = h + vSpeed; + + if ((h < 0 && v < 0) || (h > 0 && v > 0)) { + return false; + } + + if (alignAngle(angleY, 35 * DEG2SHORT)) { + return false; + } + + if (state == REACH) + { + if (c_checkSwing()) { + animSet(this, ANIM_HANG_SWING, true); + } else { + animSet(this, ANIM_HANG, true); + } + } else { + animSet(this, ANIM_HANG, true, 12); + } + + cinfo.offset.y = cinfo.f.floor - getBounds().minY; + + c_applyOffset(); + + flags.gravity = false; + hSpeed = 0; + vSpeed = 0; + + return true; + } + + bool c_checkDrop() { + // TODO getTrigger here + + if ((health > 0) && (input & IN_ACTION)) { + flags.gravity = false; + vSpeed = 0; + return false; + } + + flags.gravity = true; + hSpeed = 2; + vSpeed = 1; + + animSet(this, ANIM_FALL_FORTH, true); + + return true; + } + + bool c_checkWaterOut() { + if (!(input & IN_ACTION) || + (cinfo.type != CT_FRONT) || + (cinfo.f.ceiling > 0) || + (cinfo.m.ceiling > -LARA_STEP_HEIGHT) || + (abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT)) + { + return false; + } + + int32 h = cinfo.f.floor + LARA_HEIGHT_SURF; + + if (h <= -512 || h > 316) { + return false; + } + + if (alignAngle(angleY, 35 * DEG2SHORT)) { + return false; + } + + angleY += h - 5; + + updateRoom(curItemIndex, -LARA_HEIGHT / 2); + + alignWall(); + + /* TODO for main branch + if ((h < -128) || (level->version & TR::VER_TR1)) { + setAnimV2(ANIM_WATER_OUT, true); + specular = LARA_WET_SPECULAR; + } else if (h < 128) { + setAnimV2(ANIM_SURF_OUT, true); + } else { + setAnimV2(ANIM_SURF_STAND, true); + } + game->waterDrop(pos, 128.0f, 0.2f); + */ + animSet(this, ANIM_WATER_OUT, true); + + waterState = WATER_STATE_ABOVE; + goalState = STOP; + angleX = 0; + angleZ = 0; + hSpeed = 0; + vSpeed = 0; + flags.gravity = false; + + return true; + } + + void c_default() { + cinfo.gapPos = LARA_STEP_HEIGHT; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(this, LARA_HEIGHT, 0); + } + + void c_step() { + cinfo.gapPos = (waterState == WATER_STATE_WADE) ? -WALL : 128; + cinfo.gapNeg = -128; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(this, LARA_HEIGHT, 0); + + if (c_checkCeiling()) return; + + if (c_checkWall()) { + animSet(this, ANIM_STAND, true); + } + + if (c_checkSlide()) return; + + pos.y += cinfo.m.floor; + } + + void c_fall() { + if (vSpeed <= 0 || cinfo.m.floor > 0) + return; + + if (c_checkLanding()) { + goalState = DEATH; + } else if (state == FORWARD_JUMP && (input & IN_UP) && !(input & IN_WALK)) { + goalState = RUN; + } else if (state == FALL) { + animSet(this, ANIM_LANDING, true); + } else { + goalState = STOP; + } + + stopScreaming(); + + pos.y += cinfo.m.floor; + vSpeed = 0; + flags.gravity = false; + + if (state == FORWARD_JUMP) { + animUpdate(this); + } + } + + void c_jump() { + cinfo.gapPos = -WALL; + cinfo.gapNeg = (state == REACH) ? 0 : -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 192; + + collideRoom(this, state == UP_JUMP ? LARA_HEIGHT_JUMP : LARA_HEIGHT, 0); + + if ((state == REACH || state == UP_JUMP) && c_checkHang()) { + return; + } + + c_applyOffset(); + + bool slide = (state == FALL) || (state == REACH) || (state == UP_JUMP); + + if ((cinfo.type == CT_CEILING) || ((cinfo.type == CT_FRONT_CEILING) && slide)) { + if (vSpeed <= 0) { + vSpeed = 1; + } + } else if (!slide && ((cinfo.type == CT_FRONT) || (cinfo.type == CT_FRONT_CEILING))) { + animSet(this, ANIM_SMASH_JUMP, true, 1); + moveAngle -= ANGLE_180; + hSpeed /= 4; + if (vSpeed <= 0) { + vSpeed = 1; + } + } else if (cinfo.type == CT_FLOOR_CEILING) { + pos.x -= (LARA_RADIUS * phd_sin(cinfo.angle)) >> FIXED_SHIFT; + pos.z -= (LARA_RADIUS * phd_cos(cinfo.angle)) >> FIXED_SHIFT; + cinfo.m.floor = 0; + hSpeed = 0; + if (vSpeed <= 0) { + vSpeed = 16; + } + } else if (cinfo.type == CT_LEFT) { + angleY += 5 * DEG2SHORT; + } else if (cinfo.type == CT_RIGHT) { + angleY -= 5 * DEG2SHORT; + } + + c_fall(); + } + + void c_slide() { + cinfo.gapPos = -WALL; + cinfo.gapNeg = -512; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(this, LARA_HEIGHT, 0); + + if (c_checkCeiling()) return; + + c_checkWall(); + + if (c_checkFall(200, state == SLIDE ? ANIM_FALL_FORTH : ANIM_FALL_BACK)) return; + + c_checkSlide(); + + pos.y += cinfo.m.floor; + + if (cinfo.m.slantType != SLANT_HIGH) { + goalState = STOP; + } + } + + void c_roll() { + vSpeed = 0; + flags.gravity = false; + + cinfo.gapPos = -WALL; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(this, LARA_HEIGHT, 0); + + if (c_checkCeiling()) return; + + if (c_checkSlide()) return; + + if (c_checkFall(200, state == ROLL_START ? ANIM_FALL_FORTH : ANIM_FALL_BACK)) return; + + c_applyOffset(); + + pos.y += cinfo.m.floor; + } + + void c_hang(int32 angleDelta) { + c_angle(angleDelta); + cinfo.gapPos = -WALL; + cinfo.gapNeg = WALL; + cinfo.gapCeiling = 0; + collideRoom(this, LARA_HEIGHT, 0); + + bool noFloor = cinfo.f.floor < 200; + + c_angle(ANGLE_0); + cinfo.gapPos = -WALL; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 0; + + switch (cinfo.quadrant) { + case 0 : pos.z += 2; break; + case 1 : pos.x += 2; break; + case 2 : pos.z -= 2; break; + case 3 : pos.x -= 2; break; + } + + collideRoom(this, LARA_HEIGHT, 0); + + moveAngle = angleY + angleDelta; + + if (health <= 0 || !(input & IN_ACTION)) { + animSet(this, ANIM_FALL_HANG, true, 9); + + cinfo.offset.y = cinfo.f.floor - getBounds().minY + 2; + c_applyOffset(); + + hSpeed = 2; + vSpeed = 1; + flags.gravity = true; + return; + } + + vSpeed = 0; + flags.gravity = false; + + if (noFloor || (cinfo.type != CT_FRONT) || (cinfo.m.ceiling >= 0) || abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT) + { + if (state != HANG) { + animSet(this, ANIM_HANG, true, 21); + } + pos = cinfo.pos; + return; + } + + if (cinfo.quadrant & 1) { + pos.x += cinfo.offset.x; + } else { + pos.z += cinfo.offset.z; + } + + int32 h = cinfo.f.floor - getBounds().minY; + if (abs(h) <= 256) { + pos.y += h; + } + } + + C_HANDLER( WALK ) + { + vSpeed = 0; + flags.gravity = false; + cinfo.stopOnLava = true; + + c_angle(ANGLE_0); + c_default(); + + if (c_checkCeiling()) return; + + if (c_checkClimbUp()) return; + + if (c_checkWall()) { + if (frameIndex >= 29 && frameIndex <= 47) { + animSet(this, ANIM_STAND_RIGHT, false); + } else if ((frameIndex >= 22 && frameIndex <= 28) || (frameIndex >= 48 && frameIndex <= 57)) { + animSet(this, ANIM_STAND_LEFT, false); + } else { + animSet(this, ANIM_STAND, false); + } + } + + if (c_checkFall(LARA_STEP_HEIGHT)) return; + + // descend + if (cinfo.m.floor > 128) { + if (frameIndex >= 28 && frameIndex <= 45) { + animSet(this, ANIM_WALK_DESCEND_RIGHT, false); + } else { + animSet(this, ANIM_WALK_DESCEND_LEFT, false); + } + } + + // ascend + if (cinfo.m.floor >= -LARA_STEP_HEIGHT && cinfo.m.floor < -128) { + if (frameIndex >= 27 && frameIndex <= 44) { + animSet(this, ANIM_WALK_ASCEND_RIGHT, false); + } else { + animSet(this, ANIM_WALK_ASCEND_LEFT, false); + } + } + + if (c_checkSlide()) return; + + pos.y += cinfo.m.floor; + } + + C_HANDLER( RUN ) + { + c_angle(ANGLE_0); + + cinfo.gapPos = -WALL; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(this, LARA_HEIGHT, 0); + + if (c_checkCeiling()) return; + + if (c_checkClimbUp()) return; + + if (c_checkWall()) { + angleZ = 0; + + if (cinfo.f.slantType == SLANT_NONE && cinfo.f.floor < -LARA_SMASH_HEIGHT && frameIndex < 22) { + animSet(this, frameIndex < 10 ? ANIM_SMASH_RUN_LEFT : ANIM_SMASH_RUN_RIGHT, false); + state = SPLAT; + return; + } + + animSet(this, ANIM_STAND, true); + } + + if (c_checkFall(LARA_STEP_HEIGHT)) return; + + // ascend + if (cinfo.m.floor >= -LARA_STEP_HEIGHT && cinfo.m.floor < -128) { + if (frameIndex >= 3 && frameIndex <= 14) { + animSet(this, ANIM_RUN_ASCEND_RIGHT, false); + } else { + animSet(this, ANIM_RUN_ASCEND_LEFT, false); + } + } + + if (c_checkSlide()) return; + + if (cinfo.m.floor >= 50) { + pos.y += 50; + return; + } + + pos.y += cinfo.m.floor; + } + + C_HANDLER( STOP ) + { + vSpeed = 0; + flags.gravity = false; + + c_angle(ANGLE_0); + c_default(); + + if (c_checkCeiling()) return; + + if (c_checkFall(100)) return; + + if (c_checkSlide()) return; + + c_applyOffset(); + + pos.y += cinfo.m.floor; + } + + C_HANDLER( FORWARD_JUMP ) + { + c_angle(ANGLE_0); + c_jump(); + } + + C_HANDLER( POSE ) + { + c_STOP(); + } + + C_HANDLER( FAST_BACK ) + { + vSpeed = 0; + flags.gravity = false; + + c_angle(-ANGLE_180); + + cinfo.gapPos = -WALL; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(this, LARA_HEIGHT, 0); + + if (c_checkCeiling()) return; + + if (c_checkFall(200, ANIM_FALL_BACK)) return; + + if (c_checkWall()) { + animSet(this, ANIM_STAND, false); + } + + pos.y += cinfo.m.floor; + } + + C_HANDLER( TURN_RIGHT ) + { + c_angle(ANGLE_0); + c_default(); + + if (c_checkFall(100)) return; + + if (c_checkSlide()) return; + + pos.y += cinfo.m.floor; + } + + C_HANDLER( TURN_LEFT ) + { + c_TURN_RIGHT(); + } + + C_HANDLER( DEATH ) + { + cinfo.radius = LARA_RADIUS * 4; + + c_angle(ANGLE_0); + c_default(); + + c_applyOffset(); + pos.y += cinfo.m.floor; + } + + C_HANDLER( FALL ) + { + flags.gravity = true; + c_jump(); + } + + C_HANDLER( HANG ) + { + c_hang(0); + + if ((input & IN_UP) && goalState == HANG) + { + if (cinfo.f.floor <= -850 || + cinfo.f.floor >= -650 || + c_checkSpace() || cinfo.staticHit) return; + + if (input & IN_WALK) { + goalState = HANDSTAND; + } else { + goalState = HANG_UP; + } + } + } + + C_HANDLER( REACH ) + { + flags.gravity = true; + c_angle(ANGLE_0); + c_jump(); + } + + C_HANDLER( SPLAT ) + { + c_angle(ANGLE_0); + c_default(); + c_applyOffset(); + } + + C_HANDLER( TREAD ) {} // TODO + + C_HANDLER( LAND ) + { + c_STOP(); + } + + C_HANDLER( COMPRESS ) + { + vSpeed = 0; + flags.gravity = false; + + cinfo.gapPos = -WALL; + cinfo.gapNeg = WALL; + cinfo.gapCeiling = 0; + + collideRoom(this, LARA_HEIGHT, 0); + + if (cinfo.m.ceiling > -100) { + animSet(this, ANIM_STAND, true); + pos = cinfo.pos; + hSpeed = 0; + vSpeed = 0; + flags.gravity = false; + } + } + + C_HANDLER( BACK ) + { + vSpeed = 0; + flags.gravity = false; + + c_angle(-ANGLE_180); + + cinfo.gapPos = (waterState == WATER_STATE_WADE) ? -WALL : LARA_STEP_HEIGHT; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(this, LARA_HEIGHT, 0); + + if (c_checkCeiling()) return; + + if (c_checkWall()) + { + animSet(this, ANIM_STAND, true); + } + + if (cinfo.m.floor > 128 && cinfo.m.floor < LARA_STEP_HEIGHT) + { + if (frameIndex < 568) { + animSet(this, ANIM_BACK_DESCEND_LEFT, false); + } else { + animSet(this, ANIM_BACK_DESCEND_RIGHT, false); + } + } + + if (c_checkSlide()) return; + + pos.y += cinfo.m.floor; + } + + C_HANDLER( SWIM ) {} // TODO + C_HANDLER( GLIDE ) {} // TODO + + C_HANDLER( HANG_UP ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( FAST_TURN ) + { + c_STOP(); + } + + C_HANDLER( STEP_RIGHT ) + { + c_angle(+ANGLE_90); + c_step(); + } + + C_HANDLER( STEP_LEFT ) + { + c_angle(-ANGLE_90); + c_step(); + } + + C_HANDLER( ROLL_END ) + { + c_angle(-ANGLE_180); + c_roll(); + } + + C_HANDLER( SLIDE ) + { + c_angle(ANGLE_0); + c_slide(); + } + + C_HANDLER( BACK_JUMP ) + { + c_angle(-ANGLE_180); + c_jump(); + } + + C_HANDLER( RIGHT_JUMP ) + { + c_angle(ANGLE_90); + c_jump(); + } + + C_HANDLER( LEFT_JUMP ) + { + c_angle(-ANGLE_90); + c_jump(); + } + + C_HANDLER( UP_JUMP ) + { + c_angle(ANGLE_0); + c_jump(); + } + + C_HANDLER( FALL_BACK ) + { + c_angle(-ANGLE_180); + c_jump(); + } + + C_HANDLER( HANG_LEFT ) + { + c_hang(-ANGLE_90); + } + + C_HANDLER( HANG_RIGHT ) + { + c_hang(ANGLE_90); + } + + C_HANDLER( SLIDE_BACK ) + { + c_angle(-ANGLE_180); + c_slide(); + } + + C_HANDLER( SURF_TREAD ) {} // TODO + C_HANDLER( SURF_SWIM ) {} // TODO + C_HANDLER( DIVE ) {} // TODO + + C_HANDLER( PUSH_BLOCK ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( PULL_BLOCK ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( PUSH_PULL_READY ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( PICK_UP ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( SWITCH_DOWN ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( SWITCH_UP ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( USE_KEY ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( USE_PUZZLE ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( UW_DEATH ) {} // TODO + + C_HANDLER( ROLL_START ) + { + c_angle(ANGLE_0); + c_roll(); + } + + C_HANDLER( SPECIAL ) + { + // empty + } + + C_HANDLER( SURF_BACK ) {} // TODO + C_HANDLER( SURF_LEFT ) {} // TODO + C_HANDLER( SURF_RIGHT ) {} // TODO + + C_HANDLER( MIDAS_USE ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( MIDAS_DEATH ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( SWAN_DIVE ) + { + c_angle(ANGLE_0); + c_jump(); + } + + C_HANDLER( FAST_DIVE ) + { + c_angle(ANGLE_0); + c_jump(); + } + + C_HANDLER( HANDSTAND ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( WATER_OUT ) + { + c_angle(ANGLE_0); + c_default(); + } + +// update control + void updateInput() + { + input = 0; + if (camera.mode == CAMERA_MODE_FREE) return; + + if (keys & IK_LEFT) input |= IN_LEFT; + if (keys & IK_RIGHT) input |= IN_RIGHT; + if (keys & IK_UP) input |= IN_UP; + if (keys & IK_DOWN) input |= IN_DOWN; + if (keys & IK_R) input |= IN_WALK; + if (keys & IK_A) input |= IN_ACTION; + if (keys & IK_B) input |= IN_JUMP; + if ((keys & (IK_L | IK_A)) == (IK_L | IK_A)) input |= IN_WEAPON; + if ((keys & (IK_L | IK_B)) == (IK_L | IK_B)) input |= IN_UP | IN_DOWN; + if ((keys & (IK_L | IK_R)) == (IK_L | IK_R)) input |= IN_LOOK; + } + + void update() + { + updateInput(); + + cinfo.trigger = NULL; + cinfo.radius = LARA_RADIUS; + cinfo.pos = pos; + cinfo.enemyPush = true; + cinfo.enemyHit = true; + cinfo.stopOnSlant = false; + cinfo.stopOnLava = false; + + updateState(); + + angleZ = angleDec(angleZ, 1 * DEG2SHORT); + turnSpeed = angleDec(turnSpeed, 2 * DEG2SHORT); + angleY += turnSpeed; + + animUpdate(this); + + updateCollision(); + + updateRoom(curItemIndex, -LARA_HEIGHT / 2); + + //updateWeapon(); + //checkTrigger(cinfo.trigger, false); + } +}; + +const Lara::Handler Lara::sHandlers[X_MAX] = { LARA_STATES(DECL_S_HANDLER) }; +const Lara::Handler Lara::cHandlers[X_MAX] = { LARA_STATES(DECL_C_HANDLER) }; + +#undef DECL_ENUM +#undef DECL_S_HANDLER +#undef DECL_C_HANDLER +#undef S_HANDLER +#undef C_HANDLER + +#endif diff --git a/src/platform/gba/level.h b/src/platform/gba/level.h index 971bb93..0913f81 100644 --- a/src/platform/gba/level.h +++ b/src/platform/gba/level.h @@ -2,12 +2,9 @@ #define H_LEVEL #include "common.h" -#include "camera.h" - -#define GRAVITY 6 // level file data ------------------- -int32 tilesCount; +//int32 tilesCount; extern const uint8* tiles; extern uint16 palette[256]; @@ -27,11 +24,20 @@ const int32* meshOffsets; const int32* nodesPtr; -int32 animsCount; +//int32 animsCount; const Anim* anims; -int32 framesCount; -const uint16* frames; +//int32 animStatesCount; +const AnimState* animStates; + +//int32 animRangesCount; +const AnimRange* animRanges; + +//int32 animCommandsCount; +const int16* animCommands; + +//int32 animFramesCount; +const uint16* animFrames; int32 modelsCount; EWRAM_DATA Model models[MAX_MODELS]; @@ -43,34 +49,19 @@ const StaticMesh* staticMeshes; int32 itemsCount; EWRAM_DATA Item items[MAX_ITEMS]; + +const uint16* soundMap; + +//int32 soundInfosCount; +const SoundInfo* soundInfos; + +//int32 soundDataSize; +const uint8* soundData; + +//int32 soundOffsetsCount; +const uint32* soundOffsets; // ----------------------------------- -struct Room { - Rect clip; - uint8 firstItem; - bool visible; - - // TODO leave in ROM - int32 x, z; - uint16 vCount; - uint16 qCount; - uint16 tCount; - uint16 pCount; - uint16 lCount; - uint16 mCount; - uint16 zSectors; - uint16 xSectors; - uint16 ambient; - - const RoomInfo::Vertex* vertices; - const Quad* quads; - const Triangle* triangles; - const RoomInfo::Portal* portals; - const RoomInfo::Sector* sectors; - const RoomInfo::Light* lights; - const RoomInfo::Mesh* meshes; -}; - int16 roomsCount; EWRAM_DATA Room rooms[64]; @@ -80,41 +71,24 @@ int32 visRoomsCount; int32 visRooms[16]; #define ROOM_VISIBLE (1 << 15) -#define SEQ_GLYPH 190 - -enum FloorType { - FLOOR_TYPE_NONE, - FLOOR_TYPE_PORTAL, - FLOOR_TYPE_FLOOR, - FLOOR_TYPE_CEILING, -}; - -int32 seqGlyphs; - -extern uint32 gVerticesCount; -extern Rect clip; - -void roomReset(int32 roomIndex) -{ - Room* room = rooms + roomIndex; - - room->visible = false; - room->clip = { FRAME_WIDTH, FRAME_HEIGHT, 0, 0 }; -} void roomItemAdd(int32 roomIndex, int32 itemIndex) { - ASSERT(items[itemIndex].nextItem == NO_ITEM); - + Item* item = items + itemIndex; Room* room = rooms + roomIndex; - items[itemIndex].nextItem = room->firstItem; + ASSERT(item->nextItem == NO_ITEM); + + item->room = roomIndex; + item->nextItem = room->firstItem; room->firstItem = itemIndex; } -void roomItemRemove(int32 roomIndex, int32 itemIndex) +void roomItemRemove(int32 itemIndex) { - Room* room = rooms + roomIndex; + Item* item = items + itemIndex; + Room* room = rooms + item->room; + item->room = NO_ROOM; int32 prevIndex = NO_ITEM; int32 index = room->firstItem; @@ -174,8 +148,28 @@ void deactivateItem(int32 itemIndex) } } -void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_OFF alignment bytes - tilesCount = *((int32*)(data + 4)); +void fixLightmap(int32 palIndex) +{ + uint16 color = palette[palIndex]; + + int32 r = 0x1F & (color); + int32 g = 0x1F & (color >> 5); + int32 b = 0x1F & (color >> 10); + + for (int32 i = 0; i < 32; i++) + { + int32 lum = 31 - i; + + int32 lumR = X_CLAMP((r * lum) / 14, 0, 31); + int32 lumG = X_CLAMP((g * lum) / 14, 0, 31); + int32 lumB = X_CLAMP((b * lum) / 14, 0, 31); + + palette[lightmap[i * 256 + palIndex]] = lumR | (lumG << 5) | (lumB << 10); + } +} + +void readLevel(const uint8* data) { // TODO non-hardcode level loader, added *_OFF alignment bytes +// tilesCount = *((int32*)(data + 4)); tiles = data + 8; #define MDL_OFF 2 @@ -189,13 +183,25 @@ void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_O meshData = data + 908172 + 4; meshOffsets = (int32*)(data + 975724 + 4); - animsCount = *((int32*)(data + 976596)); +// animsCount = *((int32*)(data + 976596)); anims = (Anim*)(data + 976596 + 4); ASSERT((intptr_t)anims % 4 == 0); - framesCount = *((int32*)(data + 992990)); - frames = (uint16*)(data + 992990 + 4); - ASSERT((intptr_t)frames % 2 == 0); +// animStatesCount = *((int32*)(data + 985464)); + animStates = (AnimState*)(data + 985464 + 4); + ASSERT((intptr_t)animStates % 2 == 0); + +// animRangesCount = *((int32*)(data + 986872)); + animRanges = (AnimRange*)(data + 986872 + 4); + ASSERT((intptr_t)animRanges % 2 == 0); + +// animCommandsCount = *((int32*)(data + 988868)); + animCommands = (int16*)(data + 988868 + 4); + ASSERT((intptr_t)animCommands % 2 == 0); + +// animFramesCount = *((int32*)(data + 992990)); + animFrames = (uint16*)(data + 992990 + 4); + ASSERT((intptr_t)animFrames % 2 == 0); nodesPtr = (int32*)(data + 990318); @@ -218,6 +224,18 @@ void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_O itemsCount = *((int32*)(data + 1319252 + MDL_OFF + ITM_OFF)); const uint8* itemsPtr = (data + 1319252 + 4 + MDL_OFF + ITM_OFF); + soundMap = (uint16*)(data + 1329540 + MDL_OFF + ITM_OFF); + +// soundInfosCount = *((int32*)(data + 1330052 + MDL_OFF + ITM_OFF)); + soundInfos = (SoundInfo*)(data + 1330052 + 4 + MDL_OFF + ITM_OFF); + +// soundDataSize = *((int32*)(data + 1330624 + MDL_OFF + ITM_OFF)); + soundData = (uint8*)(data + 1330624 + 4 + MDL_OFF + ITM_OFF); + +// soundOffsetsCount = *((int32*)(data + 2533294 + MDL_OFF + ITM_OFF)); + soundOffsets = (uint32*)(data + 2533294 + 4 + MDL_OFF + ITM_OFF); + + memset(items, 0, sizeof(items)); for (int32 i = 0; i < itemsCount; i++) { memcpy(items + i, itemsPtr, FILE_ITEM_SIZE); itemsPtr += FILE_ITEM_SIZE; @@ -235,19 +253,28 @@ void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_O for (int i = 0; i < 256; i++) { #if defined(_WIN32) || defined(__GBA__) + // grayscale palette + //uint8 c = ((p[0] + p[1] + p[2]) / 3) >> 1; + //palette[i] = c | (c << 5) | (c << 10); + palette[i] = (p[0] >> 1) | ((p[1] >> 1) << 5) | ((p[2] >> 1) << 10); #elif defined(__TNS__) palette[i] = (p[2] >> 1) | ((p[1] >> 1) << 5) | ((p[0] >> 1) << 10); #endif p += 3; } + palette[0] = 0; // black or transparent + + // TODO preprocess fix Laras palette + fixLightmap(6); // boots + fixLightmap(14); // skin // prepare rooms - uint8 *ptr = (uint8*)roomsPtr; + uint8* ptr = (uint8*)roomsPtr; for (int32 roomIndex = 0; roomIndex < roomsCount; roomIndex++) { - const RoomInfo *room = (RoomInfo*)ptr; + const RoomInfo* room = (RoomInfo*)ptr; ptr += sizeof(RoomInfo); uint32 dataSize; @@ -255,7 +282,6 @@ void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_O uint8* skipPtr = ptr + dataSize * 2; Room &desc = rooms[roomIndex]; - roomReset(roomIndex); desc.firstItem = NO_ITEM; @@ -327,485 +353,321 @@ void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_O { staticMeshesMap[staticMeshes[i].id] = i; } +} -// prepare items - for (int32 i = 0; i < itemsCount; i++) { - Item* item = items + i; - - item->angleX = 0; - item->angleZ = 0; - item->vSpeed = 0; - item->hSpeed = 0; - item->nextItem = NO_ITEM; - item->nextActive = NO_ITEM; - item->animIndex = models[modelsMap[item->type]].animIndex; - item->frameIndex = anims[item->animIndex].frameBegin; - item->state = anims[item->animIndex].state; - item->nextState = item->state; - item->goalState = item->state; - item->intensity = 4096; // TODO lighting - - item->flags.gravity = 0; - - if (item->room > -1) { - roomItemAdd(item->room, i); - } - - if (item->type == ITEM_LARA) { - activateItem(i); - } - - // TODO remove - if (item->type == ITEM_WOLF || - item->type == ITEM_BEAR || - item->type == ITEM_BAT || - item->type == ITEM_CRYSTAL) - { - activateItem(i); - } +int32 getBridgeFloor(const Item* item, int32 x, int32 z) +{ + if (item->type == ITEM_BRIDGE_1) { + return item->pos.y; } -// prepare glyphs - for (int32 i = 0; i < spritesSeqCount; i++) { - if (spritesSeq[i].type == SEQ_GLYPH) { - seqGlyphs = i; + int32 h; + if (item->angleY == ANGLE_0) { + h = 1024 - x; + } else if (item->angleY == -ANGLE_180) { + h = x; + } else if (item->angleY == ANGLE_90) { + h = z; + } else { + h = 1024 - z; + } + + h &= 1023; + + return item->pos.y + ((item->type == ITEM_BRIDGE_2) ? (h >> 2) : (h >> 1)); +} + +int32 getTrapDoorFloor(const Item* item, int32 x, int32 z) +{ + int32 dx = (item->pos.x >> 10) - (x >> 10); + int32 dz = (item->pos.z >> 10) - (z >> 10); + + if (((dx == 0) && (dz == 0)) || + ((dx == 0) && (dz == 1) && (item->angleY == ANGLE_0)) || + ((dx == 0) && (dz == -1) && (item->angleY == -ANGLE_180)) || + ((dx == 1) && (dz == 0) && (item->angleY == ANGLE_90)) || + ((dx == -1) && (dz == 0) && (item->angleY == -ANGLE_90))) + { + return item->pos.y; + } + + return WALL; +} + +int32 getDrawBridgeFloor(const Item* item, int32 x, int32 z) +{ + int32 dx = (item->pos.x >> 10) - (x >> 10); + int32 dz = (item->pos.z >> 10) - (z >> 10); + + if (((dx == 0) && ((dz == -1) || (dz == -2)) && (item->angleY == ANGLE_0)) || + ((dx == 0) && ((dz == 1) || (dz == 2)) && (item->angleY == -ANGLE_180)) || + ((dz == 0) && ((dx == -1) || (dz == -2)) && (item->angleY == ANGLE_90)) || + ((dz == 0) && ((dx == 1) || (dz == 2)) && (item->angleY == -ANGLE_90))) + { + return item->pos.y; + } + + return WALL; +} + +void getItemFloorData(const Item* item, int32 x, int32 y, int32 z, int32* floor, int32* ceiling) +{ + int32 h = WALL; + + switch (item->type) + { + case ITEM_TRAP_FLOOR: + { + if (item->state == 0 || item->state == 1) { + h = item->pos.y - 512; + } break; } - } - - camera.init(); - camera.room = 0; -} - -void drawMesh(int16 meshIndex, uint16 intensity) { - int32 offset = meshOffsets[meshIndex]; - const uint8* ptr = meshData + offset; - - ptr += 2 * 5; // skip [cx, cy, cz, radius, flags] - - int16 vCount = *(int16*)ptr; ptr += 2; - const vec3s* vertices = (vec3s*)ptr; - ptr += vCount * 3 * sizeof(int16); - - int16 nCount = *(int16*)ptr; ptr += 2; - //const int16* normals = (int16*)ptr; - if (nCount > 0) { // normals - ptr += nCount * 3 * sizeof(int16); - } else { // intensity - ptr += vCount * sizeof(int16); - } - - int16 rCount = *(int16*)ptr; ptr += 2; - Quad* rFaces = (Quad*)ptr; ptr += rCount * sizeof(Quad); - - int16 tCount = *(int16*)ptr; ptr += 2; - Triangle* tFaces = (Triangle*)ptr; ptr += tCount * sizeof(Triangle); - - int16 crCount = *(int16*)ptr; ptr += 2; - Quad* crFaces = (Quad*)ptr; ptr += crCount * sizeof(Quad); - - int16 ctCount = *(int16*)ptr; ptr += 2; - Triangle* ctFaces = (Triangle*)ptr; ptr += ctCount * sizeof(Triangle); - - int32 startVertex = gVerticesCount; - - PROFILE_START(); - transformMesh(vertices, vCount, intensity); - PROFILE_STOP(dbg_transform); - - PROFILE_START(); - faceAddMesh(rFaces, crFaces, tFaces, ctFaces, rCount, crCount, tCount, ctCount, startVertex); - PROFILE_STOP(dbg_poly); -} - -Frame* getFrame(const Item* item, const Model* model) -{ - const Anim* anim = anims + item->animIndex; - - int32 frameSize = sizeof(Frame) / 2 + model->mCount * 2; - int32 frameIndex = (item->frameIndex - anim->frameBegin) / anim->frameRate;//* FixedInvU(anim->frameRate) >> 16; - - return (Frame*)(frames + anim->frameOffset / 2 + frameIndex * frameSize); -} - -void drawItem(const Item* item) { - int32 modelIndex = modelsMap[item->type]; - if (modelIndex == NO_MODEL) { - return; // TODO sprite items - } - - const Model* model = models + modelIndex; - - if (model->mCount == 1 && meshOffsets[model->mStart] == 0) return; - - Frame* frame = getFrame(item, model); - uint16* frameAngles = frame->angles + 1; - - matrixPush(); - matrixTranslateAbs(item->pos.x, item->pos.y, item->pos.z); - matrixRotateYXZ(item->angleX, item->angleY, item->angleZ); - - if (boxIsVisible(&frame->box)) { - // non-aligned access (TODO) - uint32 nodeIndex; - memcpy(&nodeIndex, &model->nodeIndex, sizeof(nodeIndex)); - Node nodes[32]; - memcpy(nodes, nodesPtr + nodeIndex, (model->mCount - 1) * sizeof(Node)); - - const Node* node = nodes; - - matrixFrame(frame->pos.x, frame->pos.y, frame->pos.z, frameAngles); - - drawMesh(model->mStart, item->intensity); - - for (int32 i = 1; i < model->mCount; i++) + case ITEM_DRAWBRIDGE: { - if (node->flags & 1) matrixPop(); - if (node->flags & 2) matrixPush(); - - frameAngles += 2; - matrixFrame(node->pos.x, node->pos.y, node->pos.z, frameAngles); - - drawMesh(model->mStart + i, item->intensity); - - node++; + if (item->state == 1) { + h = getDrawBridgeFloor(item, x, z); + } + break; } - } - - matrixPop(); -} - -void drawNumber(int32 number, int32 x, int32 y) { - const int32 widths[] = { 12, 8, 10, 10, 10, 10, 10, 10, 10, 10 }; - - const Sprite *glyphSprites = sprites + spritesSeq[seqGlyphs].sStart; - - while (number > 0) { - x -= widths[number % 10]; - drawGlyph(glyphSprites + 52 + (number % 10), x, y); - number /= 10; - } -} - -void drawRoom(int32 roomIndex) { - const Room* room = rooms + roomIndex; - - clip = room->clip; - - int32 startVertex = gVerticesCount; - - matrixPush(); - matrixTranslateAbs(room->x, 0, room->z); - - PROFILE_START(); - transformRoom(room->vertices, room->vCount); - PROFILE_STOP(dbg_transform); - - matrixPop(); - - PROFILE_START(); - faceAddRoom(room->quads, room->qCount, room->triangles, room->tCount, startVertex); - - for (int32 i = 0; i < room->mCount; i++) - { - const RoomInfo::Mesh* mesh = room->meshes + i; - const StaticMesh* staticMesh = staticMeshes + staticMeshesMap[mesh->staticMeshId]; - - if (!(staticMesh->flags & 2)) continue; // invisible - - // TODO align RoomInfo::Mesh (room relative int16?) - vec3i pos; - memcpy(&pos, &mesh->pos, sizeof(pos)); - - matrixPush(); - matrixTranslateAbs(pos.x, pos.y, pos.z); - matrixRotateY(mesh->rotation); - - if (boxIsVisible(&staticMesh->vbox)) { - drawMesh(staticMesh->meshIndex, mesh->intensity); + case ITEM_BRIDGE_1: + case ITEM_BRIDGE_2: + case ITEM_BRIDGE_3: + { + h = getBridgeFloor(item, x, z); + break; } + case ITEM_TRAP_DOOR_1: + case ITEM_TRAP_DOOR_2: + { + if (item->state == 0) { + h = getTrapDoorFloor(item, x, z); + } - matrixPop(); + if ((floor && (h >= *floor)) || (ceiling && (h <= *ceiling))) + { + h = WALL; + } + } + default : return; } - int32 itemIndex = room->firstItem; - while (itemIndex != NO_ITEM) + if (floor && (y <= h)) { - drawItem(items + itemIndex); - itemIndex = items[itemIndex].nextItem; + *floor = h; } - PROFILE_STOP(dbg_poly); - roomReset(roomIndex); - - flush(); + if (ceiling && (y > h)) + { + *ceiling = h + 256; + } } -const RoomInfo::Sector* getSector(int32 roomIndex, int32 x, int32 z) { +const RoomInfo::Sector* getSector(int32 roomIndex, int32 x, int32 z) +{ Room &room = rooms[roomIndex]; - int32 sx = clamp((x - room.x) >> 10, 0, room.xSectors - 1); - int32 sz = clamp((z - room.z) >> 10, 0, room.zSectors - 1); + int32 sx = X_CLAMP((x - room.x) >> 10, 0, room.xSectors - 1); + int32 sz = X_CLAMP((z - room.z) >> 10, 0, room.zSectors - 1); return room.sectors + sx * room.zSectors + sz; } -int32 getRoomIndex(int32 roomIndex, const vec3i* pos) { - const RoomInfo::Sector *sector = getSector(roomIndex, pos->x, pos->z); - - if (sector->floorIndex) { - const uint16 *data = floors + sector->floorIndex; - int16 type = *data++; - - if (type == FLOOR_TYPE_FLOOR) { - data++; - type = *data++; - } - - if (type == FLOOR_TYPE_CEILING) { - data++; - type = *data++; - } - - if ((type & 0xFF) == FLOOR_TYPE_PORTAL) { - roomIndex = *data; - } +const RoomInfo::Sector* getSectorBelow(const RoomInfo::Sector* sector, int32 x, int32 z) +{ + while (sector->roomBelow != NO_ROOM) + { + Room* room = rooms + sector->roomBelow; + int32 sx = (x - room->x) >> 10; + int32 sz = (z - room->z) >> 10; + sector = room->sectors + sx * room->zSectors + sz; } + return sector; +} - while (sector->roomAbove != NO_ROOM && pos->y < (sector->ceiling << 8)) { +const RoomInfo::Sector* getSectorAbove(const RoomInfo::Sector* sector, int32 x, int32 z) +{ + while (sector->roomAbove != NO_ROOM) + { + Room* room = rooms + sector->roomAbove; + int32 sx = (x - room->x) >> 10; + int32 sz = (z - room->z) >> 10; + sector = room->sectors + sx * room->zSectors + sz; + } + return sector; +} + +int32 getRoomIndex(int32 roomIndex, int32 x, int32 y, int32 z) +{ + const RoomInfo::Sector* sector = getSector(roomIndex, x, z); + + while (1) + { + if (!sector->floorIndex) + break; + + // always in this order + // - floor + // - ceiling + // - portal + // - other + + FloorData* fd = (FloorData*)(floors + sector->floorIndex); + FloorData::Command cmd = (fd++)->cmd; + + if (cmd.func == FLOOR_TYPE_FLOOR) // skip floor + { + if (cmd.end) break; + fd++; + cmd = (fd++)->cmd; + } + + if (cmd.func == FLOOR_TYPE_CEILING) // skip ceiling + { + if (cmd.end) break; + fd++; + cmd = (fd++)->cmd; + } + + if (cmd.func != FLOOR_TYPE_PORTAL) // no portal + break; + + roomIndex = fd->value; + sector = getSector(roomIndex, x, z); + }; + + while (sector->roomAbove != NO_ROOM && y < (sector->ceiling << 8)) + { roomIndex = sector->roomAbove; - sector = getSector(roomIndex, pos->x, pos->z); + sector = getSector(roomIndex, x, z); } - while (sector->roomBelow != 0xFF && pos->y >= (sector->floor << 8)) { + while (sector->roomBelow != NO_ROOM && y >= (sector->floor << 8)) + { roomIndex = sector->roomBelow; - sector = getSector(roomIndex, pos->x, pos->z); + sector = getSector(roomIndex, x, z); } return roomIndex; } -bool checkPortal(int32 roomIndex, const RoomInfo::Portal* portal) { - Room &room = rooms[roomIndex]; - - vec3i d; - d.x = portal->v[0].x - camera.pos.x + room.x; - d.y = portal->v[0].y - camera.pos.y; - d.z = portal->v[0].z - camera.pos.z + room.z; - - if (DP33(portal->n, d) >= 0) { - return false; - } - - int32 x0 = room.clip.x1; - int32 y0 = room.clip.y1; - int32 x1 = room.clip.x0; - int32 y1 = room.clip.y0; - - int32 znear = 0, zfar = 0; - - Matrix &m = matrixGet(); - - vec3i pv[4]; - - for (int32 i = 0; i < 4; i++) { - const vec3s &v = portal->v[i]; - - int32 x = DP43(m[0], v); - int32 y = DP43(m[1], v); - int32 z = DP43(m[2], v); - - pv[i].x = x; - pv[i].y = y; - pv[i].z = z; - - if (z <= VIEW_MIN_F) { - znear++; - continue; - } - - if (z >= VIEW_MAX_F) { - zfar++; - } - - if (z != 0) { - z >>= FOV_SHIFT; - x = (x / z) + (FRAME_WIDTH / 2); - y = (y / z) + (FRAME_HEIGHT / 2); - } else { - x = (x > 0) ? clip.x1 : clip.x0; - y = (y > 0) ? clip.y1 : clip.y0; - } - - if (x < x0) x0 = x; - if (x > x1) x1 = x; - if (y < y0) y0 = y; - if (y > y1) y1 = y; - } - - if (znear == 4 || zfar == 4) return false; - - if (znear) { - vec3i *a = pv; - vec3i *b = pv + 3; - for (int32 i = 0; i < 4; i++) { - if ((a->z < 0) ^ (b->z < 0)) { - if (a->x < 0 && b->x < 0) { - x0 = 0; - } else if (a->x > 0 && b->x > 0) { - x1 = FRAME_WIDTH; - } else { - x0 = 0; - x1 = FRAME_WIDTH; - } - - if (a->y < 0 && b->y < 0) { - y0 = 0; - } else if (a->y > 0 && b->y > 0) { - y1 = FRAME_HEIGHT; - } else { - y0 = 0; - y1 = FRAME_HEIGHT; - } - } - b = a; - a++; - } - } - - if (x0 < room.clip.x0) x0 = room.clip.x0; - if (x1 > room.clip.x1) x1 = room.clip.x1; - if (y0 < room.clip.y0) y0 = room.clip.y0; - if (y1 > room.clip.y1) y1 = room.clip.y1; - - if (x0 >= x1 || y0 >= y1) return false; - - Room &nextRoom = rooms[portal->roomIndex]; - - if (x0 < nextRoom.clip.x0) nextRoom.clip.x0 = x0; - if (x1 > nextRoom.clip.x1) nextRoom.clip.x1 = x1; - if (y0 < nextRoom.clip.y0) nextRoom.clip.y0 = y0; - if (y1 > nextRoom.clip.y1) nextRoom.clip.y1 = y1; - - if (!nextRoom.visible) { - nextRoom.visible = true; - visRooms[visRoomsCount++] = portal->roomIndex; - } - - return true; -} - -void getVisibleRooms(int32 roomIndex) +void getTriggerFloorData(const RoomInfo::Sector* sector, int32 x, int32 y, int32 z, int32* floor, int32* ceiling) { - const Room* room = rooms + roomIndex; + if (!sector->floorIndex) + return; - matrixPush(); - matrixTranslateAbs(room->x, 0, room->z); + FloorData::Command cmd; + FloorData* fd = (FloorData*)(floors + sector->floorIndex); - for (int32 i = 0; i < room->pCount; i++) - { - const RoomInfo::Portal* portal = room->portals + i; - - if (checkPortal(roomIndex, portal)) + do { + cmd = (fd++)->cmd; + + switch (cmd.func) { - getVisibleRooms(portal->roomIndex); + case FLOOR_TYPE_PORTAL: + case FLOOR_TYPE_FLOOR: + case FLOOR_TYPE_CEILING: + { + fd++; + break; + } + + case FLOOR_TYPE_TRIGGER: + { + fd++; + FloorData::TriggerCommand trigger; + + do { + trigger = (fd++)->triggerCmd; + + if (trigger.action == TRIGGER_ACTION_ACTIVATE) + { + getItemFloorData(items + trigger.args, x, y, z, floor, ceiling); + } + + if (trigger.action == TRIGGER_ACTION_CAMERA_SWITCH) + { + trigger = (fd++)->triggerCmd; // skip camera index + } + + } while (!trigger.end); + + break; + } + + case FLOOR_TYPE_LAVA: + break; + } + + } while (!cmd.end); +} + +FloorData floorSlant; + +int32 getFloor(const RoomInfo::Sector* sector, int32 x, int32 y, int32 z) +{ + const RoomInfo::Sector* lowerSector = getSectorBelow(sector, x, z); + + int32 floor = lowerSector->floor << 8; + + floorSlant.value = 0; + + if (lowerSector->floorIndex) + { + FloorData* fd = (FloorData*)(floors + lowerSector->floorIndex); + FloorData::Command cmd = (fd++)->cmd; + + if (cmd.func == FLOOR_TYPE_FLOOR) // found floor + { + floorSlant = *fd; + int32 sx = fd->slantX; + int32 sz = fd->slantZ; + int32 dx = x & 1023; + int32 dz = z & 1023; + floor -= sx * (sx < 0 ? dx : (dx - 1023)) >> 2; + floor -= sz * (sz < 0 ? dz : (dz - 1023)) >> 2; } } - matrixPop(); + getTriggerFloorData(lowerSector, x, y, z, &floor, NULL); + + return floor; } -void drawRooms() +int32 getCeiling(const RoomInfo::Sector* sector, int32 x, int32 y, int32 z) { - rooms[camera.room].clip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT }; - visRoomsCount = 0; - visRooms[visRoomsCount++] = camera.room; + const RoomInfo::Sector* upperSector = getSectorAbove(sector, x, z); - getVisibleRooms(camera.room); + int32 ceiling = upperSector->ceiling << 8; - while (visRoomsCount--) + if (upperSector->floorIndex) { - drawRoom(visRooms[visRoomsCount]); - } -} + FloorData* fd = (FloorData*)(floors + upperSector->floorIndex); + FloorData::Command cmd = (fd++)->cmd; -void move(Item* item, const Anim* anim) -{ - int32 speed = anim->speed; - - if (item->flags.gravity) - { - speed += anim->accel * (item->frameIndex - anim->frameBegin - 1); - item->hSpeed -= speed >> 16; - speed += anim->accel; - item->hSpeed += speed >> 16; - - item->vSpeed += (item->vSpeed < 128) ? GRAVITY : 1; - - item->pos.y += item->vSpeed; - } else { - speed += anim->accel * (item->frameIndex - anim->frameBegin); - - item->hSpeed = speed >> 16; - } - - item->pos.x += phd_sin(item->angleY) * item->hSpeed >> FIXED_SHIFT; - item->pos.z += phd_cos(item->angleY) * item->hSpeed >> FIXED_SHIFT; -} - -void animChange(Item* item, const Anim* anim) -{ - if (!anim->scCount) return; - // check state change -} - -void animCommand(bool fx, Item* item, const Anim* anim) -{ - if (!anim->acCount) return; - // check animation command -} - -const Anim* animSet(Item* item, int32 animIndex, int32 frameIndex) -{ - item->animIndex = animIndex; - item->frameIndex = frameIndex; - item->state = anims[animIndex].state; - - return anims + animIndex; -} - -void animUpdate(Item* item) -{ - const Anim* anim = anims + item->animIndex; - - item->frameIndex++; - - animChange(item, anim); - - if (item->frameIndex > anim->frameEnd) - { - animCommand(false, item, anim); - anim = animSet(item, anim->nextAnimIndex, anim->nextFrameIndex); - } - - animCommand(true, item, anim); - - //move(item, anim); -} - -void updateItems() -{ - int32 itemIndex = firstActive; - while (itemIndex != NO_ITEM) - { - Item* item = items + itemIndex; - - if (modelsMap[item->type] != NO_MODEL) { - animUpdate(item); + if (cmd.func == FLOOR_TYPE_FLOOR) // skip floor + { + fd++; + cmd = (fd++)->cmd; } - itemIndex = item->nextActive; + if (cmd.func == FLOOR_TYPE_CEILING) // found ceiling + { + int32 sx = fd->slantX; + int32 sz = fd->slantZ; + int32 dx = x & 1023; + int32 dz = z & 1023; + ceiling -= sx * (sx < 0 ? (dx - 1023) : dx) >> 2; + ceiling += sz * (sz < 0 ? dz : (dz - 1023)) >> 2; + } } + + const RoomInfo::Sector* lowerSector = getSectorBelow(sector, x, z); + + getTriggerFloorData(lowerSector, x, y, z, NULL, &ceiling); + + return ceiling; } + #endif diff --git a/src/platform/gba/main.cpp b/src/platform/gba/main.cpp index 87dcf8b..605c42f 100644 --- a/src/platform/gba/main.cpp +++ b/src/platform/gba/main.cpp @@ -1,27 +1,28 @@ -#include "common.h" -#include "level.h" -#include "camera.h" +#if defined(_WIN32) + void* LEVEL1_PHD; +#elif defined(__GBA__) + #include "LEVEL1_PHD.h" +#elif defined(__TNS__) + void* LEVEL1_PHD; +#endif + +#include "game.h" + +Game game; #if defined(_WIN32) - uint8* LEVEL1_PHD; + uint32 SCREEN[FRAME_WIDTH * FRAME_HEIGHT]; - uint32 SCREEN[WIDTH * HEIGHT]; - - extern uint16 fb[WIDTH * HEIGHT]; + HWND hWnd; LARGE_INTEGER g_timer; LARGE_INTEGER g_current; - #define WND_SCALE 4 + #define WND_WIDTH 240*4 + #define WND_HEIGHT 160*4 #elif defined(__GBA__) - #include "LEVEL1_PHD.h" - - extern uint32 fb; + // #elif defined(__TNS__) - uint8* LEVEL1_PHD; - - extern uint16 fb[WIDTH * HEIGHT]; - unsigned int osTime; volatile unsigned int *timerBUS; volatile unsigned int *timerCLK; @@ -78,8 +79,6 @@ } #endif -bool keys[IK_MAX] = {}; - int32 fps; int32 frameIndex = 0; int32 fpsCounter = 0; @@ -87,188 +86,146 @@ int32 fpsCounter = 0; #ifdef PROFILE uint32 dbg_transform; uint32 dbg_poly; - uint32 dbg_sort; uint32 dbg_flush; uint32 dbg_vert_count; uint32 dbg_poly_count; #endif -void update(int32 frames) { - for (int32 i = 0; i < frames; i++) { - updateItems(); - camera.update(); +EWRAM_DATA ALIGN16 uint8 soundBuffer[2 * SND_SAMPLES + 32]; // 32 bytes of silence for DMA overrun while interrupt +uint32 curSoundBuffer = 0; + +#if defined(_WIN32) +HWAVEOUT waveOut; +WAVEFORMATEX waveFmt = { WAVE_FORMAT_PCM, 1, SND_OUTPUT_FREQ, SND_OUTPUT_FREQ, 1, 8, sizeof(waveFmt) }; +WAVEHDR waveBuf[2]; + +void soundInit() { + sound.init(); + + if (waveOutOpen(&waveOut, WAVE_MAPPER, &waveFmt, (INT_PTR)hWnd, 0, CALLBACK_WINDOW) != MMSYSERR_NOERROR) { + return; + } + + memset(&waveBuf, 0, sizeof(waveBuf)); + for (int i = 0; i < 2; i++) { + WAVEHDR *waveHdr = waveBuf + i; + waveHdr->dwBufferLength = SND_SAMPLES; + waveHdr->lpData = (LPSTR)(soundBuffer + i * SND_SAMPLES); + waveOutPrepareHeader(waveOut, waveHdr, sizeof(WAVEHDR)); + waveOutWrite(waveOut, waveHdr, sizeof(WAVEHDR)); } } -#ifdef TEST -void faceAddQuad(uint32 flags, const Index* indices, int32 startVertex); +void soundFill() { + WAVEHDR *waveHdr = waveBuf + curSoundBuffer; + waveOutUnprepareHeader(waveOut, waveHdr, sizeof(WAVEHDR)); + sound.fill((uint8*)waveHdr->lpData, SND_SAMPLES); + waveOutPrepareHeader(waveOut, waveHdr, sizeof(WAVEHDR)); + waveOutWrite(waveOut, waveHdr, sizeof(WAVEHDR)); + curSoundBuffer ^= 1; +} +#elif defined(__GBA__) +void soundInit() +{ + sound.init(); -extern Vertex gVertices[MAX_VERTICES]; - -INLINE int32 classify(const Vertex* v) { - return (v->x < clip.x0 ? 1 : 0) | - (v->x > clip.x1 ? 2 : 0) | - (v->y < clip.y0 ? 4 : 0) | - (v->y > clip.y1 ? 8 : 0); + REG_SOUNDCNT_X = SSTAT_ENABLE; + REG_SOUNDCNT_H = SDS_ATMR0 | SDS_A100 | SDS_AL | SDS_AR | SDS_ARESET; + REG_TM0D = 65536 - (16777216 / SND_OUTPUT_FREQ); + REG_TM0CNT = TM_ENABLE; + REG_DMA1DAD = (u32)®_FIFO_A; } -void drawTest() { - static Rect testClip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT }; - static int32 testTile = 707; // 712 - -#ifdef _WIN32 - Sleep(16); - - int dx = 0; - int dy = 0; - - if (GetAsyncKeyState(VK_LEFT)) dx--; - if (GetAsyncKeyState(VK_RIGHT)) dx++; - if (GetAsyncKeyState(VK_UP)) dy--; - if (GetAsyncKeyState(VK_DOWN)) dy++; - - if (GetAsyncKeyState('T')) { - testClip.x0 += dx; - testClip.y0 += dy; +void soundFill() +{ + if (curSoundBuffer == 1) { + REG_DMA1CNT = 0; + REG_DMA1SAD = (u32)soundBuffer; + REG_DMA1CNT = DMA_DST_FIXED | DMA_REPEAT | DMA_16 | DMA_AT_FIFO | DMA_ENABLE; } - if (GetAsyncKeyState('B')) { - testClip.x1 += dx; - testClip.y1 += dy; - } - - if (GetAsyncKeyState('U')) { - testTile += dx; - if (testTile < 0) testTile = 0; - if (testTile >= texturesCount) testTile = texturesCount - 1; - } -#endif - - clip = testClip; - - gVertices[0].x = 50 + 50; - gVertices[0].y = 50; - - gVertices[1].x = FRAME_WIDTH - 50 - 50; - gVertices[1].y = 50; - - gVertices[2].x = FRAME_WIDTH - 50; - gVertices[2].y = FRAME_HEIGHT - 50; - - gVertices[3].x = 50; - gVertices[3].y = FRAME_HEIGHT - 50; - - for (int i = 0; i < 4; i++) { - gVertices[i].z = 100; - gVertices[i].g = 128; - gVertices[i].clip = classify(gVertices + i); - } - gVerticesCount = 4; - - Index indices[] = { 0, 1, 2, 3, 0, 2, 3 }; - - faceAddQuad(testTile, indices, 0); - -#ifdef _WIN32 - for (int y = 0; y < FRAME_HEIGHT; y++) { - for (int x = 0; x < FRAME_WIDTH; x++) { - if (x == clip.x0 || x == clip.x1 - 1 || y == clip.y0 || y == clip.y1 - 1) - fb[y * FRAME_WIDTH + x] = 255; - } - } -#endif - - flush(); + sound.fill(soundBuffer + curSoundBuffer * SND_SAMPLES, SND_SAMPLES); + curSoundBuffer ^= 1; } #endif -void render() { - clear(); - - #ifdef TEST - #ifdef __GBA__ - VBlankIntrWait(); - #endif - - int32 cycles = 0; - PROFILE_START(); - drawTest(); - PROFILE_STOP(cycles); - - drawNumber(cycles, FRAME_WIDTH, 32); - #else - #ifdef PROFILE - dbg_transform = 0; - dbg_poly = 0; - dbg_sort = 0; - dbg_flush = 0; - dbg_vert_count = 0; - dbg_poly_count = 0; - #endif - - drawRooms(); - - #ifdef PROFILE - drawNumber(dbg_transform, FRAME_WIDTH, 32); - drawNumber(dbg_poly, FRAME_WIDTH, 48); - drawNumber(dbg_sort, FRAME_WIDTH, 64); - drawNumber(dbg_flush, FRAME_WIDTH, 80); - drawNumber(dbg_vert_count, FRAME_WIDTH, 96); - drawNumber(dbg_poly_count, FRAME_WIDTH, 112); - #endif - - #endif - - drawNumber(fps, FRAME_WIDTH, 16); -} - #if defined(_WIN32) HDC hDC; void blit() { - for (int i = 0; i < WIDTH * HEIGHT; i++) { +#ifdef ROTATE90_MODE + for (int i = 0; i < FRAME_WIDTH * FRAME_HEIGHT; i++) { + int32 x = FRAME_HEIGHT - (i % FRAME_HEIGHT) - 1; + int32 y = i / FRAME_HEIGHT; + uint16 c = ((uint16*)fb)[x * FRAME_WIDTH + y]; + SCREEN[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000; + } + const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), FRAME_HEIGHT, -FRAME_WIDTH, 1, 32, BI_RGB, 0, 0, 0, 0, 0 }; + StretchDIBits(hDC, 0, 0, WND_WIDTH, WND_HEIGHT, 0, 0, FRAME_HEIGHT, FRAME_WIDTH, SCREEN, &bmi, DIB_RGB_COLORS, SRCCOPY); +#else + for (int i = 0; i < FRAME_WIDTH * FRAME_HEIGHT; i++) { uint16 c = ((uint16*)fb)[i]; SCREEN[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000; } - - const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), WIDTH, -HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 }; - StretchDIBits(hDC, 0, 0, 240 * WND_SCALE, 160 * WND_SCALE, 0, 0, WIDTH, HEIGHT, SCREEN, &bmi, DIB_RGB_COLORS, SRCCOPY); + const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), FRAME_WIDTH, -FRAME_HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 }; + StretchDIBits(hDC, 0, 0, WND_WIDTH, WND_HEIGHT, 0, 0, FRAME_WIDTH, FRAME_HEIGHT, SCREEN, &bmi, DIB_RGB_COLORS, SRCCOPY); +#endif } LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_DESTROY : + { PostQuitMessage(0); break; + } + case WM_KEYDOWN : - case WM_KEYUP : { - InputKey key = IK_MAX; + case WM_KEYUP : + { + InputKey key = IK_NONE; switch (wParam) { - case VK_UP : key = IK_UP; break; - case VK_RIGHT : key = IK_RIGHT; break; - case VK_DOWN : key = IK_DOWN; break; - case VK_LEFT : key = IK_LEFT; break; - case 'Z' : key = IK_A; break; - case 'X' : key = IK_B; break; - case 'A' : key = IK_L; break; - case 'S' : key = IK_R; break; + case VK_UP : key = IK_UP; break; + case VK_RIGHT : key = IK_RIGHT; break; + case VK_DOWN : key = IK_DOWN; break; + case VK_LEFT : key = IK_LEFT; break; + case 'Z' : key = IK_B; break; + case 'X' : key = IK_A; break; + case 'A' : key = IK_L; break; + case 'S' : key = IK_R; break; + case VK_RETURN : key = IK_START; break; + case VK_SPACE : key = IK_SELECT; break; } - if (key != IK_MAX) { - keys[key] = msg != WM_KEYUP; + + if (msg != WM_KEYUP) { + keys |= key; + } else { + keys &= ~key; } break; } + + case MM_WOM_DONE : + { + soundFill(); + break; + } + default : return DefWindowProc(hWnd, msg, wParam, lParam); } return 0; } -#endif + +#elif defined(__GBA__) void vblank() { frameIndex++; + soundFill(); } +#endif + int main(void) { #if defined(_WIN32) || defined(__TNS__) { @@ -293,27 +250,26 @@ int main(void) { } #elif defined(__GBA__) // set low latency mode via WAITCNT register (thanks to GValiente) - #define REG_WAITCNT_NV *(u16*)(REG_BASE + 0x0204) - REG_WAITCNT_NV |= (0x0008 | 0x0010 | 0x4000); + REG_WSCNT = WS_ROM0_N2 | WS_ROM0_S1 | WS_PREFETCH; #endif - initLUT(); - - readLevel(LEVEL1_PHD); + game.init(); #if defined(_WIN32) - RECT r = { 0, 0, 240 * WND_SCALE, 160 * WND_SCALE }; + RECT r = { 0, 0, WND_WIDTH, WND_HEIGHT }; AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, false); int wx = (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2; int wy = (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2; - HWND hWnd = CreateWindow("static", "OpenLara GBA", WS_OVERLAPPEDWINDOW, wx + r.left, wy + r.top, r.right - r.left, r.bottom - r.top, 0, 0, 0, 0); + hWnd = CreateWindow("static", "OpenLara GBA", WS_OVERLAPPEDWINDOW, wx + r.left, wy + r.top, r.right - r.left, r.bottom - r.top, 0, 0, 0, 0); hDC = GetDC(hWnd); SetWindowLong(hWnd, GWL_WNDPROC, (LONG)&wndProc); ShowWindow(hWnd, SW_SHOWDEFAULT); + soundInit(); + MSG msg; int startTime = GetTickCount() - 33; @@ -326,51 +282,68 @@ int main(void) { } else { int frame = (GetTickCount() - startTime) / 33; - update(frame - lastFrame); + game.update(frame - lastFrame); lastFrame = frame; - render(); + game.render(); blit(); } } while (msg.message != WM_QUIT); #elif defined(__GBA__) - irqInit(); - irqSet(IRQ_VBLANK, vblank); - irqEnable(IRQ_VBLANK); + irq_init(NULL); + irq_add(II_VBLANK, vblank); + irq_enable(II_VBLANK); - uint16 mode = BG2_ON | BACKBUFFER; + // Undocumented - Green Swap trick for fake-AA feature (thanks to GValiente) + // https://mgba-emu.github.io/gbatek/#4000002h---undocumented---green-swap-rw + //uint16 &GreenSwap = *(uint16*)0x4000002; + //GreenSwap = 1; - mode |= MODE_5; + soundInit(); + uint16 mode = DCNT_MODE5 | DCNT_BG2 | DCNT_PAGE; + +#ifdef ROTATE90_MODE + REG_BG2PA = 0; + REG_BG2PB = (1 << 8); + REG_BG2PC = -(1 << 7); + REG_BG2PD = 0; + REG_BG2Y = (FRAME_HEIGHT << 8) - 128; +#else REG_BG2PA = 256 - 64 - 16 - 4 - 1; REG_BG2PD = 256 - 48 - 2; +#endif int32 lastFrameIndex = -1; while (1) { //VBlankIntrWait(); + REG_DISPCNT = (mode ^= DCNT_PAGE); - SetMode(mode ^= BACKBUFFER); fb ^= 0xA000; - scanKeys(); - uint16 key = keysDown() | keysHeld(); - keys[IK_UP] = (key & KEY_UP); - keys[IK_RIGHT] = (key & KEY_RIGHT); - keys[IK_DOWN] = (key & KEY_DOWN); - keys[IK_LEFT] = (key & KEY_LEFT); - keys[IK_A] = (key & KEY_A); - keys[IK_B] = (key & KEY_B); - keys[IK_L] = (key & KEY_L); - keys[IK_R] = (key & KEY_R); + { // input + keys = 0; + key_poll(); + if (key_is_down(KEY_UP)) keys |= IK_UP; + if (key_is_down(KEY_RIGHT)) keys |= IK_RIGHT; + if (key_is_down(KEY_DOWN)) keys |= IK_DOWN; + if (key_is_down(KEY_LEFT)) keys |= IK_LEFT; + if (key_is_down(KEY_A)) keys |= IK_A; + if (key_is_down(KEY_B)) keys |= IK_B; + if (key_is_down(KEY_L)) keys |= IK_L; + if (key_is_down(KEY_R)) keys |= IK_R; + if (key_is_down(KEY_START)) keys |= IK_START; + if (key_is_down(KEY_SELECT)) keys |= IK_SELECT; + } int32 frame = frameIndex / 2; - update(frame - lastFrameIndex); + game.update(frame - lastFrameIndex); lastFrameIndex = frame; - render(); + game.render(); fpsCounter++; if (frameIndex >= 60) { @@ -400,39 +373,40 @@ int main(void) { while (1) { - inputUpdate(); - - if (keyDown(KEY_NSPIRE_ESC)) { - break; + keys = 0; + + inputUpdate(); + + if (keyDown(KEY_NSPIRE_ESC)) + { + break; + } + + if (touchInfo && touchReport.contact) + { + float tx = float(touchReport.x) / float(touchInfo->width) * 2.0f - 1.0f; + float ty = float(touchReport.y) / float(touchInfo->height) * 2.0f - 1.0f; + + if (tx < -0.5f) keys |= IK_LEFT; + if (tx > 0.5f) keys |= IK_RIGHT; + if (ty > 0.5f) keys |= IK_UP; + if (ty < -0.5f) keys |= IK_DOWN]; + } + + if (keyDown(KEY_NSPIRE_2)) keys |= IK_A; + if (keyDown(KEY_NSPIRE_3)) keys |= IK_B; + if (keyDown(KEY_NSPIRE_7)) keys |= IK_L; + if (keyDown(KEY_NSPIRE_9)) keys |= IK_R; + if (keyDown(KEY_NSPIRE_ENTER)) keys |= IK_START; + if (keyDown(KEY_NSPIRE_SPACE)) keys |= IK_SELECT; } - if (touchInfo && touchReport.contact) - { - float tx = float(touchReport.x) / float(touchInfo->width) * 2.0f - 1.0f; - float ty = float(touchReport.y) / float(touchInfo->height) * 2.0f - 1.0f; - - keys[IK_LEFT] = tx < -0.5f; - keys[IK_RIGHT] = tx > 0.5f; - keys[IK_UP] = ty > 0.5f; - keys[IK_DOWN] = ty < -0.5f; - } else { - keys[IK_LEFT] = - keys[IK_RIGHT] = - keys[IK_UP] = - keys[IK_DOWN] = false; - } - - keys[IK_A] = keyDown(KEY_NSPIRE_2); - keys[IK_B] = keyDown(KEY_NSPIRE_3); - keys[IK_L] = keyDown(KEY_NSPIRE_7); - keys[IK_R] = keyDown(KEY_NSPIRE_9); - int time = GetTickCount() - startTime; - update((time - lastTime) / 16); + game.update((time - lastTime) / 16); lastTime = time; - render(); + game.render(); lcd_blit(fb, SCR_320x240_8); //msleep(16); diff --git a/src/platform/gba/rasterizer.s b/src/platform/gba/rasterizer.s new file mode 100644 index 0000000..71f5d3d --- /dev/null +++ b/src/platform/gba/rasterizer.s @@ -0,0 +1,396 @@ +.section .iwram +.arm + +// Thanks to Gericom + +.macro LIGHTMAP_INDEX res, g + and \res, \g, #0x1F00 // res = g & 0x1F00 +.endm + +.macro LIGHTMAP_OFFSET res, index, lightmap + add \res, \res, \lightmap // res += lightmap +.endm + +.macro FETCH_LIGHTMAP res, index, lightmap + ldrb \res, [\lightmap, \index] // res = lightmap[index] +.endm + +.macro FETCH_TILE res, uv, tile + and \res, \uv, #0xFF00 + orr \res, \res, \uv, lsr #24 // res = (uv & 0xFF00) | (uv >> 24) + ldrb \res, [\tile, \res] // res = tile[res] +.endm + +.macro FETCH_PALETTE res, idx, pal + mov \res, \idx, lsl #1 + ldrh \res, [\pal, \res] // res = pal[index] +.endm + +.macro PUT_PIXEL buf, col, off + strh \col, [\buf, #\off] // buf[off/2] = col +.endm + +.macro INC_PIXEL buf, col, off + ldrh \col, [\buf, #\off] + add \col, \col, #4 + strh \col, [\buf, #\off] // buf[off/2] = col +.endm + +.macro PUT_PIXEL_A buf, col, off + cmp \col, #0 // if (col != 0) + strneh \col, [\buf, #\off] // buf[off/2] = col +.endm + +.macro CHECK_WIDTH width, stack + subs \width, \width, #1 + ldmmifd sp!, \stack // if (--width < 0) return +.endm + +.macro PUT_PIXELS_F width, pix0, pix1, stack + PUT_PIXEL r0, r2, \pix0 + + CHECK_WIDTH \width, "\stack" + + PUT_PIXEL r0, r2, \pix1 +.endm + +.macro PUT_PIXELS_G width, pix0, pix1, stack +// r2 - g +// r3 - dgdx +// r11 - palette +// lr - lightmap + LIGHTMAP_INDEX r6, r2 + FETCH_LIGHTMAP r6, r6, lr + FETCH_PALETTE r6, r6, r11 + + PUT_PIXEL r0, r6, \pix0 + + CHECK_WIDTH \width, "\stack" + + PUT_PIXEL r0, r6, \pix1 + + add r2, r2, r3 // g += dgdx +.endm + +.macro PUT_PIXELS_FT width, pix0, pix1, stack + FETCH_TILE r6, r2, r12 + FETCH_LIGHTMAP r6, r6, lr + FETCH_PALETTE r6, r6, r11 + PUT_PIXEL r0, r6, \pix0 + + CHECK_WIDTH \width, "\stack" + + add r2, r2, r3 // t += dtdx + + FETCH_TILE r6, r2, r12 + FETCH_LIGHTMAP r6, r6, lr + FETCH_PALETTE r6, r6, r11 + PUT_PIXEL r0, r6, \pix1 + + add r2, r2, r3 // t += dtdx +.endm + +.macro PUT_PIXELS_GT width, pix0, pix1, stack + LIGHTMAP_INDEX r7, r2 + LIGHTMAP_OFFSET r7, r7, lr + + FETCH_TILE r6, r3, r12 + FETCH_LIGHTMAP r6, r6, r7 + FETCH_PALETTE r6, r6, r11 + PUT_PIXEL r0, r6, \pix0 + + CHECK_WIDTH \width, "\stack" + + add r3, r3, r5 // t += dtdx + + FETCH_TILE r6, r3, r12 + FETCH_LIGHTMAP r6, r6, r7 + FETCH_PALETTE r6, r6, r11 + PUT_PIXEL r0, r6, \pix1 + + add r3, r3, r5 // t += dtdx + add r2, r2, r4 // g += dgdx +.endm + +.macro PUT_PIXELS_FTA width, pix0, pix1, stack + FETCH_TILE r6, r2, r12 + FETCH_LIGHTMAP r6, r6, lr + FETCH_PALETTE r6, r6, r11 + PUT_PIXEL_A r0, r6, \pix0 + + CHECK_WIDTH \width, "\stack" + + add r2, r2, r3 // t += dtdx + + FETCH_TILE r6, r2, r12 + FETCH_LIGHTMAP r6, r6, lr + FETCH_PALETTE r6, r6, r11 + PUT_PIXEL_A r0, r6, \pix1 + + add r2, r2, r3 // t += dtdx +.endm + +.macro PUT_PIXELS_GTA width, pix0, pix1, stack + LIGHTMAP_INDEX r7, r2 + LIGHTMAP_OFFSET r7, r7, lr + + FETCH_TILE r6, r3, r12 + FETCH_LIGHTMAP r6, r6, r7 + FETCH_PALETTE r6, r6, r11 + PUT_PIXEL_A r0, r6, \pix0 + + CHECK_WIDTH \width, "\stack" + + add r3, r3, r5 // t += dtdx + + FETCH_TILE r6, r3, r12 + FETCH_LIGHTMAP r6, r6, r7 + FETCH_PALETTE r6, r6, r11 + PUT_PIXEL_A r0, r6, \pix1 + + add r3, r3, r5 // t += dtdx + add r2, r2, r4 // g += dgdx +.endm + +.macro SCANLINE buf, width, func, stack + sub \width, \width, #1 //--width +1: + \func \width, 0, 2, "\stack" // 0, 1 + CHECK_WIDTH \width, "\stack" + + \func \width, 4, 6, "\stack" // 2, 3 + CHECK_WIDTH \width, "\stack" + + \func \width, 8, 10, "\stack" // 4, 5 + CHECK_WIDTH \width, "\stack" + + \func \width, 12, 14, "\stack" // 6, 7 + + add \buf, \buf, #16 // buf += 8px + + subs \width, \width, #1 + bpl 1b + ldmfd sp!, \stack +.endm + +.global scanlineF_asm +scanlineF_asm: +// r0 = pixel +// r1 = width +// r2 = color + stmfd sp!, {lr} + + SCANLINE r0, r1, PUT_PIXELS_F, "{pc}" + +.global scanlineG_asm +scanlineG_asm: +// r0 = pixel +// r1 = width +// r2 = g +// r3 = dgdx + stmfd sp!, {r6,r11,lr} + ldr lr,= ft_lightmap + ldr r11,= palette + ldr lr, [lr] // ft_lightmap = *ft_lightmap + + SCANLINE r0, r1, PUT_PIXELS_G, "{r6,r11,pc}" + +.global scanlineFT_asm +scanlineFT_asm: +// r0 = pixel +// r1 = width +// r2 = t +// r3 = dtdx + stmfd sp!, {r6,r7,r11,lr} + ldr r11,= palette + ldr r12,= tile + ldr lr,= ft_lightmap + ldr r12, [r12] // tile = *tile + ldr lr, [lr] // ft_lightmap = *ft_lightmap + + SCANLINE r0, r1, PUT_PIXELS_FT, "{r6,r7,r11,pc}" + +.global scanlineGT_asm +scanlineGT_asm: +// r0 = pixel +// r1 = width +// r2 = g +// r3 = t + stmfd sp!, {r4,r5,r6,r7,r11,lr} +stack_offset = 6 * 4 +dgdx = stack_offset + 0 +dtdx = stack_offset + 4 + ldr r4, [sp, #dgdx] + ldr r5, [sp, #dtdx] + ldr r11,= palette + ldr r12,= tile + ldr lr,= lightmap + ldr r12, [r12] + + SCANLINE r0, r1, PUT_PIXELS_GT, "{r4,r5,r6,r7,r11,pc}" + +.global scanlineFTA_asm +scanlineFTA_asm: +// r0 = pixel +// r1 = width +// r2 = t +// r3 = dtdx + stmfd sp!, {r6,r7,r11,lr} + ldr r11,= palette + ldr r12,= tile + ldr lr,= ft_lightmap + ldr r12, [r12] // tile = *tile + ldr lr, [lr] // ft_lightmap = *ft_lightmap + + SCANLINE r0, r1, PUT_PIXELS_FTA, "{r6,r7,r11,pc}" + +.global scanlineGTA_asm +scanlineGTA_asm: +// r0 = pixel +// r1 = width +// r2 = g +// r3 = t + stmfd sp!, {r4,r5,r6,r7,r11,lr} +stack_offset = 6 * 4 +dgdx = stack_offset + 0 +dtdx = stack_offset + 4 + ldr r4, [sp, #dgdx] + ldr r5, [sp, #dtdx] + ldr r11,= palette + ldr r12,= tile + ldr lr,= lightmap + ldr r12, [r12] + + SCANLINE r0, r1, PUT_PIXELS_GTA, "{r4,r5,r6,r7,r11,pc}" + + + +//------------ WORK IN PROGRESS --------------- + +.macro CHECK_WIDTH_INNER width + subs \width, \width, #1 + bmi 2f // if (--width < 0) return +.endm + +.macro PUT_PIXELS_F_INNER buf, width, color, pix0, pix1 + PUT_PIXEL \buf, \color, \pix0 + + CHECK_WIDTH_INNER \width + + PUT_PIXEL \buf, \color, \pix1 +.endm + +.macro SCANLINE_INNER buf, width, color, func + sub \width, \width, #1 //--width +3: + \func \buf, \width, \color, 0, 2 // 0, 1 + CHECK_WIDTH_INNER \width + + \func \buf, \width, \color, 4, 6 // 2, 3 + CHECK_WIDTH_INNER \width + + \func \buf, \width, \color, 8, 10 // 4, 5 + CHECK_WIDTH_INNER \width + + \func \buf, \width, \color, 12, 14 // 6, 7 + + add \buf, \buf, #16 // buf += 8px + + subs \width, \width, #1 + bpl 3b +.endm + + +.global rasterizeF_inner_asm +rasterizeF_inner_asm: +// r0 = pixel +// r1 = L edge +// r2 = R edge +// r3 = color + stmfd sp!, {r4,r5,r6,r7,r8,lr} +// ip - h + ldr ip, [r1] // ip = L.h + ldr lr, [r2] // lr = R.h + cmp lr, ip + movlt r4, lr // if (R.h < L.h) h = R.h + movge r4, ip // else h = L.h + sub ip, ip, r4 + sub lr, lr, r4 + str ip, [r1] // L.h -= h + str lr, [r2] // R.h -= h + + ldr ip, [r1, #4] // ip = L.x + ldr lr, [r2, #4] // lr = R.x + ldr r7, [r1, #16] // r1 = L.dx + ldr r8, [r2, #16] // r2 = R.dx + + sub r4, r4, #1 +1: + lsr r6, ip, #16 // r6 = x1 = (L.x >> 16) + rsbs r5, r6, lr, lsr #16 // r5 = width = (R.x >> 16) - r6 + ble 2f // if (width <= 0) go next scanline + + add r6, r0, r6, lsl #1 // r6 = pixel + x1 + + SCANLINE_INNER r6, r5, r3, PUT_PIXELS_F_INNER + +2: + add ip, ip, r7 // L.x += L.dx + add lr, lr, r8 // R.x += R.dx + add r0, r0, #320 // pixel += FRAME_WIDTH (160) + + subs r4, r4, #1 + bpl 1b + str ip, [r1, #4] + str lr, [r2, #4] + + ldmfd sp!, {r4,r5,r6,r7,r8,pc} + + +/* TODO +.global rasterizeF_asm +rasterizeF_asm: +// r0 = pixel +// r1 = L edge +// r2 = R edge +// r3 = palIndex -> color + +// r4 - Lh +// r5 - Lx +// r6 - Ldx +// r7 - Rh +// r8 - Rx +// r9 - Rdx + // x1 - asr + // r12 - width + + stmfd sp!, {r4,r5,r6,r7,r8,r9,lr} + + ldr lr,= lightmap + ldr r12,= palette + + // uint16 color = palette[lightmap[(L.top->v.g << 8) | palIndex]]; + ldr r6, [r1, #28] // r6 = L.top + ldrb r6, [r6, #6] // r6 = L.top->v.g + orr r3, r3, r6, lsl #8 // r3 = (L.top->v.g << 8) | palIndex + ldrb r3, [lr, r3] // r3 = lightmap[r3] + add r3, r12, r3, lsl #1 // r3 = palette + r6 + ldrh r3, [r3] // r3 = palette[r3] // color + +1: + 2: + ldr r7, [r1] + cmp r7, #0 + ldreq r7, [r1, #28] + ldreq r7, [r7, #12] + + + beq 2b + + + + + SCANLINE r0, r1, PUT_PIXELS_F, "{r4,r5,r6,r7,r11,pc}" + + b 1b +*/ diff --git a/src/platform/gba/sound.h b/src/platform/gba/sound.h new file mode 100644 index 0000000..5a385f0 --- /dev/null +++ b/src/platform/gba/sound.h @@ -0,0 +1,118 @@ +#ifndef H_SOUND +#define H_SOUND + +#include "common.h" + +struct Sound +{ + enum SoundMode { + UNIQUE, + REPLAY, + LOOP, + }; + + struct Sample + { + const uint8* data; + int32 size; + int32 pos; + int32 inc; + int32 volume; + + X_INLINE void fill(int16* buffer, int32 count) + { + for (int32 i = 0; i < count; i++) + { + buffer[i] += SND_DECODE(data[pos >> SND_FIXED_SHIFT]) * volume >> SND_VOL_SHIFT; + + pos += inc; + if (pos >= size) + { + // TODO LOOP + data = NULL; + return; + } + } + } + }; + + Sample channels[SND_CHANNELS]; + int32 channelsCount; + + void fill(uint8* buffer, int32 count) + { + if (channelsCount == 0) + { + dmaFill(buffer, SND_ENCODE(0), count); + return; + } + + int16 tmp[SND_SAMPLES]; + dmaFill(tmp, 0, sizeof(tmp)); + + int32 ch = channelsCount; + while (ch--) + { + Sample* sample = channels + ch; + + sample->fill(tmp, count); + + if (!sample->data) { + channels[ch] = channels[--channelsCount]; + } + } + + for (int32 i = 0; i < count; i++) + { + buffer[i] = SND_ENCODE(X_CLAMP(tmp[i], -128, 127)); + } + } + + #define CALC_INC (((SND_SAMPLE_FREQ << SND_FIXED_SHIFT) / SND_OUTPUT_FREQ) * pitch >> SND_PITCH_SHIFT) + + Sample* play(const uint8* data, int32 size, int32 volume, int32 pitch, int32 mode) + { + if (mode == UNIQUE || mode == REPLAY) + { + for (int32 i = 0; i < channelsCount; i++) + { + Sample* sample = channels + i; + + if (sample->data != data) + continue; + + sample->inc = CALC_INC; + sample->volume = volume; + + if (mode == REPLAY) + { + sample->pos = 0; + } + + return sample; + } + } + + if (channelsCount >= SND_CHANNELS) { + return NULL; + } + + Sample* sample = channels + channelsCount++; + sample->data = data; + sample->size = size << SND_FIXED_SHIFT; + sample->pos = 0; + sample->inc = CALC_INC; + sample->volume = volume + 1; + + return sample; + } + + void init() + { + channelsCount = 0; + } +}; + +Sound sound; + +#endif