collada 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,33 +20,38 @@
20
20
 
21
21
  require 'collada/parser/visual_scene'
22
22
  require 'collada/parser/geometry'
23
+ require 'collada/parser/animation'
24
+ require 'collada/parser/controller'
23
25
 
24
26
  module Collada
25
27
  module Parser
26
28
  class Library
27
29
  SECTIONS = {
28
30
  :visual_scenes => ['COLLADA/library_visual_scenes/visual_scene', VisualScene],
29
- :geometries => ['COLLADA/library_geometries/geometry', Geometry]
31
+ :geometries => ['COLLADA/library_geometries/geometry', Geometry],
32
+ :animations => ['COLLADA/library_animations/animation', Animation],
33
+ :controllers => ['COLLADA/library_controllers/controller', Controller],
30
34
  }
31
35
 
32
- def initialize(sections = {})
36
+ def initialize(doc, sections = {})
37
+ @doc = doc
33
38
  @sections = sections
34
39
  end
35
40
 
36
41
  def [] key
37
- @sections[key]
42
+ @sections.fetch(key) do
43
+ path, klass = SECTIONS[key]
44
+
45
+ raise ArgumentError.new("Invalid section name #{key}!") unless klass
46
+
47
+ @sections[key] = OrderedMap.parse(@doc, path) do |element|
48
+ klass.parse(@doc, element)
49
+ end
50
+ end
38
51
  end
39
52
 
40
53
  def self.parse(doc)
41
- sections = {}
42
-
43
- SECTIONS.each do |key, (path, klass)|
44
- sections[key] = OrderedMap.parse(doc, path) do |element|
45
- klass.parse(doc, element)
46
- end
47
- end
48
-
49
- return Library.new(sections)
54
+ return Library.new(doc)
50
55
  end
51
56
  end
52
57
  end
@@ -67,131 +67,6 @@ module Collada
67
67
 
68
68
  class Geometry
69
69
  class Mesh
