tiny_gltf 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +3 -0
  3. data/Gemfile.lock +3 -3
  4. data/bin/setup +2 -0
  5. data/ext/tiny_gltf/rb_tiny_gltf.h +18 -7
  6. data/ext/tiny_gltf/rb_tiny_gltf_accessor.cpp +4 -12
  7. data/ext/tiny_gltf/rb_tiny_gltf_animation.cpp +6 -5
  8. data/ext/tiny_gltf/rb_tiny_gltf_animation_channel.cpp +6 -5
  9. data/ext/tiny_gltf/rb_tiny_gltf_animation_sampler.cpp +6 -5
  10. data/ext/tiny_gltf/rb_tiny_gltf_asset.cpp +4 -3
  11. data/ext/tiny_gltf/rb_tiny_gltf_buffer.cpp +37 -8
  12. data/ext/tiny_gltf/rb_tiny_gltf_buffer_view.cpp +4 -3
  13. data/ext/tiny_gltf/rb_tiny_gltf_camera.cpp +4 -3
  14. data/ext/tiny_gltf/rb_tiny_gltf_extension_map.cpp +2 -2
  15. data/ext/tiny_gltf/rb_tiny_gltf_image.cpp +72 -12
  16. data/ext/tiny_gltf/rb_tiny_gltf_init.c +66 -1
  17. data/ext/tiny_gltf/rb_tiny_gltf_light.cpp +2 -1
  18. data/ext/tiny_gltf/rb_tiny_gltf_material.cpp +6 -5
  19. data/ext/tiny_gltf/rb_tiny_gltf_mesh.cpp +5 -17
  20. data/ext/tiny_gltf/rb_tiny_gltf_model.cpp +10 -10
  21. data/ext/tiny_gltf/rb_tiny_gltf_node.cpp +8 -7
  22. data/ext/tiny_gltf/rb_tiny_gltf_parameter_map.cpp +2 -2
  23. data/ext/tiny_gltf/rb_tiny_gltf_primitive.cpp +8 -7
  24. data/ext/tiny_gltf/rb_tiny_gltf_sampler.cpp +3 -2
  25. data/ext/tiny_gltf/rb_tiny_gltf_scene.cpp +7 -6
  26. data/ext/tiny_gltf/rb_tiny_gltf_skin.cpp +6 -5
  27. data/ext/tiny_gltf/rb_tiny_gltf_texture.cpp +7 -6
  28. data/ext/tiny_gltf/rb_tiny_gltf_types.cpp +7 -7
  29. data/ext/tiny_gltf/rb_tiny_gltf_value.cpp +5 -4
  30. data/ext/tiny_gltf/stb_image.h +1890 -869
  31. data/ext/tiny_gltf/stb_image_write.h +1241 -1451
  32. data/ext/tiny_gltf/tiny_gltf.h +3671 -1082
  33. data/lib/tiny_gltf.rb +385 -126
  34. data/lib/tiny_gltf/version.rb +1 -1
  35. metadata +4 -4
data/lib/tiny_gltf.rb CHANGED
@@ -1,26 +1,81 @@
1
+ require 'fiddle'
1
2
  require "tiny_gltf/version"
2
3
  require "tiny_gltf/tiny_gltf"
3
4
 
4
5
  module TinyGLTF
6
+ # If a sampler exists but does not reference an image, the GLTF spec
7
+ # indicates we should use a default image. You can specify your own default
8
+ # image by calling Model#default_image=. The _default_ default image is
9
+ # constructed using this data, which represents a PNG-encoded, 1-pixel,
10
+ # opaque white image.
11
+ IMAGE_URI_1PX_WHITE_OPAQUE_PNG = ('data:image/png;base64,iVBORw0KGgoAAAANS'+
12
+ 'UhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4'+
13
+ 'nGP6DwABBQECz6AuzQAAAABJRU5ErkJggg==').freeze
14
+
5
15
  class Error < StandardError; end
