Code:
unit LtbFile;
interface
uses Contnrs, SysUtils;
const
LTB_OK = 0;
const
LTB_FAIL = 1;
const
//-----------------------------------------------
// Offsets.
//-----------------------------------------------
LTB_FILEOFFSET_COMLINELENGTH = 84; // Command line length description.
LTB_FILEOFFSET_MESHCOUNT = 86 + 8; // Mesh count description.
LTB_FILEOFFSET_FIRSTMESH = 86 + 12; // Beginning of the first mesh.
LTB_MESHOFFSET_VERTEXCOUNT = 49; // Vertex count.
LTB_MESHOFFSET_FACECOUNT = 53; // Face count.
LTB_MESHOFFSET_MESHTYPE_HEAD = 57; // Mesh header.
LTB_MESHOFFSET_MESHTYPE = 61; // Mesh type. 1 - not skinned;
// 2 - extra float;
// 4 & 3 - skinned;
LTB_MESHOFFSET_FIRSTVERTEX = 83; // First vertex in mesh.
LTB_MESHOFFSET_FIRSTVERTEX_DS = 85; // First vertex in mesh (not skinned).
//-----------------------------------------------
// Mesh type.
//-----------------------------------------------
LTB_MESHTYPE_NOTSKINNED = 1;
LTB_MESHTYPE_EXTRAFLOAT = 2;
LTB_MESHTYPE_SKINNED = 4;
LTB_MESHTYPE_SKINNEDALT = 3;
LTB_MESHTYPE_TWOEXTRAFLOAT = 5;
type
_LtbVector3 = record
x, y, z: Single;
end;
_LtbVertex = record
Position: _LtbVector3;
Normal : _LtbVector3;
Weights : _LtbVector3;
U, V : Single;
end;
_LtbVertices = array of _LtbVertex;
_LtbIndices = array of Word;
//-----------------------------------------------
// Ltb Mesh class.
//-----------------------------------------------
CLtb******sh = class
private
VertexCount: Word;
IndexCount: Word;
Vertices: _LtbVertices;
Indices: _LtbIndices;
MeshName: ShortString;
public
constructor Create( const MeshName: ShortString;
const VertexCount, IndexCount: Word;
const Vertices: _LtbVertices;
const Indices: _LtbIndices );
function GetMeshName( ): ShortString;
function GetVertices( out Vertices: _LtbVertices ): Word;
function GetIndices( out Indices: _LtbIndices ): Word;
destructor Destroy( ); override;
end;
//-----------------------------------------------
// Ltb File class.
//-----------------------------------------------
CLtbFile = class
private
MeshList: TObjectList;
CommandLine: String;
public
constructor Create( );
function GetMeshCount( ): Cardinal;
function GetMesh( const MeshIndex: Byte ): CLtb******sh;
function GetCommandLine( ): String;
function LoadFromFile( const LtbFileName: ShortString ): Byte;
destructor Destroy( ); override;
end;
implementation
//-----------------------------------------------
// Initialize mesh.
//-----------------------------------------------
constructor CLtb******sh.Create( const MeshName: ShortString;
const VertexCount, IndexCount: Word;
const Vertices: _LtbVertices;
const Indices: _LtbIndices );
begin
Self.MeshName := MeshName;
// Copy vertices.
Self.VertexCount := VertexCount;
SetLength( Self.Vertices, VertexCount );
Self.Vertices := Copy( Vertices, 0, VertexCount );
// Copy indices.
Self.IndexCount := IndexCount;
SetLength( Self.Vertices, IndexCount );
Self.Indices := Copy( Indices, 0, IndexCount );
end;
//-----------------------------------------------
// Get mesh name.
//-----------------------------------------------
function CLtb******sh.GetMeshName( ): ShortString;
begin
Result := MeshName;
end;
//-----------------------------------------------
// Get mesh vertices. Returns number of vertices.
//-----------------------------------------------
function CLtb******sh.GetVertices( out Vertices: _LtbVertices ): Word;
begin
Vertices := Self.Vertices;
Result := VertexCount;
end;
//-----------------------------------------------
// Get mesh indices. Returns number of indices.
//-----------------------------------------------
function CLtb******sh.GetIndices( out Indices: _LtbIndices ): Word;
begin
Indices := Self.Indices;
Result := IndexCount;
end;
//-----------------------------------------------
// Destroy mesh.
//-----------------------------------------------
destructor CLtb******sh.Destroy( );
begin
Vertices := nil;
Indices := nil;
end;
//-----------------------------------------------
// Initialize LTB file.
//-----------------------------------------------
constructor CLtbFile.Create( );
begin
MeshList := TObjectList.Create( );
end;
//-----------------------------------------------
// Get mesh count.
//-----------------------------------------------
function CLtbFile.GetMeshCount( ): Cardinal;
begin
Result := MeshLis*****unt;
end;
//-----------------------------------------------
// Get mesh by it's index.
//-----------------------------------------------
function CLtbFile.GetMesh( const MeshIndex: Byte ): CLtb******sh;
begin
if ( MeshIndex < MeshLis*****unt ) then
Result := CLtb******sh( MeshList[MeshIndex] )
else
Result := nil;
end;
//-----------------------------------------------
// Get command line.
//-----------------------------------------------
function CLtbFile.GetCommandLine( ): String;
begin
Result := CommandLine;
end;
//-----------------------------------------------
// Load peices (meshes) from a LTB file.
//-----------------------------------------------
function CLtbFile.LoadFromFile( const LtbFileName: ShortString ): Byte;
var
i, j : Integer;
_File: File;
BaseOffset: Cardinal; // Base reading offset.
GeoOffset : Integer;
SkipDataSize: Cardinal;
ByteDataA, ByteDataB, ByteDataC, ByteDataD: Byte;
WordData : Word;
CardData : Cardinal;
MeshCount : Cardinal;
MeshName: String;
VertexCount: Word;
IndexCount : Word;
Vertices : _LtbVertices;
Indices : _LtbIndices;
IncludeWeights: Boolean;
IncludePostData: Boolean;
SectionSize: Byte;
begin
Result := LTB_FAIL;
if ( not FileExists( LtbFileName ) ) then
Exit;
// Assign file.
AssignFile( _File, LtbFileName );
// Reset to file's start.
Reset( _File, 1 );
// Clear mesh list.
MeshList.Clear( );
// Reset base offset.
BaseOffset := 0;
// Read command line length.
Seek( _File, BaseOffset + LTB_FILEOFFSET_COMLINELENGTH );
BlockRead( _File, WordData, SizeOf( Word ) );
SetLength( CommandLine, WordData );
// Read command line.
BlockRead( _File, CommandLine[1], WordData );
// Read mesh count.
Seek( _File, BaseOffset + LTB_FILEOFFSET_MESHCOUNT + WordData );
BlockRead( _File, MeshCount, SizeOf( Cardinal ) );
// Seek for first mesh.
Seek( _File, BaseOffset + LTB_FILEOFFSET_FIRSTMESH + WordData );
for i := 0 to ( MeshCount - 1 ) do
begin
// Read mesh name length.
BlockRead( _File, WordData, SizeOf( Word ) );
SetLength( MeshName, WordData );
// Read mesh name.
BlockRead( _File, MeshName[1], WordData );
// Set new base offset.
BaseOffset := FilePos( _File );
// Read vertex count.
Seek( _File, BaseOffset + LTB_MESHOFFSET_VERTEXCOUNT );
BlockRead( _File, VertexCount, SizeOf( Word ) );
// Read face count.
Seek( _File, BaseOffset + LTB_MESHOFFSET_FACECOUNT );
BlockRead( _File, IndexCount, SizeOf( Word ) );
// Since LTB models store their face count, instead
// of index count, we need to multiply it by 3 (Each face - 3 indices).
IndexCount := IndexCount * 3;
// Resize vertices and indices arrays.
SetLength( Vertices, VertexCount );
SetLength( Indices, IndexCount );
// Look-up mesh type.
Seek( _File, BaseOffset + LTB_MESHOFFSET_MESHTYPE_HEAD );
BlockRead( _File, WordData, SizeOf( Word ) );
SkipDataSize := 0;
if ( not ( WordData = LTB_MESHTYPE_SKINNEDALT ) ) then
begin
Seek( _File, BaseOffset + LTB_MESHOFFSET_MESHTYPE );
BlockRead( _File, WordData, SizeOf( Word ) );
end
else
WordData := LTB_MESHTYPE_TWOEXTRAFLOAT;
IncludeWeights := False;
IncludePostData := False;
if ( WordData = LTB_MESHTYPE_NOTSKINNED ) then
begin
// Mesh type: Not skinned.
IncludeWeights := False;
IncludePostData := False;
end
else
if ( WordData = LTB_MESHTYPE_EXTRAFLOAT ) then
begin
// Mesh type: Extra float.
IncludeWeights := False;
IncludePostData := True;
SkipDataSize := SizeOf( Single );
end
else
if ( ( WordData = LTB_MESHTYPE_SKINNED ) or
( WordData = LTB_MESHTYPE_SKINNEDALT ) ) then
begin
// Mesh type: Skinned.
IncludeWeights := True;
IncludePostData := True;
end
else
if ( WordData = LTB_MESHTYPE_TWOEXTRAFLOAT ) then
begin
// Mesh type: Double extra float.
IncludeWeights := False;
IncludePostData := True;
SkipDataSize := SizeOf( Single ) * 2;
end;
Seek( _File, BaseOffset + LTB_MESHOFFSET_FIRSTVERTEX );
BlockRead( _File, ByteDataA, SizeOf( Byte ) );
Seek( _File, BaseOffset + LTB_MESHOFFSET_FIRSTVERTEX + 1 );
BlockRead( _File, ByteDataB, SizeOf( Byte ) );
Seek( _File, BaseOffset + LTB_MESHOFFSET_FIRSTVERTEX + 2 );
BlockRead( _File, ByteDataC, SizeOf( Byte ) );
Seek( _File, BaseOffset + LTB_MESHOFFSET_FIRSTVERTEX + 3 );
BlockRead( _File, ByteDataD, SizeOf( Byte ) );
if ( ( ByteDataA = 0 ) and ( ByteDataB = 0 ) and ( ByteDataC <> 0 ) and ( ByteDataD <> 0 ) ) then
GeoOffset := LTB_MESHOFFSET_FIRSTVERTEX_DS
else
GeoOffset := LTB_MESHOFFSET_FIRSTVERTEX;
// Seek to first vertex.
Seek( _File, BaseOffset + Cardinal( GeoOffset ) );
// Vertices.
for j := 0 to ( VertexCount - 1 ) do
begin
// Read position.
BlockRead( _File, Vertices[j].Position, SizeOf( _LtbVector3 ) );
if ( IncludeWeights ) then
// Read weights.
BlockRead( _File, Vertices[j].Weights, SizeOf( _LtbVector3 ) );
// Read normal.
BlockRead( _File, Vertices[j].Normal, SizeOf( _LtbVector3 ) );
Seek( _File, FilePos( _File ) + Integer( SkipDataSize ) );
// Read u.
BlockRead( _File, Vertices[j].u, SizeOf( Single ) );
// Read v.
BlockRead( _File, Vertices[j].v, SizeOf( Single ) );
end;
// Indices.
for j := 0 to ( IndexCount - 1 ) do
// Read index.
BlockRead( _File, Indices[j], SizeOf( Word ) );
// ** UNKNOWN BEGIN **
// Post-data.
if ( IncludePostData ) then
begin
// Read section count.
BlockRead( _File, CardData, SizeOf( Cardinal ) );
for j := 0 to ( CardData - 1 ) do
Seek( _File, FilePos( _File ) + 12 );
// Final section.
BlockRead( _File, SectionSize, SizeOf( Byte ) );
Seek( _File, FilePos( _File ) + SectionSize );
end
else
BlockRead( _File, WordData, SizeOf( Word ) );
// ** UNKNOWN END **
// Store mesh.
MeshList.Add( CLtb******sh.Create( MeshName, VertexCount, IndexCount,
Vertices, Indices ) );
end;
Vertices := nil;
Indices := nil;
// Close file, after we're done with it.
CloseFile( _File );
Result := LTB_OK;
end;
//-----------------------------------------------
// Destroy LTB file.
//-----------------------------------------------
destructor CLtbFile.Destroy( );
begin
MeshList.Free( );
end;
end.
Damn censoring fail