mirror of
https://github.com/luksamuk/engine-psx.git
synced 2025-04-28 13:28:02 +03:00
Add conversion of models from .RSD to .MDL
This commit is contained in:
parent
b0fd36db4f
commit
f13ddc3c3d
15 changed files with 2797 additions and 1774 deletions
BIN
assets/objs/common/ring.mdl
Normal file
BIN
assets/objs/common/ring.mdl
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load diff
8
iso.xml
8
iso.xml
|
@ -98,6 +98,14 @@
|
|||
|
||||
</dir> <!-- LEVELS -->
|
||||
|
||||
<dir name="OBJS">
|
||||
<dir name="COMMON">
|
||||
<file name="RING.MDL"
|
||||
type="data"
|
||||
source="${PROJECT_SOURCE_DIR}/assets/objs/common/ring.mdl" />
|
||||
</dir> <!-- COMMON -->
|
||||
</dir> <!-- OBJS -->
|
||||
|
||||
<dir name="MISC">
|
||||
<file name="DISK.TIM"
|
||||
type="data"
|
||||
|
|
2
tools/convrsd/.gitignore
vendored
Normal file
2
tools/convrsd/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
__pycache__/
|
||||
|
44
tools/convrsd/common.py
Normal file
44
tools/convrsd/common.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
from ctypes import c_ubyte, c_short, c_ushort
|
||||
from enum import Enum
|
||||
|
||||
c_short = c_short.__ctype_be__
|
||||
c_ushort = c_ushort.__ctype_be__
|
||||
# c_int = c_int.__ctype_be__
|
||||
# c_uint = c_uint.__ctype_be__
|
||||
|
||||
|
||||
class SVECTOR:
|
||||
vx: c_short
|
||||
vy: c_short
|
||||
vz: c_short
|
||||
|
||||
def __repr__(self):
|
||||
return f"{{ {self.vx.value:05X}, {self.vy.value:05X}, {self.vz.value:05X} }}"
|
||||
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
|
||||
class FaceType(Enum):
|
||||
TRIANGLE = 0
|
||||
QUAD = 1
|
||||
LINE = 2
|
||||
SPRITE = 3
|
||||
|
||||
|
||||
class MaterialType(Enum):
|
||||
Flat = "C"
|
||||
Gouraud = "G"
|
||||
Texture = "T"
|
||||
TextureFlat = "D"
|
||||
TextureGouraud = "H"
|
||||
|
||||
# Don't care about these others:
|
||||
MATERIAL_W = "W" # Repeating textures, no-color
|
||||
MATERIAL_S = "S" # Repeating textures, flat colored
|
||||
MATERIAL_N = "N" # Repeating textures, gouraud-shaded
|
||||
|
||||
|
||||
class ShadingType(Enum):
|
||||
FLAT = "F"
|
||||
GOURAUD = "G"
|
320
tools/convrsd/convrsd.py
Executable file
320
tools/convrsd/convrsd.py
Executable file
|
@ -0,0 +1,320 @@
|
|||
#!/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pprint import pp as pprint
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from fixedpoint import tofixed12
|
||||
from common import *
|
||||
from face import *
|
||||
from material import *
|
||||
|
||||
from import_types import *
|
||||
from export_types import *
|
||||
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
|
||||
def parse_rsd(filename: str) -> RSDModel:
|
||||
if not os.path.isfile(filename):
|
||||
print(f"File {filename} does not exist")
|
||||
exit(1)
|
||||
|
||||
filename = os.path.abspath(filename)
|
||||
rsd = RSDModel()
|
||||
with open(filename, "r") as f:
|
||||
# Read magic
|
||||
while True:
|
||||
rsd.magic = f.readline().strip()
|
||||
if not rsd.magic:
|
||||
print(f"Unexpected EOF at {filename}")
|
||||
exit(1)
|
||||
if rsd.magic[0] == "#":
|
||||
continue
|
||||
if rsd.magic[:4] != "@RSD":
|
||||
print(f"Could not parse RSD file {filename}")
|
||||
exit(1)
|
||||
break
|
||||
|
||||
ply_filename = None
|
||||
mat_filename = None
|
||||
# Read other lines
|
||||
while True:
|
||||
buffer = f.readline().strip()
|
||||
if not buffer:
|
||||
break
|
||||
elif buffer[:3] == "PLY":
|
||||
ply_filename = buffer[4:]
|
||||
elif buffer[:3] == "MAT":
|
||||
mat_filename = buffer[4:]
|
||||
elif buffer[:4] == "MTEX":
|
||||
# TODO
|
||||
pass
|
||||
|
||||
if ply_filename:
|
||||
ply_filename = os.path.dirname(filename) + "/" + ply_filename
|
||||
rsd.ply = parse_ply(ply_filename.strip())
|
||||
if mat_filename:
|
||||
mat_filename = os.path.dirname(filename) + "/" + mat_filename
|
||||
rsd.mat = parse_mat(mat_filename.strip())
|
||||
return rsd
|
||||
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
|
||||
def _parse_face(buffer: str) -> Face:
|
||||
buffer = buffer.split()
|
||||
ftype = FaceType(int(buffer[0]))
|
||||
|
||||
if ftype == FaceType.TRIANGLE:
|
||||
v = TriangleFace()
|
||||
v.iv0 = c_ushort(int(buffer[1]))
|
||||
v.iv1 = c_ushort(int(buffer[2]))
|
||||
v.iv2 = c_ushort(int(buffer[3]))
|
||||
v.in0 = c_ushort(int(buffer[5]))
|
||||
v.in1 = c_ushort(int(buffer[6]))
|
||||
v.in2 = c_ushort(int(buffer[7]))
|
||||
return v
|
||||
elif ftype == FaceType.QUAD:
|
||||
v = QuadFace()
|
||||
v.iv0 = c_ushort(int(buffer[1]))
|
||||
v.iv1 = c_ushort(int(buffer[2]))
|
||||
v.iv2 = c_ushort(int(buffer[3]))
|
||||
v.iv3 = c_ushort(int(buffer[4]))
|
||||
v.in0 = c_ushort(int(buffer[5]))
|
||||
v.in1 = c_ushort(int(buffer[6]))
|
||||
v.in2 = c_ushort(int(buffer[7]))
|
||||
v.iv3 = c_ushort(int(buffer[8]))
|
||||
return v
|
||||
elif ftype == FaceType.LINE:
|
||||
v = LineFace()
|
||||
v.iv0 = c_ushort(int(buffer[1]))
|
||||
v.iv1 = c_ushort(int(buffer[2]))
|
||||
return v
|
||||
elif ftype == FaceType.SPRITE:
|
||||
v = SpriteFace()
|
||||
v.iv0 = c_ushort(int(buffer[1]))
|
||||
v.iv1 = c_ushort(int(buffer[2]))
|
||||
v.iw = c_ushort(int(buffer[3]))
|
||||
v.ih = c_ushort(int(buffer[4]))
|
||||
return v
|
||||
else:
|
||||
print(f"Unknown face type {ftype}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def parse_ply(filename: str) -> PlyData:
|
||||
if not os.path.isfile(filename):
|
||||
print(f"File {filename} does not exist")
|
||||
exit(1)
|
||||
|
||||
ply = PlyData()
|
||||
with open(filename, "r") as f:
|
||||
# Read magic
|
||||
while True:
|
||||
ply.magic = f.readline().strip()
|
||||
if not ply.magic:
|
||||
print(f"Unexpected EOF at {filename}")
|
||||
exit(1)
|
||||
if ply.magic[0] == "#":
|
||||
continue
|
||||
if ply.magic[:4] != "@PLY":
|
||||
print(f"Could not parse PLY file {filename}")
|
||||
exit(1)
|
||||
break
|
||||
|
||||
step = 0
|
||||
vertices = 0
|
||||
normals = 0
|
||||
faces = 0
|
||||
while True:
|
||||
buffer = f.readline().strip()
|
||||
if not buffer:
|
||||
break
|
||||
elif buffer[0] == "#":
|
||||
continue
|
||||
|
||||
if step == 0:
|
||||
numbers = buffer.split()
|
||||
ply.num_vertices = c_ushort(int(numbers[0]))
|
||||
ply.num_normals = c_ushort(int(numbers[1]))
|
||||
ply.num_faces = c_ushort(int(numbers[2]))
|
||||
vertices = ply.num_vertices.value
|
||||
normals = ply.num_normals.value
|
||||
faces = ply.num_faces.value
|
||||
step += 1
|
||||
elif step == 1:
|
||||
if vertices > 0:
|
||||
values = buffer.split()
|
||||
values = [tofixed12(float(x)) for x in values]
|
||||
vertex = SVECTOR()
|
||||
vertex.vx = c_short(values[0])
|
||||
vertex.vy = c_short(values[1])
|
||||
vertex.vz = c_short(values[2])
|
||||
ply.vertices.append(vertex)
|
||||
vertices -= 1
|
||||
elif normals > 0:
|
||||
values = buffer.split()
|
||||
values = [tofixed12(float(x)) for x in values]
|
||||
normal = SVECTOR()
|
||||
normal.vx = c_short(values[0])
|
||||
normal.vy = c_short(values[1])
|
||||
normal.vz = c_short(values[2])
|
||||
ply.normals.append(normal)
|
||||
normals -= 1
|
||||
elif faces > 0:
|
||||
ply.faces.append(_parse_face(buffer))
|
||||
faces -= 1
|
||||
else:
|
||||
step += 1
|
||||
else:
|
||||
break
|
||||
assert ply.num_vertices.value == len(ply.vertices)
|
||||
assert ply.num_normals.value == len(ply.normals)
|
||||
assert ply.num_faces.value == len(ply.faces)
|
||||
return ply
|
||||
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
|
||||
def _parse_material(buffer: str) -> Material:
|
||||
buffer = buffer.split()
|
||||
|
||||
material = Material()
|
||||
material.ipolygon = c_ushort(int(buffer[0]))
|
||||
material.flag = c_ubyte(int(buffer[1]))
|
||||
material.shading = ShadingType(buffer[2])
|
||||
|
||||
mtype = MaterialType(buffer[3])
|
||||
if mtype == MaterialType.Flat:
|
||||
material.info = FlatMaterial()
|
||||
material.info.r0 = c_ubyte(int(buffer[4]))
|
||||
material.info.g0 = c_ubyte(int(buffer[5]))
|
||||
material.info.b0 = c_ubyte(int(buffer[6]))
|
||||
elif mtype == MaterialType.Gouraud:
|
||||
material.info = GouraudMaterial()
|
||||
material.info.r0 = c_ubyte(int(buffer[4]))
|
||||
material.info.g0 = c_ubyte(int(buffer[5]))
|
||||
material.info.b0 = c_ubyte(int(buffer[6]))
|
||||
material.info.r1 = c_ubyte(int(buffer[5]))
|
||||
material.info.g1 = c_ubyte(int(buffer[6]))
|
||||
material.info.b1 = c_ubyte(int(buffer[7]))
|
||||
material.info.r2 = c_ubyte(int(buffer[8]))
|
||||
material.info.g2 = c_ubyte(int(buffer[9]))
|
||||
material.info.b2 = c_ubyte(int(buffer[10]))
|
||||
material.info.r3 = c_ubyte(int(buffer[11]))
|
||||
material.info.g3 = c_ubyte(int(buffer[12]))
|
||||
material.info.b3 = c_ubyte(int(buffer[13]))
|
||||
elif mtype == MaterialType.Texture:
|
||||
m = TextureMaterial()
|
||||
material.info.u0 = c_ubyte(int(buffer[4]))
|
||||
material.info.v0 = c_ubyte(int(buffer[5]))
|
||||
material.info.u1 = c_ubyte(int(buffer[6]))
|
||||
material.info.v1 = c_ubyte(int(buffer[7]))
|
||||
material.info.u2 = c_ubyte(int(buffer[8]))
|
||||
material.info.v2 = c_ubyte(int(buffer[9]))
|
||||
material.info.u3 = c_ubyte(int(buffer[10]))
|
||||
material.info.v3 = c_ubyte(int(buffer[11]))
|
||||
elif mtype == MaterialType.TextureFlat:
|
||||
material.info = TextureFlatMaterial()
|
||||
material.info.u0 = c_ubyte(int(buffer[4]))
|
||||
material.info.v0 = c_ubyte(int(buffer[5]))
|
||||
material.info.u1 = c_ubyte(int(buffer[6]))
|
||||
material.info.v1 = c_ubyte(int(buffer[7]))
|
||||
material.info.u2 = c_ubyte(int(buffer[8]))
|
||||
material.info.v2 = c_ubyte(int(buffer[9]))
|
||||
material.info.u3 = c_ubyte(int(buffer[10]))
|
||||
material.info.v3 = c_ubyte(int(buffer[11]))
|
||||
material.info.r0 = c_ubyte(int(buffer[12]))
|
||||
material.info.g0 = c_ubyte(int(buffer[13]))
|
||||
material.info.b0 = c_ubyte(int(buffer[14]))
|
||||
elif mtype == MaterialType.TextureGouraud:
|
||||
material.info = TextureGouraudMaterial()
|
||||
material.info.u0 = c_ubyte(int(buffer[4]))
|
||||
material.info.v0 = c_ubyte(int(buffer[5]))
|
||||
material.info.u1 = c_ubyte(int(buffer[6]))
|
||||
material.info.v1 = c_ubyte(int(buffer[7]))
|
||||
material.info.u2 = c_ubyte(int(buffer[8]))
|
||||
material.info.v2 = c_ubyte(int(buffer[9]))
|
||||
material.info.u3 = c_ubyte(int(buffer[10]))
|
||||
material.info.v3 = c_ubyte(int(buffer[11]))
|
||||
material.info.r0 = c_ubyte(int(buffer[12]))
|
||||
material.info.g0 = c_ubyte(int(buffer[13]))
|
||||
material.info.b0 = c_ubyte(int(buffer[14]))
|
||||
material.info.r1 = c_ubyte(int(buffer[15]))
|
||||
material.info.g1 = c_ubyte(int(buffer[16]))
|
||||
material.info.b1 = c_ubyte(int(buffer[17]))
|
||||
material.info.r2 = c_ubyte(int(buffer[18]))
|
||||
material.info.g2 = c_ubyte(int(buffer[19]))
|
||||
material.info.b2 = c_ubyte(int(buffer[20]))
|
||||
material.info.r3 = c_ubyte(int(buffer[21]))
|
||||
material.info.g3 = c_ubyte(int(buffer[22]))
|
||||
material.info.b3 = c_ubyte(int(buffer[23]))
|
||||
else:
|
||||
print(f"Unhandled material type {mtype}")
|
||||
exit(1)
|
||||
|
||||
return material
|
||||
|
||||
|
||||
def parse_mat(filename: str) -> MatData:
|
||||
if not os.path.isfile(filename):
|
||||
print(f"File {filename} does not exist")
|
||||
exit(1)
|
||||
|
||||
mat = MatData()
|
||||
with open(filename, "r") as f:
|
||||
# Read magic
|
||||
while True:
|
||||
mat.magic = f.readline().strip()
|
||||
if not mat.magic:
|
||||
print(f"Unexpected EOF at {filename}")
|
||||
exit(1)
|
||||
if mat.magic[0] == "#":
|
||||
continue
|
||||
if mat.magic[:4] != "@MAT":
|
||||
print(f"Could not parse MAT file {filename}")
|
||||
exit(1)
|
||||
break
|
||||
|
||||
step = 0
|
||||
items = 0
|
||||
while True:
|
||||
buffer = f.readline().strip()
|
||||
if not buffer:
|
||||
break
|
||||
elif buffer[0] == "#":
|
||||
continue
|
||||
|
||||
if step == 0:
|
||||
mat.num_items = c_ushort(int(buffer))
|
||||
items = mat.num_items.value
|
||||
step += 1
|
||||
elif step == 1:
|
||||
if items > 0:
|
||||
mat.materials.append(_parse_material(buffer))
|
||||
items -= 1
|
||||
else:
|
||||
step += 1
|
||||
else:
|
||||
break
|
||||
assert mat.num_items.value == len(mat.materials)
|
||||
return mat
|
||||
|
||||
|
||||
def main():
|
||||
filename = sys.argv[1]
|
||||
rsd = parse_rsd(filename.strip())
|
||||
mdl = convert_model(rsd)
|
||||
out_filename = os.path.splitext(filename)[0] + ".mdl"
|
||||
with open(out_filename, "wb") as f:
|
||||
mdl.write(f)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
581
tools/convrsd/export_types.py
Normal file
581
tools/convrsd/export_types.py
Normal file
|
@ -0,0 +1,581 @@
|
|||
from dataclasses import dataclass, field
|
||||
from common import SVECTOR
|
||||
from enum import Enum
|
||||
from face import *
|
||||
from material import *
|
||||
from import_types import RSDModel
|
||||
|
||||
|
||||
class PolyType(Enum):
|
||||
F3 = c_ubyte(0)
|
||||
G3 = c_ubyte(1)
|
||||
F4 = c_ubyte(2)
|
||||
G4 = c_ubyte(3)
|
||||
FT3 = c_ubyte(4)
|
||||
GT3 = c_ubyte(5)
|
||||
FT4 = c_ubyte(6)
|
||||
GT4 = c_ubyte(7)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PolyF3:
|
||||
ftype: c_ubyte = PolyType.F3
|
||||
r0: c_ubyte = c_ubyte(0)
|
||||
g0: c_ubyte = c_ubyte(0)
|
||||
b0: c_ubyte = c_ubyte(0)
|
||||
iv0: c_ushort = c_ushort(0)
|
||||
iv1: c_ushort = c_ushort(0)
|
||||
iv2: c_ushort = c_ushort(0)
|
||||
in0: c_ushort = c_ushort(0)
|
||||
|
||||
@staticmethod
|
||||
def from_pair(face: TriangleFace, material: FlatMaterial) -> "PolyF3":
|
||||
p = PolyF3()
|
||||
p.r0 = material.r0
|
||||
p.g0 = material.g0
|
||||
p.b0 = material.b0
|
||||
p.iv0 = face.iv0
|
||||
p.iv1 = face.iv1
|
||||
p.iv2 = face.iv2
|
||||
p.in0 = face.in0
|
||||
return p
|
||||
|
||||
def write(self, f):
|
||||
f.write(self.ftype.value)
|
||||
f.write(self.r0)
|
||||
f.write(self.g0)
|
||||
f.write(self.b0)
|
||||
f.write(self.iv0)
|
||||
f.write(self.iv1)
|
||||
f.write(self.iv2)
|
||||
f.write(self.in0)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PolyG3:
|
||||
ftype: c_ubyte = PolyType.G3
|
||||
r0: c_ubyte = c_ubyte(0)
|
||||
g0: c_ubyte = c_ubyte(0)
|
||||
b0: c_ubyte = c_ubyte(0)
|
||||
r1: c_ubyte = c_ubyte(0)
|
||||
g1: c_ubyte = c_ubyte(0)
|
||||
b1: c_ubyte = c_ubyte(0)
|
||||
r2: c_ubyte = c_ubyte(0)
|
||||
g2: c_ubyte = c_ubyte(0)
|
||||
b2: c_ubyte = c_ubyte(0)
|
||||
iv0: c_ushort = c_ushort(0)
|
||||
iv1: c_ushort = c_ushort(0)
|
||||
iv2: c_ushort = c_ushort(0)
|
||||
in0: c_ushort = c_ushort(0)
|
||||
in1: c_ushort = c_ushort(0)
|
||||
in2: c_ushort = c_ushort(0)
|
||||
|
||||
@staticmethod
|
||||
def from_pair(face: TriangleFace, material: GouraudMaterial) -> "PolyG3":
|
||||
p = PolyG3()
|
||||
p.r0 = material.r0
|
||||
p.g0 = material.g0
|
||||
p.b0 = material.b0
|
||||
p.r1 = material.r1
|
||||
p.g1 = material.g1
|
||||
p.b1 = material.b1
|
||||
p.r2 = material.r2
|
||||
p.g2 = material.g2
|
||||
p.b2 = material.b2
|
||||
p.iv0 = face.iv0
|
||||
p.iv1 = face.iv1
|
||||
p.iv2 = face.iv2
|
||||
p.in0 = face.in0
|
||||
p.in1 = face.in1
|
||||
p.in2 = face.in2
|
||||
return p
|
||||
|
||||
def write(self, f):
|
||||
f.write(self.ftype.value)
|
||||
f.write(self.r0)
|
||||
f.write(self.g0)
|
||||
f.write(self.b0)
|
||||
f.write(self.r1)
|
||||
f.write(self.g1)
|
||||
f.write(self.b1)
|
||||
f.write(self.r2)
|
||||
f.write(self.g2)
|
||||
f.write(self.b2)
|
||||
f.write(self.iv0)
|
||||
f.write(self.iv1)
|
||||
f.write(self.iv2)
|
||||
f.write(self.in0)
|
||||
f.write(self.in1)
|
||||
f.write(self.in2)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PolyF4:
|
||||
ftype: c_ubyte = PolyType.F4
|
||||
r0: c_ubyte = c_ubyte(0)
|
||||
g0: c_ubyte = c_ubyte(0)
|
||||
b0: c_ubyte = c_ubyte(0)
|
||||
iv0: c_ushort = c_ushort(0)
|
||||
iv1: c_ushort = c_ushort(0)
|
||||
iv2: c_ushort = c_ushort(0)
|
||||
iv3: c_ushort = c_ushort(0)
|
||||
in0: c_ushort = c_ushort(0)
|
||||
|
||||
@staticmethod
|
||||
def from_pair(face: QuadFace, material: FlatMaterial) -> "PolyF4":
|
||||
p = PolyF4()
|
||||
p.r0 = material.r0
|
||||
p.g0 = material.g0
|
||||
p.b0 = material.b0
|
||||
p.iv0 = face.iv0
|
||||
p.iv1 = face.iv1
|
||||
p.iv2 = face.iv2
|
||||
p.iv3 = face.iv3
|
||||
p.in0 = face.in0
|
||||
return p
|
||||
|
||||
def write(self, f):
|
||||
f.write(self.ftype.value)
|
||||
f.write(self.r0)
|
||||
f.write(self.g0)
|
||||
f.write(self.b0)
|
||||
f.write(self.iv0)
|
||||
f.write(self.iv1)
|
||||
f.write(self.iv2)
|
||||
f.write(self.iv3)
|
||||
f.write(self.in0)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PolyG4:
|
||||
ftype: c_ubyte = PolyType.G4
|
||||
r0: c_ubyte = c_ubyte(0)
|
||||
g0: c_ubyte = c_ubyte(0)
|
||||
b0: c_ubyte = c_ubyte(0)
|
||||
r1: c_ubyte = c_ubyte(0)
|
||||
g1: c_ubyte = c_ubyte(0)
|
||||
b1: c_ubyte = c_ubyte(0)
|
||||
r2: c_ubyte = c_ubyte(0)
|
||||
g2: c_ubyte = c_ubyte(0)
|
||||
b2: c_ubyte = c_ubyte(0)
|
||||
r3: c_ubyte = c_ubyte(0)
|
||||
g3: c_ubyte = c_ubyte(0)
|
||||
b3: c_ubyte = c_ubyte(0)
|
||||
iv0: c_ushort = c_ushort(0)
|
||||
iv1: c_ushort = c_ushort(0)
|
||||
iv2: c_ushort = c_ushort(0)
|
||||
iv3: c_ushort = c_ushort(0)
|
||||
in0: c_ushort = c_ushort(0)
|
||||
in1: c_ushort = c_ushort(0)
|
||||
in2: c_ushort = c_ushort(0)
|
||||
in3: c_ushort = c_ushort(0)
|
||||
|
||||
@staticmethod
|
||||
def from_pair(face: QuadFace, material: GouraudMaterial) -> "PolyG4":
|
||||
p = PolyG4()
|
||||
p.r0 = material.r0
|
||||
p.g0 = material.g0
|
||||
p.b0 = material.b0
|
||||
p.r1 = material.r1
|
||||
p.g1 = material.g1
|
||||
p.b1 = material.b1
|
||||
p.r2 = material.r2
|
||||
p.g2 = material.g2
|
||||
p.b2 = material.b2
|
||||
p.r3 = material.r3
|
||||
p.g3 = material.g3
|
||||
p.b3 = material.b3
|
||||
p.iv0 = face.iv0
|
||||
p.iv1 = face.iv1
|
||||
p.iv2 = face.iv2
|
||||
p.iv3 = face.iv3
|
||||
p.in0 = face.in0
|
||||
p.in1 = face.in1
|
||||
p.in2 = face.in2
|
||||
p.in3 = face.in3
|
||||
return p
|
||||
|
||||
def write(self, f):
|
||||
f.write(self.ftype.value)
|
||||
f.write(self.r0)
|
||||
f.write(self.g0)
|
||||
f.write(self.b0)
|
||||
f.write(self.r1)
|
||||
f.write(self.g1)
|
||||
f.write(self.b1)
|
||||
f.write(self.r2)
|
||||
f.write(self.g2)
|
||||
f.write(self.b2)
|
||||
f.write(self.r3)
|
||||
f.write(self.g3)
|
||||
f.write(self.b3)
|
||||
f.write(self.iv0)
|
||||
f.write(self.iv1)
|
||||
f.write(self.iv2)
|
||||
f.write(self.iv3)
|
||||
f.write(self.in0)
|
||||
f.write(self.in1)
|
||||
f.write(self.in2)
|
||||
f.write(self.in3)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PolyFT3:
|
||||
ftype: c_ubyte = PolyType.FT3
|
||||
u0: c_ubyte = c_ubyte(0)
|
||||
v0: c_ubyte = c_ubyte(0)
|
||||
u1: c_ubyte = c_ubyte(0)
|
||||
v1: c_ubyte = c_ubyte(0)
|
||||
u2: c_ubyte = c_ubyte(0)
|
||||
v2: c_ubyte = c_ubyte(0)
|
||||
r0: c_ubyte = c_ubyte(0)
|
||||
g0: c_ubyte = c_ubyte(0)
|
||||
b0: c_ubyte = c_ubyte(0)
|
||||
iv0: c_ushort = c_ushort(0)
|
||||
iv1: c_ushort = c_ushort(0)
|
||||
iv2: c_ushort = c_ushort(0)
|
||||
in0: c_ushort = c_ushort(0)
|
||||
|
||||
@staticmethod
|
||||
def from_pair(face: TriangleFace, material: TextureFlatMaterial) -> "PolyFT3":
|
||||
p = PolyFT3()
|
||||
p.u0 = material.u0
|
||||
p.v0 = material.v0
|
||||
p.u1 = material.u1
|
||||
p.v1 = material.v1
|
||||
p.u2 = material.u2
|
||||
p.v2 = material.v2
|
||||
p.r0 = material.r0
|
||||
p.g0 = material.g0
|
||||
p.b0 = material.b0
|
||||
p.iv0 = face.iv0
|
||||
p.iv1 = face.iv1
|
||||
p.iv2 = face.iv2
|
||||
p.in0 = face.in0
|
||||
return p
|
||||
|
||||
def write(self, f):
|
||||
f.write(self.ftype.value)
|
||||
f.write(self.u0)
|
||||
f.write(self.v0)
|
||||
f.write(self.u1)
|
||||
f.write(self.v1)
|
||||
f.write(self.u2)
|
||||
f.write(self.v2)
|
||||
f.write(self.r0)
|
||||
f.write(self.g0)
|
||||
f.write(self.b0)
|
||||
f.write(self.iv0)
|
||||
f.write(self.iv1)
|
||||
f.write(self.iv2)
|
||||
f.write(self.in0)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PolyGT3:
|
||||
ftype: c_ubyte = PolyType.GT3
|
||||
u0: c_ubyte = c_ubyte(0)
|
||||
v0: c_ubyte = c_ubyte(0)
|
||||
u1: c_ubyte = c_ubyte(0)
|
||||
v1: c_ubyte = c_ubyte(0)
|
||||
u2: c_ubyte = c_ubyte(0)
|
||||
v2: c_ubyte = c_ubyte(0)
|
||||
r0: c_ubyte = c_ubyte(0)
|
||||
g0: c_ubyte = c_ubyte(0)
|
||||
b0: c_ubyte = c_ubyte(0)
|
||||
r1: c_ubyte = c_ubyte(0)
|
||||
g1: c_ubyte = c_ubyte(0)
|
||||
b1: c_ubyte = c_ubyte(0)
|
||||
r2: c_ubyte = c_ubyte(0)
|
||||
g2: c_ubyte = c_ubyte(0)
|
||||
b2: c_ubyte = c_ubyte(0)
|
||||
iv0: c_ushort = c_ushort(0)
|
||||
iv1: c_ushort = c_ushort(0)
|
||||
iv2: c_ushort = c_ushort(0)
|
||||
in0: c_ushort = c_ushort(0)
|
||||
in1: c_ushort = c_ushort(0)
|
||||
in2: c_ushort = c_ushort(0)
|
||||
|
||||
@staticmethod
|
||||
def from_pair(face: TriangleFace, material: TextureGouraudMaterial) -> "PolyGT3":
|
||||
p = PolyGT3()
|
||||
p.u0 = material.u0
|
||||
p.v0 = material.v0
|
||||
p.u1 = material.u1
|
||||
p.v1 = material.v1
|
||||
p.u2 = material.u2
|
||||
p.v2 = material.v2
|
||||
p.r0 = material.r0
|
||||
p.g0 = material.g0
|
||||
p.b0 = material.b0
|
||||
p.r1 = material.r1
|
||||
p.g1 = material.g1
|
||||
p.b1 = material.b1
|
||||
p.r2 = material.r2
|
||||
p.g2 = material.g2
|
||||
p.b2 = material.b2
|
||||
p.iv0 = face.iv0
|
||||
p.iv1 = face.iv1
|
||||
p.iv2 = face.iv2
|
||||
p.in0 = face.in0
|
||||
p.in1 = face.in1
|
||||
p.in2 = face.in2
|
||||
return p
|
||||
|
||||
def write(self, f):
|
||||
f.write(self.ftype.value)
|
||||
f.write(self.u0)
|
||||
f.write(self.v0)
|
||||
f.write(self.u1)
|
||||
f.write(self.v1)
|
||||
f.write(self.u2)
|
||||
f.write(self.v2)
|
||||
f.write(self.r0)
|
||||
f.write(self.g0)
|
||||
f.write(self.b0)
|
||||
f.write(self.r1)
|
||||
f.write(self.g1)
|
||||
f.write(self.b1)
|
||||
f.write(self.r2)
|
||||
f.write(self.g2)
|
||||
f.write(self.b2)
|
||||
f.write(self.iv0)
|
||||
f.write(self.iv1)
|
||||
f.write(self.iv2)
|
||||
f.write(self.in0)
|
||||
f.write(self.in1)
|
||||
f.write(self.in2)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PolyFT4:
|
||||
ftype: c_ubyte = PolyType.FT4
|
||||
u0: c_ubyte = c_ubyte(0)
|
||||
v0: c_ubyte = c_ubyte(0)
|
||||
u1: c_ubyte = c_ubyte(0)
|
||||
v1: c_ubyte = c_ubyte(0)
|
||||
u2: c_ubyte = c_ubyte(0)
|
||||
v2: c_ubyte = c_ubyte(0)
|
||||
u3: c_ubyte = c_ubyte(0)
|
||||
v3: c_ubyte = c_ubyte(0)
|
||||
r0: c_ubyte = c_ubyte(0)
|
||||
g0: c_ubyte = c_ubyte(0)
|
||||
b0: c_ubyte = c_ubyte(0)
|
||||
iv0: c_ushort = c_ushort(0)
|
||||
iv1: c_ushort = c_ushort(0)
|
||||
iv2: c_ushort = c_ushort(0)
|
||||
iv3: c_ushort = c_ushort(0)
|
||||
in0: c_ushort = c_ushort(0)
|
||||
|
||||
@staticmethod
|
||||
def from_pair(face: QuadFace, material: TextureFlatMaterial) -> "PolyFT4":
|
||||
p = PolyFT4()
|
||||
f.write(self.u0)
|
||||
f.write(self.v0)
|
||||
f.write(self.u1)
|
||||
f.write(self.v1)
|
||||
f.write(self.u2)
|
||||
f.write(self.v2)
|
||||
f.write(self.u3)
|
||||
f.write(self.v3)
|
||||
f.write(self.r0)
|
||||
f.write(self.g0)
|
||||
f.write(self.b0)
|
||||
f.write(self.iv0)
|
||||
f.write(self.iv1)
|
||||
f.write(self.iv2)
|
||||
f.write(self.iv3)
|
||||
f.write(self.in0)
|
||||
return p
|
||||
|
||||
def write(self, f):
|
||||
f.write(self.ftype.value)
|
||||
f.write(self.u0)
|
||||
f.write(self.v0)
|
||||
f.write(self.u1)
|
||||
f.write(self.v1)
|
||||
f.write(self.u2)
|
||||
f.write(self.v2)
|
||||
f.write(self.u3)
|
||||
f.write(self.v3)
|
||||
f.write(self.r0)
|
||||
f.write(self.g0)
|
||||
f.write(self.b0)
|
||||
f.write(self.iv0)
|
||||
f.write(self.iv1)
|
||||
f.write(self.iv2)
|
||||
f.write(self.iv3)
|
||||
f.write(self.in0)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PolyGT4:
|
||||
ftype: c_ubyte = PolyType.GT4
|
||||
u0: c_ubyte = c_ubyte(0)
|
||||
v0: c_ubyte = c_ubyte(0)
|
||||
u1: c_ubyte = c_ubyte(0)
|
||||
v1: c_ubyte = c_ubyte(0)
|
||||
u2: c_ubyte = c_ubyte(0)
|
||||
v2: c_ubyte = c_ubyte(0)
|
||||
u3: c_ubyte = c_ubyte(0)
|
||||
v3: c_ubyte = c_ubyte(0)
|
||||
r0: c_ubyte = c_ubyte(0)
|
||||
g0: c_ubyte = c_ubyte(0)
|
||||
b0: c_ubyte = c_ubyte(0)
|
||||
r1: c_ubyte = c_ubyte(0)
|
||||
g1: c_ubyte = c_ubyte(0)
|
||||
b1: c_ubyte = c_ubyte(0)
|
||||
r2: c_ubyte = c_ubyte(0)
|
||||
g2: c_ubyte = c_ubyte(0)
|
||||
b2: c_ubyte = c_ubyte(0)
|
||||
r3: c_ubyte = c_ubyte(0)
|
||||
g3: c_ubyte = c_ubyte(0)
|
||||
b3: c_ubyte = c_ubyte(0)
|
||||
iv0: c_ushort = c_ushort(0)
|
||||
iv1: c_ushort = c_ushort(0)
|
||||
iv2: c_ushort = c_ushort(0)
|
||||
iv3: c_ushort = c_ushort(0)
|
||||
in0: c_ushort = c_ushort(0)
|
||||
in1: c_ushort = c_ushort(0)
|
||||
in2: c_ushort = c_ushort(0)
|
||||
in3: c_ushort = c_ushort(0)
|
||||
|
||||
@staticmethod
|
||||
def from_pair(face: QuadFace, material: TextureGouraudMaterial) -> "PolyGT4":
|
||||
p = PolyGT4()
|
||||
f.write(self.u0)
|
||||
f.write(self.v0)
|
||||
f.write(self.u1)
|
||||
f.write(self.v1)
|
||||
f.write(self.u2)
|
||||
f.write(self.v2)
|
||||
f.write(self.u3)
|
||||
f.write(self.v3)
|
||||
f.write(self.r0)
|
||||
f.write(self.g0)
|
||||
f.write(self.b0)
|
||||
f.write(self.r1)
|
||||
f.write(self.g1)
|
||||
f.write(self.b1)
|
||||
f.write(self.r2)
|
||||
f.write(self.g2)
|
||||
f.write(self.b2)
|
||||
f.write(self.r3)
|
||||
f.write(self.g3)
|
||||
f.write(self.b3)
|
||||
f.write(self.iv0)
|
||||
f.write(self.iv1)
|
||||
f.write(self.iv2)
|
||||
f.write(self.iv3)
|
||||
f.write(self.in0)
|
||||
f.write(self.in1)
|
||||
f.write(self.in2)
|
||||
f.write(self.in3)
|
||||
return p
|
||||
|
||||
def write(self, f):
|
||||
f.write(self.ftype.value)
|
||||
f.write(self.u0)
|
||||
f.write(self.v0)
|
||||
f.write(self.u1)
|
||||
f.write(self.v1)
|
||||
f.write(self.u2)
|
||||
f.write(self.v2)
|
||||
f.write(self.u3)
|
||||
f.write(self.v3)
|
||||
f.write(self.r0)
|
||||
f.write(self.g0)
|
||||
f.write(self.b0)
|
||||
f.write(self.r1)
|
||||
f.write(self.g1)
|
||||
f.write(self.b1)
|
||||
f.write(self.r2)
|
||||
f.write(self.g2)
|
||||
f.write(self.b2)
|
||||
f.write(self.r3)
|
||||
f.write(self.g3)
|
||||
f.write(self.b3)
|
||||
f.write(self.iv0)
|
||||
f.write(self.iv1)
|
||||
f.write(self.iv2)
|
||||
f.write(self.iv3)
|
||||
f.write(self.in0)
|
||||
f.write(self.in1)
|
||||
f.write(self.in2)
|
||||
f.write(self.in3)
|
||||
|
||||
|
||||
Polygon = PolyF3 | PolyG3 | PolyF4 | PolyG4 | PolyFT3 | PolyGT3 | PolyFT4 | PolyGT4
|
||||
|
||||
|
||||
def gen_polygon(face, material) -> Polygon:
|
||||
ftype = type(face)
|
||||
mtype = type(material.info)
|
||||
|
||||
if ftype == TriangleFace:
|
||||
if mtype == FlatMaterial:
|
||||
return PolyF3.from_pair(face, material.info)
|
||||
elif mtype == GouraudMaterial:
|
||||
return PolyG3.from_pair(face, material.info)
|
||||
elif mtype == TextureFlatMaterial:
|
||||
return PolyFT3.from_pair(face, material.info)
|
||||
elif mtype == TextureGouraudMaterial:
|
||||
return PolyGT3.from_pair(face, material.info)
|
||||
else:
|
||||
print(f"Unknown material type {mtype}")
|
||||
exit(1)
|
||||
elif ftype == QuadFace:
|
||||
if mtype == FlatMaterial:
|
||||
return PolyF4.from_pair(face, material.info)
|
||||
elif mtype == GouraudMaterial:
|
||||
return PolyG4.from_pair(face, material.info)
|
||||
elif mtype == TextureFlatMaterial:
|
||||
return PolyFT4.from_pair(face, material.info)
|
||||
elif mtype == TextureGouraudMaterial:
|
||||
return PolyGT4.from_pair(face, material.info)
|
||||
else:
|
||||
print(f"Unknown material type {mtype}")
|
||||
exit(1)
|
||||
else:
|
||||
print(f"Unknown face type {type(face)}")
|
||||
exit(1)
|
||||
|
||||
|
||||
@dataclass
|
||||
class MDLModel:
|
||||
num_vertices: c_ushort = c_ushort(0)
|
||||
num_normals: c_ushort = c_ushort(0)
|
||||
num_polys: c_ushort = c_ushort(0)
|
||||
vertices: [SVECTOR] = field(default_factory=list)
|
||||
normals: [SVECTOR] = field(default_factory=list)
|
||||
polygons: [Polygon] = field(default_factory=list)
|
||||
|
||||
def write(self, f):
|
||||
f.write(self.num_vertices)
|
||||
f.write(self.num_normals)
|
||||
f.write(self.num_polys)
|
||||
for v in self.vertices:
|
||||
f.write(v.vx)
|
||||
f.write(v.vy)
|
||||
f.write(v.vz)
|
||||
for n in self.normals:
|
||||
f.write(n.vx)
|
||||
f.write(n.vy)
|
||||
f.write(n.vz)
|
||||
for p in self.polygons:
|
||||
p.write(f)
|
||||
|
||||
|
||||
def convert_model(rsd: RSDModel) -> MDLModel:
|
||||
model = MDLModel()
|
||||
model.num_vertices = rsd.ply.num_vertices
|
||||
model.num_normals = rsd.ply.num_normals
|
||||
model.vertices = rsd.ply.vertices
|
||||
model.normals = rsd.ply.normals
|
||||
|
||||
pairs = rsd.ply.pair_materials_and_faces(rsd.mat.materials)
|
||||
for pair in pairs:
|
||||
model.polygons.append(gen_polygon(pair[0], pair[1]))
|
||||
model.num_polys = c_ushort(len(model.polygons))
|
||||
|
||||
return model
|
65
tools/convrsd/face.py
Normal file
65
tools/convrsd/face.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
from dataclasses import dataclass, field
|
||||
from common import *
|
||||
|
||||
|
||||
@dataclass
|
||||
class TriangleFace:
|
||||
iv0: c_ushort = c_ushort(0)
|
||||
iv1: c_ushort = c_ushort(0)
|
||||
iv2: c_ushort = c_ushort(0)
|
||||
__unused0: c_ushort = c_ushort(0)
|
||||
in0: c_ushort = c_ushort(0)
|
||||
in1: c_ushort = c_ushort(0)
|
||||
in2: c_ushort = c_ushort(0)
|
||||
__unused1: c_ushort = c_ushort(0)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Triangle iv0:{self.iv0.value}, iv1:{self.iv1.value}, iv2:{self.iv2.value}, in0:{self.in0.value}, in1:{self.in1.value}, in2{self.in2.value}>"
|
||||
|
||||
|
||||
@dataclass
|
||||
class QuadFace:
|
||||
iv0: c_ushort = c_ushort(0)
|
||||
iv1: c_ushort = c_ushort(0)
|
||||
iv2: c_ushort = c_ushort(0)
|
||||
iv3: c_ushort = c_ushort(0)
|
||||
in0: c_ushort = c_ushort(0)
|
||||
in1: c_ushort = c_ushort(0)
|
||||
in2: c_ushort = c_ushort(0)
|
||||
in3: c_ushort = c_ushort(0)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Quad iv0:{self.iv0.value}, iv1:{self.iv1.value}, iv2:{self.iv2.value}, iv3:{self.iv3.value}, in0:{self.in0.value}, in1:{self.in1.value}, in2{self.in2.value}, in3:{self.in3.value}>"
|
||||
|
||||
|
||||
@dataclass
|
||||
class LineFace:
|
||||
iv0: c_ushort = c_ushort(0)
|
||||
iv1: c_ushort = c_ushort(0)
|
||||
__unused0: c_ushort = c_ushort(0)
|
||||
__unused1: c_ushort = c_ushort(0)
|
||||
__unused2: c_ushort = c_ushort(0)
|
||||
__unused3: c_ushort = c_ushort(0)
|
||||
__unused4: c_ushort = c_ushort(0)
|
||||
__unused5: c_ushort = c_ushort(0)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Line iv0:{self.iv0.value}, iv1:{self.iv1.value}>"
|
||||
|
||||
|
||||
@dataclass
|
||||
class SpriteFace:
|
||||
iv0: c_ushort = c_ushort(0)
|
||||
iv1: c_ushort = c_ushort(0)
|
||||
iw: c_ushort = c_ushort(0)
|
||||
ih: c_ushort = c_ushort(0)
|
||||
__unused0: c_ushort = c_ushort(0)
|
||||
__unused1: c_ushort = c_ushort(0)
|
||||
__unused2: c_ushort = c_ushort(0)
|
||||
__unused3: c_ushort = c_ushort(0)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Sprite iv0:{self.iv0.value}, iv1:{self.iv1.value}, iw:{self.iv2.value}, ih:{self.iv3.value}>"
|
||||
|
||||
|
||||
Face = TriangleFace | QuadFace | LineFace | SpriteFace
|
6
tools/convrsd/fixedpoint.py
Normal file
6
tools/convrsd/fixedpoint.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
def tofixed(value: float, scale: int) -> int:
|
||||
return int(value * (1 << scale))
|
||||
|
||||
|
||||
def tofixed12(value: float) -> int:
|
||||
return tofixed(value, 12)
|
37
tools/convrsd/import_types.py
Normal file
37
tools/convrsd/import_types.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
from dataclasses import dataclass, field
|
||||
|
||||
from common import *
|
||||
from face import *
|
||||
from material import *
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlyData:
|
||||
magic: str = ""
|
||||
num_vertices: c_ushort = c_ushort(0)
|
||||
num_normals: c_ushort = c_ushort(0)
|
||||
numfaces: c_ushort = c_ushort(0)
|
||||
vertices: [SVECTOR] = field(default_factory=list)
|
||||
normals: [SVECTOR] = field(default_factory=list)
|
||||
faces: [Face] = field(default_factory=list)
|
||||
|
||||
def pair_materials_and_faces(self, materials) -> (Face, Material):
|
||||
pairs = []
|
||||
for material in materials:
|
||||
# Use type(face) to determine if triangle or quad
|
||||
# Use type(material.info) to determine if F, G, FT, GT
|
||||
pairs.append((self.faces[material.ipolygon.value], material))
|
||||
return pairs
|
||||
|
||||
|
||||
@dataclass
|
||||
class MatData:
|
||||
magic: str = ""
|
||||
num_items: c_ushort = c_ushort(0)
|
||||
materials: [Material] = field(default_factory=list)
|
||||
|
||||
|
||||
class RSDModel:
|
||||
magic: str = ""
|
||||
ply: PlyData | None = None
|
||||
mat: MatData | None = None
|
135
tools/convrsd/material.py
Normal file
135
tools/convrsd/material.py
Normal file
|
@ -0,0 +1,135 @@
|
|||
from dataclasses import dataclass, field
|
||||
|
||||
from common import *
|
||||
|
||||
|
||||
@dataclass
|
||||
class FlatMaterial:
|
||||
r0: c_ubyte = c_ubyte(0)
|
||||
g0: c_ubyte = c_ubyte(0)
|
||||
b0: c_ubyte = c_ubyte(0)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<F #{self.r0.value:02X}{self.g0.value:02X}{self.b0.value:02X}>"
|
||||
|
||||
|
||||
@dataclass
|
||||
class GouraudMaterial:
|
||||
r0: c_ubyte = c_ubyte(0)
|
||||
g0: c_ubyte = c_ubyte(0)
|
||||
b0: c_ubyte = c_ubyte(0)
|
||||
r1: c_ubyte = c_ubyte(0)
|
||||
g1: c_ubyte = c_ubyte(0)
|
||||
b1: c_ubyte = c_ubyte(0)
|
||||
r2: c_ubyte = c_ubyte(0)
|
||||
g2: c_ubyte = c_ubyte(0)
|
||||
b2: c_ubyte = c_ubyte(0)
|
||||
r3: c_ubyte = c_ubyte(0)
|
||||
g3: c_ubyte = c_ubyte(0)
|
||||
b3: c_ubyte = c_ubyte(0)
|
||||
|
||||
def __repr__(self):
|
||||
rgb0 = f"#{self.r0.value:02X}{self.g0.value:02X}{self.b0.value:02X}"
|
||||
rgb1 = f"#{self.r1.value:02X}{self.g1.value:02X}{self.b1.value:02X}"
|
||||
rgb2 = f"#{self.r2.value:02X}{self.g2.value:02X}{self.b2.value:02X}"
|
||||
rgb3 = f"#{self.r3.value:02X}{self.g3.value:02X}{self.b3.value:02X}"
|
||||
return f"<G {rgb0}, {rgb1}, {rgb2}, {rgb3}>"
|
||||
|
||||
|
||||
@dataclass
|
||||
class TextureMaterial:
|
||||
u0: c_ubyte = c_ubyte(0)
|
||||
v0: c_ubyte = c_ubyte(0)
|
||||
u1: c_ubyte = c_ubyte(0)
|
||||
v1: c_ubyte = c_ubyte(0)
|
||||
u2: c_ubyte = c_ubyte(0)
|
||||
v2: c_ubyte = c_ubyte(0)
|
||||
u3: c_ubyte = c_ubyte(0)
|
||||
v3: c_ubyte = c_ubyte(0)
|
||||
|
||||
def __repr__(self):
|
||||
uv0 = f"({self.u0.value:02X}, {self.v0.value:02X})"
|
||||
uv1 = f"({self.u1.value:02X}, {self.v1.value:02X})"
|
||||
uv2 = f"({self.u2.value:02X}, {self.v2.value:02X})"
|
||||
uv3 = f"({self.u3.value:02X}, {self.v3.value:02X})"
|
||||
return f"<T {uv0} {uv1} {uv2} {uv3}>"
|
||||
|
||||
|
||||
@dataclass
|
||||
class TextureFlatMaterial:
|
||||
u0: c_ubyte = c_ubyte(0)
|
||||
v0: c_ubyte = c_ubyte(0)
|
||||
u1: c_ubyte = c_ubyte(0)
|
||||
v1: c_ubyte = c_ubyte(0)
|
||||
u2: c_ubyte = c_ubyte(0)
|
||||
v2: c_ubyte = c_ubyte(0)
|
||||
u3: c_ubyte = c_ubyte(0)
|
||||
v3: c_ubyte = c_ubyte(0)
|
||||
r0: c_ubyte = c_ubyte(0)
|
||||
g0: c_ubyte = c_ubyte(0)
|
||||
b0: c_ubyte = c_ubyte(0)
|
||||
|
||||
def __repr__(self):
|
||||
uv0 = f"({self.u0.value:02X}, {self.v0.value:02X})"
|
||||
uv1 = f"({self.u1.value:02X}, {self.v1.value:02X})"
|
||||
uv2 = f"({self.u2.value:02X}, {self.v2.value:02X})"
|
||||
uv3 = f"({self.u3.value:02X}, {self.v3.value:02X})"
|
||||
rgb0 = f"#{self.r0.value:02X}{self.g0.value:02X}{self.b0.value:02X}"
|
||||
return f"<FT {rgb0} {{{uv0} {uv1} {uv2} {uv3}}}"
|
||||
|
||||
|
||||
@dataclass
|
||||
class TextureGouraudMaterial:
|
||||
u0: c_ubyte = c_ubyte(0)
|
||||
v0: c_ubyte = c_ubyte(0)
|
||||
u1: c_ubyte = c_ubyte(0)
|
||||
v1: c_ubyte = c_ubyte(0)
|
||||
u2: c_ubyte = c_ubyte(0)
|
||||
v2: c_ubyte = c_ubyte(0)
|
||||
u3: c_ubyte = c_ubyte(0)
|
||||
v3: c_ubyte = c_ubyte(0)
|
||||
r0: c_ubyte = c_ubyte(0)
|
||||
g0: c_ubyte = c_ubyte(0)
|
||||
b0: c_ubyte = c_ubyte(0)
|
||||
r1: c_ubyte = c_ubyte(0)
|
||||
g1: c_ubyte = c_ubyte(0)
|
||||
b1: c_ubyte = c_ubyte(0)
|
||||
r2: c_ubyte = c_ubyte(0)
|
||||
g2: c_ubyte = c_ubyte(0)
|
||||
b2: c_ubyte = c_ubyte(0)
|
||||
r3: c_ubyte = c_ubyte(0)
|
||||
g3: c_ubyte = c_ubyte(0)
|
||||
b3: c_ubyte = c_ubyte(0)
|
||||
|
||||
def __repr__(self):
|
||||
uv0 = f"({self.u0.value:02X}, {self.v0.value:02X})"
|
||||
uv1 = f"({self.u1.value:02X}, {self.v1.value:02X})"
|
||||
uv2 = f"({self.u2.value:02X}, {self.v2.value:02X})"
|
||||
uv3 = f"({self.u3.value:02X}, {self.v3.value:02X})"
|
||||
rgb0 = f"#{self.r0.value:02X}{self.g0.value:02X}{self.b0.value:02X}"
|
||||
rgb1 = f"#{self.r1.value:02X}{self.g1.value:02X}{self.b1.value:02X}"
|
||||
rgb2 = f"#{self.r2.value:02X}{self.g2.value:02X}{self.b2.value:02X}"
|
||||
rgb3 = f"#{self.r3.value:02X}{self.g3.value:02X}{self.b3.value:02X}"
|
||||
return (
|
||||
f"<GT {{{rgb0} {uv0}}} {{{rgb1} {uv1}}} {{{rgb2} {uv2}}} {{{rgb3} {uv3}}}>"
|
||||
)
|
||||
|
||||
|
||||
MaterialInfo = (
|
||||
FlatMaterial
|
||||
| GouraudMaterial
|
||||
| TextureMaterial
|
||||
| TextureFlatMaterial
|
||||
| TextureGouraudMaterial
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Material:
|
||||
ipolygon: c_ushort = c_ushort(0)
|
||||
flag: c_ubyte = c_ubyte(0)
|
||||
shading: ShadingType = ShadingType.FLAT
|
||||
info: MaterialInfo | None = None
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Material (Face {self.ipolygon.value}): {self.shading} ST({self.flag.value:02X}); {self.info}>"
|
|
@ -1,34 +0,0 @@
|
|||
#ifndef FIXEDPOINT_HPP
|
||||
#define FIXEDPOINT_HPP
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
|
||||
template<class Type, size_t Scale>
|
||||
class FixedPoint
|
||||
{
|
||||
private:
|
||||
const static Type factor = 1 << Scale;
|
||||
Type data;
|
||||
public:
|
||||
FixedPoint(double value) {
|
||||
*this = value;
|
||||
}
|
||||
|
||||
FixedPoint&
|
||||
operator=(double value) {
|
||||
this->data = static_cast<Type>(value * factor);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Type
|
||||
getRaw() const {
|
||||
return this->data;
|
||||
}
|
||||
};
|
||||
|
||||
typedef FixedPoint<int32_t, 12> psxfixed32;
|
||||
typedef FixedPoint<int16_t, 12> psxfixed16;
|
||||
|
||||
#endif
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#include <iostream>
|
||||
#include "fixedpoint.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
double value1 = 3.1416;
|
||||
double value2 = 1.0;
|
||||
|
||||
FixedPoint<int32_t, 12> fvalue1(value1);
|
||||
FixedPoint<int32_t, 12> fvalue2(value2);
|
||||
|
||||
std::cout << "Value 1: " << value1 << " -> " << fvalue1.getRaw() << std::endl
|
||||
<< "Value 2: " << value2 << " -> " << fvalue2.getRaw() << std::endl;
|
||||
return 0;
|
||||
}
|
|
@ -1,246 +0,0 @@
|
|||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include "fixedpoint.hpp"
|
||||
|
||||
struct SVECTOR
|
||||
{
|
||||
int16_t vx;
|
||||
int16_t vy;
|
||||
int16_t vz;
|
||||
//int16_t _unused;
|
||||
};
|
||||
|
||||
struct CVECTOR
|
||||
{
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
/* Face attributes */
|
||||
#define TYPE_TRIANGLE 0x0 // ARGS: FLAG V0 V1 V2 __ N0 N1 N2 __
|
||||
#define TYPE_QUAD 0x1 // ARGS: FLAG V0 V1 V2 V3 N0 N1 N2 N3
|
||||
#define TYPE_LINE 0x2 // ARGS: FLAG V0 V1 __ __ __ __ __ __
|
||||
#define TYPE_SPRITE 0x3 // ARGS: FLAG V0 W H
|
||||
|
||||
union Face
|
||||
{
|
||||
struct {
|
||||
uint8_t flag;
|
||||
uint16_t param[8];
|
||||
} gn; // Generic
|
||||
|
||||
struct {
|
||||
uint8_t flag; // flag == 0
|
||||
uint16_t iv0;
|
||||
uint16_t iv1;
|
||||
uint16_t iv2;
|
||||
uint16_t _unused0;
|
||||
uint16_t in0;
|
||||
uint16_t in1;
|
||||
uint16_t in2;
|
||||
uint16_t _unused1;
|
||||
} triangle;
|
||||
|
||||
struct {
|
||||
uint8_t flag; // flag == 1
|
||||
uint16_t iv0;
|
||||
uint16_t iv1;
|
||||
uint16_t iv2;
|
||||
uint16_t iv3;
|
||||
uint16_t in0;
|
||||
uint16_t in1;
|
||||
uint16_t in2;
|
||||
uint16_t in3;
|
||||
} quad;
|
||||
|
||||
struct {
|
||||
uint8_t flag; // flag == 2
|
||||
uint16_t iv0;
|
||||
uint16_t iv1;
|
||||
uint16_t __unused0;
|
||||
uint16_t __unused1;
|
||||
uint16_t __unused2;
|
||||
uint16_t __unused3;
|
||||
uint16_t __unused4;
|
||||
uint16_t __unused5;
|
||||
} line;
|
||||
|
||||
struct {
|
||||
uint8_t flag; // flag == 3
|
||||
uint16_t iv0;
|
||||
uint16_t iv1;
|
||||
uint16_t iw;
|
||||
uint16_t ih;
|
||||
uint16_t __unused2;
|
||||
uint16_t __unused3;
|
||||
uint16_t __unused4;
|
||||
uint16_t __unused5;
|
||||
} sprite;
|
||||
};
|
||||
|
||||
// ========================
|
||||
|
||||
enum MaterialType
|
||||
{
|
||||
MATERIAL_TYPE_C, // "C": Flat colored
|
||||
MATERIAL_TYPE_G, // "G": Gouraud shaded
|
||||
MATERIAL_TYPE_T, // "T": Textured no-color
|
||||
MATERIAL_TYPE_D, // "T": Textured, flat colored
|
||||
MATERIAL_TYPE_H, // "H": Textured, gouraud shaded
|
||||
|
||||
// MATERIAL_TYPE_W, // "W": Repeating textures, no-color
|
||||
// MATERIAL_TYPE_S, // "S": Repeating textures, flat colored
|
||||
// MATERIAL_TYPE_N, // "N": Repeating textures, gouraud shaded
|
||||
};
|
||||
|
||||
union MaterialInfo
|
||||
{
|
||||
MaterialType type;
|
||||
|
||||
struct {
|
||||
MaterialType type;
|
||||
uint8_t r0;
|
||||
uint8_t g0;
|
||||
uint8_t b0;
|
||||
} f;
|
||||
|
||||
struct {
|
||||
MaterialType type;
|
||||
uint8_t r0;
|
||||
uint8_t g0;
|
||||
uint8_t b0;
|
||||
uint8_t r1;
|
||||
uint8_t g1;
|
||||
uint8_t b1;
|
||||
uint8_t r2;
|
||||
uint8_t g2;
|
||||
uint8_t b2;
|
||||
uint8_t r3;
|
||||
uint8_t g3;
|
||||
uint8_t b3;
|
||||
} g;
|
||||
|
||||
struct {
|
||||
MaterialType type;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
uint8_t u3;
|
||||
uint8_t v3;
|
||||
} t;
|
||||
|
||||
struct {
|
||||
MaterialType type;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
uint8_t u3;
|
||||
uint8_t v3;
|
||||
uint8_t r0;
|
||||
uint8_t g0;
|
||||
uint8_t b0;
|
||||
} ft;
|
||||
|
||||
struct {
|
||||
MaterialType type;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
uint8_t u3;
|
||||
uint8_t v3;
|
||||
uint8_t r0;
|
||||
uint8_t g0;
|
||||
uint8_t b0;
|
||||
uint8_t r1;
|
||||
uint8_t g1;
|
||||
uint8_t b1;
|
||||
uint8_t r2;
|
||||
uint8_t g2;
|
||||
uint8_t b2;
|
||||
uint8_t r3;
|
||||
uint8_t g3;
|
||||
uint8_t b3;
|
||||
} gt;
|
||||
|
||||
// Won't care about the rest
|
||||
};
|
||||
|
||||
enum ShadingType
|
||||
{
|
||||
SHADING_TYPE_FLAT, // "F"
|
||||
SHADING_TYPE_GOURAUD, // "G"
|
||||
};
|
||||
|
||||
struct Material
|
||||
{
|
||||
// Can be in 0-5 format, specifies polygons 0 thru 5 use this material
|
||||
uint16_t ipolygon;
|
||||
uint8_t flag;
|
||||
ShadingType shading;
|
||||
MaterialInfo info;
|
||||
};
|
||||
|
||||
// ========================
|
||||
|
||||
struct PlyData
|
||||
{
|
||||
char magic[11];
|
||||
uint32_t num_vertices;
|
||||
uint32_t num_normals;
|
||||
uint32_t num_faces;
|
||||
SVECTOR *vertices;
|
||||
SVECTOR *normals;
|
||||
Face *faces;
|
||||
};
|
||||
|
||||
struct MatData
|
||||
{
|
||||
char magic[11];
|
||||
uint16_t num_items;
|
||||
Material *data;
|
||||
};
|
||||
|
||||
// ========================
|
||||
|
||||
struct RSDModel
|
||||
{
|
||||
char magic[11];
|
||||
PlyData ply;
|
||||
MatData mat;
|
||||
// TODO: NTEX and TEX[n] for textures
|
||||
};
|
||||
|
||||
// ========================
|
||||
|
||||
void
|
||||
parse_rsd(const char *filename, RSDModel &model)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
parse_ply(const char *filename, PlyData &ply)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
parse_mat(const char *filename, MatData &mat)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
// Read and parse RSD file and its acessories
|
||||
// Convert RSD to actual engine model format (still don't know what to do here...)
|
||||
return 0;
|
||||
}
|
122
tools/layouts/mdl.hexpat
Normal file
122
tools/layouts/mdl.hexpat
Normal file
|
@ -0,0 +1,122 @@
|
|||
#include <type/color.pat>
|
||||
|
||||
enum PolygonType: u8 {
|
||||
F3 = 0,
|
||||
G3 = 1,
|
||||
F4 = 2,
|
||||
G4 = 3,
|
||||
FT3 = 4,
|
||||
GT3 = 5,
|
||||
FT4 = 6,
|
||||
GT4 = 7,
|
||||
};
|
||||
|
||||
struct Polygon {
|
||||
PolygonType ftype;
|
||||
if(ftype == PolygonType::F3) {
|
||||
le type::RGB8 rgb0;
|
||||
be u16 iv0;
|
||||
be u16 iv1;
|
||||
be u16 iv2;
|
||||
be u16 in0;
|
||||
} else if(ftype == PolygonType::G3) {
|
||||
le type::RGB8 rgb0;
|
||||
le type::RGB8 rgb1;
|
||||
le type::RGB8 rgb2;
|
||||
be u16 iv0;
|
||||
be u16 iv1;
|
||||
be u16 iv2;
|
||||
be u16 in0;
|
||||
be u16 in1;
|
||||
be u16 in2;
|
||||
} else if(ftype == PolygonType::F4) {
|
||||
le type::RGB8 rgb0;
|
||||
be u16 iv0;
|
||||
be u16 iv1;
|
||||
be u16 iv2;
|
||||
be u16 iv3;
|
||||
be u16 in0;
|
||||
} else if(ftype == PolygonType::G4) {
|
||||
le type::RGB8 rgb0;
|
||||
le type::RGB8 rgb1;
|
||||
le type::RGB8 rgb2;
|
||||
le type::RGB8 rgb3;
|
||||
be u16 iv0;
|
||||
be u16 iv1;
|
||||
be u16 iv2;
|
||||
be u16 iv3;
|
||||
be u16 in0;
|
||||
be u16 in1;
|
||||
be u16 in2;
|
||||
be u16 in3;
|
||||
} else if(ftype == PolygonType::FT3) {
|
||||
u8 u0; u8 v0;
|
||||
u8 u1; u8 v1;
|
||||
u8 u2; u8 v2;
|
||||
le type::RGB8 rgb0;
|
||||
be u16 iv0;
|
||||
be u16 iv1;
|
||||
be u16 iv2;
|
||||
be u16 in0;
|
||||
} else if(ftype == PolygonType::GT3) {
|
||||
u8 u0; u8 v0;
|
||||
u8 u1; u8 v1;
|
||||
u8 u2; u8 v2;
|
||||
le type::RGB8 rgb0;
|
||||
le type::RGB8 rgb1;
|
||||
le type::RGB8 rgb2;
|
||||
be u16 iv0;
|
||||
be u16 iv1;
|
||||
be u16 iv2;
|
||||
be u16 in0;
|
||||
be u16 in1;
|
||||
be u16 in2;
|
||||
} else if(ftype == PolygonType::FT4) {
|
||||
u8 u0; u8 v0;
|
||||
u8 u1; u8 v1;
|
||||
u8 u2; u8 v2;
|
||||
u8 u3; u8 v3;
|
||||
le type::RGB8 rgb0;
|
||||
be u16 iv0;
|
||||
be u16 iv1;
|
||||
be u16 iv2;
|
||||
be u16 iv3;
|
||||
be u16 in0;
|
||||
} else if(ftype == PolygonType::GT4) {
|
||||
u8 u0; u8 v0;
|
||||
u8 u1; u8 v1;
|
||||
u8 u2; u8 v2;
|
||||
u8 u3; u8 v3;
|
||||
le type::RGB8 rgb0;
|
||||
le type::RGB8 rgb1;
|
||||
le type::RGB8 rgb2;
|
||||
le type::RGB8 rgb3;
|
||||
be u16 iv0;
|
||||
be u16 iv1;
|
||||
be u16 iv2;
|
||||
be u16 iv3;
|
||||
be u16 in0;
|
||||
be u16 in1;
|
||||
be u16 in2;
|
||||
be u16 in3;
|
||||
} else {
|
||||
std::error(std::format("Undefined polygon type: {}\n", ftype));
|
||||
}
|
||||
};
|
||||
|
||||
struct SVECTOR {
|
||||
be s16 vx;
|
||||
be s16 vy;
|
||||
be s16 vz;
|
||||
};
|
||||
|
||||
struct Model {
|
||||
be u16 num_vertices;
|
||||
be u16 num_normals;
|
||||
be u16 num_polys;
|
||||
SVECTOR vertices[num_vertices];
|
||||
SVECTOR normals[num_normals];
|
||||
Polygon polygons[num_polys];
|
||||
};
|
||||
|
||||
Model data @ 0x00;
|
Loading…
Add table
Add a link
Reference in a new issue