armory/blender/exporter.py

2730 lines
87 KiB
Python
Raw Normal View History

2016-06-22 10:32:19 +02:00
# Armory Scene Exporter by Lubos Lenco
# http://armory3d.org/
2015-12-18 01:01:41 +01:00
#
2016-06-22 10:32:19 +02:00
# Based on Open Game Engine Exchange
2015-10-30 13:23:09 +01:00
# http://opengex.org/
2016-06-22 10:32:19 +02:00
# Export plugin for Blender by Eric Lengyel
2015-10-30 13:23:09 +01:00
# Copyright 2015, Terathon Software LLC
#
# This software is licensed under the Creative Commons
# Attribution-ShareAlike 3.0 Unported License:
# http://creativecommons.org/licenses/by-sa/3.0/deed.en_US
2016-06-30 13:22:05 +02:00
# bl_info = {
2016-07-20 17:33:17 +02:00
# "name": "Armory format (.arm)",
2016-06-30 13:22:05 +02:00
# "description": "Armory Exporter",
# "author": "Eric Lengyel, Lubos Lenco",
# "version": (1, 0, 0),
# "location": "File > Import-Export",
# "wiki_url": "http://armory3d.org/",
# "category": "Import-Export"}
2015-10-30 13:23:09 +01:00
2015-11-28 21:51:42 +01:00
import os
2015-10-30 13:23:09 +01:00
import bpy
import math
2015-11-28 16:53:52 +01:00
from mathutils import *
2016-06-22 10:32:19 +02:00
import time
2016-01-28 12:32:05 +01:00
import ast
2016-06-07 09:38:49 +02:00
import write_probes
2015-10-30 13:23:09 +01:00
from bpy_extras.io_utils import ExportHelper
2016-07-19 19:42:46 +02:00
import assets
2016-07-20 17:33:17 +02:00
import utils
2015-10-30 13:23:09 +01:00
kNodeTypeNode = 0
kNodeTypeBone = 1
kNodeTypeGeometry = 2
kNodeTypeLight = 3
kNodeTypeCamera = 4
2015-12-18 01:01:41 +01:00
kNodeTypeSpeaker = 5
2015-10-30 13:23:09 +01:00
kAnimationSampled = 0
kAnimationLinear = 1
kAnimationBezier = 2
kExportEpsilon = 1.0e-6
2015-12-18 01:01:41 +01:00
structIdentifier = ["node", "bone_node", "geometry_node", "light_node", "camera_node", "speaker_node"]
2015-10-30 13:23:09 +01:00
subtranslationName = ["xpos", "ypos", "zpos"]
subrotationName = ["xrot", "yrot", "zrot"]
subscaleName = ["xscl", "yscl", "zscl"]
deltaSubtranslationName = ["dxpos", "dypos", "dzpos"]
deltaSubrotationName = ["dxrot", "dyrot", "dzrot"]
deltaSubscaleName = ["dxscl", "dyscl", "dzscl"]
axisName = ["x", "y", "z"]
2016-06-22 10:32:19 +02:00
class Vertex:
2016-07-28 22:38:11 +02:00
__slots__ = ("co", "normal", "uvs", "col", "loop_indices", "index", "bone_weights", "bone_indices", "bone_count", "vertexIndex")
2016-06-22 10:32:19 +02:00
def __init__(self, mesh, loop):
2016-07-28 22:38:11 +02:00
self.vertexIndex = loop.vertex_index
2016-06-22 10:32:19 +02:00
i = loop.index
2016-07-28 22:38:11 +02:00
self.co = mesh.vertices[self.vertexIndex].co.freeze()
2016-06-22 10:32:19 +02:00
self.normal = loop.normal.freeze()
self.uvs = tuple(layer.data[i].uv.freeze() for layer in mesh.uv_layers)
self.col = [0, 0, 0]
if len(mesh.vertex_colors) > 0:
self.col = mesh.vertex_colors[0].data[i].color.freeze()
self.loop_indices = [i]
# Take the four most influential groups
2016-07-28 22:38:11 +02:00
# groups = sorted(mesh.vertices[self.vertexIndex].groups, key=lambda group: group.weight, reverse=True)
# if len(groups) > 4:
# groups = groups[:4]
2016-06-22 10:32:19 +02:00
2016-07-28 22:38:11 +02:00
# self.bone_weights = [group.weight for group in groups]
# self.bone_indices = [group.group for group in groups]
# self.bone_count = len(self.bone_weights)
2016-06-22 10:32:19 +02:00
self.index = 0
def __hash__(self):
return hash((self.co, self.normal, self.uvs))
def __eq__(self, other):
eq = (
(self.co == other.co) and
(self.normal == other.normal) and
(self.uvs == other.uvs)
)
if eq:
indices = self.loop_indices + other.loop_indices
self.loop_indices = indices
other.loop_indices = indices
return eq
2015-10-30 13:23:09 +01:00
class ExportVertex:
2016-03-09 15:29:46 +01:00
__slots__ = ("hash", "vertexIndex", "faceIndex", "position", "normal", "color", "texcoord0", "texcoord1")
2015-10-30 13:23:09 +01:00
def __init__(self):
self.color = [1.0, 1.0, 1.0]
self.texcoord0 = [0.0, 0.0]
self.texcoord1 = [0.0, 0.0]
def __eq__(self, v):
if (self.hash != v.hash):
return (False)
if (self.position != v.position):
return (False)
if (self.normal != v.normal):
return (False)
if (self.texcoord0 != v.texcoord0):
return (False)
2016-06-22 10:32:19 +02:00
if (self.color != v.color):
2015-10-30 13:23:09 +01:00
return (False)
2016-06-22 10:32:19 +02:00
if (self.texcoord1 != v.texcoord1):
return (False)
2015-10-30 13:23:09 +01:00
return (True)
def Hash(self):
h = hash(self.position[0])
h = h * 21737 + hash(self.position[1])
h = h * 21737 + hash(self.position[2])
h = h * 21737 + hash(self.normal[0])
h = h * 21737 + hash(self.normal[1])
h = h * 21737 + hash(self.normal[2])
h = h * 21737 + hash(self.color[0])
h = h * 21737 + hash(self.color[1])
h = h * 21737 + hash(self.color[2])
h = h * 21737 + hash(self.texcoord0[0])
h = h * 21737 + hash(self.texcoord0[1])
h = h * 21737 + hash(self.texcoord1[0])
h = h * 21737 + hash(self.texcoord1[1])
self.hash = h
2016-01-11 13:07:44 +01:00
class ArmoryExporter(bpy.types.Operator, ExportHelper):
"""Export to Armory format"""
bl_idname = "export_scene.armory"
bl_label = "Export Armory"
2016-07-20 17:33:17 +02:00
filename_ext = ".arm"
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
option_export_selection = bpy.props.BoolProperty(name="Export Selection Only", description="Export only selected objects", default=False)
option_sample_animation = bpy.props.BoolProperty(name="Force Sampled Animation", description="Always export animation as per-frame samples", default=True)
option_geometry_only = bpy.props.BoolProperty(name="Export Geometry Only", description="Export only geometry data", default=True)
option_geometry_per_file = bpy.props.BoolProperty(name="Export Geometry Per File", description="Export each geometry to individual file", default=False)
option_optimize_geometry = bpy.props.BoolProperty(name="Optimized Geometry Exported", description="Slower but exports slightly smaller data", default=False)
option_minimize = bpy.props.BoolProperty(name="Export Minimized", description="Export binary data", default=True)
2015-10-30 13:23:09 +01:00
def WriteMatrix(self, matrix):
2016-01-21 22:44:15 +01:00
return [matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3],
matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3],
matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3],
matrix[3][0], matrix[3][1], matrix[3][2], matrix[3][3]]
2015-10-30 13:23:09 +01:00
def WriteVector2D(self, vector):
return [vector[0], vector[1]]
def WriteVector3D(self, vector):
return [vector[0], vector[1], vector[2]]
def WriteVertexArray2D(self, vertexArray, attrib):
va = []
count = len(vertexArray)
k = 0
lineCount = count >> 3
for i in range(lineCount):
for j in range(7):
va += self.WriteVector2D(getattr(vertexArray[k], attrib))
k += 1
va += self.WriteVector2D(getattr(vertexArray[k], attrib))
k += 1
count &= 7
if (count != 0):
for j in range(count - 1):
va += self.WriteVector2D(getattr(vertexArray[k], attrib))
k += 1
va += self.WriteVector2D(getattr(vertexArray[k], attrib))
return va
def WriteVertexArray3D(self, vertexArray, attrib):
va = []
count = len(vertexArray)
k = 0
lineCount = count >> 3
for i in range(lineCount):
for j in range(7):
va += self.WriteVector3D(getattr(vertexArray[k], attrib))
k += 1
va += self.WriteVector3D(getattr(vertexArray[k], attrib))
k += 1
count &= 7
if (count != 0):
for j in range(count - 1):
va += self.WriteVector3D(getattr(vertexArray[k], attrib))
k += 1
va += self.WriteVector3D(getattr(vertexArray[k], attrib))
return va
def WriteTriangle(self, triangleIndex, indexTable):
i = triangleIndex * 3
return [indexTable[i], indexTable[i + 1], indexTable[i + 2]]
def WriteTriangleArray(self, count, indexTable):
va = []
triangleIndex = 0
lineCount = count >> 4
for i in range(lineCount):
for j in range(15):
va += self.WriteTriangle(triangleIndex, indexTable)
triangleIndex += 1
va += self.WriteTriangle(triangleIndex, indexTable)
triangleIndex += 1
count &= 15
if (count != 0):
for j in range(count - 1):
va += self.WriteTriangle(triangleIndex, indexTable)
triangleIndex += 1
va += self.WriteTriangle(triangleIndex, indexTable)
return va
2016-01-20 15:22:01 +01:00
def get_geoms_file_path(self, object_id):
index = self.filepath.rfind('/')
geom_fp = self.filepath[:(index+1)] + 'geoms/'
if not os.path.exists(geom_fp):
os.makedirs(geom_fp)
2016-07-20 17:33:17 +02:00
return geom_fp + object_id + '.arm'
2015-10-30 13:23:09 +01:00
@staticmethod
def GetNodeType(node):
2016-07-21 14:31:33 +02:00
if node.type == "MESH":
if len(node.data.polygons) != 0:
return kNodeTypeGeometry
elif node.type == "FONT":
return kNodeTypeGeometry
elif node.type == "LAMP":
2015-10-30 13:23:09 +01:00
type = node.data.type
2016-07-21 14:31:33 +02:00
if type == "SUN" or type == "POINT" or type == "SPOT":
return kNodeTypeLight
elif node.type == "CAMERA":
return kNodeTypeCamera
elif node.type == "SPEAKER":
return kNodeTypeSpeaker
return kNodeTypeNode
2015-10-30 13:23:09 +01:00
@staticmethod
def GetShapeKeys(mesh):
shapeKeys = mesh.shape_keys
if ((shapeKeys) and (len(shapeKeys.key_blocks) > 1)):
return (shapeKeys)
return (None)
def FindNode(self, name):
for nodeRef in self.nodeArray.items():
if (nodeRef[0].name == name):
return (nodeRef)
return (None)
@staticmethod
def ClassifyAnimationCurve(fcurve):
linearCount = 0
bezierCount = 0
for key in fcurve.keyframe_points:
interp = key.interpolation
if (interp == "LINEAR"):
linearCount += 1
elif (interp == "BEZIER"):
bezierCount += 1
else:
return (kAnimationSampled)
if (bezierCount == 0):
return (kAnimationLinear)
elif (linearCount == 0):
return (kAnimationBezier)
return (kAnimationSampled)
@staticmethod
def AnimationKeysDifferent(fcurve):
keyCount = len(fcurve.keyframe_points)
if (keyCount > 0):
key1 = fcurve.keyframe_points[0].co[1]
for i in range(1, keyCount):
key2 = fcurve.keyframe_points[i].co[1]
if (math.fabs(key2 - key1) > kExportEpsilon):
return (True)
return (False)
@staticmethod
def AnimationTangentsNonzero(fcurve):
keyCount = len(fcurve.keyframe_points)
if (keyCount > 0):
key = fcurve.keyframe_points[0].co[1]
left = fcurve.keyframe_points[0].handle_left[1]
right = fcurve.keyframe_points[0].handle_right[1]
if ((math.fabs(key - left) > kExportEpsilon) or (math.fabs(right - key) > kExportEpsilon)):
return (True)
for i in range(1, keyCount):
key = fcurve.keyframe_points[i].co[1]
left = fcurve.keyframe_points[i].handle_left[1]
right = fcurve.keyframe_points[i].handle_right[1]
if ((math.fabs(key - left) > kExportEpsilon) or (math.fabs(right - key) > kExportEpsilon)):
return (True)
return (False)
@staticmethod
def MatricesDifferent(m1, m2):
for i in range(4):
for j in range(4):
if (math.fabs(m1[i][j] - m2[i][j]) > kExportEpsilon):
return (True)
return (False)
@staticmethod
def CollectBoneAnimation(armature, name):
path = "pose.bones[\"" + name + "\"]."
curveArray = []
if (armature.animation_data):
action = armature.animation_data.action
if (action):
for fcurve in action.fcurves:
if (fcurve.data_path.startswith(path)):
curveArray.append(fcurve)
return (curveArray)
@staticmethod
def AnimationPresent(fcurve, kind):
if (kind != kAnimationBezier):
2016-01-11 13:07:44 +01:00
return (ArmoryExporter.AnimationKeysDifferent(fcurve))
return ((ArmoryExporter.AnimationKeysDifferent(fcurve)) or (ArmoryExporter.AnimationTangentsNonzero(fcurve)))
2015-10-30 13:23:09 +01:00
@staticmethod
2016-03-09 13:08:46 +01:00
def calc_tangent(v0, v1, v2, uv0, uv1, uv2):
deltaPos1 = v1 - v0
deltaPos2 = v2 - v0
deltaUV1 = uv1 - uv0
deltaUV2 = uv2 - uv0
d = (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x)
if d != 0:
r = 1.0 / d
else:
r = 1.0
tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r
2016-03-11 15:07:41 +01:00
# bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r
2016-03-09 13:08:46 +01:00
return tangent
@staticmethod
2016-03-09 15:29:46 +01:00
def DeindexMesh(mesh, materialTable):
2015-10-30 13:23:09 +01:00
# This function deindexes all vertex positions, colors, and texcoords.
# Three separate ExportVertex structures are created for each triangle.
vertexArray = mesh.vertices
exportVertexArray = []
faceIndex = 0
for face in mesh.tessfaces:
k1 = face.vertices[0]
k2 = face.vertices[1]
k3 = face.vertices[2]
v1 = vertexArray[k1]
v2 = vertexArray[k2]
v3 = vertexArray[k3]
exportVertex = ExportVertex()
exportVertex.vertexIndex = k1
exportVertex.faceIndex = faceIndex
exportVertex.position = v1.co
exportVertex.normal = v1.normal if (face.use_smooth) else face.normal
exportVertexArray.append(exportVertex)
exportVertex = ExportVertex()
exportVertex.vertexIndex = k2
exportVertex.faceIndex = faceIndex
exportVertex.position = v2.co
exportVertex.normal = v2.normal if (face.use_smooth) else face.normal
exportVertexArray.append(exportVertex)
exportVertex = ExportVertex()
exportVertex.vertexIndex = k3
exportVertex.faceIndex = faceIndex
exportVertex.position = v3.co
exportVertex.normal = v3.normal if (face.use_smooth) else face.normal
exportVertexArray.append(exportVertex)
materialTable.append(face.material_index)
if (len(face.vertices) == 4):
k1 = face.vertices[0]
k2 = face.vertices[2]
k3 = face.vertices[3]
v1 = vertexArray[k1]
v2 = vertexArray[k2]
v3 = vertexArray[k3]
exportVertex = ExportVertex()
exportVertex.vertexIndex = k1
exportVertex.faceIndex = faceIndex
exportVertex.position = v1.co
exportVertex.normal = v1.normal if (face.use_smooth) else face.normal
exportVertexArray.append(exportVertex)
exportVertex = ExportVertex()
exportVertex.vertexIndex = k2
exportVertex.faceIndex = faceIndex
exportVertex.position = v2.co
exportVertex.normal = v2.normal if (face.use_smooth) else face.normal
exportVertexArray.append(exportVertex)
exportVertex = ExportVertex()
exportVertex.vertexIndex = k3
exportVertex.faceIndex = faceIndex
exportVertex.position = v3.co
exportVertex.normal = v3.normal if (face.use_smooth) else face.normal
exportVertexArray.append(exportVertex)
materialTable.append(face.material_index)
faceIndex += 1
colorCount = len(mesh.tessface_vertex_colors)
if (colorCount > 0):
colorFace = mesh.tessface_vertex_colors[0].data
vertexIndex = 0
faceIndex = 0
for face in mesh.tessfaces:
cf = colorFace[faceIndex]
exportVertexArray[vertexIndex].color = cf.color1
vertexIndex += 1
exportVertexArray[vertexIndex].color = cf.color2
vertexIndex += 1
exportVertexArray[vertexIndex].color = cf.color3
vertexIndex += 1
if (len(face.vertices) == 4):
exportVertexArray[vertexIndex].color = cf.color1
vertexIndex += 1
exportVertexArray[vertexIndex].color = cf.color3
vertexIndex += 1
exportVertexArray[vertexIndex].color = cf.color4
vertexIndex += 1
faceIndex += 1
texcoordCount = len(mesh.tessface_uv_textures)
if (texcoordCount > 0):
texcoordFace = mesh.tessface_uv_textures[0].data
vertexIndex = 0
faceIndex = 0
for face in mesh.tessfaces:
tf = texcoordFace[faceIndex]
2016-07-19 19:42:46 +02:00
exportVertexArray[vertexIndex].texcoord0 = [tf.uv1[0], 1.0 - tf.uv1[1]] # Reverse TCY
2015-10-30 13:23:09 +01:00
vertexIndex += 1
2016-07-19 19:42:46 +02:00
exportVertexArray[vertexIndex].texcoord0 = [tf.uv2[0], 1.0 - tf.uv2[1]]
2015-10-30 13:23:09 +01:00
vertexIndex += 1
2016-07-19 19:42:46 +02:00
exportVertexArray[vertexIndex].texcoord0 = [tf.uv3[0], 1.0 - tf.uv3[1]]
2015-10-30 13:23:09 +01:00
vertexIndex += 1
if (len(face.vertices) == 4):
2016-07-19 19:42:46 +02:00
exportVertexArray[vertexIndex].texcoord0 = [tf.uv1[0], 1.0 - tf.uv1[1]]
2015-10-30 13:23:09 +01:00
vertexIndex += 1
2016-07-19 19:42:46 +02:00
exportVertexArray[vertexIndex].texcoord0 = [tf.uv3[0], 1.0 - tf.uv3[1]]
2015-10-30 13:23:09 +01:00
vertexIndex += 1
2016-07-19 19:42:46 +02:00
exportVertexArray[vertexIndex].texcoord0 = [tf.uv4[0], 1.0 - tf.uv4[1]]
2015-10-30 13:23:09 +01:00
vertexIndex += 1
faceIndex += 1
if (texcoordCount > 1):
texcoordFace = mesh.tessface_uv_textures[1].data
vertexIndex = 0
faceIndex = 0
for face in mesh.tessfaces:
tf = texcoordFace[faceIndex]
exportVertexArray[vertexIndex].texcoord1 = tf.uv1
vertexIndex += 1
exportVertexArray[vertexIndex].texcoord1 = tf.uv2
vertexIndex += 1
exportVertexArray[vertexIndex].texcoord1 = tf.uv3
vertexIndex += 1
if (len(face.vertices) == 4):
exportVertexArray[vertexIndex].texcoord1 = tf.uv1
vertexIndex += 1
exportVertexArray[vertexIndex].texcoord1 = tf.uv3
vertexIndex += 1
exportVertexArray[vertexIndex].texcoord1 = tf.uv4
vertexIndex += 1
faceIndex += 1
for ev in exportVertexArray:
ev.Hash()
return (exportVertexArray)
@staticmethod
def FindExportVertex(bucket, exportVertexArray, vertex):
for index in bucket:
if (exportVertexArray[index] == vertex):
return (index)
return (-1)
@staticmethod
def UnifyVertices(exportVertexArray, indexTable):
2016-06-22 10:32:19 +02:00
# Non-indexed
# for i in range(len(exportVertexArray)):
# indexTable.append(i)
# return exportVertexArray
2015-10-30 13:23:09 +01:00
# This function looks for identical vertices having exactly the same position, normal,
# color, and texcoords. Duplicate vertices are unified, and a new index table is returned.
bucketCount = len(exportVertexArray) >> 3
if (bucketCount > 1):
# Round down to nearest power of two.
while True:
count = bucketCount & (bucketCount - 1)
if (count == 0):
break
bucketCount = count
else:
bucketCount = 1
hashTable = [[] for i in range(bucketCount)]
unifiedVertexArray = []
for i in range(len(exportVertexArray)):
ev = exportVertexArray[i]
bucket = ev.hash & (bucketCount - 1)
2016-06-22 10:32:19 +02:00
index = -1
for b in hashTable[bucket]:
if exportVertexArray[b] == ev:
index = b
break
if index < 0:
2015-10-30 13:23:09 +01:00
indexTable.append(len(unifiedVertexArray))
unifiedVertexArray.append(ev)
hashTable[bucket].append(i)
else:
indexTable.append(indexTable[index])
2016-06-22 10:32:19 +02:00
return unifiedVertexArray
2015-10-30 13:23:09 +01:00
def ExportBone(self, armature, bone, scene, o):
nodeRef = self.nodeArray.get(bone)
if (nodeRef):
2016-07-20 17:33:17 +02:00
o['type'] = structIdentifier[nodeRef["nodeType"]]
o['id'] = nodeRef["structName"]
2015-10-30 13:23:09 +01:00
2015-10-31 22:47:43 +01:00
#name = bone.name
#if (name != ""):
# o.name = name
2015-10-30 13:23:09 +01:00
self.ExportBoneTransform(armature, bone, scene, o)
2016-07-20 17:33:17 +02:00
o['nodes'] = [] # TODO
2015-10-30 13:23:09 +01:00
for subnode in bone.children:
2016-07-20 17:33:17 +02:00
so = {}
2015-10-30 13:23:09 +01:00
self.ExportBone(armature, subnode, scene, so)
2016-07-20 17:33:17 +02:00
o['nodes'].append(so)
2015-10-30 13:23:09 +01:00
# Export any ordinary nodes that are parented to this bone.
boneSubnodeArray = self.boneParentArray.get(bone.name)
if (boneSubnodeArray):
poseBone = None
if (not bone.use_relative_parent):
poseBone = armature.pose.bones.get(bone.name)
for subnode in boneSubnodeArray:
self.ExportNode(subnode, scene, poseBone, o)
def ExportNodeSampledAnimation(self, node, scene, o):
2016-01-20 15:22:01 +01:00
# This function exports animation as full 4x4 matrices for each frame.
2015-10-30 13:23:09 +01:00
currentFrame = scene.frame_current
currentSubframe = scene.frame_subframe
animationFlag = False
m1 = node.matrix_local.copy()
2016-07-21 14:31:33 +02:00
# Font in
2015-10-30 13:23:09 +01:00
for i in range(self.beginFrame, self.endFrame):
scene.frame_set(i)
m2 = node.matrix_local
2016-01-11 13:07:44 +01:00
if (ArmoryExporter.MatricesDifferent(m1, m2)):
2015-10-30 13:23:09 +01:00
animationFlag = True
break
2016-07-21 14:31:33 +02:00
# Font out
2015-10-30 13:23:09 +01:00
if (animationFlag):
2016-07-20 17:33:17 +02:00
o['animation'] = {}
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
tracko = {}
tracko['target'] = "transform"
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
tracko['time'] = {}
tracko['time']['values'] = []
2015-10-30 13:23:09 +01:00
for i in range(self.beginFrame, self.endFrame):
2016-07-20 17:33:17 +02:00
tracko['time']['values'].append(((i - self.beginFrame) * self.frameTime))
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
tracko['time']['values'].append((self.endFrame * self.frameTime))
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
tracko['value'] = {}
tracko['value']['values'] = []
2015-10-30 13:23:09 +01:00
for i in range(self.beginFrame, self.endFrame):
scene.frame_set(i)
2016-07-20 17:33:17 +02:00
tracko['value']['values'].append(self.WriteMatrix(node.matrix_local))
2015-10-30 13:23:09 +01:00
scene.frame_set(self.endFrame)
2016-07-20 17:33:17 +02:00
tracko['value']['values'].append(self.WriteMatrix(node.matrix_local))
o['animation']['tracks'] = [tracko]
2015-10-30 13:23:09 +01:00
scene.frame_set(currentFrame, currentSubframe)
def ExportBoneSampledAnimation(self, poseBone, scene, o):
# This function exports bone animation as full 4x4 matrices for each frame.
currentFrame = scene.frame_current
currentSubframe = scene.frame_subframe
animationFlag = False
m1 = poseBone.matrix.copy()
for i in range(self.beginFrame, self.endFrame):
scene.frame_set(i)
m2 = poseBone.matrix
2016-01-11 13:07:44 +01:00
if (ArmoryExporter.MatricesDifferent(m1, m2)):
2015-10-30 13:23:09 +01:00
animationFlag = True
break
if (animationFlag):
2016-07-20 17:33:17 +02:00
o['animation'] = {}
tracko = {}
tracko['target'] = "transform"
tracko['time'] = {}
tracko['time']['values'] = []
2015-10-30 13:23:09 +01:00
for i in range(self.beginFrame, self.endFrame):
2016-07-20 17:33:17 +02:00
tracko['time']['values'].append(((i - self.beginFrame) * self.frameTime))
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
tracko['time']['values'].append((self.endFrame * self.frameTime))
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
tracko['value'] = {}
tracko['value']['values'] = []
2015-10-30 13:23:09 +01:00
parent = poseBone.parent
if (parent):
for i in range(self.beginFrame, self.endFrame):
scene.frame_set(i)
2016-07-20 17:33:17 +02:00
tracko['value']['values'].append(self.WriteMatrix(parent.matrix.inverted() * poseBone.matrix))
2015-10-30 13:23:09 +01:00
scene.frame_set(self.endFrame)
2016-07-20 17:33:17 +02:00
tracko['value']['values'].append(self.WriteMatrix(parent.matrix.inverted() * poseBone.matrix))
2015-10-30 13:23:09 +01:00
else:
for i in range(self.beginFrame, self.endFrame):
scene.frame_set(i)
2016-07-20 17:33:17 +02:00
tracko['value']['values'].append(self.WriteMatrix(poseBone.matrix))
2015-10-30 13:23:09 +01:00
scene.frame_set(self.endFrame)
2016-07-20 17:33:17 +02:00
tracko['value']['values'].append(self.WriteMatrix(poseBone.matrix))
o['animation']['tracks'] = [tracko]
2015-10-30 13:23:09 +01:00
scene.frame_set(currentFrame, currentSubframe)
2016-07-17 20:29:53 +02:00
def ExportKeyTimes(self, fcurve):
2016-07-20 17:33:17 +02:00
keyo = {}
2016-07-17 20:29:53 +02:00
# self.IndentWrite(B"Key {float {")
2016-07-20 17:33:17 +02:00
keyo['values'] = []
2016-07-17 20:29:53 +02:00
keyCount = len(fcurve.keyframe_points)
for i in range(keyCount):
# if (i > 0):
# self.Write(B", ")
time = fcurve.keyframe_points[i].co[0] - self.beginFrame
2016-07-20 17:33:17 +02:00
keyo['values'].append(time * self.frameTime)
2016-07-17 20:29:53 +02:00
# self.WriteFloat(time * self.frameTime)
# self.Write(B"}}\n")
return keyo
def ExportKeyTimeControlPoints(self, fcurve):
2016-07-20 17:33:17 +02:00
keyminuso = {}
2016-07-17 20:29:53 +02:00
# self.IndentWrite(B"Key (kind = \"-control\") {float {")
2016-07-20 17:33:17 +02:00
keyminuso['values'] = []
2016-07-17 20:29:53 +02:00
keyCount = len(fcurve.keyframe_points)
for i in range(keyCount):
# if (i > 0):
# self.Write(B", ")
ctrl = fcurve.keyframe_points[i].handle_left[0] - self.beginFrame
2016-07-20 17:33:17 +02:00
keyminuso['values'].append(ctrl * self.frameTime)
2016-07-17 20:29:53 +02:00
# self.WriteFloat(ctrl * self.frameTime)
# self.Write(B"}}\n")
2016-07-20 17:33:17 +02:00
keypluso = {}
keypluso['values'] = []
2016-07-17 20:29:53 +02:00
# self.IndentWrite(B"Key (kind = \"+control\") {float {")
for i in range(keyCount):
# if (i > 0):
# self.Write(B", ")
ctrl = fcurve.keyframe_points[i].handle_right[0] - self.beginFrame
2016-07-20 17:33:17 +02:00
keypluso['values'].append(ctrl * self.frameTime)
2016-07-17 20:29:53 +02:00
# self.WriteFloat(ctrl * self.frameTime)
# self.Write(B"}}\n")
return keyminuso, keypluso
def ExportKeyValues(self, fcurve):
2016-07-20 17:33:17 +02:00
keyo = {}
keyo['values'] = []
2016-07-17 20:29:53 +02:00
# self.IndentWrite(B"Key {float {")
keyCount = len(fcurve.keyframe_points)
for i in range(keyCount):
# if (i > 0):
# self.Write(B", ")
value = fcurve.keyframe_points[i].co[1]
2016-07-20 17:33:17 +02:00
keyo['values'].append(value)
2016-07-17 20:29:53 +02:00
# self.WriteFloat(value)
# self.Write(B"}}\n")
return keyo
def ExportKeyValueControlPoints(self, fcurve):
2016-07-20 17:33:17 +02:00
keyminuso = {}
keyminuso['values'] = []
2016-07-17 20:29:53 +02:00
# self.IndentWrite(B"Key (kind = \"-control\") {float {")
keyCount = len(fcurve.keyframe_points)
for i in range(keyCount):
# if (i > 0):
# self.Write(B", ")
ctrl = fcurve.keyframe_points[i].handle_left[1]
2016-07-20 17:33:17 +02:00
keyminuso['values'].append(ctrl)
2016-07-17 20:29:53 +02:00
# self.WriteFloat(ctrl)
# self.Write(B"}}\n")
2016-07-20 17:33:17 +02:00
keypluso = {}
keypluso['values'] = []
2016-07-17 20:29:53 +02:00
# self.IndentWrite(B"Key (kind = \"+control\") {float {")
for i in range(keyCount):
# if (i > 0):
# self.Write(B", ")
ctrl = fcurve.keyframe_points[i].handle_right[1]
2016-07-20 17:33:17 +02:00
keypluso['values'].append(ctrl)
2016-07-17 20:29:53 +02:00
# self.WriteFloat(ctrl)
# self.Write(B"}}\n")
return keypluso, keypluso
def ExportAnimationTrack(self, fcurve, kind, target, newline):
# This function exports a single animation track. The curve types for the
# Time and Value structures are given by the kind parameter.
2016-07-20 17:33:17 +02:00
tracko = {}
tracko['target'] = target
2016-07-17 20:29:53 +02:00
# self.IndentWrite(B"Track (target = %", 0, newline)
# self.Write(target)
# self.Write(B")\n")
# self.IndentWrite(B"{\n")
# self.indentLevel += 1
if (kind != kAnimationBezier):
# self.IndentWrite(B"Time\n")
# self.IndentWrite(B"{\n")
# self.indentLevel += 1
2016-07-20 17:33:17 +02:00
tracko['time'] = self.ExportKeyTimes(fcurve)
2016-07-17 20:29:53 +02:00
# self.IndentWrite(B"}\n\n", -1)
# self.IndentWrite(B"Value\n", -1)
# self.IndentWrite(B"{\n", -1)
2016-07-20 17:33:17 +02:00
tracko['value'] = self.ExportKeyValues(fcurve)
2016-07-17 20:29:53 +02:00
# self.indentLevel -= 1
# self.IndentWrite(B"}\n")
else:
2016-07-20 17:33:17 +02:00
tracko['curve'] = 'bezier'
2016-07-17 20:29:53 +02:00
# self.IndentWrite(B"Time (curve = \"bezier\")\n")
# self.IndentWrite(B"{\n")
# self.indentLevel += 1
2016-07-20 17:33:17 +02:00
tracko['time'] = self.ExportKeyTimes(fcurve)
tracko['time_control_plus'], tracko['time_control_minus'] = self.ExportKeyTimeControlPoints(fcurve)
2016-07-17 20:29:53 +02:00
# self.IndentWrite(B"}\n\n", -1)
# self.IndentWrite(B"Value (curve = \"bezier\")\n", -1)
# self.IndentWrite(B"{\n", -1)
2016-07-20 17:33:17 +02:00
tracko['value'] = self.ExportKeyValues(fcurve)
tracko['value_control_plus'], tracko['value_control_minus'] = self.ExportKeyValueControlPoints(fcurve)
2016-07-17 20:29:53 +02:00
# self.indentLevel -= 1
# self.IndentWrite(B"}\n")
# self.indentLevel -= 1
# self.IndentWrite(B"}\n")
return tracko
2015-10-30 13:23:09 +01:00
def ExportNodeTransform(self, node, scene, o):
posAnimCurve = [None, None, None]
rotAnimCurve = [None, None, None]
sclAnimCurve = [None, None, None]
posAnimKind = [0, 0, 0]
rotAnimKind = [0, 0, 0]
sclAnimKind = [0, 0, 0]
deltaPosAnimCurve = [None, None, None]
deltaRotAnimCurve = [None, None, None]
deltaSclAnimCurve = [None, None, None]
deltaPosAnimKind = [0, 0, 0]
deltaRotAnimKind = [0, 0, 0]
deltaSclAnimKind = [0, 0, 0]
positionAnimated = False
rotationAnimated = False
scaleAnimated = False
posAnimated = [False, False, False]
rotAnimated = [False, False, False]
sclAnimated = [False, False, False]
deltaPositionAnimated = False
deltaRotationAnimated = False
deltaScaleAnimated = False
deltaPosAnimated = [False, False, False]
deltaRotAnimated = [False, False, False]
deltaSclAnimated = [False, False, False]
mode = node.rotation_mode
2016-01-20 15:22:01 +01:00
sampledAnimation = ((ArmoryExporter.sampleAnimationFlag) or (mode == "QUATERNION") or (mode == "AXIS_ANGLE"))
2015-10-30 13:23:09 +01:00
if ((not sampledAnimation) and (node.animation_data)):
action = node.animation_data.action
if (action):
for fcurve in action.fcurves:
2016-01-11 13:07:44 +01:00
kind = ArmoryExporter.ClassifyAnimationCurve(fcurve)
2015-10-30 13:23:09 +01:00
if (kind != kAnimationSampled):
if (fcurve.data_path == "location"):
for i in range(3):
if ((fcurve.array_index == i) and (not posAnimCurve[i])):
posAnimCurve[i] = fcurve
posAnimKind[i] = kind
2016-01-11 13:07:44 +01:00
if (ArmoryExporter.AnimationPresent(fcurve, kind)):
2015-10-30 13:23:09 +01:00
posAnimated[i] = True
elif (fcurve.data_path == "delta_location"):
for i in range(3):
if ((fcurve.array_index == i) and (not deltaPosAnimCurve[i])):
deltaPosAnimCurve[i] = fcurve
deltaPosAnimKind[i] = kind
2016-01-11 13:07:44 +01:00
if (ArmoryExporter.AnimationPresent(fcurve, kind)):
2015-10-30 13:23:09 +01:00
deltaPosAnimated[i] = True
elif (fcurve.data_path == "rotation_euler"):
for i in range(3):
if ((fcurve.array_index == i) and (not rotAnimCurve[i])):
rotAnimCurve[i] = fcurve
rotAnimKind[i] = kind
2016-01-11 13:07:44 +01:00
if (ArmoryExporter.AnimationPresent(fcurve, kind)):
2015-10-30 13:23:09 +01:00
rotAnimated[i] = True
elif (fcurve.data_path == "delta_rotation_euler"):
for i in range(3):
if ((fcurve.array_index == i) and (not deltaRotAnimCurve[i])):
deltaRotAnimCurve[i] = fcurve
deltaRotAnimKind[i] = kind
2016-01-11 13:07:44 +01:00
if (ArmoryExporter.AnimationPresent(fcurve, kind)):
2015-10-30 13:23:09 +01:00
deltaRotAnimated[i] = True
elif (fcurve.data_path == "scale"):
for i in range(3):
if ((fcurve.array_index == i) and (not sclAnimCurve[i])):
sclAnimCurve[i] = fcurve
sclAnimKind[i] = kind
2016-01-11 13:07:44 +01:00
if (ArmoryExporter.AnimationPresent(fcurve, kind)):
2015-10-30 13:23:09 +01:00
sclAnimated[i] = True
elif (fcurve.data_path == "delta_scale"):
for i in range(3):
if ((fcurve.array_index == i) and (not deltaSclAnimCurve[i])):
deltaSclAnimCurve[i] = fcurve
deltaSclAnimKind[i] = kind
2016-01-11 13:07:44 +01:00
if (ArmoryExporter.AnimationPresent(fcurve, kind)):
2015-10-30 13:23:09 +01:00
deltaSclAnimated[i] = True
elif ((fcurve.data_path == "rotation_axis_angle") or (fcurve.data_path == "rotation_quaternion") or (fcurve.data_path == "delta_rotation_quaternion")):
sampledAnimation = True
break
else:
sampledAnimation = True
break
positionAnimated = posAnimated[0] | posAnimated[1] | posAnimated[2]
rotationAnimated = rotAnimated[0] | rotAnimated[1] | rotAnimated[2]
scaleAnimated = sclAnimated[0] | sclAnimated[1] | sclAnimated[2]
deltaPositionAnimated = deltaPosAnimated[0] | deltaPosAnimated[1] | deltaPosAnimated[2]
deltaRotationAnimated = deltaRotAnimated[0] | deltaRotAnimated[1] | deltaRotAnimated[2]
deltaScaleAnimated = deltaSclAnimated[0] | deltaSclAnimated[1] | deltaSclAnimated[2]
if ((sampledAnimation) or ((not positionAnimated) and (not rotationAnimated) and (not scaleAnimated) and (not deltaPositionAnimated) and (not deltaRotationAnimated) and (not deltaScaleAnimated))):
# If there's no keyframe animation at all, then write the node transform as a single 4x4 matrix.
# We might still be exporting sampled animation below.
2016-07-20 17:33:17 +02:00
o['transform'] = {}
2015-10-30 13:23:09 +01:00
if (sampledAnimation):
2016-07-20 17:33:17 +02:00
o['transform']['target'] = "transform"
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
o['transform']['values'] = self.WriteMatrix(node.matrix_local)
2015-10-30 13:23:09 +01:00
if (sampledAnimation):
self.ExportNodeSampledAnimation(node, scene, o)
else:
structFlag = False
2016-07-20 17:33:17 +02:00
o['transform'] = {}
o['transform']['values'] = self.WriteMatrix(node.matrix_local)
2016-07-17 20:29:53 +02:00
2016-07-20 17:33:17 +02:00
o['animation_transforms'] = []
2016-07-17 20:29:53 +02:00
2015-10-30 13:23:09 +01:00
deltaTranslation = node.delta_location
if (deltaPositionAnimated):
# When the delta location is animated, write the x, y, and z components separately
# so they can be targeted by different tracks having different sets of keys.
for i in range(3):
pos = deltaTranslation[i]
if ((deltaPosAnimated[i]) or (math.fabs(pos) > kExportEpsilon)):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'translation_' + axisName[i]
animo['name'] = deltaSubtranslationName[i]
animo['value'] = pos
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Translation %", 0, structFlag)
# self.Write(deltaSubtranslationName[i])
# self.Write(B" (kind = \"")
# self.Write(axisName[i])
# self.Write(B"\")\n")
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float {", 1)
# self.WriteFloat(pos)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
elif ((math.fabs(deltaTranslation[0]) > kExportEpsilon) or (math.fabs(deltaTranslation[1]) > kExportEpsilon) or (math.fabs(deltaTranslation[2]) > kExportEpsilon)):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'translation'
animo['values'] = self.WriteVector3D(deltaTranslation)
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Translation\n")
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float[3] {", 1)
# self.WriteVector3D(deltaTranslation)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
translation = node.location
if (positionAnimated):
# When the location is animated, write the x, y, and z components separately
# so they can be targeted by different tracks having different sets of keys.
for i in range(3):
pos = translation[i]
if ((posAnimated[i]) or (math.fabs(pos) > kExportEpsilon)):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'translation_' + axisName[i]
animo['name'] = subtranslationName[i]
animo['value'] = pos
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Translation %", 0, structFlag)
# self.Write(subtranslationName[i])
# self.Write(B" (kind = \"")
# self.Write(axisName[i])
# self.Write(B"\")\n")
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float {", 1)
# self.WriteFloat(pos)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
elif ((math.fabs(translation[0]) > kExportEpsilon) or (math.fabs(translation[1]) > kExportEpsilon) or (math.fabs(translation[2]) > kExportEpsilon)):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'translation'
animo['values'] = self.WriteVector3D(translation)
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Translation\n")
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float[3] {", 1)
# self.WriteVector3D(translation)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
if (deltaRotationAnimated):
# When the delta rotation is animated, write three separate Euler angle rotations
# so they can be targeted by different tracks having different sets of keys.
for i in range(3):
axis = ord(mode[2 - i]) - 0x58
angle = node.delta_rotation_euler[axis]
if ((deltaRotAnimated[axis]) or (math.fabs(angle) > kExportEpsilon)):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'rotation_' + axisName[axis]
animo['name'] = deltaSubrotationName[axis]
animo['value'] = angle
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Rotation %", 0, structFlag)
# self.Write(deltaSubrotationName[axis])
# self.Write(B" (kind = \"")
# self.Write(axisName[axis])
# self.Write(B"\")\n")
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float {", 1)
# self.WriteFloat(angle)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
else:
# When the delta rotation is not animated, write it in the representation given by
# the node's current rotation mode. (There is no axis-angle delta rotation.)
if (mode == "QUATERNION"):
quaternion = node.delta_rotation_quaternion
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)):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'rotation_quaternion'
animo['values'] = self.WriteQuaternion(quaternion)
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Rotation (kind = \"quaternion\")\n", 0, structFlag)
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float[4] {", 1)
# self.WriteQuaternion(quaternion)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
else:
for i in range(3):
axis = ord(mode[2 - i]) - 0x58
angle = node.delta_rotation_euler[axis]
if (math.fabs(angle) > kExportEpsilon):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'rotation_' + axisName[axis]
animo['value'] = angle
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Rotation (kind = \"", 0, structFlag)
# self.Write(axisName[axis])
# self.Write(B"\")\n")
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float {", 1)
# self.WriteFloat(angle)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
if (rotationAnimated):
# When the rotation is animated, write three separate Euler angle rotations
# so they can be targeted by different tracks having different sets of keys.
for i in range(3):
axis = ord(mode[2 - i]) - 0x58
angle = node.rotation_euler[axis]
if ((rotAnimated[axis]) or (math.fabs(angle) > kExportEpsilon)):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'rotation_' + axisName[axis]
animo['name'] = subrotationName[axis]
animo['value'] = angle
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Rotation %", 0, structFlag)
# self.Write(subrotationName[axis])
# self.Write(B" (kind = \"")
# self.Write(axisName[axis])
# self.Write(B"\")\n")
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float {", 1)
# self.WriteFloat(angle)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
else:
# When the rotation is not animated, write it in the representation given by
# the node's current rotation mode.
if (mode == "QUATERNION"):
quaternion = node.rotation_quaternion
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)):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'rotation_quaternion'
animo['values'] = self.WriteQuaternion(quaternion)
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Rotation (kind = \"quaternion\")\n", 0, structFlag)
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float[4] {", 1)
# self.WriteQuaternion(quaternion)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
elif (mode == "AXIS_ANGLE"):
if (math.fabs(node.rotation_axis_angle[0]) > kExportEpsilon):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'rotation_axis'
animo['values'] = self.WriteVector4D(node.rotation_axis_angle)
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Rotation (kind = \"axis\")\n", 0, structFlag)
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float[4] {", 1)
# self.WriteVector4D(node.rotation_axis_angle)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
else:
for i in range(3):
axis = ord(mode[2 - i]) - 0x58
angle = node.rotation_euler[axis]
if (math.fabs(angle) > kExportEpsilon):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'rotation_' + axisName[axis]
animo['value'] = angle
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Rotation (kind = \"", 0, structFlag)
# self.Write(axisName[axis])
# self.Write(B"\")\n")
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float {", 1)
# self.WriteFloat(angle)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
deltaScale = node.delta_scale
if (deltaScaleAnimated):
# When the delta scale is animated, write the x, y, and z components separately
# so they can be targeted by different tracks having different sets of keys.
for i in range(3):
scl = deltaScale[i]
if ((deltaSclAnimated[i]) or (math.fabs(scl) > kExportEpsilon)):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'scale_' + axisName[i]
animo['name'] = deltaSubscaleName[i]
animo['value'] = scl
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Scale %", 0, structFlag)
# self.Write(deltaSubscaleName[i])
# self.Write(B" (kind = \"")
# self.Write(axisName[i])
# self.Write(B"\")\n")
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float {", 1)
# self.WriteFloat(scl)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
elif ((math.fabs(deltaScale[0] - 1.0) > kExportEpsilon) or (math.fabs(deltaScale[1] - 1.0) > kExportEpsilon) or (math.fabs(deltaScale[2] - 1.0) > kExportEpsilon)):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'scale'
animo['values'] = self.WriteVector3D(deltaScale)
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Scale\n", 0, structFlag)
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float[3] {", 1)
# self.WriteVector3D(deltaScale)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
scale = node.scale
if (scaleAnimated):
# When the scale is animated, write the x, y, and z components separately
# so they can be targeted by different tracks having different sets of keys.
for i in range(3):
scl = scale[i]
if ((sclAnimated[i]) or (math.fabs(scl) > kExportEpsilon)):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'scale_' + axisName[i]
animo['name'] = subscaleName[i]
animo['value'] = scl
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Scale %", 0, structFlag)
# self.Write(subscaleName[i])
# self.Write(B" (kind = \"")
# self.Write(axisName[i])
# self.Write(B"\")\n")
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float {", 1)
# self.WriteFloat(scl)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
elif ((math.fabs(scale[0] - 1.0) > kExportEpsilon) or (math.fabs(scale[1] - 1.0) > kExportEpsilon) or (math.fabs(scale[2] - 1.0) > kExportEpsilon)):
2016-07-20 17:33:17 +02:00
animo = {}
o['animation_transforms'].append(animo)
animo['type'] = 'scale'
animo['values'] = self.WriteVector3D(scale)
2015-10-30 13:23:09 +01:00
# self.IndentWrite(B"Scale\n", 0, structFlag)
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"float[3] {", 1)
# self.WriteVector3D(scale)
# self.Write(B"}")
# self.IndentWrite(B"}\n", 0, True)
structFlag = True
2016-01-20 15:22:01 +01:00
# Export the animation tracks.
2016-07-20 17:33:17 +02:00
o['animation'] = {}
o['animation']['begin'] = (action.frame_range[0] - self.beginFrame) * self.frameTime
o['animation']['end'] = (action.frame_range[1] - self.beginFrame) * self.frameTime
o['animation']['tracks'] = []
2016-01-20 15:22:01 +01:00
# self.IndentWrite(B"Animation (begin = ", 0, True)
# self.WriteFloat((action.frame_range[0] - self.beginFrame) * self.frameTime)
# self.Write(B", end = ")
# self.WriteFloat((action.frame_range[1] - self.beginFrame) * self.frameTime)
# self.Write(B")\n")
# self.IndentWrite(B"{\n")
# self.indentLevel += 1
# structFlag = False
2016-07-17 20:29:53 +02:00
if (positionAnimated):
for i in range(3):
if (posAnimated[i]):
tracko = self.ExportAnimationTrack(posAnimCurve[i], posAnimKind[i], subtranslationName[i], structFlag)
2016-07-20 17:33:17 +02:00
o['animation']['tracks'].append(tracko)
2016-07-17 20:29:53 +02:00
structFlag = True
if (rotationAnimated):
for i in range(3):
if (rotAnimated[i]):
tracko = self.ExportAnimationTrack(rotAnimCurve[i], rotAnimKind[i], subrotationName[i], structFlag)
2016-07-20 17:33:17 +02:00
o['animation']['tracks'].append(tracko)
2016-07-17 20:29:53 +02:00
structFlag = True
if (scaleAnimated):
for i in range(3):
if (sclAnimated[i]):
tracko = self.ExportAnimationTrack(sclAnimCurve[i], sclAnimKind[i], subscaleName[i], structFlag)
2016-07-20 17:33:17 +02:00
o['animation']['tracks'].append(tracko)
2016-07-17 20:29:53 +02:00
structFlag = True
if (deltaPositionAnimated):
for i in range(3):
if (deltaPosAnimated[i]):
tracko = self.ExportAnimationTrack(deltaPosAnimCurve[i], deltaPosAnimKind[i], deltaSubtranslationName[i], structFlag)
2016-07-20 17:33:17 +02:00
o['animation']['tracks'].append(tracko)
2016-07-17 20:29:53 +02:00
structFlag = True
if (deltaRotationAnimated):
for i in range(3):
if (deltaRotAnimated[i]):
tracko = self.ExportAnimationTrack(deltaRotAnimCurve[i], deltaRotAnimKind[i], deltaSubrotationName[i], structFlag)
2016-07-20 17:33:17 +02:00
o['animation']['tracks'].append(tracko)
2016-07-17 20:29:53 +02:00
structFlag = True
if (deltaScaleAnimated):
for i in range(3):
if (deltaSclAnimated[i]):
tracko = self.ExportAnimationTrack(deltaSclAnimCurve[i], deltaSclAnimKind[i], deltaSubscaleName[i], structFlag)
2016-07-20 17:33:17 +02:00
o['animation']['tracks'].append(tracko)
2016-07-17 20:29:53 +02:00
structFlag = True
2016-01-20 15:22:01 +01:00
2015-10-30 13:23:09 +01:00
def ProcessBone(self, bone):
2016-01-20 15:22:01 +01:00
if ((ArmoryExporter.exportAllFlag) or (bone.select)):
2015-10-31 22:47:43 +01:00
self.nodeArray[bone] = {"nodeType" : kNodeTypeBone, "structName" : bone.name}
2015-10-30 13:23:09 +01:00
for subnode in bone.children:
self.ProcessBone(subnode)
def ProcessNode(self, node):
2016-01-20 15:22:01 +01:00
if ((ArmoryExporter.exportAllFlag) or (node.select)):
2016-01-11 13:07:44 +01:00
type = ArmoryExporter.GetNodeType(node)
2016-01-20 15:22:01 +01:00
if ArmoryExporter.option_geometry_only and type != kNodeTypeGeometry:
return
2015-10-31 22:47:43 +01:00
self.nodeArray[node] = {"nodeType" : type, "structName" : node.name}
2015-10-30 13:23:09 +01:00
if (node.parent_type == "BONE"):
boneSubnodeArray = self.boneParentArray.get(node.parent_bone)
if (boneSubnodeArray):
boneSubnodeArray.append(node)
else:
self.boneParentArray[node.parent_bone] = [node]
if (node.type == "ARMATURE"):
skeleton = node.data
if (skeleton):
for bone in skeleton.bones:
if (not bone.parent):
self.ProcessBone(bone)
2016-01-20 15:22:01 +01:00
if node.type != 'MESH' or self.node_has_instanced_children(node) == False:
2015-12-02 00:05:20 +01:00
for subnode in node.children:
self.ProcessNode(subnode)
2015-10-30 13:23:09 +01:00
def ProcessSkinnedMeshes(self):
for nodeRef in self.nodeArray.items():
if (nodeRef[1]["nodeType"] == kNodeTypeGeometry):
armature = nodeRef[0].find_armature()
if (armature):
for bone in armature.data.bones:
boneRef = self.FindNode(bone.name)
if (boneRef):
# If a node is used as a bone, then we force its type to be a bone.
boneRef[1]["nodeType"] = kNodeTypeBone
def ExportBoneTransform(self, armature, bone, scene, o):
curveArray = self.CollectBoneAnimation(armature, bone.name)
2016-01-20 15:22:01 +01:00
animation = ((len(curveArray) != 0) or (ArmoryExporter.sampleAnimationFlag))
2015-10-30 13:23:09 +01:00
transform = bone.matrix_local.copy()
parentBone = bone.parent
if (parentBone):
transform = parentBone.matrix_local.inverted() * transform
poseBone = armature.pose.bones.get(bone.name)
if (poseBone):
transform = poseBone.matrix.copy()
parentPoseBone = poseBone.parent
if (parentPoseBone):
transform = parentPoseBone.matrix.inverted() * transform
2016-07-20 17:33:17 +02:00
o['transform'] = {}
2015-10-30 13:23:09 +01:00
#if (animation):
# self.Write(B" %transform")
2016-07-20 17:33:17 +02:00
o['transform']['values'] = self.WriteMatrix(transform)
2015-10-30 13:23:09 +01:00
if ((animation) and (poseBone)):
self.ExportBoneSampledAnimation(poseBone, scene, o)
def ExportMaterialRef(self, material, index, o):
2016-06-03 17:18:38 +02:00
if material == None:
return
2015-10-30 13:23:09 +01:00
if (not material in self.materialArray):
2015-10-31 22:47:43 +01:00
self.materialArray[material] = {"structName" : material.name}
2016-07-20 17:33:17 +02:00
o['material_refs'].append(self.materialArray[material]["structName"])
2015-10-30 13:23:09 +01:00
2015-12-11 18:25:02 +01:00
def ExportParticleSystemRef(self, psys, index, o):
if (not psys.settings in self.particleSystemArray):
self.particleSystemArray[psys.settings] = {"structName" : psys.settings.name}
2016-07-20 17:33:17 +02:00
pref = {}
pref['id'] = psys.name
pref['seed'] = psys.seed
pref['particle'] = self.particleSystemArray[psys.settings]["structName"]
o['particle_refs'].append(pref)
2015-10-30 13:23:09 +01:00
2016-07-12 00:09:02 +02:00
def get_viewport_matrix(self):
screen = bpy.context.window.screen
for area in screen.areas:
if area.type == 'VIEW_3D':
for space in area.spaces:
if space.type == 'VIEW_3D':
return space.region_3d.view_matrix
return None
2015-10-30 13:23:09 +01:00
def ExportNode(self, node, scene, poseBone = None, parento = None):
# This function exports a single node in the scene and includes its name,
# object reference, material references (for geometries), and transform.
# Subnodes are then exported recursively.
2016-05-15 00:39:44 +02:00
if self.cb_preprocess_node(node) == False:
return
2015-10-31 00:30:36 +01:00
2015-10-30 13:23:09 +01:00
nodeRef = self.nodeArray.get(node)
if (nodeRef):
type = nodeRef["nodeType"]
2016-07-20 17:33:17 +02:00
o = {}
o['type'] = structIdentifier[type]
o['id'] = nodeRef["structName"]
2015-10-30 13:23:09 +01:00
2016-06-30 13:22:05 +02:00
if (node.hide_render):
2016-07-20 17:33:17 +02:00
o['visible'] = False
2015-10-30 13:23:09 +01:00
# Export the object reference and material references.
object = node.data
2016-07-21 14:31:33 +02:00
2015-10-30 13:23:09 +01:00
if (type == kNodeTypeGeometry):
if (not object in self.geometryArray):
2015-10-31 22:47:43 +01:00
self.geometryArray[object] = {"structName" : object.name, "nodeTable" : [node]}
2015-10-30 13:23:09 +01:00
else:
self.geometryArray[object]["nodeTable"].append(node)
2016-07-27 14:25:01 +02:00
oid = utils.safe_filename(self.geometryArray[object]["structName"])
2016-01-20 15:22:01 +01:00
if ArmoryExporter.option_geometry_per_file:
2016-07-20 17:33:17 +02:00
o['object_ref'] = 'geom_' + oid + '/' + oid
2016-01-20 15:22:01 +01:00
else:
2016-07-20 17:33:17 +02:00
o['object_ref'] = oid
2015-12-11 18:25:02 +01:00
2016-07-20 17:33:17 +02:00
o['material_refs'] = []
2015-10-30 13:23:09 +01:00
for i in range(len(node.material_slots)):
2016-07-21 13:05:40 +02:00
if self.node_has_override_material(node): # Overwrite material slot
o['material_refs'].append(node.override_material_name)
2016-01-11 16:03:55 +01:00
else: # Export assigned material
self.ExportMaterialRef(node.material_slots[i].material, i, o)
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
o['particle_refs'] = []
2015-12-11 18:25:02 +01:00
for i in range(len(node.particle_systems)):
self.ExportParticleSystemRef(node.particle_systems[i], i, o)
2016-03-28 23:02:42 +02:00
2016-07-20 17:33:17 +02:00
o['dimensions'] = [node.dimensions[0], node.dimensions[1], node.dimensions[2]]
2016-01-11 13:07:44 +01:00
#shapeKeys = ArmoryExporter.GetShapeKeys(object)
2015-10-30 13:23:09 +01:00
#if (shapeKeys):
# self.ExportMorphWeights(node, shapeKeys, scene, o)
# TODO
elif (type == kNodeTypeLight):
if (not object in self.lightArray):
2015-10-31 22:47:43 +01:00
self.lightArray[object] = {"structName" : object.name, "nodeTable" : [node]}
2015-10-30 13:23:09 +01:00
else:
self.lightArray[object]["nodeTable"].append(node)
2016-07-20 17:33:17 +02:00
o['object_ref'] = self.lightArray[object]["structName"]
2015-10-30 13:23:09 +01:00
elif (type == kNodeTypeCamera):
if (not object in self.cameraArray):
2015-10-31 22:47:43 +01:00
self.cameraArray[object] = {"structName" : object.name, "nodeTable" : [node]}
2015-10-30 13:23:09 +01:00
else:
self.cameraArray[object]["nodeTable"].append(node)
2016-07-20 17:33:17 +02:00
o['object_ref'] = self.cameraArray[object]["structName"]
2015-10-30 13:23:09 +01:00
2015-12-18 01:01:41 +01:00
elif (type == kNodeTypeSpeaker):
if (not object in self.speakerArray):
self.speakerArray[object] = {"structName" : object.name, "nodeTable" : [node]}
else:
self.speakerArray[object]["nodeTable"].append(node)
2016-07-20 17:33:17 +02:00
o['object_ref'] = self.speakerArray[object]["structName"]
2015-12-18 01:01:41 +01:00
2015-10-30 13:23:09 +01:00
if (poseBone):
# If the node is parented to a bone and is not relative, then undo the bone's transform.
2016-07-20 17:33:17 +02:00
o['transform'] = {}
o['transform']['values'] = self.WriteMatrix(poseBone.matrix.inverted())
2015-10-30 13:23:09 +01:00
# Export the transform. If the node is animated, then animation tracks are exported here.
self.ExportNodeTransform(node, scene, o)
2016-07-12 00:09:02 +02:00
# Viewport Camera - overwrite active camera matrix with viewport matrix
if type == kNodeTypeCamera and bpy.data.worlds[0].CGPlayViewportCamera:
viewport_matrix = self.get_viewport_matrix()
if viewport_matrix != None:
2016-07-20 17:33:17 +02:00
o['transform']['values'] = self.WriteMatrix(viewport_matrix.inverted())
# Do not apply parent matrix
2016-07-20 17:33:17 +02:00
o['local_transform_only'] = True
2016-07-12 00:09:02 +02:00
2015-10-30 13:23:09 +01:00
if (node.type == "ARMATURE"):
skeleton = node.data
if (skeleton):
2016-07-20 17:33:17 +02:00
o['nodes'] = []
2016-07-28 22:38:11 +02:00
o['bones_ref'] = 'bones_' + o['id']
2016-01-20 15:22:01 +01:00
# TODO: use option_geometry_per_file
2016-07-28 22:38:11 +02:00
fp = self.get_geoms_file_path(o['bones_ref'])
2016-07-19 19:42:46 +02:00
assets.add(fp)
2016-01-20 15:22:01 +01:00
2016-07-28 22:38:11 +02:00
if node.data.armature_cached == False or not os.path.exists(fp):
2015-12-17 23:48:59 +01:00
bones = []
for bone in skeleton.bones:
if (not bone.parent):
2016-07-20 17:33:17 +02:00
boneo = {}
2015-12-17 23:48:59 +01:00
self.ExportBone(node, bone, scene, boneo)
#o.nodes.append(boneo)
bones.append(boneo)
2016-01-20 15:22:01 +01:00
2015-12-17 23:48:59 +01:00
# Save bones separately
2016-07-20 17:33:17 +02:00
bones_obj = {}
bones_obj['nodes'] = bones
utils.write_arm(fp, bones_obj)
2016-07-28 22:38:11 +02:00
node.data.armature_cached = True
2015-10-30 13:23:09 +01:00
if (parento == None):
2016-07-20 17:33:17 +02:00
self.output['nodes'].append(o)
2015-10-30 13:23:09 +01:00
else:
2016-07-20 17:33:17 +02:00
parento['nodes'].append(o)
2015-10-30 13:23:09 +01:00
2016-07-21 17:45:39 +02:00
self.cb_export_node(node, o, type)
2015-10-30 13:23:09 +01:00
2016-01-20 15:22:01 +01:00
if not hasattr(o, 'nodes'):
2016-07-20 17:33:17 +02:00
o['nodes'] = []
2015-10-30 13:23:09 +01:00
2016-01-20 15:22:01 +01:00
if node.type != 'MESH' or self.node_has_instanced_children(node) == False:
2015-12-02 00:05:20 +01:00
for subnode in node.children:
if (subnode.parent_type != "BONE"):
self.ExportNode(subnode, scene, None, o)
2016-07-28 22:38:11 +02:00
def ExportSkinQuality(self, node, armature, exportVertexArray, om):
2015-10-30 13:23:09 +01:00
# This function exports all skinning data, which includes the skeleton
# and per-vertex bone influence data.
2016-07-20 17:33:17 +02:00
oskin = {}
om['skin'] = oskin
2015-10-30 13:23:09 +01:00
# Write the skin bind pose transform.
2016-07-20 17:33:17 +02:00
otrans = {}
oskin['transform'] = otrans
otrans['values'] = self.WriteMatrix(node.matrix_world)
2015-10-30 13:23:09 +01:00
# Export the skeleton, which includes an array of bone node references
# and and array of per-bone bind pose transforms.
2016-07-20 17:33:17 +02:00
oskel = {}
oskin['skeleton'] = oskel
2015-10-30 13:23:09 +01:00
# Write the bone node reference array.
2016-07-20 17:33:17 +02:00
oskel['bone_ref_array'] = []
2015-10-30 13:23:09 +01:00
boneArray = armature.data.bones
boneCount = len(boneArray)
#self.IndentWrite(B"ref\t\t\t// ")
#self.WriteInt(boneCount)
for i in range(boneCount):
boneRef = self.FindNode(boneArray[i].name)
if (boneRef):
2016-07-20 17:33:17 +02:00
oskel['bone_ref_array'].append(boneRef[1]["structName"])
2015-10-30 13:23:09 +01:00
else:
2016-07-20 17:33:17 +02:00
oskel['bone_ref_array'].append("null")
2015-10-30 13:23:09 +01:00
# Write the bind pose transform array.
2016-07-20 17:33:17 +02:00
oskel['transforms'] = []
2015-10-30 13:23:09 +01:00
#self.IndentWrite(B"float[16]\t// ")
#self.WriteInt(boneCount)
for i in range(boneCount):
2016-07-20 17:33:17 +02:00
oskel['transforms'].append(self.WriteMatrix(armature.matrix_world * boneArray[i].matrix_local))
2015-10-30 13:23:09 +01:00
# Export the per-vertex bone influence data.
groupRemap = []
for group in node.vertex_groups:
groupName = group.name
for i in range(boneCount):
if (boneArray[i].name == groupName):
groupRemap.append(i)
break
else:
groupRemap.append(-1)
boneCountArray = []
boneIndexArray = []
boneWeightArray = []
meshVertexArray = node.data.vertices
for ev in exportVertexArray:
boneCount = 0
totalWeight = 0.0
for element in meshVertexArray[ev.vertexIndex].groups:
boneIndex = groupRemap[element.group]
boneWeight = element.weight
if ((boneIndex >= 0) and (boneWeight != 0.0)):
boneCount += 1
totalWeight += boneWeight
boneIndexArray.append(boneIndex)
boneWeightArray.append(boneWeight)
boneCountArray.append(boneCount)
if (totalWeight != 0.0):
normalizer = 1.0 / totalWeight
for i in range(-boneCount, 0):
boneWeightArray[i] *= normalizer
# Write the bone count array. There is one entry per vertex.
2016-07-20 17:33:17 +02:00
oskin['bone_count_array'] = boneCountArray
2015-10-30 13:23:09 +01:00
# Write the bone index array. The number of entries is the sum of the bone counts for all vertices.
2016-07-20 17:33:17 +02:00
oskin['bone_index_array'] = boneIndexArray
2015-10-30 13:23:09 +01:00
# Write the bone weight array. The number of entries is the sum of the bone counts for all vertices.
2016-07-20 17:33:17 +02:00
oskin['bone_weight_array'] = boneWeightArray
2015-10-30 13:23:09 +01:00
2016-07-28 22:38:11 +02:00
def ExportSkinFast(self, node, armature, vert_list, om):
oskin = {}
om['skin'] = oskin
otrans = {}
oskin['transform'] = otrans
otrans['values'] = self.WriteMatrix(node.matrix_world)
oskel = {}
oskin['skeleton'] = oskel
oskel['bone_ref_array'] = []
boneArray = armature.data.bones
boneCount = len(boneArray)
for i in range(boneCount):
boneRef = self.FindNode(boneArray[i].name)
if (boneRef):
oskel['bone_ref_array'].append(boneRef[1]["structName"])
else:
oskel['bone_ref_array'].append("null")
oskel['transforms'] = []
for i in range(boneCount):
oskel['transforms'].append(self.WriteMatrix(armature.matrix_world * boneArray[i].matrix_local))
boneCountArray = []
boneIndexArray = []
boneWeightArray = []
for vtx in vert_list:
boneCountArray.append(vtx.bone_count)
boneIndexArray += vtx.bone_indices
boneWeightArray += vtx.bone_weights
oskin['bone_count_array'] = boneCountArray
oskin['bone_index_array'] = boneIndexArray
oskin['bone_weight_array'] = boneWeightArray
2016-06-22 10:32:19 +02:00
def calc_tangents(self, posa, nora, uva, ia):
triangle_count = int(len(ia) / 3)
vertex_count = int(len(posa) / 3)
tangents = [0] * vertex_count * 3
# bitangents = [0] * vertex_count * 3
for i in range(0, triangle_count):
i0 = ia[i * 3 + 0]
i1 = ia[i * 3 + 1]
i2 = ia[i * 3 + 2]
# TODO: Slow
v0 = Vector((posa[i0 * 3 + 0], posa[i0 * 3 + 1], posa[i0 * 3 + 2]))
v1 = Vector((posa[i1 * 3 + 0], posa[i1 * 3 + 1], posa[i1 * 3 + 2]))
v2 = Vector((posa[i2 * 3 + 0], posa[i2 * 3 + 1], posa[i2 * 3 + 2]))
uv0 = Vector((uva[i0 * 2 + 0], uva[i0 * 2 + 1]))
uv1 = Vector((uva[i1 * 2 + 0], uva[i1 * 2 + 1]))
uv2 = Vector((uva[i2 * 2 + 0], uva[i2 * 2 + 1]))
tangent = ArmoryExporter.calc_tangent(v0, v1, v2, uv0, uv1, uv2)
tangents[i0 * 3 + 0] += tangent.x
tangents[i0 * 3 + 1] += tangent.y
tangents[i0 * 3 + 2] += tangent.z
tangents[i1 * 3 + 0] += tangent.x
tangents[i1 * 3 + 1] += tangent.y
tangents[i1 * 3 + 2] += tangent.z
tangents[i2 * 3 + 0] += tangent.x
tangents[i2 * 3 + 1] += tangent.y
tangents[i2 * 3 + 2] += tangent.z
# bitangents[i0 * 3 + 0] += bitangent.x
# bitangents[i0 * 3 + 1] += bitangent.y
# bitangents[i0 * 3 + 2] += bitangent.z
# bitangents[i1 * 3 + 0] += bitangent.x
# bitangents[i1 * 3 + 1] += bitangent.y
# bitangents[i1 * 3 + 2] += bitangent.z
# bitangents[i2 * 3 + 0] += bitangent.x
# bitangents[i2 * 3 + 1] += bitangent.y
# bitangents[i2 * 3 + 2] += bitangent.z
# Orthogonalize
for i in range(0, vertex_count):
# Slow
t = Vector((tangents[i * 3], tangents[i * 3 + 1], tangents[i * 3 + 2]))
# b = Vector((bitangents[i * 3], bitangents[i * 3 + 1], bitangents[i * 3 + 2]))
n = Vector((nora[i * 3], nora[i * 3 + 1], nora[i * 3 + 2]))
v = t - n * n.dot(t)
v.normalize()
# Calculate handedness
# cnv = n.cross(v)
# if cnv.dot(b) < 0.0:
# v = v * -1.0
tangents[i * 3] = v.x
tangents[i * 3 + 1] = v.y
tangents[i * 3 + 2] = v.z
return tangents
def write_geometry(self, node, fp, o):
# One geometry data per file
if ArmoryExporter.option_geometry_per_file:
2016-07-20 17:33:17 +02:00
geom_obj = {}
geom_obj['geometry_resources'] = [o]
utils.write_arm(fp, geom_obj)
2016-06-22 10:32:19 +02:00
self.node_set_geometry_cached(node, True)
else:
2016-07-20 17:33:17 +02:00
self.output['geometry_resources'].append(o)
2016-06-22 10:32:19 +02:00
def export_geometry_fast(self, exportMesh, node, fp, o, om):
# Much faster export but produces slightly less efficient data
exportMesh.calc_normals_split()
exportMesh.calc_tessface()
vert_list = { Vertex(exportMesh, loop) : 0 for loop in exportMesh.loops}.keys()
num_verts = len(vert_list)
num_uv_layers = len(exportMesh.uv_layers)
num_colors = len(exportMesh.vertex_colors)
vdata = [0] * num_verts * 3
ndata = [0] * num_verts * 3
if num_uv_layers > 0:
t0data = [0] * num_verts * 2
if num_uv_layers > 1:
t1data = [0] * num_verts * 2
if num_colors > 0:
cdata = [0] * num_verts * 3
# Make arrays
for i, vtx in enumerate(vert_list):
vtx.index = i
2016-07-28 22:38:11 +02:00
2016-06-22 10:32:19 +02:00
co = vtx.co
normal = vtx.normal
for j in range(3):
vdata[(i * 3) + j] = co[j]
ndata[(i * 3) + j] = normal[j]
if num_uv_layers > 0:
t0data[i * 2] = vtx.uvs[0].x
2016-07-19 19:42:46 +02:00
t0data[i * 2 + 1] = 1.0 - vtx.uvs[0].y # Reverse TCY
2016-06-22 10:32:19 +02:00
if num_uv_layers > 1:
t1data[i * 2] = vtx.uvs[1].x
t1data[i * 2 + 1] = vtx.uvs[1].y
if num_colors > 0:
2016-07-10 00:51:39 +02:00
cdata[i * 3] = vtx.col[0]
cdata[i * 3 + 1] = vtx.col[1]
cdata[i * 3 + 2] = vtx.col[2]
2016-06-22 10:32:19 +02:00
# Output
2016-07-20 17:33:17 +02:00
om['vertex_arrays'] = []
pa = {}
pa['attrib'] = "position"
pa['size'] = 3
pa['values'] = vdata
om['vertex_arrays'].append(pa)
na = {}
na['attrib'] = "normal"
na['size'] = 3
na['values'] = ndata
om['vertex_arrays'].append(na)
2016-06-22 10:32:19 +02:00
if num_uv_layers > 0:
2016-07-20 17:33:17 +02:00
ta = {}
ta['attrib'] = "texcoord"
ta['size'] = 2
ta['values'] = t0data
om['vertex_arrays'].append(ta)
2016-06-22 10:32:19 +02:00
if num_uv_layers > 1:
2016-07-20 17:33:17 +02:00
ta2 = {}
ta2['attrib'] = "texcoord1"
ta2['size'] = 2
ta2['values'] = t1data
om['vertex_arrays'].append(ta2)
2016-06-22 10:32:19 +02:00
if num_colors > 0:
2016-07-20 17:33:17 +02:00
ca = {}
ca['attrib'] = "color"
ca['size'] = 3
ca['values'] = cdata
om['vertex_arrays'].append(ca)
2016-06-22 10:32:19 +02:00
# Indices
prims = {ma.name if ma else '': [] for ma in exportMesh.materials}
if not prims:
prims = {'': []}
vert_dict = {i : v for v in vert_list for i in v.loop_indices}
for poly in exportMesh.polygons:
first = poly.loop_start
if len(exportMesh.materials) == 0:
prim = prims['']
else:
mat = exportMesh.materials[poly.material_index]
prim = prims[mat.name if mat else '']
indices = [vert_dict[i].index for i in range(first, first+poly.loop_total)]
if poly.loop_total == 3:
prim += indices
elif poly.loop_total > 3:
2016-07-28 22:38:11 +02:00
for i in range(poly.loop_total-2):
2016-06-22 10:32:19 +02:00
prim += (indices[-1], indices[i], indices[i + 1])
# Write indices
2016-07-20 17:33:17 +02:00
om['index_arrays'] = []
2016-06-22 10:32:19 +02:00
for mat, prim in prims.items():
idata = [0] * len(prim)
for i, v in enumerate(prim):
idata[i] = v
2016-07-20 17:33:17 +02:00
ia = {}
ia['size'] = 3
ia['values'] = idata
2016-07-27 14:25:01 +02:00
ia['material'] = 0
# Find material index for multi-mat mesh
if len(exportMesh.materials) > 1:
for i in range(0, len(exportMesh.materials)):
if mat == exportMesh.materials[i].name:
ia['material'] = i
break
2016-07-20 17:33:17 +02:00
om['index_arrays'].append(ia)
2016-06-22 10:32:19 +02:00
# Make tangents
if (self.get_export_tangents(exportMesh) == True and num_uv_layers > 0):
2016-07-20 17:33:17 +02:00
tana = {}
tana['attrib'] = "tangent"
tana['size'] = 3
tana['values'] = self.calc_tangents(pa['values'], na['values'], ta['values'], om['index_arrays'][0]['values'])
om['vertex_arrays'].append(tana)
2016-06-22 10:32:19 +02:00
2016-07-28 22:38:11 +02:00
return vert_list
2015-10-30 13:23:09 +01:00
def ExportGeometry(self, objectRef, scene):
# This function exports a single geometry object.
2015-11-28 21:51:42 +01:00
node = objectRef[1]["nodeTable"][0]
2016-07-27 14:25:01 +02:00
oid = utils.safe_filename(objectRef[1]["structName"])
2015-10-30 13:23:09 +01:00
2015-12-02 00:05:20 +01:00
# Check if geometry is using instanced rendering
2016-01-20 15:22:01 +01:00
is_instanced, instance_offsets = self.object_process_instancing(node, objectRef[1]["nodeTable"])
2015-11-28 21:51:42 +01:00
# No export necessary
2016-01-20 15:22:01 +01:00
if ArmoryExporter.option_geometry_per_file:
fp = self.get_geoms_file_path('geom_' + oid)
2016-07-19 19:42:46 +02:00
assets.add(fp)
2016-01-20 15:22:01 +01:00
if self.node_is_geometry_cached(node) == True and os.path.exists(fp):
return
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
o = {}
o['id'] = oid
2015-11-28 21:51:42 +01:00
mesh = objectRef[0]
2015-10-30 13:23:09 +01:00
structFlag = False;
# Save the morph state if necessary.
activeShapeKeyIndex = node.active_shape_key_index
showOnlyShapeKey = node.show_only_shape_key
currentMorphValue = []
2016-01-11 13:07:44 +01:00
shapeKeys = ArmoryExporter.GetShapeKeys(mesh)
2015-10-30 13:23:09 +01:00
if (shapeKeys):
node.active_shape_key_index = 0
node.show_only_shape_key = True
baseIndex = 0
relative = shapeKeys.use_relative
if (relative):
morphCount = 0
baseName = shapeKeys.reference_key.name
for block in shapeKeys.key_blocks:
if (block.name == baseName):
baseIndex = morphCount
break
morphCount += 1
morphCount = 0
for block in shapeKeys.key_blocks:
currentMorphValue.append(block.value)
block.value = 0.0
if (block.name != ""):
# self.IndentWrite(B"Morph (index = ", 0, structFlag)
# self.WriteInt(morphCount)
# if ((relative) and (morphCount != baseIndex)):
# self.Write(B", base = ")
# self.WriteInt(baseIndex)
# self.Write(B")\n")
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"Name {string {\"", 1)
# self.Write(bytes(block.name, "UTF-8"))
# self.Write(B"\"}}\n")
# self.IndentWrite(B"}\n")
structFlag = True
morphCount += 1
shapeKeys.key_blocks[0].value = 1.0
mesh.update()
2016-07-20 17:33:17 +02:00
om = {}
om['primitive'] = "triangles"
2015-10-30 13:23:09 +01:00
armature = node.find_armature()
applyModifiers = (not armature)
# Apply all modifiers to create a new mesh with tessfaces.
# We don't apply modifiers for a skinned mesh because we need the vertex positions
# before they are deformed by the armature modifier in order to export the proper
# bind pose. This does mean that modifiers preceding the armature modifier are ignored,
# but the Blender API does not provide a reasonable way to retrieve the mesh at an
# arbitrary stage in the modifier stack.
exportMesh = node.to_mesh(scene, applyModifiers, "RENDER", True, False)
2016-07-19 19:42:46 +02:00
# Process geometry
2016-07-28 22:38:11 +02:00
geom_opt = ArmoryExporter.option_optimize_geometry
if geom_opt:
unifiedVertexArray = self.export_geometry_quality(exportMesh, node, fp, o, om)
if (armature):
self.ExportSkinQuality(node, armature, unifiedVertexArray, om)
2016-07-19 19:42:46 +02:00
else:
2016-07-28 22:38:11 +02:00
vert_list = self.export_geometry_fast(exportMesh, node, fp, o, om)
if (armature):
self.ExportSkinQuality(node, armature, vert_list, om)
# self.ExportSkinFast(node, armature, vert_list, om)
2016-07-19 19:42:46 +02:00
# Restore the morph state.
if (shapeKeys):
node.active_shape_key_index = activeShapeKeyIndex
node.show_only_shape_key = showOnlyShapeKey
for m in range(len(currentMorphValue)):
shapeKeys.key_blocks[m].value = currentMorphValue[m]
2016-06-22 10:32:19 +02:00
2016-07-19 19:42:46 +02:00
mesh.update()
# Save offset data for instanced rendering
if is_instanced == True:
2016-07-20 17:33:17 +02:00
om['instance_offsets'] = instance_offsets
2016-07-19 19:42:46 +02:00
# Export usage
2016-07-20 17:33:17 +02:00
om['static_usage'] = self.get_geometry_static_usage(node.data)
2016-07-19 19:42:46 +02:00
2016-07-20 17:33:17 +02:00
o['mesh'] = om
2016-07-19 19:42:46 +02:00
self.write_geometry(node, fp, o)
2016-06-22 10:32:19 +02:00
def export_geometry_quality(self, exportMesh, node, fp, o, om):
2015-10-30 13:23:09 +01:00
# Triangulate mesh and remap vertices to eliminate duplicates.
materialTable = []
2016-03-09 15:29:46 +01:00
exportVertexArray = ArmoryExporter.DeindexMesh(exportMesh, materialTable)
2015-10-30 13:23:09 +01:00
triangleCount = len(materialTable)
indexTable = []
2016-01-11 13:07:44 +01:00
unifiedVertexArray = ArmoryExporter.UnifyVertices(exportVertexArray, indexTable)
2015-10-30 13:23:09 +01:00
vertexCount = len(unifiedVertexArray)
# Write the position array.
2016-07-20 17:33:17 +02:00
om['vertex_arrays'] = []
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
pa = {}
pa['attrib'] = "position"
pa['size'] = 3
pa['values'] = self.WriteVertexArray3D(unifiedVertexArray, "position")
2015-10-30 13:23:09 +01:00
#self.WriteInt(vertexCount)
2016-07-20 17:33:17 +02:00
om['vertex_arrays'].append(pa)
2015-10-30 13:23:09 +01:00
# Write the normal array.
2016-07-20 17:33:17 +02:00
na = {}
na['attrib'] = "normal"
na['size'] = 3
na['values'] = self.WriteVertexArray3D(unifiedVertexArray, "normal")
om['vertex_arrays'].append(na)
2015-10-30 13:23:09 +01:00
# Write the color array if it exists.
colorCount = len(exportMesh.tessface_vertex_colors)
if (colorCount > 0):
2016-07-20 17:33:17 +02:00
ca = {}
ca['attrib'] = "color"
ca['size'] = 3
ca['values'] = self.WriteVertexArray3D(unifiedVertexArray, "color")
om['vertex_arrays'].append(ca)
2015-10-30 13:23:09 +01:00
# Write the texcoord arrays.
texcoordCount = len(exportMesh.tessface_uv_textures)
if (texcoordCount > 0):
2016-07-20 17:33:17 +02:00
ta = {}
ta['attrib'] = "texcoord"
ta['size'] = 2
ta['values'] = self.WriteVertexArray2D(unifiedVertexArray, "texcoord0")
om['vertex_arrays'].append(ta)
2015-10-30 13:23:09 +01:00
if (texcoordCount > 1):
2016-07-20 17:33:17 +02:00
ta2 = {}
ta2['attrib'] = "texcoord1"
ta2['size'] = 2
ta2['values'] = self.WriteVertexArray2D(unifiedVertexArray, "texcoord1")
om['vertex_arrays'].append(ta2)
2015-10-30 13:23:09 +01:00
# If there are multiple morph targets, export them here.
2016-01-20 15:22:01 +01:00
# if (shapeKeys):
# shapeKeys.key_blocks[0].value = 0.0
# for m in range(1, len(currentMorphValue)):
# shapeKeys.key_blocks[m].value = 1.0
# mesh.update()
# node.active_shape_key_index = m
# morphMesh = node.to_mesh(scene, applyModifiers, "RENDER", True, False)
# # Write the morph target position array.
2015-10-30 13:23:09 +01:00
2016-01-20 15:22:01 +01:00
# self.IndentWrite(B"VertexArray (attrib = \"position\", morph = ", 0, True)
# self.WriteInt(m)
# self.Write(B")\n")
# self.IndentWrite(B"{\n")
# self.indentLevel += 1
# self.IndentWrite(B"float[3]\t\t// ")
# self.WriteInt(vertexCount)
# self.IndentWrite(B"{\n", 0, True)
# self.WriteMorphPositionArray3D(unifiedVertexArray, morphMesh.vertices)
# self.IndentWrite(B"}\n")
# self.indentLevel -= 1
# self.IndentWrite(B"}\n\n")
# # Write the morph target normal array.
# self.IndentWrite(B"VertexArray (attrib = \"normal\", morph = ")
# self.WriteInt(m)
# self.Write(B")\n")
# self.IndentWrite(B"{\n")
# self.indentLevel += 1
# self.IndentWrite(B"float[3]\t\t// ")
# self.WriteInt(vertexCount)
# self.IndentWrite(B"{\n", 0, True)
# self.WriteMorphNormalArray3D(unifiedVertexArray, morphMesh.vertices, morphMesh.tessfaces)
# self.IndentWrite(B"}\n")
# self.indentLevel -= 1
# self.IndentWrite(B"}\n")
# bpy.data.meshes.remove(morphMesh)
# Write the index arrays.
2016-07-20 17:33:17 +02:00
om['index_arrays'] = []
2015-10-30 13:23:09 +01:00
maxMaterialIndex = 0
for i in range(len(materialTable)):
index = materialTable[i]
if (index > maxMaterialIndex):
maxMaterialIndex = index
2016-01-20 15:22:01 +01:00
if (maxMaterialIndex == 0):
2015-10-30 13:23:09 +01:00
# There is only one material, so write a single index array.
2016-07-20 17:33:17 +02:00
ia = {}
ia['size'] = 3
ia['values'] = self.WriteTriangleArray(triangleCount, indexTable)
ia['material'] = 0
om['index_arrays'].append(ia)
2015-10-30 13:23:09 +01:00
else:
# If there are multiple material indexes, then write a separate index array for each one.
materialTriangleCount = [0 for i in range(maxMaterialIndex + 1)]
for i in range(len(materialTable)):
materialTriangleCount[materialTable[i]] += 1
for m in range(maxMaterialIndex + 1):
if (materialTriangleCount[m] != 0):
materialIndexTable = []
for i in range(len(materialTable)):
if (materialTable[i] == m):
k = i * 3
materialIndexTable.append(indexTable[k])
materialIndexTable.append(indexTable[k + 1])
materialIndexTable.append(indexTable[k + 2])
2016-07-20 17:33:17 +02:00
ia = {}
ia['size'] = 3
ia['values'] = self.WriteTriangleArray(materialTriangleCount[m], materialIndexTable)
ia['material'] = m
om['index_arrays'].append(ia)
2015-11-28 16:53:52 +01:00
2016-03-09 15:29:46 +01:00
# Export tangents
2016-06-22 10:32:19 +02:00
if (self.get_export_tangents(exportMesh) == True and len(exportMesh.uv_textures) > 0):
2016-07-20 17:33:17 +02:00
tana = {}
tana['attrib'] = "tangent"
tana['size'] = 3
tana['values'] = self.calc_tangents(pa['values'], na['values'], ta['values'], om['index_arrays'][0]['values'])
om['vertex_arrays'].append(tana)
2016-03-09 15:29:46 +01:00
2015-10-30 13:23:09 +01:00
# Delete the new mesh that we made earlier.
bpy.data.meshes.remove(exportMesh)
2016-07-28 22:38:11 +02:00
return unifiedVertexArray
2016-01-11 21:10:33 +01:00
2015-10-30 13:23:09 +01:00
def ExportLight(self, objectRef):
# This function exports a single light object.
2016-07-20 17:33:17 +02:00
o = {}
o['id'] = objectRef[1]["structName"]
2015-10-30 13:23:09 +01:00
object = objectRef[0]
type = object.type
2016-06-26 12:11:51 +02:00
if type == 'SUN':
2016-07-20 17:33:17 +02:00
o['type'] = 'sun'
2016-06-26 12:11:51 +02:00
elif type == 'POINT':
2016-07-20 17:33:17 +02:00
o['type'] = 'point'
2016-06-26 12:11:51 +02:00
elif type == 'SPOT':
2016-07-20 17:33:17 +02:00
o['type'] = 'spot'
o['spot_size'] = math.cos(object.spot_size / 2)
o['spot_blend'] = object.spot_blend
2016-06-26 12:11:51 +02:00
else: # Hemi, area
2016-07-20 17:33:17 +02:00
o['type'] = 'sun'
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
o['cast_shadow'] = object.cycles.cast_shadow
o['near_plane'] = object.light_clip_start
o['far_plane'] = object.light_clip_end
2015-10-30 13:23:09 +01:00
2016-05-16 12:01:12 +02:00
# Parse nodes, only emission for now
2016-06-26 12:11:51 +02:00
# Merge with nodes_material
2016-05-16 12:01:12 +02:00
for n in object.node_tree.nodes:
if n.type == 'EMISSION':
2016-06-22 10:32:19 +02:00
col = n.inputs[0].default_value
2016-07-20 17:33:17 +02:00
o['color'] = [col[0], col[1], col[2]]
o['strength'] = n.inputs[1].default_value / 1000.0
2016-05-16 12:01:12 +02:00
break
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
self.output['light_resources'].append(o)
2015-10-30 13:23:09 +01:00
def ExportCamera(self, objectRef):
# This function exports a single camera object.
2016-07-20 17:33:17 +02:00
o = {}
o['id'] = objectRef[1]["structName"]
2015-10-30 13:23:09 +01:00
#self.WriteNodeTable(objectRef)
object = objectRef[0]
2015-10-31 22:47:43 +01:00
#o.fov = object.angle_x
2016-07-20 17:33:17 +02:00
o['near_plane'] = object.clip_start
o['far_plane'] = object.clip_end
2015-12-17 20:37:12 +01:00
if object.type == 'PERSP':
2016-07-20 17:33:17 +02:00
o['type'] = 'perspective'
2015-12-17 20:37:12 +01:00
else:
2016-07-20 17:33:17 +02:00
o['type'] = 'orthographic'
2015-10-30 13:23:09 +01:00
2016-07-20 17:33:17 +02:00
self.cb_export_camera(object, o)
self.output['camera_resources'].append(o)
2015-10-30 13:23:09 +01:00
2015-12-18 01:01:41 +01:00
def ExportSpeaker(self, objectRef):
# This function exports a single speaker object
2016-07-20 17:33:17 +02:00
o = {}
o['id'] = objectRef[1]["structName"]
2015-12-18 01:01:41 +01:00
object = objectRef[0]
2016-01-24 22:32:51 +01:00
if object.sound:
2016-07-20 17:33:17 +02:00
o['sound'] = object.sound.name.split('.')[0]
2016-01-24 22:32:51 +01:00
else:
2016-07-20 17:33:17 +02:00
o['sound'] = ''
self.output['speaker_resources'].append(o)
2015-10-30 13:23:09 +01:00
def ExportMaterials(self):
2016-05-18 01:34:21 +02:00
# This function exports all of the materials used in the scene
2015-10-30 13:23:09 +01:00
for materialRef in self.materialArray.items():
material = materialRef[0]
2016-05-18 01:34:21 +02:00
# If the material is unlinked, material becomes None
2015-10-30 13:23:09 +01:00
if material == None:
continue
2016-05-18 01:34:21 +02:00
2016-07-20 17:33:17 +02:00
o = {}
o['id'] = materialRef[1]["structName"]
2016-01-20 15:22:01 +01:00
self.cb_export_material(material, o)
2016-07-20 17:33:17 +02:00
self.output['material_resources'].append(o)
2015-10-30 13:23:09 +01:00
2015-12-11 18:25:02 +01:00
def ExportParticleSystems(self):
for particleRef in self.particleSystemArray.items():
2016-07-20 17:33:17 +02:00
o = {}
2015-12-11 18:25:02 +01:00
psettings = particleRef[0]
if psettings == None:
continue
2016-07-20 17:33:17 +02:00
o['id'] = particleRef[1]["structName"]
o['count'] = psettings.count
o['lifetime'] = psettings.lifetime
o['normal_factor'] = psettings.normal_factor;
o['object_align_factor'] = [psettings.object_align_factor[0], psettings.object_align_factor[1], psettings.object_align_factor[2]]
o['factor_random'] = psettings.factor_random
self.output['particle_resources'].append(o)
def ExportWorlds(self):
# for worldRef in self.worldArray.items():
for worldRef in bpy.data.worlds:
2016-07-20 17:33:17 +02:00
o = {}
# w = worldRef[0]
w = worldRef
# o.id = worldRef[1]["structName"]
2016-07-20 17:33:17 +02:00
o['id'] = w.name
2016-06-07 09:38:49 +02:00
self.cb_export_world(w, o)
2016-07-20 17:33:17 +02:00
self.output['world_resources'].append(o)
2015-10-30 13:23:09 +01:00
def ExportObjects(self, scene):
2016-01-20 15:22:01 +01:00
if not ArmoryExporter.option_geometry_only:
2016-07-20 17:33:17 +02:00
self.output['light_resources'] = []
self.output['camera_resources'] = []
self.output['speaker_resources'] = []
2016-01-20 15:22:01 +01:00
for objectRef in self.lightArray.items():
self.ExportLight(objectRef)
for objectRef in self.cameraArray.items():
self.ExportCamera(objectRef)
for objectRef in self.speakerArray.items():
self.ExportSpeaker(objectRef)
2016-01-11 16:03:55 +01:00
for objectRef in self.geometryArray.items():
2016-07-20 17:33:17 +02:00
self.output['geometry_resources'] = [];
2016-01-11 16:03:55 +01:00
self.ExportGeometry(objectRef, scene)
2015-10-30 13:23:09 +01:00
def execute(self, context):
2016-06-22 10:32:19 +02:00
profile_time = time.time()
2016-07-20 17:33:17 +02:00
self.output = {}
2015-10-30 13:23:09 +01:00
scene = context.scene
originalFrame = scene.frame_current
originalSubframe = scene.frame_subframe
self.restoreFrame = False
self.beginFrame = scene.frame_start
self.endFrame = scene.frame_end
self.frameTime = 1.0 / (scene.render.fps_base * scene.render.fps)
self.nodeArray = {}
self.geometryArray = {}
self.lightArray = {}
self.cameraArray = {}
2015-12-18 01:01:41 +01:00
self.speakerArray = {}
2015-10-30 13:23:09 +01:00
self.materialArray = {}
2015-12-11 18:25:02 +01:00
self.particleSystemArray = {}
2016-07-20 17:33:17 +02:00
self.worldArray = {} # Export all worlds
2015-10-30 13:23:09 +01:00
self.boneParentArray = {}
2016-05-16 12:01:12 +02:00
self.materialToObjectDict = dict()
2016-05-18 01:34:21 +02:00
self.materialToGameObjectDict = dict()
self.objectToGameObjectDict = dict()
self.uvprojectUsersArray = [] # For processing decals
2015-10-30 13:23:09 +01:00
2016-02-08 17:28:05 +01:00
# Store used shaders and assets in this scene
2016-01-20 15:22:01 +01:00
ArmoryExporter.shader_references = []
2016-02-08 17:28:05 +01:00
ArmoryExporter.asset_references = []
2016-01-20 15:22:01 +01:00
ArmoryExporter.exportAllFlag = not self.option_export_selection
ArmoryExporter.sampleAnimationFlag = self.option_sample_animation
ArmoryExporter.option_geometry_only = self.option_geometry_only
ArmoryExporter.option_geometry_per_file = self.option_geometry_per_file
2016-07-19 19:42:46 +02:00
ArmoryExporter.option_optimize_geometry = self.option_optimize_geometry
2016-01-20 15:22:01 +01:00
ArmoryExporter.option_minimize = self.option_minimize
self.cb_preprocess()
2015-10-30 13:23:09 +01:00
for object in scene.objects:
if (not object.parent):
self.ProcessNode(object)
self.ProcessSkinnedMeshes()
2016-07-20 17:33:17 +02:00
self.output['nodes'] = []
2015-10-30 13:23:09 +01:00
for object in scene.objects:
if (not object.parent):
self.ExportNode(object, scene)
2016-01-20 15:22:01 +01:00
if not ArmoryExporter.option_geometry_only:
2016-07-20 17:33:17 +02:00
self.output['material_resources'] = []
2016-01-20 15:22:01 +01:00
self.ExportMaterials()
2015-11-30 22:58:07 +01:00
2016-07-20 17:33:17 +02:00
self.output['particle_resources'] = []
2016-01-20 15:22:01 +01:00
self.ExportParticleSystems()
2016-07-20 17:33:17 +02:00
self.output['world_resources'] = []
self.ExportWorlds()
2016-07-20 17:33:17 +02:00
self.output['world_ref'] = scene.world.name
2015-12-11 18:25:02 +01:00
2016-07-20 17:33:17 +02:00
self.output['gravity'] = [scene.gravity[0], scene.gravity[1], scene.gravity[2]]
2016-07-18 12:31:31 +02:00
2015-10-30 13:23:09 +01:00
self.ExportObjects(scene)
2016-05-18 01:34:21 +02:00
self.cb_postprocess()
2015-10-30 13:23:09 +01:00
if (self.restoreFrame):
scene.frame_set(originalFrame, originalSubframe)
2016-07-20 17:33:17 +02:00
# Write .arm
utils.write_arm(self.filepath, self.output)
2015-10-30 13:23:09 +01:00
2016-06-22 10:32:19 +02:00
print('Scene built in ' + str(time.time() - profile_time))
2015-10-30 13:23:09 +01:00
return {'FINISHED'}
2016-01-20 15:22:01 +01:00
# Callbacks
def node_has_instanced_children(self, node):
#return False
return node.instanced_children
def node_is_geometry_cached(self, node):
#return False
2016-07-28 13:21:27 +02:00
if node.data.geometry_cached_verts != len(node.data.vertices):
return False
if node.data.geometry_cached_edges != len(node.data.edges):
return False
return node.data.geometry_cached
2016-01-20 15:22:01 +01:00
def node_set_geometry_cached(self, node, b):
#return
2016-07-28 13:21:27 +02:00
node.data.geometry_cached = b
if b:
node.data.geometry_cached_verts = len(node.data.vertices)
node.data.geometry_cached_edges = len(node.data.edges)
2016-01-20 15:22:01 +01:00
2016-07-21 13:05:40 +02:00
def node_has_override_material(self, node):
2016-01-20 15:22:01 +01:00
#return False
2016-07-21 13:05:40 +02:00
return node.override_material
2016-01-20 15:22:01 +01:00
def get_geometry_static_usage(self, data):
#return True
return data.static_usage
def get_export_tangents(self, mesh):
#return False
for m in mesh.materials:
if m.export_tangents == True:
return True
return False
def object_process_instancing(self, node, refs):
#return False, None
is_instanced = False
instance_offsets = None
for n in refs:
if n.instanced_children == True:
is_instanced = True
# Save offset data
instance_offsets = [0, 0, 0] # Include parent
for sn in n.children:
2016-07-27 14:25:01 +02:00
# Do not take parent matrix into account
loc = sn.matrix_local.to_translation()
instance_offsets.append(loc.x)
instance_offsets.append(loc.y)
instance_offsets.append(loc.z)
2016-07-28 22:38:11 +02:00
# m = sn.matrix_local
# instance_offsets.append(m[0][3]) #* m[0][0]) # Scale
# instance_offsets.append(m[1][3]) #* m[1][1])
# instance_offsets.append(m[2][3]) #* m[2][2])
2016-01-20 15:22:01 +01:00
break
return is_instanced, instance_offsets
def cb_preprocess(self):
#return
ArmoryExporter.option_geometry_only = False
ArmoryExporter.option_geometry_per_file = True
2016-07-19 19:42:46 +02:00
ArmoryExporter.option_optimize_geometry = bpy.data.worlds[0].CGOptimizeGeometry
ArmoryExporter.option_minimize = bpy.data.worlds[0].CGMinimize
2016-07-21 14:31:33 +02:00
ArmoryExporter.option_sample_animation = bpy.data.worlds[0].CGSampledAnimation
ArmoryExporter.sampleAnimationFlag = ArmoryExporter.option_sample_animation
2016-01-20 15:22:01 +01:00
# Only one pipeline for scene for now
2016-02-08 17:28:05 +01:00
# Used for material shader export and khafile
2016-01-20 15:22:01 +01:00
if (len(bpy.data.cameras) > 0):
2016-02-08 17:28:05 +01:00
ArmoryExporter.pipeline_id = bpy.data.cameras[0].pipeline_id
# Gather passes, not very elegant
ArmoryExporter.pipeline_passes = []
for node_group in bpy.data.node_groups:
if node_group.name == bpy.data.cameras[0].pipeline_path:
for node in node_group.nodes:
if node.bl_idname == 'DrawGeometryNodeType':
ArmoryExporter.pipeline_passes.append(node.inputs[1].default_value) # Context
break
2016-05-27 01:12:21 +02:00
ArmoryExporter.geometry_context = bpy.data.cameras[0].geometry_context
ArmoryExporter.shadows_context = bpy.data.cameras[0].shadows_context
ArmoryExporter.translucent_context = bpy.data.cameras[0].translucent_context
2016-07-21 11:22:34 +02:00
ArmoryExporter.overlay_context = bpy.data.cameras[0].overlay_context
2015-10-30 13:23:09 +01:00
2016-05-15 00:39:44 +02:00
def cb_preprocess_node(self, node): # Returns false if node should not be exported
#return True
2016-05-18 01:34:21 +02:00
export_node = True
# Disabled object
if node.game_export == False:
return False
2016-05-15 00:39:44 +02:00
for m in node.modifiers:
if m.type == 'OCEAN':
2016-07-17 20:29:53 +02:00
# Do not export ocean geometry, just take specified constants
2016-05-18 01:34:21 +02:00
export_node = False
2016-07-17 20:29:53 +02:00
wrd = bpy.data.worlds[0]
wrd.generate_ocean = True
# Take position and bounds
wrd.generate_ocean_level = node.location.z
2016-06-03 17:18:38 +02:00
elif m.type == 'UV_PROJECT' and m.show_render:
2016-05-18 01:34:21 +02:00
self.uvprojectUsersArray.append(node)
2016-05-15 00:39:44 +02:00
2016-05-18 01:34:21 +02:00
return export_node
def cb_postprocess(self):
# Check uv project users
for node in self.uvprojectUsersArray:
for m in node.modifiers:
if m.type == 'UV_PROJECT':
# Mark all projectors as decals
for pnode in m.projectors:
o = self.objectToGameObjectDict[node]
po = self.objectToGameObjectDict[pnode.object]
2016-07-20 17:33:17 +02:00
po['type'] = 'decal_node'
po['material_refs'] = [o['material_refs'][0] + '_decal'] # Will fetch a proper context used in render path
2016-05-18 01:34:21 +02:00
break
2016-05-15 00:39:44 +02:00
2016-07-21 17:45:39 +02:00
def cb_export_node(self, node, o, type):
2016-01-20 15:22:01 +01:00
#return
# Export traits
2016-07-20 17:33:17 +02:00
o['traits'] = []
2016-01-20 15:22:01 +01:00
for t in node.my_traitlist:
if t.enabled_prop == False:
continue
2016-07-20 17:33:17 +02:00
x = {}
2016-02-07 23:03:52 +01:00
if t.type_prop == 'Nodes' and t.nodes_name_prop != '':
2016-07-20 17:33:17 +02:00
x['type'] = 'Script'
2016-07-27 14:25:01 +02:00
x['class_name'] = bpy.data.worlds[0].CGProjectPackage + '.node.' + utils.safe_filename(t.nodes_name_prop)
2016-01-25 22:29:50 +01:00
elif t.type_prop == 'Scene Instance':
2016-07-20 17:33:17 +02:00
x['type'] = 'Script'
x['class_name'] = 'armory.trait.internal.SceneInstance'
2016-07-27 14:25:01 +02:00
x['parameters'] = [utils.safe_filename(t.scene_prop)]
2016-01-25 22:29:50 +01:00
elif t.type_prop == 'Animation':
2016-07-20 17:33:17 +02:00
x['type'] = 'Script'
x['class_name'] = 'armory.trait.internal.Animation'
2016-01-25 22:29:50 +01:00
names = []
starts = []
ends = []
2016-07-31 11:49:55 +02:00
speeds = []
loops = []
reflects = []
2016-07-10 00:51:39 +02:00
for at in t.my_animationtraitlist:
2016-01-25 22:29:50 +01:00
if at.enabled_prop:
names.append(at.name)
starts.append(at.start_prop)
ends.append(at.end_prop)
2016-07-31 11:49:55 +02:00
speeds.append(at.speed_prop)
loops.append(at.loop_prop)
reflects.append(at.reflect_prop)
x['parameters'] = [t.start_track_name_prop, names, starts, ends, speeds, loops, reflects]
2016-01-25 22:29:50 +01:00
else: # Script
2016-07-20 17:33:17 +02:00
x['type'] = 'Script'
2016-07-10 00:51:39 +02:00
if t.type_prop == 'Bundled Script':
trait_prefix = 'armory.trait.'
else:
trait_prefix = bpy.data.worlds[0].CGProjectPackage + '.'
2016-07-20 17:33:17 +02:00
x['class_name'] = trait_prefix + t.class_name_prop
2016-07-10 00:51:39 +02:00
if len(t.my_paramstraitlist) > 0:
2016-07-20 17:33:17 +02:00
x['parameters'] = []
2016-07-10 00:51:39 +02:00
for pt in t.my_paramstraitlist: # Append parameters
2016-07-20 17:33:17 +02:00
x['parameters'].append(ast.literal_eval(pt.name))
2016-01-20 15:22:01 +01:00
2016-07-20 17:33:17 +02:00
o['traits'].append(x)
2016-01-20 15:22:01 +01:00
# Rigid body trait
if node.rigid_body != None:
rb = node.rigid_body
2016-05-05 19:50:03 +02:00
shape = 0 # BOX
2016-01-20 15:22:01 +01:00
if rb.collision_shape == 'SPHERE':
2016-05-05 19:50:03 +02:00
shape = 1
2016-01-20 15:22:01 +01:00
elif rb.collision_shape == 'CONVEX_HULL':
2016-05-05 19:50:03 +02:00
shape = 2
2016-01-20 15:22:01 +01:00
elif rb.collision_shape == 'MESH':
2016-02-15 14:26:45 +01:00
if rb.enabled:
2016-05-05 19:50:03 +02:00
shape = 3 # Mesh
2016-02-15 14:26:45 +01:00
else:
2016-05-05 19:50:03 +02:00
shape = 8 # Static Mesh
2016-01-20 15:22:01 +01:00
elif rb.collision_shape == 'CONE':
2016-05-05 19:50:03 +02:00
shape = 4
2016-01-20 15:22:01 +01:00
elif rb.collision_shape == 'CYLINDER':
2016-05-05 19:50:03 +02:00
shape = 5
2016-01-20 15:22:01 +01:00
elif rb.collision_shape == 'CAPSULE':
2016-05-05 19:50:03 +02:00
shape = 6
2016-01-20 15:22:01 +01:00
body_mass = 0
if rb.enabled:
body_mass = rb.mass
2016-07-20 17:33:17 +02:00
x = {}
x['type'] = 'Script'
x['class_name'] = 'armory.trait.internal.RigidBody'
x['parameters'] = [body_mass, shape, rb.friction]
2016-05-06 23:46:04 +02:00
if rb.use_margin:
2016-07-20 17:33:17 +02:00
x['parameters'].append(rb.collision_margin)
o['traits'].append(x)
2016-05-18 01:34:21 +02:00
2016-07-21 17:45:39 +02:00
if type == kNodeTypeCamera:
2016-07-27 14:25:01 +02:00
# Debug console enabled, attach console overlay to each camera
if bpy.data.worlds[0].CGPlayConsole:
console_trait = {}
console_trait['type'] = 'Script'
console_trait['class_name'] = 'armory.trait.internal.Console'
console_trait['parameters'] = []
o['traits'].append(console_trait)
# Viewport camera enabled, attach navigation if enabled
if bpy.data.worlds[0].CGPlayViewportCamera and bpy.data.worlds[0].CGPlayViewportNavigation == 'Walk':
navigation_trait = {}
navigation_trait['type'] = 'Script'
navigation_trait['class_name'] = 'armory.trait.WalkNavigation'
navigation_trait['parameters'] = []
o['traits'].append(navigation_trait)
2016-07-21 17:45:39 +02:00
2016-05-18 01:34:21 +02:00
# Map objects to game objects
self.objectToGameObjectDict[node] = o
2016-05-16 12:01:12 +02:00
# Map objects to materials, can be used in later stages
for i in range(len(node.material_slots)):
mat = node.material_slots[i].material
if mat in self.materialToObjectDict:
2016-05-18 01:34:21 +02:00
self.materialToObjectDict[mat].append(node)
self.materialToGameObjectDict[mat].append(o)
2016-05-16 12:01:12 +02:00
else:
2016-05-18 01:34:21 +02:00
self.materialToObjectDict[mat] = [node]
self.materialToGameObjectDict[mat] = [o]
2016-01-20 15:22:01 +01:00
def cb_export_camera(self, object, o):
#return
2016-07-20 17:33:17 +02:00
o['frustum_culling'] = object.frustum_culling
o['pipeline'] = object.pipeline_path + '/' + object.pipeline_path # Same file name and id
2016-01-20 15:22:01 +01:00
if 'Background' in bpy.data.worlds[0].node_tree.nodes: # TODO: parse node tree
col = bpy.data.worlds[0].node_tree.nodes['Background'].inputs[0].default_value
2016-07-20 17:33:17 +02:00
o['clear_color'] = [col[0], col[1], col[2], col[3]]
2016-01-20 15:22:01 +01:00
else:
2016-07-20 17:33:17 +02:00
o['clear_color'] = [0.0, 0.0, 0.0, 1.0]
2016-01-20 15:22:01 +01:00
def cb_export_material(self, material, o):
#return
defs = []
2016-07-30 11:56:57 +02:00
2016-05-16 13:02:16 +02:00
if material.skip_context != '':
2016-07-20 17:33:17 +02:00
o['skip_context'] = material.skip_context
2016-07-30 11:56:57 +02:00
if material.override_cull:
o['override_context'] = {}
o['override_context']['cull_mode'] = material.override_cull_mode
2016-07-20 17:33:17 +02:00
o['contexts'] = []
2016-05-27 01:12:21 +02:00
# Geometry context
2016-07-20 17:33:17 +02:00
c = {}
c['id'] = ArmoryExporter.geometry_context
c['bind_constants'] = []
2016-01-31 00:20:07 +01:00
2016-07-20 17:33:17 +02:00
const = {}
const['id'] = 'receiveShadow'
const['bool'] = material.receive_shadow
c['bind_constants'].append(const)
2016-01-31 00:20:07 +01:00
2016-07-20 17:33:17 +02:00
const = {}
const['id'] = 'mask'
const['float'] = material.stencil_mask
c['bind_constants'].append(const)
2016-04-02 18:19:52 +02:00
2016-07-20 17:33:17 +02:00
c['bind_textures'] = []
2016-02-07 23:03:52 +01:00
2016-05-18 01:34:21 +02:00
# If material user has decal modifier, parse decal material context
mat_users = self.materialToObjectDict[material]
# Get decal uv map name
decal_uv_layer = None
for ob in mat_users:
for m in ob.modifiers:
if m.type == 'UV_PROJECT':
decal_uv_layer = m.uv_layer
break
# Get decal context from pipes
decal_context = bpy.data.cameras[0].last_decal_context
2016-01-20 15:22:01 +01:00
# Parse nodes
2016-05-18 01:34:21 +02:00
import nodes_material
# Parse from material output
if decal_uv_layer == None:
nodes_material.parse(self, material, c, defs)
2016-07-20 17:33:17 +02:00
o['contexts'].append(c)
2016-05-18 01:34:21 +02:00
# Decal attached, split material into two separate ones
# Mandatory starting point from mix node for now
else:
2016-07-20 17:33:17 +02:00
o2 = {}
o2['id'] = o['id'] + '_decal'
o2['contexts'] = []
c2 = {}
c2['id'] = decal_context
c2['bind_constants'] = []
c2['bind_textures'] = []
2016-05-18 01:34:21 +02:00
defs2 = []
tree = material.node_tree
output_node = nodes_material.get_output_node(tree)
mix_node = nodes_material.find_node_by_link(tree, output_node, output_node.inputs[0])
surface_node1 = nodes_material.find_node_by_link(tree, mix_node, mix_node.inputs[1])
surface_node2 = nodes_material.find_node_by_link(tree, mix_node, mix_node.inputs[2])
nodes_material.parse_from(self, material, c, defs, surface_node1)
nodes_material.parse_from(self, material, c2, defs2, surface_node2)
2016-07-20 17:33:17 +02:00
o['contexts'].append(c)
o2['contexts'].append(c2)
2016-05-18 01:34:21 +02:00
self.finalize_shader(o2, defs2, [decal_context])
2016-07-20 17:33:17 +02:00
self.output['material_resources'].append(o2)
2016-07-21 11:22:34 +02:00
2016-07-21 13:05:40 +02:00
# Override context
if material.override_shader_context:
c['id'] = material.override_shader_context_name
2016-05-27 01:12:21 +02:00
# If material has transparency change to translucent context
2016-07-21 13:05:40 +02:00
elif '_Translucent' in defs:
2016-05-27 01:12:21 +02:00
defs.remove('_Translucent')
2016-07-20 17:33:17 +02:00
c['id'] = ArmoryExporter.translucent_context
2016-07-21 13:05:40 +02:00
# X-Ray enabled
elif material.overlay:
# Change to overlay context
c['id'] = ArmoryExporter.overlay_context
2016-05-27 01:12:21 +02:00
# Otherwise add shadows context
else:
2016-07-20 17:33:17 +02:00
c = {}
c['id'] = ArmoryExporter.shadows_context
o['contexts'].append(c)
2016-05-27 01:12:21 +02:00
2016-05-18 01:34:21 +02:00
# Material users
2016-01-20 15:22:01 +01:00
for ob in mat_users:
# Instancing used by material user
if ob.instanced_children or len(ob.particle_systems) > 0:
defs.append('_Instancing')
# GPU Skinning
if ob.find_armature():
defs.append('_Skinning')
2016-01-25 20:43:11 +01:00
# Billboarding
if len(ob.constraints) > 0 and ob.constraints[0].target != None and \
ob.constraints[0].target.type == 'CAMERA' and ob.constraints[0].mute == False:
defs.append('_Billboard')
2016-01-20 15:22:01 +01:00
# Whether objects should export tangent data
2016-02-15 12:48:15 +01:00
normal_mapping = '_NMTex' in defs
2016-01-29 10:52:13 +01:00
if material.export_tangents != normal_mapping:
material.export_tangents = normal_mapping
2016-01-20 15:22:01 +01:00
# Delete geometry caches
for ob in mat_users:
2016-07-28 13:21:27 +02:00
ob.data.geometry_cached = False
2016-01-20 15:22:01 +01:00
break
2016-05-18 01:34:21 +02:00
# Process defs and append resources
2016-07-21 13:05:40 +02:00
if material.override_shader == False:
2016-05-18 01:34:21 +02:00
self.finalize_shader(o, defs, ArmoryExporter.pipeline_passes)
2016-01-20 15:22:01 +01:00
else:
2016-01-29 10:52:13 +01:00
# TODO: gather defs from vertex data when custom shader is used
2016-07-21 13:05:40 +02:00
o['shader'] = material.override_shader_name
2016-06-07 09:38:49 +02:00
def cb_export_world(self, world, o):
2016-07-20 17:33:17 +02:00
o['brdf'] = 'brdf'
o['probes'] = []
2016-06-07 09:38:49 +02:00
# Main probe
2016-07-10 00:51:39 +02:00
world_generate_radiance = False
defs = bpy.data.worlds[0].world_defs
if '_EnvTex' in defs: # Radiance only for texture
world_generate_radiance = bpy.data.worlds[0].generate_radiance
2016-07-12 00:09:02 +02:00
generate_irradiance = True #'_EnvTex' in defs or '_EnvSky' in defs or '_EnvCon' in defs
2016-06-07 09:38:49 +02:00
envtex = bpy.data.cameras[0].world_envtex_name.rsplit('.', 1)[0]
num_mips = bpy.data.cameras[0].world_envtex_num_mips
strength = bpy.data.cameras[0].world_envtex_strength
2016-07-10 00:51:39 +02:00
po = self.make_probe('world', envtex, num_mips, strength, 1.0, [0, 0, 0], [0, 0, 0], world_generate_radiance, generate_irradiance)
2016-07-20 17:33:17 +02:00
o['probes'].append(po)
2016-06-07 09:38:49 +02:00
2016-07-10 00:51:39 +02:00
if '_EnvSky' in defs:
2016-06-30 13:22:05 +02:00
# Sky data for probe
2016-07-20 17:33:17 +02:00
po['sun_direction'] = list(bpy.data.cameras[0].world_envtex_sun_direction)
po['turbidity'] = bpy.data.cameras[0].world_envtex_turbidity
po['ground_albedo'] = bpy.data.cameras[0].world_envtex_ground_albedo
2016-06-30 13:22:05 +02:00
2016-06-07 09:38:49 +02:00
# Probe cameras attached in scene
for cam in bpy.data.cameras:
if cam.is_probe:
# Generate probe straight here for now
volume_object = bpy.data.objects[cam.probe_volume]
volume = [volume_object.scale[0], volume_object.scale[1], volume_object.scale[2]]
volume_center = [volume_object.location[0], volume_object.location[1], volume_object.location[2]]
disable_hdr = cam.probe_texture.endswith('.jpg')
2016-06-30 13:22:05 +02:00
generate_radiance = cam.probe_generate_radiance
if world_generate_radiance == False:
generate_radiance = False
2016-07-17 20:29:53 +02:00
cam.probe_num_mips = write_probes.write_probes('Assets/' + cam.probe_texture, disable_hdr, cam.probe_num_mips, generate_radiance=generate_radiance)
2016-06-07 09:38:49 +02:00
base_name = cam.probe_texture.rsplit('.', 1)[0]
2016-07-10 00:51:39 +02:00
po = self.make_probe(cam.name, base_name, cam.probe_num_mips, cam.probe_strength, cam.probe_blending, volume, volume_center, generate_radiance, generate_irradiance)
2016-07-20 17:33:17 +02:00
o['probes'].append(po)
2016-06-07 09:38:49 +02:00
2016-07-10 00:51:39 +02:00
def make_probe(self, id, envtex, mipmaps, strength, blending, volume, volume_center, generate_radiance, generate_irradiance):
2016-07-20 17:33:17 +02:00
po = {}
po['id'] = id
2016-06-30 13:22:05 +02:00
if generate_radiance:
2016-07-20 17:33:17 +02:00
po['radiance'] = envtex + '_radiance'
po['radiance_mipmaps'] = mipmaps
2016-07-10 00:51:39 +02:00
if generate_irradiance:
2016-07-20 17:33:17 +02:00
po['irradiance'] = envtex + '_irradiance'
2016-07-10 00:51:39 +02:00
else:
2016-07-20 17:33:17 +02:00
po['irradiance'] = '' # No irradiance data, fallback to default at runtime
po['strength'] = strength
po['blending'] = blending
po['volume'] = volume
po['volume_center'] = volume_center
2016-06-07 09:38:49 +02:00
return po
2016-05-18 01:34:21 +02:00
def finalize_shader(self, o, defs, pipeline_passes):
# Merge duplicates and sort
defs = sorted(list(set(defs)))
# Select correct shader variant
ext = ''
for d in defs:
ext += d
2016-06-03 17:18:38 +02:00
# Append world defs
ext += bpy.data.worlds[0].world_defs
2016-05-18 01:34:21 +02:00
# Shader res
shader_res_name = ArmoryExporter.pipeline_id + ext
2016-07-20 17:33:17 +02:00
shader_res_path = 'compiled/ShaderResources/' + ArmoryExporter.pipeline_id + '/' + shader_res_name + '.arm'
2016-05-18 01:34:21 +02:00
# Stencil mask
# if material.stencil_mask > 0:
# mask_ext = "_mask" + str(material.stencil_mask)
# shader_res_name_with_mask = shader_res_name + mask_ext
2016-07-20 17:33:17 +02:00
# shader_res_path_with_mask = 'compiled/ShaderResources/' + ArmoryExporter.pipeline_id + '/' + shader_res_name_with_mask + '.arm'
2016-05-18 01:34:21 +02:00
# # Copy resource if it does not exist and set stencil mask
# if not os.path.isfile(shader_res_path_with_mask):
# json_file = open(shader_res_path).read()
# json_data = json.loads(json_file)
# res = json_data['shader_resources'][0]
# res['id'] += mask_ext
# for c in res['contexts']:
# c['stencil_pass'] = 'replace'
# c['stencil_reference_value'] = material.stencil_mask
# with open(shader_res_path_with_mask, 'w') as f:
# json.dump(json_data, f)
# ArmoryExporter.asset_references.append(shader_res_path_with_mask)
# o.shader = shader_res_name_with_mask + '/' + shader_res_name_with_mask
# # No stencil mask
# else:
ArmoryExporter.asset_references.append(shader_res_path)
2016-07-20 17:33:17 +02:00
o['shader'] = shader_res_name + '/' + shader_res_name
2016-05-18 01:34:21 +02:00
# Process all passes from pipeline
for pipe_pass in pipeline_passes:
shader_name = pipe_pass + ext
ArmoryExporter.shader_references.append('compiled/Shaders/' + ArmoryExporter.pipeline_id + '/' + shader_name)
2015-10-30 13:23:09 +01:00
def register():
2016-01-11 13:07:44 +01:00
bpy.utils.register_class(ArmoryExporter)
2015-10-30 13:23:09 +01:00
def unregister():
2016-01-11 13:07:44 +01:00
bpy.utils.unregister_class(ArmoryExporter)
2015-10-30 13:23:09 +01:00
if __name__ == "__main__":
register()