70
- class Parameter
71
- def initialize(name, type)
72
- @name = name
73
- @type = type
74
- end
75
-
76
- attr :name
77
- attr :type
78
-
79
- def self.parse(doc, element)
80
- self.new(element.attributes['name'], element.attributes['type'].to_sym)
81
- end
82
- end
83
-
84
- class Accessor
85
- include Enumerable
86
-
87
- def initialize(array, parameters, options = {})
88
- @array = array
89
- @parameters = parameters
90
- @names = parameters.collect{|parameter| parameter.name}
91
-
92
- @offset = (options[:offset] || 0).to_i
93
- @stride = (options[:stride] || parameters.size).to_i
94
- end
95
-
96
- attr :array
97
- attr :parameters
98
- attr :names
99
-
100
- attr :offset
101
- attr :stride
102
-
103
- def read index
104
- base = @offset + (index * @stride)
105
- values = @array[base, @parameters.size]
106
-
107
- @names.zip(values)
108
- end
109
-
110
- def [] index
111
- read(index).delete_if{|(name, value)| name == nil}
112
- end
113
-
114
- def size
115
- (@array.size - @offset) / @stride
116
- end
117
-
118
- def each
119
- size.times.each do |i|
120
- yield self[i]
121
- end
122
- end
123
-
124
- def self.parse_parameters(doc, element)
125
- OrderedMap.parse(element, 'param', 'name') do |param_element|
126
- Parameter.parse(doc, param_element)
127
- end
128
- end
129
-
130
- def self.parse(doc, element, arrays = {})
131
- if (array_id = element.attributes['source'])
132
- array_id.sub!(/^#/, '')
133
-
134
- array = arrays[array_id]
135
- else
136
- array = Mesh.parse_arrays(doc, element).first
137
- end
138
-
139
- parameters = parse_parameters(doc, element)
140
-
141
- options = {
142
- :offset => element.attributes['offset'],
143
- :stride => element.attributes['stride'],
144
- }
145
-
146
- self.new(array, parameters, options)
147
- end
148
- end
149
-
150
- class Source
151
- def initialize(id, accessor)
152
- @id = id
153
- @accessor = accessor
154
- end
155
-
156
- attr :id
157
- attr :accessor
158
-
159
- def self.parse(doc, element, arrays = {})
160
- accessor = Accessor.parse(doc, element.elements['technique_common/accessor'], arrays)
161
-
162
- self.new(element.attributes['id'], accessor)
163
- end
164
- end
165
-
166
- class Input
167
- def initialize(semantic, source, offset = 0)
168
- @semantic = semantic
169
- @source = source
170
- @offset = offset
171
- end
172
-
173
- attr :semantic
174
- attr :source
175
- attr :offset
176
-
177
- def inspect
178
- "<Input: #{@semantic}>"
179
- end
180
-
181
- def self.parse(doc, element, sources = {})
182
- semantic = element.attributes['semantic']
183
-
184
- if (source_id = element.attributes['source'])
185
- source_id.sub!(/^#/, '')
186
- source = sources[source_id]
187
- end
188
-
189
- offset = element.attributes['offset'] || 0
190
-
191
- self.new(semantic, source, offset.to_i)
192
- end
193
- end
194
-
195
70
  class TriangleVertices
196
71
  def count(index)
197
72
  3
@@ -302,14 +177,8 @@ module Collada
302
177
  attr :sources
303
178
  attr :polygons
304
179
 
305
- def self.parse_arrays(doc, element)
306
- OrderedMap.parse(element, '//float_array | //int_array', 'name') do |array_element|
307
- array_element.text.strip.split(/\s+/).collect &:to_f
308
- end
309
- end
310
-
311
180
  def self.parse(doc, element)
312
- arrays = parse_arrays(doc, element)
181
+ arrays = Source.parse_arrays(doc, element)
313
182
 
314
183
  sources = OrderedMap.parse(element, 'source') do |source_element|
315
184
  Source.parse(doc, source_element, arrays)
@@ -37,6 +37,14 @@ module Collada
37
37
  attr :ordered
38
38
  attr :indexed
39
39
 
40
+ def keys
41
+ @indexed.keys
42
+ end
43
+
44
+ def values
45
+ @ordered
46
+ end
47
+
40
48
  def [] key
41
49
  @indexed[key]
42
50
  end
@@ -49,6 +57,11 @@ module Collada
49
57
  @ordered.size
50
58
  end
51
59
 
60
+ def append(key, value)
61
+ @indexed[key] = value
62
+ @ordered << value
63
+ end
64
+
52
65
  def self.parse(top, path, id_key = 'id')
53
66
  ordered = []
54
67
  indexed = {}
@@ -64,5 +77,345 @@ module Collada
64
77
  return OrderedMap.new(ordered, indexed)
65
78
  end
66
79
  end
80
+
81
+ class Reference
82
+ def initialize(kind, url)
83
+ @kind = kind
84
+ @url = url
85
+ end
86
+
87
+ attr :kind
88
+ attr :url
89
+
90
+ def id
91
+ url.sub(/^.*\#/, '')
92
+ end
93
+
94
+ def lookup(library)
95
+ library[@kind].each do |item|
96
+ return item if item.id == id
97
+ end
98
+
99
+ return nil
100
+ end
101
+
102
+ def to_s
103
+ "\#<#{self.class} #{@url.dump} in #{@kind}>"
104
+ end
105
+ end
106
+
107
+ class Attribute
108
+ def initialize(semantic, value)
109
+ @semantic = semantic
110
+ @value = value
111
+ end
112
+
113
+ attr :semantic
114
+ attr :value
115
+
116
+ def [] key
117
+ @value[key]
118
+ end
119
+
120
+ def == other
121
+ @value == other.value
122
+ end
123
+
124
+ def inspect
125
+ "Attribute.#{@semantic}(#{@value.inspect})"
126
+ end
127
+
128
+ def self.method_missing(method, *args)
129
+ if args.size == 1 && Hash === args[0]
130
+ new(method, args[0])
131
+ else
132
+ new(method, args)
133
+ end
134
+ end
135
+
136
+ def flatten
137
+ if Array === @value
138
+ @value.collect{|attribute| attribute.flatten}
139
+ else
140
+ self
141
+ end
142
+ end
143
+
144
+ def self.flatten(attributes)
145
+ attributes.collect{|attribute| attribute.flatten}.flatten
146
+ end
147
+
148
+ def self.to_hash(attributes)
149
+ Hash[attributes.collect{|attribute| [attribute.semantic, attribute.value]}]
150
+ end
151
+
152
+ def self.merge(attributes)
153
+ attributes.collect{|attribute| attribute.value}.inject(:merge)
154
+ end
155
+ end
156
+
157
+ class Parameter
158
+ def initialize(name, type)
159
+ @name = name ? name.to_sym : nil
160
+ @type = type
161
+ end
162
+
163
+ attr :name
164
+ attr :type
165
+
166
+ def size
167
+ 1
168
+ end
169
+
170
+ def read value
171
+ value.first
172
+ end
173
+
174
+ def self.parse(doc, element)
175
+ name = element.attributes['name']
176
+ type = element.attributes['type']
177
+
178
+ case type
179
+ when /float(\d)x(\d)/
180
+ MatrixParameter.new(name, type, [$1.to_i, $2.to_i])
181
+ when /float(\d)/
182
+ VectorParameter.new(name, type, [$1.to_i, $2.to_i])
183
+ else
184
+ Parameter.new(name, type)
185
+ end
186
+ end
187
+ end
188
+
189
+ class MatrixParameter < Parameter
190
+ def initialize(name, type, dimensions)
191
+ super name, type
192
+
193
+ @rows = dimensions[1]
194
+ @size = dimensions[0] * dimensions[1]
195
+ end
196
+
197
+ attr :size
198
+
199
+ def read(value)
200
+ Matrix[*(value.each_slice(@rows).to_a)]
201
+ end
202
+ end
203
+
204
+ class VectorParameter < Parameter
205
+ def initialize(name, type, size)
206
+ super name, type
207
+
208
+ @size = size
209
+ end
210
+
211
+ attr :size
212
+
213
+ def read(value)
214
+ Vector[*value]
215
+ end
216
+ end
217
+
218
+ class Accessor
219
+ include Enumerable
220
+
221
+ def initialize(array, parameters, count, options = {})
222
+ @array = array
223
+ @parameters = parameters
224
+ @count = count
225
+
226
+ # Chunk size and stride are usually the same in many cases.
227
+ @chunk_size = @parameters.inject(0) {|sum, parameter| sum + parameter.size}
228
+
229
+ @offset = (options[:offset] || 0).to_i
230
+ @stride = (options[:stride] || @chunk_size).to_i
231
+ end
232
+
233
+ attr :array
234
+ attr :parameters
235
+ attr :names
236
+
237
+ attr :count
238
+ attr :offset
239
+ attr :stride
240
+
241
+ def read index
242
+ base = @offset + (index * @stride)
243
+ values = @array[base, @chunk_size]
244
+
245
+ @parameters.collect{|parameter| [parameter.name, parameter.read(values.shift(parameter.size))]}
246
+ end
247
+
248
+ def [] index
249
+ read(index).delete_if{|(name, value)| name == nil}
250
+ end
251
+
252
+ def size
253
+ (@array.size - @offset) / @stride
254
+ end
255
+
256
+ def each
257
+ size.times.each do |i|
258
+ yield self[i]
259
+ end
260
+ end
261
+
262
+ def self.parse_parameters(doc, element)
263
+ OrderedMap.parse(element, 'param', 'name') do |param_element|
264
+ Parameter.parse(doc, param_element)
265
+ end
266
+ end
267
+
268
+ def self.parse(doc, element, arrays = {})
269
+ if (array_id = element.attributes['source'])
270
+ array_id.sub!(/^#/, '')
271
+
272
+ array = arrays[array_id]
273
+ else
274
+ array = Mesh.parse_arrays(doc, element).first
275
+ end
276
+
277
+ raise UnsupportedFeature.new("Source array binding must be valid (id=#{array_id})") unless array
278
+
279
+ parameters = parse_parameters(doc, element)
280
+ count = element.attributes['count'].to_i
281
+
282
+ options = {
283
+ :offset => element.attributes['offset'],
284
+ :stride => element.attributes['stride'],
285
+ }
286
+
287
+ self.new(array, parameters, count, options)
288
+ end
289
+ end
290
+
291
+ # A source that reads directly from a data array:
292
+ class Source
293
+ def initialize(id, accessor)
294
+ @id = id
295
+ @accessor = accessor
296
+ end
297
+
298
+ attr :id
299
+ attr :accessor
300
+
301
+ def self.parse(doc, element, arrays = {})
302
+ accessor = Accessor.parse(doc, element.elements['technique_common/accessor'], arrays)
303
+
304
+ self.new(element.attributes['id'], accessor)
305
+ end
306
+
307
+ def self.parse_arrays(doc, element)
308
+ OrderedMap.parse(element, '//float_array | //int_array | //Name_array') do |array_element|
309
+ case array_element.name
310
+ when 'Name_array'
311
+ array_element.text.strip.split(/\s+/)
312
+ else
313
+ array_element.text.strip.split(/\s+/).collect &:to_f
314
+ end
315
+ end
316
+ end
317
+
318
+ def [] index
319
+ Hash[@accessor[index]]
320
+ end
321
+ end
322
+
323
+ class Input
324
+ # `Vertices` or `Source` are both okay for source.
325
+ def initialize(semantic, source, offset = 0)
326
+ @semantic = semantic
327
+ @source = source
328
+ @offset = offset
329
+ end
330
+
331
+ attr :semantic
332
+ attr :source
333
+ attr :offset
334
+
335
+ def size
336
+ @source.accessor.count
337
+ end
338
+
339
+ def [] index
340
+ Attribute.new(@semantic, @source[index])
341
+ end
342
+
343
+ def self.parse(doc, element, sources = {})
344
+ semantic = element.attributes['semantic']
345
+
346
+ if (source_id = element.attributes['source'])
347
+ source_id.sub!(/^#/, '')
348
+ source = sources[source_id]
349
+ end
350
+
351
+ raise UnsupportedFeature.new("Can't instantiate input with nil source (#{source_id})!") unless source
352
+
353
+ offset = element.attributes['offset'] || 0
354
+
355
+ self.new(semantic.downcase.to_sym, source, offset.to_i)
356
+ end
357
+ end
358
+
359
+ class Sampler
360
+ include Enumerable
361
+
362
+ def initialize(id, inputs)
363
+ @id = id
364
+ @inputs = inputs
365
+ end
366
+
367
+ attr :id
368
+ attr :inputs
369
+
370
+ # Vertices by index, same interface as Input.
371
+ def [] index
372
+ @inputs.collect do |input|
373
+ input[index]
374
+ end
375
+ end
376
+
377
+ def self.parse_inputs(doc, element, sources = {})
378
+ OrderedMap.parse(element, 'input') do |input_element|
379
+ Input.parse(doc, input_element, sources)
380
+ end
381
+ end
382
+
383
+ def self.parse(doc, element, sources = {})
384
+ inputs = parse_inputs(doc, element, sources)
385
+
386
+ self.new(element.attributes['id'], inputs)
387
+ end
388
+
389
+ def size
390
+ @inputs.collect{|input| input.size}.max
391
+ end
392
+
393
+ def each
394
+ size.times do |i|
395
+ yield self[i]
396
+ end
397
+ end
398
+ end
399
+
400
+ class Channel
401
+ def initialize(source, target)
402
+ @source = source
403
+ @target = target
404
+ end
405
+
406
+ attr :source
407
+ attr :target
408
+
409
+ def self.parse(doc, element, sources = {})
410
+ source_id = element.attributes['source'].sub(/^#/, '')
411
+ target_id = element.attributes['target']
412
+
413
+ source = sources[source_id]
414
+
415
+ raise ArgumentError.new("Could not find #{source_id} in #{sources.keys.inspect}!") unless source
416
+
417
+ self.new(source, target_id)
418
+ end
419
+ end
67
420
  end
68
421
  end