def load_ani(filename, context): name, ext = os.path.splitext(os.path.basename(filename)) file = open(filename, 'rb') ani = MabinogiAnimation() try: magic, version, ani.frameCount, _, ani.baseTime, _, ani.boneCount = struct.unpack( "<4sihhhii", file.read(22)) except: print("Error parsing file header!") file.close() return if magic != b'pa!\x00': print("Not a supported file type!") file.close() return file.seek(9 * 4, os.SEEK_CUR) for b in range(ani.boneCount): ani.bone += [ MabinogiAniData(), ] _, ani.bone[b].mDataCount, _, ani.bone[b].mTime, ani.bone[ b].mSize = struct.unpack("<ihhii", file.read(16)) file.seek(2 * 4, os.SEEK_CUR) ani.bone[b].frames = list() print("bone %d" % b) for f in range(ani.bone[b].mDataCount): ani.bone[b].frames += [ MabinogiFrame(), ] ani.bone[b].frames[f].mTime = struct.unpack("<i", file.read(4))[0] ani.bone[b].frames[f].move = struct.unpack("<4f", file.read(16)) ani.bone[b].frames[f].roto = list( struct.unpack("<4f", file.read(16))) ani.bone[b].frames[f].roto = [-ani.bone[b].frames[f].roto[3] ] + ani.bone[b].frames[f].roto[:3] if f == 0: print(ani.bone[b].frames[f].mTime) print("%.2f %.2f %.2f %.2f" % ani.bone[b].frames[f].move) print("%.2f %.2f %.2f %.2f" % tuple(ani.bone[b].frames[f].roto)) #find if the selected object is a an armature armature = None sel_ob = None if len(context.selected_objects) > 0: sel_ob = context.selected_objects[0] if type(sel_ob.data) == bpy.types.Armature: armature = sel_ob.data else: print("No armature selected") return if len(armature.bones) != ani.boneCount: print("Bone count doesn't match") sel_ob.animation_data_create() action = bpy.data.actions.new(name=name) sel_ob.animation_data.action = action pb = sel_ob.pose.bones eb = sel_ob.data.bones pose_bones = dict() edit_bones = dict() for i in range(len(pb)): bone_id = int(pb[i].name[:pb[i].name.index('__')]) pose_bones[bone_id] = pb[i] edit_bones[bone_id] = eb[i] bone_space = mathutils.Matrix( ((0, 1, 0, 0), (0, 0, 1, 0), (1, 0, 0, 0), (0, 0, 0, 1))) for b in range(ani.boneCount): for f in range(ani.bone[b].mDataCount): context.scene.frame_set(ani.bone[b].frames[f].mTime / 50) pos = ani.bone[b].frames[f].move[:3] quat = mathutils.Quaternion(ani.bone[b].frames[f].roto) mat = mathutils.Matrix.Translation(pos) * quat.to_matrix().to_4x4() link = edit_bones[b].matrix_local * bone_space.inverted() if edit_bones[b].parent is not None: parent_link = edit_bones[ b].parent.matrix_local * bone_space.inverted() link = parent_link.inverted() * link mat *= bone_space link *= bone_space pose_bones[b].matrix_basis = link.inverted() * mat pose_bones[b].keyframe_insert("rotation_quaternion") pose_bones[b].keyframe_insert("location")
def animate_convert_rotation_axis_angle(axis_angle): q = mathutils.Quaternion((axis_angle[1], axis_angle[2], axis_angle[3]), axis_angle[0]) return [q.x, q.y, q.z, q.w]
def ModalMove(self, context): scene = context.scene pFS = scene.FSimProps pFSM = scene.FSimMainProps startFrame = pFSM.fsim_start_frame endFrame = pFSM.fsim_end_frame nFrame = scene.frame_current print("nFrame: ", nFrame) #Get the effort and direction change to head toward the target RqdEffort, RqdDirection, RqdDirectionV = self.Target( self.sTargetRig, self.sTargetProxy, pFS) if nFrame == startFrame: self.sOldRqdEffort = RqdEffort context.scene.frame_set(nFrame + 1) self.sOld_back_fin = self.sBack_fin_middle.matrix.decompose()[0] return 1 TargetEffort = pFS.pEffortGain * (pFS.pEffortIntegral * RqdEffort + (RqdEffort - self.sOldRqdEffort)) self.sOldRqdEffort = RqdEffort pFS.sEffort = pFS.pEffortGain * RqdEffort * pFS.pEffortRamp + pFS.sEffort * ( 1.0 - pFS.pEffortRamp) pFS.sEffort = min(pFS.sEffort, 1.0) #print("Required, Effort:", RqdEffort, pFS.sEffort) #Pec fin simulation self.PecSimulation(nFrame, pFS, startFrame) #Convert effort into tail frequency and amplitude (Fades to a low value if in hover mode) pFS.pFreq = self.rMaxFreq * ( (1 - self.sHoverMode) * (1.0 / (pFS.sEffort + 0.01)) + self.sHoverMode * 2.0) pFS.pTailAngle = self.rMaxTailAngle * ( (1 - self.sHoverMode) * pFS.sEffort + self.sHoverMode * pFS.pHoverTailFrc) #print("rMax, Frc: %.2f, %.2f" % (self.rMaxTailAngle, pFS.pHoverTailFrc)) #Convert direction into Tail Offset angle (Work out swim turn angle and Hover turn angle and mix) xSwimTailAngleOffset = RqdDirection * pFS.pMaxSteeringAngle xHoverTailAngleOffset = pFS.pMaxSteeringAngle * self.sHoverTurn / 30.0 xHoverTailAngleOffset = 0.0 # xHoverFactor = max(0,(1.0 - self.sHoverMode * 4.0)) pFS.sTailAngleOffset = pFS.sTailAngleOffset * ( 1 - pFS.pEffortRamp ) + pFS.pEffortRamp * max( 0, (1.0 - self.sHoverMode * 2.0) ) * xSwimTailAngleOffset + pFS.pEffortRamp * self.sHoverMode * xHoverTailAngleOffset # pFS.sTailAngleOffset = pFS.sTailAngleOffset * (1 - pFS.pEffortRamp) + pFS.pEffortRamp * xSwimTailAngleOffset # print("xHoverOffset, TailOffset: ", xHoverTailAngleOffset, pFS.sTailAngleOffset) # print("HoverMode, xSwimTailAngleOffset: ", self.sHoverMode, xSwimTailAngleOffset) #Hover 'Twitch' calculations (Make the fish do some random twisting during hover mode) if self.sHoverMode < 0.5: #Not hovering so reset self.sTwitchTarget = 0.0 self.sTwitchFrame = 0.0 else: #Hovering, so check if the twitch frame has been reached if nFrame >= self.sTwitchFrame: #set new twitch frame self.sTwitchFrame = nFrame + pFS.pHoverTwitchTime * (random() - 0.5) #Only twitch while not resting if self.sTwitchFrame < self.sRestartFrame and self.sTwitchFrame > self.sRestFrame: self.sTwitchFrame = self.sRestartFrame + 5 #set a new twitch target angle self.sTwitchTarget = pFS.pHoverTwitch * 2.0 * (random() - 0.5) self.sTwitchAngle = self.sTwitchAngle * 0.9 + 0.1 * self.sTwitchTarget #print("Twitch Angle: ", self.sTwitchAngle) #Spine Movement self.sState = self.sState + 360.0 / pFS.pFreq xTailAngle = math.sin(math.radians(self.sState)) * math.radians( pFS.pTailAngle) + math.radians( pFS.sTailAngleOffset) + math.radians(self.sTwitchAngle) #print("Components: %.2f, %.2f, %.2f" % (math.sin(math.radians(self.sState))*math.radians(pFS.pTailAngle),math.radians(pFS.sTailAngleOffset),math.radians(self.sTwitchAngle))) #print("TailAngle", math.degrees(xTailAngle)) self.sSpine_master.rotation_quaternion = mathutils.Quaternion( (0.0, 0.0, 1.0), xTailAngle) self.sSpine_master.keyframe_insert(data_path='rotation_quaternion', frame=(nFrame)) ChestRot = mathutils.Quaternion( (0.0, 0.0, 1.0), -xTailAngle * pFS.pChestRatio) # - math.radians(pFS.sTailAngleOffset)) self.sChest.rotation_quaternion = ChestRot * mathutils.Quaternion( (1.0, 0.0, 0.0), -math.fabs(math.radians(pFS.sTailAngleOffset)) * pFS.pChestRaise * (1.0 - self.sHoverMode)) #print("Torso:", pFS.sTailAngleOffset) self.sTorso.rotation_quaternion = mathutils.Quaternion( (0.0, 1.0, 0.0), -math.radians(pFS.sTailAngleOffset) * pFS.pLeanIntoTurn * (1.0 - self.sHoverMode)) self.sChest.keyframe_insert(data_path='rotation_quaternion', frame=(nFrame)) self.sTorso.keyframe_insert(data_path='rotation_quaternion', frame=(nFrame)) #context.scene.update() # #Tail Movment if (nFrame == startFrame): back_fin_dif = 0 else: back_fin_dif = (self.sBack_fin_middle.matrix.decompose()[0].x - self.sOld_back_fin.x) self.sOld_back_fin = self.sBack_fin_middle.matrix.decompose()[0] #Tailfin bending based on phase offset pMaxTailScale = pFS.pMaxTailFinAngle * ( 1.0 / pFS.pTailFinStiffness) * 0.2 / 30.0 self.sBack_fin1_scale = 1.0 + math.sin( math.radians(self.sState + pFS.pTailFinPhase)) * pMaxTailScale * ( pFS.pTailAngle / self.rMaxTailAngle) # print("Bend Factor: ", (pFS.pTailAngle / self.rMaxTailAngle)) self.sBack_fin1.scale[1] = self.sBack_fin1_scale self.sBack_fin2.scale[1] = 1 - ( 1 - self.sBack_fin1_scale) * pFS.pTailFinStubRatio self.sBack_fin1.keyframe_insert(data_path='scale', frame=(nFrame)) self.sBack_fin2.keyframe_insert(data_path='scale', frame=(nFrame)) SideFinRot = math.sin( math.radians(self.sState + pFS.pSideFinPhase)) * pFS.pMaxSideFinAngle self.sSideFinL.rotation_quaternion = mathutils.Quaternion( (1, 0, 0), math.radians(-SideFinRot)) self.sSideFinR.rotation_quaternion = mathutils.Quaternion( (1, 0, 0), math.radians(SideFinRot)) self.sSideFinL.keyframe_insert(data_path='rotation_quaternion', frame=(nFrame)) self.sSideFinR.keyframe_insert(data_path='rotation_quaternion', frame=(nFrame)) #Do Object movment with Forward force and Angular force TailFinAngle = (self.sBack_fin1_scale - 1.0) * 30.0 / 0.4 TailFinAngleForce = math.sin(math.radians(TailFinAngle)) # ForwardForce = -back_fin_dif * TailFinAngleForce * pFS.pPower ForwardForce = math.fabs(math.cos(math.radians( self.sState))) * math.radians( pFS.pTailAngle) * 15.0 * pFS.pPower / pFS.pMaxFreq # print("Force", ForwardForce, math.fabs(math.cos(math.radians(self.sState))), math.radians(pFS.pTailAngle)) #Angular force due to 'swish' AngularForce = back_fin_dif / pFS.pAngularDrag #Angular force due to rudder effect AngularForce += xTailAngle * pFS.sVelocity[1] / pFS.pAngularDrag #Fake Angular force to make turning more effective AngularForce += -(pFS.sTailAngleOffset / pFS.pMaxSteeringAngle) * pFS.pTurnAssist #Angular force for vertical movement self.sAngularForceV = self.sAngularForceV * ( 1 - pFS.pEffortRamp) + RqdDirectionV * pFS.pMaxVerticalAngle #print("TailFinAngle, AngularForce", xTailAngle, AngularForce) if self.sHoverMode < 0.1: self.ObjectMovment(self.sTargetRig, ForwardForce, AngularForce, self.sAngularForceV, nFrame, self.sTargetProxy, pFS) else: self.ObjectMovmentHover(self.sTargetRig, nFrame, self.sTargetProxy, pFS) #Go to next frame, or finish wm = context.window_manager # print("Frame: ", nFrame) if nFrame == endFrame: return 0 else: wm.progress_update((len(self.sArmatures) - self.nArmature) * 99.0 / len(self.sArmatures)) context.scene.frame_set(nFrame + 1) return 1
def lp_left(bm, lp, wid, vm, wm): # pass size = len(lp) up = wm.inverted() * vm.inverted() * Vector((0, 0, 1)) lp_off = [] faces = [] for i in range(size - 1): if i == 0: pt = bm.verts[lp[i]].co pre = bm.verts[lp[-2]].co nxt = bm.verts[lp[1]].co pre_ind = lp[size - 2] nxt_ind = lp[1] else: bm.verts.ensure_lookup_table() pt = bm.verts[lp[i]].co pre = bm.verts[lp[i - 1]].co nxt = bm.verts[lp[i + 1]].co pre_ind = lp[i - 1] nxt_ind = lp[i + 1] vec1 = pt - pre vec2 = pt - nxt mid = vec1.normalized() + vec2.normalized() if mid.length < 10e-4: up2 = Vector((0, 0, 1)) mid = up2.cross(vec1) else: xx = mid.cross(vec1).dot(up) if xx > 0: mid.negate() mid.normalize() if pre_ind == nxt_ind: mid = (pt - pre).normalized() q_a = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(90.0)) q_b = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(-180.0)) mid.rotate(q_a) pt1 = pt + mid * wid mid.rotate(q_b) pt2 = pt + mid * wid new_vert_1 = bm.verts.new(pt1) new_vert_2 = bm.verts.new(pt2) lp_off.append([new_vert_1, new_vert_2]) else: ang = mid.angle(pre - pt) vec_len = wid / (math.sin(ang)) # print(wid) pt = pt + mid * vec_len new_vert = bm.verts.new(pt) lp_off.append(new_vert) lp_off.append(lp_off[0]) bm.verts.index_update() for i in range(len(lp_off) - 1): bm.verts.ensure_lookup_table() p1 = bm.verts[lp[i]] p2 = bm.verts[lp[i + 1]] p3 = lp_off[i + 1] p4 = lp_off[i] if isinstance(p3, list): faces.append((p1, p2, p3[0], p4)) # faces.append((p3[0],p2,p3[1])) elif isinstance(p4, list): faces.append((p1, p2, p3, p4[1])) else: faces.append((p1, p2, p3, p4)) return faces
import mathutils import math # a new rotation 90 degrees about the Y axis quat_a = mathutils.Quaternion((0.7071068, 0.0, 0.7071068, 0.0)) # passing values to Quaternion's directly can be confusing so axis, angle # is supported for initializing too quat_b = mathutils.Quaternion((0.0, 1.0, 0.0), math.radians(90.0)) print("Check quaternions match", quat_a == quat_b) # like matrices, quaternions can be multiplied to accumulate rotational values quat_a = mathutils.Quaternion((0.0, 1.0, 0.0), math.radians(90.0)) quat_b = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(45.0)) quat_out = quat_a * quat_b # print the quat, euler degrees for mear mortals and (axis, angle) print("Final Rotation:") print(quat_out) print("%.2f, %.2f, %.2f" % tuple(math.degrees(a) for a in quat_out.to_euler())) print("(%.2f, %.2f, %.2f), %.2f" % (quat_out.axis[:] + (math.degrees(quat_out.angle), ))) # multiple rotations can be interpolated using the exponential map quat_c = mathutils.Quaternion((1.0, 0.0, 0.0), math.radians(15.0)) exp_avg = (quat_a.to_exponential_map() + quat_b.to_exponential_map() + quat_c.to_exponential_map()) / 3.0 quat_avg = mathutils.Quaternion(exp_avg) print("Average rotation:") print(quat_avg)
def import_armature(self, niArmature): """Scans an armature hierarchy, and returns a whole armature. This is done outside the normal node tree scan to allow for positioning of the bones before skins are attached.""" armature_name = self.import_name(niArmature) b_armatureData = Blender.Armature.Armature() b_armatureData.name = armature_name b_armatureData.drawAxes = True b_armatureData.envelopes = False b_armatureData.vertexGroups = True b_armatureData.draw_type = 'STICK' b_armature = self.context.scene.objects.new(b_armatureData, armature_name) # make armature editable and create bones b_armatureData.makeEditable() niChildBones = [ child for child in niArmature.children if self.is_bone(child) ] for niBone in niChildBones: self.import_bone(niBone, b_armature, b_armatureData, niArmature) b_armatureData.update() # TODO: Move to Animation.py # The armature has been created in editmode, # now we are ready to set the bone keyframes. if self.nif_common.properties.animation: # create an action action = Blender.Armature.NLA.NewAction() action.setActive(b_armature) # go through all armature pose bones # see http://www.elysiun.com/forum/viewtopic.php?t=58693 self.nif_common.info('Importing Animations') for bone_name, b_posebone in b_armature.getPose().bones.items(): # denote progress self.nif_common.debug('Importing animation for bone %s' % bone_name) niBone = self.nif_common.blocks[bone_name] # get bind matrix (NIF format stores full transformations in keyframes, # but Blender wants relative transformations, hence we need to know # the bind position for conversion). Since # [ SRchannel 0 ] [ SRbind 0 ] [ SRchannel * SRbind 0 ] [ SRtotal 0 ] # [ Tchannel 1 ] * [ Tbind 1 ] = [ Tchannel * SRbind + Tbind 1 ] = [ Ttotal 1 ] # with # 'total' the transformations as stored in the NIF keyframes, # 'bind' the Blender bind pose, and # 'channel' the Blender IPO channel, # it follows that # Schannel = Stotal / Sbind # Rchannel = Rtotal * inverse(Rbind) # Tchannel = (Ttotal - Tbind) * inverse(Rbind) / Sbind bone_bm = self.nif_common.import_matrix(niBone) # base pose niBone_bind_scale, niBone_bind_rot, niBone_bind_trans = self.decompose_srt( bone_bm) niBone_bind_rot_inv = mathutils.Matrix(niBone_bind_rot) niBone_bind_rot_inv.invert() niBone_bind_quat_inv = niBone_bind_rot_inv.toQuat() # we also need the conversion of the original matrix to the # new bone matrix, say X, # B' = X * B # (with B' the Blender matrix and B the NIF matrix) because we # need that # C' * B' = X * C * B # and therefore # C' = X * C * B * inverse(B') = X * C * inverse(X), # where X = B' * inverse(B) # # In detail: # [ SRX 0 ] [ SRC 0 ] [ SRX 0 ] # [ TX 1 ] * [ TC 1 ] * inverse( [ TX 1 ] ) = # [ SRX * SRC 0 ] [ inverse(SRX) 0 ] # [ TX * SRC + TC 1 ] * [ -TX * inverse(SRX) 1 ] = # [ SRX * SRC * inverse(SRX) 0 ] # [ (TX * SRC + TC - TX) * inverse(SRX) 1 ] # Hence # SC' = SX * SC / SX = SC # RC' = RX * RC * inverse(RX) # TC' = (TX * SC * RC + TC - TX) * inverse(RX) / SX extra_matrix_scale, extra_matrix_rot, extra_matrix_trans = self.decompose_srt( self.bones_extra_matrix[niBone]) extra_matrix_quat = extra_matrix_rot.toQuat() extra_matrix_rot_inv = mathutils.Matrix(extra_matrix_rot) extra_matrix_rot_inv.invert() extra_matrix_quat_inv = extra_matrix_rot_inv.toQuat() # now import everything # ############################## # get controller, interpolator, and data # note: the NiKeyframeController check also includes # NiTransformController (see hierarchy!) kfc = self.find_controller(niBone, NifFormat.NiKeyframeController) # old style: data directly on controller kfd = kfc.data if kfc else None # new style: data via interpolator kfi = kfc.interpolator if kfc else None # next is a quick hack to make the new transform # interpolator work as if it is an old style keyframe data # block parented directly on the controller if isinstance(kfi, NifFormat.NiTransformInterpolator): kfd = kfi.data # for now, in this case, ignore interpolator kfi = None # B-spline curve import if isinstance(kfi, NifFormat.NiBSplineInterpolator): times = list(kfi.get_times()) translations = list(kfi.get_translations()) scales = list(kfi.get_scales()) rotations = list(kfi.get_rotations()) # if we have translation keys, we make a dictionary of # rot_keys and scale_keys, this makes the script work MUCH # faster in most cases if translations: scale_keys_dict = {} rot_keys_dict = {} # scales: ignore for now, implement later # should come here scales = None # rotations if rotations: self.nif_common.debug( 'Rotation keys...(bspline quaternions)') for time, quat in zip(times, rotations): frame = 1 + int(time * self.nif_common.fps + 0.5) quat = mathutils.Quaternion( [quat[0], quat[1], quat[2], quat[3]]) # beware, CrossQuats takes arguments in a # counter-intuitive order: # q1.toMatrix() * q2.toMatrix() == CrossQuats(q2, q1).toMatrix() quatVal = CrossQuats( niBone_bind_quat_inv, quat) # Rchannel = Rtotal * inverse(Rbind) rot = CrossQuats( CrossQuats(extra_matrix_quat_inv, quatVal), extra_matrix_quat) # C' = X * C * inverse(X) b_posebone.quat = rot b_posebone.insertKey(b_armature, frame, [Blender.Object.Pose.ROT]) # fill optimizer dictionary if translations: rot_keys_dict[frame] = mathutils.Quaternion( rot) # translations if translations: self.nif_common.debug('Translation keys...(bspline)') for time, translation in zip(times, translations): # time 0.0 is frame 1 frame = 1 + int(time * self.nif_common.fps + 0.5) trans = mathutils.Vector(*translation) locVal = ( trans - niBone_bind_trans ) * niBone_bind_rot_inv * ( 1.0 / niBone_bind_scale ) # Tchannel = (Ttotal - Tbind) * inverse(Rbind) / Sbind # the rotation matrix is needed at this frame (that's # why the other keys are inserted first) if rot_keys_dict: try: rot = rot_keys_dict[frame].toMatrix() except KeyError: # fall back on slow method ipo = action.getChannelIpo(bone_name) quat = mathutils.Quaternion() quat.x = ipo.getCurve('QuatX').evaluate( frame) quat.y = ipo.getCurve('QuatY').evaluate( frame) quat.z = ipo.getCurve('QuatZ').evaluate( frame) quat.w = ipo.getCurve('QuatW').evaluate( frame) rot = quat.toMatrix() else: rot = mathutils.Matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]) # we also need the scale at this frame if scale_keys_dict: try: sizeVal = scale_keys_dict[frame] except KeyError: ipo = action.getChannelIpo(bone_name) if ipo.getCurve('SizeX'): sizeVal = ipo.getCurve( 'SizeX').evaluate( frame) # assume uniform scale else: sizeVal = 1.0 else: sizeVal = 1.0 size = mathutils.Matrix([[sizeVal, 0.0, 0.0], [0.0, sizeVal, 0.0], [0.0, 0.0, sizeVal]]) # now we can do the final calculation loc = ( extra_matrix_trans * size * rot + locVal - extra_matrix_trans) * extra_matrix_rot_inv * ( 1.0 / extra_matrix_scale ) # C' = X * C * inverse(X) b_posebone.loc = loc b_posebone.insertKey(b_armature, frame, [Blender.Object.Pose.LOC]) # delete temporary dictionaries if translations: del scale_keys_dict del rot_keys_dict # NiKeyframeData and NiTransformData import elif isinstance(kfd, NifFormat.NiKeyframeData): translations = kfd.translations scales = kfd.scales # if we have translation keys, we make a dictionary of # rot_keys and scale_keys, this makes the script work MUCH # faster in most cases if translations: scale_keys_dict = {} rot_keys_dict = {} # add the keys # Scaling if scales.keys: self.nif_common.debug('Scale keys...') for scaleKey in scales.keys: # time 0.0 is frame 1 frame = 1 + int(scaleKey.time * self.nif_common.fps + 0.5) sizeVal = scaleKey.value size = sizeVal / niBone_bind_scale # Schannel = Stotal / Sbind b_posebone.size = mathutils.Vector(size, size, size) b_posebone.insertKey(b_armature, frame, [Blender.Object.Pose.SIZE ]) # this is very slow... :( # fill optimizer dictionary if translations: scale_keys_dict[frame] = size # detect the type of rotation keys rotation_type = kfd.rotation_type # Euler Rotations if rotation_type == 4: # uses xyz rotation if kfd.xyz_rotations[0].keys: self.nif_common.debug('Rotation keys...(euler)') for xkey, ykey, zkey in zip(kfd.xyz_rotations[0].keys, kfd.xyz_rotations[1].keys, kfd.xyz_rotations[2].keys): # time 0.0 is frame 1 # XXX it is assumed that all the keys have the # XXX same times!!! if (abs(xkey.time - ykey.time) > self.nif_common.properties.epsilon or abs(xkey.time - zkey.time) > self.nif_common.properties.epsilon): self.nif_common.warning( "xyz key times do not correspond, " "animation may not be correctly imported") frame = 1 + int(xkey.time * self.nif_common.fps + 0.5) euler = mathutils.Euler([ xkey.value * 180.0 / math.pi, ykey.value * 180.0 / math.pi, zkey.value * 180.0 / math.pi ]) quat = euler.toQuat() # beware, CrossQuats takes arguments in a counter-intuitive order: # q1.toMatrix() * q2.toMatrix() == CrossQuats(q2, q1).toMatrix() quatVal = CrossQuats( niBone_bind_quat_inv, quat) # Rchannel = Rtotal * inverse(Rbind) rot = CrossQuats( CrossQuats(extra_matrix_quat_inv, quatVal), extra_matrix_quat) # C' = X * C * inverse(X) b_posebone.quat = rot b_posebone.insertKey(b_armature, frame, [Blender.Object.Pose.ROT ]) # this is very slow... :( # fill optimizer dictionary if translations: rot_keys_dict[frame] = mathutils.Quaternion( rot) # Quaternion Rotations else: # TODO take rotation type into account for interpolation if kfd.quaternion_keys: self.nif_common.debug( 'Rotation keys...(quaternions)') quaternion_keys = kfd.quaternion_keys for key in quaternion_keys: frame = 1 + int(key.time * self.nif_common.fps + 0.5) keyVal = key.value quat = mathutils.Quaternion( [keyVal.w, keyVal.x, keyVal.y, keyVal.z]) # beware, CrossQuats takes arguments in a # counter-intuitive order: # q1.toMatrix() * q2.toMatrix() == CrossQuats(q2, q1).toMatrix() quatVal = CrossQuats( niBone_bind_quat_inv, quat) # Rchannel = Rtotal * inverse(Rbind) rot = CrossQuats( CrossQuats(extra_matrix_quat_inv, quatVal), extra_matrix_quat) # C' = X * C * inverse(X) b_posebone.quat = rot b_posebone.insertKey(b_armature, frame, [Blender.Object.Pose.ROT]) # fill optimizer dictionary if translations: rot_keys_dict[frame] = mathutils.Quaternion( rot) #else: # print("Rotation keys...(unknown)" + # "WARNING: rotation animation data of type" + # " %i found, but this type is not yet supported; data has been skipped""" % rotation_type) # Translations if translations.keys: self.nif_common.debug('Translation keys...') for key in translations.keys: # time 0.0 is frame 1 frame = 1 + int(key.time * self.nif_common.fps + 0.5) keyVal = key.value trans = mathutils.Vector(keyVal.x, keyVal.y, keyVal.z) locVal = ( trans - niBone_bind_trans ) * niBone_bind_rot_inv * ( 1.0 / niBone_bind_scale ) # Tchannel = (Ttotal - Tbind) * inverse(Rbind) / Sbind # the rotation matrix is needed at this frame (that's # why the other keys are inserted first) if rot_keys_dict: try: rot = rot_keys_dict[frame].toMatrix() except KeyError: # fall back on slow method ipo = action.getChannelIpo(bone_name) quat = mathutils.Quaternion() quat.x = ipo.getCurve('QuatX').evaluate(frame) quat.y = ipo.getCurve('QuatY').evaluate(frame) quat.z = ipo.getCurve('QuatZ').evaluate(frame) quat.w = ipo.getCurve('QuatW').evaluate(frame) rot = quat.toMatrix() else: rot = mathutils.Matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]) # we also need the scale at this frame if scale_keys_dict: try: sizeVal = scale_keys_dict[frame] except KeyError: ipo = action.getChannelIpo(bone_name) if ipo.getCurve('SizeX'): sizeVal = ipo.getCurve('SizeX').evaluate( frame) # assume uniform scale else: sizeVal = 1.0 else: sizeVal = 1.0 size = mathutils.Matrix([[sizeVal, 0.0, 0.0], [0.0, sizeVal, 0.0], [0.0, 0.0, sizeVal]]) # now we can do the final calculation loc = (extra_matrix_trans * size * rot + locVal - extra_matrix_trans) * extra_matrix_rot_inv * ( 1.0 / extra_matrix_scale ) # C' = X * C * inverse(X) b_posebone.loc = loc b_posebone.insertKey(b_armature, frame, [Blender.Object.Pose.LOC]) if translations: del scale_keys_dict del rot_keys_dict # set extend mode for all ipo curves if kfc: try: ipo = action.getChannelIpo(bone_name) except ValueError: # no channel for bone_name pass else: for b_curve in ipo: b_curve.extend = self.nif_common.get_extend_from_flags( kfc.flags) # constraints (priority) # must be done outside edit mode hence after calling for bone_name, b_posebone in b_armature.getPose().bones.items(): # find bone nif block niBone = self.nif_common.blocks[bone_name] # store bone priority, if applicable if niBone.name in self.nif_common.bone_priorities: constr = b_posebone.constraints.append( bpy.types.Constraint.NULL) constr.name = "priority:%i" % self.nif_common.bone_priorities[ niBone.name] return b_armature
def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): from bpy_extras.image_utils import load_image #print previous_chunk.bytes_read, 'BYTES READ' contextObName = None contextLamp = [None, None] # object, Data contextMaterial = None contextMatrix_rot = None # Blender.mathutils.Matrix(); contextMatrix.identity() #contextMatrix_tx = None # Blender.mathutils.Matrix(); contextMatrix.identity() contextMesh_vertls = None # flat array: (verts * 3) contextMesh_facels = None contextMeshMaterials = [] # (matname, [face_idxs]) contextMeshUV = None # flat array (verts * 2) TEXTURE_DICT = {} MATDICT = {} # TEXMODE = Mesh.FaceModes['TEX'] # Localspace variable names, faster. STRUCT_SIZE_FLOAT = struct.calcsize('f') STRUCT_SIZE_2FLOAT = struct.calcsize('2f') STRUCT_SIZE_3FLOAT = struct.calcsize('3f') STRUCT_SIZE_4FLOAT = struct.calcsize('4f') STRUCT_SIZE_UNSIGNED_SHORT = struct.calcsize('H') STRUCT_SIZE_4UNSIGNED_SHORT = struct.calcsize('4H') STRUCT_SIZE_4x3MAT = struct.calcsize('ffffffffffff') # STRUCT_SIZE_4x3MAT = calcsize('ffffffffffff') # print STRUCT_SIZE_4x3MAT, ' STRUCT_SIZE_4x3MAT' # only init once object_list = [] # for hierarchy object_parent = [] # index of parent in hierarchy, 0xFFFF = no parent pivot_list = [] # pivots with hierarchy handling def putContextMesh(myContextMesh_vertls, myContextMesh_facels, myContextMeshMaterials): bmesh = bpy.data.meshes.new(contextObName) if myContextMesh_facels is None: myContextMesh_facels = [] if myContextMesh_vertls: bmesh.vertices.add(len(myContextMesh_vertls) // 3) bmesh.vertices.foreach_set("co", myContextMesh_vertls) nbr_faces = len(myContextMesh_facels) bmesh.polygons.add(nbr_faces) bmesh.loops.add(nbr_faces * 3) eekadoodle_faces = [] for v1, v2, v3 in myContextMesh_facels: eekadoodle_faces.extend((v3, v1, v2) if v3 == 0 else (v1, v2, v3)) bmesh.polygons.foreach_set("loop_start", range(0, nbr_faces * 3, 3)) bmesh.polygons.foreach_set("loop_total", (3,) * nbr_faces) bmesh.loops.foreach_set("vertex_index", eekadoodle_faces) if bmesh.polygons and contextMeshUV: bmesh.uv_textures.new() uv_faces = bmesh.uv_textures.active.data[:] else: uv_faces = None for mat_idx, (matName, faces) in enumerate(myContextMeshMaterials): if matName is None: bmat = None else: bmat = MATDICT.get(matName) # in rare cases no materials defined. if bmat: img = TEXTURE_DICT.get(bmat.name) else: print(" warning: material %r not defined!" % matName) bmat = MATDICT[matName] = bpy.data.materials.new(matName) img = None bmesh.materials.append(bmat) # can be None if uv_faces and img: for fidx in faces: bmesh.polygons[fidx].material_index = mat_idx uv_faces[fidx].image = img else: for fidx in faces: bmesh.polygons[fidx].material_index = mat_idx if uv_faces: uvl = bmesh.uv_layers.active.data[:] for fidx, pl in enumerate(bmesh.polygons): face = myContextMesh_facels[fidx] v1, v2, v3 = face # eekadoodle if v3 == 0: v1, v2, v3 = v3, v1, v2 uvl[pl.loop_start].uv = contextMeshUV[v1 * 2: (v1 * 2) + 2] uvl[pl.loop_start + 1].uv = contextMeshUV[v2 * 2: (v2 * 2) + 2] uvl[pl.loop_start + 2].uv = contextMeshUV[v3 * 2: (v3 * 2) + 2] # always a tri bmesh.validate() bmesh.update() ob = bpy.data.objects.new(contextObName, bmesh) object_dictionary[contextObName] = ob SCN.objects.link(ob) importedObjects.append(ob) if contextMatrix_rot: ob.matrix_local = contextMatrix_rot object_matrix[ob] = contextMatrix_rot.copy() #a spare chunk new_chunk = chunk() temp_chunk = chunk() CreateBlenderObject = False def read_float_color(temp_chunk): temp_data = file.read(STRUCT_SIZE_3FLOAT) temp_chunk.bytes_read += STRUCT_SIZE_3FLOAT return [float(col) for col in struct.unpack('<3f', temp_data)] def read_float(temp_chunk): temp_data = file.read(STRUCT_SIZE_FLOAT) temp_chunk.bytes_read += STRUCT_SIZE_FLOAT return struct.unpack('<f', temp_data)[0] def read_short(temp_chunk): temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) temp_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT return struct.unpack('<H', temp_data)[0] def read_byte_color(temp_chunk): temp_data = file.read(struct.calcsize('3B')) temp_chunk.bytes_read += 3 return [float(col) / 255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb def read_texture(new_chunk, temp_chunk, name, mapto): new_texture = bpy.data.textures.new(name, type='IMAGE') u_scale, v_scale, u_offset, v_offset = 1.0, 1.0, 0.0, 0.0 mirror = False extension = 'wrap' while (new_chunk.bytes_read < new_chunk.length): #print 'MAT_TEXTURE_MAP..while', new_chunk.bytes_read, new_chunk.length read_chunk(file, temp_chunk) if temp_chunk.ID == MAT_MAP_FILEPATH: texture_name, read_str_len = read_string(file) img = TEXTURE_DICT[contextMaterial.name] = load_image(texture_name, dirname) temp_chunk.bytes_read += read_str_len # plus one for the null character that gets removed elif temp_chunk.ID == MAT_MAP_USCALE: u_scale = read_float(temp_chunk) elif temp_chunk.ID == MAT_MAP_VSCALE: v_scale = read_float(temp_chunk) elif temp_chunk.ID == MAT_MAP_UOFFSET: u_offset = read_float(temp_chunk) elif temp_chunk.ID == MAT_MAP_VOFFSET: v_offset = read_float(temp_chunk) elif temp_chunk.ID == MAT_MAP_TILING: tiling = read_short(temp_chunk) if tiling & 0x2: extension = 'mirror' elif tiling & 0x10: extension = 'decal' elif temp_chunk.ID == MAT_MAP_ANG: print("\nwarning: ignoring UV rotation") skip_to_end(file, temp_chunk) new_chunk.bytes_read += temp_chunk.bytes_read # add the map to the material in the right channel if img: add_texture_to_material(img, new_texture, (u_scale, v_scale), (u_offset, v_offset), extension, contextMaterial, mapto) dirname = os.path.dirname(file.name) #loop through all the data for this chunk (previous chunk) and see what it is while (previous_chunk.bytes_read < previous_chunk.length): #print '\t', previous_chunk.bytes_read, 'keep going' #read the next chunk #print 'reading a chunk' read_chunk(file, new_chunk) #is it a Version chunk? if new_chunk.ID == VERSION: #print 'if new_chunk.ID == VERSION:' #print 'found a VERSION chunk' #read in the version of the file #it's an unsigned short (H) temp_data = file.read(struct.calcsize('I')) version = struct.unpack('<I', temp_data)[0] new_chunk.bytes_read += 4 # read the 4 bytes for the version number #this loader works with version 3 and below, but may not with 4 and above if version > 3: print('\tNon-Fatal Error: Version greater than 3, may not load correctly: ', version) #is it an object info chunk? elif new_chunk.ID == OBJECTINFO: #print 'elif new_chunk.ID == OBJECTINFO:' # print 'found an OBJECTINFO chunk' process_next_chunk(file, new_chunk, importedObjects, IMAGE_SEARCH) #keep track of how much we read in the main chunk new_chunk.bytes_read += temp_chunk.bytes_read #is it an object chunk? elif new_chunk.ID == OBJECT: if CreateBlenderObject: putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials) contextMesh_vertls = [] contextMesh_facels = [] ## preparando para receber o proximo objeto contextMeshMaterials = [] # matname:[face_idxs] contextMeshUV = None #contextMesh.vertexUV = 1 # Make sticky coords. # Reset matrix contextMatrix_rot = None #contextMatrix_tx = None CreateBlenderObject = True contextObName, read_str_len = read_string(file) new_chunk.bytes_read += read_str_len #is it a material chunk? elif new_chunk.ID == MATERIAL: # print("read material") #print 'elif new_chunk.ID == MATERIAL:' contextMaterial = bpy.data.materials.new('Material') elif new_chunk.ID == MAT_NAME: #print 'elif new_chunk.ID == MAT_NAME:' material_name, read_str_len = read_string(file) # print("material name", material_name) #plus one for the null character that ended the string new_chunk.bytes_read += read_str_len contextMaterial.name = material_name.rstrip() # remove trailing whitespace MATDICT[material_name] = contextMaterial elif new_chunk.ID == MAT_AMBIENT: #print 'elif new_chunk.ID == MAT_AMBIENT:' read_chunk(file, temp_chunk) if temp_chunk.ID == MAT_FLOAT_COLOR: contextMaterial.mirror_color = read_float_color(temp_chunk) # temp_data = file.read(struct.calcsize('3f')) # temp_chunk.bytes_read += 12 # contextMaterial.mirCol = [float(col) for col in struct.unpack('<3f', temp_data)] elif temp_chunk.ID == MAT_24BIT_COLOR: contextMaterial.mirror_color = read_byte_color(temp_chunk) # temp_data = file.read(struct.calcsize('3B')) # temp_chunk.bytes_read += 3 # contextMaterial.mirCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb else: skip_to_end(file, temp_chunk) new_chunk.bytes_read += temp_chunk.bytes_read elif new_chunk.ID == MAT_DIFFUSE: #print 'elif new_chunk.ID == MAT_DIFFUSE:' read_chunk(file, temp_chunk) if temp_chunk.ID == MAT_FLOAT_COLOR: contextMaterial.diffuse_color = read_float_color(temp_chunk) # temp_data = file.read(struct.calcsize('3f')) # temp_chunk.bytes_read += 12 # contextMaterial.rgbCol = [float(col) for col in struct.unpack('<3f', temp_data)] elif temp_chunk.ID == MAT_24BIT_COLOR: contextMaterial.diffuse_color = read_byte_color(temp_chunk) # temp_data = file.read(struct.calcsize('3B')) # temp_chunk.bytes_read += 3 # contextMaterial.rgbCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb else: skip_to_end(file, temp_chunk) # print("read material diffuse color", contextMaterial.diffuse_color) new_chunk.bytes_read += temp_chunk.bytes_read elif new_chunk.ID == MAT_SPECULAR: #print 'elif new_chunk.ID == MAT_SPECULAR:' read_chunk(file, temp_chunk) if temp_chunk.ID == MAT_FLOAT_COLOR: contextMaterial.specular_color = read_float_color(temp_chunk) # temp_data = file.read(struct.calcsize('3f')) # temp_chunk.bytes_read += 12 # contextMaterial.mirCol = [float(col) for col in struct.unpack('<3f', temp_data)] elif temp_chunk.ID == MAT_24BIT_COLOR: contextMaterial.specular_color = read_byte_color(temp_chunk) # temp_data = file.read(struct.calcsize('3B')) # temp_chunk.bytes_read += 3 # contextMaterial.mirCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb else: skip_to_end(file, temp_chunk) new_chunk.bytes_read += temp_chunk.bytes_read elif new_chunk.ID == MAT_TEXTURE_MAP: read_texture(new_chunk, temp_chunk, "Diffuse", "COLOR") elif new_chunk.ID == MAT_SPECULAR_MAP: read_texture(new_chunk, temp_chunk, "Specular", "SPECULARITY") elif new_chunk.ID == MAT_OPACITY_MAP: read_texture(new_chunk, temp_chunk, "Opacity", "ALPHA") elif new_chunk.ID == MAT_BUMP_MAP: read_texture(new_chunk, temp_chunk, "Bump", "NORMAL") elif new_chunk.ID == MAT_TRANSPARENCY: #print 'elif new_chunk.ID == MAT_TRANSPARENCY:' read_chunk(file, temp_chunk) temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) temp_chunk.bytes_read += 2 contextMaterial.alpha = 1 - (float(struct.unpack('<H', temp_data)[0]) / 100) new_chunk.bytes_read += temp_chunk.bytes_read elif new_chunk.ID == OBJECT_LAMP: # Basic lamp support. temp_data = file.read(STRUCT_SIZE_3FLOAT) x, y, z = struct.unpack('<3f', temp_data) new_chunk.bytes_read += STRUCT_SIZE_3FLOAT # no lamp in dict that would be confusing contextLamp[1] = bpy.data.lamps.new("Lamp", 'POINT') contextLamp[0] = ob = bpy.data.objects.new("Lamp", contextLamp[1]) SCN.objects.link(ob) importedObjects.append(contextLamp[0]) #print 'number of faces: ', num_faces #print x,y,z contextLamp[0].location = x, y, z # Reset matrix contextMatrix_rot = None #contextMatrix_tx = None #print contextLamp.name, elif new_chunk.ID == OBJECT_MESH: # print 'Found an OBJECT_MESH chunk' pass elif new_chunk.ID == OBJECT_VERTICES: ''' Worldspace vertex locations ''' # print 'elif new_chunk.ID == OBJECT_VERTICES:' temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) num_verts = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += 2 # print 'number of verts: ', num_verts contextMesh_vertls = struct.unpack('<%df' % (num_verts * 3), file.read(STRUCT_SIZE_3FLOAT * num_verts)) new_chunk.bytes_read += STRUCT_SIZE_3FLOAT * num_verts # dummyvert is not used atm! #print 'object verts: bytes read: ', new_chunk.bytes_read elif new_chunk.ID == OBJECT_FACES: # print 'elif new_chunk.ID == OBJECT_FACES:' temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) num_faces = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += 2 #print 'number of faces: ', num_faces # print '\ngetting a face' temp_data = file.read(STRUCT_SIZE_4UNSIGNED_SHORT * num_faces) new_chunk.bytes_read += STRUCT_SIZE_4UNSIGNED_SHORT * num_faces # 4 short ints x 2 bytes each contextMesh_facels = struct.unpack('<%dH' % (num_faces * 4), temp_data) contextMesh_facels = [contextMesh_facels[i - 3:i] for i in range(3, (num_faces * 4) + 3, 4)] elif new_chunk.ID == OBJECT_MATERIAL: # print 'elif new_chunk.ID == OBJECT_MATERIAL:' material_name, read_str_len = read_string(file) new_chunk.bytes_read += read_str_len # remove 1 null character. temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) num_faces_using_mat = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * num_faces_using_mat) new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * num_faces_using_mat temp_data = struct.unpack("<%dH" % (num_faces_using_mat), temp_data) contextMeshMaterials.append((material_name, temp_data)) #look up the material in all the materials elif new_chunk.ID == OBJECT_UV: temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) num_uv = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += 2 temp_data = file.read(STRUCT_SIZE_2FLOAT * num_uv) new_chunk.bytes_read += STRUCT_SIZE_2FLOAT * num_uv contextMeshUV = struct.unpack('<%df' % (num_uv * 2), temp_data) elif new_chunk.ID == OBJECT_TRANS_MATRIX: # How do we know the matrix size? 54 == 4x4 48 == 4x3 temp_data = file.read(STRUCT_SIZE_4x3MAT) data = list(struct.unpack('<ffffffffffff', temp_data)) new_chunk.bytes_read += STRUCT_SIZE_4x3MAT contextMatrix_rot = mathutils.Matrix((data[:3] + [0], data[3:6] + [0], data[6:9] + [0], data[9:] + [1], )).transposed() elif (new_chunk.ID == MAT_MAP_FILEPATH): texture_name, read_str_len = read_string(file) if contextMaterial.name not in TEXTURE_DICT: TEXTURE_DICT[contextMaterial.name] = load_image(texture_name, dirname, place_holder=False, recursive=IMAGE_SEARCH) new_chunk.bytes_read += read_str_len # plus one for the null character that gets removed elif new_chunk.ID == EDITKEYFRAME: pass # including these here means their EK_OB_NODE_HEADER are scanned elif new_chunk.ID in {ED_KEY_AMBIENT_NODE, ED_KEY_OBJECT_NODE, ED_KEY_CAMERA_NODE, ED_KEY_TARGET_NODE, ED_KEY_LIGHT_NODE, ED_KEY_L_TARGET_NODE, ED_KEY_SPOTLIGHT_NODE}: # another object is being processed child = None elif new_chunk.ID == EK_OB_NODE_HEADER: object_name, read_str_len = read_string(file) new_chunk.bytes_read += read_str_len temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 2) new_chunk.bytes_read += 4 temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) hierarchy = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += 2 child = object_dictionary.get(object_name) if child is None: child = bpy.data.objects.new(object_name, None) # create an empty object SCN.objects.link(child) importedObjects.append(child) object_list.append(child) object_parent.append(hierarchy) pivot_list.append(mathutils.Vector((0.0, 0.0, 0.0))) elif new_chunk.ID == EK_OB_INSTANCE_NAME: object_name, read_str_len = read_string(file) # child.name = object_name child.name += "." + object_name object_dictionary[object_name] = child new_chunk.bytes_read += read_str_len # print("new instance object:", object_name) elif new_chunk.ID == EK_OB_PIVOT: # translation temp_data = file.read(STRUCT_SIZE_3FLOAT) pivot = struct.unpack('<3f', temp_data) new_chunk.bytes_read += STRUCT_SIZE_3FLOAT pivot_list[len(pivot_list) - 1] = mathutils.Vector(pivot) elif new_chunk.ID == EK_OB_POSITION_TRACK: # translation new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 5 temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 5) temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) nkeys = struct.unpack('<H', temp_data)[0] temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 2 for i in range(nkeys): temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) nframe = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 2) new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 2 temp_data = file.read(STRUCT_SIZE_3FLOAT) loc = struct.unpack('<3f', temp_data) new_chunk.bytes_read += STRUCT_SIZE_3FLOAT if nframe == 0: child.location = loc elif new_chunk.ID == EK_OB_ROTATION_TRACK: # rotation new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 5 temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 5) temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) nkeys = struct.unpack('<H', temp_data)[0] temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 2 for i in range(nkeys): temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) nframe = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 2) new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 2 temp_data = file.read(STRUCT_SIZE_4FLOAT) rad, axis_x, axis_y, axis_z = struct.unpack("<4f", temp_data) new_chunk.bytes_read += STRUCT_SIZE_4FLOAT if nframe == 0: child.rotation_euler = mathutils.Quaternion((axis_x, axis_y, axis_z), -rad).to_euler() # why negative? elif new_chunk.ID == EK_OB_SCALE_TRACK: # translation new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 5 temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 5) temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) nkeys = struct.unpack('<H', temp_data)[0] temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 2 for i in range(nkeys): temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) nframe = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 2) new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 2 temp_data = file.read(STRUCT_SIZE_3FLOAT) sca = struct.unpack('<3f', temp_data) new_chunk.bytes_read += STRUCT_SIZE_3FLOAT if nframe == 0: child.scale = sca else: # (new_chunk.ID!=VERSION or new_chunk.ID!=OBJECTINFO or new_chunk.ID!=OBJECT or new_chunk.ID!=MATERIAL): # print 'skipping to end of this chunk' #print("unknown chunk: "+hex(new_chunk.ID)) buffer_size = new_chunk.length - new_chunk.bytes_read binary_format = "%ic" % buffer_size temp_data = file.read(struct.calcsize(binary_format)) new_chunk.bytes_read += buffer_size #update the previous chunk bytes read # print 'previous_chunk.bytes_read += new_chunk.bytes_read' # print previous_chunk.bytes_read, new_chunk.bytes_read previous_chunk.bytes_read += new_chunk.bytes_read ## print 'Bytes left in this chunk: ', previous_chunk.length - previous_chunk.bytes_read # FINISHED LOOP # There will be a number of objects still not added if CreateBlenderObject: putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials) # Assign parents to objects # check _if_ we need to assign first because doing so recalcs the depsgraph for ind, ob in enumerate(object_list): parent = object_parent[ind] if parent == ROOT_OBJECT: if ob.parent is not None: ob.parent = None else: if ob.parent != object_list[parent]: ob.parent = object_list[parent] # pivot_list[ind] += pivot_list[parent] # XXX, not sure this is correct, should parent space matrix be applied before combining? # fix pivots for ind, ob in enumerate(object_list): if ob.type == 'MESH': pivot = pivot_list[ind] pivot_matrix = object_matrix.get(ob, mathutils.Matrix()) # unlikely to fail pivot_matrix = mathutils.Matrix.Translation(pivot_matrix.to_3x3() * -pivot) ob.data.transform(pivot_matrix)
def get_mesh_string(obj, boneWeightCount): mesh = obj.to_mesh(bpy.context.scene, True, "PREVIEW") vertices = [] normals = [] tangents = [] colors = [] uvs = [] bones = [] boneIndices = [] boneWeights = [] vertex_number = -1 for face in obj.data.polygons: vertices_in_face = face.vertices[:] for vertex in vertices_in_face: vertex_number += 1 vertices.append(obj.data.vertices[vertex].co.x) vertices.append(obj.data.vertices[vertex].co.y) vertices.append(obj.data.vertices[vertex].co.z) normals.append(obj.data.vertices[vertex].normal.x) normals.append(obj.data.vertices[vertex].normal.y) normals.append(obj.data.vertices[vertex].normal.z) if len(mesh.tessface_uv_textures) > 0: for data in mesh.tessface_uv_textures.active.data: uvs.append(data.uv1.x) uvs.append(data.uv1.y) uvs.append(data.uv2.x) uvs.append(data.uv2.y) uvs.append(data.uv3.x) uvs.append(data.uv3.y) if len(mesh.tessface_vertex_colors) > 0: for data in mesh.tessface_vertex_colors.active.data: colors.append(data.color1.r) colors.append(data.color1.g) colors.append(data.color1.b) colors.append(data.color2.r) colors.append(data.color2.g) colors.append(data.color2.b) colors.append(data.color3.r) colors.append(data.color3.g) colors.append(data.color3.b) if (len(bpy.data.armatures) > 0): armature, armatureObject = get_armature() for face in obj.data.polygons: vertices_in_face = face.vertices[:] for vertex_index in vertices_in_face: vertex = obj.data.vertices[vertex_index] bone_array = [] for group in vertex.groups: index = group.group weight = group.weight bone_array.append([index, weight]) bone_array.sort(key=operator.itemgetter(1), reverse=True) total_weight = 0.0 for i in range(len(bone_array)): total_weight += bone_array[i][1] for i in range(len(bone_array)): bone_array[i][1] /= total_weight for i in range(boneWeightCount): if i < len(bone_array): bone_proxy = bone_array[i] found = 0 index = bone_proxy[0] weight = bone_proxy[1] for j, bone in enumerate(armature.bones): if obj.vertex_groups[index].name == bone.name: boneIndices.append(j) boneWeights.append(weight) found = 1 break if found != 1: boneIndices.append(0) boneWeights.append(0) else: boneIndices.append(0) boneWeights.append(0) bone_id = -1 for bone in armature.bones: bone_id += 1 parent_index = -1 weight = 0 skinned = "0" name = bone.name matrix_local = bone.matrix_local pos = mathutils.Vector((1.0, 1.0, 1.0)) rot = mathutils.Quaternion((0.0, 0.0, 0.0, 1.0)) scl = mathutils.Vector((1.0, 1.0, 1.0)) if bone.parent != None: parent_index = i = 0 matrix_local = bone.parent.matrix_local.inverted( ) * matrix_local for parent in armature.bones: if parent.name == bone.parent.name: parent_index = i i += 1 pos, rot, scl = matrix_local.decompose() bindPose = bone.matrix_local j = -1 for boneIndex in boneIndices: j += 1 if int(boneIndex) == bone_id: weight += float(boneWeights[j]) if weight > 0: skinned = "1" bones.append( TEMPLATE_BONE % { "parent": parent_index, "name": name, "skinned": skinned, "bindPose": mat4_string(bindPose.inverted()), "position": ", ".join( [float_str(pos.x), float_str(pos.y), float_str(pos.z)]), "rotation": ", ".join([ float_str(rot.x), float_str(rot.y), float_str(rot.z), float_str(rot.w) ]), "scale": ", ".join( [float_str(scl.x), float_str(scl.y), float_str(scl.z)]) }) return TEMPLATE_FILE % { "vertices": flat_array(vertices), "normals": flat_array(normals), "colors": flat_array(colors), "uvs": flat_array(uvs), "bones": ",".join(bones), "boneWeightCount": str(boneWeightCount), "boneIndices": flat_array(boneIndices), "boneWeights": flat_array(boneWeights) }
import mathutils import math import bmesh import threading import socket import queue from bpy.props import ( BoolProperty, StringProperty, FloatProperty, PointerProperty ) ROTATE_X_90 = mathutils.Quaternion((1.0, 0.0, 0.0), math.radians(90.0)) def unity_quaternion_to_blender(w, x, y, z): return ROTATE_X_90 @ mathutils.Quaternion((-float(w), float(x), float(y), -float(z))) ### IMPORT ### def import_ar_recording(context, report, filepath, include_camera, include_cloud, include_planes, include_camera_position, include_camera_rotation, *args, **kwargs): scene = context.scene fps = scene.render.fps start_time = None
def FM_constraints(ob): # Pseudo code for special constraint treatment: # # If tol1dist or tol1rot is exceeded: # If normal constraint: It will be detached # If spring constraint: It will be set to active # If tol2dist or tol2rot is exceeded: # If spring constraint: It will be detached print() print("Creating Fracture Modifier constraints from BCB data...") time_const = time.time() scene = bpy.context.scene md = ob.modifiers["Fracture"] fmode_bak = md.fracture_mode md.fracture_mode = 'EXTERNAL' ### Create dict of FM mesh islands (speed optimization) mesh_islands = {} for mi in md.mesh_islands: mesh_islands[mi.name] = mi ### Unpack BCB data from text file try: s = bpy.data.texts[asciiExportName + ".txt"].as_string() except: print( "Error: No export data found, couldn't build Fracture Modifier object." ) return cDef, exData, exPairs, objNames = pickle.loads( zlib.decompress(base64.decodestring(s.encode()))) ### Overwrite mesh island settings with those from the original RBs (FM overwrites this state) # # Remove refresh handler from memory (required to overwrite settings) # if len(bpy.app.handlers.fracture_refresh) > 0: # bpy.app.handlers.fracture_refresh.clear() # qHandler = 1 # else: qHandler = 0 # Overwrite mesh island settings for mi in md.mesh_islands: obj_rb = scene.objects[mi.name].rigid_body #print("mass", mi.rigidbody.mass, mi.name) mi.rigidbody.type = obj_rb.type mi.rigidbody.kinematic = obj_rb.kinematic mi.rigidbody.collision_shape = obj_rb.collision_shape mi.rigidbody.mass = obj_rb.mass mi.rigidbody.friction = obj_rb.friction mi.rigidbody.restitution = obj_rb.restitution mi.rigidbody.use_margin = obj_rb.use_margin mi.rigidbody.collision_margin = obj_rb.collision_margin # # Reload refresh handler # if qHandler: # bpy.app.handlers.fracture_refresh.append(FM_shards) ### Copy custom (not BCB generated) constraints between original shards to the FM try: emptyObjs = bpy.data.groups["RigidBodyConstraints"].objects except: emptyObjs = [] emptyObjs = [ obj for obj in emptyObjs if obj.type == 'EMPTY' and not obj.hide and obj.is_visible(bpy.context.scene) and obj.rigid_body_constraint != None ] for objConst in emptyObjs: if "BCB_FM" not in objConst.keys(): objA = objConst.rigid_body_constraint.object1 objB = objConst.rigid_body_constraint.object2 if objA != None and objB != None \ and objA.name in objNames and objB.name in objNames: cProps = getAttribsOfConstraint(objConst.rigid_body_constraint) ### Add settings to constraint try: con = md.mesh_constraints.new(mesh_islands[objA.name], mesh_islands[objB.name], cProps["type"]) except: pass else: objConst["BCB_FM"] = 1 # Mark empty as used in FM con.name = objConst.name con.location = objConst.location if objConst.rotation_mode == "QUATERNION": con.rotation = objConst.rotation_quaternion else: con.rotation = objConst.rotation_euler.to_quaternion() ### Write constraint parameters for p in cProps.items(): if p[0] not in {"object1", "object2"}: try: attr = getattr(con, p[0]) # Current value except: if p[0] not in missingAttribs: missingAttribs.append(p[0]) else: if p[1] != attr: # Overwrite only when different #print("Set: ", p[0], p[1]) setattr(con, p[0], p[1]) ### Create BCB constraints cnt = 0 missingAttribs = [] for pair in exPairs: ### Get data that is only stored once per connection ob1 = pair[0] ob2 = pair[1] consts = pair[2] for const in consts: cProps, cDatb = exData[const] ### Get data that can be individual for each constraint name = cDatb[ 0] # A name is not mandatory, mainly for debugging purposes loc = cDatb[1] tol1 = cDatb[4] tol2 = cDatb[5] rotm = cDatb[6] rot = cDatb[7] try: type = cProps["type"] except: type = cDef["type"] ### Decode custom BCB attributes # 1st tolerances (elastic -> plastic) # ["TOLERANCE", tol1dist, tol1rot] if tol1 is not None and tol1[0] in {"TOLERANCE"}: tol1dist = tol1[1] tol1rot = tol1[2] else: tol1dist = -1 tol1rot = -1 # 2nd tolerances (plastic -> broken) # ["PLASTIC"/"PLASTIC_OFF", tol2dist, tol2rot] if tol2 is not None and tol2[0] in {"PLASTIC", "PLASTIC_OFF"}: tol2dist = tol2[1] tol2rot = tol2[2] if tol2[0] == "PLASTIC": plastic = True else: plastic = False else: tol2dist = -1 tol2rot = -1 plastic = False # Rotation if rotm == "QUATERNION": # If no quaternion exists we assume no rotation is available rot = mathutils.Quaternion(rot) else: # If None or EULER then no rotation is available rot = mathutils.Quaternion((1.0, 0.0, 0.0, 0.0)) ### Add settings to constraint try: con = md.mesh_constraints.new(mesh_islands[ob1], mesh_islands[ob2], type) except: pass else: con.name = name con.location = loc con.rotation = rot con.plastic = plastic con.breaking_distance = tol1dist con.breaking_angle = tol1rot con.plastic_distance = tol2dist con.plastic_angle = tol2rot ### Obsolete since FM now uses Blender defaults for constraints: ### Write constraint defaults first (FM doesn't set constraint to Blender defaults) for p in cDef.items(): if p[0] not in {"object1", "object2"}: try: attr = getattr(con, p[0]) # Current value except: if p[0] not in missingAttribs: missingAttribs.append(p[0]) else: if p[1] != attr: # Overwrite only when different #print("attr", p[0], p[1], attr) setattr(con, p[0], p[1]) ### Write own changed parameters (because it's a diff based on defaults) for p in cProps.items(): if p[0] not in {"object1", "object2"}: try: attr = getattr(con, p[0]) # Current value except: if p[0] not in missingAttribs: missingAttribs.append(p[0]) else: if p[1] != attr: # Overwrite only when different #print("Set: ", p[0], p[1]) setattr(con, p[0], p[1]) cnt += 1 if len(missingAttribs): print("Warning: Following constraint attribute(s) missing:") print( "(This is a bug in FM, branch seems not to be up to date with Blender.)" ) for attr in missingAttribs: print(attr) md.fracture_mode = fmode_bak md.refresh = False print('Time constraints: %0.2f s' % (time.time() - time_const)) print('Imported: %d constraints (%d shards)' % (cnt, len(md.mesh_islands))) # Remove this handler from memory (will be reloaded on FM_shards()) bpy.app.handlers.fracture_constraint_refresh.clear()
_obj = bpy.data.objects.new('obj111', _msh) _verts = [(-2, -2, -2), (2, -2, -2), (0, 2, -2), (0, 0, 2)] _verts = [(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)] _edgs = [] # [(0,1),(1,2),(2,0),(0,3),(1,3),(2,3)] _fcs = [(0, 2, 1), (0, 1, 3), (1, 2, 3), (2, 0, 3)] _verts = [ (-1, -1, 0), (-1, 1, 0), (1, 1, 0), (1, -1, 0), (0, 0, 2), ] _edgs = [] _fcs = [(0, 3, 2, 1), (0, 1, 4), (1, 2, 4), (2, 3, 4), (3, 0, 4)] for i in range(len(_verts)): v = mathutils.Vector(_verts[i]) _verts[i] = v _msh.from_pydata(_verts, _edgs, _fcs) _msh.validate() bpy.context.scene.collection.objects.link(_obj) _obj.rotation_mode = 'QUATERNION' _obj.rotation_quaternion = mathutils.Quaternion( (0.4, 0.4, 0.4, math.sqrt(1 - 3 * 0.16))) _obj.location = mathutils.Vector((0.1, 0.1, 0.1)) _obj.scale = mathutils.Vector((0.1, 0.1, 0.1))
camera_data.sensor_width = 36 camera_data.sensor_height = 24 # Creamos el objeto de camara con los datos camera_object = bpy.data.objects.new("Camera", camera_data) # Cambiamos la camara activa # > Localizacion camera_object.location = mathutils.Vector( (float(parameters["location"]["x"]), float(parameters["location"]["y"]), float(parameters["location"]["z"]))) # > Rotacion (cuaterniones) camera_object.rotation_mode = "QUATERNION" camera_object.rotation_quaternion = mathutils.Quaternion( (float(parameters["qua"]["_w"]), float(parameters["qua"]["_x"]), float(parameters["qua"]["_y"]), float(parameters["qua"]["_z"]))) # Añadimos la camara a la escena bpy.context.scene.collection.objects.link(camera_object) # Ponemos la nueva camara como camara activa bpy.context.scene.camera = camera_object # Parametros de renderizado # Transformacion del color filmica bpy.context.scene.view_settings.view_transform = "Filmic" bpy.context.scene.view_settings.look = "Medium Contrast" bpy.context.scene.render.threads = 2
def apply_to_frame(self, coord_system): assert isinstance(coord_system, CoordSystem) rotation = mathutils.Quaternion(self.axis, self.angle).to_matrix() * coord_system.orientation.to_matrix() return CoordSystem(coord_system.position + self.translation, rotation.to_quaternion().normalized())
################################################################ print( "===============================================================================" ) armature = bpy.data.objects[armature_name] action = armature.animation_data.action fcurves = action.fcurves rot_diffs = {} for k in range(len(fcurves) // FCURVE_INFO_SIZE_PER_BONE): rot_idx = k * FCURVE_INFO_SIZE_PER_BONE + FCURVE_ROT_OFFSET src_ori = mathutils.Quaternion( (fcurves[rot_idx + 0].keyframe_points[0].co[1], fcurves[rot_idx + 1].keyframe_points[0].co[1], fcurves[rot_idx + 2].keyframe_points[0].co[1], fcurves[rot_idx + 3].keyframe_points[0].co[1])) dst_ori = mathutils.Quaternion( (fcurves[rot_idx + 0].keyframe_points[-1].co[1], fcurves[rot_idx + 1].keyframe_points[-1].co[1], fcurves[rot_idx + 2].keyframe_points[-1].co[1], fcurves[rot_idx + 3].keyframe_points[-1].co[1])) rot_diffs[rot_idx] = src_ori.rotation_difference(dst_ori) for k in range(len(fcurves) // FCURVE_INFO_SIZE_PER_BONE): rot_idx = k * FCURVE_INFO_SIZE_PER_BONE + FCURVE_ROT_OFFSET num_frames = len(fcurves[rot_idx].keyframe_points) for i in range(num_frames - 1): ori = mathutils.Quaternion( (fcurves[rot_idx + 0].keyframe_points[i].co[1],
def explicit_rot(self, b, final, initial): q = self.substract_rot(final, initial) null_rot = m.Quaternion((1.,0.,0.,0.)) if q != null_rot: print('self.rotate(\'{}\', m.Quaternion(({:.4f},{:.4f},{:.4f},{:.4f})))'.format(b, q.w,q.x,q.y,q.z))
def unity_quaternion_to_blender(w, x, y, z): return ROTATE_X_90 @ mathutils.Quaternion((-float(w), float(x), float(y), -float(z)))
def test(self): self.armature.animation_data_create() self.armature.animation_data.action = bpy.data.actions.new(name = 'Run') reset_all() set_frame(1) self.move('footL', m.Vector((0.0000,-0.2786,0.4208))) self.move('footR', m.Vector((0.0000,0.2193,0.0000))) self.move('armL', m.Vector((-0.8331,0.0000,-0.8861))) self.move('armR', m.Vector((0.8090,0.0000,-0.9342))) self.rotate('pelvis', m.Quaternion((0.9610,-0.2493,-0.0302,-0.1163))) self.move('pelvis', m.Vector((0.0000,-0.0296,-0.1126))) self.rotate('spine1', m.Quaternion((0.9541,0.2909,-0.0625,0.0347))) self.rotate('spine2', m.Quaternion((0.9782,0.2017,-0.0433,0.0241))) self.rotate('head', m.Quaternion((1.0000,0.0000,-0.0000,0.0000))) confirm_animation_pose() add_frames(10) self.rotate('footL', m.Quaternion((0.9386,-0.3451,-0.0000,0.0000))) self.move('footL', m.Vector((0.0000,-0.5173,0.1142))) self.rotate('footR', m.Quaternion((-0.7016,-0.7126,-0.0000,0.0000))) self.move('footR', m.Vector((0.0000,0.5630,0.3252))) self.move('armL', m.Vector((-0.8331,0.5658,-0.6814))) self.move('armR', m.Vector((0.8090,-0.6300,-0.8901))) self.rotate('pelvis', m.Quaternion((0.9610,-0.2493,-0.0302,-0.1163))) self.move('pelvis', m.Vector((0.0000,-0.0296,-0.1126))) self.rotate('spine1', m.Quaternion((0.9541,0.2909,-0.0625,0.0347))) self.rotate('spine2', m.Quaternion((0.9782,0.2017,-0.0433,0.0241))) self.rotate('head', m.Quaternion((1.0000,0.0000,-0.0000,0.0000))) confirm_animation_pose() add_frames(10) self.move('footL', m.Vector((0.0000,0.2193,0.0000))) self.move('footR', m.Vector((0.0000,-0.2786,0.4208))) self.move('armL', m.Vector((-0.8090,0.0000,-0.9342))) self.move('armR', m.Vector((0.8331,0.0000,-0.8861))) self.rotate('pelvis', m.Quaternion((0.9610,-0.2493,0.0302,0.1163))) self.move('pelvis', m.Vector((0.0000,-0.0296,-0.1126))) self.rotate('spine1', m.Quaternion((0.9541,0.2909,0.0625,-0.0347))) self.rotate('spine2', m.Quaternion((0.9782,0.2017,0.0433,-0.0241))) self.rotate('head', m.Quaternion((1.0000,0.0000,0.0000,-0.0000))) confirm_animation_pose()
def import_armature(data): """Scans an armature hierarchy, and returns a whole armature. This is done outside the normal node tree scan to allow for positioning of the bones before skins are attached.""" bone_info = data.bone_info if bone_info: # armature_name = "Test" # b_armature_data = bpy.data.armatures.new(armature_name) # b_armature_data.display_type = 'STICK' # b_armature_data.show_axes = True # # set axis orientation for export # # b_armature_data.niftools.axis_forward = NifOp.props.axis_forward # # b_armature_data.niftools.axis_up = NifOp.props.axis_up # b_armature_obj = create_ob(armature_name, b_armature_data) # b_armature_obj.show_in_front = True # # LOD(b_armature_obj, 10) # bone_names = [matrix_util.bone_name_for_blender(n) for n in data.bone_names] # # print(bone_names) # # print("ovl order") # # make armature editable and create bones # bpy.ops.object.mode_set(mode='EDIT', toggle=False) # for bone_name, o_mat, o_parent_ind in zip(bone_names, bone_info.inverse_bind_matrices, bone_info.bone_parents): # # print(bone_name) # # create a new bone # if not bone_name: # bone_name = "Dummy" # b_edit_bone = b_armature_data.edit_bones.new(bone_name) # # get armature space matrix in blender's coordinate space # # n_bind = matrix_util.import_matrix(o_mat).inverted() # # it should not be needed once we are sure we read the right matrices # raw_mat = matrix_util.import_matrix(o_mat) # # print(bone_name, list(int(round(math.degrees(x))) for x in raw_mat.to_euler())) # # print(bone_name, list(int(round(math.degrees(x))) for x in raw_mat.inverted().to_euler()), "inv") # n_bind = raw_mat.inverted_safe() # b_bind = matrix_util.nif_bind_to_blender_bind(n_bind) # # the following is a workaround because blender can no longer set matrices to bones directly # tail, roll = matrix_util.mat3_to_vec_roll(b_bind.to_3x3()) # b_edit_bone.head = b_bind.to_translation() # b_edit_bone.tail = tail + b_edit_bone.head # b_edit_bone.roll = roll # # link to parent # try: # if o_parent_ind != 255: # b_parent_bone = b_armature_data.edit_bones[bone_names[o_parent_ind]] # b_edit_bone.parent = b_parent_bone # except: # pass # # fix_bone_lengths(b_armature_data) # bpy.ops.object.mode_set(mode='OBJECT', toggle=False) armature_name = "Test" b_armature_data = bpy.data.armatures.new(armature_name) b_armature_data.display_type = 'STICK' # b_armature_data.show_axes = True # set axis orientation for export # b_armature_data.niftools.axis_forward = NifOp.props.axis_forward # b_armature_data.niftools.axis_up = NifOp.props.axis_up b_armature_obj = create_ob(armature_name, b_armature_data) b_armature_obj.show_in_front = True # LOD(b_armature_obj, 10) bone_names = [ matrix_util.bone_name_for_blender(n) for n in data.bone_names ] # make armature editable and create bones bpy.ops.object.mode_set(mode='EDIT', toggle=False) mats = {} bones = bone_info.jwe_bones if bone_info.jwe_bones else bone_info.pz_bones for bone_name, bone, o_parent_ind in zip(bone_names, bones, bone_info.bone_parents): if not bone_name: bone_name = "Dummy" b_edit_bone = b_armature_data.edit_bones.new(bone_name) # local space matrix, in ms2 orientation n_bind = mathutils.Quaternion((bone.rot.w, bone.rot.x, bone.rot.y, bone.rot.z)).to_matrix().to_4x4() n_bind.translation = (bone.loc.x, bone.loc.y, bone.loc.z) # link to parent try: if o_parent_ind != 255: parent_name = bone_names[o_parent_ind] b_parent_bone = b_armature_data.edit_bones[parent_name] b_edit_bone.parent = b_parent_bone # calculate ms2 armature space matrix n_bind = mats[parent_name] @ n_bind except: print( f"Bone hierarchy error for bone {bone_name} with parent index {o_parent_ind}" ) # store the ms2 armature space matrix mats[bone_name] = n_bind print() print(bone_name) print("ms2\n", n_bind) # change orientation for blender bones b_bind = matrix_util.nif_bind_to_blender_bind(n_bind) # b_bind = n_bind # print("n_bindxflip") # print(matrix_util.xflip @ n_bind) # set orientation to blender bone tail, roll = bpy.types.Bone.AxisRollFromMatrix(b_bind.to_3x3()) b_edit_bone.head = b_bind.to_translation() b_edit_bone.tail = tail + b_edit_bone.head b_edit_bone.roll = roll print("bbind\n", b_bind, "\noutput\n", matrix_util.blender_bind_to_nif_bind(b_edit_bone.matrix), "\nb edit\n", matrix_util.xflipper(b_edit_bone.matrix)) #print(n_bind - matrix_util.blender_bind_to_nif_bind(b_edit_bone.matrix)) fix_bone_lengths(b_armature_data) bpy.ops.object.mode_set(mode='OBJECT', toggle=False) # print("blender order") # for bone in b_armature_data.bones: # print(bone.name) # print("restored order") # bone_names_restored = ovl_bones(b_armature_data) # for bone in bone_names_restored: # print(bone) for i, bone_name in enumerate(bone_names): # print(i, bone_name) bone = b_armature_obj.pose.bones[bone_name] # bone = b_armature_data.bones[bone_name] bone["index"] = i return b_armature_obj
def convert_swizzle_rotation(rot): """ Converts a quaternion rotation from Blender coordinate system to glTF coordinate system. 'w' is still at first position. """ return mathutils.Quaternion((rot[0], rot[1], rot[3], -rot[2]))
def import_constraint(self, hkbody): """Imports a bone havok constraint as Blender object constraint.""" assert (isinstance(hkbody, NifFormat.bhkRigidBody)) # check for constraints if not hkbody.constraints: return # find objects if len(collision.DICT_HAVOK_OBJECTS[hkbody]) != 1: NifLog.warn( "Rigid body with no or multiple shapes, constraints skipped") return b_hkobj = collision.DICT_HAVOK_OBJECTS[hkbody][0] NifLog.info(f"Importing constraints for b_hkobj.name") # now import all constraints for hkconstraint in hkbody.constraints: # check constraint entities if not hkconstraint.num_entities == 2: NifLog.warn("Constraint with more than 2 entities, skipped") continue if not hkconstraint.entities[0] is hkbody: NifLog.warn("First constraint entity not self, skipped") continue if not hkconstraint.entities[1] in collision.DICT_HAVOK_OBJECTS: NifLog.warn("Second constraint entity not imported, skipped") continue # get constraint descriptor if isinstance(hkconstraint, NifFormat.bhkRagdollConstraint): hkdescriptor = hkconstraint.ragdoll b_hkobj.rigid_body.enabled = True elif isinstance(hkconstraint, NifFormat.bhkLimitedHingeConstraint): hkdescriptor = hkconstraint.limited_hinge b_hkobj.rigid_body.enabled = True elif isinstance(hkconstraint, NifFormat.bhkHingeConstraint): hkdescriptor = hkconstraint.hinge b_hkobj.rigid_body.enabled = True elif isinstance(hkconstraint, NifFormat.bhkMalleableConstraint): if hkconstraint.type == 7: hkdescriptor = hkconstraint.ragdoll b_hkobj.rigid_body.enabled = False elif hkconstraint.type == 2: hkdescriptor = hkconstraint.limited_hinge b_hkobj.rigid_body.enabled = False else: NifLog.warn( f"Unknown malleable type ({hkconstraint.type:s}), skipped" ) # TODO [constraint][flag] Damping parameters not yet in Blender Python API # TODO [constraint][flag] tau (force between bodies) not supported by Blender else: NifLog.warn( f"Unknown constraint type ({hkconstraint.__class__.__name__}), skipped" ) continue # todo [constraints] the following is no longer possible, fixme return # add the constraint as a rigid body joint b_constr = b_hkobj.constraints.new('RIGID_BODY_JOINT') b_constr.name = b_hkobj.name b_constr.show_pivot = True # note: rigidbodyjoint parameters (from Constraint.c) # CONSTR_RB_AXX 0.0 # CONSTR_RB_AXY 0.0 # CONSTR_RB_AXZ 0.0 # CONSTR_RB_EXTRAFZ 0.0 # CONSTR_RB_MAXLIMIT0 0.0 # CONSTR_RB_MAXLIMIT1 0.0 # CONSTR_RB_MAXLIMIT2 0.0 # CONSTR_RB_MAXLIMIT3 0.0 # CONSTR_RB_MAXLIMIT4 0.0 # CONSTR_RB_MAXLIMIT5 0.0 # CONSTR_RB_MINLIMIT0 0.0 # CONSTR_RB_MINLIMIT1 0.0 # CONSTR_RB_MINLIMIT2 0.0 # CONSTR_RB_MINLIMIT3 0.0 # CONSTR_RB_MINLIMIT4 0.0 # CONSTR_RB_MINLIMIT5 0.0 # CONSTR_RB_PIVX 0.0 # CONSTR_RB_PIVY 0.0 # CONSTR_RB_PIVZ 0.0 # CONSTR_RB_TYPE 12 # LIMIT 63 # PARSIZEY 63 # TARGET [Object "capsule.002"] # limit 3, 4, 5 correspond to angular limits along x, y and z # and are measured in degrees # pivx/y/z is the pivot point # set constraint target b_constr.target = collision.DICT_HAVOK_OBJECTS[ hkconstraint.entities[1]][0] # set rigid body type (generic) b_constr.pivot_type = 'GENERIC_6_DOF' # limiting parameters (limit everything) b_constr.use_angular_limit_x = True b_constr.use_angular_limit_y = True b_constr.use_angular_limit_z = True # get pivot point pivot = mathutils.Vector( (hkdescriptor.pivot_b.x, hkdescriptor.pivot_b.y, hkdescriptor.pivot_b.z)) * self.HAVOK_SCALE # get z- and x-axes of the constraint # (also see export_nif.py NifImport.export_constraints) if isinstance(hkdescriptor, NifFormat.RagdollDescriptor): b_constr.pivot_type = 'CONE_TWIST' # for ragdoll, take z to be the twist axis (central axis of the # cone, that is) axis_z = mathutils.Vector( (hkdescriptor.twist_a.x, hkdescriptor.twist_a.y, hkdescriptor.twist_a.z)) # for ragdoll, let x be the plane vector axis_x = mathutils.Vector( (hkdescriptor.plane_a.x, hkdescriptor.plane_a.y, hkdescriptor.plane_a.z)) # set the angle limits # (see http://niftools.sourceforge.net/wiki/Oblivion/Bhk_Objects/Ragdoll_Constraint # for a nice picture explaining this) b_constr.limit_angle_min_x = hkdescriptor.plane_min_angle b_constr.limit_angle_max_x = hkdescriptor.plane_max_angle b_constr.limit_angle_min_y = -hkdescriptor.cone_max_angle b_constr.limit_angle_max_y = hkdescriptor.cone_max_angle b_constr.limit_angle_min_z = hkdescriptor.twist_min_angle b_constr.limit_angle_max_z = hkdescriptor.twist_max_angle b_hkobj.niftools_constraint.LHMaxFriction = hkdescriptor.max_friction elif isinstance(hkdescriptor, NifFormat.LimitedHingeDescriptor): # for hinge, y is the vector on the plane of rotation defining # the zero angle axis_y = mathutils.Vector((hkdescriptor.perp_2_axle_in_a_1.x, hkdescriptor.perp_2_axle_in_a_1.y, hkdescriptor.perp_2_axle_in_a_1.z)) # for hinge, take x to be the the axis of rotation # (this corresponds with Blender's convention for hinges) axis_x = mathutils.Vector( (hkdescriptor.axle_a.x, hkdescriptor.axle_a.y, hkdescriptor.axle_a.z)) # for hinge, z is the vector on the plane of rotation defining # the positive direction of rotation axis_z = mathutils.Vector((hkdescriptor.perp_2_axle_in_a_2.x, hkdescriptor.perp_2_axle_in_a_2.y, hkdescriptor.perp_2_axle_in_a_2.z)) # they should form a orthogonal basis if (mathutils.Vector.cross(axis_x, axis_y) - axis_z).length > 0.01: # either not orthogonal, or negative orientation if (mathutils.Vector.cross(-axis_x, axis_y) - axis_z).length > 0.01: NifLog.warn( f"Axes are not orthogonal in {hkdescriptor.__class__.__name__}; Arbitrary orientation has been chosen" ) axis_z = mathutils.Vector.cross(axis_x, axis_y) else: # fix orientation NifLog.warn( f"X axis flipped in {hkdescriptor.__class__.__name__} to fix orientation" ) axis_x = -axis_x # getting properties with no blender constraint equivalent and setting as obj properties b_constr.limit_angle_max_x = hkdescriptor.max_angle b_constr.limit_angle_min_x = hkdescriptor.min_angle b_hkobj.niftools_constraint.LHMaxFriction = hkdescriptor.max_friction if hasattr(hkconstraint, "tau"): b_hkobj.niftools_constraint.tau = hkconstraint.tau b_hkobj.niftools_constraint.damping = hkconstraint.damping elif isinstance(hkdescriptor, NifFormat.HingeDescriptor): # for hinge, y is the vector on the plane of rotation defining # the zero angle axis_y = mathutils.Vector((hkdescriptor.perp_2_axle_in_a_1.x, hkdescriptor.perp_2_axle_in_a_1.y, hkdescriptor.perp_2_axle_in_a_1.z)) # for hinge, z is the vector on the plane of rotation defining # the positive direction of rotation axis_z = mathutils.Vector((hkdescriptor.perp_2_axle_in_a_2.x, hkdescriptor.perp_2_axle_in_a_2.y, hkdescriptor.perp_2_axle_in_a_2.z)) # take x to be the the axis of rotation # (this corresponds with Blender's convention for hinges) axis_x = mathutils.Vector.cross(axis_y, axis_z) b_hkobj.niftools_constraint.LHMaxFriction = hkdescriptor.max_friction else: raise ValueError("Unknown descriptor {0}".format( hkdescriptor.__class__.__name__)) # transform pivot point and constraint matrix into object # coordinates # (also see export_nif.py NifImport.export_constraints) # the pivot point v is in hkbody coordinates # however blender expects it in object coordinates, v' # v * R * B = v' * O * T * B' # with R = rigid body transform (usually unit tf) # B = nif bone matrix # O = blender object transform # T = bone tail matrix (translation in Y direction) # B' = blender bone matrix # so we need to cancel out the object transformation by # v' = v * R * B * B'^{-1} * T^{-1} * O^{-1} # the local rotation L at the pivot point must be such that # (axis_z + v) * R * B = ([0 0 1] * L + v') * O * T * B' # so (taking the rotation parts of all matrices!!!) # [0 0 1] * L = axis_z * R * B * B'^{-1} * T^{-1} * O^{-1} # and similarly # [1 0 0] * L = axis_x * R * B * B'^{-1} * T^{-1} * O^{-1} # hence these give us the first and last row of L # which is exactly enough to provide the euler angles # multiply with rigid body transform if isinstance(hkbody, NifFormat.bhkRigidBodyT): # set rotation transform = mathutils.Quaternion( (hkbody.rotation.w, hkbody.rotation.x, hkbody.rotation.y, hkbody.rotation.z)).to_matrix() transform.resize_4x4() # set translation transform[0][3] = hkbody.translation.x * self.HAVOK_SCALE transform[1][3] = hkbody.translation.y * self.HAVOK_SCALE transform[2][3] = hkbody.translation.z * self.HAVOK_SCALE # apply transform # pivot = pivot * transform transform = transform.to_3x3() axis_z = axis_z * transform axis_x = axis_x * transform # TODO [armature] update this to use the new bone system # next, cancel out bone matrix correction # note that B' = X * B with X = self.nif_import.dict_bones_extra_matrix[B] # so multiply with the inverse of X # for niBone in self.nif_import.dict_bones_extra_matrix: # if niBone.collision_object \ # and niBone.collision_object.body is hkbody: # transform = mathutils.Matrix( # self.nif_import.dict_bones_extra_matrix[niBone]) # transform.invert() # pivot = pivot * transform # transform = transform.to_3x3() # axis_z = axis_z * transform # axis_x = axis_x * transform # break # # cancel out bone tail translation # if b_hkobj.parent_bone: # pivot[1] -= b_hkobj.parent.data.bones[ # b_hkobj.parent_bone].length # cancel out object transform transform = mathutils.Matrix(b_hkobj.matrix_local) transform.invert() # pivot = pivot * transform transform = transform.to_3x3() axis_z = axis_z * transform axis_x = axis_x * transform # set pivot point b_constr.pivot_x = pivot[0] b_constr.pivot_y = pivot[1] b_constr.pivot_z = pivot[2] # set euler angles constr_matrix = mathutils.Matrix( (axis_x, mathutils.Vector.cross(axis_z, axis_x), axis_z)) constr_euler = constr_matrix.to_euler() b_constr.axis_x = constr_euler.x b_constr.axis_y = constr_euler.y b_constr.axis_z = constr_euler.z # DEBUG assert ((axis_x - mathutils.Vector( (1, 0, 0)) * constr_matrix).length < 0.0001) assert ((axis_z - mathutils.Vector( (0, 0, 1)) * constr_matrix).length < 0.0001) # the generic rigid body type is very buggy... so for simulation purposes let's transform it into ball and hinge if isinstance(hkdescriptor, NifFormat.RagdollDescriptor): # cone_twist b_constr.pivot_type = 'CONE_TWIST' elif isinstance( hkdescriptor, (NifFormat.LimitedHingeDescriptor, NifFormat.HingeDescriptor)): # (limited) hinge b_constr.pivot_type = 'HINGE' else: raise ValueError("Unknown descriptor {0}".format( hkdescriptor.__class__.__name__))
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 Args: position(list): The position to include in the dictionary. rotation(list): The rotation to include into the dictionary, either euler angle or quaternion. pivot(list, optional): The pivot point. (Default value = (0) 0: 0): Returns: : 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 animate_rotation_quaternion(export_settings, rotation_quaternion, interpolation, node_type, node_name, action_name, matrix_correction, matrix_basis): """ Calculates/gathers the key value pairs for quaternion transformations. """ joint_cache = export_settings['gltf_joint_cache'][action_name] if not joint_cache.get(node_name): joint_cache[node_name] = {} keys = animate_gather_keys(export_settings, rotation_quaternion, interpolation) times = animate_convert_keys(keys) result = {} result_in_tangent = {} result_out_tangent = {} keyframe_index = 0 for timeIndex, time in enumerate(times): rotation = [1.0, 0.0, 0.0, 0.0] in_tangent = [1.0, 0.0, 0.0, 0.0] out_tangent = [1.0, 0.0, 0.0, 0.0] if node_type == 'JOINT': if joint_cache[node_name].get(keys[keyframe_index]): tmp_location, rotation, tmp_scale = joint_cache[node_name][keys[keyframe_index]] else: bpy.context.scene.frame_set(keys[keyframe_index]) matrix = matrix_correction * matrix_basis tmp_location, rotation, tmp_scale = matrix.decompose() joint_cache[node_name][keys[keyframe_index]] = [tmp_location, rotation, tmp_scale] else: channel_index = 0 for blender_fcurve in rotation_quaternion: if blender_fcurve is not None: if interpolation == 'CUBICSPLINE': blender_key_frame = blender_fcurve.keyframe_points[keyframe_index] rotation[channel_index] = blender_key_frame.co[1] if timeIndex == 0: in_tangent_value = 0.0 else: in_tangent_value = 3.0 * (blender_key_frame.co[1] - blender_key_frame.handle_left[1]) / (time - times[timeIndex - 1]) if timeIndex == len(times) - 1: out_tangent_value = 0.0 else: out_tangent_value = 3.0 * (blender_key_frame.handle_right[1] - blender_key_frame.co[1]) / (times[timeIndex + 1] - time) in_tangent[channel_index] = in_tangent_value out_tangent[channel_index] = out_tangent_value else: value = blender_fcurve.evaluate(keys[keyframe_index]) rotation[channel_index] = value channel_index += 1 rotation = mathutils.Quaternion((rotation[0], rotation[1], rotation[2], rotation[3])) in_tangent = convert_swizzle_rotation(in_tangent, export_settings) out_tangent = convert_swizzle_rotation(out_tangent, export_settings) # handle parent inverse matrix = rotation.to_matrix().to_4x4() matrix = matrix_correction * matrix rotation = matrix.to_quaternion() # Bring back to internal Quaternion notation. rotation = convert_swizzle_rotation([rotation[0], rotation[1], rotation[2], rotation[3]], export_settings) # Bring to glTF Quaternion notation. rotation = [rotation[1], rotation[2], rotation[3], rotation[0]] in_tangent = [in_tangent[1], in_tangent[2], in_tangent[3], in_tangent[0]] out_tangent = [out_tangent[1], out_tangent[2], out_tangent[3], out_tangent[0]] result[time] = rotation result_in_tangent[time] = in_tangent result_out_tangent[time] = out_tangent keyframe_index += 1 return result, result_in_tangent, result_out_tangent
def draw_limits(cls): bgl.glEnable(bgl.GL_BLEND) bgl.glDisable(bgl.GL_DEPTH_TEST) cam = bpy.context.active_object if (cam is not None and cam.type == "CAMERA" and cam.data.b4w_show_limits_in_viewport and cam_has_limits(cam)): ms = cam.data.b4w_move_style if ms == "TARGET": # get orientation view_dir = cam.data.b4w_target - cam.location orientation = get_camera_orientation(cam, view_dir) # get limits hor_rot_limits = get_target_hor_rot_limits(cam, orientation) vert_rot_limits = get_target_vert_rot_limits(cam, orientation) dist_limits = get_target_distance_limits(cam) # draw limits rotation_mat = mathutils.Matrix.Rotation(-math.pi / 2, 3, 'Z') draw_target_limits(cam, hor_rot_limits, dist_limits, rotation_mat, BLUE_COLOR, cam.data.b4w_use_horizontal_clamping) rotation_mat = mathutils.Matrix.Rotation(-math.pi / 2, 3, 'X') phi_middle = ( wrap_2pi(hor_rot_limits[1] - hor_rot_limits[0]) / 2 + hor_rot_limits[0] - math.pi / 2) rotation_mat.rotate(mathutils.Quaternion((0, 0, 1), phi_middle)) draw_target_limits(cam, vert_rot_limits, dist_limits, rotation_mat, RED_COLOR, cam.data.b4w_use_vertical_clamping) bgl.glColor4f(ORANGE_COLOR[0], ORANGE_COLOR[1], ORANGE_COLOR[2], 1) draw_point(cam.data.b4w_target, NORMAL_POINT_SIZE) elif ms == "EYE": # get orientation view_dir = mathutils.Vector((0, 0, -1)) view_dir.rotate(cam.matrix_world.to_quaternion()) orientation = get_camera_orientation(cam, view_dir) # get limits hor_rot_limits = get_eye_hor_rot_limits(cam, orientation) vert_rot_limits = get_eye_vert_rot_limits(cam, orientation) # draw limits rotation_mat = mathutils.Matrix.Rotation(math.pi / 2, 3, 'Z') draw_eye_limits(cam, hor_rot_limits, rotation_mat, BLUE_COLOR, cam.data.b4w_use_horizontal_clamping) rotation_mat = mathutils.Matrix.Rotation(math.pi / 2, 3, 'X') phi_middle = ( wrap_2pi(hor_rot_limits[1] - hor_rot_limits[0]) / 2 + hor_rot_limits[0] + math.pi / 2) rotation_mat.rotate(mathutils.Quaternion((0, 0, 1), phi_middle)) draw_eye_limits(cam, vert_rot_limits, rotation_mat, RED_COLOR, cam.data.b4w_use_vertical_clamping) elif ms == "HOVER": # get orientation view_dir = mathutils.Vector((0, 0, -1)) view_dir.rotate(cam.matrix_world.to_quaternion()) orientation = get_camera_orientation(cam, view_dir) # get limits res = mathutils.geometry.intersect_line_plane( cam.location, cam.location + view_dir, mathutils.Vector((0, 0, cam.data.b4w_hover_zero_level)), mathutils.Vector((0, 0, 1))) cross_point = (mathutils.Vector( (cam.location.x, cam.location.y, cam.data.b4w_hover_zero_level)) if res is None else res) rad_vec = cam.location - cross_point hor_trans_limits = get_hover_hor_trans_limits(cam) vert_trans_limits = get_hover_vert_trans_limits(cam) zoom_limits = get_hover_zoom_limits(cam, orientation["theta"], rad_vec.length) hover_pivot = calc_hover_pivot(cross_point, hor_trans_limits, vert_trans_limits) rotation_mat = mathutils.Matrix.Rotation( orientation["phi"] - math.pi / 2, 3, 'Z') draw_hover_limits(cam, hover_pivot, hor_trans_limits, vert_trans_limits, zoom_limits, rotation_mat, orientation["theta"], rad_vec) bgl.glColor4f(ORANGE_COLOR[0], ORANGE_COLOR[1], ORANGE_COLOR[2], 1) draw_point(hover_pivot, NORMAL_POINT_SIZE)
def convert_swizzle_rotation(rot): return mathutils.Quaternion((rot[0], rot[1], rot[3], -rot[2]))
import mathutils # zero length vector vec = mathutils.Vector((0.0, 0.0, 1.0)) # unit length vector vec_a = vec.normalized() vec_b = mathutils.Vector((0.0, 1.0, 2.0)) vec2d = mathutils.Vector((1.0, 2.0)) vec3d = mathutils.Vector((1.0, 0.0, 0.0)) vec4d = vec_a.to_4d() # other mathutuls types quat = mathutils.Quaternion() matrix = mathutils.Matrix() # Comparison operators can be done on Vector classes: # (In)equality operators == and != test component values, e.g. 1,2,3 != 3,2,1 vec_a == vec_b vec_a != vec_b # Ordering operators >, >=, > and <= test vector length. vec_a > vec_b vec_a >= vec_b vec_a < vec_b vec_a <= vec_b # Math can be performed on Vector classes
def applyANM(header, boneList): import bpy # http://blender.stackexchange.com/a/8392 # http://blender.stackexchange.com/a/31709 try: bpy.ops.objects['lolArmature'].mode_set(mode='EDIT') print("TEST") except: pass scene = bpy.context.scene ob = bpy.context.scene.objects['lolArmature'] editBones = ob.data.edit_bones poseBones = ob.pose.bones parentOffset = {} parentOffRot = {} for editBone in editBones: if editBone.parent != None: # get offset from parent bone in bone's object space parentOffset[editBone.name] = mathutils.Vector( editBone.head - editBone.parent.head) @ editBone.matrix # get bone rotation relative to the parent bone parentOffRot[editBone.name] = editBone.parent.matrix.to_quaternion( ).rotation_difference(editBone.matrix.to_quaternion()) else: parentOffset[editBone.name] = mathutils.Vector( editBone.head) @ editBone.matrix parentOffRot[editBone.name] = mathutils.Quaternion( [1.0, 0.0, 0.0, 0.0]).rotation_difference(editBone.matrix.to_quaternion()) if header.version in [1, 3, 4, 5]: scene.render.fps = header.playbackFPS scene.frame_end = header.numFrames - 1 scene.frame_start = 0 for f in range(header.numFrames): print("frame %s processing" % f) scene.frame_set(f) for b in boneList: n = b.name boneRotation = b.orientations[f] bonePosition = b.positions[f] poseBone = poseBones[n] editBone = editBones[n] if poseBone.parent: # bonePosition is in parent bone's object space so convert to absolute position bonePosition = bonePosition @ poseBone.parent.matrix.inverted( ) # convert absolute position to position in bone's object space bonePosition = bonePosition @ editBone.matrix poseBone.rotation_quaternion = parentOffRot[n].inverted( ) @ boneRotation poseBone.location = bonePosition - parentOffset[n] for dp in ["rotation_quaternion", "location"]: poseBone.keyframe_insert(data_path=dp, frame=f) # ob.keyframe_insert(data_path="pose") elif header.version == 4: raise NotImplementedError("version 4 not supported yet") else: raise ValueError("Version not supported", header.version)
def PecSimulation(self, nFrame, pFS, startFrame): # print("Pecs") if self.sPecFinTopL == None or self.sPecFinTopR == None: return #Update State and main angle self.sPecState = self.sPecState + 360.0 / pFS.pMaxPecFreq xPecAngle = math.sin(math.radians(self.sPecState)) * math.radians( pFS.pMaxPecAngle) yPecAngle = math.sin( math.radians(self.sPecState + 90.0)) * math.radians( pFS.pMaxPecAngle * 2) #Rest Period Calculations if nFrame >= self.sRestartFrame: self.sRestAmount = max(0.0, self.sRestAmount - pFS.pPecTransition) if self.sRestAmount < 0.1: self.sRestFrame = nFrame + pFS.pPecDuration self.sRestartFrame = self.sRestFrame + pFS.pPecDuty * pFS.pPecDuration if (nFrame >= self.sRestFrame and nFrame < self.sRestartFrame and self.sRestAmount < 1.0): self.sRestAmount = min(1.0, self.sRestAmount + pFS.pPecTransition) # print("RestAmount: ", self.sRestAmount, self.sRestFrame, self.sRestartFrame) #Add the same side fin wobble to the pec fins to stop them looking boring when not flapping SideFinRot = math.radians( math.sin(math.radians(self.sState + pFS.pSideFinPhase)) * pFS.pMaxSideFinAngle) #Slerp between oscillating angle and rest angle depending on hover status and reset periods # xRestAmount = 1 means no flapping due to either resting or not hovering xRestAmount = (1.0 - (1.0 - self.sRestAmount) * self.sHoverMode) # print("xRestAmaount: ", xRestAmount) # print("RestAmount: ", self.sRestAmount, self.sRestFrame, self.sRestartFrame) # print("HoverMode: ", self.sHoverMode) yAng = mathutils.Quaternion((0.0, 1.0, 0.0), yPecAngle) # yAng = mathutils.Quaternion((0.0, 1.0, 0.0), 0) xAng = yAng * mathutils.Quaternion((1.0, 0.0, 0.0), -xPecAngle) xAng = xAng.slerp( mathutils.Quaternion((1.0, 0.0, 0.0), math.radians(pFS.pPecOffset)), xRestAmount) self.sPecFinPalmL.rotation_quaternion = xAng * mathutils.Quaternion( (1.0, 0.0, 0.0), SideFinRot) self.sPecFinPalmL.keyframe_insert(data_path='rotation_quaternion', frame=(nFrame)) # print("Palm Animate: ", nFrame) #Tip deflection based on phase offset xMaxPecScale = pFS.pMaxPecAngle * (1.0 / pFS.pPecStiffness) * 0.2 / 30.0 self.sPec_scale = 1.0 + math.sin( math.radians(self.sPecState - pFS.pPecPhase)) * xMaxPecScale * (1.0 - xRestAmount) self.sPecFinTopL.scale[1] = self.sPec_scale self.sPecFinBottomL.scale[1] = 1 - ( 1 - self.sPec_scale) * pFS.pPecStubRatio self.sPecFinTopL.keyframe_insert(data_path='scale', frame=(nFrame)) self.sPecFinBottomL.keyframe_insert(data_path='scale', frame=(nFrame)) #copy to the right fin #If fins are opposing if not pFS.pPecSynch: yAng = mathutils.Quaternion((0.0, 1.0, 0.0), yPecAngle) xAng = yAng * mathutils.Quaternion((1.0, 0.0, 0.0), xPecAngle) xAng = xAng.slerp( mathutils.Quaternion((1.0, 0.0, 0.0), math.radians(pFS.pPecOffset)), xRestAmount) self.sPecFinPalmR.rotation_quaternion = xAng * mathutils.Quaternion( (1.0, 0.0, 0.0), SideFinRot) self.sPecFinTopR.scale[1] = 1 / self.sPec_scale self.sPecFinBottomR.scale[1] = 1 - ( 1 - 1 / self.sPec_scale) * pFS.pPecStubRatio else: yAng = mathutils.Quaternion((0.0, 1.0, 0.0), -yPecAngle) xAng = yAng * mathutils.Quaternion((1.0, 0.0, 0.0), -xPecAngle) xAng = xAng.slerp( mathutils.Quaternion((1.0, 0.0, 0.0), math.radians(pFS.pPecOffset)), xRestAmount) self.sPecFinPalmR.rotation_quaternion = xAng * mathutils.Quaternion( (1.0, 0.0, 0.0), SideFinRot) self.sPecFinTopR.scale[1] = self.sPec_scale self.sPecFinBottomR.scale[1] = 1 - ( 1 - self.sPec_scale) * pFS.pPecStubRatio self.sPecFinPalmR.keyframe_insert(data_path='rotation_quaternion', frame=(nFrame)) self.sPecFinTopR.keyframe_insert(data_path='scale', frame=(nFrame)) self.sPecFinBottomR.keyframe_insert(data_path='scale', frame=(nFrame))
def exportANM(skelObj, output_filepath, input_filepath, OVERWRITE_FILE_VERSION, VERSION): import bpy (import_header, import_bonelist) = importANM(input_filepath) bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.select_all(action='DESELECT') skelObj.select = True bpy.ops.object.mode_set(mode='EDIT') scene = bpy.context.scene objBones = skelObj.data.bones pb = skelObj.pose.bones numBones = len(objBones) header = import_header if OVERWRITE_FILE_VERSION: header.version = VERSION #Apply changes to the header based on the new version here if header.version in [0, 2, 3]: header.numBones = numBones header.numFrames = scene.frame_end - scene.frame_start + 1 boneList = [] parentOffset = {} parentOffRot = {} for i, b in enumerate(objBones): boneList.append(anmBone()) boneList[-1].name = b.name #most bones have a value of zero / bones without parent have a value of 2 / if b.parent != None: boneList[-1].unknown = 0 parentOffset[b.name] = mathutils.Vector( b.matrix_local.decompose()[0] - b.parent.matrix_local.decompose()[0]) @ b.matrix_local parentOffRot[b.name] = objBones[ b.parent.name].matrix_local.to_quaternion( ).rotation_difference(b.matrix_local.to_quaternion()) else: boneList[-1].unknown = 2 parentOffset[b.name] = mathutils.Vector( b.head) @ b.matrix_local parentOffRot[b.name] = mathutils.Quaternion( [1.0, 0.0, 0.0, 0.0]) for f in range(scene.frame_start, scene.frame_end + 1): bpy.context.scene.frame_set(f) for b in boneList: n = b.name objBone = objBones[n] poseBone = pb[n] bonePos = poseBone.location bonePos = bonePos + parentOffset[n] boneOrient = parentOffRot[n] @ poseBone.rotation_quaternion if objBone.parent != None: bonePos = bonePos @ objBone.matrix_local.inverted() bonePos = bonePos @ poseBone.parent.matrix bonePos[2] = -bonePos[2] b.add_frame(bonePos, boneOrient) anmFid = open(output_filepath, 'wb') header.toFile(anmFid) for b in boneList: b.toFile(anmFid, import_header.version) anmFid.close() else: raise ValueError("Version %d not supported!" % header.version)
def rotateQuaternion(cubelets, axis, angle): vecByAxis = {'X': (1, 0, 0), 'Y': (0, 1, 0), 'Z': (0, 0, 1)} qDelta = mathutils.Quaternion(vecByAxis[axis], angle) for cubelet in cubelets: cubelet.rotation_quaternion = qDelta @ cubelet.rotation_quaternion
def execute(self, context): common.preferences().custom_normal_blend = self.custom_normal_blend bpy.ops.object.mode_set(mode='OBJECT') ob = context.active_object me = ob.data is_shaped = bool(me.shape_keys) pre_selected_objects = context.selected_objects[:] pre_mode = ob.mode if is_shaped: pre_relative_keys = [ s.relative_key.name for s in me.shape_keys.key_blocks ] pre_active_shape_key_index = ob.active_shape_key_index shape_names = [s.name for s in me.shape_keys.key_blocks] shape_deforms = [] for shape in me.shape_keys.key_blocks: shape_deforms.append( [shape.data[v.index].co.copy() for v in me.vertices]) ob.active_shape_key_index = len(me.shape_keys.key_blocks) - 1 for i in me.shape_keys.key_blocks[:]: ob.shape_key_remove(ob.active_shape_key) new_shape_deforms = [] for shape_index, deforms in enumerate(shape_deforms): temp_ob = ob.copy() temp_me = me.copy() temp_ob.data = temp_me context.scene.objects.link(temp_ob) for vert in temp_me.vertices: vert.co = deforms[vert.index].copy() override = context.copy() override['object'] = temp_ob for index, mod in enumerate(temp_ob.modifiers): if self.is_applies[index]: try: bpy.ops.object.modifier_apply(override, modifier=mod.name) except: ob.modifiers.remove(mod) new_shape_deforms.append( [v.co.copy() for v in temp_me.vertices]) common.remove_data(temp_ob) common.remove_data(temp_me) if ob.active_shape_key_index != 0: ob.active_shape_key_index = 0 me.update() copy_modifiers = ob.modifiers[:] for index, mod in enumerate(copy_modifiers): if self.is_applies[index] and mod.type != 'ARMATURE': if mod.type == 'MIRROR': for vg in ob.vertex_groups[:]: replace_list = ((r'\.L$', ".R"), (r'\.R$', ".L"), (r'\.l$', ".r"), (r'\.r$', ".l"), (r'_L$', "_R"), (r'_R$', "_L"), (r'_l$', "_r"), (r'_r$', "_l")) for before, after in replace_list: mirrored_name = re.sub(before, after, vg.name) if mirrored_name not in ob.vertex_groups: ob.vertex_groups.new(mirrored_name) try: bpy.ops.object.modifier_apply(modifier=mod.name) except: ob.modifiers.remove(mod) arm_ob = None for mod in ob.modifiers: if mod.type == "ARMATURE": arm_ob = mod.object if arm_ob: bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='OBJECT') arm = arm_ob.data arm_pose = arm_ob.pose pose_quats = {} for bone in arm.bones: pose_bone = arm_pose.bones[bone.name] bone_quat = bone.matrix_local.to_quaternion() pose_quat = pose_bone.matrix.to_quaternion() result_quat = pose_quat * bone_quat.inverted() pose_quats[bone.name] = result_quat.copy() custom_normals = [] for loop in me.loops: vert = me.vertices[loop.vertex_index] no = vert.normal.copy() total_weight = 0.0 for vge in vert.groups: vg = ob.vertex_groups[vge.group] try: pose_quats[vg.name] except KeyError: continue total_weight += vge.weight total_quat = mathutils.Quaternion() for vge in vert.groups: vg = ob.vertex_groups[vge.group] try: total_quat = total_quat.slerp( pose_quats[vg.name], vge.weight / total_weight) except KeyError: pass no.rotate(total_quat) custom_normals.append(no) for index, mod in enumerate(copy_modifiers): if self.is_applies[index] and mod.type == 'ARMATURE': try: bpy.ops.object.modifier_apply(modifier=mod.name) except: ob.modifiers.remove(mod) context.scene.objects.active = ob if is_shaped: for deforms in new_shape_deforms: if len(me.vertices) != len(deforms): self.report( type={'ERROR'}, message= "ミラー等が原因で頂点数が変わっているためシェイプキーを格納できません、中止するのでCtrl+Z等で元に戻し修正してください。" ) return {'CANCELLED'} for shape_index, deforms in enumerate(new_shape_deforms): bpy.ops.object.shape_key_add(from_mix=False) shape = ob.active_shape_key shape.name = shape_names[shape_index] for vert in me.vertices: shape.data[vert.index].co = deforms[vert.index].copy() for shape_index, shape in enumerate(me.shape_keys.key_blocks): shape.relative_key = me.shape_keys.key_blocks[ pre_relative_keys[shape_index]] ob.active_shape_key_index = pre_active_shape_key_index for temp_ob in pre_selected_objects: temp_ob.select = True bpy.ops.object.mode_set(mode=pre_mode) if arm_ob: for i, loop in enumerate(me.loops): vert = me.vertices[loop.vertex_index] no = vert.normal.copy() try: custom_rot = mathutils.Vector( (0.0, 0.0, 1.0)).rotation_difference(custom_normals[i]) except: continue original_rot = mathutils.Vector( (0.0, 0.0, 1.0)).rotation_difference(no) output_rot = original_rot.slerp(custom_rot, self.custom_normal_blend) output_no = mathutils.Vector((0.0, 0.0, 1.0)) output_no.rotate(output_rot) custom_normals[i] = output_no me.use_auto_smooth = True me.normals_split_custom_set(custom_normals) return {'FINISHED'}