Class: Mittsu::OpenGLRenderer

Inherits:
Object
  • Object
show all
Defined in:
lib/mittsu/renderers/opengl_renderer.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parameters = {}) ⇒ OpenGLRenderer

Returns a new instance of OpenGLRenderer.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 23

def initialize(parameters = {})
  puts "OpenGLRenderer (Revision #{REVISION})"

  @pixel_ratio = 1.0

  @precision = parameters.fetch(:precision, 'highp') # not sure if OpenGL works with the whole 'highp' thing...
  @_alpha = parameters.fetch(:alpha, false)
  @_depth = parameters.fetch(:depth, true)
  @_stencil = parameters.fetch(:stencil, true)
  @_antialias = parameters.fetch(:antialias, false)
  @_premultiplied_alpha = parameters.fetch(:premultiplied_alpha, true)
  @_preserve_drawing_buffer = parameters.fetch(:preserve_drawing_buffer, false)
  @_logarithmic_depth_buffer = parameters.fetch(:logarithmic_depth_buffer, false)

  @_clear_color = Color.new(0x000000)
  @_clear_alpha = 0.0

  @width = parameters.fetch(:width, 800)
  @height = parameters.fetch(:height, 600)
  @title = parameters.fetch(:title, "Mittsu #{REVISION}")

  @lights = []

  @_opengl_objects = {}
  @_opengl_objects_immediate = []

  @opaque_objects = []
  @transparent_objects = []

  @sprites = []
  @lens_flares = []

  # public properties

  # @dom_element = _canvas
  # @context = nil

  # clearing

  @auto_clear = true
  @auto_clear_color = true
  @auto_clear_depth = true
  @auto_clear_stencil = true

  # scene graph

  @sort_objects = true

  # physically based shading

  @gamma_factor = 2.0 # backwards compat???
  @gamma_input = false
  @gamma_output = false

  # shadow map

  @shadow_map_enabled = false
  @shadow_map_type = PCFShadowMap
  @shadow_map_cull_face = CullFaceFront
  @shadow_map_debug = false
  @shadow_map_cascade = false

  # morphs

  @max_morph_targets = 8
  @max_morph_normals = 4

  # info

  @info = {
    memory: {
      programs: 0,
      geometries: 0,
      textures: 0
    },
    render: {
      calls: 0,
      vertices: 0,
      faces: 0,
      points: 0
    }
  }

  # internal properties

  @_programs = []

  # internal state cache

  @_current_program = nil
  @_current_framebuffer = nil
  @_current_material_id = -1
  @_current_geometry_program = ''
  @_current_camera = nil

  @_used_texture_units = 0
  @_viewport_x = 0
  @_viewport_y = 0
  @_current_width = 0
  @_current_height = 0

  # frustum

  @_frustum = Frustum.new

  # camera matrices cache

  @_proj_screen_matrix = Matrix4.new
  @_vector3 = Vector3.new

  # light arrays cache

  @_direction = Vector3.new
  @_lights_need_update = true
  # TODO: re-imagine this thing as a bunch of classes...
  @_lights = {
    ambient: [0, 0, 0],
    directional: { length: 0, colors: [], positions: [] },
    point: { length: 0, colors: [], positions: [], distances: [], decays: [] },
    spot: { length: 0, colors: [], positions: [], distances: [], directions: [], angles_cos: [], exponents: [], decays: [] },
    hemi: { length: 0, sky_colors: [], ground_colors: [], positions: []}
  }

  @geometry_groups = {}
  @geometry_group_counter = 0

  @shader_ids = {
    # MeshDepthMaterial => :depth, # TODO...
    # MeshNormalMaterial => :normal, # TODO...
    MeshBasicMaterial => :basic,
    MeshLambertMaterial => :lambert,
    MeshPhongMaterial => :phong,
    LineBasicMaterial => :basic,
    # LineDashedMaterial => :dashed, # TODO...
    # PointCloudMaterial => :particle_basic # TODO...
  }

  # initialize

  begin
    # attributes = {
    #   alpha: _alpha,
    #   depth: _depth,
    #   stencil: _stencil,
    #   antialias: _antialias,
    #   premultiplied_alpha: _premultiplied_alpha,
    #   preserve_drawing_buffer: _preserve_drawing_buffer
    # }

    @window = GLFW::Window.new(@width, @height, @title)

    @_viewport_width, @_viewport_height = *(@window.framebuffer_size)

    # TODO: handle losing opengl context??
  rescue => error
    puts "ERROR: Mittsu::OpenGLRenderer: #{error.inspect}"
  end

  @state = OpenGLState.new(self.method(:param_mittsu_to_gl))

  # TODO: get shader precision format???
  # TODO: load extensions??

  reset_gl_state
  set_default_gl_state

  # GPU capabilities

  @_max_textures = get_gl_parameter(GL_MAX_TEXTURE_IMAGE_UNITS)
  @_max_vertex_textures = get_gl_parameter(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS)
  @_max_texture_size = get_gl_parameter(GL_MAX_TEXTURE_SIZE)
  @_max_cubemap_size = get_gl_parameter(GL_MAX_CUBE_MAP_TEXTURE_SIZE)

  @_supports_vertex_textures = @_max_vertex_textures > 0
  @_supports_bone_textures = @_supports_vertex_textures && false # TODO: extensions.get('OES_texture_float') ????

  #

  # TODO: get more shader precision formats ???

  # TODO: clear precision to maximum available ???

  # Plugins

  # TODO: when plugins are ready
  @shadow_map_plugin = ShadowMapPlugin.new(self, @lights, @_opengl_objects, @_opengl_objects_immediate)
  #
  # @sprite_plugin = SpritePlugin(self, @sprites)
  # @lens_flare_plugin = LensFlarePlugin(self, @lens_flares)

  # Events

  @on_object_removed = -> (event) {
    object = event.target
    object.traverse do |child|
      child.remove_event_listener(:remove, @on_object_removed)
      remove_child(child)
    end
  }

  @on_geometry_dispose = -> (event) {
    geometry = event.target
    geometry.remove_event_listener(:dispose, @on_geometry_dispose)
    deallocate_geometry(geometry)
  }

  @on_texture_dispose = -> (event) {
    texture = event.target
    texture.remove_event_listener(:dispose, @on_texture_dispose)
    deallocate_texture(texture)
    @info[:memory][:textures] -= 1
  }

  @on_render_target_dispose = -> (event) {
    render_target = event.target
    render_target.remove_event_listener(:dispose, @on_render_target_dispose)
    deallocate_render_target(render_target)
    @info[:memory][:textures] -= 1
  }

  @on_material_dispose = -> (event) {
    material = event.target
    material.remove_event_listener(:dispose, @on_material_dispose)
    deallocate_material(material)
  }
