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

#15 add TR3 PSX format support (with bugs)

This commit is contained in:
XProger
2018-05-07 04:28:42 +03:00
parent 837322c02e
commit 6a71022e58
5 changed files with 267 additions and 254 deletions

View File

@@ -1101,6 +1101,9 @@ namespace TR {
struct { uint16 r:5, g:5, b:5, a:1; };
uint16 value;
Color16() {}
Color16(uint16 value) : value(value) {}
Color32 getBGR() const { return Color32((b << 3) | (b >> 2), (g << 3) | (g >> 2), (r << 3) | (r >> 2), 255); }
operator Color24() const { return Color24((r << 3) | (r >> 2), (g << 3) | (g >> 2), (b << 3) | (b >> 2)); }
operator Color32() const { return Color32((r << 3) | (r >> 2), (g << 3) | (g >> 2), (b << 3) | (b >> 2), -a); }
@@ -1122,6 +1125,7 @@ namespace TR {
Tile tile; // tile or palette index
uint16 attribute:15, animated:1; // 0 - opaque, 1 - transparent, 2 - blend additive, animated
short2 texCoord[4];
short2 texCoordAtlas[4];
short4 getMinMax() const {
return short4(
@@ -1131,6 +1135,15 @@ namespace TR {
max(max(texCoord[0].y, texCoord[1].y), texCoord[2].y)
);
}
short4 getMinMaxAtlas() const {
return short4(
min(min(texCoordAtlas[0].x, texCoordAtlas[1].x), texCoordAtlas[2].x),
min(min(texCoordAtlas[0].y, texCoordAtlas[1].y), texCoordAtlas[2].y),
max(max(texCoordAtlas[0].x, texCoordAtlas[1].x), texCoordAtlas[2].x),
max(max(texCoordAtlas[0].y, texCoordAtlas[1].y), texCoordAtlas[2].y)
);
}
};
struct SpriteTexture {
@@ -1138,10 +1151,15 @@ namespace TR {
uint16 tile;
int16 l, t, r, b;
short2 texCoord[2];
short2 texCoordAtlas[2];
short4 getMinMax() const {
return short4( texCoord[0].x, texCoord[0].y, texCoord[1].x, texCoord[1].y );
}
short4 getMinMaxAtlas() const {
return short4( texCoordAtlas[0].x, texCoordAtlas[0].y, texCoordAtlas[1].x, texCoordAtlas[1].y );
}
};
// used for access from ::cmp func
@@ -1294,8 +1312,8 @@ namespace TR {
};
uint16 value;
} flags;
uint16 reverbType;
uint8 waterScheme;
uint8 reverbType;
uint8 filter;
uint8 align;
int32 waterLevel;
@@ -1448,7 +1466,7 @@ namespace TR {
int16 radius;
union {
struct {
uint16 transparent:1, reserved:15;
uint16 isStatic:1, reserved:15;
};
uint16 value;
} flags;
@@ -2252,7 +2270,6 @@ namespace TR {
LevelID id;
int32 tilesCount;
Tile32 *tiles;
uint32 unused;
@@ -2354,6 +2371,8 @@ namespace TR {
uint32 *soundOffsets;
uint32 *soundSize;
Color32 skyColor;
SaveGame save;
SaveGame::CurrentState state;
@@ -2553,8 +2572,8 @@ namespace TR {
stream.setPos(startPos + offsetTexTiles + 8);
}
if (version & VER_PC) {
// tiles
if (version & VER_PC) {
stream.read(tiles8, stream.read(tilesCount));
}
@@ -2562,10 +2581,8 @@ namespace TR {
stream.read(tiles16, tilesCount);
if (version == VER_TR1_PSX) {
// tiles
stream.read(tiles4, tilesCount = 13);
stream.read(cluts, clutsCount = 1024);
//stream.seek(0x4000);
}
if (version != VER_TR3_PSX)
@@ -2636,10 +2653,11 @@ namespace TR {
if (version == VER_TR2_PSX || version == VER_TR3_PSX) {
stream.read(tiles4, stream.read(tilesCount));
stream.read(cluts, stream.read(clutsCount));
if (version == VER_TR3_PSX) {
stream.seek(clutsCount * sizeof(CLUT));
} else
stream.read(clutsCount);
if (version == VER_TR3_PSX)
clutsCount *= 2; // read underwater cluts too
stream.read(cluts, clutsCount);
if (version != VER_TR3_PSX)
stream.seek(4);
}
@@ -2714,7 +2732,6 @@ namespace TR {
stream.seek(4);
if (version == VER_TR3_PSX) {
uint32 skyColor;
stream.read(skyColor);
int32 roomTexCount;
@@ -2723,17 +2740,16 @@ namespace TR {
if (roomTexCount) {
ObjectTexture *oldTex = objectTextures;
// reallocate textures info to add room textures
objectTextures = new ObjectTexture[objectTexturesCount + roomTexCount * 3];
objectTextures = new ObjectTexture[objectTexturesCount + roomTexCount];
if (oldTex) {
memcpy(objectTextures, oldTex, sizeof(ObjectTexture) * objectTexturesCount);
delete[] oldTex;
}
// load room textures
for (int i = objectTexturesCount; i < objectTexturesCount + roomTexCount * 3; i++) {
for (int i = objectTexturesCount; i < objectTexturesCount + roomTexCount; i++) {
ObjectTexture &t = objectTextures[i];
readObjectTex(stream, t);
t.attribute = 0;
readObjectTex(stream, t, true);
stream.seek(2 * 16); // skip 2 mipmap levels
}
// remap room texture indices
@@ -2742,16 +2758,13 @@ namespace TR {
for (int j = 0; j < d.fCount; j++) {
Face &f = d.faces[j];
//ASSERT(d.faces[j].flags.texture < roomTexCount);
ASSERT(f.flags.texture < roomTexCount);
if (f.flags.texture < roomTexCount) {
f.flags.texture *= 3;
f.flags.texture += objectTexturesCount;
}
f.flags.texture += objectTexturesCount;
}
}
objectTexturesCount += roomTexCount * 3;
LOG("objTex:%d + roomTex:%d = %d\n", objectTexturesCount, roomTexCount, objectTexturesCount + roomTexCount);
objectTexturesCount += roomTexCount;
}
}
@@ -2798,6 +2811,19 @@ namespace TR {
stream.read(cameraFrames, stream.read(cameraFramesCount));
}
// Amiga -> PC color palette for TR1 PC
if (version == VER_TR1_PC) {
ASSERT(palette);
Color24 *c = palette;
for (int i = 0; i < 256; i++) {
c->r <<= 2;
c->g <<= 2;
c->b <<= 2;
c++;
}
}
initRoomMeshes();
initAnimTex();
@@ -2813,7 +2839,6 @@ namespace TR {
}
~Level() {
delete[] tiles;
// rooms
for (int i = 0; i < roomsCount; i++) {
Room &r = rooms[i];
@@ -3094,7 +3119,8 @@ namespace TR {
stream.read(info);
q = 3;
}
if (!(info ^ 0x03FF)) // info is 0b1111111111
uint32 texture = info & 0x03FF;
if (texture == 0x03FF) // ending flag
break;
info >>= 10;
stream.seek(4); // skip rectangle indices
@@ -3103,10 +3129,8 @@ namespace TR {
}
}
if (partsCount != 0)
stream.seek(4); // skip unknown shit
ASSERT(d.size * 2 == stream.pos - startOffset);
ASSERT(int(d.size * 2) >= stream.pos - startOffset);
stream.setPos(startOffset + d.size * 2);
d.fCount = d.rCount + d.tCount;
d.faces = d.fCount ? new Face[d.fCount] : NULL;
@@ -3137,27 +3161,32 @@ namespace TR {
Room::Data::Vertex &v = d.vertices[d.vCount++];
struct {
uint32 z:5, y:5, x:5, :1, lighting:8, attributes:8;
uint32 z:5, y:5, x:5, color:16, unknown:1;
} cv;
stream.raw(&cv, sizeof(cv));
//ASSERT(cv.attribute == 0);
v.vertex.x = (cv.x << 10);
v.vertex.y = (cv.y << 8) + r.info.yTop;
v.vertex.z = (cv.z << 10);
v.attributes = 0;
v.color = Color16(cv.color);
}
// read triangles
for (uint8 i = 0; i < tCount; i++) {
struct {
uint32 i0:7, i1:7, i2:7, doubleSided:1, texture:10;
uint32 i0:7, i1:7, i2:7, texture:10, unknown:1;
} t;
stream.raw(&t, sizeof(t));
ASSERT(t.unknown == 0);
Face &f = d.faces[d.fCount++];
f.flags.texture = t.texture;
f.flags.doubleSided = t.doubleSided;
f.flags.doubleSided = false;
f.vCount = 3;
f.colored = false;
f.vertices[0] = vStart + t.i0;
@@ -3178,21 +3207,20 @@ namespace TR {
}
uint32 texture = info & 0x03FF;
//ASSERT(!(info ^ 0x3FF) == !(texture ^ 0x3FF));
if (!(info ^ 0x3FF))
if (texture == 0x3FF)
break;
info >>= 10;
struct {
uint32 i0:7, i1:7, i2:7, i3:7, :3, doubleSided:1;
uint32 i0:7, i1:7, i2:7, i3:7, unknown:4;
} r;
stream.raw(&r, sizeof(r));
ASSERT(r.unknown == 0);
Face &f = d.faces[d.fCount++];
f.flags.texture = texture;
f.flags.doubleSided = r.doubleSided;
f.flags.doubleSided = false;
f.vCount = 4;
f.colored = false;
f.vertices[0] = vStart + r.i0;
@@ -3200,11 +3228,6 @@ namespace TR {
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);
@@ -3230,7 +3253,7 @@ namespace TR {
v.vertex.y = (cv.y << 8) + r.info.yTop;
v.vertex.z = (cv.z << 10);
lighting = cv.lighting;
v.attributes = cv.attributes;
v.attributes = 0;//cv.attributes;
} else {
stream.read(v.vertex.x);
stream.read(v.vertex.y);
@@ -3339,7 +3362,8 @@ namespace TR {
if (partsCount != 0)
stream.seek(4); // skip unknown shit
ASSERT(d.size * 2 == stream.pos - startOffset);
ASSERT(int(d.size * 2) >= stream.pos - startOffset);
stream.setPos(startOffset + d.size * 2);
// portals
stream.read(r.portals, stream.read(r.portalsCount));
@@ -3435,48 +3459,38 @@ namespace TR {
if (version & VER_TR3) {
Color16 color;
stream.read(color.value);
m.color = color.getBGR();
}
if (version & VER_TR2)
m.color = color;
stream.seek(2);
} else {
if (version & VER_TR2)
stream.seek(2);
uint16 intensity;
stream.read(intensity);
if ((version & VER_VERSION) < VER_TR3) {
int value = clamp((intensity > 0x1FFF) ? 255 : (255 - (intensity >> 5)), 0, 255);
m.color.r = m.color.g = m.color.b = value;
m.color.a = 0;
uint16 intensity;
stream.read(intensity);
if ((version & VER_VERSION) < VER_TR3) {
int value = clamp((intensity > 0x1FFF) ? 255 : (255 - (intensity >> 5)), 0, 255);
m.color.r = m.color.g = m.color.b = value;
m.color.a = 0;
}
}
stream.read(m.meshID);
if (version == VER_TR1_PSX)
stream.seek(2); // just an align for PSX version
stream.seek(2); // skip padding
}
// misc flags
stream.read(r.alternateRoom);
stream.read(r.flags.value);
if (version == VER_TR3_PC) {
if (version & VER_TR3) {
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)
@@ -3489,20 +3503,29 @@ namespace TR {
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);
if (type == Entity::SKY && version == VER_TR3_PSX) {
mesh.center.x =
mesh.center.y =
mesh.center.z =
mesh.radius =
mesh.tCount = 0;
mesh.flags.value = 0x80;
stream.read(mesh.vCount);
stream.read(mesh.rCount);
} else {
stream.read(mesh.center);
stream.read(mesh.radius);
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);
} else {
stream.read(mesh.flags.value);
stream.read(mesh.vCount);
}
}
switch (version) {
@@ -3667,11 +3690,13 @@ namespace TR {
stream.read(mesh.vertices[i].normal);
}
ASSERT(stream.pos == fOffset);
if (type != Entity::SKY) {
ASSERT(stream.pos == fOffset);
stream.setPos(fOffset);
stream.read(mesh.tCount);
stream.read(mesh.rCount);
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;
@@ -3682,18 +3707,17 @@ namespace TR {
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;
uint32 i0:8, i1:8, i2:8, tex:8;
} r;
stream.raw(&r, sizeof(r));
//stream.raw(&indices, sizeof(indices));
Face &f = mesh.faces[idx++];
f.flags.doubleSided = false;
f.flags.texture = (info & 0xFF) | (r.tex << 8);
f.colored = false;
f.vCount = 3;
f.vertices[0] = r.i0;
f.vertices[1] = r.i1;
@@ -3709,7 +3733,7 @@ namespace TR {
stream.read(info);
Face &f = mesh.faces[idx++];
f.flags.doubleSided = 0;
f.flags.doubleSided = false;
f.flags.texture = info & 0xFFFF;
f.colored = false;
f.vCount = 4;
@@ -3774,16 +3798,16 @@ namespace TR {
}
}
void readObjectTex(Stream &stream, ObjectTexture &t) {
void readObjectTex(Stream &stream, ObjectTexture &t, bool isRoom = false) {
#define SET_PARAMS(t, d, c) {\
t.clut = c;\
t.tile = d.tile;\
t.attribute = d.attribute;\
t.animated = false;\
t.texCoord[0] = short2( d.x0, d.y0 );\
t.texCoord[1] = short2( d.x1, d.y1 );\
t.texCoord[2] = short2( d.x2, d.y2 );\
t.texCoord[3] = short2( d.x3, d.y3 );\
t.texCoord[0] = t.texCoordAtlas[0] = short2( d.x0, d.y0 );\
t.texCoord[1] = t.texCoordAtlas[1] = short2( d.x1, d.y1 );\
t.texCoord[2] = t.texCoordAtlas[2] = short2( d.x2, d.y2 );\
t.texCoord[3] = t.texCoordAtlas[3] = short2( d.x3, d.y3 );\
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);\
}
@@ -3817,6 +3841,18 @@ namespace TR {
uint16 attribute;
} d;
stream.raw(&d, sizeof(d));
if (version == VER_TR3_PSX) {
if (d.attribute == 0)
d.attribute = 0;
else if (d.attribute == 2)
d.attribute = 2;
else if (d.attribute == 0xFFFF)
d.attribute = 1;
else {
//ASSERT(false);
d.attribute = 0;
}
}
SET_PARAMS(t, d, d.clut);
break;
}
@@ -3857,8 +3893,8 @@ namespace TR {
} d;
stream.raw(&d, sizeof(d));
SET_PARAMS(t, d, 0);
t.texCoord[0] = short2( d.u, d.v );
t.texCoord[1] = short2( (uint8)(d.u + (d.w >> 8)), (uint8)(d.v + (d.h >> 8)) );
t.texCoord[0] = t.texCoordAtlas[0] = short2( d.u, d.v );
t.texCoord[1] = t.texCoordAtlas[1] = short2( (uint8)(d.u + (d.w >> 8)), (uint8)(d.v + (d.h >> 8)) );
break;
}
case VER_TR1_PSX :
@@ -3873,8 +3909,8 @@ namespace TR {
} d;
stream.raw(&d, sizeof(d));
SET_PARAMS(t, d, d.clut);
t.texCoord[0] = short2( d.u0, d.v0 );
t.texCoord[1] = short2( d.u1, d.v1 );
t.texCoord[0] = t.texCoordAtlas[0] = short2( d.u0, d.v0 );
t.texCoord[1] = t.texCoordAtlas[1] = short2( d.u1, d.v1 );
break;
}
default : ASSERT(false);
@@ -3898,12 +3934,16 @@ namespace TR {
void readAnimTex(Stream &stream) {
stream.read(animTexturesData, stream.read(animTexturesDataSize));
animTextures = NULL;
if (!animTexturesDataSize)
return;
uint16 *ptr = animTexturesData;
animTexturesCount = *ptr++;
if (!animTexturesCount) {
animTextures = NULL;
if (!animTexturesCount)
return;
}
animTextures = new AnimTexture*[animTexturesCount];
for (int i = 0; i < animTexturesCount; i++) {
animTextures[i] = (AnimTexture*)ptr;
@@ -3974,37 +4014,45 @@ namespace TR {
}
}
void initTiles() {
tiles = new Tile32[tilesCount];
void fillObjectTexture(Tile32 *dst, const short4 &uv, int16 tileIndex, int16 clutIndex) {
// convert to RGBA
switch (version) {
case VER_TR1_PC : {
ASSERT(tiles8);
ASSERT(palette);
for (int i = 0; i < 256; i++) { // Amiga -> PC color palette
Color24 &c = palette[i];
c.r <<= 2;
c.g <<= 2;
c.b <<= 2;
}
for (int i = 0; i < tilesCount; i++) {
Color32 *ptr = &tiles[i].color[0];
for (int y = 0; y < 256; y++) {
for (int x = 0; x < 256; x++) {
uint8 index = tiles8[i].index[y * 256 + x];
Color24 &p = palette[index];
if (index != 0) {
ptr[x].r = p.r;
ptr[x].g = p.g;
ptr[x].b = p.b;
ptr[x].a = 255;
} else
ptr[x].r = ptr[x].g = ptr[x].b = ptr[x].a = 0;
}
ptr += 256;
Color32 *ptr = &dst->color[uv.y * 256];
for (int y = uv.y; y < uv.w; y++) {
for (int x = uv.x; x < uv.z; x++) {
ASSERT(x >= 0 && y >= 0 && x < 256 && y < 256);
uint8 index = tiles8[tileIndex].index[y * 256 + x];
Color24 &p = palette[index];
if (index != 0) {
ptr[x].r = p.r;
ptr[x].g = p.g;
ptr[x].b = p.b;
ptr[x].a = 255;
} else
ptr[x].r = ptr[x].g = ptr[x].b = ptr[x].a = 0;
}
ptr += 256;
}
break;
}
case VER_TR2_PC :
case VER_TR3_PC : {
ASSERT(tiles16);
Color32 *ptr = &dst->color[uv.y * 256];
for (int y = uv.y; y < uv.w; y++) {
for (int x = uv.x; x < uv.z; x++) {
Color32 c = tiles16[tileIndex].color[y * 256 + x];
ptr[x].r = c.b;
ptr[x].g = c.g;
ptr[x].b = c.r;
ptr[x].a = c.a;
}
ptr += 256;
}
break;
}
@@ -4014,63 +4062,17 @@ namespace TR {
ASSERT(tiles4);
ASSERT(cluts);
for (int i = 0; i < objectTexturesCount; i++) {
ObjectTexture &t = objectTextures[i];
CLUT &clut = cluts[t.clut];
Tile32 &dst = tiles[t.tile.index];
Tile4 &src = tiles4[t.tile.index];
CLUT &clut = cluts[clutIndex];
Tile4 &src = tiles4[tileIndex];
int minX = min(min(t.texCoord[0].x, t.texCoord[1].x), t.texCoord[2].x);
int maxX = max(max(t.texCoord[0].x, t.texCoord[1].x), t.texCoord[2].x);
int minY = min(min(t.texCoord[0].y, t.texCoord[1].y), t.texCoord[2].y);
int maxY = max(max(t.texCoord[0].y, t.texCoord[1].y), t.texCoord[2].y);
for (int y = uv.y; y < uv.w; y++)
for (int x = uv.x; x < uv.z; x++)
dst->color[y * 256 + x] = clut.color[(x % 2) ? src.index[(y * 256 + x) / 2].b : src.index[(y * 256 + x) / 2].a];
for (int y = minY; y <= maxY; y++)
for (int x = minX; x <= maxX; x++)
dst.color[y * 256 + x] = clut.color[(x % 2) ? src.index[(y * 256 + x) / 2].b : src.index[(y * 256 + x) / 2].a];
}
for (int i = 0; i < spriteTexturesCount; i++) {
SpriteTexture &t = spriteTextures[i];
CLUT &clut = cluts[t.clut];
Tile32 &dst = tiles[t.tile];
Tile4 &src = tiles4[t.tile];
for (int y = t.texCoord[0].y; y <= t.texCoord[1].y; y++)
for (int x = t.texCoord[0].x; x <= t.texCoord[1].x; x += 2) {
dst.color[y * 256 + x + 0] = clut.color[src.index[(y * 256 + x) / 2].a];
dst.color[y * 256 + x + 1] = clut.color[src.index[(y * 256 + x) / 2].b];
}
}
break;
}
case VER_TR2_PC :
case VER_TR3_PC : {
ASSERT(tiles16);
for (int i = 0; i < tilesCount; i++) {
Color32 *ptr = &tiles[i].color[0];
for (int y = 0; y < 256; y++) {
for (int x = 0; x < 256; x++) {
Color32 c = tiles16[i].color[y * 256 + x];
ptr[x].r = c.b;
ptr[x].g = c.g;
ptr[x].b = c.r;
ptr[x].a = c.a;
}
ptr += 256;
}
}
break;
}
default : ASSERT(false);
}
delete[] tiles8;
delete[] tiles16;
tiles8 = NULL;
tiles16 = NULL;
}
// common methods
@@ -4121,7 +4123,7 @@ namespace TR {
int16 getModelIndex(Entity::Type type) const {
//#ifndef _DEBUG
if ((type >= Entity::AI_GUARD && type <= Entity::AI_CHECK) || (type >= Entity::GLOW_2 && type <= Entity::ENEMY_BAT_SWARM) || type == Entity::WATERFALL || type == Entity::KILL_ALL_TRIGGERS || type == Entity::VIEW_TARGET || type == Entity::SOUND_DOOR_BELL || type == Entity::SOUND_ALARM_BELL)
if ((type >= Entity::AI_GUARD && type <= Entity::AI_CHECK) || (type >= Entity::GLOW_2 && type <= Entity::ENEMY_BAT_SWARM) || type == Entity::WATERFALL || type == Entity::KILL_ALL_TRIGGERS || type == Entity::VIEW_TARGET || type == Entity::SOUND_DOOR_BELL || type == Entity::SOUND_ALARM_BELL || type == Entity::TRIPWIRE)
return 0;
//#endif

View File

@@ -915,7 +915,9 @@ struct Level : IGame {
}
}
static void fillCallback(int id, int width, int height, int tileX, int tileY, void *userData, void *data) {
TR::Tile32 *tileData;
static void fillCallback(int id, int tileX, int tileY, int atlasWidth, int atlasHeight, Atlas::Tile &tile, void *userData, void *data) {
static const uint32 barColor[UI::BAR_MAX][25] = {
// flash bar
{ 0x00000000, 0xFFA20058, 0xFFFFFFFF, 0xFFA20058, 0x00000000 },
@@ -936,27 +938,32 @@ struct Level : IGame {
int stride = 256, uvCount;
short2 *uv = NULL;
TR::Level *level = (TR::Level*)userData;
Level *owner = (Level*)userData;
TR::Level *level = &owner->level;
TR::Color32 *src, *dst = (TR::Color32*)data;
short4 mm;
if (id < level->objectTexturesCount) { // textures
TR::ObjectTexture &t = level->objectTextures[id];
mm = t.getMinMax();
src = level->tiles[t.tile.index].color;
uv = t.texCoord;
src = owner->tileData->color;
uv = t.texCoordAtlas;
uvCount = 4;
level->fillObjectTexture(owner->tileData, tile.uv, tile.tile, tile.clut);
} else {
id -= level->objectTexturesCount;
if (id < level->spriteTexturesCount) { // sprites
TR::SpriteTexture &t = level->spriteTextures[id];
mm = t.getMinMax();
src = level->tiles[t.tile].color;
uv = t.texCoord;
src = owner->tileData->color;
uv = t.texCoordAtlas;
uvCount = 2;
level->fillObjectTexture(owner->tileData, tile.uv, tile.tile, tile.clut);
} else { // common (generated) textures
id -= level->spriteTexturesCount;
TR::ObjectTexture *tex;
mm.x = mm.y = mm.z = mm.w = 0;
stride = 1;
@@ -982,7 +989,7 @@ struct Level : IGame {
}
memset(tex, 0, sizeof(*tex));
uv = tex->texCoord;
uv = tex->texCoordAtlas;
uv[2].y += mm.w;
uv[3].y += mm.w;
uv[1].x += mm.z;
@@ -995,15 +1002,17 @@ struct Level : IGame {
if (data) {
int w = mm.z - mm.x + 1;
int h = mm.w - mm.y + 1;
int dstIndex = tileY * width + tileX;
dst += tileY * atlasWidth + tileX;
for (int y = -ATLAS_BORDER; y < h + ATLAS_BORDER; y++) {
for (int x = -ATLAS_BORDER; x < w + ATLAS_BORDER; x++) {
TR::Color32 *p = &src[mm.y * stride + mm.x];
ASSERT((x + ATLAS_BORDER + tileX) >= 0 && (x + ATLAS_BORDER + tileX) < atlasWidth);
ASSERT((y + ATLAS_BORDER + tileY) >= 0 && (y + ATLAS_BORDER + tileY) < atlasHeight);
p += clamp(x, 0, w - 1);
p += clamp(y, 0, h - 1) * stride;
dst[dstIndex++] = *p;
dst[x + ATLAS_BORDER] = *p;
}
dstIndex += width - ATLAS_BORDER * 2 - w;
dst += atlasWidth;
}
cx += tileX + ATLAS_BORDER;
@@ -1016,8 +1025,8 @@ struct Level : IGame {
uv[i].x += cx;
uv[i].y += cy;
uv[i].x = int32(uv[i].x) * 32767 / width;
uv[i].y = int32(uv[i].y) * 32767 / height;
uv[i].x = int32(uv[i].x) * 32767 / atlasWidth;
uv[i].y = int32(uv[i].y) * 32767 / atlasHeight;
}
// apply ref for instanced tile
@@ -1025,11 +1034,11 @@ struct Level : IGame {
int ref = tileX;
if (ref < level->objectTexturesCount) { // textures
mm = level->objectTextures[ref].getMinMax();
mm = level->objectTextures[ref].getMinMaxAtlas();
} else {
ref -= level->objectTexturesCount;
if (ref < level->spriteTexturesCount) // sprites
mm = level->spriteTextures[ref].getMinMax();
mm = level->spriteTextures[ref].getMinMaxAtlas();
else
ASSERT(false); // only object textures and sprites may be instanced
}
@@ -1084,49 +1093,49 @@ struct Level : IGame {
#error atlas packing is not allowed for this platform
#endif
level.initTiles();
//dumpGlyphs();
int texIdx = (level.version & TR::VER_PSX) ? 256 : 0; // skip palette color for PSX version
int texIdx = 0;
// repack texture tiles
Atlas *tiles = new Atlas(level.objectTexturesCount + level.spriteTexturesCount + UI::BAR_MAX, &level, fillCallback);
Atlas *tiles = new Atlas(level.objectTexturesCount + level.spriteTexturesCount + UI::BAR_MAX, this, fillCallback);
// add textures
for (int i = texIdx; i < level.objectTexturesCount; i++) {
TR::ObjectTexture &t = level.objectTextures[i];
int16 tx = (t.tile.index % 4) * 256;
int16 ty = (t.tile.index / 4) * 256;
short4 uv;
uv.x = tx + min(min(t.texCoord[0].x, t.texCoord[1].x), t.texCoord[2].x);
uv.y = ty + min(min(t.texCoord[0].y, t.texCoord[1].y), t.texCoord[2].y);
uv.z = tx + max(max(t.texCoord[0].x, t.texCoord[1].x), t.texCoord[2].x) + 1;
uv.w = ty + max(max(t.texCoord[0].y, t.texCoord[1].y), t.texCoord[2].y) + 1;
uv.x = min(min(t.texCoord[0].x, t.texCoord[1].x), t.texCoord[2].x);
uv.y = min(min(t.texCoord[0].y, t.texCoord[1].y), t.texCoord[2].y);
uv.z = max(max(t.texCoord[0].x, t.texCoord[1].x), t.texCoord[2].x) + 1;
uv.w = max(max(t.texCoord[0].y, t.texCoord[1].y), t.texCoord[2].y) + 1;
tiles->add(uv, texIdx++);
tiles->add(texIdx++, uv, t.tile.index, t.clut);
}
// add sprites
for (int i = 0; i < level.spriteTexturesCount; i++) {
TR::SpriteTexture &t = level.spriteTextures[i];
int16 tx = (t.tile % 4) * 256;
int16 ty = (t.tile / 4) * 256;
short4 uv;
uv.x = tx + t.texCoord[0].x;
uv.y = ty + t.texCoord[0].y;
uv.z = tx + t.texCoord[1].x + 1;
uv.w = ty + t.texCoord[1].y + 1;
uv.x = t.texCoord[0].x;
uv.y = t.texCoord[0].y;
uv.z = t.texCoord[1].x + 1;
uv.w = t.texCoord[1].y + 1;
tiles->add(uv, texIdx++);
tiles->add(texIdx++, uv, t.tile, t.clut);
}
// add common textures
const short2 bar[UI::BAR_MAX] = { short2(0, 4), short2(0, 4), short2(0, 4), short2(4, 4), short2(0, 0) };
for (int i = 0; i < UI::BAR_MAX; i++)
tiles->add(short4(i * 32, 4096, i * 32 + bar[i].x, 4096 + bar[i].y), texIdx++);
tiles->add(texIdx++, short4(i * 32, 4096, i * 32 + bar[i].x, 4096 + bar[i].y));
// get result texture
tileData = new TR::Tile32();
atlas = tiles->pack();
delete[] tileData;
tileData = NULL;
atlas->setFilterQuality(Core::settings.detail.filter);
delete tiles;
@@ -1134,8 +1143,6 @@ struct Level : IGame {
LOG("atlas: %d x %d\n", atlas->width, atlas->height);
PROFILE_LABEL(TEXTURE, atlas->ID, "atlas");
delete[] level.tiles;
level.tiles = NULL;
#else
#ifdef _PSP
atlas = new Texture(level.tiles4, level.tilesCount, level.cluts, level.clutsCount);
@@ -1260,7 +1267,7 @@ struct Level : IGame {
Core::setBlending(bmNone);
Core::setDepthTest(false);
setShader(Core::pass, Shader::FLASH, false, false);
Core::active.shader->setParam(uMaterial, vec4(0.5f, 0.0f, 0.0f, 0.0f));
Core::active.shader->setParam(uMaterial, vec4(1.0f / 1.8f, 0.0f, 0.0f, 0.0f));
// anim.getJoints(Basis(quat(0, 0, 0, 1), vec3(0)), 0, false));//Basis(anim.getJointRot(0), vec3(0)));
Core::setBasis(&b, 1);

View File

@@ -625,7 +625,7 @@ struct MeshBuilder {
for (int i = 0; i < 9; i++) {
Vertex &v0 = vertices[vCount + i * 2 + 0];
v0.normal = short4( 0, -1, 0, 32767 );
v0.texCoord = short4( whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 );
v0.texCoord = short4( whiteTile.texCoordAtlas[0].x, whiteTile.texCoordAtlas[0].y, 32767, 32767 );
v0.color = v0.light = ubyte4( 0, 0, 0, 0 );
if (i == 8) {
@@ -702,7 +702,7 @@ struct MeshBuilder {
pos.rotate(cs);
v.coord = short4( short(pos.x), short(pos.y), 0, 0 );
v.normal = short4( 0, 0, 0, 32767 );
v.texCoord = short4( whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 );
v.texCoord = short4( whiteTile.texCoordAtlas[0].x, whiteTile.texCoordAtlas[0].y, 32767, 32767 );
v.color = ubyte4( 255, 255, 255, 255 );
v.light = ubyte4( 255, 255, 255, 255 );
@@ -995,7 +995,7 @@ struct MeshBuilder {
int count = triangle ? 3 : 4;
for (int i = 0; i < count; i++) {
Vertex &v = vertices[vCount + i];
v.texCoord = short4( tex->texCoord[i].x, tex->texCoord[i].y, 32767, 32767 );
v.texCoord = short4( tex->texCoordAtlas[i].x, tex->texCoordAtlas[i].y, 32767, 32767 );
}
if (((level->version & TR::VER_PSX)) && !triangle) // TODO: swap vertices instead of rectangle indices and vertices.texCoords (WRONG lighting in TR2!)
@@ -1142,10 +1142,10 @@ struct MeshBuilder {
quad[0].light = quad[1].light = ubyte4( tColor.r, tColor.g, tColor.b, tColor.a );
quad[2].light = quad[3].light = ubyte4( bColor.r, bColor.g, bColor.b, bColor.a );
quad[0].texCoord = short4( sprite.texCoord[0].x, sprite.texCoord[0].y, sprite.l, -sprite.t );
quad[1].texCoord = short4( sprite.texCoord[1].x, sprite.texCoord[0].y, sprite.r, -sprite.t );
quad[2].texCoord = short4( sprite.texCoord[1].x, sprite.texCoord[1].y, sprite.r, -sprite.b );
quad[3].texCoord = short4( sprite.texCoord[0].x, sprite.texCoord[1].y, sprite.l, -sprite.b );
quad[0].texCoord = short4( sprite.texCoordAtlas[0].x, sprite.texCoordAtlas[0].y, sprite.l, -sprite.t );
quad[1].texCoord = short4( sprite.texCoordAtlas[1].x, sprite.texCoordAtlas[0].y, sprite.r, -sprite.t );
quad[2].texCoord = short4( sprite.texCoordAtlas[1].x, sprite.texCoordAtlas[1].y, sprite.r, -sprite.b );
quad[3].texCoord = short4( sprite.texCoordAtlas[0].x, sprite.texCoordAtlas[1].y, sprite.l, -sprite.b );
vCount += 4;
}
@@ -1171,7 +1171,7 @@ struct MeshBuilder {
else
v.light = *((ubyte4*)&color);
short2 uv = tile.texCoord[i];
short2 uv = tile.texCoordAtlas[i];
v.texCoord = short4( uv.x, uv.y, 32767, 32767 );
}
@@ -1180,7 +1180,7 @@ struct MeshBuilder {
}
void addFrame(Index *indices, Vertex *vertices, int &iCount, int &vCount, const vec2 &pos, const vec2 &size, uint32 color1, uint32 color2) {
short4 uv = short4( whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 );
short4 uv = short4( whiteTile.texCoordAtlas[0].x, whiteTile.texCoordAtlas[0].y, 32767, 32767 );
int16 minX = int16(pos.x);
int16 minY = int16(pos.y);

View File

@@ -834,14 +834,21 @@ struct Texture {
#define ATLAS_BORDER 8
struct Atlas {
typedef void (Callback)(int id, int width, int height, int x, int y, void *userData, void *data);
struct Tile {
int32 id;
int16 tile;
int16 clut;
short4 uv;
} *tiles;
typedef void (Callback)(int id, int tileX, int tileY, int atalsWidth, int atlasHeight, Tile &tile, void *userData, void *data);
struct Node {
Node *child[2];
short4 rect;
int id;
int tileIndex;
Node(short l, short t, short r, short b) : rect(l, t, r, b), id(-1) {
Node(short l, short t, short r, short b) : rect(l, t, r, b), tileIndex(-1) {
child[0] = child[1] = NULL;
}
@@ -850,15 +857,15 @@ struct Atlas {
delete child[1];
}
Node* insert(const short4 &tile, int id) {
Node* insert(const short4 &tile, int tileIndex) {
ASSERT(tile.x != 0x7FFF);
if (child[0] != NULL && child[1] != NULL) {
Node *node = child[0]->insert(tile, id);
Node *node = child[0]->insert(tile, tileIndex);
if (node != NULL) return node;
return child[1]->insert(tile, id);
return child[1]->insert(tile, tileIndex);
} else {
if (this->id != -1)
if (this->tileIndex != -1)
return NULL;
int16 w = rect.z - rect.x;
@@ -870,7 +877,7 @@ struct Atlas {
return NULL;
if (w == tx && h == ty) {
this->id = id;
this->tileIndex = tileIndex;
return this;
}
@@ -885,16 +892,11 @@ struct Atlas {
child[1] = new Node(rect.x, rect.y + ty, rect.z, rect.w);
}
return child[0]->insert(tile, id);
return child[0]->insert(tile, tileIndex);
}
}
} *root;
struct Tile {
int id;
short4 uv;
} *tiles;
int tilesCount;
int size;
int width, height;
@@ -910,17 +912,19 @@ struct Atlas {
delete[] tiles;
}
void add(short4 uv, int id) {
void add(int32 id, short4 uv, int16 tile = 0, int16 clut = 0) {
for (int i = 0; i < tilesCount; i++)
if (tiles[i].uv == uv) {
if (tiles[i].uv == uv && tiles[i].tile == tile && tiles[i].clut == clut) {
uv.x = 0x7FFF;
uv.y = tiles[i].id;
uv.z = uv.w = 0;
break;
}
tiles[tilesCount].id = id;
tiles[tilesCount].uv = uv;
tiles[tilesCount].id = id;
tiles[tilesCount].tile = tile;
tiles[tilesCount].clut = clut;
tiles[tilesCount].uv = uv;
tilesCount++;
if (uv.x != 0x7FFF)
@@ -930,7 +934,7 @@ struct Atlas {
bool insertAll(int *indices) {
for (int i = 0; i < tilesCount; i++) {
int idx = indices[i];
if (tiles[idx].uv.x != 0x7FFF && !root->insert(tiles[idx].uv, tiles[idx].id))
if (tiles[idx].uv.x != 0x7FFF && !root->insert(tiles[idx].uv, idx))
return false;
}
return true;
@@ -965,7 +969,7 @@ struct Atlas {
// pack
while (1) {
delete root;
root = new Node(0, 0, width, height);
root = new Node(0, 0, width - 1, height - 1);
if (insertAll(indices))
break;
@@ -994,17 +998,17 @@ struct Atlas {
void fill(Node *node, void *data) {
if (!node) return;
if (node->id == -1) {
if (node->tileIndex == -1) {
fill(node->child[0], data);
fill(node->child[1], data);
} else
callback(node->id, width, height, node->rect.x, node->rect.y, userData, data);
callback(tiles[node->tileIndex].id, node->rect.x, node->rect.y, width, height, tiles[node->tileIndex], userData, data);
}
void fillInstances() {
for (int i = 0; i < tilesCount; i++)
if (tiles[i].uv.x == 0x7FFF)
callback(tiles[i].id, width, height, tiles[i].uv.y, 0, userData, NULL);
callback(tiles[i].id, tiles[i].uv.y, 0, width, height, tiles[i], userData, NULL);
}
};

View File

@@ -1099,7 +1099,7 @@ struct Lightning : Controller {
void setVertex(Vertex &v, const vec3 &coord, int16 joint, int idx) {
v.coord = toCoord(coord, joint);
v.normal = short4( 0, -1, 0, 0 );
v.texCoord = short4( barTile[0].texCoord[idx].x, barTile[0].texCoord[idx].y, 32767, 32767 );
v.texCoord = short4( barTile[0].texCoordAtlas[idx].x, barTile[0].texCoordAtlas[idx].y, 32767, 32767 );
v.color = ubyte4( 255, 255, 255, 255 );
}