Important information: this site is currently scheduled to go offline indefinitely by end of the year.

Tomb Raider Legend/Anniversary

Post questions about game models here, or help out others!
Greg5
n00b
Posts: 12
Joined: Sun Jun 19, 2022 1:00 am

Re: Tomb Raider Legend/Anniversary

Post by Greg5 »

Raq wrote: Thu Jul 28, 2022 5:37 pm First public release of the exporter is here!

After a lot of testing by me and some modders from the Tomb Raider Modding Discord server, the first public release of the exporter is now available. I will also update the first post of the thread.
Wow! Awesome job! I've tested it a bit and was able to load higher poly model in game (increased vertice count for one mesh and edited it a bit).

Image

Are you planning to add support for other models? If I change Anniversary model, I would like to also change frontend_lara and c_lara (model shown in outfit selection menu) for consistency. There is also cine_lara (cinematic?) bit I'm not sure if it's actually used.

The other outfit models would be nice too.


Also there is a bug in the exporter (or it is Noesis feature) that sometimes a number is added to mesh name when loading to Noesis from .fbx, so you end up with mesh name like "0000_Mesh_20_tpageid_2826256_dg_27" instead "Mesh_20_tpageid_2826256_dg_27" and exporter fails with error:
ERROR: The mesh name of one or more of your meshes is written incorrectly

For now I have "fixed" it like this:

Code: Select all

splitted = mesh.name.split("_")

if len(splitted) == 7:
    del splitted[0]
    
if len(splitted) != 6:
    print ("\nERROR: The mesh name of one or more of your meshes is written incorrectly")
    print(mesh.name)
    return 0
But you might want to look at it.
SwiftStarz
ultra-n00b
Posts: 2
Joined: Sat Jul 30, 2022 3:12 am
Has thanked: 2 times

Re: Tomb Raider Legend/Anniversary

Post by SwiftStarz »

Wow, this is wonderful. Great job.

I am curious if you have looked at the Wii version at all? A couple months ago I looked at some of the tools and tried to piece together an extractor. I think I got the files extracted out of the DRM but not much further than that. I'll take a look at your scripts and see if it might be modifiable for the Wii version.

My end goal is to extract all the textures from the game via a tool (along with some post processing). If you have any details that would help, please let me know. Thanks again!
User avatar
Raq
beginner
Posts: 22
Joined: Sat Nov 16, 2019 6:04 pm
Has thanked: 2 times
Been thanked: 10 times

Re: Tomb Raider Legend/Anniversary

Post by Raq »

Greg5 wrote: Sat Jul 30, 2022 1:20 pm
Also there is a bug in the exporter (or it is Noesis feature) that sometimes a number is added to mesh name when loading to Noesis from .fbx, so you end up with mesh name like "0000_Mesh_20_tpageid_2826256_dg_27" instead "Mesh_20_tpageid_2826256_dg_27" and exporter fails with error:
ERROR: The mesh name of one or more of your meshes is written incorrectly

For now I have "fixed" it like this:

Code: Select all

splitted = mesh.name.split("_")

if len(splitted) == 7:
    del splitted[0]
    
if len(splitted) != 6:
    print ("\nERROR: The mesh name of one or more of your meshes is written incorrectly")
    print(mesh.name)
    return 0
But you might want to look at it.
This is not an issue with the exporter. Noesis automatically adds that prefix whenever it encounters two or more meshes named exactly the same.
SwiftStarz wrote: Sat Jul 30, 2022 6:59 pm Wow, this is wonderful. Great job.

I am curious if you have looked at the Wii version at all? A couple months ago I looked at some of the tools and tried to piece together an extractor. I think I got the files extracted out of the DRM but not much further than that. I'll take a look at your scripts and see if it might be modifiable for the Wii version.

My end goal is to extract all the textures from the game via a tool (along with some post processing). If you have any details that would help, please let me know. Thanks again!
I remember looking at Wii texture format but I couldn't make sense out of it. Consoles always have some weird compressions. If you research it and manage to understand them I'll gladly add support for them on the script.
Greg5
n00b
Posts: 12
Joined: Sun Jun 19, 2022 1:00 am

