Now tries and attach imported models to an armature, and place it at the bone.

No longer imports LOD models.

bugfix: Was rotating parts 180degrees around the X axis.
bugfix: Was failing export due to changes in the way vertex weights are stored in GeoMeshes.
master
TigerKat 4 years ago
parent c00220a410
commit 3c469c0276

@ -2,7 +2,7 @@
bl_info = { bl_info = {
"name": "City of Heroes (.geo)", "name": "City of Heroes (.geo)",
"author": "TigerKat", "author": "TigerKat",
"version": (0, 2, 2), "version": (0, 2, 3),
"blender": (2, 80, 0), "blender": (2, 80, 0),
"location": "File > Import/Export,", "location": "File > Import/Export,",
"description": "City of Heroes (.geo)", "description": "City of Heroes (.geo)",

@ -838,6 +838,18 @@ class Model:
def encodeHeader(self): def encodeHeader(self):
self.geo.header_data += self.header_data 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): def dump(self):
print(" radius: %s" % self.radius) print(" radius: %s" % self.radius)
print(" tex_count: %s" % self.tex_count) print(" tex_count: %s" % self.tex_count)
@ -972,11 +984,14 @@ class Model:
for i in range(len(geomesh.textures)): for i in range(len(geomesh.textures)):
texture_remap.append(self.geo.getTextureIndex(geomesh.textures[i])) texture_remap.append(self.geo.getTextureIndex(geomesh.textures[i]))
#Determine the remap for converting geomesh weight/bones indexes to geo weight/bone indexes. #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)): for i in range(len(geomesh.weights)):
weight_remap.append(BONES_LOOKUP.get(geomesh.weights[i], 0)) #weight_remap.append(BONES_LOOKUP.get(geomesh.weights[i], 0))
if len(weight_remap) <= 0: weight_remap[geomesh.weights[i]] = BONES_LOOKUP.get(geomesh.weights[i], 0)
weight_remap.append(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 #Convert vetices to: positions, normals, uvs, weights
for i, v in enumerate(geomesh.geovertex): for i, v in enumerate(geomesh.geovertex):
self.verts.append(v.coord) self.verts.append(v.coord)
@ -984,6 +999,7 @@ class Model:
self.sts.append(v.uv) self.sts.append(v.uv)
if geomesh.have_weights: if geomesh.have_weights:
weights = v.selectWeights(2) weights = v.selectWeights(2)
#print(weights)
if len(weights) == 0: if len(weights) == 0:
self.weights.append([1, 0]) self.weights.append([1, 0])
self.weight_bones.append([weight_remap[0], weight_remap[0]]) self.weight_bones.append([weight_remap[0], weight_remap[0]])

@ -84,6 +84,15 @@ class GeoMesh:
self.weights_map[name] = index self.weights_map[name] = index
self.weights.append(name) self.weights.append(name)
return index 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): def addFace(self, geovertices, texture_name):
l = len(geovertices) l = len(geovertices)

@ -18,10 +18,12 @@ if 0:
return l return l
else: else:
def import_fix_coord(v): def import_fix_coord(v):
return (-v[0], v[2], -v[1]) return (-v[0], -v[2], v[1])
def import_fix_normal(v): def import_fix_normal(v):
return ( v[0], -v[2], v[1]) return ( v[0], v[2], -v[1])
def import_fix_winding(l): def import_fix_winding(l):
l = list(l)
l.reverse()
return l return l
def convert_model(geo_model, mesh_data, obj, scale): 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)] 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)] normals = [n for v in geomesh.geovertex for n in import_fix_normal(v.normal)]
#print("normals: %s" % (normals, ))
loop_totals = [] loop_totals = []
loop_starts = [] loop_starts = []
i = 0 i = 0
@ -78,17 +81,53 @@ def convert_model(geo_model, mesh_data, obj, scale):
#todo: attempt to load textures/images #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 getBonePosition(arm, name):
return getBonePositionBody(arm.bones[name])
def load(operator, context, scale = 1.0, filepath = "", global_matrix = None, use_mesh_modifiers = True): def load(operator, context, scale = 1.0, filepath = "", global_matrix = None, use_mesh_modifiers = True, ignore_lod = True):
#load .geo #load .geo
fh_in = open(filepath, "rb") fh_in = open(filepath, "rb")
geo = Geo() geo = Geo()
geo.loadFromFile(fh_in) geo.loadFromFile(fh_in)
fh_in.close() 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: 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) #create object matching model's name (or next equivilant)
mesh = bpy.data.meshes.new(name = geo_model.name.decode("utf-8")) mesh = bpy.data.meshes.new(name = model_name)
obj = bpy.data.objects.new(geo_model.name.decode("utf-8"), mesh) obj = bpy.data.objects.new(model_name, mesh)
#convert model to mesh #convert model to mesh
convert_model(geo_model, mesh, obj, scale) convert_model(geo_model, mesh, obj, scale)
#Create object for this mesh #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.collection.objects.link(obj)
bpy.context.view_layer.objects.active = 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) obj.select_set(True)
pass pass
pass pass

Loading…
Cancel
Save