mittsu 0.1.1 → 0.1.2

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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -1
  3. data/README.md +1 -1
  4. data/lib/mittsu/cameras/orthographic_camera.rb +13 -0
  5. data/lib/mittsu/cameras/perspective_camera.rb +11 -0
  6. data/lib/mittsu/core/geometry.rb +12 -9
  7. data/lib/mittsu/core/object_3d.rb +33 -66
  8. data/lib/mittsu/lights/ambient_light.rb +8 -0
  9. data/lib/mittsu/lights/directional_light.rb +9 -0
  10. data/lib/mittsu/lights/hemisphere_light.rb +9 -0
  11. data/lib/mittsu/lights/point_light.rb +11 -0
  12. data/lib/mittsu/lights/spot_light.rb +13 -0
  13. data/lib/mittsu/loaders.rb +1 -0
  14. data/lib/mittsu/loaders/mtl_loader.rb +5 -12
  15. data/lib/mittsu/loaders/obj_loader.rb +212 -0
  16. data/lib/mittsu/loaders/obj_mtl_loader.rb +11 -207
  17. data/lib/mittsu/materials/material.rb +5 -2
  18. data/lib/mittsu/materials/mesh_basic_material.rb +0 -9
  19. data/lib/mittsu/math/color.rb +44 -104
  20. data/lib/mittsu/math/matrix3.rb +8 -1
  21. data/lib/mittsu/math/matrix4.rb +6 -0
  22. data/lib/mittsu/math/vector.rb +251 -0
  23. data/lib/mittsu/math/vector2.rb +14 -213
  24. data/lib/mittsu/math/vector3.rb +61 -351
  25. data/lib/mittsu/math/vector4.rb +45 -295
  26. data/lib/mittsu/objects/line.rb +12 -2
  27. data/lib/mittsu/objects/mesh.rb +18 -9
  28. data/lib/mittsu/renderers/glfw_window.rb +15 -13
  29. data/lib/mittsu/renderers/opengl/core/opengl_geometry.rb +253 -0
  30. data/lib/mittsu/renderers/opengl/core/opengl_object_3d.rb +131 -0
  31. data/lib/mittsu/renderers/opengl/lights/opengl_ambient_light.rb +26 -0
  32. data/lib/mittsu/renderers/opengl/lights/opengl_directional_light.rb +35 -0
  33. data/lib/mittsu/renderers/opengl/lights/opengl_hemisphere_light.rb +42 -0
  34. data/lib/mittsu/renderers/opengl/lights/opengl_light.rb +52 -0
  35. data/lib/mittsu/renderers/opengl/lights/opengl_point_light.rb +36 -0
  36. data/lib/mittsu/renderers/opengl/lights/opengl_spot_light.rb +47 -0
  37. data/lib/mittsu/renderers/opengl/materials/opengl_line_basic_material.rb +16 -0
  38. data/lib/mittsu/renderers/opengl/materials/opengl_material.rb +275 -0
  39. data/lib/mittsu/renderers/opengl/materials/opengl_mesh_basic_material.rb +69 -0
  40. data/lib/mittsu/renderers/opengl/materials/opengl_mesh_lambert_material.rb +29 -0
  41. data/lib/mittsu/renderers/opengl/materials/opengl_mesh_phong_material.rb +40 -0
  42. data/lib/mittsu/renderers/opengl/materials/opengl_shader_material.rb +11 -0
  43. data/lib/mittsu/renderers/opengl/objects/opengl_group.rb +8 -0
  44. data/lib/mittsu/renderers/opengl/objects/opengl_line.rb +54 -0
  45. data/lib/mittsu/renderers/opengl/objects/opengl_mesh.rb +77 -0
  46. data/lib/mittsu/renderers/opengl/opengl_buffer.rb +5 -0
  47. data/lib/mittsu/renderers/opengl/opengl_debug.rb +49 -7
  48. data/lib/mittsu/renderers/opengl/opengl_default_target.rb +54 -0
  49. data/lib/mittsu/renderers/opengl/opengl_geometry_group.rb +763 -0
  50. data/lib/mittsu/renderers/opengl/opengl_geometry_like.rb +130 -0
  51. data/lib/mittsu/renderers/opengl/opengl_helper.rb +161 -0
  52. data/lib/mittsu/renderers/opengl/opengl_implementations.rb +61 -0
  53. data/lib/mittsu/renderers/opengl/opengl_light_renderer.rb +43 -0
  54. data/lib/mittsu/renderers/opengl/opengl_mittsu_params.rb +53 -0
  55. data/lib/mittsu/renderers/opengl/opengl_program.rb +147 -296
  56. data/lib/mittsu/renderers/opengl/opengl_state.rb +3 -5
  57. data/lib/mittsu/renderers/opengl/plugins/shadow_map_plugin.rb +12 -10
  58. data/lib/mittsu/renderers/opengl/scenes/opengl_scene.rb +8 -0
  59. data/lib/mittsu/renderers/opengl/textures/opengl_compressed_texture.rb +21 -0
  60. data/lib/mittsu/renderers/opengl/textures/opengl_cube_texture.rb +75 -0
  61. data/lib/mittsu/renderers/opengl/textures/opengl_data_texture.rb +23 -0
  62. data/lib/mittsu/renderers/opengl/textures/opengl_texture.rb +111 -0
  63. data/lib/mittsu/renderers/opengl_render_target.rb +117 -2
  64. data/lib/mittsu/renderers/opengl_renderer.rb +653 -2978
  65. data/lib/mittsu/renderers/shaders/rbsl_loader.rb +166 -0
  66. data/lib/mittsu/renderers/shaders/shader_chunk.rb +6 -9
  67. data/lib/mittsu/renderers/shaders/shader_chunk/shadowmap_fragment.glsl +36 -37
  68. data/lib/mittsu/renderers/shaders/shader_lib.rb +26 -403
  69. data/lib/mittsu/renderers/shaders/shader_lib/basic/basic_fragment.rbsl +37 -0
  70. data/lib/mittsu/renderers/shaders/shader_lib/basic/basic_uniforms.rbslu +3 -0
  71. data/lib/mittsu/renderers/shaders/shader_lib/basic/basic_vertex.rbsl +33 -0
  72. data/lib/mittsu/renderers/shaders/shader_lib/cube/cube_fragment.rbsl +12 -0
  73. data/lib/mittsu/renderers/shaders/shader_lib/cube/cube_uniforms.rbslu +2 -0
  74. data/lib/mittsu/renderers/shaders/shader_lib/cube/cube_vertex.rbsl +12 -0
  75. data/lib/mittsu/renderers/shaders/shader_lib/depth_rgba/depth_rgba_fragment.rbsl +26 -0
  76. data/lib/mittsu/renderers/shaders/shader_lib/depth_rgba/depth_rgba_uniforms.rbslu +0 -0
  77. data/lib/mittsu/renderers/shaders/shader_lib/depth_rgba/depth_rgba_vertex.rbsl +12 -0
  78. data/lib/mittsu/renderers/shaders/shader_lib/lambert/lambert_fragment.rbsl +56 -0
  79. data/lib/mittsu/renderers/shaders/shader_lib/lambert/lambert_uniforms.rbslu +7 -0
  80. data/lib/mittsu/renderers/shaders/shader_lib/lambert/lambert_vertex.rbsl +37 -0
  81. data/lib/mittsu/renderers/shaders/shader_lib/phong/phong_fragment.rbsl +45 -0
  82. data/lib/mittsu/renderers/shaders/shader_lib/phong/phong_uniforms.rbslu +11 -0
  83. data/lib/mittsu/renderers/shaders/shader_lib/phong/phong_vertex.rbsl +43 -0
  84. data/lib/mittsu/renderers/shaders/shader_templates/fragment.glsl.erb +105 -0
  85. data/lib/mittsu/renderers/shaders/shader_templates/vertex.glsl.erb +143 -0
  86. data/lib/mittsu/renderers/shaders/uniforms_lib.rb +54 -55
  87. data/lib/mittsu/textures/texture.rb +5 -2
  88. data/lib/mittsu/version.rb +1 -1
  89. data/run_all_examples.sh +7 -0
  90. metadata +77 -28
  91. data/.ruby-version +0 -1
  92. data/lib/mittsu/core/hash_object.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 39e01ae0fe72f4d908f00512bcbe537ad5277f5c