end

Instance Attribute Details

#auto_clearObject

Returns the value of attribute auto_clear.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def auto_clear
  @auto_clear
end

#auto_clear_colorObject

Returns the value of attribute auto_clear_color.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def auto_clear_color
  @auto_clear_color
end

#auto_clear_depthObject

Returns the value of attribute auto_clear_depth.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def auto_clear_depth
  @auto_clear_depth
end

#auto_clear_stencilObject

Returns the value of attribute auto_clear_stencil.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def auto_clear_stencil
  @auto_clear_stencil
end

#gamma_factorObject

Returns the value of attribute gamma_factor.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def gamma_factor
  @gamma_factor
end

#gamma_inputObject

Returns the value of attribute gamma_input.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def gamma_input
  @gamma_input
end

#gamma_outputObject

Returns the value of attribute gamma_output.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def gamma_output
  @gamma_output
end

#heightObject

Returns the value of attribute height.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def height
  @height
end

#infoObject

Returns the value of attribute info.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def info
  @info
end

#max_morph_normalsObject

Returns the value of attribute max_morph_normals.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def max_morph_normals
  @max_morph_normals
end

#max_morph_targetsObject

Returns the value of attribute max_morph_targets.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def max_morph_targets
  @max_morph_targets
end

#pixel_ratioObject

Returns the value of attribute pixel_ratio.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def pixel_ratio
  @pixel_ratio
end

#shadow_map_cascadeObject

Returns the value of attribute shadow_map_cascade.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def shadow_map_cascade
  @shadow_map_cascade
end

#shadow_map_cull_faceObject

Returns the value of attribute shadow_map_cull_face.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def shadow_map_cull_face
  @shadow_map_cull_face
