def import_keyframe_controller(self, n_kfc, b_obj, bone_name=None, n_bone_bind_scale=None, n_bone_bind_rot_inv=None, n_bone_bind_trans=None): NifLog.debug('Importing keyframe controller for' + b_obj.name) b_action = b_obj.animation_data.action if bone_name: b_obj = b_obj.pose.bones[bone_name] translations = [] scales = [] rotations = [] eulers = [] n_kfd = None # transform controllers (dartgun.nif) if isinstance(n_kfc, NifFormat.NiTransformController): if n_kfc.interpolator: n_kfd = n_kfc.interpolator.data # B-spline curve import elif isinstance(n_kfc, NifFormat.NiBSplineInterpolator): # used by WLP2 (tiger.kf), but only for non-LocRotScale data # eg. bone stretching - see controlledblock.get_variable_1() # do not support this for now, no good representation in Blender if isinstance(n_kfc, NifFormat.NiBSplineCompFloatInterpolator): # pyffi lacks support for this, but the following gets float keys # keys = list(kfc._getCompKeys(kfc.offset, 1, kfc.bias, kfc.multiplier)) return times = list(n_kfc.get_times()) # just do these temp steps to avoid generating empty fcurves down the line trans_temp = [ mathutils.Vector(tup) for tup in n_kfc.get_translations() ] if trans_temp: translations = zip(times, trans_temp) rot_temp = [ mathutils.Quaternion(tup) for tup in n_kfc.get_rotations() ] if rot_temp: rotations = zip(times, rot_temp) scale_temp = list(n_kfc.get_scales()) if scale_temp: scales = zip(times, scale_temp) # Bsplines are Bezier curves interp_rot = interp_loc = interp_scale = "BEZIER" else: # ZT2 & Fallout n_kfd = n_kfc.data if isinstance(n_kfd, NifFormat.NiKeyframeData): interp_rot = self.get_b_interp_from_n_interp(n_kfd.rotation_type) interp_loc = self.get_b_interp_from_n_interp( n_kfd.translations.interpolation) interp_scale = self.get_b_interp_from_n_interp( n_kfd.scales.interpolation) if n_kfd.rotation_type == 4: b_obj.rotation_mode = "XYZ" # uses xyz rotation if n_kfd.xyz_rotations[0].keys: # euler keys need not be sampled at the same time in KFs # but we need complete key sets to do the space conversion # so perform linear interpolation to import all keys properly # get all the keys' times times_x = [key.time for key in n_kfd.xyz_rotations[0].keys] times_y = [key.time for key in n_kfd.xyz_rotations[1].keys] times_z = [key.time for key in n_kfd.xyz_rotations[2].keys] # the unique time stamps we have to sample all curves at times_all = sorted(set(times_x + times_y + times_z)) # the actual resampling x_r = interpolate( times_all, times_x, [key.value for key in n_kfd.xyz_rotations[0].keys]) y_r = interpolate( times_all, times_y, [key.value for key in n_kfd.xyz_rotations[1].keys]) z_r = interpolate( times_all, times_z, [key.value for key in n_kfd.xyz_rotations[2].keys]) eulers = zip(times_all, zip(x_r, y_r, z_r)) else: b_obj.rotation_mode = "QUATERNION" rotations = [(key.time, key.value) for key in n_kfd.quaternion_keys] if n_kfd.scales.keys: scales = [(key.time, key.value) for key in n_kfd.scales.keys] if n_kfd.translations.keys: translations = [(key.time, key.value) for key in n_kfd.translations.keys] # ZT2 - get extrapolation for every kfc if isinstance(n_kfc, NifFormat.NiKeyframeController): flags = n_kfc.flags # fallout, Loki - we set extrapolation according to the root NiControllerSequence.cycle_type else: flags = None if eulers: NifLog.debug('Rotation keys..(euler)') fcurves = self.create_fcurves(b_action, "rotation_euler", range(3), flags, bone_name) for t, val in eulers: key = mathutils.Euler(val) if bone_name: key = math.import_keymat( n_bone_bind_rot_inv, key.to_matrix().to_4x4()).to_euler() self.add_key(fcurves, t, key, interp_rot) elif rotations: NifLog.debug('Rotation keys...(quaternions)') fcurves = self.create_fcurves(b_action, "rotation_quaternion", range(4), flags, bone_name) for t, val in rotations: key = mathutils.Quaternion([val.w, val.x, val.y, val.z]) if bone_name: key = math.import_keymat( n_bone_bind_rot_inv, key.to_matrix().to_4x4()).to_quaternion() self.add_key(fcurves, t, key, interp_rot) if translations: NifLog.debug('Translation keys...') fcurves = self.create_fcurves(b_action, "location", range(3), flags, bone_name) for t, val in translations: key = mathutils.Vector([val.x, val.y, val.z]) if bone_name: key = math.import_keymat( n_bone_bind_rot_inv, mathutils.Matrix.Translation( key - n_bone_bind_trans)).to_translation() self.add_key(fcurves, t, key, interp_loc) if scales: NifLog.debug('Scale keys...') fcurves = self.create_fcurves(b_action, "scale", range(3), flags, bone_name) for t, val in scales: key = (val, val, val) self.add_key(fcurves, t, key, interp_scale)
def convert_euler(bullet_euler): """ input blender rad angles and convert to bullet rad coordinates """ return list(mathutils.Euler(bullet_euler, "ZYX"))
def calc_pose_formats(position, rotation, pivot=(0, 0, 0)): """Create a dictionary containing various representations of the pose represented by 'position' and 'rotation': - translation == position - rotation_quaternion == rotation - rotation_euler: euler angles - matrix: position and rotation in 4x4 matrix form :param position: The position to include in the dictionary. :type position: list :param rotation: The rotation to include into the dictionary. It can either be an euler angle or a quaternion. :type rotation: list :param pivot: The pivot point. :type pivot: list :return: dict """ px, py, pz = position if len(rotation) == 3: rot = mathutils.Euler(rotation).to_quaternion() # TODO delete me? #print(rotation) else: rot = mathutils.Quaternion(rotation) # TODO delete me? #if angle_offset is not 0.0: # axis_vec = mathutils.Vector(axis) # offset_matrix = mathutils.Matrix.Rotation(angle_offset, 4, axis_vec) #get_axis_rotation_matrix(axis, angle_offset) # rot_matrix = rot.to_matrix().to_4x4() # applied_matrix = rot_matrix * offset_matrix # rot = applied_matrix.to_quaternion() rw, rx, ry, rz = rot pose_dict = {} neg_pivot_translation = mathutils.Matrix.Translation( (-pivot[0], -pivot[1], -pivot[2])) pivot_translation = mathutils.Matrix.Translation(pivot) rotation_matrix = mathutils.Quaternion(rot).to_matrix().to_4x4() translation = mathutils.Matrix.Translation(position) # TODO delete me? #print() #print("translation:", translation) #print("neg_pivot_translation:", neg_pivot_translation) #print("rotation_matrix:", rotation_matrix) #print("pivot_translation", pivot_translation) #print() #transformation_matrix = translation * neg_pivot_translation * rotation_matrix * pivot_translation transformation_matrix = translation * rotation_matrix * neg_pivot_translation rm = transformation_matrix #TODO: this is not good matrix = [[rm[0][0], rm[0][1], rm[0][2], rm[0][3]], [rm[1][0], rm[1][1], rm[1][2], rm[1][3]], [rm[2][0], rm[2][1], rm[2][2], rm[2][3]], [rm[3][0], rm[3][1], rm[3][2], rm[3][3]]] pose_dict['matrix'] = matrix loc, rot, sca = transformation_matrix.decompose() pose_dict['translation'] = [loc.x, loc.y, loc.z] pose_dict['rotation_quaternion'] = [rot.w, rot.x, rot.y, rot.z] euler = rot.to_euler() pose_dict['rotation_euler'] = [euler.x, euler.y, euler.z] # TODO delete me? #translation = [px, py, pz] #quaternion = mathutils.Quaternion([rw, rx, ry, rz]) #euler = quaternion.to_euler() ##print(euler) #pose_dict['translation'] = translation ##pose_dict['rotation_quaternion'] = [rw, rx, ry, rz] #pose_dict['rotation_euler'] = [euler.x, euler.y, euler.z] #rm = quaternion.to_matrix() #matrix = [[rm[0][0], rm[0][1], rm[0][2], px], # [rm[1][0], rm[1][1], rm[1][2], py], # [rm[2][0], rm[2][1], rm[2][2], pz], # [0.0, 0.0, 0.0, 1.0]] # # pose_dict['matrix'] = matrix #print() #print('pose_dict:', pose_dict) #print() return pose_dict
def constructMovement(self, J, helicity, amt, rig, a, b, y, o): # Linkages aa = [[0 for i in range(4)] for j in range(4)] # Link α(i) - α(j) ab = [[0 for i in range(4)] for j in range(4)] # Link α(i) - β(j) ya = [[0 for i in range(4)] for j in range(4)] # Link γ(i) - α(j) ao = [[0 for i in range(4)] for j in range(4)] # Link α(i) - δ(j) ob = [[0 for i in range(self.J)] for j in range(self.J)] # Link δ(i) - β(j) yy = [[0 for i in range(self.J)] for j in range(self.J)] # Link γ(i) - γ(j) by = [[0 for i in range(self.J)] for j in range(self.J)] # Link β(i) - γ(j) yo = [[0 for i in range(self.J)] for j in range(self.J)] # Link γ(i) - δ(j) rig.location = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') rig.show_in_front = True amt.show_names = True amt.display_type = 'STICK' # amt.display_type = 'BBONE' # Link object to scene bpy.data.collections['movement'].objects.link(rig) bpy.context.view_layer.objects.active = rig bpy.context.view_layer.update() # Edit bpy.ops.object.editmode_toggle() # Construction Linkage aa[2][1] = amt.edit_bones.new('a2a1') aa[2][1].head = a[2] aa[2][1].tail = a[1] ab[1][1] = amt.edit_bones.new('a1b1') ab[1][1].head = a[1] ab[1][1].tail = b[1] ab[1][1].parent = aa[2][1] by[1][1] = amt.edit_bones.new('b1y1') by[1][1].head = b[1] by[1][1].tail = y[1] by[1][1].parent = ab[1][1] by[1][1].use_inherit_rotation = False ya[1][2] = amt.edit_bones.new('y1a2') ya[1][2].head = y[1] ya[1][2].tail = a[2] ya[1][2].parent = by[1][1] ao[2][1] = amt.edit_bones.new('a2o1') ao[2][1].head = a[2] ao[2][1].tail = o[1] ao[2][1].parent = ya[1][2] ob[1][2] = amt.edit_bones.new('o1b2') ob[1][2].head = o[1] ob[1][2].tail = b[2] ob[1][2].parent = ao[2][1] yy[1][2] = amt.edit_bones.new('y1y2') yy[1][2].head = y[1] yy[1][2].tail = y[2] yy[1][2].parent = by[1][1] for j in range(2, J - 1): by[j][j] = amt.edit_bones.new('b'+ str(j) + 'y'+ str(j)) by[j][j].head = b[j] by[j][j].tail = y[j] by[j][j].parent = ob[j-1][j] yo[j][j] = amt.edit_bones.new('y'+ str(j) + 'o'+ str(j)) yo[j][j].head = y[j] yo[j][j].tail = o[j] yo[j][j].parent = yy[j-1][j] yy[j][j+1] = amt.edit_bones.new('y'+ str(j) + 'y'+ str(j+1)) yy[j][j+1].head = y[j] yy[j][j+1].tail = y[j+1] yy[j][j+1].parent = by[j][j] if j < (J-2): ob[j][j+1] = amt.edit_bones.new('o'+ str(j) + 'b'+ str(j+1)) ob[j][j+1].head = o[j] ob[j][j+1].tail = b[j+1] ob[j][j+1].parent = yo[j][j] # all bones select # Bone constraints. Armature must be in pose mode. bpy.ops.object.mode_set(mode='POSE') bpy.ops.pose.select_all(action="SELECT") # Edit bpy.ops.object.editmode_toggle() if helicity == 'right': bpy.ops.armature.calculate_roll(type='GLOBAL_POS_Z') else: bpy.ops.armature.calculate_roll(type='GLOBAL_NEG_Z') # IK constraint cns = rig.pose.bones['y1a2'].constraints.new('IK') cns.name = 'Ik' cns.target = rig cns.subtarget = 'a2a1' cns.chain_count = 2 cns.use_stretch = False for j in range(2, J - 1): cns = rig.pose.bones['b'+str(j) +'y'+str(j)].constraints.new('IK') cns.name = 'Ik' cns.target = rig cns.subtarget = 'y'+str(j)+'o'+str(j) cns.iterations = 500 cns.chain_count = 2 cns.use_stretch = False bpy.ops.object.mode_set(mode='OBJECT')
def configMovement(self, P, A, J, a, b, y, o): a[1] = mathutils.Euler((P, A, 0.0), 'XYZ') print ("a1 =", a[1]) a[2] = mathutils.Euler((A, -A, 0.0), 'XYZ') print ("a2 =", a[2]) b[1] = mathutils.Euler((-A, A, 0.0), 'XYZ') print ("b1 =", b[1]) o[1] = mathutils.Euler((A, A, 0.0), 'XYZ') print ("o1 =", o[1]) B = A * 2 * sqrt (2) C = B + (B * sqrt (2)) D = C * sqrt (2) E = C + D y[1] = mathutils.Euler((-A, -A, 0.0), 'XYZ') print ("y1 =", y[1]) b[2] = mathutils.Euler(((A*3/0.431828)*A, (A/0.431828)*A, 0.0), 'XYZ') print ("b2 =", b[2]) b[3] = mathutils.Euler(((-A/0.431828)*A, (1.66503/0.431828)*A, 0.0), 'XYZ') print ("b3 =", b[3]) y[2] = mathutils.Euler(((A/0.431828)*A, (-A/0.431828)*A, 0.0), 'XYZ') print ("y2 =", y[2]) y[3] = mathutils.Euler(((A/0.431828)*A, (1.66503/0.431828)*A, 0.0), 'XYZ') print ("y3 =", y[3]) o[2] = mathutils.Euler(((-A/0.431828)*A, (-A/0.431828)*A, 0.0), 'XYZ') print ("o2 =", o[2]) o[3] = mathutils.Euler(((A/0.431828)*A, (2.53357/0.431828)*A, 0.0), 'XYZ') print ("o3 =", o[3]) y[4] = mathutils.Euler(((A*3/0.431828)*A, (1.66503/0.431828)*A, 0.0), 'XYZ') print ("y4 =", y[4])
def execute(self, context): scene = context.scene stageProps=EDFLibrary.CalcStageBladeAngles(R=scene.reaction,\ phi=scene.flowCoefficient,\ psi=scene.stageLoading,\ radius=scene.meanLineRadius, rpm = scene.rpm) print("") print("") print("Drawing 2D Rotor/Stator blades") print("") print("") chord = 1 deltaBeta = (stageProps.beta2 - stageProps.beta1) camber = chord / 2 / math.sin(deltaBeta) - chord / 2 / math.tan( deltaBeta) print("beta " + str((stageProps.beta1 + stageProps.beta2) / 2 * 180 / math.pi)) print("rotorcamber " + str(camber * 100)) print("deltaBeta " + str(deltaBeta * 180 / math.pi)) TurboMachLib.NACA4(name='rotor2D',\ camber_root=camber*100,\ camber_tip=camber*100,\ camber_position=35,\ thickness=6,\ bladeHeight=1,\ twistAngle=0,\ rootChord=1,\ tipChord=1,\ centerOfTwist=[0,0],\ nspan=1,\ npts=24) DLUtils.RotateObject( 'rotor2D', mathutils.Euler([0, 0, -(stageProps.beta2 + stageProps.beta1) / 2])) deltaAlpha = (stageProps.alpha2 - stageProps.alpha1) camber = chord / 2 / math.sin(deltaAlpha) - chord / 2 / math.tan( deltaAlpha) print("alpha " + str((stageProps.alpha1 + stageProps.alpha2) / 2 * 180 / math.pi)) print("statorcamber " + str(camber * 100)) print("deltaAlpha " + str(deltaAlpha * 180 / math.pi)) TurboMachLib.NACA4(name='stator2D',\ camber_root=camber*100,\ camber_tip=camber*100,\ camber_position=35,\ thickness=6,\ bladeHeight=1,\ twistAngle=0,\ rootChord=1,\ tipChord=1,\ centerOfTwist=[0,0],\ nspan=1,\ npts=15) DLUtils.MoveObject('stator2D', mathutils.Vector([1, 0, 0])) DLUtils.RotateObject( 'stator2D', mathutils.Euler( [0, 0, (stageProps.alpha2 + stageProps.alpha1) / 2])) #stageProps.GenerateReport("test.txt") return {'FINISHED'}
def road_generate(ops, road_width, invert): bpy.ops.object.mode_set(mode='EDIT') me = bpy.context.edit_object.data bm = bmesh.from_edit_mesh(me) edges = [e for e in bm.edges if e.select and not e.hide] lines = get_lines(edges) if len(lines) != 1: ops.report({"WARNING"}, "1つの線になるように辺を選択して下さい") return False vs = get_sorted_verts(lines[0]) if invert: vs.reverse() vs_l = [] vs_r = [] rot_l = mathutils.Euler((0.0, 0.0, math.radians(90.0)), 'XYZ') rot_r = mathutils.Euler((0.0, 0.0, math.radians(-90.0)), 'XYZ') rot_c = None for n, v in enumerate(vs): #v.select = False vb = None vf = None if n != 0: vb = vs[n - 1] if n != len(vs) - 1: vf = vs[n + 1] co_bl = None co_br = None co_fl = None co_fr = None if vb != None: rot_b = (v.co - vb.co) rot_b.z = 0.0 rot_b.normalize() co_bl = mathutils.Vector(rot_b) co_bl.rotate(rot_l) co_bl *= road_width / 2 co_br = mathutils.Vector(rot_b) co_br.rotate(rot_r) co_br *= road_width / 2 if vf != None: rot_f = (vf.co - v.co) rot_f.z = 0.0 rot_f.normalize() co_fl = mathutils.Vector(rot_f) co_fl.rotate(rot_l) co_fl *= road_width / 2 co_fr = mathutils.Vector(rot_f) co_fr.rotate(rot_r) co_fr *= road_width / 2 if n == 0: vs_l = vs_l + [v.co + co_fl] vs_r = vs_r + [v.co + co_fr] elif n == len(vs) - 1: vs_l = vs_l + [v.co + co_bl] vs_r = vs_r + [v.co + co_br] else: vc = get_cross_point(vb.co + co_bl, v.co + co_bl, vf.co + co_fl, v.co + co_fl, False) vc.z = v.co.z vs_l = vs_l + [vc] vc = get_cross_point(vb.co + co_br, v.co + co_br, vf.co + co_fr, v.co + co_fr, False) vc.z = v.co.z vs_r = vs_r + [vc] vb = None vbl = None vbr = None for v, vl, vr in zip(vs, vs_l, vs_r): vfl = bm.verts.new(vl) vfr = bm.verts.new(vr) if vb != None: bm.faces.new([vfl, vbl, vb, v]).select = True bm.faces.new([v, vb, vbr, vfr]).select = True vb = v vbl = vfl vbr = vfr bmesh.update_edit_mesh(me) bm.calc_tessface() bm.normal_update() return True
def _import_main(fpath, context, creader): object_name = os.path.basename(fpath.lower()) bpy_arm_obj = None renamemap = {} meshes_data = None unread_chunks = [] for (cid, data) in creader: if cid == Chunks.Object.VERSION: reader = PackedReader(data) ver = reader.getf('H')[0] if ver != 0x10: raise AppError('unsupported OBJECT format version', log.props(version=ver)) elif cid == Chunks.Object.MESHES: meshes_data = data elif (cid == Chunks.Object.SURFACES) or (cid == Chunks.Object.SURFACES1) or \ (cid == Chunks.Object.SURFACES2): reader = PackedReader(data) surfaces_count = reader.int() if cid == Chunks.Object.SURFACES: try: xrlc_reader = PackedReader( creader.next(Chunks.Object.SURFACES_XRLC)) xrlc_shaders = [ xrlc_reader.gets() for _ in range(surfaces_count) ] except: xrlc_shaders = ['default' for _ in range(surfaces_count)] for surface_index in range(surfaces_count): if cid == Chunks.Object.SURFACES: name = reader.gets() eshader = reader.gets() flags = reader.getf('B')[0] reader.skip(4 + 4) # fvf and TCs count texture = reader.gets() vmap = reader.gets() if texture != vmap or not (texture and vmap): old_object_format = False renamemap[vmap.lower()] = vmap else: # old format (Objects\Rainbow\lest.object) old_object_format = True vmap = 'Texture' gamemtl = 'default' cshader = xrlc_shaders[surface_index] else: name = reader.gets() eshader = reader.gets() cshader = reader.gets() gamemtl = reader.gets( ) if cid == Chunks.Object.SURFACES2 else 'default' texture = reader.gets() vmap = reader.gets() if texture != vmap or not (texture and vmap): old_object_format = False renamemap[vmap.lower()] = vmap else: # old format (Objects\corps\corp_BYAKA.object) old_object_format = True vmap = 'Texture' renamemap[vmap.lower()] = vmap flags = reader.int() reader.skip(4 + 4) # fvf and ? bpy_material = None tx_filepart = texture.replace('\\', os.path.sep).lower() for material in bpy.data.materials: if not material.name.startswith(name): continue if material.xray.flags != flags: continue if material.xray.eshader != eshader: continue if material.xray.cshader != cshader: continue if material.xray.gamemtl != gamemtl: continue if (not texture) and (not vmap): all_empty_slots = all( not slot for slot in material.texture_slots) if all_empty_slots: bpy_material = material break ts_found = False for slot in material.texture_slots: if not slot: continue if slot.uv_layer != vmap: continue if not _is_compatible_texture(slot.texture, tx_filepart): continue ts_found = True break if not ts_found: continue bpy_material = material break if bpy_material is None: bpy_material = bpy.data.materials.new(name) bpy_material.xray.version = context.version bpy_material.xray.flags = flags bpy_material.xray.eshader = eshader bpy_material.xray.cshader = cshader bpy_material.xray.gamemtl = gamemtl bpy_material.use_shadeless = True bpy_material.use_transparency = True bpy_material.alpha = 0 if texture: bpy_texture = bpy.data.textures.get(texture) if (bpy_texture is None) \ or not _is_compatible_texture(bpy_texture, tx_filepart): bpy_texture = bpy.data.textures.new(texture, type='IMAGE') bpy_texture.image = context.image(texture) bpy_texture.use_preview_alpha = True bpy_texture_slot = bpy_material.texture_slots.add() bpy_texture_slot.texture = bpy_texture bpy_texture_slot.texture_coords = 'UV' bpy_texture_slot.uv_layer = vmap bpy_texture_slot.use_map_color_diffuse = True bpy_texture_slot.use_map_alpha = True context.loaded_materials[name] = bpy_material elif (cid == Chunks.Object.BONES) or (cid == Chunks.Object.BONES1): if cid == Chunks.Object.BONES: reader = PackedReader(data) bones_count = reader.int() if not bones_count: continue # Do not create an armature if zero bones if bpy and (bpy_arm_obj is None): bpy_armature = bpy.data.armatures.new(object_name) bpy_armature.use_auto_ik = True bpy_armature.draw_type = 'STICK' bpy_arm_obj = bpy.data.objects.new(object_name, bpy_armature) bpy_arm_obj.show_x_ray = True bpy.context.scene.objects.link(bpy_arm_obj) bpy.context.scene.objects.active = bpy_arm_obj if cid == Chunks.Object.BONES: for _ in range(bones_count): name, parent, vmap = reader.gets(), reader.gets( ), reader.gets() offset, rotate, length = read_v3f(reader), read_v3f( reader), reader.getf('f')[0] rotate = rotate[2], rotate[1], rotate[0] bpy_bone = _create_bone(context, bpy_arm_obj, name, parent, vmap, offset, rotate, length, renamemap) xray = bpy_bone.xray xray.mass.gamemtl = 'default_object' xray.mass.value = 10 xray.ikjoint.lim_x_spr, xray.ikjoint.lim_x_dmp = 1, 1 xray.ikjoint.lim_y_spr, xray.ikjoint.lim_y_dmp = 1, 1 xray.ikjoint.lim_z_spr, xray.ikjoint.lim_z_dmp = 1, 1 xray.ikjoint.spring = 1 xray.ikjoint.damping = 1 else: for (_, bdat) in ChunkedReader(data): _import_bone(context, ChunkedReader(bdat), bpy_arm_obj, renamemap) bpy.ops.object.mode_set(mode='EDIT') try: if context.operator.shaped_bones: bones = bpy_armature.edit_bones lenghts = [0] * len(bones) for i, bone in enumerate(bones): min_rad_sq = math.inf for j, bone1 in enumerate(bones): if j == i: continue rad_sq = (bone1.head - bone.head).length_squared if rad_sq < min_rad_sq: min_rad_sq = rad_sq lenghts[i] = math.sqrt(min_rad_sq) for bone, length in zip(bones, lenghts): bone.length = min(max(length * 0.4, 0.01), 0.1) finally: bpy.ops.object.mode_set(mode='OBJECT') for bone in bpy_arm_obj.pose.bones: bone.rotation_mode = 'ZXY' elif (cid == Chunks.Object.PARTITIONS0) or (cid == Chunks.Object.PARTITIONS1): bpy.context.scene.objects.active = bpy_arm_obj bpy.ops.object.mode_set(mode='POSE') try: reader = PackedReader(data) for _partition_idx in range(reader.int()): bpy.ops.pose.group_add() bone_group = bpy_arm_obj.pose.bone_groups.active bone_group.name = reader.gets() for _bone_idx in range(reader.int()): name = reader.gets( ) if cid == Chunks.Object.PARTITIONS1 else reader.int( ) bpy_arm_obj.pose.bones[name].bone_group = bone_group finally: bpy.ops.object.mode_set(mode='OBJECT') elif cid == Chunks.Object.MOTIONS: if not context.import_motions: continue reader = PackedReader(data) import_motions(reader, bpy_arm_obj) elif cid == Chunks.Object.LIB_VERSION: pass # skip obsolete chunk else: unread_chunks.append((cid, data)) mesh_objects = [] for (_, mdat) in ChunkedReader(meshes_data): mesh = _import_mesh(context, ChunkedReader(mdat), renamemap) if bpy_arm_obj: bpy_armmod = mesh.modifiers.new(name='Armature', type='ARMATURE') bpy_armmod.object = bpy_arm_obj mesh.parent = bpy_arm_obj mesh_objects.append(mesh) bpy.context.scene.objects.link(mesh) bpy_obj = bpy_arm_obj if bpy_obj is None: if len(mesh_objects) == 1: bpy_obj = mesh_objects[0] bpy_obj.name = object_name else: bpy_obj = bpy.data.objects.new(object_name, None) for mesh in mesh_objects: mesh.parent = bpy_obj bpy.context.scene.objects.link(bpy_obj) bpy_obj.xray.version = context.version bpy_obj.xray.isroot = True if fpath.lower().startswith( context.objects_folder.lower()) and context.objects_folder: object_folder_length = len(context.objects_folder) bpy_obj.xray.export_path = os.path.dirname( fpath.lower())[object_folder_length:] for (cid, data) in unread_chunks: if cid == Chunks.Object.TRANSFORM: reader = PackedReader(data) pos = read_v3f(reader) rot = read_v3f(reader) bpy_obj.matrix_basis *= mathutils.Matrix.Translation(pos) \ * mathutils.Euler(rot, 'YXZ').to_matrix().to_4x4() elif cid == Chunks.Object.FLAGS: length_data = len(data) if length_data == 4: bpy_obj.xray.flags = PackedReader(data).int() elif length_data == 1: # old object format bpy_obj.xray.flags = PackedReader(data).getf('B')[0] elif cid == Chunks.Object.USERDATA: bpy_obj.xray.userdata = \ PackedReader(data).gets(onerror=lambda e: log.warn('bad userdata', error=e)) elif cid == Chunks.Object.LOD_REF: bpy_obj.xray.lodref = PackedReader(data).gets() elif cid == Chunks.Object.REVISION: reader = PackedReader(data) bpy_obj.xray.revision.owner = reader.gets() bpy_obj.xray.revision.ctime = reader.int() bpy_obj.xray.revision.moder = reader.gets() bpy_obj.xray.revision.mtime = reader.int() elif cid == Chunks.Object.MOTION_REFS: mrefs = bpy_obj.xray.motionrefs_collection for mref in PackedReader(data).gets().split(','): mrefs.add().name = mref elif cid == Chunks.Object.SMOTIONS3: reader = PackedReader(data) mrefs = bpy_obj.xray.motionrefs_collection for _ in range(reader.int()): mrefs.add().name = reader.gets() else: log.debug('unknown chunk', cid=cid)
def run(self): self._init_bvh_tree() cam_ob = bpy.context.scene.camera cam = cam_ob.data # Set resolution and aspect ratio, as they have an influence on the near plane bpy.context.scene.render.resolution_x = self.config.get_int( "resolution_x", 512) bpy.context.scene.render.resolution_y = self.config.get_int( "resolution_y", 512) bpy.context.scene.render.pixel_aspect_x = self.config.get_float( "pixel_aspect_x", 1) room_id = 0 for room_obj in bpy.context.scene.objects: # Find room objects if "type" in room_obj and room_obj[ "type"] == "Room" and "bbox" in room_obj: floor_obj = self._find_floor(room_obj) if floor_obj is None: continue number_of_cams = self._calc_number_of_cams_in_room(room_obj) print("Generating " + str(number_of_cams) + " cams for room " + room_obj.name + " (" + str(room_obj["roomTypes"]) + ")") # Now try to generate the requested number of cams successful_tries = 0 tries = 0 while successful_tries < number_of_cams and tries < self.max_tries_per_room: tries += 1 position = self._sample_position(room_obj) if not self._position_is_above_floor(position, floor_obj): continue orientation = self._sample_orientation() # Compute the world matrix of a cam with the given pose world_matrix = mathutils.Matrix.Translation( mathutils.Vector(position)) @ mathutils.Euler( orientation, 'XYZ').to_matrix().to_4x4() if not self._perform_obstacle_in_view_check( cam, position, world_matrix): continue if self._scene_coverage_score( cam, position, world_matrix) < self.min_interest_score: continue # Set the camera pose at the next frame self.cam_pose_collection.add_item({ "location": list(position), "rotation": list(orientation), "room_id": room_id }) successful_tries += 1 print(str(tries) + " tries were necessary") room_id += 1
def configMovement(self, P, A, J, a, b, y, o): a[1] = mathutils.Euler((P, A, 0.0), 'XYZ') print("a1 =", a[1]) a[2] = mathutils.Euler((A, -A, 0.0), 'XYZ') print("a2 =", a[2]) b[1] = mathutils.Euler((-A, A, 0.0), 'XYZ') print("b1 =", b[1]) o[1] = mathutils.Euler((A, A, 0.0), 'XYZ') print("o1 =", o[1]) B = A * 2 * sqrt(2) C = B + (B * sqrt(2)) D = C * sqrt(2) E = C + D y[1] = mathutils.Euler((-A, -A, 0.0), 'XYZ') print("y1 =", y[1]) b[2] = mathutils.Euler( ((3.37305 / 0.578724) * A, (-2.2156 / 0.578724) * A, 0.0), 'XYZ') print("b2 =", b[2]) b[3] = mathutils.Euler( ((3.37305 / 0.578724) * A, (-6.16738 / 0.578724) * A, 0.0), 'XYZ') print("b3 =", b[3]) b[4] = mathutils.Euler( ((1.05816 / 0.578724) * A, (-4.5305 / 0.578724) * A, 0.0), 'XYZ') print("b4 =", b[4]) y[2] = mathutils.Euler( ((2.2156 / 0.578724) * A, (-3.37305 / 0.578724) * A, 0.0), 'XYZ') print("y2 =", y[2]) y[3] = mathutils.Euler( ((5.00993 / 0.578724) * A, (-6.16738 / 0.578724) * A, 0.0), 'XYZ') print("y3 =", y[3]) o[2] = b[2] print("o2 =", o[2]) o[3] = mathutils.Euler( ((5.00993 / 0.578724) * A, (-4.5305 / 0.578724) * A, 0.0), 'XYZ') print("o3 =", o[3]) y[4] = y[2] print("y4 =", y[4]) o[4] = b[4] print("o4 =", o[4]) b[5] = mathutils.Euler( ((-1.73617 / 0.578724) * A, (-1.73617 / 0.578724) * A, 0.0), 'XYZ') print("b5 =", b[5]) y[5] = y[1] print("y5 =", y[5]) o[5] = b[5] y[6] = mathutils.Euler( ((-3.37305 / 0.578724) * A, (2.2156 / 0.578724) * A, 0.0), 'XYZ') print("y6 =", y[6])
def createGeometry(viscol, geomsrc, linkobj=None): """Creates Blender object for visual or collision objects. If the creation fails, nothing is returned. These entries in the dictionary are mandatory: | **geometry**: | **type**: type of geometry (mesh, box, cylinder, sphere) Depending on the geometry type other values are required: `size`, `radius`, `length` These entries are optional: | **geometry**: | **scale**: scale for the new geometry | **material**: material name to assign to the visual | **pose**: specifies the placement of the new object relative to the optional linkobj | **translation**: position vector for the new object | **rotation_euler**: rotation for the new object Furthermore any generic properties, prepended by a ``$`` will be added as custom properties to the visual/collision object. E.g. ``$test/etc`` would be put to visual/test/etc for a visual object. However, these properties are extracted only in the first layer of hierarchy. Args: viscol(dict): visual/collision model dictionary representation geomsrc(str): phobostype of the new object linkobj(bpy.types.Object, optional): link object to attach the visual/collision object to (Default value = None) Returns: bpy.types.Object or None: the new geometry object or nothing """ if 'geometry' not in viscol or viscol['geometry'] is {}: log("Could not create {}. Geometry information not defined!".format(geomsrc), 'ERROR') return None bpy.ops.object.select_all(action='DESELECT') geom = viscol['geometry'] # create the Blender object if geom['type'] == 'mesh': bpy.context.scene.layers = bUtils.defLayers(defs.layerTypes[geomsrc]) meshname = "".join(os.path.basename(geom["filename"]).split(".")[:-1]) if not os.path.isfile(geom['filename']): log( "This path " + geom['filename'] + " is no file. Object " + viscol['name'] + " will have empty mesh!", 'ERROR', ) bpy.ops.object.add(type='MESH') newgeom = bpy.context.active_object nUtils.safelyName(newgeom, viscol['name'], phobostype=geomsrc) else: if meshname in bpy.data.meshes: log('Assigning copy of existing mesh ' + meshname + ' to ' + viscol['name'], 'INFO') bpy.ops.object.add(type='MESH') newgeom = bpy.context.object newgeom.data = bpy.data.meshes[meshname] else: log("Importing mesh for {0} element: '{1}".format(geomsrc, viscol['name']), 'INFO') filetype = geom['filename'].split('.')[-1].lower() newgeom = meshes.importMesh(geom['filename'], filetype) # bpy.data.meshes[newgeom].name = meshname if not newgeom: log('Failed to import mesh file ' + geom['filename'], 'ERROR') return else: if geom['type'] == 'box': dimensions = geom['size'] elif geom['type'] == 'cylinder': dimensions = (geom['radius'], geom['length']) elif geom['type'] == 'sphere': dimensions = geom['radius'] # TODO add support for heightmap, image, plane and polyline geometries (see sdf!) else: log( "Unknown geometry type of " + geomsrc + viscol['name'] + '. Placing empty coordinate system.', "ERROR", ) bpy.ops.object.empty_add(type='PLAIN_AXES', radius=0.2) obj = bpy.context.object obj.phobostype = geomsrc nUtils.safelyName(bpy.context.active_object, viscol['name'], phobostype=geomsrc) return None log("Creating primtive for {0}: {1}".format(geomsrc, viscol['name']), 'INFO') newgeom = bUtils.createPrimitive( viscol['name'], geom['type'], dimensions, phobostype=geomsrc ) newgeom.select = True bpy.ops.object.transform_apply(scale=True) # from here it's the same for both meshes and primitives newgeom['geometry/type'] = geom['type'] if geomsrc == 'visual': if 'material' in viscol: assignMaterial(newgeom, viscol['material']) else: log('No material for visual {}.'.format(viscol['name']), 'WARNING') # write generic custom properties for prop in viscol: if prop.startswith('$'): for tag in viscol[prop]: newgeom[prop[1:] + '/' + tag] = viscol[prop][tag] nUtils.safelyName(newgeom, viscol['name']) newgeom[geomsrc + '/name'] = viscol['name'] newgeom.phobostype = geomsrc # place geometric object relative to its parent link if linkobj: if 'pose' in viscol: log("Setting transformation of element: " + viscol['name'], 'DEBUG') location = mathutils.Matrix.Translation(viscol['pose']['translation']) rotation = ( mathutils.Euler(tuple(viscol['pose']['rotation_euler']), 'XYZ').to_matrix().to_4x4() ) else: log("No pose in element: " + viscol['name'], 'DEBUG') location = mathutils.Matrix.Identity(4) rotation = mathutils.Matrix.Identity(4) eUtils.parentObjectsTo(newgeom, linkobj) newgeom.matrix_local = location * rotation # scale imported object if 'scale' in geom: newgeom.scale = geom['scale'] # make object smooth eUtils.smoothen_surface(newgeom) return newgeom
def execute(self, context): from . import export_mesh from . import export_armature from . import export_action from .export_armature import create_cal3d_skeleton from .export_mesh import create_cal3d_materials from .export_mesh import create_cal3d_mesh from .export_action import create_cal3d_animation cal3d_dirname = os.path.dirname(self.filepath) cal3d_skeleton = None cal3d_materials = [] cal3d_meshes = [] cal3d_animations = [] armature_obj = None # base_translation, base_rotation, and base_scale are user adjustments to the export base_translation = mathutils.Vector([0.0, 0.0, 0.0]) base_rotation = mathutils.Euler([self.base_rotation[0], self.base_rotation[1], self.base_rotation[2]], 'XYZ').to_matrix() base_scale = self.base_scale fps = self.fps #visible_objects = [ob for ob in context.scene.objects if ob.is_visible(context.scene)] visible_objects = context.selected_objects # Export armatures try: for obj in visible_objects: if obj.type == "ARMATURE": if cal3d_skeleton: raise RuntimeError("Only one armature is supported per scene") armature_obj = obj cal3d_skeleton = create_cal3d_skeleton(obj, obj.data, base_rotation.copy(), base_translation.copy(), base_scale, 900) except Exception as e: print("###### ERROR DURING ARMATURE EXPORT ######") traceback.print_exc() return {"FINISHED"} # Export meshes and materials try: cal3d_materials = create_cal3d_materials(cal3d_dirname, self.imagepath_prefix, 900) for obj in visible_objects: if obj.type == "MESH" and obj.is_visible(context.scene): cal3d_meshes.append(create_cal3d_mesh(context.scene, obj, cal3d_skeleton, cal3d_materials, base_rotation, base_translation, base_scale, 900, self.use_groups, False, armature_obj)) except RuntimeError as e: print("###### ERROR DURING MESH EXPORT ######") print(e) return {"FINISHED"} # Export animations try: if cal3d_skeleton: for action in bpy.data.actions: cal3d_animation = create_cal3d_animation(cal3d_skeleton, action, fps, 900) if cal3d_animation: cal3d_animations.append(cal3d_animation) except RuntimeError as e: print("###### ERROR DURING ACTION EXPORT ######") print(e) return {"FINISHED"} if cal3d_skeleton: if self.skeleton_binary_bool == 'binary': skeleton_filename = self.skeleton_prefix + cal3d_skeleton.name + ".csf" skeleton_filepath = os.path.join(cal3d_dirname, skeleton_filename) cal3d_skeleton_file = open(skeleton_filepath, "wb") cal3d_skeleton.to_cal3d_binary(cal3d_skeleton_file) else: skeleton_filename = self.skeleton_prefix + cal3d_skeleton.name + ".xsf" skeleton_filepath = os.path.join(cal3d_dirname, skeleton_filename) cal3d_skeleton_file = open(skeleton_filepath, "wt") cal3d_skeleton_file.write(cal3d_skeleton.to_cal3d_xml()) cal3d_skeleton_file.close() print("Wrote skeleton '%s'" % (skeleton_filename)) i = 0 for cal3d_material in cal3d_materials: if self.material_binary_bool == 'binary': material_filename = self.material_prefix + cal3d_material.name + ".crf" material_filepath = os.path.join(cal3d_dirname, material_filename) cal3d_material_file = open(material_filepath, "wb") cal3d_material.to_cal3d_binary(cal3d_material_file) else: material_filename = self.material_prefix + cal3d_material.name + ".xrf" material_filepath = os.path.join(cal3d_dirname, material_filename) cal3d_material_file = open(material_filepath, "wt") cal3d_material_file.write(cal3d_material.to_cal3d_xml()) cal3d_material_file.close() print("Wrote material '%s' with index %s" % (material_filename, i)) i += 1 for cal3d_mesh in cal3d_meshes: if self.mesh_binary_bool == 'binary': mesh_filename = self.mesh_prefix + cal3d_mesh.name + ".cmf" mesh_filepath = os.path.join(cal3d_dirname, mesh_filename) cal3d_mesh_file = open(mesh_filepath, "wb") cal3d_mesh.to_cal3d_binary(cal3d_mesh_file) else: mesh_filename = self.mesh_prefix + cal3d_mesh.name + ".xmf" mesh_filepath = os.path.join(cal3d_dirname, mesh_filename) cal3d_mesh_file = open(mesh_filepath, "wt") cal3d_mesh_file.write(cal3d_mesh.to_cal3d_xml()) cal3d_mesh_file.close() print("Wrote mesh '%s' with materials %s" % (mesh_filename, [x.material_id for x in cal3d_mesh.submeshes])) for cal3d_animation in cal3d_animations: if self.animation_binary_bool == 'binary': animation_filename = self.anim_prefix + cal3d_animation.name + ".caf" animation_filepath = os.path.join(cal3d_dirname, animation_filename) cal3d_animation_file = open(animation_filepath, "wb") cal3d_animation.to_cal3d_binary(cal3d_animation_file) else: animation_filename = self.anim_prefix + cal3d_animation.name + ".xaf" animation_filepath = os.path.join(cal3d_dirname, animation_filename) cal3d_animation_file = open(animation_filepath, "wt") cal3d_animation_file.write(cal3d_animation.to_cal3d_xml()) cal3d_animation_file.close() print("Wrote animation '%s'" % (animation_filename)) if self.export_cfg: cal3d_cfg_file = open(self.filepath, "wt") # lolwut? #cal3d_cfg_file.write("path={0}\n".format("data\\models\\" + os.path.basename(self.filepath[:-4])+ "\\")) #cal3d_cfg_file.write("scale=0.01f\n") if cal3d_skeleton: if self.skeleton_binary_bool == 'binary': skeleton_filename = self.skeleton_prefix + cal3d_skeleton.name + ".csf" else: skeleton_filename = self.skeleton_prefix + cal3d_skeleton.name + ".xsf" cal3d_cfg_file.write("skeleton={0}\n".format(skeleton_filename)) for cal3d_animation in cal3d_animations: if self.animation_binary_bool == 'binary': animation_filename = self.anim_prefix + cal3d_animation.name + ".caf" else: animation_filename = self.anim_prefix + cal3d_animation.name + ".xaf" cal3d_cfg_file.write("animation={0}\n".format(animation_filename)) for cal3d_material in cal3d_materials: if self.material_binary_bool == 'binary': material_filename = self.material_prefix + cal3d_material.name + ".crf" else: material_filename = self.material_prefix + cal3d_material.name + ".xrf" cal3d_cfg_file.write("material={0}\n".format(material_filename)) for cal3d_mesh in cal3d_meshes: if self.mesh_binary_bool == 'binary': mesh_filename = self.mesh_prefix + cal3d_mesh.name + ".cmf" else: mesh_filename = self.mesh_prefix + cal3d_mesh.name + ".xmf" cal3d_cfg_file.write("mesh={0}\n".format(mesh_filename)) cal3d_cfg_file.close() return {"FINISHED"}
def configMovement(self, P, A, J, a, b, y, o): a[1] = mathutils.Euler((P, A, 0.0), 'XYZ') print("a1 =", a[1]) a[2] = mathutils.Euler((A, -A, 0.0), 'XYZ') print("a2 =", a[2]) b[1] = mathutils.Euler((-A, A, 0.0), 'XYZ') print("b1 =", b[1]) o[1] = mathutils.Euler((A, A, 0.0), 'XYZ') print("o1 =", o[1]) B = A * 2 * sqrt(2) C = B + (B * sqrt(2)) D = C * sqrt(2) E = C + D y[1] = mathutils.Euler((-A, -A, 0.0), 'XYZ') print("y1 =", y[1]) b[2] = mathutils.Euler(((9.4 / 0.6) * A, (-8.2 / 0.6) * A, 0.0), 'XYZ') print("b2 =", b[2]) b[3] = mathutils.Euler( ((7.399085 / 0.6) * A, (-19.717056 / 0.6) * A, 0.0), 'XYZ') print("b3 =", b[3]) y[2] = mathutils.Euler(((8.2 / 0.6) * A, (-9.4 / 0.6) * A, 0.0), 'XYZ') print("y2 =", y[2]) y[3] = mathutils.Euler( ((9.08969 / 0.6) * A, (-19.569149 / 0.6) * A, 0.0), 'XYZ') print("y3 =", y[3]) y[4] = mathutils.Euler( ((9.2376 / 0.6) * A, (-21.259787 / 0.6) * A, 0.0), 'XYZ') print("y4 =", y[4]) o[2] = mathutils.Euler( ((6.509395 / 0.6) * A, (-9.547907 / 0.6) * A, 0.0), 'XYZ') print("o2 =", o[2]) o[3] = mathutils.Euler( ((10.38971 / 0.6) * A, (-20.659994 / 0.6) * A, 0.0), 'XYZ') print("o3 =", o[3])
def run(self): """ Performs physics simulation in the scene. """ # locations of all soon to be active objects before we shift their origin points locations_before_origin_shift = {} for obj in get_all_mesh_objects(): if obj["physics"]: locations_before_origin_shift.update( {obj.name: obj.location.copy()}) # enable rigid body and shift origin point for active objects locations_after_origin_shift = self._add_rigidbody() # compute origin point shift for all active objects origin_shift = {} for obj in locations_after_origin_shift: shift = locations_before_origin_shift[ obj] - locations_after_origin_shift[obj] origin_shift.update({obj: shift}) bpy.context.scene.rigidbody_world.steps_per_second = self.steps_per_sec bpy.context.scene.rigidbody_world.solver_iterations = self.solver_iters obj_poses_before_sim = self._get_pose() # perform simulation obj_poses_after_sim = self._do_simulation() # reset origin point of all active objects to the total shift location of the 3D cursor for obj in get_all_mesh_objects(): if obj.rigid_body.type == "ACTIVE": bpy.context.view_layer.objects.active = obj obj.select_set(True) # compute relative object rotation before and after simulation R_obj_before_sim = mathutils.Euler( obj_poses_before_sim[obj.name]['rotation']).to_matrix() R_obj_after = mathutils.Euler( obj_poses_after_sim[obj.name]['rotation']).to_matrix() R_obj_rel = R_obj_before_sim @ R_obj_after.transposed() # compute origin shift in object coordinates origin_shift[ obj.name] = R_obj_rel.transposed() @ origin_shift[obj.name] # set 3d cursor location to the total shift of the object bpy.context.scene.cursor.location = origin_shift[ obj.name] + obj_poses_after_sim[obj.name]['location'] bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN') obj.select_set(False) # reset 3D cursor location bpy.context.scene.cursor.location = mathutils.Vector([0, 0, 0]) # get current poses curr_pose = self._get_pose() # displace for the origin shift final_poses = {} for obj in curr_pose: final_poses.update({ obj: { 'location': curr_pose[obj]['location'] + origin_shift[obj], 'rotation': curr_pose[obj]['rotation'] } }) self._set_pose(final_poses) self._remove_rigidbody()
def Set_Rotation_Curves(action, mode_from, mode_to, remove, selection, object_curves): # copy the action so we can remove curves and re-add them... action_copy = action.copy() # these two bool conditions would be annoying to keep calling on... is_from_euler = True if mode_from in [ 'XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX' ] else False is_to_euler = True if mode_to in [ 'XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX' ] else False # get the strings we need for the to and from data paths... rot_path_to = "rotation_quaternion" if mode_to == 'QUATERNION' else "rotation_axis_angle" if mode_to == 'AXIS_ANGLE' else "rotation_euler" rot_path_from = "rotation_quaternion" if mode_from == 'QUATERNION' else "rotation_axis_angle" if mode_from == 'AXIS_ANGLE' else "rotation_euler" # get the original rotation fcurves... rot_curves = Get_Rotation_Curves(action, rot_path_from, selection, object_curves) # then we want to operate on the copy... rot_curves_copy = Get_Rotation_Curves(action_copy, rot_path_from, selection, object_curves) rot_curves_from = rot_curves_copy[rot_path_from] # iterate over data path and channel index dictionary... for d_path, indices in rot_curves_from.items(): # if the data_path is the one we wish to change from... if d_path.endswith(rot_path_from): # get the base path without rotation string... b_path = d_path[:-19] if mode_from in ['QUATERNION', 'AXIS_ANGLE' ] else d_path[:-14] # if we are coming from eulers to quat/axis angle... if (is_from_euler and not is_to_euler): # and there is already a w curve... if (b_path + rot_path_to in rot_curves[rot_path_to] and 0 in rot_curves[rot_path_to][b_path + rot_path_to]): # remove it... action.fcurves.remove( rot_curves[rot_path_to][b_path + rot_path_to][new_index]) # add a w curve... w_curve = action.fcurves.new( data_path=b_path + rot_path_to, index=0, action_group=b_path.partition('"')[2].split('"')[0]) # iterate over the index and channel dictionary... for index, fcurve in indices.items(): if not (index == 0 and (is_to_euler and not is_from_euler)): # new index is subtracted by 1 if we are switching to euler from quat/axis angle and + 1 if we are switching to quat/axis angle from euler... new_index = index + ( -1 if (is_to_euler and not is_from_euler) else 1 if (is_from_euler and not is_to_euler) else 0) # if the new curve already exists... if b_path + rot_path_to in rot_curves[ rot_path_to] and new_index in rot_curves[ rot_path_to][b_path + rot_path_to]: # remove it... action.fcurves.remove( rot_curves[rot_path_to][b_path + rot_path_to][new_index]) # add the new curve by adding the the base data path to the desired data path... if fcurve.group: new_curve = action.fcurves.new( data_path=b_path + rot_path_to, index=new_index, action_group=fcurve.group.name) else: new_curve = action.fcurves.new(data_path=b_path + rot_path_to, index=new_index) new_curve.auto_smoothing = fcurve.auto_smoothing new_curve.extrapolation = fcurve.extrapolation # for each keyframe in the fcurve... for key in fcurve.keyframe_points: # get the rotation as evaluated floats from the curves of the same data path... w = rot_curves_from[d_path][0].evaluate(key.co[0]) x = rot_curves_from[d_path][1].evaluate( key.co[0] ) if not is_from_euler else rot_curves_from[d_path][ 0].evaluate(key.co[0]) y = rot_curves_from[d_path][2].evaluate( key.co[0] ) if not is_from_euler else rot_curves_from[d_path][ 1].evaluate(key.co[0]) z = rot_curves_from[d_path][3].evaluate( key.co[0] ) if not is_from_euler else rot_curves_from[d_path][ 2].evaluate(key.co[0]) # create a quaternion from the evaluated floats... quat = ( mathutils.Quaternion( (w, x, y, z)) if mode_from == 'QUATERNION' else # axis angle wants to be a quaternion created a little different... mathutils.Quaternion( (x, y, z), w) if mode_from == 'AXIS_ANGLE' else # euler wants to be converted to quaternion... mathutils.Euler( (x, y, z), mode_from).to_quaternion()) # set that rotation to what we want it to be... new_rot = ( quat if mode_to == 'QUATERNION' else # rotation needs to be unpacked for axis angle... [ quat.to_axis_angle()[1], quat.to_axis_angle()[0][0], quat.to_axis_angle()[0][1], quat.to_axis_angle()[0][2] ] if mode_to == 'AXIS_ANGLE' else # rotation needs converting for euler... quat.to_euler(mode_to)) # add a new key in the same place... new_key = new_curve.keyframe_points.insert( key.co[0], new_rot[new_index], keyframe_type='KEYFRAME') # set that keyframes curve coordinates and settings... new_key.co[0] = key.co[0] new_key.co[1] = new_rot[new_index] new_key.handle_left_type = key.handle_left_type new_key.handle_right_type = key.handle_right_type # custom curve handles will be close approximations until i can figured out a better way to calculate them... new_key.handle_left[0] = key.handle_left[0] new_key.handle_left[1] = new_key.co[1] * ( key.handle_left[1] / (key.co[1] + (1 if key.co[1] == 0 else 0))) new_key.handle_right[0] = key.handle_right[0] new_key.handle_right[1] = new_key.co[1] * ( key.handle_right[1] / (key.co[1] + (1 if key.co[1] == 0 else 0))) # and the rest of the keyframe settings... new_key.interpolation = key.interpolation new_key.period = key.period new_key.easing = key.easing new_key.amplitude = key.amplitude new_key.back = key.back if key.type in [ 'KEYFRAME', 'BREAKDOWN', 'MOVING_HOLD', 'EXTREME', 'JITTER' ]: new_key.type = key.type # if we came from eulers... if (is_from_euler and not is_to_euler): if w_curve != None: # we need to key w everytime any channel is keyed... (unable to support interpolation and custom handles on a curve that doesn't exist) w_key = w_curve.keyframe_points.insert( key.co[0], new_rot[0], keyframe_type='KEYFRAME') w_key.co[1] = new_rot[0] w_key.handle_left[1] = new_rot[0] w_key.handle_right[1] = new_rot[0] # if we want to remove the old fcurves... if remove and not (is_from_euler and is_to_euler): for fcurve in [ fc for fc in action.fcurves if fc.data_path == d_path ]: action.fcurves.remove(fcurve) # get rid of the copy we operated on... bpy.data.actions.remove(action_copy)
def configMovement(self, P, A, J, a, b, y, o): a[1] = mathutils.Euler((P, A, 0.0), 'XYZ') print("a1 =", a[1]) a[2] = mathutils.Euler((A, -A, 0.0), 'XYZ') print("a2 =", a[2]) b[1] = mathutils.Euler((-A, A, 0.0), 'XYZ') print("b1 =", b[1]) o[1] = mathutils.Euler((A, A, 0.0), 'XYZ') print("o1 =", o[1]) B = A * 2 * sqrt(2) C = B + (B * sqrt(2)) D = C * sqrt(2) E = C + D y[1] = mathutils.Euler((-A, -A, 0.0), 'XYZ') print("y1 =", y[1]) b[2] = mathutils.Euler(((4.08 / 0.7) * A, (-2.68 / 0.7) * A, 0.0), 'XYZ') print("b2 =", b[2]) b[3] = mathutils.Euler( ((2.520382 / 0.7) * A, (-7.734981 / 0.7) * A, 0.0), 'XYZ') print("b3 =", b[3]) b[4] = mathutils.Euler( ((4.650852 / 0.7) * A, (-10.086805 / 0.7) * A, 0.0), 'XYZ') print("b4 =", b[4]) y[2] = mathutils.Euler(((2.68 / 0.7) * A, (-4.08 / 0.7) * A, 0.0), 'XYZ') print("y2 =", y[2]) y[3] = mathutils.Euler( ((4.314873 / 0.7) * A, (-8.571764 / 0.7) * A, 0.0), 'XYZ') print("y3 =", y[3]) y[4] = mathutils.Euler( ((4.065916 / 0.7) * A, (-9.98368 / 0.7) * A, 0.0), 'XYZ') print("y4 =", y[4]) y[5] = mathutils.Euler( ((3.816914 / 0.7) * A, (-11.395846 / 0.7) * A, 0.0), 'XYZ') print("y5 =", y[5]) o[2] = mathutils.Euler( ((4.5405 / 0.7) * A, (-3.402836 / 0.7) * A, 0.0), 'XYZ') print("o2 =", o[2]) o[3] = mathutils.Euler( ((4.899491 / 0.7) * A, (-8.674883 / 0.7) * A, 0.0), 'XYZ') print("o3 =", o[3]) o[4] = b[4] print("o4 =", o[4])
def pmx_euler2quat(eular: XMLRotate) -> Math.Vector: radian = (radians(eular.x), radians(eular.y), radians(eular.z)) rotate_euler = Math.Euler(radian, "ZXY") rotate_quat = rotate_euler.to_quaternion() return Math.Vector( (rotate_quat.x, rotate_quat.y, rotate_quat.z, rotate_quat.w))
def render_scene(args, num_objects=5, output_index=0, output_split='none', output_image='render.png', output_scene='render_json', output_blendfile=None, img_template='image%d.png'): bpy.ops.wm.open_mainfile(filepath=args.base_scene_blendfile) # Load materials utils.load_materials(args.material_dir) # node_path = '/home/bozidar/uni/prac/repos/clevr-dataset-gen/image_generation/data/NodeGroupMulti4.blend' node_path = '/home/bozidar/uni/prac/repos/clevr-dataset-gen/image_generation/data/NodeGroup.blend' with bpy.data.libraries.load(node_path) as (data_from, data_to): data_to.objects = data_from.objects data_to.materials = data_from.materials data_to.node_groups = data_from.node_groups node_mat = data_to.materials[0] node_group_elems = data_to.node_groups[0].nodes[ "ColorRamp"].color_ramp.elements # for i in segm_colors: # print(list(i.color)) # Set render arguments so we can get pixel coordinates later. # We use functionality specific to the CYCLES renderer so BLENDER_RENDER # cannot be used. render_args = bpy.context.scene.render render_args.engine = "CYCLES" render_args.filepath = output_image render_args.resolution_x = args.width render_args.resolution_y = args.height render_args.resolution_percentage = 100 render_args.tile_x = args.render_tile_size render_args.tile_y = args.render_tile_size if args.use_gpu == 1: # Blender changed the API for enabling CUDA at some point if bpy.app.version < (2, 78, 0): bpy.context.user_preferences.system.compute_device_type = 'CUDA' bpy.context.user_preferences.system.compute_device = 'CUDA_0' else: cycles_prefs = bpy.context.user_preferences.addons[ 'cycles'].preferences cycles_prefs.compute_device_type = 'CUDA' # Some CYCLES-specific stuff bpy.data.worlds['World'].cycles.sample_as_light = True bpy.context.scene.cycles.blur_glossy = 2.0 bpy.context.scene.cycles.samples = args.render_num_samples bpy.context.scene.cycles.transparent_min_bounces = args.render_min_bounces bpy.context.scene.cycles.transparent_max_bounces = args.render_max_bounces if args.use_gpu == 1: bpy.context.scene.cycles.device = 'GPU' # This will give ground-truth information about the scene and its objects scene_struct = { 'split': output_split, 'image_index': output_index, 'image_filename': os.path.basename(output_image), 'objects': [], 'directions': {}, } # Put a plane on the ground so we can compute cardinal directions if bpy.app.version < (2, 80, 0): bpy.ops.mesh.primitive_plane_add(radius=5) else: bpy.ops.mesh.primitive_plane_add(size=5) plane = bpy.context.object def rand(L): return 2.0 * L * (random.random() - 0.5) # Add random jitter to camera position if args.camera_jitter > 0: for i in range(3): bpy.data.objects['Camera'].location[i] += rand(args.camera_jitter) # Figure out the left, up, and behind directions along the plane and record # them in the scene structure camera = bpy.data.objects['Camera'] plane_normal = plane.data.vertices[0].normal if bpy.app.version < (2, 80, 0): cam_behind = camera.matrix_world.to_quaternion() * Vector((0, 0, -1)) cam_left = camera.matrix_world.to_quaternion() * Vector((-1, 0, 0)) cam_up = camera.matrix_world.to_quaternion() * Vector((0, 1, 0)) else: cam_behind = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1)) cam_left = camera.matrix_world.to_quaternion() @ Vector((-1, 0, 0)) cam_up = camera.matrix_world.to_quaternion() @ Vector((0, 1, 0)) plane_behind = (cam_behind - cam_behind.project(plane_normal)).normalized() plane_left = (cam_left - cam_left.project(plane_normal)).normalized() plane_up = cam_up.project(plane_normal).normalized() # Delete the plane; we only used it for normals anyway. The base scene file # contains the actual ground plane. utils.delete_object(plane) # Save all six axis-aligned directions in the scene struct scene_struct['directions']['behind'] = tuple(plane_behind) scene_struct['directions']['front'] = tuple(-plane_behind) scene_struct['directions']['left'] = tuple(plane_left) scene_struct['directions']['right'] = tuple(-plane_left) scene_struct['directions']['above'] = tuple(plane_up) scene_struct['directions']['below'] = tuple(-plane_up) # Add random jitter to lamp positions if args.key_light_jitter > 0: for i in range(3): bpy.data.objects['Lamp_Key'].location[i] += rand( args.key_light_jitter) if args.back_light_jitter > 0: for i in range(3): bpy.data.objects['Lamp_Back'].location[i] += rand( args.back_light_jitter) if args.fill_light_jitter > 0: for i in range(3): bpy.data.objects['Lamp_Fill'].location[i] += rand( args.fill_light_jitter) # Now make some random objects objects, blender_objects = add_random_objects(scene_struct, num_objects, args, camera) # Segmentation materials and colors n = len(objects) node_mat.node_tree.nodes['Group'].inputs[1].default_value = n segm_mat = [] segm_color = [] for i in range(n + 1): node_mat.node_tree.nodes['Group'].inputs[0].default_value = i segm_mat.append(node_mat.copy()) segm_color.append(list(node_group_elems[i].color)) print(segm_mat) print(segm_color) angles = [-50, 90] steps = 5 for i, a in enumerate(np.linspace(*angles, steps)): # position = bpy.data.objects['Lamp_Key'].location # r = R.from_euler(axis, a, degrees=True).as_matrix() r = mathutils.Euler((0.0, math.radians(a), 0.0), 'XYZ') # r = mathutils.Euler((math.radians(30), math.radians(a), 0.0), 'XYZ') # bpy.data.objects['Lamp_Back'].location.rotate(r) # bpy.data.objects['Area'].location.rotate(r) bpy.data.objects['Area'].rotation_euler = r scene_struct['image_index'] = output_index * steps + i render_args.filepath = img_template % (output_index * steps + i) # bpy.data.objects['Sphere_0'].select = True # obj = bpy.context.selected_objects # for obj in bpy.context.selected_objects: # obj.select = False # bpy.context.scene.objects.active = None # bpy.context.scene.objects.active = bpy.data.objects['Ground'] print('---------------------------') print(objects) print('---------------------------') print(bpy.data.objects.items()) print('---------------------------') # exit() # Render the scene and dump the scene data structure scene_struct['objects'] = objects scene_struct['relationships'] = compute_all_relationships(scene_struct) while True: try: bpy.ops.render.render(write_still=True) break except Exception as e: print(e) with open(output_scene, 'w') as f: json.dump(scene_struct, f, indent=2) if output_blendfile is not None: print('===============================>', output_blendfile) bpy.ops.wm.save_as_mainfile(filepath=output_blendfile) # segm rendering s = render_args.filepath ind = s.rindex('.') render_args.filepath = s[:ind] + '_segm' + s[ind:] prev_mat = [] bpy.data.objects['Ground'].data.materials.clear() bpy.data.objects['Ground'].data.materials.append(segm_mat[0]) for i in range(n): prev_mat.append(bpy.data.objects[i - n].data.materials[0]) scene_name = bpy.data.objects[i - n].name index = -1 for obj in objects: if obj['scene_name'] == scene_name: index = obj['index'] obj['segm_color'] = segm_color[obj['index'] + 1] bpy.data.objects[i - n].data.materials.clear() bpy.data.objects[i - n].data.materials.append(segm_mat[index + 1]) while True: try: bpy.ops.render.render(write_still=True) break except Exception as e: print(e) bpy.data.objects['Ground'].data.materials.clear() for i in range(n): bpy.data.objects[i - n].data.materials.clear() bpy.data.objects[i - n].data.materials.append(prev_mat[i])
def lanes_generate(ops, left_lanes, right_lanes, road_width, offset, invert): bpy.ops.object.mode_set(mode='EDIT') me = bpy.context.edit_object.data bm = bmesh.from_edit_mesh(me) edges = [e for e in bm.edges if e.select and not e.hide] lines = get_lines(edges) if len(lines) != 1: ops.report({"WARNING"}, "1つの線になるように辺を選択して下さい") return False vs = get_sorted_verts(lines[0]) if invert: vs.reverse() vs_n = [] rot_l = mathutils.Euler((0.0, 0.0, math.radians(90.0)), 'XYZ') rot_r = mathutils.Euler((0.0, 0.0, math.radians(-90.0)), 'XYZ') rot_c = None for n, v in enumerate(vs): v.select = False vb = None vf = None if n != 0: vb = vs[n - 1] if n != len(vs) - 1: vf = vs[n + 1] co_bn = None co_fn = None if vb != None: rot_b = (v.co - vb.co) rot_b.z = 0.0 rot_b.normalize() co_bn = mathutils.Vector(rot_b) co_bn.rotate(rot_l) co_bn *= road_width / 2 if vf != None: rot_f = (vf.co - v.co) rot_f.z = 0.0 rot_f.normalize() co_fn = mathutils.Vector(rot_f) co_fn.rotate(rot_l) co_fn *= road_width / 2 if n == 0: vs_n = vs_n + [v.co + co_fn] elif n == len(vs) - 1: vs_n = vs_n + [v.co + co_bn] else: vc = get_cross_point(vb.co + co_bn, v.co + co_bn, vf.co + co_fn, v.co + co_fn, False) vc.z = v.co.z vs_n = vs_n + [vc] vb = None for v in zip(vs_n): nv = bm.verts.new(v) if vb != None: bm.edges.new([vb, nv]).select = True vb = nv bmesh.update_edit_mesh(me) return True
for f in range(0, s_end + 1): px = camdata["cameraFrames"][f]["position"]["x"] py = camdata["cameraFrames"][f]["position"]["y"] pz = camdata["cameraFrames"][f]["position"]["z"] rx = float(camdata["cameraFrames"][f]["rotation"]["x"]) ry = camdata["cameraFrames"][f]["rotation"]["y"] rz = camdata["cameraFrames"][f]["rotation"]["z"] # position set in relation to first frame - scale to 1/100 cam.location.x = (px - psx) / 100 cam.location.y = (py - psy) / 100 cam.location.z = (pz - psz) / 100 eul = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') eul.rotate_axis('X', math.radians(-rx)) eul.rotate_axis('Y', math.radians(ry)) eul.rotate_axis('Z', math.radians(-rz + 180)) cam.rotation_euler = eul cam.keyframe_insert(data_path="location", index=-1, frame=f + 1) cam.keyframe_insert(data_path="rotation_euler", index=-1, frame=f + 1) cam.data.type = 'PERSP' cam.data.lens_unit = 'FOV' # camera "lens" based on json for f in range(0, s_end + 1):
def __xyzw_from_euler(xyzw): q = mathutils.Euler(xyzw[:3], xyzw[3]).to_quaternion() return [q.x, q.y, q.z, q.w]
def load(operator, context, files=[], filepath="", set_fps=False): starttime = time.clock() dirname, filename = os.path.split(filepath) data = load_bani(filepath) # data 0 has various scales and counts anim_length = data.header.data_0.animation_length num_frames = data.header.data_0.num_frames global_corr_euler = mathutils.Euler( [math.radians(k) for k in (0, -90, -90)]) global_corr_mat = global_corr_euler.to_matrix().to_4x4() fps = int(round(num_frames / anim_length)) bpy.context.scene.frame_start = 0 bpy.context.scene.frame_end = num_frames - 1 print("Banis fps", fps) ob = get_armature() bones_table = [(bone["index"], bone.name) for bone in ob.pose.bones] bone_names = [tup[1] for tup in sorted(bones_table)] # ns = ("def_rearLegUpr_joint.L", "def_rearLegUprHalfTwist_joint.L", "def_rearLegUprAllTwist_joint.L", # "def_rearLegLwr_joint.L", "def_rearLegLwrHalfTwist_joint.L", "def_rearLegLwrAllTwist_joint.L") # # ns = ("def_c_neck_joint", "def_c_head_joint", "def_wing_joint.L", "def_wing02_joint.L", "def_wing_joint.R", "def_wing02_joint.R") # # ns = ("def_rearHorselink_joint.L", "def_rearFoot_joint.L", "def_toeRearRing1_joint.L", "def_toeRearRing2_joint.L", "def_toeRearRing3_joint.L", # # "def_rearHorselink_joint.R", "def_rearFoot_joint.R", "def_toeRearRing1_joint.R", "def_toeRearRing2_joint.R", "def_toeRearRing3_joint.R") # # # a rotation is not relative to the parent, but relative to the rest pose # # for n in ns: # i = bone_names.index(n) # euler = data.eulers[0, i] # # loc = data.locs[:, i] # loc = data.locs[0, i] # print(n, "euler", euler) # print(n, "locat", loc) # return # bone_names = ovl_bones(ob.data) # goliath imports fine with the correct name order # bone_names = ['def_c_root_joint', 'def_c_hips_joint', 'def_c_spine1_joint', 'def_c_spine2_joint', 'def_c_chestBreath_joint', 'def_c_spine3_joint', 'def_c_chest_joint', 'def_c_neck1_joint', 'def_c_head_joint', 'def_c_jaw_joint', 'def_l_clavicle_joint', 'def_l_frontLegUpr_joint', 'def_l_frontLegLwr_joint', 'def_l_frontFoot_joint', 'def_l_toeFrontIndex1_joint', 'def_l_toeFrontIndex2_joint', 'def_l_toeFrontPinky1_joint', 'def_l_toeFrontPinky2_joint', 'def_l_toeFrontPinky3_joint', 'def_l_toeFrontRing1_joint', 'def_l_toeFrontRing2_joint', 'def_l_toeFrontRing3_joint', 'def_l_frontLegLwrAllTwist_joint', 'def_l_frontLegLwrHalfTwist_joint', 'def_l_frontLegUprAllTwist_joint', 'def_r_clavicle_joint', 'def_r_frontLegUpr_joint', 'def_r_frontLegLwr_joint', 'def_r_frontFoot_joint', 'def_r_toeFrontIndex1_joint', 'def_r_toeFrontIndex2_joint', 'def_r_toeFrontPinky1_joint', 'def_r_toeFrontPinky2_joint', 'def_r_toeFrontPinky3_joint', 'def_r_toeFrontRing1_joint', 'def_r_toeFrontRing2_joint', 'def_r_toeFrontRing3_joint', 'def_r_frontLegLwrAllTwist_joint', 'def_r_frontLegLwrHalfTwist_joint', 'def_r_frontLegUprAllTwist_joint', 'def_l_rearLegUpr_joint', 'def_l_rearLegLwr_joint', 'def_l_rearHorselink_joint', 'def_l_rearFoot_joint', 'def_l_toeRearMid1_joint', 'def_l_toeRearMid2_joint', 'def_l_toeRearMid3_joint', 'def_l_toeRearPinky1_joint', 'def_l_toeRearPinky2_joint', 'def_l_toeRearPinky3_joint', 'def_l_toeRearRing1_joint', 'def_l_toeRearRing2_joint', 'def_l_toeRearRing3_joint', 'def_l_toeRearThumb1_joint', 'def_l_toeRearThumb2_joint', 'def_l_toeRearThumb3_joint', 'def_l_rearLegLwrAllTwist_joint', 'def_l_rearLegLwrHalfTwist_joint', 'def_l_rearLegUprAllTwist_joint', 'def_r_rearLegUpr_joint', 'def_r_rearLegLwr_joint', 'def_r_rearHorselink_joint', 'def_r_rearFoot_joint', 'def_r_toeRearMid1_joint', 'def_r_toeRearMid2_joint', 'def_r_toeRearMid3_joint', 'def_r_toeRearPinky1_joint', 'def_r_toeRearPinky2_joint', 'def_r_toeRearPinky3_joint', 'def_r_toeRearRing1_joint', 'def_r_toeRearRing2_joint', 'def_r_toeRearRing3_joint', 'def_r_toeRearThumb1_joint', 'def_r_toeRearThumb2_joint', 'def_r_toeRearThumb3_joint', 'def_r_rearLegLwrAllTwist_joint', 'def_r_rearLegLwrHalfTwist_joint', 'def_r_rearLegUprAllTwist_joint', 'def_c_throat_joint', 'def_l_eyelidUpr_joint', 'def_r_eyelidUpr_joint', 'def_l_toeFrontIndex3_joint', 'def_l_toeFrontThumb1_joint', 'def_l_toeFrontThumb2_joint', 'def_l_toeFrontThumb3_joint', 'def_l_frontLegUprHalfTwist_joint', 'def_r_toeFrontIndex3_joint', 'def_r_toeFrontThumb1_joint', 'def_r_toeFrontThumb2_joint', 'def_r_toeFrontThumb3_joint', 'def_r_frontLegUprHalfTwist_joint', 'def_l_chestBreath_joint', 'def_r_chestBreath_joint', 'def_l_toeRearIndex1_joint', 'def_l_toeRearIndex2_joint', 'def_l_toeRearIndex3_joint', 'def_l_rearLegUprHalfTwist_joint', 'def_r_toeRearIndex1_joint', 'def_r_toeRearIndex2_joint', 'def_r_toeRearIndex3_joint', 'def_r_rearLegUprHalfTwist_joint', 'rig_l_frontToe_joint', 'rig_r_frontToe_joint', 'rig_l_rearToe_joint', 'rig_r_rearToe_joint', 'srb'] # print(bone_names) # print(len(bone_names), len(data.bones_frames_eulers), len(data.bones_frames_locs)) # assert( len(bone_names) == len(data.bones_frames_eulers) == len(data.bones_frames_locs) ) action = create_anim(ob, filename) # go over list of euler keys for i, bone_name in enumerate(bone_names): # print(i, bone_name) # bone_keys = data.eulers_dict[bone_name] # bone_name = bone_name.decode() # get pose pbone pbone = ob.pose.bones[bone_name] pbone.rotation_mode = "XYZ" # get object mode bone obone = ob.data.bones[bone_name] armature_space_matrix = obone.matrix_local for frame_i in range(data.header.data_0.num_frames): bpy.context.scene.frame_set(frame_i) euler = data.eulers[frame_i, i] loc = data.locs[frame_i, i] # create fcurves data_type = "rotation_euler" # fcu = [action.fcurves.new(data_path = 'pose.bones["'+bone_name+'"].'+data_type, index = i, action_group = bone_name) for i in (0,1,2)] # for fcurve, k in zip(fcu, key): # fcurve.keyframe_points.insert(frame_i, math.radians(k))#.interpolation = "Linear" euler = mathutils.Euler([math.radians(k) for k in euler]) # experiments # trans = (global_corr_mat @ mathutils.Vector(loc)) + armature_space_matrix.translation # mdl2 vectors: (-x,-z,y) # loc = mathutils.Vector((-loc[0], -loc[2], loc[1])) loc = mathutils.Vector(loc) # trans = (mathutils.Vector(loc)) + armature_space_matrix.translation # the eulers are applied globally to the bone, equivalent to the user doing R+X, R+Y, R+Z for each axis. # this expresses the rotation that should be done in blender coordinates about the center of the bone space_corrected_rot = global_corr_mat @ euler.to_matrix().to_4x4() # rot_mat is the final armature space matrix of the posed bone rot_mat = space_corrected_rot @ armature_space_matrix # rot_mat.translation = (space_corrected_rot @ loc) + armature_space_matrix.translation # loc_key = (space_corrected_rot @ mathutils.Vector(loc)) loc_key = (euler.to_matrix().to_4x4() @ loc) # loc_key = ( loc @ space_corrected_rot) # loc_key = mathutils.Vector((-loc_key[0], -loc_key[2], loc_key[1])) # rot_mat.translation = loc_key + armature_space_matrix.translation # the ideal translation as calculated by blender rot_mat.translation = pbone.matrix.translation # print(rot_mat) pbone.matrix = rot_mat pbone.keyframe_insert(data_path="rotation_euler", frame=frame_i, group=bone_name) pbone.keyframe_insert(data_path="location", frame=frame_i, group=bone_name) return {'FINISHED'}
def constructLink(self, A, J, helicity, rig, move, part): # Move and rotate the tip bone in pose mode bpy.context.view_layer.objects.active = rig Y = 1.1838*A for n in rig.pose.bones: if n.name != "o" + str(J-2) + "b" + str(J-1): # we can get the object from the pose bone obj = n.id_data matrix_final = obj.matrix_world @ n.matrix # Create armature and object lnk = bpy.data.armatures.new(n.name[:len(n.name)]+'.data.' + helicity) lnk_rig = bpy.data.objects.new(n.name[:len(n.name)]+'.link.' + helicity, lnk) lnk_rig.location = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') # rig.show_in_front = True lnk.show_names = True lnk.display_type = 'STICK' bpy.data.collections['link'].objects.link(lnk_rig) bpy.context.view_layer.objects.active = lnk_rig bpy.context.view_layer.update() # Create bones # mode='EDIT' bpy.ops.object.editmode_toggle() link = lnk.edit_bones.new(n.name[:len(n.name)]) link.head = (0.0, 0.0, 0.0) link.tail = (0.0, Y, 0.0) link_head = lnk.edit_bones.new('head') link_head.head = (0.0, 0.0, 0.1) link_head.tail = (0.0, 0.0, 0.0) link_head.parent = link link_head.use_inherit_scale = False link_tail = lnk.edit_bones.new('tail') link_tail.head = (0.0, Y, 0.0) link_tail.tail = (0.0, Y, -0.1) link_tail.parent = link link_tail.use_inherit_scale = False bpy.ops.object.mode_set(mode='OBJECT') ob = bpy.data.objects[n.name[:len(n.name)]+'.mesh.' + move + '.' + part +'.' + helicity] ob.location = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') # Give mesh object an armature modifier, using vertex groups but # not envelopes mod = ob.modifiers.new('MyRigModif', 'ARMATURE') mod.object = lnk_rig mod.use_bone_envelopes = False mod.use_vertex_groups = True # Bone constraints. Armature must be in pose mode. bpy.ops.object.mode_set(mode='POSE') # Copy rotation constraints Base -> Tip pBase = lnk_rig.pose.bones[n.name[:len(n.name)]] cns = pBase.constraints.new('COPY_LOCATION') cns.name = 'Copy_Location' cns.target = rig cns.subtarget = n.name[:len(n.name)] cns.owner_space = 'WORLD' cns.target_space = 'WORLD' # Copy rotation constraints Base -> Tip pBase = lnk_rig.pose.bones[n.name[:len(n.name)]] cns = pBase.constraints.new('COPY_ROTATION') cns.name = 'Copy_Rotation' cns.target = rig cns.subtarget = n.name[:len(n.name)] cns.owner_space = 'WORLD' cns.target_space = 'WORLD' # StretchTo constraint Mid -> Tip with influence 0.5 cns1 = pBase.constraints.new('STRETCH_TO') cns1.name = 'Stretch' cns1.target = rig cns1.subtarget = n.name[:len(n.name)] cns1.head_tail = 1 cns1.rest_length = Y cns1.influence = 1 cns1.keep_axis = 'PLANE_Z' cns1.volume = 'NO_VOLUME' bpy.ops.object.mode_set(mode='OBJECT')
def configMovement(self, P, A, J, a, b, y, o): a[1] = mathutils.Euler((P, A, 0.0), 'XYZ') print("a1 =", a[1]) a[2] = mathutils.Euler((A, -A, 0.0), 'XYZ') print("a2 =", a[2]) b[1] = mathutils.Euler((-A, A, 0.0), 'XYZ') print("b1 =", b[1]) o[1] = mathutils.Euler((A, A, 0.0), 'XYZ') print("o1 =", o[1]) B = A * 2 * sqrt(2) C = B + (B * sqrt(2)) D = C * sqrt(2) E = C + D y[1] = mathutils.Euler((-A, -A, 0.0), 'XYZ') print("y1 =", y[1]) b[2] = mathutils.Euler(((9.4 / 0.6) * A, (-8.2 / 0.6) * A, 0.0), 'XYZ') print("b2 =", b[2]) b[3] = mathutils.Euler( ((9.202797 / 0.6) * A, (-19.69939 / 0.6) * A, 0.0), 'XYZ') print("b3 =", b[3]) y[2] = mathutils.Euler(((8.2 / 0.6) * A, (-9.4 / 0.6) * A, 0.0), 'XYZ') print("y2 =", y[2]) y[3] = mathutils.Euler( ((10.842033 / 0.6) * A, (-19.260159 / 0.6) * A, 0.0), 'XYZ') print("y3 =", y[3]) y[4] = mathutils.Euler( ((11.281277 / 0.6) * A, (-20.899431 / 0.6) * A, 0.0), 'XYZ') print("y4 =", y[4]) o[2] = mathutils.Euler( ((6.560764 / 0.6) * A, (-9.83923 / 0.6) * A, 0.0), 'XYZ') print("o2 =", o[2]) o[3] = mathutils.Euler( ((12.311729 / 0.6) * A, (-20.108688 / 0.6) * A, 0.0), 'XYZ') print("o3 =", o[3])
def createGeometry(viscol, geomsrc, linkobj=None): """Creates Blender object for visual or collision objects. Returns reference to new object or None if creation failed. Args: viscol(dict): visual/collision dictionary element geomsrc(str): new object's phobostype linkobj(bpy.types.Object): link object Returns: bpy.types.Object or None """ if 'geometry' not in viscol or viscol['geometry'] is {}: return None bpy.ops.object.select_all(action='DESELECT') geom = viscol['geometry'] # create the Blender object if geom['type'] == 'mesh': bpy.context.scene.layers = bUtils.defLayers(defs.layerTypes[geomsrc]) meshname = "".join(os.path.basename(geom["filename"]).split(".")[:-1]) if not os.path.isfile(geom['filename']): log(geom['filename'] + " is no file. Object " + viscol['name'] + " will have empty mesh!", "ERROR") #bpy.data.meshes.new(meshname) bpy.ops.object.add(type='MESH') newgeom = bpy.context.active_object nUtils.safelyName(newgeom, viscol['name'], phobostype=geomsrc) else: if meshname in bpy.data.meshes: log('Assigning copy of existing mesh ' + meshname + ' to ' + viscol['name'], 'INFO') bpy.ops.object.add(type='MESH') newgeom = bpy.context.object newgeom.data = bpy.data.meshes[meshname] else: log("Importing mesh for {0} element: '{1}".format(geomsrc, viscol['name']), 'INFO') filetype = geom['filename'].split('.')[-1].lower() newgeom = meshes.importMesh(geom['filename'], filetype) newgeom.data.name = meshname if not newgeom: log('Failed to import mesh file ' + geom['filename'], 'ERROR') return # scale imported object if 'scale' in geom: newgeom.scale = geom['scale'] else: if geom['type'] == 'box': dimensions = geom['size'] elif geom['type'] == 'cylinder': dimensions = (geom['radius'], geom['length']) elif geom['type'] == 'sphere': dimensions = geom['radius'] else: log("Unknown geometry type of " + geomsrc + viscol['name'] + '. Placing empty coordinate system.', "ERROR") bpy.ops.object.empty_add(type='PLAIN_AXES', radius=0.2) obj = bpy.context.object obj.phobostype = geomsrc nUtils.safelyName(bpy.context.active_object, viscol['name'], phobostype=geomsrc) return None log('Creating primtive for {0}: {1}'.format(geomsrc, viscol['name']), 'INFO') newgeom = bUtils.createPrimitive(viscol['name'], geom['type'], dimensions, phobostype=geomsrc) newgeom.select = True bpy.ops.object.transform_apply(scale=True) # from here it's the same for both meshes and primitives newgeom['geometry/type'] = geom['type'] if geomsrc == 'visual': try: assignMaterial(newgeom, viscol['material']) except KeyError: log('No material for visual ' + viscol['name'], 'DEBUG') for prop in viscol: if prop.startswith('$'): for tag in viscol[prop]: newgeom[prop[1:]+'/'+tag] = viscol[prop][tag] nUtils.safelyName(newgeom, viscol['name']) newgeom[geomsrc+"/name"] = viscol['name'] newgeom.phobostype = geomsrc # place geometric object relative to its parent link if linkobj: if 'pose' in viscol: log('Setting transformation of element: ' + viscol['name'], 'DEBUG') location = mathutils.Matrix.Translation(viscol['pose']['translation']) rotation = mathutils.Euler(tuple(viscol['pose']['rotation_euler']), 'XYZ').to_matrix().to_4x4() else: log('No pose in element: ' + viscol['name'], 'DEBUG') location = mathutils.Matrix.Identity(4) rotation = mathutils.Matrix.Identity(4) sUtils.selectObjects([newgeom, linkobj], True, 1) bpy.ops.object.parent_set(type='BONE_RELATIVE') newgeom.matrix_local = location * rotation if 'scale' in viscol['geometry']: newgeom.scale = mathutils.Vector(viscol['geometry']['scale']) return newgeom
def evaluate(self, f): return mathutils.Euler(self.fcurves_evaluator.evaluate(f)).to_quaternion()
def convert_inverse_euler(blender_euler): """ input rad euler angles and convert to blender rad coordinates """ return list(mathutils.Euler(blender_euler, "ZYX"))
def write_actions(actions): global current_scene open_class("UpdateCallbacks") open_class("osgAnimation::BasicAnimationManager") write_indented("num_animations %d" % (len(actions))) for action in actions: open_class("osgAnimation::Animation") write_indented("name \"%s\"" % (action.name)) # restructure fcurves into channels (a channel combines x, y, and z for translation for example) channels = {} for fcurve in action.fcurves: if fcurve.data_path not in channels: channels[fcurve.data_path] = {} for keyframe in fcurve.keyframe_points: if keyframe.co[0] not in channels[fcurve.data_path]: channels[fcurve.data_path][keyframe.co[0]] = {} channels[fcurve.data_path][keyframe.co[0]][ fcurve.array_index] = keyframe.co[1] # fix any "holes" ie in blender we can animate on the x channel only - in this case we should either drop the animation or evaluate the curve at the hole for path in iter(channels): channel = channels[path] bone_name = get_bone_from_path(path) bone_property = get_property_from_path(path) channel_skipped = False if bone_name != None and bone_property != None: num_properties = 0 if bone_property.lower() == 'scale': num_properties = 3 elif bone_property.lower() == 'location': num_properties = 3 elif bone_property.lower() == 'rotation_euler': pose_bone = get_pose_bone_by_name(bone_name) if pose_bone != None and 'Z' in pose_bone.rotation_mode: num_properties = 3 elif bone_property.lower() == 'rotation_quaternion': pose_bone = get_pose_bone_by_name(bone_name) if pose_bone != None and pose_bone.rotation_mode == 'QUATERNION': num_properties = 4 if num_properties > 0: for keyframe in channels[path].keys(): for i in range(0, num_properties): if not i in channels[path][keyframe]: fcurve = get_action_fcurve(action, path, i) if fcurve != None: channels[path][keyframe][ i] = fcurve.evaluate(keyframe) else: # no fcurve exists for this property # we will have to skip the entire action channels.remove(path) channel_skipped = True if channel_skipped: break if channel_skipped: break write_indented("num_channels %d" % (len(channels))) for path in iter(channels): channel = channels[path] bone_name = get_bone_from_path(path) bone_property = get_property_from_path(path) if bone_name != None and bone_property != None: if bone_property.lower() == 'scale': open_class("Vec3LinearChannel") write_indented("name \"scale\"") write_indented("target \"%s\"" % (bone_name)) open_class("Keyframes %d" % (len(channel))) for timestamp in sorted(channel.keys()): #write_indented("key %f %f %f %f" % (timestamp/current_scene.render.fps, channel[timestamp][1], channel[timestamp][0], channel[timestamp][2])) write_indented( "key %f %f %f %f" % (timestamp / current_scene.render.fps, channel[timestamp][0], channel[timestamp][2], channel[timestamp][1])) close_class() close_class() elif bone_property.lower() == 'location': open_class("Vec3LinearChannel") write_indented("name \"translate\"") write_indented("target \"%s\"" % (bone_name)) open_class("Keyframes %d" % (len(channel))) for timestamp in sorted(channel.keys()): # note the axis translation #write_indented("key %f %f %f %f" % (timestamp/current_scene.render.fps, channel[timestamp][1], -channel[timestamp][0], channel[timestamp][2])) #write_indented("key %f %f %f %f" % (timestamp/current_scene.render.fps, channel[timestamp][1], channel[timestamp][0], channel[timestamp][2])) #write_indented("key %f %f %f %f" % (timestamp/current_scene.render.fps, channel[timestamp][0], channel[timestamp][1], channel[timestamp][2])) write_indented( "key %f %f %f %f" % (timestamp / current_scene.render.fps, channel[timestamp][0], channel[timestamp][2], -channel[timestamp][1])) close_class() close_class() elif bone_property.lower() == 'rotation_euler': # first - tweak the axis # then convert to quaternion pose_bone = get_pose_bone_by_name(bone_name) if pose_bone != None and 'Z' in pose_bone.rotation_mode: open_class("QuatSphericalLinearChannel") write_indented("name \"quaternion\"") write_indented("target \"%s\"" % (bone_name)) open_class("Keyframes %d" % (len(channel))) for timestamp in sorted(channel.keys()): euler = mathutils.Euler() euler.order = pose_bone.rotation_mode euler.x = channel[timestamp][0] euler.y = channel[timestamp][1] euler.z = channel[timestamp][2] # reorder euler # t = euler.x # euler.x = euler.y # euler.y = -t quat = euler.to_quaternion() # quat.rotate(mathutils.Matrix.Rotation(math.radians(180), 4, 'Z')) # quat. write_indented( "key %f %f %f %f %f" % (timestamp / current_scene.render.fps, quat.x, quat.y, quat.z, quat.w)) close_class() close_class() elif bone_property.lower() == 'rotation_quaternion': pose_bone = get_pose_bone_by_name(bone_name) if pose_bone != None and pose_bone.rotation_mode == 'QUATERNION': open_class("QuatSphericalLinearChannel") write_indented("name \"quaternion\"") write_indented("target \"%s\"" % (bone_name)) open_class("Keyframes %d" % (len(channel))) for timestamp in sorted(channel.keys()): quat = mathutils.Quaternion() quat.w = channel[timestamp][0] quat.x = channel[timestamp][1] quat.y = channel[timestamp][2] quat.z = channel[timestamp][3] # convert to euler fix the axis and then back to quat #euler = quat.to_euler('XYZ') #t = euler.x #euler.x = euler.y #euler.y = -t #quat = euler.to_quaternion() # quat.rotate(mathutils.Matrix.Rotation(math.radians(-90), 4, 'Y')) write_indented( "key %f %f %f %f %f" % (timestamp / current_scene.render.fps, quat.x, quat.y, quat.z, quat.w)) close_class() close_class() # end if bone_name and bone_property # end for path in channels close_class() close_class() close_class()
def configMovement(self, P, A, J, a, b, y, o): mat_a = [0 for i in range(4)] # Joint α matrix mat_b = [0 for i in range(self.J)] # Joint β matrix mat_y = [0 for i in range(self.J)] # Joint γ matrix mat_o = [0 for i in range(self.J)] # Joint δ matrix a[1] = mathutils.Euler((P, A, 0.0), 'XYZ') print("a1 =", a[1]) a[2] = mathutils.Euler((A, -A, 0.0), 'XYZ') print("a2 =", a[2]) b[1] = mathutils.Euler((-A, A, 0.0), 'XYZ') print("b1 =", b[1]) o[1] = mathutils.Euler((A, A, 0.0), 'XYZ') print("o1 =", o[1]) B = A * 2 * sqrt(2) C = B + (B * sqrt(2)) D = C * sqrt(2) E = C + D a[0] = mathutils.Euler((-A - E + (D * 0.5), -A - (D * 0.5), 0.0), 'XYZ') print("a0 =", a[0]) mat_a[0] = Matrix.Translation(a[0]) a[3] = mathutils.Euler((0 - a[0].x, 0 - a[0].y, 0 - a[0].z), 'XYZ') print("a3 =", a[3]) mat_a[3] = Matrix.Translation(a[3]) y[1] = mathutils.Euler((-A, -A, 0.0), 'XYZ') print("y1 =", y[1]) mat_y[1] = Matrix.Translation(y[1]) ### pattern A b[2] = mathutils.Euler((a[0].x + E + (A * 2), a[0].y + (A * 2), 0.0), 'XYZ') print("b2 =", b[2]) mat_b[2] = Matrix.Translation(b[2]) b[3] = mathutils.Euler((a[0].x + E - (D * 0.5), a[0].y - (A * 2), 0.0), 'XYZ') print("b3 =", b[3]) mat_b[3] = Matrix.Translation(b[3]) y[2] = mathutils.Euler((a[0].x + E, a[0].y, 0.0), 'XYZ') print("y2 =", y[2]) mat_y[2] = Matrix.Translation(y[2]) y[3] = mathutils.Euler( (a[0].x + E - (D * 0.5), a[0].y - (D * 0.5), 0.0), 'XYZ') print("y3 =", y[3]) mat_y[3] = Matrix.Translation(y[3]) o[2] = mathutils.Euler((a[0].x + E + (A * 2), a[0].y - (A * 2), 0.0), 'XYZ') print("o2 =", o[2]) mat_o[2] = Matrix.Translation(o[2]) o[3] = mathutils.Euler((a[0].x + E - (D * 0.5) - (A * 2), a[0].y - (D * 0.5) - (A * 2), 0.0), 'XYZ') print("o3 =", o[3]) mat_o[3] = Matrix.Translation(o[3]) ### pattern A end org_rot_mat = Matrix.Rotation(math.radians(0), 4, 'Z') # define the rotation rot_mat = Matrix.Rotation(math.radians(-45), 4, 'Z') for j in range(2, J - 2): mat_y[j + 2] = mat_a[0] @ org_rot_mat @ rot_mat @ mat_a[3] @ mat_y[j] # obj.matrix_world = mat_y[j + 2] # extract components back out of the matrix loc, rot, sca = mat_y[j + 2].decompose() y[j + 2] = mathutils.Euler(loc, 'XYZ') print("y" + str(j + 2) + " = ", y[j + 2], rot, sca) mat_b[j + 2] = mat_a[0] @ org_rot_mat @ rot_mat @ mat_a[3] @ mat_b[j] # obj.matrix_world = mat_b[j + 2] # extract components back out of the matrix loc, rot, sca = mat_b[j + 2].decompose() b[j + 2] = mathutils.Euler(loc, 'XYZ') print("b" + str(j + 2) + " = ", b[j + 2], rot, sca) mat_o[j + 2] = mat_a[0] @ org_rot_mat @ rot_mat @ mat_a[3] @ mat_o[j] # obj.matrix_world = mat_o[j + 2] # extract components back out of the matrix loc, rot, sca = mat_o[j + 2].decompose() o[j + 2] = mathutils.Euler(loc, 'XYZ') print("o" + str(j + 2) + " = ", o[j + 2], rot, sca)
def export_keyframes(self, b_action, space, parent_block, bind_matrix=None, extra_mat_inv=None): if self.properties.animation == 'GEOM_NIF' and self.nif_export.version < 0x0A020000: # keyframe controllers are not present in geometry only files # for more recent versions, the controller and interpolators are # present, only the data is not present (see further on) return # only localspace keyframes need to be exported assert (space == 'localspace') # make sure the parent is of the right type assert (isinstance(parent_block, NifFormat.NiNode)) # add a keyframecontroller block, and refer to this block in the # parent's time controller if self.nif_export.version < 0x0A020000: kfc = self.nif_export.objecthelper.create_block( "NiKeyframeController", b_action) else: kfc = self.nif_export.objecthelper.create_block( "NiTransformController", b_action) kfi = self.nif_export.objecthelper.create_block( "NiTransformInterpolator", b_action) # link interpolator from the controller kfc.interpolator = kfi # set interpolator default data scale, quat, trans = \ parent_block.get_transform().get_scale_quat_translation() kfi.translation.x = trans.x kfi.translation.y = trans.y kfi.translation.z = trans.z kfi.rotation.x = quat.x kfi.rotation.y = quat.y kfi.rotation.z = quat.z kfi.rotation.w = quat.w kfi.scale = scale parent_block.add_controller(kfc) # determine cycle mode for this controller # this is stored in the blender action fcurves # while we're at it, we also determine the # start and stop frames extend = None if b_action: start_frame = +1000000 stop_frame = -1000000 for curve in b_action: # get cycle mode if extend is None: extend = curve.extend elif extend != curve.extend: self.nif_export.warning( "Inconsistent extend type in %s, will use %s." % (b_action, extend)) # get start and stop frames start_frame = min( start_frame, min(btriple.pt[0] for btriple in curve.bezierPoints)) stop_frame = max( stop_frame, max(btriple.pt[0] for btriple in curve.bezierPoints)) else: # dummy b_action # default extend, start, and end extend = bpy.types.FCurve.ExtendTypes.CYCLIC start_frame = self.context.scene.frame_start stop_frame = self.context.scene.frame_end # fill in the non-trivial values kfc.flags = 8 # active kfc.flags |= self.get_flags_from_extend(extend) kfc.frequency = 1.0 kfc.phase = 0.0 kfc.start_time = (start_frame - 1) * self.context.scene.render.fps kfc.stop_time = (stop_frame - 1) * self.context.scene.render.fps if self.properties.animation == 'GEOM_NIF': # keyframe data is not present in geometry files return # -> get keyframe information # some calculations if bind_matrix: bind_scale, bind_rot, bind_trans = nif_utils.decompose_srt( bind_matrix) bind_quat = bind_rot.toQuat() else: bind_scale = 1.0 bind_rot = mathutils.Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) bind_quat = mathutils.Quaternion(1, 0, 0, 0) bind_trans = mathutils.Vector() if extra_mat_inv: extra_scale_inv, extra_rot_inv, extra_trans_inv = \ nif_utils.decompose_srt(extra_mat_inv) extra_quat_inv = extra_rot_inv.toQuat() else: extra_scale_inv = 1.0 extra_rot_inv = mathutils.Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) extra_quat_inv = mathutils.Quaternion(1, 0, 0, 0) extra_trans_inv = mathutils.Vector() # sometimes we need to export an empty keyframe... this will take care of that if b_action is None: scale_curve = {} rot_curve = {} trans_curve = {} # the usual case comes now... else: # merge the animation curves into a rotation vector and translation vector curve scale_curve = {} rot_curve = {} trans_curve = {} # the following code makes these assumptions # Note: PO = Pose, OB = Object, MA = Material *********************************************************************************** assert (Ipo.PO_SCALEX == Ipo.OB_SCALEX) assert (Ipo.PO_LOCX == Ipo.OB_LOCX) # check validity of curves for curvecollection in ((Ipo.PO_SCALEX, Ipo.PO_SCALEY, Ipo.PO_SCALEZ), (Ipo.PO_LOCX, Ipo.PO_LOCY, Ipo.PO_LOCZ), (Ipo.PO_QUATX, Ipo.PO_QUATY, Ipo.PO_QUATZ, Ipo.PO_QUATW), (Ipo.OB_ROTX, Ipo.OB_ROTY, Ipo.OB_ROTZ)): # skip invalid curves try: b_action[curvecollection[0]] except KeyError: continue # check that if any curve is defined in the collection # then all curves are defined in the collection if (any(b_action[curve] for curve in curvecollection) and not all(b_action[curve] for curve in curvecollection)): keytype = { Ipo.PO_SCALEX: "SCALE", Ipo.PO_LOCX: "LOC", Ipo.PO_QUATX: "ROT", Ipo.OB_ROTX: "ROT" } raise nif_utils.NifError( "missing curves in %s; insert %s key at frame 1 and try again" % (b_action, keytype[curvecollection[0]])) # go over all curves b_action_curves = list(b_action.curveConsts.values()) for curve in b_action_curves: # skip empty curves if b_action[curve] is None: continue # non-empty curve: go over all frames of the curve for btriple in b_action[curve].bezierPoints: frame = btriple.pt[0] if (frame < self.context.scene.frame_start) or ( frame > self.context.scene.frame_end): continue # PO_SCALEX == OB_SCALEX, so this does both pose and object # scale if curve in (Ipo.PO_SCALEX, Ipo.PO_SCALEY, Ipo.PO_SCALEZ): # support only uniform scaling... take the mean scale_curve[frame] = ( b_action[Ipo.PO_SCALEX][frame] + b_action[Ipo.PO_SCALEY][frame] + b_action[Ipo.PO_SCALEZ][frame]) / 3.0 # SC' * SB' / SX scale_curve[frame] = \ scale_curve[frame] * bind_scale * extra_scale_inv # object rotation elif curve in (Ipo.OB_ROTX, Ipo.OB_ROTY, Ipo.OB_ROTZ): rot_curve[frame] = mathutils.Euler([ 10 * ipo[Ipo.OB_ROTX][frame], 10 * ipo[Ipo.OB_ROTY][frame], 10 * ipo[Ipo.OB_ROTZ][frame] ]) # use quat if we have bind matrix and/or extra matrix # XXX maybe we should just stick with eulers?? if bind_matrix or extra_mat_inv: rot_curve[frame] = rot_curve[frame].toQuat() # beware, mathutils.Quaternion.cross takes arguments in a counter-intuitive order: # q1.to_matrix() * q2.to_matrix() == mathutils.Quaternion.cross(q2, q1).to_matrix() rot_curve[frame] = mathutils.Quaternion.cross( mathutils.Quaternion.cross( bind_quat, rot_curve[frame]), extra_quat_inv) # inverse(RX) * RC' * RB' # pose rotation elif curve in (Ipo.PO_QUATX, Ipo.PO_QUATY, Ipo.PO_QUATZ, Ipo.PO_QUATW): rot_curve[frame] = mathutils.Quaternion() rot_curve[frame].x = b_action[Ipo.PO_QUATX][frame] rot_curve[frame].y = b_action[Ipo.PO_QUATY][frame] rot_curve[frame].z = b_action[Ipo.PO_QUATZ][frame] rot_curve[frame].w = b_action[Ipo.PO_QUATW][frame] # beware, mathutils.Quaternion.cross takes arguments in a counter-intuitive order: # q1.to_matrix() * q2.to_matrix() == mathutils.Quaternion.cross(q2, q1).to_matrix() rot_curve[frame] = mathutils.Quaternion.cross( mathutils.Quaternion.cross(bind_quat, rot_curve[frame]), extra_quat_inv) # inverse(RX) * RC' * RB' # PO_LOCX == OB_LOCX, so this does both pose and object # location elif curve in (Ipo.PO_LOCX, Ipo.PO_LOCY, Ipo.PO_LOCZ): trans_curve[frame] = mathutils.Vector([ b_action[Ipo.PO_LOCX][frame], b_action[Ipo.PO_LOCY][frame], b_action[Ipo.PO_LOCZ][frame] ]) # T = - TX * inverse(RX) * RC' * RB' * SC' * SB' / SX + TC' * SB' * RB' + TB' trans_curve[frame] *= bind_scale trans_curve[frame] *= bind_rot trans_curve[frame] += bind_trans # we need RC' and SC' if Ipo.OB_ROTX in b_action_curves and b_action[ Ipo.OB_ROTX]: rot_c = mathutils.Euler([ 10 * b_action[Ipo.OB_ROTX][frame], 10 * b_action[Ipo.OB_ROTY][frame], 10 * b_action[Ipo.OB_ROTZ][frame] ]).to_matrix() elif Ipo.PO_QUATX in b_action_curves and b_action[ Ipo.PO_QUATX]: rot_c = mathutils.Quaternion() rot_c.x = b_action[Ipo.PO_QUATX][frame] rot_c.y = b_action[Ipo.PO_QUATY][frame] rot_c.z = b_action[Ipo.PO_QUATZ][frame] rot_c.w = b_action[Ipo.PO_QUATW][frame] rot_c = rot_c.to_matrix() else: rot_c = mathutils.Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) # note, PO_SCALEX == OB_SCALEX, so this does both if b_action[Ipo.PO_SCALEX]: # support only uniform scaling... take the mean scale_c = (b_action[Ipo.PO_SCALEX][frame] + b_action[Ipo.PO_SCALEY][frame] + b_action[Ipo.PO_SCALEZ][frame]) / 3.0 else: scale_c = 1.0 trans_curve[frame] += \ extra_trans_inv * rot_c * bind_rot * \ scale_c * bind_scale # -> now comes the real export if (max(len(rot_curve), len(trans_curve), len(scale_curve)) <= 1 and self.nif_export.version >= 0x0A020000): # only add data if number of keys is > 1 # (see importer comments with import_kf_root: a single frame # keyframe denotes an interpolator without further data) # insufficient keys, so set the data and we're done! if trans_curve: trans = list(trans_curve.values())[0] kfi.translation.x = trans[0] kfi.translation.y = trans[1] kfi.translation.z = trans[2] if rot_curve: rot = list(rot_curve.values())[0] # XXX blender weirdness... Euler() is a function!! if isinstance(rot, mathutils.Euler().__class__): rot = rot.toQuat() kfi.rotation.x = rot.x kfi.rotation.y = rot.y kfi.rotation.z = rot.z kfi.rotation.w = rot.w # ignore scale for now... kfi.scale = 1.0 # done! return # add the keyframe data if self.nif_export.version < 0x0A020000: kfd = self.nif_export.objecthelper.create_block( "NiKeyframeData", b_action) kfc.data = kfd else: # number of frames is > 1, so add transform data kfd = self.nif_export.objecthelper.create_block( "NiTransformData", b_action) kfi.data = kfd frames = list(rot_curve.keys()) frames.sort() # XXX blender weirdness... Euler() is a function!! if (frames and isinstance( list(rot_curve.values())[0], mathutils.Euler().__class__)): # eulers kfd.rotation_type = NifFormat.KeyType.XYZ_ROTATION kfd.num_rotation_keys = 1 # *NOT* len(frames) this crashes the engine! kfd.xyz_rotations[0].num_keys = len(frames) kfd.xyz_rotations[1].num_keys = len(frames) kfd.xyz_rotations[2].num_keys = len(frames) # TODO: quadratic interpolation? kfd.xyz_rotations[0].interpolation = NifFormat.KeyType.LINEAR kfd.xyz_rotations[1].interpolation = NifFormat.KeyType.LINEAR kfd.xyz_rotations[2].interpolation = NifFormat.KeyType.LINEAR kfd.xyz_rotations[0].keys.update_size() kfd.xyz_rotations[1].keys.update_size() kfd.xyz_rotations[2].keys.update_size() for i, frame in enumerate(frames): # TODO: speed up by not recalculating stuff rot_frame_x = kfd.xyz_rotations[0].keys[i] rot_frame_y = kfd.xyz_rotations[1].keys[i] rot_frame_z = kfd.xyz_rotations[2].keys[i] rot_frame_x.time = (frame - 1) * self.context.scene.render.fps rot_frame_y.time = (frame - 1) * self.context.scene.render.fps rot_frame_z.time = (frame - 1) * self.context.scene.render.fps rot_frame_x.value = rot_curve[ frame].x * 3.14159265358979323846 / 180.0 rot_frame_y.value = rot_curve[ frame].y * 3.14159265358979323846 / 180.0 rot_frame_z.value = rot_curve[ frame].z * 3.14159265358979323846 / 180.0 else: # quaternions # TODO: quadratic interpolation? kfd.rotation_type = NifFormat.KeyType.LINEAR kfd.num_rotation_keys = len(frames) kfd.quaternion_keys.update_size() for i, frame in enumerate(frames): rot_frame = kfd.quaternion_keys[i] rot_frame.time = (frame - 1) * self.context.scene.render.fps rot_frame.value.w = rot_curve[frame].w rot_frame.value.x = rot_curve[frame].x rot_frame.value.y = rot_curve[frame].y rot_frame.value.z = rot_curve[frame].z frames = list(trans_curve.keys()) frames.sort() kfd.translations.interpolation = NifFormat.KeyType.LINEAR kfd.translations.num_keys = len(frames) kfd.translations.keys.update_size() for i, frame in enumerate(frames): trans_frame = kfd.translations.keys[i] trans_frame.time = (frame - 1) * self.context.scene.render.fps trans_frame.value.x = trans_curve[frame][0] trans_frame.value.y = trans_curve[frame][1] trans_frame.value.z = trans_curve[frame][2] frames = list(scale_curve.keys()) frames.sort() kfd.scales.interpolation = NifFormat.KeyType.LINEAR kfd.scales.num_keys = len(frames) kfd.scales.keys.update_size() for i, frame in enumerate(frames): scale_frame = kfd.scales.keys[i] scale_frame.time = (frame - 1) * self.context.scene.render.fps scale_frame.value = scale_curve[frame]