from .logger import *
from .package_level import *
from .f_curve_animatable import *
from .armature import *
from .material import *
from .shape_key_group import *
import bpy
import math
from mathutils import Vector, Quaternion
from random import randint
# used in Mesh & Node constructors, defined in BABYLON.AbstractMesh
BILLBOARDMODE_NONE = 0
#BILLBOARDMODE_X = 1
#BILLBOARDMODE_Y = 2
#BILLBOARDMODE_Z = 4
BILLBOARDMODE_ALL = 7
# used in Mesh constructor, defined in BABYLON.PhysicsImpostor
SPHERE_IMPOSTER = 1
BOX_IMPOSTER = 2
#PLANE_IMPOSTER = 3
MESH_IMPOSTER = 4
CAPSULE_IMPOSTER = 5
CONE_IMPOSTER = 6
CYLINDER_IMPOSTER = 7
PARTICLE_IMPOSTER = 8
SHAPE_KEY_GROUPS_ALLOWED = False
ZERO_V = Vector((0, 0, 0))
ZERO_Q = Quaternion((1, 0, 0, 0))
#===============================================================================
class Mesh(FCurveAnimatable):
def __init__(self, object, scene, exporter):
self.scene = scene
self.name = object.name
Logger.log('processing begun of mesh: ' + self.name)
self.define_animations(object, True, True, True) #Should animations be done when forcedParent
self.isVisible = not object.hide_render
self.isPickable = object.data.isPickable
self.isEnabled = not object.data.loadDisabled
if hasattr(object.data, 'useFlatShading') and object.data.useFlatShading:
hasModifier = False
# extra checking not really working; all checking going to be pulled in future
for con in object.constraints:
if con.name == 'EDGE_SPLIT':
hasModifier = True
break
if not hasModifier:
Logger.warn('Found Obsolete "Use Flat Shading" property set True. Replaced by "Edge Split" modifier instead', 2)
self.checkCollisions = object.data.checkCollisions
self.receiveShadows = object.data.receiveShadows
self.castShadows = object.data.castShadows
self.freezeWorldMatrix = object.data.freezeWorldMatrix
self.layer = getLayer(object) # used only for lights with 'This Layer Only' checked, not exported
self.tags = object.data.tags
# hasSkeleton detection & skeletonID determination
self.hasSkeleton = False
objArmature = None # if there's an armature, this will be the one!
if len(object.vertex_groups) > 0 and not object.data.ignoreSkeleton:
objArmature = object.find_armature()
if objArmature != None:
# used to get bone index, since could be skipping IK bones
skeleton = exporter.get_skeleton(objArmature.name)
self.hasSkeleton = skeleton is not None
if not self.hasSkeleton:
Logger.warn('No skeleton with name "' + objArmature.name + '" found skeleton ignored.', 2)
else:
i = 0
for obj in scene.objects:
if obj.type == "ARMATURE":
if obj == objArmature:
self.skeletonId = i
break
else:
i += 1
# determine Position, rotation, & scaling
# Use local matrix
locMatrix = object.matrix_local
if objArmature != None:
# unless the armature is the parent
if object.parent and object.parent == objArmature:
locMatrix = object.matrix_world * object.parent.matrix_world.inverted()
loc, rot, scale = locMatrix.decompose()
self.position = loc
if object.rotation_mode == 'QUATERNION':
self.rotationQuaternion = rot
else:
self.rotation = scale_vector(rot.to_euler('XYZ'), -1)
self.scaling = scale
# ensure no unapplied rotation or scale, when there is an armature
self.hasUnappliedTransforms = (self.scaling.x != 1 or self.scaling.y != 1 or self.scaling.z != 1 or
(hasattr(self, 'rotation' ) and not same_vertex (self.rotation , ZERO_V, FLOAT_PRECISION_DEFAULT)) or
(hasattr(self, 'rotationQuaternion') and not same_quaternion(self.rotationQuaternion, ZERO_Q, FLOAT_PRECISION_DEFAULT))
)
# determine parent & dataName
self.dataName = object.data.name # used to support shared vertex instances in later passed
if object.parent and object.parent.type != 'ARMATURE':
self.parentId = object.parent.name
# Physics
if object.rigid_body != None:
shape_items = {'SPHERE' : SPHERE_IMPOSTER,
'BOX' : BOX_IMPOSTER,
'MESH' : MESH_IMPOSTER,
'CAPSULE' : CAPSULE_IMPOSTER,
'CONE' : CONE_IMPOSTER,
'CYLINDER' : CYLINDER_IMPOSTER,
'CONVEX_HULL': PARTICLE_IMPOSTER}
shape_type = shape_items[object.rigid_body.collision_shape]
self.physicsImpostor = shape_type
mass = object.rigid_body.mass
if mass < 0.005:
mass = 0
self.physicsMass = mass
self.physicsFriction = object.rigid_body.friction
self.physicsRestitution = object.rigid_body.restitution
# Get if this will be an instance of another, before processing materials, to avoid multi-bakes
sourceMesh = exporter.getSourceMeshInstance(self.dataName)
if sourceMesh is not None:
#need to make sure rotation mode matches, since value initially copied in InstancedMesh constructor
if hasattr(sourceMesh, 'rotationQuaternion'):
instRot = None
instRotq = rot
else:
instRot = scale_vector(rot.to_euler('XYZ'), -1)
instRotq = None
instance = MeshInstance(self, instRot, instRotq)
sourceMesh.instances.append(instance)
Logger.log('mesh is an instance of : ' + sourceMesh.name + '. Processing halted.', 2)
return
else:
self.instances = []
# process all of the materials required
recipe = BakingRecipe(object)
self.billboardMode = BILLBOARDMODE_ALL if recipe.isBillboard else BILLBOARDMODE_NONE
if recipe.needsBaking:
if recipe.multipleRenders:
Logger.warn('Mixing of Cycles & Blender Render in same mesh not supported. No materials exported.', 2)
else:
bakedMat = BakedMaterial(exporter, object, recipe)
exporter.materials.append(bakedMat)
self.materialId = bakedMat.name
else:
bjs_material_slots = []
for slot in object.material_slots:
# None will be returned when either the first encounter or must be unique due to baked textures
material = exporter.getMaterial(slot.name)
if (material != None):
Logger.log('registered as also a user of material: ' + slot.name, 2)
else:
material = StdMaterial(slot, exporter, object)
exporter.materials.append(material)
bjs_material_slots.append(material)
if len(bjs_material_slots) == 1:
self.materialId = bjs_material_slots[0].name
elif len(bjs_material_slots) > 1:
multimat = MultiMaterial(bjs_material_slots, len(exporter.multiMaterials), exporter.nameSpace)
self.materialId = multimat.name
exporter.multiMaterials.append(mult