end

#shadow_map_debugObject

Returns the value of attribute shadow_map_debug.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def shadow_map_debug
  @shadow_map_debug
end

#shadow_map_enabledObject

Returns the value of attribute shadow_map_enabled.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def shadow_map_enabled
  @shadow_map_enabled
end

#shadow_map_typeObject

Returns the value of attribute shadow_map_type.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def shadow_map_type
  @shadow_map_type
end

#sort_objectsObject

Returns the value of attribute sort_objects.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def sort_objects
  @sort_objects
end

#stateObject

Returns the value of attribute state.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def state
  @state
end

#widthObject

Returns the value of attribute width.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def width
  @width
end

#windowObject

Returns the value of attribute window.



21
22
23
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 21

def window
  @window
end

Instance Method Details

#clear(color = true, depth = true, stencil = true) ⇒ Object



314
315
316
317
318
319
320
321
322
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 314

def clear(color = true, depth = true, stencil = true)
  bits = 0

  bits |= GL_COLOR_BUFFER_BIT if color
  bits |= GL_DEPTH_BUFFER_BIT if depth
  bits |= GL_STENCIL_BUFFER_BIT if stencil

  glClear(bits)
end

#clear_depthObject



324
325
326
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 324

def clear_depth
  glClear(GL_DEPTH_BUFFER_BIT)
end

#clear_stencilObject



328
329
330
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 328

def clear_stencil
  glClear(GL_STENCIL_BUFFER_BIT)
end

#clear_target(render_target, color, depth, stencil) ⇒ Object



332
333
334
335
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 332

def clear_target(render_target, color, depth, stencil)
  set_render_target(render_target)
  clear(color, depth, stencil)
end

#enable_scissor_test(enable) ⇒ Object



289
290
291
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 289

def enable_scissor_test(enable)
  enable ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST)
end

#get_clear_alphaObject



305
306
307
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 305

def get_clear_alpha
  @_clear_alpha
end

#get_clear_colorObject

clearing



295
296
297
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 295

def get_clear_color
  @_clear_color
end

#max_anisotropyObject

TODO: supports_float_textures? ??? TODO: supports[half|standard|compressed|blend min max] … ???



260
261
262
263
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 260

def max_anisotropy
  @_max_anisotropy ||= nil
  # TODO: get max anisotropy ????
end

#render(scene, camera, render_target = nil, force_clear = false) ⇒ Object



445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 445

def render(scene, camera, render_target = nil, force_clear = false)
  if !camera.is_a?(Camera)
    puts "ERROR: Mittsu::OpenGLRenderer#render: camera is not an instance of Mittsu::Camera"
    return
  end

  fog = scene.fog

  # reset caching for this frame

  @_current_geometry_program = ''
  @_current_material_id = -1
  @_current_camera = nil
  @_lights_need_update = true

  # update scene graph
  scene.update_matrix_world if scene.auto_update

  # update camera matrices and frustum
  camera.update_matrix_world if camera.parent.nil?

  # update skeleton objects
  # TODO: when SkinnedMesh is defined
  # scene.traverse do |object|
  #   if object.is_a? SkinnedMesh
  #     object.skeleton.update
  #   end
  # end

  camera.matrix_world_inverse.inverse(camera.matrix_world)

  @_proj_screen_matrix.multiply_matrices(camera.projection_matrix, camera.matrix_world_inverse)
  @_frustum.set_from_matrix(@_proj_screen_matrix)

  @lights.clear
  @opaque_objects.clear
  @transparent_objects.clear

  @sprites.clear
  @lens_flares.clear

  project_object(scene)

  if @sort_objects
    @opaque_objects.sort { |a,b| painter_sort_stable(a,b) }
    @transparent_objects.sort { |a,b| reverse_painter_sort_stable(a,b) }
  end

  # custom render plugins
  # TODO: when plugins are ready
  @shadow_map_plugin.render(scene, camera)

  #

  @info[:render][:calls] = 0
  @info[:render][:vertices] = 0
  @info[:render][:faces] = 0
  @info[:render][:points] = 0

  set_render_target(render_target)

  if @auto_clear || force_clear
    clear(@auto_clear_color, @auto_clear_depth, @auto_clear_stencil)
  end

  # set matrices for immediate objects

  @_opengl_objects_immediate.each do |opengl_object|
    object = opengl_object[:object]

    if object.visible
      setup_matrices(object, camera)
      unroll_immediate_buffer_material(opengl_object)
    end
  end

  if scene.override_material
    override_material = scene.override_material

    set_material(override_material)

    render_objects(opaque_object, camera, @lights, fog, override_material)
    render_objects(transparent_objects, camera, @lights, fog, override_material)
    render_objects_immediate(@_opengl_objects_immediate, nil, camera, @lights, fog, override_material)
  else
    # opaque pass (front-to-back order)

    @state.set_blending(NoBlending)

    render_objects(@opaque_objects, camera, @lights, fog, nil)
    render_objects_immediate(@_opengl_objects_immediate, :opaque, camera, @lights, fog, nil)

    # transparent pass (back-to-front-order)

    render_objects(@transparent_objects, camera, @lights, fog, nil)
    render_objects_immediate(@_opengl_objects_immediate, :transparent, camera, @lights, fog, nil)
  end

  # custom render plugins (post pass)

  # TODO: when plugins are ready
  # @sprite_plugin.render(scene, camera)
  # lens_flare_plugin.render(scene, camera, @_current_width, @_current_height)

  # generate mipmap if we're using any kind of mipmap filtering
  if render_target && render_target.generate_mipmaps && render_target.min_filter != NearestFilter && render_target.min_filter != LinearFilter
    update_render_target_mipmap(render_target)
  end

  # endure depth buffer writing is enabled so it can be cleared on next render
  @state.set_depth_test(true)
  @state.set_depth_write(true)
  @state.set_color_write(true)

  #glFinish ??????