16
+
17
+ module Base
18
+ attr_reader :model
19
+
20
+ # :nodoc:
21
+ def self.included(base)
22
+ base.module_eval do
23
+ class << self
24
+ # :nodoc:
25
+ # Easy way to differentiate between properties that should show up in
26
+ # #inspect and #to_h, and those which should not.
27
+ def attr_inspectable(name)
28
+ name = name.to_sym
29
+ @inspectable_attrs ||= []
30
+ @inspectable_attrs.push name unless @inspectable_attrs.include?(name)
31
+ attr_reader name
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def to_h
38
+ {}.tap do |hash|
39
+ self.class.instance_variable_get(:"@inspectable_attrs")&.each do |name|
40
+ val = send(name)
41
+ if val.kind_of?(Array) then hash[name] = val.map { |el| el.kind_of?(Base) ? el.to_h : el }
42
+ elsif val.kind_of?(Base) then hash[name] = val.to_h
43
+ else hash[name] = val
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def inspect
50
+ to_h.inspect
51
+ end
52
+
53
+ def to_json(*o)
54
+ to_h.to_json(*o)
55
+ end
56
+ end
6
57
 
7
58
  class Model
8
- attr_reader :asset
9
- attr_reader :accessors
10
- attr_reader :animations
11
- attr_reader :buffers
12
- attr_reader :buffer_views
13
- attr_reader :materials
14
- attr_reader :meshes
15
- attr_reader :nodes
16
- attr_reader :textures
17
- attr_reader :images
18
- attr_reader :skins
19
- attr_reader :samplers
20
- attr_reader :cameras
21
- attr_reader :scenes
22
- attr_reader :lights
23
- attr_reader :default_scene_index
59
+ include Base
60
+ attr_inspectable :asset
61
+ attr_inspectable :accessors
62
+ attr_inspectable :animations
63
+ attr_inspectable :buffers
64
+ attr_inspectable :buffer_views
65
+ attr_inspectable :materials
66
+ attr_inspectable :meshes
67
+ attr_inspectable :nodes
68
+ attr_inspectable :textures
69
+ attr_inspectable :images
70
+ attr_inspectable :skins
71
+ attr_inspectable :samplers
72
+ attr_inspectable :cameras
73
+ attr_inspectable :scenes
74
+ attr_inspectable :lights
75
+ attr_inspectable :default_scene_index
76
+
77
+ attr_writer :default_image
78
+ attr_writer :default_sampler
24
79
 
25
80
  def initialize
26
81
  @default_scene_index = nil
@@ -39,222 +94,426 @@ module TinyGLTF
39
94
  @scenes = []
40
95
  @lights = []
41
96
  end
97
+
98
+ def default_image
99
+ @default_image ||= Image.new(self, uri: IMAGE_URI_1PX_WHITE_OPAQUE_PNG)
100
+ end
101
+
102
+ def default_sampler
103
+ @default_sampler ||= Sampler.new(self)
104
+ end
42
105
  end
43
106
 
44
107
  class Accessor
45
- attr_reader :buffer_view_index
108
+ include Base
109
+ attr_inspectable :buffer_view_index
46
110
  # One of `:byte`, `:ubyte`, `:short`, `:ushort`, `:int`, `:uint`,
47
111
  # `:float`, `:double`, `:unknown`
48
- attr_reader :component_type
49
- attr_reader :count
50
- attr_reader :name
51
- attr_reader :byte_offset
112
+ attr_inspectable :component_type
113
+ attr_inspectable :count
114
+ attr_inspectable :name
115
+ attr_inspectable :byte_offset
52
116
  # One of `:vec2`, `:vec3`, `:vec4`, `:mat2`, `:mat3`, `:mat4`, `:scalar`,
53
117
  # `:vector`, `:matrix`, `:unknown`
