Class: Bvh::Motion::ChannelData

Inherits:
Hash
  • Object
show all
Defined in:
lib/bvh/motion/channel_data.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bone, *args, &block) ⇒ ChannelData

Returns a new instance of ChannelData.



7
8
9
10
# File 'lib/bvh/motion/channel_data.rb', line 7

def initialize(bone, *args, &block)
  @bone = bone
  super(*args, &block)
end

Instance Attribute Details

#boneObject (readonly)

The bone that is related to this channel data.



5
6
7
# File 'lib/bvh/motion/channel_data.rb', line 5

def bone
  @bone
end

Instance Method Details

#arithmetic_proc(target) ⇒ Object

call-seq:

channel_data + channel_data  => new_channel_data
channel_data - channel_data  => new_channel_data
channel_data / channel_data  => new_channel_data
channel_data * channel_data  => new_channel_data
channel_data + number => new_channel_data
channel_data - number => new_channel_data
channel_data / number => new_channel_data
channel_data * number => new_channel_data

Performs arithmetic on this frame with the target. The second operand may be either a number or another ChannelData. If the target is a number, then that number is added to, subtracted from, multiplied with, or divided against each channel of this ChannelData.

If the target is another ChannelData, the arithmetic looks something like this:

return_value['Xposition'] = channel_data_one['Xposition'] * channel_data_two['Xposition']
return_value['Yposition'] = channel_data_one['Yposition'] * channel_data_two['Yposition']
return_value['Zposition'] = channel_data_one['Zposition'] * channel_data_two['Zposition']
. . .

Both objects must contain the same number of channels, and must also reference the same bone.

Returns a new instance of ChannelData containing the result.



36
37
38
# File 'lib/bvh/motion/channel_data.rb', line 36

def arithmetic_proc(target)
  # Fooled you again! I metaprogrammed it to save some typing!
end

#get_channel(channel) ⇒ Object

Retrieves and returns the specified channel datum, raising an error if its key is not found. If the channel key is a symbol, it is converted to a string before the lookup is performed.



66
67
68
69
70
# File 'lib/bvh/motion/channel_data.rb', line 66

def get_channel(channel)
  channel = channel.to_s if channel.kind_of?(Symbol)
  raise "Channel not found: #{channel} (expected one of #{self.keys.inspect})" unless self.keys.include?(channel)
  self[channel]
end

#relative_transform_matrixObject Also known as: local_transform_matrix

Returns the transform matrix for this bone’s channel data. See also Frame#relative_transform_matrix and Frame#absolute_transform_matrix.

The resultant matrix needs to be multiplied against the parent bone’s transform matrix in order to be accurate to worldspace. Otherwise it’s only accurate in local space. If you’re not sure what that means, then use Frame#absolute_transform_matrix instead.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/bvh/motion/channel_data.rb', line 95

def relative_transform_matrix()
  # theta is retrieved from the set of numbers loaded from the BVH file, or is supplied directly
  #
  # R is the matrix calculated for a rotation of theta degrees around the vector V
  #    This is performed on right, view and up vectors, multiplying the three matrices together
  #    to construct the rotation matrix for this bone. Since the calculations are done in world
  #    space, the right, up and view vectors are [1,0,0]; [0,1,0]; [0,0,1], respectively. No reason
  #    we couldn't attach a Camera object to this bone and figure it out that way, but it'd actually
  #    be more work to create and maintain the camera than to just calculate the matrix. Obviously,
  #    the resultant matrix needs to be multiplied against the parent bone's rotation matrix in order
  #    to be accurate to worldspace. Otherwise it's only accurate in local space.
  #
  # R = [tx^2 + c,  txy - sz,  txz + sy,  0]
  #     [txy + sz,  ty^2 + c,  tyz - sx,  0]
  #     [txz - sy,  tyz + sx,  tz^2 + c,  0]
  #     [       0,         0,         0,  1]
  #
  # where c = cos theta,
  #       s = sin theta,
  #       t = 1 - c
  # [x,y,z] = a unit vector on the axis of rotation

  # bone.channels.each so that we don't lose order of operation
  r = Matrix.identity(4)
  bone.channels.each do |chan|
    v = nil
    case chan
      when 'Xrotation' then v = [1,0,0] # right vector
      when 'Yrotation' then v = [0,1,0] # up vector
      when 'Zrotation' then v = [0,0,1] # view vector
      else next # ignore nonrotational values. To my knowledge, this includes only position values.
    end
    theta = self[chan]
    x, y, z = v
    c, s, t = Math.cos(theta), Math.sin(theta), 1 - Math.cos(theta)
    mat = Matrix.identity(4)
    mat[0,0], mat[0,1], mat[0,2] = t*(x**2) + c, t*x*y - s*z, t*x*z + s*y
    mat[1,0], mat[1,1], mat[1,2] = t*x*y + s*z, t*(y**2) + c, t*y*z - s*x
    mat[2,0], mat[2,1], mat[2,2] = t*x*z - s*y, t*y*z + s*x, t*(z**2) + c
    r *= mat
  end

  # Finally, the last row is simply set to the translation and/or bone offset.
  r[0,3] = bone.offset[0] + (self.key?("Xposition") ? self['Xposition'] : 0)
  r[1,3] = bone.offset[1] + (self.key?("Yposition") ? self['Yposition'] : 0)
  r[2,3] = bone.offset[2] + (self.key?("Zposition") ? self['Zposition'] : 0)
  r
end

#rotate!(channel, theta) ⇒ Object

Modifies the specified channel datum, resulting in a rotation around the specified channel.



73
74
75
76
77
78
79
80
# File 'lib/bvh/motion/channel_data.rb', line 73

def rotate!(channel, theta)
  theta = get_channel(channel)+theta
  # this doesn't really have an effect, but may help keep the file from getting junked.
  if theta >= 360 then theta %= 360
  elsif theta <= -360 then theta %= -360
  end
  set_channel(channel, theta)
end

#set_channel(channel, theta) ⇒ Object

Sets the specified channel to a value of theta, and then returns self.



58
59
60
61
62
# File 'lib/bvh/motion/channel_data.rb', line 58

def set_channel(channel, theta)
  channel = channel.to_s if channel.kind_of? Symbol
  raise "Channel not found: #{channel} (expected one of #{self.keys.inspect})" unless self.keys.include? channel
  self[channel] = theta
end

#translate!(x, y, z) ⇒ Object

Adds x, y and z to the X, Y and Z position channels, resulting in a “movement” or translation.



83
84
85
86
87
# File 'lib/bvh/motion/channel_data.rb', line 83

def translate!(x, y, z)
  set_channel('Xposition', get_channel('Xposition')+x)
  set_channel('Yposition', get_channel('Yposition')+y)
  set_channel('Zposition', get_channel('Zposition')+z)
end