end

#render_buffer(camera, lights, fog, material, geometry_group, object) ⇒ Object



567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 567

def render_buffer(camera, lights, fog, material, geometry_group, object)
  return unless material.visible

  # TODO: place to put this ???
  vertex_array = geometry_group[:_opengl_vertex_array]
  if vertex_array
    glBindVertexArray(vertex_array)
  end

  update_object(object)

  program = set_program(camera, lights, fog, material, object)

  attributes = program.attributes

  update_buffers = false
  wireframe_bit = material.wireframe ? 1 : 0
  geometry_program = "#{geometry_group[:id]}_#{program.id}_#{wireframe_bit}"

  if geometry_program != @_current_geometry_program
    @_current_geometry_program = geometry_program
    update_buffers = true
  end

  @state.init_attributes if update_buffers

  # vertices
  if !material.morph_targets && attributes['position'] && attributes['position'] >= 0
    if update_buffers
      glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_vertex_buffer])

      @state.enable_attribute(attributes['position'])

      glVertexAttribPointer(attributes['position'], 3, GL_FLOAT, GL_FALSE, 0, 0)
    end
  elsif object.morph_target_base
    setup_morph_targets(material, geometry_group, object)
  end

  if update_buffers
    # custom attributes

    # use the per-geometry_group custom attribute arrays which are setup in init_mesh_buffers

    if geometry_group[:_opengl_custom_attributes_list]
      geometry_group[:_opengl_custom_attributes_list].each do |attribute|
        if attributes[attribute.buffer.belongs_to_attribute] >= 0
          glBindBuffer(GL_ARRAY_BUFFER, attribute.buffer)

          @state.enable_attribute(attributes[attribute.buffer.belongs_to_attribute])

          glVertexAttribPointer(attributes[attribute.buffer.belongs_to_attribute], attribute.size, GL_FLOAT, GL_FALSE, 0, 0)
        end
      end
    end

    # colors

    if attributes['color'] && attributes['color'] >= 0
      glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_color_buffer])

      @state.enable_attribute(attributes['color'])

      glVertexAttribPointer(attributes['color'], 3, GL_FLOAT, GL_FALSE, 0, 0)
    elsif !material.default_attribute_values.nil?
      glVertexAttrib3fv(attributes['color'], material.default_attribute_values.color)
    end

    # normals

    if attributes['normal'] && attributes['normal'] >= 0
      glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_normal_buffer])

      @state.enable_attribute(attributes['normal'])

      glVertexAttribPointer(attributes['normal'], 3, GL_FLOAT, GL_FALSE, 0, 0)
    end

    # tangents

    if attributes['tangent'] && attributes['tangent'] >= 0
      glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_tangent_buffer])

      @state.enable_attribute(attributes['tangent'])

      glVertexAttribPointer(attributes['tangent'], 4, GL_FLOAT, GL_FALSE, 0, 0)
    end

    # uvs

    if attributes['uv'] && attributes['uv'] >= 0
      if object.geometry.face_vertex_uvs[0]
        glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_uv_buffer])

        @state.enable_attribute(attributes['uv'])

        glVertexAttribPointer(attributes['uv'], 2, GL_FLOAT, GL_FALSE, 0, 0)
      elsif !material.default_attribute_values.nil?
        glVertexAttrib2fv(attributes['uv'], material.default_attribute_values.uv)
      end
    end

    if attributes['uv2'] && attributes['uv2'] >= 0
      if object.geometry.face_vertex_uvs[1]
        glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_uv2_buffer])

        @state.enable_attribute(attributes['uv2'])

        glVertexAttribPointer(attributes['uv2'], 2, GL_FLOAT, GL_FALSE, 0, 0)
      elsif !material.default_attribute_values.nil?
        glVertexAttrib2fv(attributes['uv2'], material.default_attribute_values.uv2)
      end
    end

    if material.skinning && attributes['skin_index'] && attributes['skin_weight'] && attributes['skin_index'] >= 0 && attributes['skin_weight'] >= 0
      glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_skin_indices_buffer])

      @state.enable_attribute(attributes['skin_index'])

      glVertexAttribPointer(attributes['skin_index'], 4, GL_FLOAT, GL_FALSE, 0, 0)

      glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_skin_weight_buffer])

      @state.enable_attribute(attributes['skin_weight'])

      glVertexAttribPointer(attributes['skin_weight'], 4, GL_FLOAT, GL_FALSE, 0, 0)
    end

    # line distances

    if attributes['line_distances'] && attributes['line_distances'] >= 0
      glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_line_distance_buffer])

      @state.enable_attribute(attributes['line_distance'])

      glVertexAttribPointer(attributes['line_distance'], 1, GL_FLOAT, GL_FALSE, 0, 0)
    end
  end

  @state.disable_unused_attributes

  case object

  # render mesh
  when Mesh
    type = GL_UNSIGNED_INT # geometry_group[:_type_array] == Uint32Array ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT

    # wireframe
    if material.wireframe
      @state.set_line_width(material.wireframe_linewidth * @pixel_ratio)

      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry_group[:_opengl_line_buffer]) if update_buffers
      glDrawElements(GL_LINES, geometry_group[:_opengl_line_count], type, 0)

    # triangles
    else
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry_group[:_opengl_face_buffer]) if update_buffers
      glDrawElements(GL_TRIANGLES, geometry_group[:_opengl_face_count], type, 0)
    end

    @info[:render][:calls] += 1
    @info[:render][:vertices] += geometry_group[:_opengl_face_count]
    @info[:render][:faces] += geometry_group[:_opengl_face_count] / 3
  when Line
    mode = object.mode == LineStrip ? GL_LINE_STRIP : GL_LINES

    @state.set_line_width(material.line_width * @pixel_ratio)

    glDrawArrays(mode, 0, geometry_group[:_opengl_line_count])

    @info[:render][:calls] += 1

  # TODO: render particles
  # when PointCloud
  #   glDrawArrays(GL_POINTS, 0, geometry_group[:_opengl_particle_count])
  #
  #   @info[:render][:calls] += 1
  #   @info[:render][:points] += geometry_group[:_opengl_particle_count]
  end
