summaryrefslogtreecommitdiff
path: root/3dlib/obj2tmdl.py
diff options
context:
space:
mode:
Diffstat (limited to '3dlib/obj2tmdl.py')
-rwxr-xr-x3dlib/obj2tmdl.py492
1 files changed, 0 insertions, 492 deletions
diff --git a/3dlib/obj2tmdl.py b/3dlib/obj2tmdl.py
deleted file mode 100755
index a000ca1..0000000
--- a/3dlib/obj2tmdl.py
+++ /dev/null
@@ -1,492 +0,0 @@
-#!/usr/bin/env python2
-# Obj2tmdl
-# Converter for Wavefront OBJ => Treeki Model
-
-import os
-import struct
-import sys
-u32 = struct.Struct('>I')
-u16 = struct.Struct('>H')
-u8 = struct.Struct('>B')
-f32 = struct.Struct('>f')
-
-sys.path.append('/home/Treeki/Wii.py/Wii.py')
-sys.path.append('/home/me/Packages/Wii.py')
-import Wii
-
-class MaterialLib:
- class Material:
- pass
-
-
- def __init__(self, filename):
- self.current_mat = None
- self.materials = {}
-
- for line in open(filename, 'r'):
- self.parse_line(line)
-
-
- def parse_line(self, line):
- line = line.strip()
- if line == '' or line[0] == '#':
- return
-
- line = line.split()
- cmd = line[0]
-
- if cmd == 'newmtl':
- self.current_mat = MaterialLib.Material()
- self.materials[line[1]] = self.current_mat
-
- elif cmd == 'map_Ka':
- self.current_mat.texture = ' '.join(line[1:])
-
- elif cmd == 'Kd':
- r = int(float(line[1]) * 255)
- g = int(float(line[2]) * 255)
- b = int(float(line[3]) * 255)
- self.current_mat.colour = (r,g,b,255)
-
-
- def prepare_textures(self):
- # get a list of every texture file used
- self.texture_id = {}
- textures = []
- id = 0
-
- for mat in self.materials.values():
- if hasattr(mat, 'texture'):
- if mat.texture not in textures:
- textures.append(mat.texture)
- self.texture_id[mat.texture] = id
- id += 1
-
-
- if len(textures) == 0:
- self.tpl = None
- return
-
- # create a gxtexconv script file and an ID mapping
- #script = []
- #self.texture_id = {}
- #
- #for tex, id in zip(textures, range(len(textures))):
- # script.append('<filepath=\"%s\" id=\"tex%d\" colfmt=5 />' % (tex.replace('\\','/'),id))
- # self.texture_id[id] = tex
- #
- #print script
- #
- #open('texture_script_temp_0991.scf', 'w').write('\n'.join(script))
- #
- #os.system('gxtexconv -s texture_script_temp_0991.scf -o generated_texture_0991.tpl')
-
- # fix this later
- texture_files = [tex.replace('\\','/').replace(' ','\\ ').replace('.jpg','.png') for tex in textures]
- print 'zetsubou wpng RGB5A3 %s generated_texture_0991.tpl' % ' '.join(texture_files)
- os.system('zetsubou wpng RGB5A3 %s generated_texture_0991.tpl' % ' '.join(texture_files))
- self.tpl = open('generated_texture_0991.tpl', 'rb').read()
-
- #os.remove('texture_script_temp_0991.scf')
- os.remove('generated_texture_0991.tpl')
-
-
-
-class ObjReader:
- class ObjShape:
- def __init__(self, name):
- self.face_groups = [ \
- ObjReader.ObjFaceGroup('quads'), \
- ObjReader.ObjFaceGroup('triangles')]
-
- self.quad_group, self.tri_group = self.face_groups
-
- self.shape_name = name
- self.material_name = None
-
-
- class ObjFace:
- __slots__ = ('vertices', 'texcoords', 'normals')
-
- def __init__(self, vertices, texcoords, normals):
- self.vertices = vertices
- self.texcoords = texcoords
- self.normals = normals
-
-
- class ObjFaceGroup:
- def __init__(self, type):
- self.type = type
- self.faces = []
-
-
- def __init__(self):
- self.material_lib = None
-
- self.vertex_lists = []
- self.texcoord_lists = []
- self.normal_lists = []
- self.shapes = []
-
- self.current_shape = None
-
-
- def parse_line(self, line):
- line = line.strip()
- if line == '' or line[0] == '#':
- return
-
- line = line.split()
- cmd = line[0]
-
- if cmd == 'v':
- self.vertex_lists.append(map(float, line[1:]))
-
- elif cmd == 'vt':
- tc = map(float, line[1:])[:2] # chop off third coord
- tc[1] = 1.0 - tc[1]
- self.texcoord_lists.append(tc)
-
- elif cmd == 'vn':
- self.normal_lists.append(map(float, line[1:]))
-
- elif cmd == 'f':
- v, t, n = [], [], []
-
- if len(line) == 4:
- # triangle
- group = self.current_shape.tri_group
- elif len(line) == 5:
- # quad
- group = self.current_shape.quad_group
-
- for entry in line[1:]:
- entry_split = map(lambda x:-1 if x == '' else int(x), entry.split('/'))
-
- v.append(self.vertex_lists[entry_split[0] - 1])
-
- if entry_split[1] == -1:
- t.append([0.0, 0.0])
- else:
- t.append(self.texcoord_lists[entry_split[1] - 1])
-
- if entry_split[2] == -1:
- n.append([0.0, 0.0, 0.0])
- else:
- n.append(self.normal_lists[entry_split[2] - 1])
-
- group.faces.append(ObjReader.ObjFace(v, t, n))
-
- #elif cmd == 'g':
- # self.current_shape = ObjReader.ObjShape(line[1])
- # self.shapes.append(self.current_shape)
-
- elif cmd == 'mtllib':
- self.material_lib = MaterialLib(os.path.join(self.path, ' '.join(line[1:])))
-
- elif cmd == 'usemtl':
- self.current_shape = ObjReader.ObjShape(line[1])
- self.shapes.append(self.current_shape)
- self.current_shape.material_name = line[1]
-
-
-
-# === Treeki Model Format ===
-# Header:
-#
-# struct tmdl_header {
-# u32 magic; // always TMDL
-# u32 version; // currently 2
-# u32 shape_count;
-# };
-# [[Followed by an array of u32s for each shape offset]]
-#
-# struct tmdl_shape {
-# u32 dl_offset; // must be aligned to 0x20
-# u32 dl_size; // must be aligned to 0x20
-# u32 mat_offset;
-# u32 pos_arr_offset; // must be aligned to 0x20
-# u32 pos_arr_size; // must be aligned to 0x20
-# u32 nrm_arr_offset; // must be aligned to 0x20
-# u32 nrm_arr_size; // must be aligned to 0x20
-# u32 uv_arr_offset; // must be aligned to 0x20
-# u32 uv_arr_size; // must be aligned to 0x20
-# };
-#
-# struct tmdl_material {
-# u32 texture_id; // once the model is bound, points to id within tpl
-# };
-#
-# dl_offset points to a GX display list which is used for
-# rendering the model in question.
-
-
-class TmdlWriter:
- def __init__(self):
- self.shapes = []
-
-
- def build_with_obj(self, obj):
- self.shapes = obj.shapes
- self.material_lib = obj.material_lib
- self.materials = obj.material_lib.materials
-
- self.material_lib.prepare_textures()
-
-
- def pack(self):
- offset = 0
-
- header = struct.pack('>4sI I', 'TMDL', 2, len(self.shapes))
- offset += len(header)
-
- # reserve space for the shape offsets, we'll add them later
- offset += (4 * len(self.shapes))
-
- # create the materials
- material_offs = {}
- material_data = ''
-
- for name,material in self.materials.iteritems():
- material_offs[name] = offset
- material_struct = self.build_material_struct(material)
- material_data += material_struct
- offset += len(material_struct)
-
-
- # create the shapes -- first, we'll assemble the GX arrays and DLs
- shape_arrays = []
- packed_shape_arrays = []
- shape_display_lists = []
-
- print 'Building shapes... [%d]' % len(self.shapes)
-
- count = 0
- for shape in self.shapes:
- pos = self.build_array_from_shape_attr(shape, 'vertices')
- nrm = self.build_array_from_shape_attr(shape, 'normals')
- uv = self.build_array_from_shape_attr(shape, 'texcoords')
- shape_arrays.append((pos,nrm,uv))
-
- ppos = self.pack_data_array(pos)
- pnrm = self.pack_data_array(nrm)
- puv = self.pack_data_array(uv)
- packed_shape_arrays.append((ppos,pnrm,puv))
-
- dl = self.build_display_list_with_shape(shape, pos, nrm, uv)
- shape_display_lists.append(dl)
- count += 1
-
- print '%d done' % count
-
- print 'Shapes built'
-
-
- # calculate offsets to every display list
- # shape struct is currently 36 bytes, change this if the size changes
- # also, make sure they're aligned to an offset of 0x20
- temp_offset = offset + (len(self.shapes) * 36)
-
- if temp_offset & 0x1f != 0:
- aligned_offset = (temp_offset + 0x20) & ~0x1f
- dl_start_padding = '\0' * (aligned_offset - temp_offset)
- temp_offset = aligned_offset
- else:
- dl_start_padding = ''
-
- shape_display_list_offsets = []
- for dl in shape_display_lists:
- shape_display_list_offsets.append(temp_offset)
- temp_offset += len(dl)
-
- # calculate offsets to every GX array
- pos_array_offsets = []
- nrm_array_offsets = []
- uv_array_offsets = []
-
- for pos,nrm,uv in packed_shape_arrays:
- pos_array_offsets.append(temp_offset)
- temp_offset += len(pos)
- nrm_array_offsets.append(temp_offset)
- temp_offset += len(nrm)
- uv_array_offsets.append(temp_offset)
- temp_offset += len(uv)
-
-
- # and now, create the shape structs themselves
- shape_offsets = []
- shape_data = ''
-
- for shape, dl, dl_offset, pos_offs, nrm_offs, uv_offs, (pos, nrm, uv) in \
- zip(self.shapes, shape_display_lists, shape_display_list_offsets, \
- pos_array_offsets, nrm_array_offsets, uv_array_offsets, \
- packed_shape_arrays):
-
- # first off, store the offset
- shape_offsets.append(offset)
-
- # now build the struct
- shape_struct = struct.pack('>IIIIIIIII', \
- dl_offset, len(dl), material_offs[shape.material_name], \
- pos_offs, len(pos), \
- nrm_offs, len(nrm), \
- uv_offs, len(uv))
-
- shape_data += shape_struct
- offset += len(shape_struct)
-
-
- # almost there!
- print 'Packing model...'
-
- tmdl_bits = [header]
- tmdl_add = tmdl_bits.append
-
- for offs in shape_offsets:
- tmdl_add(u32.pack(offs))
-
- tmdl_add(material_data)
- tmdl_add(shape_data)
- tmdl_add(dl_start_padding)
-
- tmdl_bits += shape_display_lists
-
- for pos,nrm,uv in packed_shape_arrays:
- tmdl_bits.append(pos)
- tmdl_bits.append(nrm)
- tmdl_bits.append(uv)
-
- return ''.join(tmdl_bits)
-
-
- def build_material_struct(self, material):
- texture_id = 0xFFFFFFFF
- if hasattr(material, 'texture'):
- texture_id = self.material_lib.texture_id[material.texture]
-
- cR, cG, cB, cA = material.colour
- return struct.pack('>IBBBB', texture_id, cR, cG, cB, cA)
-
-
- def build_array_from_shape_attr(self, shape, attr):
- assemble = []
-
- for g in shape.face_groups:
- for f in g.faces:
- assemble += f.__dict__[attr]
-
- final = []
- for i in assemble:
- if i not in final:
- final.append(i)
-
- if len(final) >= 0xFFFE:
- print '=== WARNING! List too big! ==='
-
- return final
-
-
- def pack_data_array(self, data):
- out = []
-
- for piece in data:
- for bit in piece:
- out.append(f32.pack(bit))
-
- packed = ''.join(out)
-
- if len(packed) % 0x20 != 0:
- aligned = (len(packed) + 0x1f) & ~0x1fe
- packed += '\0' * (aligned - len(packed))
-
- return packed
-
-
- def build_display_list_with_shape(self, shape, vtx_list, nrm_list, uv_list):
- # http://hitmen.c02.at/files/yagcd/yagcd/chap5.html
- # assume vertex descriptor: [data is specified in this order]
- # GXClearVtxDesc()
- # GXSetVtxDesc(GX_VA_POS, GX_DIRECT)
- # GXSetVtxDesc(GX_VA_NRM, GX_DIRECT)
- # GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT)
-
- dl_bits = []
- _dl_add = dl_bits.append
-
- _vtx_index = vtx_list.index
- _nrm_index = nrm_list.index
- _uv_index = uv_list.index
-
- _u16_pack = u16.pack
-
- for face_group in shape.face_groups:
- if len(face_group.faces) == 0:
- continue
-
- vtx_num = reduce(lambda x,y:x+len(y.vertices), face_group.faces, 0)
-
- # commands: GX_QUADS = 0x80, GX_TRIANGLES = 0x90
- if face_group.type == 'quads':
- _dl_add('\x80') # opcode
- elif face_group.type == 'triangles':
- _dl_add('\x90') # opcode
-
- _dl_add(_u16_pack(vtx_num))
-
- # now send over the data
- for face in face_group.faces:
- for v, t, n in zip(face.vertices, face.texcoords, face.normals):
- _dl_add(_u16_pack(_vtx_index(v)))
- _dl_add(_u16_pack(_nrm_index(n)))
- _dl_add(_u16_pack(_uv_index(t)))
-
- # done!
- return self.build_display_list(dl_bits)
-
-
- def build_display_list(self, dl_bits):
- # assemble it
- dl = ''.join(dl_bits)
-
- # pad the display list to 0x20 bytes with GX_NOP (0x00)
- if len(dl) % 0x20 != 0:
- pad_count = ((len(dl) + 0x20) & ~0x1F) - len(dl)
- dl += '\x00' * pad_count
-
- return dl
-
-
-#file = r'H:\ISOs\NSMBWii\testmush3'
-#file = 'testmdl2'
-#file = 'simple2'
-file = sys.argv[1]
-shortfn = file[file.rfind('/')+1:]
-
-if 'tex-only' in sys.argv:
- mtl = MaterialLib(file+'.mtl')
- mtl.prepare_textures()
- open('tex_%s.tpl' % shortfn, 'wb').write(mtl.tpl)
- sys.exit()
-
-
-
-obj = ObjReader()
-if '/' in file:
- obj.path = file[:file.rfind('/')]
-else:
- obj.path = '.'
-
-for line in open(file+'.obj'):
- obj.parse_line(line)
-
-tmdl = TmdlWriter()
-tmdl.build_with_obj(obj)
-
-tmdl_file = tmdl.pack()
-tpl_file = tmdl.material_lib.tpl
-
-arc = Wii.U8()
-arc['t3d'] = None
-arc['t3d/mdl_%s.tmdl' % shortfn] = tmdl_file
-if tpl_file: arc['t3d/tex_%s.tpl' % shortfn] = tpl_file
-arc.dumpFile('%s.arc' % file)