mittsu 0.1.1 → 0.1.2

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