tiny_gltf 0.1.1 → 1.0.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.
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