4
- data.tar.gz: a2430f7af5ba20c763c876037fca369307c9ae9f
3
+ metadata.gz: a116d4e3970e8c01c66d77f51eae67c987240017
4
+ data.tar.gz: ee8cf7cd8bbeeb6936b6609777d1c767a72b48fd
5
5
  SHA512:
6
- metadata.gz: 8909e0371cbc10c1d43798d34f12d76494014d20cad3fe7cc1f721ec1622242e881b88b25e213a328c7e9ef7bb5158887bd710f20ec7c1b83efdce2bff037eb1
7
- data.tar.gz: f78ec69a08d2c18853e3b6b5ad6754123711a44f857a6def2d7e288534c5d511b472c4abe0a0db2f4835a87cdaa11c460094ae9858bf4b8f64b85ff2b87b9ce2
6
+ metadata.gz: 0fff4b0a5cba6d3088677d89434e7e2af7223d597dd10b438fd92eeb52aa6ab587fd3cb8e406e4a984d8b178d09bf97b9a1325b139ec200bfbf13ad0eecaeab3
7
+ data.tar.gz: fed0e180de0ae1f9e91af33817c73f2ece532ce8bf5ffcae87655f27278205e819188979a53b742e989c25ab777f5a32bf992d7601e2fd749c51855b85691651
data/Gemfile CHANGED
@@ -2,4 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- gem "codeclimate-test-reporter", group: :test, require: nil
5
+ gem "codeclimate-test-reporter", group: :test, require: false
6
+ gem 'coveralls', group: :test, require: false
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Mittsu
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/mittsu.svg)](https://badge.fury.io/rb/mittsu) [![Dependency Status](https://gemnasium.com/jellymann/mittsu.svg)](https://gemnasium.com/jellymann/mittsu) [![Circle CI](https://circleci.com/gh/jellymann/mittsu/tree/master.svg?style=shield)](https://circleci.com/gh/jellymann/mittsu/tree/master) [![Test Coverage](https://codeclimate.com/github/jellymann/mittsu/badges/coverage.svg)](https://codeclimate.com/github/jellymann/mittsu/coverage) [![Code Climate](https://codeclimate.com/github/jellymann/mittsu/badges/gpa.svg)](https://codeclimate.com/github/jellymann/mittsu)
3
+ [![Gem Version](https://badge.fury.io/rb/mittsu.svg)](https://badge.fury.io/rb/mittsu) [![Dependency Status](https://gemnasium.com/jellymann/mittsu.svg)](https://gemnasium.com/jellymann/mittsu) [![Circle CI](https://circleci.com/gh/jellymann/mittsu/tree/master.svg?style=shield)](https://circleci.com/gh/jellymann/mittsu/tree/master) [![Coverage Status](https://coveralls.io/repos/github/jellymann/mittsu/badge.svg?branch=master)](https://coveralls.io/github/jellymann/mittsu?branch=master) [![Code Climate](https://codeclimate.com/github/jellymann/mittsu/badges/gpa.svg)](https://codeclimate.com/github/jellymann/mittsu)
4
4
 
5
5
  3D Graphics Library for Ruby
6
6
 
@@ -49,5 +49,18 @@ module Mittsu
49
49
 
50
50
  camera
51
51
  end
52
+
53
+ protected
54
+
55
+ def jsonify
56
+ data = super
57
+ data[:left] = self.left
58
+ data[:right] = self.right
59
+ data[:top] = self.top
60
+ data[:bottom] = self.bottom
61
+ data[:near] = self.near
62
+ data[:far] = self.far
63
+ data
64
+ end
52
65
  end
53
66
  end
@@ -111,5 +111,16 @@ module Mittsu
111
111
 
112
112
  camera
113
113
  end
114
+
115
+ protected
116
+
117
+ def jsonify
118
+ data = super
119
+ data[:fov] = self.fov
120
+ data[:aspect] = self.aspect
121
+ data[:near] = self.near
122
+ data[:far] = self.far
123
+ data
124
+ end
114
125
  end
115
126
  end
@@ -1,9 +1,8 @@
1
1
  require 'securerandom'
2
2
  require 'mittsu'
3
- require 'mittsu/core/hash_object'
4
3
 
5
4
  module Mittsu
6
- class Geometry < HashObject
5
+ class Geometry
7
6
  include EventDispatcher
8
7
 
9
8
  MorphNormal = Struct.new(:face_normals, :vertex_normals)
@@ -17,7 +16,7 @@ module Mittsu
17
16
 
18
17
  def initialize
19
18
  super
20
-
19
+
21
20
  @id = (@@id ||= 1).tap { @@id += 1 }
22
21
 
23
22
  @name = ''
@@ -526,12 +525,12 @@ module Mittsu
526
525
  faces << get_color_index(vertex_colors[2], colors_hash, colors)
527
526
  end
528
527
  end
529
- output.data = {}
530
- output.data.vertices = vertices
531
- output.data.normals = normals
532
- output.data.colors = colors unless colors.empty?
533
- output.data.uvs = [uvs] unless uvs.empty? # temporal backward compatibility
534
- output.data.faces = faces
528
+ output[:data] = {}
529
+ output[:data][:vertices] = vertices
530
+ output[:data][:normals] = normals
531
+ output[:data][:colors] = colors unless colors.empty?
532
+ output[:data][:uvs] = [uvs] unless uvs.empty? # temporal backward compatibility
533
+ output[:data][:faces] = faces
535
534
 
536
535
  #
537
536
 
@@ -563,6 +562,10 @@ module Mittsu
563
562
  self.dispatch_event type: :dispose
564
563
  end
565
564
 
565
+ def implementation(renderer)
566
+ @_implementation ||= renderer.create_implementation(self)
567
+ end
568
+
566
569
  private
567
570
 
568
571
  def set_bit(value, position, enabled)
@@ -1,9 +1,8 @@
1
1
  require 'securerandom'
2
2
  require 'mittsu'
3
- require 'mittsu/core/hash_object'
4
3
 
5
4
  module Mittsu
6
- class Object3D < HashObject
5
+ class Object3D
7
6
  include EventDispatcher
8
7
 
9
8
  attr_accessor :name, :children, :up, :position, :rotation, :quaternion, :scale, :rotation_auto_update, :matrix, :matrix_world, :matrix_auto_update, :matrix_world_needs_update, :visible, :cast_shadow, :receive_shadow, :frustum_culled, :render_order, :user_data, :parent, :geometry
@@ -271,7 +270,7 @@ module Mittsu
271
270
 
272
271
  def traverse_ancestors(&callback)
273
272
  if @parent
274
- callback.yielf @parent
273
+ callback.yield @parent
275
274
  @parent.traverse_ancestors(&callback)
276
275
  end
277
276
  end
@@ -308,11 +307,12 @@ module Mittsu
308
307
  }
309
308
  @_geometries = {}
310
309
  @_materials = {}
311
- @_output[:object] = parse_object(self)
310
+ @_output[:object] = self.jsonify
312
311
  @_output
313
312
  end
314
313
 
315
- def clone(object = Object3D.new, recursive = true)
314
+ def clone(object = nil, recursive = true)
315
+ object ||= Object3D.new
316
316
  object.name = @name
317
317
  object.up.copy(@up)
318
318
  object.position.copy(@position)
@@ -336,9 +336,34 @@ module Mittsu
336
336
  object
337
337
  end
338
338
 
339
- private
339
+ def implementation(renderer)
340
+ @_implementation ||= renderer.create_implementation(self)
341
+ end
342
+
343
+ protected
340
344
 
341
- def parse_geometry(geometry)
345
+ def jsonify
346
+ data = {
347
+ uuid: @uuid,
348
+ type: @type,
349
+ matrix: @matrix.to_a
350
+ }
351
+ data[:name] = @name unless @name.nil? || @name.empty?
352
+ data[:user_data] = @user_data unless @user_data.nil? || @user_data.empty?
353
+ data[:visible] = @visible unless @visible
354
+
355
+ if !self.children.empty?
356
+ data[:children] = @children.map do |child|
357
+ child.jsonify
358
+ end
359
+ end
360
+
361
+ # TODO: implement jsonify for PointCloud
362
+
363
+ data
364
+ end
365
+
366
+ def jsonify_geometry(geometry)
342
367
  @_output[:geometries] ||= []
343
368
  if @_geometries[geometry.uuid].nil?
344
369
  json = geometry.to_json
@@ -349,7 +374,7 @@ module Mittsu
349
374
  geometry.uuid
350
375
  end
351
376
 
352
- def parse_material(material)
377
+ def jsonify_material(material)
353
378
  @_output[:materials] ||= []
354
379
  if @_materials[material.uuid].nil?
355
380
  json = material.to_json
@@ -359,63 +384,5 @@ module Mittsu
359
384
  end
360
385
  material.uuid
361
386
  end
362
-
363
- def parse_object(object)
364
- data = {}
365
- data[:uuid] = object.uuid
366
- data[:type] = object.type
367
- data[:name] = object.name unless object.name.nil? || object.name.empty?
368
- data.user_data = object.user_data unless object.user_data.nil? || object.user_data.empty?
369
- data[:visible] = object.visible unless object.visible
370
-
371
- case object
372
- when PerspectiveCamera
373
- data[:fov] = object.fov
374
- data[:aspect] = object.aspect
375
- data[:near] = object.near
376
- data[:far] = object.far
377
- when OrthographicCamera
378
- data[:left] = object.left
379
- data[:right] = object.right
380
- data[:top] = object.top
381
- data[:bottom] = object.bottom
382
- data[:near] = object.near
383
- data[:far] = object.far
384
- when AmbientLight
385
- data[:color] = object.color.get_hex
386
- when DirectionalLight
387
- data[:color] = object.color.get_hex
388
- data[:intensity] = object.intensity
389
- when PointLight
390
- data[:color] = object.color.get_hex
391
- data[:intensity] = object.intensity
392
- data[:distance] = object.distance
393
- data[:decay] = object.decay
394
- when SpotLight
395
- data[:color] = object.color.get_hex
396
- data[:intensity] = object.intensity
397
- data[:distance] = object.distance
398
- data[:angle] = object.angle
399
- data[:exponent] = object.exponent
400
- data[:decay] = object.decay
401
- when HemisphereLight
402
- data[:color] = object.color.get_hex
403
- data[:ground_color] = object.ground_color.get_hex
404
- when Mesh, Line, PointCloud
405
- data[:geometry] = parse_geometry(object.geometry)
406
- data[:material] = parse_material(object.material)
407
- data[:mode] = object.mode if object.is_a? Line
408
- when Sprite
409
- data[:material] = parse_material(object.material)
410
- end
411
- data[:matrix] = object.matrix.to_a
412
- if !object.children.length.empty?
413
- data[:children] = []
414
- object.children.each do |child|
415
- data[:children] << parse_object(child)
416
- end
417
- end
418
- data
419
- end
420
387
  end
421
388
  end
@@ -12,5 +12,13 @@ module Mittsu
12
12
  super(light)
13
13
  light
14
14
  end
15
+
16
+ protected
17
+
18
+ def jsonify
19
+ data = super
20
+ data[:color] = self.color.get_hex
21
+ data
22
+ end
15
23
  end
16
24
  end
@@ -127,5 +127,14 @@ module Mittsu
127
127
  light.shadow_cascade_near_z = @shadow_cascade_near_z.dup
128
128
  light.shadow_cascade_far_z = @shadow_cascade_far_z.dup
129
129
  end
130
+
131
+ protected
132
+
133
+ def jsonify
134
+ data = super
135
+ data[:color] = self.color.get_hex
136
+ data[:intensity] = self.intensity
137
+ data
138
+ end
130
139
  end
131
140
  end
@@ -25,5 +25,14 @@ module Mittsu
25
25
 
26
26
  light
27
27
  end
28
+
29
+ protected
30
+
31
+ def jsonify
32
+ data = super
33
+ data[:color] = self.color.get_hex
34
+ data[:ground_color] = self.ground_color.get_hex
35
+ data
36
+ end
28
37
  end
29
38
  end
@@ -23,5 +23,16 @@ module Mittsu
23
23
  light.decay = @decay
24
24
  light
25
25
  end
26
+
27
+ protected
28
+
29
+ def jsonify
30
+ data = super
31
+ data[:color] = self.color.get_hex
32
+ data[:intensity] = self.intensity
33
+ data[:distance] = self.distance
34
+ data[:decay] = self.decay
35
+ data
36
+ end
26
37
  end
27
38
  end
@@ -100,5 +100,18 @@ module Mittsu
100
100
 
101
101
  return light
102
102
  end
103
+
104
+ protected
105
+
106
+ def jsonify
107
+ data = super
108
+ data[:color] = self.color.get_hex
109
+ data[:intensity] = self.intensity
110
+ data[:distance] = self.distance
111
+ data[:angle] = self.angle
112
+ data[:exponent] = self.exponent
113
+ data[:decay] = self.decay
114
+ data
115
+ end
103
116
  end
104
117
  end
@@ -3,5 +3,6 @@ require 'mittsu/loaders/loader'
3
3
  require 'mittsu/loaders/loading_manager'
4
4
  require 'mittsu/loaders/image_loader'
5
5
  require 'mittsu/loaders/file_loader'
6
+ require 'mittsu/loaders/obj_loader'
6
7
  require 'mittsu/loaders/mtl_loader'
7
8
  require 'mittsu/loaders/obj_mtl_loader'
@@ -2,15 +2,13 @@ module Mittsu
2
2
  class MTLLoader
3
3
  include EventDispatcher
4
4
 
5
- def initialize(base_url, options = {}) # TODO: cross_origin?
5
+ def initialize(base_url, options = {})
6
6
  @base_url = base_url
7
7
  @options = options
8
- # @cross_origin = cross_origin
9
8
  end
10
9
 
11
10
  def load(url)
12
11
  loader = FileLoader.new
13
- # loader.cross_origin = @cross_origin
14
12
 
15
13
  text = loader.load File.join(@base_url, url)
16
14
  parse(text)
@@ -36,14 +34,11 @@ module Mittsu
36
34
  value = value.strip
37
35
 
38
36
  if key == "newmtl"
39
- # New material
40
-
41
37
  info = { name: value };
42
38
  materials_info[value] = info
43
39
  elsif info
44
40
  if key == "ka" || key == "kd" || key == "ks"
45
- ss = value.split(delimiter_pattern).take(3)
46
- info[key] = [ss[0].to_f, ss[1].to_f, ss[2].to_f]
41
+ info[key] = value.split(delimiter_pattern).take(3).map(&:to_f)
47
42
  else
48
43
  info[key] = value
49
44
  end
@@ -81,8 +76,7 @@ module Mittsu
81
76
  converted = {}
82
77
 
83
78
  materials_info.each do |mn, mat|
84
- covmat = {}
85
- converted[mn] = covmat
79
+ covmat = converted[mn] ={}
86
80
 
87
81
  mat.each do |prop, value|
88
82
  save = true
@@ -97,7 +91,7 @@ module Mittsu
97
91
  end
98
92
 
99
93
  if @options && @options[:ignore_zero_rgbs]
100
- if value.take(3).any?(&:zero?)
94
+ if value.take(3).all?(&:zero?)
101
95
  # ignore
102
96
  save = false
103
97
  end
@@ -108,7 +102,7 @@ module Mittsu
108
102
  # factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent)
109
103
 
110
104
  if @options && @options[:invert_transparency]
111
- value = 1.0 - value
105
+ value = 1.0 - value.to_f
112
106
  end
113
107
  end
114
108
 
@@ -155,7 +149,6 @@ module Mittsu
155
149
  texture = Texture.new
156
150
 
157
151
  loader = ImageLoader.new
158
- # loader.cross_origin = @cross_origin # TODO: ???
159
152
  image = loader.load url
160
153
 
161
154
  texture.image = ensure_power_of_two(image)
@@ -0,0 +1,212 @@
1
+ module Mittsu
2
+ class OBJLoader
3
+ include EventDispatcher
4
+
5
+ FLOAT = /[\d|.|+|\-|e]+/
6
+
7
+ VERTEX_PATTERN = /^v\s+(#{FLOAT})\s+(#{FLOAT})\s+(#{FLOAT})/
8
+ NORMAL_PATTERN = /^vn\s+(#{FLOAT})\s+(#{FLOAT})\s+(#{FLOAT})/
9
+ UV_PATTERN = /^vt\s+(#{FLOAT})\s+(#{FLOAT})/
10
+
11
+ FACE_PATTERN = /^f\s+/
12
+ FACE_V_PATTERN = /^f\s+(\d+)\s+(\d+)\s+(\d+)(?:\s+(\d+))?/
13
+ FACE_V_VT_PATTERN = /^f\s+(\d+)\/(\d+)\s+(\d+)\/(\d+)\s+(\d+)\/(\d+)(?:\s+(\d+)\/(\d+))?/
14
+ FACE_V_VN_PATTERN = /^f\s+(\d+)\/\/(\d+)\s+(\d+)\/\/(\d+)\s+(\d+)\/\/(\d+)(?:\s+(\d+)\/\/(\d+))?/
15
+ FACE_V_VT_VN_PATTERN = /^f\s+(\d+)\/(\d+)\/(\d+)\s+(\d+)\/(\d+)\/(\d+)\s+(\d+)\/(\d+)\/(\d+)(?:\s+(\d+)\/(\d+)\/(\d+))?/
16
+
17
+ OBJECT_PATTERN = /^o\s+(.+)$/
18
+ GROUP_PATTERN = /^g\s+(.+)$/
19
+ SMOOTH_GROUP_PATTERN = /^s\s+(\d|true|false|on|off)$/
20
+
21
+ USE_MTL_PATTERN = /^usemtl\s+(.+)$/
22
+ LOAD_MTL_PATTERN = /^mtllib\s+(.+)$/
23
+
24
+ def initialize(manager = DefaultLoadingManager)
25
+ @manager = manager
26
+ end
27
+
28
+ def load(url)
29
+ loader = FileLoader.new(@manager)
30
+
31
+ text = loader.load(url)
32
+ parse(text)
33
+ end
34
+
35
+ def parse(data)
36
+ init_parsing
37
+ relevant_lines(data).each { |line| parse_line(line) }
38
+ end_object
39
+ @group
40
+ end
41
+
42
+ private
43
+
44
+ def parse_line(line)
45
+ case line
46
+ when VERTEX_PATTERN then handle_vertex($1.to_f, $2.to_f, $3.to_f)
47
+ when NORMAL_PATTERN then handle_normal($1.to_f, $2.to_f, $3.to_f)
48
+ when UV_PATTERN then handle_uv($1.to_f, $2.to_f)
49
+
50
+ when FACE_PATTERN then parse_face(line)
51
+
52
+ when OBJECT_PATTERN then new_object($1) and reset_vertices
53
+ when GROUP_PATTERN # ignore
54
+ when SMOOTH_GROUP_PATTERN # ignore
55
+
56
+ when USE_MTL_PATTERN then set_material($1)
57
+ when LOAD_MTL_PATTERN # TODO
58
+ else raise "Mittsu::OBJMTLLoader: Unhandled line #{line}"
59
+ end
60
+ end
61
+
62
+ def parse_face(line)
63
+ case line
64
+ when FACE_V_PATTERN then handle_face(
65
+ [$1, $2, $3, $4]) #face
66
+ #(uv)
67
+ #(normal)
68
+ when FACE_V_VT_PATTERN then handle_face(
69
+ [$1, $3, $5, $7], #face
70
+ [$2, $4, $6, $8]) #uv
71
+ #(normal)
72
+ when FACE_V_VN_PATTERN then handle_face(
73
+ [$1, $3, $5, $7 ], #face
74
+ [], #(uv)
75
+ [$2, $4, $6, $8 ]) #normal
76
+ when FACE_V_VT_VN_PATTERN then handle_face(
77
+ [$1, $4, $7, $10], #face
78
+ [$2, $5, $8, $11], #uv
79
+ [$3, $6, $9, $12]) #normal
80
+ end
81
+ end
82
+
83
+ def face3(a, b, c, normals = nil)
84
+ Face3.new(a, b, c, normals)
85
+ end
86
+
87
+ def init_parsing
88
+ @face_offset = 0
89
+ @group = Group.new
90
+ @vertices = []
91
+ @normals = []
92
+ @uvs = []
93
+ end
94
+
95
+ def reset_vertices
96
+ @face_offset = @face_offset + @vertices.length
97
+ @vertices = []
98
+ end
99
+
100
+ def relevant_lines(raw_lines)
101
+ raw_lines.split("\n").map(&:strip).reject(&:empty?).reject{|l| l.start_with? '#'}
102
+ end
103
+
104
+ def new_object(object_name = '')
105
+ end_object
106
+ @object = Object3D.new
107
+ @object.name = object_name
108
+ end
109
+
110
+ def end_object
111
+ return if @object.nil?
112
+ end_mesh
113
+ @group.add(@object)
114
+ @object = nil
115
+ end
116
+
117
+ def new_mesh
118
+ end_mesh
119
+ new_object if @object.nil?
120
+ @geometry = Geometry.new
121
+ @mesh = Mesh.new(@geometry, @material || MeshLambertMaterial.new)
122
+ @mesh.name = @object.name
123
+ @mesh.name += " #{@material.name}" unless @material.nil?
124
+ end
125
+
126
+ def end_mesh
127
+ return if @mesh.nil? || @vertices.empty?
128
+ @geometry.vertices = @vertices
129
+
130
+ @geometry.merge_vertices
131
+ @geometry.compute_face_normals
132
+ @geometry.compute_bounding_sphere
133
+
134
+ @object.add(@mesh)
135
+ @mesh = nil
136
+ end
137
+
138
+ def set_material(material_name)
139
+ end_mesh
140
+
141
+ @material = MeshLambertMaterial.new
142
+ @material.name = material_name
143
+ end
144
+
145
+ def handle_vertex(x, y, z)
146
+ @vertices << Vector3.new(x, y, z)
147
+ end
148
+
149
+ def handle_normal(x, y, z)
150
+ @normals << Vector3.new(x, y, z)
151
+ end
152
+
153
+ def handle_uv(u, v)
154
+ @uvs << Vector2.new(u, v)
155
+ end
156
+
157
+ def add_face(a, b, c, normal_inds = nil)
158
+ if normal_inds.nil?
159
+ @geometry.faces << face3(
160
+ a.to_i - (@face_offset + 1),
161
+ b.to_i - (@face_offset + 1),
162
+ c.to_i - (@face_offset + 1)
163
+ )
164
+ else
165
+ @geometry.faces << face3(
166
+ a.to_i - (@face_offset + 1),
167
+ b.to_i - (@face_offset + 1),
168
+ c.to_i - (@face_offset + 1),
169
+ normal_inds.take(3).map { |i| @normals[i.to_i - 1].clone }
170
+ )
171
+ end
172
+ end
173
+
174
+ def add_uvs(a, b, c)
175
+ @geometry.face_vertex_uvs[0] << [
176
+ @uvs[a.to_i - 1].clone,
177
+ @uvs[b.to_i - 1].clone,
178
+ @uvs[c.to_i - 1].clone
179
+ ]
180
+ end
181
+
182
+ def handle_triangle(faces, uvs, normal_inds)
183
+ add_face(faces[0], faces[1], faces[2], normal_inds)
184
+
185
+ if !uvs.nil? && !uvs.empty?
186
+ add_uvs(uvs[0], uvs[1], uvs[2])
187
+ end
188
+ end
189
+
190
+ def handle_face(faces, uvs = [], normal_inds = [])
191
+ new_mesh if @mesh.nil?
192
+ if faces[3].nil?
193
+ handle_triangle(faces, uvs, normal_inds)
194
+ else
195
+ handle_quad(faces, uvs, normal_inds)
196
+ end
197
+ end
198
+
199
+ def handle_quad(faces, uvs, normal_inds)
200
+ handle_quad_triangle(faces, uvs, normal_inds, [0, 1, 3])
201
+ handle_quad_triangle(faces, uvs, normal_inds, [1, 2, 3])
202
+ end
203
+
204
+ def handle_quad_triangle(faces, uvs, normal_inds, tri_inds)
205
+ handle_triangle(
206
+ faces.values_at(*tri_inds).compact,
207
+ uvs.values_at(*tri_inds).compact,
208
+ normal_inds.values_at(*tri_inds).compact
209
+ )
210
+ end
211
+ end
212
+ end