end

#reset_gl_stateObject



337
338
339
340
341
342
343
344
345
346
347
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 337

def reset_gl_state
  @_current_program = nil
  @_current_camera = nil

  @_current_geometry_program = ''
  @_current_material_id = -1

  @_lights_need_update = true

  @state.reset
end

#set_clear_alpha(alpha) ⇒ Object



309
310
311
312
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 309

def set_clear_alpha(alpha)
  @_clear_alpha = alpha
  clear_color(@_clear_color.r, @_clear_color.g, @_clear_color.b, @_clear_alpha)
end

#set_clear_color(color, alpha = 1.0) ⇒ Object



299
300
301
302
303
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 299

def set_clear_color(color, alpha = 1.0)
  @_clear_color.set(color)
  @_clear_alpha = alpha
  clear_color(@_clear_color.r, @_clear_color.g, @_clear_color.b, @_clear_alpha)
end

#set_material_faces(material) ⇒ Object



562
563
564
565
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 562

def set_material_faces(material)
  @state.set_double_sided(material.side == DoubleSide)
  @state.set_flip_sided(material.side == BackSide)
end

#set_render_target(render_target = nil) ⇒ Object



349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 349

def set_render_target(render_target = nil)
  # TODO: when OpenGLRenderTargetCube exists
  is_cube = false # render_target.is_a? OpenGLRenderTargetCube

  if render_target && render_target[:_opengl_framebuffer].nil?
    render_target.depth_buffer = true if render_target.depth_buffer.nil?
    render_target.stencil_buffer = true if render_target.stencil_buffer.nil?

    render_target.add_event_listener(:dispose, @on_render_target_dispose)

    render_target[:_opengl_texture] = glCreateTexture

    @info[:memory][:textures] += 1

    # Setup texture, create render and frame buffers

    is_target_power_of_two = Math.power_of_two?(render_target.width) && Math.power_of_two?(render_target.height)
    gl_format = param_mittsu_to_gl(render_target.format)
    gl_type = param_mittsu_to_gl(render_target.type)

    if is_cube
      # TODO
    else
      render_target[:_opengl_framebuffer] = glCreateFramebuffer

      if render_target.share_depth_from
        render_target[:_opengl_renderbuffer] = render_target.share_depth_from[:_opengl_renderbuffer]
      else
        render_target[:_opengl_renderbuffer] = glCreateRenderbuffer
      end

      glBindTexture(GL_TEXTURE_2D, render_target[:_opengl_texture])
      set_texture_parameters(GL_TEXTURE_2D, render_target, is_target_power_of_two)

      glTexImage2D(GL_TEXTURE_2D, 0, gl_format, render_target.width, render_target.height, 0, gl_format, gl_type, nil)

      setup_framebuffer(render_target[:_opengl_framebuffer], render_target, GL_TEXTURE_2D)

      if render_target.share_depth_from
        if render_target.depth_buffer && !render_target.stencil_buffer
          glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, render_target[:_opengl_renderbuffer])
        elsif render_target.depth_buffer && render_target.stencil_buffer
          glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, render_target[:_opengl_renderbuffer])
        end
      else
        setup_renderbuffer(render_target[:_opengl_renderbuffer], render_target)
      end

      glGenerateMipmap(GL_TEXTURE_2D) if is_target_power_of_two
    end

    # Release everything

    if is_cube
      # TODO
    else
      glBindTexture(GL_TEXTURE_2D, 0)
    end

    glBindRenderbuffer(GL_RENDERBUFFER, 0)
    glBindFramebuffer(GL_FRAMEBUFFER, 0)
  end

  if render_target
    if is_cube
      # TODO
    else
      framebuffer = render_target[:_opengl_framebuffer]
    end

    width = render_target.width
    height = render_target.height

    vx = 0
    vy = 0
  else
    framebuffer = nil

    width = @_viewport_width
    height = @_viewport_height

    vx = @_viewport_x
    vy = @_viewport_y
  end

  if framebuffer != @_current_framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer || 0)
    glViewport(vx, vy, width, height)

    @_current_framebuffer = framebuffer
  end

  @_current_width = width
  @_current_height = height
