1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-01-17 21:09:00 +01:00

GBA/DOS version update

This commit is contained in:
XProger 2021-04-27 12:38:53 +03:00
parent a4eac0afa1
commit 7e8c9d5e59
34 changed files with 8323 additions and 3018 deletions

View File

@ -33,10 +33,11 @@ LIBTONC := $(DEVKITPRO)/libtonc
#---------------------------------------------------------------------------------
ARCH := -mthumb -mthumb-interwork
CFLAGS := -g -Wall -save-temps -O3 -D__GBA__\
CFLAGS := -g -Wall -O3 -D__GBA__\
-mcpu=arm7tdmi -mtune=arm7tdmi\
-fomit-frame-pointer\
-ffast-math\
-Wno-class-memaccess\
$(ARCH)
CFLAGS += $(INCLUDE)
@ -151,14 +152,13 @@ soundbank.bin soundbank.h : $(AUDIOFILES)
#---------------------------------------------------------------------------------
@mmutil $^ -osoundbank.bin -hsoundbank.h
#---------------------------------------------------------------------------------
# This rule links in binary data with the .bin extension
#---------------------------------------------------------------------------------
%.PHD.o %_PHD.h : %.PHD
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
%.WAV.o %_WAV.h : %.WAV
@echo $(notdir $<)
@$(bin2o)
-include $(DEPSDIR)/*.d

View File

@ -20,8 +20,8 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="common.cpp" />
<ClCompile Include="render.iwram.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="render.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="camera.h" />
@ -33,6 +33,11 @@
<ClInclude Include="item.h" />
<ClInclude Include="lara.h" />
<ClInclude Include="level.h" />
<ClInclude Include="object.h" />
<ClInclude Include="rasterizer_mode13.h" />
<ClInclude Include="rasterizer_mode4.h" />
<ClInclude Include="rasterizer_mode5.h" />
<ClInclude Include="room.h" />
<ClInclude Include="sound.h" />
</ItemGroup>
<PropertyGroup Label="Globals">

View File

@ -14,12 +14,18 @@ enum CameraMode
CAMERA_MODE_FOLLOW = 1,
CAMERA_MODE_COMBAT = 2,
CAMERA_MODE_FIXED = 3,
CAMERA_MODE_OBJECT = 4,
};
struct Camera
{
vec3i viewPos;
vec3i targetPos;
struct Location {
Room* room;
vec3i pos;
};
Location view;
Location target;
int16 targetAngleX;
int16 targetAngleY;
@ -27,7 +33,8 @@ struct Camera
int16 angleX;
int16 angleY;
int32 room;
AABB frustumBase;
Item* item;
@ -40,25 +47,19 @@ struct Camera
mode = CAMERA_MODE_FOLLOW;
modeSwitch = false;
viewPos.x = 75162;
viewPos.y = 2048;
viewPos.z = 5000;
angleX = 0;
angleY = 0;
targetPos = viewPos;
view.pos = vec3i(0);
target.pos = view.pos;
targetDist = CAM_DIST_FOLLOW;
targetAngleX = 0;
targetAngleY = 0;
targetDist = CAM_DIST_FOLLOW;
angleX = 0;
angleY = 16 << 8;
//angleX = -0x1000;
//angleY = int16(0x8000);
}
void freeControl()
{
matrixSetView(viewPos, angleX, angleY);
matrixSetView(view.pos, angleX, angleY);
Matrix &m = matrixGet();
@ -71,36 +72,67 @@ struct Camera
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;
view.pos.x += m[2].x * CAM_SPEED >> 10;
view.pos.y += m[2].y * CAM_SPEED >> 10;
view.pos.z += m[2].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;
view.pos.x -= m[2].x * CAM_SPEED >> 10;
view.pos.y -= m[2].y * CAM_SPEED >> 10;
view.pos.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;
view.room = view.room->getRoom(view.pos.x, view.pos.y, view.pos.z);
const RoomInfo::Sector* sector = view.room->getSector(view.pos.x, view.pos.z);
int32 floor = sector->getFloor(view.pos.x, view.pos.y, view.pos.z);
int32 ceiling = sector->getCeiling(view.pos.x, view.pos.y, view.pos.z) - 256;
if (floor != WALL) {
viewPos.y = X_MIN(viewPos.y, floor - 256);
view.pos.y = X_MIN(view.pos.y, floor - 256);
}
if (ceiling != WALL)
{
viewPos.y = X_MAX(viewPos.y, ceiling + 256);
view.pos.y = X_MAX(view.pos.y, ceiling + 256);
}
}
int32 traceX()
{
return 1;
}
int32 traceZ()
{
return 1;
}
bool trace()
{
int32 dx = abs(view.pos.x - target.pos.x);
int32 dz = abs(view.pos.z - target.pos.z);
int32 tx, tz;
if (dx < dz) {
tx = traceX();
tz = traceZ();
if (tz == 0) return false;
} else {
tz = traceZ();
tx = traceX();
if (tx == 0) return false;
}
return true;
}
void update()
{
if (keys & IK_START)
@ -123,46 +155,108 @@ struct Camera
freeControl();
}
if (mode == CAMERA_MODE_FOLLOW && item) {
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);
const Box &box = item->getBoundingBox();
ty += box.maxY + ((box.minY - box.maxY) * 3 >> 2);
targetPos.x = tx;
targetPos.y += (ty - targetPos.y) >> 2;
targetPos.z = tz;
target.pos.x = tx;
target.pos.y += (ty - target.pos.y) >> 2;
target.pos.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);
int32 cx = target.pos.x - (phd_sin(angle) * dz >> FIXED_SHIFT);
int32 cy = target.pos.y - 256 + dy;
int32 cz = target.pos.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;
view.pos.x += (cx - view.pos.x) >> 2;
view.pos.y += (cy - view.pos.y) >> 2;
view.pos.z += (cz - view.pos.z) >> 2;
updateRoom();
anglesFromVector(targetPos.x - viewPos.x, targetPos.y - viewPos.y, targetPos.z - viewPos.z, angleX, angleY);
vec3i dir = target.pos - view.pos;
anglesFromVector(dir.x, dir.y, dir.z, angleX, angleY);
}
matrixSetView(viewPos, angleX, angleY);
prepareFrustum();
matrixSetView(view.pos, angleX, angleY);
updateRoom();
// reset additional angles, Lara states can override it during the update proc
targetAngleX = 0;
targetAngleY = 0;
targetDist = CAM_DIST_FOLLOW;
}
void prepareFrustum()
{
matrixSetIdentity();
matrixRotateY(angleY);
matrixRotateX(angleX);
static const vec3i v[5] = {
// near plane
vec3i( 0, 0, 0 ),
// far plane
vec3i( -FRUSTUM_FAR_X, -FRUSTUM_FAR_Y, FRUSTUM_FAR_Z ),
vec3i( FRUSTUM_FAR_X, -FRUSTUM_FAR_Y, FRUSTUM_FAR_Z ),
vec3i( -FRUSTUM_FAR_X, FRUSTUM_FAR_Y, FRUSTUM_FAR_Z ),
vec3i( FRUSTUM_FAR_X, FRUSTUM_FAR_Y, FRUSTUM_FAR_Z )
};
const Matrix &m = matrixGet();
frustumBase.minX = 0xFFFFFFF;
frustumBase.maxX = -0xFFFFFFF;
frustumBase.minY = 0xFFFFFFF;
frustumBase.maxY = -0xFFFFFFF;
frustumBase.minZ = 0xFFFFFFF;
frustumBase.maxZ = -0xFFFFFFF;
for (int32 i = 0; i < 5; i++)
{
int32 x = DP43(m[0], v[i]) >> FIXED_SHIFT;
int32 y = DP43(m[1], v[i]) >> FIXED_SHIFT;
int32 z = DP43(m[2], v[i]) >> FIXED_SHIFT;
frustumBase.minX = X_MIN(frustumBase.minX, x);
frustumBase.maxX = X_MAX(frustumBase.maxX, x);
frustumBase.minY = X_MIN(frustumBase.minY, y);
frustumBase.maxY = X_MAX(frustumBase.maxY, y);
frustumBase.minZ = X_MIN(frustumBase.minZ, z);
frustumBase.maxZ = X_MAX(frustumBase.maxZ, z);
}
frustumBase.minX += view.pos.x - 1024;
frustumBase.maxX += view.pos.x + 1024;
frustumBase.minY += view.pos.y - 1024;
frustumBase.maxY += view.pos.y + 1024;
frustumBase.minZ += view.pos.z - 1024;
frustumBase.maxZ += view.pos.z + 1024;
}
void updateFrustum(int32 offsetX, int32 offsetY, int32 offsetZ)
{
frustumAABB.minX = frustumBase.minX - offsetX;
frustumAABB.maxX = frustumBase.maxX - offsetX;
frustumAABB.minY = frustumBase.minY - offsetY;
frustumAABB.maxY = frustumBase.maxY - offsetY;
frustumAABB.minZ = frustumBase.minZ - offsetZ;
frustumAABB.maxZ = frustumBase.maxZ - offsetZ;
}
};
Camera camera;
#endif
#endif

View File

@ -13,8 +13,8 @@ enum CollisionType {
CT_FLOOR_CEILING = (1 << 5),
};
struct CollisionInfo {
struct CollisionInfo
{
enum SideType {
ST_MIDDLE,
ST_FRONT,
@ -23,13 +23,14 @@ struct CollisionInfo {
ST_MAX
};
struct Side {
struct Side
{
int32 floor;
int32 ceiling;
SlantType slantType;
};
int16 *trigger;
const FloorData* trigger;
Side m;
Side f;
@ -63,9 +64,9 @@ struct CollisionInfo {
{
SlantType slantType;
if (floorSlant.slantX == 0 && floorSlant.slantZ == 0) {
if (gLastFloorSlant.slantX == 0 && gLastFloorSlant.slantZ == 0) {
slantType = SLANT_NONE;
} else if (abs(floorSlant.slantX) < 3 && abs(floorSlant.slantZ) < 3) {
} else if (abs(gLastFloorSlant.slantX) < 3 && abs(gLastFloorSlant.slantZ) < 3) {
slantType = SLANT_LOW;
} else {
slantType = SLANT_HIGH;
@ -108,31 +109,49 @@ int32 alignOffset(int32 a, int32 b)
return -(a + 1);
}
bool collideStatic(CollisionInfo &cinfo, const vec3i &p, int32 height) {
/*
bool collideStatic(Room* room, 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));
Box objBox;
objBox.minX = -cinfo.radius;
objBox.maxX = cinfo.radius;
objBox.minZ = -cinfo.radius;
objBox.maxZ = cinfo.radius;
objBox.minY = -height;
objBox.maxY = 0;
// TODO: check linked rooms
for (int j = 0; j < level->roomsCount; j++) {
const TR::Room &room = level->rooms[j];
Room** nearRoom = room->getNearRooms(p, cinfo.radius, height);
for (int i = 0; i < room.meshesCount; i++) {
TR::Room::Mesh &m = room.meshes[i];
TR::StaticMesh &sm = level->staticMeshes[m.meshIndex];
while (*nearRoom)
{
const Room* room = *nearRoom++;
if (sm.flags & 1) continue;
for (int i = 0; i < room->mCount; i++)
{
const RoomInfo::Mesh* mesh = room->meshes + i;
BoxV2 meshBox = sm.getBox(true);
meshBox.rotate90(m.rotation.value / ANGLE_90);
meshBox.translate(m.pos);
#ifdef NO_STATIC_MESHES
if (mesh->staticMeshId != STATIC_MESH_GATE) continue;
#endif
if (!objBox.intersect(meshBox)) continue;
const StaticMesh* staticMesh = staticMeshes + staticMeshesMap[mesh->staticMeshId];
cinfo.offset = meshBox.pushOut2D(objBox);
if (staticMesh->flags & 1) continue;
Box meshBox = boxRotate(staticMesh->cbox, mesh->rotation);
// TODO align RoomInfo::Mesh (room relative int16?)
vec3i pos;
memcpy(&pos, &(mesh->pos[0]), sizeof(pos));
pos -= p;
boxTranslate(meshBox, pos);
if (!boxIntersect(meshBox, objBox)) continue;
cinfo.offset = boxPushOut(meshBox, objBox);
bool flip = (cinfo.quadrant > 1);
@ -163,7 +182,7 @@ bool collideStatic(CollisionInfo &cinfo, const vec3i &p, int32 height) {
return true;
}
}
*/
return false;
}
@ -182,20 +201,20 @@ void collideRoom(Item* item, int32 height, int32 yOffset = 0)
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);\
const Room* room = item->room->getRoom(v.x, cy, v.z);\
const RoomInfo::Sector* sector = room->getSector(v.x, v.z);\
floor = sector->getFloor(v.x, cy, v.z);\
if (floor != WALL) floor -= p.y;\
ceiling = getCeiling(sector, v.x, cy, v.z);\
ceiling = sector->getCeiling(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.trigger = gLastFloorData;
cinfo.slantX = gLastFloorSlant.slantX;
cinfo.slantZ = gLastFloorSlant.slantZ;
cinfo.setSide(CollisionInfo::ST_MIDDLE, floor, ceiling);
@ -257,7 +276,7 @@ void collideRoom(Item* item, int32 height, int32 yOffset = 0)
cinfo.setSide(CollisionInfo::ST_RIGHT, floor, ceiling);
// static objects
collideStatic(cinfo, p, height);
collideStatic(item->room, cinfo, p, height);
// check middle
if (cinfo.m.floor == WALL)

View File

@ -1,11 +1,16 @@
#include "common.h"
Rect clip;
vec3i viewPos;
uint32 keys;
AABB frustumAABB;
Rect viewport;
vec3i cameraViewPos;
Matrix matrixStack[MAX_MATRICES];
int32 matrixStackIndex = 0;
uint32 keys;
SaveGame gSaveGame;
const FloorData* gLastFloorData;
FloorData gLastFloorSlant;
const uint16 divTable[DIV_TABLE_SIZE] = {
0xFFFF, 0xFFFF, 0x8000, 0x5555, 0x4000, 0x3333, 0x2AAA, 0x2492,
@ -612,6 +617,57 @@ void anglesFromVector(int32 x, int32 y, int32 z, int16 &angleX, int16 &angleY)
}
}
Box boxRotate(const Box &box, int16 angle)
{
if (angle == ANGLE_90) {
return Box( box.minZ, box.maxZ, box.minY, box.maxY, -box.maxX, -box.minX );
} else if (angle == -ANGLE_90) {
return Box( -box.maxZ, -box.minZ, box.minY, box.maxY, box.minX, box.maxX );
} else if (angle == ANGLE_180) {
return Box( -box.maxX, -box.minX, box.minY, box.maxY, -box.maxZ, -box.minZ );
}
return box;
}
void boxTranslate(Box &box, const vec3i &offset)
{
box.minX += offset.x;
box.maxX += offset.x;
box.minY += offset.y;
box.maxY += offset.y;
box.minZ += offset.z;
box.maxZ += offset.z;
}
bool boxIntersect(const Box &a, const Box &b)
{
return !(a.maxX <= b.minX || a.minX >= b.maxX ||
a.maxY <= b.minY || a.minY >= b.maxY ||
a.maxZ <= b.minZ || a.minZ >= b.maxZ);
}
bool boxContains(const Box &a, const vec3i &p)
{
return !(a.minX > p.x || a.maxX < p.x ||
a.minY > p.y || a.maxY < p.y ||
a.minZ > p.z || a.maxZ < p.z);
}
vec3i boxPushOut(const Box &a, const Box &b)
{
int32 ax = b.maxX - a.minX;
int32 bx = a.maxX - b.minX;
int32 az = b.maxZ - a.minZ;
int32 bz = a.maxZ - b.minZ;
vec3i p;
p.y = 0;
p.x = (ax < bx) ? -ax : bx;
p.z = (az < bz) ? -az : bz;
return p;
}
/*
void initDivTable()
{
@ -635,26 +691,6 @@ void initDivTable()
}
*/
Matrix& matrixGet()
{
return matrixStack[matrixStackIndex];
}
void matrixPush()
{
ASSERT(matrixStackIndex < MAX_MATRICES - 1);
Matrix &a = matrixStack[matrixStackIndex++];
Matrix &b = matrixStack[matrixStackIndex];
memcpy(b, a, sizeof(Matrix));
}
void matrixPop()
{
ASSERT(matrixStackIndex > 0);
matrixStackIndex--;
}
void matrixTranslate(const vec3i &pos)
{
Matrix &m = matrixGet();
@ -665,7 +701,7 @@ void matrixTranslate(const vec3i &pos)
void matrixTranslateAbs(const vec3i &pos)
{
vec3i d = pos - viewPos;
vec3i d = pos - cameraViewPos;
Matrix &m = matrixGet();
m[0][3] = DP33(m[0], d);
@ -752,6 +788,23 @@ void matrixRotateYXZ(int32 angleX, int32 angleY, int32 angleZ)
if (angleZ) matrixRotateZ(angleZ);
}
void matrixRotateZXY(int32 angleX, int32 angleY, int32 angleZ)
{
if (angleZ) matrixRotateZ(angleZ);
if (angleX) matrixRotateX(angleX);
if (angleY) matrixRotateY(angleY);
}
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(pos);
matrixRotateYXZ(angleX, angleY, angleZ);
}
#define LERP_FAST(a, b, mul, div) a = (a + b) >> 1
#define LERP_SLOW(a, b, mul, div) a = a + (b - a) * mul / div
@ -776,14 +829,36 @@ void matrixLerp(const Matrix &n, int32 multiplier, int32 divider)
}
}
void matrixFrame(const vec3i &pos, uint16* angles)
void matrixSetIdentity()
{
int32 angleX = (angles[1] & 0x3FF0) << 2;
int32 angleY = (angles[1] & 0x000F) << 12 | (angles[0] & 0xFC00) >> 4;
int32 angleZ = (angles[0] & 0x03FF) << 6;
Matrix &m = matrixGet();
matrixTranslate(pos);
matrixRotateYXZ(angleX, angleY, angleZ);
#ifdef ROTATE90_MODE
m[0][0] = 0;
m[0][1] = 0x4000;
m[0][2] = 0;
m[0][3] = 0;
m[1][0] = -0x4000;
m[1][1] = 0;
m[1][2] = 0;
m[1][3] = 0;
#else
m[0][0] = 0x4000;
m[0][1] = 0;
m[0][2] = 0;
m[0][3] = 0;
m[1][0] = 0;
m[1][1] = 0x4000;
m[1][2] = 0;
m[1][3] = 0;
#endif
m[2][0] = 0;
m[2][1] = 0;
m[2][2] = 0x4000;
m[2][3] = 0;
}
void matrixSetView(const vec3i &pos, int32 angleX, int32 angleY)
@ -796,36 +871,31 @@ void matrixSetView(const vec3i &pos, int32 angleX, int32 angleY)
Matrix &m = matrixGet();
#ifdef ROTATE90_MODE
m[1][0] = -(cy);
m[1][1] = -(0);
m[1][2] = -(-sy);
m[1][3] = pos.x;
m[0][0] = (sx * sy) >> FIXED_SHIFT;
m[0][1] = cx;
m[0][2] = (sx * cy) >> FIXED_SHIFT;
m[0][3] = pos.y;
m[0][3] = 0;
m[2][0] = (cx * sy) >> FIXED_SHIFT;
m[2][1] = -sx;
m[2][2] = (cx * cy) >> FIXED_SHIFT;
m[2][3] = pos.z;
m[1][0] = -cy;
m[1][1] = 0;
m[1][2] = sy;
m[1][3] = 0;
#else
m[0][0] = cy;
m[0][1] = 0;
m[0][2] = -sy;
m[0][3] = pos.x;
m[0][3] = 0;
m[1][0] = (sx * sy) >> FIXED_SHIFT;
m[1][1] = cx;
m[1][2] = (sx * cy) >> FIXED_SHIFT;
m[1][3] = pos.y;
m[1][3] = 0;
#endif
m[2][0] = (cx * sy) >> FIXED_SHIFT;
m[2][1] = -sx;
m[2][2] = (cx * cy) >> FIXED_SHIFT;
m[2][3] = pos.z;
#endif
m[2][3] = 0;
viewPos = pos;
}
cameraViewPos = pos;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,2 +1,4 @@
mv render.cpp render.iwram.cpp
make
mv render.iwram.cpp render.cpp
./mGBA.exe C:\\Projects\\OpenLara\\src\\platform\\gba\\OpenLara.gba

View File

@ -0,0 +1,3 @@
rm *.obj
wcl386.exe *.cpp -fe=OpenLara.exe -i="C:\WATCOM/h" -w4 -e25 -zq -ox -d2 -6r -bt=dos -fo=.obj -zmf -xd -l=pmodew
C:\Dosbox\dosbox -conf dosbox.conf OpenLara.exe

View File

