I have been trying to write a Python script to replace the walkcycle action modifers that have been removed from 2.6x, so that a character can move along a Bezier curve. However, I am getting some confusing results. I have the source code of my Python script pasted below and a .blend file attached that I was using the script on. I successfully got the script to move the character so that its feet and back move in the correct locations along the path, but I can’t get them to rotate properly. I have a source action with a character walking in a straight line and my goal is to create a new action with it walking on a curve. The main function that does this is the one called MakeDeformedAct which starts on line 149.

I suspect that line 200 of my script is incorrect:

```
new_mat = RotMat*Mathutils.Quaternion(w,x,y,z).toMatrix()
```

This line is meant to rotate the bones with respect to the Bezier curve. RotMat is a rotation matrix that determines how the bone must rotate to match the curve. w, x, y, and z are the quaternion values of the rotation of the bone in the source action. I have the matrix of the individual bones on lines 157-58:

```
BoneMatrix = arm.bones[b].matrix['ARMATURESPACE'].rotationPart()
BoneMatrix1 = BoneMatrix.copy().invert()
```

I suspect that I need to include this in line 200, but the results were even more confusing when I did that. I printed some values when the script ran from lines 206-08.

```
print new_mat.toEuler()
print (new_mat*BoneMatrix).toEuler()
print (BoneMatrix*new_mat).toEuler()
```

For the bone back4 on frame 41, the results were:

```
[0.000000, -0.000000, -103.284904](euler)
[-161.764252, 76.715096, -0.000029](euler)
[90.000008, 0.000000, 76.715103](euler)
```

However, at frame 41, back4 was keyed at (58,0,0), which was different from the (0,0,-103.3) printed by the script. I think the result I want is (0,-103.3,0) but I don’t know how to change line 200 to get that result. Does anyone know why the Euler I printed does not match the rotation values I got from transform properties?

For reference, I am still using 2.49b because I use PyConstraints, but I think the .blend will still work fine without them.

