Last active
August 29, 2024 00:53
-
-
Save windwakr/c1b2a455bce93053117f765277ab63a9 to your computer and use it in GitHub Desktop.
Runescape 3 RS3 NXT 3D model format
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Models in the cache have an lzma wrapper | |
BIG ENDIAN | |
1 byte, header 0x03 | |
1 int, compressed size? | |
1 int, decompressed size? | |
compressed data | |
The model data itself | |
LITTLE ENDIAN | |
1 byte, header always 0x02? | |
2 bytes, unknown | |
1 short, segmentCnt | |
1 byte, billboard? | |
1 byte, particle1? | |
1 bytes, particle2? | |
segmentCnt TIMES | |
1 int, flags | |
0b0001 hasFaceColors | |
0b0010 hasFaceAlphas (?) | |
0b0100 unk1 | |
0b1000 hasVertexSkins (?) | |
1 byte, unknown | |
1 short, materialIdx | |
1 short, faceCnt | |
if hasFaceColors | |
faceCnt shorts, face colors | |
if hasFaceAlphas | |
faceCnt bytes, face alphas? | |
if unk1 | |
faceCnt shorts, unknown | |
1 byte, indexCnt (can be up to 5, I think) | |
indexCnt TIMES | |
1 short, length | |
length shorts, index (LODs?) | |
1 short, vertCnt | |
vertCnt TIMES | |
signed short, vertex X | |
signed short, vertex Y | |
signed short, vertex Z | |
vertCnt TIMES | |
signed byte, normal X | |
signed byte, normal Y | |
signed byte, normal Z | |
vertCnt TIMES [1] | |
signed byte, tangent X | |
signed byte, tangent Y | |
signed byte, tangent Z | |
signed byte, bitangent sign | |
vertCnt TIMES | |
float16BE, U | |
float16BE, V | |
if hasVertexSkins | |
vertCnt shorts, skinning related? [2] | |
if billboard | |
(TODO) | |
if particle1 | |
(TODO) | |
if particle2 | |
(TODO) | |
----- | |
Faces are built using the first index. | |
How do face colors get selected for LODs beyond the first? | |
Models are horizontally flipped? | |
Textures need to have the repeating edges cropped out for UVs to fit properly? | |
materialIdx is which file to read in Index 26 Archive 0. | |
They specify which diffuse/normal/whatever textures to use, etc. and other material settings. | |
[1] I've not actually tested that these bytes are tangent vectors, but they seem to be. The fourth one is always -128 or 127. | |
[2] From what I gather, these assign a vertex to a group. | |
There are keyframes that tell the groups how to move/rotate stored elsewhere. Please correct me if I'm wrong about this. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[WIP] Format of the files in Index 26, Archive 0 | |
BIG ENDIAN | |
1 byte, materialType (old/new?) | |
if materialType == 0 | |
1 byte, texture size?[1] | |
1 byte, unknown | |
1 int, flags | |
if (flags & 0x01) or (flags & 0x10) | |
1 int, texture id? | |
if (flags & 0x08) or (flags & 0x02) | |
1 int, unknown | |
1 byte, unknown &7 and >>3&7 | |
1 int, moreflags | |
if (moreflags & 0x40000) | |
1 int, unknown | |
if (moreflags & 0x80000) | |
1 int, unknown | |
1 float?, unknown 1.0/value | |
1 float?, unknown 1.0/value | |
2 ints, unknown | |
if (moreflags & 0x10) | |
2 ints, unknown | |
if (flags & 0x02) | |
1 int, unknown | |
2 bytes, unknown | |
1 byte, unk1 | |
if (unk1 == 1) | |
1 byte, unknown | |
if (moreflags & 0x800) | |
3 ints, unknown | |
1 byte, unk2 | |
if (unk2 & 0x01) | |
1 signed short, unknown value * 0.000060560167 | |
if (unk2 & 0x02) | |
1 signed short, unknown value * 0.000060560167 | |
1 byte, unk3 | |
if (unk3 == 0x01) | |
2 bytes, unknown | |
1 int, unknown | |
7 bytes, unknown | |
1 short, unknown | |
else | |
1 int, flags | |
if (flags & 0x20) | |
1 byte, texture size?[1] | |
1 int, texture id? | |
if (flags & 0x40) | |
1 byte, texture size?[1] | |
1 int, texture id? | |
if (flags & 0x80) | |
1 byte, texture size?[1] | |
1 int, texture id? | |
if (flags & 0x40000) | |
1 int, unknown | |
if (flags & 0x80000) | |
1 int, unknown | |
1 float?, unknown 1.0/value | |
1 float?, unknown 1.0/value | |
2 ints, unknown | |
if (flags & 0x1000) | |
1 int, unknown | |
if (flags & 0x2000) | |
1 int, unknown | |
if (flags & 0x4000) | |
1 int, unknown | |
if (flags & 0x40) | |
1 int, unknown | |
if (flags & 0x8000) | |
1 int, unknown | |
if (flags & 0x800) | |
3 ints, unknown | |
if (flags & 0x10000) | |
1 int, unknown | |
if (flags & 0x20000) | |
1 int, unknown | |
if (flags & 0x100) | |
1 signed short, unknown value * 0.000060560167 | |
if (flags & 0x200) | |
1 signed short, unknown value * 0.000060560167 | |
1 byte, unknown &7 and >>3&7 | |
2 bytes, unknown | |
1 bytes, unk1 | |
if (unk1 == 1) | |
1 byte, unknown | |
1 short, unknown | |
1 byte, unknown | |
----- | |
[1] some var set to 64,128,256,512,1024 depending on the value read | |
mat type 1 | |
second texture: R channel = emission, G = normal G, B = normal B, Alpha = normal R | |
third texture: R channel = metal, G = roughness, B = ? |
Some recent update added new flags for model segments. Haven't had a chance to check what the game code is doing, but flags & 32 seems to indicate an int and 3*that bytes at the end of a segment. Don't really care to look into it further than that, tbh. Not really interested in this anymore.
This is very outdated. If you're looking for info on RS3 model/cache formats then see Skillbert's excellent tool here
https://github.com/skillbert/rsmv
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Raksha with textures. Two models, one is just the spikes.
Are the LODs even used in game?