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-08-21 00:16:13 +02:00
import subprocess
2017-06-05 02:32:51 +02:00
import shutil
2017-03-15 12:30:14 +01:00
import arm . utils
import arm . write_probes as write_probes
import arm . assets as assets
import arm . log as log
import arm . material . make as make_material
import arm . material . mat_batch as mat_batch
import arm . nodes as nodes
import arm . make_renderer as make_renderer
import arm . make_renderpath as make_renderpath
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
2017-05-24 11:04:15 +02:00
NodeTypeDecal = 6
2016-11-28 14:40:07 +01:00
AnimationTypeSampled = 0
AnimationTypeLinear = 1
AnimationTypeBezier = 2
ExportEpsilon = 1.0e-6
2015-10-30 13:23:09 +01:00
2017-05-24 11:04:15 +02:00
structIdentifier = [ " object " , " bone_object " , " mesh_object " , " lamp_object " , " camera_object " , " speaker_object " , " decal_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 :
2017-04-11 23:21:42 +02:00
__slots__ = ( " co " , " normal " , " uvs " , " col " , " loop_indices " , " index " , " bone_weights " , " bone_indices " , " bone_count " , " vertex_index " )
2016-09-02 23:11:04 +02:00
def __init__ ( self , mesh , loop ) :
2017-04-11 23:21:42 +02:00
self . vertex_index = loop . vertex_index
2016-09-02 23:11:04 +02:00
i = loop . index
2017-04-11 23:21:42 +02:00
self . co = mesh . vertices [ self . vertex_index ] . co . freeze ( )
2016-09-02 23:11:04 +02:00
self . normal = loop . normal . freeze ( )
self . uvs = tuple ( layer . data [ i ] . uv . freeze ( ) for layer in mesh . uv_layers )
self . col = [ 0 , 0 , 0 ]
if len ( mesh . vertex_colors ) > 0 :
self . col = mesh . vertex_colors [ 0 ] . data [ i ] . color . freeze ( )
self . loop_indices = [ i ]
# Take the four most influential groups
2017-04-11 23:21:42 +02:00
# groups = sorted(mesh.vertices[self.vertex_index].groups, key=lambda group: group.weight, reverse=True)
2016-09-02 23:11:04 +02:00
# 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 :
2017-04-11 23:21:42 +02:00
__slots__ = ( " hash " , " vertex_index " , " face_index " , " position " , " normal " , " color " , " texcoord0 " , " texcoord1 " )
2016-09-02 23:11:04 +02:00
def __init__ ( self ) :
self . color = [ 1.0 , 1.0 , 1.0 ]
self . texcoord0 = [ 0.0 , 0.0 ]
self . texcoord1 = [ 0.0 , 0.0 ]
def __eq__ ( self , v ) :
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 ] ]
2017-05-17 17:06:52 +02:00
def write_va2d ( 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
2017-04-01 21:25:57 +02: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_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
2017-05-17 17:06:52 +02:00
def write_va3d ( 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
2017-04-01 21:25:57 +02: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_triangle ( triangle_index , index_table )
triangle_index + = 1
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 ]
2017-04-01 21:25:57 +02: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 ]
2017-04-01 21:25:57 +02: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
2017-02-28 22:26:35 +01:00
if action :
2016-09-02 23:11:04 +02:00
for fcurve in action . fcurves :
2017-02-28 22:26:35 +01:00
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 = [ ]
2017-04-11 23:21:42 +02:00
face_index = 0
2016-09-02 23:11:04 +02:00
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 ( )
2017-04-11 23:21:42 +02:00
exportVertex . vertex_index = k1
exportVertex . face_index = face_index
2016-09-02 23:11:04 +02:00
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 ( )
2017-04-11 23:21:42 +02:00
exportVertex . vertex_index = k2
exportVertex . face_index = face_index
2016-09-02 23:11:04 +02:00
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 ( )
2017-04-11 23:21:42 +02:00
exportVertex . vertex_index = k3
exportVertex . face_index = face_index
2016-09-02 23:11:04 +02:00
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
2017-02-15 09:56:48 +01:00
if len ( face . vertices ) == 4 :
2016-09-02 23:11:04 +02:00
k1 = face . vertices [ 0 ]
k2 = face . vertices [ 2 ]
k3 = face . vertices [ 3 ]
v1 = vertexArray [ k1 ]
v2 = vertexArray [ k2 ]
v3 = vertexArray [ k3 ]
exportVertex = ExportVertex ( )
2017-04-11 23:21:42 +02:00
exportVertex . vertex_index = k1
exportVertex . face_index = face_index
2016-09-02 23:11:04 +02:00
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 ( )
2017-04-11 23:21:42 +02:00
exportVertex . vertex_index = k2
exportVertex . face_index = face_index
2016-09-02 23:11:04 +02:00
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 ( )
2017-04-11 23:21:42 +02:00
exportVertex . vertex_index = k3
exportVertex . face_index = face_index
2016-09-02 23:11:04 +02:00
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
2017-04-11 23:21:42 +02:00
face_index + = 1
2016-09-02 23:11:04 +02:00
2017-05-17 17:06:52 +02:00
color_count = len ( mesh . tessface_vertex_colors )
if color_count > 0 :
2016-09-02 23:11:04 +02:00
colorFace = mesh . tessface_vertex_colors [ 0 ] . data
2017-04-11 23:21:42 +02:00
vertex_index = 0
face_index = 0
2016-09-02 23:11:04 +02:00
for face in mesh . tessfaces :
2017-04-11 23:21:42 +02:00
cf = colorFace [ face_index ]
export_vertex_array [ vertex_index ] . color = cf . color1
vertex_index + = 1
export_vertex_array [ vertex_index ] . color = cf . color2
vertex_index + = 1
export_vertex_array [ vertex_index ] . color = cf . color3
vertex_index + = 1
2016-09-02 23:11:04 +02:00
2017-02-15 09:56:48 +01:00
if len ( face . vertices ) == 4 :
2017-04-11 23:21:42 +02:00
export_vertex_array [ vertex_index ] . color = cf . color1
vertex_index + = 1
export_vertex_array [ vertex_index ] . color = cf . color3
vertex_index + = 1
export_vertex_array [ vertex_index ] . color = cf . color4
vertex_index + = 1
2016-09-02 23:11:04 +02:00
2017-04-11 23:21:42 +02:00
face_index + = 1
2016-09-02 23:11:04 +02:00
texcoordCount = len ( mesh . tessface_uv_textures )
2017-02-15 09:56:48 +01:00
if texcoordCount > 0 :
2016-09-02 23:11:04 +02:00
texcoordFace = mesh . tessface_uv_textures [ 0 ] . data
2017-04-11 23:21:42 +02:00
vertex_index = 0
face_index = 0
2016-09-02 23:11:04 +02:00
for face in mesh . tessfaces :
2017-04-11 23:21:42 +02:00
tf = texcoordFace [ face_index ]
export_vertex_array [ vertex_index ] . texcoord0 = [ tf . uv1 [ 0 ] , 1.0 - tf . uv1 [ 1 ] ] # Reverse TCY
vertex_index + = 1
export_vertex_array [ vertex_index ] . texcoord0 = [ tf . uv2 [ 0 ] , 1.0 - tf . uv2 [ 1 ] ]
vertex_index + = 1
export_vertex_array [ vertex_index ] . texcoord0 = [ tf . uv3 [ 0 ] , 1.0 - tf . uv3 [ 1 ] ]
vertex_index + = 1
2016-09-02 23:11:04 +02:00
2017-02-15 09:56:48 +01:00
if len ( face . vertices ) == 4 :
2017-04-11 23:21:42 +02:00
export_vertex_array [ vertex_index ] . texcoord0 = [ tf . uv1 [ 0 ] , 1.0 - tf . uv1 [ 1 ] ]
vertex_index + = 1
export_vertex_array [ vertex_index ] . texcoord0 = [ tf . uv3 [ 0 ] , 1.0 - tf . uv3 [ 1 ] ]
vertex_index + = 1
export_vertex_array [ vertex_index ] . texcoord0 = [ tf . uv4 [ 0 ] , 1.0 - tf . uv4 [ 1 ] ]
vertex_index + = 1
2016-09-02 23:11:04 +02:00
2017-04-11 23:21:42 +02:00
face_index + = 1
2016-09-02 23:11:04 +02:00
2017-02-15 09:56:48 +01:00
if texcoordCount > 1 :
2016-09-02 23:11:04 +02:00
texcoordFace = mesh . tessface_uv_textures [ 1 ] . data
2017-04-11 23:21:42 +02:00
vertex_index = 0
face_index = 0
2016-09-02 23:11:04 +02:00
for face in mesh . tessfaces :
2017-04-11 23:21:42 +02:00
tf = texcoordFace [ face_index ]
export_vertex_array [ vertex_index ] . texcoord1 = [ tf . uv1 [ 0 ] , 1.0 - tf . uv1 [ 1 ] ]
vertex_index + = 1
export_vertex_array [ vertex_index ] . texcoord1 = [ tf . uv2 [ 0 ] , 1.0 - tf . uv2 [ 1 ] ]
vertex_index + = 1
export_vertex_array [ vertex_index ] . texcoord1 = [ tf . uv3 [ 0 ] , 1.0 - tf . uv3 [ 1 ] ]
vertex_index + = 1
2016-09-02 23:11:04 +02:00
2017-02-15 09:56:48 +01:00
if len ( face . vertices ) == 4 :
2017-04-11 23:21:42 +02:00
export_vertex_array [ vertex_index ] . texcoord1 = [ tf . uv1 [ 0 ] , 1.0 - tf . uv1 [ 1 ] ]
vertex_index + = 1
export_vertex_array [ vertex_index ] . texcoord1 = [ tf . uv3 [ 0 ] , 1.0 - tf . uv3 [ 1 ] ]
vertex_index + = 1
export_vertex_array [ vertex_index ] . texcoord1 = [ tf . uv4 [ 0 ] , 1.0 - tf . uv4 [ 1 ] ]
vertex_index + = 1
2016-09-02 23:11:04 +02:00
2017-04-11 23:21:42 +02:00
face_index + = 1
2016-09-02 23:11:04 +02:00
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
2017-02-15 09:56:48 +01:00
if bucketCount > 1 :
2016-09-02 23:11:04 +02:00
# Round down to nearest power of two.
while True :
count = bucketCount & ( bucketCount - 1 )
2017-04-01 21:25:57 +02:00
if count == 0 :
2016-09-02 23:11:04 +02:00
break
bucketCount = count
else :
bucketCount = 1
hashTable = [ [ ] for i in range ( bucketCount ) ]
2017-05-17 17:06:52 +02:00
unifiedVA = [ ]
2016-09-02 23:11:04 +02:00
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 :
2017-05-17 17:06:52 +02:00
indexTable . append ( len ( unifiedVA ) )
unifiedVA . append ( ev )
2016-09-02 23:11:04 +02:00
hashTable [ bucket ] . append ( i )
else :
indexTable . append ( indexTable [ index ] )
2017-05-17 17:06:52 +02:00
return unifiedVA
2016-09-02 23:11:04 +02:00
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 )
2017-02-15 09:56:48 +01:00
if bobjectRef :
2016-09-02 23:11:04 +02:00
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 )
2017-02-15 09:56:48 +01:00
if boneSubbobjectArray :
2016-09-02 23:11:04 +02:00
poseBone = None
2017-02-28 22:26:35 +01:00
if not bone . use_relative_parent :
2016-09-02 23:11:04 +02:00
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
2017-02-15 09:56:48 +01:00
if ArmoryExporter . matrices_different ( m1 , m2 ) :
2016-09-02 23:11:04 +02:00
animationFlag = True
break
# Font out
2017-02-15 09:56:48 +01:00
if animationFlag :
2016-09-02 23:11:04 +02:00
o [ ' animation ' ] = { }
tracko = { }
tracko [ ' target ' ] = " transform "
2017-03-16 14:53:43 +01:00
tracko [ ' times ' ] = [ ]
2016-09-02 23:11:04 +02:00
for i in range ( self . beginFrame , self . endFrame ) :
2017-03-16 14:53:43 +01:00
tracko [ ' times ' ] . append ( ( ( i - self . beginFrame ) * self . frameTime ) )
2016-09-02 23:11:04 +02:00
2017-03-16 14:53:43 +01:00
tracko [ ' times ' ] . append ( ( self . endFrame * self . frameTime ) )
2016-09-02 23:11:04 +02:00
2017-03-16 14:53:43 +01:00
tracko [ ' values ' ] = [ ]
2016-09-02 23:11:04 +02:00
for i in range ( self . beginFrame , self . endFrame ) :
scene . frame_set ( i )
2017-03-16 14:53:43 +01:00
tracko [ ' values ' ] + = self . write_matrix ( bobject . matrix_local ) # Continuos array of matrix transforms
2016-09-02 23:11:04 +02:00
scene . frame_set ( self . endFrame )
2017-03-16 14:53:43 +01:00
tracko [ ' values ' ] + = 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
2017-04-01 21:25:57 +02:00
if ArmoryExporter . matrices_different ( m1 , m2 ) :
2016-09-02 23:11:04 +02:00
animationFlag = True
break
2017-02-15 09:56:48 +01:00
if animationFlag :
2016-09-02 23:11:04 +02:00
o [ ' animation ' ] = { }
tracko = { }
tracko [ ' target ' ] = " transform "
2017-03-16 14:53:43 +01:00
tracko [ ' times ' ] = [ ]
2016-09-02 23:11:04 +02:00
for i in range ( begin_frame , end_frame ) :
2017-03-16 14:53:43 +01:00
tracko [ ' times ' ] . append ( ( ( i - begin_frame ) * self . frameTime ) )
2016-09-02 23:11:04 +02:00
2017-03-16 14:53:43 +01:00
tracko [ ' times ' ] . append ( ( end_frame * self . frameTime ) )
2016-09-02 23:11:04 +02:00
2017-03-16 14:53:43 +01:00
tracko [ ' values ' ] = [ ]
2016-09-02 23:11:04 +02:00
parent = poseBone . parent
2017-02-15 09:56:48 +01:00
if parent :
2016-09-02 23:11:04 +02:00
for i in range ( begin_frame , end_frame ) :
scene . frame_set ( i )
2017-03-16 14:53:43 +01:00
tracko [ ' values ' ] + = self . write_matrix ( parent . matrix . inverted ( ) * poseBone . matrix )
2016-09-02 23:11:04 +02:00
scene . frame_set ( end_frame )
2017-03-16 14:53:43 +01:00
tracko [ ' values ' ] + = 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 )
2017-03-16 14:53:43 +01:00
tracko [ ' values ' ] + = self . write_matrix ( poseBone . matrix )
2016-09-02 23:11:04 +02:00
scene . frame_set ( end_frame )
2017-03-16 14:53:43 +01:00
tracko [ ' values ' ] + = 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 ) :
2017-03-16 14:53:43 +01:00
keyo = [ ]
2016-09-02 23:11:04 +02:00
keyCount = len ( fcurve . keyframe_points )
for i in range ( keyCount ) :
time = fcurve . keyframe_points [ i ] . co [ 0 ] - self . beginFrame
2017-03-16 14:53:43 +01:00
keyo . append ( time * self . frameTime )
2016-09-02 23:11:04 +02:00
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
2017-03-16 14:53:43 +01:00
def export_key_values ( self , fcurve ) :
keyo = [ ]
2016-09-02 23:11:04 +02:00
keyCount = len ( fcurve . keyframe_points )
for i in range ( keyCount ) :
value = fcurve . keyframe_points [ i ] . co [ 1 ]
2017-03-16 14:53:43 +01:00
keyo . append ( value )
2016-09-02 23:11:04 +02:00
return keyo
2016-10-19 13:28:06 +02:00
def export_key_value_control_points ( self , fcurve ) :
2017-03-16 14:53:43 +01:00
keyminuso = [ ]
2016-09-02 23:11:04 +02:00
keyCount = len ( fcurve . keyframe_points )
for i in range ( keyCount ) :
ctrl = fcurve . keyframe_points [ i ] . handle_left [ 1 ]
2017-03-16 14:53:43 +01:00
keyminuso . append ( ctrl )
2016-09-02 23:11:04 +02:00
2017-03-16 14:53:43 +01:00
keypluso = [ ]
2016-09-02 23:11:04 +02:00
for i in range ( keyCount ) :
ctrl = fcurve . keyframe_points [ i ] . handle_right [ 1 ]
2017-03-16 14:53:43 +01:00
keypluso . append ( ctrl )
2016-09-02 23:11:04 +02:00
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
2017-04-01 21:25:57 +02:00
if kind != AnimationTypeBezier :
2017-03-16 14:53:43 +01:00
tracko [ ' times ' ] = self . export_key_times ( fcurve )
tracko [ ' values ' ] = self . export_key_values ( fcurve )
2016-09-02 23:11:04 +02:00
else :
tracko [ ' curve ' ] = ' bezier '
2017-03-16 14:53:43 +01:00
tracko [ ' times ' ] = self . export_key_times ( fcurve )
tracko [ ' times_control_plus ' ] , tracko [ ' times_control_minus ' ] = self . export_key_time_control_points ( fcurve )
2016-09-02 23:11:04 +02:00
2017-03-16 14:53:43 +01:00
tracko [ ' values ' ] = self . export_key_values ( fcurve )
tracko [ ' values_control_plus ' ] , tracko [ ' values_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
2017-04-08 20:05:35 +02:00
sampledAnimation = ( ArmoryExporter . sample_animation_flag or mode == " QUATERNION " or mode == " AXIS_ANGLE " )
2016-09-02 23:11:04 +02:00
2017-04-01 21:25:57 +02:00
if ( not sampledAnimation ) and ( bobject . animation_data ) :
2016-09-02 23:11:04 +02:00
action = bobject . animation_data . action
2017-02-15 09:56:48 +01:00
if action :
2016-09-02 23:11:04 +02:00
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 ) :
2017-04-01 21:25:57 +02:00
if ( fcurve . array_index == i ) and ( not locAnimCurve [ i ] ) :
2016-09-02 23:11:04 +02:00
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 ) :
2017-04-01 21:25:57 +02:00
if ( fcurve . array_index == i ) and ( not deltaPosAnimCurve [ i ] ) :
2016-09-02 23:11:04 +02:00
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 ) :
2017-04-01 21:25:57 +02:00
if ( fcurve . array_index == i ) and ( not rotAnimCurve [ i ] ) :
2016-09-02 23:11:04 +02:00
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 ) :
2017-04-01 21:25:57 +02:00
if ( fcurve . array_index == i ) and ( not deltaRotAnimCurve [ i ] ) :
2016-09-02 23:11:04 +02:00
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 ) :
2017-04-01 21:25:57 +02:00
if ( fcurve . array_index == i ) and ( not sclAnimCurve [ i ] ) :
2016-09-02 23:11:04 +02:00
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 ) :
2017-04-01 21:25:57 +02:00
if ( fcurve . array_index == i ) and ( not deltaSclAnimCurve [ i ] ) :
2016-09-02 23:11:04 +02:00
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
2017-04-01 21:25:57 +02:00
elif ( fcurve . data_path == " rotation_axis_angle " ) or ( fcurve . data_path == " rotation_quaternion " ) or ( fcurve . data_path == " delta_rotation_quaternion " ) :
2016-09-02 23:11:04 +02:00
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 ]
2017-04-01 21:25:57 +02:00
if ( sampledAnimation ) or ( ( not locationAnimated ) and ( not rotationAnimated ) and ( not scaleAnimated ) and ( not deltaPositionAnimated ) and ( not deltaRotationAnimated ) and ( not deltaScaleAnimated ) ) :
2016-09-02 23:11:04 +02:00
# 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 ]
2017-04-01 21:25:57 +02: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
2017-04-01 21:25:57 +02: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 ]
2017-04-01 21:25:57 +02: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
2017-04-01 21:25:57 +02: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 ]
2017-04-01 21:25:57 +02: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
2017-04-01 21:25:57 +02: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 ]
2017-04-01 21:25:57 +02: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.
2017-04-01 21:25:57 +02:00
if mode == " QUATERNION " :
2016-09-02 23:11:04 +02:00
quaternion = bobject . rotation_quaternion
2017-04-01 21:25:57 +02: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
2017-04-01 21:25:57 +02:00
elif mode == " AXIS_ANGLE " :
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 ]
2017-04-01 21:25:57 +02: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
2017-04-01 21:25:57 +02:00
if deltaScaleAnimated :
2016-09-02 23:11:04 +02:00
# 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 ]
2017-04-01 21:25:57 +02: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
2017-04-01 21:25:57 +02: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
2017-04-01 21:25:57 +02:00
if scaleAnimated :
2016-09-02 23:11:04 +02:00
# 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 ]
2017-04-01 21:25:57 +02: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
2017-04-01 21:25:57 +02: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
2017-04-01 21:25:57 +02:00
# Export the animation tracks
2016-09-02 23:11:04 +02:00
o [ ' animation ' ] = { }
o [ ' animation ' ] [ ' begin ' ] = ( action . frame_range [ 0 ] - self . beginFrame ) * self . frameTime
o [ ' animation ' ] [ ' end ' ] = ( action . frame_range [ 1 ] - self . beginFrame ) * self . frameTime
o [ ' animation ' ] [ ' tracks ' ] = [ ]
2017-01-31 17:58:30 +01:00
if locationAnimated :
2016-09-02 23:11:04 +02:00
for i in range ( 3 ) :
2017-04-01 21:25:57 +02:00
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
2017-01-31 17:58:30 +01:00
if rotationAnimated :
2016-09-02 23:11:04 +02:00
for i in range ( 3 ) :
2017-04-01 21:25:57 +02:00
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
2017-01-31 17:58:30 +01:00
if scaleAnimated :
2016-09-02 23:11:04 +02:00
for i in range ( 3 ) :
2017-04-01 21:25:57 +02:00
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
2017-01-31 17:58:30 +01:00
if deltaPositionAnimated :
2016-09-02 23:11:04 +02:00
for i in range ( 3 ) :
2017-04-01 21:25:57 +02:00
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
2017-01-31 17:58:30 +01:00
if deltaRotationAnimated :
2016-09-02 23:11:04 +02:00
for i in range ( 3 ) :
2017-04-01 21:25:57 +02:00
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
2017-01-31 17:58:30 +01:00
if deltaScaleAnimated :
2016-09-02 23:11:04 +02:00
for i in range ( 3 ) :
2017-04-01 21:25:57 +02:00
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 ) :
2017-04-08 20:05:35 +02:00
if ArmoryExporter . export_all_flag 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 ) :
2017-04-08 20:05:35 +02:00
if ArmoryExporter . export_all_flag 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
2017-01-31 17:58:30 +01:00
if bobject . parent_type == " BONE " :
2016-09-02 23:11:04 +02:00
boneSubbobjectArray = self . boneParentArray . get ( bobject . parent_bone )
2017-01-31 17:58:30 +01:00
if boneSubbobjectArray :
2016-09-02 23:11:04 +02:00
boneSubbobjectArray . append ( bobject )
else :
self . boneParentArray [ bobject . parent_bone ] = [ bobject ]
2017-01-31 17:58:30 +01:00
if bobject . type == " ARMATURE " :
2016-09-02 23:11:04 +02:00
skeleton = bobject . data
2017-04-01 21:25:57 +02:00
if skeleton :
2016-09-02 23:11:04 +02:00
for bone in skeleton . bones :
2017-04-01 21:25:57 +02:00
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 :
2017-02-28 22:26:35 +01: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 )
2017-04-08 20:05:35 +02:00
animation = ( ( len ( curveArray ) != 0 ) or ArmoryExporter . sample_animation_flag )
2016-09-02 23:11:04 +02:00
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
2017-02-28 22:26:35 +01:00
if parentPoseBone :
2016-09-02 23:11:04 +02:00
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-03-01 11:45:55 +01:00
def use_default_material ( self , bobject , o ) :
2017-04-04 23:11:31 +02:00
if arm . utils . export_bone_data ( bobject ) :
2017-03-01 11:45:55 +01:00
o [ ' material_refs ' ] . append ( ' armdefaultskin ' )
self . defaultSkinMaterialObjects . append ( bobject )
else :
2017-01-29 13:07:58 +01:00
o [ ' material_refs ' ] . append ( ' armdefault ' )
2017-01-10 23:36:18 +01:00
self . defaultMaterialObjects . append ( bobject )
2017-03-01 11:45:55 +01:00
def export_material_ref ( self , bobject , material , index , o ) :
if material == None : # Use default for empty mat slots
self . use_default_material ( bobject , o )
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
2017-05-20 19:07:15 +02: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 " ]
2017-08-09 00:14:30 +02:00
# Linked object, not present in scene
2017-08-08 16:44:33 +02:00
if bobject not in self . objectToArmObjectDict :
o = { }
o [ ' traits ' ] = [ ]
2017-08-09 00:14:30 +02:00
o [ ' spawn ' ] = False
2017-08-08 16:44:33 +02:00
self . objectToArmObjectDict [ bobject ] = o
2017-04-02 13:13:43 +02:00
o = self . objectToArmObjectDict [ bobject ]
2016-09-02 23:11:04 +02:00
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 :
2017-01-29 16:15:04 +01:00
o [ ' mobile ' ] = False
if bobject . dupli_type == ' GROUP ' and bobject . dupli_group != None :
o [ ' group_ref ' ] = bobject . dupli_group . name
2016-09-02 23:11:04 +02:00
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
2017-05-13 17:17:43 +02:00
if objref != None :
objname = self . asset_name ( objref )
2016-12-05 01:54:01 +01:00
# Lods
2017-03-11 01:50:47 +01:00
if bobject . type == ' MESH ' and hasattr ( objref , ' my_lodlist ' ) and len ( objref . my_lodlist ) > 0 :
2016-12-05 01:54:01 +01:00
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-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 )
2017-05-13 17:17:43 +02:00
oid = arm . utils . safestr ( 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 )
2017-07-12 11:03:24 +02:00
if bobject . material_slots [ i ] . material != None and bobject . material_slots [ i ] . material . decal :
2017-05-24 11:04:15 +02:00
o [ ' type ' ] = ' decal_object '
2017-01-29 13:07:58 +01:00
# No material, mimic cycles and assign default
2016-09-02 23:11:04 +02:00
if len ( o [ ' material_refs ' ] ) == 0 :
2017-03-01 11:45:55 +01:00
self . use_default_material ( bobject , o )
2016-09-02 23:11:04 +02:00
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)
2017-04-01 21:25:57 +02:00
#if shapeKeys:
2016-09-02 23:11:04 +02:00
# 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
2017-05-20 19:07:15 +02:00
# 6 directional lamps if cubemap is disabled
if type == NodeTypeLamp and objref . type == ' POINT ' and objref . lamp_omni_shadows and not objref . lamp_omni_shadows_cubemap :
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
2017-04-11 23:21:42 +02:00
if bobject . type == ' ARMATURE ' and bobject . data != None :
2017-02-28 22:26:35 +01:00
bdata = bobject . data # Armature data
2016-11-13 12:13:41 +01:00
action = None # Reference start action
2017-04-11 23:21:42 +02:00
# Get action
if bobject . animation_data == None :
bobject . animation_data_create ( )
else :
action = bobject . animation_data . action
if action == None :
actions = bpy . data . actions
action = actions . get ( ' armorypose ' , actions . new ( name = ' armorypose ' ) )
2016-11-13 12:13:41 +01:00
if bobject . edit_actions_prop :
action = bpy . data . actions [ bobject . start_action_name_prop ]
2017-04-11 23:21:42 +02:00
# Bone export
armatureid = self . asset_name ( bdata )
2017-05-13 17:17:43 +02:00
armatureid = arm . utils . safestr ( armatureid )
2017-04-11 23:21:42 +02:00
ext = ' '
if self . is_compress ( bdata ) :
ext = ' .zip '
o [ ' bones_ref ' ] = ' bones_ ' + armatureid + ' _ ' + action . name + ext
# Write bones
if bdata . edit_actions :
export_actions = [ ]
for t in bdata . my_actiontraitlist :
export_actions . append ( bpy . data . actions [ t . name ] )
else : # Use default
export_actions = [ action ]
# orig_action = bobject.animation_data.action
# bobject.animation_data.action = orig_action
for action in export_actions :
bobject . animation_data . action = action
fp = self . get_meshes_file_path ( ' bones_ ' + armatureid + ' _ ' + action . name , compressed = self . is_compress ( bdata ) )
assets . add ( fp )
if bdata . data_cached == False or not os . path . exists ( fp ) :
bones = [ ]
for bone in bdata . bones :
if not bone . parent :
boneo = { }
self . export_bone ( bobject , bone , scene , boneo , action )
bones . append ( boneo )
# Save bones separately
bones_obj = { }
bones_obj [ ' objects ' ] = bones
arm . utils . write_arm ( fp , bones_obj )
bdata . 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 :
2017-01-31 17:58:30 +01:00
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
2017-05-17 17:06:52 +02:00
def export_skin_quality ( self , bobject , armature , export_vertex_array , o ) :
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 = { }
2017-05-17 17:06:52 +02:00
o [ ' skin ' ] = oskin
2016-09-02 23:11:04 +02:00
# 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 ' ] = [ ]
2017-04-11 23:21:42 +02:00
bone_array = armature . data . bones
bone_count = len ( bone_array )
max_bones = bpy . data . worlds [ ' Arm ' ] . generate_gpu_skin_max_bones
if bone_count > max_bones :
log . warn ( bobject . name + ' - ' + str ( bone_count ) + ' bones found, exceeds maximum of ' + str ( max_bones ) + ' bones defined - raise the value in Camera Data - Armory Render Props - Max Bones ' )
2016-09-02 23:11:04 +02:00
2017-04-11 23:21:42 +02:00
for i in range ( bone_count ) :
boneRef = self . find_node ( bone_array [ i ] . name )
2017-04-01 21:25:57 +02:00
if boneRef :
2016-09-02 23:11:04 +02:00
oskel [ ' bone_ref_array ' ] . append ( boneRef [ 1 ] [ " structName " ] )
else :
oskel [ ' bone_ref_array ' ] . append ( " null " )
# Write the bind pose transform array
oskel [ ' transforms ' ] = [ ]
2017-04-11 23:21:42 +02:00
for i in range ( bone_count ) :
oskel [ ' transforms ' ] . append ( self . write_matrix ( armature . matrix_world * bone_array [ i ] . matrix_local ) )
2016-09-02 23:11:04 +02:00
# Export the per-vertex bone influence data
2017-04-11 23:21:42 +02:00
group_remap = [ ]
2016-09-02 23:11:04 +02:00
for group in bobject . vertex_groups :
2017-04-11 23:21:42 +02:00
group_name = group . name
for i in range ( bone_count ) :
if bone_array [ i ] . name == group_name :
group_remap . append ( i )
2016-09-02 23:11:04 +02:00
break
else :
2017-04-11 23:21:42 +02:00
group_remap . append ( - 1 )
2016-09-02 23:11:04 +02:00
2017-04-11 23:21:42 +02:00
bone_count_array = [ ]
bone_index_array = [ ]
bone_weight_array = [ ]
2016-09-02 23:11:04 +02:00
2017-03-20 20:07:25 +01:00
warn_bones = False
2016-10-19 13:28:06 +02:00
mesh_vertex_array = bobject . data . vertices
for ev in export_vertex_array :
2017-04-11 23:21:42 +02:00
bone_count = 0
total_weight = 0.0
bone_values = [ ]
for element in mesh_vertex_array [ ev . vertex_index ] . groups :
bone_index = group_remap [ element . group ]
bone_weight = element . weight
if bone_index > = 0 and bone_weight != 0.0 :
bone_values . append ( ( bone_weight , bone_index ) )
total_weight + = bone_weight
bone_count + = 1
if bone_count > 4 : # Four bones max
bone_count = 4
bone_values = bone_values [ : 4 ]
warn_bones = True
bone_count_array . append ( bone_count )
# Take highest weights
for bv in reversed ( sorted ( bone_values ) ) :
bone_index_array . append ( bv [ 1 ] )
bone_weight_array . append ( bv [ 0 ] )
if total_weight != 0.0 :
normalizer = 1.0 / total_weight
for i in range ( 0 , bone_count ) :
bone_weight_array [ - i ] * = normalizer
2016-09-02 23:11:04 +02:00
2017-03-20 20:07:25 +01:00
if warn_bones :
2017-04-11 23:21:42 +02:00
log . warn ( bobject . name + ' - more than 4 bones influence single vertex - taking highest weights ' )
2017-03-20 20:07:25 +01:00
2016-09-02 23:11:04 +02:00
# Write the bone count array. There is one entry per vertex.
2017-04-11 23:21:42 +02:00
oskin [ ' bone_count_array ' ] = bone_count_array
2016-09-02 23:11:04 +02:00
# Write the bone index array. The number of entries is the sum of the bone counts for all vertices.
2017-04-11 23:21:42 +02:00
oskin [ ' bone_index_array ' ] = bone_index_array
2016-09-02 23:11:04 +02:00
# Write the bone weight array. The number of entries is the sum of the bone counts for all vertices.
2017-04-11 23:21:42 +02:00
oskin [ ' bone_weight_array ' ] = bone_weight_array
2016-09-02 23:11:04 +02:00
2017-05-17 17:06:52 +02:00
# def export_skin_fast(self, bobject, armature, vert_list, o):
2017-02-28 22:26:35 +01:00
# oskin = {}
2017-05-17 17:06:52 +02:00
# o['skin'] = oskin
2017-02-28 22:26:35 +01:00
# otrans = {}
# oskin['transform'] = otrans
# otrans['values'] = self.write_matrix(bobject.matrix_world)
# oskel = {}
# oskin['skeleton'] = oskel
# oskel['bone_ref_array'] = []
2017-04-11 23:21:42 +02:00
# bone_array = armature.data.bones
# bone_count = len(bone_array)
# for i in range(bone_count):
# boneRef = self.find_node(bone_array[i].name)
2017-04-01 21:25:57 +02:00
# if boneRef:
2017-02-28 22:26:35 +01:00
# oskel['bone_ref_array'].append(boneRef[1]["structName"])
# else:
# oskel['bone_ref_array'].append("null")
# oskel['transforms'] = []
2017-04-11 23:21:42 +02:00
# for i in range(bone_count):
# oskel['transforms'].append(self.write_matrix(armature.matrix_world * bone_array[i].matrix_local))
2017-02-28 22:26:35 +01:00
2017-04-11 23:21:42 +02:00
# bone_count_array = []
# bone_index_array = []
# bone_weight_array = []
2017-02-28 22:26:35 +01:00
# for vtx in vert_list:
2017-04-11 23:21:42 +02:00
# bone_count_array.append(vtx.bone_count)
# bone_index_array += vtx.bone_indices
# bone_weight_array += vtx.bone_weights
# oskin['bone_count_array'] = bone_count_array
# oskin['bone_index_array'] = bone_index_array
# oskin['bone_weight_array'] = bone_weight_array
2016-09-02 23:11:04 +02:00
def calc_tangents ( self , posa , nora , uva , ia ) :
triangle_count = int ( len ( ia ) / 3 )
vertex_count = int ( len ( posa ) / 3 )
tangents = [ 0 ] * vertex_count * 3
# bitangents = [0] * vertex_count * 3
for i in range ( 0 , triangle_count ) :
i0 = ia [ i * 3 + 0 ]
i1 = ia [ i * 3 + 1 ]
i2 = ia [ i * 3 + 2 ]
# TODO: Slow
v0 = Vector ( ( posa [ i0 * 3 + 0 ] , posa [ i0 * 3 + 1 ] , posa [ i0 * 3 + 2 ] ) )
v1 = Vector ( ( posa [ i1 * 3 + 0 ] , posa [ i1 * 3 + 1 ] , posa [ i1 * 3 + 2 ] ) )
v2 = Vector ( ( posa [ i2 * 3 + 0 ] , posa [ i2 * 3 + 1 ] , posa [ i2 * 3 + 2 ] ) )
uv0 = Vector ( ( uva [ i0 * 2 + 0 ] , uva [ i0 * 2 + 1 ] ) )
uv1 = Vector ( ( uva [ i1 * 2 + 0 ] , uva [ i1 * 2 + 1 ] ) )
uv2 = Vector ( ( uva [ i2 * 2 + 0 ] , uva [ i2 * 2 + 1 ] ) )
tangent = ArmoryExporter . calc_tangent ( v0 , v1 , v2 , uv0 , uv1 , uv2 )
tangents [ i0 * 3 + 0 ] + = tangent . x
tangents [ i0 * 3 + 1 ] + = tangent . y
tangents [ i0 * 3 + 2 ] + = tangent . z
tangents [ i1 * 3 + 0 ] + = tangent . x
tangents [ i1 * 3 + 1 ] + = tangent . y
tangents [ i1 * 3 + 2 ] + = tangent . z
tangents [ i2 * 3 + 0 ] + = tangent . x
tangents [ i2 * 3 + 1 ] + = tangent . y
tangents [ i2 * 3 + 2 ] + = tangent . z
# bitangents[i0 * 3 + 0] += bitangent.x
# bitangents[i0 * 3 + 1] += bitangent.y
# bitangents[i0 * 3 + 2] += bitangent.z
# bitangents[i1 * 3 + 0] += bitangent.x
# bitangents[i1 * 3 + 1] += bitangent.y
# bitangents[i1 * 3 + 2] += bitangent.z
# bitangents[i2 * 3 + 0] += bitangent.x
# bitangents[i2 * 3 + 1] += bitangent.y
# bitangents[i2 * 3 + 2] += bitangent.z
# Orthogonalize
for i in range ( 0 , vertex_count ) :
# Slow
t = Vector ( ( tangents [ i * 3 ] , tangents [ i * 3 + 1 ] , tangents [ i * 3 + 2 ] ) )
# b = Vector((bitangents[i * 3], bitangents[i * 3 + 1], bitangents[i * 3 + 2]))
n = Vector ( ( nora [ i * 3 ] , nora [ i * 3 + 1 ] , nora [ i * 3 + 2 ] ) )
v = t - n * n . dot ( t )
v . normalize ( )
# Calculate handedness
# cnv = n.cross(v)
# if cnv.dot(b) < 0.0:
# v = v * -1.0
tangents [ i * 3 ] = v . x
tangents [ i * 3 + 1 ] = v . y
tangents [ i * 3 + 2 ] = v . z
return tangents
def write_mesh ( self , bobject , fp , o ) :
# One mesh data per file
if ArmoryExporter . option_mesh_per_file :
mesh_obj = { }
mesh_obj [ ' mesh_datas ' ] = [ o ]
2017-03-15 12:30:14 +01:00
arm . utils . 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 )
2017-05-17 17:06:52 +02:00
def make_va ( self , attrib , size , values ) :
va = { }
va [ ' attrib ' ] = attrib
va [ ' size ' ] = size
va [ ' values ' ] = values
return va
def export_mesh_fast ( self , exportMesh , bobject , fp , o ) :
2016-09-02 23:11:04 +02:00
# 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 )
2017-05-17 17:06:52 +02:00
has_tex = self . get_export_uvs ( exportMesh ) == True and num_uv_layers > 0
has_tex1 = has_tex == True and num_uv_layers > 1
2016-09-02 23:11:04 +02:00
num_colors = len ( exportMesh . vertex_colors )
2017-05-17 17:06:52 +02:00
has_col = self . get_export_vcols ( exportMesh ) == True and num_colors > 0
has_tang = self . has_tangents ( exportMesh )
2016-09-02 23:11:04 +02:00
vdata = [ 0 ] * num_verts * 3
ndata = [ 0 ] * num_verts * 3
2017-05-17 17:06:52 +02:00
if has_tex :
2016-09-02 23:11:04 +02:00
t0data = [ 0 ] * num_verts * 2
2017-05-17 17:06:52 +02:00
if has_tex1 :
2016-09-02 23:11:04 +02:00
t1data = [ 0 ] * num_verts * 2
2017-05-17 17:06:52 +02:00
if has_col :
2016-09-02 23:11:04 +02:00
cdata = [ 0 ] * num_verts * 3
2017-05-17 17:06:52 +02:00
# va_stride = 3 + 3 # pos + nor
# va_name = 'pos_nor'
# if has_tex:
# va_stride += 2
# va_name += '_tex'
# if has_tex1:
# va_stride += 2
# va_name += '_tex1'
# if has_col > 0:
# va_stride += 3
# va_name += '_col'
# if has_tang:
# va_stride += 3
# va_name += '_tang'
# vdata = [0] * num_verts * va_stride
2016-09-02 23:11:04 +02:00
# 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 ]
2017-05-17 17:06:52 +02:00
if has_tex :
2016-09-02 23:11:04 +02:00
t0data [ i * 2 ] = vtx . uvs [ 0 ] . x
t0data [ i * 2 + 1 ] = 1.0 - vtx . uvs [ 0 ] . y # Reverse TCY
2017-05-17 17:06:52 +02:00
if has_tex1 :
2016-09-02 23:11:04 +02:00
t1data [ i * 2 ] = vtx . uvs [ 1 ] . x
2017-02-15 09:56:48 +01:00
t1data [ i * 2 + 1 ] = 1.0 - vtx . uvs [ 1 ] . y
2017-05-17 17:06:52 +02:00
if has_col > 0 :
2016-09-02 23:11:04 +02:00
cdata [ i * 3 ] = vtx . col [ 0 ]
cdata [ i * 3 + 1 ] = vtx . col [ 1 ]
cdata [ i * 3 + 2 ] = vtx . col [ 2 ]
2017-05-17 17:06:52 +02:00
2016-09-02 23:11:04 +02:00
# Output
2017-05-17 17:06:52 +02:00
o [ ' vertex_arrays ' ] = [ ]
pa = self . make_va ( ' pos ' , 3 , vdata )
o [ ' vertex_arrays ' ] . append ( pa )
na = self . make_va ( ' nor ' , 3 , ndata )
o [ ' vertex_arrays ' ] . append ( na )
2016-12-17 23:48:18 +01:00
2017-05-17 17:06:52 +02:00
if has_tex :
ta = self . make_va ( ' tex ' , 2 , t0data )
o [ ' vertex_arrays ' ] . append ( ta )
if has_tex1 :
ta1 = self . make_va ( ' tex1 ' , 2 , t1data )
o [ ' vertex_arrays ' ] . append ( ta1 )
2016-12-17 23:48:18 +01:00
2017-05-17 17:06:52 +02:00
if has_col :
ca = self . make_va ( ' col ' , 3 , cdata )
o [ ' vertex_arrays ' ] . append ( ca )
2016-09-02 23:11:04 +02:00
# Indices
prims = { ma . name if ma else ' ' : [ ] for ma in exportMesh . materials }
if not prims :
prims = { ' ' : [ ] }
vert_dict = { i : v for v in vert_list for i in v . loop_indices }
for poly in exportMesh . polygons :
first = poly . loop_start
if len ( exportMesh . materials ) == 0 :
prim = prims [ ' ' ]
else :
2017-05-12 23:27:58 +02:00
mat = exportMesh . materials [ min ( poly . material_index , len ( exportMesh . materials ) - 1 ) ]
2016-09-02 23:11:04 +02:00
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
2017-05-17 17:06:52 +02:00
o [ ' index_arrays ' ] = [ ]
2016-09-02 23:11:04 +02:00
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
2017-05-17 17:06:52 +02:00
o [ ' index_arrays ' ] . append ( ia )
2016-09-02 23:11:04 +02:00
# Make tangents
2017-05-17 17:06:52 +02:00
if has_tang :
tanga_vals = self . calc_tangents ( pa [ ' values ' ] , na [ ' values ' ] , ta [ ' values ' ] , o [ ' index_arrays ' ] [ 0 ] [ ' values ' ] )
tanga = self . make_va ( ' tang ' , 3 , tanga_vals )
o [ ' vertex_arrays ' ] . append ( tanga )
2016-09-02 23:11:04 +02:00
return vert_list
2017-05-17 17:06:52 +02:00
def has_tangents ( self , exportMesh ) :
return self . get_export_uvs ( exportMesh ) == True and self . get_export_tangents ( exportMesh ) == True and len ( exportMesh . uv_layers ) > 0
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 ]
2017-05-13 17:17:43 +02:00
oid = arm . utils . safestr ( 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 )
2017-06-06 18:29:33 +02:00
if bobject . data . sdfgen :
sdf_path = fp . replace ( ' /mesh_ ' , ' /sdf_ ' )
assets . add ( sdf_path )
2016-09-02 23:11:04 +02:00
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)
2017-04-01 21:25:57 +02:00
# if (relative) and (morphCount != baseIndex):
2016-09-02 23:11:04 +02:00
# 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 ( )
armature = bobject . find_armature ( )
2017-04-02 13:13:43 +02:00
apply_modifiers = not armature
2016-09-02 23:11:04 +02:00
2017-04-02 13:13:43 +02:00
# Apply all modifiers to create a new mesh with tessfaces
exportMesh = bobject . to_mesh ( scene , apply_modifiers , " RENDER " , True , False )
2016-09-02 23:11:04 +02:00
2016-12-21 19:15:51 +01:00
if exportMesh == None :
2017-04-01 21:25:57 +02:00
log . warn ( oid + ' was not exported ' )
2016-12-21 19:15:51 +01:00
return
2016-11-12 21:34:06 +01:00
if len ( exportMesh . uv_layers ) > 2 :
2017-04-01 21:25:57 +02:00
log . warn ( oid + ' exceeds maximum of 2 UV Maps supported ' )
2016-11-12 21:34:06 +01:00
2016-09-02 23:11:04 +02:00
# Process meshes
if ArmoryExporter . option_optimize_mesh :
2017-05-17 17:06:52 +02:00
vert_list = self . export_mesh_quality ( exportMesh , bobject , fp , o )
2016-11-28 14:40:07 +01:00
if armature :
2017-05-17 17:06:52 +02:00
self . export_skin_quality ( bobject , armature , vert_list , o )
2016-09-02 23:11:04 +02:00
else :
2017-05-17 17:06:52 +02:00
vert_list = self . export_mesh_fast ( exportMesh , bobject , fp , o )
2016-11-28 14:40:07 +01:00
if armature :
2017-05-17 17:06:52 +02:00
self . export_skin_quality ( bobject , armature , vert_list , o )
# self.export_skin_fast(bobject, armature, vert_list, o)
2016-09-02 23:11:04 +02:00
2017-01-04 00:13:52 +01:00
# Save aabb
2017-05-17 17:06:52 +02:00
for va in o [ ' vertex_arrays ' ] :
if va [ ' attrib ' ] . startswith ( ' pos ' ) :
2017-01-04 00:13:52 +01:00
positions = va [ ' values ' ]
2017-05-17 17:06:52 +02:00
stride = 0
ar = va [ ' attrib ' ] . split ( ' _ ' )
for a in ar :
if a == ' pos ' or a == ' nor ' or a == ' col ' or a == ' tang ' :
stride + = 3
elif a == ' tex ' or a == ' tex1 ' :
stride + = 2
elif a == ' bone ' or a == ' weight ' :
stride + = 4
2017-01-04 00:13:52 +01:00
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 ] ;
2017-05-17 17:06:52 +02:00
i + = stride ;
2017-01-17 14:48:47 +01:00
if hasattr ( bobject . data , ' mesh_aabb ' ) :
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 ] ) ]
2017-01-04 00:13:52 +01:00
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 :
2017-05-17 17:06:52 +02:00
o [ ' instance_offsets ' ] = instance_offsets
2016-09-02 23:11:04 +02:00
# Export usage
2016-11-30 01:33:26 +01:00
if bobject . data . dynamic_usage :
2017-05-17 17:06:52 +02:00
o [ ' dynamic_usage ' ] = bobject . data . dynamic_usage
2016-09-02 23:11:04 +02:00
2017-06-05 02:32:51 +02:00
if bobject . data . sdfgen :
o [ ' sdf_ref ' ] = ' sdf_ ' + oid
2016-09-02 23:11:04 +02:00
self . write_mesh ( bobject , fp , o )
2017-06-05 02:32:51 +02:00
if bobject . data . sdfgen :
# Copy input
sdk_path = arm . utils . get_sdk_path ( )
sdfgen_path = sdk_path + ' /armory/tools/sdfgen '
shutil . copy ( fp , sdfgen_path + ' /krom/mesh.arm ' )
2017-08-02 11:46:54 +02:00
# Extract basecolor
# Assume Armpry PBR with linked texture for now
# mat = bobject.material_slots[0].material
# img = None
# for n in mat.node_tree.nodes:
# if n.type == 'GROUP' and n.node_tree.name.startswith('Armory PBR') and n.inputs[0].is_linked:
# img = n.inputs[0].links[0].from_node.image
# fp_img = bpy.path.abspath(img.filepath)
# shutil.copy(fp_img, sdfgen_path + '/krom/mesh.png')
2017-06-05 02:32:51 +02:00
# Run
krom_location , krom_path = arm . utils . krom_paths ( )
krom_dir = sdfgen_path + ' /krom '
2017-07-17 13:50:17 +02:00
krom_res = sdfgen_path + ' /krom '
2017-06-05 02:32:51 +02:00
subprocess . check_output ( [ krom_path , krom_dir , krom_res , ' --nosound ' , ' --nowindow ' ] )
# Copy output
sdf_path = fp . replace ( ' /mesh_ ' , ' /sdf_ ' )
shutil . copy ( ' out.bin ' , sdf_path )
assets . add ( sdf_path )
os . remove ( ' out.bin ' )
os . remove ( sdfgen_path + ' /krom/mesh.arm ' )
2017-08-02 11:46:54 +02:00
# if img != None:
# os.remove(sdfgen_path + '/krom/mesh.png')
2017-06-05 02:32:51 +02:00
2017-05-17 17:06:52 +02:00
def export_mesh_quality ( self , exportMesh , bobject , fp , o ) :
2017-01-14 16:32:10 +01:00
# 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 = [ ]
2017-05-17 17:06:52 +02:00
unifiedVA = ArmoryExporter . unify_vertices ( export_vertex_array , indexTable )
2016-09-02 23:11:04 +02:00
2017-01-14 16:32:10 +01:00
# Write the position array
2017-05-17 17:06:52 +02:00
o [ ' vertex_arrays ' ] = [ ]
pa = self . make_va ( ' pos ' , 3 , self . write_va3d ( unifiedVA , " position " ) )
o [ ' vertex_arrays ' ] . append ( pa )
2017-01-14 16:32:10 +01:00
# Write the normal array
2017-05-17 17:06:52 +02:00
na = self . make_va ( ' nor ' , 3 , self . write_va3d ( unifiedVA , " normal " ) )
o [ ' vertex_arrays ' ] . append ( na )
2016-09-02 23:11:04 +02:00
2017-01-14 16:32:10 +01:00
# Write the color array if it exists
2017-05-17 17:06:52 +02:00
color_count = len ( exportMesh . tessface_vertex_colors )
if self . get_export_vcols ( exportMesh ) == True and color_count > 0 :
ca = self . make_va ( ' col ' , 3 , self . write_va3d ( unifiedVA , " color " ) )
o [ ' vertex_arrays ' ] . append ( ca )
2016-09-02 23:11:04 +02:00
2017-01-14 16:32:10 +01:00
# Write the texcoord arrays
2016-09-02 23:11:04 +02:00
texcoordCount = len ( exportMesh . tessface_uv_textures )
2016-12-17 23:48:18 +01:00
if self . get_export_uvs ( exportMesh ) == True and texcoordCount > 0 :
2017-05-17 17:06:52 +02:00
ta = self . make_va ( ' tex ' , 2 , self . write_va2d ( unifiedVA , " texcoord0 " ) )
o [ ' vertex_arrays ' ] . append ( ta )
2017-04-01 21:25:57 +02:00
if texcoordCount > 1 :
2017-05-17 17:06:52 +02:00
ta2 = self . make_va ( ' tex1 ' , 2 , self . write_va2d ( unifiedVA , " texcoord1 " ) )
o [ ' vertex_arrays ' ] . append ( ta2 )
2016-09-02 23:11:04 +02:00
2017-01-14 16:32:10 +01:00
# If there are multiple morph targets, export them here
2017-04-01 21:25:57 +02:00
# if shapeKeys:
2016-09-02 23:11:04 +02:00
# 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
2017-04-02 13:13:43 +02:00
# morphMesh = bobject.to_mesh(scene, apply_modifiers, "RENDER", True, False)
2016-09-02 23:11:04 +02:00
# # 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)
2017-05-17 17:06:52 +02:00
# self.WriteMorphPositionArray3D(unifiedVA, morphMesh.vertices)
2016-09-02 23:11:04 +02:00
# 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)
2017-05-17 17:06:52 +02:00
# self.WriteMorphNormalArray3D(unifiedVA, morphMesh.vertices, morphMesh.tessfaces)
2016-09-02 23:11:04 +02:00
# 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
2017-05-17 17:06:52 +02:00
o [ ' index_arrays ' ] = [ ]
2016-09-02 23:11:04 +02:00
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
2017-05-17 17:06:52 +02:00
o [ ' index_arrays ' ] . append ( ia )
2016-09-02 23:11:04 +02:00
else :
# If there are multiple material indexes, then write a separate index array for each one.
materialTriangleCount = [ 0 for i in range ( maxMaterialIndex + 1 ) ]
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 ) :
2017-04-01 21:25:57 +02:00
if materialTriangleCount [ m ] != 0 :
2016-09-02 23:11:04 +02:00
materialIndexTable = [ ]
2016-10-19 13:28:06 +02:00
for i in range ( len ( material_table ) ) :
2017-04-01 21:25:57 +02:00
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
2017-05-17 17:06:52 +02:00
o [ ' index_arrays ' ] . append ( ia )
2016-09-02 23:11:04 +02:00
# Export tangents
2017-05-17 17:06:52 +02:00
if self . has_tangents ( exportMesh ) :
tanga_vals = self . calc_tangents ( pa [ ' values ' ] , na [ ' values ' ] , ta [ ' values ' ] , o [ ' index_arrays ' ] [ 0 ] [ ' values ' ] )
2017-05-22 15:55:34 +02:00
tanga = self . make_va ( ' tang ' , 3 , tanga_vals )
2017-05-17 17:06:52 +02:00
o [ ' vertex_arrays ' ] . append ( tanga )
2016-09-02 23:11:04 +02:00
# Delete the new mesh that we made earlier
bpy . data . meshes . remove ( exportMesh )
2017-05-17 17:06:52 +02:00
return unifiedVA
2016-09-02 23:11:04 +02:00
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
2017-03-03 14:36:01 +01:00
if bpy . data . cameras [ 0 ] . rp_shadowmap == ' None ' :
o [ ' shadowmap_size ' ] = 0
else :
o [ ' shadowmap_size ' ] = int ( bpy . data . cameras [ 0 ] . rp_shadowmap )
2016-09-02 23:11:04 +02:00
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
2017-06-23 15:47:51 +02:00
if objtype == ' POINT ' and objref . lamp_omni_shadows and not arm . utils . get_gapi ( ) . startswith ( ' direct3d ' ) :
2017-03-18 18:44:21 +01:00
o [ ' fov ' ] = 1.5708 # 90 deg
2017-05-20 19:07:15 +02:00
if objref . lamp_omni_shadows_cubemap :
o [ ' shadowmap_cube ' ] = True
o [ ' shadows_bias ' ] * = 4.0
2017-05-25 16:48:41 +02:00
# if (o['near_plane'] <= 0.11:
# o['near_plane'] /= 10 # Prevent acne on close surfaces
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
2017-03-04 16:46:39 +01:00
# Normalize lamp strength
2016-12-20 01:39:16 +01:00
if o [ ' type ' ] == ' point ' or o [ ' type ' ] == ' spot ' :
2017-05-06 00:22:15 +02:00
o [ ' strength ' ] * = 0.026
2016-12-20 01:39:16 +01:00
elif o [ ' type ' ] == ' area ' :
2017-06-25 23:16:49 +02:00
o [ ' strength ' ] * = 0.26
2017-02-22 12:10:53 +01:00
elif o [ ' type ' ] == ' sun ' :
2017-05-06 00:22:15 +02:00
o [ ' strength ' ] * = 0.325
2017-02-25 17:13:22 +01:00
# TODO: Lamp texture test..
if n . inputs [ 0 ] . is_linked :
color_node = n . inputs [ 0 ] . links [ 0 ] . from_node
if color_node . type == ' TEX_IMAGE ' :
o [ ' color_texture ' ] = color_node . image . name
2016-12-20 01:39:16 +01:00
break
2016-11-07 16:11:35 +01:00
2017-05-20 19:07:15 +02:00
if objtype == ' POINT ' and objref . lamp_omni_shadows and not objref . lamp_omni_shadows_cubemap :
# 6 separate maps
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 :
2017-05-23 01:03:44 +02:00
unpack_path = arm . utils . 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 :
2017-05-13 17:17:43 +02:00
assets . add ( arm . utils . asset_path ( objref . sound . filepath ) ) # Link sound to assets
2016-09-02 23:11:04 +02:00
2017-03-15 12:30:14 +01:00
o [ ' sound ' ] = arm . utils . extract_filename ( objref . sound . filepath )
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 )
2017-03-01 11:45:55 +01:00
def make_default_mat ( self , mat_name , mat_objs ) :
if mat_name in bpy . data . materials :
return
mat = bpy . data . materials . new ( name = mat_name )
mat . use_nodes = True
o = { }
o [ ' name ' ] = mat . name
o [ ' contexts ' ] = [ ]
mat_users = dict ( )
mat_users [ mat ] = mat_objs
mat_armusers = dict ( )
mat_armusers [ mat ] = [ o ]
make_material . parse ( mat , o , mat_users , mat_armusers , ArmoryExporter . renderpath_id )
self . output [ ' material_datas ' ] . append ( o )
bpy . data . materials . remove ( mat )
2016-09-30 23:24:18 +02:00
def export_materials ( self ) :
2017-03-14 20:43:54 +01:00
wrd = bpy . data . worlds [ ' Arm ' ]
if wrd . arm_batch_materials :
mat_users = self . materialToObjectDict
mat_armusers = self . materialToArmObjectDict
rid = ArmoryExporter . renderpath_id
mat_batch . build ( self . materialArray , mat_users , mat_armusers , rid )
2017-01-13 00:16:00 +01:00
transluc_used = False
2017-02-15 13:15:24 +01:00
overlays_used = False
decals_used = False
2017-08-08 11:47:04 +02:00
# sss_used = False
2016-09-02 23:11:04 +02:00
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
if material . skip_context != ' ' :
o [ ' skip_context ' ] = material . skip_context
2017-07-06 23:47:12 +02:00
if material . two_sided_shading or wrd . force_no_culling :
2016-12-17 15:34:43 +01:00
o [ ' override_context ' ] = { }
2017-07-06 23:47:12 +02:00
o [ ' override_context ' ] [ ' cull_mode ' ] = ' none '
elif material . override_cull_mode != ' Clockwise ' :
o [ ' override_context ' ] = { }
o [ ' override_context ' ] [ ' cull_mode ' ] = material . override_cull_mode
2016-12-17 15:34:43 +01:00
o [ ' contexts ' ] = [ ]
2017-01-23 00:48:59 +01:00
if not material . use_nodes :
material . use_nodes = True
2017-03-14 20:43:54 +01:00
mat_users = self . materialToObjectDict
mat_armusers = self . materialToArmObjectDict
rid = ArmoryExporter . renderpath_id
sd , rpasses = make_material . parse ( material , o , mat_users , mat_armusers , rid )
2017-02-15 13:15:24 +01:00
2017-03-14 20:43:54 +01:00
if ' translucent ' in rpasses :
2017-01-13 00:16:00 +01:00
transluc_used = True
2017-03-14 20:43:54 +01:00
if ' overlay ' in rpasses :
2017-02-15 13:15:24 +01:00
overlays_used = True
2017-03-14 20:43:54 +01:00
if ' decal ' in rpasses :
2017-02-15 13:15:24 +01:00
decals_used = True
2016-12-20 01:39:16 +01:00
uv_export = False
2017-03-02 10:15:22 +01:00
tang_export = False
2016-12-20 01:39:16 +01:00
vcol_export = False
2017-01-04 00:13:52 +01:00
vs_str = ' '
2017-05-25 16:48:41 +02:00
for con in sd [ ' contexts ' ] :
for elem in con [ ' vertex_structure ' ] :
if len ( vs_str ) > 0 :
vs_str + = ' , '
vs_str + = elem [ ' name ' ]
if elem [ ' name ' ] == ' tang ' :
tang_export = True
elif elem [ ' name ' ] == ' tex ' :
uv_export = True
elif elem [ ' name ' ] == ' col ' :
vcol_export = True
# TODO: use array and remove duplis to ensure correctness
2017-01-04 00:13:52 +01:00
material . vertex_structure = vs_str
2016-12-20 01:39:16 +01:00
2017-03-02 10:15:22 +01:00
if ( material . export_tangents != tang_export ) or \
2016-12-20 01:39:16 +01:00
( material . export_uvs != uv_export ) or \
( material . export_vcols != vcol_export ) :
material . export_uvs = uv_export
material . export_vcols = vcol_export
2017-03-02 10:15:22 +01:00
material . export_tangents = tang_export
2016-12-20 01:39:16 +01:00
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
2017-03-01 11:45:55 +01:00
if len ( self . defaultMaterialObjects ) > 0 :
self . make_default_mat ( ' armdefault ' , self . defaultMaterialObjects )
if len ( self . defaultSkinMaterialObjects ) > 0 :
self . make_default_mat ( ' armdefaultskin ' , self . defaultSkinMaterialObjects )
2017-02-15 13:15:24 +01:00
# Auto-enable render-path featues
if len ( bpy . data . cameras ) > 0 :
rebuild_rp = False
cam = bpy . data . cameras [ 0 ]
if cam . rp_translucency_state == ' Auto ' and cam . rp_translucency != transluc_used :
cam . rp_translucency = transluc_used
rebuild_rp = True
if cam . rp_overlays_state == ' Auto ' and cam . rp_overlays != overlays_used :
cam . rp_overlays = overlays_used
rebuild_rp = True
if cam . rp_decals_state == ' Auto ' and cam . rp_decals != decals_used :
cam . rp_decals = decals_used
rebuild_rp = True
2017-08-08 11:47:04 +02:00
# if cam.rp_sss_state == 'Auto' and cam.rp_sss != sss_used:
# cam.rp_sss = sss_used
# rebuild_rp = True
2017-02-15 13:15:24 +01:00
if rebuild_rp :
self . rebuild_render_path ( cam )
def rebuild_render_path ( self , cam ) :
# No shader invalidate required?
make_renderer . make_renderer ( cam )
# Rebuild modified path
2017-03-15 12:30:14 +01:00
assets_path = arm . utils . get_sdk_path ( ) + ' armory/Assets/ '
2017-02-15 13:15:24 +01:00
make_renderpath . build_node_trees ( assets_path )
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 )
2017-05-18 23:40:10 +02:00
def export_grease_pencils ( self ) :
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
2017-05-23 01:03:44 +02:00
assets . add_shader_data ( arm . utils . build_dir ( ) + ' /compiled/Shaders/grease_pencil/grease_pencil.arm ' )
assets . add_shader ( arm . utils . build_dir ( ) + ' /compiled/Shaders/grease_pencil/grease_pencil.frag.glsl ' )
assets . add_shader ( arm . utils . build_dir ( ) + ' /compiled/Shaders/grease_pencil/grease_pencil.vert.glsl ' )
assets . add_shader ( arm . utils . build_dir ( ) + ' /compiled/Shaders/grease_pencil/grease_pencil_shadows.frag.glsl ' )
assets . add_shader ( arm . utils . build_dir ( ) + ' /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 ]
2017-03-15 12:30:14 +01:00
arm . utils . 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
2017-04-08 20:05:35 +02:00
def execute ( self , context , filepath , scene = None ) :
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
2017-04-08 20:05:35 +02:00
self . scene = context . scene if scene == None else scene
2016-09-02 23:11:04 +02:00
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 ( )
2017-03-01 11:45:55 +01:00
self . defaultMaterialObjects = [ ] # If no material is assigned, provide default to mimick cycles
self . defaultSkinMaterialObjects = [ ]
2017-02-15 16:55:46 +01:00
self . materialToArmObjectDict = dict ( )
self . objectToArmObjectDict = dict ( )
2016-09-02 23:11:04 +02:00
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-04-02 13:13:43 +02:00
# Map objects to game objects
o = { }
o [ ' traits ' ] = [ ]
self . objectToArmObjectDict [ bobject ] = o
# Process
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
2017-05-13 17:17:43 +02:00
self . output [ ' name ' ] = arm . utils . safestr ( self . scene . name )
2017-04-01 21:25:57 +02:00
if self . filepath . endswith ( ' .zip ' ) :
2016-10-17 00:02:51 +02:00
self . output [ ' name ' ] + = ' .zip '
2017-04-04 23:11:31 +02:00
# Fix material variants
# Skinned and non-skined objects can not share material
matvars = [ ]
matvars_boskin = [ ]
for bo in self . scene . objects :
if arm . utils . export_bone_data ( bo ) :
matvars_boskin . append ( bo )
for slot in bo . material_slots :
mat_name = slot . material . name + ' _armskin '
mat = bpy . data . materials . get ( mat_name )
if mat == None :
mat = slot . material . copy ( )
mat . name = mat_name
matvars . append ( mat )
slot . material = mat
2017-04-11 23:21:42 +02:00
# Auto-bones
wrd = bpy . data . worlds [ ' Arm ' ]
if wrd . generate_gpu_skin_max_bones_auto :
max_bones = 8
for armature in bpy . data . armatures :
if max_bones < len ( armature . bones ) :
max_bones = len ( armature . bones )
wrd . generate_gpu_skin_max_bones = max_bones
2016-09-02 23:11:04 +02:00
self . output [ ' objects ' ] = [ ]
2017-04-04 23:11:31 +02:00
for bo in self . scene . objects :
if not bo . parent :
self . export_object ( bo , self . scene )
2017-01-29 16:15:04 +01:00
if len ( bpy . data . groups ) > 0 :
self . output [ ' groups ' ] = [ ]
for group in bpy . data . groups :
o = { }
o [ ' name ' ] = group . name
o [ ' object_refs ' ] = [ ]
2017-08-08 16:44:33 +02:00
# Add unparented objects only, then instantiate full object child tree
for bobject in group . objects :
if bobject . parent == None :
# Add external linked objects
if bobject . name not in self . scene . objects :
self . process_bobject ( bobject )
self . export_object ( bobject , self . scene )
o [ ' object_refs ' ] . append ( bobject . name + ' _Lib ' )
else :
o [ ' object_refs ' ] . append ( bobject . name )
2017-01-29 16:15:04 +01:00
self . output [ ' groups ' ] . append ( o )
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 :
2017-05-13 17:17:43 +02:00
if self . scene . name == arm . utils . 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
2017-05-25 16:48:41 +02:00
if not bpy . data . worlds [ ' Arm ' ] . arm_deinterleaved_buffers :
for bobject in self . scene . objects :
if len ( bobject . material_slots ) > 1 :
mat = bobject . material_slots [ 0 ] . material
if mat == None :
2017-01-10 23:36:18 +01:00
continue
2017-05-25 16:48:41 +02:00
vs = mat . vertex_structure
for i in range ( len ( bobject . material_slots ) ) :
nmat = bobject . material_slots [ i ] . material
if nmat == None :
continue
if vs != nmat . vertex_structure :
log . warn ( ' Object ' + bobject . name + ' - unable to bind materials to vertex data, please separate object by material (select object - edit mode - P - By Material) or enable Deinterleaved Buffers in Armory Player ' )
break
2017-01-04 00:13:52 +01:00
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-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 ' )
2017-01-16 15:26:57 +01:00
if self . restoreFrame :
2016-09-02 23:11:04 +02:00
self . scene . frame_set ( originalFrame , originalSubframe )
2017-01-16 15:26:57 +01:00
# Scene root traits
if bpy . data . worlds [ ' Arm ' ] . arm_physics != ' Disabled ' and ArmoryExporter . export_physics :
if not ' traits ' in self . output :
self . output [ ' traits ' ] = [ ]
x = { }
x [ ' type ' ] = ' Script '
x [ ' class_name ' ] = ' armory.trait.internal.PhysicsWorld '
self . output [ ' traits ' ] . append ( x )
2017-08-10 14:10:37 +02:00
ArmoryExporter . import_traits . append ( x [ ' class_name ' ] )
2017-01-16 15:26:57 +01:00
if bpy . data . worlds [ ' Arm ' ] . arm_navigation != ' Disabled ' and ArmoryExporter . export_navigation :
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 )
2017-08-10 14:10:37 +02:00
ArmoryExporter . import_traits . append ( x [ ' class_name ' ] )
2017-01-16 15:26:57 +01:00
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
2017-03-15 12:30:14 +01:00
arm . utils . write_arm ( self . filepath , self . output )
2016-09-02 23:11:04 +02:00
2017-04-04 23:11:31 +02:00
# Remove created material variants
for bo in matvars_boskin :
for slot in bo . material_slots : # Set back to original material
orig_mat = slot . material . name [ : - len ( ' _armskin ' ) ]
slot . material = bpy . data . materials [ orig_mat ]
for mat in matvars :
bpy . data . materials . remove ( mat , do_unlink = True )
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
2017-05-17 17:06:52 +02:00
instance_offsets = [ 0.0 , 0.0 , 0.0 ] # Include parent
2016-09-02 23:11:04 +02:00
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
2017-01-29 16:15:04 +01:00
# Instance render groups with same children?
# elif n.dupli_type == 'GROUP' and n.dupli_group != None:
# is_instanced = True
# instance_offsets = []
# for sn in bpy.data.groups[n.dupli_group].objects:
# loc = sn.matrix_local.to_translation()
# instance_offsets.append(loc.x)
# instance_offsets.append(loc.y)
# instance_offsets.append(loc.z)
# break
2016-09-02 23:11:04 +02:00
return is_instanced , instance_offsets
2016-10-19 13:28:06 +02:00
def preprocess ( self ) :
2017-04-08 20:05:35 +02:00
ArmoryExporter . export_all_flag = True
2016-10-19 13:28:06 +02:00
ArmoryExporter . export_physics = False # Indicates whether rigid body is exported
2016-12-07 10:37:08 +01:00
ArmoryExporter . export_navigation = False
2017-06-20 11:35:24 +02:00
ArmoryExporter . export_ui = 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
2017-08-10 14:10:37 +02:00
ArmoryExporter . import_traits = [ ] # Referenced traits
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
2017-04-08 20:05:35 +02:00
ArmoryExporter . sample_animation_flag = ArmoryExporter . option_sample_animation
2016-09-02 23:11:04 +02:00
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
2017-01-13 00:16:00 +01:00
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
2017-07-04 13:29:01 +02:00
wrd . generate_ocean_level = 0.0 #bobject.location.z
2016-09-02 23:11:04 +02:00
return export_object
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
2017-03-15 12:30:14 +01:00
if arm . utils . is_bone_animation_enabled ( bobject ) or arm . utils . is_object_animation_enabled ( bobject ) :
2016-10-12 17:52:27 +02:00
x = { }
2017-03-20 02:21:13 +01:00
x [ ' frame_time ' ] = 1 / self . scene . render . fps
2016-10-12 17:52:27 +02:00
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
2017-03-15 12:30:14 +01:00
if arm . utils . 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
2017-03-31 14:18:47 +02:00
if hasattr ( bobject , ' my_traitlist ' ) :
for t in bobject . my_traitlist :
if t . enabled_prop == False :
2016-09-02 23:11:04 +02:00
continue
2017-03-31 14:18:47 +02:00
x = { }
if t . type_prop == ' Logic Nodes ' and t . nodes_name_prop != ' ' :
x [ ' type ' ] = ' Script '
2017-05-13 17:17:43 +02:00
x [ ' class_name ' ] = arm . utils . safestr ( bpy . data . worlds [ ' Arm ' ] . arm_project_package ) + ' .node. ' + arm . utils . safesrc ( t . nodes_name_prop )
2017-05-26 11:51:08 +02:00
elif t . type_prop == ' JS Script ' :
2017-03-31 14:18:47 +02:00
basename = t . jsscript_prop . split ( ' . ' ) [ 0 ]
x [ ' type ' ] = ' Script '
x [ ' class_name ' ] = ' armory.trait.internal.JSScript '
2017-05-26 16:05:14 +02:00
x [ ' parameters ' ] = [ " ' " + basename + " ' " ]
2017-05-23 01:03:44 +02:00
scriptspath = arm . utils . get_fp_build ( ) + ' /compiled/scripts/ '
2017-03-31 14:18:47 +02:00
if not os . path . exists ( scriptspath ) :
os . makedirs ( scriptspath )
2017-05-26 11:51:08 +02:00
# Write js to file
assetpath = arm . utils . build_dir ( ) + ' /compiled/scripts/ ' + t . jsscript_prop + ' .js '
targetpath = arm . utils . get_fp ( ) + ' / ' + assetpath
with open ( targetpath , ' w ' ) as f :
f . write ( bpy . data . texts [ t . jsscript_prop ] . as_string ( ) )
assets . add ( assetpath )
2017-05-26 16:05:14 +02:00
elif t . type_prop == ' UI Canvas ' :
2017-06-20 11:35:24 +02:00
ArmoryExporter . export_ui = True
2017-05-26 16:05:14 +02:00
x [ ' type ' ] = ' Script '
x [ ' class_name ' ] = ' armory.trait.internal.CanvasScript '
x [ ' parameters ' ] = [ " ' " + t . canvas_name_prop + " ' " ]
# assets.add(assetpath) # Bundled is auto-added
2017-07-27 11:43:37 +02:00
# Read file list and add canvas assets
assetpath = arm . utils . get_fp ( ) + ' /Bundled/canvas/ ' + t . canvas_name_prop + ' .files '
2017-08-07 10:47:29 +02:00
if os . path . exists ( assetpath ) :
with open ( assetpath ) as f :
fileList = f . read ( ) . splitlines ( )
for asset in fileList :
2017-08-09 23:18:48 +02:00
# Relative to the root/Bundled/canvas path
asset = asset [ 6 : ] # Strip ../../ to start in project root
2017-08-07 10:47:29 +02:00
assets . add ( asset )
2017-03-31 14:18:47 +02:00
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. '
# 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 ' :
ArmoryExporter . export_navigation = True
2017-05-23 01:03:44 +02:00
nav_path = arm . utils . get_fp_build ( ) + ' /compiled/Assets/navigation '
2017-03-31 14:18:47 +02:00
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:
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
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 ) )
f . write ( " %.4f \n " % ( v . co [ 1 ] * bobject . scale . y ) ) # Flipped
for p in bobject . data . polygons :
f . write ( " f " )
for i in reversed ( p . vertices ) : # Flipped normals
f . write ( " %d " % ( i + 1 ) )
f . write ( " \n " )
else :
2017-05-13 17:17:43 +02:00
trait_prefix = arm . utils . safestr ( bpy . data . worlds [ ' Arm ' ] . arm_project_package ) + ' . '
2017-03-31 14:18:47 +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
2017-05-17 17:06:52 +02:00
x [ ' parameters ' ] . append ( pt . name )
2017-07-24 02:27:22 +02:00
if len ( t . my_propstraitlist ) > 0 :
x [ ' props ' ] = [ ]
for pt in t . my_propstraitlist : # Append props
x [ ' props ' ] . append ( pt . name )
x [ ' props ' ] . append ( pt . value )
2017-03-31 14:18:47 +02:00
o [ ' traits ' ] . append ( x )
2016-09-02 23:11:04 +02:00
# 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 '
2017-05-17 17:06:52 +02:00
x [ ' parameters ' ] = [ str ( body_mass ) , str ( shape ) , str ( rb . friction ) , str ( rb . restitution ) ]
2016-09-02 23:11:04 +02:00
if rb . use_margin :
2017-05-17 17:06:52 +02:00
x [ ' parameters ' ] . append ( str ( rb . collision_margin ) )
2016-12-08 14:30:39 +01:00
else :
2017-05-17 17:06:52 +02:00
x [ ' parameters ' ] . append ( ' 0.0 ' )
x [ ' parameters ' ] . append ( str ( rb . linear_damping ) )
x [ ' parameters ' ] . append ( str ( rb . angular_damping ) )
x [ ' parameters ' ] . append ( str ( rb . type == ' PASSIVE ' ) . lower ( ) )
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
2017-05-17 17:06:52 +02:00
cloth_trait [ ' parameters ' ] = [ str ( soft_type ) , str ( bend ) , str ( soft_mod . settings . mass ) , str ( bobject . soft_body_margin ) ]
2017-01-10 10:41:06 +01:00
o [ ' traits ' ] . append ( cloth_trait )
2017-04-02 13:13:43 +02:00
if soft_type == 0 and soft_mod . settings . use_pin_cloth :
self . add_hook_trait ( o , bobject , ' ' , soft_mod . settings . vertex_group_mass )
# RB Constraint
if bobject . rigid_body_constraint != None :
rbc = bobject . rigid_body_constraint
target = rbc . object1 if rbc . object2 . name == bobject . name else rbc . object2
to = self . objectToArmObjectDict [ target ]
2017-07-04 11:11:34 +02:00
if rbc . type == ' HINGE ' :
self . add_constraint_trait ( o , rbc . object1 , rbc . object2 )
else :
self . add_hook_trait ( to , target , bobject . name , ' ' )
2017-04-02 13:13:43 +02:00
# Hook modifier
2017-04-28 12:43:40 +02:00
# hook_mod = None
# for m in bobject.modifiers:
# if m.type == 'HOOK':
# hook_mod = m
# break
# if hook_mod != None:
# group_name = hook_mod.vertex_group
# target_name = hook_mod.object.name
# self.add_hook_trait(o, bobject, target_name, group_name)
2017-01-10 10:41:06 +01:00
2017-04-02 13:13:43 +02:00
# Camera traits
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 :
2017-06-20 11:35:24 +02:00
ArmoryExporter . export_ui = True
2016-09-02 23:11:04 +02:00
console_trait = { }
console_trait [ ' type ' ] = ' Script '
2017-05-06 00:22:15 +02:00
console_trait [ ' class_name ' ] = ' armory.trait.internal.DebugConsole '
2016-09-02 23:11:04 +02:00
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 '
2017-05-17 17:06:52 +02:00
navigation_trait [ ' parameters ' ] = [ str ( arm . utils . get_ease_viewport_camera ( ) ) . lower ( ) ]
2016-09-02 23:11:04 +02:00
o [ ' traits ' ] . append ( navigation_trait )
# 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 )
2017-02-15 16:55:46 +01:00
self . materialToArmObjectDict [ mat ] . append ( o )
2016-09-02 23:11:04 +02:00
else :
self . materialToObjectDict [ mat ] = [ bobject ]
2017-02-15 16:55:46 +01:00
self . materialToArmObjectDict [ mat ] = [ o ]
2016-09-02 23:11:04 +02:00
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 )
2017-08-10 14:10:37 +02:00
for x in o [ ' traits ' ] :
ArmoryExporter . import_traits . append ( x [ ' class_name ' ] )
2016-09-02 23:11:04 +02:00
2017-04-02 13:13:43 +02:00
def add_hook_trait ( self , o , bobject , target_name , group_name ) :
hook_trait = { }
hook_trait [ ' type ' ] = ' Script '
hook_trait [ ' class_name ' ] = ' armory.trait.internal.PhysicsHook '
verts = [ ]
if group_name != ' ' :
group = bobject . vertex_groups [ group_name ] . index
for v in bobject . data . vertices :
for g in v . groups :
if g . group == group :
verts . append ( v . co . x )
verts . append ( v . co . y )
verts . append ( v . co . z )
2017-05-27 12:01:08 +02:00
hook_trait [ ' parameters ' ] = [ " ' " + target_name + " ' " , str ( verts ) ]
2017-04-02 13:13:43 +02:00
o [ ' traits ' ] . append ( hook_trait )
2017-07-04 11:11:34 +02:00
def add_constraint_trait ( self , o , rb1 , rb2 ) :
constr_trait = { }
constr_trait [ ' type ' ] = ' Script '
constr_trait [ ' class_name ' ] = ' armory.trait.internal.PhysicsConstraint '
constr_trait [ ' parameters ' ] = [ " ' " + rb1 . name + " ' " , " ' " + rb2 . name + " ' " ]
o [ ' traits ' ] . append ( constr_trait )
2016-10-19 13:28:06 +02:00
def post_export_world ( self , world , o ) :
2017-01-14 12:44:43 +01:00
defs = bpy . data . worlds [ ' Arm ' ] . world_defs + bpy . data . worlds [ ' Arm ' ] . rp_defs
2016-10-02 19:52:40 +02:00
bgcol = world . world_envtex_color
2017-01-30 12:02:40 +01:00
if ' _LDR ' in defs : # No compositor used
2016-10-02 19:52:40 +02:00
for i in range ( 0 , 3 ) :
bgcol [ i ] = pow ( bgcol [ i ] , 1.0 / 2.2 )
2017-03-15 12:30:14 +01:00
o [ ' background_color ' ] = arm . utils . color_to_int ( bgcol )
2016-10-02 19:52:40 +02:00
2017-05-13 17:17:43 +02:00
wmat_name = arm . utils . safestr ( 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