54
- attr_reader :type
55
- attr_reader :extras
56
- attr_reader :min
57
- attr_reader :max
118
+ attr_inspectable :type
119
+ attr_inspectable :extras
120
+ attr_inspectable :min
121
+ attr_inspectable :max
122
+
123
+ def component_size
124
+ case component_type
125
+ when :byte, :ubyte then 1
126
+ when :short, :ushort then 2
127
+ when :int, :uint then 4
128
+ when :float then 4
129
+ when :double then 8
130
+ else raise "Can't calculate component size for component type #{component_type.inspect}"
131
+ end
132
+ end
133
+
134
+ def num_components
135
+ case type
136
+ when :scalar then 1
137
+ when :vec2 then 2
138
+ when :vec3 then 3
139
+ when :vec4, :mat2 then 4
140
+ when :mat3 then 9
141
+ when :mat4 then 16
142
+ else raise "Can't calculate number of components for type #{type.inspect}"
143
+ end
144
+ end
145
+
146
+ def element_size
147
+ component_size * num_components
148
+ end
149
+
150
+ def byte_stride
151
+ # stride in buffer view is optional, when 0 or omitted it's assumed to
152
+ # be tightly packed, which == element_size
153
+ stride = buffer_view.byte_stride
154
+ stride && stride > 0 ? stride : element_size
155
+ end
156
+
157
+ def byte_length
158
+ byte_stride * count
159
+ end
160
+
161
+ def to_ptr
162
+ view_ptr = buffer_view.to_ptr
163
+ length = byte_length
164
+ length = view_ptr.size if length > view_ptr.size
165
+ Fiddle::Pointer.new view_ptr + byte_offset, length
166
+ end
58
167
 
59
168
  def normalized?
60
169
  !!@normalized
61
170
  end
171
+
172
+ def buffer_view
173
+ model.buffer_views[buffer_view_index]
174
+ end
62
175
  end
63
176
 
64
177
  class Animation
65
- attr_reader :name
66
- attr_reader :channels
67
- attr_reader :samplers
68
- attr_reader :extras
178
+ include Base
179
+ attr_inspectable :name
180
+ attr_inspectable :channels
181
+ attr_inspectable :samplers
182
+ attr_inspectable :extras
69
183
  end
70
184
 
71
185
  class AnimationChannel
72
- attr_reader :sampler_index
186
+ include Base
187
+ attr_reader :animation
188
+ attr_inspectable :sampler_index
73
189
  # index of the node to target
74
- attr_reader :target_node_index
190
+ attr_inspectable :target_node_index
75
191
  # one of `:translation`, `:rotation`, `:scale`, `:weights`
76
- attr_reader :target_path
77
- attr_reader :extras
192
+ attr_inspectable :target_path
193
+ attr_inspectable :extras
194
+
195
+ def model
196
+ animation.model
197
+ end
198
+
199
+ def sampler
200
+ animation.samplers[sampler_index]
201
+ end
202
+
203
+ def target_node
204
+ model.nodes[target_node_index]
205
+ end
78
206
  end
79
207
 
80
208
  class AnimationSampler
81
- attr_reader :input
82
- attr_reader :output
209
+ include Base
210
+ attr_reader :animation
211
+ attr_inspectable :input_index
212
+ attr_inspectable :output_index
83
213
  # one of `:linear`, `:step`, `:catmullromspline`, `:cubicspline`, `:linear`
84
- attr_reader :interpolation
85
- attr_reader :extras
214
+ attr_inspectable :interpolation
215
+ attr_inspectable :extras
216
+
217
+ def model
218
+ animation.model
219
+ end
220
+
221
+ def input
222
+ model.accessors[input_index]
223
+ end
224
+
225
+ def output
226
+ model.accessors[output_index]
227
+ end
86
228
  end
87
229
 
88
230
  class Asset
89
- attr_reader :version
90
- attr_reader :generator
91
- attr_reader :minVersion
92
- attr_reader :copyright
93
- attr_reader :extensions
94
- attr_reader :extras
231
+ include Base
232
+ attr_inspectable :version
233
+ attr_inspectable :generator
234
+ attr_inspectable :minVersion
235
+ attr_inspectable :copyright
236
+ attr_inspectable :extensions
237
+ attr_inspectable :extras
95
238
  end
96
239
 
97
240
  class Buffer
98
- attr_reader :name
99
- attr_reader :data
100
- attr_reader :uri
101
- attr_reader :extras
241
+ include Base
242
+ attr_inspectable :name
243
+ attr_inspectable :data
244
+ attr_inspectable :uri
245
+ attr_inspectable :extras
102
246
  end
103
247
 
104
248
  class BufferView
