Jump to content

[PC] Richard Burns Rally .SGS/.SGC models


Go to solution Solved by shak-otay,

Recommended Posts

Posted

I know the guys over at the RBR community are at least planning to work on the game's model formats so they can be edited in Blender, but I'd like to know if it's possible to rip them for the time being as I've tried ripping the character models from it using Ninja Ripper but ended up not getting a T-pose.

SGS.7z Cars.7z

  • Engineers
Posted (edited)

Low poly models usually don't raise that big interest but the mesh format shouldn't be a big obstacle, or should it?

corolla-sgc.png

edit: didn't know whether triangle strips is the best choice here. Saved with normal triangles, drag&dropped it onto Noesis, F4 for backface culling,

doesn't look much worse, imho. But after reviewing I'd suggest using strips.

Noesis_Vi51f4SsSh.png

Edited by shak-otay
  • Like 1
Posted
12 minutes ago, shak-otay said:

Low poly models usually don't raise that big interest but the mesh format shouldn't be a big obstacle, or should it?

corolla-sgc.png

edit: didn't know whether triangle strips is the best choice here. Saved with normal triangles, drag&dropped it onto Noesis, F4 for backface culling,

doesn't look much worse, imho. But after reviewing I'd suggest using strips.

Noesis_Vi51f4SsSh.png

Same goes for characters (.SGS files), right? I know @Karpati already reversed the car model formats but not the character stuff iirc.

  • Engineers
Posted
25 minutes ago, huckleberrypie said:

Same goes for characters (.SGS files), right? I know @Karpati already reversed the car model formats

Dunno. If I had known the cars have already been reversed I'd checked the character SGS instead.

  • Like 1
Posted
2 minutes ago, shak-otay said:

Dunno. If I had known the cars have already been reversed I'd checked the character SGS instead.

Sorry about that. What I do know is that both share more or less the same structure but .SGS has skeletal model information. I could open some of the more simpler ones in Zmodeler 2 e.g. a can of Red Bull but not the human characters themselves.

  • Engineers
Posted (edited)

"stride" (or FVFsize in hex2obj) is a multiple of 4 for float based formats. Using vertex start address 0x3f94 (type in 3f94 without the 0x) for mechanic-1-4_01.sgs and trying 12 in a first step and pressing the go2 button you'll see "NORM" in each 2nd line in the lower left window. This means the FVFsize is 24 in this case.

The vertexcount is calculated automatically (see lower left window) after pressing the go1 button. Provided you set the correct start address and FI count of the FI block. That's just a matter of experience. Or simply divide sizeOfVertex block by FVFsize. Works in most cases.

Edited by shak-otay
  • Thanks 1
  • Engineers
Posted (edited)

Struct of SGS/SGC is pretty much same...

Here's some info...

//------------------------------------------------
//--- 010 Editor v14.0 Binary Template
//
//      File: 
//   Authors: 
//   Version: 
//   Purpose: 
//  Category: 
// File Mask: 
//  ID Bytes: 
//   History: 
//------------------------------------------------
LittleEndian();OutputPaneClear();

local uint32 i,j,k,l,TotalSize=FileSize();

string Sign;

struct
{
    do
    {
        struct 
        {
            uint32 ResourceSign;
            enum <uint32>
            {
                Mesh = 0,
                Bone = 3,
                Material = 5,
                MaterialEffect = 15,
                Skeleton = 18,
            }ResourceType;
            uint32 Unknown_2;
            uint32 Unknown_3;
            uint32 ResourceSize;
            local uint32 ResourceInfoEndOffset=FTell();
            byte ResourceData[ResourceSize];
            local uint32 cPos=FTell();
        }ResourceInfo;
    }
    while (ResourceInfo.ResourceSign == 12 & ResourceInfo.cPos != TotalSize);
}ResourceTable;

EDiT: Now you can dump it with this bms script.

###################################
get BaseFileName basename
get FileSize asize
get Sign string

do
	get ResourceSign uint32
	get ResourceType uint32
	get Unknown_2 uint32
	get Unknown_3 uint32
	get ResourceSize uint32
	savepos ResourceOffset
	getdstring ResourceData ResourceSize
	savepos cPos
	
	if ResourceType == 0
		set Extension string "mesh"
	elif ResourceType == 3
		set Extension string "bone"
	elif ResourceType == 5
		set Extension string "material"
	elif ResourceType == 15
		set Extension string "materialEffect"
	elif ResourceType == 18
		set Extension string "skeleton"
	else
		set Extension uint32 ResourceType
	endif
	
	string FileName p= "%s/%u.%s" BaseFileName ResourceOffset Extension
	log FileName ResourceOffset ResourceSize
while ResourceSign == 12 & cPos != FileSize

 

Edited by h3x3r
  • Thanks 1
Posted (edited)
11 hours ago, h3x3r said:

Struct of SGS/SGC is pretty much same...

Here's some info...

//------------------------------------------------
//--- 010 Editor v14.0 Binary Template
//
//      File: 
//   Authors: 
//   Version: 
//   Purpose: 
//  Category: 
// File Mask: 
//  ID Bytes: 
//   History: 
//------------------------------------------------
LittleEndian();OutputPaneClear();

