collada 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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