105
- attr_reader :name
106
- attr_reader :buffer_index
249
+ include Base
250
+ attr_inspectable :name
251
+ attr_inspectable :buffer_index
107
252
  # minimum 0, default 0
108
- attr_reader :byte_offset
253
+ attr_inspectable :byte_offset
109
254
  # required, minimum 1
110
- attr_reader :byte_length
255
+ attr_inspectable :byte_length
111
256
  # minimum 4, maximum 252 (multiple of 4), default 0 = understood to be
112
257
  # tightly packed
113
- attr_reader :byte_stride
258
+ attr_inspectable :byte_stride
114
259
  # one of `:buffer`, `:element_array_buffer`
115
- attr_reader :target
116
- attr_reader :extras
260
+ attr_inspectable :target
261
+ attr_inspectable :extras
262
+
263
+ def buffer
264
+ model.buffers[buffer_index]
265
+ end
266
+
267
+ def to_ptr
268
+ Fiddle::Pointer.new(buffer.to_ptr + byte_offset, byte_length)
269
+ end
117
270
  end
118
271
 
119
272
  class Camera
273
+ include Base
120
274
  # One of `:perspective` or `:orthographic`
121
- attr_reader :type
122
- attr_reader :name
275
+ attr_inspectable :type
276
+ attr_inspectable :name
123
277
  # Only useful if #type is `:perspective`
124
- attr_reader :aspect_ratio
278
+ attr_inspectable :aspect_ratio
125
279
  # Only useful if #type is `:perspective`
126
- attr_reader :yfov
280
+ attr_inspectable :yfov
127
281
  # Only useful if #type is `:orthographic`
128
- attr_reader :xmag
282
+ attr_inspectable :xmag
129
283
  # Only useful if #type is `:orthographic`
130
- attr_reader :ymag
131
- attr_reader :znear
284
+ attr_inspectable :ymag
285
+ attr_inspectable :znear
132
286
  # `+Infinity` if #type is `:perspective` and an infinite projection should
133
287
  # be used.
134
- attr_reader :zfar
135
- attr_reader :extensions
136
- attr_reader :extras
288
+ attr_inspectable :zfar
289
+ attr_inspectable :extensions
290
+ attr_inspectable :extras
137
291
  end
138
292
 
139
293
  class Image
140
- attr_reader :name
141
- attr_reader :width
142
- attr_reader :height
143
- attr_reader :components
144
- attr_reader :data
145
- attr_reader :buffer_view_index
294
+ include Base
295
+ attr_inspectable :name
296
+ attr_inspectable :width
297
+ attr_inspectable :height
298
+ attr_inspectable :components
299
+ attr_inspectable :buffer_view_index
146
300
  # One of `"image/jpeg"`, `"image/png"`, `"image/bmp"`, `"image/gif"`,
147
301
  # or `nil`
148
- attr_reader :mime_type
149
- attr_reader :uri
150
- attr_reader :extras
151
- attr_reader :extensions
302
+ attr_inspectable :mime_type
303
+ attr_inspectable :extras
304
+ attr_inspectable :extensions
305
+
306
+ def initialize(model = nil,
307
+ name: nil,
308
+ width: nil,
309
+ height: nil,
310
+ components: nil,
311
+ buffer_view_index: nil,
312
+ mime_type: nil,
313
+ uri: nil,
314
+ extras: nil,
315
+ extensions: nil)
316
+ @model = model
317
+ @name = name
318
+ @width = width
319
+ @height = height
320
+ @components = components
321
+ @buffer_view_index = buffer_view_index
322
+ @mime_type = mime_type
323
+ @extras = extras
324
+ @extensions = extensions
325
+ self.uri = uri if uri
326
+ end
327
+
328
+ def buffer_view
329
+ buffer_view_index && model.buffer_views[buffer_view_index]
330
+ end
152
331
  end
153
332
 
154
333
  class Light
155
- attr_reader :name
156
- attr_reader :color
157
- attr_reader :type
334
+ include Base
335
+ attr_inspectable :name
336
+ attr_inspectable :color
337
+ attr_inspectable :type
158
338
  end
159
339
 
160
340
  class Primitive
341
+ include Base
161
342
  # A Hash whose values are integers, where each integer is the index of the
