Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TR3 PSX support #1366

Merged
merged 1 commit into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
578 changes: 480 additions & 98 deletions trlevel/Level.cpp

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions trlevel/Level.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,15 +176,21 @@ namespace trlevel

// New level bits:
void read_header(std::basic_ispanstream<uint8_t>& file, std::vector<uint8_t>& bytes, trview::Activity& activity, const LoadCallbacks& callbacks);
void read_object_textures_tr3_psx(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void read_palette_tr1(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void read_palette_tr2_3(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void read_room_textures_tr3_psx(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void read_textiles_tr1_pc(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void read_textiles_tr1_psx(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void read_textiles_tr3_psx(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void read_sprite_textures_psx(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void adjust_room_textures_psx();

void load_tr1_pc(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void load_tr1_psx(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void load_tr2_pc(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void load_tr3_pc(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void load_tr3_psx(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void load_tr4_pc(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void load_tr4_pc_remastered(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
void load_tr5_pc(std::basic_ispanstream<uint8_t>& file, trview::Activity& activity, const LoadCallbacks& callbacks);
Expand All @@ -195,6 +201,10 @@ namespace trlevel
std::optional<std::vector<uint8_t>> load_main_sfx() const;
void load_ngle_sound_fx(trview::Activity& activity, std::basic_ispanstream<uint8_t>& file, const LoadCallbacks& callbacks);

void generate_mesh(tr_mesh& mesh, std::basic_ispanstream<uint8_t>& stream);
void generate_mesh_tr1_psx(tr_mesh& mesh, std::basic_ispanstream<uint8_t>& stream);
void generate_mesh_tr3_psx(tr_mesh& mesh, std::basic_ispanstream<uint8_t>& stream);

PlatformAndVersion _platform_and_version;

std::vector<tr_colour> _palette;
Expand Down
24 changes: 24 additions & 0 deletions trlevel/tr_rooms.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "tr_rooms.h"
#include <ranges>

using namespace DirectX::SimpleMath;

Expand Down Expand Up @@ -90,4 +91,27 @@ namespace trlevel
});
return new_vertices;
}

std::vector<trview_room_vertex> convert_vertices_tr3_psx(std::vector<uint32_t> vertices, int32_t y_top)
{
return vertices |
std::views::transform([=](auto&& v)
{
const uint16_t colour = (v >> 15) & 0x7fff;
return trview_room_vertex
{
.vertex =
{
.x = static_cast<int16_t>(((v >> 10) & 0x1f) << 10),
.y = static_cast<int16_t>((((v >> 5) & 0x1f) << 8) + y_top),
.z = static_cast<int16_t>((v & 0x1f) << 10)
},
.attributes = 0,
.colour = trview::Colour(
static_cast<float>((colour & 0x1F)) / 0x1F,
static_cast<float>(((colour >> 5) & 0x1F)) / 0x1F,
static_cast<float>(((colour >> 10) & 0x1F)) / 0x1F)
};
}) | std::ranges::to<std::vector>();
}
}
1 change: 1 addition & 0 deletions trlevel/tr_rooms.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ namespace trlevel
std::vector<trview_room_vertex> convert_vertices(std::vector<tr2_room_vertex> vertices);
std::vector<trview_room_vertex> convert_vertices(std::vector<tr3_room_vertex> vertices);
std::vector<trview_room_vertex> convert_vertices(std::vector<tr5_room_vertex> vertices);
std::vector<trview_room_vertex> convert_vertices_tr3_psx(std::vector<uint32_t> vertices, int32_t y_top);
}
19 changes: 19 additions & 0 deletions trlevel/trtypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,23 @@ namespace trlevel
| std::views::transform([](const auto& vert) { return tr_vertex{ vert.x, vert.y, vert.z }; })
| std::ranges::to<std::vector>();
}

std::vector<tr4_mesh_face3> convert_tr3_psx_room_triangles(std::vector<uint32_t> triangles, uint16_t total_vertices)
{
return triangles |
std::views::transform([=](const auto& t)
{
return tr4_mesh_face3
{
.vertices =
{
static_cast<uint16_t>(total_vertices + (t & 0x7f)),
static_cast<uint16_t>(total_vertices + ((t >> 7) & 0x7f)),
static_cast<uint16_t>(total_vertices + ((t >> 14) & 0x7f))
},
.texture = static_cast<uint16_t>(t >> 21),
.effects = 0
};
}) | std::ranges::to<std::vector>();
}
}
7 changes: 5 additions & 2 deletions trlevel/trtypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -478,10 +478,11 @@ namespace trlevel
uint16_t Clut;
uint8_t x1;
uint8_t y1;
uint16_t Tile : 14, : 2;
uint16_t Tile;
uint8_t x2;
uint8_t y2;
uint16_t Unknown;
uint8_t tri_draw;
uint8_t quad_draw;
uint8_t x3;
uint8_t y3;
uint16_t Attribute;
Expand Down Expand Up @@ -865,6 +866,8 @@ namespace trlevel

std::vector<tr_vertex> convert_vertices(std::vector<tr_vertex_psx> vertices);

std::vector<tr4_mesh_face3> convert_tr3_psx_room_triangles(std::vector<uint32_t> triangles, uint16_t total_vertices);

constexpr LightType convert(LightTypeTR3 type);
}

Expand Down
10 changes: 7 additions & 3 deletions trview.app/Elements/Item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,11 @@ namespace trview
const uint32_t end_pointer = static_cast<uint32_t>(model.StartingMesh + model.NumMeshes);
for (uint32_t mesh_pointer = model.StartingMesh; mesh_pointer < end_pointer; ++mesh_pointer)
{
_meshes.push_back(mesh_storage.mesh(mesh_pointer));
auto mesh = mesh_storage.mesh(mesh_pointer);
if (mesh)
{
_meshes.push_back(mesh);
}
}
}
}
Expand Down Expand Up @@ -225,7 +229,7 @@ namespace trview
{
for (const auto& triangle : _meshes[i]->transparent_triangles())
{
transparency.add(triangle.transform(_world_transforms[i] * _world, colour));
transparency.add(triangle.transform(_world_transforms[i] * _world, colour, true));
}
}

Expand All @@ -234,7 +238,7 @@ namespace trview
auto world = create_billboard(_position, _offset, _scale, camera);
for (const auto& triangle : _sprite_mesh->transparent_triangles())
{
transparency.add(triangle.transform(world, colour));
transparency.add(triangle.transform(world, colour, true));
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions trview.app/Elements/Room.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ namespace trview
{
for (const auto& triangle : _mesh->transparent_triangles())
{
transparency.add(triangle.transform(_room_offset, colour));
transparency.add(triangle.transform(_room_offset, colour, !has_flag(render_filter, RenderFilter::Lighting)));
}

for (const auto& static_mesh : _static_meshes)
Expand Down Expand Up @@ -656,7 +656,7 @@ namespace trview

const auto add_tri = [&triangles](const Vector3& v0, const Vector3& v1, const Vector3& v2)
{
triangles.push_back(TransparentTriangle(v0, v1, v2, ITrigger::Trigger_Colour));
triangles.push_back(TransparentTriangle(v0, v1, v2, ITrigger::Trigger_Colour, ITrigger::Trigger_Colour, ITrigger::Trigger_Colour));
};

// + Y
Expand Down
17 changes: 16 additions & 1 deletion trview.app/Elements/StaticMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ namespace trview

void StaticMesh::render(const ICamera& camera, const ILevelTextureStorage& texture_storage, const DirectX::SimpleMath::Color& colour)
{
if (!_mesh)
{
return;
}

if (_type == Type::Sprite)
{
_world = create_billboard(_position, Vector3(0, -0.5f, 0), _scale, camera);
Expand All @@ -68,19 +73,29 @@ namespace trview

void StaticMesh::get_transparent_triangles(ITransparencyBuffer& transparency, const ICamera& camera, const DirectX::SimpleMath::Color& colour)
{
if (!_mesh)
{
return;
}

if (_type == Type::Sprite)
{
_world = create_billboard(_position, Vector3(0, -0.5f, 0), _scale, camera);
}

for (const auto& triangle : _mesh->transparent_triangles())
{
transparency.add(triangle.transform(_world, colour));
transparency.add(triangle.transform(_world, colour, true));
}
}

PickResult StaticMesh::pick(const DirectX::SimpleMath::Vector3& position, const DirectX::SimpleMath::Vector3& direction) const
{
if (!_mesh)
{
return {};
}

const auto transform = _world.Invert();
auto normal_direction = Vector3::TransformNormal(direction, transform);
normal_direction.Normalize();
Expand Down
2 changes: 1 addition & 1 deletion trview.app/Elements/Trigger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ namespace trview
using namespace DirectX::SimpleMath;
for (auto& triangle : _mesh->transparent_triangles())
{
transparency.add(triangle.transform(Matrix::Identity, colour));
transparency.add(triangle.transform(Matrix::Identity, colour, true));
}
}

Expand Down
21 changes: 13 additions & 8 deletions trview.app/Geometry/IMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ namespace trview

std::vector<TransparentTriangle> transparent_triangles
{
{ vertices[0].pos, vertices[1].pos, vertices[2].pos, vertices[0].uv, vertices[1].uv, vertices[2].uv, tile, TransparentTriangle::Mode::Normal },
{ vertices[2].pos, vertices[1].pos, vertices[3].pos, vertices[2].uv, vertices[1].uv, vertices[3].uv, tile, TransparentTriangle::Mode::Normal },
{ vertices[0].pos, vertices[1].pos, vertices[2].pos, vertices[0].uv, vertices[1].uv, vertices[2].uv, tile, TransparentTriangle::Mode::Normal, Colour::White, Colour::White, Colour::White },
{ vertices[2].pos, vertices[1].pos, vertices[3].pos, vertices[2].uv, vertices[1].uv, vertices[3].uv, tile, TransparentTriangle::Mode::Normal, Colour::White, Colour::White, Colour::White },
};

std::vector<Triangle> collision_triangles
Expand Down Expand Up @@ -264,13 +264,18 @@ namespace trview
uvs[i] = texture_storage.uv(texture, i);
}

if (texture_storage.platform_and_version().platform == Platform::PSX)
{
std::swap(uvs[2], uvs[3]);
}

const bool double_sided = rect.texture & 0x8000;

TransparentTriangle::Mode transparency_mode;
if (determine_transparency(texture_storage.attribute(texture), rect.effects, transparency_mode))
{
transparent_triangles.emplace_back(verts[0], verts[1], verts[2], uvs[0], uvs[1], uvs[2], texture_storage.tile(texture), transparency_mode);
transparent_triangles.emplace_back(verts[2], verts[3], verts[0], uvs[2], uvs[3], uvs[0], texture_storage.tile(texture), transparency_mode);
transparent_triangles.emplace_back(verts[0], verts[1], verts[2], uvs[0], uvs[1], uvs[2], texture_storage.tile(texture), transparency_mode, colors[0], colors[1], colors[2]);
transparent_triangles.emplace_back(verts[2], verts[3], verts[0], uvs[2], uvs[3], uvs[0], texture_storage.tile(texture), transparency_mode, colors[2], colors[3], colors[0]);
if (transparent_collision)
{
collision_triangles.emplace_back(verts[0], verts[1], verts[2]);
Expand All @@ -280,8 +285,8 @@ namespace trview
if (double_sided)
{

transparent_triangles.emplace_back(verts[2], verts[1], verts[0], uvs[2], uvs[1], uvs[0], texture_storage.tile(texture), transparency_mode);
transparent_triangles.emplace_back(verts[0], verts[3], verts[2], uvs[0], uvs[3], uvs[2], texture_storage.tile(texture), transparency_mode);
transparent_triangles.emplace_back(verts[2], verts[1], verts[0], uvs[2], uvs[1], uvs[0], texture_storage.tile(texture), transparency_mode, colors[2], colors[1], colors[0]);
transparent_triangles.emplace_back(verts[0], verts[3], verts[2], uvs[0], uvs[3], uvs[2], texture_storage.tile(texture), transparency_mode, colors[0], colors[3], colors[2]);
if (transparent_collision)
{
collision_triangles.emplace_back(verts[2], verts[1], verts[0]);
Expand Down Expand Up @@ -365,14 +370,14 @@ namespace trview
TransparentTriangle::Mode transparency_mode;
if (determine_transparency(texture_storage.attribute(texture), tri.effects, transparency_mode))
{
transparent_triangles.emplace_back(verts[0], verts[1], verts[2], uvs[0], uvs[1], uvs[2], texture_storage.tile(texture), transparency_mode);
transparent_triangles.emplace_back(verts[0], verts[1], verts[2], uvs[0], uvs[1], uvs[2], texture_storage.tile(texture), transparency_mode, colors[0], colors[1], colors[2]);
if (transparent_collision)
{
collision_triangles.emplace_back(verts[0], verts[1], verts[2]);
}
if (double_sided)
{
transparent_triangles.emplace_back(verts[2], verts[1], verts[0], uvs[2], uvs[1], uvs[0], texture_storage.tile(texture), transparency_mode);
transparent_triangles.emplace_back(verts[2], verts[1], verts[0], uvs[2], uvs[1], uvs[0], texture_storage.tile(texture), transparency_mode, colors[2], colors[1], colors[0]);
if (transparent_collision)
{
collision_triangles.emplace_back(verts[2], verts[1], verts[0]);
Expand Down
2 changes: 1 addition & 1 deletion trview.app/Geometry/TransparencyBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ namespace trview
auto normal = triangle.normal();
for (uint32_t i = 0; i < 3; ++i)
{
_vertices[index++] = { triangle.vertices[i], normal, triangle.uvs[i], triangle.colour };
_vertices[index++] = { triangle.vertices[i], normal, triangle.uvs[i], triangle.colours[i] };
}
}

Expand Down
15 changes: 9 additions & 6 deletions trview.app/Geometry/TransparentTriangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ using namespace DirectX::SimpleMath;

namespace trview
{
TransparentTriangle::TransparentTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Vector2& uv0, const Vector2& uv1, const Vector2& uv2, uint32_t texture, Mode mode, Color colour)
: vertices{ v0, v1, v2 }, uvs{ uv0, uv1, uv2 }, texture(texture), mode(mode), colour(colour)
TransparentTriangle::TransparentTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Vector2& uv0, const Vector2& uv1, const Vector2& uv2, uint32_t texture, Mode mode, const DirectX::SimpleMath::Color& c0, const DirectX::SimpleMath::Color& c1, const DirectX::SimpleMath::Color& c2)
: vertices{ v0, v1, v2 }, uvs{ uv0, uv1, uv2 }, texture(texture), mode(mode), colours{ c0, c1, c2 }
{
Vector3 minimum = Vector3::Min(Vector3::Min(v0, v1), v2);
Vector3 maximum = Vector3::Max(Vector3::Max(v0, v1), v2);
position = Vector3::Lerp(minimum, maximum, 0.5f);
}

TransparentTriangle::TransparentTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Color& colour)
: TransparentTriangle(v0, v1, v2, Vector2::Zero, Vector2::Zero, Vector2::Zero, Untextured, Mode::Normal, colour)
TransparentTriangle::TransparentTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const DirectX::SimpleMath::Color& c0, const DirectX::SimpleMath::Color& c1, const DirectX::SimpleMath::Color& c2)
: TransparentTriangle(v0, v1, v2, Vector2::Zero, Vector2::Zero, Vector2::Zero, Untextured, Mode::Normal, c0, c1, c2)
{
}

Expand All @@ -26,14 +26,17 @@ namespace trview
return first.Cross(second);
}

TransparentTriangle TransparentTriangle::transform(const DirectX::SimpleMath::Matrix& matrix, const DirectX::SimpleMath::Color& colour_override) const
TransparentTriangle TransparentTriangle::transform(const DirectX::SimpleMath::Matrix& matrix, const DirectX::SimpleMath::Color& colour_override, bool use_colour_override) const
{
using namespace DirectX::SimpleMath;
return TransparentTriangle(
Vector3::Transform(vertices[0], matrix),
Vector3::Transform(vertices[1], matrix),
Vector3::Transform(vertices[2], matrix),
uvs[0], uvs[1], uvs[2], texture, mode, colour_override);
uvs[0], uvs[1], uvs[2], texture, mode,
use_colour_override ? colour_override : colours[0],
use_colour_override ? colour_override : colours[1],
use_colour_override ? colour_override : colours[2]);
}

// Determine whether the face should be transparent give the attribute and effects values. The
Expand Down
8 changes: 4 additions & 4 deletions trview.app/Geometry/TransparentTriangle.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@ namespace trview

TransparentTriangle(const DirectX::SimpleMath::Vector3& v0, const DirectX::SimpleMath::Vector3& v1, const DirectX::SimpleMath::Vector3& v2,
const DirectX::SimpleMath::Vector2& uv0, const DirectX::SimpleMath::Vector2& uv1, const DirectX::SimpleMath::Vector2& uv2,
uint32_t texture, Mode mode, DirectX::SimpleMath::Color = { 1,1,1,1 });
uint32_t texture, Mode mode, const DirectX::SimpleMath::Color& c0, const DirectX::SimpleMath::Color& c1, const DirectX::SimpleMath::Color& c2);

/// Create an untextured transparent triangle with the specified colour.
/// @param v0 The first vertex.
/// @param v1 The second vertex.
/// @param v2 The third vertex.
/// @param colour The colour for the triangle.
TransparentTriangle(const DirectX::SimpleMath::Vector3& v0, const DirectX::SimpleMath::Vector3& v1, const DirectX::SimpleMath::Vector3& v2, const DirectX::SimpleMath::Color& colour);
TransparentTriangle(const DirectX::SimpleMath::Vector3& v0, const DirectX::SimpleMath::Vector3& v1, const DirectX::SimpleMath::Vector3& v2, const DirectX::SimpleMath::Color& c0, const DirectX::SimpleMath::Color& c1, const DirectX::SimpleMath::Color& c2);

DirectX::SimpleMath::Vector3 normal() const;

TransparentTriangle transform(const DirectX::SimpleMath::Matrix& matrix, const DirectX::SimpleMath::Color& colour_override) const;
TransparentTriangle transform(const DirectX::SimpleMath::Matrix& matrix, const DirectX::SimpleMath::Color& colour_override, bool use_colour_override) const;

// The world space positions that make up the triangle.
DirectX::SimpleMath::Vector3 vertices[3];
Expand All @@ -41,7 +41,7 @@ namespace trview

Mode mode;

DirectX::SimpleMath::Color colour{ 1, 1, 1, 1 };
DirectX::SimpleMath::Color colours[3];
};

// Determine whether the face should be transparent give the attribute and effects values. The
Expand Down
2 changes: 2 additions & 0 deletions trview.app/Graphics/ILevelTextureStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ namespace trview
virtual graphics::Texture geometry_texture() const = 0;

virtual uint32_t num_object_textures() const = 0;

virtual trlevel::PlatformAndVersion platform_and_version() const = 0;
};
}

Loading