local uint32 i,j,k,l,TotalSize=FileSize();

string Sign;

struct
{
    do
    {
        struct 
        {
            uint32 ResourceSign;
            enum <uint32>
            {
                Mesh = 0,
                Bone = 3,
                Material = 5,
                MaterialEffect = 15,
                Skeleton = 18,
            }ResourceType;
            uint32 Unknown_2;
            uint32 Unknown_3;
            uint32 ResourceSize;
            local uint32 ResourceInfoEndOffset=FTell();
            byte ResourceData[ResourceSize];
            local uint32 cPos=FTell();
        }ResourceInfo;
    }
    while (ResourceInfo.ResourceSign == 12 & ResourceInfo.cPos != TotalSize);
}ResourceTable;

EDiT: Now you can dump it with this bms script.

###################################
get BaseFileName basename
get FileSize asize
get Sign string

do
	get ResourceSign uint32
	get ResourceType uint32
	get Unknown_2 uint32
	get Unknown_3 uint32
	get ResourceSize uint32
	savepos ResourceOffset
	getdstring ResourceData ResourceSize
	savepos cPos
	
	if ResourceType == 0
		set Extension string "mesh"
	elif ResourceType == 3
		set Extension string "bone"
	elif ResourceType == 5
		set Extension string "material"
	elif ResourceType == 15
		set Extension string "materialEffect"
	elif ResourceType == 18
		set Extension string "skeleton"
	else
		set Extension uint32 ResourceType
	endif
	
	string FileName p= "%s/%u.%s" BaseFileName ResourceOffset Extension
	log FileName ResourceOffset ResourceSize
while ResourceSign == 12 & cPos != FileSize

 

What about a Noesis script?

I am able to figure out where the vertices are and more or less the count but the faces are a clincher to get.

Screenshot (786).png

Edited by huckleberrypie
Posted (edited)
2 hours ago, shak-otay said:

Faces are simple for the mentioned mechanics:

mechanic.png

Got it now, thanks!

Screenshot (793).png

EDIT: There's one problem tho: I don't get any UVs for some reason.

Edited by huckleberrypie
  • Engineers
Posted (edited)

I have made a Noesis script but it's basic. Just simple mesh export. No materials/ no bones/ no skin.

Anyway this is mesh struct. Just basic.

//------------------------------------------------
//--- 010 Editor v14.0 Binary Template
//
//      File: 
//   Authors: 
//   Version: 
//   Purpose: 
//  Category: 
// File Mask: 
//  ID Bytes: 
//   History: 
//------------------------------------------------
LittleEndian();OutputPaneClear();

local uint32 i,j,k,l,TotalSize=FileSize();

string MeshName;
uint32 Unknown_0;
uint32 Unknown_1;
float Unknown_2[27];
float Unknown_3[5];
uint32 TotalVertexCount;
uint32 StrideType;
if (StrideType == 19)
{
    byte VertexBuffer[TotalVertexCount*24];
    byte ColorBuffer[TotalVertexCount*4];
}
else if (StrideType == 23)
{
    byte VertexBuffer[TotalVertexCount*24];
    byte UVBuffer[TotalVertexCount*8];
    byte ColorBuffer[TotalVertexCount*4];
}    
uint32 TotalIndexCount;
byte IndexBuffer[TotalIndexCount*6];

uint32 ShapeIndex;
struct 
{
    string MaterialName;
    uint32 IndexCount; // *3
    uint32 Unknown_0;
    uint32 VertexCount;
    uint32 Unknown_1;
}ShapeInfo[ShapeIndex]<optimize=false>;

FSeek(startof(VertexBuffer));

if (StrideType == 19)
{
    struct
    {
        float VPosX,VPosY,VPosZ,VNPosX,VNPosY,VNPosZ;
    }Vertices[TotalVertexCount]<optimize=false>;
}
else if (StrideType == 23)
{
    struct
    {
        float VPosX,VPosY,VPosZ,VNPosX,VNPosY,VNPosZ;
    }Vertices[TotalVertexCount]<optimize=false>;
    
    struct
    {
        float UVPosX,UVPosY;
    }UnitVector[TotalVertexCount]<optimize=false>;
}

FSeek(startof(IndexBuffer));

for (i=0; i < ShapeIndex; i++)
{
    struct
    {
        uint16 F1,F2,F3;
    }Indices[ShapeInfo[i].IndexCount]<optimize=false>;
    
}

image.thumb.png.df7e18aa58c0df16efda89fab83d450e.png

Edited by h3x3r
  • Thanks 2
Posted
1 hour ago, h3x3r said:

I have made a Noesis script but it's basic. Just simple mesh export. No materials/ no bones/ no skin.

Anyway this is mesh struct. Just basic.

//------------------------------------------------
//--- 010 Editor v14.0 Binary Template
//
//      File: 
//   Authors: 
//   Version: 
//   Purpose: 
//  Category: 
// File Mask: 
//  ID Bytes: 
//   History: 
//------------------------------------------------
LittleEndian();OutputPaneClear();

local uint32 i,j,k,l,TotalSize=FileSize();

