ok
This commit is contained in:
parent
aa6adeb000
commit
237bcd2137
0
lib/__init__.py
Normal file
0
lib/__init__.py
Normal file
63
lib/fileutils.py
Normal file
63
lib/fileutils.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import struct
|
||||||
|
|
||||||
|
|
||||||
|
def read_int(fp):
|
||||||
|
return struct.unpack("<L", fp.read(4))[0]
|
||||||
|
|
||||||
|
|
||||||
|
def read_byte(fp):
|
||||||
|
return struct.unpack("<B", fp.read(1))[0]
|
||||||
|
|
||||||
|
|
||||||
|
def read_float(fp):
|
||||||
|
return struct.unpack("<f", fp.read(4))[0]
|
||||||
|
|
||||||
|
|
||||||
|
def read_short(fp):
|
||||||
|
return struct.unpack("<H", fp.read(2))[0]
|
||||||
|
|
||||||
|
|
||||||
|
def read_string(fp, size):
|
||||||
|
return read_bytes(fp, size).decode("utf8").replace("\x00", "")
|
||||||
|
|
||||||
|
|
||||||
|
def read_bytes(fp, size):
|
||||||
|
return bytes(struct.unpack('<' + str(size) + 'B', fp.read(size)))
|
||||||
|
|
||||||
|
|
||||||
|
def decompress(compressed_bytes, original_size: int):
|
||||||
|
i = 0
|
||||||
|
j = 0
|
||||||
|
dec_byte = 0
|
||||||
|
dec_bits = 8
|
||||||
|
buff_start = 0xFEE
|
||||||
|
|
||||||
|
res = bytearray(original_size)
|
||||||
|
|
||||||
|
if original_size == 0:
|
||||||
|
return res
|
||||||
|
|
||||||
|
while j < original_size:
|
||||||
|
if dec_bits == 8:
|
||||||
|
dec_byte = compressed_bytes[i]
|
||||||
|
i += 1
|
||||||
|
dec_bits = 0
|
||||||
|
if (dec_byte >> dec_bits & 1) == 0:
|
||||||
|
dec_pos = ((compressed_bytes[i] + (
|
||||||
|
(compressed_bytes[i + 1] & 0xF0) << 4) - buff_start - j) & 0xFFF) - 0x1000 + j
|
||||||
|
dec_len = (compressed_bytes[i + 1] & 0xF) + 3
|
||||||
|
i += 2
|
||||||
|
while dec_len > 0:
|
||||||
|
if dec_pos >= 0:
|
||||||
|
res[j] = res[dec_pos]
|
||||||
|
else:
|
||||||
|
res[j] = 32
|
||||||
|
j += 1
|
||||||
|
dec_pos += 1
|
||||||
|
dec_len -= 1
|
||||||
|
else:
|
||||||
|
res[j] = compressed_bytes[i]
|
||||||
|
i += 1
|
||||||
|
j += 1
|
||||||
|
dec_bits += 1
|
||||||
|
return res
|
71
lib/game.py
Normal file
71
lib/game.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
|
class Vec2:
|
||||||
|
def __init__(self, x=0.0, y=0.0):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "[%s,%s]" % (self.x, self.y)
|
||||||
|
|
||||||
|
|
||||||
|
class Vec3:
|
||||||
|
def __init__(self, x=0.0, y=0.0, z=0.0):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.z = z
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
return Vec3(self.x + other.x, self.y + other.y, self.z + other.z)
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
return self + -other
|
||||||
|
|
||||||
|
def __radd__(self, other):
|
||||||
|
return Vec3(self.x + other.x, self.y + other.y, self.z + other.z)
|
||||||
|
|
||||||
|
def __mul__(self, scalar):
|
||||||
|
return Vec3(self.x * scalar, self.y * scalar, self.z * scalar)
|
||||||
|
|
||||||
|
def __rmul__(self, scalar):
|
||||||
|
return Vec3(self.x * scalar, self.y * scalar, self.z * scalar)
|
||||||
|
|
||||||
|
def __neg__(self):
|
||||||
|
return Vec3(-self.x, -self.y, -self.z)
|
||||||
|
|
||||||
|
def __pos__(self):
|
||||||
|
return Vec3(self.x, self.y, self.z)
|
||||||
|
|
||||||
|
def __xor__(self, other):
|
||||||
|
cx = self.y * other.z - self.z * other.y
|
||||||
|
cy = self.z * other.x - self.x * other.z
|
||||||
|
cz = self.x * other.y - self.y * other.x
|
||||||
|
return Vec3(cx, cy, cz)
|
||||||
|
|
||||||
|
def cross(self, other):
|
||||||
|
return self ^ other
|
||||||
|
|
||||||
|
def normalize(self):
|
||||||
|
w = math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)
|
||||||
|
if w == 0:
|
||||||
|
return Vec3()
|
||||||
|
else:
|
||||||
|
return Vec3(self.x / w, self.y / w, self.z / w)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "[%s,%s,%s]" % (self.x, self.y, self.z)
|
||||||
|
|
||||||
|
|
||||||
|
class VecRGB:
|
||||||
|
def __init__(self, r=0, g=0, b=0):
|
||||||
|
self.r = r
|
||||||
|
self.g = g
|
||||||
|
self.b = b
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "[%s,%s,%s]" % (self.r, self.g, self.b)
|
||||||
|
|
||||||
|
|
||||||
|
def cross(v1: Vec3, v2: Vec3):
|
||||||
|
return v1.cross(v2)
|
70
lib/packet.py
Normal file
70
lib/packet.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import io
|
||||||
|
import struct
|
||||||
|
|
||||||
|
|
||||||
|
class Packet(io.BytesIO):
|
||||||
|
def put_byte(self, val) -> None:
|
||||||
|
self.write(struct.pack('<B', val % 256))
|
||||||
|
|
||||||
|
def get_byte(self) -> int:
|
||||||
|
return struct.unpack('<B', self.read(1))[0]
|
||||||
|
|
||||||
|
def put_bytes(self, b) -> None:
|
||||||
|
for byte in b:
|
||||||
|
self.write(struct.pack('<B', byte))
|
||||||
|
|
||||||
|
def get_bytes(self, num) -> bytes:
|
||||||
|
return bytes(struct.unpack('<'+str(num)+'B', self.read(num)))
|
||||||
|
|
||||||
|
def put_short(self, val) -> None:
|
||||||
|
self.write(struct.pack('<H', val))
|
||||||
|
|
||||||
|
def get_short(self) -> int:
|
||||||
|
return struct.unpack('<H', self.read(2))[0]
|
||||||
|
|
||||||
|
def put_long(self, val) -> None:
|
||||||
|
self.write(struct.pack('<l', val))
|
||||||
|
|
||||||
|
def put_ulong(self, val) -> None:
|
||||||
|
self.write(struct.pack('<L', val))
|
||||||
|
|
||||||
|
def get_long(self) -> int:
|
||||||
|
return struct.unpack('<l', self.read(4))[0]
|
||||||
|
|
||||||
|
def get_ulong(self) -> int:
|
||||||
|
return struct.unpack('<L', self.read(4))[0]
|
||||||
|
|
||||||
|
def put_longlong(self, val) -> None:
|
||||||
|
self.write(struct.pack('<Q', val))
|
||||||
|
|
||||||
|
def get_longlong(self) -> int:
|
||||||
|
return struct.unpack('<Q', self.read(8))[0]
|
||||||
|
|
||||||
|
def put_float(self, val) -> None:
|
||||||
|
self.write(struct.pack('<f', val))
|
||||||
|
|
||||||
|
def get_float(self) -> float:
|
||||||
|
return struct.unpack('<f', self.read(4))[0]
|
||||||
|
|
||||||
|
def put_string(self, val) -> None:
|
||||||
|
self.write(val + b'\x00')
|
||||||
|
|
||||||
|
def put_string_size(self, val, size) -> None:
|
||||||
|
self.write(val.encode("utf8") + b"\x00" * (size - len(val)))
|
||||||
|
|
||||||
|
def get_string_until_none(self) -> str:
|
||||||
|
s = ""
|
||||||
|
c = self.get_byte()
|
||||||
|
while c != 0x00:
|
||||||
|
s += chr(c)
|
||||||
|
c = self.get_byte()
|
||||||
|
return s
|
||||||
|
|
||||||
|
def get_string(self, size) -> str:
|
||||||
|
return self.get_bytes(size).decode("utf8").replace("\x00", "")
|
||||||
|
|
||||||
|
def get_3bytes_int(self) -> int:
|
||||||
|
return struct.unpack("<L", self.read(3)+b"\x00")[0]
|
||||||
|
|
||||||
|
def put_3bytes_int(self, val) -> None:
|
||||||
|
self.write(struct.pack("<L", val)[:-1])
|
496
obj2gbs.py
Normal file
496
obj2gbs.py
Normal file
@ -0,0 +1,496 @@
|
|||||||
|
import os
|
||||||
|
from typing import List, Union
|
||||||
|
from lib.packet import Packet
|
||||||
|
from lib.fileutils import *
|
||||||
|
from lib.game import Vec3, VecRGB, cross
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
GBS_VERSION = 0xaa0100be
|
||||||
|
GBSFlagNormals = 0x0001
|
||||||
|
GBSFlagUVs = 0x0002
|
||||||
|
GBSFlagRGBs = 0x0004
|
||||||
|
GBSFlagCalcNormals = 0x0008
|
||||||
|
GBSFlagMaxLit = (1 << 31)
|
||||||
|
|
||||||
|
|
||||||
|
def resize(l: List, t, num):
|
||||||
|
l.clear()
|
||||||
|
for i in range(num):
|
||||||
|
l.append(t())
|
||||||
|
|
||||||
|
|
||||||
|
class FileMaxObj:
|
||||||
|
def __init__(self):
|
||||||
|
self.vstart: int = 0
|
||||||
|
self.vcount: int = 0
|
||||||
|
self.nstart: int = 0
|
||||||
|
self.ncount: int = 0
|
||||||
|
self.noffset: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
class MaxObj(FileMaxObj):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.fstart: int = 0
|
||||||
|
self.fcount: int = 0
|
||||||
|
self.sostart: int = 0
|
||||||
|
self.socount: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
class SubObject:
|
||||||
|
def __init__(self):
|
||||||
|
self.objname: str = ""
|
||||||
|
self.maxobjindex: int = 0
|
||||||
|
self.ntris: int = 0 # count of tridata (including preceding 'count' short)
|
||||||
|
self.totaltris: int = 0
|
||||||
|
self.tridata: List[int] = [] # unsigned short
|
||||||
|
self.verticeref_start: int = 0
|
||||||
|
self.verticeref_count: int = 0
|
||||||
|
self.texname: str = ""
|
||||||
|
self.bumptexture: str = ""
|
||||||
|
self.falloff: float = 0
|
||||||
|
self.blend: float = 0
|
||||||
|
self.flags: int = 0
|
||||||
|
self.emissive: int = 0
|
||||||
|
self.ambient: int = 0
|
||||||
|
self.diffuse: int = 0
|
||||||
|
self.specular: int = 0
|
||||||
|
self.power: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
class UV:
|
||||||
|
def __init__(self, u: float = 0, v: float = 0):
|
||||||
|
self.u: float = u
|
||||||
|
self.v: float = v
|
||||||
|
|
||||||
|
|
||||||
|
class OBJMaterial:
|
||||||
|
def __init__(self):
|
||||||
|
self.name = ""
|
||||||
|
self.texture = ""
|
||||||
|
|
||||||
|
|
||||||
|
class OBJObject:
|
||||||
|
def __init__(self):
|
||||||
|
self.faces: List[OBJFace] = []
|
||||||
|
self.name = "root"
|
||||||
|
self.material: Union[None, OBJMaterial] = None
|
||||||
|
|
||||||
|
|
||||||
|
class OBJFace:
|
||||||
|
def __init__(self):
|
||||||
|
self.index_vertices: List[int] = []
|
||||||
|
self.index_uvs: List[int] = []
|
||||||
|
self.index_normals: List[int] = []
|
||||||
|
|
||||||
|
|
||||||
|
def obj_read_materials(matlib_file) -> List[OBJMaterial]:
|
||||||
|
materials = []
|
||||||
|
curr_mat = None
|
||||||
|
with open(matlib_file, "r") as fp:
|
||||||
|
while line := fp.readline():
|
||||||
|
line = line.strip()
|
||||||
|
arr = line.split(" ")
|
||||||
|
if arr[0] == "newmtl":
|
||||||
|
mat = OBJMaterial()
|
||||||
|
materials.append(mat)
|
||||||
|
mat.name = arr[1].rstrip()
|
||||||
|
print("NEW MAT: %s" % mat.name)
|
||||||
|
curr_mat = mat
|
||||||
|
if arr[0] == "map_Ka" or arr[0] == "map_Kd":
|
||||||
|
matname_without_ext = "".join(arr[1:]).split("/")[-1]
|
||||||
|
matname_without_ext = "".join(matname_without_ext.split(".")[0:-1])
|
||||||
|
curr_mat.texture = matname_without_ext
|
||||||
|
print("MAT %s HAS TEXTURE %s" % (curr_mat.name, curr_mat.texture))
|
||||||
|
return materials
|
||||||
|
|
||||||
|
|
||||||
|
class GbsData:
|
||||||
|
def __init__(self):
|
||||||
|
self.name = ""
|
||||||
|
self.optionsflags: int = 0
|
||||||
|
self.nndefs: int = 0
|
||||||
|
self.num_normals: int = 0
|
||||||
|
self.normals: List[int] = [] # word
|
||||||
|
self.indexed_normals: List[int] = [] # unsigned short
|
||||||
|
self.num_vertices: int = 0
|
||||||
|
self.vertices: List[Vec3] = []
|
||||||
|
self.nsobjs: int = 0
|
||||||
|
self.nmobjs: int = 0
|
||||||
|
self.indexed_vertices: List[int] = [] # unsigned short
|
||||||
|
self.vertrgb: List[VecRGB] = []
|
||||||
|
self.nverts: int = 0
|
||||||
|
self.vertuv: List[UV] = []
|
||||||
|
self.MaxObjs: List[MaxObj] = []
|
||||||
|
self.SubObjs: List[SubObject] = []
|
||||||
|
|
||||||
|
def read(self, file):
|
||||||
|
debug = True
|
||||||
|
self.name = os.path.basename(file)
|
||||||
|
with open(file, "rb") as fp:
|
||||||
|
version_header = read_int(fp)
|
||||||
|
if version_header != GBS_VERSION:
|
||||||
|
raise Exception("File does not appear to be a GBS file.")
|
||||||
|
|
||||||
|
self.optionsflags = read_int(fp)
|
||||||
|
self.num_vertices = read_int(fp)
|
||||||
|
resize(self.vertices, Vec3, self.num_vertices)
|
||||||
|
for i in range(self.num_vertices):
|
||||||
|
self.vertices[i].x = read_float(fp)
|
||||||
|
self.vertices[i].y = read_float(fp)
|
||||||
|
self.vertices[i].z = read_float(fp)
|
||||||
|
|
||||||
|
if self.optionsflags & GBSFlagNormals:
|
||||||
|
self.nndefs = read_int(fp)
|
||||||
|
self.num_normals = read_int(fp)
|
||||||
|
resize(self.normals, int, self.num_normals)
|
||||||
|
for i in range(self.num_normals):
|
||||||
|
self.normals[i] = read_short(fp)
|
||||||
|
|
||||||
|
self.nverts = read_int(fp)
|
||||||
|
if debug: print("NVERTS=%s, NUMVERTICES=%s, options=%s" % (self.nverts, self.num_vertices, self.optionsflags))
|
||||||
|
resize(self.indexed_vertices, int, self.nverts)
|
||||||
|
for i in range(self.nverts):
|
||||||
|
self.indexed_vertices[i] = read_short(fp)
|
||||||
|
if debug: print(self.indexed_vertices)
|
||||||
|
|
||||||
|
if self.optionsflags & GBSFlagNormals:
|
||||||
|
resize(self.indexed_normals, int, self.nverts)
|
||||||
|
for i in range(self.nverts):
|
||||||
|
self.indexed_normals[i] = read_short(fp)
|
||||||
|
|
||||||
|
if self.optionsflags & GBSFlagUVs:
|
||||||
|
resize(self.vertuv, UV, self.nverts)
|
||||||
|
if debug: print("Read %s UV" % self.nverts)
|
||||||
|
for i in range(self.nverts):
|
||||||
|
self.vertuv[i].u = read_float(fp)
|
||||||
|
self.vertuv[i].v = read_float(fp) * -1
|
||||||
|
|
||||||
|
if self.optionsflags & GBSFlagRGBs:
|
||||||
|
resize(self.vertrgb, VecRGB, self.nverts)
|
||||||
|
if debug: print("Read %s RGB" % self.nverts)
|
||||||
|
for i in range(self.nverts):
|
||||||
|
self.vertrgb[i].r = read_byte(fp)
|
||||||
|
self.vertrgb[i].g = read_byte(fp)
|
||||||
|
self.vertrgb[i].b = read_byte(fp)
|
||||||
|
|
||||||
|
# Get number of objects
|
||||||
|
self.nmobjs = read_int(fp)
|
||||||
|
if debug: print("NMOBJS = %s" % self.nmobjs)
|
||||||
|
resize(self.MaxObjs, MaxObj, self.nmobjs)
|
||||||
|
for maxobj in self.MaxObjs:
|
||||||
|
print("Processing new maxobj")
|
||||||
|
fileMaxObj = FileMaxObj()
|
||||||
|
fileMaxObj.vstart = read_int(fp)
|
||||||
|
fileMaxObj.vcount = read_int(fp)
|
||||||
|
fileMaxObj.nstart = read_int(fp)
|
||||||
|
fileMaxObj.ncount = read_int(fp)
|
||||||
|
fileMaxObj.noffset = read_int(fp)
|
||||||
|
print("vstart=%s vcount=%s nstart=%s ncount=%s noffset=%s" % (fileMaxObj.vstart, fileMaxObj.vcount, fileMaxObj.nstart, fileMaxObj.ncount, fileMaxObj.noffset))
|
||||||
|
maxobj.vstart = fileMaxObj.vstart
|
||||||
|
maxobj.vcount = fileMaxObj.vcount
|
||||||
|
maxobj.nstart = fileMaxObj.nstart
|
||||||
|
maxobj.ncount = fileMaxObj.ncount
|
||||||
|
maxobj.noffset = fileMaxObj.noffset
|
||||||
|
maxobj.fstart = 0
|
||||||
|
maxobj.fcount = 0
|
||||||
|
maxobj.sostart = 0
|
||||||
|
maxobj.socount = 0
|
||||||
|
|
||||||
|
# verify max obj
|
||||||
|
nmcount = 0
|
||||||
|
for maxobj in self.MaxObjs:
|
||||||
|
nmcount += maxobj.vcount
|
||||||
|
assert nmcount == self.num_vertices
|
||||||
|
|
||||||
|
self.nsobjs = read_int(fp)
|
||||||
|
print("Subobject count: %s" % self.nsobjs)
|
||||||
|
resize(self.SubObjs, SubObject, self.nsobjs)
|
||||||
|
num_faces = 0
|
||||||
|
len_tridata = 0
|
||||||
|
for ns in range(self.nsobjs):
|
||||||
|
if debug: print("Processing subobj %s" % ns)
|
||||||
|
object = self.SubObjs[ns]
|
||||||
|
object.objname = read_string(fp, 32)
|
||||||
|
object.maxobjindex = read_int(fp)
|
||||||
|
object.totaltris = read_int(fp)
|
||||||
|
if debug: print("read %s totaltris" % object.totaltris)
|
||||||
|
num_faces += object.totaltris
|
||||||
|
object.ntris = read_int(fp)
|
||||||
|
|
||||||
|
assert ((object.ntris - 1) / 3 == object.totaltris)
|
||||||
|
|
||||||
|
resize(object.tridata, int, object.ntris + 1)
|
||||||
|
if debug: print("read gbs: totaltris: %s, tridata is %s long, ntris: %s" % (object.totaltris, len(object.tridata), object.ntris))
|
||||||
|
len_tridata += object.ntris + 1
|
||||||
|
for i in range(object.ntris):
|
||||||
|
object.tridata[i] = read_short(fp)
|
||||||
|
|
||||||
|
# if debug: print("tridata: %s" % object.tridata)
|
||||||
|
|
||||||
|
object.verticeref_start = read_int(fp)
|
||||||
|
object.verticeref_count = read_int(fp)
|
||||||
|
if self.optionsflags & GBSFlagUVs:
|
||||||
|
object.texname = read_string(fp, 32)
|
||||||
|
object.bumptexture = read_string(fp, 32)
|
||||||
|
|
||||||
|
object.falloff = read_float(fp)
|
||||||
|
if self.optionsflags & GBSFlagRGBs:
|
||||||
|
object.blend = read_float(fp)
|
||||||
|
|
||||||
|
object.flags = read_int(fp)
|
||||||
|
object.emissive = read_int(fp)
|
||||||
|
object.ambient = read_int(fp)
|
||||||
|
object.diffuse = read_int(fp)
|
||||||
|
object.specular = read_int(fp)
|
||||||
|
object.power = read_float(fp)
|
||||||
|
if debug: print("%s (%s) falloff: %s, blend: %s, flags:%s, emissive: %s, ambiant: %s, diffuse: %s, specular: %s, power: %s" % (object.objname, object.texname, object.falloff, object.blend, object.flags, object.emissive, object.ambient, object.diffuse, object.specular, object.power))
|
||||||
|
|
||||||
|
maxobj = self.MaxObjs[object.maxobjindex]
|
||||||
|
maxobj.fcount += object.totaltris
|
||||||
|
if not maxobj.socount:
|
||||||
|
maxobj.socount = ns
|
||||||
|
|
||||||
|
maxobj.socount += 1
|
||||||
|
if debug: print("read num_faces: %s, len_tridata: %s" % (num_faces, len_tridata))
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def evaluate_tridata(tridata, tri_idx, count):
|
||||||
|
if count == 0:
|
||||||
|
count = tridata[0]
|
||||||
|
if count == 0:
|
||||||
|
return False
|
||||||
|
tri_idx = 0
|
||||||
|
v1 = tridata[tri_idx + 1]
|
||||||
|
v2 = tridata[tri_idx + 2]
|
||||||
|
v3 = tridata[tri_idx + 3]
|
||||||
|
tri_idx += 3
|
||||||
|
|
||||||
|
count -= 1
|
||||||
|
if count < 0:
|
||||||
|
count = 0xffff # max unsigned short
|
||||||
|
if count == 0:
|
||||||
|
tridata = tridata[tridata[0] * 3 + 1:]
|
||||||
|
return tridata, tri_idx, count, v1, v2, v3
|
||||||
|
|
||||||
|
def generate_normals(self):
|
||||||
|
normals: List[Vec3] = []
|
||||||
|
resize(normals, Vec3, self.num_vertices)
|
||||||
|
for subobj_i in range(len(self.SubObjs)):
|
||||||
|
subobj = self.SubObjs[subobj_i]
|
||||||
|
tridata = subobj.tridata
|
||||||
|
values = self.evaluate_tridata(tridata, -1, 0)
|
||||||
|
while values:
|
||||||
|
tridata, tri_idx, count, v1, v2, v3 = values
|
||||||
|
try:
|
||||||
|
p = cross(self.vertices[self.indexed_vertices[v2]] - self.vertices[self.indexed_vertices[v1]], self.vertices[self.indexed_vertices[v3]] - self.vertices[self.indexed_vertices[v1]])
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
normals[self.indexed_vertices[v1]] += p
|
||||||
|
normals[self.indexed_vertices[v2]] += p
|
||||||
|
normals[self.indexed_vertices[v3]] += p
|
||||||
|
values = self.evaluate_tridata(tridata, tri_idx, count)
|
||||||
|
|
||||||
|
for i in range(len(normals)):
|
||||||
|
normals[i] = normals[i].normalize()
|
||||||
|
return normals
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_obj(obj_file):
|
||||||
|
materials = []
|
||||||
|
vertices: List[Vec3] = []
|
||||||
|
uvs: List[UV] = []
|
||||||
|
normals: List[Vec3] = []
|
||||||
|
objects: List[OBJObject] = []
|
||||||
|
root_obj = OBJObject()
|
||||||
|
objects.append(root_obj)
|
||||||
|
|
||||||
|
last_material = None
|
||||||
|
current_object = root_obj
|
||||||
|
|
||||||
|
uv_ind = {}
|
||||||
|
|
||||||
|
with open(obj_file, "r") as obj_fp:
|
||||||
|
while line := obj_fp.readline():
|
||||||
|
arr = line.split(" ")
|
||||||
|
if arr[0] == "v":
|
||||||
|
v = Vec3(float(arr[1]), float(arr[2]), float(arr[3]))
|
||||||
|
vertices.append(v)
|
||||||
|
if arr[0] == "vt":
|
||||||
|
v = UV(float(arr[1]), float(arr[2]))
|
||||||
|
uvs.append(v)
|
||||||
|
if arr[0] == "vn":
|
||||||
|
v = Vec3(float(arr[1]), float(arr[2]), float(arr[3]))
|
||||||
|
normals.append(v)
|
||||||
|
if arr[0] == "f":
|
||||||
|
f = OBJFace()
|
||||||
|
f1_s = arr[1].split("/")
|
||||||
|
f2_s = arr[2].split("/")
|
||||||
|
f3_s = arr[3].split("/")
|
||||||
|
if len(f1_s) == len(f2_s) == len(f3_s) >= 1:
|
||||||
|
# face only has vertex index data
|
||||||
|
v1_index = int(f1_s[0])
|
||||||
|
v2_index = int(f2_s[0])
|
||||||
|
v3_index = int(f3_s[0])
|
||||||
|
f.index_vertices.append(v1_index)
|
||||||
|
f.index_vertices.append(v2_index)
|
||||||
|
f.index_vertices.append(v3_index)
|
||||||
|
if len(f1_s) == len(f2_s) == len(f3_s) >= 2:
|
||||||
|
# face has vertex index and uv
|
||||||
|
v1_uv_index = int(f1_s[1])
|
||||||
|
v2_uv_index = int(f2_s[1])
|
||||||
|
v3_uv_index = int(f3_s[1])
|
||||||
|
uv_ind[v1_uv_index] = v1_index
|
||||||
|
uv_ind[v2_uv_index] = v2_index
|
||||||
|
uv_ind[v3_uv_index] = v3_index
|
||||||
|
f.index_uvs.append(v1_uv_index)
|
||||||
|
f.index_uvs.append(v2_uv_index)
|
||||||
|
f.index_uvs.append(v3_uv_index)
|
||||||
|
if len(f1_s) == len(f2_s) == len(f3_s) >= 3:
|
||||||
|
# face has vertex index and uv and normal
|
||||||
|
v1_normal_index = int(f1_s[2])
|
||||||
|
v2_normal_index = int(f2_s[2])
|
||||||
|
v3_normal_index = int(f3_s[2])
|
||||||
|
f.index_normals.append(v1_normal_index)
|
||||||
|
f.index_normals.append(v2_normal_index)
|
||||||
|
f.index_normals.append(v3_normal_index)
|
||||||
|
current_object.faces.append(f)
|
||||||
|
if arr[0] == "o":
|
||||||
|
obj_name = arr[1].rstrip()
|
||||||
|
o = OBJObject()
|
||||||
|
o.name = obj_name
|
||||||
|
o.material = last_material
|
||||||
|
objects.append(o)
|
||||||
|
if len(current_object.faces) == 0:
|
||||||
|
objects.remove(current_object)
|
||||||
|
current_object = o
|
||||||
|
if arr[0] == "usemtl":
|
||||||
|
mtl_name = arr[1].rstrip()
|
||||||
|
print("mtl_name: %s" % mtl_name)
|
||||||
|
mtl = [mat for mat in materials if mat.name == mtl_name][0]
|
||||||
|
print("FOUND MTL: %s for name %s" % (mtl.texture, mtl_name))
|
||||||
|
current_object.material = mtl
|
||||||
|
last_material = mtl
|
||||||
|
if arr[0] == "mtllib":
|
||||||
|
matlib_file = arr[1].rstrip()
|
||||||
|
print("Reading mtllib %s" % matlib_file)
|
||||||
|
materials = obj_read_materials("%s/%s" % (os.path.dirname(obj_file), matlib_file))
|
||||||
|
|
||||||
|
num_faces = sum([len(o.faces) for o in objects])
|
||||||
|
print("%s vertices, %s uvs, %s normals, %s objects, %s materials, %s faces" % (len(vertices), len(uvs), len(normals), len(objects), len(materials), num_faces))
|
||||||
|
# assert len(objects) >= len(materials)
|
||||||
|
assert len(vertices) < 0xffff
|
||||||
|
|
||||||
|
len_vertices = len(vertices)
|
||||||
|
len_normals = len(normals)
|
||||||
|
|
||||||
|
data = Packet()
|
||||||
|
data.put_ulong(GBS_VERSION)
|
||||||
|
options = 0
|
||||||
|
if len(uvs) > 0:
|
||||||
|
options |= GBSFlagUVs
|
||||||
|
options |= GBSFlagCalcNormals
|
||||||
|
data.put_long(options)
|
||||||
|
data.put_long(len_vertices)
|
||||||
|
for v in vertices:
|
||||||
|
data.put_float(v.x)
|
||||||
|
data.put_float(v.y)
|
||||||
|
data.put_float(v.z)
|
||||||
|
|
||||||
|
if options & GBSFlagNormals:
|
||||||
|
for v in normals:
|
||||||
|
data.put_float(v.x)
|
||||||
|
data.put_float(v.y)
|
||||||
|
data.put_float(v.z)
|
||||||
|
|
||||||
|
data.put_ulong(len_normals)
|
||||||
|
data.put_ulong(len_normals)
|
||||||
|
for i in range(len_normals):
|
||||||
|
data.put_short(len_vertices + i)
|
||||||
|
|
||||||
|
assert len_vertices <= len(uvs)
|
||||||
|
|
||||||
|
indices = []
|
||||||
|
for uv_in in sorted(uv_ind.keys()):
|
||||||
|
indices.append(uv_ind[uv_in]-1)
|
||||||
|
|
||||||
|
nverts = max(len_vertices, len(uvs))
|
||||||
|
assert nverts == len(indices)
|
||||||
|
|
||||||
|
data.put_ulong(nverts)
|
||||||
|
for i in indices:
|
||||||
|
# indexed vert/UV
|
||||||
|
data.put_short(i)
|
||||||
|
|
||||||
|
if options & GBSFlagUVs:
|
||||||
|
for v in uvs:
|
||||||
|
data.put_float(v.u)
|
||||||
|
data.put_float(v.v * -1)
|
||||||
|
|
||||||
|
# max objects
|
||||||
|
data.put_long(1) # 1 big object
|
||||||
|
data.put_long(0) # vstart
|
||||||
|
data.put_long(len_vertices) # vcount
|
||||||
|
data.put_long(0) # nstart
|
||||||
|
data.put_long(0) # ncount
|
||||||
|
data.put_long(0) # noffset ???
|
||||||
|
|
||||||
|
# start write subobjects
|
||||||
|
data.put_long(len(objects))
|
||||||
|
for obj in objects:
|
||||||
|
print("Writing object %s" % obj.name)
|
||||||
|
data.put_string_size(obj.name, 32)
|
||||||
|
data.put_long(0) # max obj index
|
||||||
|
data.put_long(len(obj.faces)) # totaltris
|
||||||
|
print("wrote %s totaltris" % len(obj.faces))
|
||||||
|
data.put_long(3 * len(obj.faces) + 1) # ntris
|
||||||
|
print("wrote %s ntris" % (3 * len(obj.faces) + 1))
|
||||||
|
|
||||||
|
data.put_short(len(obj.faces))
|
||||||
|
for face in obj.faces: # obj.faces.length == ntris
|
||||||
|
data.put_short(face.index_uvs[0] - 1)
|
||||||
|
data.put_short(face.index_uvs[1] - 1)
|
||||||
|
data.put_short(face.index_uvs[2] - 1)
|
||||||
|
|
||||||
|
data.put_long(0) # verticeref_start
|
||||||
|
data.put_long(nverts) # verticeref_count
|
||||||
|
if options & GBSFlagUVs:
|
||||||
|
print("Writing texture to GBS: %s" % obj.material.texture)
|
||||||
|
data.put_string_size(obj.material.texture, 32) # texture
|
||||||
|
data.put_string_size(obj.material.texture, 32) # bump
|
||||||
|
|
||||||
|
data.put_float(0) # falloff
|
||||||
|
if options & GBSFlagRGBs:
|
||||||
|
data.put_float(0) # blend
|
||||||
|
|
||||||
|
data.put_long(0x40000000) # flags
|
||||||
|
data.put_long(0) # emissive
|
||||||
|
data.put_long(0) # ambient
|
||||||
|
data.put_long(0) # diffuse
|
||||||
|
data.put_long(0) # specular
|
||||||
|
data.put_float(0) # power
|
||||||
|
|
||||||
|
# write gbs file
|
||||||
|
output = "%s/%s.gbs" % (os.path.dirname(os.path.abspath(obj_file)), os.path.basename(obj_file))
|
||||||
|
with open(output, "wb") as gbs:
|
||||||
|
gbs.write(data.getvalue())
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "[name: %s, nverts:%s, num_vertices: %s, vertuv: %s, normals: %s, maxobjs: %s, subobjs:%s]" % (self.name, self.nverts, self.num_vertices, len(self.vertuv), self.num_normals, self.nmobjs, self.nsobjs)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_obj(path):
|
||||||
|
GbsData.from_obj(path)
|
||||||
|
output = "%s/%s.gbs" % (os.path.dirname(os.path.abspath(path)), os.path.basename(path))
|
||||||
|
print("Done! Output: %s" % output)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("path")
|
||||||
|
args = parser.parse_args()
|
||||||
|
if os.path.exists(args.path):
|
||||||
|
convert_obj(args.path)
|
||||||
|
else:
|
||||||
|
print("ERROR: file %s does not exist" % args.path)
|
Loading…
Reference in New Issue
Block a user