Re: Tomb Raider Legend/Anniversary

Post by Greg5 »

Raq wrote: Sun Jul 31, 2022 7:32 am
Greg5 wrote: Sat Jul 30, 2022 1:20 pm
Also there is a bug in the exporter (or it is Noesis feature) that sometimes a number is added to mesh name when loading to Noesis from .fbx, so you end up with mesh name like "0000_Mesh_20_tpageid_2826256_dg_27" instead "Mesh_20_tpageid_2826256_dg_27" and exporter fails with error:
ERROR: The mesh name of one or more of your meshes is written incorrectly

For now I have "fixed" it like this:

Code: Select all

splitted = mesh.name.split("_")

if len(splitted) == 7:
    del splitted[0]
    
if len(splitted) != 6:
    print ("\nERROR: The mesh name of one or more of your meshes is written incorrectly")
    print(mesh.name)
    return 0
But you might want to look at it.
This is not an issue with the exporter. Noesis automatically adds that prefix whenever it encounters two or more meshes named exactly the same.

It must be adding that prefix on some other occassions as well. I had 21 uniquely named meshes and it added prefix to all of them. This happens sometimes only. I think Noesis splits the mesh into two if one is too high poly... :[ Edit: Yeah, the one I edited is shown in Noesis twice and splitted... But anyway, with the fix I provided model works even if .gnc has extra mesh... [roll]


Also I tried yesterday to convert lara_classic from Legend and I couldn't get it working. After trying to move even single mesh from legend over tra skeleton (regardles if I transfer weights from original mesh to Legend one or not) modded meshes are complete mess.

Did anyone have success converting Legend models? Maybe with copying skeleton it would work? - They are nearly the same anyway.
User avatar
AxelNoir
mega-veteran
mega-veteran
Posts: 243
Joined: Fri Feb 02, 2018 9:24 pm
Has thanked: 6 times
Been thanked: 14 times

Re: Tomb Raider Legend/Anniversary

Post by AxelNoir »

Think you could make an exporter for Tomb Raider: Underworld too?
User avatar
Raq
beginner
Posts: 22
Joined: Sat Nov 16, 2019 6:04 pm
Has thanked: 2 times
Been thanked: 10 times

Re: Tomb Raider Legend/Anniversary

Post by Raq »

AxelNoir wrote: Thu Aug 11, 2022 12:11 am Think you could make an exporter for Tomb Raider: Underworld too?
Underworld's format is an absolute chore, but maybe I will look more into it in the future. For now I will just keep improving the exporter for Legend and Anniversary
Paliha
beginner
Posts: 38
Joined: Thu Nov 12, 2015 1:10 pm
Has thanked: 2 times
Been thanked: 1 time

Re: Tomb Raider Legend/Anniversary

Post by Paliha »

Good work. A little bit crooked exporting graphics, takes all the graphics resources from the container, although the texture on the model is original.
Question.

meshStream.Position = 0x4;
int bone_count1 = meshStream.ReadValueS32();
MeshBone[] bones = new MeshBone[bone_count1];
int bone_count2 = meshStream.ReadValueS32();
int bone_Offset = meshStream.ReadValueS32();
float scaleX = meshStream.ReadValueF32();
float scaleY = meshStream.ReadValueF32();
float scaleZ = meshStream.ReadValueF32();
meshStream.Position += 0x04;
int BlokVertexCount = meshStream.ReadValueS32();
int BlokVertexOffset = meshStream.ReadValueS32();
meshStream.Position = 0x58;
int FaceOffset = meshStream.ReadValueS32();

for (int boneID = 0; boneID < bone_count1; boneID++)
{
bones[boneID] = new MeshBone(boneID);
}
int z = 0;
foreach (MeshBone bone in bones)
{
meshStream.Position = bone_Offset + z * 0x40 + 0x20;
float dx = meshStream.ReadValueF32();
float dy = meshStream.ReadValueF32();
float dz = meshStream.ReadValueF32();
meshStream.Position += 12;
int parentID = meshStream.ReadValueS32();
bone.relPosition = new MeshBone.Point(dx, dy, dz);
if (parentID >= 0)
{
bone.parent = bones[parentID];
}
z++;
}
foreach (MeshBone bone in bones)
{
bone.CalculateAbsPosition();
MeshBone parent = bone.parent;
if (parent != null)
{
parent.children.Add(bone);
}
}

meshStream.Position = BlokVertexOffset;
for (int i = 0; i < BlokVertexCount; i++)
{
float vX = meshStream.ReadValueS16() * scaleX;//point
float vY = meshStream.ReadValueS16() * scaleY;//point
float vZ = meshStream.ReadValueS16() * scaleZ;//point
float nX = meshStream.ReadValueS8() / 128.0f;//normaly
float nY = meshStream.ReadValueS8() / 128.0f;//normaly
float nZ = meshStream.ReadValueS8() / 128.0f;//normaly
meshStream.ReadValueS8();
int segment = meshStream.ReadValueS16();
float tU = BitConverter.ToSingle(BitConverter.GetBytes(meshStream.ReadValueU16() << 16), 0);//texture
float tV = BitConverter.ToSingle(BitConverter.GetBytes(meshStream.ReadValueU16() << 16), 0);//texture
long backpos = meshStream.Position;
if (segment >= bone_count1)
{
meshStream.Position = bone_Offset + segment * 0x40 + 0x38;
segment = meshStream.ReadValueS16();
}
if(segment < bone_count1)
{
var absPos = bones[segment].AbsPosition;
vX += absPos.x;
vY += absPos.y;
vZ += absPos.z;
}
meshStream.Position = backpos;

temp = string.Format("v {0:0.000000} {1:0.000000} {2:0.000000}", vX, vZ, -vY);
temp = temp.Replace(",", ".");
linev.Add(temp);
temp = string.Format("vn {0:0.000000} {1:0.000000} {2:0.000000}", nX, nY, nZ);
temp = temp.Replace(",", ".");
linevn.Add(temp);
temp = string.Format("vt {0:0.000000} {1:0.000000} 0.000000", tU, tV);
temp = temp.Replace(",", ".");
linevt.Add(temp);
}
meshStream.Position = FaceData;
var blokcount = meshStream.ReadValueS16()/3;
meshStream.Position += 2;
while (blokcount != 0)
{
int groupid = meshStream.ReadValueU8();
Indices.Add(string.Format("usemtl Texture_{0}", groupid));
meshStream.Position += 0x0B;
int nextpos = meshStream.ReadValueS32();
for (int i = 0; i < blokcount; i++)
{
ushort IndexX = meshStream.ReadValueU16();
ushort IndexY = meshStream.ReadValueU16();
ushort IndexZ = meshStream.ReadValueU16();
Indices.Add(string.Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}", IndexX + 1, IndexY + 1, IndexZ + 1));
}
meshStream.Position = nextpos;
blokcount = meshStream.ReadValueS16()/3;
meshStream.Position += 2;
}
----------

Image
Last edited by Paliha on Sun Apr 09, 2023 11:50 am, edited 2 times in total.
gamer1977
beginner
Posts: 39
Joined: Thu Feb 28, 2019 6:23 am
Has thanked: 22 times
Been thanked: 3 times

Re: Tomb Raider Legend/Anniversary

Post by gamer1977 »

Paliha wrote: Tue Apr 04, 2023 11:39 am Good work. A little bit crooked exporting graphics, takes all the graphics resources from the container, although the texture on the model is original.
Question. Without a script, in c or c++ or c#, how to transform points, normals, uvs coordinates and indices? The skeleton is not needed.
This code gives a distorted rendering result:
meshStream.Position = 0x10;
float scaleX = meshStream.ReadValueF32(Endian.Little);
float scaleY = meshStream.ReadValueF32(Endian.Little);
float scaleZ = meshStream.ReadValueF32(Endian.Little);
meshStream.Position = 0x20;
int BlokVertexCount = meshStream.ReadValueS32();
int BlokVertexOffset = meshStream.ReadValueS32();
meshStream.Position = 0x58;
int FaceData = meshStream.ReadValueS32();
meshStream.Position = BlokVertexOffset;
for (int i = 0; i < BlokVertexCount; i++)
{
float vX = meshStream.ReadValueS16() * scaleX;//point
float vY = meshStream.ReadValueS16() * scaleY;//point
float vZ = meshStream.ReadValueS16() * scaleZ;//point
//var vec = new System.Numerics.Vector3(vX, vZ, vY);

double nx = ReadNormaly(meshStream);
double ny = ReadNormaly(meshStream);
double nz = ReadNormaly(meshStream);
double length = Math.Sqrt(nx * nx + ny * ny + nz * nz);
float nX = Convert.ToSingle(nx / length);//normaly
float nY = Convert.ToSingle(ny / length);//normaly
float nZ = Convert.ToSingle(nz / length);//normaly
meshStream.ReadValueS8();
int segment = meshStream.ReadValueS16();
float tU = meshStream.ReadValueS16()/ 512.0f;//texture
float tV = meshStream.ReadValueS16()/ 512.0f;//texture

temp = string.Format("v {0:0.000000} {1:0.000000} {2:0.000000}", vX, vZ, -vY);
temp = temp.Replace(",", ".");
linev.Add(temp);
temp = string.Format("vn {0:0.000000} {1:0.000000} {2:0.000000}", nX, nY, nZ);
temp = temp.Replace(",", ".");
linevn.Add(temp);
temp = string.Format("vt {0:0.000000} {1:0.000000} 0.000000", tU, tV);
temp = temp.Replace(",", ".");
linevt.Add(temp);
}
meshStream.Position = FaceData;
var blokcount = meshStream.ReadValueS16()/3;
meshStream.Position += 2;
while (blokcount != 0)
{
int groupid = meshStream.ReadValueU8();
Indices.Add(string.Format("usemtl Texture_{0}", groupid));
meshStream.Position += 0x0B;
int nextpos = meshStream.ReadValueS32();
for (int i = 0; i < blokcount; i++)
{
ushort IndexX = meshStream.ReadValueU16();
ushort IndexY = meshStream.ReadValueU16();
ushort IndexZ = meshStream.ReadValueU16();
Indices.Add(string.Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}", IndexX + 1, IndexY + 1, IndexZ + 1));
}
meshStream.Position = nextpos;
blokcount = meshStream.ReadValueS16()/3;
meshStream.Position += 2;
}
----------
If there is a working solution, I can help later with parsing containers, animation resources, media, models, textures and levels, for the entire Tomb Raider(PC) line of games to choose from.

Image
Hi can you share this tools
Paliha
beginner
Posts: 38
Joined: Thu Nov 12, 2015 1:10 pm
Has thanked: 2 times
Been thanked: 1 time

Re: Tomb Raider Legend/Anniversary

Post by Paliha »

<< Hi can you share this tools
There is too much not yet done to put the drm viewer out to the public.
With the models for this edition, all the problems are solved.
pepapoha
n00b
Posts: 16
Joined: Wed May 20, 2020 2:49 pm

Re: Tomb Raider Legend/Anniversary

Post by pepapoha »

Hello guys I am download steam this is game but how to extract files any help ?
User avatar
Raq
beginner
Posts: 22
Joined: Sat Nov 16, 2019 6:04 pm
Has thanked: 2 times
Been thanked: 10 times

Re: Tomb Raider Legend/Anniversary

Post by Raq »

Paliha wrote: Tue Apr 04, 2023 11:39 am Image
I'm super late to the party, but this looks amazing! Great work!
Post Reply