bvh 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,124 +1,124 @@
1
- class Bvh
2
- class Motion
3
-
4
- class Frame
5
- # The array of ChannelData objects for this frame: one ChannelData instance for each bone.
6
- attr_reader :channel_data
7
-
8
- def initialize()
9
- @channel_data = []
10
- end
11
-
12
- # Creates a copy of this frame, including a dup of its channel data.
13
- def copy
14
- ret = self.class.new
15
- channel_data.each do |datum|
16
- ret.channel_data << datum.dup
17
- end
18
- ret
19
- end
20
-
21
- # call-seq:
22
- # frame + frame => new_frame
23
- # frame - frame => new_frame
24
- # frame / frame => new_frame
25
- # frame * frame => new_frame
26
- # frame + number => new_frame
27
- # frame - number => new_frame
28
- # frame / number => new_frame
29
- # frame * number => new_frame
30
- #
31
- # Performs arithmetic on this frame with the target. The second operand may be either a number or another Frame.
32
- # If the target is a number, then that number is added to, subtracted from, multiplied with, or divided against
33
- # each channel of each ChannelData object in this frame.
34
- #
35
- # If the target is another Frame, the arithmetic looks something like this (simplified):
36
- # return_value.channel_data[0] = frame1.channel_data[0] * frame2.channel_data[0]
37
- # return_value.channel_data[0] = frame1.channel_data[1] * frame2.channel_data[1]
38
- # return_value.channel_data[0] = frame1.channel_data[2] * frame2.channel_data[2]
39
- # . . .
40
- #
41
- # Both frames must contain the same number of ChannelData instances, and each instance must
42
- # have the same number of channels, and each instance must also reference the same bone.
43
- #
44
- # Returns a new frame containing the result.
45
- #
46
- def arithmetic_proc(target)
47
- # Fooled you! I metaprogrammed it to save some typing!
48
- end
49
- undef arithmetic_proc
50
-
51
- [:+, :-, :/, :*].each do |operator|
52
- define_method operator do |target|
53
- if target.kind_of?(Frame)
54
- unless channel_data.length == target.channel_data.length
55
- raise "Expected both frames to have the same amount of data"
56
- end
57
- ret = Frame.new
58
- channel_data.each_with_index do |datum, index|
59
- ret.channel_data << datum.send(operator, target.channel_data[index])
60
- end
61
- ret
62
- else
63
- ret = Frame.new
64
- channel_data.each { |datum| ret.channel_data << datum.send(operator, target) }
65
- end
66
- ret
67
- end
68
- end
69
-
70
- # Returns the channel data for the specified bone.
71
- def channel_data_for(bone)
72
- @channel_data.each { |c| return c if c.bone == bone }
73
- raise "Channel data for bone not found: #{bone.name}"
74
- end
75
-
76
- # Returns the relative, or local, transform matrix for the specified bone in this frame.
77
- def relative_transform_matrix(bone)
78
- return channel_data_for(bone).relative_transform_matrix
79
- end
80
-
81
- # Returns the transform matrix of the root node multiplied with its children recursively
82
- # down to the specified bone, the result being the total transformation in worldspace for this frame.
83
- def absolute_transform_matrix(bone)
84
- relative = relative_transform_matrix(bone)
85
- if bone.parent then absolute_transform_matrix(bone.parent) * relative
86
- else relative
87
- end
88
- end
89
-
90
- # Modifies a single attribute of the channel data for this bone in this frame. Returns self.
91
- #
92
- # Ex:
93
- # bvh.last_frame.set_channel bone, 'Xrotation', 180 # => sets the bone to a 180 deg rotation around the X-axis
94
- def set_channel(bone, channel, theta)
95
- channel_data_for(bone).set_channel(channel, theta)
96
- end
97
-
98
- # Retrives the value for a specific channel for a specific bone within this frame.
99
- def get_channel(bone, channel)
100
- channel_data_for(bone).get_channel(channel)
101
- end
102
-
103
- # Modifies the channel data for this bone in this frame, resulting in a rotation around the specified
104
- # channel.
105
- #
106
- # Ex:
107
- # bvh.last_frame.set_channel bone, 'Xrotation', 180 # => rotates the bone by 180 deg rotation around the X-axis
108
- def rotate!(bone, channel, theta)
109
- set_channel(bone, channel, channel_data_for(bone).get_channel(channel) + theta)
110
- end
111
-
112
- # Adds x, y and z to the X, Y and Z position channels for this bone in this frame, resulting in a "movement" or
113
- # translation.
114
- def translate!(bone, x, y, z)
115
- set_channel(bone, 'Xposition', channel_data_for(bone).get_channel('Xposition')+x)
116
- set_channel(bone, 'Yposition', channel_data_for(bone).get_channel('Yposition')+y)
117
- set_channel(bone, 'Zposition', channel_data_for(bone).get_channel('Zposition')+z)
118
- end
119
-
120
- alias transform_matrix absolute_transform_matrix
121
- alias local_transform_matrix relative_transform_matrix
122
- end
123
- end
124
- end
1
+ class Bvh
2
+ class Motion
3
+
4
+ class Frame
5
+ # The array of ChannelData objects for this frame: one ChannelData instance for each bone.
6
+ attr_reader :channel_data
7
+
8
+ def initialize()
9
+ @channel_data = []
10
+ end
11
+
12
+ # Creates a copy of this frame, including a dup of its channel data.
13
+ def copy
14
+ ret = self.class.new
15
+ channel_data.each do |datum|
16
+ ret.channel_data << datum.dup
17
+ end
18
+ ret
19
+ end
20
+
21
+ # call-seq:
22
+ # frame + frame => new_frame
23
+ # frame - frame => new_frame
24
+ # frame / frame => new_frame
25
+ # frame * frame => new_frame
26
+ # frame + number => new_frame
27
+ # frame - number => new_frame
28
+ # frame / number => new_frame
29
+ # frame * number => new_frame
30
+ #
31
+ # Performs arithmetic on this frame with the target. The second operand may be either a number or another Frame.
32
+ # If the target is a number, then that number is added to, subtracted from, multiplied with, or divided against
33
+ # each channel of each ChannelData object in this frame.
34
+ #
35
+ # If the target is another Frame, the arithmetic looks something like this (simplified):
36
+ # return_value.channel_data[0] = frame1.channel_data[0] * frame2.channel_data[0]
37
+ # return_value.channel_data[0] = frame1.channel_data[1] * frame2.channel_data[1]
38
+ # return_value.channel_data[0] = frame1.channel_data[2] * frame2.channel_data[2]
39
+ # . . .
40
+ #
41
+ # Both frames must contain the same number of ChannelData instances, and each instance must
42
+ # have the same number of channels, and each instance must also reference the same bone.
43
+ #
44
+ # Returns a new frame containing the result.
45
+ #
46
+ def arithmetic_proc(target)
47
+ # Fooled you! I metaprogrammed it to save some typing!
48
+ end
49
+ undef arithmetic_proc
50
+
51
+ [:+, :-, :/, :*].each do |operator|
52
+ define_method operator do |target|
53
+ if target.kind_of?(Frame)
54
+ unless channel_data.length == target.channel_data.length
55
+ raise "Expected both frames to have the same amount of data"
56
+ end
57
+ ret = Frame.new
58
+ channel_data.each_with_index do |datum, index|
59
+ ret.channel_data << datum.send(operator, target.channel_data[index])
60
+ end
61
+ ret
62
+ else
63
+ ret = Frame.new
64
+ channel_data.each { |datum| ret.channel_data << datum.send(operator, target) }
65
+ end
66
+ ret
67
+ end
68
+ end
69
+
70
+ # Returns the channel data for the specified bone.
71
+ def channel_data_for(bone)
72
+ @channel_data.each { |c| return c if c.bone == bone }
73
+ raise "Channel data for bone not found: #{bone.name}"
74
+ end
75
+
76
+ # Returns the relative, or local, transform matrix for the specified bone in this frame.
77
+ def relative_transform_matrix(bone)
78
+ return channel_data_for(bone).relative_transform_matrix
79
+ end
80
+
81
+ # Returns the transform matrix of the root node multiplied with its children recursively
82
+ # down to the specified bone, the result being the total transformation in worldspace for this frame.
83
+ def absolute_transform_matrix(bone)
84
+ relative = relative_transform_matrix(bone)
85
+ if bone.parent then absolute_transform_matrix(bone.parent) * relative
86
+ else relative
87
+ end
88
+ end
89
+
90
+ # Modifies a single attribute of the channel data for this bone in this frame. Returns self.
91
+ #
92
+ # Ex:
93
+ # bvh.last_frame.set_channel bone, 'Xrotation', 180 # => sets the bone to a 180 deg rotation around the X-axis
94
+ def set_channel(bone, channel, theta)
95
+ channel_data_for(bone).set_channel(channel, theta)
96
+ end
97
+
98
+ # Retrives the value for a specific channel for a specific bone within this frame.
99
+ def get_channel(bone, channel)
100
+ channel_data_for(bone).get_channel(channel)
101
+ end
102
+
103
+ # Modifies the channel data for this bone in this frame, resulting in a rotation around the specified
104
+ # channel.
105
+ #
106
+ # Ex:
107
+ # bvh.last_frame.set_channel bone, 'Xrotation', 180 # => rotates the bone by 180 deg rotation around the X-axis
108
+ def rotate!(bone, channel, theta)
109
+ set_channel(bone, channel, channel_data_for(bone).get_channel(channel) + theta)
110
+ end
111
+
112
+ # Adds x, y and z to the X, Y and Z position channels for this bone in this frame, resulting in a "movement" or
113
+ # translation.
114
+ def translate!(bone, x, y, z)
115
+ set_channel(bone, 'Xposition', channel_data_for(bone).get_channel('Xposition')+x)
116
+ set_channel(bone, 'Yposition', channel_data_for(bone).get_channel('Yposition')+y)
117
+ set_channel(bone, 'Zposition', channel_data_for(bone).get_channel('Zposition')+z)
118
+ end
119
+
120
+ alias transform_matrix absolute_transform_matrix
121
+ alias local_transform_matrix relative_transform_matrix
122
+ end
123
+ end
124
+ end
@@ -1,177 +1,177 @@
1
- class Bvh
2
- class Parser
3
- attr_reader :filename
4
- attr_reader :source
5
-
6
- def initialize(file)
7
- @filename = file
8
- @source = File.read(file)
9
- end
10
-
11
- def parse(bvh)
12
- @bvh = bvh
13
-
14
- # a little touch-up...
15
- s = source
16
- # it's tempting to just downcase the whole string, but that'd change the case of object names, which might be
17
- # bad for the end user. So we'll swap out specific keywords instead.
18
- s.gsub!(/^((\s*)(root|offset|channels|joint|end site|hierarchy)(([^\n\{]*)(\n|\{)))/mi) do
19
- match = $~
20
- "#{match[2]}#{match[3].downcase.gsub(/\s/, '_')} \"#{match[5].strip}\"#{match[6]}"
21
- end
22
- # make { . . . } into proper Ruby blocks
23
- s.gsub!(/[\n\s]*\{/m, ' do').gsub!(/[\n\s]*\}/m, "\nend")
24
-
25
- # Finally, handle the MOTION segment, which can be treated as a single method call.
26
- s.gsub!(/^((\s*)(motion)(.*))/mi) do
27
- "#{$~[2]}#{$~[3].downcase} <<-EOF\n#{$~[4].strip}\nEOF\n"
28
- end
29
-
30
- eval(s, binding, @filename, 1)
31
- end
32
-
33
- private
34
- attr_reader :bvh, :mode, :current_node
35
-
36
- def bone(type, name, &block)
37
- raise ArgumentError, "#{type} #{name} is unexpected at this time" unless mode == :hierarchy
38
- bone = type.new(name)
39
-
40
- if current_node.nil?
41
- bvh.create_skeleton!.root = bone
42
- else
43
- current_node.add_joint!(bone)
44
- end
45
-
46
- @current_node, bone = bone, @current_node
47
- instance_eval(&block)
48
- @current_node = bone
49
- end
50
-
51
- # Ex:
52
- # root "joint-pelvis" do
53
- # offset "0.00 0.00 0.00"
54
- # channels "6 Xposition Ypoisition Zposition Zrotation Xrotation Yrotation"
55
- # joint "joint-spine3" do
56
- # . . .
57
- # end
58
- # end
59
- def root(name, &block)
60
- @current_node = nil
61
- bone Bvh::Skeleton::Bone, name, &block
62
- end
63
-
64
- # Ex:
65
- # root "joint-pelvis" do
66
- # . . .
67
- # joint "joint-spine3" do
68
- # offset "0.00 -10.65 0.00"
69
- # channels "3 Zrotation Xrotation Yrotation"
70
- # end_site do
71
- # . . .
72
- # end
73
- # end
74
- # end
75
- def joint(name, &block)
76
- bone Bvh::Skeleton::Bone, name, &block
77
- end
78
-
79
- # Ex:
80
- # root "joint-pelvis" do
81
- # . . .
82
- # joint "joint-spine3" do
83
- # . . .
84
- # end_site do
85
- # offset "0.00 -7.00 0.00"
86
- # end
87
- # end
88
- # end
89
- def end_site(*unused, &block)
90
- bone Bvh::Skeleton::Bone, nil, &block
91
- end
92
-
93
- def hierarchy(*unused)
94
- @mode = :hierarchy
95
- end
96
-
97
- def offset(val)
98
- raise ArgumentError, "OFFSET is unexpected at this time" unless current_node
99
- raise ArgumentError, "Already have OFFSET data for this node" unless current_node.offset.length == 0
100
- current_node.offset.concat val.split.collect { |i| i.to_f }
101
- end
102
-
103
- def channels(val)
104
- raise ArgumentError, "CHANNELS is unexpected at this time" unless current_node
105
- raise ArgumentError, "Already have CHANNELS data for this node" unless current_node.channels.length == 0
106
- vals = val.split
107
- count = vals.shift.to_i # how many?
108
- raise ArgumentError, "Expected #{count} channels, found #{vals.length}" if vals.length != count
109
- current_node.channels.concat vals
110
- end
111
-
112
- def motion(motion_data)
113
- raise ArgumentError, "Motion data is unexpected at this time" unless mode == :hierarchy
114
- @mode = :motion
115
- frame_count = nil
116
- motion_data.each_line do |line|
117
- line = line.downcase.strip
118
- words = line.split
119
- case words.first
120
- when 'frames:'
121
- if words[1] =~ /[^0-9]/
122
- raise ArgumentError, "Only one positive numeric integer value expected at this time (not #{words[1]})"
123
- end
124
- frame_count = words[1].to_i
125
- when 'frame'
126
- case words[1]
127
- when 'time:'
128
- if words[2] =~ /[^0-9\.]/
129
- raise ArgumentError, "Only one positive numeric decimal value expected at this time (not #{words[2]})"
130
- end
131
- bvh.frame_time = words[2].to_f
132
- else
133
- raise ArgumentError, "Motion data not understood: #{line}"
134
- end
135
- when nil, '' # blank line, do nothing
136
- else
137
- channels = words.collect do |w|
138
- raise ArgumentError, "Only numeric values are expected at this time (not #{w})" if w =~ /[^0-9\.\-]/
139
- w.to_f
140
- end
141
- add_frame(channels)
142
- end
143
- end
144
- unless frame_count.nil? or frame_count == bvh.frame_count
145
- raise ArgumentError, "Expected %s frames, found %s" % [frame_count, bvh.frame_count]
146
- end
147
- unless frame_count == 0
148
- raise ArgumentError, "Frame time is 0; this would result in infinite FPS!" if bvh.frame_time == 0
149
- raise ArgumentError, "Frame time is #{bvh.frame_time}! Should be a positive number." if bvh.frame_time < 0
150
- end
151
- end
152
-
153
- def add_frame(channels)
154
- frame = Bvh::Motion::Frame.new
155
- frame.channel_data.concat channel_data(channels, bvh.root)
156
- bvh.frames << frame
157
- raise ArgumentError, "Not enough channels: Need #{-channels.length} more" if channels.length < 0
158
- raise ArgumentError, "Too many channels: #{channels.length} unaccounted for" if channels.length > 0
159
- end
160
-
161
- # this is now a binding.
162
- # def channel_data(channels, bone)
163
- # return [] unless bone.respond_to? :channels
164
- # data = Bvh::Motion::ChannelData.new(bone)
165
- # bone.channels.each do |channel|
166
- # data[channel] = channels.shift
167
- # end
168
- # r = [ data ]
169
- # if bone.respond_to? :joints
170
- # bone.joints.each do |child|
171
- # r.concat channel_data(channels, child)
172
- # end
173
- # end
174
- # r
175
- # end
176
- end
177
- end
1
+ class Bvh
2
+ class Parser
3
+ attr_reader :filename
4
+ attr_reader :source
5
+
6
+ def initialize(file)
7
+ @filename = file
8
+ @source = File.read(file)
9
+ end
10
+
11
+ def parse(bvh)
12
+ @bvh = bvh
13
+
14
+ # a little touch-up...
15
+ s = source
16
+ # it's tempting to just downcase the whole string, but that'd change the case of object names, which might be
17
+ # bad for the end user. So we'll swap out specific keywords instead.
18
+ s.gsub!(/^((\s*)(root|offset|channels|joint|end site|hierarchy)(([^\n\{]*)(\n|\{)))/mi) do
19
+ match = $~
20
+ "#{match[2]}#{match[3].downcase.gsub(/\s/, '_')} \"#{match[5].strip}\"#{match[6]}"
21
+ end
22
+ # make { . . . } into proper Ruby blocks
23
+ s.gsub!(/[\n\s]*\{/m, ' do').gsub!(/[\n\s]*\}/m, "\nend")
24
+
25
+ # Finally, handle the MOTION segment, which can be treated as a single method call.
26
+ s.gsub!(/^((\s*)(motion)(.*))/mi) do
27
+ "#{$~[2]}#{$~[3].downcase} <<-EOF\n#{$~[4].strip}\nEOF\n"
28
+ end
29
+
30
+ eval(s, binding, @filename, 1)
31
+ end
32
+
33
+ private
34
+ attr_reader :bvh, :mode, :current_node
35
+
36
+ def bone(type, name, &block)
37
+ raise ArgumentError, "#{type} #{name} is unexpected at this time" unless mode == :hierarchy
38
+ bone = type.new(name)
39
+
40
+ if current_node.nil?
41
+ bvh.create_skeleton!.root = bone
42
+ else
43
+ current_node.add_joint!(bone)
44
+ end
45
+
46
+ @current_node, bone = bone, @current_node
47
+ instance_eval(&block)
48
+ @current_node = bone
49
+ end
50
+
51
+ # Ex:
52
+ # root "joint-pelvis" do
53
+ # offset "0.00 0.00 0.00"
54
+ # channels "6 Xposition Ypoisition Zposition Zrotation Xrotation Yrotation"
55
+ # joint "joint-spine3" do
56
+ # . . .
57
+ # end
58
+ # end
59
+ def root(name, &block)
60
+ @current_node = nil
61
+ bone Bvh::Skeleton::Bone, name, &block
62
+ end
63
+
64
+ # Ex:
65
+ # root "joint-pelvis" do
66
+ # . . .
67
+ # joint "joint-spine3" do
68
+ # offset "0.00 -10.65 0.00"
69
+ # channels "3 Zrotation Xrotation Yrotation"
70
+ # end_site do
71
+ # . . .
72
+ # end
73
+ # end
74
+ # end
75
+ def joint(name, &block)
76
+ bone Bvh::Skeleton::Bone, name, &block
77
+ end
78
+
79
+ # Ex:
80
+ # root "joint-pelvis" do
81
+ # . . .
82
+ # joint "joint-spine3" do
83
+ # . . .
84
+ # end_site do
85
+ # offset "0.00 -7.00 0.00"
86
+ # end
87
+ # end
88
+ # end
89
+ def end_site(*unused, &block)
90
+ bone Bvh::Skeleton::Bone, nil, &block
91
+ end
92
+
93
+ def hierarchy(*unused)
94
+ @mode = :hierarchy
95
+ end
96
+
97
+ def offset(val)
98
+ raise ArgumentError, "OFFSET is unexpected at this time" unless current_node
99
+ raise ArgumentError, "Already have OFFSET data for this node" unless current_node.offset.length == 0
100
+ current_node.offset.concat val.split.collect { |i| i.to_f }
101
+ end
102
+
103
+ def channels(val)
104
+ raise ArgumentError, "CHANNELS is unexpected at this time" unless current_node
105
+ raise ArgumentError, "Already have CHANNELS data for this node" unless current_node.channels.length == 0
106
+ vals = val.split
107
+ count = vals.shift.to_i # how many?
108
+ raise ArgumentError, "Expected #{count} channels, found #{vals.length}" if vals.length != count
109
+ current_node.channels.concat vals
110
+ end
111
+
112
+ def motion(motion_data)
113
+ raise ArgumentError, "Motion data is unexpected at this time" unless mode == :hierarchy
114
+ @mode = :motion
115
+ frame_count = nil
116
+ motion_data.each_line do |line|
117
+ line = line.downcase.strip
118
+ words = line.split
119
+ case words.first
120
+ when 'frames:'
121
+ if words[1] =~ /[^0-9]/
122
+ raise ArgumentError, "Only one positive numeric integer value expected at this time (not #{words[1]})"
123
+ end
124
+ frame_count = words[1].to_i
125
+ when 'frame'
126
+ case words[1]
127
+ when 'time:'
128
+ if words[2] =~ /[^0-9\.]/
129
+ raise ArgumentError, "Only one positive numeric decimal value expected at this time (not #{words[2]})"
130
+ end
131
+ bvh.frame_time = words[2].to_f
132
+ else
133
+ raise ArgumentError, "Motion data not understood: #{line}"
134
+ end
135
+ when nil, '' # blank line, do nothing
136
+ else
137
+ channels = words.collect do |w|
138
+ raise ArgumentError, "Only numeric values are expected at this time (not #{w})" if w =~ /[^0-9\.\-]/
139
+ w.to_f
140
+ end
141
+ add_frame(channels)
142
+ end
143
+ end
144
+ unless frame_count.nil? or frame_count == bvh.frame_count
145
+ raise ArgumentError, "Expected %s frames, found %s" % [frame_count, bvh.frame_count]
146
+ end
147
+ unless frame_count == 0
148
+ raise ArgumentError, "Frame time is 0; this would result in infinite FPS!" if bvh.frame_time == 0
149
+ raise ArgumentError, "Frame time is #{bvh.frame_time}! Should be a positive number." if bvh.frame_time < 0
150
+ end
151
+ end
152
+
153
+ def add_frame(channels)
154
+ frame = Bvh::Motion::Frame.new
155
+ frame.channel_data.concat channel_data(channels, bvh.root)
156
+ bvh.frames << frame
157
+ raise ArgumentError, "Not enough channels: Need #{-channels.length} more" if channels.length < 0
158
+ raise ArgumentError, "Too many channels: #{channels.length} unaccounted for" if channels.length > 0
159
+ end
160
+
161
+ # this is now a binding.
162
+ # def channel_data(channels, bone)
163
+ # return [] unless bone.respond_to? :channels
164
+ # data = Bvh::Motion::ChannelData.new(bone)
165
+ # bone.channels.each do |channel|
166
+ # data[channel] = channels.shift
167
+ # end
168
+ # r = [ data ]
169
+ # if bone.respond_to? :joints
170
+ # bone.joints.each do |child|
171
+ # r.concat channel_data(channels, child)
172
+ # end
173
+ # end
174
+ # r
175
+ # end
176
+ end
177
+ end