1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-16 18:14:05 +02:00

#15 TR3 PSX format loader (WIP)

This commit is contained in:
XProger
2018-05-04 05:28:21 +03:00
parent 584ee9f2d3
commit 837322c02e
2 changed files with 720 additions and 354 deletions

View File

@@ -2265,6 +2265,9 @@ namespace TR {
int16 meshesCount;
Mesh meshes[MAX_MESHES];
int32 meshDataCount;
uint16 *meshData;
int32 meshOffsetsCount;
uint32 *meshOffsets;
@@ -2459,12 +2462,13 @@ namespace TR {
#define MAGIC_TR3_PC1 0xFF080038
#define MAGIC_TR3_PC2 0xFF180038
#define MAGIC_TR3_PC3 0xFF180034
#define MAGIC_TR3_PSX 0xFFFFFFC8
id = TR::getLevelID(stream.size, version, isDemoLevel);
if (version == VER_UNKNOWN || version == VER_TR1_PSX) {
stream.read(magic);
if (magic != MAGIC_TR1_PC && magic != MAGIC_TR2_PC && magic != MAGIC_TR3_PC1 && magic != MAGIC_TR3_PC2 && magic != MAGIC_TR3_PC3) {
if (magic != MAGIC_TR1_PC && magic != MAGIC_TR2_PC && magic != MAGIC_TR3_PC1 && magic != MAGIC_TR3_PC2 && magic != MAGIC_TR3_PC3 && magic != MAGIC_TR3_PSX) {
soundOffset = magic;
stream.read(magic);
}
@@ -2476,6 +2480,7 @@ namespace TR {
case MAGIC_TR3_PC1 :
case MAGIC_TR3_PC2 :
case MAGIC_TR3_PC3 : version = VER_TR3_PC; break;
case MAGIC_TR3_PSX : version = VER_TR3_PSX; break;
default : ;
}
}
@@ -2499,6 +2504,25 @@ namespace TR {
stream.read(soundData, soundDataSize);
}
if (version == VER_TR3_PSX) {
stream.read(soundOffsets, stream.read(soundOffsetsCount));
stream.read(soundDataSize);
stream.read(soundData, soundDataSize);
}
if (version == VER_TR3_PSX) {
// skip code modules
int size;
for (int i = 0; i < 13; i++) {
stream.read(size);
if (size) {
stream.seek(size);
stream.read(size);
stream.seek(size);
}
}
}
if (version == VER_TR2_PC || version == VER_TR3_PC) {
stream.read(palette, 256);
stream.read(palette32, 256);
@@ -2543,18 +2567,36 @@ namespace TR {
stream.read(cluts, clutsCount = 1024);
//stream.seek(0x4000);
}
if (version != VER_TR3_PSX)
stream.read(unused);
// rooms
rooms = stream.read(roomsCount) ? new Room[roomsCount] : NULL;
for (int i = 0; i < roomsCount; i++)
readRoom(stream, rooms[i]);
readRoom(stream, i);
// floors
stream.read(floors, stream.read(floorsCount));
if (version == VER_TR3_PSX) {
// outside room offsets
stream.seek(27 * 27 * 2);
// outside rooms table
int size;
stream.read(size);
stream.seek(size);
// room mesh bbox
stream.read(size);
stream.seek(8 * size);
}
// meshes
readMeshes(stream);
meshesCount = 0;
stream.read(meshData, stream.read(meshDataCount));
stream.read(meshOffsets, stream.read(meshOffsetsCount));
// animations
stream.read(anims, stream.read(animsCount));
stream.read(states, stream.read(statesCount));
stream.read(ranges, stream.read(rangesCount));
@@ -2576,18 +2618,33 @@ namespace TR {
if (version & VER_PSX)
stream.seek(2);
m.type = Entity::remap(version, m.type);
for (int j = 0; j < m.mCount; j++)
initMesh(m.mStart + j, m.type);
}
stream.read(staticMeshes, stream.read(staticMeshesCount));
if (version == VER_TR2_PSX) {
for (int i = 0; i < staticMeshesCount; i++)
initMesh(staticMeshes[i].mesh);
remapMeshOffsetsToIndices();
delete[] meshData;
meshData = NULL;
LOG("meshes: %d\n", meshesCount);
if (version == VER_TR2_PSX || version == VER_TR3_PSX) {
stream.read(tiles4, stream.read(tilesCount));
stream.read(cluts, stream.read(clutsCount));
//stream.read(palette32, stream.read(paletteSize));
if (version == VER_TR3_PSX) {
stream.seek(clutsCount * sizeof(CLUT));
} else
stream.seek(4);
}
// textures & UV
if (version & (VER_TR1 | VER_TR2))
if (version != VER_TR3_PC)
readObjectTex(stream);
readSpriteTex(stream);
// palette for demo levels
@@ -2636,7 +2693,7 @@ namespace TR {
// animated textures
readAnimTex(stream);
if (version & VER_TR3)
if (version == VER_TR3_PC)
readObjectTex(stream);
// entities (enemies, items, lara etc.)
@@ -2656,6 +2713,48 @@ namespace TR {
if (version == VER_TR2_PSX)
stream.seek(4);
if (version == VER_TR3_PSX) {
uint32 skyColor;
stream.read(skyColor);
int32 roomTexCount;
stream.read(roomTexCount);
if (roomTexCount) {
ObjectTexture *oldTex = objectTextures;
// reallocate textures info to add room textures
objectTextures = new ObjectTexture[objectTexturesCount + roomTexCount * 3];
if (oldTex) {
memcpy(objectTextures, oldTex, sizeof(ObjectTexture) * objectTexturesCount);
delete[] oldTex;
}
// load room textures
for (int i = objectTexturesCount; i < objectTexturesCount + roomTexCount * 3; i++) {
ObjectTexture &t = objectTextures[i];
readObjectTex(stream, t);
t.attribute = 0;
}
// remap room texture indices
for (int i = 0; i < roomsCount; i++) {
Room::Data &d = rooms[i].data;
for (int j = 0; j < d.fCount; j++) {
Face &f = d.faces[j];
//ASSERT(d.faces[j].flags.texture < roomTexCount);
if (f.flags.texture < roomTexCount) {
f.flags.texture *= 3;
f.flags.texture += objectTexturesCount;
}
}
}
objectTexturesCount += roomTexCount * 3;
}
}
// sounds
stream.read(soundsMap, (version & VER_TR1) ? 256 : 370);
soundsInfo = stream.read(soundsInfoCount) ? new SoundInfo[soundsInfoCount] : NULL;
@@ -2694,6 +2793,7 @@ namespace TR {
// cinematic frames for cameras (PSX)
if (version & VER_PSX) {
if (version != VER_TR3_PSX)
stream.seek(4);
stream.read(cameraFrames, stream.read(cameraFramesCount));
}
@@ -2958,7 +3058,8 @@ namespace TR {
#endif
}
void readRoom(Stream &stream, Room &r) {
void readRoom(Stream &stream, int roomIndex) {
Room &r = rooms[roomIndex];
Room::Data &d = r.data;
// room info
stream.read(r.info);
@@ -2966,24 +3067,170 @@ namespace TR {
stream.read(d.size);
int startOffset = stream.pos;
if (version == VER_TR1_PSX) stream.seek(2);
// only for TR3 PSX
int32 partsCount;
int32 parts[16];
if (version == VER_TR3_PSX) {
d.vCount = d.tCount = d.rCount = d.fCount = 0;
// pre-calculate number of vertices, triangles and rectangles for TR3 PSX format
stream.read(partsCount);
stream.raw(parts, sizeof(parts));
for (int pIndex = 0; pIndex < partsCount; pIndex++) {
stream.setPos(startOffset + parts[pIndex] + 6);
uint8 tmpV, tmpT;
d.vCount += stream.read(tmpV);
d.tCount += stream.read(tmpT);
stream.seek(tmpV * 4 + tmpT * 4); // skip vertices and triangles
int q = 0;
while (1) {
uint32 info;
if (!q) {
stream.read(info);
q = 3;
}
if (!(info ^ 0x03FF)) // info is 0b1111111111
break;
info >>= 10;
stream.seek(4); // skip rectangle indices
d.rCount++;
q--;
}
}
if (partsCount != 0)
stream.seek(4); // skip unknown shit
ASSERT(d.size * 2 == stream.pos - startOffset);
d.fCount = d.rCount + d.tCount;
d.faces = d.fCount ? new Face[d.fCount] : NULL;
d.vertices = d.vCount ? new Room::Data::Vertex[d.vCount] : NULL;
d.vCount = d.fCount = 0;
} else
d.vertices = stream.read(d.vCount) ? new Room::Data::Vertex[d.vCount] : NULL;
if (version == VER_TR3_PSX) {
for (int pIndex = 0; pIndex < partsCount; pIndex++) {
stream.setPos(startOffset + parts[pIndex] + 4);
uint8 rIndex, rFlags;
stream.read(rIndex); // room index
stream.read(rFlags); // room flags
ASSERT(rIndex == roomIndex);
r.flags.value = rFlags;
uint8 vCount, tCount;
stream.read(vCount);
stream.read(tCount);
int32 vStart = d.vCount;
// read vertices
for (uint8 i = 0; i < vCount; i++) {
Room::Data::Vertex &v = d.vertices[d.vCount++];
struct {
uint32 z:5, y:5, x:5, :1, lighting:8, attributes:8;
} cv;
stream.raw(&cv, sizeof(cv));
v.vertex.x = (cv.x << 10);
v.vertex.y = (cv.y << 8) + r.info.yTop;
v.vertex.z = (cv.z << 10);
}
// read triangles
for (uint8 i = 0; i < tCount; i++) {
struct {
uint32 i0:7, i1:7, i2:7, doubleSided:1, texture:10;
} t;
stream.raw(&t, sizeof(t));
Face &f = d.faces[d.fCount++];
f.flags.texture = t.texture;
f.flags.doubleSided = t.doubleSided;
f.vCount = 3;
f.colored = false;
f.vertices[0] = vStart + t.i0;
f.vertices[1] = vStart + t.i1;
f.vertices[2] = vStart + t.i2;
f.vertices[3] = 0;
ASSERT(f.vertices[0] < d.vCount);
ASSERT(f.vertices[1] < d.vCount);
ASSERT(f.vertices[2] < d.vCount);
}
// read rectangles
int q = 0;
while (1) {
uint32 info;
if (!q) {
stream.read(info);
q = 3;
}
uint32 texture = info & 0x03FF;
//ASSERT(!(info ^ 0x3FF) == !(texture ^ 0x3FF));
if (!(info ^ 0x3FF))
break;
info >>= 10;
struct {
uint32 i0:7, i1:7, i2:7, i3:7, :3, doubleSided:1;
} r;
stream.raw(&r, sizeof(r));
Face &f = d.faces[d.fCount++];
f.flags.texture = texture;
f.flags.doubleSided = r.doubleSided;
f.vCount = 4;
f.colored = false;
f.vertices[0] = vStart + r.i0;
f.vertices[1] = vStart + r.i1;
f.vertices[2] = vStart + r.i2;
f.vertices[3] = vStart + r.i3;
if (texture == 1023) {
f.flags.texture = 0;
f.vertices[0] = f.vertices[1] = f.vertices[2] = f.vertices[3] = 0;
}
ASSERT(f.vertices[0] < d.vCount);
ASSERT(f.vertices[1] < d.vCount);
ASSERT(f.vertices[2] < d.vCount);
ASSERT(f.vertices[3] < d.vCount);
q--;
}
}
} else {
for (int i = 0; i < d.vCount; i++) {
Room::Data::Vertex &v = d.vertices[i];
uint16 lighting;
if (version == VER_TR2_PSX) {
union Compressed {
struct { uint32 lighting:8, attributes:8, z:5, y:5, x:5, w:1; };
uint32 value;
} comp;
stream.read(comp.value);
v.vertex.x = (comp.x << 10);
v.vertex.y = (comp.y << 8) + r.info.yTop;
v.vertex.z = (comp.z << 10);
lighting = comp.lighting;
v.attributes = comp.attributes;
ASSERT(comp.w == 0);
struct {
uint32 lighting:8, attributes:8, z:5, y:5, x:5, :1;
} cv;
stream.raw(&cv, sizeof(cv));
v.vertex.x = (cv.x << 10);
v.vertex.y = (cv.y << 8) + r.info.yTop;
v.vertex.z = (cv.z << 10);
lighting = cv.lighting;
v.attributes = cv.attributes;
} else {
stream.read(v.vertex.x);
stream.read(v.vertex.y);
@@ -3035,6 +3282,7 @@ namespace TR {
int16 tmpCount;
stream.read(tmpCount);
ASSERT(tmpCount == d.rCount);
if (version == VER_TR2_PSX) {
for (int i = 0; i < d.rCount; i++)
stream.raw(&d.faces[i].flags.value, sizeof(uint16));
@@ -3052,13 +3300,9 @@ namespace TR {
} else
for (int i = 0; i < d.rCount; i++) readFace(stream, d.faces[idx++], false, false);
if (version & VER_PSX) { // swap indices (quad strip -> quad list) only for PSX version
for (int j = 0; j < d.rCount; j++)
swap(d.faces[j].vertices[2], d.faces[j].vertices[3]);
}
stream.read(tmpCount);
ASSERT(tmpCount == d.tCount);
if (version == VER_TR2_PSX) {
stream.seek(2);
for (int i = 0; i < d.tCount; i++) {
@@ -3076,18 +3320,31 @@ namespace TR {
for (int i = 0; i < d.tCount; i++)
readFace(stream, d.faces[idx++], false, true);
}
}
if (version & VER_PSX) { // swap indices (quad strip -> quad list) only for PSX version
for (int j = 0; j < d.fCount; j++)
if (d.faces[j].vCount == 4)
swap(d.faces[j].vertices[2], d.faces[j].vertices[3]);
}
// room sprites
if (version == VER_TR2_PSX) { // there is no room sprites
if (version == VER_TR2_PSX || version == VER_TR3_PSX) { // there is no room sprites
d.sprites = NULL;
d.sCount = 0;
} else
stream.read(d.sprites, stream.read(d.sCount));
if (version == VER_TR3_PSX)
if (partsCount != 0)
stream.seek(4); // skip unknown shit
ASSERT(d.size * 2 == stream.pos - startOffset);
// portals
stream.read(r.portals, stream.read(r.portalsCount));
if (version == VER_TR2_PSX) {
if (version == VER_TR2_PSX || version == VER_TR3_PSX) {
for (int i = 0; i < r.portalsCount; i++) {
r.portals[i].vertices[0].y += r.info.yTop;
r.portals[i].vertices[1].y += r.info.yTop;
@@ -3122,11 +3379,15 @@ namespace TR {
}
// ambient light luminance
stream.read(r.ambient);
if (version != VER_TR3_PSX) {
if (version & (VER_TR2 | VER_TR3))
stream.read(r.ambient2);
if (version & VER_TR2)
stream.read(r.lightMode);
} else
stream.read(r.ambient2);
// lights
r.lights = stream.read(r.lightsCount) ? new Room::Light[r.lightsCount] : NULL;
@@ -3138,7 +3399,7 @@ namespace TR {
uint16 intensity;
if (version == VER_TR3_PC)
if (version & VER_TR3)
stream.read(light.color);
stream.read(intensity);
@@ -3196,30 +3457,53 @@ namespace TR {
// misc flags
stream.read(r.alternateRoom);
stream.read(r.flags.value);
if (version & VER_TR3) {
if (version == VER_TR3_PC) {
stream.read(r.waterScheme);
stream.read(r.reverbType);
stream.read(r.filter); // unused
}
if (version == VER_TR3_PSX) {
//stream.seek(1);
stream.read(r.waterScheme);
stream.read(r.reverbType);
stream.seek(1);
}
r.dynLightsCount = 0;
}
void initMesh(int mIndex, Entity::Type type = Entity::LARA) {
if (type == Entity::SKY)
return;
int offset = meshOffsets[mIndex];
for (int i = 0; i < meshesCount; i++)
if (meshes[i].offset == offset)
return;
Stream stream(NULL, &meshData[offset / 2], 1024 * 1024);
void readMeshes(Stream &stream) {
uint32 meshDataSize;
stream.read(meshDataSize);
int32 start = stream.pos;
int32 end = stream.pos + meshDataSize * 2;
meshesCount = 0;
while (stream.pos < end) {
Mesh &mesh = meshes[meshesCount++];
mesh.offset = stream.pos - start;
mesh.offset = offset;
uint32 fOffset;
stream.read(mesh.center);
stream.read(mesh.radius);
//ASSERT(mesh.radius >= 0);
if (version == VER_TR3_PSX) {
uint8 tmp;
uint16 tmpOffset;
mesh.vCount = stream.read(tmp);
mesh.flags.value = stream.read(tmp);
fOffset = stream.pos + stream.read(tmpOffset);
//LOG("offset:%d\n", offset - stream.pos);
} else {
stream.read(mesh.flags.value);
stream.read(mesh.vCount);
}
switch (version) {
case VER_TR1_PC :
@@ -3322,7 +3606,7 @@ namespace TR {
}
}
if (version == VER_TR2_PSX && nCount > 0) { // TODO probably for unused meshes only but need to check
if ((version & VER_TR2) && nCount > 0) { // TODO probably for unused meshes only but need to check
uint16 crCount = 0, ctCount = 0;
stream.read(crCount);
stream.seek((FACE4_SIZE + 2) * crCount);
@@ -3362,6 +3646,89 @@ namespace TR {
break;
}
case VER_TR3_PSX : {
if (!mesh.vCount) {
mesh.vertices = NULL;
mesh.faces = NULL;
mesh.tCount = mesh.rCount = mesh.fCount = 0;
break;
}
mesh.vertices = new Mesh::Vertex[mesh.vCount];
for (int i = 0; i < mesh.vCount; i++)
stream.read(mesh.vertices[i].coord);
if (mesh.flags.value & 0x80) { // static mesh without normals
for (int i = 0; i < mesh.vCount; i++)
mesh.vertices[i].normal = short4( 0, 0, 0, 0 );
} else {
for (int i = 0; i < mesh.vCount; i++)
stream.read(mesh.vertices[i].normal);
}
ASSERT(stream.pos == fOffset);
stream.setPos(fOffset);
stream.read(mesh.tCount);
stream.read(mesh.rCount);
mesh.fCount = mesh.rCount + mesh.tCount;
mesh.faces = mesh.fCount ? new Face[mesh.fCount] : NULL;
// read triangles
int idx = 0;
uint32 info;
for (int i = 0; i < mesh.tCount; i++) {
if (!(i % 4))
stream.read(info);
Face &f = mesh.faces[idx++];
f.flags.doubleSided = 0;
f.flags.texture = info & 0xFF;
f.colored = false;
f.vCount = 3;
struct {
uint32 i0:8, i1:8, i2:8, :8;
} r;
stream.raw(&r, sizeof(r));
//stream.raw(&indices, sizeof(indices));
f.vertices[0] = r.i0;
f.vertices[1] = r.i1;
f.vertices[2] = r.i2;
f.vertices[3] = 0;
info >>= 8;
}
// read rectangles
for (int i = 0; i < mesh.rCount; i++) {
if (!(i % 2))
stream.read(info);
Face &f = mesh.faces[idx++];
f.flags.doubleSided = 0;
f.flags.texture = info & 0xFFFF;
f.colored = false;
f.vCount = 4;
struct {
uint32 i0:8, i1:8, i2:8, i3:8;
} r;
stream.raw(&r, sizeof(r));
f.vertices[0] = r.i0;
f.vertices[1] = r.i1;
f.vertices[2] = r.i3;
f.vertices[3] = r.i2;
info >>= 16;
}
break;
}
default : ASSERT(false);
}
@@ -3393,20 +3760,11 @@ namespace TR {
}
#undef RECALC_ZERO_NORMALS
int32 align = (stream.pos - start - mesh.offset) % 4;
if (align) stream.seek(4 - align);
}
ASSERT(stream.pos - start == meshDataSize * 2);
stream.read(meshOffsets, stream.read(meshOffsetsCount));
// remap offsets to mesh index
void remapMeshOffsetsToIndices() {
for (int i = 0; i < meshOffsetsCount; i++) {
int index = -1;
// if (!meshOffsets[i] && i)
// index = -1;
// else
for (int j = 0; j < meshesCount; j++)
if (meshes[j].offset == meshOffsets[i]) {
index = j;
@@ -3416,7 +3774,7 @@ namespace TR {
}
}
void readObjectTex(Stream &stream) {
void readObjectTex(Stream &stream, ObjectTexture &t) {
#define SET_PARAMS(t, d, c) {\
t.clut = c;\
t.tile = d.tile;\
@@ -3429,9 +3787,6 @@ namespace TR {
ASSERT(d.x0 < 256 && d.x1 < 256 && d.x2 < 256 && d.x3 < 256 && d.y0 < 256 && d.y1 < 256 && d.y2 < 256 && d.y3 < 256);\
}
objectTextures = stream.read(objectTexturesCount) ? new ObjectTexture[objectTexturesCount] : NULL;
for (int i = 0; i < objectTexturesCount; i++) {
ObjectTexture &t = objectTextures[i];
switch (version) {
case VER_TR1_PC :
case VER_TR2_PC :
@@ -3449,7 +3804,8 @@ namespace TR {
break;
}
case VER_TR1_PSX :
case VER_TR2_PSX : {
case VER_TR2_PSX :
case VER_TR3_PSX : {
struct {
uint8 x0, y0;
uint16 clut;
@@ -3466,11 +3822,16 @@ namespace TR {
}
default : ASSERT(false);
}
}
#undef SET_PARAMS
}
void readObjectTex(Stream &stream) {
objectTextures = stream.read(objectTexturesCount) ? new ObjectTexture[objectTexturesCount] : NULL;
for (int i = 0; i < objectTexturesCount; i++)
readObjectTex(stream, objectTextures[i]);
}
void readSpriteTex(Stream &stream) {
#define SET_PARAMS(t, d, c) {\
t.clut = c;\
@@ -3501,7 +3862,8 @@ namespace TR {
break;
}
case VER_TR1_PSX :
case VER_TR2_PSX : {
case VER_TR2_PSX :
case VER_TR3_PSX : {
struct {
int16 l, t, r, b;
uint16 clut;
@@ -3647,7 +4009,8 @@ namespace TR {
break;
}
case VER_TR1_PSX :
case VER_TR2_PSX : {
case VER_TR2_PSX :
case VER_TR3_PSX : {
ASSERT(tiles4);
ASSERT(cluts);
@@ -3717,7 +4080,8 @@ namespace TR {
case VER_TR2_PC :
case VER_TR3_PC : return palette32[(texture >> 8) & 0xFF];
case VER_TR1_PSX :
case VER_TR2_PSX : {
case VER_TR2_PSX :
case VER_TR3_PSX : {
ASSERT((texture & 0x7FFF) < 256);
ObjectTexture &t = objectTextures[texture & 0x7FFF];
int idx = (t.texCoord[0].y * 256 + t.texCoord[0].x) / 2;
@@ -3741,6 +4105,7 @@ namespace TR {
case VER_TR3_PC : size = FOURCC(data + 4) + 8; break; // read size from wave header
case VER_TR1_PSX :
case VER_TR2_PSX : size = soundSize[index]; break;
case VER_TR3_PSX : return NULL; // TR3_TODO
default : ASSERT(false);
}
return new Stream(NULL, data, size);

View File

@@ -443,7 +443,7 @@ struct MeshBuilder {
TR::Model &model = level.models[i];
for (int j = 0; j < model.mCount; j++) {
int index = level.meshOffsets[model.mStart + j];
if (!index && model.mStart + j > 0)
if (index == -1)
continue;
TR::Mesh &mesh = level.meshes[index];
iCount += (mesh.rCount * 6 + mesh.tCount * 3) * DOUBLE_SIDED;
@@ -562,13 +562,14 @@ struct MeshBuilder {
#endif
int index = level.meshOffsets[model.mStart + j];
if (index || model.mStart + j <= 0) {
if (index == -1)
continue;
TR::Mesh &mesh = level.meshes[index];
#ifndef MERGE_MODELS
geom.getNextRange(vStartModel, iCount, 0xFFFF, 0xFFFF);
#endif
buildMesh(geom, blendMask, mesh, level, indices, vertices, iCount, vCount, vStartModel, j, 0, 0, 0, 0, COLOR_WHITE);
}
#ifndef MERGE_MODELS
geom.finish(iCount);