1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
import bpy, math
from . import blender_funcs
# just a DAE exporter.
# it was supposed to only rotate the meshes around the X axis by -90 degrees
# but above that it now checks for armature and vertex group stuff
# only exports the armature object selected
# write_bmd_bdl_collada function
# function to write custom collada file for
# SuperBMD conversion
def write_bmd_bdl_collada(context, filepath, triangulate):
# this thing is always needed for stuff
scene = bpy.context.scene
# if nothing is selected end the exporter
if (scene.objects.active == None or scene.objects.active.type != "ARMATURE"):
if (scene.objects.active == None):
blender_funcs.disp_msg("No Armature selected. Select one and try again.")
else:
blender_funcs.disp_msg("No Armature selected. Currently selecting: \"%s\""
% (scene.objects.active.name))
return {"FINISHED"}
# get armature object
armature = scene.objects.active
print("\nArmature found: \"%s\"" % (armature.name))
# select it
blender_funcs.select_obj(armature, False, "OBJECT")
# check if the armature contains only mesh objects inside
for child in armature.children:
if (child.type != "MESH"):
blender_funcs.disp_msg("\"%s\": contains non-mesh object (%s)." % (armature.name, child.name))
return {"FINISHED"}
# check if there is a single "root level" bone
# SuperBMD/SMG needs a sigle root bone and the rest must be added as a child to that bone
bone_with_no_parent_found = False
for bone in armature.data.bones:
# the root bone has been found
if (bone.parent == None):
if (bone_with_no_parent_found == False):
bone_with_no_parent_found = True
else: # second root bone?
blender_funcs.disp_msg("\"%s\": has 2 or more root level bones (max is 1)."
% (armature.name, child.name))
return {"FINISHED"}
# check if all meshes have an armature modifier that is
# assigned to the armature object and it is binded to
# vertex groups if none of those are just create them,
# I don't think it is bad to automate that
for mesh in armature.children:
if("Armature" not in mesh.modifiers):
blender_funcs.disp_msg("\"%s\": has no armature modifier. Adding one..." % (mesh.name))
blender_funcs.select_obj(mesh, False, "OBJECT")
bpy.ops.object.modifier_add(type = "ARMATURE")
mesh.modifiers["Armature"].object = armature
mesh.modifiers["Armature"].use_vertex_groups = True
else: # ensure bind is to a vertex group and that the bind is to the actual parent armature
if (mesh.modifiers["Armature"].use_vertex_groups == False):
blender_funcs.disp_msg("\"%s\": armature modifier wasn't binded to vertex groups" % (mesh.name))
mesh.modifiers["Armature"].use_vertex_groups = True
if (mesh.modifiers["Armature"].object != armature):
blender_funcs.disp_msg("\"%s\": armature modifier was binded to another armature object" % (mesh.name))
mesh.modifiers["Armature"].object = armature;
# check if all the vertex groups in each mesh correspond to the name of a skeleton bone
bone_name_list = []
for bone in armature.data.bones:
bone_name_list.append(bone.name)
# vertex group check
for mesh in armature.children:
for v_group in mesh.vertex_groups:
if (v_group.name not in bone_name_list):
blender_funcs.disp_msg(("\"%s\": contains non-valid vertex group \"%s\"." % (mesh.name, v_group.name))
+ " Unable to continue.")
return {"FINISHED"}
# vertex weight check
for mesh in armature.children:
for vertex in mesh.data.vertices:
# check if the vertex is assigned to at least a single vertex group
if (len(vertex.groups) == 0):
blender_funcs.disp_msg(("\"%s\": contains unweighted vertices." % (mesh.name))
+ " Unable to continue.")
return {"FINISHED"}
# check if vertex weights are normalized
vertex_weight = 0
for group in vertex.groups:
vertex_weight += group.weight
# calling round because there seems to be floating point issues here
if (round(vertex_weight, 5) > 1):
blender_funcs.disp_msg(("\"%s\": contains non normalized weight in vertices." % (mesh.name))
+ " Unable to continue.")
return {"FINISHED"}
# get the object names
obj_name = armature.name
child_names = []
for child in armature.children:
child_names.append(child.name)
# change to object view and make a copy of the armature object (with its children)
blender_funcs.select_obj(armature, True, "OBJECT")
bpy.ops.object.duplicate(linked = True)
bpy.ops.object.make_single_user(type = "SELECTED_OBJECTS", object = True, obdata = True)
old_armature = armature
armature = scene.objects.active
blender_funcs.select_obj(armature, False, "OBJECT")
# apply the transformations to the armature
armature.rotation_euler[0] = math.radians(-90)
armature.scale = 100 * armature.scale
bpy.ops.object.transform_apply(location = True, rotation = True, scale = True)
# apply the transformations to the meshes inside the armature
for i in range(0, len(armature.children)):
blender_funcs.select_obj(armature.children[i], False, "OBJECT")
bpy.ops.object.transform_apply(location = True, rotation = True, scale = True)
blender_funcs.select_obj(armature, False, "OBJECT")
# handle the object names so that they match the original model names
armature.name = obj_name
armature.data.name = obj_name
for i in range(0, len(armature.children)):
armature.children[i].name = child_names[i]
armature.children[i].data.name = child_names[i]
# delete all the rest_mat/bind_mat custom property matrices
# they seem to alter exported models in a weird way (I would expect it is a Assimp thing)
for bone in armature.data.bones:
if ("bind_mat" in bone):
bone.pop("bind_mat")
if ("rest_mat" in bone):
bone.pop("rest_mat")
# export the object
bpy.ops.wm.collada_export(filepath = filepath, use_blender_profile = False,
selected = True, include_children = True,
triangulate = triangulate)
# delete the duplicate object
blender_funcs.select_obj(armature, True, "OBJECT")
bpy.ops.object.delete(use_global = False)
# re-assign the original object names
armature = old_armature
armature.name = obj_name
armature.data.name = obj_name
for i in range(0, len(armature.children)):
armature.children[i].name = child_names[i]
armature.children[i].data.name = child_names[i]
# done!
blender_funcs.select_obj(armature, False, "OBJECT")
blender_funcs.disp_msg("Armature \"%s\" exported!" % (armature.name))
return {"FINISHED"}
# Stuff down is for the menu appending
# of the exporter to work plus some setting stuff
# comes from a Blender importer template
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator
class export_superbmd_collada(Operator, ExportHelper):
"""Export a Collada file for SuperBMD (SuperBMD only)"""
bl_idname = "export_scene.superbmd_collada"
bl_label = "Export SuperBMD Collada (.DAE)"
filename_ext = ".dae"
filter_glob = StringProperty(default = "*.dae", options = {'HIDDEN'}, maxlen = 255)
triangulate = BoolProperty(name = "Triangulate meshes",
description = "Triangulate meshes inside the armature",
default = False)
def execute(self, context):
return write_bmd_bdl_collada(context, self.filepath, self.triangulate)
# Only needed if you want to add into a dynamic menu
def menu_export_superbmd_collada(self, context):
self.layout.operator(export_superbmd_collada.bl_idname, text="SuperBMD Collada (.dae)")
# register func
@bpy.app.handlers.persistent
def register(dummy):
try:
bpy.utils.register_class(export_superbmd_collada)
bpy.types.INFO_MT_file_export.append(menu_export_superbmd_collada)
except:
return
# unregister func
def unregister():
try:
bpy.utils.unregister_class(export_superbmd_collada)
bpy.types.INFO_MT_file_export.remove(menu_export_superbmd_collada)
except:
return
|