end

#set_scissor(x, y, width, height) ⇒ Object



280
281
282
283
284
285
286
287
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 280

def set_scissor(x, y, width, height)
  glScissor(
    x * pixel_ratio,
    y * pixel_ratio,
    width * pixel_ratio,
    height * pixel_ratio
  )
end

#set_size(width, height) ⇒ Object



265
266
267
268
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 265

def set_size(width, height)
  @width, @height = width, height
  self.set_viewport(0, 0, width, height)
end

#set_texture(texture, slot) ⇒ Object



748
749
750
751
752
753
754
755
756
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 748

def set_texture(texture, slot)
  glActiveTexture(GL_TEXTURE0 + slot)

  if texture.needs_update?
    upload_texture(texture)
  else
    glBindTexture(GL_TEXTURE_2D, texture[:_opengl_texture])
  end
end

#set_viewport(x, y, width, height) ⇒ Object



270
271
272
273
274
275
276
277
278
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 270

def set_viewport(x, y, width, height)
  @_viewport_x = x * pixel_ratio
  @_viewport_x = y * pixel_ratio

  @_viewport_width = width * pixel_ratio
  @_viewport_height = height * pixel_ratio

  glViewport(@_viewport_x, @_viewport_y, @_viewport_width, @_viewport_height)
end