162
343
  # accessor containing the corresponding attribute.
163
- attr_reader :attributes
344
+ attr_inspectable :attributes
164
345
  # The index of the material to apply to this primitive when rendering.
165
- attr_reader :material_index
346
+ attr_inspectable :material_index
166
347
  # The index of the accessor that contains the indices.
167
- attr_reader :indices
348
+ attr_inspectable :indices_index
168
349
  # One of `:points`, `:line`, `:line_loop`, `:triangles`, `:triangle_strip`,
169
350
  # `:triangle_fan`, `:unknown`
170
- attr_reader :mode
351
+ attr_inspectable :mode
171
352
  # Array of morph targets, where each target is a Hash with attributes in
172
353
  # `:position, :normal, :tangent` pointing to their corresponding
173
354
  # accessors.
174
- attr_reader :morph_targets
175
- attr_reader :extras
355
+ attr_inspectable :morph_targets_indices
356
+ attr_inspectable :extras
357
+
358
+ def indices
359
+ model.accessors[indices_index]
360
+ end
361
+
362
+ def accessors
363
+ Hash[attributes.map { |(name, index)| [name, model.accessors[index]] }]
364
+ end
365
+
366
+ def morph_targets
367
+ morph_targets_indices.collect do |target|
368
+ Hash[target.map { |(name, index)| [name, model.accessors[index]] }]
369
+ end
370
+ end
371
+
372
+ def material
373
+ material_index && model.materials[material_index]
374
+ end
176
375
  end
177
376
 
377
+ # TODO: When normals are not specified, client implementations should
378
+ # calculate flat normals.
379
+ # TODO: When tangents are not specified, client implementations should
380
+ # calculate tangents using default MikkTSpace algorithms. For best results,
381
+ # the mesh triangles should also be processed using default MikkTSpace
382
+ # algorithms.
178
383
  class Mesh
179
- attr_reader :name
180
- attr_reader :primitives
384
+ include Base
385
+ attr_inspectable :name
386
+ attr_inspectable :primitives
181
387
  # weights to be applied to the Morph Targets
182
- attr_reader :weights
183
- attr_reader :morph_targets
184
- attr_reader :extensions
185
- attr_reader :extras
388
+ attr_inspectable :weights
389
+ attr_inspectable :extensions
390
+ attr_inspectable :extras
186
391
  end
187
392
 
188
393
  class Material
189
- attr_reader :name
394
+ include Base
395
+ attr_inspectable :name
190
396
  # hash containing PBR metal/roughness workflow values
191
- attr_reader :values
397
+ attr_inspectable :values
192
398
  # hash containing normal/occlusion/emissive values
193
- attr_reader :additional_values
194
- attr_reader :extensions
195
- attr_reader :extras
399
+ attr_inspectable :additional_values
400
+ attr_inspectable :extensions
401
+ attr_inspectable :extras
196
402
  end
197
403
 
198
404
  class Node
199
- attr_reader :name
405
+ include Base
406
+ attr_inspectable :name
200
407
  # the index of the camera referenced by this node
201
- attr_reader :camera_index
202
- attr_reader :skin_index
203
- attr_reader :mesh_index
204
- attr_reader :children_indices
408
+ attr_inspectable :camera_index
409
+ attr_inspectable :skin_index
410
+ attr_inspectable :mesh_index
411
+ attr_inspectable :children_indices
205
412
  # If present, all of #rotation, #scale and #translation will be present,
206
413
  # and #matrix will be nil.
207
- attr_reader :rotation
414
+ attr_inspectable :rotation
208
415
  # If present, all of #rotation, #scale and #translation will be present,
209
416
  # and #matrix will be nil.
210
- attr_reader :scale
417
+ attr_inspectable :scale
211
418
  # If present, all of #rotation, #scale and #translation will be present,
212
419
  # and #matrix will be nil.
213
- attr_reader :translation
420
+ attr_inspectable :translation
214
421
  # If present, #rotation, #scale and #translation will be nil.
215
- attr_reader :matrix
422
+ attr_inspectable :matrix
216
423
  # The weights of the instantiated Morph Target