string MeshName;
uint32 Unknown_0;
uint32 Unknown_1;
float Unknown_2[27];
float Unknown_3[5];
uint32 TotalVertexCount;
uint32 StrideType;
if (StrideType == 19)
{
    byte VertexBuffer[TotalVertexCount*24];
    byte ColorBuffer[TotalVertexCount*4];
}
else if (StrideType == 23)
{
    byte VertexBuffer[TotalVertexCount*24];
    byte UVBuffer[TotalVertexCount*8];
    byte ColorBuffer[TotalVertexCount*4];
}    
uint32 TotalIndexCount;
byte IndexBuffer[TotalIndexCount*6];

uint32 ShapeIndex;
struct 
{
    string MaterialName;
    uint32 IndexCount; // *3
    uint32 Unknown_0;
    uint32 VertexCount;
    uint32 Unknown_1;
}ShapeInfo[ShapeIndex]<optimize=false>;

FSeek(startof(VertexBuffer));

if (StrideType == 19)
{
    struct
    {
        float VPosX,VPosY,VPosZ,VNPosX,VNPosY,VNPosZ;
    }Vertices[TotalVertexCount]<optimize=false>;
}
else if (StrideType == 23)
{
    struct
    {
        float VPosX,VPosY,VPosZ,VNPosX,VNPosY,VNPosZ;
    }Vertices[TotalVertexCount]<optimize=false>;
    
    struct
    {
        float UVPosX,UVPosY;
    }UnitVector[TotalVertexCount]<optimize=false>;
}

FSeek(startof(IndexBuffer));

for (i=0; i < ShapeIndex; i++)
{
    struct
    {
        uint16 F1,F2,F3;
    }Indices[ShapeInfo[i].IndexCount]<optimize=false>;
    
}

image.thumb.png.df7e18aa58c0df16efda89fab83d450e.png

Would you mind sharing the script? Or are you still in the process of improving it?

  • Engineers
Posted

Nah no improvements so far and don't think there will be any...

from inc_noesis import *
import noesis
import rapi
import os

def registerNoesisTypes():
   handle = noesis.register("Richard Burns Rally - Geometry", ".mesh")
   noesis.setHandlerTypeCheck(handle, noepyCheckType)
   noesis.setHandlerLoadModel(handle, noepyLoadModel)
   noesis.logPopup()
   return 1
        
def noepyCheckType(data):
   bs = NoeBitStream(data)
   if len(data) < 20:
      return 0
   return 1
        
def noepyLoadModel(data, mdlList):
	bs = NoeBitStream(data)
	baseName = rapi.getExtensionlessName(rapi.getLocalFileName(rapi.getInputName()))
	ctx = rapi.rpgCreateContext()
	Underline = "_"
	MeshName = bs.readString()
	Unknown_0 = bs.readUInt()
	Unknown_1 = bs.readUInt()
	bs.read(128)
	TotalVertexCount = bs.readUInt()
	StrideType = bs.readUInt()
	if StrideType == 19:
	    VertexBuffer = bs.read(TotalVertexCount * 24)
	    ColorBuffer = bs.read(TotalVertexCount * 4)
	elif StrideType == 23:
	    VertexBuffer = bs.read(TotalVertexCount * 24)
	    UnitVectorBuffer = bs.read(TotalVertexCount * 8)
	    ColorBuffer = bs.read(TotalVertexCount * 4)
        
	TotalIndexCount = bs.readUInt()
	IndexBufferBaseOffset = bs.tell()
	bs.read(TotalIndexCount * 6)
    
	if StrideType == 19:
	    rapi.rpgBindPositionBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, 24, 0)
	    rapi.rpgBindNormalBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, 24, 12)
	elif StrideType == 23:
	    rapi.rpgBindPositionBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, 24, 0)
	    rapi.rpgBindNormalBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, 24, 12)
	    rapi.rpgBindUV1BufferOfs(UnitVectorBuffer, noesis.RPGEODATA_FLOAT, 8, 0)
            
	ShapeIndex = bs.readUInt()
	IndexCountList = []
	MaterialNameList = []
    
	for i in range(0, ShapeIndex):
		MaterialNameList.append(bs.readString())
		IndexCountList.append(bs.readUInt()*3)
		Unknown_0 = bs.readUInt()
		VertexCount = bs.readUInt()
		Unknown_1 = bs.readUInt()
    
	bs.seek(IndexBufferBaseOffset, NOESEEK_ABS)
	for i in range(0, ShapeIndex):
		IndexCount = IndexCountList[i]
		MaterialName = MaterialNameList[i]
		IndexBuffer = bs.read(IndexCount * 2)
		ShapeIdDigfmt = "{:04d}".format(i)
		rapi.rpgSetName(MeshName + Underline + ShapeIdDigfmt)
		#rapi.rpgSetMaterial(MaterialName)
		rapi.rpgCommitTriangles(IndexBuffer, noesis.RPGEODATA_USHORT, IndexCount, noesis.RPGEO_TRIANGLE)
		mdl = rapi.rpgConstructModel()
	mdlList.append(mdl)
	return 1

You can try on your own 😉

  • Thanks 1

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...