forked from bartvdbraak/blender
* Removing mocap GSoC (is an addon already).
* Fixing ffmpeg-0.8 errors. * Fixing Ketsji paths. * Removing DoSound from BGE. * Fixing audio scene update to use only current scene objects.
This commit is contained in:
parent
8e12b7b054
commit
b4b046995b
@ -706,8 +706,8 @@ elseif(WIN32)
|
||||
|
||||
if(WITH_CODEC_FFMPEG)
|
||||
set(FFMPEG_INCLUDE_DIRS
|
||||
${LIBDIR}/ffmpeg-0.8/include
|
||||
${LIBDIR}/ffmpeg-0.8/include/msvc
|
||||
${LIBDIR}/ffmpeg/include
|
||||
${LIBDIR}/ffmpeg/include/msvc
|
||||
)
|
||||
set(FFMPEG_LIBRARIES
|
||||
${LIBDIR}/ffmpeg/lib/avcodec-53.lib
|
||||
@ -839,7 +839,7 @@ elseif(WIN32)
|
||||
endif()
|
||||
|
||||
if(WITH_CODEC_FFMPEG)
|
||||
set(FFMPEG ${LIBDIR}/ffmpeg-0.8)
|
||||
set(FFMPEG ${LIBDIR}/ffmpeg)
|
||||
set(FFMPEG_INCLUDE_DIRS ${FFMPEG}/include ${FFMPEG}/include)
|
||||
set(FFMPEG_LIBRARIES avcodec-53 avformat-53 avdevice-53 avutil-51 swscale-2)
|
||||
set(FFMPEG_LIBPATH ${FFMPEG}/lib)
|
||||
@ -978,7 +978,7 @@ elseif(APPLE)
|
||||
endif()
|
||||
|
||||
if(WITH_CODEC_FFMPEG)
|
||||
set(FFMPEG ${LIBDIR}/ffmpeg-0.8)
|
||||
set(FFMPEG ${LIBDIR}/ffmpeg)
|
||||
set(FFMPEG_INCLUDE_DIRS ${FFMPEG}/include)
|
||||
set(FFMPEG_LIBRARIES avcodec avdevice avformat avutil mp3lame swscale x264 xvidcore theora theoradec theoraenc vorbis vorbisenc vorbisfile ogg)
|
||||
set(FFMPEG_LIBPATH ${FFMPEG}/lib)
|
||||
|
@ -98,7 +98,7 @@ else:
|
||||
|
||||
# enable ffmpeg support
|
||||
WITH_BF_FFMPEG = True # -DWITH_FFMPEG
|
||||
BF_FFMPEG = LIBDIR + '/ffmpeg-0.8'
|
||||
BF_FFMPEG = LIBDIR + '/ffmpeg'
|
||||
BF_FFMPEG_INC = "${BF_FFMPEG}/include"
|
||||
BF_FFMPEG_LIBPATH='${BF_FFMPEG}/lib'
|
||||
BF_FFMPEG_LIB = 'avcodec avdevice avformat avutil mp3lame swscale x264 xvidcore theora theoradec theoraenc vorbis vorbisenc vorbisfile ogg bz2'
|
||||
|
@ -3,7 +3,7 @@ LIBDIR = '${LCGDIR}'
|
||||
|
||||
# enable ffmpeg support
|
||||
WITH_BF_FFMPEG = True # -DWITH_FFMPEG
|
||||
BF_FFMPEG = LIBDIR +'/ffmpeg-0.8'
|
||||
BF_FFMPEG = LIBDIR +'/ffmpeg'
|
||||
BF_FFMPEG_INC = '${BF_FFMPEG}/include ${BF_FFMPEG}/include/msvc'
|
||||
BF_FFMPEG_LIBPATH='${BF_FFMPEG}/lib'
|
||||
BF_FFMPEG_LIB = 'avformat-53.lib avcodec-53.lib avdevice-53.lib avutil-51.lib swscale-2.lib'
|
||||
|
@ -3,7 +3,7 @@ LIBDIR = '${LCGDIR}'
|
||||
|
||||
# enable ffmpeg support
|
||||
WITH_BF_FFMPEG = True # -DWITH_FFMPEG
|
||||
BF_FFMPEG = LIBDIR +'/ffmpeg-0.8'
|
||||
BF_FFMPEG = LIBDIR +'/ffmpeg'
|
||||
BF_FFMPEG_INC = '${BF_FFMPEG}/include ${BF_FFMPEG}/include/msvc '
|
||||
BF_FFMPEG_LIBPATH='${BF_FFMPEG}/lib'
|
||||
BF_FFMPEG_LIB = 'avformat-53.lib avcodec-53.lib avdevice-53.lib avutil-51.lib swscale-2.lib'
|
||||
|
@ -1,434 +0,0 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import bpy
|
||||
from mathutils import *
|
||||
from bl_operators import nla
|
||||
from retarget import hasIKConstraint
|
||||
|
||||
### Utility Functions
|
||||
|
||||
|
||||
def getConsObj(bone):
|
||||
#utility function - returns related IK target if bone has IK
|
||||
ik = [constraint for constraint in bone.constraints if constraint.type == "IK"]
|
||||
if ik:
|
||||
ik = ik[0]
|
||||
cons_obj = ik.target
|
||||
if ik.subtarget:
|
||||
cons_obj = ik.target.pose.bones[ik.subtarget]
|
||||
else:
|
||||
cons_obj = bone
|
||||
return cons_obj
|
||||
|
||||
|
||||
def consObjToBone(cons_obj):
|
||||
#Utility function - returns related bone from ik object
|
||||
if cons_obj.name[-3:] == "Org":
|
||||
return cons_obj.name[:-3]
|
||||
else:
|
||||
return cons_obj.name
|
||||
|
||||
### And and Remove Constraints (called from operators)
|
||||
|
||||
|
||||
def addNewConstraint(m_constraint, cons_obj):
|
||||
#Decide the correct Blender constraint according to the Mocap constraint type
|
||||
if m_constraint.type == "point" or m_constraint.type == "freeze":
|
||||
c_type = "LIMIT_LOCATION"
|
||||
if m_constraint.type == "distance":
|
||||
c_type = "LIMIT_DISTANCE"
|
||||
if m_constraint.type == "floor":
|
||||
c_type = "LIMIT_LOCATION"
|
||||
#create and store the new constraint within m_constraint
|
||||
real_constraint = cons_obj.constraints.new(c_type)
|
||||
real_constraint.name = "Auto fixes " + str(len(cons_obj.constraints))
|
||||
m_constraint.real_constraint_bone = consObjToBone(cons_obj)
|
||||
m_constraint.real_constraint = real_constraint.name
|
||||
#set the rest of the constraint properties
|
||||
setConstraint(m_constraint, bpy.context)
|
||||
|
||||
|
||||
def removeConstraint(m_constraint, cons_obj):
|
||||
#remove the influence fcurve and Blender constraint
|
||||
oldConstraint = cons_obj.constraints[m_constraint.real_constraint]
|
||||
removeFcurves(cons_obj, bpy.context.active_object, oldConstraint, m_constraint)
|
||||
cons_obj.constraints.remove(oldConstraint)
|
||||
|
||||
### Update functions. There are 3: UpdateType/Bone
|
||||
### update framing (deals with changes in the desired frame range)
|
||||
### And setConstraint which deals with the rest
|
||||
|
||||
|
||||
def updateConstraintBoneType(m_constraint, context):
|
||||
#If the constraint exists, we need to remove it
|
||||
#from the old bone
|
||||
obj = context.active_object
|
||||
bones = obj.pose.bones
|
||||
if m_constraint.real_constraint:
|
||||
bone = bones[m_constraint.real_constraint_bone]
|
||||
cons_obj = getConsObj(bone)
|
||||
removeConstraint(m_constraint, cons_obj)
|
||||
#Regardless, after that we create a new constraint
|
||||
if m_constraint.constrained_bone:
|
||||
bone = bones[m_constraint.constrained_bone]
|
||||
cons_obj = getConsObj(bone)
|
||||
addNewConstraint(m_constraint, cons_obj)
|
||||
|
||||
|
||||
def setConstraintFraming(m_constraint, context):
|
||||
obj = context.active_object
|
||||
bones = obj.pose.bones
|
||||
bone = bones[m_constraint.constrained_bone]
|
||||
cons_obj = getConsObj(bone)
|
||||
real_constraint = cons_obj.constraints[m_constraint.real_constraint]
|
||||
#remove the old keyframes
|
||||
removeFcurves(cons_obj, obj, real_constraint, m_constraint)
|
||||
#set the new ones according to the m_constraint properties
|
||||
s, e = m_constraint.s_frame, m_constraint.e_frame
|
||||
s_in, s_out = m_constraint.smooth_in, m_constraint.smooth_out
|
||||
real_constraint.influence = 1
|
||||
real_constraint.keyframe_insert(data_path="influence", frame=s)
|
||||
real_constraint.keyframe_insert(data_path="influence", frame=e)
|
||||
real_constraint.influence = 0
|
||||
real_constraint.keyframe_insert(data_path="influence", frame=s - s_in)
|
||||
real_constraint.keyframe_insert(data_path="influence", frame=e + s_out)
|
||||
|
||||
|
||||
def removeFcurves(cons_obj, obj, real_constraint, m_constraint):
|
||||
#Determine if the constrained object is a bone or an empty
|
||||
if isinstance(cons_obj, bpy.types.PoseBone):
|
||||
fcurves = obj.animation_data.action.fcurves
|
||||
else:
|
||||
fcurves = cons_obj.animation_data.action.fcurves
|
||||
#Find the RNA data path of the constraint's influence
|
||||
RNA_paths = []
|
||||
RNA_paths.append(real_constraint.path_from_id("influence"))
|
||||
if m_constraint.type == "floor" or m_constraint.type == "point":
|
||||
RNA_paths += [real_constraint.path_from_id("max_x"), real_constraint.path_from_id("min_x")]
|
||||
RNA_paths += [real_constraint.path_from_id("max_y"), real_constraint.path_from_id("min_y")]
|
||||
RNA_paths += [real_constraint.path_from_id("max_z"), real_constraint.path_from_id("min_z")]
|
||||
#Retrieve the correct fcurve via the RNA data path and remove it
|
||||
fcurves_del = [fcurve for fcurve in fcurves if fcurve.data_path in RNA_paths]
|
||||
#clear the fcurve and set the frames.
|
||||
if fcurves_del:
|
||||
for fcurve in fcurves_del:
|
||||
fcurves.remove(fcurve)
|
||||
#remove armature fcurves (if user keyframed m_constraint properties)
|
||||
if obj.data.animation_data and m_constraint.type == "point":
|
||||
if obj.data.animation_data.action:
|
||||
path = m_constraint.path_from_id("targetPoint")
|
||||
m_fcurves = [fcurve for fcurve in obj.data.animation_data.action.fcurves if fcurve.data_path == path]
|
||||
for curve in m_fcurves:
|
||||
obj.data.animation_data.action.fcurves.remove(curve)
|
||||
|
||||
#Utility function for copying property fcurves over
|
||||
|
||||
|
||||
def copyFCurve(newCurve, oldCurve):
|
||||
for point in oldCurve.keyframe_points:
|
||||
newCurve.keyframe_points.insert(frame=point.co.x, value=point.co.y)
|
||||
|
||||
#Creates new fcurves for the constraint properties (for floor and point)
|
||||
|
||||
|
||||
def createConstraintFCurves(cons_obj, obj, real_constraint):
|
||||
if isinstance(cons_obj, bpy.types.PoseBone):
|
||||
c_fcurves = obj.animation_data.action.fcurves
|
||||
else:
|
||||
c_fcurves = cons_obj.animation_data.action.fcurves
|
||||
c_x_path = [real_constraint.path_from_id("max_x"), real_constraint.path_from_id("min_x")]
|
||||
c_y_path = [real_constraint.path_from_id("max_y"), real_constraint.path_from_id("min_y")]
|
||||
c_z_path = [real_constraint.path_from_id("max_z"), real_constraint.path_from_id("min_z")]
|
||||
c_constraints_path = c_x_path + c_y_path + c_z_path
|
||||
existing_curves = [fcurve for fcurve in c_fcurves if fcurve.data_path in c_constraints_path]
|
||||
if existing_curves:
|
||||
for curve in existing_curves:
|
||||
c_fcurves.remove(curve)
|
||||
xCurves, yCurves, zCurves = [], [], []
|
||||
for path in c_constraints_path:
|
||||
newCurve = c_fcurves.new(path)
|
||||
if path in c_x_path:
|
||||
xCurves.append(newCurve)
|
||||
elif path in c_y_path:
|
||||
yCurves.append(newCurve)
|
||||
else:
|
||||
zCurves.append(newCurve)
|
||||
return xCurves, yCurves, zCurves
|
||||
|
||||
|
||||
# Function that copies all settings from m_constraint to the real Blender constraints
|
||||
# Is only called when blender constraint already exists
|
||||
|
||||
|
||||
def setConstraint(m_constraint, context):
|
||||
if not m_constraint.constrained_bone:
|
||||
return
|
||||
obj = context.active_object
|
||||
bones = obj.pose.bones
|
||||
bone = bones[m_constraint.constrained_bone]
|
||||
cons_obj = getConsObj(bone)
|
||||
real_constraint = cons_obj.constraints[m_constraint.real_constraint]
|
||||
NLATracks = obj.data.mocapNLATracks[obj.data.active_mocap]
|
||||
obj.animation_data.action = bpy.data.actions[NLATracks.auto_fix_track]
|
||||
|
||||
#frame changing section
|
||||
setConstraintFraming(m_constraint, context)
|
||||
s, e = m_constraint.s_frame, m_constraint.e_frame
|
||||
s_in, s_out = m_constraint.smooth_in, m_constraint.smooth_out
|
||||
s -= s_in
|
||||
e += s_out
|
||||
#Set the blender constraint parameters
|
||||
if m_constraint.type == "point":
|
||||
constraint_settings = False # are fix settings keyframed?
|
||||
if not m_constraint.targetSpace == "constrained_boneB":
|
||||
real_constraint.owner_space = m_constraint.targetSpace
|
||||
else:
|
||||
real_constraint.owner_space = "LOCAL"
|
||||
if obj.data.animation_data:
|
||||
if obj.data.animation_data.action:
|
||||
path = m_constraint.path_from_id("targetPoint")
|
||||
m_fcurves = [fcurve for fcurve in obj.data.animation_data.action.fcurves if fcurve.data_path == path]
|
||||
if m_fcurves:
|
||||
constraint_settings = True
|
||||
xCurves, yCurves, zCurves = createConstraintFCurves(cons_obj, obj, real_constraint)
|
||||
for curve in xCurves:
|
||||
copyFCurve(curve, m_fcurves[0])
|
||||
for curve in yCurves:
|
||||
copyFCurve(curve, m_fcurves[1])
|
||||
for curve in zCurves:
|
||||
copyFCurve(curve, m_fcurves[2])
|
||||
if m_constraint.targetSpace == "constrained_boneB" and m_constraint.constrained_boneB:
|
||||
c_frame = context.scene.frame_current
|
||||
bakedPos = {}
|
||||
src_bone = bones[m_constraint.constrained_boneB]
|
||||
if not constraint_settings:
|
||||
xCurves, yCurves, zCurves = createConstraintFCurves(cons_obj, obj, real_constraint)
|
||||
print("please wait a moment, calculating fix")
|
||||
for t in range(s, e):
|
||||
context.scene.frame_set(t)
|
||||
src_bone_pos = src_bone.matrix.to_translation()
|
||||
bakedPos[t] = src_bone_pos + m_constraint.targetPoint # final position for constrained bone in object space
|
||||
context.scene.frame_set(c_frame)
|
||||
for frame in bakedPos.keys():
|
||||
pos = bakedPos[frame]
|
||||
for xCurve in xCurves:
|
||||
xCurve.keyframe_points.insert(frame=frame, value=pos.x)
|
||||
for yCurve in yCurves:
|
||||
yCurve.keyframe_points.insert(frame=frame, value=pos.y)
|
||||
for zCurve in zCurves:
|
||||
zCurve.keyframe_points.insert(frame=frame, value=pos.z)
|
||||
|
||||
if not constraint_settings:
|
||||
x, y, z = m_constraint.targetPoint
|
||||
real_constraint.max_x = x
|
||||
real_constraint.max_y = y
|
||||
real_constraint.max_z = z
|
||||
real_constraint.min_x = x
|
||||
real_constraint.min_y = y
|
||||
real_constraint.min_z = z
|
||||
real_constraint.use_max_x = True
|
||||
real_constraint.use_max_y = True
|
||||
real_constraint.use_max_z = True
|
||||
real_constraint.use_min_x = True
|
||||
real_constraint.use_min_y = True
|
||||
real_constraint.use_min_z = True
|
||||
|
||||
if m_constraint.type == "freeze":
|
||||
real_constraint.owner_space = m_constraint.targetSpace
|
||||
bpy.context.scene.frame_set(m_constraint.s_frame)
|
||||
if isinstance(cons_obj, bpy.types.PoseBone):
|
||||
x, y, z = cons_obj.bone.center + (cons_obj.bone.vector / 2) + obj.matrix_world.to_translation()
|
||||
else:
|
||||
x, y, z = cons_obj.matrix_world.to_translation()
|
||||
|
||||
real_constraint.max_x = x
|
||||
real_constraint.max_y = y
|
||||
real_constraint.max_z = z
|
||||
real_constraint.min_x = x
|
||||
real_constraint.min_y = y
|
||||
real_constraint.min_z = z
|
||||
real_constraint.use_max_x = True
|
||||
real_constraint.use_max_y = True
|
||||
real_constraint.use_max_z = True
|
||||
real_constraint.use_min_x = True
|
||||
real_constraint.use_min_y = True
|
||||
real_constraint.use_min_z = True
|
||||
|
||||
if m_constraint.type == "distance" and m_constraint.constrained_boneB:
|
||||
real_constraint.owner_space = "WORLD"
|
||||
real_constraint.target = getConsObj(bones[m_constraint.constrained_boneB])
|
||||
real_constraint.limit_mode = "LIMITDIST_ONSURFACE"
|
||||
real_constraint.distance = m_constraint.targetDist
|
||||
|
||||
if m_constraint.type == "floor" and m_constraint.targetMesh:
|
||||
real_constraint.mute = True
|
||||
real_constraint.owner_space = "WORLD"
|
||||
#calculate the positions thoughout the range
|
||||
s, e = m_constraint.s_frame, m_constraint.e_frame
|
||||
s_in, s_out = m_constraint.smooth_in, m_constraint.smooth_out
|
||||
s -= s_in
|
||||
e += s_out
|
||||
bakedPos = {}
|
||||
floor = bpy.data.objects[m_constraint.targetMesh]
|
||||
c_frame = context.scene.frame_current
|
||||
print("please wait a moment, calculating fix")
|
||||
for t in range(s, e):
|
||||
context.scene.frame_set(t)
|
||||
axis = Vector((0, 0, 100)) * obj.matrix_world.to_3x3()
|
||||
offset = Vector((0, 0, m_constraint.targetDist)) * obj.matrix_world.to_3x3()
|
||||
ray_origin = cons_obj.matrix_world.to_translation() - offset # world position of constrained bone
|
||||
ray_target = ray_origin + axis
|
||||
#convert ray points to floor's object space
|
||||
ray_origin *= floor.matrix_world.inverted()
|
||||
ray_target *= floor.matrix_world.inverted()
|
||||
hit, nor, ind = floor.ray_cast(ray_origin, ray_target)
|
||||
if hit != Vector((0, 0, 0)):
|
||||
bakedPos[t] = (hit * floor.matrix_world)
|
||||
bakedPos[t] += Vector((0, 0, m_constraint.targetDist))
|
||||
else:
|
||||
bakedPos[t] = cons_obj.matrix_world.to_translation()
|
||||
context.scene.frame_set(c_frame)
|
||||
#create keyframes for real constraint
|
||||
xCurves, yCurves, zCurves = createConstraintFCurves(cons_obj, obj, real_constraint)
|
||||
for frame in bakedPos.keys():
|
||||
pos = bakedPos[frame]
|
||||
for xCurve in xCurves:
|
||||
xCurve.keyframe_points.insert(frame=frame, value=pos.x)
|
||||
for yCurve in yCurves:
|
||||
yCurve.keyframe_points.insert(frame=frame, value=pos.y)
|
||||
for zCurve in zCurves:
|
||||
zCurve.keyframe_points.insert(frame=frame, value=pos.z)
|
||||
real_constraint.use_max_x = True
|
||||
real_constraint.use_max_y = True
|
||||
real_constraint.use_max_z = True
|
||||
real_constraint.use_min_x = True
|
||||
real_constraint.use_min_y = True
|
||||
real_constraint.use_min_z = True
|
||||
|
||||
# active/baked check
|
||||
real_constraint.mute = (not m_constraint.active)
|
||||
|
||||
|
||||
def locBake(s_frame, e_frame, bones):
|
||||
scene = bpy.context.scene
|
||||
bakeDict = {}
|
||||
for bone in bones:
|
||||
bakeDict[bone.name] = {}
|
||||
for t in range(s_frame, e_frame):
|
||||
scene.frame_set(t)
|
||||
for bone in bones:
|
||||
bakeDict[bone.name][t] = bone.matrix.copy()
|
||||
for t in range(s_frame, e_frame):
|
||||
for bone in bones:
|
||||
print(bone.bone.matrix_local.to_translation())
|
||||
bone.matrix = bakeDict[bone.name][t]
|
||||
bone.keyframe_insert("location", frame=t)
|
||||
|
||||
|
||||
# Baking function which bakes all bones effected by the constraint
|
||||
def bakeAllConstraints(obj, s_frame, e_frame, bones):
|
||||
for bone in bones:
|
||||
bone.bone.select = False
|
||||
selectedBones = [] # Marks bones that need a full bake
|
||||
simpleBake = [] # Marks bones that need only a location bake
|
||||
for end_bone in bones:
|
||||
if end_bone.name in [m_constraint.real_constraint_bone for m_constraint in obj.data.mocap_constraints]:
|
||||
#For all bones that have a constraint:
|
||||
ik = hasIKConstraint(end_bone)
|
||||
cons_obj = getConsObj(end_bone)
|
||||
if ik:
|
||||
#If it's an auto generated IK:
|
||||
if ik.chain_count == 0:
|
||||
selectedBones += bones # Chain len 0, bake everything
|
||||
else:
|
||||
selectedBones += [end_bone] + end_bone.parent_recursive[:ik.chain_count - 1] # Bake the chain
|
||||
else:
|
||||
#It's either an FK bone which we should just bake
|
||||
#OR a user created IK target bone
|
||||
simpleBake += [end_bone]
|
||||
for bone in selectedBones:
|
||||
bone.bone.select = True
|
||||
NLATracks = obj.data.mocapNLATracks[obj.data.active_mocap]
|
||||
obj.animation_data.action = bpy.data.actions[NLATracks.auto_fix_track]
|
||||
constraintTrack = obj.animation_data.nla_tracks[NLATracks.auto_fix_track]
|
||||
constraintStrip = constraintTrack.strips[0]
|
||||
constraintStrip.action_frame_start = s_frame
|
||||
constraintStrip.action_frame_end = e_frame
|
||||
constraintStrip.frame_start = s_frame
|
||||
constraintStrip.frame_end = e_frame
|
||||
if selectedBones:
|
||||
#Use bake function from NLA Bake Action operator
|
||||
nla.bake(s_frame, e_frame, action=constraintStrip.action, only_selected=True, do_pose=True, do_object=False)
|
||||
if simpleBake:
|
||||
#Do a "simple" bake, location only, world space only.
|
||||
locBake(s_frame, e_frame, simpleBake)
|
||||
|
||||
|
||||
#Calls the baking function and decativates releveant constraints
|
||||
def bakeConstraints(context):
|
||||
obj = context.active_object
|
||||
bones = obj.pose.bones
|
||||
s_frame, e_frame = context.scene.frame_start, context.scene.frame_end
|
||||
#Bake relevant bones
|
||||
bakeAllConstraints(obj, s_frame, e_frame, bones)
|
||||
for m_constraint in obj.data.mocap_constraints:
|
||||
end_bone = bones[m_constraint.real_constraint_bone]
|
||||
cons_obj = getConsObj(end_bone)
|
||||
# It's a control empty: turn the ik off
|
||||
if not isinstance(cons_obj, bpy.types.PoseBone):
|
||||
ik_con = hasIKConstraint(end_bone)
|
||||
if ik_con:
|
||||
ik_con.mute = True
|
||||
# Deactivate related Blender Constraint
|
||||
m_constraint.active = False
|
||||
|
||||
|
||||
#Deletes the baked fcurves and reactivates relevant constraints
|
||||
def unbakeConstraints(context):
|
||||
# to unbake constraints we delete the whole strip
|
||||
obj = context.active_object
|
||||
bones = obj.pose.bones
|
||||
scene = bpy.context.scene
|
||||
NLATracks = obj.data.mocapNLATracks[obj.data.active_mocap]
|
||||
obj.animation_data.action = bpy.data.actions[NLATracks.auto_fix_track]
|
||||
constraintTrack = obj.animation_data.nla_tracks[NLATracks.auto_fix_track]
|
||||
constraintStrip = constraintTrack.strips[0]
|
||||
action = constraintStrip.action
|
||||
# delete the fcurves on the strip
|
||||
for fcurve in action.fcurves:
|
||||
action.fcurves.remove(fcurve)
|
||||
# reactivate relevant constraints
|
||||
for m_constraint in obj.data.mocap_constraints:
|
||||
end_bone = bones[m_constraint.real_constraint_bone]
|
||||
cons_obj = getConsObj(end_bone)
|
||||
# It's a control empty: turn the ik back on
|
||||
if not isinstance(cons_obj, bpy.types.PoseBone):
|
||||
ik_con = hasIKConstraint(end_bone)
|
||||
if ik_con:
|
||||
ik_con.mute = False
|
||||
m_constraint.active = True
|
||||
|
||||
|
||||
def updateConstraints(obj, context):
|
||||
fixes = obj.data.mocap_constraints
|
||||
for fix in fixes:
|
||||
fix.active = False
|
||||
fix.active = True
|
@ -1,904 +0,0 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
from math import hypot, sqrt, isfinite, radians, pi
|
||||
import bpy
|
||||
import time
|
||||
from mathutils import Vector, Matrix
|
||||
|
||||
|
||||
# A Python implementation of n sized Vectors.
|
||||
# Mathutils has a max size of 4, and we need at least 5 for Simplify Curves and even more for Cross Correlation.
|
||||
# Vector utility functions
|
||||
class NdVector:
|
||||
vec = []
|
||||
|
||||
def __init__(self, vec):
|
||||
self.vec = vec[:]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.vec)
|
||||
|
||||
def __mul__(self, otherMember):
|
||||
if (isinstance(otherMember, int) or
|
||||
isinstance(otherMember, float)):
|
||||
return NdVector([otherMember * x for x in self.vec])
|
||||
else:
|
||||
a = self.vec
|
||||
b = otherMember.vec
|
||||
n = len(self)
|
||||
return sum([a[i] * b[i] for i in range(n)])
|
||||
|
||||
def __sub__(self, otherVec):
|
||||
a = self.vec
|
||||
b = otherVec.vec
|
||||
n = len(self)
|
||||
return NdVector([a[i] - b[i] for i in range(n)])
|
||||
|
||||
def __add__(self, otherVec):
|
||||
a = self.vec
|
||||
b = otherVec.vec
|
||||
n = len(self)
|
||||
return NdVector([a[i] + b[i] for i in range(n)])
|
||||
|
||||
def __div__(self, scalar):
|
||||
return NdVector([x / scalar for x in self.vec])
|
||||
|
||||
def vecLength(self):
|
||||
return sqrt(self * self)
|
||||
|
||||
def vecLengthSq(self):
|
||||
return (self * self)
|
||||
|
||||
def normalize(self):
|
||||
len = self.length
|
||||
self.vec = [x / len for x in self.vec]
|
||||
|
||||
def copy(self):
|
||||
return NdVector(self.vec)
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self.vec[i]
|
||||
|
||||
def x(self):
|
||||
return self.vec[0]
|
||||
|
||||
def y(self):
|
||||
return self.vec[1]
|
||||
|
||||
def resize_2d(self):
|
||||
return Vector((self.x, self.y))
|
||||
|
||||
length = property(vecLength)
|
||||
lengthSq = property(vecLengthSq)
|
||||
x = property(x)
|
||||
y = property(y)
|
||||
|
||||
|
||||
#Sampled Data Point class for Simplify Curves
|
||||
class dataPoint:
|
||||
index = 0
|
||||
# x,y1,y2,y3 coordinate of original point
|
||||
co = NdVector((0, 0, 0, 0, 0))
|
||||
#position according to parametric view of original data, [0,1] range
|
||||
u = 0
|
||||
#use this for anything
|
||||
temp = 0
|
||||
|
||||
def __init__(self, index, co, u=0):
|
||||
self.index = index
|
||||
self.co = co
|
||||
self.u = u
|
||||
|
||||
|
||||
#Cross Correlation Function
|
||||
#http://en.wikipedia.org/wiki/Cross_correlation
|
||||
#IN: curvesA, curvesB - bpy_collection/list of fcurves to analyze. Auto-Correlation is when they are the same.
|
||||
# margin - When searching for the best "start" frame, how large a neighborhood of frames should we inspect (similar to epsilon in Calculus)
|
||||
#OUT: startFrame, length of new anim, and curvesA
|
||||
def crossCorrelationMatch(curvesA, curvesB, margin):
|
||||
dataA = []
|
||||
dataB = []
|
||||
start, end = curvesA[0].range()
|
||||
start = int(start)
|
||||
end = int(end)
|
||||
|
||||
#transfer all fcurves data on each frame to a single NdVector.
|
||||
for i in range(1, end):
|
||||
vec = []
|
||||
for fcurve in curvesA:
|
||||
vec.append(fcurve.evaluate(i))
|
||||
dataA.append(NdVector(vec))
|
||||
vec = []
|
||||
for fcurve in curvesB:
|
||||
vec.append(fcurve.evaluate(i))
|
||||
dataB.append(NdVector(vec))
|
||||
|
||||
#Comparator for Cross Correlation. "Classic" implementation uses dot product, as do we.
|
||||
def comp(a, b):
|
||||
return a * b
|
||||
|
||||
#Create Rxy, which holds the Cross Correlation data.
|
||||
N = len(dataA)
|
||||
Rxy = [0.0] * N
|
||||
for i in range(N):
|
||||
for j in range(i, min(i + N, N)):
|
||||
Rxy[i] += comp(dataA[j], dataB[j - i])
|
||||
for j in range(i):
|
||||
Rxy[i] += comp(dataA[j], dataB[j - i + N])
|
||||
Rxy[i] /= float(N)
|
||||
|
||||
#Find the Local maximums in the Cross Correlation data via numerical derivative.
|
||||
def LocalMaximums(Rxy):
|
||||
Rxyd = [Rxy[i] - Rxy[i - 1] for i in range(1, len(Rxy))]
|
||||
maxs = []
|
||||
for i in range(1, len(Rxyd) - 1):
|
||||
a = Rxyd[i - 1]
|
||||
b = Rxyd[i]
|
||||
#sign change (zerocrossing) at point i, denoting max point (only)
|
||||
if (a >= 0 and b < 0) or (a < 0 and b >= 0):
|
||||
maxs.append((i, max(Rxy[i], Rxy[i - 1])))
|
||||
return [x[0] for x in maxs]
|
||||
#~ return max(maxs, key=lambda x: x[1])[0]
|
||||
|
||||
#flms - the possible offsets of the first part of the animation. In Auto-Corr, this is the length of the loop.
|
||||
flms = LocalMaximums(Rxy[0:int(len(Rxy))])
|
||||
ss = []
|
||||
|
||||
#for every local maximum, find the best one - i.e. also has the best start frame.
|
||||
for flm in flms:
|
||||
diff = []
|
||||
|
||||
for i in range(len(dataA) - flm):
|
||||
diff.append((dataA[i] - dataB[i + flm]).lengthSq)
|
||||
|
||||
def lowerErrorSlice(diff, e):
|
||||
#index, error at index
|
||||
bestSlice = (0, 100000)
|
||||
for i in range(e, len(diff) - e):
|
||||
errorSlice = sum(diff[i - e:i + e + 1])
|
||||
if errorSlice < bestSlice[1]:
|
||||
bestSlice = (i, errorSlice, flm)
|
||||
return bestSlice
|
||||
|
||||
s = lowerErrorSlice(diff, margin)
|
||||
ss.append(s)
|
||||
|
||||
#Find the best result and return it.
|
||||
ss.sort(key=lambda x: x[1])
|
||||
return ss[0][2], ss[0][0], dataA
|
||||
|
||||
|
||||
#Uses auto correlation (cross correlation of the same set of curves) and trims the active_object's fcurves
|
||||
#Except for location curves (which in mocap tend to be not cyclic, e.g. a walk cycle forward)
|
||||
#Transfers the fcurve data to a list of NdVector (length of list is number of fcurves), and calls the cross correlation function.
|
||||
#Then trims the fcurve accordingly.
|
||||
#IN: Nothing, set the object you want as active and call. Assumes object has animation_data.action!
|
||||
#OUT: Trims the object's fcurves (except location curves).
|
||||
def autoloop_anim():
|
||||
context = bpy.context
|
||||
obj = context.active_object
|
||||
|
||||
def locCurve(x):
|
||||
x.data_path == "location"
|
||||
|
||||
fcurves = [x for x in obj.animation_data.action.fcurves if not locCurve(x)]
|
||||
|
||||
margin = 10
|
||||
|
||||
flm, s, data = crossCorrelationMatch(fcurves, fcurves, margin)
|
||||
loop = data[s:s + flm]
|
||||
|
||||
#performs blending with a root falloff on the seam's neighborhood to ensure good tiling.
|
||||
for i in range(1, margin + 1):
|
||||
w1 = sqrt(float(i) / margin)
|
||||
loop[-i] = (loop[-i] * w1) + (loop[0] * (1 - w1))
|
||||
|
||||
for curve in fcurves:
|
||||
pts = curve.keyframe_points
|
||||
for i in range(len(pts) - 1, -1, -1):
|
||||
pts.remove(pts[i])
|
||||
|
||||
for c, curve in enumerate(fcurves):
|
||||
pts = curve.keyframe_points
|
||||
for i in range(len(loop)):
|
||||
pts.insert(i + 2, loop[i][c])
|
||||
|
||||
context.scene.frame_end = flm
|
||||
|
||||
|
||||
#simplifyCurves: performes the bulk of the samples to bezier conversion.
|
||||
#IN: curveGroup - which can be a collection of singleFcurves, or grouped (via nested lists) .
|
||||
# error - threshold of permittable error (max distance) of the new beziers to the original data
|
||||
# reparaError - threshold of error where we should try to fix the parameterization rather than split the existing curve. > error, usually by a small constant factor for best performance.
|
||||
# maxIterations - maximum number of iterations of reparameterizations we should attempt. (Newton-Rahpson is not guarenteed to converge, so this is needed).
|
||||
# group_mode - boolean, indicating wether we should place bezier keyframes on the same x (frame), or optimize each individual curve.
|
||||
#OUT: None. Deletes the existing curves and creates the new beziers.
|
||||
def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode):
|
||||
|
||||
#Calculates the unit tangent of point v
|
||||
def unitTangent(v, data_pts):
|
||||
tang = NdVector((0, 0, 0, 0, 0))
|
||||
if v != 0:
|
||||
#If it's not the first point, we can calculate a leftside tangent
|
||||
tang += data_pts[v].co - data_pts[v - 1].co
|
||||
if v != len(data_pts) - 1:
|
||||
#If it's not the last point, we can calculate a rightside tangent
|
||||
tang += data_pts[v + 1].co - data_pts[v].co
|
||||
tang.normalize()
|
||||
return tang
|
||||
|
||||
#assign parametric u value for each point in original data, via relative arc length
|
||||
#http://en.wikipedia.org/wiki/Arc_length
|
||||
def chordLength(data_pts, s, e):
|
||||
totalLength = 0
|
||||
for pt in data_pts[s:e + 1]:
|
||||
i = pt.index
|
||||
if i == s:
|
||||
chordLength = 0
|
||||
else:
|
||||
chordLength = (data_pts[i].co - data_pts[i - 1].co).length
|
||||
totalLength += chordLength
|
||||
pt.temp = totalLength
|
||||
for pt in data_pts[s:e + 1]:
|
||||
if totalLength == 0:
|
||||
print(s, e)
|
||||
pt.u = (pt.temp / totalLength)
|
||||
|
||||
# get binomial coefficient lookup table, this function/table is only called with args
|
||||
# (3,0),(3,1),(3,2),(3,3),(2,0),(2,1),(2,2)!
|
||||
binomDict = {(3, 0): 1,
|
||||
(3, 1): 3,
|
||||
(3, 2): 3,
|
||||
(3, 3): 1,
|
||||
(2, 0): 1,
|
||||
(2, 1): 2,
|
||||
(2, 2): 1}
|
||||
|
||||
#value at pt t of a single bernstein Polynomial
|
||||
def bernsteinPoly(n, i, t):
|
||||
binomCoeff = binomDict[(n, i)]
|
||||
return binomCoeff * pow(t, i) * pow(1 - t, n - i)
|
||||
|
||||
# fit a single cubic to data points in range [s(tart),e(nd)].
|
||||
def fitSingleCubic(data_pts, s, e):
|
||||
|
||||
# A - matrix used for calculating C matrices for fitting
|
||||
def A(i, j, s, e, t1, t2):
|
||||
if j == 1:
|
||||
t = t1
|
||||
if j == 2:
|
||||
t = t2
|
||||
u = data_pts[i].u
|
||||
return t * bernsteinPoly(3, j, u)
|
||||
|
||||
# X component, used for calculating X matrices for fitting
|
||||
def xComponent(i, s, e):
|
||||
di = data_pts[i].co
|
||||
u = data_pts[i].u
|
||||
v0 = data_pts[s].co
|
||||
v3 = data_pts[e].co
|
||||
a = v0 * bernsteinPoly(3, 0, u)
|
||||
b = v0 * bernsteinPoly(3, 1, u)
|
||||
c = v3 * bernsteinPoly(3, 2, u)
|
||||
d = v3 * bernsteinPoly(3, 3, u)
|
||||
return (di - (a + b + c + d))
|
||||
|
||||
t1 = unitTangent(s, data_pts)
|
||||
t2 = unitTangent(e, data_pts)
|
||||
c11 = sum([A(i, 1, s, e, t1, t2) * A(i, 1, s, e, t1, t2) for i in range(s, e + 1)])
|
||||
c12 = sum([A(i, 1, s, e, t1, t2) * A(i, 2, s, e, t1, t2) for i in range(s, e + 1)])
|
||||
c21 = c12
|
||||
c22 = sum([A(i, 2, s, e, t1, t2) * A(i, 2, s, e, t1, t2) for i in range(s, e + 1)])
|
||||
|
||||
x1 = sum([xComponent(i, s, e) * A(i, 1, s, e, t1, t2) for i in range(s, e + 1)])
|
||||
x2 = sum([xComponent(i, s, e) * A(i, 2, s, e, t1, t2) for i in range(s, e + 1)])
|
||||
|
||||
# calculate Determinate of the 3 matrices
|
||||
det_cc = c11 * c22 - c21 * c12
|
||||
det_cx = c11 * x2 - c12 * x1
|
||||
det_xc = x1 * c22 - x2 * c12
|
||||
|
||||
# if matrix is not homogenous, fudge the data a bit
|
||||
if det_cc == 0:
|
||||
det_cc = 0.01
|
||||
|
||||
# alpha's are the correct offset for bezier handles
|
||||
alpha0 = det_xc / det_cc # offset from right (first) point
|
||||
alpha1 = det_cx / det_cc # offset from left (last) point
|
||||
|
||||
sRightHandle = data_pts[s].co.copy()
|
||||
sTangent = t1 * abs(alpha0)
|
||||
sRightHandle += sTangent # position of first pt's handle
|
||||
eLeftHandle = data_pts[e].co.copy()
|
||||
eTangent = t2 * abs(alpha1)
|
||||
eLeftHandle += eTangent # position of last pt's handle.
|
||||
|
||||
# return a 4 member tuple representing the bezier
|
||||
return (data_pts[s].co,
|
||||
sRightHandle,
|
||||
eLeftHandle,
|
||||
data_pts[e].co)
|
||||
|
||||
# convert 2 given data points into a cubic bezier.
|
||||
# handles are offset along the tangent at
|
||||
# a 3rd of the length between the points.
|
||||
def fitSingleCubic2Pts(data_pts, s, e):
|
||||
alpha0 = alpha1 = (data_pts[s].co - data_pts[e].co).length / 3
|
||||
|
||||
sRightHandle = data_pts[s].co.copy()
|
||||
sTangent = unitTangent(s, data_pts) * abs(alpha0)
|
||||
sRightHandle += sTangent # position of first pt's handle
|
||||
eLeftHandle = data_pts[e].co.copy()
|
||||
eTangent = unitTangent(e, data_pts) * abs(alpha1)
|
||||
eLeftHandle += eTangent # position of last pt's handle.
|
||||
|
||||
#return a 4 member tuple representing the bezier
|
||||
return (data_pts[s].co,
|
||||
sRightHandle,
|
||||
eLeftHandle,
|
||||
data_pts[e].co)
|
||||
|
||||
#evaluate bezier, represented by a 4 member tuple (pts) at point t.
|
||||
def bezierEval(pts, t):
|
||||
sumVec = NdVector((0, 0, 0, 0, 0))
|
||||
for i in range(4):
|
||||
sumVec += pts[i] * bernsteinPoly(3, i, t)
|
||||
return sumVec
|
||||
|
||||
#calculate the highest error between bezier and original data
|
||||
#returns the distance and the index of the point where max error occurs.
|
||||
def maxErrorAmount(data_pts, bez, s, e):
|
||||
maxError = 0
|
||||
maxErrorPt = s
|
||||
if e - s < 3:
|
||||
return 0, None
|
||||
for pt in data_pts[s:e + 1]:
|
||||
bezVal = bezierEval(bez, pt.u)
|
||||
normalize_error = pt.co.length
|
||||
if normalize_error == 0:
|
||||
normalize_error = 1
|
||||
tmpError = (pt.co - bezVal).length / normalize_error
|
||||
if tmpError >= maxError:
|
||||
maxError = tmpError
|
||||
maxErrorPt = pt.index
|
||||
return maxError, maxErrorPt
|
||||
|
||||
#calculated bezier derivative at point t.
|
||||
#That is, tangent of point t.
|
||||
def getBezDerivative(bez, t):
|
||||
n = len(bez) - 1
|
||||
sumVec = NdVector((0, 0, 0, 0, 0))
|
||||
for i in range(n - 1):
|
||||
sumVec += (bez[i + 1] - bez[i]) * bernsteinPoly(n - 1, i, t)
|
||||
return sumVec
|
||||
|
||||
#use Newton-Raphson to find a better paramterization of datapoints,
|
||||
#one that minimizes the distance (or error)
|
||||
# between bezier and original data.
|
||||
def newtonRaphson(data_pts, s, e, bez):
|
||||
for pt in data_pts[s:e + 1]:
|
||||
if pt.index == s:
|
||||
pt.u = 0
|
||||
elif pt.index == e:
|
||||
pt.u = 1
|
||||
else:
|
||||
u = pt.u
|
||||
qu = bezierEval(bez, pt.u)
|
||||
qud = getBezDerivative(bez, u)
|
||||
#we wish to minimize f(u),
|
||||
#the squared distance between curve and data
|
||||
fu = (qu - pt.co).length ** 2
|
||||
fud = (2 * (qu.x - pt.co.x) * (qud.x)) - (2 * (qu.y - pt.co.y) * (qud.y))
|
||||
if fud == 0:
|
||||
fu = 0
|
||||
fud = 1
|
||||
pt.u = pt.u - (fu / fud)
|
||||
|
||||
#Create data_pts, a list of dataPoint type, each is assigned index i, and an NdVector
|
||||
def createDataPts(curveGroup, group_mode):
|
||||
data_pts = []
|
||||
if group_mode:
|
||||
print([x.data_path for x in curveGroup])
|
||||
for i in range(len(curveGroup[0].keyframe_points)):
|
||||
x = curveGroup[0].keyframe_points[i].co.x
|
||||
y1 = curveGroup[0].keyframe_points[i].co.y
|
||||
y2 = curveGroup[1].keyframe_points[i].co.y
|
||||
y3 = curveGroup[2].keyframe_points[i].co.y
|
||||
y4 = 0
|
||||
if len(curveGroup) == 4:
|
||||
y4 = curveGroup[3].keyframe_points[i].co.y
|
||||
data_pts.append(dataPoint(i, NdVector((x, y1, y2, y3, y4))))
|
||||
else:
|
||||
for i in range(len(curveGroup.keyframe_points)):
|
||||
x = curveGroup.keyframe_points[i].co.x
|
||||
y1 = curveGroup.keyframe_points[i].co.y
|
||||
y2 = 0
|
||||
y3 = 0
|
||||
y4 = 0
|
||||
data_pts.append(dataPoint(i, NdVector((x, y1, y2, y3, y4))))
|
||||
return data_pts
|
||||
|
||||
#Recursively fit cubic beziers to the data_pts between s and e
|
||||
def fitCubic(data_pts, s, e):
|
||||
# if there are less than 3 points, fit a single basic bezier
|
||||
if e - s < 3:
|
||||
bez = fitSingleCubic2Pts(data_pts, s, e)
|
||||
else:
|
||||
#if there are more, parameterize the points
|
||||
# and fit a single cubic bezier
|
||||
chordLength(data_pts, s, e)
|
||||
bez = fitSingleCubic(data_pts, s, e)
|
||||
|
||||
#calculate max error and point where it occurs
|
||||
maxError, maxErrorPt = maxErrorAmount(data_pts, bez, s, e)
|
||||
#if error is small enough, reparameterization might be enough
|
||||
if maxError < reparaError and maxError > error:
|
||||
for i in range(maxIterations):
|
||||
newtonRaphson(data_pts, s, e, bez)
|
||||
if e - s < 3:
|
||||
bez = fitSingleCubic2Pts(data_pts, s, e)
|
||||
else:
|
||||
bez = fitSingleCubic(data_pts, s, e)
|
||||
|
||||
#recalculate max error and point where it occurs
|
||||
maxError, maxErrorPt = maxErrorAmount(data_pts, bez, s, e)
|
||||
|
||||
#repara wasn't enough, we need 2 beziers for this range.
|
||||
#Split the bezier at point of maximum error
|
||||
if maxError > error:
|
||||
fitCubic(data_pts, s, maxErrorPt)
|
||||
fitCubic(data_pts, maxErrorPt, e)
|
||||
else:
|
||||
#error is small enough, return the beziers.
|
||||
beziers.append(bez)
|
||||
return
|
||||
|
||||
# deletes the sampled points and creates beziers.
|
||||
def createNewCurves(curveGroup, beziers, group_mode):
|
||||
#remove all existing data points
|
||||
if group_mode:
|
||||
for fcurve in curveGroup:
|
||||
for i in range(len(fcurve.keyframe_points) - 1, 0, -1):
|
||||
fcurve.keyframe_points.remove(fcurve.keyframe_points[i])
|
||||
else:
|
||||
fcurve = curveGroup
|
||||
for i in range(len(fcurve.keyframe_points) - 1, 0, -1):
|
||||
fcurve.keyframe_points.remove(fcurve.keyframe_points[i])
|
||||
|
||||
#insert the calculated beziers to blender data.\
|
||||
if group_mode:
|
||||
for fullbez in beziers:
|
||||
for i, fcurve in enumerate(curveGroup):
|
||||
bez = [Vector((vec[0], vec[i + 1])) for vec in fullbez]
|
||||
newKey = fcurve.keyframe_points.insert(frame=bez[0].x, value=bez[0].y)
|
||||
newKey.handle_right = (bez[1].x, bez[1].y)
|
||||
|
||||
newKey = fcurve.keyframe_points.insert(frame=bez[3].x, value=bez[3].y)
|
||||
newKey.handle_left = (bez[2].x, bez[2].y)
|
||||
else:
|
||||
for bez in beziers:
|
||||
for vec in bez:
|
||||
vec.resize_2d()
|
||||
newKey = fcurve.keyframe_points.insert(frame=bez[0].x, value=bez[0].y)
|
||||
newKey.handle_right = (bez[1].x, bez[1].y)
|
||||
|
||||
newKey = fcurve.keyframe_points.insert(frame=bez[3].x, value=bez[3].y)
|
||||
newKey.handle_left = (bez[2].x, bez[2].y)
|
||||
|
||||
# indices are detached from data point's frame (x) value and
|
||||
# stored in the dataPoint object, represent a range
|
||||
|
||||
data_pts = createDataPts(curveGroup, group_mode)
|
||||
|
||||
s = 0 # start
|
||||
e = len(data_pts) - 1 # end
|
||||
|
||||
beziers = []
|
||||
|
||||
#begin the recursive fitting algorithm.
|
||||
fitCubic(data_pts, s, e)
|
||||
#remove old Fcurves and insert the new ones
|
||||
createNewCurves(curveGroup, beziers, group_mode)
|
||||
|
||||
|
||||
#Main function of simplification, which called by Operator
|
||||
#IN:
|
||||
# sel_opt- either "sel" (selected) or "all" for which curves to effect
|
||||
# error- maximum error allowed, in fraction (20% = 0.0020, which is the default),
|
||||
# i.e. divide by 10000 from percentage wanted.
|
||||
# group_mode- boolean, to analyze each curve seperately or in groups,
|
||||
# where a group is all curves that effect the same property/RNA path
|
||||
def fcurves_simplify(context, obj, sel_opt="all", error=0.002, group_mode=True):
|
||||
# main vars
|
||||
fcurves = obj.animation_data.action.fcurves
|
||||
|
||||
if sel_opt == "sel":
|
||||
sel_fcurves = [fcurve for fcurve in fcurves if fcurve.select]
|
||||
else:
|
||||
sel_fcurves = fcurves[:]
|
||||
|
||||
#Error threshold for Newton Raphson reparamatizing
|
||||
reparaError = error * 32
|
||||
maxIterations = 16
|
||||
|
||||
if group_mode:
|
||||
fcurveDict = {}
|
||||
#this loop sorts all the fcurves into groups of 3 or 4,
|
||||
#based on their RNA Data path, which corresponds to
|
||||
#which property they effect
|
||||
for curve in sel_fcurves:
|
||||
if curve.data_path in fcurveDict: # if this bone has been added, append the curve to its list
|
||||
fcurveDict[curve.data_path].append(curve)
|
||||
else:
|
||||
fcurveDict[curve.data_path] = [curve] # new bone, add a new dict value with this first curve
|
||||
fcurveGroups = fcurveDict.values()
|
||||
else:
|
||||
fcurveGroups = sel_fcurves
|
||||
|
||||
if error > 0.00000:
|
||||
#simplify every selected curve.
|
||||
totalt = 0
|
||||
for i, fcurveGroup in enumerate(fcurveGroups):
|
||||
print("Processing curve " + str(i + 1) + "/" + str(len(fcurveGroups)))
|
||||
t = time.clock()
|
||||
simplifyCurves(fcurveGroup, error, reparaError, maxIterations, group_mode)
|
||||
t = time.clock() - t
|
||||
print(str(t)[:5] + " seconds to process last curve")
|
||||
totalt += t
|
||||
print(str(totalt)[:5] + " seconds, total time elapsed")
|
||||
|
||||
return
|
||||
|
||||
|
||||
# Implementation of non-linear median filter, with variable kernel size
|
||||
# Double pass - one marks spikes, the other smooths them
|
||||
# Expects sampled keyframes on everyframe
|
||||
# IN: None. Performs the operations on the active_object's fcurves. Expects animation_data.action to exist!
|
||||
# OUT: None. Fixes the fcurves "in-place".
|
||||
def denoise_median():
|
||||
context = bpy.context
|
||||
obj = context.active_object
|
||||
fcurves = obj.animation_data.action.fcurves
|
||||
medKernel = 1 # actually *2+1... since it this is offset
|
||||
flagKernel = 4
|
||||
highThres = (flagKernel * 2) - 1
|
||||
lowThres = 0
|
||||
for fcurve in fcurves:
|
||||
orgPts = fcurve.keyframe_points[:]
|
||||
flaggedFrames = []
|
||||
# mark frames that are spikes by sorting a large kernel
|
||||
for i in range(flagKernel, len(fcurve.keyframe_points) - flagKernel):
|
||||
center = orgPts[i]
|
||||
neighborhood = orgPts[i - flagKernel: i + flagKernel]
|
||||
neighborhood.sort(key=lambda pt: pt.co[1])
|
||||
weight = neighborhood.index(center)
|
||||
if weight >= highThres or weight <= lowThres:
|
||||
flaggedFrames.append((i, center))
|
||||
# clean marked frames with a simple median filter
|
||||
# averages all frames in the kernel equally, except center which has no weight
|
||||
for i, pt in flaggedFrames:
|
||||
newValue = 0
|
||||
sumWeights = 0
|
||||
neighborhood = [neighpt.co[1] for neighpt in orgPts[i - medKernel: i + medKernel + 1] if neighpt != pt]
|
||||
newValue = sum(neighborhood) / len(neighborhood)
|
||||
pt.co[1] = newValue
|
||||
return
|
||||
|
||||
|
||||
# Recieves armature, and rotations all bones by 90 degrees along the X axis
|
||||
# This fixes the common axis issue BVH files have when importing.
|
||||
# IN: Armature (bpy.types.Armature)
|
||||
def rotate_fix_armature(arm_data):
|
||||
global_matrix = Matrix.Rotation(radians(90), 4, "X")
|
||||
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
||||
#disconnect all bones for ease of global rotation
|
||||
connectedBones = []
|
||||
for bone in arm_data.edit_bones:
|
||||
if bone.use_connect:
|
||||
connectedBones.append(bone.name)
|
||||
bone.use_connect = False
|
||||
|
||||
#rotate all the bones around their center
|
||||
for bone in arm_data.edit_bones:
|
||||
bone.transform(global_matrix)
|
||||
|
||||
#reconnect the bones
|
||||
for bone in connectedBones:
|
||||
arm_data.edit_bones[bone].use_connect = True
|
||||
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
||||
|
||||
|
||||
#Roughly scales the performer armature to match the enduser armature
|
||||
#IN: perfromer_obj, enduser_obj, Blender objects whose .data is an armature.
|
||||
def scale_fix_armature(performer_obj, enduser_obj):
|
||||
perf_bones = performer_obj.data.bones
|
||||
end_bones = enduser_obj.data.bones
|
||||
|
||||
def calculateBoundingRadius(bones):
|
||||
center = Vector()
|
||||
for bone in bones:
|
||||
center += bone.head_local
|
||||
center /= len(bones)
|
||||
radius = 0
|
||||
for bone in bones:
|
||||
dist = (bone.head_local - center).length
|
||||
if dist > radius:
|
||||
radius = dist
|
||||
return radius
|
||||
|
||||
perf_rad = calculateBoundingRadius(performer_obj.data.bones)
|
||||
end_rad = calculateBoundingRadius(enduser_obj.data.bones)
|
||||
#end_avg = enduser_obj.dimensions
|
||||
factor = end_rad / perf_rad * 1.2
|
||||
performer_obj.scale *= factor
|
||||
|
||||
|
||||
#Guess Mapping
|
||||
#Given a performer and enduser armature, attempts to guess the hiearchy mapping
|
||||
def guessMapping(performer_obj, enduser_obj):
|
||||
perf_bones = performer_obj.data.bones
|
||||
end_bones = enduser_obj.data.bones
|
||||
|
||||
root = perf_bones[0]
|
||||
|
||||
def findBoneSide(bone):
|
||||
if "Left" in bone:
|
||||
return "Left", bone.replace("Left", "").lower().replace(".", "")
|
||||
if "Right" in bone:
|
||||
return "Right", bone.replace("Right", "").lower().replace(".", "")
|
||||
if "L" in bone:
|
||||
return "Left", bone.replace("Left", "").lower().replace(".", "")
|
||||
if "R" in bone:
|
||||
return "Right", bone.replace("Right", "").lower().replace(".", "")
|
||||
return "", bone
|
||||
|
||||
def nameMatch(bone_a, bone_b):
|
||||
# nameMatch - recieves two strings, returns 2 if they are relatively the same, 1 if they are the same but R and L and 0 if no match at all
|
||||
side_a, noside_a = findBoneSide(bone_a)
|
||||
side_b, noside_b = findBoneSide(bone_b)
|
||||
if side_a == side_b:
|
||||
if noside_a in noside_b or noside_b in noside_a:
|
||||
return 2
|
||||
else:
|
||||
if noside_a in noside_b or noside_b in noside_a:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def guessSingleMapping(perf_bone):
|
||||
possible_bones = [end_bones[0]]
|
||||
|
||||
while possible_bones:
|
||||
for end_bone in possible_bones:
|
||||
match = nameMatch(perf_bone.name, end_bone.name)
|
||||
if match == 2 and not perf_bone.map:
|
||||
perf_bone.map = end_bone.name
|
||||
#~ elif match == 1 and not perf_bone.map:
|
||||
#~ oppo = perf_bones[oppositeBone(perf_bone)].map
|
||||
# if oppo:
|
||||
# perf_bone = oppo
|
||||
newPossibleBones = []
|
||||
for end_bone in possible_bones:
|
||||
newPossibleBones += list(end_bone.children)
|
||||
possible_bones = newPossibleBones
|
||||
|
||||
for child in perf_bone.children:
|
||||
guessSingleMapping(child)
|
||||
|
||||
guessSingleMapping(root)
|
||||
|
||||
|
||||
# Creates limit rotation constraints on the enduser armature based on range of motion (max min of fcurves) of the performer.
|
||||
# IN: context (bpy.context, etc.), and 2 blender objects which are armatures
|
||||
# OUT: creates the limit constraints.
|
||||
def limit_dof(context, performer_obj, enduser_obj):
|
||||
limitDict = {}
|
||||
perf_bones = [bone for bone in performer_obj.pose.bones if bone.bone.map]
|
||||
c_frame = context.scene.frame_current
|
||||
for bone in perf_bones:
|
||||
limitDict[bone.bone.map] = [1000, 1000, 1000, -1000, -1000, -1000]
|
||||
for t in range(context.scene.frame_start, context.scene.frame_end):
|
||||
context.scene.frame_set(t)
|
||||
for bone in perf_bones:
|
||||
end_bone = enduser_obj.pose.bones[bone.bone.map]
|
||||
bake_matrix = bone.matrix
|
||||
rest_matrix = end_bone.bone.matrix_local
|
||||
|
||||
if end_bone.parent and end_bone.bone.use_inherit_rotation:
|
||||
srcParent = bone.parent
|
||||
parent_mat = srcParent.matrix
|
||||
parent_rest = end_bone.parent.bone.matrix_local
|
||||
parent_rest_inv = parent_rest.inverted()
|
||||
parent_mat_inv = parent_mat.inverted()
|
||||
bake_matrix = parent_mat_inv * bake_matrix
|
||||
rest_matrix = parent_rest_inv * rest_matrix
|
||||
|
||||
rest_matrix_inv = rest_matrix.inverted()
|
||||
bake_matrix = rest_matrix_inv * bake_matrix
|
||||
|
||||
mat = bake_matrix
|
||||
euler = mat.to_euler()
|
||||
limitDict[bone.bone.map][0] = min(limitDict[bone.bone.map][0], euler.x)
|
||||
limitDict[bone.bone.map][1] = min(limitDict[bone.bone.map][1], euler.y)
|
||||
limitDict[bone.bone.map][2] = min(limitDict[bone.bone.map][2], euler.z)
|
||||
limitDict[bone.bone.map][3] = max(limitDict[bone.bone.map][3], euler.x)
|
||||
limitDict[bone.bone.map][4] = max(limitDict[bone.bone.map][4], euler.y)
|
||||
limitDict[bone.bone.map][5] = max(limitDict[bone.bone.map][5], euler.z)
|
||||
for bone in enduser_obj.pose.bones:
|
||||
existingConstraint = [constraint for constraint in bone.constraints if constraint.name == "DOF Limitation"]
|
||||
if existingConstraint:
|
||||
bone.constraints.remove(existingConstraint[0])
|
||||
end_bones = [bone for bone in enduser_obj.pose.bones if bone.name in limitDict.keys()]
|
||||
for bone in end_bones:
|
||||
#~ if not bone.is_in_ik_chain:
|
||||
newCons = bone.constraints.new("LIMIT_ROTATION")
|
||||
newCons.name = "DOF Limitation"
|
||||
newCons.owner_space = "LOCAL"
|
||||
newCons.min_x, newCons.min_y, newCons.min_z, newCons.max_x, newCons.max_y, newCons.max_z = limitDict[bone.name]
|
||||
newCons.use_limit_x = True
|
||||
newCons.use_limit_y = True
|
||||
newCons.use_limit_z = True
|
||||
context.scene.frame_set(c_frame)
|
||||
|
||||
|
||||
# Removes the constraints that were added by limit_dof on the enduser_obj
|
||||
def limit_dof_toggle_off(context, enduser_obj):
|
||||
for bone in enduser_obj.pose.bones:
|
||||
existingConstraint = [constraint for constraint in bone.constraints if constraint.name == "DOF Limitation"]
|
||||
if existingConstraint:
|
||||
bone.constraints.remove(existingConstraint[0])
|
||||
|
||||
|
||||
# Reparameterizes a blender path via keyframing it's eval_time to match a stride_object's forward velocity.
|
||||
# IN: Context, stride object (blender object with location keyframes), path object.
|
||||
def path_editing(context, stride_obj, path):
|
||||
y_fcurve = [fcurve for fcurve in stride_obj.animation_data.action.fcurves if fcurve.data_path == "location"][1]
|
||||
s, e = context.scene.frame_start, context.scene.frame_end # y_fcurve.range()
|
||||
s = int(s)
|
||||
e = int(e)
|
||||
y_s = y_fcurve.evaluate(s)
|
||||
y_e = y_fcurve.evaluate(e)
|
||||
direction = (y_e - y_s) / abs(y_e - y_s)
|
||||
existing_cons = [constraint for constraint in stride_obj.constraints if constraint.type == "FOLLOW_PATH"]
|
||||
for cons in existing_cons:
|
||||
stride_obj.constraints.remove(cons)
|
||||
path_cons = stride_obj.constraints.new("FOLLOW_PATH")
|
||||
if direction < 0:
|
||||
path_cons.forward_axis = "TRACK_NEGATIVE_Y"
|
||||
else:
|
||||
path_cons.forward_axis = "FORWARD_Y"
|
||||
path_cons.target = path
|
||||
path_cons.use_curve_follow = True
|
||||
path.data.path_duration = e - s
|
||||
try:
|
||||
path.data.animation_data.action.fcurves
|
||||
except AttributeError:
|
||||
path.data.keyframe_insert("eval_time", frame=0)
|
||||
eval_time_fcurve = [fcurve for fcurve in path.data.animation_data.action.fcurves if fcurve.data_path == "eval_time"]
|
||||
eval_time_fcurve = eval_time_fcurve[0]
|
||||
totalLength = 0
|
||||
parameterization = {}
|
||||
print("evaluating curve")
|
||||
for t in range(s, e - 1):
|
||||
if s == t:
|
||||
chordLength = 0
|
||||
else:
|
||||
chordLength = (y_fcurve.evaluate(t) - y_fcurve.evaluate(t + 1))
|
||||
totalLength += chordLength
|
||||
parameterization[t] = totalLength
|
||||
for t in range(s + 1, e - 1):
|
||||
if totalLength == 0:
|
||||
print("no forward motion")
|
||||
parameterization[t] /= totalLength
|
||||
parameterization[t] *= e - s
|
||||
parameterization[e] = e - s
|
||||
for t in parameterization.keys():
|
||||
eval_time_fcurve.keyframe_points.insert(frame=t, value=parameterization[t])
|
||||
y_fcurve.mute = True
|
||||
print("finished path editing")
|
||||
|
||||
|
||||
#Animation Stitching
|
||||
#Stitches two retargeted animations together via NLA settings.
|
||||
#IN: enduser_obj, a blender armature that has had two retargets applied.
|
||||
def anim_stitch(context, enduser_obj):
|
||||
stitch_settings = enduser_obj.data.stitch_settings
|
||||
action_1 = stitch_settings.first_action
|
||||
action_2 = stitch_settings.second_action
|
||||
if stitch_settings.stick_bone != "":
|
||||
selected_bone = enduser_obj.pose.bones[stitch_settings.stick_bone]
|
||||
else:
|
||||
selected_bone = enduser_obj.pose.bones[0]
|
||||
scene = context.scene
|
||||
TrackNamesA = enduser_obj.data.mocapNLATracks[action_1]
|
||||
TrackNamesB = enduser_obj.data.mocapNLATracks[action_2]
|
||||
enduser_obj.data.active_mocap = action_1
|
||||
anim_data = enduser_obj.animation_data
|
||||
# add tracks for action 2
|
||||
mocapAction = bpy.data.actions[TrackNamesB.base_track]
|
||||
mocapTrack = anim_data.nla_tracks.new()
|
||||
mocapTrack.name = TrackNamesB.base_track
|
||||
mocapStrip = mocapTrack.strips.new(TrackNamesB.base_track, stitch_settings.blend_frame, mocapAction)
|
||||
mocapStrip.extrapolation = "HOLD_FORWARD"
|
||||
mocapStrip.blend_in = stitch_settings.blend_amount
|
||||
mocapStrip.action_frame_start += stitch_settings.second_offset
|
||||
mocapStrip.action_frame_end += stitch_settings.second_offset
|
||||
constraintTrack = anim_data.nla_tracks.new()
|
||||
constraintTrack.name = TrackNamesB.auto_fix_track
|
||||
constraintAction = bpy.data.actions[TrackNamesB.auto_fix_track]
|
||||
constraintStrip = constraintTrack.strips.new(TrackNamesB.auto_fix_track, stitch_settings.blend_frame, constraintAction)
|
||||
constraintStrip.extrapolation = "HOLD_FORWARD"
|
||||
constraintStrip.blend_in = stitch_settings.blend_amount
|
||||
userTrack = anim_data.nla_tracks.new()
|
||||
userTrack.name = TrackNamesB.manual_fix_track
|
||||
userAction = bpy.data.actions[TrackNamesB.manual_fix_track]
|
||||
userStrip = userTrack.strips.new(TrackNamesB.manual_fix_track, stitch_settings.blend_frame, userAction)
|
||||
userStrip.extrapolation = "HOLD_FORWARD"
|
||||
userStrip.blend_in = stitch_settings.blend_amount
|
||||
#stride bone
|
||||
if enduser_obj.parent:
|
||||
if enduser_obj.parent.name == "stride_bone":
|
||||
stride_bone = enduser_obj.parent
|
||||
stride_anim_data = stride_bone.animation_data
|
||||
stride_anim_data.use_nla = True
|
||||
stride_anim_data.action = None
|
||||
for track in stride_anim_data.nla_tracks:
|
||||
stride_anim_data.nla_tracks.remove(track)
|
||||
actionATrack = stride_anim_data.nla_tracks.new()
|
||||
actionATrack.name = TrackNamesA.stride_action
|
||||
actionAStrip = actionATrack.strips.new(TrackNamesA.stride_action, 0, bpy.data.actions[TrackNamesA.stride_action])
|
||||
actionAStrip.extrapolation = "NOTHING"
|
||||
actionBTrack = stride_anim_data.nla_tracks.new()
|
||||
actionBTrack.name = TrackNamesB.stride_action
|
||||
actionBStrip = actionBTrack.strips.new(TrackNamesB.stride_action, stitch_settings.blend_frame, bpy.data.actions[TrackNamesB.stride_action])
|
||||
actionBStrip.action_frame_start += stitch_settings.second_offset
|
||||
actionBStrip.action_frame_end += stitch_settings.second_offset
|
||||
actionBStrip.blend_in = stitch_settings.blend_amount
|
||||
actionBStrip.extrapolation = "NOTHING"
|
||||
#we need to change the stride_bone's action to add the offset
|
||||
scene.frame_set(stitch_settings.blend_frame - 1)
|
||||
desired_pos = (selected_bone.matrix.to_translation() * enduser_obj.matrix_world)
|
||||
scene.frame_set(stitch_settings.blend_frame)
|
||||
actual_pos = (selected_bone.matrix.to_translation() * enduser_obj.matrix_world)
|
||||
offset = actual_pos - desired_pos
|
||||
|
||||
for i, fcurve in enumerate([fcurve for fcurve in bpy.data.actions[TrackNamesB.stride_action].fcurves if fcurve.data_path == "location"]):
|
||||
print(offset[i], i, fcurve.array_index)
|
||||
for pt in fcurve.keyframe_points:
|
||||
pt.co.y -= offset[i]
|
||||
pt.handle_left.y -= offset[i]
|
||||
pt.handle_right.y -= offset[i]
|
||||
|
||||
|
||||
#Guesses setting for animation stitching via Cross Correlation
|
||||
def guess_anim_stitch(context, enduser_obj):
|
||||
stitch_settings = enduser_obj.data.stitch_settings
|
||||
action_1 = stitch_settings.first_action
|
||||
action_2 = stitch_settings.second_action
|
||||
TrackNamesA = enduser_obj.data.mocapNLATracks[action_1]
|
||||
TrackNamesB = enduser_obj.data.mocapNLATracks[action_2]
|
||||
mocapA = bpy.data.actions[TrackNamesA.base_track]
|
||||
mocapB = bpy.data.actions[TrackNamesB.base_track]
|
||||
curvesA = mocapA.fcurves
|
||||
curvesB = mocapB.fcurves
|
||||
flm, s, data = crossCorrelationMatch(curvesA, curvesB, 10)
|
||||
print("Guessed the following for start and offset: ", s, flm)
|
||||
enduser_obj.data.stitch_settings.blend_frame = flm
|
||||
enduser_obj.data.stitch_settings.second_offset = s
|
@ -1,559 +0,0 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import bpy
|
||||
from mathutils import *
|
||||
from math import radians, acos, pi
|
||||
from bl_operators import nla
|
||||
|
||||
|
||||
def hasIKConstraint(pose_bone):
|
||||
#utility function / predicate, returns True if given bone has IK constraint
|
||||
ik = [constraint for constraint in pose_bone.constraints if constraint.type == "IK"]
|
||||
if ik:
|
||||
return ik[0]
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def createDictionary(perf_arm, end_arm):
|
||||
# clear any old data
|
||||
for end_bone in end_arm.bones:
|
||||
for mapping in end_bone.reverseMap:
|
||||
end_bone.reverseMap.remove(0)
|
||||
|
||||
for perf_bone in perf_arm.bones:
|
||||
#find its match and add perf_bone to the match's mapping
|
||||
if perf_bone.map:
|
||||
end_bone = end_arm.bones[perf_bone.map]
|
||||
newMap = end_bone.reverseMap.add()
|
||||
newMap.name = perf_bone.name
|
||||
end_bone.foot = perf_bone.foot
|
||||
|
||||
#root is the root of the enduser
|
||||
root = end_arm.bones[0].name
|
||||
feetBones = [bone.name for bone in perf_arm.bones if bone.foot]
|
||||
return feetBones, root
|
||||
|
||||
|
||||
def loadMapping(perf_arm, end_arm):
|
||||
for end_bone in end_arm.bones:
|
||||
#find its match and add perf_bone to the match's mapping
|
||||
if end_bone.reverseMap:
|
||||
for perf_bone in end_bone.reverseMap:
|
||||
perf_arm.bones[perf_bone.name].map = end_bone.name
|
||||
|
||||
#creation of intermediate armature
|
||||
# the intermediate armature has the hiearchy of the end user,
|
||||
# does not have rotation inheritence
|
||||
# and bone roll is identical to the performer
|
||||
# its purpose is to copy over the rotations
|
||||
# easily while concentrating on the hierarchy changes
|
||||
|
||||
|
||||
def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene, step):
|
||||
#creates and keyframes an empty with its location
|
||||
#the original position of the tail bone
|
||||
#useful for storing the important data in the original motion
|
||||
#i.e. using this empty to IK the chain to that pos / DEBUG
|
||||
|
||||
#Simple 1to1 retarget of a bone
|
||||
def singleBoneRetarget(inter_bone, perf_bone):
|
||||
perf_world_rotation = perf_bone.matrix
|
||||
inter_world_base_rotation = inter_bone.bone.matrix_local
|
||||
inter_world_base_inv = inter_world_base_rotation.inverted()
|
||||
bake_matrix = (inter_world_base_inv.to_3x3() * perf_world_rotation.to_3x3())
|
||||
return bake_matrix.to_4x4()
|
||||
|
||||
#uses 1to1 and interpolation/averaging to match many to 1 retarget
|
||||
def manyPerfToSingleInterRetarget(inter_bone, performer_bones_s):
|
||||
retarget_matrices = [singleBoneRetarget(inter_bone, perf_bone) for perf_bone in performer_bones_s]
|
||||
lerp_matrix = Matrix()
|
||||
for i in range(len(retarget_matrices) - 1):
|
||||
first_mat = retarget_matrices[i]
|
||||
next_mat = retarget_matrices[i + 1]
|
||||
lerp_matrix = first_mat.lerp(next_mat, 0.5)
|
||||
return lerp_matrix
|
||||
|
||||
#determines the type of hierachy change needed and calls the
|
||||
#right function
|
||||
def retargetPerfToInter(inter_bone):
|
||||
if inter_bone.bone.reverseMap:
|
||||
perf_bone_name = inter_bone.bone.reverseMap
|
||||
# 1 to many not supported yet
|
||||
# then its either a many to 1 or 1 to 1
|
||||
if len(perf_bone_name) > 1:
|
||||
performer_bones_s = [performer_bones[map.name] for map in perf_bone_name]
|
||||
#we need to map several performance bone to a single
|
||||
inter_bone.matrix_basis = manyPerfToSingleInterRetarget(inter_bone, performer_bones_s)
|
||||
else:
|
||||
perf_bone = performer_bones[perf_bone_name[0].name]
|
||||
inter_bone.matrix_basis = singleBoneRetarget(inter_bone, perf_bone)
|
||||
#Some bones have incorrect roll on the source armature, and need to be marked for fixing
|
||||
if inter_bone.bone.twistFix:
|
||||
inter_bone.matrix_basis *= Matrix.Rotation(radians(180), 4, "Y")
|
||||
rot_mode = inter_bone.rotation_mode
|
||||
if rot_mode == "QUATERNION":
|
||||
inter_bone.keyframe_insert("rotation_quaternion")
|
||||
elif rot_mode == "AXIS_ANGLE":
|
||||
inter_bone.keyframe_insert("rotation_axis_angle")
|
||||
else:
|
||||
inter_bone.keyframe_insert("rotation_euler")
|
||||
|
||||
#creates the intermediate armature object
|
||||
inter_obj = enduser_obj.copy()
|
||||
inter_obj.data = inter_obj.data.copy() # duplicate data
|
||||
bpy.context.scene.objects.link(inter_obj)
|
||||
inter_obj.name = "intermediate"
|
||||
bpy.context.scene.objects.active = inter_obj
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
#add some temporary connecting bones in case end user bones are not connected to their parents
|
||||
rollDict = {}
|
||||
print("creating temp bones")
|
||||
for bone in inter_obj.data.edit_bones:
|
||||
if not bone.use_connect and bone.parent:
|
||||
if inter_obj.data.bones[bone.parent.name].reverseMap or inter_obj.data.bones[bone.name].reverseMap:
|
||||
newBone = inter_obj.data.edit_bones.new("Temp")
|
||||
newBone.head = bone.parent.tail
|
||||
newBone.tail = bone.head
|
||||
newBone.parent = bone.parent
|
||||
bone.parent = newBone
|
||||
bone.use_connect = True
|
||||
newBone.use_connect = True
|
||||
rollDict[bone.name] = bone.roll
|
||||
bone.roll = 0
|
||||
#resets roll
|
||||
print("retargeting to intermediate")
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
inter_obj.data.name = "inter_arm"
|
||||
inter_arm = inter_obj.data
|
||||
performer_bones = performer_obj.pose.bones
|
||||
inter_bones = inter_obj.pose.bones
|
||||
#clears inheritance
|
||||
for inter_bone in inter_bones:
|
||||
if inter_bone.bone.reverseMap:
|
||||
inter_bone.bone.use_inherit_rotation = False
|
||||
else:
|
||||
inter_bone.bone.use_inherit_rotation = True
|
||||
|
||||
for t in range(s_frame, e_frame, step):
|
||||
if (t - s_frame) % 10 == 0:
|
||||
print("First pass: retargeting frame {0}/{1}".format(t, e_frame - s_frame))
|
||||
scene.frame_set(t)
|
||||
for bone in inter_bones:
|
||||
retargetPerfToInter(bone)
|
||||
|
||||
return inter_obj
|
||||
|
||||
# this procedure copies the rotations over from the intermediate
|
||||
# armature to the end user one.
|
||||
# As the hierarchies are 1 to 1, this is a simple matter of
|
||||
# copying the rotation, while keeping in mind bone roll, parenting, etc.
|
||||
# TODO: Control Bones: If a certain bone is constrained in a way
|
||||
# that its rotation is determined by another (a control bone)
|
||||
# We should determine the right pos of the control bone.
|
||||
# Scale: ? Should work but needs testing.
|
||||
|
||||
|
||||
def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene, step):
|
||||
inter_bones = inter_obj.pose.bones
|
||||
end_bones = enduser_obj.pose.bones
|
||||
|
||||
#Basic "visual baking" function, for transfering rotations from intermediate to end user
|
||||
def bakeTransform(end_bone):
|
||||
src_bone = inter_bones[end_bone.name]
|
||||
trg_bone = end_bone
|
||||
bake_matrix = src_bone.matrix
|
||||
rest_matrix = trg_bone.bone.matrix_local
|
||||
|
||||
if trg_bone.parent and trg_bone.bone.use_inherit_rotation:
|
||||
srcParent = src_bone.parent
|
||||
if "Temp" in srcParent.name:
|
||||
srcParent = srcParent.parent
|
||||
parent_mat = srcParent.matrix
|
||||
parent_rest = trg_bone.parent.bone.matrix_local
|
||||
parent_rest_inv = parent_rest.inverted()
|
||||
parent_mat_inv = parent_mat.inverted()
|
||||
bake_matrix = parent_mat_inv * bake_matrix
|
||||
rest_matrix = parent_rest_inv * rest_matrix
|
||||
|
||||
rest_matrix_inv = rest_matrix.inverted()
|
||||
bake_matrix = rest_matrix_inv * bake_matrix
|
||||
end_bone.matrix_basis = bake_matrix
|
||||
rot_mode = end_bone.rotation_mode
|
||||
if rot_mode == "QUATERNION":
|
||||
end_bone.keyframe_insert("rotation_quaternion")
|
||||
elif rot_mode == "AXIS_ANGLE":
|
||||
end_bone.keyframe_insert("rotation_axis_angle")
|
||||
else:
|
||||
end_bone.keyframe_insert("rotation_euler")
|
||||
if not end_bone.bone.use_connect:
|
||||
end_bone.keyframe_insert("location")
|
||||
|
||||
for bone in end_bone.children:
|
||||
bakeTransform(bone)
|
||||
|
||||
for t in range(s_frame, e_frame, step):
|
||||
if (t - s_frame) % 10 == 0:
|
||||
print("Second pass: retargeting frame {0}/{1}".format(t, e_frame - s_frame))
|
||||
scene.frame_set(t)
|
||||
end_bone = end_bones[root]
|
||||
end_bone.location = Vector((0, 0, 0))
|
||||
end_bone.keyframe_insert("location")
|
||||
bakeTransform(end_bone)
|
||||
|
||||
#recieves the performer feet bones as a variable
|
||||
# by "feet" I mean those bones that have plants
|
||||
# (they don't move, despite root moving) somewhere in the animation.
|
||||
|
||||
|
||||
def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame, scene, enduser_obj_mat):
|
||||
|
||||
perf_bones = performer_obj.pose.bones
|
||||
end_bones = enduser_obj.pose.bones
|
||||
|
||||
perfRoot = perf_bones[0].name
|
||||
endFeet = [perf_bones[perfBone].bone.map for perfBone in perfFeet]
|
||||
locDictKeys = perfFeet + endFeet + [perfRoot]
|
||||
|
||||
def tailLoc(bone):
|
||||
return bone.center + (bone.vector / 2)
|
||||
|
||||
#Step 1 - we create a dict that contains these keys:
|
||||
#(Performer) Hips, Feet
|
||||
#(End user) Feet
|
||||
# where the values are their world position on each frame in range (s,e)
|
||||
|
||||
locDict = {}
|
||||
for key in locDictKeys:
|
||||
locDict[key] = []
|
||||
|
||||
for t in range(scene.frame_start, scene.frame_end):
|
||||
scene.frame_set(t)
|
||||
for bone in perfFeet:
|
||||
locDict[bone].append(tailLoc(perf_bones[bone]))
|
||||
locDict[perfRoot].append(tailLoc(perf_bones[perfRoot]))
|
||||
for bone in endFeet:
|
||||
locDict[bone].append(tailLoc(end_bones[bone]))
|
||||
|
||||
# now we take our locDict and analyze it.
|
||||
# we need to derive all chains
|
||||
|
||||
def locDeriv(key, t):
|
||||
graph = locDict[key]
|
||||
return graph[t + 1] - graph[t]
|
||||
|
||||
# now find the plant frames, where perfFeet don't move much
|
||||
|
||||
linearAvg = []
|
||||
|
||||
for key in perfFeet:
|
||||
for i in range(len(locDict[key]) - 1):
|
||||
v = locDeriv(key, i)
|
||||
if (v.length < 0.1):
|
||||
hipV = locDeriv(perfRoot, i)
|
||||
endV = locDeriv(perf_bones[key].bone.map, i)
|
||||
#this is a plant frame.
|
||||
#lets see what the original hip delta is, and the corresponding
|
||||
#end bone's delta
|
||||
if endV.length != 0:
|
||||
linearAvg.append(hipV.length / endV.length)
|
||||
|
||||
action_name = performer_obj.animation_data.action.name
|
||||
#is there a stride_bone?
|
||||
if "stride_bone" in bpy.data.objects:
|
||||
stride_action = bpy.data.actions.new("Stride Bone " + action_name)
|
||||
stride_action.use_fake_user = True
|
||||
stride_bone = enduser_obj.parent
|
||||
stride_bone.animation_data.action = stride_action
|
||||
else:
|
||||
bpy.ops.object.add()
|
||||
stride_bone = bpy.context.active_object
|
||||
stride_bone.name = "stride_bone"
|
||||
print(stride_bone)
|
||||
stride_bone.location = enduser_obj_mat.to_translation()
|
||||
print(linearAvg)
|
||||
if linearAvg:
|
||||
#determine the average change in scale needed
|
||||
avg = sum(linearAvg) / len(linearAvg)
|
||||
scene.frame_set(s_frame)
|
||||
initialPos = (tailLoc(perf_bones[perfRoot]) / avg)
|
||||
for t in range(s_frame, e_frame):
|
||||
scene.frame_set(t)
|
||||
#calculate the new position, by dividing by the found ratio between performer and enduser
|
||||
newTranslation = (tailLoc(perf_bones[perfRoot]) / avg)
|
||||
stride_bone.location = enduser_obj_mat * (newTranslation - initialPos)
|
||||
stride_bone.keyframe_insert("location")
|
||||
else:
|
||||
stride_bone.keyframe_insert("location")
|
||||
stride_bone.animation_data.action.name = ("Stride Bone " + action_name)
|
||||
|
||||
return stride_bone
|
||||
|
||||
|
||||
def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene, step):
|
||||
bpy.ops.object.select_name(name=enduser_obj.name, extend=False)
|
||||
end_bones = enduser_obj.pose.bones
|
||||
for pose_bone in end_bones:
|
||||
ik_constraint = hasIKConstraint(pose_bone)
|
||||
if ik_constraint:
|
||||
target_is_bone = False
|
||||
# set constraint target to corresponding empty if targetless,
|
||||
# if not, keyframe current target to corresponding empty
|
||||
perf_bone = pose_bone.bone.reverseMap[-1].name
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
orgLocTrg = originalLocationTarget(pose_bone, enduser_obj)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
if not ik_constraint.target:
|
||||
ik_constraint.target = enduser_obj
|
||||
ik_constraint.subtarget = pose_bone.name + "IK"
|
||||
target = orgLocTrg
|
||||
|
||||
# There is a target now
|
||||
if ik_constraint.subtarget:
|
||||
target = ik_constraint.target.pose.bones[ik_constraint.subtarget]
|
||||
target.bone.use_local_location = False
|
||||
target_is_bone = True
|
||||
else:
|
||||
target = ik_constraint.target
|
||||
|
||||
# bake the correct locations for the ik target bones
|
||||
for t in range(s_frame, e_frame, step):
|
||||
scene.frame_set(t)
|
||||
if target_is_bone:
|
||||
final_loc = pose_bone.tail - target.bone.matrix_local.to_translation()
|
||||
else:
|
||||
final_loc = pose_bone.tail
|
||||
target.location = final_loc
|
||||
target.keyframe_insert("location")
|
||||
ik_constraint.mute = False
|
||||
scene.frame_set(s_frame)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
|
||||
def turnOffIK(enduser_obj):
|
||||
end_bones = enduser_obj.pose.bones
|
||||
for pose_bone in end_bones:
|
||||
ik_constraint = hasIKConstraint(pose_bone)
|
||||
if ik_constraint:
|
||||
ik_constraint.mute = True
|
||||
|
||||
|
||||
#copy the object matrixes and clear them (to be reinserted later)
|
||||
def cleanAndStoreObjMat(performer_obj, enduser_obj):
|
||||
perf_obj_mat = performer_obj.matrix_world.copy()
|
||||
enduser_obj_mat = enduser_obj.matrix_world.copy()
|
||||
zero_mat = Matrix()
|
||||
performer_obj.matrix_world = zero_mat
|
||||
enduser_obj.matrix_world = zero_mat
|
||||
return perf_obj_mat, enduser_obj_mat
|
||||
|
||||
|
||||
#restore the object matrixes after parenting the auto generated IK empties
|
||||
def restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone, scene, s_frame):
|
||||
pose_bones = enduser_obj.pose.bones
|
||||
for pose_bone in pose_bones:
|
||||
if pose_bone.name + "Org" in bpy.data.objects:
|
||||
empty = bpy.data.objects[pose_bone.name + "Org"]
|
||||
empty.parent = stride_bone
|
||||
performer_obj.matrix_world = perf_obj_mat
|
||||
enduser_obj.parent = stride_bone
|
||||
scene.frame_set(s_frame)
|
||||
enduser_obj_mat = enduser_obj_mat.to_3x3().to_4x4() * Matrix.Translation(stride_bone.matrix_world.to_translation())
|
||||
enduser_obj.matrix_world = enduser_obj_mat
|
||||
|
||||
|
||||
#create (or return if exists) the related IK empty to the bone
|
||||
def originalLocationTarget(end_bone, enduser_obj):
|
||||
if not end_bone.name + "IK" in enduser_obj.data.bones:
|
||||
newBone = enduser_obj.data.edit_bones.new(end_bone.name + "IK")
|
||||
newBone.head = end_bone.tail
|
||||
newBone.tail = end_bone.tail + Vector((0, 0.1, 0))
|
||||
else:
|
||||
newBone = enduser_obj.pose.bones[end_bone.name + "IK"]
|
||||
return newBone
|
||||
|
||||
|
||||
#create the specified NLA setup for base animation, constraints and tweak layer.
|
||||
def NLASystemInitialize(enduser_arm, context):
|
||||
enduser_obj = context.active_object
|
||||
NLATracks = enduser_arm.mocapNLATracks[enduser_obj.data.active_mocap]
|
||||
name = NLATracks.name
|
||||
anim_data = enduser_obj.animation_data
|
||||
s_frame = 0
|
||||
print(name)
|
||||
if ("Base " + name) in bpy.data.actions:
|
||||
mocapAction = bpy.data.actions[("Base " + name)]
|
||||
else:
|
||||
print("That retargeted anim has no base action")
|
||||
anim_data.use_nla = True
|
||||
for track in anim_data.nla_tracks:
|
||||
anim_data.nla_tracks.remove(track)
|
||||
mocapTrack = anim_data.nla_tracks.new()
|
||||
mocapTrack.name = "Base " + name
|
||||
NLATracks.base_track = mocapTrack.name
|
||||
mocapStrip = mocapTrack.strips.new("Base " + name, s_frame, mocapAction)
|
||||
constraintTrack = anim_data.nla_tracks.new()
|
||||
constraintTrack.name = "Auto fixes " + name
|
||||
NLATracks.auto_fix_track = constraintTrack.name
|
||||
if ("Auto fixes " + name) in bpy.data.actions:
|
||||
constraintAction = bpy.data.actions[("Auto fixes " + name)]
|
||||
else:
|
||||
constraintAction = bpy.data.actions.new("Auto fixes " + name)
|
||||
constraintAction.use_fake_user = True
|
||||
constraintStrip = constraintTrack.strips.new("Auto fixes " + name, s_frame, constraintAction)
|
||||
constraintStrip.extrapolation = "NOTHING"
|
||||
userTrack = anim_data.nla_tracks.new()
|
||||
userTrack.name = "Manual fixes " + name
|
||||
NLATracks.manual_fix_track = userTrack.name
|
||||
if ("Manual fixes " + name) in bpy.data.actions:
|
||||
userAction = bpy.data.actions[("Manual fixes " + name)]
|
||||
else:
|
||||
userAction = bpy.data.actions.new("Manual fixes " + name)
|
||||
userAction.use_fake_user = True
|
||||
userStrip = userTrack.strips.new("Manual fixes " + name, s_frame, userAction)
|
||||
userStrip.extrapolation = "HOLD"
|
||||
userStrip.blend_type = "ADD"
|
||||
anim_data.nla_tracks.active = constraintTrack
|
||||
anim_data.action_extrapolation = "NOTHING"
|
||||
#set the stride_bone's action
|
||||
if "stride_bone" in bpy.data.objects:
|
||||
stride_bone = bpy.data.objects["stride_bone"]
|
||||
if NLATracks.stride_action:
|
||||
stride_bone.animation_data.action = bpy.data.actions[NLATracks.stride_action]
|
||||
else:
|
||||
NLATracks.stride_action = stride_bone.animation_data.action.name
|
||||
stride_bone.animation_data.action.use_fake_user = True
|
||||
anim_data.action = None
|
||||
|
||||
|
||||
def preAdvancedRetargeting(performer_obj, enduser_obj):
|
||||
createDictionary(performer_obj.data, enduser_obj.data)
|
||||
bones = enduser_obj.pose.bones
|
||||
map_bones = [bone for bone in bones if bone.bone.reverseMap]
|
||||
perf_root = performer_obj.pose.bones[0].name
|
||||
for bone in map_bones:
|
||||
perf_bone = bone.bone.reverseMap[0].name
|
||||
|
||||
cons = bone.constraints.new('COPY_ROTATION')
|
||||
cons.name = "retargetTemp"
|
||||
locks = bone.lock_rotation
|
||||
cons.use_x = not locks[0]
|
||||
cons.use_y = not locks[1]
|
||||
cons.use_z = not locks[2]
|
||||
cons.target = performer_obj
|
||||
cons.subtarget = perf_bone
|
||||
cons.target_space = 'WORLD'
|
||||
cons.owner_space = 'WORLD'
|
||||
|
||||
if (not bone.bone.use_connect) and (perf_bone != perf_root):
|
||||
cons = bone.constraints.new('COPY_LOCATION')
|
||||
cons.name = "retargetTemp"
|
||||
cons.target = performer_obj
|
||||
cons.subtarget = perf_bone
|
||||
cons.use_x = True
|
||||
cons.use_y = True
|
||||
cons.use_z = True
|
||||
cons.target_space = 'LOCAL'
|
||||
cons.owner_space = 'LOCAL'
|
||||
|
||||
|
||||
def prepareForBake(enduser_obj):
|
||||
bones = enduser_obj.pose.bones
|
||||
for bone in bones:
|
||||
bone.bone.select = False
|
||||
map_bones = [bone for bone in bones if bone.bone.reverseMap]
|
||||
for bone in map_bones:
|
||||
for cons in bone.constraints:
|
||||
if "retargetTemp" in cons.name:
|
||||
bone.bone.select = True
|
||||
|
||||
|
||||
def cleanTempConstraints(enduser_obj):
|
||||
bones = enduser_obj.pose.bones
|
||||
map_bones = [bone for bone in bones if bone.bone.reverseMap]
|
||||
for bone in map_bones:
|
||||
for cons in bone.constraints:
|
||||
if "retargetTemp" in cons.name:
|
||||
bone.constraints.remove(cons)
|
||||
|
||||
|
||||
#Main function that runs the retargeting sequence.
|
||||
#If advanced == True, we assume constraint's were already created
|
||||
def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame):
|
||||
perf_arm = performer_obj.data
|
||||
end_arm = enduser_obj.data
|
||||
advanced = end_arm.advancedRetarget
|
||||
step = end_arm.frameStep
|
||||
|
||||
try:
|
||||
enduser_obj.animation_data.action = bpy.data.actions.new("temp")
|
||||
enduser_obj.animation_data.action.use_fake_user = True
|
||||
except:
|
||||
print("no need to create new action")
|
||||
|
||||
print("creating Dictionary")
|
||||
feetBones, root = createDictionary(perf_arm, end_arm)
|
||||
print("cleaning stuff up")
|
||||
perf_obj_mat, enduser_obj_mat = cleanAndStoreObjMat(performer_obj, enduser_obj)
|
||||
if not advanced:
|
||||
turnOffIK(enduser_obj)
|
||||
print("Creating intermediate armature (for first pass)")
|
||||
inter_obj = createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene, step)
|
||||
print("First pass: retargeting from intermediate to end user")
|
||||
retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene, step)
|
||||
else:
|
||||
prepareForBake(enduser_obj)
|
||||
print("Retargeting pose (Advanced Retarget)")
|
||||
nla.bake(s_frame, e_frame, action=enduser_obj.animation_data.action, only_selected=True, do_pose=True, do_object=False, step=step)
|
||||
name = performer_obj.animation_data.action.name
|
||||
enduser_obj.animation_data.action.name = "Base " + name
|
||||
print("Second pass: retargeting root translation and clean up")
|
||||
stride_bone = copyTranslation(performer_obj, enduser_obj, feetBones, root, s_frame, e_frame, scene, enduser_obj_mat)
|
||||
if not advanced:
|
||||
IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene, step)
|
||||
bpy.ops.object.select_name(name=stride_bone.name, extend=False)
|
||||
restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone, scene, s_frame)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
if not advanced:
|
||||
bpy.ops.object.select_name(name=inter_obj.name, extend=False)
|
||||
bpy.ops.object.delete()
|
||||
else:
|
||||
cleanTempConstraints(enduser_obj)
|
||||
bpy.ops.object.select_name(name=enduser_obj.name, extend=False)
|
||||
|
||||
if not name in [tracks.name for tracks in end_arm.mocapNLATracks]:
|
||||
NLATracks = end_arm.mocapNLATracks.add()
|
||||
NLATracks.name = name
|
||||
else:
|
||||
NLATracks = end_arm.mocapNLATracks[name]
|
||||
end_arm.active_mocap = name
|
||||
print("retargeting done!")
|
||||
|
||||
|
||||
def isRigAdvanced(enduser_obj):
|
||||
bones = enduser_obj.pose.bones
|
||||
for bone in bones:
|
||||
for constraint in bone.constraints:
|
||||
if constraint.type != "IK":
|
||||
return True
|
||||
if enduser_obj.data.animation_data:
|
||||
if enduser_obj.data.animation_data.drivers:
|
||||
return True
|
@ -1,850 +0,0 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import bpy
|
||||
|
||||
from bpy.props import *
|
||||
from bpy import *
|
||||
import mocap_constraints
|
||||
import retarget
|
||||
import mocap_tools
|
||||
|
||||
### reloads modules (for testing purposes only)
|
||||
from imp import reload
|
||||
reload(mocap_constraints)
|
||||
reload(retarget)
|
||||
reload(mocap_tools)
|
||||
|
||||
from mocap_constraints import *
|
||||
|
||||
# MocapConstraint class
|
||||
# Defines MocapConstraint datatype, used to add and configute mocap constraints
|
||||
# Attached to Armature data
|
||||
|
||||
|
||||
class MocapConstraint(bpy.types.PropertyGroup):
|
||||
name = bpy.props.StringProperty(name="Name",
|
||||
default="Mocap Fix",
|
||||
description="Name of Mocap Fix",
|
||||
update=setConstraint)
|
||||
constrained_bone = bpy.props.StringProperty(name="Bone",
|
||||
default="",
|
||||
description="Constrained Bone",
|
||||
update=updateConstraintBoneType)
|
||||
constrained_boneB = bpy.props.StringProperty(name="Bone (2)",
|
||||
default="",
|
||||
description="Other Constrained Bone (optional, depends on type)",
|
||||
update=setConstraint)
|
||||
s_frame = bpy.props.IntProperty(name="S",
|
||||
default=0,
|
||||
description="Start frame of Fix",
|
||||
update=setConstraint)
|
||||
e_frame = bpy.props.IntProperty(name="E",
|
||||
default=100,
|
||||
description="End frame of Fix",
|
||||
update=setConstraint)
|
||||
smooth_in = bpy.props.IntProperty(name="In",
|
||||
default=10,
|
||||
description="Amount of frames to smooth in",
|
||||
update=setConstraint,
|
||||
min=0)
|
||||
smooth_out = bpy.props.IntProperty(name="Out",
|
||||
default=10,
|
||||
description="Amount of frames to smooth out",
|
||||
update=setConstraint,
|
||||
min=0)
|
||||
targetMesh = bpy.props.StringProperty(name="Mesh",
|
||||
default="",
|
||||
description="Target of Fix - Mesh (optional, depends on type)",
|
||||
update=setConstraint)
|
||||
active = bpy.props.BoolProperty(name="Active",
|
||||
default=True,
|
||||
description="Fix is active",
|
||||
update=setConstraint)
|
||||
show_expanded = bpy.props.BoolProperty(name="Show Expanded",
|
||||
default=True,
|
||||
description="Fix is fully shown")
|
||||
targetPoint = bpy.props.FloatVectorProperty(name="Point", size=3,
|
||||
subtype="XYZ", default=(0.0, 0.0, 0.0),
|
||||
description="Target of Fix - Point",
|
||||
update=setConstraint)
|
||||
targetDist = bpy.props.FloatProperty(name="Offset",
|
||||
default=0.0,
|
||||
description="Distance and Floor Fixes - Desired offset",
|
||||
update=setConstraint)
|
||||
targetSpace = bpy.props.EnumProperty(
|
||||
items=[("WORLD", "World Space", "Evaluate target in global space"),
|
||||
("LOCAL", "Object space", "Evaluate target in object space"),
|
||||
("constrained_boneB", "Other Bone Space", "Evaluate target in specified other bone space")],
|
||||
name="Space",
|
||||
description="In which space should Point type target be evaluated",
|
||||
update=setConstraint)
|
||||
type = bpy.props.EnumProperty(name="Type of constraint",
|
||||
items=[("point", "Maintain Position", "Bone is at a specific point"),
|
||||
("freeze", "Maintain Position at frame", "Bone does not move from location specified in target frame"),
|
||||
("floor", "Stay above", "Bone does not cross specified mesh object eg floor"),
|
||||
("distance", "Maintain distance", "Target bones maintained specified distance")],
|
||||
description="Type of Fix",
|
||||
update=updateConstraintBoneType)
|
||||
real_constraint = bpy.props.StringProperty()
|
||||
real_constraint_bone = bpy.props.StringProperty()
|
||||
|
||||
|
||||
bpy.utils.register_class(MocapConstraint)
|
||||
|
||||
bpy.types.Armature.mocap_constraints = bpy.props.CollectionProperty(type=MocapConstraint)
|
||||
|
||||
|
||||
# Animation Stitch Settings, used for animation stitching of 2 retargeted animations.
|
||||
class AnimationStitchSettings(bpy.types.PropertyGroup):
|
||||
first_action = bpy.props.StringProperty(name="Action 1",
|
||||
description="First action in stitch")
|
||||
second_action = bpy.props.StringProperty(name="Action 2",
|
||||
description="Second action in stitch")
|
||||
blend_frame = bpy.props.IntProperty(name="Stitch frame",
|
||||
description="Frame to locate stitch on")
|
||||
blend_amount = bpy.props.IntProperty(name="Blend amount",
|
||||
description="Size of blending transitiion, on both sides of the stitch",
|
||||
default=10)
|
||||
second_offset = bpy.props.IntProperty(name="Second offset",
|
||||
description="Frame offset for 2nd animation, where it should start",
|
||||
default=10)
|
||||
stick_bone = bpy.props.StringProperty(name="Stick Bone",
|
||||
description="Bone to freeze during transition",
|
||||
default="")
|
||||
|
||||
bpy.utils.register_class(AnimationStitchSettings)
|
||||
|
||||
|
||||
# MocapNLA Tracks. Stores which tracks/actions are associated with each retargeted animation.
|
||||
class MocapNLATracks(bpy.types.PropertyGroup):
|
||||
name = bpy.props.StringProperty()
|
||||
base_track = bpy.props.StringProperty()
|
||||
auto_fix_track = bpy.props.StringProperty()
|
||||
manual_fix_track = bpy.props.StringProperty()
|
||||
stride_action = bpy.props.StringProperty()
|
||||
|
||||
bpy.utils.register_class(MocapNLATracks)
|
||||
|
||||
|
||||
#Update function for Advanced Retarget boolean variable.
|
||||
def advancedRetargetToggle(self, context):
|
||||
enduser_obj = context.active_object
|
||||
performer_obj = [obj for obj in context.selected_objects if obj != enduser_obj]
|
||||
if enduser_obj is None or len(performer_obj) != 1:
|
||||
print("Need active and selected armatures")
|
||||
return
|
||||
else:
|
||||
performer_obj = performer_obj[0]
|
||||
if self.advancedRetarget:
|
||||
retarget.preAdvancedRetargeting(performer_obj, enduser_obj)
|
||||
else:
|
||||
retarget.cleanTempConstraints(enduser_obj)
|
||||
|
||||
|
||||
#Animation Stitch Settings Property
|
||||
bpy.types.Armature.stitch_settings = bpy.props.PointerProperty(type=AnimationStitchSettings)
|
||||
#Current/Active retargeted animation on the armature
|
||||
bpy.types.Armature.active_mocap = bpy.props.StringProperty(update=retarget.NLASystemInitialize)
|
||||
#Collection of retargeted animations and their NLA Tracks on the armature
|
||||
bpy.types.Armature.mocapNLATracks = bpy.props.CollectionProperty(type=MocapNLATracks)
|
||||
#Advanced retargeting boolean property
|
||||
bpy.types.Armature.advancedRetarget = bpy.props.BoolProperty(default=False, update=advancedRetargetToggle)
|
||||
#frame step - frequency of frames to retarget. Skipping is useful for previewing, faster work etc.
|
||||
bpy.types.Armature.frameStep = smooth_out = bpy.props.IntProperty(name="Frame Skip",
|
||||
default=1,
|
||||
description="Amount of frames to skip - for previewing retargets quickly. 1 is fully sampled",
|
||||
min=1)
|
||||
|
||||
|
||||
def toggleIKBone(self, context):
|
||||
#Update function for IK functionality. Is called when IK prop checkboxes are toggled.
|
||||
if self.IKRetarget:
|
||||
if not self.is_in_ik_chain:
|
||||
print(self.name + " IK toggled ON!")
|
||||
ik = self.constraints.new('IK')
|
||||
#ik the whole chain up to the root, excluding
|
||||
chainLen = 0
|
||||
for parent_bone in self.parent_recursive:
|
||||
chainLen += 1
|
||||
if hasIKConstraint(parent_bone):
|
||||
break
|
||||
deformer_children = [child for child in parent_bone.children if child.bone.use_deform]
|
||||
#~ if len(deformer_children) > 1:
|
||||
#~ break
|
||||
ik.chain_count = chainLen
|
||||
for bone in self.parent_recursive:
|
||||
if bone.is_in_ik_chain:
|
||||
bone.IKRetarget = True
|
||||
else:
|
||||
print(self.name + " IK toggled OFF!")
|
||||
cnstrn_bones = []
|
||||
newChainLength = []
|
||||
if hasIKConstraint(self):
|
||||
cnstrn_bones = [self]
|
||||
elif self.is_in_ik_chain:
|
||||
cnstrn_bones = [child for child in self.children_recursive if hasIKConstraint(child)]
|
||||
for cnstrn_bone in cnstrn_bones:
|
||||
newChainLength.append(cnstrn_bone.parent_recursive.index(self) + 1)
|
||||
if cnstrn_bones:
|
||||
# remove constraint, and update IK retarget for all parents of cnstrn_bone up to chain_len
|
||||
for i, cnstrn_bone in enumerate(cnstrn_bones):
|
||||
print(cnstrn_bone.name)
|
||||
if newChainLength:
|
||||
ik = hasIKConstraint(cnstrn_bone)
|
||||
ik.chain_count = newChainLength[i]
|
||||
else:
|
||||
ik = hasIKConstraint(cnstrn_bone)
|
||||
cnstrn_bone.constraints.remove(ik)
|
||||
cnstrn_bone.IKRetarget = False
|
||||
for bone in cnstrn_bone.parent_recursive:
|
||||
if not bone.is_in_ik_chain:
|
||||
bone.IKRetarget = False
|
||||
|
||||
|
||||
#MocapMap class for storing mapping on enduser performer,
|
||||
# where a bone may be linked to more than one on the performer
|
||||
class MocapMapping(bpy.types.PropertyGroup):
|
||||
name = bpy.props.StringProperty()
|
||||
|
||||
bpy.utils.register_class(MocapMapping)
|
||||
|
||||
#string property for storing performer->enduser mapping
|
||||
bpy.types.Bone.map = bpy.props.StringProperty()
|
||||
#Collection Property for storing enduser->performer mapping
|
||||
bpy.types.Bone.reverseMap = bpy.props.CollectionProperty(type=MocapMapping)
|
||||
#Boolean property for storing foot bone toggle
|
||||
bpy.types.Bone.foot = bpy.props.BoolProperty(name="Foot",
|
||||
description="Marks this bone as a 'foot', which determines retargeted animation's translation",
|
||||
default=False)
|
||||
#Boolean property for storing if this bone is twisted along the y axis,
|
||||
# which can happen due to various sources of performers
|
||||
bpy.types.Bone.twistFix = bpy.props.BoolProperty(name="Twist Fix",
|
||||
description="Fix Twist on this bone",
|
||||
default=False)
|
||||
#Boolean property for toggling ik retargeting for this bone
|
||||
bpy.types.PoseBone.IKRetarget = bpy.props.BoolProperty(name="IK",
|
||||
description="Toggles IK Retargeting method for given bone",
|
||||
update=toggleIKBone, default=False)
|
||||
|
||||
|
||||
def updateIKRetarget():
|
||||
# ensures that Blender constraints and IK properties are in sync
|
||||
# currently runs when module is loaded, should run when scene is loaded
|
||||
# or user adds a constraint to armature. Will be corrected in the future,
|
||||
# once python callbacks are implemented
|
||||
for obj in bpy.data.objects:
|
||||
if obj.pose:
|
||||
bones = obj.pose.bones
|
||||
for pose_bone in bones:
|
||||
if pose_bone.is_in_ik_chain or hasIKConstraint(pose_bone):
|
||||
pose_bone.IKRetarget = True
|
||||
else:
|
||||
pose_bone.IKRetarget = False
|
||||
|
||||
updateIKRetarget()
|
||||
|
||||
|
||||
class MocapPanel(bpy.types.Panel):
|
||||
# Motion capture retargeting panel
|
||||
bl_label = "Mocap tools"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.label("Preprocessing")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = 'EXPAND'
|
||||
row.operator("mocap.samples", text='Samples to Beziers')
|
||||
row.operator("mocap.denoise", text='Clean noise')
|
||||
row.operator("mocap.rotate_fix", text='Fix BVH Axis Orientation')
|
||||
row.operator("mocap.scale_fix", text='Auto scale Performer')
|
||||
row2 = self.layout.row(align=True)
|
||||
row2.operator("mocap.looper", text='Loop animation')
|
||||
row2.operator("mocap.limitdof", text='Constrain Rig')
|
||||
row2.operator("mocap.removelimitdof", text='Unconstrain Rig')
|
||||
self.layout.label("Retargeting")
|
||||
enduser_obj = bpy.context.active_object
|
||||
performer_obj = [obj for obj in bpy.context.selected_objects if obj != enduser_obj]
|
||||
if enduser_obj is None or len(performer_obj) != 1:
|
||||
self.layout.label("Select performer rig and target rig (as active)")
|
||||
else:
|
||||
self.layout.operator("mocap.guessmapping", text="Guess Hiearchy Mapping")
|
||||
row3 = self.layout.row(align=True)
|
||||
column1 = row3.column(align=True)
|
||||
column1.label("Performer Rig")
|
||||
column2 = row3.column(align=True)
|
||||
column2.label("Enduser Rig")
|
||||
performer_obj = performer_obj[0]
|
||||
if performer_obj.data and enduser_obj.data:
|
||||
if performer_obj.data.name in bpy.data.armatures and enduser_obj.data.name in bpy.data.armatures:
|
||||
perf = performer_obj.data
|
||||
enduser_arm = enduser_obj.data
|
||||
perf_pose_bones = enduser_obj.pose.bones
|
||||
for bone in perf.bones:
|
||||
row = self.layout.row()
|
||||
row.prop(data=bone, property='foot', text='', icon='POSE_DATA')
|
||||
row.label(bone.name)
|
||||
row.prop_search(bone, "map", enduser_arm, "bones")
|
||||
row.operator("mocap.selectmap", text='', icon='CURSOR').perf_bone = bone.name
|
||||
label_mod = "FK"
|
||||
if bone.map:
|
||||
pose_bone = perf_pose_bones[bone.map]
|
||||
if pose_bone.is_in_ik_chain:
|
||||
label_mod = "ik chain"
|
||||
if hasIKConstraint(pose_bone):
|
||||
label_mod = "ik end"
|
||||
row.prop(data=bone, property='twistFix', text='', icon='RNA')
|
||||
row.prop(pose_bone, 'IKRetarget')
|
||||
row.label(label_mod)
|
||||
else:
|
||||
row.label(" ")
|
||||
row.label(" ")
|
||||
mapRow = self.layout.row()
|
||||
mapRow.operator("mocap.savemapping", text='Save mapping')
|
||||
mapRow.operator("mocap.loadmapping", text='Load mapping')
|
||||
self.layout.prop(data=performer_obj.animation_data.action, property='name', text='Action Name')
|
||||
self.layout.prop(enduser_arm, "advancedRetarget", text='Advanced Retarget')
|
||||
self.layout.prop(enduser_arm, "frameStep")
|
||||
self.layout.operator("mocap.retarget", text='RETARGET!')
|
||||
|
||||
|
||||
class MocapConstraintsPanel(bpy.types.Panel):
|
||||
#Motion capture constraints panel
|
||||
bl_label = "Mocap Fixes"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
if context.active_object:
|
||||
if context.active_object.data:
|
||||
if context.active_object.data.name in bpy.data.armatures:
|
||||
enduser_obj = context.active_object
|
||||
enduser_arm = enduser_obj.data
|
||||
layout.operator_menu_enum("mocap.addmocapfix", "type")
|
||||
layout.operator("mocap.updateconstraints", text='Update Fixes')
|
||||
bakeRow = layout.row()
|
||||
bakeRow.operator("mocap.bakeconstraints", text='Bake Fixes')
|
||||
bakeRow.operator("mocap.unbakeconstraints", text='Unbake Fixes')
|
||||
layout.separator()
|
||||
for i, m_constraint in enumerate(enduser_arm.mocap_constraints):
|
||||
box = layout.box()
|
||||
headerRow = box.row()
|
||||
headerRow.prop(m_constraint, 'show_expanded', text='', icon='TRIA_DOWN' if m_constraint.show_expanded else 'TRIA_RIGHT', emboss=False)
|
||||
headerRow.prop(m_constraint, 'type', text='')
|
||||
headerRow.prop(m_constraint, 'name', text='')
|
||||
headerRow.prop(m_constraint, 'active', icon='MUTE_IPO_ON' if not m_constraint.active else'MUTE_IPO_OFF', text='', emboss=False)
|
||||
headerRow.operator("mocap.removeconstraint", text="", icon='X', emboss=False).constraint = i
|
||||
if m_constraint.show_expanded:
|
||||
box.separator()
|
||||
box.prop_search(m_constraint, 'constrained_bone', enduser_obj.pose, "bones", icon='BONE_DATA')
|
||||
if m_constraint.type == "distance" or m_constraint.type == "point":
|
||||
box.prop_search(m_constraint, 'constrained_boneB', enduser_obj.pose, "bones", icon='CONSTRAINT_BONE')
|
||||
frameRow = box.row()
|
||||
frameRow.label("Frame Range:")
|
||||
frameRow.prop(m_constraint, 's_frame')
|
||||
frameRow.prop(m_constraint, 'e_frame')
|
||||
smoothRow = box.row()
|
||||
smoothRow.label("Smoothing:")
|
||||
smoothRow.prop(m_constraint, 'smooth_in')
|
||||
smoothRow.prop(m_constraint, 'smooth_out')
|
||||
targetRow = box.row()
|
||||
targetLabelCol = targetRow.column()
|
||||
targetLabelCol.label("Target settings:")
|
||||
targetPropCol = targetRow.column()
|
||||
if m_constraint.type == "floor":
|
||||
targetPropCol.prop_search(m_constraint, 'targetMesh', bpy.data, "objects")
|
||||
if m_constraint.type == "point" or m_constraint.type == "freeze":
|
||||
box.prop(m_constraint, 'targetSpace')
|
||||
if m_constraint.type == "point":
|
||||
targetPropCol.prop(m_constraint, 'targetPoint')
|
||||
if m_constraint.type == "distance" or m_constraint.type == "floor":
|
||||
targetPropCol.prop(m_constraint, 'targetDist')
|
||||
layout.separator()
|
||||
|
||||
|
||||
class ExtraToolsPanel(bpy.types.Panel):
|
||||
# Motion capture retargeting panel
|
||||
bl_label = "Extra Mocap Tools"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator('mocap.pathediting', text="Follow Path")
|
||||
layout.label("Animation Stitching")
|
||||
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
|
||||
if activeIsArmature:
|
||||
enduser_arm = context.active_object.data
|
||||
layout.label("Retargeted Animations:")
|
||||
layout.prop_search(enduser_arm, "active_mocap", enduser_arm, "mocapNLATracks")
|
||||
settings = enduser_arm.stitch_settings
|
||||
layout.prop_search(settings, "first_action", enduser_arm, "mocapNLATracks")
|
||||
layout.prop_search(settings, "second_action", enduser_arm, "mocapNLATracks")
|
||||
layout.prop(settings, "blend_frame")
|
||||
layout.prop(settings, "blend_amount")
|
||||
layout.prop(settings, "second_offset")
|
||||
layout.prop_search(settings, "stick_bone", context.active_object.pose, "bones")
|
||||
layout.operator('mocap.animstitchguess', text="Guess Settings")
|
||||
layout.operator('mocap.animstitch', text="Stitch Animations")
|
||||
|
||||
|
||||
class OBJECT_OT_RetargetButton(bpy.types.Operator):
|
||||
#Retargeting operator. Assumes selected and active armatures, where the performer (the selected one)
|
||||
# has an action for retargeting
|
||||
'''Retarget animation from selected armature to active armature '''
|
||||
bl_idname = "mocap.retarget"
|
||||
bl_label = "Retargets active action from Performer to Enduser"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
s_frame = scene.frame_start
|
||||
e_frame = scene.frame_end
|
||||
enduser_obj = context.active_object
|
||||
performer_obj = [obj for obj in context.selected_objects if obj != enduser_obj]
|
||||
if enduser_obj is None or len(performer_obj) != 1:
|
||||
print("Need active and selected armatures")
|
||||
else:
|
||||
performer_obj = performer_obj[0]
|
||||
s_frame, e_frame = performer_obj.animation_data.action.frame_range
|
||||
s_frame = int(s_frame)
|
||||
e_frame = int(e_frame)
|
||||
if retarget.isRigAdvanced(enduser_obj) and not enduser_obj.data.advancedRetarget:
|
||||
print("Recommended to use Advanced Retargeting method")
|
||||
enduser_obj.data.advancedRetarget = True
|
||||
else:
|
||||
retarget.totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
|
||||
performer_obj = [obj for obj in context.selected_objects if obj != context.active_object]
|
||||
if performer_obj:
|
||||
return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature) and performer_obj[0].animation_data
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class OBJECT_OT_SaveMappingButton(bpy.types.Operator):
|
||||
#Operator for saving mapping to enduser armature
|
||||
'''Save mapping to active armature (for future retargets) '''
|
||||
bl_idname = "mocap.savemapping"
|
||||
bl_label = "Saves user generated mapping from Performer to Enduser"
|
||||
|
||||
def execute(self, context):
|
||||
enduser_obj = bpy.context.active_object
|
||||
performer_obj = [obj for obj in bpy.context.selected_objects if obj != enduser_obj][0]
|
||||
retarget.createDictionary(performer_obj.data, enduser_obj.data)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
|
||||
performer_obj = [obj for obj in context.selected_objects if obj != context.active_object]
|
||||
if performer_obj:
|
||||
return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class OBJECT_OT_LoadMappingButton(bpy.types.Operator):
|
||||
'''Load saved mapping from active armature'''
|
||||
#Operator for loading mapping to enduser armature
|
||||
bl_idname = "mocap.loadmapping"
|
||||
bl_label = "Loads user generated mapping from Performer to Enduser"
|
||||
|
||||
def execute(self, context):
|
||||
enduser_obj = bpy.context.active_object
|
||||
performer_obj = [obj for obj in bpy.context.selected_objects if obj != enduser_obj][0]
|
||||
retarget.loadMapping(performer_obj.data, enduser_obj.data)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
|
||||
performer_obj = [obj for obj in context.selected_objects if obj != context.active_object]
|
||||
if performer_obj:
|
||||
return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class OBJECT_OT_SelectMapBoneButton(bpy.types.Operator):
|
||||
#Operator for setting selected bone in enduser armature to the performer mapping
|
||||
'''Select a bone for faster mapping'''
|
||||
bl_idname = "mocap.selectmap"
|
||||
bl_label = "Select a bone for faster mapping"
|
||||
perf_bone = bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
enduser_obj = bpy.context.active_object
|
||||
performer_obj = [obj for obj in bpy.context.selected_objects if obj != enduser_obj][0]
|
||||
selectedBone = ""
|
||||
for bone in enduser_obj.data.bones:
|
||||
boneVis = bone.layers
|
||||
for i in range(32):
|
||||
if boneVis[i] and enduser_obj.data.layers[i]:
|
||||
if bone.select:
|
||||
selectedBone = bone.name
|
||||
break
|
||||
performer_obj.data.bones[self.perf_bone].map = selectedBone
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
|
||||
performer_obj = [obj for obj in context.selected_objects if obj != context.active_object]
|
||||
if performer_obj:
|
||||
return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class OBJECT_OT_ConvertSamplesButton(bpy.types.Operator):
|
||||
#Operator to convert samples to beziers on the selected object
|
||||
'''Convert active armature's sampled keyframed to beziers'''
|
||||
bl_idname = "mocap.samples"
|
||||
bl_label = "Converts samples / simplifies keyframes to beziers"
|
||||
|
||||
def execute(self, context):
|
||||
mocap_tools.fcurves_simplify(context, context.active_object)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object.animation_data
|
||||
|
||||
|
||||
class OBJECT_OT_LooperButton(bpy.types.Operator):
|
||||
#Operator to trim fcurves which contain a few loops to a single one on the selected object
|
||||
'''Trim active armature's animation to a single cycle, given a cyclic animation (such as a walk cycle)'''
|
||||
bl_idname = "mocap.looper"
|
||||
bl_label = "loops animation / sampled mocap data"
|
||||
|
||||
def execute(self, context):
|
||||
mocap_tools.autoloop_anim()
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object.animation_data
|
||||
|
||||
|
||||
class OBJECT_OT_DenoiseButton(bpy.types.Operator):
|
||||
#Operator to denoise impluse noise on the active object's fcurves
|
||||
'''Denoise active armature's animation. Good for dealing with 'bad' frames inherent in mocap animation'''
|
||||
bl_idname = "mocap.denoise"
|
||||
bl_label = "Denoises sampled mocap data "
|
||||
|
||||
def execute(self, context):
|
||||
mocap_tools.denoise_median()
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object.animation_data
|
||||
|
||||
|
||||
class OBJECT_OT_LimitDOFButton(bpy.types.Operator):
|
||||
#Operator to analyze performer armature and apply rotation constraints on the enduser armature
|
||||
'''Create limit constraints on the active armature from the selected armature's animation's range of motion'''
|
||||
bl_idname = "mocap.limitdof"
|
||||
bl_label = "Analyzes animations Max/Min DOF and adds hard/soft constraints"
|
||||
|
||||
def execute(self, context):
|
||||
performer_obj = [obj for obj in context.selected_objects if obj != context.active_object][0]
|
||||
mocap_tools.limit_dof(context, performer_obj, context.active_object)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
|
||||
performer_obj = [obj for obj in context.selected_objects if obj != context.active_object]
|
||||
if performer_obj:
|
||||
return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class OBJECT_OT_RemoveLimitDOFButton(bpy.types.Operator):
|
||||
#Removes constraints created by above operator
|
||||
'''Removes previously created limit constraints on the active armature'''
|
||||
bl_idname = "mocap.removelimitdof"
|
||||
bl_label = "Removes previously created limit constraints on the active armature"
|
||||
|
||||
def execute(self, context):
|
||||
mocap_tools.limit_dof_toggle_off(context, context.active_object)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
activeIsArmature = False
|
||||
if context.active_object:
|
||||
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
|
||||
return activeIsArmature
|
||||
|
||||
|
||||
class OBJECT_OT_RotateFixArmature(bpy.types.Operator):
|
||||
#Operator to fix common imported Mocap data issue of wrong axis system on active object
|
||||
'''Realign the active armature's axis system to match Blender (Commonly needed after bvh import)'''
|
||||
bl_idname = "mocap.rotate_fix"
|
||||
bl_label = "Rotates selected armature 90 degrees (fix for bvh import)"
|
||||
|
||||
def execute(self, context):
|
||||
mocap_tools.rotate_fix_armature(context.active_object.data)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
return isinstance(context.active_object.data, bpy.types.Armature)
|
||||
|
||||
|
||||
class OBJECT_OT_ScaleFixArmature(bpy.types.Operator):
|
||||
#Operator to scale down the selected armature to match the active one
|
||||
'''Rescale selected armature to match the active animation, for convienence'''
|
||||
bl_idname = "mocap.scale_fix"
|
||||
bl_label = "Scales performer armature to match target armature"
|
||||
|
||||
def execute(self, context):
|
||||
enduser_obj = bpy.context.active_object
|
||||
performer_obj = [obj for obj in bpy.context.selected_objects if obj != enduser_obj][0]
|
||||
mocap_tools.scale_fix_armature(performer_obj, enduser_obj)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
|
||||
performer_obj = [obj for obj in context.selected_objects if obj != context.active_object]
|
||||
if performer_obj:
|
||||
return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class MOCAP_OT_AddMocapFix(bpy.types.Operator):
|
||||
#Operator to add a post-retarget fix
|
||||
'''Add a post-retarget fix - useful for fixing certain artifacts following the retarget'''
|
||||
bl_idname = "mocap.addmocapfix"
|
||||
bl_label = "Add Mocap Fix to target armature"
|
||||
type = bpy.props.EnumProperty(name="Type of Fix",
|
||||
items=[("point", "Maintain Position", "Bone is at a specific point"),
|
||||
("freeze", "Maintain Position at frame", "Bone does not move from location specified in target frame"),
|
||||
("floor", "Stay above", "Bone does not cross specified mesh object eg floor"),
|
||||
("distance", "Maintain distance", "Target bones maintained specified distance")],
|
||||
description="Type of fix")
|
||||
|
||||
def execute(self, context):
|
||||
enduser_obj = bpy.context.active_object
|
||||
enduser_arm = enduser_obj.data
|
||||
new_mcon = enduser_arm.mocap_constraints.add()
|
||||
new_mcon.type = self.type
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
return isinstance(context.active_object.data, bpy.types.Armature)
|
||||
|
||||
|
||||
class OBJECT_OT_RemoveMocapConstraint(bpy.types.Operator):
|
||||
#Operator to remove a post-retarget fix
|
||||
'''Remove this post-retarget fix'''
|
||||
bl_idname = "mocap.removeconstraint"
|
||||
bl_label = "Removes fixes from target armature"
|
||||
constraint = bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
enduser_obj = bpy.context.active_object
|
||||
enduser_arm = enduser_obj.data
|
||||
m_constraints = enduser_arm.mocap_constraints
|
||||
m_constraint = m_constraints[self.constraint]
|
||||
if m_constraint.real_constraint:
|
||||
bone = enduser_obj.pose.bones[m_constraint.real_constraint_bone]
|
||||
cons_obj = getConsObj(bone)
|
||||
removeConstraint(m_constraint, cons_obj)
|
||||
m_constraints.remove(self.constraint)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
return isinstance(context.active_object.data, bpy.types.Armature)
|
||||
|
||||
|
||||
class OBJECT_OT_BakeMocapConstraints(bpy.types.Operator):
|
||||
#Operator to bake all post-retarget fixes
|
||||
'''Bake all post-retarget fixes to the Retarget Fixes NLA Track'''
|
||||
bl_idname = "mocap.bakeconstraints"
|
||||
bl_label = "Bake all fixes to target armature"
|
||||
|
||||
def execute(self, context):
|
||||
bakeConstraints(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
return isinstance(context.active_object.data, bpy.types.Armature)
|
||||
|
||||
|
||||
class OBJECT_OT_UnbakeMocapConstraints(bpy.types.Operator):
|
||||
#Operator to unbake all post-retarget fixes
|
||||
'''Unbake all post-retarget fixes - removes the baked data from the Retarget Fixes NLA Track'''
|
||||
bl_idname = "mocap.unbakeconstraints"
|
||||
bl_label = "Unbake all fixes to target armature"
|
||||
|
||||
def execute(self, context):
|
||||
unbakeConstraints(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
return isinstance(context.active_object.data, bpy.types.Armature)
|
||||
|
||||
|
||||
class OBJECT_OT_UpdateMocapConstraints(bpy.types.Operator):
|
||||
#Operator to update all post-retarget fixes, similar to update dependencies on drivers
|
||||
#Needed because python properties lack certain callbacks and some fixes take a while to recalculate.
|
||||
'''Updates all post-retarget fixes - needed after changes to armature object or pose'''
|
||||
bl_idname = "mocap.updateconstraints"
|
||||
bl_label = "Updates all fixes to target armature - neccesary to take under consideration changes to armature object or pose"
|
||||
|
||||
def execute(self, context):
|
||||
updateConstraints(context.active_object, context)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
return isinstance(context.active_object.data, bpy.types.Armature)
|
||||
|
||||
|
||||
class OBJECT_OT_GuessHierachyMapping(bpy.types.Operator):
|
||||
#Operator which calls heurisitic function to guess mapping between 2 armatures
|
||||
'''Attemps to auto figure out hierarchy mapping'''
|
||||
bl_idname = "mocap.guessmapping"
|
||||
bl_label = "Attemps to auto figure out hierarchy mapping"
|
||||
|
||||
def execute(self, context):
|
||||
enduser_obj = bpy.context.active_object
|
||||
performer_obj = [obj for obj in bpy.context.selected_objects if obj != enduser_obj][0]
|
||||
mocap_tools.guessMapping(performer_obj, enduser_obj)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
|
||||
performer_obj = [obj for obj in context.selected_objects if obj != context.active_object]
|
||||
if performer_obj:
|
||||
return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class OBJECT_OT_PathEditing(bpy.types.Operator):
|
||||
#Operator which calls path editing function, making active object follow the selected curve.
|
||||
'''Sets active object (stride object) to follow the selected curve'''
|
||||
bl_idname = "mocap.pathediting"
|
||||
bl_label = "Sets active object (stride object) to follow the selected curve"
|
||||
|
||||
def execute(self, context):
|
||||
path = [obj for obj in context.selected_objects if obj != context.active_object][0]
|
||||
mocap_tools.path_editing(context, context.active_object, path)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
selected_objs = [obj for obj in context.selected_objects if obj != context.active_object and isinstance(obj.data, bpy.types.Curve)]
|
||||
return selected_objs
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class OBJECT_OT_AnimationStitchingButton(bpy.types.Operator):
|
||||
#Operator which calls stitching function, combining 2 animations onto the NLA.
|
||||
'''Stitches two defined animations into a single one via alignment of NLA Tracks'''
|
||||
bl_idname = "mocap.animstitch"
|
||||
bl_label = "Stitches two defined animations into a single one via alignment of NLA Tracks"
|
||||
|
||||
def execute(self, context):
|
||||
mocap_tools.anim_stitch(context, context.active_object)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
activeIsArmature = False
|
||||
if context.active_object:
|
||||
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
|
||||
if activeIsArmature:
|
||||
stitch_settings = context.active_object.data.stitch_settings
|
||||
return (stitch_settings.first_action and stitch_settings.second_action)
|
||||
return False
|
||||
|
||||
|
||||
class OBJECT_OT_GuessAnimationStitchingButton(bpy.types.Operator):
|
||||
#Operator which calls stitching function heuristic, setting good values for above operator.
|
||||
'''Guesses the stitch frame and second offset for animation stitch'''
|
||||
bl_idname = "mocap.animstitchguess"
|
||||
bl_label = "Guesses the stitch frame and second offset for animation stitch"
|
||||
|
||||
def execute(self, context):
|
||||
mocap_tools.guess_anim_stitch(context, context.active_object)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
activeIsArmature = False
|
||||
if context.active_object:
|
||||
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
|
||||
if activeIsArmature:
|
||||
stitch_settings = context.active_object.data.stitch_settings
|
||||
return (stitch_settings.first_action and stitch_settings.second_action)
|
||||
return False
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
@ -136,7 +136,7 @@ void sound_read_waveform(struct bSound* sound);
|
||||
|
||||
int sound_get_channels(struct bSound* sound);
|
||||
|
||||
void sound_update_scene(struct Main* bmain, struct Scene* scene);
|
||||
void sound_update_scene(struct Scene* scene);
|
||||
|
||||
void* sound_get_factory(void* sound);
|
||||
|
||||
|
@ -968,7 +968,7 @@ static void scene_update_tagged_recursive(Main *bmain, Scene *scene, Scene *scen
|
||||
scene_update_drivers(bmain, scene);
|
||||
|
||||
/* update sound system animation */
|
||||
sound_update_scene(bmain, scene);
|
||||
sound_update_scene(scene);
|
||||
}
|
||||
|
||||
/* this is called in main loop, doing tagged updates before redraw */
|
||||
|
@ -3150,18 +3150,14 @@ void seq_update_sound_bounds_all(Scene *scene)
|
||||
{
|
||||
Editing *ed = scene->ed;
|
||||
|
||||
if(ed)
|
||||
{
|
||||
if(ed) {
|
||||
Sequence *seq;
|
||||
|
||||
for(seq = ed->seqbase.first; seq; seq = seq->next)
|
||||
{
|
||||
if(seq->type == SEQ_META)
|
||||
{
|
||||
for(seq = ed->seqbase.first; seq; seq = seq->next) {
|
||||
if(seq->type == SEQ_META) {
|
||||
seq_update_sound_bounds_recursive(scene, seq);
|
||||
}
|
||||
else if(ELEM(seq->type, SEQ_SOUND, SEQ_SCENE))
|
||||
{
|
||||
else if(ELEM(seq->type, SEQ_SOUND, SEQ_SCENE)) {
|
||||
seq_update_sound_bounds(scene, seq);
|
||||
}
|
||||
}
|
||||
@ -3170,8 +3166,7 @@ void seq_update_sound_bounds_all(Scene *scene)
|
||||
|
||||
void seq_update_sound_bounds(Scene* scene, Sequence *seq)
|
||||
{
|
||||
if(seq->scene_sound)
|
||||
{
|
||||
if(seq->scene_sound) {
|
||||
sound_move_scene_sound(scene, seq->scene_sound, seq->startdisp, seq->enddisp, seq->startofs + seq->anim_startofs);
|
||||
/* mute is set in seq_update_muting_recursive */
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "DNA_sound_types.h"
|
||||
#include "DNA_speaker_types.h"
|
||||
|
||||
#define WITH_AUDASPACE
|
||||
#ifdef WITH_AUDASPACE
|
||||
# include "AUD_C-API.h"
|
||||
#endif
|
||||
@ -649,9 +650,10 @@ int sound_get_channels(struct bSound* sound)
|
||||
return info.specs.channels;
|
||||
}
|
||||
|
||||
void sound_update_scene(struct Main* bmain, struct Scene* scene)
|
||||
void sound_update_scene(struct Scene* scene)
|
||||
{
|
||||
Object* ob;
|
||||
Base* base;
|
||||
NlaTrack* track;
|
||||
NlaStrip* strip;
|
||||
Speaker* speaker;
|
||||
@ -660,8 +662,9 @@ void sound_update_scene(struct Main* bmain, struct Scene* scene)
|
||||
void* handle;
|
||||
float quat[4];
|
||||
|
||||
for(ob = bmain->object.first; ob; ob = ob->id.next)
|
||||
for(base = FIRSTBASE; base; base=base->next)
|
||||
{
|
||||
ob = base->object;
|
||||
if(ob->type == OB_SPEAKER)
|
||||
{
|
||||
if(ob->adt)
|
||||
|
@ -45,37 +45,14 @@ set(INC
|
||||
../../blender/gpu
|
||||
../../blender/imbuf
|
||||
../../blender/makesdna
|
||||
../../blender/makesrna
|
||||
../../blender/python
|
||||
../../blender/python/generic
|
||||
../../blender/python/mathutils
|
||||
../../../intern/container
|
||||
../../../intern/guardedalloc
|
||||
../../../intern/container
|
||||
../../../source/gameengine/Rasterizer/RAS_OpenGLRasterizer
|
||||
../../../source/gameengine/Converter
|
||||
../../../source/gameengine/BlenderRoutines
|
||||
../../../source/blender/imbuf
|
||||
../../../intern/moto/include
|
||||
../../../intern/string
|
||||
../../../source/gameengine/Ketsji
|
||||
../../../source/blender/blenlib
|
||||
../../../source/blender/blenfont
|
||||
../../../source/blender/blenkernel
|
||||
../../../source/blender/python
|
||||
../../../source/blender/python/generic
|
||||
../../../source/blender
|
||||
../../../source/blender/makesdna
|
||||
../../../source/blender/makesrna
|
||||
../../../source/gameengine/Rasterizer
|
||||
../../../source/gameengine/GameLogic
|
||||
../../../source/gameengine/Expressions
|
||||
../../../source/gameengine/Ketsji/KXNetwork
|
||||
../../../source/gameengine/Network
|
||||
../../../source/gameengine/SceneGraph
|
||||
../../../source/gameengine/Physics/common
|
||||
../../../source/gameengine/Network/LoopBackNetwork
|
||||
../../../source/blender/blenloader
|
||||
../../../source/blender/gpu
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
@ -96,7 +96,6 @@ const char KX_KetsjiEngine::m_profileLabels[tc_numCategories][15] = {
|
||||
"Animations:", // tc_animations
|
||||
"Network:", // tc_network
|
||||
"Scenegraph:", // tc_scenegraph
|
||||
"Sound:", // tc_sound
|
||||
"Rasterizer:", // tc_rasterizer
|
||||
"Services:", // tc_services
|
||||
"Overhead:", // tc_overhead
|
||||
@ -692,8 +691,6 @@ else
|
||||
else
|
||||
if(scene->getSuspendedTime()==0.0)
|
||||
scene->setSuspendedTime(m_clockTime);
|
||||
|
||||
DoSound(scene);
|
||||
|
||||
m_logger->StartLog(tc_services, m_kxsystem->GetTimeInSeconds(), true);
|
||||
}
|
||||
@ -769,8 +766,6 @@ else
|
||||
if(scene->getSuspendedTime()==0.0)
|
||||
scene->setSuspendedTime(m_clockTime);
|
||||
|
||||
DoSound(scene);
|
||||
|
||||
m_logger->StartLog(tc_services, m_kxsystem->GetTimeInSeconds(), true);
|
||||
}
|
||||
}
|
||||
@ -1003,35 +998,6 @@ const STR_String& KX_KetsjiEngine::GetExitString()
|
||||
}
|
||||
|
||||
|
||||
|
||||
void KX_KetsjiEngine::DoSound(KX_Scene* scene)
|
||||
{
|
||||
m_logger->StartLog(tc_sound, m_kxsystem->GetTimeInSeconds(), true);
|
||||
|
||||
// nothing to do here, everything relative now...
|
||||
/*KX_Camera* cam = scene->GetActiveCamera();
|
||||
if (!cam)
|
||||
return;
|
||||
|
||||
AUD_I3DDevice* dev = AUD_get3DDevice();
|
||||
if(dev)
|
||||
{
|
||||
AUD_Vector3 v;
|
||||
//float q[4];
|
||||
//cam->NodeGetWorldPosition().getValue(v.get());
|
||||
dev->setListenerLocation(v);
|
||||
|
||||
//cam->GetLinearVelocity().getValue(v.get());
|
||||
dev->setListenerVelocity(v);
|
||||
|
||||
//cam->NodeGetWorldOrientation().getRotation().getValue(q);
|
||||
//dev->setListenerOrientation(AUD_Quaternion(q[3], q[0], q[1], q[2]));
|
||||
dev->setListenerOrientation(AUD_Quaternion());
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
void KX_KetsjiEngine::SetBackGround(KX_WorldInfo* wi)
|
||||
{
|
||||
if (wi->hasWorld())
|
||||
|
@ -153,7 +153,6 @@ private:
|
||||
tc_animations,
|
||||
tc_network,
|
||||
tc_scenegraph,
|
||||
tc_sound,
|
||||
tc_rasterizer,
|
||||
tc_services, // time spend in miscelaneous activities
|
||||
tc_overhead, // profile info drawing overhead
|
||||
@ -199,7 +198,6 @@ private:
|
||||
void RenderDebugProperties();
|
||||
void RenderShadowBuffers(KX_Scene *scene);
|
||||
void SetBackGround(KX_WorldInfo* worldinfo);
|
||||
void DoSound(KX_Scene* scene);
|
||||
void RenderFonts(KX_Scene* scene);
|
||||
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user