#supports_vertex_textures?Boolean

TODO: get_context ??? TODO: force_context_loss ???

Returns:

  • (Boolean)


253
254
255
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 253

def supports_vertex_textures?
  @_supports_vertex_textures
end

#upload_texture(texture) ⇒ Object



758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
# File 'lib/mittsu/renderers/opengl_renderer.rb', line 758

def upload_texture(texture)
  if texture[:_opengl_init].nil?
    texture[:_opengl_init] = true
    texture.add_event_listener :dispose, @on_texture_dispose
    texture[:_opengl_texture] = glCreateTexture
    @info[:memory][:textures] += 1
  end

  glBindTexture(GL_TEXTURE_2D, texture[:_opengl_texture])

  # glPixelStorei(GL_UNPACK_FLIP_Y_WEBGL, texture.flip_y) ???
  # glPixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiply_alpha) ???
  glPixelStorei(GL_UNPACK_ALIGNMENT, texture.unpack_alignment)

  texture.image = clamp_to_max_size(texture.image, @_max_texture_size)

  image = texture.image
  is_image_power_of_two = Math.power_of_two?(image.width) && Math.power_of_two?(image.height)
  gl_format = param_mittsu_to_gl(texture.format)
  gl_type = param_mittsu_to_gl(texture.type)

  set_texture_parameters(GL_TEXTURE_2D, texture, is_image_power_of_two)

  mipmaps = texture.mipmaps

  if texture.is_a?(DataTexture)
    # use manually created mipmaps if available
    # if there are no manual mipmaps
    # set 0 level mipmap and then use GL to generate other mipmap levels

    if !mipmaps.empty? && is_image_power_of_two
      mipmaps.each_with_index do |mipmap, i|
        glTexImage2D(GL_TEXTURE_2D, i, gl_format, mipmap.width, mipmap.height, 0, gl_format, gl_type, mipmap.data)
      end
    else
      glTexImage2D(GL_TEXTURE_2D, 0, gl_format, image.width, image.height, 0, gl_format, gl_type, image.data)
    end
  elsif texture.is_a?(CompressedTexture)
    mipmaps.each_with_index do |mipmap, i|
      if texture.format != RGBAFormat && texture.format != RGBFormat
        if get_compressed_texture_formats.index(gl_format)
          glCompressedTexImage2D(GL_TEXTURE_2D, i, gl_format, mipmap.width, mipmap.height, 0, mipmap.data)
        else
          puts 'WARNING: Mittsu::OpenGLRenderer: Attempt to load unsupported compressed texture format in #upload_texture'
        end
      else
        glTexImage2D(GL_TEXTURE_2D, i, gl_format, mipmap.width, mipmap.height, 0, gl_format, gl_type, mipmap.data)
      end
    end
  else # regular texture (image, video, canvas)
    # use manually created mipmaps if available
    # if there are no manual mipmaps
    # set 0 level mipmap and then use GL to generate other mipmap levels

    if !mipmaps.empty? && is_image_power_of_two
      mipmaps.each_with_index do |mipmap, i|
        glTexImage2D(GL_TEXTURE_2D, i, gl_format, mipmap.width, mipmap.height, 0, gl_format, gl_type, mipmap.data)
      end

      texture.generate_mipmaps = false
    else
      glTexImage2D(GL_TEXTURE_2D, 0, gl_format, texture.image.width, texture.image.height, 0, gl_format, gl_type, texture.image.data)
    end
  end

  if texture.generate_mipmaps && is_image_power_of_two
    glGenerateMipmap(GL_TEXTURE_2D)
  end

  texture.needs_update = false

  if texture.on_update
    texture.on_update.()
  end
end