217
- attr_reader :weights
218
- attr_reader :extensions
219
- attr_reader :extras
424
+ attr_inspectable :weights
425
+ attr_inspectable :extensions
426
+ attr_inspectable :extras
427
+
428
+ def skin
429
+ skin_index && model.skins[skin_index]
430
+ end
431
+
432
+ def mesh
433
+ mesh_index && model.meshes[mesh_index]
434
+ end
435
+
436
+ def children
437
+ children_indices.map { |index| model.nodes[index] }
438
+ end
439
+
440
+ def camera
441
+ camera_index && model.cameras[camera_index]
442
+ end
220
443
  end
221
444
 
222
445
  class Sampler
223
- attr_reader :name
446
+ include Base
447
+ attr_inspectable :name
224
448
  # One of `:nearest`, `:linear`, `:nearest_mipmap_linear`,
225
449
  # `:linear_mipmap_nearest`, `:nearest_mipmap_linear`,
226
450
  # `:linear_mipmap_linear`
227
- attr_reader :min_filter
451
+ attr_inspectable :min_filter
228
452
  # One of `:nearest`, `:linear`
229
- attr_reader :mag_filter
453
+ attr_inspectable :mag_filter
230
454
  # One of `:clamp_to_edge`, `:mirrored_repeat`, `:repeat`
231
- attr_reader :wrap_s
455
+ attr_inspectable :wrap_s
232
456
  # One of `:clamp_to_edge`, `:mirrored_repeat`, `:repeat`
233
- attr_reader :wrap_t
457
+ attr_inspectable :wrap_t
234
458
  # One of `:clamp_to_edge`, `:mirrored_repeat`, `:repeat`
235
- attr_reader :wrap_r
236
- attr_reader :extras
459
+ attr_inspectable :wrap_r
460
+ attr_inspectable :extras
461
+
462
+ def initialize(model = nil,
463
+ wrap_s: :repeat,
464
+ wrap_r: :repeat,
465
+ wrap_t: :repeat,
466
+ min_filter: :linear,
467
+ mag_filter: :linear)
468
+ @model = model
469
+ @wrap_s = wrap_s
470
+ @wrap_r = wrap_r
471
+ @wrap_t = wrap_t
472
+ @min_filter = min_filter
473
+ @mag_filter = mag_filter
474
+ end
475
+
476
+ def support_power_of_two?
477
+ wrap_s == :clamp_to_edge &&
478
+ wrap_r == :clamp_to_edge &&
479
+ wrap_t == :clamp_to_edge &&
480
+ (min_filter == :linear || min_filter == :nearest) &&
481
+ (mag_filter == :linear || mag_filter == :nearest)
482
+ end
483
+
484
+ alias pot? support_power_of_two?
237
485
  end
238
486
 
239
487
  class Scene
240
- attr_reader :name
241
- attr_reader :nodes
242
- attr_reader :extensions
243
- attr_reader :extras
488
+ include Base
489
+ attr_inspectable :name
490
+ attr_inspectable :nodes_indices
491
+ attr_inspectable :extensions
492
+ attr_inspectable :extras
244
493
  end
245
494
 
246
495
  class Skin
247
- attr_reader :name
248
- attr_reader :inverse_bind_matrices
249
- attr_reader :skeleton_root_node_index
250
- attr_reader :joint_node_indices
496
+ include Base
497
+ attr_inspectable :name
498
+ attr_inspectable :inverse_bind_matrices
499
+ attr_inspectable :skeleton_root_node_index
500
+ attr_inspectable :joint_nodes_indices
251
501
  end
252
502
 
253
503
  class Texture
254
- attr_reader :name
255
- attr_reader :sampler_index
256
- attr_reader :source_index
257
- attr_reader :extensions
258
- attr_reader :extras
504
+ include Base
505
+ attr_inspectable :name
506
+ attr_inspectable :sampler_index
507
+ attr_inspectable :source_index
508
+ attr_inspectable :extensions
509
+ attr_inspectable :extras
510
+
511
+ def source
512
+ source_index ? model.images[source_index] : model.default_image
513
+ end
514
+
515
+ def sampler
516
+ sampler_index ? model.samplers[sampler_index] : model.default_sampler
517
+ end
259
518
  end
260
519
  end