diff --git a/__init__.py b/__init__.py index 73713f8..f1cfb4a 100644 --- a/__init__.py +++ b/__init__.py @@ -2,7 +2,7 @@ bl_info = { "name": "City of Heroes (.geo)", "author": "TigerKat", - "version": (0, 2, 2), + "version": (0, 2, 3), "blender": (2, 80, 0), "location": "File > Import/Export,", "description": "City of Heroes (.geo)", diff --git a/geo.py b/geo.py index b87f765..21d9b67 100755 --- a/geo.py +++ b/geo.py @@ -838,6 +838,18 @@ class Model: def encodeHeader(self): self.geo.header_data += self.header_data + def getBoneRoot(self): + if self.bone_count <= 0: + return None + #todo: find the root properly. For now it just grabs the lowest bone id and hopes it's right. + #print("getBoneRoot():") + bid = self.bone_ids[0] + for i in range(self.bone_count): + b = self.bone_ids[i] + #print(" %s : %s" % (bid, b)) + if b < bid: + bid = b + return BONES_LIST[bid] def dump(self): print(" radius: %s" % self.radius) print(" tex_count: %s" % self.tex_count) @@ -972,11 +984,14 @@ class Model: for i in range(len(geomesh.textures)): texture_remap.append(self.geo.getTextureIndex(geomesh.textures[i])) #Determine the remap for converting geomesh weight/bones indexes to geo weight/bone indexes. - weight_remap = [] + geomesh.rebuildWeightsList() + weight_remap = {} for i in range(len(geomesh.weights)): - weight_remap.append(BONES_LOOKUP.get(geomesh.weights[i], 0)) - if len(weight_remap) <= 0: - weight_remap.append(0) + #weight_remap.append(BONES_LOOKUP.get(geomesh.weights[i], 0)) + weight_remap[geomesh.weights[i]] = BONES_LOOKUP.get(geomesh.weights[i], 0) + #print(" '%s' -> %s" % (geomesh.weights[i], weight_remap[geomesh.weights[i]])) + #if len(weight_remap) <= 0: + # weight_remap.append(0) #Convert vetices to: positions, normals, uvs, weights for i, v in enumerate(geomesh.geovertex): self.verts.append(v.coord) @@ -984,6 +999,7 @@ class Model: self.sts.append(v.uv) if geomesh.have_weights: weights = v.selectWeights(2) + #print(weights) if len(weights) == 0: self.weights.append([1, 0]) self.weight_bones.append([weight_remap[0], weight_remap[0]]) diff --git a/geomesh.py b/geomesh.py index 835f394..2348725 100644 --- a/geomesh.py +++ b/geomesh.py @@ -84,6 +84,15 @@ class GeoMesh: self.weights_map[name] = index self.weights.append(name) return index + def rebuildWeightsList(self): + self.weights_map = {} + self.weights = [] + for v in self.geovertex: + for w in v.weights: + if w[0] in self.weights_map: + continue + self.weights_map[w[0]] = len(self.weights) + self.weights.append(w[0]) def addFace(self, geovertices, texture_name): l = len(geovertices) diff --git a/import_geo.py b/import_geo.py index 1d2faaa..fcc2789 100644 --- a/import_geo.py +++ b/import_geo.py @@ -18,10 +18,12 @@ if 0: return l else: def import_fix_coord(v): - return (-v[0], v[2], -v[1]) + return (-v[0], -v[2], v[1]) def import_fix_normal(v): - return ( v[0], -v[2], v[1]) + return ( v[0], v[2], -v[1]) def import_fix_winding(l): + l = list(l) + l.reverse() return l def convert_model(geo_model, mesh_data, obj, scale): @@ -37,6 +39,7 @@ def convert_model(geo_model, mesh_data, obj, scale): coords = [c for v in geomesh.geovertex for c in import_scale_coord(import_fix_coord(v.coord), scale)] normals = [n for v in geomesh.geovertex for n in import_fix_normal(v.normal)] + #print("normals: %s" % (normals, )) loop_totals = [] loop_starts = [] i = 0 @@ -78,17 +81,53 @@ def convert_model(geo_model, mesh_data, obj, scale): #todo: attempt to load textures/images +def getBonePositionBody(bone): + if bone.parent is None: + p = bone.tail + print("root: %s: %s (%s)" % (bone, p, bone.head)) + return p + else: + p = bone.tail + getBonePositionBody(bone.parent) + print(" : %s: %s (%s) -> %s" % (bone, bone.tail, bone.head, p)) + return p -def load(operator, context, scale = 1.0, filepath = "", global_matrix = None, use_mesh_modifiers = True): +def getBonePosition(arm, name): + return getBonePositionBody(arm.bones[name]) + +def load(operator, context, scale = 1.0, filepath = "", global_matrix = None, use_mesh_modifiers = True, ignore_lod = True): #load .geo fh_in = open(filepath, "rb") geo = Geo() geo.loadFromFile(fh_in) fh_in.close() + #Choose the first selected armature as the armature to attach to. + armature = None + for ob in context.selected_objects: + if ob.type == "ARMATURE": + armature_obj = ob + armature = ob.data + break + else: + #If none found try again with all objects. + for ob in bpy.data.objects: + if ob.type == "ARMATURE": + armature_obj = ob + armature = ob.data + break for geo_model in geo.models: + model_name = geo_model.name.decode("utf-8") + if ignore_lod: + islod = False + #todo: better LOD model detection + for l in ["_LOD1", "_LOD2"]: + if l in model_name: + islod = True + break + if islod: + continue #create object matching model's name (or next equivilant) - mesh = bpy.data.meshes.new(name = geo_model.name.decode("utf-8")) - obj = bpy.data.objects.new(geo_model.name.decode("utf-8"), mesh) + mesh = bpy.data.meshes.new(name = model_name) + obj = bpy.data.objects.new(model_name, mesh) #convert model to mesh convert_model(geo_model, mesh, obj, scale) #Create object for this mesh @@ -96,6 +135,20 @@ def load(operator, context, scale = 1.0, filepath = "", global_matrix = None, us bpy.context.collection.objects.link(obj) bpy.context.view_layer.objects.active = obj + + #Try and attach it to the armature. + if armature != None: + bone_name = geo_model.getBoneRoot() + if bone_name is None: + #No bones, don't attach. + pass + elif bone_name in armature.bones: + bone_pos = getBonePosition(armature, bone_name) + obj.matrix_world = mathutils.Matrix.Translation(bone_pos) + obj.modifiers.new(name = 'Armature', type = 'ARMATURE') + obj.modifiers['Armature'].object = armature_obj + else: + print("Bone '%s' not found in armature '%s', skipping." % (bone_name, armature)) obj.select_set(True) pass pass