· 8 years ago · Dec 18, 2017, 10:42 PM
1# =============================================================
2#
3# Open Game Engine Exchange
4# http://opengex.org/
5#
6# Export plugin for Blender
7# by Eric Lengyel
8#
9# Version 1.1.2.2
10#
11# Copyright 2015, Terathon Software LLC
12#
13# This software is licensed under the Creative Commons
14# Attribution-ShareAlike 3.0 Unported License:
15#
16# http://creativecommons.org/licenses/by-sa/3.0/deed.en_US
17#
18# =============================================================
19
20
21bl_info = {
22 "name": "OpenGEX format (.ogex)",
23 "description": "Terathon Software OpenGEX Exporter",
24 "author": "Eric Lengyel, modified by Jon Micheelsen to support an additional UV set",
25 "version": (1, 1, 2, 2),
26 "location": "File > Import-Export",
27 "wiki_url": "http://opengex.org/",
28 "category": "Import-Export"}
29
30
31import bpy
32import math
33from bpy_extras.io_utils import ExportHelper
34
35
36kNodeTypeNode = 0
37kNodeTypeBone = 1
38kNodeTypeGeometry = 2
39kNodeTypeLight = 3
40kNodeTypeCamera = 4
41
42kAnimationSampled = 0
43kAnimationLinear = 1
44kAnimationBezier = 2
45
46kExportEpsilon = 1.0e-6
47
48
49structIdentifier = [B"Node $", B"BoneNode $", B"GeometryNode $", B"LightNode $", B"CameraNode $"]
50
51
52subtranslationName = [B"xpos", B"ypos", B"zpos"]
53subrotationName = [B"xrot", B"yrot", B"zrot"]
54subscaleName = [B"xscl", B"yscl", B"zscl"]
55deltaSubtranslationName = [B"dxpos", B"dypos", B"dzpos"]
56deltaSubrotationName = [B"dxrot", B"dyrot", B"dzrot"]
57deltaSubscaleName = [B"dxscl", B"dyscl", B"dzscl"]
58axisName = [B"x", B"y", B"z"]
59
60
61class ExportVertex:
62 __slots__ = ("hash", "vertexIndex", "faceIndex", "position", "normal", "color", "texcoord0", "texcoord1", "texcoord2")
63
64 def __init__(self):
65 self.color = [1.0, 1.0, 1.0]
66 self.texcoord0 = [0.0, 0.0]
67 self.texcoord1 = [0.0, 0.0]
68 self.texcoord2 = [0.0, 0.0]
69
70 def __eq__(self, v):
71 if (self.hash != v.hash):
72 return (False)
73 if (self.position != v.position):
74 return (False)
75 if (self.normal != v.normal):
76 return (False)
77 if (self.color != v.color):
78 return (False)
79 if (self.texcoord0 != v.texcoord0):
80 return (False)
81 if (self.texcoord1 != v.texcoord1):
82 return (False)
83 if (self.texcoord2 != v.texcoord2):
84 return (False)
85 return (True)
86
87 def Hash(self):
88 h = hash(self.position[0])
89 h = h * 21737 + hash(self.position[1])
90 h = h * 21737 + hash(self.position[2])
91 h = h * 21737 + hash(self.normal[0])
92 h = h * 21737 + hash(self.normal[1])
93 h = h * 21737 + hash(self.normal[2])
94 h = h * 21737 + hash(self.color[0])
95 h = h * 21737 + hash(self.color[1])
96 h = h * 21737 + hash(self.color[2])
97 h = h * 21737 + hash(self.texcoord0[0])
98 h = h * 21737 + hash(self.texcoord0[1])
99 h = h * 21737 + hash(self.texcoord1[0])
100 h = h * 21737 + hash(self.texcoord1[1])
101 h = h * 21737 + hash(self.texcoord2[0])
102 h = h * 21737 + hash(self.texcoord2[1])
103 self.hash = h
104
105
106class OpenGexExporter(bpy.types.Operator, ExportHelper):
107 """Export to OpenGEX format"""
108 bl_idname = "export_scene.ogex"
109 bl_label = "Export OpenGEX"
110 filename_ext = ".ogex"
111
112 option_export_selection = bpy.props.BoolProperty(name = "Export Selection Only", description = "Export only selected objects", default = False)
113 option_sample_animation = bpy.props.BoolProperty(name = "Force Sampled Animation", description = "Always export animation as per-frame samples", default = False)
114
115
116 def Write(self, text):
117 self.file.write(text)
118
119
120 def IndentWrite(self, text, extra = 0, newline = False):
121 if (newline):
122 self.file.write(B"\n")
123 for i in range(self.indentLevel + extra):
124 self.file.write(B"\t")
125 self.file.write(text)
126
127
128 def WriteInt(self, i):
129 self.file.write(bytes(str(i), "UTF-8"))
130
131
132 def WriteFloat(self, f):
133 self.file.write(bytes(str(f), "UTF-8"))
134
135
136 def WriteMatrix(self, matrix):
137 self.IndentWrite(B"{", 1)
138 self.WriteFloat(matrix[0][0])
139 self.Write(B", ")
140 self.WriteFloat(matrix[1][0])
141 self.Write(B", ")
142 self.WriteFloat(matrix[2][0])
143 self.Write(B", ")
144 self.WriteFloat(matrix[3][0])
145 self.Write(B",\n")
146
147 self.IndentWrite(B" ", 1)
148 self.WriteFloat(matrix[0][1])
149 self.Write(B", ")
150 self.WriteFloat(matrix[1][1])
151 self.Write(B", ")
152 self.WriteFloat(matrix[2][1])
153 self.Write(B", ")
154 self.WriteFloat(matrix[3][1])
155 self.Write(B",\n")
156
157 self.IndentWrite(B" ", 1)
158 self.WriteFloat(matrix[0][2])
159 self.Write(B", ")
160 self.WriteFloat(matrix[1][2])
161 self.Write(B", ")
162 self.WriteFloat(matrix[2][2])
163 self.Write(B", ")
164 self.WriteFloat(matrix[3][2])
165 self.Write(B",\n")
166
167 self.IndentWrite(B" ", 1)
168 self.WriteFloat(matrix[0][3])
169 self.Write(B", ")
170 self.WriteFloat(matrix[1][3])
171 self.Write(B", ")
172 self.WriteFloat(matrix[2][3])
173 self.Write(B", ")
174 self.WriteFloat(matrix[3][3])
175 self.Write(B"}\n")
176
177
178 def WriteMatrixFlat(self, matrix):
179 self.IndentWrite(B"{", 1)
180 self.WriteFloat(matrix[0][0])
181 self.Write(B", ")
182 self.WriteFloat(matrix[1][0])
183 self.Write(B", ")
184 self.WriteFloat(matrix[2][0])
185 self.Write(B", ")
186 self.WriteFloat(matrix[3][0])
187 self.Write(B", ")
188 self.WriteFloat(matrix[0][1])
189 self.Write(B", ")
190 self.WriteFloat(matrix[1][1])
191 self.Write(B", ")
192 self.WriteFloat(matrix[2][1])
193 self.Write(B", ")
194 self.WriteFloat(matrix[3][1])
195 self.Write(B", ")
196 self.WriteFloat(matrix[0][2])
197 self.Write(B", ")
198 self.WriteFloat(matrix[1][2])
199 self.Write(B", ")
200 self.WriteFloat(matrix[2][2])
201 self.Write(B", ")
202 self.WriteFloat(matrix[3][2])
203 self.Write(B", ")
204 self.WriteFloat(matrix[0][3])
205 self.Write(B", ")
206 self.WriteFloat(matrix[1][3])
207 self.Write(B", ")
208 self.WriteFloat(matrix[2][3])
209 self.Write(B", ")
210 self.WriteFloat(matrix[3][3])
211 self.Write(B"}")
212
213
214 def WriteColor(self, color):
215 self.Write(B"{")
216 self.WriteFloat(color[0])
217 self.Write(B", ")
218 self.WriteFloat(color[1])
219 self.Write(B", ")
220 self.WriteFloat(color[2])
221 self.Write(B"}")
222
223
224 def WriteFileName(self, filename):
225 length = len(filename)
226 if (length != 0):
227 if ((length > 2) and (filename[1] == ":")):
228 self.Write(B"//")
229 self.Write(bytes(filename[0], "UTF-8"))
230 self.Write(bytes(filename[2:length].replace("\\", "/"), "UTF-8"))
231 else:
232 self.Write(bytes(filename.replace("\\", "/"), "UTF-8"))
233
234
235 def WriteIntArray(self, valueArray):
236 count = len(valueArray)
237 k = 0
238
239 lineCount = count >> 6
240 for i in range(lineCount):
241 self.IndentWrite(B"", 1)
242 for j in range(63):
243 self.WriteInt(valueArray[k])
244 self.Write(B", ")
245 k += 1
246
247 self.WriteInt(valueArray[k])
248 k += 1
249
250 if (i * 64 < count - 64):
251 self.Write(B",\n")
252 else:
253 self.Write(B"\n")
254
255 count &= 63
256 if (count != 0):
257 self.IndentWrite(B"", 1)
258 for j in range(count - 1):
259 self.WriteInt(valueArray[k])
260 self.Write(B", ")
261 k += 1
262
263 self.WriteInt(valueArray[k])
264 self.Write(B"\n")
265
266
267 def WriteFloatArray(self, valueArray):
268 count = len(valueArray)
269 k = 0
270
271 lineCount = count >> 4
272 for i in range(lineCount):
273 self.IndentWrite(B"", 1)
274 for j in range(15):
275 self.WriteFloat(valueArray[k])
276 self.Write(B", ")
277 k += 1
278
279 self.WriteFloat(valueArray[k])
280 k += 1
281
282 if (i * 16 < count - 16):
283 self.Write(B",\n")
284 else:
285 self.Write(B"\n")
286
287 count &= 15
288 if (count != 0):
289 self.IndentWrite(B"", 1)
290 for j in range(count - 1):
291 self.WriteFloat(valueArray[k])
292 self.Write(B", ")
293 k += 1
294
295 self.WriteFloat(valueArray[k])
296 self.Write(B"\n")
297
298
299 def WriteVector2D(self, vector):
300 self.Write(B"{")
301 self.WriteFloat(vector[0])
302 self.Write(B", ")
303 self.WriteFloat(vector[1])
304 self.Write(B"}")
305
306
307 def WriteVector3D(self, vector):
308 self.Write(B"{")
309 self.WriteFloat(vector[0])
310 self.Write(B", ")
311 self.WriteFloat(vector[1])
312 self.Write(B", ")
313 self.WriteFloat(vector[2])
314 self.Write(B"}")
315
316
317 def WriteVector4D(self, vector):
318 self.Write(B"{")
319 self.WriteFloat(vector[0])
320 self.Write(B", ")
321 self.WriteFloat(vector[1])
322 self.Write(B", ")
323 self.WriteFloat(vector[2])
324 self.Write(B", ")
325 self.WriteFloat(vector[3])
326 self.Write(B"}")
327
328
329 def WriteQuaternion(self, quaternion):
330 self.Write(B"{")
331 self.WriteFloat(quaternion[1])
332 self.Write(B", ")
333 self.WriteFloat(quaternion[2])
334 self.Write(B", ")
335 self.WriteFloat(quaternion[3])
336 self.Write(B", ")
337 self.WriteFloat(quaternion[0])
338 self.Write(B"}")
339
340
341 def WriteVertexArray2D(self, vertexArray, attrib):
342 count = len(vertexArray)
343 k = 0
344
345 lineCount = count >> 3
346 for i in range(lineCount):
347 self.IndentWrite(B"", 1)
348 for j in range(7):
349 self.WriteVector2D(getattr(vertexArray[k], attrib))
350 self.Write(B", ")
351 k += 1
352
353 self.WriteVector2D(getattr(vertexArray[k], attrib))
354 k += 1
355
356 if (i * 8 < count - 8):
357 self.Write(B",\n")
358 else:
359 self.Write(B"\n")
360
361 count &= 7
362 if (count != 0):
363 self.IndentWrite(B"", 1)
364 for j in range(count - 1):
365 self.WriteVector2D(getattr(vertexArray[k], attrib))
366 self.Write(B", ")
367 k += 1
368
369 self.WriteVector2D(getattr(vertexArray[k], attrib))
370 self.Write(B"\n")
371
372
373 def WriteVertexArray3D(self, vertexArray, attrib):
374 count = len(vertexArray)
375 k = 0
376
377 lineCount = count >> 3
378 for i in range(lineCount):
379 self.IndentWrite(B"", 1)
380 for j in range(7):
381 self.WriteVector3D(getattr(vertexArray[k], attrib))
382 self.Write(B", ")
383 k += 1
384
385 self.WriteVector3D(getattr(vertexArray[k], attrib))
386 k += 1
387
388 if (i * 8 < count - 8):
389 self.Write(B",\n")
390 else:
391 self.Write(B"\n")
392
393 count &= 7
394 if (count != 0):
395 self.IndentWrite(B"", 1)
396 for j in range(count - 1):
397 self.WriteVector3D(getattr(vertexArray[k], attrib))
398 self.Write(B", ")
399 k += 1
400
401 self.WriteVector3D(getattr(vertexArray[k], attrib))
402 self.Write(B"\n")
403
404
405 def WriteMorphPositionArray3D(self, vertexArray, meshVertexArray):
406 count = len(vertexArray)
407 k = 0
408
409 lineCount = count >> 3
410 for i in range(lineCount):
411 self.IndentWrite(B"", 1)
412 for j in range(7):
413 self.WriteVector3D(meshVertexArray[vertexArray[k].vertexIndex].co)
414 self.Write(B", ")
415 k += 1
416
417 self.WriteVector3D(meshVertexArray[vertexArray[k].vertexIndex].co)
418 k += 1
419
420 if (i * 8 < count - 8):
421 self.Write(B",\n")
422 else:
423 self.Write(B"\n")
424
425 count &= 7
426 if (count != 0):
427 self.IndentWrite(B"", 1)
428 for j in range(count - 1):
429 self.WriteVector3D(meshVertexArray[vertexArray[k].vertexIndex].co)
430 self.Write(B", ")
431 k += 1
432
433 self.WriteVector3D(meshVertexArray[vertexArray[k].vertexIndex].co)
434 self.Write(B"\n")
435
436
437 def WriteMorphNormalArray3D(self, vertexArray, meshVertexArray, tessFaceArray):
438 count = len(vertexArray)
439 k = 0
440
441 lineCount = count >> 3
442 for i in range(lineCount):
443 self.IndentWrite(B"", 1)
444 for j in range(7):
445 face = tessFaceArray[vertexArray[k].faceIndex]
446 self.WriteVector3D(meshVertexArray[vertexArray[k].vertexIndex].normal if (face.use_smooth) else face.normal)
447 self.Write(B", ")
448 k += 1
449
450 face = tessFaceArray[vertexArray[k].faceIndex]
451 self.WriteVector3D(meshVertexArray[vertexArray[k].vertexIndex].normal if (face.use_smooth) else face.normal)
452 k += 1
453
454 if (i * 8 < count - 8):
455 self.Write(B",\n")
456 else:
457 self.Write(B"\n")
458
459 count &= 7
460 if (count != 0):
461 self.IndentWrite(B"", 1)
462 for j in range(count - 1):
463 face = tessFaceArray[vertexArray[k].faceIndex]
464 self.WriteVector3D(meshVertexArray[vertexArray[k].vertexIndex].normal if (face.use_smooth) else face.normal)
465 self.Write(B", ")
466 k += 1
467
468 face = tessFaceArray[vertexArray[k].faceIndex]
469 self.WriteVector3D(meshVertexArray[vertexArray[k].vertexIndex].normal if (face.use_smooth) else face.normal)
470 self.Write(B"\n")
471
472
473 def WriteTriangle(self, triangleIndex, indexTable):
474 i = triangleIndex * 3
475 self.Write(B"{")
476 self.WriteInt(indexTable[i])
477 self.Write(B", ")
478 self.WriteInt(indexTable[i + 1])
479 self.Write(B", ")
480 self.WriteInt(indexTable[i + 2])
481 self.Write(B"}")
482
483
484 def WriteTriangleArray(self, count, indexTable):
485 triangleIndex = 0
486
487 lineCount = count >> 4
488 for i in range(lineCount):
489 self.IndentWrite(B"", 1)
490 for j in range(15):
491 self.WriteTriangle(triangleIndex, indexTable)
492 self.Write(B", ")
493 triangleIndex += 1
494
495 self.WriteTriangle(triangleIndex, indexTable)
496 triangleIndex += 1
497
498 if (i * 16 < count - 16):
499 self.Write(B",\n")
500 else:
501 self.Write(B"\n")
502
503 count &= 15
504 if (count != 0):
505 self.IndentWrite(B"", 1)
506 for j in range(count - 1):
507 self.WriteTriangle(triangleIndex, indexTable)
508 self.Write(B", ")
509 triangleIndex += 1
510
511 self.WriteTriangle(triangleIndex, indexTable)
512 self.Write(B"\n")
513
514
515 def WriteNodeTable(self, objectRef):
516 first = True
517 for node in objectRef[1]["nodeTable"]:
518 if (first):
519 self.Write(B"\t\t// ")
520 else:
521 self.Write(B", ")
522 self.Write(bytes(node.name, "UTF-8"))
523 first = False
524
525
526 @staticmethod
527 def GetNodeType(node):
528 if (node.type == "MESH"):
529 if (len(node.data.polygons) != 0):
530 return (kNodeTypeGeometry)
531 elif (node.type == "LAMP"):
532 type = node.data.type
533 if ((type == "SUN") or (type == "POINT") or (type == "SPOT")):
534 return (kNodeTypeLight)
535 elif (node.type == "CAMERA"):
536 return (kNodeTypeCamera)
537
538 return (kNodeTypeNode)
539
540
541 @staticmethod
542 def GetShapeKeys(mesh):
543 shapeKeys = mesh.shape_keys
544 if ((shapeKeys) and (len(shapeKeys.key_blocks) > 1)):
545 return (shapeKeys)
546
547 return (None)
548
549
550 def FindNode(self, name):
551 for nodeRef in self.nodeArray.items():
552 if (nodeRef[0].name == name):
553 return (nodeRef)
554 return (None)
555
556
557 @staticmethod
558 def DeindexMesh(mesh, materialTable):
559
560 # This function deindexes all vertex positions, colors, and texcoords.
561 # Three separate ExportVertex structures are created for each triangle.
562
563 vertexArray = mesh.vertices
564 exportVertexArray = []
565 faceIndex = 0
566
567 for face in mesh.tessfaces:
568 k1 = face.vertices[0]
569 k2 = face.vertices[1]
570 k3 = face.vertices[2]
571
572 v1 = vertexArray[k1]
573 v2 = vertexArray[k2]
574 v3 = vertexArray[k3]
575
576 exportVertex = ExportVertex()
577 exportVertex.vertexIndex = k1
578 exportVertex.faceIndex = faceIndex
579 exportVertex.position = v1.co
580 exportVertex.normal = v1.normal if (face.use_smooth) else face.normal
581 exportVertexArray.append(exportVertex)
582
583 exportVertex = ExportVertex()
584 exportVertex.vertexIndex = k2
585 exportVertex.faceIndex = faceIndex
586 exportVertex.position = v2.co
587 exportVertex.normal = v2.normal if (face.use_smooth) else face.normal
588 exportVertexArray.append(exportVertex)
589
590 exportVertex = ExportVertex()
591 exportVertex.vertexIndex = k3
592 exportVertex.faceIndex = faceIndex
593 exportVertex.position = v3.co
594 exportVertex.normal = v3.normal if (face.use_smooth) else face.normal
595 exportVertexArray.append(exportVertex)
596
597 materialTable.append(face.material_index)
598
599 if (len(face.vertices) == 4):
600 k1 = face.vertices[0]
601 k2 = face.vertices[2]
602 k3 = face.vertices[3]
603
604 v1 = vertexArray[k1]
605 v2 = vertexArray[k2]
606 v3 = vertexArray[k3]
607
608 exportVertex = ExportVertex()
609 exportVertex.vertexIndex = k1
610 exportVertex.faceIndex = faceIndex
611 exportVertex.position = v1.co
612 exportVertex.normal = v1.normal if (face.use_smooth) else face.normal
613 exportVertexArray.append(exportVertex)
614
615 exportVertex = ExportVertex()
616 exportVertex.vertexIndex = k2
617 exportVertex.faceIndex = faceIndex
618 exportVertex.position = v2.co
619 exportVertex.normal = v2.normal if (face.use_smooth) else face.normal
620 exportVertexArray.append(exportVertex)
621
622 exportVertex = ExportVertex()
623 exportVertex.vertexIndex = k3
624 exportVertex.faceIndex = faceIndex
625 exportVertex.position = v3.co
626 exportVertex.normal = v3.normal if (face.use_smooth) else face.normal
627 exportVertexArray.append(exportVertex)
628
629 materialTable.append(face.material_index)
630
631 faceIndex += 1
632
633 colorCount = len(mesh.tessface_vertex_colors)
634 if (colorCount > 0):
635 colorFace = mesh.tessface_vertex_colors[0].data
636 vertexIndex = 0
637 faceIndex = 0
638
639 for face in mesh.tessfaces:
640 cf = colorFace[faceIndex]
641 exportVertexArray[vertexIndex].color = cf.color1
642 vertexIndex += 1
643 exportVertexArray[vertexIndex].color = cf.color2
644 vertexIndex += 1
645 exportVertexArray[vertexIndex].color = cf.color3
646 vertexIndex += 1
647
648 if (len(face.vertices) == 4):
649 exportVertexArray[vertexIndex].color = cf.color1
650 vertexIndex += 1
651 exportVertexArray[vertexIndex].color = cf.color3
652 vertexIndex += 1
653 exportVertexArray[vertexIndex].color = cf.color4
654 vertexIndex += 1
655
656 faceIndex += 1
657
658 texcoordCount = len(mesh.tessface_uv_textures)
659 if (texcoordCount > 0):
660 texcoordFace = mesh.tessface_uv_textures[0].data
661 vertexIndex = 0
662 faceIndex = 0
663
664 for face in mesh.tessfaces:
665 tf = texcoordFace[faceIndex]
666 exportVertexArray[vertexIndex].texcoord0 = tf.uv1
667 vertexIndex += 1
668 exportVertexArray[vertexIndex].texcoord0 = tf.uv2
669 vertexIndex += 1
670 exportVertexArray[vertexIndex].texcoord0 = tf.uv3
671 vertexIndex += 1
672
673 if (len(face.vertices) == 4):
674 exportVertexArray[vertexIndex].texcoord0 = tf.uv1
675 vertexIndex += 1
676 exportVertexArray[vertexIndex].texcoord0 = tf.uv3
677 vertexIndex += 1
678 exportVertexArray[vertexIndex].texcoord0 = tf.uv4
679 vertexIndex += 1
680
681 faceIndex += 1
682
683 if (texcoordCount > 1):
684 texcoordFace = mesh.tessface_uv_textures[1].data
685 vertexIndex = 0
686 faceIndex = 0
687
688 for face in mesh.tessfaces:
689 tf = texcoordFace[faceIndex]
690 exportVertexArray[vertexIndex].texcoord1 = tf.uv1
691 vertexIndex += 1
692 exportVertexArray[vertexIndex].texcoord1 = tf.uv2
693 vertexIndex += 1
694 exportVertexArray[vertexIndex].texcoord1 = tf.uv3
695 vertexIndex += 1
696
697 if (len(face.vertices) == 4):
698 exportVertexArray[vertexIndex].texcoord1 = tf.uv1
699 vertexIndex += 1
700 exportVertexArray[vertexIndex].texcoord1 = tf.uv3
701 vertexIndex += 1
702 exportVertexArray[vertexIndex].texcoord1 = tf.uv4
703 vertexIndex += 1
704
705 faceIndex += 1
706
707 if (texcoordCount > 2):
708 texcoordFace = mesh.tessface_uv_textures[2].data
709 vertexIndex = 0
710 faceIndex = 0
711
712 for face in mesh.tessfaces:
713 tf = texcoordFace[faceIndex]
714 exportVertexArray[vertexIndex].texcoord2 = tf.uv1
715 vertexIndex += 1
716 exportVertexArray[vertexIndex].texcoord2 = tf.uv2
717 vertexIndex += 1
718 exportVertexArray[vertexIndex].texcoord2 = tf.uv3
719 vertexIndex += 1
720
721 if (len(face.vertices) == 4):
722 exportVertexArray[vertexIndex].texcoord2 = tf.uv1
723 vertexIndex += 1
724 exportVertexArray[vertexIndex].texcoord2 = tf.uv3
725 vertexIndex += 1
726 exportVertexArray[vertexIndex].texcoord2 = tf.uv4
727 vertexIndex += 1
728
729 faceIndex += 1
730
731 for ev in exportVertexArray:
732 ev.Hash()
733
734 return (exportVertexArray)
735
736
737 @staticmethod
738 def FindExportVertex(bucket, exportVertexArray, vertex):
739 for index in bucket:
740 if (exportVertexArray[index] == vertex):
741 return (index)
742
743 return (-1)
744
745
746 @staticmethod
747 def UnifyVertices(exportVertexArray, indexTable):
748
749 # This function looks for identical vertices having exactly the same position, normal,
750 # color, and texcoords. Duplicate vertices are unified, and a new index table is returned.
751
752 bucketCount = len(exportVertexArray) >> 3
753 if (bucketCount > 1):
754
755 # Round down to nearest power of two.
756
757 while True:
758 count = bucketCount & (bucketCount - 1)
759 if (count == 0):
760 break
761 bucketCount = count
762 else:
763 bucketCount = 1
764
765 hashTable = [[] for i in range(bucketCount)]
766 unifiedVertexArray = []
767
768 for i in range(len(exportVertexArray)):
769 ev = exportVertexArray[i]
770 bucket = ev.hash & (bucketCount - 1)
771 index = OpenGexExporter.FindExportVertex(hashTable[bucket], exportVertexArray, ev)
772 if (index < 0):
773 indexTable.append(len(unifiedVertexArray))
774 unifiedVertexArray.append(ev)
775 hashTable[bucket].append(i)
776 else:
777 indexTable.append(indexTable[index])
778
779 return (unifiedVertexArray)
780
781
782 def ProcessBone(self, bone):
783 if ((self.exportAllFlag) or (bone.select)):
784 self.nodeArray[bone] = {"nodeType" : kNodeTypeBone, "structName" : bytes("node" + str(len(self.nodeArray) + 1), "UTF-8")}
785
786 for subnode in bone.children:
787 self.ProcessBone(subnode)
788
789
790 def ProcessNode(self, node):
791 if ((self.exportAllFlag) or (node.select)):
792 type = OpenGexExporter.GetNodeType(node)
793 self.nodeArray[node] = {"nodeType" : type, "structName" : bytes("node" + str(len(self.nodeArray) + 1), "UTF-8")}
794
795 if (node.parent_type == "BONE"):
796 boneSubnodeArray = self.boneParentArray.get(node.parent_bone)
797 if (boneSubnodeArray):
798 boneSubnodeArray.append(node)
799 else:
800 self.boneParentArray[node.parent_bone] = [node]
801
802 if (node.type == "ARMATURE"):
803 skeleton = node.data
804 if (skeleton):
805 for bone in skeleton.bones:
806 if (not bone.parent):
807 self.ProcessBone(bone)
808
809 for subnode in node.children:
810 self.ProcessNode(subnode)
811
812
813 def ProcessSkinnedMeshes(self):
814 for nodeRef in self.nodeArray.items():
815 if (nodeRef[1]["nodeType"] == kNodeTypeGeometry):
816 armature = nodeRef[0].find_armature()
817 if (armature):
818 for bone in armature.data.bones:
819 boneRef = self.FindNode(bone.name)
820 if (boneRef):
821
822 # If a node is used as a bone, then we force its type to be a bone.
823
824 boneRef[1]["nodeType"] = kNodeTypeBone
825
826
827 @staticmethod
828 def ClassifyAnimationCurve(fcurve):
829 linearCount = 0
830 bezierCount = 0
831
832 for key in fcurve.keyframe_points:
833 interp = key.interpolation
834 if (interp == "LINEAR"):
835 linearCount += 1
836 elif (interp == "BEZIER"):
837 bezierCount += 1
838 else:
839 return (kAnimationSampled)
840
841 if (bezierCount == 0):
842 return (kAnimationLinear)
843 elif (linearCount == 0):
844 return (kAnimationBezier)
845
846 return (kAnimationSampled)
847
848
849 @staticmethod
850 def AnimationKeysDifferent(fcurve):
851 keyCount = len(fcurve.keyframe_points)
852 if (keyCount > 0):
853 key1 = fcurve.keyframe_points[0].co[1]
854
855 for i in range(1, keyCount):
856 key2 = fcurve.keyframe_points[i].co[1]
857 if (math.fabs(key2 - key1) > kExportEpsilon):
858 return (True)
859
860 return (False)
861
862
863 @staticmethod
864 def AnimationTangentsNonzero(fcurve):
865 keyCount = len(fcurve.keyframe_points)
866 if (keyCount > 0):
867 key = fcurve.keyframe_points[0].co[1]
868 left = fcurve.keyframe_points[0].handle_left[1]
869 right = fcurve.keyframe_points[0].handle_right[1]
870 if ((math.fabs(key - left) > kExportEpsilon) or (math.fabs(right - key) > kExportEpsilon)):
871 return (True)
872
873 for i in range(1, keyCount):
874 key = fcurve.keyframe_points[i].co[1]
875 left = fcurve.keyframe_points[i].handle_left[1]
876 right = fcurve.keyframe_points[i].handle_right[1]
877 if ((math.fabs(key - left) > kExportEpsilon) or (math.fabs(right - key) > kExportEpsilon)):
878 return (True)
879
880 return (False)
881
882
883 @staticmethod
884 def AnimationPresent(fcurve, kind):
885 if (kind != kAnimationBezier):
886 return (OpenGexExporter.AnimationKeysDifferent(fcurve))
887
888 return ((OpenGexExporter.AnimationKeysDifferent(fcurve)) or (OpenGexExporter.AnimationTangentsNonzero(fcurve)))
889
890
891 @staticmethod
892 def MatricesDifferent(m1, m2):
893 for i in range(4):
894 for j in range(4):
895 if (math.fabs(m1[i][j] - m2[i][j]) > kExportEpsilon):
896 return (True)
897
898 return (False)
899
900
901 @staticmethod
902 def CollectBoneAnimation(armature, name):
903 path = "pose.bones[\"" + name + "\"]."
904 curveArray = []
905
906 if (armature.animation_data):
907 action = armature.animation_data.action
908 if (action):
909 for fcurve in action.fcurves:
910 if (fcurve.data_path.startswith(path)):
911 curveArray.append(fcurve)
912
913 return (curveArray)
914
915
916 def ExportKeyTimes(self, fcurve):
917 self.IndentWrite(B"Key {float {")
918
919 keyCount = len(fcurve.keyframe_points)
920 for i in range(keyCount):
921 if (i > 0):
922 self.Write(B", ")
923
924 time = fcurve.keyframe_points[i].co[0] - self.beginFrame
925 self.WriteFloat(time * self.frameTime)
926
927 self.Write(B"}}\n")
928
929
930 def ExportKeyTimeControlPoints(self, fcurve):
931 self.IndentWrite(B"Key (kind = \"-control\") {float {")
932
933 keyCount = len(fcurve.keyframe_points)
934 for i in range(keyCount):
935 if (i > 0):
936 self.Write(B", ")
937
938 ctrl = fcurve.keyframe_points[i].handle_left[0] - self.beginFrame
939 self.WriteFloat(ctrl * self.frameTime)
940
941 self.Write(B"}}\n")
942 self.IndentWrite(B"Key (kind = \"+control\") {float {")
943
944 for i in range(keyCount):
945 if (i > 0):
946 self.Write(B", ")
947
948 ctrl = fcurve.keyframe_points[i].handle_right[0] - self.beginFrame
949 self.WriteFloat(ctrl * self.frameTime)
950
951 self.Write(B"}}\n")
952
953
954 def ExportKeyValues(self, fcurve):
955 self.IndentWrite(B"Key {float {")
956
957 keyCount = len(fcurve.keyframe_points)
958 for i in range(keyCount):
959 if (i > 0):
960 self.Write(B", ")
961
962 value = fcurve.keyframe_points[i].co[1]
963 self.WriteFloat(value)
964
965 self.Write(B"}}\n")
966
967
968 def ExportKeyValueControlPoints(self, fcurve):
969 self.IndentWrite(B"Key (kind = \"-control\") {float {")
970
971 keyCount = len(fcurve.keyframe_points)
972 for i in range(keyCount):
973 if (i > 0):
974 self.Write(B", ")
975
976 ctrl = fcurve.keyframe_points[i].handle_left[1]
977 self.WriteFloat(ctrl)
978
979 self.Write(B"}}\n")
980 self.IndentWrite(B"Key (kind = \"+control\") {float {")
981
982 for i in range(keyCount):
983 if (i > 0):
984 self.Write(B", ")
985
986 ctrl = fcurve.keyframe_points[i].handle_right[1]
987 self.WriteFloat(ctrl)
988
989 self.Write(B"}}\n")
990
991
992 def ExportAnimationTrack(self, fcurve, kind, target, newline):
993
994 # This function exports a single animation track. The curve types for the
995 # Time and Value structures are given by the kind parameter.
996
997 self.IndentWrite(B"Track (target = %", 0, newline)
998 self.Write(target)
999 self.Write(B")\n")
1000 self.IndentWrite(B"{\n")
1001 self.indentLevel += 1
1002
1003 if (kind != kAnimationBezier):
1004 self.IndentWrite(B"Time\n")
1005 self.IndentWrite(B"{\n")
1006 self.indentLevel += 1
1007
1008 self.ExportKeyTimes(fcurve)
1009
1010 self.IndentWrite(B"}\n\n", -1)
1011 self.IndentWrite(B"Value\n", -1)
1012 self.IndentWrite(B"{\n", -1)
1013
1014 self.ExportKeyValues(fcurve)
1015
1016 self.indentLevel -= 1
1017 self.IndentWrite(B"}\n")
1018
1019 else:
1020 self.IndentWrite(B"Time (curve = \"bezier\")\n")
1021 self.IndentWrite(B"{\n")
1022 self.indentLevel += 1
1023
1024 self.ExportKeyTimes(fcurve)
1025 self.ExportKeyTimeControlPoints(fcurve)
1026
1027 self.IndentWrite(B"}\n\n", -1)
1028 self.IndentWrite(B"Value (curve = \"bezier\")\n", -1)
1029 self.IndentWrite(B"{\n", -1)
1030
1031 self.ExportKeyValues(fcurve)
1032 self.ExportKeyValueControlPoints(fcurve)
1033
1034 self.indentLevel -= 1
1035 self.IndentWrite(B"}\n")
1036
1037 self.indentLevel -= 1
1038 self.IndentWrite(B"}\n")
1039
1040
1041 def ExportNodeSampledAnimation(self, node, scene):
1042
1043 # This function exports animation as full 4x4 matrices for each frame.
1044
1045 currentFrame = scene.frame_current
1046 currentSubframe = scene.frame_subframe
1047
1048 animationFlag = False
1049 m1 = node.matrix_local.copy()
1050
1051 for i in range(self.beginFrame, self.endFrame):
1052 scene.frame_set(i)
1053 m2 = node.matrix_local
1054 if (OpenGexExporter.MatricesDifferent(m1, m2)):
1055 animationFlag = True
1056 break
1057
1058 if (animationFlag):
1059 self.IndentWrite(B"Animation\n", 0, True)
1060 self.IndentWrite(B"{\n")
1061 self.indentLevel += 1
1062
1063 self.IndentWrite(B"Track (target = %transform)\n")
1064 self.IndentWrite(B"{\n")
1065 self.indentLevel += 1
1066
1067 self.IndentWrite(B"Time\n")
1068 self.IndentWrite(B"{\n")
1069 self.indentLevel += 1
1070
1071 self.IndentWrite(B"Key {float {")
1072
1073 for i in range(self.beginFrame, self.endFrame):
1074 self.WriteFloat((i - self.beginFrame) * self.frameTime)
1075 self.Write(B", ")
1076
1077 self.WriteFloat(self.endFrame * self.frameTime)
1078 self.Write(B"}}\n")
1079
1080 self.IndentWrite(B"}\n\n", -1)
1081 self.IndentWrite(B"Value\n", -1)
1082 self.IndentWrite(B"{\n", -1)
1083
1084 self.IndentWrite(B"Key\n")
1085 self.IndentWrite(B"{\n")
1086 self.indentLevel += 1
1087
1088 self.IndentWrite(B"float[16]\n")
1089 self.IndentWrite(B"{\n")
1090
1091 for i in range(self.beginFrame, self.endFrame):
1092 scene.frame_set(i)
1093 self.WriteMatrixFlat(node.matrix_local)
1094 self.Write(B",\n")
1095
1096 scene.frame_set(self.endFrame)
1097 self.WriteMatrixFlat(node.matrix_local)
1098 self.IndentWrite(B"}\n", 0, True)
1099
1100 self.indentLevel -= 1
1101 self.IndentWrite(B"}\n")
1102
1103 self.indentLevel -= 1
1104 self.IndentWrite(B"}\n")
1105
1106 self.indentLevel -= 1
1107 self.IndentWrite(B"}\n")
1108
1109 self.indentLevel -= 1
1110 self.IndentWrite(B"}\n")
1111
1112 scene.frame_set(currentFrame, currentSubframe)
1113
1114
1115 def ExportBoneSampledAnimation(self, poseBone, scene):
1116
1117 # This function exports bone animation as full 4x4 matrices for each frame.
1118
1119 currentFrame = scene.frame_current
1120 currentSubframe = scene.frame_subframe
1121
1122 animationFlag = False
1123 m1 = poseBone.matrix.copy()
1124
1125 for i in range(self.beginFrame, self.endFrame):
1126 scene.frame_set(i)
1127 m2 = poseBone.matrix
1128 if (OpenGexExporter.MatricesDifferent(m1, m2)):
1129 animationFlag = True
1130 break
1131
1132 if (animationFlag):
1133 self.IndentWrite(B"Animation\n", 0, True)
1134 self.IndentWrite(B"{\n")
1135 self.indentLevel += 1
1136
1137 self.IndentWrite(B"Track (target = %transform)\n")
1138 self.IndentWrite(B"{\n")
1139 self.indentLevel += 1
1140
1141 self.IndentWrite(B"Time\n")
1142 self.IndentWrite(B"{\n")
1143 self.indentLevel += 1
1144
1145 self.IndentWrite(B"Key {float {")
1146
1147 for i in range(self.beginFrame, self.endFrame):
1148 self.WriteFloat((i - self.beginFrame) * self.frameTime)
1149 self.Write(B", ")
1150
1151 self.WriteFloat(self.endFrame * self.frameTime)
1152 self.Write(B"}}\n")
1153
1154 self.IndentWrite(B"}\n\n", -1)
1155 self.IndentWrite(B"Value\n", -1)
1156 self.IndentWrite(B"{\n", -1)
1157
1158 self.IndentWrite(B"Key\n")
1159 self.IndentWrite(B"{\n")
1160 self.indentLevel += 1
1161
1162 self.IndentWrite(B"float[16]\n")
1163 self.IndentWrite(B"{\n")
1164
1165 parent = poseBone.parent
1166 if (parent):
1167 for i in range(self.beginFrame, self.endFrame):
1168 scene.frame_set(i)
1169 self.WriteMatrixFlat(parent.matrix.inverted() * poseBone.matrix)
1170 self.Write(B",\n")
1171
1172 scene.frame_set(self.endFrame)
1173 self.WriteMatrixFlat(parent.matrix.inverted() * poseBone.matrix)
1174 self.IndentWrite(B"}\n", 0, True)
1175
1176 else:
1177 for i in range(self.beginFrame, self.endFrame):
1178 scene.frame_set(i)
1179 self.WriteMatrixFlat(poseBone.matrix)
1180 self.Write(B",\n")
1181
1182 scene.frame_set(self.endFrame)
1183 self.WriteMatrixFlat(poseBone.matrix)
1184 self.IndentWrite(B"}\n", 0, True)
1185
1186 self.indentLevel -= 1
1187 self.IndentWrite(B"}\n")
1188
1189 self.indentLevel -= 1
1190 self.IndentWrite(B"}\n")
1191
1192 self.indentLevel -= 1
1193 self.IndentWrite(B"}\n")
1194
1195 self.indentLevel -= 1
1196 self.IndentWrite(B"}\n")
1197
1198 scene.frame_set(currentFrame, currentSubframe)
1199
1200
1201 def ExportMorphWeightSampledAnimationTrack(self, block, target, scene, newline):
1202 currentFrame = scene.frame_current
1203 currentSubframe = scene.frame_subframe
1204
1205 self.IndentWrite(B"Track (target = %", 0, newline)
1206 self.Write(target)
1207 self.Write(B")\n")
1208 self.IndentWrite(B"{\n")
1209 self.indentLevel += 1
1210
1211 self.IndentWrite(B"Time\n")
1212 self.IndentWrite(B"{\n")
1213 self.indentLevel += 1
1214
1215 self.IndentWrite(B"Key {float {")
1216
1217 for i in range(self.beginFrame, self.endFrame):
1218 self.WriteFloat((i - self.beginFrame) * self.frameTime)
1219 self.Write(B", ")
1220
1221 self.WriteFloat(self.endFrame * self.frameTime)
1222 self.Write(B"}}\n")
1223
1224 self.IndentWrite(B"}\n\n", -1)
1225 self.IndentWrite(B"Value\n", -1)
1226 self.IndentWrite(B"{\n", -1)
1227
1228 self.IndentWrite(B"Key {float {")
1229
1230 for i in range(self.beginFrame, self.endFrame):
1231 scene.frame_set(i)
1232 self.WriteFloat(block.value)
1233 self.Write(B", ")
1234
1235 scene.frame_set(self.endFrame)
1236 self.WriteFloat(block.value)
1237 self.Write(B"}}\n")
1238
1239 self.indentLevel -= 1
1240 self.IndentWrite(B"}\n")
1241
1242 self.indentLevel -= 1
1243 self.IndentWrite(B"}\n")
1244
1245 scene.frame_set(currentFrame, currentSubframe)
1246
1247
1248 def ExportNodeTransform(self, node, scene):
1249 posAnimCurve = [None, None, None]
1250 rotAnimCurve = [None, None, None]
1251 sclAnimCurve = [None, None, None]
1252 posAnimKind = [0, 0, 0]
1253 rotAnimKind = [0, 0, 0]
1254 sclAnimKind = [0, 0, 0]
1255
1256 deltaPosAnimCurve = [None, None, None]
1257 deltaRotAnimCurve = [None, None, None]
1258 deltaSclAnimCurve = [None, None, None]
1259 deltaPosAnimKind = [0, 0, 0]
1260 deltaRotAnimKind = [0, 0, 0]
1261 deltaSclAnimKind = [0, 0, 0]
1262
1263 positionAnimated = False
1264 rotationAnimated = False
1265 scaleAnimated = False
1266 posAnimated = [False, False, False]
1267 rotAnimated = [False, False, False]
1268 sclAnimated = [False, False, False]
1269
1270 deltaPositionAnimated = False
1271 deltaRotationAnimated = False
1272 deltaScaleAnimated = False
1273 deltaPosAnimated = [False, False, False]
1274 deltaRotAnimated = [False, False, False]
1275 deltaSclAnimated = [False, False, False]
1276
1277 mode = node.rotation_mode
1278 sampledAnimation = ((self.sampleAnimationFlag) or (mode == "QUATERNION") or (mode == "AXIS_ANGLE"))
1279
1280 if ((not sampledAnimation) and (node.animation_data)):
1281 action = node.animation_data.action
1282 if (action):
1283 for fcurve in action.fcurves:
1284 kind = OpenGexExporter.ClassifyAnimationCurve(fcurve)
1285 if (kind != kAnimationSampled):
1286 if (fcurve.data_path == "location"):
1287 for i in range(3):
1288 if ((fcurve.array_index == i) and (not posAnimCurve[i])):
1289 posAnimCurve[i] = fcurve
1290 posAnimKind[i] = kind
1291 if (OpenGexExporter.AnimationPresent(fcurve, kind)):
1292 posAnimated[i] = True
1293 elif (fcurve.data_path == "delta_location"):
1294 for i in range(3):
1295 if ((fcurve.array_index == i) and (not deltaPosAnimCurve[i])):
1296 deltaPosAnimCurve[i] = fcurve
1297 deltaPosAnimKind[i] = kind
1298 if (OpenGexExporter.AnimationPresent(fcurve, kind)):
1299 deltaPosAnimated[i] = True
1300 elif (fcurve.data_path == "rotation_euler"):
1301 for i in range(3):
1302 if ((fcurve.array_index == i) and (not rotAnimCurve[i])):
1303 rotAnimCurve[i] = fcurve
1304 rotAnimKind[i] = kind
1305 if (OpenGexExporter.AnimationPresent(fcurve, kind)):
1306 rotAnimated[i] = True
1307 elif (fcurve.data_path == "delta_rotation_euler"):
1308 for i in range(3):
1309 if ((fcurve.array_index == i) and (not deltaRotAnimCurve[i])):
1310 deltaRotAnimCurve[i] = fcurve
1311 deltaRotAnimKind[i] = kind
1312 if (OpenGexExporter.AnimationPresent(fcurve, kind)):
1313 deltaRotAnimated[i] = True
1314 elif (fcurve.data_path == "scale"):
1315 for i in range(3):
1316 if ((fcurve.array_index == i) and (not sclAnimCurve[i])):
1317 sclAnimCurve[i] = fcurve
1318 sclAnimKind[i] = kind
1319 if (OpenGexExporter.AnimationPresent(fcurve, kind)):
1320 sclAnimated[i] = True
1321 elif (fcurve.data_path == "delta_scale"):
1322 for i in range(3):
1323 if ((fcurve.array_index == i) and (not deltaSclAnimCurve[i])):
1324 deltaSclAnimCurve[i] = fcurve
1325 deltaSclAnimKind[i] = kind
1326 if (OpenGexExporter.AnimationPresent(fcurve, kind)):
1327 deltaSclAnimated[i] = True
1328 elif ((fcurve.data_path == "rotation_axis_angle") or (fcurve.data_path == "rotation_quaternion") or (fcurve.data_path == "delta_rotation_quaternion")):
1329 sampledAnimation = True
1330 break
1331 else:
1332 sampledAnimation = True
1333 break
1334
1335 positionAnimated = posAnimated[0] | posAnimated[1] | posAnimated[2]
1336 rotationAnimated = rotAnimated[0] | rotAnimated[1] | rotAnimated[2]
1337 scaleAnimated = sclAnimated[0] | sclAnimated[1] | sclAnimated[2]
1338
1339 deltaPositionAnimated = deltaPosAnimated[0] | deltaPosAnimated[1] | deltaPosAnimated[2]
1340 deltaRotationAnimated = deltaRotAnimated[0] | deltaRotAnimated[1] | deltaRotAnimated[2]
1341 deltaScaleAnimated = deltaSclAnimated[0] | deltaSclAnimated[1] | deltaSclAnimated[2]
1342
1343 if ((sampledAnimation) or ((not positionAnimated) and (not rotationAnimated) and (not scaleAnimated) and (not deltaPositionAnimated) and (not deltaRotationAnimated) and (not deltaScaleAnimated))):
1344
1345 # If there's no keyframe animation at all, then write the node transform as a single 4x4 matrix.
1346 # We might still be exporting sampled animation below.
1347
1348 self.IndentWrite(B"Transform")
1349
1350 if (sampledAnimation):
1351 self.Write(B" %transform")
1352
1353 self.IndentWrite(B"{\n", 0, True)
1354 self.indentLevel += 1
1355
1356 self.IndentWrite(B"float[16]\n")
1357 self.IndentWrite(B"{\n")
1358 self.WriteMatrix(node.matrix_local)
1359 self.IndentWrite(B"}\n")
1360
1361 self.indentLevel -= 1
1362 self.IndentWrite(B"}\n")
1363
1364 if (sampledAnimation):
1365 self.ExportNodeSampledAnimation(node, scene)
1366
1367 else:
1368 structFlag = False
1369
1370 deltaTranslation = node.delta_location
1371 if (deltaPositionAnimated):
1372
1373 # When the delta location is animated, write the x, y, and z components separately
1374 # so they can be targeted by different tracks having different sets of keys.
1375
1376 for i in range(3):
1377 pos = deltaTranslation[i]
1378 if ((deltaPosAnimated[i]) or (math.fabs(pos) > kExportEpsilon)):
1379 self.IndentWrite(B"Translation %", 0, structFlag)
1380 self.Write(deltaSubtranslationName[i])
1381 self.Write(B" (kind = \"")
1382 self.Write(axisName[i])
1383 self.Write(B"\")\n")
1384 self.IndentWrite(B"{\n")
1385 self.IndentWrite(B"float {", 1)
1386 self.WriteFloat(pos)
1387 self.Write(B"}")
1388 self.IndentWrite(B"}\n", 0, True)
1389
1390 structFlag = True
1391
1392 elif ((math.fabs(deltaTranslation[0]) > kExportEpsilon) or (math.fabs(deltaTranslation[1]) > kExportEpsilon) or (math.fabs(deltaTranslation[2]) > kExportEpsilon)):
1393 self.IndentWrite(B"Translation\n")
1394 self.IndentWrite(B"{\n")
1395 self.IndentWrite(B"float[3] {", 1)
1396 self.WriteVector3D(deltaTranslation)
1397 self.Write(B"}")
1398 self.IndentWrite(B"}\n", 0, True)
1399
1400 structFlag = True
1401
1402 translation = node.location
1403 if (positionAnimated):
1404
1405 # When the location is animated, write the x, y, and z components separately
1406 # so they can be targeted by different tracks having different sets of keys.
1407
1408 for i in range(3):
1409 pos = translation[i]
1410 if ((posAnimated[i]) or (math.fabs(pos) > kExportEpsilon)):
1411 self.IndentWrite(B"Translation %", 0, structFlag)
1412 self.Write(subtranslationName[i])
1413 self.Write(B" (kind = \"")
1414 self.Write(axisName[i])
1415 self.Write(B"\")\n")
1416 self.IndentWrite(B"{\n")
1417 self.IndentWrite(B"float {", 1)
1418 self.WriteFloat(pos)
1419 self.Write(B"}")
1420 self.IndentWrite(B"}\n", 0, True)
1421
1422 structFlag = True
1423
1424 elif ((math.fabs(translation[0]) > kExportEpsilon) or (math.fabs(translation[1]) > kExportEpsilon) or (math.fabs(translation[2]) > kExportEpsilon)):
1425 self.IndentWrite(B"Translation\n")
1426 self.IndentWrite(B"{\n")
1427 self.IndentWrite(B"float[3] {", 1)
1428 self.WriteVector3D(translation)
1429 self.Write(B"}")
1430 self.IndentWrite(B"}\n", 0, True)
1431
1432 structFlag = True
1433
1434 if (deltaRotationAnimated):
1435
1436 # When the delta rotation is animated, write three separate Euler angle rotations
1437 # so they can be targeted by different tracks having different sets of keys.
1438
1439 for i in range(3):
1440 axis = ord(mode[2 - i]) - 0x58
1441 angle = node.delta_rotation_euler[axis]
1442 if ((deltaRotAnimated[axis]) or (math.fabs(angle) > kExportEpsilon)):
1443 self.IndentWrite(B"Rotation %", 0, structFlag)
1444 self.Write(deltaSubrotationName[axis])
1445 self.Write(B" (kind = \"")
1446 self.Write(axisName[axis])
1447 self.Write(B"\")\n")
1448 self.IndentWrite(B"{\n")
1449 self.IndentWrite(B"float {", 1)
1450 self.WriteFloat(angle)
1451 self.Write(B"}")
1452 self.IndentWrite(B"}\n", 0, True)
1453
1454 structFlag = True
1455
1456 else:
1457
1458 # When the delta rotation is not animated, write it in the representation given by
1459 # the node's current rotation mode. (There is no axis-angle delta rotation.)
1460
1461 if (mode == "QUATERNION"):
1462 quaternion = node.delta_rotation_quaternion
1463 if ((math.fabs(quaternion[0] - 1.0) > kExportEpsilon) or (math.fabs(quaternion[1]) > kExportEpsilon) or (math.fabs(quaternion[2]) > kExportEpsilon) or (math.fabs(quaternion[3]) > kExportEpsilon)):
1464 self.IndentWrite(B"Rotation (kind = \"quaternion\")\n", 0, structFlag)
1465 self.IndentWrite(B"{\n")
1466 self.IndentWrite(B"float[4] {", 1)
1467 self.WriteQuaternion(quaternion)
1468 self.Write(B"}")
1469 self.IndentWrite(B"}\n", 0, True)
1470
1471 structFlag = True
1472
1473 else:
1474 for i in range(3):
1475 axis = ord(mode[2 - i]) - 0x58
1476 angle = node.delta_rotation_euler[axis]
1477 if (math.fabs(angle) > kExportEpsilon):
1478 self.IndentWrite(B"Rotation (kind = \"", 0, structFlag)
1479 self.Write(axisName[axis])
1480 self.Write(B"\")\n")
1481 self.IndentWrite(B"{\n")
1482 self.IndentWrite(B"float {", 1)
1483 self.WriteFloat(angle)
1484 self.Write(B"}")
1485 self.IndentWrite(B"}\n", 0, True)
1486
1487 structFlag = True
1488
1489 if (rotationAnimated):
1490
1491 # When the rotation is animated, write three separate Euler angle rotations
1492 # so they can be targeted by different tracks having different sets of keys.
1493
1494 for i in range(3):
1495 axis = ord(mode[2 - i]) - 0x58
1496 angle = node.rotation_euler[axis]
1497 if ((rotAnimated[axis]) or (math.fabs(angle) > kExportEpsilon)):
1498 self.IndentWrite(B"Rotation %", 0, structFlag)
1499 self.Write(subrotationName[axis])
1500 self.Write(B" (kind = \"")
1501 self.Write(axisName[axis])
1502 self.Write(B"\")\n")
1503 self.IndentWrite(B"{\n")
1504 self.IndentWrite(B"float {", 1)
1505 self.WriteFloat(angle)
1506 self.Write(B"}")
1507 self.IndentWrite(B"}\n", 0, True)
1508
1509 structFlag = True
1510
1511 else:
1512
1513 # When the rotation is not animated, write it in the representation given by
1514 # the node's current rotation mode.
1515
1516 if (mode == "QUATERNION"):
1517 quaternion = node.rotation_quaternion
1518 if ((math.fabs(quaternion[0] - 1.0) > kExportEpsilon) or (math.fabs(quaternion[1]) > kExportEpsilon) or (math.fabs(quaternion[2]) > kExportEpsilon) or (math.fabs(quaternion[3]) > kExportEpsilon)):
1519 self.IndentWrite(B"Rotation (kind = \"quaternion\")\n", 0, structFlag)
1520 self.IndentWrite(B"{\n")
1521 self.IndentWrite(B"float[4] {", 1)
1522 self.WriteQuaternion(quaternion)
1523 self.Write(B"}")
1524 self.IndentWrite(B"}\n", 0, True)
1525
1526 structFlag = True
1527
1528 elif (mode == "AXIS_ANGLE"):
1529 if (math.fabs(node.rotation_axis_angle[0]) > kExportEpsilon):
1530 self.IndentWrite(B"Rotation (kind = \"axis\")\n", 0, structFlag)
1531 self.IndentWrite(B"{\n")
1532 self.IndentWrite(B"float[4] {", 1)
1533 self.WriteVector4D(node.rotation_axis_angle)
1534 self.Write(B"}")
1535 self.IndentWrite(B"}\n", 0, True)
1536
1537 structFlag = True
1538
1539 else:
1540 for i in range(3):
1541 axis = ord(mode[2 - i]) - 0x58
1542 angle = node.rotation_euler[axis]
1543 if (math.fabs(angle) > kExportEpsilon):
1544 self.IndentWrite(B"Rotation (kind = \"", 0, structFlag)
1545 self.Write(axisName[axis])
1546 self.Write(B"\")\n")
1547 self.IndentWrite(B"{\n")
1548 self.IndentWrite(B"float {", 1)
1549 self.WriteFloat(angle)
1550 self.Write(B"}")
1551 self.IndentWrite(B"}\n", 0, True)
1552
1553 structFlag = True
1554
1555 deltaScale = node.delta_scale
1556 if (deltaScaleAnimated):
1557
1558 # When the delta scale is animated, write the x, y, and z components separately
1559 # so they can be targeted by different tracks having different sets of keys.
1560
1561 for i in range(3):
1562 scl = deltaScale[i]
1563 if ((deltaSclAnimated[i]) or (math.fabs(scl) > kExportEpsilon)):
1564 self.IndentWrite(B"Scale %", 0, structFlag)
1565 self.Write(deltaSubscaleName[i])
1566 self.Write(B" (kind = \"")
1567 self.Write(axisName[i])
1568 self.Write(B"\")\n")
1569 self.IndentWrite(B"{\n")
1570 self.IndentWrite(B"float {", 1)
1571 self.WriteFloat(scl)
1572 self.Write(B"}")
1573 self.IndentWrite(B"}\n", 0, True)
1574
1575 structFlag = True
1576
1577 elif ((math.fabs(deltaScale[0] - 1.0) > kExportEpsilon) or (math.fabs(deltaScale[1] - 1.0) > kExportEpsilon) or (math.fabs(deltaScale[2] - 1.0) > kExportEpsilon)):
1578 self.IndentWrite(B"Scale\n", 0, structFlag)
1579 self.IndentWrite(B"{\n")
1580 self.IndentWrite(B"float[3] {", 1)
1581 self.WriteVector3D(deltaScale)
1582 self.Write(B"}")
1583 self.IndentWrite(B"}\n", 0, True)
1584
1585 structFlag = True
1586
1587 scale = node.scale
1588 if (scaleAnimated):
1589
1590 # When the scale is animated, write the x, y, and z components separately
1591 # so they can be targeted by different tracks having different sets of keys.
1592
1593 for i in range(3):
1594 scl = scale[i]
1595 if ((sclAnimated[i]) or (math.fabs(scl) > kExportEpsilon)):
1596 self.IndentWrite(B"Scale %", 0, structFlag)
1597 self.Write(subscaleName[i])
1598 self.Write(B" (kind = \"")
1599 self.Write(axisName[i])
1600 self.Write(B"\")\n")
1601 self.IndentWrite(B"{\n")
1602 self.IndentWrite(B"float {", 1)
1603 self.WriteFloat(scl)
1604 self.Write(B"}")
1605 self.IndentWrite(B"}\n", 0, True)
1606
1607 structFlag = True
1608
1609 elif ((math.fabs(scale[0] - 1.0) > kExportEpsilon) or (math.fabs(scale[1] - 1.0) > kExportEpsilon) or (math.fabs(scale[2] - 1.0) > kExportEpsilon)):
1610 self.IndentWrite(B"Scale\n", 0, structFlag)
1611 self.IndentWrite(B"{\n")
1612 self.IndentWrite(B"float[3] {", 1)
1613 self.WriteVector3D(scale)
1614 self.Write(B"}")
1615 self.IndentWrite(B"}\n", 0, True)
1616
1617 structFlag = True
1618
1619 # Export the animation tracks.
1620
1621 self.IndentWrite(B"Animation (begin = ", 0, True)
1622 self.WriteFloat((action.frame_range[0] - self.beginFrame) * self.frameTime)
1623 self.Write(B", end = ")
1624 self.WriteFloat((action.frame_range[1] - self.beginFrame) * self.frameTime)
1625 self.Write(B")\n")
1626 self.IndentWrite(B"{\n")
1627 self.indentLevel += 1
1628
1629 structFlag = False
1630
1631 if (positionAnimated):
1632 for i in range(3):
1633 if (posAnimated[i]):
1634 self.ExportAnimationTrack(posAnimCurve[i], posAnimKind[i], subtranslationName[i], structFlag)
1635 structFlag = True
1636
1637 if (rotationAnimated):
1638 for i in range(3):
1639 if (rotAnimated[i]):
1640 self.ExportAnimationTrack(rotAnimCurve[i], rotAnimKind[i], subrotationName[i], structFlag)
1641 structFlag = True
1642
1643 if (scaleAnimated):
1644 for i in range(3):
1645 if (sclAnimated[i]):
1646 self.ExportAnimationTrack(sclAnimCurve[i], sclAnimKind[i], subscaleName[i], structFlag)
1647 structFlag = True
1648
1649 if (deltaPositionAnimated):
1650 for i in range(3):
1651 if (deltaPosAnimated[i]):
1652 self.ExportAnimationTrack(deltaPosAnimCurve[i], deltaPosAnimKind[i], deltaSubtranslationName[i], structFlag)
1653 structFlag = True
1654
1655 if (deltaRotationAnimated):
1656 for i in range(3):
1657 if (deltaRotAnimated[i]):
1658 self.ExportAnimationTrack(deltaRotAnimCurve[i], deltaRotAnimKind[i], deltaSubrotationName[i], structFlag)
1659 structFlag = True
1660
1661 if (deltaScaleAnimated):
1662 for i in range(3):
1663 if (deltaSclAnimated[i]):
1664 self.ExportAnimationTrack(deltaSclAnimCurve[i], deltaSclAnimKind[i], deltaSubscaleName[i], structFlag)
1665 structFlag = True
1666
1667 self.indentLevel -= 1
1668 self.IndentWrite(B"}\n")
1669
1670
1671 def ExportBoneTransform(self, armature, bone, scene):
1672
1673 curveArray = self.CollectBoneAnimation(armature, bone.name)
1674 animation = ((len(curveArray) != 0) or (self.sampleAnimationFlag))
1675
1676 transform = bone.matrix_local.copy()
1677 parentBone = bone.parent
1678 if (parentBone):
1679 transform = parentBone.matrix_local.inverted() * transform
1680
1681 poseBone = armature.pose.bones.get(bone.name)
1682 if (poseBone):
1683 transform = poseBone.matrix.copy()
1684 parentPoseBone = poseBone.parent
1685 if (parentPoseBone):
1686 transform = parentPoseBone.matrix.inverted() * transform
1687
1688 self.IndentWrite(B"Transform")
1689
1690 if (animation):
1691 self.Write(B" %transform")
1692
1693 self.IndentWrite(B"{\n", 0, True)
1694 self.indentLevel += 1
1695
1696 self.IndentWrite(B"float[16]\n")
1697 self.IndentWrite(B"{\n")
1698 self.WriteMatrix(transform)
1699 self.IndentWrite(B"}\n")
1700
1701 self.indentLevel -= 1
1702 self.IndentWrite(B"}\n")
1703
1704 if ((animation) and (poseBone)):
1705 self.ExportBoneSampledAnimation(poseBone, scene)
1706
1707
1708 def ExportMaterialRef(self, material, index):
1709 if (not material in self.materialArray):
1710 self.materialArray[material] = {"structName" : bytes("material" + str(len(self.materialArray) + 1), "UTF-8")}
1711
1712 self.IndentWrite(B"MaterialRef (index = ")
1713 self.WriteInt(index)
1714 self.Write(B") {ref {$")
1715 self.Write(self.materialArray[material]["structName"])
1716 self.Write(B"}}\n")
1717
1718
1719 def ExportMorphWeights(self, node, shapeKeys, scene):
1720 action = None
1721 curveArray = []
1722 indexArray = []
1723
1724 if (shapeKeys.animation_data):
1725 action = shapeKeys.animation_data.action
1726 if (action):
1727 for fcurve in action.fcurves:
1728 if ((fcurve.data_path.startswith("key_blocks[")) and (fcurve.data_path.endswith("].value"))):
1729 keyName = fcurve.data_path.strip("abcdehklopstuvy[]_.")
1730 if ((keyName[0] == "\"") or (keyName[0] == "'")):
1731 index = shapeKeys.key_blocks.find(keyName.strip("\"'"))
1732 if (index >= 0):
1733 curveArray.append(fcurve)
1734 indexArray.append(index)
1735 else:
1736 curveArray.append(fcurve)
1737 indexArray.append(int(keyName))
1738
1739 if ((not action) and (node.animation_data)):
1740 action = node.animation_data.action
1741 if (action):
1742 for fcurve in action.fcurves:
1743 if ((fcurve.data_path.startswith("data.shape_keys.key_blocks[")) and (fcurve.data_path.endswith("].value"))):
1744 keyName = fcurve.data_path.strip("abcdehklopstuvy[]_.")
1745 if ((keyName[0] == "\"") or (keyName[0] == "'")):
1746 index = shapeKeys.key_blocks.find(keyName.strip("\"'"))
1747 if (index >= 0):
1748 curveArray.append(fcurve)
1749 indexArray.append(index)
1750 else:
1751 curveArray.append(fcurve)
1752 indexArray.append(int(keyName))
1753
1754 animated = (len(curveArray) != 0)
1755 referenceName = shapeKeys.reference_key.name if (shapeKeys.use_relative) else ""
1756
1757 for k in range(len(shapeKeys.key_blocks)):
1758 self.IndentWrite(B"MorphWeight", 0, (k == 0))
1759
1760 if (animated):
1761 self.Write(B" %mw")
1762 self.WriteInt(k)
1763
1764 self.Write(B" (index = ")
1765 self.WriteInt(k)
1766 self.Write(B") {float {")
1767
1768 block = shapeKeys.key_blocks[k]
1769 self.WriteFloat(block.value if (block.name != referenceName) else 1.0)
1770
1771 self.Write(B"}}\n")
1772
1773 if (animated):
1774 self.IndentWrite(B"Animation (begin = ", 0, True)
1775 self.WriteFloat((action.frame_range[0] - self.beginFrame) * self.frameTime)
1776 self.Write(B", end = ")
1777 self.WriteFloat((action.frame_range[1] - self.beginFrame) * self.frameTime)
1778 self.Write(B")\n")
1779 self.IndentWrite(B"{\n")
1780 self.indentLevel += 1
1781
1782 structFlag = False
1783
1784 for a in range(len(curveArray)):
1785 k = indexArray[a]
1786 target = bytes("mw" + str(k), "UTF-8")
1787
1788 fcurve = curveArray[a]
1789 kind = OpenGexExporter.ClassifyAnimationCurve(fcurve)
1790 if ((kind != kAnimationSampled) and (not self.sampleAnimationFlag)):
1791 self.ExportAnimationTrack(fcurve, kind, target, structFlag)
1792 else:
1793 self.ExportMorphWeightSampledAnimationTrack(shapeKeys.key_blocks[k], target, scene, structFlag)
1794
1795 structFlag = True
1796
1797 self.indentLevel -= 1
1798 self.IndentWrite(B"}\n")
1799
1800
1801 def ExportBone(self, armature, bone, scene):
1802 nodeRef = self.nodeArray.get(bone)
1803 if (nodeRef):
1804 self.IndentWrite(structIdentifier[nodeRef["nodeType"]], 0, True)
1805 self.Write(nodeRef["structName"])
1806
1807 self.IndentWrite(B"{\n", 0, True)
1808 self.indentLevel += 1
1809
1810 name = bone.name
1811 if (name != ""):
1812 self.IndentWrite(B"Name {string {\"")
1813 self.Write(bytes(name, "UTF-8"))
1814 self.Write(B"\"}}\n\n")
1815
1816 self.ExportBoneTransform(armature, bone, scene)
1817
1818 for subnode in bone.children:
1819 self.ExportBone(armature, subnode, scene)
1820
1821 # Export any ordinary nodes that are parented to this bone.
1822
1823 boneSubnodeArray = self.boneParentArray.get(bone.name)
1824 if (boneSubnodeArray):
1825 poseBone = None
1826 if (not bone.use_relative_parent):
1827 poseBone = armature.pose.bones.get(bone.name)
1828
1829 for subnode in boneSubnodeArray:
1830 self.ExportNode(subnode, scene, poseBone)
1831
1832 if (nodeRef):
1833 self.indentLevel -= 1
1834 self.IndentWrite(B"}\n")
1835
1836
1837 def ExportNode(self, node, scene, poseBone = None):
1838
1839 # This function exports a single node in the scene and includes its name,
1840 # object reference, material references (for geometries), and transform.
1841 # Subnodes are then exported recursively.
1842
1843 nodeRef = self.nodeArray.get(node)
1844 if (nodeRef):
1845 type = nodeRef["nodeType"]
1846 self.IndentWrite(structIdentifier[type], 0, True)
1847 self.Write(nodeRef["structName"])
1848
1849 if (type == kNodeTypeGeometry):
1850 if (node.hide_render):
1851 self.Write(B" (visible = false)")
1852
1853 self.IndentWrite(B"{\n", 0, True)
1854 self.indentLevel += 1
1855
1856 structFlag = False
1857
1858 # Export the node's name if it has one.
1859
1860 name = node.name
1861 if (name != ""):
1862 self.IndentWrite(B"Name {string {\"")
1863 self.Write(bytes(name, "UTF-8"))
1864 self.Write(B"\"}}\n")
1865 structFlag = True
1866
1867 # Export the object reference and material references.
1868
1869 object = node.data
1870
1871 if (type == kNodeTypeGeometry):
1872 if (not object in self.geometryArray):
1873 self.geometryArray[object] = {"structName" : bytes("geometry" + str(len(self.geometryArray) + 1), "UTF-8"), "nodeTable" : [node]}
1874 else:
1875 self.geometryArray[object]["nodeTable"].append(node)
1876
1877 self.IndentWrite(B"ObjectRef {ref {$")
1878 self.Write(self.geometryArray[object]["structName"])
1879 self.Write(B"}}\n")
1880
1881 for i in range(len(node.material_slots)):
1882 self.ExportMaterialRef(node.material_slots[i].material, i)
1883
1884 shapeKeys = OpenGexExporter.GetShapeKeys(object)
1885 if (shapeKeys):
1886 self.ExportMorphWeights(node, shapeKeys, scene)
1887
1888 structFlag = True
1889
1890 elif (type == kNodeTypeLight):
1891 if (not object in self.lightArray):
1892 self.lightArray[object] = {"structName" : bytes("light" + str(len(self.lightArray) + 1), "UTF-8"), "nodeTable" : [node]}
1893 else:
1894 self.lightArray[object]["nodeTable"].append(node)
1895
1896 self.IndentWrite(B"ObjectRef {ref {$")
1897 self.Write(self.lightArray[object]["structName"])
1898 self.Write(B"}}\n")
1899 structFlag = True
1900
1901 elif (type == kNodeTypeCamera):
1902 if (not object in self.cameraArray):
1903 self.cameraArray[object] = {"structName" : bytes("camera" + str(len(self.cameraArray) + 1), "UTF-8"), "nodeTable" : [node]}
1904 else:
1905 self.cameraArray[object]["nodeTable"].append(node)
1906
1907 self.IndentWrite(B"ObjectRef {ref {$")
1908 self.Write(self.cameraArray[object]["structName"])
1909 self.Write(B"}}\n")
1910 structFlag = True
1911
1912 if (structFlag):
1913 self.Write(B"\n")
1914
1915 if (poseBone):
1916
1917 # If the node is parented to a bone and is not relative, then undo the bone's transform.
1918
1919 self.IndentWrite(B"Transform\n")
1920 self.IndentWrite(B"{\n")
1921 self.indentLevel += 1
1922
1923 self.IndentWrite(B"float[16]\n")
1924 self.IndentWrite(B"{\n")
1925 self.WriteMatrix(poseBone.matrix.inverted())
1926 self.IndentWrite(B"}\n")
1927
1928 self.indentLevel -= 1
1929 self.IndentWrite(B"}\n")
1930
1931 # Export the transform. If the node is animated, then animation tracks are exported here.
1932
1933 self.ExportNodeTransform(node, scene)
1934
1935 if (node.type == "ARMATURE"):
1936 skeleton = node.data
1937 if (skeleton):
1938 for bone in skeleton.bones:
1939 if (not bone.parent):
1940 self.ExportBone(node, bone, scene)
1941
1942 for subnode in node.children:
1943 if (subnode.parent_type != "BONE"):
1944 self.ExportNode(subnode, scene)
1945
1946 if (nodeRef):
1947 self.indentLevel -= 1
1948 self.IndentWrite(B"}\n")
1949
1950
1951 def ExportSkin(self, node, armature, exportVertexArray):
1952
1953 # This function exports all skinning data, which includes the skeleton
1954 # and per-vertex bone influence data.
1955
1956 self.IndentWrite(B"Skin\n", 0, True)
1957 self.IndentWrite(B"{\n")
1958 self.indentLevel += 1
1959
1960 # Write the skin bind pose transform.
1961
1962 self.IndentWrite(B"Transform\n")
1963 self.IndentWrite(B"{\n")
1964 self.indentLevel += 1
1965
1966 self.IndentWrite(B"float[16]\n")
1967 self.IndentWrite(B"{\n")
1968 self.WriteMatrix(node.matrix_world)
1969 self.IndentWrite(B"}\n")
1970
1971 self.indentLevel -= 1
1972 self.IndentWrite(B"}\n\n")
1973
1974 # Export the skeleton, which includes an array of bone node references
1975 # and and array of per-bone bind pose transforms.
1976
1977 self.IndentWrite(B"Skeleton\n")
1978 self.IndentWrite(B"{\n")
1979 self.indentLevel += 1
1980
1981 # Write the bone node reference array.
1982
1983 self.IndentWrite(B"BoneRefArray\n")
1984 self.IndentWrite(B"{\n")
1985 self.indentLevel += 1
1986
1987 boneArray = armature.data.bones
1988 boneCount = len(boneArray)
1989
1990 self.IndentWrite(B"ref\t\t\t// ")
1991 self.WriteInt(boneCount)
1992 self.IndentWrite(B"{\n", 0, True)
1993 self.IndentWrite(B"", 1)
1994
1995 for i in range(boneCount):
1996 boneRef = self.FindNode(boneArray[i].name)
1997 if (boneRef):
1998 self.Write(B"$")
1999 self.Write(boneRef[1]["structName"])
2000 else:
2001 self.Write(B"null")
2002
2003 if (i < boneCount - 1):
2004 self.Write(B", ")
2005 else:
2006 self.Write(B"\n")
2007
2008 self.IndentWrite(B"}\n")
2009
2010 self.indentLevel -= 1
2011 self.IndentWrite(B"}\n\n")
2012
2013 # Write the bind pose transform array.
2014
2015 self.IndentWrite(B"Transform\n")
2016 self.IndentWrite(B"{\n")
2017 self.indentLevel += 1
2018
2019 self.IndentWrite(B"float[16]\t// ")
2020 self.WriteInt(boneCount)
2021 self.IndentWrite(B"{\n", 0, True)
2022
2023 for i in range(boneCount):
2024 self.WriteMatrixFlat(armature.matrix_world * boneArray[i].matrix_local)
2025 if (i < boneCount - 1):
2026 self.Write(B",\n")
2027
2028 self.IndentWrite(B"}\n", 0, True)
2029
2030 self.indentLevel -= 1
2031 self.IndentWrite(B"}\n")
2032
2033 self.indentLevel -= 1
2034 self.IndentWrite(B"}\n\n")
2035
2036 # Export the per-vertex bone influence data.
2037
2038 groupRemap = []
2039
2040 for group in node.vertex_groups:
2041 groupName = group.name
2042 for i in range(boneCount):
2043 if (boneArray[i].name == groupName):
2044 groupRemap.append(i)
2045 break
2046 else:
2047 groupRemap.append(-1)
2048
2049 boneCountArray = []
2050 boneIndexArray = []
2051 boneWeightArray = []
2052
2053 meshVertexArray = node.data.vertices
2054 for ev in exportVertexArray:
2055 boneCount = 0
2056 totalWeight = 0.0
2057 for element in meshVertexArray[ev.vertexIndex].groups:
2058 boneIndex = groupRemap[element.group]
2059 boneWeight = element.weight
2060 if ((boneIndex >= 0) and (boneWeight != 0.0)):
2061 boneCount += 1
2062 totalWeight += boneWeight
2063 boneIndexArray.append(boneIndex)
2064 boneWeightArray.append(boneWeight)
2065 boneCountArray.append(boneCount)
2066
2067 if (totalWeight != 0.0):
2068 normalizer = 1.0 / totalWeight
2069 for i in range(-boneCount, 0):
2070 boneWeightArray[i] *= normalizer
2071
2072 # Write the bone count array. There is one entry per vertex.
2073
2074 self.IndentWrite(B"BoneCountArray\n")
2075 self.IndentWrite(B"{\n")
2076 self.indentLevel += 1
2077
2078 self.IndentWrite(B"unsigned_int16\t\t// ")
2079 self.WriteInt(len(boneCountArray))
2080 self.IndentWrite(B"{\n", 0, True)
2081 self.WriteIntArray(boneCountArray)
2082 self.IndentWrite(B"}\n")
2083
2084 self.indentLevel -= 1
2085 self.IndentWrite(B"}\n\n")
2086
2087 # Write the bone index array. The number of entries is the sum of the bone counts for all vertices.
2088
2089 self.IndentWrite(B"BoneIndexArray\n")
2090 self.IndentWrite(B"{\n")
2091 self.indentLevel += 1
2092
2093 self.IndentWrite(B"unsigned_int16\t\t// ")
2094 self.WriteInt(len(boneIndexArray))
2095 self.IndentWrite(B"{\n", 0, True)
2096 self.WriteIntArray(boneIndexArray)
2097 self.IndentWrite(B"}\n")
2098
2099 self.indentLevel -= 1
2100 self.IndentWrite(B"}\n\n")
2101
2102 # Write the bone weight array. The number of entries is the sum of the bone counts for all vertices.
2103
2104 self.IndentWrite(B"BoneWeightArray\n")
2105 self.IndentWrite(B"{\n")
2106 self.indentLevel += 1
2107
2108 self.IndentWrite(B"float\t\t// ")
2109 self.WriteInt(len(boneWeightArray))
2110 self.IndentWrite(B"{\n", 0, True)
2111 self.WriteFloatArray(boneWeightArray)
2112 self.IndentWrite(B"}\n")
2113
2114 self.indentLevel -= 1
2115 self.IndentWrite(B"}\n")
2116
2117 self.indentLevel -= 1
2118 self.IndentWrite(B"}\n")
2119
2120
2121 def ExportGeometry(self, objectRef, scene):
2122
2123 # This function exports a single geometry object.
2124
2125 self.Write(B"\nGeometryObject $")
2126 self.Write(objectRef[1]["structName"])
2127 self.WriteNodeTable(objectRef)
2128
2129 self.Write(B"\n{\n")
2130 self.indentLevel += 1
2131
2132 node = objectRef[1]["nodeTable"][0]
2133 mesh = objectRef[0]
2134
2135 structFlag = False;
2136
2137 # Save the morph state if necessary.
2138
2139 activeShapeKeyIndex = node.active_shape_key_index
2140 showOnlyShapeKey = node.show_only_shape_key
2141 currentMorphValue = []
2142
2143 shapeKeys = OpenGexExporter.GetShapeKeys(mesh)
2144 if (shapeKeys):
2145 node.active_shape_key_index = 0
2146 node.show_only_shape_key = True
2147
2148 baseIndex = 0
2149 relative = shapeKeys.use_relative
2150 if (relative):
2151 morphCount = 0
2152 baseName = shapeKeys.reference_key.name
2153 for block in shapeKeys.key_blocks:
2154 if (block.name == baseName):
2155 baseIndex = morphCount
2156 break
2157 morphCount += 1
2158
2159 morphCount = 0
2160 for block in shapeKeys.key_blocks:
2161 currentMorphValue.append(block.value)
2162 block.value = 0.0
2163
2164 if (block.name != ""):
2165 self.IndentWrite(B"Morph (index = ", 0, structFlag)
2166 self.WriteInt(morphCount)
2167
2168 if ((relative) and (morphCount != baseIndex)):
2169 self.Write(B", base = ")
2170 self.WriteInt(baseIndex)
2171
2172 self.Write(B")\n")
2173 self.IndentWrite(B"{\n")
2174 self.IndentWrite(B"Name {string {\"", 1)
2175 self.Write(bytes(block.name, "UTF-8"))
2176 self.Write(B"\"}}\n")
2177 self.IndentWrite(B"}\n")
2178 structFlag = True
2179
2180 morphCount += 1
2181
2182 shapeKeys.key_blocks[0].value = 1.0
2183 mesh.update()
2184
2185 self.IndentWrite(B"Mesh (primitive = \"triangles\")\n", 0, structFlag)
2186 self.IndentWrite(B"{\n")
2187 self.indentLevel += 1
2188
2189 armature = node.find_armature()
2190 applyModifiers = (not armature)
2191
2192 # Apply all modifiers to create a new mesh with tessfaces.
2193
2194 # We don't apply modifiers for a skinned mesh because we need the vertex positions
2195 # before they are deformed by the armature modifier in order to export the proper
2196 # bind pose. This does mean that modifiers preceding the armature modifier are ignored,
2197 # but the Blender API does not provide a reasonable way to retrieve the mesh at an
2198 # arbitrary stage in the modifier stack.
2199
2200 exportMesh = node.to_mesh(scene, applyModifiers, "RENDER", True, False)
2201
2202 # Triangulate mesh and remap vertices to eliminate duplicates.
2203
2204 materialTable = []
2205 exportVertexArray = OpenGexExporter.DeindexMesh(exportMesh, materialTable)
2206 triangleCount = len(materialTable)
2207
2208 indexTable = []
2209 unifiedVertexArray = OpenGexExporter.UnifyVertices(exportVertexArray, indexTable)
2210 vertexCount = len(unifiedVertexArray)
2211
2212 # Write the position array.
2213
2214 self.IndentWrite(B"VertexArray (attrib = \"position\")\n")
2215 self.IndentWrite(B"{\n")
2216 self.indentLevel += 1
2217
2218 self.IndentWrite(B"float[3]\t\t// ")
2219 self.WriteInt(vertexCount)
2220 self.IndentWrite(B"{\n", 0, True)
2221 self.WriteVertexArray3D(unifiedVertexArray, "position")
2222 self.IndentWrite(B"}\n")
2223
2224 self.indentLevel -= 1
2225 self.IndentWrite(B"}\n\n")
2226
2227 # Write the normal array.
2228
2229 self.IndentWrite(B"VertexArray (attrib = \"normal\")\n")
2230 self.IndentWrite(B"{\n")
2231 self.indentLevel += 1
2232
2233 self.IndentWrite(B"float[3]\t\t// ")
2234 self.WriteInt(vertexCount)
2235 self.IndentWrite(B"{\n", 0, True)
2236 self.WriteVertexArray3D(unifiedVertexArray, "normal")
2237 self.IndentWrite(B"}\n")
2238
2239 self.indentLevel -= 1
2240 self.IndentWrite(B"}\n")
2241
2242 # Write the color array if it exists.
2243
2244 colorCount = len(exportMesh.tessface_vertex_colors)
2245 if (colorCount > 0):
2246 self.IndentWrite(B"VertexArray (attrib = \"color\")\n", 0, True)
2247 self.IndentWrite(B"{\n")
2248 self.indentLevel += 1
2249
2250 self.IndentWrite(B"float[3]\t\t// ")
2251 self.WriteInt(vertexCount)
2252 self.IndentWrite(B"{\n", 0, True)
2253 self.WriteVertexArray3D(unifiedVertexArray, "color")
2254 self.IndentWrite(B"}\n")
2255
2256 self.indentLevel -= 1
2257 self.IndentWrite(B"}\n")
2258
2259 # Write the texcoord arrays.
2260
2261 texcoordCount = len(exportMesh.tessface_uv_textures)
2262 if (texcoordCount > 0):
2263 self.IndentWrite(B"VertexArray (attrib = \"texcoord\")\n", 0, True)
2264 self.IndentWrite(B"{\n")
2265 self.indentLevel += 1
2266
2267 self.IndentWrite(B"float[2]\t\t// ")
2268 self.WriteInt(vertexCount)
2269 self.IndentWrite(B"{\n", 0, True)
2270 self.WriteVertexArray2D(unifiedVertexArray, "texcoord0")
2271 self.IndentWrite(B"}\n")
2272
2273 self.indentLevel -= 1
2274 self.IndentWrite(B"}\n")
2275
2276 if (texcoordCount > 1):
2277 self.IndentWrite(B"VertexArray (attrib = \"texcoord[1]\")\n", 0, True)
2278 self.IndentWrite(B"{\n")
2279 self.indentLevel += 1
2280
2281 self.IndentWrite(B"float[2]\t\t// ")
2282 self.WriteInt(vertexCount)
2283 self.IndentWrite(B"{\n", 0, True)
2284 self.WriteVertexArray2D(unifiedVertexArray, "texcoord1")
2285 self.IndentWrite(B"}\n")
2286
2287 self.indentLevel -= 1
2288 self.IndentWrite(B"}\n")
2289
2290 if (texcoordCount > 2):
2291 self.IndentWrite(B"VertexArray (attrib = \"texcoord[2]\")\n", 0, True)
2292 self.IndentWrite(B"{\n")
2293 self.indentLevel += 1
2294
2295 self.IndentWrite(B"float[2]\t\t// ")
2296 self.WriteInt(vertexCount)
2297 self.IndentWrite(B"{\n", 0, True)
2298 self.WriteVertexArray2D(unifiedVertexArray, "texcoord2")
2299 self.IndentWrite(B"}\n")
2300
2301 self.indentLevel -= 1
2302 self.IndentWrite(B"}\n")
2303
2304 # If there are multiple morph targets, export them here.
2305
2306 if (shapeKeys):
2307 shapeKeys.key_blocks[0].value = 0.0
2308 for m in range(1, len(currentMorphValue)):
2309 shapeKeys.key_blocks[m].value = 1.0
2310 mesh.update()
2311
2312 node.active_shape_key_index = m
2313 morphMesh = node.to_mesh(scene, applyModifiers, "RENDER", True, False)
2314
2315 # Write the morph target position array.
2316
2317 self.IndentWrite(B"VertexArray (attrib = \"position\", morph = ", 0, True)
2318 self.WriteInt(m)
2319 self.Write(B")\n")
2320 self.IndentWrite(B"{\n")
2321 self.indentLevel += 1
2322
2323 self.IndentWrite(B"float[3]\t\t// ")
2324 self.WriteInt(vertexCount)
2325 self.IndentWrite(B"{\n", 0, True)
2326 self.WriteMorphPositionArray3D(unifiedVertexArray, morphMesh.vertices)
2327 self.IndentWrite(B"}\n")
2328
2329 self.indentLevel -= 1
2330 self.IndentWrite(B"}\n\n")
2331
2332 # Write the morph target normal array.
2333
2334 self.IndentWrite(B"VertexArray (attrib = \"normal\", morph = ")
2335 self.WriteInt(m)
2336 self.Write(B")\n")
2337 self.IndentWrite(B"{\n")
2338 self.indentLevel += 1
2339
2340 self.IndentWrite(B"float[3]\t\t// ")
2341 self.WriteInt(vertexCount)
2342 self.IndentWrite(B"{\n", 0, True)
2343 self.WriteMorphNormalArray3D(unifiedVertexArray, morphMesh.vertices, morphMesh.tessfaces)
2344 self.IndentWrite(B"}\n")
2345
2346 self.indentLevel -= 1
2347 self.IndentWrite(B"}\n")
2348
2349 bpy.data.meshes.remove(morphMesh)
2350
2351 # Write the index arrays.
2352
2353 maxMaterialIndex = 0
2354 for i in range(len(materialTable)):
2355 index = materialTable[i]
2356 if (index > maxMaterialIndex):
2357 maxMaterialIndex = index
2358
2359 if (maxMaterialIndex == 0):
2360
2361 # There is only one material, so write a single index array.
2362
2363 self.IndentWrite(B"IndexArray\n", 0, True)
2364 self.IndentWrite(B"{\n")
2365 self.indentLevel += 1
2366
2367 self.IndentWrite(B"unsigned_int32[3]\t\t// ")
2368 self.WriteInt(triangleCount)
2369 self.IndentWrite(B"{\n", 0, True)
2370 self.WriteTriangleArray(triangleCount, indexTable)
2371 self.IndentWrite(B"}\n")
2372
2373 self.indentLevel -= 1
2374 self.IndentWrite(B"}\n")
2375
2376 else:
2377
2378 # If there are multiple material indexes, then write a separate index array for each one.
2379
2380 materialTriangleCount = [0 for i in range(maxMaterialIndex + 1)]
2381 for i in range(len(materialTable)):
2382 materialTriangleCount[materialTable[i]] += 1
2383
2384 for m in range(maxMaterialIndex + 1):
2385 if (materialTriangleCount[m] != 0):
2386 materialIndexTable = []
2387 for i in range(len(materialTable)):
2388 if (materialTable[i] == m):
2389 k = i * 3
2390 materialIndexTable.append(indexTable[k])
2391 materialIndexTable.append(indexTable[k + 1])
2392 materialIndexTable.append(indexTable[k + 2])
2393
2394 self.IndentWrite(B"IndexArray (material = ", 0, True)
2395 self.WriteInt(m)
2396 self.Write(B")\n")
2397 self.IndentWrite(B"{\n")
2398 self.indentLevel += 1
2399
2400 self.IndentWrite(B"unsigned_int32[3]\t\t// ")
2401 self.WriteInt(materialTriangleCount[m])
2402 self.IndentWrite(B"{\n", 0, True)
2403 self.WriteTriangleArray(materialTriangleCount[m], materialIndexTable)
2404 self.IndentWrite(B"}\n")
2405
2406 self.indentLevel -= 1
2407 self.IndentWrite(B"}\n")
2408
2409 # If the mesh is skinned, export the skinning data here.
2410
2411 if (armature):
2412 self.ExportSkin(node, armature, unifiedVertexArray)
2413
2414 # Restore the morph state.
2415
2416 if (shapeKeys):
2417 node.active_shape_key_index = activeShapeKeyIndex
2418 node.show_only_shape_key = showOnlyShapeKey
2419
2420 for m in range(len(currentMorphValue)):
2421 shapeKeys.key_blocks[m].value = currentMorphValue[m]
2422
2423 mesh.update()
2424
2425 # Delete the new mesh that we made earlier.
2426
2427 bpy.data.meshes.remove(exportMesh)
2428
2429 self.indentLevel -= 1
2430 self.IndentWrite(B"}\n")
2431
2432 self.indentLevel -= 1
2433 self.Write(B"}\n")
2434
2435
2436 def ExportLight(self, objectRef):
2437
2438 # This function exports a single light object.
2439
2440 self.Write(B"\nLightObject $")
2441 self.Write(objectRef[1]["structName"])
2442
2443 object = objectRef[0]
2444 type = object.type
2445
2446 self.Write(B" (type = ")
2447 pointFlag = False
2448 spotFlag = False
2449
2450 if (type == "SUN"):
2451 self.Write(B"\"infinite\"")
2452 elif (type == "POINT"):
2453 self.Write(B"\"point\"")
2454 pointFlag = True
2455 else:
2456 self.Write(B"\"spot\"")
2457 pointFlag = True
2458 spotFlag = True
2459
2460 if (not object.use_shadow):
2461 self.Write(B", shadow = false")
2462
2463 self.Write(B")")
2464 self.WriteNodeTable(objectRef)
2465
2466 self.Write(B"\n{\n")
2467 self.indentLevel += 1
2468
2469 # Export the light's color, and include a separate intensity if necessary.
2470
2471 self.IndentWrite(B"Color (attrib = \"light\") {float[3] {")
2472 self.WriteColor(object.color)
2473 self.Write(B"}}\n")
2474
2475 intensity = object.energy
2476 if (intensity != 1.0):
2477 self.IndentWrite(B"Param (attrib = \"intensity\") {float {")
2478 self.WriteFloat(intensity)
2479 self.Write(B"}}\n")
2480
2481 if (pointFlag):
2482
2483 # Export a separate attenuation function for each type that's in use.
2484
2485 falloff = object.falloff_type
2486
2487 if (falloff == "INVERSE_LINEAR"):
2488 self.IndentWrite(B"Atten (curve = \"inverse\")\n", 0, True)
2489 self.IndentWrite(B"{\n")
2490
2491 self.IndentWrite(B"Param (attrib = \"scale\") {float {", 1)
2492 self.WriteFloat(object.distance)
2493 self.Write(B"}}\n")
2494
2495 self.IndentWrite(B"}\n")
2496
2497 elif (falloff == "INVERSE_SQUARE"):
2498 self.IndentWrite(B"Atten (curve = \"inverse_square\")\n", 0, True)
2499 self.IndentWrite(B"{\n")
2500
2501 self.IndentWrite(B"Param (attrib = \"scale\") {float {", 1)
2502 self.WriteFloat(math.sqrt(object.distance))
2503 self.Write(B"}}\n")
2504
2505 self.IndentWrite(B"}\n")
2506
2507 elif (falloff == "LINEAR_QUADRATIC_WEIGHTED"):
2508 if (object.linear_attenuation != 0.0):
2509 self.IndentWrite(B"Atten (curve = \"inverse\")\n", 0, True)
2510 self.IndentWrite(B"{\n")
2511
2512 self.IndentWrite(B"Param (attrib = \"scale\") {float {", 1)
2513 self.WriteFloat(object.distance)
2514 self.Write(B"}}\n")
2515
2516 self.IndentWrite(B"Param (attrib = \"constant\") {float {", 1)
2517 self.WriteFloat(1.0)
2518 self.Write(B"}}\n")
2519
2520 self.IndentWrite(B"Param (attrib = \"linear\") {float {", 1)
2521 self.WriteFloat(object.linear_attenuation)
2522 self.Write(B"}}\n")
2523
2524 self.IndentWrite(B"}\n\n")
2525
2526 if (object.quadratic_attenuation != 0.0):
2527 self.IndentWrite(B"Atten (curve = \"inverse_square\")\n")
2528 self.IndentWrite(B"{\n")
2529
2530 self.IndentWrite(B"Param (attrib = \"scale\") {float {", 1)
2531 self.WriteFloat(object.distance)
2532 self.Write(B"}}\n")
2533
2534 self.IndentWrite(B"Param (attrib = \"constant\") {float {", 1)
2535 self.WriteFloat(1.0)
2536 self.Write(B"}}\n")
2537
2538 self.IndentWrite(B"Param (attrib = \"quadratic\") {float {", 1)
2539 self.WriteFloat(object.quadratic_attenuation)
2540 self.Write(B"}}\n")
2541
2542 self.IndentWrite(B"}\n")
2543
2544 if (object.use_sphere):
2545 self.IndentWrite(B"Atten (curve = \"linear\")\n", 0, True)
2546 self.IndentWrite(B"{\n")
2547
2548 self.IndentWrite(B"Param (attrib = \"end\") {float {", 1)
2549 self.WriteFloat(object.distance)
2550 self.Write(B"}}\n")
2551
2552 self.IndentWrite(B"}\n")
2553
2554 if (spotFlag):
2555
2556 # Export additional angular attenuation for spot lights.
2557
2558 self.IndentWrite(B"Atten (kind = \"angle\", curve = \"linear\")\n", 0, True)
2559 self.IndentWrite(B"{\n")
2560
2561 endAngle = object.spot_size * 0.5
2562 beginAngle = endAngle * (1.0 - object.spot_blend)
2563
2564 self.IndentWrite(B"Param (attrib = \"begin\") {float {", 1)
2565 self.WriteFloat(beginAngle)
2566 self.Write(B"}}\n")
2567
2568 self.IndentWrite(B"Param (attrib = \"end\") {float {", 1)
2569 self.WriteFloat(endAngle)
2570 self.Write(B"}}\n")
2571
2572 self.IndentWrite(B"}\n")
2573
2574 self.indentLevel -= 1
2575 self.Write(B"}\n")
2576
2577
2578 def ExportCamera(self, objectRef):
2579
2580 # This function exports a single camera object.
2581
2582 self.Write(B"\nCameraObject $")
2583 self.Write(objectRef[1]["structName"])
2584 self.WriteNodeTable(objectRef)
2585
2586 self.Write(B"\n{\n")
2587 self.indentLevel += 1
2588
2589 object = objectRef[0]
2590
2591 self.IndentWrite(B"Param (attrib = \"fov\") {float {")
2592 self.WriteFloat(object.angle_x)
2593 self.Write(B"}}\n")
2594
2595 self.IndentWrite(B"Param (attrib = \"near\") {float {")
2596 self.WriteFloat(object.clip_start)
2597 self.Write(B"}}\n")
2598
2599 self.IndentWrite(B"Param (attrib = \"far\") {float {")
2600 self.WriteFloat(object.clip_end)
2601 self.Write(B"}}\n")
2602
2603 self.indentLevel -= 1
2604 self.Write(B"}\n")
2605
2606
2607 def ExportObjects(self, scene):
2608 for objectRef in self.geometryArray.items():
2609 self.ExportGeometry(objectRef, scene)
2610 for objectRef in self.lightArray.items():
2611 self.ExportLight(objectRef)
2612 for objectRef in self.cameraArray.items():
2613 self.ExportCamera(objectRef)
2614
2615
2616 def ExportTexture(self, textureSlot, attrib):
2617
2618 # This function exports a single texture from a material.
2619
2620 self.IndentWrite(B"Texture (attrib = \"", 0, True)
2621 self.Write(attrib)
2622 self.Write(B"\")\n")
2623
2624 self.IndentWrite(B"{\n")
2625 self.indentLevel += 1
2626
2627 self.IndentWrite(B"string {\"")
2628 self.WriteFileName(textureSlot.texture.image.filepath)
2629 self.Write(B"\"}\n")
2630
2631 # If the texture has a scale and/or offset, then export a coordinate transform.
2632
2633 uscale = textureSlot.scale[0]
2634 vscale = textureSlot.scale[1]
2635 uoffset = textureSlot.offset[0]
2636 voffset = textureSlot.offset[1]
2637
2638 if ((uscale != 1.0) or (vscale != 1.0) or (uoffset != 0.0) or (voffset != 0.0)):
2639 matrix = [[uscale, 0.0, 0.0, 0.0], [0.0, vscale, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [uoffset, voffset, 0.0, 1.0]]
2640
2641 self.IndentWrite(B"Transform\n", 0, True)
2642 self.IndentWrite(B"{\n")
2643 self.indentLevel += 1
2644
2645 self.IndentWrite(B"float[16]\n")
2646 self.IndentWrite(B"{\n")
2647 self.WriteMatrix(matrix)
2648 self.IndentWrite(B"}\n")
2649
2650 self.indentLevel -= 1
2651 self.IndentWrite(B"}\n")
2652
2653 self.indentLevel -= 1
2654 self.IndentWrite(B"}\n")
2655
2656
2657 def ExportMaterials(self):
2658
2659 # This function exports all of the materials used in the scene.
2660
2661 for materialRef in self.materialArray.items():
2662 material = materialRef[0]
2663
2664 self.Write(B"\nMaterial $")
2665 self.Write(materialRef[1]["structName"])
2666 self.Write(B"\n{\n")
2667 self.indentLevel += 1
2668
2669 if (material.name != ""):
2670 self.IndentWrite(B"Name {string {\"")
2671 self.Write(bytes(material.name, "UTF-8"))
2672 self.Write(B"\"}}\n\n")
2673
2674 intensity = material.diffuse_intensity
2675 diffuse = [material.diffuse_color[0] * intensity, material.diffuse_color[1] * intensity, material.diffuse_color[2] * intensity]
2676
2677 self.IndentWrite(B"Color (attrib = \"diffuse\") {float[3] {")
2678 self.WriteColor(diffuse)
2679 self.Write(B"}}\n")
2680
2681 intensity = material.specular_intensity
2682 specular = [material.specular_color[0] * intensity, material.specular_color[1] * intensity, material.specular_color[2] * intensity]
2683
2684 if ((specular[0] > 0.0) or (specular[1] > 0.0) or (specular[2] > 0.0)):
2685 self.IndentWrite(B"Color (attrib = \"specular\") {float[3] {")
2686 self.WriteColor(specular)
2687 self.Write(B"}}\n")
2688
2689 self.IndentWrite(B"Param (attrib = \"specular_power\") {float {")
2690 self.WriteFloat(material.specular_hardness)
2691 self.Write(B"}}\n")
2692
2693 emission = material.emit
2694 if (emission > 0.0):
2695 self.IndentWrite(B"Color (attrib = \"emission\") {float[3] {")
2696 self.WriteColor([emission, emission, emission])
2697 self.Write(B"}}\n")
2698
2699 diffuseTexture = None
2700 specularTexture = None
2701 emissionTexture = None
2702 transparencyTexture = None
2703 normalTexture = None
2704
2705 for textureSlot in material.texture_slots:
2706 if ((textureSlot) and (textureSlot.use) and (textureSlot.texture.type == "IMAGE")):
2707 if (((textureSlot.use_map_color_diffuse) or (textureSlot.use_map_diffuse)) and (not diffuseTexture)):
2708 diffuseTexture = textureSlot
2709 elif (((textureSlot.use_map_color_spec) or (textureSlot.use_map_specular)) and (not specularTexture)):
2710 specularTexture = textureSlot
2711 elif ((textureSlot.use_map_emit) and (not emissionTexture)):
2712 emissionTexture = textureSlot
2713 elif ((textureSlot.use_map_translucency) and (not transparencyTexture)):
2714 transparencyTexture = textureSlot
2715 elif ((textureSlot.use_map_normal) and (not normalTexture)):
2716 normalTexture = textureSlot
2717
2718 if (diffuseTexture):
2719 self.ExportTexture(diffuseTexture, B"diffuse")
2720 if (specularTexture):
2721 self.ExportTexture(specularTexture, B"specular")
2722 if (emissionTexture):
2723 self.ExportTexture(emissionTexture, B"emission")
2724 if (transparencyTexture):
2725 self.ExportTexture(transparencyTexture, B"transparency")
2726 if (normalTexture):
2727 self.ExportTexture(normalTexture, B"normal")
2728
2729 self.indentLevel -= 1
2730 self.Write(B"}\n")
2731
2732
2733 def ExportMetrics(self, scene):
2734 scale = scene.unit_settings.scale_length
2735
2736 if (scene.unit_settings.system == "IMPERIAL"):
2737 scale *= 0.3048
2738
2739 self.Write(B"Metric (key = \"distance\") {float {")
2740 self.WriteFloat(scale)
2741 self.Write(B"}}\n")
2742
2743 self.Write(B"Metric (key = \"angle\") {float {1.0}}\n")
2744 self.Write(B"Metric (key = \"time\") {float {1.0}}\n")
2745 self.Write(B"Metric (key = \"up\") {string {\"z\"}}\n")
2746
2747
2748 def execute(self, context):
2749 self.file = open(self.filepath, "wb")
2750
2751 self.indentLevel = 0
2752
2753 scene = context.scene
2754 self.ExportMetrics(scene)
2755
2756 originalFrame = scene.frame_current
2757 originalSubframe = scene.frame_subframe
2758 self.restoreFrame = False
2759
2760 self.beginFrame = scene.frame_start
2761 self.endFrame = scene.frame_end
2762 self.frameTime = 1.0 / (scene.render.fps_base * scene.render.fps)
2763
2764 self.nodeArray = {}
2765 self.geometryArray = {}
2766 self.lightArray = {}
2767 self.cameraArray = {}
2768 self.materialArray = {}
2769 self.boneParentArray = {}
2770
2771 self.exportAllFlag = not self.option_export_selection
2772 self.sampleAnimationFlag = self.option_sample_animation
2773
2774 for object in scene.objects:
2775 if (not object.parent):
2776 self.ProcessNode(object)
2777
2778 self.ProcessSkinnedMeshes()
2779
2780 for object in scene.objects:
2781 if (not object.parent):
2782 self.ExportNode(object, scene)
2783
2784 self.ExportObjects(scene)
2785 self.ExportMaterials()
2786
2787 if (self.restoreFrame):
2788 scene.frame_set(originalFrame, originalSubframe)
2789
2790 self.file.close()
2791 return {'FINISHED'}
2792
2793
2794
2795def menu_func(self, context):
2796 self.layout.operator(OpenGexExporter.bl_idname, text = "OpenGEX (.ogex)")
2797
2798def register():
2799 bpy.utils.register_class(OpenGexExporter)
2800 bpy.types.INFO_MT_file_export.append(menu_func)
2801
2802def unregister():
2803 bpy.types.INFO_MT_file_export.remove(menu_func)
2804 bpy.utils.unregister_class(OpenGexExporter)
2805
2806if __name__ == "__main__":
2807 register()