2016-08-04 22:38:56 +02:00
# Armory Scene Exporter
2016-06-22 10:32:19 +02:00
# 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
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
2016-07-19 19:42:46 +02:00
import assets
2016-10-27 01:11:11 +02:00
import armutils
2016-08-21 00:16:13 +02:00
import subprocess
2016-10-19 13:28:06 +02:00
import log
2016-12-20 01:39:16 +01:00
import material . make as make_material
2016-10-19 13:28:06 +02:00
import nodes
2015-10-30 13:23:09 +01:00
2016-11-28 14:40:07 +01:00
NodeTypeNode = 0
NodeTypeBone = 1
NodeTypeMesh = 2
NodeTypeLamp = 3
NodeTypeCamera = 4
NodeTypeSpeaker = 5
AnimationTypeSampled = 0
AnimationTypeLinear = 1
AnimationTypeBezier = 2
ExportEpsilon = 1.0e-6
2015-10-30 13:23:09 +01:00
2016-08-25 00:26:01 +02:00
structIdentifier = [ " object " , " bone_object " , " mesh_object " , " lamp_object " , " camera_object " , " speaker_object " ]
2015-10-30 13:23:09 +01:00
2016-08-25 00:26:01 +02:00
subtranslationName = [ " xloc " , " yloc " , " zloc " ]
2015-10-30 13:23:09 +01:00
subrotationName = [ " xrot " , " yrot " , " zrot " ]
subscaleName = [ " xscl " , " yscl " , " zscl " ]
2016-08-25 00:26:01 +02:00
deltaSubtranslationName = [ " dxloc " , " dyloc " , " dzloc " ]
2015-10-30 13:23:09 +01:00
deltaSubrotationName = [ " dxrot " , " dyrot " , " dzrot " ]
deltaSubscaleName = [ " dxscl " , " dyscl " , " dzscl " ]
axisName = [ " x " , " y " , " z " ]
2016-06-22 10:32:19 +02:00
class Vertex :
2016-09-02 23:11:04 +02:00
__slots__ = ( " co " , " normal " , " uvs " , " col " , " loop_indices " , " index " , " bone_weights " , " bone_indices " , " bone_count " , " vertexIndex " )
def __init__ ( self , mesh , loop ) :
self . vertexIndex = loop . vertex_index
i = loop . index
self . co = mesh . vertices [ self . vertexIndex ] . co . freeze ( )
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
# groups = sorted(mesh.vertices[self.vertexIndex].groups, key=lambda group: group.weight, reverse=True)
# if len(groups) > 4:
# groups = groups[:4]
# 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)
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
2016-06-22 10:32:19 +02:00
2015-10-30 13:23:09 +01:00
class ExportVertex :
2016-09-02 23:11:04 +02:00
__slots__ = ( " hash " , " vertexIndex " , " faceIndex " , " position " , " normal " , " color " , " texcoord0 " , " texcoord1 " )
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 ) :
2016-12-15 23:50:21 +01:00
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
if self . color != v . color :
return False
if self . texcoord1 != v . texcoord1 :
return False
return True
2016-09-02 23:11:04 +02:00
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
2015-10-30 13:23:09 +01:00
2016-10-19 13:28:06 +02:00
class ArmoryExporter :
''' Export to Armory format '''
def write_matrix ( self , matrix ) :
2016-09-02 23:11:04 +02: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 ] ]
2016-10-19 13:28:06 +02:00
def write_vector2d ( self , vector ) :
2016-09-02 23:11:04 +02:00
return [ vector [ 0 ] , vector [ 1 ] ]
2016-10-19 13:28:06 +02:00
def write_vector3d ( self , vector ) :
2016-09-02 23:11:04 +02:00
return [ vector [ 0 ] , vector [ 1 ] , vector [ 2 ] ]
2016-10-19 13:28:06 +02:00
def write_vertex_array2d ( self , vertexArray , attrib ) :
2016-09-02 23:11:04 +02:00
va = [ ]
count = len ( vertexArray )
k = 0
lineCount = count >> 3
for i in range ( lineCount ) :
for j in range ( 7 ) :
2016-10-19 13:28:06 +02:00
va + = self . write_vector2d ( getattr ( vertexArray [ k ] , attrib ) )
2016-09-02 23:11:04 +02:00
k + = 1
2016-10-19 13:28:06 +02:00
va + = self . write_vector2d ( getattr ( vertexArray [ k ] , attrib ) )
2016-09-02 23:11:04 +02:00
k + = 1
count & = 7
if ( count != 0 ) :
for j in range ( count - 1 ) :
2016-10-19 13:28:06 +02:00
va + = self . write_vector2d ( getattr ( vertexArray [ k ] , attrib ) )
2016-09-02 23:11:04 +02:00
k + = 1
2016-10-19 13:28:06 +02:00
va + = self . write_vector2d ( getattr ( vertexArray [ k ] , attrib ) )
2016-09-02 23:11:04 +02:00
return va
2016-10-19 13:28:06 +02:00
def write_vertex_array3d ( self , vertex_array , attrib ) :
2016-09-02 23:11:04 +02:00
va = [ ]
2016-10-19 13:28:06 +02:00
count = len ( vertex_array )
2016-09-02 23:11:04 +02:00
k = 0
lineCount = count >> 3
for i in range ( lineCount ) :
for j in range ( 7 ) :
2016-10-19 13:28:06 +02:00
va + = self . write_vector3d ( getattr ( vertex_array [ k ] , attrib ) )
2016-09-02 23:11:04 +02:00
k + = 1
2016-10-19 13:28:06 +02:00
va + = self . write_vector3d ( getattr ( vertex_array [ k ] , attrib ) )
2016-09-02 23:11:04 +02:00
k + = 1
count & = 7
2016-11-21 16:49:32 +01:00
if count != 0 :
2016-09-02 23:11:04 +02:00
for j in range ( count - 1 ) :
2016-10-19 13:28:06 +02:00
va + = self . write_vector3d ( getattr ( vertex_array [ k ] , attrib ) )
2016-09-02 23:11:04 +02:00
k + = 1
2016-10-19 13:28:06 +02:00
va + = self . write_vector3d ( getattr ( vertex_array [ k ] , attrib ) )
2016-09-02 23:11:04 +02:00
return va
2016-10-19 13:28:06 +02:00
def write_triangle ( self , triangle_index , index_table ) :
i = triangle_index * 3
return [ index_table [ i ] , index_table [ i + 1 ] , index_table [ i + 2 ] ]
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
def write_triangle_array ( self , count , index_table ) :
2016-09-02 23:11:04 +02:00
va = [ ]
2016-10-19 13:28:06 +02:00
triangle_index = 0
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
line_count = count >> 4
for i in range ( line_count ) :
2016-09-02 23:11:04 +02:00
for j in range ( 15 ) :
2016-10-19 13:28:06 +02:00
va + = self . write_triangle ( triangle_index , index_table )
triangle_index + = 1
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
va + = self . write_triangle ( triangle_index , index_table )
triangle_index + = 1
2016-09-02 23:11:04 +02:00
count & = 15
if ( count != 0 ) :
for j in range ( count - 1 ) :
2016-10-19 13:28:06 +02:00
va + = self . write_triangle ( triangle_index , index_table )
triangle_index + = 1
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
va + = self . write_triangle ( triangle_index , index_table )
2016-09-02 23:11:04 +02:00
return va
2016-10-15 12:17:33 +02:00
def get_meshes_file_path ( self , object_id , compressed = False ) :
2016-09-02 23:11:04 +02:00
index = self . filepath . rfind ( ' / ' )
2016-10-19 13:28:06 +02:00
mesh_fp = self . filepath [ : ( index + 1 ) ] + ' meshes/ '
2016-09-02 23:11:04 +02:00
if not os . path . exists ( mesh_fp ) :
os . makedirs ( mesh_fp )
2016-10-15 12:17:33 +02:00
ext = ' .zip ' if compressed else ' .arm '
return mesh_fp + object_id + ext
2016-09-02 23:11:04 +02:00
2016-10-15 12:17:33 +02:00
def get_greasepencils_file_path ( self , object_id , compressed = False ) :
2016-09-30 23:24:18 +02:00
index = self . filepath . rfind ( ' / ' )
2016-10-19 13:28:06 +02:00
gp_fp = self . filepath [ : ( index + 1 ) ] + ' greasepencils/ '
2016-09-30 23:24:18 +02:00
if not os . path . exists ( gp_fp ) :
os . makedirs ( gp_fp )
2016-10-15 12:17:33 +02:00
ext = ' .zip ' if compressed else ' .arm '
return gp_fp + object_id + ext
2016-09-30 23:24:18 +02:00
2016-09-02 23:11:04 +02:00
@staticmethod
2016-10-19 13:28:06 +02:00
def get_bobject_type ( bobject ) :
2016-09-02 23:11:04 +02:00
if bobject . type == " MESH " :
if len ( bobject . data . polygons ) != 0 :
2016-11-28 14:40:07 +01:00
return NodeTypeMesh
2016-09-02 23:11:04 +02:00
elif bobject . type == " FONT " :
2016-11-28 14:40:07 +01:00
return NodeTypeMesh
elif bobject . type == " META " : # Metaball
return NodeTypeMesh
2016-09-02 23:11:04 +02:00
elif bobject . type == " LAMP " :
2016-11-28 14:40:07 +01:00
return NodeTypeLamp
2016-09-02 23:11:04 +02:00
elif bobject . type == " CAMERA " :
2016-11-28 14:40:07 +01:00
return NodeTypeCamera
2016-09-02 23:11:04 +02:00
elif bobject . type == " SPEAKER " :
2016-11-28 14:40:07 +01:00
return NodeTypeSpeaker
return NodeTypeNode
2016-09-02 23:11:04 +02:00
@staticmethod
2016-10-19 13:28:06 +02:00
def get_shape_keys ( mesh ) :
2016-11-28 14:40:07 +01:00
if not hasattr ( mesh , ' shape_keys ' ) : # Metaball
return None
2016-10-19 13:28:06 +02:00
shape_keys = mesh . shape_keys
2016-11-28 14:40:07 +01:00
if shape_keys and len ( shape_keys . key_blocks ) > 1 :
2016-10-19 13:28:06 +02:00
return shape_keys
return None
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
def find_node ( self , name ) :
for bobject_ref in self . bobjectArray . items ( ) :
2016-11-21 16:49:32 +01:00
if bobject_ref [ 0 ] . name == name :
return bobject_ref
2016-10-19 13:28:06 +02:00
return None
2016-09-02 23:11:04 +02:00
@staticmethod
2016-10-19 13:28:06 +02:00
def classify_animation_curve ( fcurve ) :
linear_count = 0
bezier_count = 0
2016-09-02 23:11:04 +02:00
for key in fcurve . keyframe_points :
interp = key . interpolation
2016-11-21 16:49:32 +01:00
if interp == " LINEAR " :
2016-10-19 13:28:06 +02:00
linear_count + = 1
2016-11-21 16:49:32 +01:00
elif interp == " BEZIER " :
2016-10-19 13:28:06 +02:00
bezier_count + = 1
2016-09-02 23:11:04 +02:00
else :
2016-11-28 14:40:07 +01:00
return AnimationTypeSampled
2016-09-02 23:11:04 +02:00
2016-11-21 16:49:32 +01:00
if bezier_count == 0 :
2016-11-28 14:40:07 +01:00
return AnimationTypeLinear
2016-11-21 16:49:32 +01:00
elif linear_count == 0 :
2016-11-28 14:40:07 +01:00
return AnimationTypeBezier
return AnimationTypeSampled
2016-09-02 23:11:04 +02:00
@staticmethod
2016-10-19 13:28:06 +02:00
def animation_keys_different ( fcurve ) :
key_count = len ( fcurve . keyframe_points )
2016-11-21 16:49:32 +01:00
if key_count > 0 :
2016-09-02 23:11:04 +02:00
key1 = fcurve . keyframe_points [ 0 ] . co [ 1 ]
2016-10-19 13:28:06 +02:00
for i in range ( 1 , key_count ) :
2016-09-02 23:11:04 +02:00
key2 = fcurve . keyframe_points [ i ] . co [ 1 ]
2016-11-28 14:40:07 +01:00
if math . fabs ( key2 - key1 ) > ExportEpsilon :
2016-10-19 13:28:06 +02:00
return True
return False
2016-09-02 23:11:04 +02:00
@staticmethod
2016-10-19 13:28:06 +02:00
def animation_tangents_nonzero ( fcurve ) :
key_count = len ( fcurve . keyframe_points )
2016-11-21 16:49:32 +01:00
if key_count > 0 :
2016-09-02 23:11:04 +02:00
key = fcurve . keyframe_points [ 0 ] . co [ 1 ]
left = fcurve . keyframe_points [ 0 ] . handle_left [ 1 ]
right = fcurve . keyframe_points [ 0 ] . handle_right [ 1 ]
2016-11-28 14:40:07 +01:00
if ( ( math . fabs ( key - left ) > ExportEpsilon ) or ( math . fabs ( right - key ) > ExportEpsilon ) ) :
2016-10-19 13:28:06 +02:00
return True
for i in range ( 1 , key_count ) :
2016-09-02 23:11:04 +02:00
key = fcurve . keyframe_points [ i ] . co [ 1 ]
left = fcurve . keyframe_points [ i ] . handle_left [ 1 ]
right = fcurve . keyframe_points [ i ] . handle_right [ 1 ]
2016-11-28 14:40:07 +01:00
if ( ( math . fabs ( key - left ) > ExportEpsilon ) or ( math . fabs ( right - key ) > ExportEpsilon ) ) :
2016-10-19 13:28:06 +02:00
return True
return False
2016-09-02 23:11:04 +02:00
@staticmethod
2016-10-19 13:28:06 +02:00
def matrices_different ( m1 , m2 ) :
2016-09-02 23:11:04 +02:00
for i in range ( 4 ) :
for j in range ( 4 ) :
2016-11-28 14:40:07 +01:00
if math . fabs ( m1 [ i ] [ j ] - m2 [ i ] [ j ] ) > ExportEpsilon :
2016-10-19 13:28:06 +02:00
return True
return False
2016-09-02 23:11:04 +02:00
@staticmethod
2016-10-19 13:28:06 +02:00
def collect_bone_animation ( armature , name ) :
2016-09-02 23:11:04 +02:00
path = " pose.bones[ \" " + name + " \" ]. "
2016-10-19 13:28:06 +02:00
curve_array = [ ]
2016-09-02 23:11:04 +02:00
2016-11-21 16:49:32 +01:00
if armature . animation_data :
2016-09-02 23:11:04 +02:00
action = armature . animation_data . action
if ( action ) :
for fcurve in action . fcurves :
if ( fcurve . data_path . startswith ( path ) ) :
2016-10-19 13:28:06 +02:00
curve_array . append ( fcurve )
return curve_array
2016-09-02 23:11:04 +02:00
@staticmethod
2016-10-19 13:28:06 +02:00
def animation_present ( fcurve , kind ) :
2016-11-28 14:40:07 +01:00
if kind != AnimationTypeBezier :
2016-11-21 16:49:32 +01:00
return ArmoryExporter . animation_keys_different ( fcurve )
2016-10-19 13:28:06 +02:00
return ( ( ArmoryExporter . animation_keys_different ( fcurve ) ) or ( ArmoryExporter . animation_tangents_nonzero ( fcurve ) ) )
2016-09-02 23:11:04 +02:00
@staticmethod
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
# bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r
return tangent
@staticmethod
2016-10-19 13:28:06 +02:00
def deindex_mesh ( mesh , material_table ) :
2016-09-02 23:11:04 +02:00
# This function deindexes all vertex positions, colors, and texcoords.
# Three separate ExportVertex structures are created for each triangle.
vertexArray = mesh . vertices
2016-10-19 13:28:06 +02:00
export_vertex_array = [ ]
2016-09-02 23:11:04 +02:00
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
2016-10-19 13:28:06 +02:00
export_vertex_array . append ( exportVertex )
2016-09-02 23:11:04 +02:00
exportVertex = ExportVertex ( )
exportVertex . vertexIndex = k2
exportVertex . faceIndex = faceIndex
exportVertex . position = v2 . co
exportVertex . normal = v2 . normal if ( face . use_smooth ) else face . normal
2016-10-19 13:28:06 +02:00
export_vertex_array . append ( exportVertex )
2016-09-02 23:11:04 +02:00
exportVertex = ExportVertex ( )
exportVertex . vertexIndex = k3
exportVertex . faceIndex = faceIndex
exportVertex . position = v3 . co
exportVertex . normal = v3 . normal if ( face . use_smooth ) else face . normal
2016-10-19 13:28:06 +02:00
export_vertex_array . append ( exportVertex )
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
material_table . append ( face . material_index )
2016-09-02 23:11:04 +02:00
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
2016-10-19 13:28:06 +02:00
export_vertex_array . append ( exportVertex )
2016-09-02 23:11:04 +02:00
exportVertex = ExportVertex ( )
exportVertex . vertexIndex = k2
exportVertex . faceIndex = faceIndex
exportVertex . position = v2 . co
exportVertex . normal = v2 . normal if ( face . use_smooth ) else face . normal
2016-10-19 13:28:06 +02:00
export_vertex_array . append ( exportVertex )
2016-09-02 23:11:04 +02:00
exportVertex = ExportVertex ( )
exportVertex . vertexIndex = k3
exportVertex . faceIndex = faceIndex
exportVertex . position = v3 . co
exportVertex . normal = v3 . normal if ( face . use_smooth ) else face . normal
2016-10-19 13:28:06 +02:00
export_vertex_array . append ( exportVertex )
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
material_table . append ( face . material_index )
2016-09-02 23:11:04 +02:00
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 ]
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . color = cf . color1
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . color = cf . color2
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . color = cf . color3
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
if ( len ( face . vertices ) == 4 ) :
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . color = cf . color1
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . color = cf . color3
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . color = cf . color4
2016-09-02 23:11:04 +02:00
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-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . texcoord0 = [ tf . uv1 [ 0 ] , 1.0 - tf . uv1 [ 1 ] ] # Reverse TCY
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . texcoord0 = [ tf . uv2 [ 0 ] , 1.0 - tf . uv2 [ 1 ] ]
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . texcoord0 = [ tf . uv3 [ 0 ] , 1.0 - tf . uv3 [ 1 ] ]
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
if ( len ( face . vertices ) == 4 ) :
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . texcoord0 = [ tf . uv1 [ 0 ] , 1.0 - tf . uv1 [ 1 ] ]
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . texcoord0 = [ tf . uv3 [ 0 ] , 1.0 - tf . uv3 [ 1 ] ]
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . texcoord0 = [ tf . uv4 [ 0 ] , 1.0 - tf . uv4 [ 1 ] ]
2016-09-02 23:11:04 +02: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 ]
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . texcoord1 = tf . uv1
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . texcoord1 = tf . uv2
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . texcoord1 = tf . uv3
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
if ( len ( face . vertices ) == 4 ) :
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . texcoord1 = tf . uv1
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . texcoord1 = tf . uv3
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
2016-10-19 13:28:06 +02:00
export_vertex_array [ vertexIndex ] . texcoord1 = tf . uv4
2016-09-02 23:11:04 +02:00
vertexIndex + = 1
faceIndex + = 1
2016-10-19 13:28:06 +02:00
for ev in export_vertex_array :
2016-09-02 23:11:04 +02:00
ev . Hash ( )
2016-10-19 13:28:06 +02:00
return export_vertex_array
2016-09-02 23:11:04 +02:00
@staticmethod
2016-10-19 13:28:06 +02:00
def unify_vertices ( export_vertex_array , indexTable ) :
2016-09-02 23:11:04 +02: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.
2016-10-19 13:28:06 +02:00
bucketCount = len ( export_vertex_array ) >> 3
2016-09-02 23:11:04 +02:00
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 = [ ]
2016-10-19 13:28:06 +02:00
for i in range ( len ( export_vertex_array ) ) :
ev = export_vertex_array [ i ]
2016-09-02 23:11:04 +02:00
bucket = ev . hash & ( bucketCount - 1 )
index = - 1
for b in hashTable [ bucket ] :
2016-10-19 13:28:06 +02:00
if export_vertex_array [ b ] == ev :
2016-09-02 23:11:04 +02:00
index = b
break
if index < 0 :
indexTable . append ( len ( unifiedVertexArray ) )
unifiedVertexArray . append ( ev )
hashTable [ bucket ] . append ( i )
else :
indexTable . append ( indexTable [ index ] )
return unifiedVertexArray
2016-10-19 13:28:06 +02:00
def export_bone ( self , armature , bone , scene , o , action ) :
2016-09-02 23:11:04 +02:00
bobjectRef = self . bobjectArray . get ( bone )
if ( bobjectRef ) :
o [ ' type ' ] = structIdentifier [ bobjectRef [ " objectType " ] ]
o [ ' name ' ] = bobjectRef [ " structName " ]
2016-10-19 13:28:06 +02:00
self . export_bone_transform ( armature , bone , scene , o , action )
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
o [ ' children ' ] = [ ]
2016-09-02 23:11:04 +02:00
for subbobject in bone . children :
so = { }
2016-10-19 13:28:06 +02:00
self . export_bone ( armature , subbobject , scene , so , action )
2016-09-05 17:03:20 +02:00
o [ ' children ' ] . append ( so )
2016-09-02 23:11:04 +02:00
# Export any ordinary objects that are parented to this bone
boneSubbobjectArray = self . boneParentArray . get ( bone . name )
if ( boneSubbobjectArray ) :
poseBone = None
if ( not bone . use_relative_parent ) :
poseBone = armature . pose . bones . get ( bone . name )
for subbobject in boneSubbobjectArray :
2016-10-19 13:28:06 +02:00
self . export_object ( subbobject , scene , poseBone , o )
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
def export_object_sampled_animation ( self , bobject , scene , o ) :
2016-09-02 23:11:04 +02:00
# This function exports animation as full 4x4 matrices for each frame
currentFrame = scene . frame_current
currentSubframe = scene . frame_subframe
animationFlag = False
m1 = bobject . matrix_local . copy ( )
# Font in
for i in range ( self . beginFrame , self . endFrame ) :
scene . frame_set ( i )
m2 = bobject . matrix_local
2016-10-19 13:28:06 +02:00
if ( ArmoryExporter . matrices_different ( m1 , m2 ) ) :
2016-09-02 23:11:04 +02:00
animationFlag = True
break
# Font out
if ( animationFlag ) :
o [ ' animation ' ] = { }
tracko = { }
tracko [ ' target ' ] = " transform "
tracko [ ' time ' ] = { }
tracko [ ' time ' ] [ ' values ' ] = [ ]
for i in range ( self . beginFrame , self . endFrame ) :
tracko [ ' time ' ] [ ' values ' ] . append ( ( ( i - self . beginFrame ) * self . frameTime ) )
tracko [ ' time ' ] [ ' values ' ] . append ( ( self . endFrame * self . frameTime ) )
tracko [ ' value ' ] = { }
tracko [ ' value ' ] [ ' values ' ] = [ ]
for i in range ( self . beginFrame , self . endFrame ) :
scene . frame_set ( i )
2016-10-19 13:28:06 +02:00
tracko [ ' value ' ] [ ' values ' ] . append ( self . write_matrix ( bobject . matrix_local ) )
2016-09-02 23:11:04 +02:00
scene . frame_set ( self . endFrame )
2016-10-19 13:28:06 +02:00
tracko [ ' value ' ] [ ' values ' ] . append ( self . write_matrix ( bobject . matrix_local ) )
2016-09-02 23:11:04 +02:00
o [ ' animation ' ] [ ' tracks ' ] = [ tracko ]
scene . frame_set ( currentFrame , currentSubframe )
def get_action_framerange ( self , action ) :
2016-09-16 01:11:34 +02:00
begin_frame = int ( action . frame_range [ 0 ] ) # Action frames
2016-09-02 23:11:04 +02:00
end_frame = int ( action . frame_range [ 1 ] )
if self . beginFrame > begin_frame : # Cap frames to timeline bounds
begin_frame = self . beginFrame
2016-09-16 01:11:34 +02:00
if self . endFrame < end_frame :
2016-09-02 23:11:04 +02:00
end_frame = self . endFrame
return begin_frame , end_frame
2016-10-19 13:28:06 +02:00
def export_bone_sampled_animation ( self , poseBone , scene , o , action ) :
2016-09-02 23:11:04 +02:00
# This function exports bone animation as full 4x4 matrices for each frame.
currentFrame = scene . frame_current
currentSubframe = scene . frame_subframe
# Frame range
begin_frame , end_frame = self . get_action_framerange ( action )
animationFlag = False
m1 = poseBone . matrix . copy ( )
for i in range ( begin_frame , end_frame ) :
scene . frame_set ( i )
m2 = poseBone . matrix
2016-10-19 13:28:06 +02:00
if ( ArmoryExporter . matrices_different ( m1 , m2 ) ) :
2016-09-02 23:11:04 +02:00
animationFlag = True
break
if ( animationFlag ) :
o [ ' animation ' ] = { }
tracko = { }
tracko [ ' target ' ] = " transform "
tracko [ ' time ' ] = { }
tracko [ ' time ' ] [ ' values ' ] = [ ]
for i in range ( begin_frame , end_frame ) :
tracko [ ' time ' ] [ ' values ' ] . append ( ( ( i - begin_frame ) * self . frameTime ) )
tracko [ ' time ' ] [ ' values ' ] . append ( ( end_frame * self . frameTime ) )
tracko [ ' value ' ] = { }
tracko [ ' value ' ] [ ' values ' ] = [ ]
parent = poseBone . parent
if ( parent ) :
for i in range ( begin_frame , end_frame ) :
scene . frame_set ( i )
2016-10-19 13:28:06 +02:00
tracko [ ' value ' ] [ ' values ' ] . append ( self . write_matrix ( parent . matrix . inverted ( ) * poseBone . matrix ) )
2016-09-02 23:11:04 +02:00
scene . frame_set ( end_frame )
2016-10-19 13:28:06 +02:00
tracko [ ' value ' ] [ ' values ' ] . append ( self . write_matrix ( parent . matrix . inverted ( ) * poseBone . matrix ) )
2016-09-02 23:11:04 +02:00
else :
for i in range ( begin_frame , end_frame ) :
scene . frame_set ( i )
2016-10-19 13:28:06 +02:00
tracko [ ' value ' ] [ ' values ' ] . append ( self . write_matrix ( poseBone . matrix ) )
2016-09-02 23:11:04 +02:00
scene . frame_set ( end_frame )
2016-10-19 13:28:06 +02:00
tracko [ ' value ' ] [ ' values ' ] . append ( self . write_matrix ( poseBone . matrix ) )
2016-09-02 23:11:04 +02:00
o [ ' animation ' ] [ ' tracks ' ] = [ tracko ]
scene . frame_set ( currentFrame , currentSubframe )
2016-10-19 13:28:06 +02:00
def export_key_times ( self , fcurve ) :
2016-09-02 23:11:04 +02:00
keyo = { }
keyo [ ' values ' ] = [ ]
keyCount = len ( fcurve . keyframe_points )
for i in range ( keyCount ) :
time = fcurve . keyframe_points [ i ] . co [ 0 ] - self . beginFrame
keyo [ ' values ' ] . append ( time * self . frameTime )
return keyo
2016-10-19 13:28:06 +02:00
def export_key_time_control_points ( self , fcurve ) :
2016-09-02 23:11:04 +02:00
keyminuso = { }
keyminuso [ ' values ' ] = [ ]
keyCount = len ( fcurve . keyframe_points )
for i in range ( keyCount ) :
ctrl = fcurve . keyframe_points [ i ] . handle_left [ 0 ] - self . beginFrame
keyminuso [ ' values ' ] . append ( ctrl * self . frameTime )
keypluso = { }
keypluso [ ' values ' ] = [ ]
for i in range ( keyCount ) :
ctrl = fcurve . keyframe_points [ i ] . handle_right [ 0 ] - self . beginFrame
keypluso [ ' values ' ] . append ( ctrl * self . frameTime )
return keyminuso , keypluso
def ExportKeyValues ( self , fcurve ) :
keyo = { }
keyo [ ' values ' ] = [ ]
keyCount = len ( fcurve . keyframe_points )
for i in range ( keyCount ) :
value = fcurve . keyframe_points [ i ] . co [ 1 ]
keyo [ ' values ' ] . append ( value )
return keyo
2016-10-19 13:28:06 +02:00
def export_key_value_control_points ( self , fcurve ) :
2016-09-02 23:11:04 +02:00
keyminuso = { }
keyminuso [ ' values ' ] = [ ]
keyCount = len ( fcurve . keyframe_points )
for i in range ( keyCount ) :
ctrl = fcurve . keyframe_points [ i ] . handle_left [ 1 ]
keyminuso [ ' values ' ] . append ( ctrl )
keypluso = { }
keypluso [ ' values ' ] = [ ]
for i in range ( keyCount ) :
ctrl = fcurve . keyframe_points [ i ] . handle_right [ 1 ]
keypluso [ ' values ' ] . append ( ctrl )
return keypluso , keypluso
2016-10-19 13:28:06 +02:00
def export_animation_track ( self , fcurve , kind , target , newline ) :
2016-09-02 23:11:04 +02:00
# This function exports a single animation track. The curve types for the
# Time and Value structures are given by the kind parameter.
tracko = { }
tracko [ ' target ' ] = target
2016-11-28 14:40:07 +01:00
if ( kind != AnimationTypeBezier ) :
2016-10-19 13:28:06 +02:00
tracko [ ' time ' ] = self . export_key_times ( fcurve )
2016-09-02 23:11:04 +02:00
tracko [ ' value ' ] = self . ExportKeyValues ( fcurve )
else :
tracko [ ' curve ' ] = ' bezier '
2016-10-19 13:28:06 +02:00
tracko [ ' time ' ] = self . export_key_times ( fcurve )
tracko [ ' time_control_plus ' ] , tracko [ ' time_control_minus ' ] = self . export_key_time_control_points ( fcurve )
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
tracko [ ' value ' ] = self . ExportKeyValues ( fcurve )
tracko [ ' value_control_plus ' ] , tracko [ ' value_control_minus ' ] = self . export_key_value_control_points ( fcurve )
2016-09-02 23:11:04 +02:00
return tracko
2016-10-19 13:28:06 +02:00
def export_object_transform ( self , bobject , scene , o ) :
2016-09-02 23:11:04 +02:00
locAnimCurve = [ None , None , None ]
rotAnimCurve = [ None , None , None ]
sclAnimCurve = [ None , None , None ]
locAnimKind = [ 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 ]
locationAnimated = False
rotationAnimated = False
scaleAnimated = False
locAnimated = [ 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 = bobject . rotation_mode
sampledAnimation = ( ( ArmoryExporter . sampleAnimationFlag ) or ( mode == " QUATERNION " ) or ( mode == " AXIS_ANGLE " ) )
if ( ( not sampledAnimation ) and ( bobject . animation_data ) ) :
action = bobject . animation_data . action
if ( action ) :
for fcurve in action . fcurves :
2016-10-19 13:28:06 +02:00
kind = ArmoryExporter . classify_animation_curve ( fcurve )
2016-11-28 14:40:07 +01:00
if kind != AnimationTypeSampled :
2016-11-30 15:54:39 +01:00
if fcurve . data_path == " location " :
2016-09-02 23:11:04 +02:00
for i in range ( 3 ) :
if ( ( fcurve . array_index == i ) and ( not locAnimCurve [ i ] ) ) :
locAnimCurve [ i ] = fcurve
locAnimKind [ i ] = kind
2016-11-30 15:54:39 +01:00
if ArmoryExporter . animation_present ( fcurve , kind ) :
2016-09-02 23:11:04 +02:00
locAnimated [ i ] = True
2016-11-07 16:11:35 +01:00
elif fcurve . data_path == " delta_location " :
2016-09-02 23:11:04 +02:00
for i in range ( 3 ) :
if ( ( fcurve . array_index == i ) and ( not deltaPosAnimCurve [ i ] ) ) :
deltaPosAnimCurve [ i ] = fcurve
deltaPosAnimKind [ i ] = kind
2016-11-30 15:54:39 +01:00
if ArmoryExporter . animation_present ( fcurve , kind ) :
2016-09-02 23:11:04 +02:00
deltaPosAnimated [ i ] = True
2016-11-07 16:11:35 +01:00
elif fcurve . data_path == " rotation_euler " :
2016-09-02 23:11:04 +02:00
for i in range ( 3 ) :
if ( ( fcurve . array_index == i ) and ( not rotAnimCurve [ i ] ) ) :
rotAnimCurve [ i ] = fcurve
rotAnimKind [ i ] = kind
2016-11-30 15:54:39 +01:00
if ArmoryExporter . animation_present ( fcurve , kind ) :
2016-09-02 23:11:04 +02:00
rotAnimated [ i ] = True
2016-11-07 16:11:35 +01:00
elif fcurve . data_path == " delta_rotation_euler " :
2016-09-02 23:11:04 +02:00
for i in range ( 3 ) :
if ( ( fcurve . array_index == i ) and ( not deltaRotAnimCurve [ i ] ) ) :
deltaRotAnimCurve [ i ] = fcurve
deltaRotAnimKind [ i ] = kind
2016-11-30 15:54:39 +01:00
if ArmoryExporter . animation_present ( fcurve , kind ) :
2016-09-02 23:11:04 +02:00
deltaRotAnimated [ i ] = True
2016-11-07 16:11:35 +01:00
elif fcurve . data_path == " scale " :
2016-09-02 23:11:04 +02:00
for i in range ( 3 ) :
if ( ( fcurve . array_index == i ) and ( not sclAnimCurve [ i ] ) ) :
sclAnimCurve [ i ] = fcurve
sclAnimKind [ i ] = kind
2016-11-30 15:54:39 +01:00
if ArmoryExporter . animation_present ( fcurve , kind ) :
2016-09-02 23:11:04 +02:00
sclAnimated [ i ] = True
2016-11-07 16:11:35 +01:00
elif fcurve . data_path == " delta_scale " :
2016-09-02 23:11:04 +02:00
for i in range ( 3 ) :
if ( ( fcurve . array_index == i ) and ( not deltaSclAnimCurve [ i ] ) ) :
deltaSclAnimCurve [ i ] = fcurve
deltaSclAnimKind [ i ] = kind
2016-11-30 15:54:39 +01:00
if ArmoryExporter . animation_present ( fcurve , kind ) :
2016-09-02 23:11:04 +02: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
locationAnimated = locAnimated [ 0 ] | locAnimated [ 1 ] | locAnimated [ 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 locationAnimated ) 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 object transform as a single 4x4 matrix.
# We might still be exporting sampled animation below.
o [ ' transform ' ] = { }
2016-12-07 21:13:54 +01:00
if sampledAnimation :
2016-09-02 23:11:04 +02:00
o [ ' transform ' ] [ ' target ' ] = " transform "
2016-10-19 13:28:06 +02:00
o [ ' transform ' ] [ ' values ' ] = self . write_matrix ( bobject . matrix_local )
2016-09-02 23:11:04 +02:00
2016-11-07 16:11:35 +01:00
if sampledAnimation :
2016-10-19 13:28:06 +02:00
self . export_object_sampled_animation ( bobject , scene , o )
2016-09-02 23:11:04 +02:00
else :
structFlag = False
o [ ' transform ' ] = { }
2016-10-19 13:28:06 +02:00
o [ ' transform ' ] [ ' values ' ] = self . write_matrix ( bobject . matrix_local )
2016-09-02 23:11:04 +02:00
o [ ' animation_transforms ' ] = [ ]
deltaTranslation = bobject . delta_location
2016-11-07 16:11:35 +01:00
if deltaPositionAnimated :
2016-09-02 23:11:04 +02:00
# 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 ]
2016-11-28 14:40:07 +01:00
if ( ( deltaPosAnimated [ i ] ) or ( math . fabs ( pos ) > ExportEpsilon ) ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' translation_ ' + axisName [ i ]
animo [ ' name ' ] = deltaSubtranslationName [ i ]
animo [ ' value ' ] = pos
# 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
2016-11-28 14:40:07 +01:00
elif ( ( math . fabs ( deltaTranslation [ 0 ] ) > ExportEpsilon ) or ( math . fabs ( deltaTranslation [ 1 ] ) > ExportEpsilon ) or ( math . fabs ( deltaTranslation [ 2 ] ) > ExportEpsilon ) ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' translation '
2016-10-19 13:28:06 +02:00
animo [ ' values ' ] = self . write_vector3d ( deltaTranslation )
2016-09-02 23:11:04 +02:00
structFlag = True
translation = bobject . location
2016-11-07 16:11:35 +01:00
if locationAnimated :
2016-09-02 23:11:04 +02:00
# 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 ]
2016-11-28 14:40:07 +01:00
if ( ( locAnimated [ i ] ) or ( math . fabs ( pos ) > ExportEpsilon ) ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' translation_ ' + axisName [ i ]
animo [ ' name ' ] = subtranslationName [ i ]
animo [ ' value ' ] = pos
structFlag = True
2016-11-28 14:40:07 +01:00
elif ( ( math . fabs ( translation [ 0 ] ) > ExportEpsilon ) or ( math . fabs ( translation [ 1 ] ) > ExportEpsilon ) or ( math . fabs ( translation [ 2 ] ) > ExportEpsilon ) ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' translation '
2016-10-19 13:28:06 +02:00
animo [ ' values ' ] = self . write_vector3d ( translation )
2016-09-02 23:11:04 +02:00
structFlag = True
2016-11-07 16:11:35 +01:00
if deltaRotationAnimated :
2016-09-02 23:11:04 +02:00
# 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 = bobject . delta_rotation_euler [ axis ]
2016-11-28 14:40:07 +01:00
if ( ( deltaRotAnimated [ axis ] ) or ( math . fabs ( angle ) > ExportEpsilon ) ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' rotation_ ' + axisName [ axis ]
animo [ ' name ' ] = deltaSubrotationName [ axis ]
animo [ ' value ' ] = angle
structFlag = True
else :
# When the delta rotation is not animated, write it in the representation given by
# the object's current rotation mode. (There is no axis-angle delta rotation.)
2016-11-07 16:11:35 +01:00
if mode == " QUATERNION " :
2016-09-02 23:11:04 +02:00
quaternion = bobject . delta_rotation_quaternion
2016-11-28 14:40:07 +01:00
if ( ( math . fabs ( quaternion [ 0 ] - 1.0 ) > ExportEpsilon ) or ( math . fabs ( quaternion [ 1 ] ) > ExportEpsilon ) or ( math . fabs ( quaternion [ 2 ] ) > ExportEpsilon ) or ( math . fabs ( quaternion [ 3 ] ) > ExportEpsilon ) ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' rotation_quaternion '
animo [ ' values ' ] = self . WriteQuaternion ( quaternion )
structFlag = True
else :
for i in range ( 3 ) :
axis = ord ( mode [ 2 - i ] ) - 0x58
angle = bobject . delta_rotation_euler [ axis ]
2016-11-30 15:54:39 +01:00
if math . fabs ( angle ) > ExportEpsilon :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' rotation_ ' + axisName [ axis ]
animo [ ' value ' ] = angle
structFlag = True
2016-11-07 16:11:35 +01:00
if rotationAnimated :
2016-09-02 23:11:04 +02:00
# 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 = bobject . rotation_euler [ axis ]
2016-11-28 14:40:07 +01:00
if ( ( rotAnimated [ axis ] ) or ( math . fabs ( angle ) > ExportEpsilon ) ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' rotation_ ' + axisName [ axis ]
animo [ ' name ' ] = subrotationName [ axis ]
animo [ ' value ' ] = angle
structFlag = True
else :
# When the rotation is not animated, write it in the representation given by
# the object's current rotation mode.
if ( mode == " QUATERNION " ) :
quaternion = bobject . rotation_quaternion
2016-11-28 14:40:07 +01:00
if ( ( math . fabs ( quaternion [ 0 ] - 1.0 ) > ExportEpsilon ) or ( math . fabs ( quaternion [ 1 ] ) > ExportEpsilon ) or ( math . fabs ( quaternion [ 2 ] ) > ExportEpsilon ) or ( math . fabs ( quaternion [ 3 ] ) > ExportEpsilon ) ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' rotation_quaternion '
animo [ ' values ' ] = self . WriteQuaternion ( quaternion )
structFlag = True
elif ( mode == " AXIS_ANGLE " ) :
2016-11-28 14:40:07 +01:00
if ( math . fabs ( bobject . rotation_axis_angle [ 0 ] ) > ExportEpsilon ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' rotation_axis '
animo [ ' values ' ] = self . WriteVector4D ( bobject . rotation_axis_angle )
structFlag = True
else :
for i in range ( 3 ) :
axis = ord ( mode [ 2 - i ] ) - 0x58
angle = bobject . rotation_euler [ axis ]
2016-11-28 14:40:07 +01:00
if ( math . fabs ( angle ) > ExportEpsilon ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' rotation_ ' + axisName [ axis ]
animo [ ' value ' ] = angle
structFlag = True
deltaScale = bobject . 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 ]
2016-11-28 14:40:07 +01:00
if ( ( deltaSclAnimated [ i ] ) or ( math . fabs ( scl ) > ExportEpsilon ) ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' scale_ ' + axisName [ i ]
animo [ ' name ' ] = deltaSubscaleName [ i ]
animo [ ' value ' ] = scl
structFlag = True
2016-11-28 14:40:07 +01:00
elif ( ( math . fabs ( deltaScale [ 0 ] - 1.0 ) > ExportEpsilon ) or ( math . fabs ( deltaScale [ 1 ] - 1.0 ) > ExportEpsilon ) or ( math . fabs ( deltaScale [ 2 ] - 1.0 ) > ExportEpsilon ) ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' scale '
2016-10-19 13:28:06 +02:00
animo [ ' values ' ] = self . write_vector3d ( deltaScale )
2016-09-02 23:11:04 +02:00
structFlag = True
scale = bobject . 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 ]
2016-11-28 14:40:07 +01:00
if ( ( sclAnimated [ i ] ) or ( math . fabs ( scl ) > ExportEpsilon ) ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' scale_ ' + axisName [ i ]
animo [ ' name ' ] = subscaleName [ i ]
animo [ ' value ' ] = scl
structFlag = True
2016-11-28 14:40:07 +01:00
elif ( ( math . fabs ( scale [ 0 ] - 1.0 ) > ExportEpsilon ) or ( math . fabs ( scale [ 1 ] - 1.0 ) > ExportEpsilon ) or ( math . fabs ( scale [ 2 ] - 1.0 ) > ExportEpsilon ) ) :
2016-09-02 23:11:04 +02:00
animo = { }
o [ ' animation_transforms ' ] . append ( animo )
animo [ ' type ' ] = ' scale '
2016-10-19 13:28:06 +02:00
animo [ ' values ' ] = self . write_vector3d ( scale )
2016-09-02 23:11:04 +02:00
structFlag = True
# Export the animation tracks.
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 ' ] = [ ]
if ( locationAnimated ) :
for i in range ( 3 ) :
if ( locAnimated [ i ] ) :
2016-10-19 13:28:06 +02:00
tracko = self . export_animation_track ( locAnimCurve [ i ] , locAnimKind [ i ] , subtranslationName [ i ] , structFlag )
2016-09-02 23:11:04 +02:00
o [ ' animation ' ] [ ' tracks ' ] . append ( tracko )
structFlag = True
if ( rotationAnimated ) :
for i in range ( 3 ) :
if ( rotAnimated [ i ] ) :
2016-10-19 13:28:06 +02:00
tracko = self . export_animation_track ( rotAnimCurve [ i ] , rotAnimKind [ i ] , subrotationName [ i ] , structFlag )
2016-09-02 23:11:04 +02:00
o [ ' animation ' ] [ ' tracks ' ] . append ( tracko )
structFlag = True
if ( scaleAnimated ) :
for i in range ( 3 ) :
if ( sclAnimated [ i ] ) :
2016-10-19 13:28:06 +02:00
tracko = self . export_animation_track ( sclAnimCurve [ i ] , sclAnimKind [ i ] , subscaleName [ i ] , structFlag )
2016-09-02 23:11:04 +02:00
o [ ' animation ' ] [ ' tracks ' ] . append ( tracko )
structFlag = True
if ( deltaPositionAnimated ) :
for i in range ( 3 ) :
if ( deltaPosAnimated [ i ] ) :
2016-10-19 13:28:06 +02:00
tracko = self . export_animation_track ( deltaPosAnimCurve [ i ] , deltaPosAnimKind [ i ] , deltaSubtranslationName [ i ] , structFlag )
2016-09-02 23:11:04 +02:00
o [ ' animation ' ] [ ' tracks ' ] . append ( tracko )
structFlag = True
if ( deltaRotationAnimated ) :
for i in range ( 3 ) :
if ( deltaRotAnimated [ i ] ) :
2016-10-19 13:28:06 +02:00
tracko = self . export_animation_track ( deltaRotAnimCurve [ i ] , deltaRotAnimKind [ i ] , deltaSubrotationName [ i ] , structFlag )
2016-09-02 23:11:04 +02:00
o [ ' animation ' ] [ ' tracks ' ] . append ( tracko )
structFlag = True
if ( deltaScaleAnimated ) :
for i in range ( 3 ) :
if ( deltaSclAnimated [ i ] ) :
2016-10-19 13:28:06 +02:00
tracko = self . export_animation_track ( deltaSclAnimCurve [ i ] , deltaSclAnimKind [ i ] , deltaSubscaleName [ i ] , structFlag )
2016-09-02 23:11:04 +02:00
o [ ' animation ' ] [ ' tracks ' ] . append ( tracko )
structFlag = True
2016-10-19 13:28:06 +02:00
def process_bone ( self , bone ) :
2016-09-02 23:11:04 +02:00
if ( ( ArmoryExporter . exportAllFlag ) or ( bone . select ) ) :
2016-11-28 14:40:07 +01:00
self . bobjectArray [ bone ] = { " objectType " : NodeTypeBone , " structName " : bone . name }
2016-09-02 23:11:04 +02:00
for subbobject in bone . children :
2016-10-19 13:28:06 +02:00
self . process_bone ( subbobject )
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
def process_bobject ( self , bobject ) :
2016-09-02 23:11:04 +02:00
if ( ( ArmoryExporter . exportAllFlag ) or ( bobject . select ) ) :
2016-10-19 13:28:06 +02:00
btype = ArmoryExporter . get_bobject_type ( bobject )
2016-09-02 23:11:04 +02:00
2016-11-28 14:40:07 +01:00
if ArmoryExporter . option_mesh_only and btype != NodeTypeMesh :
2016-09-02 23:11:04 +02:00
return
2016-11-21 16:49:32 +01:00
self . bobjectArray [ bobject ] = { " objectType " : btype , " structName " : self . asset_name ( bobject ) }
2016-09-02 23:11:04 +02:00
if ( bobject . parent_type == " BONE " ) :
boneSubbobjectArray = self . boneParentArray . get ( bobject . parent_bone )
if ( boneSubbobjectArray ) :
boneSubbobjectArray . append ( bobject )
else :
self . boneParentArray [ bobject . parent_bone ] = [ bobject ]
if ( bobject . type == " ARMATURE " ) :
skeleton = bobject . data
if ( skeleton ) :
for bone in skeleton . bones :
if ( not bone . parent ) :
2016-10-19 13:28:06 +02:00
self . process_bone ( bone )
2016-09-02 23:11:04 +02:00
2016-09-30 23:24:18 +02:00
if bobject . type != ' MESH ' or bobject . instanced_children == False :
2016-09-02 23:11:04 +02:00
for subbobject in bobject . children :
2016-10-19 13:28:06 +02:00
self . process_bobject ( subbobject )
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
def process_skinned_meshes ( self ) :
2016-09-02 23:11:04 +02:00
for bobjectRef in self . bobjectArray . items ( ) :
2016-11-28 14:40:07 +01:00
if bobjectRef [ 1 ] [ " objectType " ] == NodeTypeMesh :
2016-09-02 23:11:04 +02:00
armature = bobjectRef [ 0 ] . find_armature ( )
2016-11-28 14:40:07 +01:00
if armature :
2016-09-02 23:11:04 +02:00
for bone in armature . data . bones :
2016-10-19 13:28:06 +02:00
boneRef = self . find_node ( bone . name )
2016-11-28 14:40:07 +01:00
if boneRef :
2016-09-02 23:11:04 +02:00
# If an object is used as a bone, then we force its type to be a bone.
2016-11-28 14:40:07 +01:00
boneRef [ 1 ] [ " objectType " ] = NodeTypeBone
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
def export_bone_transform ( self , armature , bone , scene , o , action ) :
curveArray = self . collect_bone_animation ( armature , bone . name )
2016-09-02 23:11:04 +02:00
animation = ( ( len ( curveArray ) != 0 ) or ( ArmoryExporter . sampleAnimationFlag ) )
transform = bone . matrix_local . copy ( )
parentBone = bone . parent
2016-11-28 14:40:07 +01:00
if parentBone :
2016-09-02 23:11:04 +02:00
transform = parentBone . matrix_local . inverted ( ) * transform
poseBone = armature . pose . bones . get ( bone . name )
2016-11-28 14:40:07 +01:00
if poseBone :
2016-09-02 23:11:04 +02:00
transform = poseBone . matrix . copy ( )
parentPoseBone = poseBone . parent
if ( parentPoseBone ) :
transform = parentPoseBone . matrix . inverted ( ) * transform
o [ ' transform ' ] = { }
2016-10-19 13:28:06 +02:00
o [ ' transform ' ] [ ' values ' ] = self . write_matrix ( transform )
2016-09-02 23:11:04 +02:00
2016-11-28 14:40:07 +01:00
if animation and poseBone :
2016-10-19 13:28:06 +02:00
self . export_bone_sampled_animation ( poseBone , scene , o , action )
2016-09-02 23:11:04 +02:00
2016-11-21 16:49:32 +01:00
def asset_name ( self , bdata ) :
s = bdata . name
# Append library name if linked
if bdata . library != None :
s + = ' _ ' + bdata . library . name
return s
2017-01-10 23:36:18 +01:00
def export_material_ref ( self , bobject , material , index , o ) :
if material == None : # Use default for empty mat slots
o [ ' material_refs ' ] . append ( ' __default ' )
self . defaultMaterialObjects . append ( bobject )
self . export_default_material = True
2016-09-02 23:11:04 +02:00
return
2016-11-21 16:49:32 +01:00
if not material in self . materialArray :
self . materialArray [ material ] = { " structName " : self . asset_name ( material ) }
2016-09-02 23:11:04 +02:00
o [ ' material_refs ' ] . append ( self . materialArray [ material ] [ " structName " ] )
2016-10-19 13:28:06 +02:00
def export_particle_system_ref ( self , psys , index , o ) :
2016-11-21 16:49:32 +01:00
if not psys . settings in self . particleSystemArray :
2016-09-02 23:11:04 +02:00
self . particleSystemArray [ psys . settings ] = { " structName " : psys . settings . name }
pref = { }
pref [ ' name ' ] = psys . name
pref [ ' seed ' ] = psys . seed
pref [ ' particle ' ] = self . particleSystemArray [ psys . settings ] [ " structName " ]
o [ ' particle_refs ' ] . append ( pref )
def get_viewport_view_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
def get_viewport_projection_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 . perspective_matrix
return None
2016-11-07 16:11:35 +01:00
def make_fake_omni_lamps ( self , o , bobject ) :
# Look down
o [ ' transform ' ] [ ' values ' ] = [ 1.0 , 0.0 , 0.0 , bobject . location . x , 0.0 , 1.0 , 0.0 , bobject . location . y , 0.0 , 0.0 , 1.0 , bobject . location . z , 0.0 , 0.0 , 0.0 , 1.0 ]
if not hasattr ( o , ' children ' ) :
o [ ' children ' ] = [ ]
# Make child lamps
for i in range ( 0 , 5 ) :
child_lamp = { }
child_lamp [ ' name ' ] = o [ ' name ' ] + ' __ ' + str ( i )
child_lamp [ ' data_ref ' ] = o [ ' data_ref ' ]
child_lamp [ ' type ' ] = ' lamp_object '
if i == 0 :
mat = [ 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , - 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 ]
elif i == 1 :
mat = [ 0.0 , 0.0 , - 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 ]
elif i == 2 :
mat = [ 0.0 , - 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 , 0.0 , - 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 ]
elif i == 3 :
mat = [ 0.0 , - 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , - 1.0 , 0.0 , 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 ]
elif i == 4 :
mat = [ - 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , - 1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 ]
child_lamp [ ' transform ' ] = { }
child_lamp [ ' transform ' ] [ ' values ' ] = mat
o [ ' children ' ] . append ( child_lamp )
2016-10-19 13:28:06 +02:00
def export_object ( self , bobject , scene , poseBone = None , parento = None ) :
2016-09-02 23:11:04 +02:00
# This function exports a single object in the scene and includes its name,
# object reference, material references (for meshes), and transform.
# Subobjects are then exported recursively.
2016-10-19 13:28:06 +02:00
if self . preprocess_object ( bobject ) == False :
2016-09-02 23:11:04 +02:00
return
bobjectRef = self . bobjectArray . get ( bobject )
2016-11-07 16:11:35 +01:00
if bobjectRef :
2016-09-02 23:11:04 +02:00
type = bobjectRef [ " objectType " ]
o = { }
o [ ' type ' ] = structIdentifier [ type ]
o [ ' name ' ] = bobjectRef [ " structName " ]
2016-11-08 16:32:32 +01:00
if bobject . hide_render or not bobject . game_visible :
2016-09-02 23:11:04 +02:00
o [ ' visible ' ] = False
2016-11-08 16:32:32 +01:00
if not bobject . cycles_visibility . camera :
o [ ' visible_mesh ' ] = False
if not bobject . cycles_visibility . shadow :
o [ ' visible_shadow ' ] = False
2016-09-02 23:11:04 +02:00
if bobject . spawn == False :
o [ ' spawn ' ] = False
if bobject . mobile == False :
p [ ' mobile ' ] = False
if ArmoryExporter . option_spawn_all_layers == False :
layer_found = False
for l in self . active_layers :
if bobject . layers [ l ] == True :
layer_found = True
break
if layer_found == False :
o [ ' spawn ' ] = False
2016-12-05 01:54:01 +01:00
# Export the object reference and material references
2016-09-02 23:11:04 +02:00
objref = bobject . data
2016-12-05 01:54:01 +01:00
# Lods
if bobject . type == ' MESH ' and len ( objref . my_lodlist ) > 0 :
o [ ' lods ' ] = [ ]
for l in objref . my_lodlist :
if l . enabled_prop == False :
continue
lod = { }
lod [ ' object_ref ' ] = l . name
lod [ ' screen_size ' ] = l . screen_size_prop
o [ ' lods ' ] . append ( lod )
if objref . lod_material :
o [ ' lod_material ' ] = True
2016-09-02 23:11:04 +02:00
2016-11-21 16:49:32 +01:00
# Remove unsafe chars from data names
2016-11-22 21:52:30 +01:00
if objref != None :
safe = armutils . safefilename ( objref . name )
if objref . name != safe :
objref . name = safe
2016-11-21 16:49:32 +01:00
2016-11-22 21:52:30 +01:00
objname = self . asset_name ( objref )
2016-11-21 16:49:32 +01:00
2016-11-28 14:40:07 +01:00
if type == NodeTypeMesh :
2016-11-07 16:11:35 +01:00
if not objref in self . meshArray :
2016-11-21 16:49:32 +01:00
self . meshArray [ objref ] = { " structName " : objname , " objectTable " : [ bobject ] }
2016-09-02 23:11:04 +02:00
else :
self . meshArray [ objref ] [ " objectTable " ] . append ( bobject )
2016-10-27 01:11:11 +02:00
oid = armutils . safe_filename ( self . meshArray [ objref ] [ " structName " ] )
2016-09-02 23:11:04 +02:00
if ArmoryExporter . option_mesh_per_file :
2016-10-15 12:17:33 +02:00
ext = ' '
if self . is_compress ( objref ) :
ext = ' .zip '
o [ ' data_ref ' ] = ' mesh_ ' + oid + ext + ' / ' + oid
2016-09-02 23:11:04 +02:00
else :
o [ ' data_ref ' ] = oid
o [ ' material_refs ' ] = [ ]
for i in range ( len ( bobject . material_slots ) ) :
2016-09-23 00:34:42 +02:00
if bobject . override_material : # Overwrite material slot
2016-09-02 23:11:04 +02:00
o [ ' material_refs ' ] . append ( bobject . override_material_name )
else : # Export assigned material
2017-01-10 23:36:18 +01:00
self . export_material_ref ( bobject , bobject . material_slots [ i ] . material , i , o )
2016-09-02 23:11:04 +02:00
# No material, mimick cycles and assign default
if len ( o [ ' material_refs ' ] ) == 0 :
o [ ' material_refs ' ] . append ( ' __default ' )
2016-12-20 00:39:18 +01:00
self . defaultMaterialObjects . append ( bobject )
2016-09-02 23:11:04 +02:00
self . export_default_material = True
2016-10-02 19:52:40 +02:00
num_psys = len ( bobject . particle_systems )
if num_psys > 0 :
o [ ' particle_refs ' ] = [ ]
for i in range ( 0 , num_psys ) :
2016-10-19 13:28:06 +02:00
self . export_particle_system_ref ( bobject . particle_systems [ i ] , i , o )
2016-09-02 23:11:04 +02:00
2017-01-07 13:50:55 +01:00
o [ ' dimensions ' ] = [ bobject . dimensions [ 0 ] , bobject . dimensions [ 1 ] , bobject . dimensions [ 2 ] ]
# Origin not in geometry center
2017-01-04 00:13:52 +01:00
if hasattr ( bobject . data , ' mesh_aabb ' ) :
2017-01-07 13:50:55 +01:00
dx = bobject . data . mesh_aabb [ 0 ] * bobject . scale [ 0 ]
dy = bobject . data . mesh_aabb [ 1 ] * bobject . scale [ 1 ]
dz = bobject . data . mesh_aabb [ 2 ] * bobject . scale [ 2 ]
if dx > o [ ' dimensions ' ] [ 0 ] :
o [ ' dimensions ' ] [ 0 ] = dx
if dy > o [ ' dimensions ' ] [ 1 ] :
o [ ' dimensions ' ] [ 1 ] = dy
if dz > o [ ' dimensions ' ] [ 2 ] :
o [ ' dimensions ' ] [ 2 ] = dz
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
#shapeKeys = ArmoryExporter.get_shape_keys(objref)
2016-09-02 23:11:04 +02:00
#if (shapeKeys):
# self.ExportMorphWeights(bobject, shapeKeys, scene, o)
# TODO
2016-11-28 14:40:07 +01:00
elif type == NodeTypeLamp :
2016-11-07 16:11:35 +01:00
if not objref in self . lampArray :
2016-11-21 16:49:32 +01:00
self . lampArray [ objref ] = { " structName " : objname , " objectTable " : [ bobject ] }
2016-09-02 23:11:04 +02:00
else :
self . lampArray [ objref ] [ " objectTable " ] . append ( bobject )
o [ ' data_ref ' ] = self . lampArray [ objref ] [ " structName " ]
2016-11-28 14:40:07 +01:00
elif type == NodeTypeCamera :
2017-01-03 12:20:46 +01:00
if ' spawn ' in o and o [ ' spawn ' ] == False :
self . camera_spawned = False
else :
2017-01-03 01:26:06 +01:00
self . camera_spawned = True
2016-11-07 16:11:35 +01:00
if not objref in self . cameraArray :
2016-11-21 16:49:32 +01:00
self . cameraArray [ objref ] = { " structName " : objname , " objectTable " : [ bobject ] }
2016-09-02 23:11:04 +02:00
else :
self . cameraArray [ objref ] [ " objectTable " ] . append ( bobject )
o [ ' data_ref ' ] = self . cameraArray [ objref ] [ " structName " ]
2016-11-28 14:40:07 +01:00
elif type == NodeTypeSpeaker :
2016-11-07 16:11:35 +01:00
if not objref in self . speakerArray :
2016-11-21 16:49:32 +01:00
self . speakerArray [ objref ] = { " structName " : objname , " objectTable " : [ bobject ] }
2016-09-02 23:11:04 +02:00
else :
self . speakerArray [ objref ] [ " objectTable " ] . append ( bobject )
o [ ' data_ref ' ] = self . speakerArray [ objref ] [ " structName " ]
2016-11-07 16:11:35 +01:00
if poseBone :
2016-09-02 23:11:04 +02:00
# If the object is parented to a bone and is not relative, then undo the bone's transform
o [ ' transform ' ] = { }
2016-10-19 13:28:06 +02:00
o [ ' transform ' ] [ ' values ' ] = self . write_matrix ( poseBone . matrix . inverted ( ) )
2016-09-02 23:11:04 +02:00
# Export the transform. If object is animated, then animation tracks are exported here
2016-10-19 13:28:06 +02:00
self . export_object_transform ( bobject , scene , o )
2016-09-02 23:11:04 +02:00
2016-11-07 16:11:35 +01:00
# 6 directional lamps
2016-11-28 14:40:07 +01:00
if type == NodeTypeLamp and objref . type == ' POINT ' and objref . lamp_omni_shadows :
2016-11-07 16:11:35 +01:00
self . make_fake_omni_lamps ( o , bobject )
2016-09-02 23:11:04 +02:00
# Viewport Camera - overwrite active camera matrix with viewport matrix
2016-11-28 14:40:07 +01:00
if type == NodeTypeCamera and bpy . data . worlds [ ' Arm ' ] . arm_play_viewport_camera and self . scene . camera != None and bobject . name == self . scene . camera . name :
2016-09-02 23:11:04 +02:00
viewport_matrix = self . get_viewport_view_matrix ( )
if viewport_matrix != None :
2016-10-19 13:28:06 +02:00
o [ ' transform ' ] [ ' values ' ] = self . write_matrix ( viewport_matrix . inverted ( ) )
2016-09-02 23:11:04 +02:00
# Do not apply parent matrix
o [ ' local_transform_only ' ] = True
2016-11-13 12:13:41 +01:00
if bobject . type == " ARMATURE " :
2016-09-02 23:11:04 +02:00
armdata = bobject . data # Armature data
2016-11-13 12:13:41 +01:00
action = None # Reference start action
if bobject . edit_actions_prop :
action = bpy . data . actions [ bobject . start_action_name_prop ]
elif bobject . animation_data != None : # Use default
action = bobject . animation_data . action
if armdata and action != None :
2016-11-21 16:49:32 +01:00
armatureid = self . asset_name ( armdata )
armatureid = armutils . safe_filename ( armatureid )
2016-10-15 12:17:33 +02:00
ext = ' '
if self . is_compress ( armdata ) :
ext = ' .zip '
o [ ' bones_ref ' ] = ' bones_ ' + armatureid + ' _ ' + action . name + ext
2016-09-02 23:11:04 +02:00
# Write bones
if armdata . edit_actions :
export_actions = [ ]
2016-09-16 01:11:34 +02:00
for t in armdata . my_actiontraitlist :
export_actions . append ( bpy . data . actions [ t . name ] )
2016-09-02 23:11:04 +02:00
else : # Use default
export_actions = [ action ]
for action in export_actions :
2016-09-05 17:03:20 +02:00
if armdata . animation_data == None :
continue
2016-09-02 23:11:04 +02:00
armdata . animation_data . action = action
2016-10-15 12:17:33 +02:00
fp = self . get_meshes_file_path ( ' bones_ ' + armatureid + ' _ ' + action . name , compressed = self . is_compress ( armdata ) )
2016-09-02 23:11:04 +02:00
assets . add ( fp )
2016-10-15 12:17:33 +02:00
if armdata . data_cached == False or not os . path . exists ( fp ) :
2016-09-02 23:11:04 +02:00
bones = [ ]
for bone in armdata . bones :
if ( not bone . parent ) :
boneo = { }
2016-10-19 13:28:06 +02:00
self . export_bone ( bobject , bone , scene , boneo , action )
2016-09-02 23:11:04 +02:00
#o.objects.append(boneo)
bones . append ( boneo )
# Save bones separately
bones_obj = { }
2016-09-16 01:11:34 +02:00
bones_obj [ ' objects ' ] = bones
2016-10-27 01:11:11 +02:00
armutils . write_arm ( fp , bones_obj )
2016-10-15 12:17:33 +02:00
armdata . data_cached = True
2016-09-02 23:11:04 +02:00
2016-11-07 16:11:35 +01:00
if parento == None :
2016-09-02 23:11:04 +02:00
self . output [ ' objects ' ] . append ( o )
else :
2016-09-05 17:03:20 +02:00
parento [ ' children ' ] . append ( o )
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
self . post_export_object ( bobject , o , type )
2016-09-02 23:11:04 +02:00
2016-10-02 19:52:40 +02:00
if not hasattr ( o , ' children ' ) and len ( bobject . children ) > 0 :
2016-09-05 17:03:20 +02:00
o [ ' children ' ] = [ ]
2016-09-02 23:11:04 +02:00
2016-09-30 23:24:18 +02:00
if bobject . type != ' MESH ' or bobject . instanced_children == False :
2016-09-02 23:11:04 +02:00
for subbobject in bobject . children :
if ( subbobject . parent_type != " BONE " ) :
2016-10-19 13:28:06 +02:00
self . export_object ( subbobject , scene , None , o )
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
def export_skin_quality ( self , bobject , armature , export_vertex_array , om ) :
2016-09-02 23:11:04 +02:00
# This function exports all skinning data, which includes the skeleton
# and per-vertex bone influence data
oskin = { }
om [ ' skin ' ] = oskin
# Write the skin bind pose transform
otrans = { }
oskin [ ' transform ' ] = otrans
2016-10-19 13:28:06 +02:00
otrans [ ' values ' ] = self . write_matrix ( bobject . matrix_world )
2016-09-02 23:11:04 +02:00
# Export the skeleton, which includes an array of bone object references
# and and array of per-bone bind pose transforms
oskel = { }
oskin [ ' skeleton ' ] = oskel
# Write the bone object reference array
oskel [ ' bone_ref_array ' ] = [ ]
boneArray = armature . data . bones
boneCount = len ( boneArray )
for i in range ( boneCount ) :
2016-10-19 13:28:06 +02:00
boneRef = self . find_node ( boneArray [ i ] . name )
2016-09-02 23:11:04 +02:00
if ( boneRef ) :
oskel [ ' bone_ref_array ' ] . append ( boneRef [ 1 ] [ " structName " ] )
else :
oskel [ ' bone_ref_array ' ] . append ( " null " )
# Write the bind pose transform array
oskel [ ' transforms ' ] = [ ]
for i in range ( boneCount ) :
2016-10-19 13:28:06 +02:00
oskel [ ' transforms ' ] . append ( self . write_matrix ( armature . matrix_world * boneArray [ i ] . matrix_local ) )
2016-09-02 23:11:04 +02:00
# Export the per-vertex bone influence data
groupRemap = [ ]
for group in bobject . 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 = [ ]
2016-10-19 13:28:06 +02:00
mesh_vertex_array = bobject . data . vertices
for ev in export_vertex_array :
2016-09-02 23:11:04 +02:00
boneCount = 0
totalWeight = 0.0
2016-10-19 13:28:06 +02:00
for element in mesh_vertex_array [ ev . vertexIndex ] . groups :
2016-09-02 23:11:04 +02:00
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.
oskin [ ' bone_count_array ' ] = boneCountArray
# Write the bone index array. The number of entries is the sum of the bone counts for all vertices.
oskin [ ' bone_index_array ' ] = boneIndexArray
# Write the bone weight array. The number of entries is the sum of the bone counts for all vertices.
oskin [ ' bone_weight_array ' ] = boneWeightArray
2016-10-19 13:28:06 +02:00
def export_skin_fast ( self , bobject , armature , vert_list , om ) :
2016-09-02 23:11:04 +02:00
oskin = { }
om [ ' skin ' ] = oskin
otrans = { }
oskin [ ' transform ' ] = otrans
2016-10-19 13:28:06 +02:00
otrans [ ' values ' ] = self . write_matrix ( bobject . matrix_world )
2016-09-02 23:11:04 +02:00
oskel = { }
oskin [ ' skeleton ' ] = oskel
oskel [ ' bone_ref_array ' ] = [ ]
boneArray = armature . data . bones
boneCount = len ( boneArray )
for i in range ( boneCount ) :
2016-10-19 13:28:06 +02:00
boneRef = self . find_node ( boneArray [ i ] . name )
2016-09-02 23:11:04 +02:00
if ( boneRef ) :
oskel [ ' bone_ref_array ' ] . append ( boneRef [ 1 ] [ " structName " ] )
else :
oskel [ ' bone_ref_array ' ] . append ( " null " )
oskel [ ' transforms ' ] = [ ]
for i in range ( boneCount ) :
2016-10-19 13:28:06 +02:00
oskel [ ' transforms ' ] . append ( self . write_matrix ( armature . matrix_world * boneArray [ i ] . matrix_local ) )
2016-09-02 23:11:04 +02:00
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
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_mesh ( self , bobject , fp , o ) :
# One mesh data per file
if ArmoryExporter . option_mesh_per_file :
mesh_obj = { }
mesh_obj [ ' mesh_datas ' ] = [ o ]
2016-10-27 01:11:11 +02:00
armutils . write_arm ( fp , mesh_obj )
2016-09-30 23:24:18 +02:00
bobject . data . mesh_cached = True
2016-11-28 14:40:07 +01:00
if bobject . type != ' FONT ' and bobject . type != ' META ' :
2016-09-30 23:24:18 +02:00
bobject . data . mesh_cached_verts = len ( bobject . data . vertices )
bobject . data . mesh_cached_edges = len ( bobject . data . edges )
2016-09-02 23:11:04 +02:00
else :
self . output [ ' mesh_datas ' ] . append ( o )
def export_mesh_fast ( self , exportMesh , bobject , 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
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
t0data [ i * 2 + 1 ] = 1.0 - vtx . uvs [ 0 ] . y # Reverse TCY
if num_uv_layers > 1 :
t1data [ i * 2 ] = vtx . uvs [ 1 ] . x
t1data [ i * 2 + 1 ] = vtx . uvs [ 1 ] . y
if num_colors > 0 :
cdata [ i * 3 ] = vtx . col [ 0 ]
cdata [ i * 3 + 1 ] = vtx . col [ 1 ]
cdata [ i * 3 + 2 ] = vtx . col [ 2 ]
# Output
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-12-17 23:48:18 +01:00
if self . get_export_uvs ( exportMesh ) == True and num_uv_layers > 0 :
2016-09-02 23:11:04 +02:00
ta = { }
ta [ ' attrib ' ] = " texcoord "
ta [ ' size ' ] = 2
ta [ ' values ' ] = t0data
om [ ' vertex_arrays ' ] . append ( ta )
if num_uv_layers > 1 :
2016-11-12 21:34:06 +01:00
ta1 = { }
ta1 [ ' attrib ' ] = " texcoord1 "
ta1 [ ' size ' ] = 2
ta1 [ ' values ' ] = t1data
om [ ' vertex_arrays ' ] . append ( ta1 )
2016-12-17 23:48:18 +01:00
if self . get_export_vcols ( exportMesh ) == True and num_colors > 0 :
2016-09-02 23:11:04 +02:00
ca = { }
ca [ ' attrib ' ] = " color "
ca [ ' size ' ] = 3
ca [ ' values ' ] = cdata
om [ ' vertex_arrays ' ] . append ( ca )
# 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 :
for i in range ( poly . loop_total - 2 ) :
prim + = ( indices [ - 1 ] , indices [ i ] , indices [ i + 1 ] )
# Write indices
om [ ' index_arrays ' ] = [ ]
for mat , prim in prims . items ( ) :
idata = [ 0 ] * len ( prim )
for i , v in enumerate ( prim ) :
idata [ i ] = v
ia = { }
ia [ ' size ' ] = 3
ia [ ' values ' ] = idata
ia [ ' material ' ] = 0
# Find material index for multi-mat mesh
if len ( exportMesh . materials ) > 1 :
for i in range ( 0 , len ( exportMesh . materials ) ) :
2017-01-10 23:36:18 +01:00
if ( exportMesh . materials [ i ] != None and mat == exportMesh . materials [ i ] . name ) or \
( exportMesh . materials [ i ] == None and mat == ' ' ) : # Default material for empty slots
2016-09-02 23:11:04 +02:00
ia [ ' material ' ] = i
break
om [ ' index_arrays ' ] . append ( ia )
# Make tangents
2016-12-17 23:48:18 +01:00
if self . get_export_tangents ( exportMesh ) == True and num_uv_layers > 0 :
2016-09-02 23:11:04 +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 )
return vert_list
2016-10-19 13:28:06 +02:00
def do_export_mesh ( self , objectRef , scene ) :
2016-09-02 23:11:04 +02:00
# This function exports a single mesh object
bobject = objectRef [ 1 ] [ " objectTable " ] [ 0 ]
2016-10-27 01:11:11 +02:00
oid = armutils . safe_filename ( objectRef [ 1 ] [ " structName " ] )
2016-09-02 23:11:04 +02:00
# Check if mesh is using instanced rendering
is_instanced , instance_offsets = self . object_process_instancing ( bobject , objectRef [ 1 ] [ " objectTable " ] )
2016-10-17 00:02:51 +02:00
2016-09-02 23:11:04 +02:00
# No export necessary
if ArmoryExporter . option_mesh_per_file :
2016-10-15 12:17:33 +02:00
fp = self . get_meshes_file_path ( ' mesh_ ' + oid , compressed = self . is_compress ( bobject . data ) )
2016-09-02 23:11:04 +02:00
assets . add ( fp )
if self . object_is_mesh_cached ( bobject ) == True and os . path . exists ( fp ) :
return
2016-11-21 16:49:32 +01:00
print ( ' Exporting mesh ' + self . asset_name ( bobject . data ) )
2016-09-08 14:08:31 +02:00
2016-09-02 23:11:04 +02:00
o = { }
o [ ' name ' ] = oid
mesh = objectRef [ 0 ]
structFlag = False ;
2016-11-28 14:40:07 +01:00
# Save the morph state if necessary
2016-09-02 23:11:04 +02:00
activeShapeKeyIndex = bobject . active_shape_key_index
showOnlyShapeKey = bobject . show_only_shape_key
currentMorphValue = [ ]
2016-10-19 13:28:06 +02:00
shapeKeys = ArmoryExporter . get_shape_keys ( mesh )
2016-11-28 14:40:07 +01:00
if shapeKeys :
2016-09-02 23:11:04 +02:00
bobject . active_shape_key_index = 0
bobject . show_only_shape_key = True
baseIndex = 0
relative = shapeKeys . use_relative
2016-11-28 14:40:07 +01:00
if relative :
2016-09-02 23:11:04 +02:00
morphCount = 0
baseName = shapeKeys . reference_key . name
for block in shapeKeys . key_blocks :
2016-11-28 14:40:07 +01:00
if block . name == baseName :
2016-09-02 23:11:04 +02:00
baseIndex = morphCount
break
morphCount + = 1
morphCount = 0
for block in shapeKeys . key_blocks :
currentMorphValue . append ( block . value )
block . value = 0.0
2016-11-28 14:40:07 +01:00
if block . name != " " :
2016-09-02 23:11:04 +02:00
# 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")
2016-10-19 13:28:06 +02:00
# TODO
2016-09-02 23:11:04 +02:00
structFlag = True
morphCount + = 1
shapeKeys . key_blocks [ 0 ] . value = 1.0
mesh . update ( )
om = { }
2016-11-30 01:33:26 +01:00
# Triangles is default
# om['primitive'] = "triangles"
2016-09-02 23:11:04 +02:00
armature = bobject . find_armature ( )
2016-11-28 14:40:07 +01:00
applyModifiers = not armature
2016-09-02 23:11:04 +02:00
# 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 = bobject . to_mesh ( scene , applyModifiers , " RENDER " , True , False )
2016-12-21 19:15:51 +01:00
if exportMesh == None :
print ( ' Armory Warning: ' + oid + ' was not exported ' )
return
2016-11-12 21:34:06 +01:00
if len ( exportMesh . uv_layers ) > 2 :
print ( ' Armory Warning: ' + oid + ' exceeds maximum of 2 UV Maps supported ' )
2016-09-02 23:11:04 +02:00
# Process meshes
if ArmoryExporter . option_optimize_mesh :
unifiedVertexArray = self . export_mesh_quality ( exportMesh , bobject , fp , o , om )
2016-11-28 14:40:07 +01:00
if armature :
2016-10-19 13:28:06 +02:00
self . export_skin_quality ( bobject , armature , unifiedVertexArray , om )
2016-09-02 23:11:04 +02:00
else :
vert_list = self . export_mesh_fast ( exportMesh , bobject , fp , o , om )
2016-11-28 14:40:07 +01:00
if armature :
2016-10-19 13:28:06 +02:00
self . export_skin_quality ( bobject , armature , vert_list , om )
# self.export_skin_fast(bobject, armature, vert_list, om)
2016-09-02 23:11:04 +02:00
2017-01-04 00:13:52 +01:00
# Save aabb
for va in om [ ' vertex_arrays ' ] :
if va [ ' attrib ' ] == ' position ' :
positions = va [ ' values ' ]
aabb_min = [ - 0.01 , - 0.01 , - 0.01 ]
aabb_max = [ 0.01 , 0.01 , 0.01 ]
i = 0
while i < len ( positions ) :
if positions [ i ] > aabb_max [ 0 ] :
aabb_max [ 0 ] = positions [ i ] ;
if positions [ i + 1 ] > aabb_max [ 1 ] :
aabb_max [ 1 ] = positions [ i + 1 ] ;
if positions [ i + 2 ] > aabb_max [ 2 ] :
aabb_max [ 2 ] = positions [ i + 2 ] ;
if positions [ i ] < aabb_min [ 0 ] :
aabb_min [ 0 ] = positions [ i ] ;
if positions [ i + 1 ] < aabb_min [ 1 ] :
aabb_min [ 1 ] = positions [ i + 1 ] ;
if positions [ i + 2 ] < aabb_min [ 2 ] :
aabb_min [ 2 ] = positions [ i + 2 ] ;
i + = 3 ;
bobject . data . mesh_aabb = [ abs ( aabb_min [ 0 ] ) + abs ( aabb_max [ 0 ] ) , abs ( aabb_min [ 1 ] ) + abs ( aabb_max [ 1 ] ) , abs ( aabb_min [ 2 ] ) + abs ( aabb_max [ 2 ] ) ]
break
2016-10-19 13:28:06 +02:00
# Restore the morph state
2016-11-28 14:40:07 +01:00
if shapeKeys :
2016-09-02 23:11:04 +02:00
bobject . active_shape_key_index = activeShapeKeyIndex
bobject . show_only_shape_key = showOnlyShapeKey
for m in range ( len ( currentMorphValue ) ) :
shapeKeys . key_blocks [ m ] . value = currentMorphValue [ m ]
mesh . update ( )
# Save offset data for instanced rendering
if is_instanced == True :
om [ ' instance_offsets ' ] = instance_offsets
# Export usage
2016-11-30 01:33:26 +01:00
if bobject . data . dynamic_usage :
om [ ' dynamic_usage ' ] = bobject . data . dynamic_usage
2016-09-02 23:11:04 +02:00
o [ ' mesh ' ] = om
self . write_mesh ( bobject , fp , o )
def export_mesh_quality ( self , exportMesh , bobject , fp , o , om ) :
# Triangulate mesh and remap vertices to eliminate duplicates.
2016-10-19 13:28:06 +02:00
material_table = [ ]
export_vertex_array = ArmoryExporter . deindex_mesh ( exportMesh , material_table )
triangleCount = len ( material_table )
2016-09-02 23:11:04 +02:00
indexTable = [ ]
2016-10-19 13:28:06 +02:00
unifiedVertexArray = ArmoryExporter . unify_vertices ( export_vertex_array , indexTable )
2016-09-02 23:11:04 +02:00
# Write the position array.
om [ ' vertex_arrays ' ] = [ ]
pa = { }
pa [ ' attrib ' ] = " position "
pa [ ' size ' ] = 3
2016-10-19 13:28:06 +02:00
pa [ ' values ' ] = self . write_vertex_array3d ( unifiedVertexArray , " position " )
2016-09-02 23:11:04 +02:00
om [ ' vertex_arrays ' ] . append ( pa )
# Write the normal array.
na = { }
na [ ' attrib ' ] = " normal "
na [ ' size ' ] = 3
2016-10-19 13:28:06 +02:00
na [ ' values ' ] = self . write_vertex_array3d ( unifiedVertexArray , " normal " )
2016-09-02 23:11:04 +02:00
om [ ' vertex_arrays ' ] . append ( na )
# Write the color array if it exists.
colorCount = len ( exportMesh . tessface_vertex_colors )
2016-12-17 23:48:18 +01:00
if self . get_export_vcols ( exportMesh ) == True and colorCount > 0 :
2016-09-02 23:11:04 +02:00
ca = { }
ca [ ' attrib ' ] = " color "
ca [ ' size ' ] = 3
2016-10-19 13:28:06 +02:00
ca [ ' values ' ] = self . write_vertex_array3d ( unifiedVertexArray , " color " )
2016-09-02 23:11:04 +02:00
om [ ' vertex_arrays ' ] . append ( ca )
# Write the texcoord arrays.
texcoordCount = len ( exportMesh . tessface_uv_textures )
2016-12-17 23:48:18 +01:00
if self . get_export_uvs ( exportMesh ) == True and texcoordCount > 0 :
2016-09-02 23:11:04 +02:00
ta = { }
ta [ ' attrib ' ] = " texcoord "
ta [ ' size ' ] = 2
2016-10-19 13:28:06 +02:00
ta [ ' values ' ] = self . write_vertex_array2d ( unifiedVertexArray , " texcoord0 " )
2016-09-02 23:11:04 +02:00
om [ ' vertex_arrays ' ] . append ( ta )
if ( texcoordCount > 1 ) :
ta2 = { }
ta2 [ ' attrib ' ] = " texcoord1 "
ta2 [ ' size ' ] = 2
2016-10-19 13:28:06 +02:00
ta2 [ ' values ' ] = self . write_vertex_array2d ( unifiedVertexArray , " texcoord1 " )
2016-09-02 23:11:04 +02:00
om [ ' vertex_arrays ' ] . append ( ta2 )
# If there are multiple morph targets, export them here.
# 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()
# bobject.active_shape_key_index = m
# morphMesh = bobject.to_mesh(scene, applyModifiers, "RENDER", True, False)
# # Write the morph target position array.
# 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.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.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)
2016-12-17 23:48:18 +01:00
# Write the index arrays
2016-09-02 23:11:04 +02:00
om [ ' index_arrays ' ] = [ ]
maxMaterialIndex = 0
2016-10-19 13:28:06 +02:00
for i in range ( len ( material_table ) ) :
index = material_table [ i ]
2016-12-17 23:48:18 +01:00
if index > maxMaterialIndex :
2016-09-02 23:11:04 +02:00
maxMaterialIndex = index
2016-12-17 23:48:18 +01:00
if maxMaterialIndex == 0 :
2016-09-02 23:11:04 +02:00
# There is only one material, so write a single index array.
ia = { }
ia [ ' size ' ] = 3
2016-10-19 13:28:06 +02:00
ia [ ' values ' ] = self . write_triangle_array ( triangleCount , indexTable )
2016-09-02 23:11:04 +02:00
ia [ ' material ' ] = 0
om [ ' index_arrays ' ] . append ( ia )
else :
# If there are multiple material indexes, then write a separate index array for each one.
materialTriangleCount = [ 0 for i in range ( maxMaterialIndex + 1 ) ]
2016-10-19 13:28:06 +02:00
for i in range ( len ( material_table ) ) :
materialTriangleCount [ material_table [ i ] ] + = 1
2016-09-02 23:11:04 +02:00
for m in range ( maxMaterialIndex + 1 ) :
if ( materialTriangleCount [ m ] != 0 ) :
materialIndexTable = [ ]
2016-10-19 13:28:06 +02:00
for i in range ( len ( material_table ) ) :
if ( material_table [ i ] == m ) :
2016-09-02 23:11:04 +02:00
k = i * 3
materialIndexTable . append ( indexTable [ k ] )
materialIndexTable . append ( indexTable [ k + 1 ] )
materialIndexTable . append ( indexTable [ k + 2 ] )
ia = { }
ia [ ' size ' ] = 3
2016-10-19 13:28:06 +02:00
ia [ ' values ' ] = self . write_triangle_array ( materialTriangleCount [ m ] , materialIndexTable )
2016-09-02 23:11:04 +02:00
ia [ ' material ' ] = m
om [ ' index_arrays ' ] . append ( ia )
# Export tangents
2016-12-17 23:48:18 +01:00
if self . get_export_tangents ( exportMesh ) == True and len ( exportMesh . uv_textures ) > 0 :
2016-09-02 23:11:04 +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 )
# Delete the new mesh that we made earlier
bpy . data . meshes . remove ( exportMesh )
return unifiedVertexArray
2016-10-19 13:28:06 +02:00
def export_lamp ( self , objectRef ) :
2016-09-02 23:11:04 +02:00
# This function exports a single lamp object
o = { }
o [ ' name ' ] = objectRef [ 1 ] [ " structName " ]
objref = objectRef [ 0 ]
objtype = objref . type
if objtype == ' SUN ' :
o [ ' type ' ] = ' sun '
elif objtype == ' POINT ' :
o [ ' type ' ] = ' point '
elif objtype == ' SPOT ' :
o [ ' type ' ] = ' spot '
o [ ' spot_size ' ] = math . cos ( objref . spot_size / 2 )
2016-11-03 19:07:16 +01:00
o [ ' spot_blend ' ] = objref . spot_blend / 10 # Cycles defaults to 0.15
2016-11-06 15:07:13 +01:00
elif objtype == ' AREA ' :
o [ ' type ' ] = ' area '
o [ ' size ' ] = objref . size
o [ ' size_y ' ] = objref . size_y
else : # Hemi
2016-09-02 23:11:04 +02:00
o [ ' type ' ] = ' sun '
o [ ' cast_shadow ' ] = objref . cycles . cast_shadow
o [ ' near_plane ' ] = objref . lamp_clip_start
o [ ' far_plane ' ] = objref . lamp_clip_end
o [ ' fov ' ] = objref . lamp_fov
o [ ' shadows_bias ' ] = objref . lamp_shadows_bias
if o [ ' type ' ] == ' sun ' : # Scale bias for ortho light matrix
o [ ' shadows_bias ' ] * = 10.0
2016-12-07 21:13:54 +01:00
if ( objtype == ' POINT ' or objtype == ' SPOT ' ) and objref . shadow_soft_size > 0.1 : # No sun for now
2016-12-01 18:28:07 +01:00
lamp_size = objref . shadow_soft_size
# Slightly higher bias for high sizes
if lamp_size > 1 :
o [ ' shadows_bias ' ] + = 0.00001 * lamp_size
2016-12-01 18:46:48 +01:00
o [ ' lamp_size ' ] = lamp_size * 10 # Match to Cycles
2016-09-02 23:11:04 +02:00
2016-11-05 20:57:04 +01:00
# Parse nodes
2016-12-20 01:39:16 +01:00
# Emission only for now
tree = objref . node_tree
for n in tree . nodes :
if n . type == ' EMISSION ' :
col = n . inputs [ 0 ] . default_value
o [ ' color ' ] = [ col [ 0 ] , col [ 1 ] , col [ 2 ] ]
o [ ' strength ' ] = n . inputs [ 1 ] . default_value
# Normalize point/spot strength
if o [ ' type ' ] == ' point ' or o [ ' type ' ] == ' spot ' :
o [ ' strength ' ] / = 1000.0
elif o [ ' type ' ] == ' area ' :
o [ ' strength ' ] / = 1000.0
# Texture test..
# if n.inputs[0].is_linked:
# color_node = nodes.find_node_by_link(tree, n, n.inputs[0])
# if color_node.type == 'TEX_IMAGE':
# o['color_texture'] = color_node.image.name
# make_texture(None, '', color_node, None)
## bpy.data.worlds['Arm'].world_defs += '_LampColTex'
break
2016-11-07 16:11:35 +01:00
# Fake omni shadows
if objref . lamp_omni_shadows :
2016-11-07 17:01:21 +01:00
o [ ' fov ' ] = 1.5708 # 90 deg
2016-11-07 16:11:35 +01:00
o [ ' strength ' ] / = 6
2016-09-02 23:11:04 +02:00
self . output [ ' lamp_datas ' ] . append ( o )
2016-10-19 13:28:06 +02:00
def export_camera ( self , objectRef ) :
2016-09-02 23:11:04 +02:00
# This function exports a single camera object
o = { }
o [ ' name ' ] = objectRef [ 1 ] [ " structName " ]
#self.WriteNodeTable(objectRef)
objref = objectRef [ 0 ]
o [ ' near_plane ' ] = objref . clip_start
o [ ' far_plane ' ] = objref . clip_end
o [ ' fov ' ] = objref . angle
# Viewport Camera - override fov for every camera for now
2016-11-05 14:12:36 +01:00
# if bpy.data.worlds['Arm'].arm_play_viewport_camera and ArmoryExporter.in_viewport:
# # Extract fov from projection
# mat = self.get_viewport_projection_matrix()
# if mat != None:
# yscale = mat[1][1]
# if yscale < 0:
# yscale *= -1 # Reverse
# fov = math.atan(1.0 / yscale) * 0.9
# o['fov'] = fov
2016-09-02 23:11:04 +02:00
if objref . type == ' PERSP ' :
o [ ' type ' ] = ' perspective '
else :
o [ ' type ' ] = ' orthographic '
if objref . is_mirror :
o [ ' is_mirror ' ] = True
o [ ' mirror_resolution_x ' ] = int ( objref . mirror_resolution_x )
o [ ' mirror_resolution_y ' ] = int ( objref . mirror_resolution_y )
o [ ' frustum_culling ' ] = objref . frustum_culling
2016-09-05 17:03:20 +02:00
o [ ' render_path ' ] = objref . renderpath_path + ' / ' + objref . renderpath_path # Same file name and id
2016-09-02 23:11:04 +02:00
if self . scene . world != None and ' Background ' in self . scene . world . node_tree . nodes : # TODO: parse node tree
background_node = self . scene . world . node_tree . nodes [ ' Background ' ]
col = background_node . inputs [ 0 ] . default_value
strength = background_node . inputs [ 1 ] . default_value
o [ ' clear_color ' ] = [ col [ 0 ] * strength , col [ 1 ] * strength , col [ 2 ] * strength , col [ 3 ] ]
else :
o [ ' clear_color ' ] = [ 0.0 , 0.0 , 0.0 , 1.0 ]
self . output [ ' camera_datas ' ] . append ( o )
2016-10-19 13:28:06 +02:00
def export_speaker ( self , objectRef ) :
2016-09-02 23:11:04 +02:00
# This function exports a single speaker object
o = { }
o [ ' name ' ] = objectRef [ 1 ] [ " structName " ]
objref = objectRef [ 0 ]
if objref . sound :
# Packed
if objref . sound . packed_file != None :
2016-10-27 01:11:11 +02:00
unpack_path = armutils . get_fp ( ) + ' /build/compiled/Assets/unpacked '
2016-09-02 23:11:04 +02:00
if not os . path . exists ( unpack_path ) :
os . makedirs ( unpack_path )
unpack_filepath = unpack_path + ' / ' + objref . sound . name
if os . path . isfile ( unpack_filepath ) == False or os . path . getsize ( unpack_filepath ) != objref . sound . packed_file . size :
with open ( unpack_filepath , ' wb ' ) as f :
f . write ( objref . sound . packed_file . data )
assets . add ( unpack_filepath )
# External
else :
2016-10-27 01:11:11 +02:00
assets . add ( armutils . safe_assetpath ( objref . sound . filepath ) ) # Link sound to assets
2016-09-02 23:11:04 +02:00
2016-10-27 01:11:11 +02:00
o [ ' sound ' ] = armutils . extract_filename ( objref . sound . filepath )
o [ ' sound ' ] = armutils . safe_filename ( o [ ' sound ' ] )
2016-09-02 23:11:04 +02:00
else :
o [ ' sound ' ] = ' '
2016-09-23 00:34:42 +02:00
o [ ' muted ' ] = objref . muted
o [ ' loop ' ] = objref . loop
o [ ' stream ' ] = objref . stream
o [ ' volume ' ] = objref . volume
o [ ' pitch ' ] = objref . pitch
o [ ' attenuation ' ] = objref . attenuation
2016-09-02 23:11:04 +02:00
self . output [ ' speaker_datas ' ] . append ( o )
2016-09-30 23:24:18 +02:00
def export_materials ( self ) :
2016-09-02 23:11:04 +02:00
# This function exports all of the materials used in the scene
for materialRef in self . materialArray . items ( ) :
material = materialRef [ 0 ]
# If the material is unlinked, material becomes None
if material == None :
continue
o = { }
o [ ' name ' ] = materialRef [ 1 ] [ " structName " ]
2016-11-08 16:32:32 +01:00
2016-12-17 15:34:43 +01:00
wrd = bpy . data . worlds [ ' Arm ' ]
if material . skip_context != ' ' :
o [ ' skip_context ' ] = material . skip_context
if material . override_cull or wrd . force_no_culling :
o [ ' override_context ' ] = { }
if wrd . force_no_culling :
o [ ' override_context ' ] [ ' cull_mode ' ] = ' none '
else :
o [ ' override_context ' ] [ ' cull_mode ' ] = material . override_cull_mode
o [ ' contexts ' ] = [ ]
2016-12-20 01:39:16 +01:00
sd = make_material . parse ( material , o , self . materialToObjectDict , ArmoryExporter . renderpath_id )
uv_export = False
tan_export = False
vcol_export = False
2017-01-04 00:13:52 +01:00
vs_str = ' '
2016-12-20 01:39:16 +01:00
for elem in sd [ ' vertex_structure ' ] :
2017-01-04 00:13:52 +01:00
if len ( vs_str ) > 0 :
vs_str + = ' , '
vs_str + = elem [ ' name ' ]
2016-12-20 01:39:16 +01:00
if elem [ ' name ' ] == ' tan ' :
tan_export = True
elif elem [ ' name ' ] == ' tex ' :
uv_export = True
elif elem [ ' name ' ] == ' col ' :
vcol_export = True
2017-01-04 00:13:52 +01:00
material . vertex_structure = vs_str
2016-12-20 01:39:16 +01:00
if ( material . export_tangents != tan_export ) or \
( material . export_uvs != uv_export ) or \
( material . export_vcols != vcol_export ) :
material . export_uvs = uv_export
material . export_vcols = vcol_export
material . export_tangents = tan_export
mat_users = self . materialToObjectDict [ material ]
for ob in mat_users :
ob . data . mesh_cached = False
2016-12-17 15:34:43 +01:00
2016-09-02 23:11:04 +02:00
self . output [ ' material_datas ' ] . append ( o )
2016-12-21 00:51:04 +01:00
material . is_cached = True
2016-09-02 23:11:04 +02:00
2016-12-20 00:39:18 +01:00
# Object with no material assigned in the scene
2016-09-02 23:11:04 +02:00
if self . export_default_material :
2016-12-20 00:39:18 +01:00
if not ' __default ' in bpy . data . materials :
mat = bpy . data . materials . new ( name = " __default " )
mat . use_nodes = True
o = { }
o [ ' name ' ] = mat . name
o [ ' contexts ' ] = [ ]
mat_users = dict ( )
mat_users [ mat ] = self . defaultMaterialObjects
2016-12-20 01:39:16 +01:00
make_material . parse ( mat , o , mat_users , ArmoryExporter . renderpath_id )
2016-12-20 00:39:18 +01:00
self . output [ ' material_datas ' ] . append ( o )
bpy . data . materials . remove ( mat )
2016-09-02 23:11:04 +02:00
2016-09-30 23:24:18 +02:00
def export_particle_systems ( self ) :
2016-09-02 23:11:04 +02:00
for particleRef in self . particleSystemArray . items ( ) :
o = { }
psettings = particleRef [ 0 ]
if psettings == None :
continue
o [ ' name ' ] = 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_datas ' ] . append ( o )
2016-09-30 23:24:18 +02:00
def export_worlds ( self ) :
2016-09-02 23:11:04 +02:00
worldRef = self . scene . world
if worldRef != None :
o = { }
w = worldRef
o [ ' name ' ] = w . name
2016-10-19 13:28:06 +02:00
self . post_export_world ( w , o )
2016-09-02 23:11:04 +02:00
self . output [ ' world_datas ' ] . append ( o )
2016-09-30 23:24:18 +02:00
def export_grease_pencils ( self ) :
2016-12-21 19:15:51 +01:00
return # Disabled for now
2016-09-30 23:24:18 +02:00
gpRef = self . scene . grease_pencil
2016-10-02 19:52:40 +02:00
if gpRef == None or self . scene . gp_export == False :
2016-09-30 23:24:18 +02:00
return
# ArmoryExporter.option_mesh_per_file # Currently always exports to separate file
2016-10-15 12:17:33 +02:00
fp = self . get_greasepencils_file_path ( ' greasepencil_ ' + gpRef . name , compressed = self . is_compress ( gpRef ) )
2016-09-30 23:24:18 +02:00
assets . add ( fp )
2016-10-15 12:17:33 +02:00
ext = ' '
if self . is_compress ( gpRef ) :
ext = ' .zip '
self . output [ ' grease_pencil_ref ' ] = ' greasepencil_ ' + gpRef . name + ext + ' / ' + gpRef . name
2016-09-30 23:24:18 +02:00
assets . add_shader_data ( ' build/compiled/ShaderDatas/grease_pencil/grease_pencil.arm ' )
assets . add_shader ( ' build/compiled/Shaders/grease_pencil/grease_pencil.frag.glsl ' )
assets . add_shader ( ' build/compiled/Shaders/grease_pencil/grease_pencil.vert.glsl ' )
2016-10-02 19:52:40 +02:00
assets . add_shader ( ' build/compiled/Shaders/grease_pencil/grease_pencil_shadows.frag.glsl ' )
assets . add_shader ( ' build/compiled/Shaders/grease_pencil/grease_pencil_shadows.vert.glsl ' )
2016-09-30 23:24:18 +02:00
if gpRef . data_cached == True and os . path . exists ( fp ) :
return
2016-10-19 13:28:06 +02:00
gpo = self . post_export_grease_pencil ( gpRef )
2016-09-30 23:24:18 +02:00
gp_obj = { }
gp_obj [ ' grease_pencil_datas ' ] = [ gpo ]
2016-10-27 01:11:11 +02:00
armutils . write_arm ( fp , gp_obj )
2016-09-30 23:24:18 +02:00
gpRef . data_cached = True
2016-10-15 12:17:33 +02:00
def is_compress ( self , obj ) :
return ArmoryExporter . compress_enabled and obj . data_compressed
2016-10-19 13:28:06 +02:00
def export_objects ( self , scene ) :
2016-09-02 23:11:04 +02:00
if not ArmoryExporter . option_mesh_only :
self . output [ ' lamp_datas ' ] = [ ]
self . output [ ' camera_datas ' ] = [ ]
self . output [ ' speaker_datas ' ] = [ ]
for objectRef in self . lampArray . items ( ) :
2016-10-19 13:28:06 +02:00
self . export_lamp ( objectRef )
2016-09-02 23:11:04 +02:00
for objectRef in self . cameraArray . items ( ) :
2016-10-19 13:28:06 +02:00
self . export_camera ( objectRef )
2016-09-02 23:11:04 +02:00
for objectRef in self . speakerArray . items ( ) :
2016-10-19 13:28:06 +02:00
self . export_speaker ( objectRef )
2016-09-02 23:11:04 +02:00
for objectRef in self . meshArray . items ( ) :
self . output [ ' mesh_datas ' ] = [ ] ;
2016-10-19 13:28:06 +02:00
self . do_export_mesh ( objectRef , scene )
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
def execute ( self , context , filepath ) :
2016-09-02 23:11:04 +02:00
profile_time = time . time ( )
self . output = { }
2016-10-19 13:28:06 +02:00
self . filepath = filepath
2016-09-02 23:11:04 +02:00
self . scene = context . scene
originalFrame = self . scene . frame_current
originalSubframe = self . scene . frame_subframe
self . restoreFrame = False
self . beginFrame = self . scene . frame_start
self . endFrame = self . scene . frame_end
self . frameTime = 1.0 / ( self . scene . render . fps_base * self . scene . render . fps )
self . bobjectArray = { }
self . meshArray = { }
self . lampArray = { }
self . cameraArray = { }
2017-01-03 01:26:06 +01:00
self . camera_spawned = False
2016-09-02 23:11:04 +02:00
self . speakerArray = { }
self . materialArray = { }
self . particleSystemArray = { }
self . worldArray = { } # Export all worlds
self . boneParentArray = { }
self . materialToObjectDict = dict ( )
2016-12-20 00:39:18 +01:00
self . defaultMaterialObjects = [ ]
2016-09-02 23:11:04 +02:00
self . materialToGameObjectDict = dict ( )
self . objectToGameObjectDict = dict ( )
self . uvprojectUsersArray = [ ] # For processing decals
self . export_default_material = False # If no material is assigned, provide default to mimick cycles
self . active_layers = [ ]
for i in range ( 0 , len ( self . scene . layers ) ) :
if self . scene . layers [ i ] == True :
self . active_layers . append ( i )
2016-10-19 13:28:06 +02:00
self . preprocess ( )
2016-09-02 23:11:04 +02:00
for bobject in self . scene . objects :
2017-01-04 00:13:52 +01:00
if not bobject . parent :
2016-10-19 13:28:06 +02:00
self . process_bobject ( bobject )
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
self . process_skinned_meshes ( )
2016-09-02 23:11:04 +02:00
2016-10-27 01:11:11 +02:00
self . output [ ' name ' ] = armutils . safe_filename ( self . scene . name )
2016-10-17 00:02:51 +02:00
if ( self . filepath . endswith ( ' .zip ' ) ) :
self . output [ ' name ' ] + = ' .zip '
2016-09-02 23:11:04 +02:00
self . output [ ' objects ' ] = [ ]
for object in self . scene . objects :
if ( not object . parent ) :
2016-10-19 13:28:06 +02:00
self . export_object ( object , self . scene )
2016-09-02 23:11:04 +02:00
if not ArmoryExporter . option_mesh_only :
if self . scene . camera != None :
self . output [ ' camera_ref ' ] = self . scene . camera . name
2016-09-08 14:08:31 +02:00
else :
2016-10-27 01:11:11 +02:00
if armutils . safe_filename ( self . scene . name ) == armutils . get_project_scene_name ( ) :
2016-09-08 14:08:31 +02:00
print ( ' Armory Warning: No camera found in active scene ' )
2016-09-02 23:11:04 +02:00
self . output [ ' material_datas ' ] = [ ]
2016-09-30 23:24:18 +02:00
self . export_materials ( )
2016-09-02 23:11:04 +02:00
2017-01-04 00:13:52 +01:00
# Ensure same vertex structure for object materials
for bobject in self . scene . objects :
if len ( bobject . material_slots ) > 1 :
2017-01-10 23:36:18 +01:00
mat = bobject . material_slots [ 0 ] . material
if mat == None :
continue
vs = mat . vertex_structure
2017-01-04 00:13:52 +01:00
for i in range ( len ( bobject . material_slots ) ) :
2017-01-10 23:36:18 +01:00
nmat = bobject . material_slots [ i ] . material
if nmat == None :
continue
if vs != nmat . vertex_structure :
2017-01-04 00:13:52 +01:00
log . warn ( ' Object ' + bobject . name + ' - unable to bind materials to vertex data, please separate object by material for now (select object - edit mode - P - By Material) ' )
break
2016-09-02 23:11:04 +02:00
self . output [ ' particle_datas ' ] = [ ]
2016-09-30 23:24:18 +02:00
self . export_particle_systems ( )
2016-09-02 23:11:04 +02:00
self . output [ ' world_datas ' ] = [ ]
2016-09-30 23:24:18 +02:00
self . export_worlds ( )
self . output [ ' grease_pencil_datas ' ] = [ ]
self . export_grease_pencils ( )
2016-09-02 23:11:04 +02:00
if self . scene . world != None :
self . output [ ' world_ref ' ] = self . scene . world . name
self . output [ ' gravity ' ] = [ self . scene . gravity [ 0 ] , self . scene . gravity [ 1 ] , self . scene . gravity [ 2 ] ]
2016-09-03 13:30:52 +02:00
# Scene root traits
2016-10-17 00:02:51 +02:00
if bpy . data . worlds [ ' Arm ' ] . arm_physics != ' Disabled ' :
2016-12-07 02:01:42 +01:00
if not ' traits ' in self . output :
self . output [ ' traits ' ] = [ ]
2016-09-03 13:30:52 +02:00
x = { }
x [ ' type ' ] = ' Script '
x [ ' class_name ' ] = ' armory.trait.internal.PhysicsWorld '
self . output [ ' traits ' ] . append ( x )
2016-12-07 02:01:42 +01:00
if bpy . data . worlds [ ' Arm ' ] . arm_navigation != ' Disabled ' :
if not ' traits ' in self . output :
self . output [ ' traits ' ] = [ ]
x = { }
x [ ' type ' ] = ' Script '
x [ ' class_name ' ] = ' armory.trait.internal.Navigation '
self . output [ ' traits ' ] . append ( x )
2016-09-03 13:30:52 +02:00
2016-10-19 13:28:06 +02:00
self . export_objects ( self . scene )
2016-09-02 23:11:04 +02:00
2017-01-03 01:26:06 +01:00
if not self . camera_spawned :
log . warn ( ' No camera found in active scene layers ' )
2016-10-19 13:28:06 +02:00
self . postprocess ( )
2016-09-02 23:11:04 +02:00
if ( self . restoreFrame ) :
self . scene . frame_set ( originalFrame , originalSubframe )
2016-09-14 11:49:32 +02:00
# Write embedded data references
if len ( assets . embedded_data ) > 0 :
self . output [ ' embedded_datas ' ] = [ ]
for file in assets . embedded_data :
self . output [ ' embedded_datas ' ] . append ( file )
2016-10-17 00:02:51 +02:00
# Write scene file
2016-10-27 01:11:11 +02:00
armutils . write_arm ( self . filepath , self . output )
2016-09-02 23:11:04 +02:00
print ( ' Scene built in ' + str ( time . time ( ) - profile_time ) )
return { ' FINISHED ' }
# Callbacks
def object_is_mesh_cached ( self , bobject ) :
2016-11-28 14:40:07 +01:00
if bobject . type == ' FONT ' or bobject . type == ' META ' : # No verts
2016-09-05 17:03:20 +02:00
return bobject . data . mesh_cached
2016-09-02 23:11:04 +02:00
if bobject . data . mesh_cached_verts != len ( bobject . data . vertices ) :
return False
if bobject . data . mesh_cached_edges != len ( bobject . data . edges ) :
return False
return bobject . data . mesh_cached
def get_export_tangents ( self , mesh ) :
for m in mesh . materials :
2016-11-24 23:24:55 +01:00
if m != None and m . export_tangents == True :
2016-09-02 23:11:04 +02:00
return True
return False
2016-12-17 23:48:18 +01:00
def get_export_vcols ( self , mesh ) :
for m in mesh . materials :
if m != None and m . export_vcols == True :
return True
return False
def get_export_uvs ( self , mesh ) :
for m in mesh . materials :
if m != None and m . export_uvs == True :
return True
return False
2016-09-02 23:11:04 +02:00
def object_process_instancing ( self , bobject , refs ) :
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-10-17 00:02:51 +02:00
# Child hidden
if sn . game_export == False or ( sn . hide_render and ArmoryExporter . option_export_hide_render == False ) :
continue
2016-09-02 23:11:04 +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 )
# 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])
break
return is_instanced , instance_offsets
2016-10-19 13:28:06 +02:00
def preprocess ( self ) :
ArmoryExporter . exportAllFlag = True
ArmoryExporter . export_physics = False # Indicates whether rigid body is exported
2016-12-07 10:37:08 +01:00
ArmoryExporter . export_navigation = False
2016-10-19 13:28:06 +02:00
if not hasattr ( ArmoryExporter , ' compress_enabled ' ) :
ArmoryExporter . compress_enabled = False
2016-11-05 14:12:36 +01:00
if not hasattr ( ArmoryExporter , ' in_viewport ' ) :
ArmoryExporter . in_viewport = False
2016-09-02 23:11:04 +02:00
ArmoryExporter . option_mesh_only = False
ArmoryExporter . option_mesh_per_file = True
2016-10-17 00:02:51 +02:00
ArmoryExporter . option_optimize_mesh = bpy . data . worlds [ ' Arm ' ] . arm_optimize_mesh
ArmoryExporter . option_export_hide_render = bpy . data . worlds [ ' Arm ' ] . arm_export_hide_render
ArmoryExporter . option_spawn_all_layers = bpy . data . worlds [ ' Arm ' ] . arm_spawn_all_layers
ArmoryExporter . option_minimize = bpy . data . worlds [ ' Arm ' ] . arm_minimize
ArmoryExporter . option_sample_animation = bpy . data . worlds [ ' Arm ' ] . arm_sampled_animation
2016-09-02 23:11:04 +02:00
ArmoryExporter . sampleAnimationFlag = ArmoryExporter . option_sample_animation
2016-09-05 17:03:20 +02:00
# Only one render path for scene for now
2016-09-02 23:11:04 +02:00
# Used for material shader export and khafile
if ( len ( bpy . data . cameras ) > 0 ) :
2016-09-05 17:03:20 +02:00
ArmoryExporter . renderpath_id = bpy . data . cameras [ 0 ] . renderpath_id
ArmoryExporter . renderpath_passes = bpy . data . cameras [ 0 ] . renderpath_passes . split ( ' _ ' )
2016-12-20 14:15:09 +01:00
ArmoryExporter . mesh_context = ' mesh '
ArmoryExporter . mesh_context_empty = ' '
ArmoryExporter . shadows_context = ' shadowmap '
ArmoryExporter . translucent_context = ' translucent '
ArmoryExporter . overlay_context = ' overlay '
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
def preprocess_object ( self , bobject ) : # Returns false if object should not be exported
2016-09-02 23:11:04 +02:00
export_object = True
# Disabled object
if bobject . game_export == False or ( bobject . hide_render and ArmoryExporter . option_export_hide_render == False ) :
return False
for m in bobject . modifiers :
if m . type == ' OCEAN ' :
# Do not export ocean mesh, just take specified constants
export_object = False
wrd = bpy . data . worlds [ ' Arm ' ]
wrd . generate_ocean = True
# Take position and bounds
wrd . generate_ocean_level = bobject . location . z
elif m . type == ' UV_PROJECT ' and m . show_render :
self . uvprojectUsersArray . append ( bobject )
return export_object
2016-10-19 13:28:06 +02:00
def postprocess ( self ) :
2016-09-02 23:11:04 +02:00
# Check uv project users
for bobject in self . uvprojectUsersArray :
for m in bobject . modifiers :
if m . type == ' UV_PROJECT ' :
# Mark all projectors as decals
for pnode in m . projectors :
o = self . objectToGameObjectDict [ bobject ]
po = self . objectToGameObjectDict [ pnode . object ]
po [ ' type ' ] = ' decal_object '
po [ ' material_refs ' ] = [ o [ ' material_refs ' ] [ 0 ] + ' _decal ' ] # Will fetch a proper context used in render path
break
2016-10-19 13:28:06 +02:00
def post_export_object ( self , bobject , o , type ) :
2016-10-12 17:52:27 +02:00
# Animation setup
2016-12-19 01:25:22 +01:00
if armutils . is_bone_animation_enabled ( bobject ) or armutils . is_object_animation_enabled ( bobject ) :
2016-10-12 17:52:27 +02:00
x = { }
if len ( bobject . my_cliptraitlist ) > 0 :
# Edit clips enabled
x [ ' names ' ] = [ ]
x [ ' starts ' ] = [ ]
x [ ' ends ' ] = [ ]
x [ ' speeds ' ] = [ ]
x [ ' loops ' ] = [ ]
x [ ' reflects ' ] = [ ]
for at in bobject . my_cliptraitlist :
if at . enabled_prop :
x [ ' names ' ] . append ( at . name )
x [ ' starts ' ] . append ( at . start_prop )
x [ ' ends ' ] . append ( at . end_prop )
x [ ' speeds ' ] . append ( at . speed_prop )
x [ ' loops ' ] . append ( at . loop_prop )
x [ ' reflects ' ] . append ( at . reflect_prop )
2016-11-30 15:54:39 +01:00
start_track = bobject . start_track_name_prop
if start_track == ' ' and len ( bobject . my_cliptraitlist ) > 0 : # Start track undefined
start_track = bobject . my_cliptraitlist [ 0 ] . name
x [ ' start_track ' ] = start_track
2016-10-12 17:52:27 +02:00
x [ ' max_bones ' ] = bpy . data . worlds [ ' Arm ' ] . generate_gpu_skin_max_bones
else :
# Export default clip, taking full action
2016-12-19 01:25:22 +01:00
if armutils . is_bone_animation_enabled ( bobject ) :
2016-10-12 17:52:27 +02:00
begin_frame , end_frame = self . get_action_framerange ( bobject . parent . animation_data . action )
else :
begin_frame , end_frame = self . get_action_framerange ( bobject . animation_data . action )
x [ ' start_track ' ] = ' default '
x [ ' names ' ] = [ ' default ' ]
x [ ' starts ' ] = [ begin_frame ]
x [ ' ends ' ] = [ end_frame ]
x [ ' speeds ' ] = [ 1.0 ]
x [ ' loops ' ] = [ True ]
x [ ' reflects ' ] = [ False ]
x [ ' max_bones ' ] = bpy . data . worlds [ ' Arm ' ] . generate_gpu_skin_max_bones
o [ ' animation_setup ' ] = x
2016-09-02 23:11:04 +02:00
# Export traits
o [ ' traits ' ] = [ ]
for t in bobject . my_traitlist :
if t . enabled_prop == False :
continue
x = { }
if t . type_prop == ' Logic Nodes ' and t . nodes_name_prop != ' ' :
x [ ' type ' ] = ' Script '
2016-12-07 10:19:45 +01:00
x [ ' class_name ' ] = bpy . data . worlds [ ' Arm ' ] . arm_project_package + ' .node. ' + armutils . safe_source_name ( t . nodes_name_prop )
2016-09-02 23:11:04 +02:00
elif t . type_prop == ' JS Script ' or t . type_prop == ' Python Script ' :
2016-10-12 17:52:27 +02:00
basename = t . jsscript_prop . split ( ' . ' ) [ 0 ]
2016-09-02 23:11:04 +02:00
x [ ' type ' ] = ' Script '
x [ ' class_name ' ] = ' armory.trait.internal.JSScript '
2016-10-27 01:11:11 +02:00
x [ ' parameters ' ] = [ armutils . safe_filename ( basename ) ]
scriptspath = armutils . get_fp ( ) + ' /build/compiled/scripts/ '
2016-09-02 23:11:04 +02:00
if not os . path . exists ( scriptspath ) :
os . makedirs ( scriptspath )
# Compile to JS
if t . type_prop == ' Python Script ' :
# Write py to file
2016-10-12 17:52:27 +02:00
basename_ext = basename + ' .py '
targetpath = scriptspath + basename_ext
2016-09-02 23:11:04 +02:00
with open ( targetpath , ' w ' ) as f :
f . write ( bpy . data . texts [ t . jsscript_prop ] . as_string ( ) )
2016-10-27 01:11:11 +02:00
sdk_path = armutils . get_sdk_path ( )
2016-12-21 00:51:04 +01:00
python_path = bpy . app . binary_path_python
2016-09-02 23:11:04 +02:00
cwd = os . getcwd ( )
os . chdir ( scriptspath )
# Disable minification for now, too slow
2016-10-12 17:52:27 +02:00
transproc = subprocess . Popen ( [ python_path + ' ' + sdk_path + ' /lib/transcrypt/__main__.py ' + ' ' + basename_ext + ' --nomin ' ] , shell = True )
2016-09-23 00:34:42 +02:00
transproc . wait ( )
if transproc . poll ( ) != 0 :
2016-10-19 13:28:06 +02:00
log . print_info ( ' Compiling ' + t . jsscript_prop + ' failed, check console ' )
2016-09-02 23:11:04 +02:00
os . chdir ( cwd )
# Compiled file
2016-11-01 17:26:21 +01:00
assets . add ( ' build/compiled/scripts/__javascript__/ ' + basename + ' .js ' )
2016-09-02 23:11:04 +02:00
else :
# Write js to file
assetpath = ' build/compiled/scripts/ ' + t . jsscript_prop + ' .js '
2016-10-27 01:11:11 +02:00
targetpath = armutils . get_fp ( ) + ' / ' + assetpath
2016-09-02 23:11:04 +02:00
with open ( targetpath , ' w ' ) as f :
f . write ( bpy . data . texts [ t . jsscript_prop ] . as_string ( ) )
assets . add ( assetpath )
else : # Haxe/Bundled Script
if t . class_name_prop == ' ' : # Empty class name, skip
continue
x [ ' type ' ] = ' Script '
if t . type_prop == ' Bundled Script ' :
trait_prefix = ' armory.trait. '
2016-12-07 02:01:42 +01:00
# TODO: temporary, export single mesh navmesh as obj
if t . class_name_prop == ' NavMesh ' and bobject . type == ' MESH ' and bpy . data . worlds [ ' Arm ' ] . arm_navigation != ' Disabled ' :
2016-12-07 10:37:08 +01:00
ArmoryExporter . export_navigation = True
2016-12-07 02:01:42 +01:00
nav_path = armutils . get_fp ( ) + ' /build/compiled/Assets/navigation '
if not os . path . exists ( nav_path ) :
os . makedirs ( nav_path )
nav_filepath = nav_path + ' /nav_ ' + bobject . data . name + ' .arm '
assets . add ( nav_filepath )
# TODO: Implement cache
#if os.path.isfile(nav_filepath) == False:
2016-12-08 14:30:39 +01:00
override = { ' selected_objects ' : [ bobject ] }
# bobject.scale.y *= -1
# mesh = obj.data
# for face in mesh.faces:
# face.v.reverse()
# bpy.ops.export_scene.obj(override, use_selection=True, filepath=nav_filepath, check_existing=False, use_normals=False, use_uvs=False, use_materials=False)
# bobject.scale.y *= -1
2016-12-07 02:01:42 +01:00
with open ( nav_filepath , ' w ' ) as f :
for v in bobject . data . vertices :
f . write ( " v %.4f " % ( v . co [ 0 ] * bobject . scale . x ) )
f . write ( " %.4f " % ( v . co [ 2 ] * bobject . scale . z ) )
2016-12-08 14:30:39 +01:00
f . write ( " %.4f \n " % ( v . co [ 1 ] * bobject . scale . y ) ) # Flipped
2016-12-07 02:01:42 +01:00
for p in bobject . data . polygons :
f . write ( " f " )
2016-12-08 14:30:39 +01:00
for i in reversed ( p . vertices ) : # Flipped normals
2016-12-07 02:01:42 +01:00
f . write ( " %d " % ( i + 1 ) )
f . write ( " \n " )
2016-09-02 23:11:04 +02:00
else :
2016-10-17 00:02:51 +02:00
trait_prefix = bpy . data . worlds [ ' Arm ' ] . arm_project_package + ' . '
2016-09-02 23:11:04 +02:00
x [ ' class_name ' ] = trait_prefix + t . class_name_prop
if len ( t . my_paramstraitlist ) > 0 :
x [ ' parameters ' ] = [ ]
for pt in t . my_paramstraitlist : # Append parameters
x [ ' parameters ' ] . append ( ast . literal_eval ( pt . name ) )
o [ ' traits ' ] . append ( x )
# Rigid body trait
if bobject . rigid_body != None :
2016-09-08 14:08:31 +02:00
ArmoryExporter . export_physics = True
2016-09-02 23:11:04 +02:00
rb = bobject . rigid_body
shape = 0 # BOX
if rb . collision_shape == ' SPHERE ' :
shape = 1
elif rb . collision_shape == ' CONVEX_HULL ' :
shape = 2
elif rb . collision_shape == ' MESH ' :
if rb . enabled :
shape = 3 # Mesh
else :
shape = 8 # Static Mesh
elif rb . collision_shape == ' CONE ' :
shape = 4
elif rb . collision_shape == ' CYLINDER ' :
shape = 5
elif rb . collision_shape == ' CAPSULE ' :
shape = 6
body_mass = 0
if rb . enabled :
body_mass = rb . mass
x = { }
x [ ' type ' ] = ' Script '
x [ ' class_name ' ] = ' armory.trait.internal.RigidBody '
x [ ' parameters ' ] = [ body_mass , shape , rb . friction ]
if rb . use_margin :
x [ ' parameters ' ] . append ( rb . collision_margin )
2016-12-08 14:30:39 +01:00
else :
x [ ' parameters ' ] . append ( 0.0 )
x [ ' parameters ' ] . append ( rb . linear_damping )
x [ ' parameters ' ] . append ( rb . angular_damping )
x [ ' parameters ' ] . append ( rb . type == ' PASSIVE ' )
2016-09-02 23:11:04 +02:00
o [ ' traits ' ] . append ( x )
2017-01-10 10:41:06 +01:00
# Soft bodies modifier
soft_type = - 1
soft_mod = None
for m in bobject . modifiers :
if m . type == ' CLOTH ' :
soft_type = 0
soft_mod = m
break
elif m . type == ' SOFT_BODY ' :
soft_type = 1 # Volume
soft_mod = m
break
if soft_type > = 0 :
2017-01-12 12:11:25 +01:00
ArmoryExporter . export_physics = True
2017-01-10 10:41:06 +01:00
assets . add_khafile_def ( ' arm_physics_soft ' )
cloth_trait = { }
cloth_trait [ ' type ' ] = ' Script '
cloth_trait [ ' class_name ' ] = ' armory.trait.internal.SoftBody '
if soft_type == 0 :
bend = soft_mod . settings . bending_stiffness
elif soft_type == 1 :
bend = ( soft_mod . settings . bend + 1.0 ) * 10
cloth_trait [ ' parameters ' ] = [ soft_type , bend , soft_mod . settings . mass , bobject . soft_body_margin ]
o [ ' traits ' ] . append ( cloth_trait )
2016-11-28 14:40:07 +01:00
if type == NodeTypeCamera :
2016-09-02 23:11:04 +02:00
# Debug console enabled, attach console overlay to each camera
2016-10-17 00:02:51 +02:00
if bpy . data . worlds [ ' Arm ' ] . arm_play_console :
2016-09-02 23:11:04 +02:00
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 to active camera if enabled
2016-10-17 00:02:51 +02:00
if self . scene . camera != None and bobject . name == self . scene . camera . name and bpy . data . worlds [ ' Arm ' ] . arm_play_viewport_camera and bpy . data . worlds [ ' Arm ' ] . arm_play_viewport_navigation == ' Walk ' :
2016-09-02 23:11:04 +02:00
navigation_trait = { }
navigation_trait [ ' type ' ] = ' Script '
navigation_trait [ ' class_name ' ] = ' armory.trait.WalkNavigation '
navigation_trait [ ' parameters ' ] = [ ]
o [ ' traits ' ] . append ( navigation_trait )
# Map objects to game objects
self . objectToGameObjectDict [ bobject ] = o
# Map objects to materials, can be used in later stages
for i in range ( len ( bobject . material_slots ) ) :
mat = bobject . material_slots [ i ] . material
if mat in self . materialToObjectDict :
self . materialToObjectDict [ mat ] . append ( bobject )
self . materialToGameObjectDict [ mat ] . append ( o )
else :
self . materialToObjectDict [ mat ] = [ bobject ]
self . materialToGameObjectDict [ mat ] = [ o ]
2016-10-02 19:52:40 +02:00
# Export constraints
if len ( bobject . constraints ) > 0 :
o [ ' constraints ' ] = [ ]
for constr in bobject . constraints :
2016-10-09 16:06:18 +02:00
if constr . mute :
continue
2016-10-02 19:52:40 +02:00
co = { }
co [ ' name ' ] = constr . name
co [ ' type ' ] = constr . type
if constr . type == ' COPY_LOCATION ' :
2016-11-24 18:58:12 +01:00
co [ ' target ' ] = constr . target . name
2016-10-02 19:52:40 +02:00
co [ ' use_x ' ] = constr . use_x
co [ ' use_y ' ] = constr . use_y
co [ ' use_z ' ] = constr . use_z
co [ ' invert_x ' ] = constr . invert_x
co [ ' invert_y ' ] = constr . invert_y
co [ ' invert_z ' ] = constr . invert_z
co [ ' use_offset ' ] = constr . use_offset
co [ ' influence ' ] = constr . influence
o [ ' constraints ' ] . append ( co )
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
def post_export_world ( self , world , o ) :
2016-10-02 19:52:40 +02:00
defs = bpy . data . worlds [ ' Arm ' ] . world_defs
bgcol = world . world_envtex_color
if ' _LDR ' in defs :
for i in range ( 0 , 3 ) :
bgcol [ i ] = pow ( bgcol [ i ] , 1.0 / 2.2 )
2016-10-27 01:11:11 +02:00
o [ ' background_color ' ] = armutils . color_to_int ( bgcol )
2016-10-02 19:52:40 +02:00
2016-10-27 01:11:11 +02:00
wmat_name = armutils . safe_filename ( world . name ) + ' _material '
2016-10-17 00:02:51 +02:00
o [ ' material_ref ' ] = wmat_name + ' / ' + wmat_name + ' /world '
2016-09-02 23:11:04 +02:00
o [ ' probes ' ] = [ ]
# Main probe
world_generate_radiance = False
generate_irradiance = True #'_EnvTex' in defs or '_EnvSky' in defs or '_EnvCon' in defs
2016-09-14 11:49:32 +02:00
disable_hdr = world . world_envtex_name . endswith ( ' .jpg ' )
2016-09-30 23:24:18 +02:00
radtex = world . world_envtex_name . rsplit ( ' . ' , 1 ) [ 0 ]
2016-10-02 19:52:40 +02:00
irrsharmonics = world . world_envtex_irr_name
2016-09-02 23:11:04 +02:00
# Radiance
if ' _EnvTex ' in defs :
world_generate_radiance = bpy . data . worlds [ ' Arm ' ] . generate_radiance
elif ' _EnvSky ' in defs and bpy . data . worlds [ ' Arm ' ] . generate_radiance_sky :
world_generate_radiance = bpy . data . worlds [ ' Arm ' ] . generate_radiance
radtex = ' hosek '
num_mips = world . world_envtex_num_mips
strength = world . world_envtex_strength
2016-09-30 23:24:18 +02:00
po = self . make_probe ( world . name , irrsharmonics , radtex , num_mips , strength , 1.0 , [ 0 , 0 , 0 ] , [ 0 , 0 , 0 ] , world_generate_radiance , generate_irradiance , disable_hdr )
2016-09-02 23:11:04 +02:00
o [ ' probes ' ] . append ( po )
if ' _EnvSky ' in defs :
# Sky data for probe
po [ ' sun_direction ' ] = list ( world . world_envtex_sun_direction )
po [ ' turbidity ' ] = world . world_envtex_turbidity
po [ ' ground_albedo ' ] = world . world_envtex_ground_albedo
# 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 ]
# Assume empty box of size 2, multiply by scale and dividy by 2 to get half extents
volume = [ 2 * volume_object . scale [ 0 ] / 2 , 2 * volume_object . scale [ 1 ] / 2 , 2 * volume_object . scale [ 2 ] / 2 ]
volume_center = [ volume_object . location [ 0 ] , volume_object . location [ 1 ] , volume_object . location [ 2 ] ]
disable_hdr = cam . probe_texture . endswith ( ' .jpg ' )
generate_radiance = cam . probe_generate_radiance
if world_generate_radiance == False :
generate_radiance = False
texture_path = ' // ' + cam . probe_texture
cam . probe_num_mips = write_probes . write_probes ( texture_path , disable_hdr , cam . probe_num_mips , generate_radiance = generate_radiance )
base_name = cam . probe_texture . rsplit ( ' . ' , 1 ) [ 0 ]
2016-09-14 11:49:32 +02:00
po = self . make_probe ( cam . name , base_name , base_name , cam . probe_num_mips , cam . probe_strength , cam . probe_blending , volume , volume_center , generate_radiance , generate_irradiance , disable_hdr )
2016-09-02 23:11:04 +02:00
o [ ' probes ' ] . append ( po )
2016-10-19 13:28:06 +02:00
def post_export_grease_pencil ( self , gp ) :
2016-09-30 23:24:18 +02:00
o = { }
o [ ' name ' ] = gp . name
o [ ' layers ' ] = [ ]
# originalFrame = self.scene.frame_current
for layer in gp . layers :
o [ ' layers ' ] . append ( self . export_grease_pencil_layer ( layer ) )
# self.scene.frame_set(originalFrame)
# o['palettes'] = []
# for palette in gp.palettes:
# o['palettes'].append(self.export_grease_pencil_palette(palette))
o [ ' shader ' ] = ' grease_pencil/grease_pencil '
return o
def export_grease_pencil_layer ( self , layer ) :
lo = { }
lo [ ' name ' ] = layer . info
lo [ ' opacity ' ] = layer . opacity
lo [ ' frames ' ] = [ ]
for frame in layer . frames :
if frame . frame_number > self . scene . frame_end :
break
# TODO: load GP frame data
# self.scene.frame_set(frame.frame_number)
lo [ ' frames ' ] . append ( self . export_grease_pencil_frame ( frame ) )
return lo
def export_grease_pencil_frame ( self , frame ) :
va = [ ]
cola = [ ]
colfilla = [ ]
indices = [ ]
2016-10-02 19:52:40 +02:00
num_stroke_points = [ ]
2016-09-30 23:24:18 +02:00
index_offset = 0
for stroke in frame . strokes :
for point in stroke . points :
va . append ( point . co [ 0 ] )
va . append ( point . co [ 1 ] )
va . append ( point . co [ 2 ] )
# TODO: store index to color pallete only, this is extremely wasteful
if stroke . color != None :
cola . append ( stroke . color . color [ 0 ] )
cola . append ( stroke . color . color [ 1 ] )
cola . append ( stroke . color . color [ 2 ] )
cola . append ( stroke . color . alpha )
colfilla . append ( stroke . color . fill_color [ 0 ] )
colfilla . append ( stroke . color . fill_color [ 1 ] )
colfilla . append ( stroke . color . fill_color [ 2 ] )
colfilla . append ( stroke . color . fill_alpha )
else :
cola . append ( 0.0 )
cola . append ( 0.0 )
cola . append ( 0.0 )
cola . append ( 0.0 )
colfilla . append ( 0.0 )
colfilla . append ( 0.0 )
colfilla . append ( 0.0 )
2016-10-02 19:52:40 +02:00
colfilla . append ( 0.0 )
2016-09-30 23:24:18 +02:00
for triangle in stroke . triangles :
indices . append ( triangle . v1 + index_offset )
indices . append ( triangle . v2 + index_offset )
indices . append ( triangle . v3 + index_offset )
2016-10-02 19:52:40 +02:00
num_stroke_points . append ( len ( stroke . points ) )
2016-09-30 23:24:18 +02:00
index_offset + = len ( stroke . points )
fo = { }
# TODO: merge into array of vertex arrays
fo [ ' vertex_array ' ] = { }
fo [ ' vertex_array ' ] [ ' attrib ' ] = ' pos '
fo [ ' vertex_array ' ] [ ' size ' ] = 3
fo [ ' vertex_array ' ] [ ' values ' ] = va
fo [ ' col_array ' ] = { }
fo [ ' col_array ' ] [ ' attrib ' ] = ' col '
fo [ ' col_array ' ] [ ' size ' ] = 4
fo [ ' col_array ' ] [ ' values ' ] = cola
fo [ ' colfill_array ' ] = { }
fo [ ' colfill_array ' ] [ ' attrib ' ] = ' colfill '
fo [ ' colfill_array ' ] [ ' size ' ] = 4
fo [ ' colfill_array ' ] [ ' values ' ] = colfilla
fo [ ' index_array ' ] = { }
fo [ ' index_array ' ] [ ' material ' ] = 0
fo [ ' index_array ' ] [ ' size ' ] = 3
fo [ ' index_array ' ] [ ' values ' ] = indices
2016-10-02 19:52:40 +02:00
fo [ ' num_stroke_points ' ] = num_stroke_points
2016-09-30 23:24:18 +02:00
fo [ ' frame_number ' ] = frame . frame_number
return fo
def export_grease_pencil_palette ( self , palette ) :
po = { }
po [ ' name ' ] = palette . info
po [ ' colors ' ] = [ ]
for color in palette . colors :
po [ ' colors ' ] . append ( self . export_grease_pencil_palette_color ( color ) )
return po
def export_grease_pencil_palette_color ( self , color ) :
co = { }
co [ ' name ' ] = color . name
co [ ' color ' ] = [ color . color [ 0 ] , color . color [ 1 ] , color . color [ 2 ] ]
co [ ' alpha ' ] = color . alpha
co [ ' fill_color ' ] = [ color . fill_color [ 0 ] , color . fill_color [ 1 ] , color . fill_color [ 2 ] ]
co [ ' fill_alpha ' ] = color . fill_alpha
return co
def make_probe ( self , id , irrsharmonics , radtex , mipmaps , strength , blending , volume , volume_center , generate_radiance , generate_irradiance , disable_hdr ) :
2016-09-02 23:11:04 +02:00
po = { }
po [ ' name ' ] = id
if generate_radiance :
po [ ' radiance ' ] = radtex + ' _radiance '
2016-09-14 11:49:32 +02:00
if disable_hdr :
po [ ' radiance ' ] + = ' .jpg '
else :
po [ ' radiance ' ] + = ' .hdr '
2016-09-02 23:11:04 +02:00
po [ ' radiance_mipmaps ' ] = mipmaps
if generate_irradiance :
2016-09-30 23:24:18 +02:00
po [ ' irradiance ' ] = irrsharmonics + ' _irradiance '
2016-09-02 23:11:04 +02:00
else :
po [ ' irradiance ' ] = ' ' # No irradiance data, fallback to default at runtime
po [ ' strength ' ] = strength
po [ ' blending ' ] = blending
po [ ' volume ' ] = volume
po [ ' volume_center ' ] = volume_center
return po