```
import Blender
from Blender import Mathutils
from Blender import Armature
from Blender.Armature import NLA
from Blender.Armature.NLA import *
import math
from math import *
def bezier_length(co,delta):
length = 0.0
for i in range(len(co)-1):
for j in range(delta):
length += calc_dist_3d(bezier_get_pt(co,i+(j+0.0)/delta,0),bezier_get_pt(co,i+(j+1.0)/delta,0))
return length
def bezier_get_pt(co,t,option):
if option == 0:
t0 = int(t)
t1 = t-t0
b = [0.0,0.0,0.0]
if t >= len(co)-1.0:
b = [co[-1][1][0],co[-1][1][1],co[-1][1][2]]
else:
for i in [0,1,2]:
b[i] = (1-t1)**3*co[t0][1][i] + 3*(1-t1)**2*t1*co[t0][2][i] + 3*(1-t1)*t1**2*co[t0+1][0][i] + t1**3*co[t0+1][1][i]
return b
def bezierPtFmLen(co,t,delta):
length = 0.0
i = 0
j = 0
while length < t:
if j >= delta:
j = 0
i += 1
length += calc_dist_3d(bezier_get_pt(co,i+(j+0.0)/delta,0),bezier_get_pt(co,i+(j+1.0)/delta,0))
j += 1
return bezier_get_pt(co,i+(j+0.0)/delta,0)
def member(a,b):
for i in b:
if i==a:
return 1
return 0
def lines_2d_isect(l1,l2):
if l1[0] == "" and l2[0] == "":
return ""
elif l1[0] == "":
return [l1[1],l2[0]*l1[1]+l2[1]]
elif l2[0] == "":
return [l2[1],l1[0]*l2[1]+l1[1]]
else:
if l1[0] != l2[0]:
x = (l2[1]-l1[1])/(l1[0]-l2[0])
return [x,l1[0]*x+l1[0]]
else:
return ""
def get_2d_line(c,d):
if c[1] != d[1]:
m = (d[1]-c[1])/(d[0]-c[0])
b = c[1]-m*c[0]
return [m,b]
else:
return ["",c[1]]
def get_2d_line_value(l,x):
if l[0] != "":
return l[0]*x+l[1]
else:
return ""
def get_2d_line_perp(l,x):
if l[0] != "" and l[0] != 0:
y = l[0]*x+l[1]
return [-1/l[0],y-x/l[0]]
elif l[0] == 0:
return ["",x]
else:
return [0.0,x]
def calc_dist_3d(a,b):
return ( (a[0]-b[0])**2 + (a[1]-b[1])**2 + (a[2]-b[2])**2 )**0.5
def bezierDer(co,dist,delta):
length = 0.0
i = 0
j = 0
while length < dist:
if j >= delta:
j = 0
i += 1
length += calc_dist_3d(bezier_get_pt(co,i+(j+0.0)/delta,0),bezier_get_pt(co,i+(j+1.0)/delta,0))
j += 1
d = [0.0,0.0,0.0]
t = i+(j+1.0)/delta
t0 = int(t)
t1 = t-t0
if t0 < len(co)-1:
for k in [0,1,2]:
d[k] = -3*(1-t1)**2*co[t0][1][k] + (-6*(1-t1)*t1+3*(1-t1)**2)*co[t0][2][k] + (-3*t1**2+6*(1-t1)*t1)*co[t0+1][0][k] + 3*t1**2*co[t0+1][1][k]
d = normalize(d)
else:
print "Error in bezierDer()"
return d
def normalize(v):
sum = 0.0
for i in v:
sum += i**2
w = []
for i in v:
w.append(i/sum**0.5)
return w
def GetRotationMatrix(angle,axis):
M = [ [0,0,0] , [0,0,0] , [0,0,0] ]
t = angle/180.0*3.14159265
M[0][0] = cos(t)+axis[0]**2*(1-cos(t))
M[0][1] = axis[0]*axis[1]*(1-cos(t))-axis[2]*sin(t)
M[0][2] = axis[1]*axis[2]*(1-cos(t))-axis[1]*sin(t)
M[1][0] = axis[1]*axis[0]*(1-cos(t))-axis[2]*sin(t)
M[1][1] = cos(t)+axis[1]**2*(1-cos(t))
M[1][2] = axis[1]*axis[2]*(1-cos(t))-axis[0]*sin(t)
M[2][0] = axis[2]*axis[0]*(1-cos(t))-axis[1]*sin(t)
M[2][1] = axis[2]*axis[1]*(1-cos(t))-axis[2]*sin(t)
M[2][2] = cos(t)+axis[2]**2*(1-cos(t))
matrix = Mathutils.Matrix(M[0],M[1],M[2])
return matrix
def deform_pt(orig,move,forth,bezier,delta):
forthV = Mathutils.Vector(forth[0],forth[1],forth[2])
moveOffsetV = Mathutils.Vector(move[0]-orig[0],move[1]-orig[1],move[2]-orig[2])
dist = Mathutils.ProjectVecs(moveOffsetV,forthV).length
CurvePivot = bezierPtFmLen(bezier,dist,delta)
CurvePivotV = Mathutils.Vector(CurvePivot[0],CurvePivot[1],CurvePivot[2])
bezierOriginV = Mathutils.Vector(bezier[0][1][0],bezier[0][1][1],bezier[0][1][2])
moveV = Mathutils.Vector(move[0],move[1],move[2])
Deform1 = moveV - dist*forthV
der = bezierDer(bezier,dist,delta)
derV = Mathutils.Vector(der[0],der[1],der[2])
angle = -Mathutils.AngleBetweenVecs(derV,forthV)
axis = forthV.cross(derV).normalize()
RotMat = GetRotationMatrix(angle,axis)
Deform2 = RotMat*Deform1
return [(Deform2+CurvePivotV),derV]
def MakeDeformedAction(SourceAct,NewAct,Armature,bones,forth,bezier,delta):
armO = Blender.Object.Get(Armature)
arm = armO.getData()
act = GetActions()[SourceAct]
a = NewAction(NewAct)
a.setActive(armO)
forthV = Mathutils.Vector(forth[0],forth[1],forth[2])
for b in bones:
BoneMatrix = arm.bones[b].matrix['ARMATURESPACE'].rotationPart()
BoneMatrix1 = BoneMatrix.copy().invert()
orig = arm.bones[b].head['ARMATURESPACE']
print "orig", orig
ipo = act.getChannelIpo(b)
frames = getIpoFrames(ipo)
for f in frames:
LocX = 0
LocY = 0
LocZ = 0
for i in ipo:
if i.name == "LocX": LocX = i
if i.name == "LocY": LocY = i
if i.name == "LocZ": LocZ = i
x = LocX[f]
y = LocY[f]
z = LocZ[f]
move = BoneMatrix*Mathutils.Vector(x,y,z)+orig
new_co = deform_pt(orig,move,forth,bezier,delta)
pose = armO.getPose()
new_co1 = BoneMatrix*(new_co[0]-orig)
pose.bones[b].loc[:] = [new_co1[0],new_co1[1],new_co1[2]]
pose.bones[b].insertKey(armO,int(f),Blender.Object.Pose.LOC)
angle = -Mathutils.AngleBetweenVecs(new_co[1],forthV)
axis = forthV.cross(new_co[1]).normalize()
RotMat = GetRotationMatrix(angle,axis)
RotW = 0
RotX = 0
RotY = 0
RotZ = 0
w = 0
x = 0
y = 0
z = 0
for i in ipo:
if i.name == "QuatW": RotW = i
if i.name == "QuatX": RotX = i
if i.name == "QuatY": RotY = i
if i.name == "QuatZ": RotZ = i
if RotW != 0: w = RotW[f]
if RotX != 0: x = RotX[f]
if RotY != 0: y = RotY[f]
if RotZ != 0: z = RotZ[f]
new_mat = RotMat*Mathutils.Quaternion(w,x,y,z).toMatrix()
new_euler = new_mat.toEuler()
new_quat = new_mat.toQuat()
pose.bones[b].quat[:] = [new_quat[0],new_quat[1],new_quat[2],new_quat[3]]
pose.bones[b].insertKey(armO,int(f),Blender.Object.Pose.ROT)
print f,b
print new_mat.toEuler()
print (new_mat*BoneMatrix).toEuler()
print (BoneMatrix*new_mat).toEuler()
def getIpoFrames(ipo):
frames = []
for i in ipo:
for j in i.bezierPoints:
if not(member(j.vec[1][0],frames)):
frames.append(j.vec[1][0])
frames.sort()
return frames
b = []
b.append([[-0.1,0.0,0.0] , [0.0,0.0,0.0] , [0.1,0.0,0.0] ])
b.append([[0.9,0.0,0.0] , [1.0,0.0,0.0] , [1.1,0.0,0.0] ])
c = []
c.append([ [-1.5,-0.5,0.0] , [-1.0,0.0,0.0] , [-0.5,0.5,0.0] ])
c.append([ [0.0,0.0,0.0] , [1.0,0.0,0.0] , [2.0,0.0,0.0] ])
c.append([ [2.0,-2.4,0.0] , [1.0,-2.4,0.0] , [0.0,-2.4,0.0] ])
d = []
d.append([ [0.0,1.0,0.0] , [0.0,0.0,0.0] , [0.0,-1.0,0.0] ])
d.append([ [0.707,-2.293,0.0] , [0.0,-3.0,0.0] , [-0.707,-3.707,0.0] ])
d.append([ [-1.0,-3.0,0.0] , [-2.0,-3.0,0.0] , [-3.0,-3.0,0.0] ])
print "Length", bezier_length(d,1000)
forth = [0.0,-1.0,0.0]
bones = ["back4","FootIK.R","FootIK.L"]
MakeDeformedAction("Walk_IK4","ModAct1","Person5.0.arm",bones,forth,d,1000)
"""
orig = [0.21,0.041,0.0]
move = [0.5,-4.0,0.0]
print bezierPtFmLen(d,3.0,1000)
print deform_pt(orig,move,forth,d,1000)
"""
```

### Attachments

curve_test_20120224.blend (255 KB)