@ -5,6 +5,7 @@
#include "item.h"
int32 seqGlyphs;
extern AABB fastClipAABB;
void drawNumber(int32 number, int32 x, int32 y)
{
@ -12,7 +13,7 @@ void drawNumber(int32 number, int32 x, int32 y)
12, 8, 10, 10, 10, 10, 10, 10, 10, 10
};
const Sprite *glyphSprites = sprites + spritesSeq[seqGlyphs].sStart;
const Sprite* glyphSprites = sprites + spritesSeq[seqGlyphs].sStart;
while (number > 0)
{
@ -64,21 +65,19 @@ void drawMesh(int16 meshIndex, uint16 intensity)
PROFILE_STOP(dbg_poly);
}
void drawShadow(const Item* item)
void drawShadow(const Item* item, int32 size)
{
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);
const RoomInfo::Sector* sector = item->room->getSector(item->pos.x, item->pos.z);
int32 floor = sector->getFloor(item->pos.x, item->pos.y, item->pos.z);
if (floor == WALL)
return;
int32 shadowSize = 160; // TODO per item
const Box& box = getBoundingBox(item);
const Box& box = item->getBoundingBox();
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 sx = (box.maxX - box.minX) * size >> 10;
int32 sz = (box.maxZ - box.minZ) * size >> 10;
int32 sx2 = sx << 1;
int32 sz2 = sz << 1;
@ -116,12 +115,11 @@ void drawItem(const Item* item)
const Model* model = models + modelIndex;
if (model->mCount == 1 && meshOffsets[model->mStart] == 0)
return;
AnimFrame* frame = getFrame(item, model);
AnimFrame* frame = item->getFrame(model);
uint16* frameAngles = frame->angles + 1;
camera.updateFrustum(item->pos.x, item->pos.y, item->pos.z);
matrixPush();
matrixTranslateAbs(item->pos);
matrixRotateYXZ(item->angleX, item->angleY, item->angleZ);
@ -129,12 +127,24 @@ void drawItem(const Item* item)
int32 intensity = item->intensity;
if (intensity == 0xFFFF) {
intensity = itemCalcLighting(item, frame->box);
intensity = item->calcLighting(frame->box);
}
bool isVisible = boxIsVisible(&frame->box);
if (isVisible)
int32 vis = boxIsVisible(&frame->box);
if (vis != 0)
{
enableClipping = vis < 0;
if (item->type == ITEM_BAT ||
item->type == ITEM_TRAP_FLOOR) // some objects have the wrong AABB // TODO preprocess
{
enableClipping = true;
}
// skip rooms portal clipping for objects
Rect oldViewport = viewport;
viewport = Rect( 0, 0, FRAME_WIDTH, FRAME_HEIGHT );
// non-aligned access (TODO)
uint32 nodeIndex;
memcpy(&nodeIndex, &model->nodeIndex, sizeof(nodeIndex));
@ -159,17 +169,21 @@ void drawItem(const Item* item)
node++;
}
viewport = oldViewport;
}
matrixPop();
// shadow
if (isVisible & item->flags.shadow) {
if (vis != 0 && item->flags.shadow)
{
matrixPush();
matrixTranslateAbs(item->pos);
matrixRotateY(item->angleY);
drawShadow(item);
enableClipping = true;
drawShadow(item, 160); // TODO per item shadow size
matrixPop();
}
@ -177,13 +191,17 @@ void drawItem(const Item* item)
void drawRoom(const Room* room)
{
clip = room->clip;
viewport = room->clip;
int32 startVertex = gVerticesCount;
matrixPush();
matrixTranslateAbs(vec3i(room->x, 0, room->z));
camera.updateFrustum(room->x, 0, room->z);
enableClipping = true;
PROFILE_START();
transformRoom(room->vertices, room->vCount);
PROFILE_STOP(dbg_transform);
@ -192,193 +210,64 @@ void drawRoom(const Room* room)
PROFILE_START();
faceAddRoom(room->quads, room->qCount, room->triangles, room->tCount, startVertex);
PROFILE_STOP(dbg_poly);
for (int32 i = 0; i < room->mCount; i++)
{
const RoomInfo::Mesh* mesh = room->meshes + i;
#ifdef NO_STATIC_MESHES
if (mesh->staticMeshId != STATIC_MESH_GATE) continue;
#endif
const StaticMesh* staticMesh = staticMeshes + staticMeshesMap[mesh->staticMeshId];
if (!(staticMesh->flags & 2)) continue; // invisible
// TODO align RoomInfo::Mesh (room relative int16?)
// TODO align RoomInfo::Mesh (room relative int16?)
vec3i pos;
memcpy(&pos, &mesh->pos, sizeof(pos));
memcpy(&pos, &(mesh->pos[0]), sizeof(pos));
camera.updateFrustum(pos.x, pos.y, pos.z);
matrixPush();
matrixTranslateAbs(pos);
matrixRotateY(mesh->rotation);
if (boxIsVisible(&staticMesh->vbox)) {
int32 vis = boxIsVisible(&staticMesh->vbox);
if (vis != 0) {
enableClipping = vis < 0;
drawMesh(staticMesh->meshIndex, mesh->intensity);
}
matrixPop();
}
int32 itemIndex = room->firstItem;
while (itemIndex != NO_ITEM)
Item* item = room->firstItem;
while (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 (item->flags.status != ITEM_FLAGS_STATUS_INVISIBLE) {
item->draw();
}
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;
item = item->nextItem;
}
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;
camera.view.room->clip = Rect( 0, 0, FRAME_WIDTH, FRAME_HEIGHT );
getVisibleRooms(camera.room);
Room** visRoom = camera.view.room->getVisibleRooms();
while (visRoomsCount--)
while (*visRoom)
{
Room* room = rooms + visRooms[visRoomsCount];
Room* room = *visRoom++;
drawRoom(room);
roomReset(room);
room->reset();
}
flush();
}
#ifdef TEST
@ -386,10 +275,10 @@ 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
Rect testClip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT };
int32 testTile = 10; // 707 // 712
void drawTest() {
#ifdef _WIN32
Sleep(16);
#endif
@ -445,12 +334,13 @@ void drawTest() {
gVertices[(vidx + 3) % 4].x = -50;
gVertices[(vidx + 3) % 4].y = 25;
for (int i = 0; i < 4; i++) {
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);
gVertices[i].clip = classify(gVertices[i], clip);
}
gVerticesCount = 4;
@ -459,10 +349,18 @@ void drawTest() {
faceAddQuad(testTile, indices, 0);
#ifdef _WIN32
for (int y = 0; y < FRAME_HEIGHT; y++) {
for (int x = 0; x < FRAME_WIDTH; x++) {
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)
{
#ifdef MODE4
((uint8*)fb)[y * FRAME_WIDTH + x] = 255;
#else
((uint16*)fb)[y * FRAME_WIDTH + x] = 255;
#endif
}
}
}
#endif

View File

@ -1,19 +1,75 @@
#ifndef H_ENEMY
#define H_ENEMY
struct Bat
{
#include "item.h"
struct Enemy : Item
{
Enemy(Room* room, int32 hp) : Item(room)
{
flags.shadow = true;
health = hp;
}
virtual void collide(Lara* lara, CollisionInfo* cinfo)
{
//
UNUSED(lara);
UNUSED(cinfo);
}
virtual void update()
{
updateAnim();
}
};
struct Wolf
{
struct Wolf : Enemy
{
Wolf(Room* room) : Enemy(room, 6) {}
};
struct Bear
{
struct Bear : Enemy
{
Bear(Room* room) : Enemy(room, 20) {}
};
struct Bat : Enemy
{
Bat(Room* room) : Enemy(room, 1) {}
};
/*
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_SKATER : 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;
*/
#endif

View File

@ -4,11 +4,19 @@
#include "common.h"
#include "sound.h"
#include "level.h"
#include "room.h"
#include "camera.h"
#include "item.h"
#include "lara.h"
#include "draw.h"
Lara* players[2];
Lara* getLara(const vec3i &pos)
{
UNUSED(pos); // TODO two players
return players[0]; // TODO find nearest player
}
struct Game
{
void init()
@ -20,12 +28,12 @@ struct Game
{
camera.init();
readLevel((uint8*)LEVEL1_PHD);
readLevel((uint8*)data);
// prepare rooms
for (int32 i = 0; i < roomsCount; i++)
{
roomReset(rooms + i);
rooms[i].reset();
}
// prepare items
@ -33,92 +41,91 @@ struct Game
{
Item* item = items + i;
itemInit(item);
item->init(rooms + item->startRoomIndex);
if (item->room > -1) {
roomItemAdd(item->room, i);
}
if (item->type == ITEM_LARA) {
if (item->type == ITEM_LARA)
{
camera.item = item;
//#ifdef PROFILE
// 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)
//resetItem(item, 0, vec3i(74588, 3072, 19673), 0); // level 1 (first darts)
//resetItem(item, 9, vec3i(49669, 7680, 57891), 0); // level 1 (first door)
//resetItem(item, 10, vec3i(43063, 7168, 61198), 0); // level 1 (transp)
//resetItem(item, 14, vec3i(20215, 6656, 52942), ANGLE_90 + ANGLE_45); // level 1 (bridge)
//resetItem(item, 17, vec3i(16475, 6656, 59845), ANGLE_90); // level 1 (bear)
//resetItem(item, 26, vec3i(24475, 6912, 83505), ANGLE_90); // level 1 (switch timer)
//resetItem(item, 35, vec3i(35149, 2048, 74189), ANGLE_90); // level 1 (switch timer)
//#endif
camera.room = item->room;
}
camera.view.pos = camera.target.pos = item->pos;
camera.view.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);
players[0] = (Lara*)item;
}
}
// prepare glyphs
for (int32 i = 0; i < spritesSeqCount; i++) {
if (spritesSeq[i].type == ITEM_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)
void resetItem(Item* item, int32 roomIndex, const vec3i &pos, int32 angleY)
{
roomItemRemove(itemIndex);
item->room->remove(item);
Item* item = items + itemIndex;
item->pos = vec3i(20215, 6656, 52942);
item->pos = pos;
item->angleY = angleY;
item->health = LARA_MAX_HEALTH;
roomItemAdd(roomIndex, itemIndex);
rooms[roomIndex].add(item);
}
void updateItems()
{
curItemIndex = firstActive;
while (curItemIndex != NO_ITEM)
Item* item = Item::sFirstActive;
while (item)
{
Item* item = items + curItemIndex;
if (item->type == ITEM_LARA) {
Lara* lara = (Lara*)item;
lara->update();
} else {
itemControl(item);
}
curItemIndex = item->nextActive;
Item* next = item->nextActive;
item->update();
item = next;
}
}
void update(int32 frames)
{
#ifdef TEST
return;
#endif
if (frames > MAX_UPDATE_FRAMES) {
frames = MAX_UPDATE_FRAMES;
}
for (int32 i = 0; i < frames; i++) {
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
if (keys & IK_SELECT) {
mixer.playMusic(TRACK_13_WAV);
}
}
void render()
{
#ifdef ROTATE90_MODE
#define TEXT_POSX FRAME_HEIGHT
#else
#define TEXT_POSX FRAME_WIDTH
#endif
clear();
#ifdef TEST

View File

@ -4,6 +4,7 @@
#include "common.h"
#include "sound.h"
#include "camera.h"
#include "draw.h"
int32 curItemIndex;
@ -18,24 +19,7 @@ int16 angleDec(int16 angle, int32 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)
Mixer::Sample* soundPlay(int16 id, const vec3i &pos)
{
int16 a = soundMap[id];
@ -49,7 +33,7 @@ Sound::Sample* soundPlay(int16 id, const vec3i &pos)
return NULL;
}
vec3i d = pos - camera.targetPos;
vec3i d = pos - camera.target.pos;
if (abs(d.x) >= SND_MAX_DIST || abs(d.y) >= SND_MAX_DIST || abs(d.z) >= SND_MAX_DIST) {
return NULL;
@ -61,12 +45,12 @@ Sound::Sample* soundPlay(int16 id, const vec3i &pos)
volume -= xRand() >> 2;
}
volume = X_MIN(volume, 0x7FFF) >> 9;
if (volume <= 0) {
return NULL;
}
volume = X_MIN(volume, 0x7FFF);
int32 pitch = 128;
if (b->flags.pitch) {
@ -78,77 +62,102 @@ Sound::Sample* soundPlay(int16 id, const vec3i &pos)
index += (xRand() * b->flags.count) >> 15;
}
const uint8 *data = soundData + soundOffsets[index];
int32 offset;
memcpy(&offset, soundOffsets + index, 4); // TODO preprocess and remove wave header
const uint8 *data = soundData + offset;//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);
return mixer.playSample(data, size, volume, pitch, b->flags.mode);
}
void move(Item* item, const Anim* anim)
AnimFrame* Item::getFrame(const Model* model) const
{
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;
int32 frameSize = sizeof(AnimFrame) / 2 + model->mCount * 2;
int32 keyFrame = (frameIndex - anim->frameBegin) / anim->frameRate; // TODO fixed div? check the range
return (AnimFrame*)(animFrames + anim->frameOffset / 2 + keyFrame * frameSize);
}
const Box& Item::getBoundingBox() const
{
const Model* model = models + modelsMap[type];
AnimFrame* frame = getFrame(model);
return frame->box;
}
void Item::move()
{
const Anim* anim = anims + animIndex;
int32 s = anim->speed;
if (flags.gravity)
{
s += anim->accel * (frameIndex - anim->frameBegin - 1);
hSpeed -= s >> 16;
s += anim->accel;
hSpeed += s >> 16;
vSpeed += (vSpeed < 128) ? GRAVITY : 1;
pos.y += vSpeed;
} else {
s += anim->accel * (frameIndex - anim->frameBegin);
hSpeed = s >> 16;
}
int16 angle = (type == ITEM_LARA) ? moveAngle : angleY;
pos.x += phd_sin(angle) * hSpeed >> FIXED_SHIFT;
pos.z += phd_cos(angle) * hSpeed >> FIXED_SHIFT;
}
const Anim* Item::animSet(int32 newAnimIndex, bool resetState, int32 frameOffset)
{
const Anim* anim = anims + newAnimIndex;
animIndex = newAnimIndex;
frameIndex = anim->frameBegin + frameOffset;
if (resetState) {
item->state = item->goalState = anim->state;
state = goalState = uint8(anim->state);
}
return anim;
}
const Anim* animChange(Item* item, const Anim* anim)
const Anim* Item::animChange(const Anim* anim)
{
if (!anim->statesCount || item->goalState == item->state)
if (!anim->statesCount || goalState == state)
return anim;
const AnimState* animState = animStates + anim->statesStart;
for (int32 i = 0; i < anim->statesCount; i++)
{
if (item->goalState == animState->state)
if (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 ((frameIndex >= animRange->frameBegin) && (frameIndex <= animRange->frameEnd))
{
if ((item->type != ITEM_LARA) && (item->nextState == animState->state)) {
item->nextState = 0;
if ((type != ITEM_LARA) && (nextState == animState->state)) {
nextState = 0;
}
item->frameIndex = animRange->nextFrameIndex;
item->animIndex = animRange->nextAnimIndex;
frameIndex = animRange->nextFrameIndex;
animIndex = animRange->nextAnimIndex;
anim = anims + animRange->nextAnimIndex;
item->state = anim->state;
state = uint8(anim->state);
return anim;
}
animRange++;
@ -160,7 +169,7 @@ const Anim* animChange(Item* item, const Anim* anim)
return anim;
}
void animCmd(bool fx, Item* item, const Anim* anim)
void Item::animCmd(bool fx, const Anim* anim)
{
if (!anim->commandsCount) return;
@ -177,14 +186,14 @@ void animCmd(bool fx, Item* item, const Anim* anim)
{
if (!fx)
{
int32 s = phd_sin(item->moveAngle);
int32 c = phd_cos(item->moveAngle);
int32 s = phd_sin(moveAngle);
int32 c = phd_cos(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;
pos.x += (c * x + s * z) >> FIXED_SHIFT;
pos.y += y;
pos.z += (c * z - s * x) >> FIXED_SHIFT;
}
ptr += 3;
break;
@ -192,32 +201,36 @@ void animCmd(bool fx, Item* item, const Anim* anim)
case ANIM_CMD_JUMP:
if (!fx)
{
if (item->vSpeedHack) {
item->vSpeed = item->vSpeedHack;
item->vSpeedHack = 0;
if (vSpeedHack) {
vSpeed = vSpeedHack;
vSpeedHack = 0;
} else {
item->vSpeed = ptr[0];
vSpeed = ptr[0];
}
item->hSpeed = ptr[1];
item->flags.gravity = true;
hSpeed = ptr[1];
flags.gravity = true;
}
ptr += 2;
break;
case ANIM_CMD_EMPTY:
break;
case ANIM_CMD_KILL:
if (!fx)
{
flags.status = ITEM_FLAGS_STATUS_INACTIVE;
}
break;
case ANIM_CMD_SOUND:
if (fx && item->frameIndex == ptr[0])
if (fx && frameIndex == ptr[0])
{
soundPlay(ptr[1] & 0x03FFF, item->pos);
soundPlay(ptr[1] & 0x03FFF, pos);
}
ptr += 2;
break;
case ANIM_CMD_EFFECT:
if (fx && item->frameIndex == ptr[0]) {
if (fx && frameIndex == ptr[0]) {
switch (ptr[1]) {
case FX_ROTATE_180 : item->angleY -= ANGLE_180; break;
case FX_ROTATE_180 : angleY += ANGLE_180; break;
/*
case FX_FLOOR_SHAKE : ASSERT(false);
case FX_LARA_NORMAL : animation.setAnim(ANIM_STAND); break;
@ -243,35 +256,150 @@ void animCmd(bool fx, Item* item, const Anim* anim)
}
}
void animUpdate(Item* item)
void Item::skipAnim()
{
if (modelsMap[item->type] == NO_MODEL)
return;
vec3i p = pos;
const Anim* anim = anims + item->animIndex;
item->frameIndex++;
anim = animChange(item, anim);
if (item->frameIndex > anim->frameEnd)
while (state != goalState)
{
animCmd(false, item, anim);
item->frameIndex = anim->nextFrameIndex;
item->animIndex = anim->nextAnimIndex;
anim = anims + anim->nextAnimIndex;
item->state = anim->state;
updateAnim(false);
}
animCmd(true, item, anim);
move(item, anim);
pos = p;
vSpeed = 0;
hSpeed = 0;
}
int32 calcLight(const vec3i &pos, int32 roomIndex)
void Item::updateAnim(bool movement)
{
Room* room = rooms + roomIndex;
if (modelsMap[type] == NO_MODEL)
return;
const Anim* anim = anims + animIndex;
#ifndef STATIC_ITEMS
frameIndex++;
#endif
anim = animChange(anim);
if (frameIndex > anim->frameEnd)
{
animCmd(false, anim);
frameIndex = anim->nextFrameIndex;
animIndex = anim->nextAnimIndex;
anim = anims + anim->nextAnimIndex;
state = uint8(anim->state);
}
animCmd(true, anim);
#ifndef STATIC_ITEMS
if (movement) {
move();
}
#endif
}
Item* Item::add(ItemType type, Room* room, const vec3i &pos, int32 angleY)
{
if (!Item::sFirstFree) {
ASSERT(false);
return NULL;
}
Item* item = Item::sFirstFree;
Item::sFirstFree = item->nextItem;
item->type = type;
item->pos = pos;
item->angleY = angleY;
item->intensity = -1;
item->init(room);
return item;
}
void Item::remove()
{
deactivate();
room->remove(this);
nextItem = Item::sFirstFree;
Item::sFirstFree = this;
}
void Item::activate()
{
ASSERT(!flags.active)
flags.active = true;
nextActive = sFirstActive;
sFirstActive = this;
}
void Item::deactivate()
{
Item* prev = NULL;
Item* curr = sFirstActive;
while (curr)
{
Item* next = curr->nextActive;
if (curr == this)
{
flags.active = false;
nextActive = NULL;
if (prev) {
prev->nextActive = next;
} else {
sFirstActive = next;
}
break;
}
prev = curr;
curr = next;
}
}
void Item::updateRoom(int32 offset)
{
Room* nextRoom = room->getRoom(pos.x, pos.y + offset, pos.z);
if (room != nextRoom)
{
room->remove(this);
nextRoom->add(this);
}
const RoomInfo::Sector* sector = room->getSector(pos.x, pos.z);
floor = sector->getFloor(pos.x, pos.y, pos.z);
}
int32 Item::calcLighting(const Box& box) const
{
matrixPush();
Matrix &m = matrixGet();
m[0][3] = m[1][3] = m[2][3] = 0;
matrixRotateYXZ(angleX, angleY, angleZ);
vec3i p((box.maxX + box.minX) >> 1,
(box.maxY + box.minY) >> 1,
(box.maxZ + box.minZ) >> 1);
matrixTranslate(p);
p = vec3i(m[0][3] >> FIXED_SHIFT,
m[1][3] >> FIXED_SHIFT,
m[2][3] >> FIXED_SHIFT) + pos;
matrixPop();
if (!room->lCount) {
return room->ambient;
@ -290,7 +418,7 @@ int32 calcLight(const vec3i &pos, int32 roomIndex)
memcpy(&lpos, &light->pos[0], sizeof(lpos));
memcpy(&lradius, &light->radius[0], sizeof(lradius));
vec3i d = pos - lpos;
vec3i d = p - lpos;
int32 dist = dot(d, d) >> 12;
int32 att = X_SQR(lradius) >> 12;
@ -305,80 +433,241 @@ int32 calcLight(const vec3i &pos, int32 roomIndex)
return 8191 - ((intensity + ambient) >> 1);
}
int32 itemCalcLighting(const Item* item, const Box& box)
#include "lara.h"
#include "enemy.h"
#include "object.h"
Item::Item(Room* room)
{
matrixPush();
Matrix &m = matrixGet();
m[0][3] = m[1][3] = m[2][3] = 0;
matrixRotateYXZ(item->angleX, item->angleY, item->angleZ);
angleX = 0;
angleZ = 0;
vSpeed = 0;
hSpeed = 0;
nextItem = NULL;
nextActive = NULL;
animIndex = models[modelsMap[type]].animIndex;
frameIndex = anims[animIndex].frameBegin;
state = uint8(anims[animIndex].state);
nextState = state;
goalState = state;
vec3i v((box.maxX + box.minX) >> 1,
(box.maxY + box.minY) >> 1,
(box.maxZ + box.minZ) >> 1);
flags.save = true;
flags.gravity = false;
flags.active = false;
flags.status = ITEM_FLAGS_STATUS_NONE;
flags.collision = true;
flags.custom = 0;
flags.shadow = false;
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;
if (flags.once) // once -> invisible
{
flags.status = ITEM_FLAGS_STATUS_INVISIBLE;
flags.once = false;
}
if (flags.mask == ITEM_FLAGS_MASK_ALL) // full set of mask -> reverse
{
flags.mask = 0;
flags.active = true;
flags.reverse = true;
activate();
}
ASSERT(type <= ITEM_MAX);
ASSERT(room);
room->add(this);
}
void Item::update()
{
//
}
void Item::draw()
{
drawItem(this);
}
void Item::collide(Lara* lara, CollisionInfo* cinfo)
{
UNUSED(lara);
UNUSED(cinfo);
}
Item* Item::init(Room* room)
{
#define INIT_ITEM(type, className) case ITEM_##type : return new (this) className(room)
switch (type)
{
INIT_ITEM( LARA , Lara );
// INIT_ITEM( DOPPELGANGER , ??? );
INIT_ITEM( WOLF , Wolf );
INIT_ITEM( BEAR , Bear );
INIT_ITEM( BAT , Bat );
// INIT_ITEM( CROCODILE_LAND , ??? );
// INIT_ITEM( CROCODILE_WATER , ??? );
// INIT_ITEM( LION_MALE , ??? );
// INIT_ITEM( LION_FEMALE , ??? );
// INIT_ITEM( PUMA , ??? );
// INIT_ITEM( GORILLA , ??? );
// INIT_ITEM( RAT_LAND , ??? );
// INIT_ITEM( RAT_WATER , ??? );
// INIT_ITEM( REX , ??? );
// INIT_ITEM( RAPTOR , ??? );
// INIT_ITEM( MUTANT_1 , ??? );
// INIT_ITEM( MUTANT_2 , ??? );
// INIT_ITEM( MUTANT_3 , ??? );
// INIT_ITEM( CENTAUR , ??? );
// INIT_ITEM( MUMMY , ??? );
// INIT_ITEM( LARSON , ??? );
// INIT_ITEM( PIERRE , ??? );
// INIT_ITEM( SKATEBOARD , ??? );
// INIT_ITEM( SKATER , ??? );
// INIT_ITEM( COWBOY , ??? );
// INIT_ITEM( MR_T , ??? );
// INIT_ITEM( NATLA , ??? );
// INIT_ITEM( GIANT_MUTANT , ??? );
INIT_ITEM( TRAP_FLOOR , TrapFloor );
// INIT_ITEM( TRAP_SWING_BLADE , ??? );
// INIT_ITEM( TRAP_SPIKES , ??? );
// INIT_ITEM( TRAP_BOULDER , ??? );
INIT_ITEM( DART , Dart );
INIT_ITEM( TRAP_DART_EMITTER , TrapDartEmitter );
// INIT_ITEM( DRAWBRIDGE , ??? );
// INIT_ITEM( TRAP_SLAM , ??? );
// INIT_ITEM( TRAP_SWORD , ??? );
// INIT_ITEM( HAMMER_HANDLE , ??? );
// INIT_ITEM( HAMMER_BLOCK , ??? );
// INIT_ITEM( LIGHTNING , ??? );
// INIT_ITEM( MOVING_OBJECT , ??? );
// INIT_ITEM( BLOCK_1 , ??? );
// INIT_ITEM( BLOCK_2 , ??? );
// INIT_ITEM( BLOCK_3 , ??? );
// INIT_ITEM( BLOCK_4 , ??? );
// INIT_ITEM( MOVING_BLOCK , ??? );
// INIT_ITEM( TRAP_CEILING_1 , ??? );
// INIT_ITEM( TRAP_CEILING_2 , ??? );
INIT_ITEM( SWITCH , Switch );
INIT_ITEM( SWITCH_WATER , SwitchWater );
INIT_ITEM( DOOR_1 , Door );
INIT_ITEM( DOOR_2 , Door );
INIT_ITEM( DOOR_3 , Door );
INIT_ITEM( DOOR_4 , Door );
INIT_ITEM( DOOR_5 , Door );
INIT_ITEM( DOOR_6 , Door );
INIT_ITEM( DOOR_7 , Door );
INIT_ITEM( DOOR_8 , Door );
// INIT_ITEM( TRAP_DOOR_1 , ??? );
// INIT_ITEM( TRAP_DOOR_2 , ??? );
// INIT_ITEM( BRIDGE_FLAT , ??? );
// INIT_ITEM( BRIDGE_TILT1 , ??? );
// INIT_ITEM( BRIDGE_TILT2 , ??? );
// INIT_ITEM( INV_PASSPORT , ??? );
// INIT_ITEM( INV_COMPASS , ??? );
// INIT_ITEM( INV_HOME , ??? );
// INIT_ITEM( GEARS_1 , ??? );
// INIT_ITEM( GEARS_2 , ??? );
// INIT_ITEM( GEARS_3 , ??? );
// INIT_ITEM( CUT_1 , ??? );
// INIT_ITEM( CUT_2 , ??? );
// INIT_ITEM( CUT_3 , ??? );
// INIT_ITEM( CUT_4 , ??? );
// INIT_ITEM( INV_PASSPORT_CLOSED , ??? );
// INIT_ITEM( INV_MAP , ??? );
// INIT_ITEM( CRYSTAL , ??? );
INIT_ITEM( PISTOLS , Pickup );
INIT_ITEM( SHOTGUN , Pickup );
INIT_ITEM( MAGNUMS , Pickup );
INIT_ITEM( UZIS , Pickup );
INIT_ITEM( AMMO_PISTOLS , Pickup );
INIT_ITEM( AMMO_SHOTGUN , Pickup );
INIT_ITEM( AMMO_MAGNUMS , Pickup );
INIT_ITEM( AMMO_UZIS , Pickup );
INIT_ITEM( EXPLOSIVE , Pickup );
INIT_ITEM( MEDIKIT_SMALL , Pickup );
INIT_ITEM( MEDIKIT_BIG , Pickup );
// INIT_ITEM( INV_DETAIL , ??? );
// INIT_ITEM( INV_SOUND , ??? );
// INIT_ITEM( INV_CONTROLS , ??? );
// INIT_ITEM( INV_GAMMA , ??? );
// INIT_ITEM( INV_PISTOLS , ??? );
// INIT_ITEM( INV_SHOTGUN , ??? );
// INIT_ITEM( INV_MAGNUMS , ??? );
// INIT_ITEM( INV_UZIS , ??? );
// INIT_ITEM( INV_AMMO_PISTOLS , ??? );
// INIT_ITEM( INV_AMMO_SHOTGUN , ??? );
// INIT_ITEM( INV_AMMO_MAGNUMS , ??? );
// INIT_ITEM( INV_AMMO_UZIS , ??? );
// INIT_ITEM( INV_EXPLOSIVE , ??? );
// INIT_ITEM( INV_MEDIKIT_SMALL , ??? );
// INIT_ITEM( INV_MEDIKIT_BIG , ??? );
INIT_ITEM( PUZZLE_1 , Pickup );
INIT_ITEM( PUZZLE_2 , Pickup );
INIT_ITEM( PUZZLE_3 , Pickup );
INIT_ITEM( PUZZLE_4 , Pickup );
// INIT_ITEM( INV_PUZZLE_1 , ??? );
// INIT_ITEM( INV_PUZZLE_2 , ??? );
// INIT_ITEM( INV_PUZZLE_3 , ??? );
// INIT_ITEM( INV_PUZZLE_4 , ??? );
// INIT_ITEM( PUZZLE_HOLE_1 , ??? );
// INIT_ITEM( PUZZLE_HOLE_2 , ??? );
// INIT_ITEM( PUZZLE_HOLE_3 , ??? );
// INIT_ITEM( PUZZLE_HOLE_4 , ??? );
// INIT_ITEM( PUZZLE_DONE_1 , ??? );
// INIT_ITEM( PUZZLE_DONE_2 , ??? );
// INIT_ITEM( PUZZLE_DONE_3 , ??? );
// INIT_ITEM( PUZZLE_DONE_4 , ??? );
INIT_ITEM( LEADBAR , Pickup );
// INIT_ITEM( INV_LEADBAR , ??? );
// INIT_ITEM( MIDAS_HAND , ??? );
INIT_ITEM( KEY_ITEM_1 , Pickup );
INIT_ITEM( KEY_ITEM_2 , Pickup );
INIT_ITEM( KEY_ITEM_3 , Pickup );
INIT_ITEM( KEY_ITEM_4 , Pickup );
// INIT_ITEM( INV_KEY_ITEM_1 , ??? );
// INIT_ITEM( INV_KEY_ITEM_2 , ??? );
// INIT_ITEM( INV_KEY_ITEM_3 , ??? );
// INIT_ITEM( INV_KEY_ITEM_4 , ??? );
// INIT_ITEM( KEY_HOLE_1 , ??? );
// INIT_ITEM( KEY_HOLE_2 , ??? );
// INIT_ITEM( KEY_HOLE_3 , ??? );
// INIT_ITEM( KEY_HOLE_4 , ??? );
// INIT_ITEM( SCION_PICKUP_QUALOPEC , ??? );
INIT_ITEM( SCION_PICKUP_DROP , Pickup );
INIT_ITEM( SCION_TARGET , ViewTarget );
// INIT_ITEM( SCION_PICKUP_HOLDER , ??? );
// INIT_ITEM( SCION_HOLDER , ??? );
// INIT_ITEM( INV_SCION , ??? );
// INIT_ITEM( EXPLOSION , ??? );
// INIT_ITEM( WATER_SPLASH , ??? );
// INIT_ITEM( BUBBLE , ??? );
// INIT_ITEM( BLOOD , ??? );
// INIT_ITEM( SMOKE , ??? );
// INIT_ITEM( CENTAUR_STATUE , ??? );
// INIT_ITEM( CABIN , ??? );
// INIT_ITEM( MUTANT_EGG_SMALL , ??? );
// INIT_ITEM( RICOCHET , ??? );
// INIT_ITEM( SPARKLES , ??? );
// INIT_ITEM( MUZZLE_FLASH , ??? );
INIT_ITEM( VIEW_TARGET , ViewTarget );
// INIT_ITEM( WATERFALL , ??? );
// INIT_ITEM( NATLA_BULLET , ??? );
// INIT_ITEM( MUTANT_BULLET , ??? );
// INIT_ITEM( CENTAUR_BULLET , ??? );
// INIT_ITEM( LAVA_PARTICLE , ??? );
// INIT_ITEM( TRAP_LAVA_EMITTER , ??? );
// INIT_ITEM( FLAME , ??? );
// INIT_ITEM( TRAP_FLAME_EMITTER , ??? );
// INIT_ITEM( TRAP_LAVA , ??? );
// INIT_ITEM( MUTANT_EGG_BIG , ??? );
// INIT_ITEM( BOAT , ??? );
// INIT_ITEM( EARTHQUAKE , ??? );
}
return new (this) Item(room);
}
#endif

View File

@ -6,20 +6,6 @@
#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 ) \
@ -213,7 +199,7 @@ struct Lara : Item
ANIM_SWITCH_BIG_UP = 196,
ANIM_PUSH_BUTTON = 197,
ANIM_UW_ROLL = 203,
ANIM_UW_ROLL = 203,
};
typedef void (Lara::*Handler)();
@ -228,6 +214,29 @@ struct Lara : Item
void updateCollision()
{
Room** adjRoom = room->getAdjRooms();
while (*adjRoom)
{
Item* item = (*adjRoom++)->firstItem;
while (item)
{
if (item->flags.status != ITEM_FLAGS_STATUS_INVISIBLE)
{
if (item->flags.collision)
{
vec3i d = pos - item->pos;
if (abs(d.x) < 4096 && abs(d.y) < 4096 && abs(d.z) < 4096)
{
item->collide(this, &cinfo);
}
}
}
item = item->nextItem;
}
}
(this->*cHandlers[state])();
}
@ -250,8 +259,8 @@ struct Lara : Item
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;
} else if (angle >= -(ANGLE_180 + 1 + threshold) || angle <= (ANGLE_180 + 1 + threshold)) {
angle = ANGLE_180;
}
return (angle & (ANGLE_90 - 1)) > 0;
@ -267,7 +276,7 @@ struct Lara : Item
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;
case ANGLE_180 : pos.z = z - LARA_RADIUS; break;
default : ASSERT(false);
}
}
@ -280,9 +289,9 @@ struct Lara : Item
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);
Room* roomFront = room->getRoom(x, y, z);
const RoomInfo::Sector* sector = roomFront->getSector(x, z);
int16 floor = sector->getFloor(x, y, z);
if (floor != WALL) {
floor -= pos.y;
@ -298,7 +307,7 @@ struct Lara : Item
const Model* model = models + modelIndex;
AnimFrame* frame = getFrame(this, model);
AnimFrame* frame = getFrame(model);
return frame->box;
}
@ -308,43 +317,10 @@ struct Lara : Item
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)
{
UNUSED(angle); // TODO
return true; // TODO
}
@ -406,7 +382,7 @@ struct Lara : Item
{
if ((state == RUN) || (state == STOP))
{
animSet(this, ANIM_STAND_ROLL_BEGIN, true, 2);
animSet(ANIM_STAND_ROLL_BEGIN, true, 2);
goalState = STOP;
return true;
}
@ -493,7 +469,7 @@ struct Lara : Item
} else {
s_RUN();
}
} else if ((input & IN_DOWN) && s_checkFront(-ANGLE_180)) {
} else if ((input & IN_DOWN) && s_checkFront(ANGLE_180)) {
if (input & IN_WALK) {
s_BACK();
} else {
@ -632,7 +608,7 @@ struct Lara : Item
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) {
} else if ((input & IN_DOWN) && getFront(ANGLE_180) >= -LARA_STEP_HEIGHT) {
goalState = BACK_JUMP;
}
s_checkFall();
@ -685,7 +661,7 @@ struct Lara : Item
S_HANDLER( STEP_RIGHT )
{
if (checkDeath() || (input & (IN_WALK | IN_LEFT)) != (IN_WALK | IN_LEFT))
if (checkDeath() || (input & (IN_WALK | IN_RIGHT)) != (IN_WALK | IN_RIGHT))
{
goalState = STOP;
}
@ -753,7 +729,6 @@ struct Lara : Item
S_HANDLER( HANG_LEFT )
{
camera.targetAngleX = -60 * DEG2SHORT;
camera.targetAngleY = -10 * DEG2SHORT;
s_ignoreEnemy();
@ -766,7 +741,6 @@ struct Lara : Item
S_HANDLER( HANG_RIGHT )
{
camera.targetAngleX = -60 * DEG2SHORT;
camera.targetAngleY = 10 * DEG2SHORT;
s_ignoreEnemy();
@ -908,9 +882,7 @@ struct Lara : Item
// collision control
void c_applyOffset() {
pos.x += cinfo.offset.x;
pos.y += cinfo.offset.y;
pos.z += cinfo.offset.z;
pos += cinfo.offset;
cinfo.offset = vec3i(0, 0, 0);
}
@ -926,7 +898,7 @@ struct Lara : Item
return false;
}
animSet(this, ANIM_STAND, true);
animSet(ANIM_STAND, true);
goalState = state;
hSpeed = 0;
vSpeed = 0;
@ -988,7 +960,7 @@ struct Lara : Item
angleX += 2 * DEG2SHORT;
}
int32 waterDepth = getWaterDepth(room);
int32 waterDepth = room->getWaterDepth();
if (waterDepth == WALL) {
vSpeed = 0;
@ -996,7 +968,7 @@ struct Lara : Item
} else if (waterDepth <= 512) {
waterState = WATER_STATE_WADE;
animSet(this, ANIM_SWIM_STAND, true);
animSet(ANIM_SWIM_STAND, true);
goalState = STOP;
angleX = 0;
@ -1004,10 +976,6 @@ struct Lara : Item
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;
@ -1044,23 +1012,23 @@ struct Lara : Item
} else if (cinfo.slantX < -2) {
angle = ANGLE_90;
} else if (cinfo.slantZ > 2) {
angle = -ANGLE_180;
angle = ANGLE_180;
} else {
angle = 0;
}
if (abs(angle - angleY) <= ANGLE_90) {
if (state != SLIDE) {
animSet(this, ANIM_SLIDE_FORTH, true);
animSet(ANIM_SLIDE_FORTH, true);
}
moveAngle = angle;
angleY = angle;
} else {
if (state != SLIDE_BACK) {
animSet(this, ANIM_SLIDE_BACK, true);
animSet(ANIM_SLIDE_BACK, true);
}
moveAngle = angle;
angleY = angle - ANGLE_180;
angleY = angle + ANGLE_180;
}
return true;
@ -1075,7 +1043,7 @@ struct Lara : Item
return false;
}
animSet(this, fallAnimIndex, true);
animSet(fallAnimIndex, true);
vSpeed = 0;
flags.gravity = true;
@ -1094,7 +1062,7 @@ struct Lara : Item
/* TODO
v2floor = pos.y;
checkTrigger(cinfo.trigger, false);
checkTrigger(cinfo.trigger, this);
*/
pos.y = y;
@ -1120,15 +1088,15 @@ struct Lara : Item
case ANGLE_0 : z += 256; break;
case ANGLE_90 : x += 256; break;
case -ANGLE_90 : x -= 256; break;
case -ANGLE_180 : z -= 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);
Room* roomBelow = room->getRoom(x, y, z);
const RoomInfo::Sector* sector = roomBelow->getSector(x, z);
int32 floor = sector->getFloor(x, y, z);
if (floor != WALL) {
int32 ceiling = getCeiling(sector, x, y, z);
int32 ceiling = sector->getCeiling(x, y, z);
floor -= y;
ceiling -= y;
@ -1168,31 +1136,32 @@ struct Lara : Item
if (cinfo.f.floor >= -640 && cinfo.f.floor <= -384) {
if (c_checkSpace()) return false;
animSet(this, ANIM_CLIMB_2, true);
animSet(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);
animSet(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);
animSet(ANIM_STAND, true);
goalState = UP_JUMP;
vSpeedHack = -int32(phd_sqrt(-2 * GRAVITY * (cinfo.f.floor + 800)) + 3);
animUpdate(this);
updateAnim();
} /* 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);
animSet(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);
animSet(ANIM_STAND, true);
goalState = CLIMB_START;
processAnimation();
}*/ else {
@ -1228,12 +1197,12 @@ struct Lara : Item
if (state == REACH)
{
if (c_checkSwing()) {
animSet(this, ANIM_HANG_SWING, true);
animSet(ANIM_HANG_SWING, true);
} else {
animSet(this, ANIM_HANG, true);
animSet(ANIM_HANG, true);
}
} else {
animSet(this, ANIM_HANG, true, 12);
animSet(ANIM_HANG, true, 12);
}
cinfo.offset.y = cinfo.f.floor - getBounds().minY;
@ -1260,7 +1229,7 @@ struct Lara : Item
hSpeed = 2;
vSpeed = 1;
animSet(this, ANIM_FALL_FORTH, true);
animSet(ANIM_FALL_FORTH, true);
return true;
}
@ -1287,7 +1256,7 @@ struct Lara : Item
angleY += h - 5;
updateRoom(curItemIndex, -LARA_HEIGHT / 2);
updateRoom(-LARA_HEIGHT / 2);
alignWall();
@ -1302,7 +1271,7 @@ struct Lara : Item
}
game->waterDrop(pos, 128.0f, 0.2f);
*/
animSet(this, ANIM_WATER_OUT, true);
animSet(ANIM_WATER_OUT, true);
waterState = WATER_STATE_ABOVE;
goalState = STOP;
@ -1335,7 +1304,7 @@ struct Lara : Item
if (c_checkCeiling()) return;
if (c_checkWall()) {
animSet(this, ANIM_STAND, true);
animSet(ANIM_STAND, true);
}
if (c_checkSlide()) return;
@ -1352,7 +1321,7 @@ struct Lara : Item
} else if (state == FORWARD_JUMP && (input & IN_UP) && !(input & IN_WALK)) {
goalState = RUN;
} else if (state == FALL) {
animSet(this, ANIM_LANDING, true);
animSet(ANIM_LANDING, true);
} else {
goalState = STOP;
}
@ -1364,7 +1333,7 @@ struct Lara : Item
flags.gravity = false;
if (state == FORWARD_JUMP) {
animUpdate(this);
updateAnim();
}
}
@ -1388,8 +1357,8 @@ struct Lara : Item
vSpeed = 1;
}
} else if (!slide && ((cinfo.type == CT_FRONT) || (cinfo.type == CT_FRONT_CEILING))) {
animSet(this, ANIM_SMASH_JUMP, true, 1);
moveAngle -= ANGLE_180;
animSet(ANIM_SMASH_JUMP, true, 1);
moveAngle += ANGLE_180;
hSpeed /= 4;
if (vSpeed <= 0) {
vSpeed = 1;
@ -1482,7 +1451,7 @@ struct Lara : Item
moveAngle = angleY + angleDelta;
if (health <= 0 || !(input & IN_ACTION)) {
animSet(this, ANIM_FALL_HANG, true, 9);
animSet(ANIM_FALL_HANG, true, 9);
cinfo.offset.y = cinfo.f.floor - getBounds().minY + 2;
c_applyOffset();
@ -1499,7 +1468,7 @@ struct Lara : Item
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);
animSet(ANIM_HANG, true, 21);
}
pos = cinfo.pos;
return;
@ -1532,11 +1501,11 @@ struct Lara : Item
if (c_checkWall()) {
if (frameIndex >= 29 && frameIndex <= 47) {
animSet(this, ANIM_STAND_RIGHT, false);
animSet(ANIM_STAND_RIGHT, false);
} else if ((frameIndex >= 22 && frameIndex <= 28) || (frameIndex >= 48 && frameIndex <= 57)) {
animSet(this, ANIM_STAND_LEFT, false);
animSet(ANIM_STAND_LEFT, false);
} else {
animSet(this, ANIM_STAND, false);
animSet(ANIM_STAND, false);
}
}
@ -1545,18 +1514,18 @@ struct Lara : Item
// descend
if (cinfo.m.floor > 128) {
if (frameIndex >= 28 && frameIndex <= 45) {
animSet(this, ANIM_WALK_DESCEND_RIGHT, false);
animSet(ANIM_WALK_DESCEND_RIGHT, false);
} else {
animSet(this, ANIM_WALK_DESCEND_LEFT, false);
animSet(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);
animSet(ANIM_WALK_ASCEND_RIGHT, false);
} else {
animSet(this, ANIM_WALK_ASCEND_LEFT, false);
animSet(ANIM_WALK_ASCEND_LEFT, false);
}
}
@ -1584,12 +1553,12 @@ struct Lara : Item
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);
animSet(frameIndex < 10 ? ANIM_SMASH_RUN_LEFT : ANIM_SMASH_RUN_RIGHT, false);
state = SPLAT;
return;
}
animSet(this, ANIM_STAND, true);
animSet(ANIM_STAND, true);
}
if (c_checkFall(LARA_STEP_HEIGHT)) return;
@ -1597,9 +1566,9 @@ struct Lara : Item
// ascend
if (cinfo.m.floor >= -LARA_STEP_HEIGHT && cinfo.m.floor < -128) {
if (frameIndex >= 3 && frameIndex <= 14) {
animSet(this, ANIM_RUN_ASCEND_RIGHT, false);
animSet(ANIM_RUN_ASCEND_RIGHT, false);
} else {
animSet(this, ANIM_RUN_ASCEND_LEFT, false);
animSet(ANIM_RUN_ASCEND_LEFT, false);
}
}
@ -1648,7 +1617,7 @@ struct Lara : Item
vSpeed = 0;
flags.gravity = false;
c_angle(-ANGLE_180);
c_angle(ANGLE_180);
cinfo.gapPos = -WALL;
cinfo.gapNeg = -LARA_STEP_HEIGHT;
@ -1662,7 +1631,7 @@ struct Lara : Item
if (c_checkFall(200, ANIM_FALL_BACK)) return;
if (c_checkWall()) {
animSet(this, ANIM_STAND, false);
animSet(ANIM_STAND, false);
}
pos.y += cinfo.m.floor;
@ -1753,7 +1722,7 @@ struct Lara : Item
collideRoom(this, LARA_HEIGHT, 0);
if (cinfo.m.ceiling > -100) {
animSet(this, ANIM_STAND, true);
animSet(ANIM_STAND, true);
pos = cinfo.pos;
hSpeed = 0;
vSpeed = 0;
@ -1766,7 +1735,7 @@ struct Lara : Item
vSpeed = 0;
flags.gravity = false;
c_angle(-ANGLE_180);
c_angle(ANGLE_180);
cinfo.gapPos = (waterState == WATER_STATE_WADE) ? -WALL : LARA_STEP_HEIGHT;
cinfo.gapNeg = -LARA_STEP_HEIGHT;
@ -1779,15 +1748,15 @@ struct Lara : Item
if (c_checkWall())
{
animSet(this, ANIM_STAND, true);
animSet(ANIM_STAND, true);
}
if (cinfo.m.floor > 128 && cinfo.m.floor < LARA_STEP_HEIGHT)
{
if (frameIndex < 568) {
animSet(this, ANIM_BACK_DESCEND_LEFT, false);
animSet(ANIM_BACK_DESCEND_LEFT, false);
} else {
animSet(this, ANIM_BACK_DESCEND_RIGHT, false);
animSet(ANIM_BACK_DESCEND_RIGHT, false);
}
}
@ -1824,7 +1793,7 @@ struct Lara : Item
C_HANDLER( ROLL_END )
{
c_angle(-ANGLE_180);
c_angle(ANGLE_180);
c_roll();
}
@ -1836,7 +1805,7 @@ struct Lara : Item
C_HANDLER( BACK_JUMP )
{
c_angle(-ANGLE_180);
c_angle(ANGLE_180);
c_jump();
}
@ -1860,7 +1829,7 @@ struct Lara : Item
C_HANDLER( FALL_BACK )
{
c_angle(-ANGLE_180);
c_angle(ANGLE_180);
c_jump();
}
@ -1876,7 +1845,7 @@ struct Lara : Item
C_HANDLER( SLIDE_BACK )
{
c_angle(-ANGLE_180);
c_angle(ANGLE_180);
c_slide();
}
@ -1985,6 +1954,15 @@ struct Lara : Item
c_default();
}
Lara(Room* room) : Item(room)
{
health = LARA_MAX_HEALTH;
oxygen = LARA_MAX_OXYGEN;
flags.shadow = true;
activate();
}
// update control
void updateInput()
{
@ -2003,7 +1981,7 @@ struct Lara : Item
if ((keys & (IK_L | IK_R)) == (IK_L | IK_R)) input |= IN_LOOK;
}
void update()
virtual void update()
{
updateInput();
@ -2021,14 +1999,15 @@ struct Lara : Item
turnSpeed = angleDec(turnSpeed, 2 * DEG2SHORT);
angleY += turnSpeed;
animUpdate(this);
updateAnim();
updateCollision();
updateRoom(curItemIndex, -LARA_HEIGHT / 2);
updateRoom(-LARA_HEIGHT / 2);
//updateWeapon();
//checkTrigger(cinfo.trigger, false);
checkTrigger(cinfo.trigger, this);
}
};

View File

@ -9,7 +9,8 @@ extern const uint8* tiles;
extern uint16 palette[256];
extern uint8 lightmap[256 * 32];
const uint16* floors;
const FloorData* floors;
int32 texturesCount;
extern const Texture* textures;
@ -60,95 +61,21 @@ const uint8* soundData;
//int32 soundOffsetsCount;
const uint32* soundOffsets;
#define MAX_DYN_SECTORS 1024
int32 dynSectorsCount;
EWRAM_DATA RoomInfo::Sector dynSectors[MAX_DYN_SECTORS]; // EWRAM 8k
// -----------------------------------
int16 roomsCount;
EWRAM_DATA Room rooms[64];
int32 firstActive = NO_ITEM;
Item* Item::sFirstActive;
Item* Item::sFirstFree;
int32 visRoomsCount;
int32 visRooms[16];
Room* roomsList[MAX_ROOM_LIST];
#define ROOM_VISIBLE (1 << 15)
void roomItemAdd(int32 roomIndex, int32 itemIndex)
{
Item* item = items + itemIndex;
Room* room = rooms + roomIndex;
ASSERT(item->nextItem == NO_ITEM);
item->room = roomIndex;
item->nextItem = room->firstItem;
room->firstItem = itemIndex;
}
void roomItemRemove(int32 itemIndex)
{
Item* item = items + itemIndex;
Room* room = rooms + item->room;
item->room = NO_ROOM;
int32 prevIndex = NO_ITEM;
int32 index = room->firstItem;
while (index != NO_ITEM)
{
int32 next = items[index].nextItem;
if (index == itemIndex)
{
items[index].nextItem = NO_ITEM;
if (prevIndex == NO_ITEM) {
room->firstItem = next;
} else {
items[prevIndex].nextItem = next;
}
break;
}
prevIndex = index;
index = next;
}
}
void activateItem(int32 itemIndex)
{
items[itemIndex].nextActive = firstActive;
firstActive = itemIndex;
}
void deactivateItem(int32 itemIndex)
{
int32 prevIndex = NO_ITEM;
int32 index = firstActive;
while (index != NO_ITEM)
{
int32 next = items[index].nextActive;
if (index == itemIndex)
{
items[index].nextItem = NO_ITEM;
if (prevIndex == NO_ITEM) {
firstActive = next;
} else {
items[prevIndex].nextActive = next;
}
break;
}
prevIndex = index;
index = next;
}
}
void fixLightmap(int32 palIndex)
void fixLightmap(uint16* palette, int32 palIndex)
{
uint16 color = palette[palIndex];
@ -168,7 +95,13 @@ void fixLightmap(int32 palIndex)
}
}
void readLevel(const uint8* data) { // TODO non-hardcode level loader, added *_OFF alignment bytes
void readLevel(const uint8* data) // TODO non-hardcode level loader, added *_OFF alignment bytes
{
Item::sFirstActive = NULL;
Item::sFirstFree = NULL;
dynSectorsCount = 0;
// tilesCount = *((int32*)(data + 4));
tiles = data + 8;
@ -178,7 +111,7 @@ void readLevel(const uint8* data) { // TODO non-hardcode level loader, added *_O
roomsCount = *((int16*)(data + 720908));
const RoomInfo* roomsPtr = (RoomInfo*)(data + 720908 + 2);
floors = (uint16*)(data + 899492 + 4);
floors = (FloorData*)(data + 899492 + 4);
meshData = data + 908172 + 4;
meshOffsets = (int32*)(data + 975724 + 4);
@ -229,7 +162,7 @@ void readLevel(const uint8* data) { // TODO non-hardcode level loader, added *_O
// 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));
// int32 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));
@ -237,22 +170,38 @@ void readLevel(const uint8* data) { // TODO non-hardcode level loader, added *_O
memset(items, 0, sizeof(items));
for (int32 i = 0; i < itemsCount; i++) {
memcpy(items + i, itemsPtr, FILE_ITEM_SIZE);
memcpy(&items[i].type, itemsPtr, FILE_ITEM_SIZE);
itemsPtr += FILE_ITEM_SIZE;
}
// prepare free list
for (int32 i = MAX_ITEMS - 1; i >= itemsCount; i--)
{
items[i].nextItem = items + i + 1;
}
Item::sFirstFree = items + itemsCount;
// prepare lightmap
const uint8* f_lightmap = data + 1320576 + MDL_OFF + ITM_OFF;
memcpy(lightmap, f_lightmap, sizeof(lightmap));
// TODO preprocess
for (int i = 0; i < 32; i++) {
lightmap[i * 256] = 0;
}
// prepare palette
const uint8* f_palette = data + 1328768 + MDL_OFF + ITM_OFF;
const uint8* p = f_palette;
for (int i = 0; i < 256; i++)
#ifdef MODE_PAL
uint16 palette[256];
#endif
for (int i = 0; i < 256; i++) // TODO preprocess
{
#if defined(_WIN32) || defined(__GBA__)
#if defined(_WIN32) || defined(__GBA__) || defined(__DOS__)
// grayscale palette
//uint8 c = ((p[0] + p[1] + p[2]) / 3) >> 1;
//palette[i] = c | (c << 5) | (c << 10);
@ -266,8 +215,12 @@ void readLevel(const uint8* data) { // TODO non-hardcode level loader, added *_O
palette[0] = 0; // black or transparent
// TODO preprocess fix Laras palette
fixLightmap(6); // boots
fixLightmap(14); // skin
fixLightmap(palette, 6); // boots
fixLightmap(palette, 14); // skin
#ifdef MODE_PAL
paletteSet(palette);
#endif
// prepare rooms
uint8* ptr = (uint8*)roomsPtr;
@ -283,7 +236,7 @@ void readLevel(const uint8* data) { // TODO non-hardcode level loader, added *_O
Room &desc = rooms[roomIndex];
desc.firstItem = NO_ITEM;
desc.firstItem = NULL;
// offset
memcpy(&desc.x, &room->x, sizeof(room->x));
@ -320,6 +273,7 @@ void readLevel(const uint8* data) { // TODO non-hardcode level loader, added *_O
desc.xSectors = *((uint16*)ptr);
ptr += 2;
desc.sectors = (RoomInfo::Sector*)ptr;
desc.sectorsOrig = desc.sectors;
ptr += sizeof(RoomInfo::Sector) * desc.zSectors * desc.xSectors;
desc.ambient = *((uint16*)ptr);
@ -355,319 +309,4 @@ void readLevel(const uint8* data) { // TODO non-hardcode level loader, added *_O
}
}
int32 getBridgeFloor(const Item* item, int32 x, int32 z)
{
if (item->type == ITEM_BRIDGE_1) {
return item->pos.y;
}
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;
}
case ITEM_DRAWBRIDGE:
{
if (item->state == 1) {
h = getDrawBridgeFloor(item, x, z);
}
break;
}
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);
}
if ((floor && (h >= *floor)) || (ceiling && (h <= *ceiling)))
{
h = WALL;
}
}
default : return;
}
if (floor && (y <= h))
{
*floor = h;
}
if (ceiling && (y > h))
{
*ceiling = h + 256;
}
}
const RoomInfo::Sector* getSector(int32 roomIndex, int32 x, int32 z)
{
Room &room = rooms[roomIndex];
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;
}
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;
}
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, x, z);
}
while (sector->roomBelow != NO_ROOM && y >= (sector->floor << 8))
{
roomIndex = sector->roomBelow;
sector = getSector(roomIndex, x, z);
}
return roomIndex;
}
void getTriggerFloorData(const RoomInfo::Sector* sector, int32 x, int32 y, int32 z, int32* floor, int32* ceiling)
{
if (!sector->floorIndex)
return;
FloorData::Command cmd;
FloorData* fd = (FloorData*)(floors + sector->floorIndex);
do {
cmd = (fd++)->cmd;
switch (cmd.func)
{
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;
}
}
getTriggerFloorData(lowerSector, x, y, z, &floor, NULL);
return floor;
}
int32 getCeiling(const RoomInfo::Sector* sector, int32 x, int32 y, int32 z)
{
const RoomInfo::Sector* upperSector = getSectorAbove(sector, x, z);
int32 ceiling = upperSector->ceiling << 8;
if (upperSector->floorIndex)
{
FloorData* fd = (FloorData*)(floors + upperSector->floorIndex);
FloorData::Command cmd = (fd++)->cmd;
if (cmd.func == FLOOR_TYPE_FLOOR) // skip floor
{
fd++;
cmd = (fd++)->cmd;
}
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

View File

@ -1,7 +1,9 @@
#if defined(_WIN32)
#if defined(_WIN32) || defined(__DOS__)
void* LEVEL1_PHD;
void* TRACK_13_WAV;
#elif defined(__GBA__)
#include "LEVEL1_PHD.h"
#include "TRACK_13_WAV.h"
#elif defined(__TNS__)
void* LEVEL1_PHD;
#endif
@ -10,8 +12,16 @@
Game game;
int32 fps;
int32 frameIndex = 0;
int32 fpsCounter = 0;
#if defined(_WIN32)
uint32 SCREEN[FRAME_WIDTH * FRAME_HEIGHT];
#ifdef MODE_PAL
uint32 SCREEN[FRAME_WIDTH * FRAME_HEIGHT];
#else
uint32 SCREEN[VRAM_WIDTH * FRAME_HEIGHT];
#endif
HWND hWnd;
@ -20,8 +30,22 @@ Game game;
#define WND_WIDTH 240*4
#define WND_HEIGHT 160*4
#ifdef MODE_PAL
uint16 MEM_PAL_BG[256];
#endif
void paletteSet(uint16* palette)
{
#ifdef MODE_PAL
memcpy(MEM_PAL_BG, palette, 256 * 2);
#endif
}
#elif defined(__GBA__)
//
void paletteSet(uint16* palette)
{
memcpy((uint16*)MEM_PAL_BG, palette, 256 * 2);
}
#elif defined(__TNS__)
unsigned int osTime;
volatile unsigned int *timerBUS;
@ -48,16 +72,20 @@ Game game;
return (osTime - *timerCLK) / 33;
}
void SetPalette(unsigned short* palette)
void paletteSet(uint16* palette)
{
unsigned short *palReg = (unsigned short*)0xC0000200;
memcpy(palReg, palette, 256 * 2);
memcpy((uint16*)0xC0000200, palette, 256 * 2);
}
touchpad_info_t* touchInfo;
touchpad_report_t touchReport;
uint8 inputData[0x20];
bool keyDown(const t_key &key)
{
return (*(short*)(inputData + key.tpad_row)) & key.tpad_col;
}
void inputInit()
{
touchInfo = is_touchpad ? touchpad_getinfo() : NULL;
@ -65,24 +93,154 @@ Game game;
void inputUpdate()
{
keys = 0;
if (touchInfo)
{
touchpad_scan(&touchReport);
}
memcpy(inputData, (void*)0x900E0000, 0x20);
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;
}
#elif defined(__DOS__)
#define KB_ESC 1
#define KB_A 30
#define KB_S 31
#define KB_Z 44
#define KB_X 45
#define KB_UP 72
#define KB_LEFT 75
#define KB_RIGHT 77
#define KB_DOWN 80
#define KB_ENTER 20
#define KB_TAB 15
#define DOS_ISR __interrupt __far
#define PIT_TIMER 0x08
#define PIT_KEYBOARD 0x09
void (DOS_ISR *old_timerISR)();
void (DOS_ISR *old_keyISR)();
bool keyState[128];
void setVideoMode();
#pragma aux setVideoMode = \
"mov ax,13h" \
"int 10h";
void setTextMode();
#pragma aux setTextMode = \
"mov ax,03h" \
"int 10h";
void paletteSet(uint16* palette)
{
outp(0x03C8, 0);
for (int32 i = 0; i < 256; i++)
{
uint16 c = *palette++;
outp(0x03C9, (c & 0x1F) << 1);
outp(0x03C9, ((c >> 5) & 0x1F) << 1);
outp(0x03C9, ((c >> 10) & 0x1F) << 1);
}
}
bool keyDown(const t_key &key)
void DOS_ISR timerISR()
{
return (*(short*)(inputData + key.tpad_row)) & key.tpad_col;
frameIndex++;
outp(0x20, 0x20);
}
void videoAcquire()
{
setVideoMode();
old_timerISR = _dos_getvect(PIT_TIMER);
_dos_setvect(PIT_TIMER, timerISR);
uint32 divisor = 1193182 / 60;
outp(0x43, 0x36);
outp(0x40, divisor & 0xFF);
outp(0x40, divisor >> 8);
}
void videoRelease()
{
_dos_setvect(PIT_TIMER, old_timerISR);
setTextMode();
}
void waitVBlank()
{
while ((inp(0x03DA) & 0x08));
while (!(inp(0x03DA) & 0x08));
}
void blit()
{
memcpy((uint8*)0xA0000, fb, VRAM_WIDTH * FRAME_HEIGHT * 2);
}
void DOS_ISR keyISR()
{
uint32 scancode = inp(0x60);
if (scancode != 0xE0) {
keyState[scancode & 0x7F] = ((scancode & 0x80) == 0);
}
outp(0x20, 0x20);
}
void inputAcquire()
{
old_keyISR = _dos_getvect(PIT_KEYBOARD);
_dos_setvect(PIT_KEYBOARD, keyISR);
}
void inputRelease()
{
_dos_setvect(PIT_KEYBOARD, old_keyISR);
}
void inputUpdate()
{
keys = 0;
if (keyState[KB_UP]) keys |= IK_UP;
if (keyState[KB_RIGHT]) keys |= IK_RIGHT;
if (keyState[KB_DOWN]) keys |= IK_DOWN;
if (keyState[KB_LEFT]) keys |= IK_LEFT;
if (keyState[KB_X]) keys |= IK_A;
if (keyState[KB_Z]) keys |= IK_B;
if (keyState[KB_A]) keys |= IK_L;
if (keyState[KB_S]) keys |= IK_R;
if (keyState[KB_ENTER]) keys |= IK_START;
if (keyState[KB_TAB]) keys |= IK_SELECT;
}
#endif
int32 fps;
int32 frameIndex = 0;
int32 fpsCounter = 0;
#ifdef PROFILE
uint32 dbg_transform;
uint32 dbg_poly;
@ -91,7 +249,11 @@ int32 fpsCounter = 0;
uint32 dbg_poly_count;
#endif
EWRAM_DATA ALIGN16 uint8 soundBuffer[2 * SND_SAMPLES + 32]; // 32 bytes of silence for DMA overrun while interrupt
EWRAM_DATA ALIGN16 uint8 soundBufferA[2 * SND_SAMPLES + 32]; // 32 bytes of silence for DMA overrun while interrupt
#ifdef USE_9BIT_SOUND
EWRAM_DATA ALIGN16 uint8 soundBufferB[2 * SND_SAMPLES + 32]; // for 9-bit mixer support via Direct Mixer B channel
#endif
uint32 curSoundBuffer = 0;
#if defined(_WIN32)
@ -100,7 +262,7 @@ WAVEFORMATEX waveFmt = { WAVE_FORMAT_PCM, 1, SND_OUTPUT_FREQ, SND_OUTPUT_FREQ, 1
WAVEHDR waveBuf[2];
void soundInit() {
sound.init();
mixer.init();
if (waveOutOpen(&waveOut, WAVE_MAPPER, &waveFmt, (INT_PTR)hWnd, 0, CALLBACK_WINDOW) != MMSYSERR_NOERROR) {
return;
@ -110,7 +272,7 @@ void soundInit() {
for (int i = 0; i < 2; i++) {
WAVEHDR *waveHdr = waveBuf + i;
waveHdr->dwBufferLength = SND_SAMPLES;
waveHdr->lpData = (LPSTR)(soundBuffer + i * SND_SAMPLES);
waveHdr->lpData = (LPSTR)(soundBufferA + i * SND_SAMPLES);
waveOutPrepareHeader(waveOut, waveHdr, sizeof(WAVEHDR));
waveOutWrite(waveOut, waveHdr, sizeof(WAVEHDR));
}
@ -119,7 +281,7 @@ void soundInit() {
void soundFill() {
WAVEHDR *waveHdr = waveBuf + curSoundBuffer;
waveOutUnprepareHeader(waveOut, waveHdr, sizeof(WAVEHDR));
sound.fill((uint8*)waveHdr->lpData, SND_SAMPLES);
mixer.fill((uint8*)waveHdr->lpData, NULL, SND_SAMPLES);
waveOutPrepareHeader(waveOut, waveHdr, sizeof(WAVEHDR));
waveOutWrite(waveOut, waveHdr, sizeof(WAVEHDR));
curSoundBuffer ^= 1;
@ -127,24 +289,41 @@ void soundFill() {
#elif defined(__GBA__)
void soundInit()
{
sound.init();
mixer.init();
REG_SOUNDCNT_X = SSTAT_ENABLE;
REG_SOUNDCNT_H = SDS_ATMR0 | SDS_A100 | SDS_AL | SDS_AR | SDS_ARESET;
#ifdef USE_9BIT_SOUND
REG_SOUNDCNT_H |= SDS_BTMR0 | SDS_B100 | SDS_BL | SDS_BR | SDS_BRESET;
#endif
REG_TM0D = 65536 - (16777216 / SND_OUTPUT_FREQ);
REG_TM0CNT = TM_ENABLE;
REG_DMA1DAD = (u32)&REG_FIFO_A;
#ifdef USE_9BIT_SOUND
REG_DMA2DAD = (u32)&REG_FIFO_B;
#endif
}
void soundFill()
{
if (curSoundBuffer == 1) {
REG_DMA1CNT = 0;
REG_DMA1SAD = (u32)soundBuffer;
REG_DMA1SAD = (u32)soundBufferA;
REG_DMA1CNT = DMA_DST_FIXED | DMA_REPEAT | DMA_16 | DMA_AT_FIFO | DMA_ENABLE;
#ifdef USE_9BIT_SOUND
REG_DMA2CNT = 0;
REG_DMA2SAD = (u32)soundBufferB;
REG_DMA2CNT = DMA_DST_FIXED | DMA_REPEAT | DMA_16 | DMA_AT_FIFO | DMA_ENABLE;
#endif
}
sound.fill(soundBuffer + curSoundBuffer * SND_SAMPLES, SND_SAMPLES);
mixer.fill(soundBufferA + curSoundBuffer * SND_SAMPLES,
#ifdef USE_9BIT_SOUND
soundBufferB + curSoundBuffer * SND_SAMPLES,
#else
NULL,
#endif
SND_SAMPLES);
curSoundBuffer ^= 1;
}
#endif
@ -153,7 +332,14 @@ void soundFill()
HDC hDC;
void blit() {
#ifdef ROTATE90_MODE
#ifdef MODE_PAL
for (int i = 0; i < FRAME_WIDTH * FRAME_HEIGHT; i++) {
uint16 c = MEM_PAL_BG[((uint8*)fb)[i]];
SCREEN[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000;
}
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);
#elif defined(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;
@ -163,11 +349,11 @@ void blit() {
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++) {
for (int i = 0; i < VRAM_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), FRAME_WIDTH, -FRAME_HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 };
const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), VRAM_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
}
@ -180,8 +366,10 @@ LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
break;
}
case WM_KEYDOWN :
case WM_KEYUP :
case WM_KEYDOWN :
case WM_KEYUP :
case WM_SYSKEYUP :
case WM_SYSKEYDOWN :
{
InputKey key = IK_NONE;
switch (wParam) {
@ -197,7 +385,7 @@ LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
case VK_SPACE : key = IK_SELECT; break;
}
if (msg != WM_KEYUP) {
if (msg != WM_KEYUP && msg != WM_SYSKEYUP) {
keys |= key;
} else {
keys &= ~key;
@ -227,9 +415,10 @@ void vblank() {
#endif
int main(void) {
#if defined(_WIN32) || defined(__TNS__)
#if defined(_WIN32) || defined(__TNS__) || defined(__DOS__)
{
#if defined(_WIN32)
// level1
#if defined(_WIN32) || defined(__DOS__)
FILE *f = fopen("data/LEVEL1.PHD", "rb");
#elif defined(__TNS__)
FILE *f = fopen("/documents/OpenLara/LEVEL1.PHD.tns", "rb");
@ -247,14 +436,29 @@ int main(void) {
LEVEL1_PHD = new uint8[size];
fread(LEVEL1_PHD, 1, size, f);
fclose(f);
// track 13
#if defined(_WIN32) || defined(__DOS__)
{
FILE *f = fopen("data/TRACK_13.WAV", "rb");
if (!f) {
return 0;
}
fseek(f, 0, SEEK_END);
int32 size = ftell(f);
fseek(f, 0, SEEK_SET);
TRACK_13_WAV = new uint8[size];
fread(TRACK_13_WAV, 1, size, f);
fclose(f);
}
#endif
}
#elif defined(__GBA__)
// set low latency mode via WAITCNT register (thanks to GValiente)
REG_WSCNT = WS_ROM0_N2 | WS_ROM0_S1 | WS_PREFETCH;
#endif
game.init();
#if defined(_WIN32)
RECT r = { 0, 0, WND_WIDTH, WND_HEIGHT };
@ -270,6 +474,8 @@ int main(void) {
soundInit();
game.init();
MSG msg;
int startTime = GetTickCount() - 33;
@ -280,7 +486,6 @@ int main(void) {
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
int frame = (GetTickCount() - startTime) / 33;
game.update(frame - lastFrame);
lastFrame = frame;
@ -303,17 +508,25 @@ int main(void) {
soundInit();
uint16 mode = DCNT_MODE5 | DCNT_BG2 | DCNT_PAGE;
game.init();
#ifdef ROTATE90_MODE
uint16 mode = DCNT_BG2 | DCNT_PAGE;
#ifdef MODE4
mode |= DCNT_MODE4;
REG_BG2PA = (1 << 8);
REG_BG2PD = (1 << 8);
#elif defined(ROTATE90_MODE)
mode |= DCNT_MODE5;
REG_BG2PA = 0;
REG_BG2PB = (1 << 8);
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;
mode |= DCNT_MODE5;
REG_BG2PA = (1 << 7);
REG_BG2PD = (1 << 7);
#endif
int32 lastFrameIndex = -1;
@ -365,41 +578,19 @@ int main(void) {
timerInit();
inputInit();
game.init();
int startTime = GetTickCount();
int lastTime = -16;
int fpsTime = startTime;
memset(keys, 0, sizeof(keys));
while (1)
{
inputUpdate();
if (keyDown(KEY_NSPIRE_ESC))
{
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;
break;
}
int time = GetTickCount() - startTime;
@ -419,5 +610,44 @@ int main(void) {
fpsTime = lastTime - ((lastTime - fpsTime) - 1000);
}
}
#elif defined(__DOS__)
videoAcquire();
inputAcquire();
game.init();
int32 lastFrameIndex = -1;
//int extraFrame = 0;
while (1)
{
inputUpdate();
if (keyState[KB_ESC])
break;
int32 frame = frameIndex / 2;
game.update(frame - lastFrameIndex);
lastFrameIndex = frame;
game.render();
fpsCounter++;
if (frameIndex >= 60) {
frameIndex -= 60;
lastFrameIndex -= 30;
fps = fpsCounter;
fpsCounter = 0;
}
blit();
}
inputRelease();
videoRelease();
#endif
return 0;
}

432
src/platform/gba/object.h Normal file
View File

@ -0,0 +1,432 @@
#ifndef H_OBJECT
#define H_OBJECT
#include "item.h"
#include "lara.h"
vec3i getBlockOffset(int16 angleY, int32 offset)
{
if (angleY == ANGLE_0)
return vec3i(0, 0, -offset);
if (angleY == ANGLE_180)
return vec3i(0, 0, offset);
if (angleY == ANGLE_90)
return vec3i(-offset, 0, 0);
return vec3i(offset, 0, 0);
}
struct Limit
{
Box box;
vec3s angle;
};
namespace Limits
{
static const Limit SWITCH = {
Box( -200, 200, 0, 0, 312, 512 ),
vec3s( 10 * DEG2SHORT, 30 * DEG2SHORT, 10 * DEG2SHORT )
};
};
struct Object : Item
{
Object(Room* room) : Item(room) {}
virtual void update()
{
updateAnim();
}
bool isActive()
{
if ((flags.mask != ITEM_FLAGS_MASK_ALL) || (timer == -1))
return flags.reverse == 1;
if (timer == 0)
return flags.reverse == 0;
timer--;
if (timer == 0)
timer = -1;
return flags.reverse == 0;
}
bool checkLimit(Lara* lara, const Limit& limit)
{
int16 ax = abs(lara->angleX - angleX);
int16 ay = abs(lara->angleY - angleY);
int16 az = abs(lara->angleZ - angleZ);
if (ax > limit.angle.x || ay > limit.angle.y || az > limit.angle.z)
return false;
vec3i d = lara->pos - pos;
matrixSetIdentity();
matrixRotateZXY(-angleX, -angleY, -angleZ);
const Matrix &m = matrixGet();
vec3i p;
p.x = DP33(m[0], d) >> FIXED_SHIFT;
p.y = DP33(m[1], d) >> FIXED_SHIFT;
p.z = DP33(m[2], d) >> FIXED_SHIFT;
return boxContains(limit.box, p);
}
};
struct ViewTarget : Object
{
ViewTarget(Room* room) : Object(room) {}
virtual void draw() {}
};
struct Door : Object
{
enum {
STATE_CLOSE,
STATE_OPEN,
};
Door(Room* room) : Object(room)
{
flags.collision = true;
action(true);
}
virtual void update()
{
if (isActive()) {
if (state == STATE_CLOSE) {
goalState = STATE_OPEN;
} else {
action(false);
}
} else {
if (state == STATE_OPEN) {
goalState = STATE_CLOSE;
} else {
action(true);
}
}
updateAnim();
}
virtual void collide(Lara* lara, CollisionInfo* cinfo)
{
UNUSED(lara);
UNUSED(cinfo);
}
void action(bool close)
{
vec3i nextPos = getBlockOffset(angleY, 1);
nextPos.x = pos.x + (nextPos.x << 10);
nextPos.z = pos.z + (nextPos.z << 10);
activate(close, false, room, nextPos.x, nextPos.z); // use the sector behind the door
// TODO flip rooms
}
void activate(bool close, bool behind, Room* room, int32 x, int32 z)
{
room->modify(); // make room->sectors dynamic (non ROM)
RoomInfo::Sector* sector = (RoomInfo::Sector*)room->getSector(x, z); // now we can modify room sectors
Room* nextRoom;
if (close) {
nextRoom = sector->getNextRoom();
sector->floorIndex = 0;
sector->boxIndex = NO_BOX;
sector->roomBelow = NO_ROOM;
sector->floor = NO_FLOOR;
sector->roomAbove = NO_ROOM;
sector->ceiling = NO_FLOOR;
} else {
*sector = room->sectorsOrig[sector - room->sectors];
nextRoom = sector->getNextRoom();
}
if (!behind && nextRoom) {
activate(close, true, nextRoom, pos.x, pos.z); // use sector from item pos
}
}
};
struct Switch : Object
{
enum {
STATE_UP,
STATE_DOWN,
};
Switch(Room* room) : Object(room) {}
virtual void update()
{
flags.mask |= ITEM_FLAGS_MASK_ALL;
if (!isActive())
{
goalState = STATE_DOWN;
timer = 0;
}
Object::update();
}
virtual void collide(Lara* lara, CollisionInfo* cinfo)
{
UNUSED(cinfo);
if (!(lara->input & IN_ACTION))
return;
if (lara->state != Lara::STOP)
return;
if (flags.status != ITEM_FLAGS_STATUS_NONE)
return;
if (!checkLimit(lara, Limits::SWITCH))
return;
lara->angleY = angleY;
ASSERT(state == STATE_DOWN || state == STATE_UP);
if (state == STATE_DOWN) {
lara->goalState = Lara::SWITCH_DOWN;
goalState = STATE_UP;
} else {
lara->goalState = Lara::SWITCH_UP;
goalState = STATE_DOWN;
}
flags.status = ITEM_FLAGS_STATUS_ACTIVE;
activate();
lara->skipAnim();
updateAnim();
lara->goalState = Lara::STOP;
}
bool use(int32 t)
{
if (flags.status == ITEM_FLAGS_STATUS_INACTIVE)
{
if (t > 0 && state == Switch::STATE_UP)
{
if (t != 1) {
t *= 30;
}
timer = t;
flags.status = ITEM_FLAGS_STATUS_ACTIVE;
} else {
deactivate();
flags.status = ITEM_FLAGS_STATUS_NONE;
}
return true;
}
return false;
}
};
struct SwitchWater : Switch
{
SwitchWater(Room* room) : Switch(room) {}
virtual void collide(Lara* lara, CollisionInfo* cinfo)
{
UNUSED(lara);
UNUSED(cinfo);
// TODO
}
};
struct Key : Object
{
Key(Room* room) : Object(room) {}
bool use()
{
if (flags.status == ITEM_FLAGS_STATUS_ACTIVE) // TODO check weapons
{
flags.status = ITEM_FLAGS_STATUS_INACTIVE;
return true;
}
return false;
}
};
struct Pickup : Object
{
Pickup(Room* room) : Object(room) {}
bool use()
{
if (flags.status == ITEM_FLAGS_STATUS_INVISIBLE)
{
flags.status = ITEM_FLAGS_STATUS_INACTIVE;
return true;
}
return false;
}
};
bool useSwitch(Item* item, int32 timer)
{
return ((Switch*)item)->use(timer);
}
bool useKey(Item* item)
{
return ((Key*)item)->use();
}
bool usePickup(Item* item)
{
return ((Pickup*)item)->use();
}
struct TrapFloor : Object
{
enum {
STATE_STATIC,
STATE_SHAKE,
STATE_FALL,
STATE_DOWN,
};
TrapFloor(Room* room) : Object(room) {}
virtual void update()
{
switch (state)
{
case STATE_STATIC:
if (getLara(pos)->pos.y != pos.y - 512)
{
flags.status = ITEM_FLAGS_STATUS_NONE;
deactivate();
return;
}
goalState = STATE_SHAKE;
break;
case STATE_SHAKE:
goalState = STATE_FALL;
break;
case STATE_FALL:
if (goalState != STATE_DOWN)
{
flags.gravity = true;
}
break;
}
updateAnim();
if (flags.status == ITEM_FLAGS_STATUS_INACTIVE)
{
deactivate();
return;
}
updateRoom();
if (state == STATE_FALL && pos.y >= floor)
{
pos.y = floor;
vSpeed = 0;
flags.gravity = false;
goalState = STATE_DOWN;
}
}
};
struct Dart : Object
{
Dart(Room* room) : Object(room)
{
flags.shadow = true;
soundPlay(SND_DART, pos);
// TODO create smoke
}
virtual void update()
{
// TODO collide with Lara
updateAnim();
updateRoom();
if (pos.y >= floor)
{
// TODO create spark
remove();
}
}
};
struct TrapDartEmitter : Object
{
enum {
STATE_IDLE,
STATE_FIRE
};
TrapDartEmitter(Room* room) : Object(room) {}
virtual void update()
{
goalState = isActive() ? STATE_FIRE : STATE_IDLE;
if (state == STATE_IDLE && state == goalState)
{
deactivate();
return;
}
if (state == STATE_FIRE && frameIndex == anims[animIndex].frameBegin)
{
vec3i p = getBlockOffset(angleY, 412);
p.y = -512;
p += pos;
Item* dart = Item::add(ITEM_DART, room, p, angleY);
if (dart)
{
dart->flags.status = ITEM_FLAGS_STATUS_ACTIVE;
dart->activate();
}
}
updateAnim();
}
};
#endif

View File

@ -0,0 +1,296 @@
#include "rasterizer.inc"
pixel .req r0
L .req r1
R .req r2
LMAP .req r3
TILE .req r4
tmp .req r5
N .req r6
Lh .req r7
Rh .req r8
Lx .req ip
Rx .req lr
Lt .req r9
Rt .req r10
h .req r11
Ldx .req h
Rdx .req h
Ldt .req h
Rdt .req h
indexA .req Lh
indexB .req Rh
Ry1 .req tmp
Ry2 .req Rh
Ly1 .req tmp
Ly2 .req Lh
inv .req Lh
DIVLUT .req N
DIVLUTi .req L
width .req N
t .req L
dtdx .req R
duv .req R
du .req L
dv .req R
Lduv .req h
Ldu .req N
Ldv .req h
Rduv .req h
Rdu .req N
Rdv .req h
SP_LDX = 0
SP_LDT = 4
SP_RDX = 8
SP_RDT = 12
.macro PUT_PIXELS
and indexA, t, #0xFF00
orr indexA, t, lsr #24 // indexA = t.v * 256 + t.u
ldrb indexA, [TILE, indexA]
add t, dtdx
and indexB, t, #0xFF00
orr indexB, t, lsr #24 // indexB = t.v * 256 + t.u
ldrb indexB, [TILE, indexB]
add t, dtdx
// cheap non-accurate alpha test, skip pixels pair if both are transparent
orrs indexB, indexA, indexB, lsl #8 // indexB = indexA | (indexB << 8)
ldrneb indexA, [LMAP, indexA]
ldrneb indexB, [LMAP, indexB, lsr #8]
orrne indexA, indexB, lsl #8
strneh indexA, [tmp]
add tmp, #2
.endm
.global rasterizeFTA_mode4_asm
rasterizeFTA_mode4_asm:
stmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,lr}
sub sp, #16 // reserve stack space for [Ldx, Ldt, Rdx, Rdt]
ldr LMAP, =lightmap
ldrb tmp, [L, #VERTEX_G]
add LMAP, tmp, lsl #8 // tmp = (L->v.g << 8)
ldr TILE, =tile
ldr TILE, [TILE]
mov Lh, #0 // Lh = 0
mov Rh, #0 // Rh = 0
.loop:
.calc_left_start:
cmp Lh, #0
bne .calc_left_end // if (Lh != 0) end with left
ldr N, [L, #VERTEX_PREV] // N = L->prev
ldrsh Ly1, [L, #VERTEX_Y] // Ly1 = L->v.y
ldrsh Ly2, [N, #VERTEX_Y] // Ly2 = N->v.y
subs Lh, Ly2, Ly1 // Lh = Ly2 - Ly1
blt .exit // if (Lh < 0) return
ldrsh Lx, [L, #VERTEX_X] // Lx = L->v.x
ldr Lt, [L, #VERTEX_T] // Lt = L->t
mov L, N // L = N
cmp Lh, #1 // if (Lh <= 1) skip Ldx calc
ble .skip_left_dx
lsl tmp, Lh, #1
ldr DIVLUT, =divTable
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Lh)
ldrsh Ldx, [L, #VERTEX_X]
sub Ldx, Lx
mul Ldx, tmp // Ldx = tmp * (N->v.x - Lx)
str Ldx, [sp, #SP_LDX] // store Ldx to stack
ldr Lduv, [L, #VERTEX_T]
sub Lduv, Lt // Lduv = N->v.t - Lt
asr Ldu, Lduv, #16
mul Ldu, tmp // Rdu = tmp * int16(Lduv >> 16)
lsl Ldv, Lduv, #16
asr Ldv, #16
mul Ldv, tmp // Rdv = tmp * int16(Lduv);
lsr Ldu, #16
lsl Ldu, #16
orr Ldt, Ldu, Ldv, lsr #16 // Ldt = (Rdu & 0xFFFF0000) | (Rdv >> 16)
str Ldt, [sp, #SP_LDT] // store Ldt to stack
.skip_left_dx:
lsl Lx, #16 // Lx <<= 16
b .calc_left_start
.calc_left_end:
.calc_right_start:
cmp Rh, #0
bne .calc_right_end // if (Rh != 0) end with right
ldr N, [R, #VERTEX_NEXT] // N = R->next
ldrsh Ry1, [R, #VERTEX_Y] // Ry1 = R->v.y
ldrsh Ry2, [N, #VERTEX_Y] // Ry2 = N->v.y
subs Rh, Ry2, Ry1 // Rh = Ry2 - Ry1
blt .exit // if (Rh < 0) return
ldrsh Rx, [R, #VERTEX_X] // Rx = R->v.x
ldr Rt, [R, #VERTEX_T] // Rt = R->t
mov R, N // R = N
cmp Rh, #1 // if (Rh <= 1) skip Rdx calc
ble .skip_right_dx
lsl tmp, Rh, #1
ldr DIVLUT, =divTable
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Rh)
ldrsh Rdx, [R, #VERTEX_X]
sub Rdx, Rx
mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx)
str Rdx, [sp, #SP_RDX] // store Rdx to stack
ldr Rduv, [R, #VERTEX_T]
sub Rduv, Rt // Rduv = N->v.t - Rt
asr Rdu, Rduv, #16
mul Rdu, tmp // Rdu = tmp * int16(Rduv >> 16)
lsl Rdv, Rduv, #16
asr Rdv, #16
mul Rdv, tmp // Rdv = tmp * int16(Rduv);
lsr Rdu, #16
lsl Rdu, #16
orr Rdt, Rdu, Rdv, lsr #16 // Rdt = (Rdu & 0xFFFF0000) | (Rdv >> 16)
str Rdt, [sp, #SP_RDT] // store Rdt to stack
.skip_right_dx:
lsl Rx, #16 // Rx <<= 16
b .calc_right_start
.calc_right_end:
cmp Rh, Lh // if (Rh < Lh)
movlt h, Rh // h = Rh
movge h, Lh // else h = Lh
sub Lh, h // Lh -= h
sub Rh, h // Rh -= h
stmfd sp!, {L,R,Lh,Rh} // sp-16
.scanline_start:
asr tmp, Lx, #16 // x1 = (Lx >> 16)
rsbs width, tmp, Rx, asr #16 // width = (Rx >> 16) - x1
ble .scanline_end // if (width <= 0) go next scanline
add tmp, pixel, tmp // tmp = pixel + x1
ldr DIVLUTi, =divTable
lsl inv, width, #1
ldrh inv, [DIVLUTi, inv] // inv = FixedInvU(width)
sub duv, Rt, Lt // duv = Rt - Lt
asr du, duv, #16
mul du, inv // du = inv * int16(duv >> 16)
lsl dv, duv, #16
asr dv, #16
mul dv, inv // dv = inv * int16(duv);
lsr du, #16
lsl du, #16
orr dtdx, du, dv, lsr #16 // dtdx = (du & 0xFFFF0000) | (dv >> 16)
mov t, Lt // t = Lt
// 2 bytes alignment (VRAM write requirement)
.align_left:
tst tmp, #1 // if (tmp & 1)
beq .align_right
ldrb indexB, [tmp, #-1]! // read pal index from VRAM (byte)
and indexA, t, #0xFF00
orr indexA, t, lsr #24 // res = (t & 0xFF00) | (t >> 24)
add t, dtdx
ldrb indexA, [TILE, indexA]
ldrb indexA, [LMAP, indexA]
cmp indexA, #0
ldrneb indexA, [LMAP, indexA]
orrne indexB, indexA, lsl #8
strneh indexB, [tmp]
add tmp, #2
subs width, #1 // width--;
beq .scanline_end // if (width == 0)
.align_right:
tst width, #1
beq .align_block_4px
ldrb indexB, [tmp, width]
sub Rt, dtdx
and indexA, Rt, #0xFF00
orr indexA, Rt, lsr #24 // res = (t & 0xFF00) | (t >> 24)
add Rt, dtdx
ldrb indexA, [TILE, indexA]
cmp indexA, #0
ldrneb indexA, [LMAP, indexA]
orrne indexB, indexA, indexB, lsl #8
strneh indexB, [tmp, width]
subs width, #1 // width--
beq .scanline_end // if (width == 0)
.align_block_4px:
tst width, #2
beq .align_block_8px
PUT_PIXELS
subs width, #2
beq .scanline_end
.align_block_8px:
tst width, #4
beq .scanlin_block_8px
PUT_PIXELS
PUT_PIXELS
subs width, #4
beq .scanline_end
.scanlin_block_8px:
PUT_PIXELS
PUT_PIXELS
PUT_PIXELS
PUT_PIXELS
subs width, #8
bne .scanlin_block_8px
.scanline_end:
ldr tmp, [sp, #(SP_LDX + 16)]
add Lx, tmp // Lx += Ldx from stack
ldr tmp, [sp, #(SP_LDT + 16)]
add Lt, tmp // Lt += Ldt from stack
ldr tmp, [sp, #(SP_RDX + 16)]
add Rx, tmp // Rx += Rdx from stack
ldr tmp, [sp, #(SP_RDT + 16)]
add Rt, tmp // Rt += Rdt from stack
add pixel, #VRAM_STRIDE // pixel += FRAME_WIDTH (240)
subs h, #1
bne .scanline_start
ldmfd sp!, {L,R,Lh,Rh} // sp+16
b .loop
.exit:
add sp, #16 // revert reserved space for [Ldx, Ldt, Rdx, Rdt]
ldmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,pc}

View File

@ -0,0 +1,290 @@
#include "rasterizer.inc"
pixel .req r0
L .req r1
R .req r2
LMAP .req r3
TILE .req r4
tmp .req r5
N .req r6
Lh .req r7
Rh .req r8
Lx .req ip
Rx .req lr
Lt .req r9
Rt .req r10
h .req r11
Ldx .req h
Rdx .req h
Ldt .req h
Rdt .req h
indexA .req Lh
indexB .req Rh
Ry1 .req tmp
Ry2 .req Rh
Ly1 .req tmp
Ly2 .req Lh
inv .req Lh
DIVLUT .req N
DIVLUTi .req L
width .req N
t .req L
dtdx .req R
duv .req R
du .req L
dv .req R
Lduv .req h
Ldu .req N
Ldv .req h
Rduv .req h
Rdu .req N
Rdv .req h
SP_LDX = 0
SP_LDT = 4
SP_RDX = 8
SP_RDT = 12
.macro PUT_PIXELS
and indexA, t, #0xFF00
orr indexA, t, lsr #24 // indexA = t.v * 256 + t.u
ldrb indexA, [TILE, indexA]
ldrb indexA, [LMAP, indexA]
add t, dtdx
and indexB, t, #0xFF00
orr indexB, t, lsr #24 // indexB = t.v * 256 + t.u
ldrb indexB, [TILE, indexB]
ldrb indexB, [LMAP, indexB]
add t, dtdx
orr indexA, indexB, lsl #8
strh indexA, [tmp], #2
.endm
.global rasterizeFT_mode4_asm
rasterizeFT_mode4_asm:
stmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,lr}
sub sp, #16 // reserve stack space for [Ldx, Ldt, Rdx, Rdt]
ldr LMAP, =lightmap
ldrb tmp, [L, #VERTEX_G]
add LMAP, tmp, lsl #8 // tmp = (L->v.g << 8)
ldr TILE, =tile
ldr TILE, [TILE]
mov Lh, #0 // Lh = 0
mov Rh, #0 // Rh = 0
.loop:
.calc_left_start:
cmp Lh, #0
bne .calc_left_end // if (Lh != 0) end with left
ldr N, [L, #VERTEX_PREV] // N = L->prev
ldrsh Ly1, [L, #VERTEX_Y] // Ly1 = L->v.y
ldrsh Ly2, [N, #VERTEX_Y] // Ly2 = N->v.y
subs Lh, Ly2, Ly1 // Lh = Ly2 - Ly1
blt .exit // if (Lh < 0) return
ldrsh Lx, [L, #VERTEX_X] // Lx = L->v.x
ldr Lt, [L, #VERTEX_T] // Lt = L->t
mov L, N // L = N
cmp Lh, #1 // if (Lh <= 1) skip Ldx calc
ble .skip_left_dx
lsl tmp, Lh, #1
ldr DIVLUT, =divTable
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Lh)
ldrsh Ldx, [L, #VERTEX_X]
sub Ldx, Lx
mul Ldx, tmp // Ldx = tmp * (N->v.x - Lx)
str Ldx, [sp, #SP_LDX] // store Ldx to stack
ldr Lduv, [L, #VERTEX_T]
sub Lduv, Lt // Lduv = N->v.t - Lt
asr Ldu, Lduv, #16
mul Ldu, tmp // Rdu = tmp * int16(Lduv >> 16)
lsl Ldv, Lduv, #16
asr Ldv, #16
mul Ldv, tmp // Rdv = tmp * int16(Lduv);
lsr Ldu, #16
lsl Ldu, #16
orr Ldt, Ldu, Ldv, lsr #16 // Ldt = (Rdu & 0xFFFF0000) | (Rdv >> 16)
str Ldt, [sp, #SP_LDT] // store Ldt to stack
.skip_left_dx:
lsl Lx, #16 // Lx <<= 16
b .calc_left_start
.calc_left_end:
.calc_right_start:
cmp Rh, #0
bne .calc_right_end // if (Rh != 0) end with right
ldr N, [R, #VERTEX_NEXT] // N = R->next
ldrsh Ry1, [R, #VERTEX_Y] // Ry1 = R->v.y
ldrsh Ry2, [N, #VERTEX_Y] // Ry2 = N->v.y
subs Rh, Ry2, Ry1 // Rh = Ry2 - Ry1
blt .exit // if (Rh < 0) return
ldrsh Rx, [R, #VERTEX_X] // Rx = R->v.x
ldr Rt, [R, #VERTEX_T] // Rt = R->t
mov R, N // R = N
cmp Rh, #1 // if (Rh <= 1) skip Rdx calc
ble .skip_right_dx
lsl tmp, Rh, #1
ldr DIVLUT, =divTable
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Rh)
ldrsh Rdx, [R, #VERTEX_X]
sub Rdx, Rx
mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx)
str Rdx, [sp, #SP_RDX] // store Rdx to stack
ldr Rduv, [R, #VERTEX_T]
sub Rduv, Rt // Rduv = N->v.t - Rt
asr Rdu, Rduv, #16
mul Rdu, tmp // Rdu = tmp * int16(Rduv >> 16)
lsl Rdv, Rduv, #16
asr Rdv, #16
mul Rdv, tmp // Rdv = tmp * int16(Rduv);
lsr Rdu, #16
lsl Rdu, #16
orr Rdt, Rdu, Rdv, lsr #16 // Rdt = (Rdu & 0xFFFF0000) | (Rdv >> 16)
str Rdt, [sp, #SP_RDT] // store Rdt to stack
.skip_right_dx:
lsl Rx, #16 // Rx <<= 16
b .calc_right_start
.calc_right_end:
cmp Rh, Lh // if (Rh < Lh)
movlt h, Rh // h = Rh
movge h, Lh // else h = Lh
sub Lh, h // Lh -= h
sub Rh, h // Rh -= h
stmfd sp!, {L,R,Lh,Rh} // sp-16
.scanline_start:
asr tmp, Lx, #16 // x1 = (Lx >> 16)
rsbs width, tmp, Rx, asr #16 // width = (Rx >> 16) - x1
ble .scanline_end // if (width <= 0) go next scanline
add tmp, pixel, tmp // tmp = pixel + x1
ldr DIVLUTi, =divTable
lsl inv, width, #1
ldrh inv, [DIVLUTi, inv] // inv = FixedInvU(width)
sub duv, Rt, Lt // duv = Rt - Lt
asr du, duv, #16
mul du, inv // du = inv * int16(duv >> 16)
lsl dv, duv, #16
asr dv, #16
mul dv, inv // dv = inv * int16(duv);
lsr du, #16
lsl du, #16
orr dtdx, du, dv, lsr #16 // dtdx = (du & 0xFFFF0000) | (dv >> 16)
mov t, Lt // t = Lt
// 2 bytes alignment (VRAM write requirement)
.align_left:
tst tmp, #1 // if (tmp & 1)
beq .align_right
ldrb indexB, [tmp, #-1]! // read pal index from VRAM (byte)
and indexA, t, #0xFF00
orr indexA, t, lsr #24 // res = (t & 0xFF00) | (t >> 24)
ldrb indexA, [TILE, indexA]
ldrb indexA, [LMAP, indexA]
orr indexB, indexA, lsl #8
strh indexB, [tmp], #2
add t, dtdx
subs width, #1 // width--;
beq .scanline_end // if (width == 0)
.align_right:
tst width, #1
beq .align_block_4px
ldrb indexB, [tmp, width]
subs width, #1 // width--
sub Rt, dtdx
and indexA, Rt, #0xFF00
orr indexA, Rt, lsr #24 // res = (t & 0xFF00) | (t >> 24)
add Rt, dtdx
ldrb indexA, [TILE, indexA]
ldrb indexA, [LMAP, indexA]
orr indexB, indexA, indexB, lsl #8
strh indexB, [tmp, width]
beq .scanline_end // if (width == 0)
.align_block_4px:
tst width, #2
beq .align_block_8px
PUT_PIXELS
subs width, #2
beq .scanline_end
.align_block_8px:
tst width, #4
beq .scanlin_block_8px
PUT_PIXELS
PUT_PIXELS
subs width, #4
beq .scanline_end
.scanlin_block_8px:
PUT_PIXELS
PUT_PIXELS
PUT_PIXELS
PUT_PIXELS
subs width, #8
bne .scanlin_block_8px
.scanline_end:
ldr tmp, [sp, #(SP_LDX + 16)]
add Lx, tmp // Lx += Ldx from stack
ldr tmp, [sp, #(SP_LDT + 16)]
add Lt, tmp // Lt += Ldt from stack
ldr tmp, [sp, #(SP_RDX + 16)]
add Rx, tmp // Rx += Rdx from stack
ldr tmp, [sp, #(SP_RDT + 16)]
add Rt, tmp // Rt += Rdt from stack
add pixel, #VRAM_STRIDE // pixel += FRAME_WIDTH (240)
subs h, #1
bne .scanline_start
ldmfd sp!, {L,R,Lh,Rh} // sp+16
b .loop
.exit:
add sp, #16 // revert reserved space for [Ldx, Ldt, Rdx, Rdt]
ldmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,pc}

View File

@ -0,0 +1,156 @@
#include "rasterizer.inc"
pixel .req r0
L .req r1
R .req r2
index .req r3
Lh .req r4
Rh .req r5
Lx .req ip
Rx .req lr
Ldx .req r6
Rdx .req r7
N .req r8
tmp .req r9
DIVLUT .req r10
width .req r11
h .req N
Ry1 .req tmp
Ry2 .req Rh
Ly1 .req tmp
Ly2 .req Lh
LMAP .req Lx
pair .req DIVLUT
blocks .req DIVLUT
.global rasterizeF_mode4_asm
rasterizeF_mode4_asm:
stmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,lr}
ldr LMAP, =lightmap
ldrb tmp, [L, #VERTEX_G]
orr tmp, index, tmp, lsl #8 // tmp = index | (L->v.g << 8)
ldrb index, [LMAP, tmp] // tmp = lightmap[tmp]
orr index, index, lsl #8
mov Lh, #0 // Lh = 0
mov Rh, #0 // Rh = 0
.loop:
ldr DIVLUT, =divTable
.calc_left_start:
cmp Lh, #0
bne .calc_left_end // if (Lh != 0) end with left
ldr N, [L, #VERTEX_PREV] // N = L->prev
ldrsh Ly1, [L, #VERTEX_Y] // Ly1 = L->v.y
ldrsh Ly2, [N, #VERTEX_Y] // Ly2 = N->v.y
subs Lh, Ly2, Ly1 // Lh = Ly2 - Ly1
blt .exit // if (Lh < 0) return
ldrsh Lx, [L, #VERTEX_X] // Lx = L->v.x
cmp Lh, #1 // if (Lh <= 1) skip Ldx calc
ble .skip_left_dx
lsl tmp, Lh, #1
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Lh)
ldrsh Ldx, [N, #VERTEX_X]
sub Ldx, Lx
mul Ldx, tmp // Ldx = tmp * (N->v.x - Lx)
.skip_left_dx:
lsl Lx, #16 // Lx <<= 16
mov L, N // L = N
b .calc_left_start
.calc_left_end:
.calc_right_start:
cmp Rh, #0
bne .calc_right_end // if (Rh != 0) end with right
ldr N, [R, #VERTEX_NEXT] // N = R->next
ldrsh Ry1, [R, #VERTEX_Y] // Ry1 = R->v.y
ldrsh Ry2, [N, #VERTEX_Y] // Ry2 = N->v.y
subs Rh, Ry2, Ry1 // Rh = Ry2 - Ry1
blt .exit // if (Rh < 0) return
ldrsh Rx, [R, #VERTEX_X] // Rx = R->v.x
cmp Rh, #1 // if (Rh <= 1) skip Rdx calc
ble .skip_right_dx
lsl tmp, Rh, #1
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Rh)
ldrsh Rdx, [N, #VERTEX_X]
sub Rdx, Rx
mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx)
.skip_right_dx:
lsl Rx, #16 // Rx <<= 16
mov R, N // R = N
b .calc_right_start
.calc_right_end:
cmp Rh, Lh // if (Rh < Lh)
movlt h, Rh // h = Rh
movge h, Lh // else h = Lh
sub Lh, h // Lh -= h
sub Rh, h // Rh -= h
.scanline_start:
asr tmp, Lx, #16 // x1 = (Lx >> 16)
rsbs width, tmp, Rx, asr #16 // width = (Rx >> 16) - x1
ble .scanline_end // if (width <= 0) go next scanline
add tmp, pixel, tmp // tmp = pixel + x1
// 2 bytes alignment (VRAM write requirement)
.align_left:
tst tmp, #1 // if (tmp & 1)
beq .align_right
ldrb pair, [tmp, #-1]! // *tmp++ = (*tmp & 0x00FF) | (index << 8);
orr pair, index, lsl #8
strh pair, [tmp], #2
subs width, #1 // width--;
beq .scanline_end // if (width == 0)
.align_right:
tst width, #1
beq .align_block
ldrb pair, [tmp, width]
subs width, #1 // width--
lsl pair, #8
orr pair, index, lsr #8
strh pair, [tmp, width]
beq .scanline_end // if (width == 0)
.align_block:
// 8px alignment
lsr blocks, width, #1
and blocks, blocks, #3 // blocks = (width / 2) % 4
rsbs blocks, blocks, #2
add pc, blocks, lsl #2 // pc += (2 - blocks) * 4
strh index, [tmp], #2 // if (width % 4) == 3
strh index, [tmp], #2 // if (width % 4) == 2
strh index, [tmp], #2 // if (width % 4) == 1
lsrs blocks, width, #3 // blocks = (width / 2) / 4
beq .scanline_end // if (blocks == 0) no 8px blocks
.scanline_block_8px:
strh index, [tmp], #2
strh index, [tmp], #2
strh index, [tmp], #2
strh index, [tmp], #2
subs blocks, #1
bne .scanline_block_8px
.scanline_end:
add Lx, Ldx // Lx += Ldx
add Rx, Rdx // Rx += Rdx
add pixel, #VRAM_STRIDE // pixel += FRAME_WIDTH (240)
subs h, #1
bne .scanline_start
b .loop
.exit:
ldmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,pc}

View File

@ -0,0 +1,358 @@
#include "rasterizer.inc"
pixel .req r0
L .req r1
R .req r2
Lh .req r3
Rh .req r4
Lx .req r5
Rx .req r6
Lg .req r7
Rg .req r8
Lt .req r9
Rt .req r10
tmp .req r11
N .req r12
TILE .req lr
h .req N
LMAP .req tmp
Ldx .req h
Rdx .req h
Ldg .req h
Rdg .req h
Ldt .req h
Rdt .req h
indexA .req Lh
indexB .req Rh
Ry1 .req tmp
Ry2 .req Rh
Ly1 .req tmp
Ly2 .req Lh
inv .req Lh
DIVLUT .req N
DIVLUTi .req tmp
ptr .req Lx
width .req Rx
g .req Lg
dgdx .req Rg
t .req Lt
dtdx .req Rt
duv .req R
du .req L
dv .req R
Lduv .req N
Ldu .req TILE
Ldv .req N
Rduv .req N
Rdu .req TILE
Rdv .req N
Rti .req tmp
Rgi .req tmp
SP_LDX = 0
SP_LDG = 4
SP_LDT = 8
SP_RDX = 12
SP_RDG = 16
SP_RDT = 20
.macro PUT_PIXELS
bic LMAP, g, #255
add g, dgdx
and indexA, t, #0xFF00
orr indexA, t, lsr #24 // indexA = t.v * 256 + t.u
ldrb indexA, [TILE, indexA]
add t, dtdx
and indexB, t, #0xFF00
orr indexB, t, lsr #24 // indexB = t.v * 256 + t.u
ldrb indexB, [TILE, indexB]
add t, dtdx
// cheap non-accurate alpha test, skip pixels pair if both are transparent
orrs indexB, indexA, indexB, lsl #8 // indexB = indexA | (indexB << 8)
ldrneb indexA, [LMAP, indexA]
ldrneb indexB, [LMAP, indexB, lsr #8]
orrne indexA, indexB, lsl #8
strneh indexA, [ptr]
add ptr, #2
.endm
.global rasterizeGTA_mode4_asm
rasterizeGTA_mode4_asm:
stmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,lr}
sub sp, #24 // reserve stack space for [Ldx, Ldg, Ldt, Rdx, Rdg, Rdt]
mov Lh, #0 // Lh = 0
mov Rh, #0 // Rh = 0
.loop:
.calc_left_start:
cmp Lh, #0
bne .calc_left_end // if (Lh != 0) end with left
ldr N, [L, #VERTEX_PREV] // N = L->prev
ldrsh Ly1, [L, #VERTEX_Y] // Ly1 = L->v.y
ldrsh Ly2, [N, #VERTEX_Y] // Ly2 = N->v.y
subs Lh, Ly2, Ly1 // Lh = Ly2 - Ly1
blt .exit // if (Lh < 0) return
ldrsh Lx, [L, #VERTEX_X] // Lx = L->v.x
ldrb Lg, [L, #VERTEX_G] // Lg = L->v.g
ldr Lt, [L, #VERTEX_T] // Lt = L->t
mov L, N // L = N
cmp Lh, #1 // if (Lh <= 1) skip Ldx calc
ble .skip_left_dx
lsl tmp, Lh, #1
ldr DIVLUT, =divTable
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Lh)
ldrsh Ldx, [L, #VERTEX_X]
sub Ldx, Lx
mul Ldx, tmp // Ldx = tmp * (N->v.x - Lx)
str Ldx, [sp, #SP_LDX] // store Ldx to stack
ldrb Ldg, [L, #VERTEX_G]
sub Ldg, Lg
mul Ldg, tmp // Ldg = tmp * (N->v.g - Lg)
asr Ldg, #8 // 8-bit for fractional part
str Ldg, [sp, #SP_LDG] // store Ldg to stack
ldr Lduv, [L, #VERTEX_T]
sub Lduv, Lt // Lduv = N->v.t - Lt
asr Ldu, Lduv, #16
mul Ldu, tmp // Rdu = tmp * int16(Lduv >> 16)
lsl Ldv, Lduv, #16
asr Ldv, #16
mul Ldv, tmp // Rdv = tmp * int16(Lduv);
lsr Ldu, #16
lsl Ldu, #16
orr Ldt, Ldu, Ldv, lsr #16 // Ldt = (Rdu & 0xFFFF0000) | (Rdv >> 16)
str Ldt, [sp, #SP_LDT] // store Ldt to stack
.skip_left_dx:
lsl Lx, #16 // Lx <<= 16
ldr LMAP, =lightmap // !!! lightmap should be 64k aligned
add Lg, LMAP, Lg, lsl #8 // Lg is address in lightmap array
b .calc_left_start
.calc_left_end:
.calc_right_start:
cmp Rh, #0
bne .calc_right_end // if (Rh != 0) end with right
ldr N, [R, #VERTEX_NEXT] // N = R->next
ldrsh Ry1, [R, #VERTEX_Y] // Ry1 = R->v.y
ldrsh Ry2, [N, #VERTEX_Y] // Ry2 = N->v.y
subs Rh, Ry2, Ry1 // Rh = Ry2 - Ry1
blt .exit // if (Rh < 0) return
ldrsh Rx, [R, #VERTEX_X] // Rx = R->v.x
ldrb Rg, [R, #VERTEX_G] // Rg = R->v.g
ldr Rt, [R, #VERTEX_T] // Rt = R->t
mov R, N // R = N
cmp Rh, #1 // if (Rh <= 1) skip Rdx calc
ble .skip_right_dx
lsl tmp, Rh, #1
ldr DIVLUT, =divTable
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Rh)
ldrsh Rdx, [R, #VERTEX_X]
sub Rdx, Rx
mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx)
str Rdx, [sp, #SP_RDX] // store Rdx to stack
ldrb Rdg, [R, #VERTEX_G]
sub Rdg, Rg
mul Rdg, tmp // Rdg = tmp * (N->v.g - Rg)
asr Rdg, #8 // 8-bit for fractional part
str Rdg, [sp, #SP_RDG] // store Ldg to stack
ldr Rduv, [R, #VERTEX_T]
sub Rduv, Rt // Rduv = N->v.t - Rt
asr Rdu, Rduv, #16
mul Rdu, tmp // Rdu = tmp * int16(Rduv >> 16)
lsl Rdv, Rduv, #16
asr Rdv, #16
mul Rdv, tmp // Rdv = tmp * int16(Rduv);
lsr Rdu, #16
lsl Rdu, #16
orr Rdt, Rdu, Rdv, lsr #16 // Rdt = (Rdu & 0xFFFF0000) | (Rdv >> 16)
str Rdt, [sp, #SP_RDT] // store Rdt to stack
.skip_right_dx:
lsl Rx, #16 // Rx <<= 16
ldr LMAP, =lightmap // !!! lightmap should be 64k aligned
add Rg, LMAP, Rg, lsl #8 // Rg is address in lightmap array
b .calc_right_start
.calc_right_end:
cmp Rh, Lh // if (Rh < Lh)
movlt h, Rh // h = Rh
movge h, Lh // else h = Lh
sub Lh, h // Lh -= h
sub Rh, h // Rh -= h
ldr TILE, =tile
ldr TILE, [TILE]
stmfd sp!, {L,R,Lh,Rh} // sp-16
.scanline_start:
stmfd sp!, {Lx,Rx,Lg,Rg,Lt,Rt} // sp-24
asr tmp, Lx, #16 // x1 = (Lx >> 16)
rsbs width, tmp, Rx, asr #16 // width = (Rx >> 16) - x1
ble .scanline_end // if (width <= 0) go next scanline
add ptr, pixel, tmp // ptr = pixel + x1
ldr DIVLUTi, =divTable
lsl inv, width, #1
ldrh inv, [DIVLUTi, inv] // inv = FixedInvU(width)
sub dgdx, Rg, Lg // dgdx = Rg - Lg
mul dgdx, inv // dgdx *= FixedInvU(width)
asr dgdx, #15 // dgdx >>= 15
// g == Lg (alias)
sub duv, Rt, Lt // duv = Rt - Lt
asr du, duv, #16
mul du, inv // du = inv * int16(duv >> 16)
lsl dv, duv, #16
asr dv, #16
mul dv, inv // dv = inv * int16(duv);
lsr du, #16
lsl du, #16
orr dtdx, du, dv, lsr #16 // dtdx = (du & 0xFFFF0000) | (dv >> 16)
// t == Lt (alias)
// 2 bytes alignment (VRAM write requirement)
.align_left:
tst ptr, #1 // if (ptr & 1)
beq .align_right
ldrb indexB, [ptr, #-1]! // read pal index from VRAM (byte)
bic LMAP, g, #255
add g, dgdx, asr #1
and indexA, t, #0xFF00
orr indexA, t, lsr #24 // res = (t & 0xFF00) | (t >> 24)
ldrb indexA, [TILE, indexA]
ldrb indexA, [LMAP, indexA]
orr indexB, indexA, lsl #8
strh indexB, [ptr], #2
add t, dtdx
subs width, #1 // width--;
beq .scanline_end // if (width == 0)
.align_right:
tst width, #1
beq .align_block_4px
ldrb indexB, [ptr, width]
subs width, #1 // width--
mla Rti, width, dtdx, t // Rti = width * dtdx + t
and indexA, Rti, #0xFF00
orr indexA, Rti, lsr #24 // res = (t & 0xFF00) | (t >> 24)
ldrb indexA, [TILE, indexA]
asr Rgi, dgdx, #1
mla Rgi, width, Rgi, g // Rgi = width * (dgdx / 2) + g
bic LMAP, Rgi, #255
ldrb indexA, [LMAP, indexA]
orr indexB, indexA, indexB, lsl #8
strh indexB, [ptr, width]
beq .scanline_end // if (width == 0)
.align_block_4px:
tst width, #2
beq .align_block_8px
PUT_PIXELS
subs width, #2
beq .scanline_end
.align_block_8px:
tst width, #4
beq .scanlin_block_8px
PUT_PIXELS
PUT_PIXELS
subs width, #4
beq .scanline_end
.scanlin_block_8px:
PUT_PIXELS
PUT_PIXELS
PUT_PIXELS
PUT_PIXELS
subs width, #8
bne .scanlin_block_8px
.scanline_end:
ldmfd sp!, {Lx,Rx,Lg,Rg,Lt,Rt} // sp+24
ldr tmp, [sp, #(SP_LDX + 16)]
add Lx, tmp // Lx += Ldx from stack
ldr tmp, [sp, #(SP_LDG + 16)]
add Lg, tmp // Lg += Ldg from stack
ldr tmp, [sp, #(SP_LDT + 16)]
add Lt, tmp // Lt += Ldt from stack
ldr tmp, [sp, #(SP_RDX + 16)]
add Rx, tmp // Rx += Rdx from stack
ldr tmp, [sp, #(SP_RDG + 16)]
add Rg, tmp // Rg += Rdg from stack
ldr tmp, [sp, #(SP_RDT + 16)]
add Rt, tmp // Rt += Rdt from stack
add pixel, #VRAM_STRIDE // pixel += FRAME_WIDTH (240)
subs h, #1
bne .scanline_start
ldmfd sp!, {L,R,Lh,Rh} // sp+16
b .loop
.exit:
add sp, #24 // revert reserved space for [Ldx, Ldg, Ldt, Rdx, Rdg, Rdt]
ldmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,pc}

View File

@ -0,0 +1,355 @@
#include "rasterizer.inc"
pixel .req r0
L .req r1
R .req r2
Lh .req r3
Rh .req r4
Lx .req r5
Rx .req r6
Lg .req r7
Rg .req r8
Lt .req r9
Rt .req r10
tmp .req r11
N .req r12
TILE .req lr
h .req N
LMAP .req tmp
Ldx .req h
Rdx .req h
Ldg .req h
Rdg .req h
Ldt .req h
Rdt .req h
indexA .req Lh
indexB .req Rh
Ry1 .req tmp
Ry2 .req Rh
Ly1 .req tmp
Ly2 .req Lh
inv .req Lh
DIVLUT .req N
DIVLUTi .req tmp
ptr .req Lx
width .req Rx
g .req Lg
dgdx .req Rg
t .req Lt
dtdx .req Rt
duv .req R
du .req L
dv .req R
Lduv .req N
Ldu .req TILE
Ldv .req N
Rduv .req N
Rdu .req TILE
Rdv .req N
Rti .req tmp
Rgi .req tmp
SP_LDX = 0
SP_LDG = 4
SP_LDT = 8
SP_RDX = 12
SP_RDG = 16
SP_RDT = 20
.macro PUT_PIXELS
bic LMAP, g, #255
add g, dgdx
and indexA, t, #0xFF00
orr indexA, t, lsr #24 // indexA = t.v * 256 + t.u
ldrb indexA, [TILE, indexA]
ldrb indexA, [LMAP, indexA]
add t, dtdx
and indexB, t, #0xFF00
orr indexB, t, lsr #24 // indexB = t.v * 256 + t.u
ldrb indexB, [TILE, indexB]
ldrb indexB, [LMAP, indexB]
add t, dtdx
orr indexA, indexB, lsl #8
strh indexA, [ptr], #2
.endm
.global rasterizeGT_mode4_asm
rasterizeGT_mode4_asm:
stmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,lr}
sub sp, #24 // reserve stack space for [Ldx, Ldg, Ldt, Rdx, Rdg, Rdt]
mov Lh, #0 // Lh = 0
mov Rh, #0 // Rh = 0
.loop:
.calc_left_start:
cmp Lh, #0
bne .calc_left_end // if (Lh != 0) end with left
ldr N, [L, #VERTEX_PREV] // N = L->prev
ldrsh Ly1, [L, #VERTEX_Y] // Ly1 = L->v.y
ldrsh Ly2, [N, #VERTEX_Y] // Ly2 = N->v.y
subs Lh, Ly2, Ly1 // Lh = Ly2 - Ly1
blt .exit // if (Lh < 0) return
ldrsh Lx, [L, #VERTEX_X] // Lx = L->v.x
ldrb Lg, [L, #VERTEX_G] // Lg = L->v.g
ldr Lt, [L, #VERTEX_T] // Lt = L->t
mov L, N // L = N
cmp Lh, #1 // if (Lh <= 1) skip Ldx calc
ble .skip_left_dx
lsl tmp, Lh, #1
ldr DIVLUT, =divTable
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Lh)
ldrsh Ldx, [L, #VERTEX_X]
sub Ldx, Lx
mul Ldx, tmp // Ldx = tmp * (N->v.x - Lx)
str Ldx, [sp, #SP_LDX] // store Ldx to stack
ldrb Ldg, [L, #VERTEX_G]
sub Ldg, Lg
mul Ldg, tmp // Ldg = tmp * (N->v.g - Lg)
asr Ldg, #8 // 8-bit for fractional part
str Ldg, [sp, #SP_LDG] // store Ldg to stack
ldr Lduv, [L, #VERTEX_T]
sub Lduv, Lt // Lduv = N->v.t - Lt
asr Ldu, Lduv, #16
mul Ldu, tmp // Rdu = tmp * int16(Lduv >> 16)
lsl Ldv, Lduv, #16
asr Ldv, #16
mul Ldv, tmp // Rdv = tmp * int16(Lduv);
lsr Ldu, #16
lsl Ldu, #16
orr Ldt, Ldu, Ldv, lsr #16 // Ldt = (Rdu & 0xFFFF0000) | (Rdv >> 16)
str Ldt, [sp, #SP_LDT] // store Ldt to stack
.skip_left_dx:
lsl Lx, #16 // Lx <<= 16
ldr LMAP, =lightmap // !!! lightmap should be 64k aligned
add Lg, LMAP, Lg, lsl #8 // Lg is address in lightmap array
b .calc_left_start
.calc_left_end:
.calc_right_start:
cmp Rh, #0
bne .calc_right_end // if (Rh != 0) end with right
ldr N, [R, #VERTEX_NEXT] // N = R->next
ldrsh Ry1, [R, #VERTEX_Y] // Ry1 = R->v.y
ldrsh Ry2, [N, #VERTEX_Y] // Ry2 = N->v.y
subs Rh, Ry2, Ry1 // Rh = Ry2 - Ry1
blt .exit // if (Rh < 0) return
ldrsh Rx, [R, #VERTEX_X] // Rx = R->v.x
ldrb Rg, [R, #VERTEX_G] // Rg = R->v.g
ldr Rt, [R, #VERTEX_T] // Rt = R->t
mov R, N // R = N
cmp Rh, #1 // if (Rh <= 1) skip Rdx calc
ble .skip_right_dx
lsl tmp, Rh, #1
ldr DIVLUT, =divTable
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Rh)
ldrsh Rdx, [R, #VERTEX_X]
sub Rdx, Rx
mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx)
str Rdx, [sp, #SP_RDX] // store Rdx to stack
ldrb Rdg, [R, #VERTEX_G]
sub Rdg, Rg
mul Rdg, tmp // Rdg = tmp * (N->v.g - Rg)
asr Rdg, #8 // 8-bit for fractional part
str Rdg, [sp, #SP_RDG] // store Ldg to stack
ldr Rduv, [R, #VERTEX_T]
sub Rduv, Rt // Rduv = N->v.t - Rt
asr Rdu, Rduv, #16
mul Rdu, tmp // Rdu = tmp * int16(Rduv >> 16)
lsl Rdv, Rduv, #16
asr Rdv, #16
mul Rdv, tmp // Rdv = tmp * int16(Rduv);
lsr Rdu, #16
lsl Rdu, #16
orr Rdt, Rdu, Rdv, lsr #16 // Rdt = (Rdu & 0xFFFF0000) | (Rdv >> 16)
str Rdt, [sp, #SP_RDT] // store Rdt to stack
.skip_right_dx:
lsl Rx, #16 // Rx <<= 16
ldr LMAP, =lightmap // !!! lightmap should be 64k aligned
add Rg, LMAP, Rg, lsl #8 // Rg is address in lightmap array
b .calc_right_start
.calc_right_end:
cmp Rh, Lh // if (Rh < Lh)
movlt h, Rh // h = Rh
movge h, Lh // else h = Lh
sub Lh, h // Lh -= h
sub Rh, h // Rh -= h
ldr TILE, =tile
ldr TILE, [TILE]
stmfd sp!, {L,R,Lh,Rh} // sp-16
.scanline_start:
stmfd sp!, {Lx,Rx,Lg,Rg,Lt,Rt} // sp-24
asr tmp, Lx, #16 // x1 = (Lx >> 16)
rsbs width, tmp, Rx, asr #16 // width = (Rx >> 16) - x1
ble .scanline_end // if (width <= 0) go next scanline
add ptr, pixel, tmp // ptr = pixel + x1
ldr DIVLUTi, =divTable
lsl inv, width, #1
ldrh inv, [DIVLUTi, inv] // inv = FixedInvU(width)
sub dgdx, Rg, Lg // dgdx = Rg - Lg
mul dgdx, inv // dgdx *= FixedInvU(width)
asr dgdx, #15 // dgdx >>= 15
// g == Lg (alias)
sub duv, Rt, Lt // duv = Rt - Lt
asr du, duv, #16
mul du, inv // du = inv * int16(duv >> 16)
lsl dv, duv, #16
asr dv, #16
mul dv, inv // dv = inv * int16(duv);
lsr du, #16
lsl du, #16
orr dtdx, du, dv, lsr #16 // dtdx = (du & 0xFFFF0000) | (dv >> 16)
// t == Lt (alias)
// 2 bytes alignment (VRAM write requirement)
.align_left:
tst ptr, #1 // if (ptr & 1)
beq .align_right
ldrb indexB, [ptr, #-1]! // read pal index from VRAM (byte)
bic LMAP, g, #255
add g, dgdx, asr #1
and indexA, t, #0xFF00
orr indexA, t, lsr #24 // res = (t & 0xFF00) | (t >> 24)
ldrb indexA, [TILE, indexA]
ldrb indexA, [LMAP, indexA]
orr indexB, indexA, lsl #8
strh indexB, [ptr], #2
add t, dtdx
subs width, #1 // width--;
beq .scanline_end // if (width == 0)
.align_right:
tst width, #1
beq .align_block_4px
ldrb indexB, [ptr, width]
subs width, #1 // width--
mla Rti, width, dtdx, t // Rti = width * dtdx + t
and indexA, Rti, #0xFF00
orr indexA, Rti, lsr #24 // res = (t & 0xFF00) | (t >> 24)
ldrb indexA, [TILE, indexA]
asr Rgi, dgdx, #1
mla Rgi, width, Rgi, g // Rgi = width * (dgdx / 2) + g
bic LMAP, Rgi, #255
ldrb indexA, [LMAP, indexA]
orr indexB, indexA, indexB, lsl #8
strh indexB, [ptr, width]
beq .scanline_end // if (width == 0)
.align_block_4px:
tst width, #2
beq .align_block_8px
PUT_PIXELS
subs width, #2
beq .scanline_end
.align_block_8px:
tst width, #4
beq .scanlin_block_8px
PUT_PIXELS
PUT_PIXELS
subs width, #4
beq .scanline_end
.scanlin_block_8px:
PUT_PIXELS
PUT_PIXELS
PUT_PIXELS
PUT_PIXELS
subs width, #8
bne .scanlin_block_8px
.scanline_end:
ldmfd sp!, {Lx,Rx,Lg,Rg,Lt,Rt} // sp+24
ldr tmp, [sp, #(SP_LDX + 16)]
add Lx, tmp // Lx += Ldx from stack
ldr tmp, [sp, #(SP_LDG + 16)]
add Lg, tmp // Lg += Ldg from stack
ldr tmp, [sp, #(SP_LDT + 16)]
add Lt, tmp // Lt += Ldt from stack
ldr tmp, [sp, #(SP_RDX + 16)]
add Rx, tmp // Rx += Rdx from stack
ldr tmp, [sp, #(SP_RDG + 16)]
add Rg, tmp // Rg += Rdg from stack
ldr tmp, [sp, #(SP_RDT + 16)]
add Rt, tmp // Rt += Rdt from stack
add pixel, #VRAM_STRIDE // pixel += FRAME_WIDTH (240)
subs h, #1
bne .scanline_start
ldmfd sp!, {L,R,Lh,Rh} // sp+16
b .loop
.exit:
add sp, #24 // revert reserved space for [Ldx, Ldg, Ldt, Rdx, Rdg, Rdt]
ldmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,pc}

View File

@ -0,0 +1,213 @@
#include "rasterizer.inc"
pixel .req r0
L .req r1
R .req r2
index .req r3
DIVLUT .req r4
tmp .req r5
N .req r6
Lh .req r7
Rh .req r8
Lx .req ip
Rx .req lr
Lg .req r9
Rg .req r10
h .req r11
Ldx .req h
Rdx .req Ldx
Ldg .req Ldx
Rdg .req Ldx
color .req N
Ry1 .req tmp
Ry2 .req Rh
Ly1 .req tmp
Ly2 .req Lh
LMAP .req index
pair .req Lh
width .req Rh
g .req L
dgdx .req R
SP_LDX = 0
SP_LDG = 4
SP_RDX = 8
SP_RDG = 12
.macro PUT_PIXELS
lsr color, g, #16
ldrb color, [LMAP, color, lsl #8]
orr color, color, lsl #8
strh color, [tmp], #2
add g, dgdx
.endm
.global rasterizeG_mode4_asm
rasterizeG_mode4_asm:
stmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,lr}
sub sp, #16 // reserve stack space for [Ldx, Ldg, Rdx, Rdg]
ldr tmp, =lightmap
add LMAP, index, tmp // LMAP = lightmap + index
ldr DIVLUT, =divTable
mov Lh, #0 // Lh = 0
mov Rh, #0 // Rh = 0
.loop:
.calc_left_start:
cmp Lh, #0
bne .calc_left_end // if (Lh != 0) end with left
ldr N, [L, #VERTEX_PREV] // N = L->prev
ldrsh Ly1, [L, #VERTEX_Y] // Ly1 = L->v.y
ldrsh Ly2, [N, #VERTEX_Y] // Ly2 = N->v.y
subs Lh, Ly2, Ly1 // Lh = Ly2 - Ly1
blt .exit // if (Lh < 0) return
ldrsh Lx, [L, #VERTEX_X] // Lx = L->v.x
ldrb Lg, [L, #VERTEX_G] // Lg = L->v.g
cmp Lh, #1 // if (Lh <= 1) skip Ldx calc
ble .skip_left_dx
lsl tmp, Lh, #1
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Lh)
ldrsh Ldx, [N, #VERTEX_X]
sub Ldx, Lx
mul Ldx, tmp // Ldx = tmp * (N->v.x - Lx)
str Ldx, [sp, #SP_LDX] // store Ldx to stack
ldrb Ldg, [N, #VERTEX_G]
sub Ldg, Lg
mul Ldg, tmp // Ldg = tmp * (N->v.g - Lg)
str Ldg, [sp, #SP_LDG] // store Ldg to stack
.skip_left_dx:
lsl Lx, #16 // Lx <<= 16
lsl Lg, #16 // Lg <<= 16
mov L, N // L = N
b .calc_left_start
.calc_left_end:
.calc_right_start:
cmp Rh, #0
bne .calc_right_end // if (Rh != 0) end with right
ldr N, [R, #VERTEX_NEXT] // N = R->next
ldrsh Ry1, [R, #VERTEX_Y] // Ry1 = R->v.y
ldrsh Ry2, [N, #VERTEX_Y] // Ry2 = N->v.y
subs Rh, Ry2, Ry1 // Rh = Ry2 - Ry1
blt .exit // if (Rh < 0) return
ldrsh Rx, [R, #VERTEX_X] // Rx = R->v.x
ldrb Rg, [R, #VERTEX_G] // Rg = R->v.g
cmp Rh, #1 // if (Rh <= 1) skip Rdx calc
ble .skip_right_dx
lsl tmp, Rh, #1
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Rh)
ldrsh Rdx, [N, #VERTEX_X]
sub Rdx, Rx
mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx)
str Rdx, [sp, #SP_RDX] // store Rdx to stack
ldrb Rdg, [N, #VERTEX_G]
sub Rdg, Rg
mul Rdg, tmp // Rdg = tmp * (N->v.g - Rg)
str Rdg, [sp, #SP_RDG] // store Rdg to stack
.skip_right_dx:
lsl Rx, #16 // Rx <<= 16
lsl Rg, #16 // Rg <<= 16
mov R, N // R = N
b .calc_right_start
.calc_right_end:
cmp Rh, Lh // if (Rh < Lh)
movlt h, Rh // h = Rh
movge h, Lh // else h = Lh
sub Lh, h // Lh -= h
sub Rh, h // Rh -= h
stmfd sp!, {L,R,Lh,Rh} // sp-16
.scanline_start:
asr tmp, Lx, #16 // x1 = (Lx >> 16)
rsbs width, tmp, Rx, asr #16 // width = (Rx >> 16) - x1
ble .scanline_end // if (width <= 0) go next scanline
sub dgdx, Rg, Lg
asr dgdx, #5 // dgdx = (Rg - Lg) >> 5
lsl g, width, #1
ldrh g, [DIVLUT, g]
mul dgdx, g // dgdx *= FixedInvU(width)
asr dgdx, #10 // dgdx >>= 10
mov g, Lg // g = Lg
add tmp, pixel, tmp // tmp = pixel + x1
// 2 bytes alignment (VRAM write requirement)
.align_left:
tst tmp, #1 // if (tmp & 1)
beq .align_right
ldrb pair, [tmp, #-1]! // read pal index from VRAM (byte)
lsr color, g, #16
ldrb color, [LMAP, color, lsl #8]
orr pair, color, lsl #8
strh pair, [tmp], #2
add g, dgdx, asr #1
subs width, #1 // width--;
beq .scanline_end // if (width == 0)
.align_right:
tst width, #1
beq .align_block
ldrb pair, [tmp, width]
subs width, #1 // width--
lsr color, Rg, #16
ldrb color, [LMAP, color, lsl #8]
orr pair, color, pair, lsl #8
strh pair, [tmp, width]
beq .scanline_end // if (width == 0)
.align_block:
tst width, #2
beq .scanline_block_4px
PUT_PIXELS
subs width, #2 // width -= 2;
beq .scanline_end // if (width == 0)
.scanline_block_4px:
PUT_PIXELS
PUT_PIXELS
subs width, #4
bne .scanline_block_4px
.scanline_end:
ldr tmp, [sp, #(SP_LDX + 16)]
add Lx, tmp // Lx += Ldx from stack
ldr tmp, [sp, #(SP_LDG + 16)]
add Lg, tmp // Lg += Ldg from stack
ldr tmp, [sp, #(SP_RDX + 16)]
add Rx, tmp // Rx += Rdx from stack
ldr tmp, [sp, #(SP_RDG + 16)]
add Rg, tmp // Rg += Rdg from stack
add pixel, #VRAM_STRIDE // pixel += FRAME_WIDTH (240)
subs h, #1
bne .scanline_start
ldmfd sp!, {L,R,Lh,Rh} // sp+16
b .loop
.exit:
add sp, #16 // revert reserved space for [Ldx, Ldg, Rdx, Rdg]
ldmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,pc}

View File

@ -0,0 +1,152 @@
#include "rasterizer.inc"
pixel .req r0
L .req r1
R .req r2
LMAP .req r3
Lh .req r4
Rh .req r5
Lx .req ip
Rx .req lr
Ldx .req r6
Rdx .req r7
N .req r8
tmp .req r9
DIVLUT .req r10
width .req r11
h .req N
Ry1 .req tmp
Ry2 .req Rh
Ly1 .req tmp
Ly2 .req Lh
pair .req DIVLUT
indexA .req Lh
indexB .req DIVLUT
.shadow_lightmap:
.word lightmap + 0x1A00
.global rasterizeS_mode4_asm
rasterizeS_mode4_asm:
stmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,lr}
ldr LMAP, .shadow_lightmap
mov Lh, #0 // Lh = 0
mov Rh, #0 // Rh = 0
.loop:
ldr DIVLUT, =divTable
.calc_left_start:
cmp Lh, #0
bne .calc_left_end // if (Lh != 0) end with left
ldr N, [L, #VERTEX_PREV] // N = L->prev
ldrsh Ly1, [L, #VERTEX_Y] // Ly1 = L->v.y
ldrsh Ly2, [N, #VERTEX_Y] // Ly2 = N->v.y
subs Lh, Ly2, Ly1 // Lh = Ly2 - Ly1
blt .exit // if (Lh < 0) return
ldrsh Lx, [L, #VERTEX_X] // Lx = L->v.x
cmp Lh, #1 // if (Lh <= 1) skip Ldx calc
ble .skip_left_dx
lsl tmp, Lh, #1
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Lh)
ldrsh Ldx, [N, #VERTEX_X]
sub Ldx, Lx
mul Ldx, tmp // Ldx = tmp * (N->v.x - Lx)
.skip_left_dx:
lsl Lx, #16 // Lx <<= 16
mov L, N // L = N
b .calc_left_start
.calc_left_end:
.calc_right_start:
cmp Rh, #0
bne .calc_right_end // if (Rh != 0) end with right
ldr N, [R, #VERTEX_NEXT] // N = R->next
ldrsh Ry1, [R, #VERTEX_Y] // Ry1 = R->v.y
ldrsh Ry2, [N, #VERTEX_Y] // Ry2 = N->v.y
subs Rh, Ry2, Ry1 // Rh = Ry2 - Ry1
blt .exit // if (Rh < 0) return
ldrsh Rx, [R, #VERTEX_X] // Rx = R->v.x
cmp Rh, #1 // if (Rh <= 1) skip Rdx calc
ble .skip_right_dx
lsl tmp, Rh, #1
ldrh tmp, [DIVLUT, tmp] // tmp = FixedInvU(Rh)
ldrsh Rdx, [N, #VERTEX_X]
sub Rdx, Rx
mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx)
.skip_right_dx:
lsl Rx, #16 // Rx <<= 16
mov R, N // R = N
b .calc_right_start
.calc_right_end:
cmp Rh, Lh // if (Rh < Lh)
movlt h, Rh // h = Rh
movge h, Lh // else h = Lh
sub Lh, h // Lh -= h
sub Rh, h // Rh -= h
stmfd sp!, {Lh}
.scanline_start:
asr tmp, Lx, #16 // x1 = (Lx >> 16)
rsbs width, tmp, Rx, asr #16 // width = (Rx >> 16) - x1
ble .scanline_end // if (width <= 0) go next scanline
add tmp, pixel, tmp // tmp = pixel + x1
// 2 bytes alignment (VRAM write requirement)
.align_left:
tst tmp, #1
beq .align_right
ldrh pair, [tmp, #-1]!
ldrb indexA, [LMAP, pair, lsr #8]
and pair, #0xFF
orr pair, indexA, lsl #8
strh pair, [tmp], #2
subs width, #1 // width--;
beq .scanline_end
.align_right:
tst width, #1
beq .scanline
ldrb pair, [tmp, width]
subs width, #1 // width--
and indexA, pair, #0xFF
ldrb indexA, [LMAP, indexA]
orr pair, indexA, pair, lsl #8
strh pair, [tmp, width]
beq .scanline_end // width == 0
.scanline:
ldrh pair, [tmp]
ldrb indexA, [LMAP, pair, lsr #8]
and pair, #0xFF
ldrb indexB, [LMAP, pair]
orr pair, indexB, indexA, lsl #8
strh pair, [tmp], #2
subs width, #2
bne .scanline
.scanline_end:
add Lx, Ldx // Lx += Ldx
add Rx, Rdx // Rx += Rdx
add pixel, #VRAM_STRIDE // pixel += FRAME_WIDTH (240)
subs h, #1
bne .scanline_start
ldmfd sp!, {Lh}
b .loop
.exit:
ldmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,pc}

View File

@ -0,0 +1,3 @@
.global rasterize_dummy
rasterize_dummy:
bx lr

View File

@ -0,0 +1,17 @@
.section .iwram
.arm
.equ VERTEX_X, 0
.equ VERTEX_Y, 2
.equ VERTEX_Z, 4
.equ VERTEX_G, 6
.equ VERTEX_CLIP, 7
.equ VERTEX_T, 8
.equ VERTEX_PREV, 12
.equ VERTEX_NEXT, 16
#ifdef __TNS__
.equ VRAM_STRIDE, 320
#else
.equ VRAM_STRIDE, 240
#endif

View File

@ -1,396 +0,0 @@
.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
*/

View File

@ -0,0 +1,773 @@
#ifndef H_RASTERIZER_MODE13
#define H_RASTERIZER_MODE13
#include "common.h"
extern uint8 lightmap[256 * 32];
extern const uint8* tile;
#define rasterizeS rasterizeS_mode13_c
#define rasterizeF rasterizeF_mode13_c
#define rasterizeG rasterizeG_mode13_c
#define rasterizeFT rasterizeFT_mode13_c
#define rasterizeGT rasterizeGT_mode13_c
#define rasterizeFTA rasterizeFTA_mode13_c
#define rasterizeGTA rasterizeGTA_mode13_c
void rasterizeS_mode13_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
const uint8* ft_lightmap = &lightmap[0x1A00];
int32 Lh = 0;
int32 Rh = 0;
int32 Ldx = 0;
int32 Rdx = 0;
int32 Rx;
int32 Lx;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
if (Lh > 1)
{
uint32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
if (Rh > 1) {
uint32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
volatile uint16* ptr = pixel + (x1 >> 1);
if (x1 & 1)
{
uint16 p = ptr[0];
uint16 index = ft_lightmap[p >> 8];
ptr[0] = (p & 0x00FF) | (index << 8);
ptr++;
width--;
}
if (width & 1)
{
uint16 p = ptr[width >> 1];
uint16 index = ft_lightmap[p & 0xFF];
ptr[width >> 1] = (p & 0xFF00) | index;
width--;
}
while (width)
{
uint16 p = *ptr;
uint16 index = ft_lightmap[p & 0xFF];
index |= ft_lightmap[p >> 8] << 8;
*ptr++ = index;
width -= 2;
}
#undef SHADE
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
}
}
}
void rasterizeF_mode13_c(uint16* pixel, const VertexUV* L, const VertexUV* R, int32 index)
{
uint16 color = lightmap[(L->v.g << 8) | index];
color |= (color << 8);
int32 Lh = 0;
int32 Rh = 0;
int32 Ldx = 0;
int32 Rdx = 0;
int32 Rx;
int32 Lx;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
ASSERT(L->v.y >= 0);
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
if (Lh > 1)
{
uint32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
ASSERT(R->v.y >= 0);
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
if (Rh > 1) {
uint32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
volatile uint8* ptr = (uint8*)pixel + x1;
if (intptr_t(ptr) & 1)
{
*ptr++ = uint8(color);
width--;
}
if (width & 1)
{
ptr[width - 1] = uint8(color);
}
if (width & 2)
{
*(uint16*)ptr = color;
ptr += 2;
}
width >>= 2;
while (width--)
{
*(uint16*)ptr = color;
ptr += 2;
*(uint16*)ptr = color;
ptr += 2;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
}
}
}
void rasterizeG_mode13_c(uint16* pixel, const VertexUV* L, const VertexUV* R, int32 index)
{
int32 Lh = 0, Rh = 0;
int32 Lx, Rx, Ldx = 0, Rdx = 0;
int32 Lg, Rg, Ldg = 0, Rdg = 0;
const uint8* ft_lightmap = lightmap + index;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lg = L->v.g;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
Ldg = tmp * (N->v.g - Lg);
}
Lx <<= 16;
Lg <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rg = R->v.g;
if (Rh > 1)
{
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
Rdg = tmp * (N->v.g - Rg);
}
Rx <<= 16;
Rg <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
int32 tmp = FixedInvU(width);
int32 dgdx = tmp * ((Rg - Lg) >> 5) >> 10;
int32 g = Lg;
volatile uint8* ptr = (uint8*)pixel + x1;
if (intptr_t(ptr) & 1)
{
*ptr++ = ft_lightmap[g >> 16 << 8];
g += dgdx >> 1;
width--;
}
if (width & 1)
{
ptr[width - 1] = ft_lightmap[Rg >> 16 << 8];
}
if (width & 2)
{
uint8 p = ft_lightmap[g >> 16 << 8];
g += dgdx;
*(uint16*)ptr = p | (p << 8);
ptr += 2;
}
width >>= 2;
while (width--)
{
uint8 p;
p = ft_lightmap[g >> 16 << 8];
*(uint16*)ptr = p | (p << 8);
g += dgdx;
ptr += 2;
p = ft_lightmap[g >> 16 << 8];
*(uint16*)ptr = p | (p << 8);
g += dgdx;
ptr += 2;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lg += Ldg;
Rg += Rdg;
}
}
}
void rasterizeFT_mode13_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
const uint8* ft_lightmap = &lightmap[L->v.g << 8];
int32 Lh = 0, Rh = 0;
int32 Lx, Rx, Ldx = 0, Rdx = 0;
uint32 Lt, Rt, Ldt, Rdt;
Ldt = 0;
Rdt = 0;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lt = L->t.uv;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
uint32 duv = N->t.uv - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Ldt = (du & 0xFFFF0000) | (dv >> 16);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rt = R->t.uv;
if (Rh > 1)
{
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
uint32 duv = N->t.uv - Rt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Rdt = (du & 0xFFFF0000) | (dv >> 16);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
uint32 tmp = FixedInvU(width);
uint32 duv = Rt - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
uint32 dtdx = (du & 0xFFFF0000) | (dv >> 16);
uint32 t = Lt;
volatile uint8* ptr = (uint8*)pixel + x1;
if (intptr_t(ptr) & 1)
{
*ptr++ = ft_lightmap[tile[(t & 0xFF00) | (t >> 24)]];
t += dtdx;
width--;
}
if (width & 1)
{
uint32 tmp = Rt - dtdx;
ptr[width - 1] = ft_lightmap[tile[(tmp & 0xFF00) | (tmp >> 24)]];
}
width >>= 1;
while (width--)
{
uint16 p;
p = ft_lightmap[tile[(t & 0xFF00) | (t >> 24)]];
t += dtdx;
p |= ft_lightmap[tile[(t & 0xFF00) | (t >> 24)]] << 8;
t += dtdx;
*(uint16*)ptr = p;
ptr += 2;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lt += Ldt;
Rt += Rdt;
}
}
}
void rasterizeGT_mode13_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
#ifdef ALIGNED_LIGHTMAP
ASSERT((intptr_t(lightmap) & 0xFFFF) == 0); // lightmap should be 64k aligned
#endif
int32 Lh = 0, Rh = 0;
int32 Lx, Rx, Lg, Rg, Ldx = 0, Rdx = 0, Ldg = 0, Rdg = 0;
uint32 Lt, Rt, Ldt, Rdt;
Ldt = 0;
Rdt = 0;
// 8-bit fractional part precision for Gouraud component
// has some artifacts but allow to save one reg for inner loop
// for aligned by 64k address of lightmap array
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lg = L->v.g;
Lt = L->t.uv;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
Ldg = tmp * (N->v.g - Lg) >> 8;
uint32 duv = N->t.uv - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Ldt = (du & 0xFFFF0000) | (dv >> 16);
}
Lx <<= 16;
Lg <<= 8;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rg = R->v.g;
Rt = R->t.uv;
if (Rh > 1)
{
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
Rdg = tmp * (N->v.g - Rg) >> 8;
uint32 duv = N->t.uv - Rt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Rdt = (du & 0xFFFF0000) | (dv >> 16);
}
Rx <<= 16;
Rg <<= 8;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
int32 tmp = FixedInvU(width);
int32 dgdx = tmp * (Rg - Lg) >> 15;
uint32 duv = Rt - Lt;
uint32 u = tmp * int16(duv >> 16);
uint32 v = tmp * int16(duv);
uint32 dtdx = (u & 0xFFFF0000) | (v >> 16);
int32 g = Lg;
uint32 t = Lt;
volatile uint8* ptr = (uint8*)pixel + x1;
if (intptr_t(ptr) & 1)
{
*ptr++ = lightmap[(g >> 8 << 8) | tile[(t & 0xFF00) | (t >> 24)]];
t += dtdx;
g += dgdx >> 1;
width--;
}
if (width & 1)
{
uint32 tmp = Rt - dtdx;
ptr[width - 1] = lightmap[(Rg >> 8 << 8) | tile[(tmp & 0xFF00) | (tmp >> 24)]];
}
#ifdef ALIGNED_LIGHTMAP
g += intptr_t(lightmap);
#endif
width >>= 1;
while (width--)
{
#ifdef ALIGNED_LIGHTMAP
const uint8* LMAP = (uint8*)(g >> 8 << 8);
uint16 p = LMAP[tile[(t & 0xFF00) | (t >> 24)]];
t += dtdx;
p |= LMAP[tile[(t & 0xFF00) | (t >> 24)]] << 8;
t += dtdx;
g += dgdx;
#else
uint16 p = lightmap[(g >> 8 << 8) | tile[(t & 0xFF00) | (t >> 24)]];
t += dtdx;
p |= lightmap[(g >> 8 << 8) | tile[(t & 0xFF00) | (t >> 24)]] << 8;
t += dtdx;
g += dgdx;
#endif
*(uint16*)ptr = p;
ptr += 2;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lg += Ldg;
Rg += Rdg;
Lt += Ldt;
Rt += Rdt;
}
}
}
void rasterizeFTA_mode13_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
const uint8* ft_lightmap = &lightmap[L->v.g << 8];
int32 Lh = 0, Rh = 0;
int32 Lx, Rx, Ldx = 0, Rdx = 0;
uint32 Lt, Rt, Ldt, Rdt;
Ldt = 0;
Rdt = 0;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lt = L->t.uv;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
uint32 duv = N->t.uv - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Ldt = (du & 0xFFFF0000) | (dv >> 16);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rt = R->t.uv;
if (Rh > 1)
{
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
uint32 duv = N->t.uv - Rt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Rdt = (du & 0xFFFF0000) | (dv >> 16);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
uint32 tmp = FixedInvU(width);
uint32 duv = Rt - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
uint32 dtdx = (du & 0xFFFF0000) | (dv >> 16);
uint32 t = Lt;
volatile uint8* ptr = (uint8*)pixel + x1;
if (intptr_t(ptr) & 1)
{
uint8 p = tile[(t & 0xFF00) | (t >> 24)];
if (p) {
*ptr = ft_lightmap[p];
}
ptr++;
t += dtdx;
width--;
}
if (width & 1)
{
uint32 tmp = Rt - dtdx;
uint8 p = tile[(tmp & 0xFF00) | (tmp >> 24)];
if (p) {
ptr[width - 1] = ft_lightmap[p];
}
}
width >>= 1;
while (width--)
{
uint8 indexA = tile[(t & 0xFF00) | (t >> 24)];
t += dtdx;
uint8 indexB = tile[(t & 0xFF00) | (t >> 24)];
t += dtdx;
if (indexA && indexB) {
*(uint16*)ptr = ft_lightmap[indexA] | (ft_lightmap[indexB] << 8);
}/* else if (indexA) {
*(uint16*)ptr = (*(uint16*)ptr & 0xFF00) | ft_lightmap[indexA];
} else if (indexB) {
*(uint16*)ptr = (*(uint16*)ptr & 0x00FF) | (ft_lightmap[indexB] << 8);
}*/
ptr += 2;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lt += Ldt;
Rt += Rdt;
}
}
}
void rasterizeGTA_mode13_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
rasterizeGT(pixel, L, R);
}
#endif

View File

@ -0,0 +1,807 @@
#ifndef H_RASTERIZER_MODE4
#define H_RASTERIZER_MODE4
#include "common.h"
extern uint8 lightmap[256 * 32];
extern const uint8* tile;
#if defined(__GBA__)
#define USE_ASM
#endif
#ifdef USE_ASM
extern "C" {
void rasterize_dummy(uint16* pixel, const VertexUV* L, const VertexUV* R);
void rasterizeS_mode4_asm(uint16* pixel, const VertexUV* L, const VertexUV* R);
void rasterizeF_mode4_asm(uint16* pixel, const VertexUV* L, const VertexUV* R, int32 index);
void rasterizeG_mode4_asm(uint16* pixel, const VertexUV* L, const VertexUV* R, int32 index);
void rasterizeFT_mode4_asm(uint16* pixel, const VertexUV* L, const VertexUV* R);
void rasterizeGT_mode4_asm(uint16* pixel, const VertexUV* L, const VertexUV* R);
void rasterizeFTA_mode4_asm(uint16* pixel, const VertexUV* L, const VertexUV* R);
void rasterizeGTA_mode4_asm(uint16* pixel, const VertexUV* L, const VertexUV* R);
}
#define rasterizeS rasterizeS_mode4_asm
#define rasterizeF rasterizeF_mode4_asm
#define rasterizeG rasterizeG_mode4_asm
#define rasterizeFT rasterizeFT_mode4_asm
#define rasterizeGT rasterizeGT_mode4_asm
#define rasterizeFTA rasterizeFTA_mode4_asm
#define rasterizeGTA rasterizeGTA_mode4_asm
#else
#define rasterizeS rasterizeS_mode4_c
#define rasterizeF rasterizeF_mode4_c
#define rasterizeG rasterizeG_mode4_c
#define rasterizeFT rasterizeFT_mode4_c
#define rasterizeGT rasterizeGT_mode4_c
#define rasterizeFTA rasterizeFTA_mode4_c
#define rasterizeGTA rasterizeGTA_mode4_c
void rasterizeS_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
const uint8* ft_lightmap = &lightmap[0x1A00];
int32 Lh = 0;
int32 Rh = 0;
int32 Ldx = 0;
int32 Rdx = 0;
int32 Rx;
int32 Lx;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
if (Lh > 1)
{
uint32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
if (Rh > 1) {
uint32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
volatile uint16* ptr = pixel + (x1 >> 1);
if (x1 & 1)
{
uint16 p = ptr[0];
uint16 index = ft_lightmap[p >> 8];
ptr[0] = (p & 0x00FF) | (index << 8);
ptr++;
width--;
}
if (width & 1)
{
uint16 p = ptr[width >> 1];
uint16 index = ft_lightmap[p & 0xFF];
ptr[width >> 1] = (p & 0xFF00) | index;
width--;
}
while (width)
{
uint16 p = *ptr;
uint16 index = ft_lightmap[p & 0xFF];
index |= ft_lightmap[p >> 8] << 8;
*ptr++ = index;
width -= 2;
}
#undef SHADE
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
}
}
}
void rasterizeF_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R, int32 index)
{
uint16 color = lightmap[(L->v.g << 8) | index];
color |= (color << 8);
int32 Lh = 0;
int32 Rh = 0;
int32 Ldx = 0;
int32 Rdx = 0;
int32 Rx;
int32 Lx;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
ASSERT(L->v.y >= 0);
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
if (Lh > 1)
{
uint32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
ASSERT(R->v.y >= 0);
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
if (Rh > 1) {
uint32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
volatile uint8* ptr = (uint8*)pixel + x1;
if (intptr_t(ptr) & 1)
{
ptr--;
*(uint16*)ptr = *ptr | (color << 8);
ptr += 2;
width--;
}
if (width & 1)
{
*(uint16*)(ptr + width - 1) = (ptr[width] << 8) | (color >> 8);
}
if (width & 2)
{
*(uint16*)ptr = color;
ptr += 2;
}
width >>= 2;
while (width--)
{
*(uint16*)ptr = color;
ptr += 2;
*(uint16*)ptr = color;
ptr += 2;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
}
}
}
void rasterizeG_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R, int32 index)
{
int32 Lh = 0, Rh = 0;
int32 Lx, Rx, Ldx = 0, Rdx = 0;
int32 Lg, Rg, Ldg = 0, Rdg = 0;
const uint8* ft_lightmap = lightmap + index;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lg = L->v.g;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
Ldg = tmp * (N->v.g - Lg);
}
Lx <<= 16;
Lg <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rg = R->v.g;
if (Rh > 1)
{
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
Rdg = tmp * (N->v.g - Rg);
}
Rx <<= 16;
Rg <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
int32 tmp = FixedInvU(width);
int32 dgdx = tmp * ((Rg - Lg) >> 5) >> 10;
int32 g = Lg;
volatile uint8* ptr = (uint8*)pixel + x1;
if (intptr_t(ptr) & 1)
{
ptr--;
*(uint16*)ptr = *ptr | (ft_lightmap[g >> 16 << 8] << 8);
g += dgdx >> 1;
ptr += 2;
width--;
}
if (width & 1)
{
*(uint16*)(ptr + width - 1) = (ptr[width] << 8) | ft_lightmap[Rg >> 16 << 8];
}
if (width & 2)
{
uint8 p = ft_lightmap[g >> 16 << 8];
g += dgdx;
*(uint16*)ptr = p | (p << 8);
ptr += 2;
}
width >>= 2;
while (width--)
{
uint8 p;
p = ft_lightmap[g >> 16 << 8];
*(uint16*)ptr = p | (p << 8);
g += dgdx;
ptr += 2;
p = ft_lightmap[g >> 16 << 8];
*(uint16*)ptr = p | (p << 8);
g += dgdx;
ptr += 2;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lg += Ldg;
Rg += Rdg;
}
}
}
void rasterizeFT_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
const uint8* ft_lightmap = &lightmap[L->v.g << 8];
int32 Lh = 0, Rh = 0;
int32 Lx, Rx, Ldx = 0, Rdx = 0;
uint32 Lt, Rt, Ldt, Rdt;
Ldt = 0;
Rdt = 0;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lt = L->t.uv;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
uint32 duv = N->t.uv - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Ldt = (du & 0xFFFF0000) | (dv >> 16);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rt = R->t.uv;
if (Rh > 1)
{
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
uint32 duv = N->t.uv - Rt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Rdt = (du & 0xFFFF0000) | (dv >> 16);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
uint32 tmp = FixedInvU(width);
uint32 duv = Rt - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
uint32 dtdx = (du & 0xFFFF0000) | (dv >> 16);
uint32 t = Lt;
volatile uint8* ptr = (uint8*)pixel + x1;
if (intptr_t(ptr) & 1)
{
ptr--;
*(uint16*)ptr = *ptr | (ft_lightmap[tile[(t & 0xFF00) | (t >> 24)]] << 8);
ptr += 2;
t += dtdx;
width--;
}
if (width & 1)
{
uint32 tmp = Rt - dtdx;
*(uint16*)(ptr + width - 1) = (ptr[width] << 8) | ft_lightmap[tile[(tmp & 0xFF00) | (tmp >> 24)]];
}
width >>= 1;
while (width--)
{
uint16 p;
p = ft_lightmap[tile[(t & 0xFF00) | (t >> 24)]];
t += dtdx;
p |= ft_lightmap[tile[(t & 0xFF00) | (t >> 24)]] << 8;
t += dtdx;
*(uint16*)ptr = p;
ptr += 2;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lt += Ldt;
Rt += Rdt;
}
}
}
void rasterizeGT_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
#ifdef ALIGNED_LIGHTMAP
ASSERT((intptr_t(lightmap) & 0xFFFF) == 0); // lightmap should be 64k aligned
#endif
int32 Lh = 0, Rh = 0;
int32 Lx, Rx, Lg, Rg, Ldx = 0, Rdx = 0, Ldg = 0, Rdg = 0;
uint32 Lt, Rt, Ldt, Rdt;
Ldt = 0;
Rdt = 0;
// 8-bit fractional part precision for Gouraud component
// has some artifacts but allow to save one reg for inner loop
// for aligned by 64k address of lightmap array
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lg = L->v.g;
Lt = L->t.uv;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
Ldg = tmp * (N->v.g - Lg) >> 8;
uint32 duv = N->t.uv - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Ldt = (du & 0xFFFF0000) | (dv >> 16);
}
Lx <<= 16;
Lg <<= 8;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rg = R->v.g;
Rt = R->t.uv;
if (Rh > 1)
{
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
Rdg = tmp * (N->v.g - Rg) >> 8;
uint32 duv = N->t.uv - Rt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Rdt = (du & 0xFFFF0000) | (dv >> 16);
}
Rx <<= 16;
Rg <<= 8;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
int32 tmp = FixedInvU(width);
int32 dgdx = tmp * (Rg - Lg) >> 15;
uint32 duv = Rt - Lt;
uint32 u = tmp * int16(duv >> 16);
uint32 v = tmp * int16(duv);
uint32 dtdx = (u & 0xFFFF0000) | (v >> 16);
int32 g = Lg;
uint32 t = Lt;
volatile uint8* ptr = (uint8*)pixel + x1;
if (intptr_t(ptr) & 1)
{
ptr--;
*(uint16*)ptr = *ptr | (lightmap[(g >> 8 << 8) | tile[(t & 0xFF00) | (t >> 24)]] << 8);
ptr += 2;
t += dtdx;
g += dgdx >> 1;
width--;
}
if (width & 1)
{
uint32 tmp = Rt - dtdx;
*(uint16*)(ptr + width - 1) = (ptr[width] << 8) | lightmap[(Rg >> 8 << 8) | tile[(tmp & 0xFF00) | (tmp >> 24)]];
}
#ifdef ALIGNED_LIGHTMAP
g += intptr_t(lightmap);
#endif
width >>= 1;
while (width--)
{
#ifdef ALIGNED_LIGHTMAP
const uint8* LMAP = (uint8*)(g >> 8 << 8);
uint16 p = LMAP[tile[(t & 0xFF00) | (t >> 24)]];
t += dtdx;
p |= LMAP[tile[(t & 0xFF00) | (t >> 24)]] << 8;
t += dtdx;
g += dgdx;
#else
uint16 p = lightmap[(g >> 8 << 8) | tile[(t & 0xFF00) | (t >> 24)]];
t += dtdx;
p |= lightmap[(g >> 8 << 8) | tile[(t & 0xFF00) | (t >> 24)]] << 8;
t += dtdx;
g += dgdx;
#endif
*(uint16*)ptr = p;
ptr += 2;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lg += Ldg;
Rg += Rdg;
Lt += Ldt;
Rt += Rdt;
}
}
}
void rasterizeFTA_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
const uint8* ft_lightmap = &lightmap[L->v.g << 8];
int32 Lh = 0, Rh = 0;
int32 Lx, Rx, Ldx = 0, Rdx = 0;
uint32 Lt, Rt, Ldt, Rdt;
Ldt = 0;
Rdt = 0;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lt = L->t.uv;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
uint32 duv = N->t.uv - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Ldt = (du & 0xFFFF0000) | (dv >> 16);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rt = R->t.uv;
if (Rh > 1)
{
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
uint32 duv = N->t.uv - Rt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Rdt = (du & 0xFFFF0000) | (dv >> 16);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
uint32 tmp = FixedInvU(width);
uint32 duv = Rt - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
uint32 dtdx = (du & 0xFFFF0000) | (dv >> 16);
uint32 t = Lt;
volatile uint8* ptr = (uint8*)pixel + x1;
if (intptr_t(ptr) & 1)
{
uint8 p = tile[(t & 0xFF00) | (t >> 24)];
ptr--;
if (p) {
*(uint16*)ptr = *ptr | (ft_lightmap[p] << 8);
}
ptr += 2;
t += dtdx;
width--;
}
if (width & 1)
{
uint32 tmp = Rt - dtdx;
uint8 p = tile[(tmp & 0xFF00) | (tmp >> 24)];
if (p) {
*(uint16*)(ptr + width - 1) = (ptr[width] << 8) | ft_lightmap[p];
}
}
width >>= 1;
while (width--)
{
uint8 indexA = tile[(t & 0xFF00) | (t >> 24)];
t += dtdx;
uint8 indexB = tile[(t & 0xFF00) | (t >> 24)];
t += dtdx;
if (indexA && indexB) {
*(uint16*)ptr = ft_lightmap[indexA] | (ft_lightmap[indexB] << 8);
}/* else if (indexA) {
*(uint16*)ptr = (*(uint16*)ptr & 0xFF00) | ft_lightmap[indexA];
} else if (indexB) {
*(uint16*)ptr = (*(uint16*)ptr & 0x00FF) | (ft_lightmap[indexB] << 8);
}*/
ptr += 2;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lt += Ldt;
Rt += Rdt;
}
}
}
void rasterizeGTA_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
rasterizeGT(pixel, L, R);
}
#endif
#endif

View File

@ -0,0 +1,837 @@
#ifndef H_RASTERIZER_MODE5
#define H_RASTERIZER_MODE5
#include "common.h"
#define rasterizeS rasterizeS_mode5_c
#define rasterizeF rasterizeF_mode5_c
#define rasterizeG rasterizeG_mode5_c
#define rasterizeFT rasterizeFT_mode5_c
#define rasterizeGT rasterizeGT_mode5_c
#define rasterizeFTA rasterizeFTA_mode5_c
#define rasterizeGTA rasterizeGTA_mode5_c
extern uint16 palette[256];
extern uint8 lightmap[256 * 32];
extern const uint8* tile;
void rasterize_overdraw(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
int32 Lh = 0;
int32 Rh = 0;
int32 Ldx = 0;
int32 Rdx = 0;
int32 Rx;
int32 Lx;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
if (Lh > 1)
{
uint32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
if (Rh > 1) {
uint32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
uint16* ptr = pixel + x1;
while (width--)
{
*ptr++ += 0x1084;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
}
}
}
void rasterizeS_mode5_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
int32 Lh = 0;
int32 Rh = 0;
int32 Ldx = 0;
int32 Rdx = 0;
int32 Rx;
int32 Lx;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
if (Lh > 1)
{
uint32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
if (Rh > 1) {
uint32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
uint16* ptr = pixel + x1;
while (1)
{
*ptr = (*ptr >> 1) & 0b11110111101111;
ptr++;
if (!--width) break;
*ptr = (*ptr >> 1) & 0b11110111101111;
ptr++;
if (!--width) break;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
}
}
}
void rasterizeF_mode5_c(uint16* pixel, const VertexUV* L, const VertexUV* R, int32 index)
{
uint32 color = palette[lightmap[(L->v.g << 8) | index]];
int32 Lh = 0;
int32 Rh = 0;
int32 Ldx = 0;
int32 Rdx = 0;
int32 Rx;
int32 Lx;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
if (Lh > 1)
{
uint32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
if (Rh > 1) {
uint32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
uint16* ptr = pixel + x1;
switch (width & 7)
{
case 7: *ptr++ = color;
case 6: *ptr++ = color;
case 5: *ptr++ = color;
case 4: *ptr++ = color;
case 3: *ptr++ = color;
case 2: *ptr++ = color;
case 1: *ptr++ = color;
}
int32 n = width >> 3;
while (n--) {
*ptr++ = color;
*ptr++ = color;
*ptr++ = color;
*ptr++ = color;
*ptr++ = color;
*ptr++ = color;
*ptr++ = color;
*ptr++ = color;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
}
}
}
void rasterizeG_mode5_c(uint16* pixel, const VertexUV* L, const VertexUV* R, int32 index)
{
const uint8* ft_lightmap = lightmap + index; // faster with global variable than local
int32 Lh = 0;
int32 Rh = 0;
int32 Ldx = 0;
int32 Rdx = 0;
int32 Lx;
int32 Rx;
int32 Lg;
int32 Rg;
int32 Ldg = 0;
int32 Rdg = 0;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lg = L->v.g;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
Ldg = tmp * (N->v.g - Lg);
}
Lx <<= 16;
Lg <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rg = R->v.g;
if (Rh > 1) {
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
Rdg = tmp * (N->v.g - Rg);
}
Rx <<= 16;
Rg <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
uint16* ptr = pixel + x1;
int32 d = FixedInvU(width);
int32 dgdx = d * ((Rg - Lg) >> 5) >> 10;
int32 g = Lg;
while (1) // 2px per iteration (faster than 8px on C)
{
uint32 color = palette[ft_lightmap[(g >> 8) & 0x1F00]];
*ptr++ = color;
if (!--width) break;
*ptr++ = color;
if (!--width) break;
g += dgdx;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lg += Ldg;
Rg += Rdg;
}
}
}
void rasterizeFT_mode5_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
const uint8* ft_lightmap = &lightmap[L->v.g << 8];
int32 Lh = 0, Rh = 0;
int32 Lx, Rx, Ldx = 0, Rdx = 0;
uint32 Lt, Rt, Ldt, Rdt;
Ldt = 0;
Rdt = 0;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lt = L->t.uv;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
uint32 duv = N->t.uv - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Ldt = (du & 0xFFFF0000) | (dv >> 16);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rt = R->t.uv;
if (Rh > 1)
{
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
uint32 duv = N->t.uv - Rt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Rdt = (du & 0xFFFF0000) | (dv >> 16);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
uint16* ptr = pixel + x1;
int32 d = FixedInvU(width);
uint32 duv = Rt - Lt;
uint32 u = d * int16(duv >> 16);
uint32 v = d * int16(duv);
uint32 dtdx = (u & 0xFFFF0000) | (v >> 16);
uint32 t = Lt;
while (1)
{
*ptr++ = palette[ft_lightmap[tile[(t & 0xFF00) | (t >> 24)]]];
if (!--width) break;
t += dtdx;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lt += Ldt;
Rt += Rdt;
}
}
}
void rasterizeGT_mode5_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
int32 Lh = 0, Rh = 0;
int32 Lx, Rx, Lg, Rg, Ldx = 0, Rdx = 0, Ldg = 0, Rdg = 0;
uint32 Lt, Rt, Ldt, Rdt;
Ldt = 0;
Rdt = 0;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lg = L->v.g;
Lt = L->t.uv;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
Ldg = tmp * (N->v.g - Lg);
uint32 duv = N->t.uv - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Ldt = (du & 0xFFFF0000) | (dv >> 16);
}
Lx <<= 16;
Lg <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rg = R->v.g;
Rt = R->t.uv;
if (Rh > 1)
{
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
Rdg = tmp * (N->v.g - Rg);
uint32 duv = N->t.uv - Rt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Rdt = (du & 0xFFFF0000) | (dv >> 16);
}
Rx <<= 16;
Rg <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
uint16* ptr = pixel + x1;
uint32 tmp = FixedInvU(width);
int32 dgdx = tmp * ((Rg - Lg) >> 5) >> 10;
uint32 duv = Rt - Lt;
uint32 u = tmp * int16(duv >> 16);
uint32 v = tmp * int16(duv);
uint32 dtdx = (u & 0xFFFF0000) | (v >> 16);
int32 g = Lg;
uint32 t = Lt;
while (1)
{
*ptr++ = palette[lightmap[((g >> 8) & 0x1F00) | tile[(t & 0xFF00) | (t >> 24)]]];
if (!--width) break;
t += dtdx;
*ptr++ = palette[lightmap[((g >> 8) & 0x1F00) | tile[(t & 0xFF00) | (t >> 24)]]];
if (!--width) break;
t += dtdx;
g += dgdx;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lg += Ldg;
Rg += Rdg;
Lt += Ldt;
Rt += Rdt;
}
}
}
void rasterizeFTA_mode5_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
const uint8* ft_lightmap = &lightmap[L->v.g << 8];
int32 Lh = 0, Rh = 0;
int32 Lx, Rx, Ldx = 0, Rdx = 0;
uint32 Lt, Rt, Ldt, Rdt;
Ldt = 0;
Rdt = 0;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lt = L->t.uv;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
uint32 duv = N->t.uv - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Ldt = (du & 0xFFFF0000) | (dv >> 16);
}
Lx <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rt = R->t.uv;
if (Rh > 1)
{
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
uint32 duv = N->t.uv - Rt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Rdt = (du & 0xFFFF0000) | (dv >> 16);
}
Rx <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
uint16* ptr = pixel + x1;
int32 d = FixedInvU(width);
uint32 duv = Rt - Lt;
uint32 u = d * int16(duv >> 16);
uint32 v = d * int16(duv);
uint32 dtdx = (u & 0xFFFF0000) | (v >> 16);
uint32 t = Lt;
while (1)
{
uint16 p = palette[ft_lightmap[tile[(t & 0xFF00) | (t >> 24)]]];
if (p) *ptr = p;
ptr++;
if (!--width) break;
t += dtdx;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lt += Ldt;
Rt += Rdt;
}
}
}
void rasterizeGTA_mode5_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
int32 Lh = 0, Rh = 0;
int32 Lx, Rx, Lg, Rg, Ldx = 0, Rdx = 0, Ldg = 0, Rdg = 0;
uint32 Lt, Rt, Ldt, Rdt;
Ldt = 0;
Rdt = 0;
while (1)
{
while (!Lh)
{
const VertexUV* N = L->prev;
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
Lx = L->v.x;
Lg = L->v.g;
Lt = L->t.uv;
if (Lh > 1)
{
int32 tmp = FixedInvU(Lh);
Ldx = tmp * (N->v.x - Lx);
Ldg = tmp * (N->v.g - Lg);
uint32 duv = N->t.uv - Lt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Ldt = (du & 0xFFFF0000) | (dv >> 16);
}
Lx <<= 16;
Lg <<= 16;
L = N;
}
while (!Rh)
{
const VertexUV* N = R->next;
if (N->v.y < R->v.y) return;
Rh = N->v.y - R->v.y;
Rx = R->v.x;
Rg = R->v.g;
Rt = R->t.uv;
if (Rh > 1)
{
int32 tmp = FixedInvU(Rh);
Rdx = tmp * (N->v.x - Rx);
Rdg = tmp * (N->v.g - Rg);
uint32 duv = N->t.uv - Rt;
uint32 du = tmp * int16(duv >> 16);
uint32 dv = tmp * int16(duv);
Rdt = (du & 0xFFFF0000) | (dv >> 16);
}
Rx <<= 16;
Rg <<= 16;
R = N;
}
int32 h = X_MIN(Lh, Rh);
Lh -= h;
Rh -= h;
while (h--)
{
int32 x1 = Lx >> 16;
int32 x2 = Rx >> 16;
int32 width = x2 - x1;
if (width > 0)
{
uint16* ptr = pixel + x1;
uint32 tmp = FixedInvU(width);
int32 dgdx = tmp * ((Rg - Lg) >> 5) >> 10;
uint32 duv = Rt - Lt;
uint32 u = tmp * int16(duv >> 16);
uint32 v = tmp * int16(duv);
uint32 dtdx = (u & 0xFFFF0000) | (v >> 16);
int32 g = Lg;
uint32 t = Lt;
while (1)
{
uint16 p;
p = palette[lightmap[((g >> 8) & 0x1F00) | tile[(t & 0xFF00) | (t >> 24)]]];
if (p) *ptr = p;
ptr++;
if (!--width) break;
t += dtdx;
p = palette[lightmap[((g >> 8) & 0x1F00) | tile[(t & 0xFF00) | (t >> 24)]]];
if (p) *ptr = p;
ptr++;
if (!--width) break;
t += dtdx;
g += dgdx;
}
}
pixel += VRAM_WIDTH;
Lx += Ldx;
Rx += Rdx;
Lg += Ldg;
Rg += Rdg;
Lt += Ldt;
Rt += Rdt;
}
}
}
#endif

769
src/platform/gba/render.cpp Normal file
View File

@ -0,0 +1,769 @@
#include "common.h"
#if defined(_WIN32)
uint16 fb[VRAM_WIDTH * FRAME_HEIGHT];
#elif defined(__GBA__)
uint32 fb = MEM_VRAM;
#elif defined(__TNS__)
uint16 fb[VRAM_WIDTH * FRAME_HEIGHT];
#elif defined(__DOS__)
uint16 fb[VRAM_WIDTH * FRAME_HEIGHT];
#endif
#define PAL_COLOR_TRANSP 0x0000
#define PAL_COLOR_BLACK 0x0421
#ifndef MODE_PAL
uint16 palette[256]; // IWRAM 0.5k
#endif
#if defined(__GBA__)
#define ALIGNED_LIGHTMAP
#endif
#if defined(MODE4)
#include "rasterizer_mode4.h"
#elif defined(MODE5)
#include "rasterizer_mode5.h"
#elif defined(MODE13)
#include "rasterizer_mode13.h"
#else
#error no supported video mode set
#endif
IWRAM_DATA uint8 lightmap[256 * 32]; // IWRAM 8k
const Texture* textures;
const uint8* tiles;
const uint8* tile;
uint32 gVerticesCount = 0;
int32 gFacesCount = 0; // 1 is reserved as OT terminator
EWRAM_DATA Vertex gVertices[MAX_VERTICES]; // EWRAM 16k
EWRAM_DATA Face gFaces[MAX_FACES]; // EWRAM 5k
EWRAM_DATA Face* otFaces[OT_SIZE] = { 0 };
int32 otMin = OT_SIZE - 1;
int32 otMax = 0;
bool enableAlphaTest;
bool enableClipping;
bool transformBoxRect(const Box* box, Rect* rect)
{
const Matrix &m = matrixGet();
if ((m[2][3] < VIEW_MIN_F) || (m[2][3] >= VIEW_MAX_F)) {
return false;
}
const vec3i v[8] = {
vec3i( box->minX, box->minY, box->minZ ),
vec3i( box->maxX, box->minY, box->minZ ),
vec3i( box->minX, box->maxY, box->minZ ),
vec3i( box->maxX, box->maxY, box->minZ ),
vec3i( box->minX, box->minY, box->maxZ ),
vec3i( box->maxX, box->minY, box->maxZ ),
vec3i( box->minX, box->maxY, box->maxZ ),
vec3i( box->maxX, box->maxY, box->maxZ )
};
*rect = Rect( INT_MAX, INT_MAX, INT_MIN, INT_MIN );
for (int32 i = 0; i < 8; i++) {
int32 z = DP43(m[2], v[i]);
if (z < VIEW_MIN_F || z >= VIEW_MAX_F) { // TODO znear clip
continue;
}
int32 x = DP43(m[0], v[i]);
int32 y = DP43(m[1], v[i]);
PERSPECTIVE(x, y, z);
if (x < rect->x0) rect->x0 = x;
if (x > rect->x1) rect->x1 = x;
if (y < rect->y0) rect->y0 = y;
if (y > rect->y1) rect->y1 = y;
}
rect->x0 += (FRAME_WIDTH / 2);
rect->y0 += (FRAME_HEIGHT / 2);
rect->x1 += (FRAME_WIDTH / 2);
rect->y1 += (FRAME_HEIGHT / 2);
return true;
}
int32 rectIsVisible(const Rect* rect)
{
if (rect->x0 > rect->x1 ||
rect->x0 > viewport.x1 ||
rect->x1 < viewport.x0 ||
rect->y0 > viewport.y1 ||
rect->y1 < viewport.y0) return 0; // not visible
if (rect->x0 < viewport.x0 ||
rect->x1 > viewport.x1 ||
rect->y0 < viewport.y0 ||
rect->y1 > viewport.y1) return -1; // clipped
return 1; // fully visible
}
int32 boxIsVisible(const Box* box)
{
Rect rect;
if (!transformBoxRect(box, &rect))
return 0; // not visible
return rectIsVisible(&rect);
}
void transform(const vec3s &v, int32 vg)
{
ASSERT(gVerticesCount < MAX_VERTICES);
Vertex &res = gVertices[gVerticesCount++];
#if defined(MODE4) // TODO for all modes
if (v.z < frustumAABB.minZ || v.z > frustumAABB.maxZ ||
//v.y < frustumAABB.minY || v.y > frustumAABB.maxY ||
v.x < frustumAABB.minX || v.x > frustumAABB.maxX)
{
res.clip = 16;
res.z = -1;
return;
}
#endif
const Matrix &m = matrixGet();
// TODO https://mikro.naprvyraz.sk/docs/Coding/1/3D-ROTAT.TXT
int32 z = DP43(m[2], v);
if (z < VIEW_MIN_F || z >= VIEW_MAX_F) { // TODO znear clip
res.clip = 16;
res.z = -1;
return;
}
int32 x = DP43(m[0], v);
int32 y = DP43(m[1], v);
int32 fogZ = z >> FIXED_SHIFT;
if (fogZ > FOG_MAX) {
vg = 8191;
} else if (fogZ > FOG_MIN) {
vg += (fogZ - FOG_MIN) << FOG_SHIFT;
if (vg > 8191) {
vg = 8191;
}
}
res.g = vg >> 8;
PERSPECTIVE(x, y, z);
x = X_CLAMP(x, -512, 511);
y = X_CLAMP(y, -512, 511);
res.x = x + (FRAME_WIDTH >> 1);
res.y = y + (FRAME_HEIGHT >> 1);
res.z = fogZ;
res.clip = enableClipping ? classify(res, viewport) : 0;
}
void transformRoom(const RoomInfo::Vertex* vertex, int32 vCount)
{
// TODO per-sector clipping?
for (int32 i = 0; i < vCount; i++)
{
transform(vertex->pos, vertex->lighting);
vertex++;
}
}
void transformMesh(const vec3s* vertices, int32 vCount, uint16 intensity)
{
for (int32 i = 0; i < vCount; i++) {
transform(*vertices++, intensity);
}
}
#if 0 // TODO
void clipZ(int32 znear, VertexUV *output, int32 &count, const VertexUV *a, const VertexUV *b) {
#define LERP2(a,b,t) int32((b) + (((a) - (b)) * t))
float t = (znear - b->v.sz) / float(a->v.sz - b->v.sz);
VertexUV* v = output + count++;
/*
int32 ax = (a->v.x - (FRAME_WIDTH / 2)) * a->v.z;
int32 ay = (a->v.y - (FRAME_HEIGHT / 2)) * a->v.z;
int32 bx = (b->v.x - (FRAME_WIDTH / 2)) * b->v.z;
int32 by = (b->v.y - (FRAME_HEIGHT / 2)) * b->v.z;
int32 x = LERP2(ax, bx, t);
int32 y = LERP2(ay, by, t);
*/
int32 x = LERP2(a->v.sx, b->v.sx, t);
int32 y = LERP2(a->v.sy, b->v.sy, t);
int32 z = LERP2(a->v.sz, b->v.sz, t);
v->v.x = (x / znear) + (FRAME_WIDTH / 2);
v->v.y = (y / znear) + (FRAME_HEIGHT / 2);
v->v.g = LERP2(a->v.g, b->v.g, t);
v->uv = (LERP2(a->uv & 0xFFFF, b->uv & 0xFFFF, t)) | (LERP2(a->uv >> 16, b->uv >> 16, t) << 16);
}
#endif
VertexUV* clipPoly(VertexUV* poly, VertexUV* tmp, int32 &pCount) {
#define LERP(a,b,t) (b + ((a - b) * t >> 12))
#define LERP2(a,b,ta,tb) (b + (((a - b) * ta / tb) >> 12) )
#define CLIP_AXIS(X, Y, edge, output) {\
int32 ta = (edge - b->v.X) << 12;\
int32 tb = (a->v.X - b->v.X);\
ASSERT(tb != 0);\
int32 t = ta / tb;\
VertexUV* v = output + count++;\
v->v.X = edge;\
v->v.Y = LERP2(a->v.Y, b->v.Y, ta, tb);\
/*v->v.z = LERP(a->v.z, b->v.z, t);*/\
v->v.g = LERP(a->v.g, b->v.g, t);\
v->t.u = LERP(a->t.u, b->t.u, t);\
v->t.v = LERP(a->t.v, b->t.v, t);\
}
/* TODO
#define CLIP_NEAR(znear, output) {\
//clipZ(znear, output, count, a, b);\
uint32 t = ((znear - b->v.z) << 16) / (a->v.z - b->v.z);\
VertexUV* v = output + count++;\
int32 ax = (a->v.x - (FRAME_WIDTH / 2)) * a->v.z;\
int32 ay = (a->v.y - (FRAME_HEIGHT / 2)) * a->v.z;\
int32 bx = (b->v.x - (FRAME_WIDTH / 2)) * b->v.z;\
int32 by = (b->v.y - (FRAME_HEIGHT / 2)) * b->v.z;\
int32 x = LERP(ax, bx, t);\
int32 y = LERP(ay, by, t);\
v->v.x = (x / znear) + (FRAME_WIDTH / 2);\
v->v.y = (y / znear) + (FRAME_HEIGHT / 2);\
v->v.z = LERP(a->v.z, b->v.z, t);\
v->v.g = LERP(a->v.g, b->v.g, t);\
v->uv = (LERP(a->uv & 0xFFFF, b->uv & 0xFFFF, t)) | (LERP(a->uv >> 16, b->uv >> 16, t) << 16);\
}
*/
#define CLIP_XY(X, Y, X0, X1, input, output) {\
const VertexUV *a, *b = input + pCount - 1;\
for (int32 i = 0; i < pCount; i++) {\
a = b;\
b = input + i;\
if (a->v.X < X0) {\
if (b->v.X < X0) continue;\
CLIP_AXIS(X, Y, X0, output);\
} else if (a->v.X > X1) {\
if (b->v.X > X1) continue;\
CLIP_AXIS(X, Y, X1, output);\
}\
if (b->v.X < X0) {\
CLIP_AXIS(X, Y, X0, output);\
} else if (b->v.X > X1) {\
CLIP_AXIS(X, Y, X1, output);\
} else {\
output[count++] = *b;\
}\
}\
if (count < 3) return NULL;\
}
#define ZNEAR (VIEW_MIN_F << FIXED_SHIFT >> FOV_SHIFT)
#define CLIP_Z(input, output) {\
const VertexUV *a, *b = input + pCount - 1;\
for (int32 i = 0; i < pCount; i++) {\
a = b;\
b = input + i;\
if (a->v.z < ZNEAR) {\
if (b->v.z < ZNEAR) continue;\
CLIP_NEAR(ZNEAR, output);\
}\
if (b->v.z < ZNEAR) {\
CLIP_NEAR(ZNEAR, output);\
} else {\
output[count++] = *b;\
}\
}\
if (count < 3) return NULL;\
}
int32 count = 0;
VertexUV *in = poly;
VertexUV *out = tmp;
/*
uint32 clipFlags = poly[0].v.clip | poly[1].v.clip | poly[2].v.clip;
if (pCount > 3) {
clipFlags |= poly[3].v.clip;
}
if (clipFlags & 16) {
CLIP_Z(in, out);
swap(in, out);
pCount = count;
count = 0;
}
*/
{//if (clipFlags & (1 | 2 | 4 | 8)) {
// clip x
CLIP_XY(x, y, viewport.x0, viewport.x1, in, out);
pCount = count;
count = 0;
// clip y
CLIP_XY(y, x, viewport.y0, viewport.y1, out, in);
pCount = count;
}
return in;
}
void rasterize(const Face* face, const VertexUV *top)
{
uint16* pixel = (uint16*)fb + top->v.y * VRAM_WIDTH;
#ifdef DEBUG_OVERDRAW
rasterize_overdraw(pixel, top, top);
#else
#if 0
rasterizeG(pixel, top, top, (face->flags & FACE_TEXTURE) + 10);
//rasterize_dummy(pixel, top, top);
//rasterizeGT(pixel, top, top);
#else
if (face->flags & FACE_COLORED) {
if (face->flags & FACE_FLAT) {
if (face->flags & FACE_SHADOW) {
rasterizeS(pixel, top, top);
} else {
rasterizeF(pixel, top, top, face->flags & FACE_TEXTURE);
}
} else {
rasterizeG(pixel, top, top, face->flags & FACE_TEXTURE);
}
} else {
if (enableAlphaTest) {
if (face->flags & FACE_FLAT) {
rasterizeFTA(pixel, top, top);
} else {
rasterizeGTA(pixel, top, top);
}
} else {
#if 0
rasterizeFT(pixel, top, top);
#else
if (face->flags & FACE_FLAT) {
rasterizeFT(pixel, top, top);
} else {
rasterizeGT(pixel, top, top);
}
#endif
}
}
#endif
#endif
}
void drawTriangle(const Face* face, VertexUV *v)
{
VertexUV *v1 = v + 0,
*v2 = v + 1,
*v3 = v + 2;
v1->next = v2;
v2->next = v3;
v3->next = v1;
v1->prev = v3;
v2->prev = v1;
v3->prev = v2;
const VertexUV* top = v1;
if (v1->v.y < v2->v.y) {
if (v1->v.y < v3->v.y) {
top = v1;
} else {
top = v3;
}
} else {
if (v2->v.y < v3->v.y) {
top = v2;
} else {
top = v3;
}
}
rasterize(face, top);
}
void drawQuad(const Face* face, VertexUV *v)
{
VertexUV *v1 = v + 0,
*v2 = v + 1,
*v3 = v + 2,
*v4 = v + 3;
v1->next = v2;
v2->next = v3;
v3->next = v4;
v4->next = v1;
v1->prev = v4;
v2->prev = v1;
v3->prev = v2;
v4->prev = v3;
VertexUV* top;
if (v1->v.y < v2->v.y) {
if (v1->v.y < v3->v.y) {
top = (v1->v.y < v4->v.y) ? v1 : v4;
} else {
top = (v3->v.y < v4->v.y) ? v3 : v4;
}
} else {
if (v2->v.y < v3->v.y) {
top = (v2->v.y < v4->v.y) ? v2 : v4;
} else {
top = (v3->v.y < v4->v.y) ? v3 : v4;
}
}
rasterize(face, top);
}
void drawPoly(Face* face, VertexUV* v)
{
VertexUV tmp[16];
int32 count = (face->flags & FACE_TRIANGLE) ? 3 : 4;
v = clipPoly(v, tmp, count);
if (!v) return;
if (count <= 4)
{
face->indices[0] = 0;
face->indices[1] = 1;
face->indices[2] = 2;
face->indices[3] = 3;
if (count == 3) {
if (v[0].v.y == v[1].v.y &&
v[0].v.y == v[2].v.y)
return;
drawTriangle(face, v);
} else {
if (v[0].v.y == v[1].v.y &&
v[0].v.y == v[2].v.y &&
v[0].v.y == v[3].v.y)
return;
drawQuad(face, v);
}
return;
}
VertexUV* top = v;
top->next = v + 1;
top->prev = v + count - 1;
bool skip = true;
for (int32 i = 1; i < count; i++)
{
VertexUV *p = v + i;
p->next = v + (i + 1) % count;
p->prev = v + (i - 1 + count) % count;
if (p->v.y != top->v.y)
{
if (p->v.y < top->v.y) {
top = p;
}
skip = false;
}
}
if (skip) {
return; // zero height poly
}
rasterize(face, top);
}
void drawGlyph(const Sprite *sprite, int32 x, int32 y)
{
int32 w = sprite->r - sprite->l;
int32 h = sprite->b - sprite->t;
w = (w >> 1) << 1; // make it even
int32 ix = x + sprite->l;
int32 iy = y + sprite->t;
#if defined(MODE_PAL)
uint16* pixel = (uint16*)fb + iy * VRAM_WIDTH + (ix >> 1);
#elif defined(ROTATE90_MODE)
uint16* pixel = (uint16*)fb + iy;
#else
uint16* pixel = (uint16*)fb + iy * VRAM_WIDTH + ix;
#endif
const uint8* glyphData = tiles + (sprite->tile << 16) + 256 * sprite->v + sprite->u;
while (h--)
{
#if defined(MODE_PAL)
const uint8* p = glyphData;
for (int32 i = 0; i < (w / 2); i++)
{
if (p[0] || p[1])
{
uint16 d = pixel[i];
if (p[0]) d = (d & 0xFF00) | p[0];
if (p[1]) d = (d & 0x00FF) | (p[1] << 8);
pixel[i] = d;
}
p += 2;
}
#else
for (int32 i = 0; i < w; i++)
{
if (glyphData[i] == 0) continue;
#ifdef ROTATE90_MODE
pixel[(FRAME_HEIGHT - (ix + i) - 1) * FRAME_WIDTH] = palette[glyphData[i]];
#else
pixel[i] = palette[glyphData[i]];
#endif
}
#endif
#ifdef ROTATE90_MODE
pixel += 1;
#else
pixel += VRAM_WIDTH;
#endif
glyphData += 256;
}
}
X_INLINE void faceAddToOTable(Face* face, int32 depth)
{
ASSERT(depth < OT_SIZE);
face->next = otFaces[depth];
otFaces[depth] = face;
if (depth < otMin) otMin = depth;
if (depth > otMax) otMax = depth;
}
void faceAddQuad(uint32 flags, const Index* indices, int32 startVertex)
{
ASSERT(gFacesCount < MAX_FACES);
const Vertex* v = gVertices + startVertex;
const Vertex* v1 = v + indices[0];
const Vertex* v2 = v + indices[1];
const Vertex* v3 = v + indices[2];
const Vertex* v4 = v + indices[3];
if (v1->clip == 16 || v2->clip == 16 || v3->clip == 16 || v4->clip == 16)
return;
if (enableClipping)
{
if (v1->clip & v2->clip & v3->clip & v4->clip)
return;
if (v1->clip | v2->clip | v3->clip | v4->clip) {
flags |= FACE_CLIPPED;
}
}
if (v1->g == v2->g && v1->g == v3->g && v1->g == v4->g) {
flags |= FACE_FLAT;
}
int32 depth = X_MAX(v1->z, X_MAX(v2->z, X_MAX(v3->z, v4->z))) >> OT_SHIFT;
// z-bias hack for the shadow plane
if (flags & FACE_SHADOW) {
depth = X_MAX(0, depth - 8);
}
Face *f = gFaces + gFacesCount++;
faceAddToOTable(f, depth);
f->flags = uint16(flags);
f->start = startVertex + indices[0];
f->indices[0] = 0;
f->indices[1] = indices[1] - indices[0];
f->indices[2] = indices[2] - indices[0];
f->indices[3] = indices[3] - indices[0];
}
void faceAddTriangle(uint32 flags, const Index* indices, int32 startVertex)
{
ASSERT(gFacesCount < MAX_FACES);
const Vertex* v = gVertices + startVertex;
const Vertex* v1 = v + indices[0];
const Vertex* v2 = v + indices[1];
const Vertex* v3 = v + indices[2];
if (v1->clip == 16 || v2->clip == 16 || v3->clip == 16)
return;
if (enableClipping)
{
if (v1->clip & v2->clip & v3->clip)
return;
if (v1->clip | v2->clip | v3->clip) {
flags |= FACE_CLIPPED;
}
}
if (v1->g == v2->g && v1->g == v3->g) {
flags |= FACE_FLAT;
}
int32 depth = X_MAX(v1->z, X_MAX(v2->z, v3->z)) >> OT_SHIFT;
Face *f = gFaces + gFacesCount++;
faceAddToOTable(f, depth);
f->flags = uint16(flags | FACE_TRIANGLE);
f->start = startVertex + indices[0];
f->indices[0] = 0;
f->indices[1] = indices[1] - indices[0];
f->indices[2] = indices[2] - indices[0];
}
void faceAddRoom(const Quad* quads, int32 qCount, const Triangle* triangles, int32 tCount, int32 startVertex)
{
for (uint16 i = 0; i < qCount; i++) {
faceAddQuad(quads[i].flags, quads[i].indices, startVertex);
}
for (uint16 i = 0; i < tCount; i++) {
faceAddTriangle(triangles[i].flags, triangles[i].indices, 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)
{
for (int i = 0; i < rCount; i++) {
faceAddQuad(rFaces[i].flags, rFaces[i].indices, startVertex);
}
for (int i = 0; i < crCount; i++) {
faceAddQuad(crFaces[i].flags | FACE_COLORED, crFaces[i].indices, startVertex);
}
for (int i = 0; i < tCount; i++) {
faceAddTriangle(tFaces[i].flags, tFaces[i].indices, startVertex);
}
for (int i = 0; i < ctCount; i++) {
faceAddTriangle(ctFaces[i].flags | FACE_COLORED, ctFaces[i].indices, startVertex);
}
}
#ifdef DEBUG_FACES
int32 gFacesCountMax, gVerticesCountMax;
#endif
void flush()
{
if (gFacesCount)
{
PROFILE_START();
for (int32 i = otMax; i >= otMin; i--)
{
if (!otFaces[i]) continue;
Face *face = otFaces[i];
otFaces[i] = NULL;
do {
VertexUV v[16];
uint32 flags = face->flags;
if (!(flags & FACE_COLORED))
{
const Texture &tex = textures[face->flags & FACE_TEXTURE];
tile = tiles + (tex.tile << 16);
v[0].t.uv = ((tex.uv0 << 16) | (tex.uv0 >> 16)) & 0xFF00FF00; // TODO preprocess
v[1].t.uv = ((tex.uv1 << 16) | (tex.uv1 >> 16)) & 0xFF00FF00; // TODO preprocess
v[2].t.uv = ((tex.uv2 << 16) | (tex.uv2 >> 16)) & 0xFF00FF00; // TODO preprocess
v[3].t.uv = ((tex.uv3 << 16) | (tex.uv3 >> 16)) & 0xFF00FF00; // TODO preprocess
enableAlphaTest = (tex.attribute == 1);
}
Vertex *p = gVertices + face->start;
v[0].v = p[0];
v[1].v = p[face->indices[1]];
v[2].v = p[face->indices[2]];
if (!(flags & FACE_TRIANGLE)) {
v[3].v = p[face->indices[3]];
}
if (flags & FACE_CLIPPED) {
drawPoly(face, v);
} else {
if (flags & FACE_TRIANGLE) {
drawTriangle(face, v);
} else {
drawQuad(face, v);
}
};
face = face->next;
} while (face);
}
PROFILE_STOP(dbg_flush);
otMin = OT_SIZE - 1;
otMax = 0;
}
#ifdef DEBUG_FACES
if ((gFacesCount > gFacesCountMax) || (gVerticesCount > gVerticesCountMax))
{
if (gFacesCount > gFacesCountMax) gFacesCountMax = gFacesCount;
if (gVerticesCount > gVerticesCountMax) gVerticesCountMax = gVerticesCount;
printf("v: %d f: %d\n", gVerticesCountMax, gFacesCountMax);
}
#endif
#ifdef PROFILE
dbg_vert_count += gVerticesCount;
dbg_poly_count += gFacesCount;
#endif
gVerticesCount = 0;
gFacesCount = 0;
}
void clear()
{
dmaFill((void*)fb, 0, VRAM_WIDTH * FRAME_HEIGHT * 2);
}

File diff suppressed because it is too large Load Diff

767
src/platform/gba/room.h Normal file
View File

@ -0,0 +1,767 @@
#ifndef H_ROOM
#define H_ROOM
#include "common.h"
int32 getBridgeFloor(const Item* item, int32 x, int32 z)
{
if (item->type == ITEM_BRIDGE_FLAT) {
return item->pos.y;
}
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_TILT1) ? (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 getItemFloorCeiling(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;
}
case ITEM_DRAWBRIDGE:
{
if (item->state == 1) {
h = getDrawBridgeFloor(item, x, z);
}
break;
}
case ITEM_BRIDGE_FLAT:
case ITEM_BRIDGE_TILT1:
case ITEM_BRIDGE_TILT2:
{
h = getBridgeFloor(item, x, z);
break;
}
case ITEM_TRAP_DOOR_1:
case ITEM_TRAP_DOOR_2:
{
if (item->state != 0)
return;
h = getTrapDoorFloor(item, x, z);
if ((floor && (h >= *floor)) || (ceiling && (h <= *ceiling)))
{
return;
}
}
}
if (h == WALL)
return;
if (floor && (y <= h))
{
*floor = h;
}
if (ceiling && (y > h))
{
*ceiling = h + 256;
}
}
const RoomInfo::Sector* RoomInfo::Sector::getSectorBelow(int32 posX, int32 posZ) const
{
if (roomBelow == NO_ROOM)
return this;
return rooms[roomBelow].getSector(posX, posZ);
}
const RoomInfo::Sector* RoomInfo::Sector::getSectorAbove(int32 posX, int32 posZ) const
{
if (roomAbove == NO_ROOM)
return this;
return rooms[roomAbove].getSector(posX, posZ);
}
int32 RoomInfo::Sector::getFloor(int32 x, int32 y, int32 z) const
{
gLastFloorData = NULL;
const RoomInfo::Sector* lowerSector = getSectorBelow(x, z);
int32 floor = lowerSector->floor << 8;
gLastFloorSlant.value = 0;
if (lowerSector->floorIndex)
{
const FloorData* fd = floors + lowerSector->floorIndex;
FloorData::Command cmd = (fd++)->cmd;
if (cmd.func == FLOOR_TYPE_FLOOR) // found floor
{
gLastFloorSlant = *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;
}
}
lowerSector->getTriggerFloorCeiling(x, y, z, &floor, NULL);
return floor;
}
int32 RoomInfo::Sector::getCeiling(int32 x, int32 y, int32 z) const
{
const RoomInfo::Sector* upperSector = getSectorAbove(x, z);
int32 ceiling = upperSector->ceiling << 8;
if (upperSector->floorIndex)
{
const FloorData* fd = floors + upperSector->floorIndex;
FloorData::Command cmd = (fd++)->cmd;
if (cmd.func == FLOOR_TYPE_FLOOR) // skip floor
{
fd++;
cmd = (fd++)->cmd;
}
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(x, z);
lowerSector->getTriggerFloorCeiling(x, y, z, NULL, &ceiling);
return ceiling;
}
Room* RoomInfo::Sector::getNextRoom() const
{
if (!floorIndex)
return NULL;
// always in this order
// - floor
// - ceiling
// - portal
// - other
const FloorData* fd = floors + floorIndex;
FloorData::Command cmd = (fd++)->cmd;
if (cmd.func == FLOOR_TYPE_FLOOR) // skip floor
{
if (cmd.end) return NULL;
fd++;
cmd = (fd++)->cmd;
}
if (cmd.func == FLOOR_TYPE_CEILING) // skip ceiling
{
if (cmd.end) return NULL;
fd++;
cmd = (fd++)->cmd;
}
if (cmd.func != FLOOR_TYPE_PORTAL) // no portal
return NULL;
ASSERT(fd->value != NO_ROOM);
return rooms + fd->value;
}
void RoomInfo::Sector::getTriggerFloorCeiling(int32 x, int32 y, int32 z, int32* floor, int32* ceiling) const
{
if (!floorIndex)
return;
FloorData::Command cmd;
const FloorData* fd = floors + floorIndex;
do {
cmd = (fd++)->cmd;
switch (cmd.func)
{
case FLOOR_TYPE_PORTAL:
case FLOOR_TYPE_FLOOR:
case FLOOR_TYPE_CEILING:
{
fd++;
break;
}
case FLOOR_TYPE_TRIGGER:
{
if (floor && !gLastFloorData) {
gLastFloorData = fd - 1;
}
fd++;
FloorData::TriggerCommand trigger;
do {
trigger = (fd++)->triggerCmd;
if (trigger.action == TRIGGER_ACTION_ACTIVATE_OBJECT)
{
getItemFloorCeiling(items + trigger.args, x, y, z, floor, ceiling);
}
if (trigger.action == TRIGGER_ACTION_ACTIVATE_CAMERA)
{
trigger = (fd++)->triggerCmd; // skip camera index
}
} while (!trigger.end);
break;
}
case FLOOR_TYPE_LAVA:
if (floor) {
gLastFloorData = fd - 1;
}
break;
}
} while (!cmd.end);
}
const RoomInfo::Sector* Room::getSector(int32 posX, int32 posZ) const
{
int32 sx = X_CLAMP((posX - x) >> 10, 0, xSectors - 1);
int32 sz = X_CLAMP((posZ - z) >> 10, 0, zSectors - 1);
return sectors + sx * zSectors + sz;
}
Room* Room::getRoom(int32 x, int32 y, int32 z)
{
const RoomInfo::Sector* sector = getSector(x, z);
Room* room = this;
while (1)
{
Room* nextRoom = sector->getNextRoom();
if (!nextRoom)
break;
room = nextRoom;
sector = room->getSector(x, z);
};
while (sector->roomAbove != NO_ROOM && y < (sector->ceiling << 8))
{
room = rooms + sector->roomAbove;
sector = room->getSector(x, z);
}
while (sector->roomBelow != NO_ROOM && y >= (sector->floor << 8))
{
room = rooms + sector->roomBelow;
sector = room->getSector(x, z);
}
return room;
}
int32 Room::getWaterLevel()
{
return WALL; // TODO
}
int32 Room::getWaterDepth()
{
return WALL; // TODO
}
bool Room::checkPortal(const RoomInfo::Portal* portal)
{
vec3i d;
d.x = portal->v[0].x - cameraViewPos.x + x;
d.y = portal->v[0].y - cameraViewPos.y;
d.z = portal->v[0].z - cameraViewPos.z + z;
if (DP33(portal->n, d) >= 0) {
return false;
}
int32 x0 = clip.x1;
int32 y0 = clip.y1;
int32 x1 = clip.x0;
int32 y1 = 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) ? viewport.x1 : viewport.x0;
y = (y > 0) ? viewport.y1 : viewport.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 < clip.x0) x0 = clip.x0;
if (x1 > clip.x1) x1 = clip.x1;
if (y0 < clip.y0) y0 = clip.y0;
if (y1 > clip.y1) y1 = 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;
return true;
}
Room** Room::addVisibleRoom(Room** list)
{
matrixPush();
matrixTranslateAbs(vec3i(x, 0, z));
for (int32 i = 0; i < pCount; i++)
{
const RoomInfo::Portal* portal = portals + i;
if (checkPortal(portal))
{
Room* nextRoom = rooms + portal->roomIndex;
list = nextRoom->addVisibleRoom(list);
if (!nextRoom->visible) {
nextRoom->visible = true;
*list++ = nextRoom;
}
}
}
matrixPop();
return list;
}
Room** Room::getVisibleRooms()
{
Room** list = roomsList;
list = addVisibleRoom(list);
*list++ = this;
*list++ = NULL;
ASSERT(list - roomsList <= MAX_ROOM_LIST);
return roomsList;
}
void Room::reset()
{
visible = false;
clip = Rect( FRAME_WIDTH, FRAME_HEIGHT, 0, 0 );
}
Room** Room::addNearRoom(Room** list, int32 x, int32 y, int32 z)
{
Room* nearRoom = getRoom(x, y, z);
int32 count = list - roomsList;
for (int32 i = 0; i < count; i++)
{
if (roomsList[i] == nearRoom) return list;
}
*list++ = nearRoom;
return list;
}
Room** Room::getNearRooms(const vec3i &pos, int32 radius, int32 height)
{
Room** list = roomsList;
*list++ = this;
list = addNearRoom(list, pos.x - radius, pos.y, pos.z - radius);
list = addNearRoom(list, pos.x + radius, pos.y, pos.z - radius);
list = addNearRoom(list, pos.x + radius, pos.y, pos.z + radius);
list = addNearRoom(list, pos.x - radius, pos.y, pos.z + radius);
list = addNearRoom(list, pos.x - radius, pos.y - height, pos.z - radius);
list = addNearRoom(list, pos.x + radius, pos.y - height, pos.z - radius);
list = addNearRoom(list, pos.x + radius, pos.y - height, pos.z + radius);
list = addNearRoom(list, pos.x - radius, pos.y - height, pos.z + radius);
*list++ = NULL;
return roomsList;
}
Room** Room::getAdjRooms()
{
Room** list = roomsList;
*list++ = this;
for (int32 i = 0; i < pCount; i++)
{
*list++ = rooms + portals[i].roomIndex;
}
*list++ = NULL;
return roomsList;
}
void Room::modify()
{
if (sectors == sectorsOrig) // TODO ROM only
{
// convert room->sectors to mutable (non-ROM) data
sectors = dynSectors + dynSectorsCount;
memcpy((RoomInfo::Sector*)sectors, sectorsOrig, xSectors * zSectors * sizeof(RoomInfo::Sector));
dynSectorsCount += xSectors * zSectors;
//printf("dynSectors: %d\n", dynSectorsCount);
ASSERT(dynSectorsCount <= MAX_DYN_SECTORS);
}
}
void Room::add(Item* item)
{
ASSERT(item && item->nextItem == NULL);
item->room = this;
item->nextItem = firstItem;
firstItem = item;
}
void Room::remove(Item* item)
{
ASSERT(item && item->room == this);
item->room = NULL;
Item* prev = NULL;
Item* curr = firstItem;
while (curr)
{
Item* next = curr->nextItem;
if (curr == item)
{
item->nextItem = NULL;
if (prev) {
prev->nextItem = next;
} else {
firstItem = next;
}
break;
}
prev = curr;
curr = next;
}
}
void checkTrigger(const FloorData* fd, Item* lara)
{
if (!fd) return;
if (fd->cmd.func == FLOOR_TYPE_LAVA)
{
// TODO lava
if (fd->cmd.end)
return;
fd++;
}
FloorData::Command cmd = (fd++)->cmd;
FloorData::TriggerInfo info = (fd++)->triggerInfo;
Item* switchItem = NULL;
Item* keyItem = NULL;
Item* pickupItem = NULL;
if (!lara && cmd.type != TRIGGER_TYPE_OBJECT)
return;
if (lara)
{
switch (cmd.type)
{
case TRIGGER_TYPE_ACTIVATE:
break;
case TRIGGER_TYPE_PAD:
case TRIGGER_TYPE_ANTIPAD:
if (lara->pos.y != lara->floor)
return;
break;
case TRIGGER_TYPE_SWITCH:
switchItem = items + fd->triggerCmd.args;
if (!useSwitch(switchItem, info.timer))
return;
fd++;
break;
case TRIGGER_TYPE_KEY:
keyItem = items + fd->triggerCmd.args;
if (!useKey(keyItem))
return;
fd++;
break;
case TRIGGER_TYPE_PICKUP:
pickupItem = items + fd->triggerCmd.args;
if (!usePickup(pickupItem))
return;
fd++;
break;
case TRIGGER_TYPE_OBJECT:
return;
case TRIGGER_TYPE_COMBAT:
// TODO unused?
break;
case TRIGGER_TYPE_DUMMY:
return;
}
}
while (1)
{
FloorData::TriggerCommand triggerCmd = (fd++)->triggerCmd;
switch (triggerCmd.action)
{
case TRIGGER_ACTION_ACTIVATE_OBJECT: {
Item* item = items + triggerCmd.args;
if (item->flags.once)
break;
item->timer = info.timer;
if (item->timer != 1)
item->timer *= 30;
if (cmd.type == TRIGGER_TYPE_SWITCH) {
item->flags.mask ^= info.mask;
} else if (cmd.type == TRIGGER_TYPE_ANTIPAD) {
item->flags.mask &= ~info.mask;
} else {
item->flags.mask |= info.mask;
}
if (item->flags.mask != ITEM_FLAGS_MASK_ALL)
break;
item->flags.once |= info.once;
if (item->flags.active)
break;
item->flags.status = ITEM_FLAGS_STATUS_ACTIVE;
item->activate();
break;
}
case TRIGGER_ACTION_ACTIVATE_CAMERA:
// TODO fixed camera
break;
case TRIGGER_ACTION_FLOW:
// TODO flow
break;
case TRIGGER_ACTION_FLIP:
// TODO flipmap
break;
case TRIGGER_ACTION_FLIP_ON:
// TODO flipmap
break;
case TRIGGER_ACTION_FLIP_OFF:
// TODO flipmap
break;
case TRIGGER_ACTION_CAMERA_TARGET:
// TODO change fixed camera target
break;
case TRIGGER_ACTION_END:
// TODO go to the next level
break;
case TRIGGER_ACTION_SOUNDTRACK:
// TODO soundtrack
break;
case TRIGGER_ACTION_EFFECT:
// TODO effect
break;
case TRIGGER_ACTION_SECRET: {
if ((gSaveGame.secrets >> triggerCmd.args) & 1)
break;
gSaveGame.secrets |= (1 << triggerCmd.args);
mixer.playMusic(TRACK_13_WAV); // TODO play sample?
break;
}
case TRIGGER_ACTION_CLEAR_BODIES:
break;
case TRIGGER_ACTION_FLYBY:
break;
case TRIGGER_ACTION_CUTSCENE:
break;
}
if (triggerCmd.end) break;
};
}
#endif

View File

@ -3,7 +3,26 @@
#include "common.h"
struct Sound
int16 IMA_INDEX[] = {
-1, -1, -1, -1, 2, 4, 6, 8,
};
int16 IMA_STEP[] = {
7, 8, 9, 10, 11, 12, 13, 14,
16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66,
73, 80, 88, 97, 107, 118, 130, 143,
157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658,
724, 796, 876, 963, 1060, 1166, 1282, 1411,
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
32767
};
struct Mixer
{
enum SoundMode {
UNIQUE,
@ -11,6 +30,52 @@ struct Sound
LOOP,
};
struct Music
{
const uint8* data;
int32 size;
int32 pos;
int32 smp;
int32 idx;
X_INLINE int32 getSample(uint32 n)
{
int32 step = IMA_STEP[idx];
int32 index = n & 7;
idx = X_CLAMP(idx + IMA_INDEX[index], 0, 88);
int32 diff = (2 * index + 1) * step >> 3;
if (n & 8) {
smp = X_MAX(smp - diff, -32768);
} else {
smp = X_MIN(smp + diff, 32767);
}
return smp >> 1;
}
X_INLINE void fill(int32* buffer, int32 count)
{
for (int i = 0; i < count; i += 2)
{
uint32 n = data[pos];
pos++;
if (pos >= size)
{
data = NULL;
memset(buffer, 0, (count - i) * sizeof(buffer[0]));
return;
}
buffer[i + 0] = getSample(n);
buffer[i + 1] = getSample(n >> 4);
}
}
};
struct Sample
{
const uint8* data;
@ -19,11 +84,11 @@ struct Sound
int32 inc;
int32 volume;
X_INLINE void fill(int16* buffer, int32 count)
X_INLINE void fill(int32* buffer, int32 count)
{
for (int32 i = 0; i < count; i++)
{
buffer[i] += SND_DECODE(data[pos >> SND_FIXED_SHIFT]) * volume >> SND_VOL_SHIFT;
buffer[i] += SND_DECODE(data[pos >> SND_FIXED_SHIFT]) * volume;
pos += inc;
if (pos >= size)
@ -36,19 +101,29 @@ struct Sound
}
};
Music music;
Sample channels[SND_CHANNELS];
int32 channelsCount;
void fill(uint8* buffer, int32 count)
void fill(uint8* bufferA, uint8* bufferB, int32 count)
{
if (channelsCount == 0)
UNUSED(bufferB);
if ((channelsCount == 0) && !music.data)
{
dmaFill(buffer, SND_ENCODE(0), count);
dmaFill(bufferA, SND_ENCODE(0), count);
#ifdef USE_9BIT_SOUND
dmaFill(bufferB, SND_ENCODE(0), count);
#endif
return;
}
int16 tmp[SND_SAMPLES];
dmaFill(tmp, 0, sizeof(tmp));
int32 tmp[SND_SAMPLES];
if (music.data) {
music.fill(tmp, count);
} else {
dmaFill(tmp, 0, sizeof(tmp));
}
int32 ch = channelsCount;
while (ch--)
@ -64,13 +139,24 @@ struct Sound
for (int32 i = 0; i < count; i++)
{
buffer[i] = SND_ENCODE(X_CLAMP(tmp[i], -128, 127));
int32 samp = X_CLAMP(tmp[i] >> SND_VOL_SHIFT, SND_MIN, SND_MAX);
#if defined(_WIN32)
bufferA[i] = SND_ENCODE(samp);
#elif defined(__GBA__)
#ifdef USE_9BIT_SOUND
bufferA[i] = (samp >> 1);
bufferB[i] = (samp >> 1) + (samp & 1); // TODO
#else
bufferA[i] = samp;
#endif
#endif
}
}
#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)
Sample* playSample(const uint8* data, int32 size, int32 volume, int32 pitch, int32 mode)
{
if (mode == UNIQUE || mode == REPLAY)
{
@ -97,6 +183,11 @@ struct Sound
return NULL;
}
#ifdef USE_9BIT_SOUND
// expand 8 to 9-bit
volume <<= 1;
#endif
Sample* sample = channels + channelsCount++;
sample->data = data;
sample->size = size << SND_FIXED_SHIFT;
@ -107,12 +198,23 @@ struct Sound
return sample;
}
void playMusic(const void* data)
{
music.data = (uint8*)data + 16;
music.size = *((int32*)data + 2);
music.pos = 0;
//music.volume = (1 << SND_VOL_SHIFT);
music.smp = 0;
music.idx = 0;
}
void init()
{
channelsCount = 0;
music.data = NULL;
}
};
Sound sound;
Mixer mixer;
#endif