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
2017-10-25 14:39:41 +02:00
#
2015-10-30 13:23:09 +01:00
# 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 . 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-10-01 11:00:05 +02:00
# Based on https://github.com/Kupoman/blendergltf/blob/master/blendergltf.py
2017-10-01 19:57: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-10-01 19:57:42 +02:00
self . vertex_index = loop . vertex_index
2017-10-01 11:00:05 +02:00
loop_idx = loop . index
2017-10-01 19:57:42 +02:00
self . co = mesh . vertices [ self . vertex_index ] . co [ : ]
2017-10-01 11:00:05 +02:00
self . normal = loop . normal [ : ]
self . uvs = tuple ( layer . data [ loop_idx ] . uv [ : ] for layer in mesh . uv_layers )
2016-09-02 23:11:04 +02:00
self . col = [ 0 , 0 , 0 ]
if len ( mesh . vertex_colors ) > 0 :
2017-10-01 11:00:05 +02:00
self . col = mesh . vertex_colors [ 0 ] . data [ loop_idx ] . color [ : ]
# self.colors = tuple(layer.data[loop_idx].color[:] for layer in mesh.vertex_colors)
self . loop_indices = [ loop_idx ]
2016-09-02 23:11:04 +02:00
# Take the four most influential groups
2017-10-01 19:57: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
2017-11-23 16:37:09 +01:00
( self . uvs == other . uvs ) and
( self . col == other . col )
2016-09-02 23:11:04 +02:00
)
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
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 ] ]
2017-11-20 15:59:22 +01:00
# def write_vector3d(self, vector):
# return [vector[0], vector[1], vector[2]]
2016-09-02 23:11:04 +02:00
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
@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
2017-12-04 11:24:34 +01:00
def find_bone ( self , name ) :
for bobject_ref in self . bobjectBoneArray . 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
2017-10-19 18:27:01 +02:00
# @staticmethod
# def animation_keys_different(fcurve):
# key_count = len(fcurve.keyframe_points)
# if key_count > 0:
# key1 = fcurve.keyframe_points[0].co[1]
# for i in range(1, key_count):
# key2 = fcurve.keyframe_points[i].co[1]
# if math.fabs(key2 - key1) > ExportEpsilon:
# return True
# return False
# @staticmethod
# def animation_tangents_nonzero(fcurve):
# key_count = len(fcurve.keyframe_points)
# if key_count > 0:
# key = fcurve.keyframe_points[0].co[1]
# left = fcurve.keyframe_points[0].handle_left[1]
# right = fcurve.keyframe_points[0].handle_right[1]
# if (math.fabs(key - left) > ExportEpsilon) or (math.fabs(right - key) > ExportEpsilon):
# return True
# for i in range(1, key_count):
# key = fcurve.keyframe_points[i].co[1]
# left = fcurve.keyframe_points[i].handle_left[1]
# right = fcurve.keyframe_points[i].handle_right[1]
# if (math.fabs(key - left) > ExportEpsilon) or (math.fabs(right - key) > ExportEpsilon):
# return True
# return False
2016-09-02 23:11:04 +02:00
2017-11-20 15:59:22 +01:00
# @staticmethod
# def matrices_different(m1, m2):
# for i in range(4):
# for j in range(4):
# if math.fabs(m1[i][j] - m2[i][j]) > ExportEpsilon:
# 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
2017-10-19 18:27:01 +02:00
# @staticmethod
# def animation_present(fcurve, kind):
# if kind != AnimationTypeBezier:
# return ArmoryExporter.animation_keys_different(fcurve)
# 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
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
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
2016-10-19 13:28:06 +02:00
def export_bone ( self , armature , bone , scene , o , action ) :
2017-12-04 11:24:34 +01:00
bobjectRef = self . bobjectBoneArray . get ( bone )
2017-10-10 20:46:44 +02:00
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 )
2017-10-25 14:39:41 +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 )
2017-10-25 14:39:41 +02:00
2017-11-11 18:26:28 +01:00
def export_pose_markers ( self , oanim , action ) :
if action . pose_markers == None :
return
oanim [ ' marker_frames ' ] = [ ]
oanim [ ' marker_names ' ] = [ ]
for m in action . pose_markers :
oanim [ ' marker_frames ' ] . append ( m . frame )
oanim [ ' marker_names ' ] . append ( m . name )
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
2017-11-19 13:38:54 +01:00
animation_flag = False
# m1 = bobject.matrix_local.copy()
2016-09-02 23:11:04 +02:00
# Font in
2017-10-19 18:27:01 +02:00
# for i in range(self.beginFrame, self.endFrame):
# scene.frame_set(i)
# m2 = bobject.matrix_local
# if ArmoryExporter.matrices_different(m1, m2):
2017-11-19 13:38:54 +01:00
# animation_flag = True
2017-10-19 18:27:01 +02:00
# break
2017-11-19 13:38:54 +01:00
animation_flag = bobject . animation_data != None and bobject . animation_data . action != None and bobject . type != ' ARMATURE '
2017-10-19 18:27:01 +02:00
2016-09-02 23:11:04 +02:00
# Font out
2017-11-19 13:38:54 +01:00
if animation_flag :
2017-10-19 18:27:01 +02:00
if not ' object_actions ' in o :
o [ ' object_actions ' ] = [ ]
2017-10-22 13:33:07 +02:00
action = bobject . animation_data . action
aname = arm . utils . safestr ( arm . utils . asset_name ( action ) )
fp = self . get_meshes_file_path ( ' action_ ' + aname , compressed = self . is_compress ( bobject . data ) )
assets . add ( fp )
ext = ' .zip ' if self . is_compress ( bobject . data ) else ' '
o [ ' object_actions ' ] . append ( ' action_ ' + aname + ext )
2017-10-19 18:27:01 +02:00
oaction = { }
2017-10-19 21:28:02 +02:00
oaction [ ' sampled ' ] = True
2017-10-19 18:27:01 +02:00
oaction [ ' name ' ] = action . name
oanim = { }
2017-10-22 13:33:07 +02:00
oaction [ ' anim ' ] = oanim
2016-09-02 23:11:04 +02:00
tracko = { }
tracko [ ' target ' ] = " transform "
2017-11-11 16:39:11 +01:00
tracko [ ' frames ' ] = [ ]
2016-09-02 23:11:04 +02:00
2017-11-11 16:39:11 +01:00
begin_frame , end_frame = int ( action . frame_range [ 0 ] ) , int ( action . frame_range [ 1 ] )
2017-11-19 13:38:54 +01:00
end_frame + = 1
2016-09-02 23:11:04 +02:00
2017-11-09 13:08:01 +01:00
for i in range ( begin_frame , end_frame ) :
2017-11-19 13:38:54 +01:00
tracko [ ' frames ' ] . append ( i - begin_frame )
2017-11-09 13:08:01 +01:00
2017-11-19 13:38:54 +01:00
tracko [ ' frames ' ] . append ( end_frame )
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
2017-11-09 13:08:01 +01:00
for i in range ( begin_frame , end_frame ) :
2016-09-02 23:11:04 +02:00
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
2017-10-19 18:27:01 +02:00
oanim [ ' tracks ' ] = [ tracko ]
2017-11-11 18:26:28 +01:00
self . export_pose_markers ( oanim , action )
2016-09-02 23:11:04 +02:00
2017-10-22 13:33:07 +02:00
if True : #action.arm_cached == False or not os.path.exists(fp):
print ( ' Exporting object action ' + aname )
actionf = { }
actionf [ ' objects ' ] = [ ]
actionf [ ' objects ' ] . append ( oaction )
oaction [ ' type ' ] = ' object '
oaction [ ' name ' ] = aname
oaction [ ' data_ref ' ] = ' '
oaction [ ' transform ' ] = [ ]
arm . utils . write_arm ( fp , actionf )
2017-11-11 16:39:11 +01:00
def export_key_frames ( self , fcurve ) :
2017-03-16 14:53:43 +01:00
keyo = [ ]
2017-11-19 13:38:54 +01:00
key_count = len ( fcurve . keyframe_points )
for i in range ( key_count ) :
2017-11-11 16:39:11 +01:00
frame = fcurve . keyframe_points [ i ] . co [ 0 ] - self . beginFrame
keyo . append ( frame )
2016-09-02 23:11:04 +02:00
return keyo
2017-11-11 16:39:11 +01:00
def export_key_frame_control_points ( self , fcurve ) :
2017-10-01 23:02:21 +02:00
keyminuso = [ ]
2017-11-19 13:38:54 +01:00
key_count = len ( fcurve . keyframe_points )
for i in range ( key_count ) :
2016-09-02 23:11:04 +02:00
ctrl = fcurve . keyframe_points [ i ] . handle_left [ 0 ] - self . beginFrame
2017-10-01 23:02:21 +02:00
keyminuso . append ( ctrl )
keypluso = [ ]
2017-11-19 13:38:54 +01:00
for i in range ( key_count ) :
2016-09-02 23:11:04 +02:00
ctrl = fcurve . keyframe_points [ i ] . handle_right [ 0 ] - self . beginFrame
2017-10-01 23:02:21 +02:00
keypluso . append ( ctrl )
2016-09-02 23:11:04 +02:00
return keyminuso , keypluso
2017-03-16 14:53:43 +01:00
def export_key_values ( self , fcurve ) :
keyo = [ ]
2017-11-19 13:38:54 +01:00
key_count = len ( fcurve . keyframe_points )
for i in range ( key_count ) :
2016-09-02 23:11:04 +02:00
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 = [ ]
2017-11-19 13:38:54 +01:00
key_count = len ( fcurve . keyframe_points )
for i in range ( key_count ) :
2016-09-02 23:11:04 +02:00
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 = [ ]
2017-11-19 13:38:54 +01:00
for i in range ( key_count ) :
2016-09-02 23:11:04 +02:00
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
2017-11-11 16:39:11 +01:00
# Frame and Value structures are given by the kind parameter.
2016-09-02 23:11:04 +02:00
tracko = { }
tracko [ ' target ' ] = target
2017-04-01 21:25:57 +02:00
if kind != AnimationTypeBezier :
2017-11-11 16:39:11 +01:00
tracko [ ' frames ' ] = self . export_key_frames ( fcurve )
2017-03-16 14:53:43 +01:00
tracko [ ' values ' ] = self . export_key_values ( fcurve )
2016-09-02 23:11:04 +02:00
else :
tracko [ ' curve ' ] = ' bezier '
2017-11-11 16:39:11 +01:00
tracko [ ' frames ' ] = self . export_key_frames ( fcurve )
tracko [ ' frames_control_plus ' ] , tracko [ ' frames_control_minus ' ] = self . export_key_frame_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-10-19 18:27:01 +02:00
sampledAnimation = ArmoryExporter . sample_animation_flag or mode == " QUATERNION " or mode == " AXIS_ANGLE "
2016-09-02 23:11:04 +02:00
2017-10-23 01:54:36 +02:00
if not sampledAnimation and bobject . animation_data and bobject . type != ' ARMATURE ' :
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
2017-10-19 18:27:01 +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
2017-10-19 18:27:01 +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
2017-10-19 18:27:01 +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
2017-10-19 18:27:01 +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
2017-10-19 18:27:01 +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
2017-10-19 18:27:01 +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 )
2017-10-19 18:27:01 +02:00
else : # Animated
2016-09-02 23:11:04 +02:00
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
2017-10-19 18:27:01 +02:00
if not ' object_actions ' in o :
o [ ' object_actions ' ] = [ ]
2017-10-25 14:39:41 +02:00
2017-10-19 18:27:01 +02:00
action = bobject . animation_data . action
2017-10-22 13:33:07 +02:00
aname = arm . utils . safestr ( arm . utils . asset_name ( action ) )
fp = self . get_meshes_file_path ( ' action_ ' + aname , compressed = self . is_compress ( bobject . data ) )
assets . add ( fp )
ext = ' .zip ' if self . is_compress ( bobject . data ) else ' '
o [ ' object_actions ' ] . append ( ' action_ ' + aname + ext )
oaction = { }
2017-10-19 18:27:01 +02:00
oaction [ ' name ' ] = action . name
2016-09-02 23:11:04 +02:00
2017-04-01 21:25:57 +02:00
# Export the animation tracks
2017-10-19 18:27:01 +02:00
oanim = { }
2017-10-22 13:33:07 +02:00
oaction [ ' anim ' ] = oanim
2017-10-19 18:27:01 +02:00
oanim [ ' begin ' ] = ( action . frame_range [ 0 ] - self . beginFrame )
oanim [ ' end ' ] = ( action . frame_range [ 1 ] - self . beginFrame )
oanim [ ' tracks ' ] = [ ]
2017-11-11 18:26:28 +01:00
self . export_pose_markers ( oanim , action )
2016-09-02 23:11:04 +02:00
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 )
2017-10-19 18:27:01 +02:00
oanim [ ' tracks ' ] . append ( tracko )
2016-09-02 23:11:04 +02:00
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 )
2017-10-19 18:27:01 +02:00
oanim [ ' tracks ' ] . append ( tracko )
2016-09-02 23:11:04 +02:00
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 )
2017-10-19 18:27:01 +02:00
oanim [ ' tracks ' ] . append ( tracko )
2016-09-02 23:11:04 +02:00
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 )
2017-10-19 18:27:01 +02:00
oanim [ ' tracks ' ] . append ( tracko )
2017-10-22 14:41:35 +02:00
oanim [ ' has_delta ' ] = True
2016-09-02 23:11:04 +02:00
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 )
2017-10-19 18:27:01 +02:00
oanim [ ' tracks ' ] . append ( tracko )
2017-10-22 14:41:35 +02:00
oanim [ ' has_delta ' ] = True
2016-09-02 23:11:04 +02:00
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 )
2017-10-19 18:27:01 +02:00
oanim [ ' tracks ' ] . append ( tracko )
2017-10-22 14:41:35 +02:00
oanim [ ' has_delta ' ] = True
2016-09-02 23:11:04 +02:00
structFlag = True
2017-10-25 14:39:41 +02:00
2017-10-22 13:33:07 +02:00
if True : #action.arm_cached == False or not os.path.exists(fp):
print ( ' Exporting object action ' + aname )
actionf = { }
actionf [ ' objects ' ] = [ ]
actionf [ ' objects ' ] . append ( oaction )
oaction [ ' type ' ] = ' object '
oaction [ ' name ' ] = aname
oaction [ ' data_ref ' ] = ' '
oaction [ ' transform ' ] = [ ]
arm . utils . write_arm ( fp , actionf )
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 :
2017-12-04 11:24:34 +01:00
self . bobjectBoneArray [ 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
2017-10-06 11:16:29 +02:00
self . bobjectArray [ bobject ] = { " objectType " : btype , " structName " : arm . utils . asset_name ( bobject ) }
2016-09-02 23:11:04 +02:00
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
2017-08-19 12:10:06 +02:00
if bobject . type != ' MESH ' or bobject . arm_instanced == 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 :
2017-12-04 11:24:34 +01:00
boneRef = self . find_bone ( 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 ) :
2017-11-19 13:38:54 +01:00
curve_array = self . collect_bone_animation ( armature , bone . name )
animation = len ( curve_array ) != 0 or ArmoryExporter . sample_animation_flag
2016-09-02 23:11:04 +02:00
transform = bone . matrix_local . copy ( )
2017-11-19 13:38:54 +01:00
parent_bone = bone . parent
if parent_bone :
transform = parent_bone . matrix_local . inverted ( ) * transform
2016-09-02 23:11:04 +02:00
2017-11-19 13:38:54 +01:00
pose_bone = armature . pose . bones . get ( bone . name )
if pose_bone :
transform = pose_bone . matrix . copy ( )
parent_pose_bone = pose_bone . parent
if parent_pose_bone :
transform = parent_pose_bone . matrix . inverted ( ) * transform
2016-09-02 23:11:04 +02:00
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
2017-11-19 13:38:54 +01:00
if animation and pose_bone :
begin_frame , end_frame = int ( action . frame_range [ 0 ] ) , int ( action . frame_range [ 1 ] )
# animation_flag = False
# m1 = pose_bone.matrix.copy()
# for i in range(begin_frame, end_frame):
# scene.frame_set(i)
# m2 = pose_bone.matrix
# if ArmoryExporter.matrices_different(m1, m2):
# animation_flag = True
# break
# if animation_flag:
o [ ' anim ' ] = { }
tracko = { }
o [ ' anim ' ] [ ' tracks ' ] = [ tracko ]
tracko [ ' target ' ] = " transform "
tracko [ ' frames ' ] = [ ]
for i in range ( begin_frame , end_frame + 1 ) :
tracko [ ' frames ' ] . append ( i - begin_frame )
tracko [ ' values ' ] = [ ]
self . bone_tracks . append ( ( tracko [ ' values ' ] , pose_bone ) )
2016-09-02 23:11:04 +02:00
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 :
2017-10-06 11:16:29 +02:00
self . materialArray [ material ] = { " structName " : arm . utils . 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 ) :
2017-09-21 13:22:00 +02:00
if psys . settings in self . particleSystemArray : # or not modifier.show_render:
return
2017-10-06 00:02:38 +02:00
if psys . settings . dupli_object == None or psys . settings . render_type != ' OBJECT ' :
return
2017-10-25 14:39:41 +02:00
2017-09-21 13:22:00 +02:00
self . particleSystemArray [ psys . settings ] = { " structName " : psys . settings . name }
2016-09-02 23:11:04 +02:00
pref = { }
pref [ ' name ' ] = psys . name
pref [ ' seed ' ] = psys . seed
2017-09-21 13:22:00 +02:00
pref [ ' particle ' ] = psys . settings . name
2016-09-02 23:11:04 +02:00
o [ ' particle_refs ' ] . append ( pref )
2017-10-04 19:02:55 +02:00
def get_view3d_area ( self ) :
screen = bpy . context . window . screen
for area in screen . areas :
if area . type == ' VIEW_3D ' :
return area
return None
2016-09-02 23:11:04 +02:00
def get_viewport_view_matrix ( self ) :
2017-10-30 20:00:41 +01:00
if self . play_area == None :
self . play_area = self . get_view3d_area ( )
if self . play_area == None :
2017-10-04 19:02:55 +02:00
return None
2017-10-30 20:00:41 +01:00
for space in self . play_area . spaces :
2017-10-04 19:02:55 +02:00
if space . type == ' VIEW_3D ' :
return space . region_3d . view_matrix
2016-09-02 23:11:04 +02:00
return None
def get_viewport_projection_matrix ( self ) :
2017-10-30 20:00:41 +01:00
if self . play_area == None :
self . play_area = self . get_view3d_area ( )
if self . play_area == None :
2017-10-04 19:02:55 +02:00
return None , False
for space in self . play_area . spaces :
if space . type == ' VIEW_3D ' :
# return space.region_3d.perspective_matrix # pesp = window * view
return space . region_3d . window_matrix , space . region_3d . is_perspective
2017-08-10 17:35:11 +02:00
return None , False
def get_viewport_panels_w ( self ) :
w = 0
screen = bpy . context . window . screen
for area in screen . areas :
if area . type == ' VIEW_3D ' :
for region in area . regions :
if region . type == ' TOOLS ' or region . type == ' UI ' :
if region . width > 1 :
w + = region . width
return w
def get_viewport_w ( self ) :
screen = bpy . context . window . screen
for area in screen . areas :
if area . type == ' VIEW_3D ' :
for region in area . regions :
if region . type == ' HEADER ' : # Use header to report full width, panels included
return region . width
return 0
2016-09-02 23:11:04 +02:00
2017-11-19 13:38:54 +01:00
def write_bone_matrices ( self , scene , action ) :
begin_frame , end_frame = int ( action . frame_range [ 0 ] ) , int ( action . frame_range [ 1 ] )
if len ( self . bone_tracks ) > 0 :
for i in range ( begin_frame , end_frame + 1 ) :
scene . frame_set ( i )
for track in self . bone_tracks :
values , pose_bone = track [ 0 ] , track [ 1 ]
parent = pose_bone . parent
if parent :
values + = self . write_matrix ( parent . matrix . inverted ( ) * pose_bone . matrix )
else :
values + = self . write_matrix ( pose_bone . matrix )
2017-10-29 17:29:08 +01:00
def export_object ( self , bobject , scene , 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 " ]
2017-10-29 17:29:08 +01:00
if bobject . parent_type == " BONE " :
o [ ' parent_bone ' ] = bobject . parent_bone
2017-08-19 03:08:42 +02:00
if bobject . hide_render :
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
2017-08-21 12:17:55 +02:00
if bobject . arm_spawn == False :
2016-09-02 23:11:04 +02:00
o [ ' spawn ' ] = False
2017-08-21 12:17:55 +02:00
if bobject . arm_mobile == False :
2017-01-29 16:15:04 +01:00
o [ ' mobile ' ] = False
if bobject . dupli_type == ' GROUP ' and bobject . dupli_group != None :
2017-11-23 22:12:39 +01:00
o [ ' group_ref ' ] = bobject . dupli_group . name
2016-09-02 23:11:04 +02:00
2017-11-27 00:13:37 +01:00
if bobject . users_group != None and len ( bobject . users_group ) > 0 :
o [ ' groups ' ] = [ ]
for g in bobject . users_group :
o [ ' groups ' ] . append ( g . name )
2017-09-21 18:30:02 +02:00
if bobject . arm_tilesheet != ' ' :
o [ ' tilesheet_ref ' ] = bobject . arm_tilesheet
o [ ' tilesheet_action_ref ' ] = bobject . arm_tilesheet_action
2017-08-19 03:08:42 +02:00
layer_found = False
for l in self . active_layers :
if bobject . layers [ l ] == True :
layer_found = True
break
2017-09-10 15:37:38 +02:00
if bpy . app . version > = ( 2 , 80 , 1 ) : # 2.8 Spawn all layers for now
layer_found = True
2017-08-19 03:08:42 +02:00
if layer_found == False :
o [ ' spawn ' ] = False
2016-09-02 23:11:04 +02:00
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 :
2017-10-06 11:16:29 +02:00
objname = arm . utils . asset_name ( objref )
2016-12-05 01:54:01 +01:00
# Lods
2017-08-21 12:17:55 +02:00
if bobject . type == ' MESH ' and hasattr ( objref , ' arm_lodlist ' ) and len ( objref . arm_lodlist ) > 0 :
2016-12-05 01:54:01 +01:00
o [ ' lods ' ] = [ ]
2017-08-21 12:17:55 +02:00
for l in objref . arm_lodlist :
2016-12-05 01:54:01 +01:00
if l . enabled_prop == False :
continue
lod = { }
lod [ ' object_ref ' ] = l . name
lod [ ' screen_size ' ] = l . screen_size_prop
o [ ' lods ' ] . append ( lod )
2017-08-21 12:17:55 +02:00
if objref . arm_lod_material :
2017-08-23 00:53:26 +02:00
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
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
o [ ' material_refs ' ] = [ ]
for i in range ( len ( bobject . material_slots ) ) :
2017-08-19 12:10:06 +02:00
self . export_material_ref ( bobject , bobject . material_slots [ i ] . material , i , o )
2017-08-21 12:17:55 +02:00
if bobject . material_slots [ i ] . material != None and bobject . material_slots [ i ] . material . arm_decal :
2017-08-19 12:10:06 +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 )
2017-10-25 14:39:41 +02:00
2017-12-03 11:44:03 +01:00
o [ ' dimensions ' ] = [ 0 , 0 , 0 ]
for i in range ( 0 , 3 ) :
if bobject . scale [ i ] != 0 :
o [ ' dimensions ' ] [ i ] = bobject . dimensions [ i ] / bobject . scale [ i ]
2017-01-07 13:50:55 +01:00
# Origin not in geometry center
2017-08-21 12:17:55 +02:00
if hasattr ( bobject . data , ' arm_aabb ' ) :
2017-11-26 22:23:17 +01:00
dx = bobject . data . arm_aabb [ 0 ]
dy = bobject . data . arm_aabb [ 1 ]
dz = bobject . data . arm_aabb [ 2 ]
2017-01-07 13:50:55 +01:00
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)
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 " ]
# Export the transform. If object is animated, then animation tracks are exported here
2017-12-04 12:59:18 +01:00
if bobject . type != ' ARMATURE ' and bobject . animation_data != None :
2017-10-19 18:27:01 +02:00
action = bobject . animation_data . action
export_actions = [ action ]
for track in bobject . animation_data . nla_tracks :
if track . strips == None :
continue
for strip in track . strips :
2017-12-04 12:59:18 +01:00
if strip . action == None or strip . action in export_actions :
2017-10-19 18:27:01 +02:00
continue
export_actions . append ( strip . action )
orig_action = action
for a in export_actions :
bobject . animation_data . action = a
self . export_object_transform ( bobject , scene , o )
2017-12-04 12:59:18 +01:00
if len ( export_actions ) > = 2 and export_actions [ 0 ] == None : # No action assigned
o [ ' object_actions ' ] . insert ( 0 , ' null ' )
2017-10-19 18:27:01 +02:00
bobject . animation_data . action = orig_action
else :
self . export_object_transform ( bobject , scene , o )
2016-09-02 23:11:04 +02:00
2017-10-29 17:29:08 +01:00
# If the object is parented to a bone and is not relative, then undo the bone's transform
if bobject . parent_type == " BONE " :
armature = bobject . parent . data
bone = armature . bones [ bobject . parent_bone ]
if not bone . use_relative_parent :
2017-10-30 01:50:39 +01:00
bone_translation = Vector ( ( 0 , bone . length , 0 ) ) + bone . head
2017-10-29 17:39:34 +01:00
o [ ' parent_bone_tail ' ] = [ bone_translation [ 0 ] , bone_translation [ 1 ] , bone_translation [ 2 ] ]
2017-10-29 17:29:08 +01:00
2016-09-02 23:11:04 +02:00
# Viewport Camera - overwrite active camera matrix with viewport matrix
2017-08-19 03:08:42 +02:00
if type == NodeTypeCamera and bpy . data . worlds [ ' Arm ' ] . arm_play_camera != ' Scene ' 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-09-05 23:39:24 +02:00
adata = bobject . animation_data
# Active action
2017-09-07 13:42:46 +02:00
if adata != None :
action = adata . action
if action == None :
log . warn ( ' Object ' + bobject . name + ' - No action assigned, setting to pose ' )
2017-04-11 23:21:42 +02:00
bobject . animation_data_create ( )
actions = bpy . data . actions
2017-09-07 13:42:46 +02:00
action = actions . get ( ' armorypose ' )
if action == None :
action = actions . new ( name = ' armorypose ' )
2017-04-11 23:21:42 +02:00
2017-09-05 23:39:24 +02:00
# Export actions
2017-08-19 03:08:42 +02:00
export_actions = [ action ]
2017-10-10 09:57:23 +02:00
# hasattr - armature modifier may reference non-parent armature object to deform with
if hasattr ( adata , ' nla_tracks ' ) and adata . nla_tracks != None :
2017-09-05 23:39:24 +02:00
for track in adata . nla_tracks :
if track . strips == None :
continue
for strip in track . strips :
if strip . action == None :
continue
if strip . action . name == action . name :
continue
export_actions . append ( strip . action )
2017-10-06 11:16:29 +02:00
armatureid = arm . utils . safestr ( arm . utils . asset_name ( bdata ) )
2017-09-05 23:39:24 +02:00
ext = ' .zip ' if self . is_compress ( bdata ) else ' '
2017-10-19 18:27:01 +02:00
o [ ' bone_actions ' ] = [ ]
2017-10-10 09:57:23 +02:00
for action in export_actions :
aname = arm . utils . safestr ( arm . utils . asset_name ( action ) )
2017-10-19 18:27:01 +02:00
o [ ' bone_actions ' ] . append ( ' action_ ' + armatureid + ' _ ' + aname + ext )
2017-04-11 23:21:42 +02:00
2017-09-05 23:39:24 +02:00
orig_action = bobject . animation_data . action
2017-04-11 23:21:42 +02:00
for action in export_actions :
2017-10-10 09:57:23 +02:00
aname = arm . utils . safestr ( arm . utils . asset_name ( action ) )
2017-04-11 23:21:42 +02:00
bobject . animation_data . action = action
2017-10-10 09:57:23 +02:00
fp = self . get_meshes_file_path ( ' action_ ' + armatureid + ' _ ' + aname , compressed = self . is_compress ( bdata ) )
2017-04-11 23:21:42 +02:00
assets . add ( fp )
2017-10-10 09:57:23 +02:00
if bdata . arm_cached == False or not os . path . exists ( fp ) :
2017-10-22 13:33:07 +02:00
print ( ' Exporting armature action ' + aname )
2017-04-11 23:21:42 +02:00
bones = [ ]
2017-11-19 13:38:54 +01:00
self . bone_tracks = [ ]
2017-04-11 23:21:42 +02:00
for bone in bdata . bones :
if not bone . parent :
boneo = { }
self . export_bone ( bobject , bone , scene , boneo , action )
bones . append ( boneo )
2017-11-19 13:38:54 +01:00
self . write_bone_matrices ( scene , action )
2017-11-11 18:26:28 +01:00
if len ( bones ) > 0 and ' anim ' in bones [ 0 ] :
self . export_pose_markers ( bones [ 0 ] [ ' anim ' ] , action )
2017-09-05 23:39:24 +02:00
# Save action separately
action_obj = { }
2017-10-10 09:57:23 +02:00
action_obj [ ' name ' ] = aname
2017-09-05 23:39:24 +02:00
action_obj [ ' objects ' ] = bones
arm . utils . write_arm ( fp , action_obj )
bobject . animation_data . action = orig_action
2017-10-22 13:33:07 +02:00
# TODO: cache per action
2017-10-10 09:57:23 +02:00
bdata . arm_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
2017-08-19 12:10:06 +02:00
if bobject . type != ' MESH ' or bobject . arm_instanced == False :
2016-09-02 23:11:04 +02:00
for subbobject in bobject . children :
2017-10-29 17:29:08 +01:00
self . export_object ( subbobject , scene , o )
2016-09-02 23:11:04 +02:00
2017-11-20 15:59:22 +01:00
def export_skin ( self , bobject , armature , vert_list , 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 )
2017-10-10 09:57:23 +02:00
max_bones = bpy . data . worlds [ ' Arm ' ] . arm_skin_max_bones
2017-04-11 23:21:42 +02:00
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 ) :
2017-12-04 11:24:34 +01:00
boneRef = self . find_bone ( 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
2017-12-09 10:32:08 +01:00
oskel [ ' transformsI ' ] = [ ]
if bpy . data . worlds [ ' Arm ' ] . arm_skin == ' CPU ' :
for i in range ( bone_count ) :
skeletonI = ( armature . matrix_world * bone_array [ i ] . matrix_local ) . inverted ( )
oskel [ ' transformsI ' ] . append ( self . write_matrix ( skeletonI ) )
else :
for i in range ( bone_count ) :
skeletonI = ( armature . matrix_world * bone_array [ i ] . matrix_local ) . inverted ( )
skeletonI = skeletonI * bobject . matrix_world
oskel [ ' transformsI ' ] . append ( self . write_matrix ( skeletonI ) )
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
2017-11-19 13:38:54 +01:00
vertices = bobject . data . vertices
for v in vert_list :
2017-04-11 23:21:42 +02:00
bone_count = 0
total_weight = 0.0
bone_values = [ ]
2017-11-19 13:38:54 +01:00
for g in vertices [ v . vertex_index ] . groups :
bone_index = group_remap [ g . group ]
bone_weight = g . weight
2017-04-11 23:21:42 +02:00
if bone_index > = 0 and bone_weight != 0.0 :
bone_values . append ( ( bone_weight , bone_index ) )
total_weight + = bone_weight
bone_count + = 1
2017-10-01 23:20:47 +02:00
# Take highest weights
2017-10-03 16:47:42 +02:00
bone_values . sort ( )
bone_values . reverse ( ) ;
2017-10-01 23:20:47 +02:00
2017-04-11 23:21:42 +02:00
if bone_count > 4 : # Four bones max
bone_count = 4
bone_values = bone_values [ : 4 ]
warn_bones = True
bone_count_array . append ( bone_count )
2017-10-01 23:20:47 +02:00
for bv in bone_values :
2017-04-11 23:21:42 +02:00
bone_weight_array . append ( bv [ 0 ] )
2017-10-01 23:20:47 +02:00
bone_index_array . append ( bv [ 1 ] )
2017-04-11 23:21:42 +02:00
if total_weight != 0.0 :
normalizer = 1.0 / total_weight
2017-10-01 23:20:47 +02:00
for i in range ( - bone_count , 0 ) :
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):
2017-12-04 11:24:34 +01:00
# boneRef = self.find_bone(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 ] ) )
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
tangent = ArmoryExporter . calc_tangent ( v0 , v1 , v2 , uv0 , uv1 , uv2 )
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
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
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
# 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
2017-08-21 12:17:55 +02:00
bobject . data . arm_cached = True
2017-11-03 01:22:07 +01:00
bobject . arm_cached = True
2016-11-28 14:40:07 +01:00
if bobject . type != ' FONT ' and bobject . type != ' META ' :
2017-08-21 12:17:55 +02:00
bobject . data . arm_cached_verts = len ( bobject . data . vertices )
bobject . data . arm_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
2017-11-26 19:36:14 +01:00
def export_mesh_data ( 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
2017-10-25 14:39:41 +02:00
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 :
2017-10-01 19:57:42 +02:00
t0data [ i * 2 ] = vtx . uvs [ 0 ] [ 0 ]
t0data [ i * 2 + 1 ] = 1.0 - vtx . uvs [ 0 ] [ 1 ] # Reverse TCY
2017-05-17 17:06:52 +02:00
if has_tex1 :
2017-10-01 19:57:42 +02:00
t1data [ i * 2 ] = vtx . uvs [ 1 ] [ 0 ]
t1data [ i * 2 + 1 ] = 1.0 - vtx . uvs [ 1 ] [ 1 ]
2017-05-17 17:06:52 +02:00
if has_col > 0 :
2017-10-03 15:11:15 +02:00
cdata [ i * 3 ] = pow ( vtx . col [ 0 ] , 2.2 )
cdata [ i * 3 + 1 ] = pow ( vtx . col [ 1 ] , 2.2 )
cdata [ i * 3 + 2 ] = pow ( vtx . col [ 2 ] , 2.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 )
2017-10-25 14:39:41 +02: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 )
2017-10-25 14:39:41 +02:00
2017-05-17 17:06:52 +02:00
if has_col :
ca = self . make_va ( ' col ' , 3 , cdata )
o [ ' vertex_arrays ' ] . append ( ca )
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
# Indices
prims = { ma . name if ma else ' ' : [ ] for ma in exportMesh . materials }
if not prims :
prims = { ' ' : [ ] }
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
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 ] )
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
# 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 )
2017-10-25 14:39:41 +02:00
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
2017-11-26 19:36:14 +01:00
def export_mesh ( self , objectRef , scene ) :
2016-09-02 23:11:04 +02:00
# This function exports a single mesh object
2017-11-26 19:36:14 +01:00
table = objectRef [ 1 ] [ " objectTable " ]
bobject = table [ 0 ]
2017-05-13 17:17:43 +02:00
oid = arm . utils . safestr ( objectRef [ 1 ] [ " structName " ] )
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-11-20 13:09:16 +01:00
# if hasattr(bobject.data, 'arm_sdfgen') and bobject.data.arm_sdfgen:
# sdf_path = fp.replace('/mesh_', '/sdf_')
# assets.add(sdf_path)
2017-11-03 01:22:07 +01:00
if self . is_mesh_cached ( bobject ) == True and os . path . exists ( fp ) :
2016-09-02 23:11:04 +02:00
return
2017-11-26 19:36:14 +01:00
# Check if mesh is using instanced rendering
is_instanced , instance_offsets = self . object_process_instancing ( bobject , objectRef [ 1 ] [ " objectTable " ] )
# Mesh users have different modifier stack
for i in range ( 1 , len ( table ) ) :
if not self . mod_equal_stack ( bobject , table [ i ] ) :
log . warn ( ' {0} users {1} and {2} differ in modifier stack - use Make Single User(U) - Object & Data for now ' . format ( oid , bobject . name , table [ i ] . name ) )
break
2017-10-06 11:16:29 +02:00
print ( ' Exporting mesh ' + arm . utils . 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
2017-09-10 15:37:38 +02:00
if bpy . app . version > = ( 2 , 80 , 1 ) : # 2.8
2017-09-09 20:53:46 +02:00
exportMesh = bobject . to_mesh ( scene , bpy . context . workspace . render_layer , apply_modifiers , " RENDER " , True , False )
else :
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
2017-11-26 19:36:14 +01:00
vert_list = self . export_mesh_data ( exportMesh , bobject , fp , o )
2017-11-20 15:59:22 +01:00
if armature :
self . export_skin ( 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-08-21 12:17:55 +02:00
if hasattr ( bobject . data , ' arm_aabb ' ) :
bobject . data . arm_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
2017-11-03 01:22:07 +01:00
# Not axis-aligned
# arm_aabb = [bobject.matrix_world * Vector(v) for v in bobject.bound_box]
2017-01-04 00:13:52 +01:00
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
2017-08-21 12:17:55 +02:00
if bobject . data . arm_dynamic_usage :
o [ ' dynamic_usage ' ] = bobject . data . arm_dynamic_usage
2016-09-02 23:11:04 +02:00
self . write_mesh ( bobject , fp , o )
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
2017-08-21 12:17:55 +02:00
o [ ' near_plane ' ] = objref . arm_clip_start
o [ ' far_plane ' ] = objref . arm_clip_end
o [ ' fov ' ] = objref . arm_fov
2017-11-02 23:12:17 +01:00
o [ ' shadows_bias ' ] = objref . arm_shadows_bias * 0.0001
2017-08-21 20:16:06 +02:00
rpdat = arm . utils . get_rp ( )
2017-11-22 21:17:36 +01:00
if rpdat . rp_shadowmap == ' Off ' :
2017-03-03 14:36:01 +01:00
o [ ' shadowmap_size ' ] = 0
else :
2017-08-21 20:16:06 +02:00
o [ ' shadowmap_size ' ] = int ( rpdat . rp_shadowmap )
2016-09-02 23:11:04 +02:00
if o [ ' type ' ] == ' sun ' : # Scale bias for ortho light matrix
2017-11-15 13:34:51 +01:00
o [ ' shadows_bias ' ] * = 25.0
2017-12-05 22:00:27 +01:00
if o [ ' shadowmap_size ' ] > 1024 :
o [ ' shadows_bias ' ] * = 1 / ( o [ ' shadowmap_size ' ] / 1024 ) # Less bias for bigger maps
2017-11-02 23:12:17 +01:00
if ( objtype == ' POINT ' or objtype == ' SPOT ' ) and objref . shadow_soft_size > 0.1 :
o [ ' lamp_size ' ] = objref . shadow_soft_size * 10 # Match to Cycles
2017-09-07 15:08:23 +02:00
gapi = arm . utils . get_gapi ( )
2017-10-01 19:42:47 +02:00
mobile_mat = rpdat . arm_material_model == ' Mobile ' or rpdat . arm_material_model == ' Solid '
2017-10-01 19:09:09 +02:00
if objtype == ' POINT ' and objref . arm_omni_shadows and not gapi . startswith ( ' direct3d ' ) and not mobile_mat :
2017-03-18 18:44:21 +01:00
o [ ' fov ' ] = 1.5708 # 90 deg
2017-08-19 03:08:42 +02:00
o [ ' shadowmap_cube ' ] = True
2017-11-06 14:57:08 +01:00
o [ ' shadows_bias ' ] * = 2.0
2016-09-02 23:11:04 +02:00
2017-10-27 09:05:03 +02:00
if bpy . app . version > = ( 2 , 80 , 1 ) and self . scene . view_render . engine == ' BLENDER_EEVEE ' :
o [ ' color ' ] = [ objref . color [ 0 ] , objref . color [ 1 ] , objref . color [ 2 ] ]
o [ ' strength ' ] = objref . energy
if o [ ' type ' ] == ' point ' or o [ ' type ' ] == ' spot ' :
o [ ' strength ' ] * = 1.5
elif o [ ' type ' ] == ' sun ' :
o [ ' strength ' ] * = 0.325
elif objref . node_tree != None :
tree = objref . node_tree
2017-08-30 09:19:10 +02:00
for n in tree . nodes :
2017-10-27 09:05:03 +02:00
# Emission only for now
2017-08-30 09:19:10 +02:00
if n . type == ' EMISSION ' :
col = n . inputs [ 0 ] . default_value
o [ ' color ' ] = [ col [ 0 ] , col [ 1 ] , col [ 2 ] ]
o [ ' strength ' ] = n . inputs [ 1 ] . default_value
# Normalize lamp strength
if o [ ' type ' ] == ' point ' or o [ ' type ' ] == ' spot ' :
o [ ' strength ' ] * = 0.026
elif o [ ' type ' ] == ' area ' :
o [ ' strength ' ] * = 0.26
elif o [ ' type ' ] == ' sun ' :
o [ ' strength ' ] * = 0.325
# 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
break
else :
2017-11-16 10:43:34 +01:00
# o['color'] = [1.0, 1.0, 1.0]
o [ ' color ' ] = [ objref . color [ 0 ] , objref . color [ 1 ] , objref . color [ 2 ] ]
2017-08-30 09:19:10 +02:00
o [ ' strength ' ] = 100.0 * 0.026
o [ ' type ' ] = ' point '
2016-11-07 16:11:35 +01:00
2016-09-02 23:11:04 +02:00
self . output [ ' lamp_datas ' ] . append ( o )
2017-09-07 13:42:46 +02:00
def get_camera_clear_color ( self ) :
2017-11-20 14:39:59 +01:00
if self . scene . world == None :
return [ 0.051 , 0.051 , 0.051 , 1.0 ]
if self . scene . world . node_tree == None :
2017-11-16 10:43:34 +01:00
c = self . scene . world . horizon_color
return [ c [ 0 ] , c [ 1 ] , c [ 2 ] , 1.0 ]
2017-11-20 14:39:59 +01:00
if ' Background ' in self . scene . world . node_tree . nodes :
background_node = self . scene . world . node_tree . nodes [ ' Background ' ]
col = background_node . inputs [ 0 ] . default_value
strength = background_node . inputs [ 1 ] . default_value
ar = [ col [ 0 ] * strength , col [ 1 ] * strength , col [ 2 ] * strength , col [ 3 ] ]
ar [ 0 ] = max ( min ( ar [ 0 ] , 1.0 ) , 0.0 )
ar [ 1 ] = max ( min ( ar [ 1 ] , 1.0 ) , 0.0 )
ar [ 2 ] = max ( min ( ar [ 2 ] , 1.0 ) , 0.0 )
ar [ 3 ] = max ( min ( ar [ 3 ] , 1.0 ) , 0.0 )
return ar
else :
return [ 0.051 , 0.051 , 0.051 , 1.0 ]
2017-11-16 10:43:34 +01:00
2017-11-02 23:12:17 +01:00
def extract_projection ( self , o , proj , with_planes = True ) :
2017-10-24 10:49:57 +02:00
a = proj [ 0 ] [ 0 ]
b = proj [ 1 ] [ 1 ]
c = proj [ 2 ] [ 2 ]
d = proj [ 2 ] [ 3 ]
k = ( c - 1.0 ) / ( c + 1.0 )
o [ ' fov ' ] = 2.0 * math . atan ( 1.0 / b )
2017-11-02 23:12:17 +01:00
if with_planes :
o [ ' near_plane ' ] = ( d * ( 1.0 - k ) ) / ( 2.0 * k )
o [ ' far_plane ' ] = k * o [ ' near_plane ' ] ;
2017-10-24 10:49:57 +02:00
2016-10-19 13:28:06 +02:00
def export_camera ( self , objectRef ) :
2016-09-02 23:11:04 +02:00
o = { }
o [ ' name ' ] = objectRef [ 1 ] [ " structName " ]
objref = objectRef [ 0 ]
2017-10-24 10:49:57 +02:00
camera = objectRef [ 1 ] [ " objectTable " ] [ 0 ]
render = self . scene . render
proj = camera . calc_matrix_camera (
render . resolution_x ,
render . resolution_y ,
render . pixel_aspect_x ,
render . pixel_aspect_y )
self . extract_projection ( o , proj )
2016-09-02 23:11:04 +02:00
2017-08-19 12:10:06 +02:00
wrd = bpy . data . worlds [ ' Arm ' ]
2017-10-30 20:00:41 +01:00
if wrd . arm_play_camera != ' Scene ' :
pw = self . get_viewport_panels_w ( ) # Tool shelf and properties hidden
2017-08-10 17:35:11 +02:00
proj , is_persp = self . get_viewport_projection_matrix ( )
2017-11-02 23:12:17 +01:00
windowed = not ArmoryExporter . in_viewport
if proj != None and is_persp and ( pw == 0 or windowed ) :
self . extract_projection ( o , proj , with_planes = False )
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
if objref . type == ' PERSP ' :
o [ ' type ' ] = ' perspective '
else :
o [ ' type ' ] = ' orthographic '
2017-08-21 12:17:55 +02:00
if objref . arm_render_to_texture :
o [ ' render_to_texture ' ] = True
o [ ' texture_resolution_x ' ] = int ( objref . arm_texture_resolution_x )
o [ ' texture_resolution_y ' ] = int ( objref . arm_texture_resolution_y )
2016-09-02 23:11:04 +02:00
2017-08-21 12:17:55 +02:00
o [ ' frustum_culling ' ] = objref . arm_frustum_culling
2017-08-21 15:36:21 +02:00
o [ ' render_path ' ] = ' armory_default/armory_default '
2017-09-07 13:42:46 +02:00
o [ ' clear_color ' ] = self . get_camera_clear_color ( )
2016-09-02 23:11:04 +02:00
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
2017-10-25 14:39:41 +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
2017-08-21 12:17:55 +02:00
o [ ' loop ' ] = objref . arm_loop
o [ ' stream ' ] = objref . arm_stream
2016-09-23 00:34:42 +02:00
o [ ' volume ' ] = objref . volume
o [ ' pitch ' ] = objref . pitch
o [ ' attenuation ' ] = objref . attenuation
2018-01-02 22:29:51 +01:00
o [ ' play_on_start ' ] = objref . arm_play_on_start
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 ]
2017-08-21 15:36:21 +02:00
make_material . parse ( mat , o , mat_users , mat_armusers )
2017-03-01 11:45:55 +01:00
self . output [ ' material_datas ' ] . append ( o )
bpy . data . materials . remove ( mat )
2017-08-21 12:17:55 +02:00
if bpy . data . worlds [ ' Arm ' ] . arm_culling == False :
2017-08-10 14:39:08 +02:00
o [ ' override_context ' ] = { }
o [ ' override_context ' ] [ ' cull_mode ' ] = ' none '
2017-03-01 11:45:55 +01:00
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
2017-08-21 15:36:21 +02:00
mat_batch . build ( self . materialArray , mat_users , mat_armusers )
2017-03-14 20:43:54 +01:00
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
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
o = { }
o [ ' name ' ] = materialRef [ 1 ] [ " structName " ]
2017-10-25 14:39:41 +02:00
2017-09-28 00:48:57 +02:00
if material . arm_skip_context != ' ' :
o [ ' skip_context ' ] = material . arm_skip_context
2017-08-21 12:17:55 +02:00
if material . arm_two_sided or wrd . arm_culling == False :
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 '
2017-09-17 16:59:00 +02:00
elif material . arm_cull_mode != ' clockwise ' :
2017-07-06 23:47:12 +02:00
o [ ' override_context ' ] = { }
2017-08-21 12:17:55 +02:00
o [ ' override_context ' ] [ ' cull_mode ' ] = material . arm_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
2017-08-21 15:36:21 +02:00
sd , rpasses = make_material . parse ( material , o , mat_users , mat_armusers )
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
2017-09-29 01:18:57 +02:00
for con in o [ ' contexts ' ] : # TODO: blend context
if con [ ' name ' ] == ' mesh ' and material . arm_blending :
con [ ' name ' ] = ' blend '
2017-05-25 16:48:41 +02:00
# 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 :
2017-08-21 12:17:55 +02:00
ob . data . arm_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
2017-08-19 12:10:06 +02:00
rebuild_rp = False
2017-08-21 20:16:06 +02:00
rpdat = arm . utils . get_rp ( )
if rpdat . rp_translucency_state == ' Auto ' and rpdat . rp_translucency != transluc_used :
rpdat . rp_translucency = transluc_used
2017-08-19 12:10:06 +02:00
rebuild_rp = True
2017-08-21 20:16:06 +02:00
if rpdat . rp_overlays_state == ' Auto ' and rpdat . rp_overlays != overlays_used :
rpdat . rp_overlays = overlays_used
2017-08-19 12:10:06 +02:00
rebuild_rp = True
2017-08-21 20:16:06 +02:00
if rpdat . rp_decals_state == ' Auto ' and rpdat . rp_decals != decals_used :
rpdat . rp_decals = decals_used
2017-08-19 12:10:06 +02:00
rebuild_rp = True
2017-08-21 20:16:06 +02:00
# if rpdat.rp_sss_state == 'Auto' and rpdat.rp_sss != sss_used:
# rpdat.rp_sss = sss_used
2017-08-19 12:10:06 +02:00
# rebuild_rp = True
if rebuild_rp :
2017-11-22 21:17:36 +01:00
make_renderpath . build ( )
2016-09-02 23:11:04 +02:00
2016-09-30 23:24:18 +02:00
def export_particle_systems ( self ) :
2017-09-21 18:30:02 +02:00
if len ( self . particleSystemArray ) > 0 :
self . output [ ' particle_datas ' ] = [ ]
2016-09-02 23:11:04 +02:00
for particleRef in self . particleSystemArray . items ( ) :
o = { }
psettings = particleRef [ 0 ]
if psettings == None :
continue
2017-10-06 00:02:38 +02:00
if psettings . dupli_object == None or psettings . render_type != ' OBJECT ' :
continue
2016-09-02 23:11:04 +02:00
o [ ' name ' ] = particleRef [ 1 ] [ " structName " ]
2017-09-29 01:18:57 +02:00
if psettings . arm_gpu_sim :
o [ ' gpu_sim ' ] = True
2017-10-04 14:13:36 +02:00
o [ ' type ' ] = 0 if psettings . type == ' EMITTER ' else 1 # HAIR
2017-10-15 18:16:55 +02:00
o [ ' loop ' ] = psettings . arm_loop
2017-09-25 23:26:29 +02:00
# Emission
2017-09-29 17:00:21 +02:00
o [ ' count ' ] = psettings . count * psettings . arm_count_mult
2017-09-25 23:26:29 +02:00
o [ ' frame_start ' ] = psettings . frame_start
o [ ' frame_end ' ] = psettings . frame_end
2016-09-02 23:11:04 +02:00
o [ ' lifetime ' ] = psettings . lifetime
2017-09-25 23:26:29 +02:00
o [ ' lifetime_random ' ] = psettings . lifetime_random
o [ ' emit_from ' ] = 1 if psettings . emit_from == ' VOLUME ' else 0 # VERT, FACE
# Velocity
# o['normal_factor'] = psettings.normal_factor;
# o['tangent_factor'] = psettings.tangent_factor;
# o['tangent_phase'] = psettings.tangent_phase;
2016-09-02 23:11:04 +02:00
o [ ' object_align_factor ' ] = [ psettings . object_align_factor [ 0 ] , psettings . object_align_factor [ 1 ] , psettings . object_align_factor [ 2 ] ]
2017-09-25 23:26:29 +02:00
# o['object_factor'] = psettings.object_factor;
2016-09-02 23:11:04 +02:00
o [ ' factor_random ' ] = psettings . factor_random
2017-09-25 23:26:29 +02:00
# Physics
o [ ' physics_type ' ] = 1 if psettings . physics_type == ' NEWTON ' else 0
2017-09-27 00:04:47 +02:00
o [ ' particle_size ' ] = psettings . particle_size
o [ ' size_random ' ] = psettings . size_random
o [ ' mass ' ] = psettings . mass
2017-09-25 23:26:29 +02:00
# Render
2017-09-27 00:04:47 +02:00
o [ ' dupli_object ' ] = psettings . dupli_object . name
self . objectToArmObjectDict [ psettings . dupli_object ] [ ' is_particle ' ] = True
2017-10-15 15:25:47 +02:00
# Field weights
o [ ' weight_gravity ' ] = psettings . effector_weights . gravity
2016-09-02 23:11:04 +02:00
self . output [ ' particle_datas ' ] . append ( o )
2017-10-25 14:39:41 +02:00
2017-09-21 18:30:02 +02:00
def export_tilesheets ( self ) :
wrd = bpy . data . worlds [ ' Arm ' ]
if len ( wrd . arm_tilesheetlist ) > 0 :
self . output [ ' tilesheet_datas ' ] = [ ]
for ts in wrd . arm_tilesheetlist :
o = { }
o [ ' name ' ] = ts . name
o [ ' tilesx ' ] = ts . tilesx_prop
o [ ' tilesy ' ] = ts . tilesy_prop
o [ ' framerate ' ] = ts . framerate_prop
o [ ' actions ' ] = [ ]
for tsa in ts . arm_tilesheetactionlist :
ao = { }
ao [ ' name ' ] = tsa . name
ao [ ' start ' ] = tsa . start_prop
ao [ ' end ' ] = tsa . end_prop
ao [ ' loop ' ] = tsa . loop_prop
o [ ' actions ' ] . append ( ao )
self . output [ ' tilesheet_datas ' ] . append ( o )
2016-09-30 23:24:18 +02:00
def export_worlds ( self ) :
2016-09-02 23:11:04 +02:00
worldRef = self . scene . world
if worldRef != None :
o = { }
w = worldRef
o [ ' name ' ] = w . name
2016-10-19 13:28:06 +02:00
self . post_export_world ( w , o )
2016-09-02 23:11:04 +02:00
self . output [ ' world_datas ' ] . append ( o )
2016-10-15 12:17:33 +02:00
def is_compress ( self , obj ) :
2017-08-21 12:17:55 +02:00
return ArmoryExporter . compress_enabled and obj . arm_compress
2016-10-15 12:17:33 +02:00
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 ' ] = [ ] ;
2017-11-26 19:36:14 +01:00
self . export_mesh ( objectRef , scene )
2016-09-02 23:11:04 +02:00
2017-10-04 11:31:24 +02:00
def execute ( self , context , filepath , scene = None , write_capture_info = False , play_area = None ) :
2016-09-02 23:11:04 +02:00
profile_time = time . time ( )
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
self . output = { }
2016-10-19 13:28:06 +02:00
self . filepath = filepath
2017-10-04 11:31:24 +02:00
self . play_area = play_area
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
2017-10-10 20:46:44 +02:00
print ( ' Exporting ' + arm . utils . asset_name ( self . scene ) )
2017-11-19 13:38:54 +01:00
current_frame , current_subframe = scene . frame_current , scene . frame_subframe
2016-09-02 23:11:04 +02:00
self . beginFrame = self . scene . frame_start
2017-10-01 23:02:21 +02:00
self . output [ ' frame_time ' ] = 1.0 / ( self . scene . render . fps / self . scene . render . fps_base )
2016-09-02 23:11:04 +02:00
2017-10-03 20:27:21 +02:00
if write_capture_info :
self . output [ ' capture_info ' ] = { }
2017-10-04 09:26:00 +02:00
self . output [ ' capture_info ' ] [ ' path ' ] = bpy . path . abspath ( self . scene . render . filepath )
2017-10-03 20:27:21 +02:00
self . output [ ' capture_info ' ] [ ' frame_start ' ] = self . scene . frame_start
self . output [ ' capture_info ' ] [ ' frame_end ' ] = self . scene . frame_end
2016-09-02 23:11:04 +02:00
self . bobjectArray = { }
2017-12-04 11:24:34 +01:00
self . bobjectBoneArray = { }
2016-09-02 23:11:04 +02:00
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 = [ ]
2017-11-19 13:38:54 +01:00
self . bone_tracks = [ ]
2016-09-02 23:11:04 +02:00
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
2017-09-10 15:37:38 +02:00
if bpy . app . version > = ( 2 , 80 , 1 ) : # 2.8
scene_objects = self . scene . master_collection . collections [ 0 ] . objects
else :
scene_objects = self . scene . objects
2017-09-09 20:53:46 +02:00
for bobject in 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 = [ ]
2017-10-04 14:13:36 +02:00
matslots = [ ]
2017-09-09 20:53:46 +02:00
for bo in scene_objects :
2017-04-04 23:11:31 +02:00
if arm . utils . export_bone_data ( bo ) :
for slot in bo . material_slots :
2017-10-04 14:13:36 +02:00
if slot . material == None :
continue
2017-10-10 20:46:44 +02:00
if slot . material . name . endswith ( ' _armskin ' ) :
continue
2017-10-04 14:13:36 +02:00
matslots . append ( slot )
2017-04-04 23:11:31 +02:00
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 ' ]
2017-10-10 09:57:23 +02:00
if wrd . arm_skin_max_bones_auto :
2017-04-11 23:21:42 +02:00
max_bones = 8
for armature in bpy . data . armatures :
if max_bones < len ( armature . bones ) :
max_bones = len ( armature . bones )
2017-10-10 09:57:23 +02:00
wrd . arm_skin_max_bones = max_bones
2017-04-11 23:21:42 +02:00
2016-09-02 23:11:04 +02:00
self . output [ ' objects ' ] = [ ]
2017-09-09 20:53:46 +02:00
for bo in scene_objects :
2017-04-04 23:11:31 +02:00
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 :
2017-12-03 13:25:20 +01:00
# Blender automatically stores physics objects in this group,
# can cause stuck unused objects, skip for now
2017-12-11 00:55:26 +01:00
if group . name . startswith ( ' RigidBodyWorld ' ) or group . name . startswith ( ' Trait| ' ) :
2017-12-03 13:25:20 +01:00
continue
2017-01-29 16:15:04 +01:00
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 :
2017-11-23 22:53:33 +01:00
if bobject . parent == None and bobject . arm_export :
2017-08-08 16:44:33 +02:00
# Add external linked objects
2017-12-03 13:28:30 +01:00
if bobject . name not in scene_objects : # and bobject.ls_linked
2017-08-08 16:44:33 +02:00
self . process_bobject ( bobject )
self . export_object ( bobject , self . scene )
2017-11-06 10:43:35 +01:00
o [ ' object_refs ' ] . append ( arm . utils . asset_name ( bobject ) )
2017-08-08 16:44:33 +02:00
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 ( ) :
2017-11-25 20:09:55 +01:00
log . warn ( ' 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-09-04 10:20:04 +02:00
if not wrd . arm_deinterleaved_buffers :
2017-09-09 20:53:46 +02:00
for bobject in scene_objects :
2017-05-25 16:48:41 +02:00
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
2017-10-25 14:39:41 +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 ( )
2017-09-21 18:30:02 +02:00
self . export_tilesheets ( )
2016-09-02 23:11:04 +02:00
if self . scene . world != None :
self . output [ ' world_ref ' ] = self . scene . world . name
2017-09-21 13:22:00 +02:00
if self . scene . use_gravity :
self . output [ ' gravity ' ] = [ self . scene . gravity [ 0 ] , self . scene . gravity [ 1 ] , self . scene . gravity [ 2 ] ]
else :
self . output [ ' gravity ' ] = [ 0.0 , 0.0 , 0.0 ]
2016-09-02 23:11:04 +02:00
2016-10-19 13:28:06 +02:00
self . export_objects ( self . scene )
2017-10-25 14:39:41 +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-09-07 13:42:46 +02:00
if ( len ( self . output [ ' camera_datas ' ] ) == 0 and len ( bpy . data . cameras ) == 0 ) or not self . camera_spawned :
2017-08-22 12:08:44 +02:00
log . warn ( ' Creating default camera ' )
o = { }
o [ ' name ' ] = ' DefaultCamera '
o [ ' near_plane ' ] = 0.1
2017-11-02 23:12:17 +01:00
o [ ' far_plane ' ] = 100.0
2017-08-22 12:08:44 +02:00
o [ ' fov ' ] = 0.85
2017-10-02 23:09:28 +02:00
# if ArmoryExporter.in_viewport: # Wrong P returned when no camera present?
# pw = self.get_viewport_panels_w()
# proj, is_persp = self.get_viewport_projection_matrix()
# if pw == 0 and is_persp:
# a = proj[0][0]
# b = proj[1][1]
# c = proj[2][2]
# d = proj[2][3]
# k = (c - 1.0) / (c + 1.0)
# o['near_plane'] = (d * (1.0 - k)) / (2.0 * k)
# o['far_plane'] = k * o['near_plane'];
# o['fov'] = 2.0 * math.atan(1.0 / b)
2017-08-22 12:08:44 +02:00
o [ ' type ' ] = ' perspective '
o [ ' frustum_culling ' ] = True
o [ ' render_path ' ] = ' armory_default/armory_default '
2017-09-07 13:42:46 +02:00
o [ ' clear_color ' ] = self . get_camera_clear_color ( )
2017-08-22 12:08:44 +02:00
self . output [ ' camera_datas ' ] . append ( o )
o = { }
o [ ' name ' ] = ' DefaultCamera '
o [ ' type ' ] = ' camera_object '
o [ ' data_ref ' ] = ' DefaultCamera '
o [ ' material_refs ' ] = [ ]
o [ ' transform ' ] = { }
viewport_matrix = self . get_viewport_view_matrix ( )
if viewport_matrix != None :
o [ ' transform ' ] [ ' values ' ] = self . write_matrix ( viewport_matrix . inverted ( ) )
o [ ' local_transform_only ' ] = True
else :
o [ ' transform ' ] [ ' values ' ] = [ 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 ]
o [ ' traits ' ] = [ ]
navigation_trait = { }
navigation_trait [ ' type ' ] = ' Script '
navigation_trait [ ' class_name ' ] = ' armory.trait.WalkNavigation '
navigation_trait [ ' parameters ' ] = [ str ( arm . utils . get_ease_viewport_camera ( ) ) . lower ( ) ]
o [ ' traits ' ] . append ( navigation_trait )
self . output [ ' objects ' ] . append ( o )
self . output [ ' camera_ref ' ] = ' DefaultCamera '
2017-01-16 15:26:57 +01:00
# Scene root traits
2017-09-04 10:20:04 +02:00
if wrd . arm_physics != ' Disabled ' and ArmoryExporter . export_physics :
2017-01-16 15:26:57 +01:00
if not ' traits ' in self . output :
self . output [ ' traits ' ] = [ ]
x = { }
x [ ' type ' ] = ' Script '
2017-09-30 00:54:19 +02:00
pkg = ' bullet ' if bpy . data . worlds [ ' Arm ' ] . arm_physics == ' Bullet ' else ' oimo '
x [ ' class_name ' ] = ' armory.trait.physics. ' + pkg + ' .PhysicsWorld '
2017-01-16 15:26:57 +01:00
self . output [ ' traits ' ] . append ( x )
2017-09-04 10:20:04 +02:00
if wrd . arm_navigation != ' Disabled ' and ArmoryExporter . export_navigation :
2017-01-16 15:26:57 +01:00
if not ' traits ' in self . output :
self . output [ ' traits ' ] = [ ]
x = { }
x [ ' type ' ] = ' Script '
2017-09-30 00:32:06 +02:00
x [ ' class_name ' ] = ' armory.trait.navigation.Navigation '
2017-01-16 15:26:57 +01:00
self . output [ ' traits ' ] . append ( x )
2017-09-04 10:20:04 +02:00
if wrd . arm_play_console :
if not ' traits ' in self . output :
self . output [ ' traits ' ] = [ ]
ArmoryExporter . export_ui = True
x = { }
x [ ' type ' ] = ' Script '
x [ ' class_name ' ] = ' armory.trait.internal.DebugConsole '
x [ ' parameters ' ] = [ ]
self . output [ ' traits ' ] . append ( x )
2017-08-27 12:50:09 +02:00
if len ( self . scene . arm_traitlist ) > 0 :
if not ' traits ' in self . output :
self . output [ ' traits ' ] = [ ]
self . export_traits ( self . scene , self . output )
if ' traits ' in self . output :
for x in self . output [ ' traits ' ] :
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
2017-10-04 14:13:36 +02:00
for slot in matslots : # Set back to original material
orig_mat = slot . material . name [ : - len ( ' _armskin ' ) ]
slot . material = bpy . data . materials [ orig_mat ]
2017-04-04 23:11:31 +02:00
for mat in matvars :
bpy . data . materials . remove ( mat , do_unlink = True )
2017-11-19 13:38:54 +01:00
# Restore frame
if scene . frame_current != current_frame :
scene . frame_set ( current_frame , current_subframe )
2016-09-02 23:11:04 +02:00
print ( ' Scene built in ' + str ( time . time ( ) - profile_time ) )
return { ' FINISHED ' }
# Callbacks
2017-11-03 01:22:07 +01:00
def is_mesh_cached ( self , bobject ) :
2016-11-28 14:40:07 +01:00
if bobject . type == ' FONT ' or bobject . type == ' META ' : # No verts
2017-08-21 12:17:55 +02:00
return bobject . data . arm_cached
if bobject . data . arm_cached_verts != len ( bobject . data . vertices ) :
2016-09-02 23:11:04 +02:00
return False
2017-08-21 12:17:55 +02:00
if bobject . data . arm_cached_edges != len ( bobject . data . edges ) :
2016-09-02 23:11:04 +02:00
return False
2017-11-03 01:22:07 +01:00
if not bobject . arm_cached :
return False
2017-08-21 12:17:55 +02:00
return bobject . data . arm_cached
2016-09-02 23:11:04 +02:00
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 :
2017-08-19 12:10:06 +02:00
if n . arm_instanced == True :
2016-09-02 23:11:04 +02:00
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
2017-08-21 12:17:55 +02:00
if sn . arm_export == False :
2016-10-17 00:02:51 +02:00
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-08-19 12:10:06 +02:00
wrd = bpy . data . worlds [ ' Arm ' ]
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-23 00:53:26 +02:00
if not hasattr ( ArmoryExporter , ' import_traits ' ) :
ArmoryExporter . import_traits = [ ] # Referenced traits
2016-09-02 23:11:04 +02:00
ArmoryExporter . option_mesh_only = False
ArmoryExporter . option_mesh_per_file = True
2017-08-19 12:10:06 +02:00
ArmoryExporter . option_minimize = wrd . arm_minimize
ArmoryExporter . option_sample_animation = wrd . 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
# Used for material shader export and khafile
2017-08-19 12:10:06 +02: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
2017-10-25 14:39:41 +02:00
# Disabled object
2017-08-21 12:17:55 +02:00
if bobject . arm_export == False :
2016-09-02 23:11:04 +02:00
return False
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
for m in bobject . modifiers :
if m . type == ' OCEAN ' :
# Do not export ocean mesh, just take specified constants
export_object = False
2017-08-23 22:53:39 +02:00
rdpat = arm . utils . get_rp ( )
2016-09-02 23:11:04 +02:00
wrd = bpy . data . worlds [ ' Arm ' ]
2017-09-19 11:29:01 +02:00
rdpat . rp_ocean = True
2016-09-02 23:11:04 +02:00
# Take position and bounds
2017-08-21 12:17:55 +02:00
wrd . arm_ocean_level = 0.0 #bobject.location.z
2017-07-04 13:29:01 +02:00
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-09-02 23:11:04 +02:00
# Export traits
2017-08-27 12:50:09 +02:00
self . export_traits ( bobject , o )
2016-09-02 23:11:04 +02:00
# Rigid body trait
2017-11-23 14:28:08 +01:00
if bobject . rigid_body != None and bpy . data . worlds [ ' Arm ' ] . arm_physics != ' Disabled ' :
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
2017-12-05 23:45:40 +01:00
if bobject . arm_rb_terrain : # Override selected shape as terrain..
shape = 7
elif rb . collision_shape == ' SPHERE ' :
2016-09-02 23:11:04 +02:00
shape = 1
elif rb . collision_shape == ' CONVEX_HULL ' :
shape = 2
elif rb . collision_shape == ' MESH ' :
2017-12-04 15:10:20 +01:00
shape = 3
2016-09-02 23:11:04 +02:00
elif rb . collision_shape == ' CONE ' :
shape = 4
elif rb . collision_shape == ' CYLINDER ' :
shape = 5
elif rb . collision_shape == ' CAPSULE ' :
shape = 6
2017-11-13 10:18:37 +01:00
body_mass = rb . mass
2017-12-04 15:10:20 +01:00
is_static = not rb . enabled or ( rb . type == ' PASSIVE ' and not rb . kinematic )
if is_static :
2017-11-13 10:18:37 +01:00
body_mass = 0
2016-09-02 23:11:04 +02:00
x = { }
x [ ' type ' ] = ' Script '
2017-09-30 00:54:19 +02:00
pkg = ' bullet ' if bpy . data . worlds [ ' Arm ' ] . arm_physics == ' Bullet ' else ' oimo '
x [ ' class_name ' ] = ' armory.trait.physics. ' + pkg + ' .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 ) )
2017-11-13 10:18:37 +01:00
x [ ' parameters ' ] . append ( str ( rb . kinematic ) . lower ( ) )
2017-11-06 13:01:08 +01:00
lin_fac = ' [ {0} , {1} , {2} ] ' . format ( str ( bobject . arm_rb_linear_factor [ 0 ] ) , str ( bobject . arm_rb_linear_factor [ 1 ] ) , str ( bobject . arm_rb_linear_factor [ 2 ] ) )
ang_fac = ' [ {0} , {1} , {2} ] ' . format ( str ( bobject . arm_rb_angular_factor [ 0 ] ) , str ( bobject . arm_rb_angular_factor [ 1 ] ) , str ( bobject . arm_rb_angular_factor [ 2 ] ) )
2017-10-29 19:34:10 +01:00
x [ ' parameters ' ] . append ( lin_fac )
x [ ' parameters ' ] . append ( ang_fac )
col_group = ' '
for b in rb . collision_groups :
col_group = ( ' 1 ' if b else ' 0 ' ) + col_group
x [ ' parameters ' ] . append ( str ( int ( col_group , 2 ) ) )
2017-12-12 20:07:31 +01:00
x [ ' parameters ' ] . append ( str ( bobject . arm_rb_trigger ) . lower ( ) )
2017-11-06 13:01:08 +01:00
if rb . use_deactivation or bobject . arm_rb_force_deactivation :
deact_params = lin_fac = ' [ {0} , {1} , {2} ] ' . format ( str ( rb . deactivate_linear_velocity ) , str ( rb . deactivate_angular_velocity ) , str ( bobject . arm_rb_deactivation_time ) )
x [ ' parameters ' ] . append ( deact_params )
else :
x [ ' parameters ' ] . append ( ' null ' )
2016-09-02 23:11:04 +02:00
o [ ' traits ' ] . append ( x )
2017-10-25 14:39:41 +02:00
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
2017-11-23 14:28:08 +01:00
if soft_type > = 0 and bpy . data . worlds [ ' Arm ' ] . arm_physics != ' Disabled ' :
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 '
2017-09-30 00:54:19 +02:00
pkg = ' bullet ' if bpy . data . worlds [ ' Arm ' ] . arm_physics == ' Bullet ' else ' oimo '
cloth_trait [ ' class_name ' ] = ' armory.trait.physics. ' + pkg + ' .SoftBody '
2017-01-10 10:41:06 +01:00
if soft_type == 0 :
bend = soft_mod . settings . bending_stiffness
elif soft_type == 1 :
bend = ( soft_mod . settings . bend + 1.0 ) * 10
2017-08-21 12:17:55 +02:00
cloth_trait [ ' parameters ' ] = [ str ( soft_type ) , str ( bend ) , str ( soft_mod . settings . mass ) , str ( bobject . arm_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
2017-11-23 14:28:08 +01:00
if bobject . rigid_body_constraint != None and bpy . data . worlds [ ' Arm ' ] . arm_physics != ' Disabled ' :
2017-04-02 13:13:43 +02:00
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 :
2017-08-19 03:08:42 +02:00
# Viewport camera enabled, attach navigation to active camera
if self . scene . camera != None and bobject . name == self . scene . camera . name and bpy . data . worlds [ ' Arm ' ] . arm_play_camera != ' Scene ' :
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 )
2017-10-25 14:39:41 +02:00
2016-09-02 23:11:04 +02:00
# 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 ' ] )
2017-10-25 14:39:41 +02:00
2017-08-27 12:50:09 +02:00
def export_traits ( self , bobject , o ) :
if hasattr ( bobject , ' arm_traitlist ' ) :
for t in bobject . arm_traitlist :
if t . enabled_prop == False :
continue
x = { }
if t . type_prop == ' Logic Nodes ' and t . nodes_name_prop != ' ' :
x [ ' type ' ] = ' Script '
x [ ' class_name ' ] = arm . utils . safestr ( bpy . data . worlds [ ' Arm ' ] . arm_project_package ) + ' .node. ' + arm . utils . safesrc ( t . nodes_name_prop )
2018-01-02 18:19:02 +01:00
if len ( t . nodes_name_prop ) > 0 and not t . nodes_name_prop [ 0 ] . isupper ( ) :
log . warn ( ' Logic tree name " ' + t . nodes_name_prop + ' " must start with upper-case letter ' )
2017-09-20 14:45:09 +02:00
elif t . type_prop == ' WebAssembly ' :
pass
2017-08-27 12:50:09 +02:00
elif t . type_prop == ' UI Canvas ' :
ArmoryExporter . export_ui = True
x [ ' type ' ] = ' Script '
x [ ' class_name ' ] = ' armory.trait.internal.CanvasScript '
x [ ' parameters ' ] = [ " ' " + t . canvas_name_prop + " ' " ]
# assets.add(assetpath) # Bundled is auto-added
# Read file list and add canvas assets
assetpath = arm . utils . get_fp ( ) + ' /Bundled/canvas/ ' + t . canvas_name_prop + ' .files '
if os . path . exists ( assetpath ) :
with open ( assetpath ) as f :
fileList = f . read ( ) . splitlines ( )
for asset in fileList :
# Relative to the root/Bundled/canvas path
asset = asset [ 6 : ] # Strip ../../ to start in project root
assets . add ( asset )
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
nav_path = arm . utils . get_fp_build ( ) + ' /compiled/Assets/navigation '
if not os . path . exists ( nav_path ) :
os . makedirs ( nav_path )
nav_filepath = nav_path + ' /nav_ ' + bobject . data . name + ' .arm '
assets . add ( nav_filepath )
# TODO: Implement cache
#if os.path.isfile(nav_filepath) == False:
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 :
trait_prefix = arm . utils . safestr ( bpy . data . worlds [ ' Arm ' ] . arm_project_package ) + ' . '
x [ ' class_name ' ] = trait_prefix + t . class_name_prop
if len ( t . arm_traitparamslist ) > 0 :
x [ ' parameters ' ] = [ ]
for pt in t . arm_traitparamslist : # Append parameters
x [ ' parameters ' ] . append ( pt . name )
if len ( t . arm_traitpropslist ) > 0 :
x [ ' props ' ] = [ ]
for pt in t . arm_traitpropslist : # Append props
2017-10-25 14:39:41 +02:00
prop = pt . name . replace ( ' ) ' , ' ' ) . split ( ' ( ' )
x [ ' props ' ] . append ( prop [ 0 ] )
if ( len ( prop ) > 1 ) :
if prop [ 1 ] == ' String ' :
value = " ' " + pt . value + " ' "
else :
value = pt . value
else :
value = pt . value
x [ ' props ' ] . append ( value )
2017-08-27 12:50:09 +02:00
o [ ' traits ' ] . append ( x )
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 '
2017-09-30 00:54:19 +02:00
pkg = ' bullet ' if bpy . data . worlds [ ' Arm ' ] . arm_physics == ' Bullet ' else ' oimo '
hook_trait [ ' class_name ' ] = ' armory.trait.physics. ' + pkg + ' .PhysicsHook '
2017-04-02 13:13:43 +02:00
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 '
2017-09-30 00:54:19 +02:00
pkg = ' bullet ' if bpy . data . worlds [ ' Arm ' ] . arm_physics == ' Bullet ' else ' oimo '
constr_trait [ ' class_name ' ] = ' armory.trait.physics. ' + pkg + ' .PhysicsConstraint '
2017-07-04 11:11:34 +02:00
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-09-06 13:28:59 +02:00
wrd = bpy . data . worlds [ ' Arm ' ]
2017-08-21 12:17:55 +02:00
bgcol = world . arm_envtex_color
2017-09-06 13:28:59 +02:00
if ' _LDR ' in wrd . world_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-12-13 00:10:30 +01:00
if ' _EnvSky ' in wrd . world_defs :
# Sky data for probe
o [ ' sun_direction ' ] = list ( world . arm_envtex_sun_direction )
o [ ' turbidity ' ] = world . arm_envtex_turbidity
o [ ' ground_albedo ' ] = world . arm_envtex_ground_albedo
disable_hdr = world . arm_envtex_name . endswith ( ' .jpg ' )
if ' _EnvTex ' in wrd . world_defs or ' _EnvImg ' in wrd . world_defs :
o [ ' envmap ' ] = world . arm_envtex_name . rsplit ( ' . ' , 1 ) [ 0 ]
if disable_hdr :
o [ ' envmap ' ] + = ' .jpg '
else :
o [ ' envmap ' ] + = ' .hdr '
2016-09-02 23:11:04 +02:00
o [ ' probes ' ] = [ ]
2017-09-06 13:28:59 +02:00
2016-09-02 23:11:04 +02:00
# Main probe
2017-09-07 15:08:23 +02:00
rpdat = arm . utils . get_rp ( )
2017-11-13 10:18:37 +01:00
solid_mat = rpdat . arm_material_model == ' Solid '
arm_irradiance = wrd . arm_irradiance and not solid_mat
2017-09-06 13:28:59 +02:00
arm_radiance = False
2017-08-21 12:17:55 +02:00
radtex = world . arm_envtex_name . rsplit ( ' . ' , 1 ) [ 0 ]
irrsharmonics = world . arm_envtex_irr_name
2016-09-02 23:11:04 +02:00
# Radiance
2017-09-06 13:28:59 +02:00
if ' _EnvTex ' in wrd . world_defs :
arm_radiance = bpy . data . worlds [ ' Arm ' ] . arm_radiance
elif ' _EnvSky ' in wrd . world_defs and bpy . data . worlds [ ' Arm ' ] . arm_radiance_sky :
arm_radiance = bpy . data . worlds [ ' Arm ' ] . arm_radiance
2016-09-02 23:11:04 +02:00
radtex = ' hosek '
2017-08-21 12:17:55 +02:00
num_mips = world . arm_envtex_num_mips
strength = world . arm_envtex_strength
2017-09-06 13:28:59 +02:00
po = { }
po [ ' name ' ] = world . name
if arm_irradiance :
po [ ' irradiance ' ] = irrsharmonics + ' _irradiance '
if arm_radiance :
po [ ' radiance ' ] = radtex + ' _radiance '
if disable_hdr :
po [ ' radiance ' ] + = ' .jpg '
else :
po [ ' radiance ' ] + = ' .hdr '
po [ ' radiance_mipmaps ' ] = num_mips
else :
po [ ' irradiance ' ] = ' ' # No irradiance data, fallback to default at runtime
po [ ' strength ' ] = strength
po [ ' blending ' ] = 1.0
po [ ' volume ' ] = [ 0 , 0 , 0 ]
po [ ' volume_center ' ] = [ 0 , 0 , 0 ]
o [ ' probes ' ] . append ( po )
2017-11-26 19:36:14 +01:00
# https://blender.stackexchange.com/questions/70629
def mod_equal ( self , mod1 , mod2 ) :
return all ( [ getattr ( mod1 , prop , True ) == getattr ( mod2 , prop , False ) for prop in mod1 . bl_rna . properties . keys ( ) ] )
def mod_equal_stack ( self , obj1 , obj2 ) :
if len ( obj1 . modifiers ) == 0 and len ( obj2 . modifiers ) == 0 :
return True
if len ( obj1 . modifiers ) == 0 or len ( obj2 . modifiers ) == 0 :
return False
return all ( [ self . mod_equal ( m , obj2 . modifiers [ i ] ) for i , m in enumerate ( obj1 . modifiers ) ] )