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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
|
'''
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 what is selected
'''
import bpy, math
from .my_functions import *
####################################################
# transform_apply_parent_child function
# transform_apply the parent and its child meshes
# selects the parent object at the end
#
# scene --> Blender scene in which the object exists
# parent_object --> object that has child objects
# to which the transform apply
# operator will be applied
# loc (bool) --> apply location
# rot (bool) --> apply rotation
# sca (bool) --> apply scaling
####################################################
def transform_apply_parent_child(scene, parent_object, loc, rot, sca):
#
# parent object must be selected and active for the function to work
bpy.ops.object.transform_apply(location = loc, rotation = rot, scale = sca)
# armature child mesh scaling
for child_mesh in parent_object.children:
# empty selection and active object
bpy.ops.object.select_all(action = 'DESELECT')
scene.objects.active = None
# select and activate child mesh
child_mesh.select = True
scene.objects.active = child_mesh
# apply scale and rotation
bpy.ops.object.transform_apply(location = loc, rotation = rot, scale = sca)
# select parent_object
bpy.ops.object.select_all(action='DESELECT')
scene.objects.active = None
parent_object.select = True
scene.objects.active = parent_object
#
################################################
# write_bmd_bdl_collada function (MAIN FUNCTION)
# function to write custom collada file for
# SuperBMD conversion
################################################
def write_bmd_bdl_collada(context, filepath, triangulate):
#
print("\nWriting Collada file for BMD/BDL conversion\n")
# 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'):
error_string = "No Armature object selected. Select one and try again."
print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n")
show_message(error_string, "Error exporting collada file", 'ERROR')
return {'FINISHED'}
# get armature object
armature = scene.objects.active
print("Armature found: %s" % armature.name)
print()
# change to object view
bpy.ops.object.mode_set(mode='OBJECT')
# deselect everything (to be sure nothing weird happens)
bpy.ops.object.select_all(action='DESELECT')
scene.objects.active = None
# re-select the armature object only
armature.select = True
scene.objects.active = armature
# start mesh stuff
print("\n### Mesh stuff ###")
##################################
# check the meshes on the armature
for object in armature.children:
#
if (object.type == "MESH"):
#
print()
print("Mesh: \"" + object.name + "\"")
print()
####################################
# select the mesh and make it active
object.select = True
scene.objects.active = object
# reset scale, rotation and position of the mesh
bpy.ops.object.transform_apply(location = True, rotation = True, scale = True)
##############################
# check mesh armature modifier
has_arm_mod = False
for modifiers in object.modifiers:
if (modifiers.name == "Armature"):
has_arm_mod = True
if (has_arm_mod):
print("Mesh \"" + object.name + "\" has an Armature Modifier")
if (object.modifiers["Armature"].object != armature):
print("But not assigned to the right Armature Object. Re-assigning...")
object.modifiers["Armature"].object = armature
else:
print("And it is assigned to the right Armature Object: \"" + armature.name + "\"")
else:
print("Mesh \"" + object.name + "\" does not have an Armature Modifier")
print("Creating Modifier...")
bpy.ops.object.modifier_add(type='ARMATURE')
print("Assigning Modifier object to: \"" + armature.name + "\"")
object.modifiers["Armature"].object = armature
# check if the mesh contains vertex groups
# if not, assign one that links to the main armature bone
# and weight all vertex in the mesh to it, otherwise leave
# the mesh in peace (mesh might not be weighted)
# probably make a weight checker for each mesh so the exporter is complete
is_weighted = True # variable used later
if (object.vertex_groups.active == None):
print("Mesh \"" + object.name + "\" does not have any Vertex Group assigned.")
# change to edit mode (for vertex selection stuff)
bpy.ops.object.mode_set(mode='EDIT')
# get vertex group name (first armature bone name)
ver_group_name = armature.data.bones[0].name
print("Creating Vertex Group: \"" + ver_group_name + "\"")
# create new vertex group for mesh
object.vertex_groups.new(name = ver_group_name)
print("Assigning all vertex in mesh to Vertex Group...")
# select all vertex in mesh
bpy.ops.mesh.select_all(action='SELECT')
#get vertex group created
ver_group = object.vertex_groups[ver_group_name]
# set active vertex group in mesh to new vertex group created
object.vertex_groups.active = ver_group
# assign all vertex of the mesh to said vertex group
bpy.ops.object.vertex_group_assign()
print("Vertex assigned.")
# go back to object mode
bpy.ops.object.mode_set(mode='OBJECT')
else:
# if the mesh contains vertex groups then they might not
# be correctly linked to the bones on an armature,
# in other words, the mesh might contain a vertex group which name
# isn't the name of a bone in the armature object
# (this will throw a SuperBMD error)
# Assimp.AssimpException: Error importing file: Collada: [DAE filename].dae - Invalid contents in element "n".
if (len(object.vertex_groups) > len(armature.data.bones)):
error_string = "Mesh has a vertex group that isn't related to any bone of the Armature.\nCheck the meshes and try again."
print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n")
show_message(error_string, "Error exporting collada file", 'ERROR')
return {'FINISHED'}
else:
for ver_group in object.vertex_groups:
valid_vertex_group = False
for bone in armature.data.bones:
if (ver_group.name == bone.name):
valid_vertex_group = True
break
if (valid_vertex_group == False):
error_string = "Mesh has a vertex group that isn't related to any bone of the Armature.\nCheck the meshes and try again."
print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n")
show_message(error_string, "Error exporting collada file", 'ERROR')
return {'FINISHED'}
# also the group might be valid but the vertex in
# the mesh might not be weigthed in the vertex group
# print message saying that the mesh might not be weighted
# even if it has a vertex group (set is_weighted to False)
########################################
# make loop to check for weights (later)
########################################
print("Mesh \"" + object.name + "\" has a Vertex Group assigned.")
print("Mesh might not be weighted.")
is_weighted = False
# deselect mesh at the end of each loop
object.select = False
scene.objects.active = None
#
#
print("\n### Mesh stuff done ###")
print()
#################################################################
# final part of the collada export
# print messages on vertex weight given by the is_weight variable
if (is_weighted):
print("All meshes are weighted")
else:
print("All meshes are probably weighted")
print("Finalizing export...\n")
# select armature object and make it active
# do the -90 degree rotation on X axis and scale it to 100
# then do transform_apply, do transform_apply on the meshes
# inside the armature and finally export the result into a Collada file
# the 100 scaling is done because SuperBMD isn't able
# to take the scaling factor on the DAE model and apply it to
# the mesh on the BMD/BDL conversion (it just ignores it)
armature.select = True
scene.objects.active = armature
# somehow the 2 statements before this comment change
# the armature object into pose mode (???)
# bpy.ops.object.mode_set(mode='OBJECT')
# armature scale/rotation
print("Rotating Armature -90 degrees on the X axis...")
armature.rotation_euler[0] = math.radians(-90)
print("Scaling to 100...")
armature.scale = (100, 100, 100)
# transform_apply the armature and its meshes
transform_apply_parent_child(scene, armature, False, True, True)
# export model (selection only)
print("Exporting Model...")
bpy.ops.wm.collada_export(filepath = filepath,
use_blender_profile = False,
selected = True,
include_children = True,
triangulate = triangulate)
print("Collada file (DAE) exported.")
# after export reset object rotation and scaling to original state
# i.e. apply a 90 degree rotation on X axis and scale to 0.01
# then do transform_apply
# armature scale/rotation
print("Reversing the -90 degrees rotation on the X axis...")
armature.rotation_euler[0] = math.radians(90)
print("Reversing the 100 scaling...")
armature.scale = (0.01, 0.01, 0.01)
# transform_apply the armature and its meshes
transform_apply_parent_child(scene, armature, False, True, True)
print("\n### Done! ###\n")
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_BMD_BDL_Collada(Operator, ExportHelper):
#
"""Save a Collada file for BMD/BDL conversion with SuperBMD"""
bl_idname = "export_scene.collada_bmd_bdl"
bl_label = "Export Collada (for BMD/BDL)"
filename_ext = ".dae"
filter_glob = StringProperty( default="*.dae",
options={'HIDDEN'},
maxlen=255,
)
triangulate = BoolProperty( name = "Triangulate meshes",
description = "Triangulate meshes inside armatures",
default = False,
)
def execute(self, context):
return write_bmd_bdl_collada(context, self.filepath, self.triangulate)
#
def menu_export_bmd_bdl_collada(self, context):
self.layout.operator(Export_BMD_BDL_Collada.bl_idname, text="Collada (for BMD/BDL) (.dae)")
bpy.utils.register_class(Export_BMD_BDL_Collada)
bpy.types.INFO_MT_file_export.append(menu_export_bmd_bdl_collada)
# test call
bpy.ops.export_scene.collada_bmd_bdl('INVOKE_DEFAULT')
|