lib/mittsu/renderers/opengl_renderer.rb in mittsu-0.1.1 vs lib/mittsu/renderers/opengl_renderer.rb in mittsu-0.1.2
- old
+ new
@@ -1,252 +1,80 @@
-
require 'opengl'
require 'glfw'
require 'fiddle'
OpenGL.load_lib
require 'mittsu'
require 'mittsu/renderers/glfw_window'
+require 'mittsu/renderers/opengl/opengl_implementations'
require 'mittsu/renderers/opengl/opengl_debug'
+require 'mittsu/renderers/opengl/opengl_helper'
require 'mittsu/renderers/opengl/opengl_program'
require 'mittsu/renderers/opengl/opengl_state'
+require 'mittsu/renderers/opengl/opengl_geometry_group'
+require 'mittsu/renderers/opengl/opengl_light_renderer'
+require 'mittsu/renderers/opengl/opengl_default_target'
+require 'mittsu/renderers/opengl/opengl_buffer'
require 'mittsu/renderers/opengl/plugins/shadow_map_plugin'
require 'mittsu/renderers/shaders/shader_lib'
require 'mittsu/renderers/shaders/uniforms_utils'
-include OpenGL
+include ENV['DEBUG'] ? OpenGLDebug : OpenGL
+include Mittsu::OpenGLHelper
+require 'mittsu/renderers/opengl/opengl_mittsu_params'
+
module Mittsu
class OpenGLRenderer
attr_accessor :auto_clear, :auto_clear_color, :auto_clear_depth, :auto_clear_stencil, :sort_objects, :gamma_factor, :gamma_input, :gamma_output, :shadow_map_enabled, :shadow_map_type, :shadow_map_cull_face, :shadow_map_debug, :shadow_map_cascade, :max_morph_targets, :max_morph_normals, :info, :pixel_ratio, :window, :width, :height, :state
+ attr_reader :logarithmic_depth_buffer, :max_morph_targets, :max_morph_normals, :shadow_map_type, :shadow_map_debug, :shadow_map_cascade, :programs, :light_renderer, :proj_screen_matrix
+
def initialize(parameters = {})
puts "OpenGLRenderer (Revision #{REVISION})"
- @pixel_ratio = 1.0
+ fetch_parameters(parameters)
- @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
-
+ @pixel_ratio = 1.0
@sort_objects = true
- # physically based shading
+ init_collections
+ init_clearing
+ init_gamma
+ init_shadow_properties
+ init_morphs
+ init_info
+ init_state_cache
+ init_camera_matrix_cache
- @gamma_factor = 2.0 # backwards compat???
- @gamma_input = false
- @gamma_output = false
+ @light_renderer = OpenGLLightRenderer.new(self)
- # shadow map
+ create_window
- @shadow_map_enabled = false
- @shadow_map_type = PCFShadowMap
- @shadow_map_cull_face = CullFaceFront
- @shadow_map_debug = false
- @shadow_map_cascade = false
+ @state = OpenGLState.new
- # 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
+ get_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)
+ init_plugins
+ end
- @_supports_vertex_textures = @_max_vertex_textures > 0
- @_supports_bone_textures = @_supports_vertex_textures && false # TODO: extensions.get('OES_texture_float') ????
+ def supports_bone_textures?
+ @_supports_bone_textures
+ end
- #
+ def supports_vertex_textures?
+ @_supports_vertex_textures
+ end
- # 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)
- }
+ def shadow_map_enabled?
+ @shadow_map_enabled
end
# TODO: get_context ???
# TODO: force_context_loss ???
@@ -266,17 +94,16 @@
@width, @height = width, height
self.set_viewport(0, 0, width, height)
end
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)
+ default_target.set_and_use_viewport(
+ x * pixel_ratio,
+ y * pixel_ratio,
+ width * pixel_ratio,
+ height * pixel_ratio
+ )
end
def set_scissor(x, y, width, height)
glScissor(
x * pixel_ratio,
@@ -288,10 +115,18 @@
def enable_scissor_test(enable)
enable ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST)
end
+ def object_in_frustum?(object)
+ @_frustum.intersects_object?(object)
+ end
+
+ def sort_objects?
+ @sort_objects
+ end
+
# clearing
def get_clear_color
@_clear_color
end
@@ -339,498 +174,162 @@
@_current_camera = nil
@_current_geometry_program = ''
@_current_material_id = -1
- @_lights_need_update = true
+ @light_renderer.reset
@state.reset
end
- def set_render_target(render_target = nil)
+ def set_render_target(render_target = default_target)
# TODO: when OpenGLRenderTargetCube exists
- is_cube = false # render_target.is_a? OpenGLRenderTargetCube
+ # is_cube = 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_impl = render_target.implementation(self)
- render_target.add_event_listener(:dispose, @on_render_target_dispose)
+ # TODO framebuffer logic for render target cube
+ render_target_impl.setup_buffers
- 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)
+ if render_target != @_current_render_target
+ render_target.use
+ @_current_render_target = render_target
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
- 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
+ def render(scene, camera, render_target = default_target, force_clear = false)
+ raise "ERROR: Mittsu::OpenGLRenderer#render: camera is not an instance of Mittsu::Camera" unless camera.is_a?(Camera)
- fog = scene.fog
+ reset_cache_for_this_frame
- # 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
+ update_skeleton_objects(scene)
- camera.matrix_world_inverse.inverse(camera.matrix_world)
+ update_screen_projection(camera)
+ scene.implementation(self).project
+ sort_objects_for_render if @sort_objects
- @_proj_screen_matrix.multiply_matrices(camera.projection_matrix, camera.matrix_world_inverse)
- @_frustum.set_from_matrix(@_proj_screen_matrix)
+ render_custom_plugins_pre_pass(scene, camera)
- @lights.clear
- @opaque_objects.clear
- @transparent_objects.clear
+ set_matrices_for_immediate_objects(camera)
- @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)
+ perform_auto_clear if @auto_clear || force_clear
+ render_main_pass(scene, camera)
- if @auto_clear || force_clear
- clear(@auto_clear_color, @auto_clear_depth, @auto_clear_stencil)
- end
+ render_custom_plugins_post_pass(scene, camera)
- # set matrices for immediate objects
+ render_target.implementation(self).update_mipmap
- @_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 ??????
+ ensure_depth_buffer_writing
end
def set_material_faces(material)
@state.set_double_sided(material.side == DoubleSide)
@state.set_flip_sided(material.side == BackSide)
end
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
+ geometry_group.bind_vertex_array_object
update_object(object)
program = set_program(camera, lights, fog, material, object)
-
attributes = program.attributes
+ buffers_need_update = switch_geometry_program(program, material, geometry_group)
- update_buffers = false
- wireframe_bit = material.wireframe ? 1 : 0
- geometry_program = "#{geometry_group[:id]}_#{program.id}_#{wireframe_bit}"
+ @state.init_attributes if buffers_need_update
- 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
+ geometry_group.update_vertex_buffer(attributes['position']) if buffers_need_update
elsif object.morph_target_base
- setup_morph_targets(material, geometry_group, object)
+ # TODO: when morphing is implemented
+ # 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
+ if buffers_need_update
+ geometry_group.update_other_buffers(object, material, attributes)
end
@state.disable_unused_attributes
- case object
+ object.implementation(self).render_buffer(camera, lights, fog, material, geometry_group, buffers_need_update)
- # 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])
+ # glDrawArrays(GL_POINTS, 0, geometry_group.particle_count)
#
# @info[:render][:calls] += 1
- # @info[:render][:points] += geometry_group[:_opengl_particle_count]
- end
+ # @info[:render][:points] += geometry_group.particle_count
end
- def set_texture(texture, slot)
- glActiveTexture(GL_TEXTURE0 + slot)
+ def compressed_texture_formats
+ # TODO: needs extensions.get ...
- if texture.needs_update?
- upload_texture(texture)
- else
- glBindTexture(GL_TEXTURE_2D, texture[:_opengl_texture])
- end
+ @_compressed_texture_formats ||= []
end
- 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
+ # Events
+
+ def on_object_removed(event)
+ object = event.target
+ object.traverse do |child|
+ child.remove_event_listener(:remove, method(:on_object_removed))
+ remove_child(child)
end
+ end
- glBindTexture(GL_TEXTURE_2D, texture[:_opengl_texture])
+ def on_geometry_dispose(event)
+ geometry = event.target
+ geometry.remove_event_listener(:dispose, method(:on_geometry_dispose))
+ deallocate_geometry(geometry)
+ end
- # 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)
+ def on_texture_dispose(event)
+ texture = event.target
+ texture.remove_event_listener(:dispose, method(:on_texture_dispose))
+ deallocate_texture(texture)
+ @info[:memory][:textures] -= 1
+ end
- texture.image = clamp_to_max_size(texture.image, @_max_texture_size)
+ def on_render_target_dispose(event)
+ render_target = event.target
+ render_target.remove_event_listener(:dispose, method(:on_render_target_dispose))
+ deallocate_render_target(render_target)
+ @info[:memory][:textures] -= 1
+ end
- 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)
+ def on_material_dispose(event)
+ material = event.target
+ material.remove_event_listener(:dispose, method(:on_material_dispose))
+ deallocate_material(material)
+ end
- set_texture_parameters(GL_TEXTURE_2D, texture, is_image_power_of_two)
+ def create_implementation(thing)
+ OPENGL_IMPLEMENTATIONS[thing.class].new(thing, self)
+ end
- mipmaps = texture.mipmaps
+ def clamp_to_max_size(image, max_size = @_max_texture_size)
+ width, height = image.width, image.height
+ if width > max_size || height > max_size
+ # TODO: scale the image ...
- 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
+ puts "WARNING: Mittsu::OpenGLRenderer: image is too big (#{width} x #{height}). Resized to #{@_max_texture_size} x #{@_max_texture_size}"
end
+ image
+ end
- if texture.generate_mipmaps && is_image_power_of_two
- glGenerateMipmap(GL_TEXTURE_2D)
- end
+ def add_opengl_object(buffer, object)
+ add_buffer(@_opengl_objects, buffer, object)
+ end
- texture.needs_update = false
-
- if texture.on_update
- texture.on_update.()
- end
+ def remove_opengl_object(object)
+ @_opengl_objects.delete(object.id)
end
private
def clear_color(r, g, b, a)
@@ -855,600 +354,115 @@
glEnable(GL_BLEND)
glBlendEquation(GL_FUNC_ADD)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
- glViewport(@_viewport_x, @_viewport_y, @_viewport_width, @_viewport_height)
+ default_target.use_viewport
clear_color(@_clear_color.r, @_clear_color.g, @_clear_color.b, @_clear_alpha)
end
- def get_compressed_texture_formats
- return @_compressed_texture_formats ||= []
- # TODO: needs extensions.get ...
- end
-
- def painter_sort_stable(a, b)
- if a[:object].render_order != b[:object].render_order
- a[:object].render_order - b[:object].render_order
- elsif a[:material].id != b[:material].id
- a[:material].id - b[:material].id
- elsif a[:z] != b[:z]
- a[:z] - b[:z]
- else
- a[:id] - b[:id]
- end
- end
-
- def reverse_painter_sort_stable(a, b)
- if a.object.render_order != b.object.render_order
- a.object.render_order - b.object.render_order
- elsif a.z != b.z
- b.z - a.z
- else
- a.id - b.id
- end
- end
-
- def get_gl_parameter(pname)
- data = ' '
- glGetIntegerv(pname, data)
- data.unpack('L')[0]
- end
-
- def project_object(object)
- return unless object.visible
- if object.is_a? Scene # || object.is_a? Group # TODO: when Group is defined
- # skip
- else
- init_object(object)
- if object.is_a? Light
- @lights << object
- # if object.is_a? Sprite # TODO
- # @sprites << object
- # if object.is_a? LensFlare # TODO
- # @lens_flares << object
- else
- opengl_objects = @_opengl_objects[object.id]
- if opengl_objects && (!object.frustum_culled || @_frustum.intersects_object?(object))
- opengl_objects.each do |opengl_object|
- unroll_buffer_material(opengl_object)
- opengl_object[:render] = true
- if @sort_objects
- @_vector3.set_from_matrix_position(object.matrix_world)
- @_vector3.apply_projection(@_proj_screen_matrix)
-
- opengl_object[:z] = @_vector3.z
- end
- end
- end
- end
- end
-
- object.children.each do |child|
- project_object(child)
- end
- end
-
def render_objects(render_list, camera, lights, fog, override_material)
material = nil
render_list.each do |opengl_object|
- object = opengl_object[:object]
- buffer = opengl_object[:buffer]
+ object = opengl_object.object
+ buffer = opengl_object.buffer
- setup_matrices(object, camera)
+ object.implementation(self).setup_matrices(camera)
if override_material
material = override_material
+ material_impl = material.implementation(self)
else
- material = opengl_object[:material]
+ material = opengl_object.material
next unless material
- set_material(material)
+ material_impl = material.implementation(self)
+ material_impl.set
end
set_material_faces(material)
if buffer.is_a? BufferGeometry
- render_buffer_direct(camera, lights, fog, material, buffer, object)
+ # TODO
+ # render_buffer_direct(camera, lights, fog, material, buffer, object)
else
- render_buffer(camera, lights, fog, material, buffer, object)
+ render_buffer(camera, lights, fog, material, buffer.implementation(self), object)
end
end
end
def render_objects_immediate(render_list, material_type, camera, lights, fog, override_material)
material = nil
render_list.each do |opengl_object|
- object = opengl_object[:object]
+ object = opengl_object.object
if object.visible
if override_material
material = override_material
else
- material = opengl_object[material_type]
+ material = case material_type
+ when :transparent then opengl_object.transparent
+ when :opaque then opengl_object.opaque
+ else nil
+ end
next unless material
- set_material(material)
+ material_impl = material.implementation(self)
+ material_impl.set
end
render_immediate_object(camera, lights, fog, material, object)
end
end
end
- def init_object(object)
- if object[:_opengl_init].nil?
- object[:_opengl_init] = true
- object[:_model_view_matrix] = Matrix4.new
- object[:_normal_matrix] = Matrix3.new
-
- object.add_event_listener(:removed, @on_object_removed)
- end
-
- geometry = object.geometry
-
- if geometry.nil?
- # ImmediateRenderObject
- elsif geometry[:_opengl_init].nil?
- geometry[:_opengl_init] = true
- geometry.add_event_listener(:dispose, @on_geometry_dispose)
- case object
- when BufferGeometry
- @info[:memory][:geometries] += 1
- when Mesh
- init_geometry_groups(object, geometry)
- when Line
- if geometry[:_opengl_vertex_buffer].nil?
- create_line_buffers(geometry)
- init_line_buffers(geometry, object)
-
- geometry.vertices_need_update = true
- geometry.colors_need_update = true
- geometry.line_distances_need_update
- end
- # TODO: when PointCloud exists
- # when PointCloud
- # if geometry[:_opengl_vertex_buffer].nil?
- # create_particle_buffers(geometry)
- # init_particle_buffers(geometry, object)
- #
- # geometry.vertices_need_update = true
- # geometry.colors_need_update = true
- # end
- end
- end
-
- if object[:_opengl_active].nil?
- object[:_opengl_active] = true
- case object
- when Mesh
- case geometry
- when BufferGeometry
- add_buffer(@_opengl_objects, geometry, object)
- when Geometry
- geometry_groups_list = @geometry_groups[geometry.id]
- geometry_groups_list.each do |group|
- add_buffer(@_opengl_objects, group, object)
- end
- end
- when Line #, PointCloud TODO
- add_buffer(@_opengl_objects, geometry, object)
- else
- # TODO: when ImmediateRenderObject exists
- # if object.is_a? ImmediateRenderObject || object.immediate_render_callback
- # add_buffer_immediate(@_opengl_objects_immediate, object)
- # end
- end
- end
- end
-
- def make_groups(geometry, uses_face_material = false)
- max_vertices_in_group = 65535 # TODO: OES_element_index_uint ???
-
- hash_map = {}
-
- num_morph_targets = geometry.morph_targets.length
- num_morph_normals = geometry.morph_normals.length
-
- groups = {}
- groups_list = []
-
- geometry.faces.each_with_index do |face, f|
- material_index = uses_face_material ? face.material_index : 0
-
- if !hash_map.include? material_index
- hash_map[material_index] = { hash: material_index, counter: 0 }
- end
-
- group_hash = "#{hash_map[material_index][:hash]}_#{hash_map[material_index][:counter]}"
-
- if !groups.include? group_hash
- group = {
- id: @geometry_group_counter += 1,
- faces3: [],
- material_index: material_index,
- vertices: 0,
- num_morph_targets: num_morph_targets,
- num_morph_normals: num_morph_normals
- }
-
- groups[group_hash] = group
- groups_list << group
- end
-
- if groups[group_hash][:vertices] + 3 > max_vertices_in_group
- hash_map[material_index][:counter] += 1
- group_hash = "#{hash_map[material_index][:hash]}_#{hash_map[material_index][:counter]}"
-
- if !groups.include? group_hash
- group = {
- id: @geometry_group_counter += 1,
- faces3: [],
- material_index: material_index,
- vertices: 0,
- num_morph_targets: num_morph_targets,
- num_morph_normals: num_morph_normals
- }
-
- groups[group_hash] = group
- groups_list << group
- end
- end
- groups[group_hash][:faces3] << f
- groups[group_hash][:vertices] += 3
- end
- groups_list
- end
-
- def init_geometry_groups(object, geometry)
- # material = object.material
- add_buffers = false
-
- if @geometry_groups[geometry.id].nil? || geometry.groups_need_update
- @_opengl_objects.delete object.id
-
- @geometry_groups[geometry.id] = make_groups(geometry, false) # TODO: material.is_a?(MeshFaceMaterial))
-
- geometry.groups_need_update = false
- end
-
- geometry_groups_list = @geometry_groups[geometry.id]
-
- # create separate VBOs per geometry chunk
-
- geometry_groups_list.each do |geometry_group|
- # initialize VBO on the first access
- if geometry_group[:_opengl_vertex_buffer].nil?
- create_mesh_buffers(geometry_group)
- init_mesh_buffers(geometry_group, object)
-
- geometry.vertices_need_update = true
- geometry.morph_targets_need_update = true
- geometry.elements_need_update = true
- geometry.uvs_need_update = true
- geometry.normals_need_update = true
- geometry.tangents_need_update = true
- geometry.colors_need_update = true
- else
- add_buffers = false
- end
-
- if add_buffers || object[:_opengl_active].nil?
- add_buffer(@_opengl_objects, geometry_group, object)
- end
- end
-
- object[:_opengl_active] = true
- end
-
def add_buffer(objlist, buffer, object)
id = object.id
- objlist[id] ||= []
- objlist[id] << {
- id: id,
- buffer: buffer,
- object: object,
- material: nil,
- z: 0
- }
+ (objlist[id] ||= []) << OpenGLBuffer.new(
+ buffer, object, nil, 0
+ )
end
- def unroll_buffer_material(globject)
- object = globject[:object]
- # buffer = globject[:buffer]
+ def unroll_immediate_buffer_material(opengl_object)
+ object = opengl_object.object
+ material = object.material
- # geometry = object.geometry
- material = object.material
-
- if material
- # TODO: when MeshFaceMaterial exists
- # if material.is_a? MeshFaceMaterial
- # material_index = geometry.is_a? BufferGeometry ? 0 : buffer.material_index
- #
- # material = material.materials[material_index]
- # end
- globject[:material] = material
-
- if material.transparent
- @transparent_objects << globject
- else
- @opaque_objects << globject
- end
- end
+ if material.transparent
+ opengl_object.transparent
+ opengl_object.opaque = nil
+ else
+ opengl_object.opaque = material
+ opengl_object.transparent = nil
+ end
end
- def setup_matrices(object, camera)
- object[:_model_view_matrix].tap do |model_view_matrix|
- model_view_matrix.multiply_matrices(camera.matrix_world_inverse, object.matrix_world)
- object[:_normal_matrix].normal_matrix(model_view_matrix)
- end
- end
+ def unroll_buffer_material(opengl_object)
+ object = opengl_object.object
+ buffer = opengl_object.buffer
- def set_material(material)
- if material.transparent
- @state.set_blending(material.blending, material.blend_equation, material.blend_src, material.blend_dst, material.blend_equation_alpha, material.blend_src_alpha, material.blend_dst_alpha)
- else
- @state.set_blending(NoBlending)
- end
-
- @state.set_depth_test(material.depth_test)
- @state.set_depth_write(material.depth_write)
- @state.set_color_write(material.color_write)
- @state.set_polygon_offset(material.polygon_offset, material.polygon_offset_factor, material.polygon_offset_units)
- end
-
- def create_line_buffers(geometry)
- geometry[:_opengl_vertex_array] = glCreateVertexArray
-
- geometry[:_opengl_vertex_buffer] = glCreateBuffer
- geometry[:_opengl_color_buffer] = glCreateBuffer
- geometry[:_opengl_line_distance_buffer] = glCreateBuffer
-
- @info[:memory][:geometries] += 1
- end
-
- def create_mesh_buffers(geometry_group)
- geometry_group[:_opengl_vertex_array] = glCreateVertexArray
-
- geometry_group[:_opengl_vertex_buffer] = glCreateBuffer
- geometry_group[:_opengl_normal_buffer] = glCreateBuffer
- geometry_group[:_opengl_tangent_buffer] = glCreateBuffer
- geometry_group[:_opengl_color_buffer] = glCreateBuffer
- geometry_group[:_opengl_uv_buffer] = glCreateBuffer
- geometry_group[:_opengl_uv2_buffer] = glCreateBuffer
-
- geometry_group[:_opengl_skin_indices_buffer] = glCreateBuffer
- geometry_group[:_opengl_skin_weights_buffer] = glCreateBuffer
-
- geometry_group[:_opengl_face_buffer] = glCreateBuffer
- geometry_group[:_opengl_line_buffer] = glCreateBuffer
-
- num_morph_targets = geometry_group[:num_morph_targets]
-
- if num_morph_targets
- geometry_group[:_opengl_morph_targets_buffers] = []
-
- num_morph_targets.times do |m|
- geometry_group[:_opengl_morph_targets_buffers] << glCreateBuffer
- end
- end
-
- num_morph_normals = geometry_group[:num_morph_normals]
-
- if num_morph_normals
- geometry_group[:_opengl_morph_normals_buffers] = []
-
- num_morph_normals.times do |m|
- geometry_group[:_opengl_morph_normals_buffers] << glCreateBuffer
- end
- end
-
- @info[:memory][:geometries] += 1
- end
-
- def glCreateBuffer
- @_b ||= ' '*8
- glGenBuffers(1, @_b)
- @_b.unpack('L')[0]
- end
-
- def glCreateTexture
- @_b ||= ' '*8
- glGenTextures(1, @_b)
- @_b.unpack('L')[0]
- end
-
- def glCreateVertexArray
- @_b ||= ' '*8
- glGenVertexArrays(1, @_b)
- @_b.unpack('L')[0]
- end
-
- def glCreateFramebuffer
- @_b ||= ' '*8
- glGenFramebuffers(1, @_b)
- @_b.unpack('L')[0]
- end
-
- def glCreateRenderbuffer
- @_b ||= ' '*8
- glGenRenderbuffers(1, @_b)
- @_b.unpack('L')[0]
- end
-
- def array_to_ptr_easy(data)
- size_of_element = data.first.is_a?(Float) ? Fiddle::SIZEOF_FLOAT : Fiddle::SIZEOF_INT
- if data.first.is_a?(Float)
- size_of_element = Fiddle::SIZEOF_FLOAT
- format_of_element = 'F'
- # data.map!{ |d| d.nil? ? 0.0 : d }
- else
- size_of_element = Fiddle::SIZEOF_INT
- format_of_element = 'L'
- # data.map!{ |d| d.nil? ? 0 : d }
- end
- size = data.length * size_of_element
- array_to_ptr(data, size, format_of_element)
- end
-
- def array_to_ptr(data, size, format)
- ptr = Fiddle::Pointer.malloc(size)
- ptr[0,size] = data.pack(format * data.length)
- ptr
- end
-
- def glBufferData_easy(target, data, usage)
- ptr = array_to_ptr_easy(data)
- glBufferData(target, ptr.size, ptr, usage)
- end
-
- def init_custom_attributes(object)
geometry = object.geometry
material = object.material
- nvertices = geometry.vertices.length
-
- if material.attributes
- geometry[:_opengl_custom_attributes_list] ||= []
-
- material.attributes.each do |(name, attribute)|
- if !attribute[:_opengl_initialized] || attribute.create_unique_buffers
- attribute[:_opengl_initialized] = true
-
- size = case attribute.type
- when :v2 then 2
- when :v3 then 3
- when :v4 then 4
- when :c then 3
- else 1
- end
-
- attribute.size = size
-
- attribute.array = Array.new(nvertices * size) # Float32Array
-
- attribute.buffer = glCreateBuffer
- attribute.buffer.belongs_to_attribute = name
-
- attribute.needs_update = true
- end
-
- geometry[:_opengl_custom_attributes_list] << attribute
+ if material
+ if material.is_a? MeshFaceMaterial
+ material_index = geometry.is_a?(BufferGeometry) ? 0 : buffer.material_index
+ material = material.materials[material_index]
end
- end
- end
- def init_line_buffers(geometry, object)
- nvertices = geometry.vertices.length
+ opengl_object.material = material
- geometry[:_vertex_array] = Array.new(nvertices * 3, 0.0) # Float32Array
- geometry[:_color_array] = Array.new(nvertices * 3, 0.0) # Float32Array
- geometry[:_line_distance_array] = Array.new(nvertices, 0.0) # Float32Array
-
- geometry[:_opengl_line_count] = nvertices
-
- init_custom_attributes(object)
- end
-
- def init_mesh_buffers(geometry_group, object)
- geometry = object.geometry
- faces3 = geometry_group[:faces3]
-
- nvertices = faces3.length * 3
- ntris = faces3.length * 1
- nlines = faces3.length * 3
-
- material = get_buffer_material(object, geometry_group)
-
- geometry_group[:_vertex_array] = Array.new(nvertices * 3) # Float32Array
- geometry_group[:_normal_array] = Array.new(nvertices * 3) # Float32Array
- geometry_group[:_color_array] = Array.new(nvertices * 3) # Float32Array
- geometry_group[:_uv_array] = Array.new(nvertices * 2) # Float32Array
-
- if geometry.face_vertex_uvs.length > 1
- geometry_group[:_uv2_array] = Array.new(nvertices * 2) # Float32Array
- end
-
- if geometry.has_tangents
- geometry_group[:_tangent_array] = Array.new(nvertices * 4) # Float32Array
- end
-
- if !object.geometry.skin_weights.empty? && !object.geometry.skin_indices.empty?
- geometry_group[:_skin_index_array] = Array.new(nvertices * 4) # Float32Array
- geometry_group[:_skin_weight_array] = Array.new(nvertices * 4)
- end
-
- # UintArray from OES_element_index_uint ???
-
- geometry_group[:_type_array] = Array # UintArray ???
- geometry_group[:_face_array] = Array.new(ntris * 3)
- geometry_group[:_line_array] = Array.new(nlines * 2)
-
- num_morph_targets = geometry_group[:num_morph_targets]
-
- if !num_morph_targets.zero?
- geometry_group[:_morph_targets_arrays] = []
-
- num_morph_targets.times do |m|
- geometry_group[:_morph_targets_arrays] << Array.new(nvertices * 3) # Float32Array ???
+ if material.transparent
+ @transparent_objects << opengl_object
+ else
+ @opaque_objects << opengl_object
end
end
-
- num_morph_normals = geometry_group[:num_morph_normals]
-
- if !num_morph_targets.zero?
- geometry_group[:_morph_normals_arrays] = []
-
- num_morph_normals.times do |m|
- geometry_group[:_morph_normals_arrays] << Array.new(nvertices * 3) # Float32Array ???
- end
- end
-
- geometry_group[:_opengl_face_count] = ntris * 3
- geometry_group[:_opengl_line_count] = nlines * 2
-
- # custom attributes
-
- if material.attributes
- if geometry_group[:_opengl_custom_attributes_list].nil?
- geometry_group[:_opengl_custom_attributes_list] = []
- end
-
- material.attributes.each do |(name, original_attribute)|
- attribute = {}
- original_attribute.each do |(key, value)|
- attribute[key] = value
- end
-
- if !attribute[:_opengl_initialized] || attribute[:create_unique_buffers]
- attribute[:_opengl_initialized] = true
-
- size = case attribute[:type]
- when :v2 then 2
- when :v3, :c then 3
- when :v4 then 4
- else 1 # :f and :i
- end
-
- attribute[:size] = size
- attribute[:array] = Array.new(nvertices * size) # Float32Array
-
- attribute[:buffer] = glCreateBuffer
- attribute[:buffer_belongs_to_attribute] = name
-
- original_attribute[:needs_update] = true
- attribute[:_original] = original_attribute
- end
-
- geometry_group[:_opengl_custom_attributes_list] << attribute
- end
- end
-
- geometry_group[:_initted_arrays] = true
end
+ # FIXME: refactor
def update_object(object)
geometry = object.geometry
+ object_impl = object.implementation(self)
if geometry.is_a? BufferGeometry
- # TODO: geomertry vertex array ?????
+ # TODO: geometry vertex array ?????
# glBindVertexArray geometry.vertex_array
geometry.attributes.each do |(key, attribute)|
buffer_type = (key == 'index') ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER
@@ -1471,828 +485,56 @@
end
attribute.needs_update = false
end
end
- elsif object.is_a? Mesh
- # check all geometry groups
- if geometry.groups_need_update
- init_geometry_groups(object, geometry)
- end
-
- geometry_groups_list = @geometry_groups[geometry.id]
-
- material = nil
- geometry_groups_list.each do |geometry_group|
- # TODO: place to put this???
- # glBindVertexArray(geometry_group[:_opengl_vertex_array])
- material = get_buffer_material(object, geometry_group)
-
- custom_attributes_dirty = material.attributes && are_custom_attributes_dirty(material)
-
- if geometry.vertices_need_update || geometry.morph_targets_need_update || geometry.elements_need_update || geometry.uvs_need_update || geometry.normals_need_update || geometry.colors_need_update || geometry.tangents_need_update || custom_attributes_dirty
- set_mesh_buffers(geometry_group, object, GL_DYNAMIC_DRAW, !geometry.dynamic, material)
- end
- end
-
- geometry.vertices_need_update = false
- geometry.morph_targets_need_update = false
- geometry.elements_need_update = false
- geometry.uvs_need_update = false
- geometry.normals_need_update = false
- geometry.colors_need_update = false
- geometry.tangents_need_update = false
-
- material.attributes && clear_custom_attributes(material)
- elsif (object.is_a? Line)
- # TODO: glBindVertexArray ???
- material = get_buffer_material(object, geometry)
- custom_attributes_dirty = material.attributes && are_custom_attributes_dirty(material)
-
- if geometry.vertices_need_update || geometry.colors_need_update || geometry.line_distances_need_update || custom_attributes_dirty
- set_line_buffers(geometry, GL_DYNAMIC_DRAW)
- end
-
- geometry.vertices_need_update = false
- geometry.colors_need_update = false
- geometry.line_distances_need_update = false
-
- material.attributes && clear_custom_attributes(material)
- elsif object.is_A? PointCloud
- # TODO: glBindVertexArray ???
- material = get_buffer_material(object, geometry)
- custom_attributes_dirty = material.attributes && are_custom_attributes_dirty(material)
-
- if geometry.vertices_need_update || geometry.colors_need_update || custom_attributes_dirty
- set_particle_buffers(geometry, GL_DYNAMIC_DRAW, object)
- end
-
- geometry.vertices_need_update = false
- geometry.colors_need_update = false
-
- material.attributes && clear_custom_attributes(material)
+ else
+ object_impl.update
end
+ # TODO: when PointCloud exists
+ # elsif object.is_A? PointCloud
+ # # TODO: glBindVertexArray ???
+ # material = object_impl.buffer_material(geometry)
+ # custom_attributes_dirty = material.attributes && are_custom_attributes_dirty(material)
+ #
+ # if geometry.vertices_need_update || geometry.colors_need_update || custom_attributes_dirty
+ # set_particle_buffers(geometry, GL_DYNAMIC_DRAW, object)
+ # end
+ #
+ # geometry.vertices_need_update = false
+ # geometry.colors_need_update = false
+ #
+ # material.attributes && clear_custom_attributes(material)
+ # end
end
- def get_buffer_material(object, geometry_group)
- # TODO: when MeshFaceMaterial exists
- # object.material.is_a?(MeshFaceMaterial) ? object.material.materials[geometry_group[:material_index]] : object.material
-
- object.material # for now...
- end
-
- def set_line_buffers(geometry, hint)
- vertices = geometry.vertices
- colors = geometry.colors
- line_distances = geometry.line_distances_need_update
-
- vertex_array = geometry[:_vertex_array]
- color_array = geometry[:_color_array]
- line_distance_array = geometry[:_line_distance_array]
-
- custom_attributes = geometry[:_opengl_custom_attributes_list]
-
- if geometry.vertices_need_update
- vertices.each_with_index do |vertex, v|
- offset = v * 3
-
- vertex_array[offset] = vertex.x
- vertex_array[offset + 1] = vertex.y
- vertex_array[offset + 2] = vertex.z
- end
-
- glBindBuffer(GL_ARRAY_BUFFER, geometry[:_opengl_vertex_buffer])
- glBufferData_easy(GL_ARRAY_BUFFER, vertex_array, hint)
- end
-
- if geometry.colors_need_update
- colors.each_with_index do |color, c|
- offset = c * 3
-
- color_array[offset] = color.r
- color_array[offset + 1] = color.g
- color_array[offset + 2] = color.b
- end
-
- glBindBuffer(GL_ARRAY_BUFFER, geometry[:_opengl_color_buffer])
- glBufferData_easy(GL_ARRAY_BUFFER, color_array, hint)
- end
-
- if geometry.line_distances_need_update
- line_distances.each_with_index do |l, d|
- line_distance_array[d] = l
- end
-
- glBindBuffer(GL_ARRAY_BUFFER, geometry[:_opengl_line_distance_buffer])
- glBufferData_easy(GL_ARRAY_BUFFER, line_distance_array, hint)
- end
-
- if custom_attributes
- custom_attribute.each do |custom_attribute|
- offset = 0
-
- values = custom_attribute.value
-
- case custom_attribute.size
- when 1
- value.each_with_index do |value, ca|
- custom_attribute.array[ca] = value
- end
- when 2
- values.each_with_index do |value, ca|
- custom_attribute[offset ] = value.x
- custom_attribute[offset + 1] = value.y
-
- offset += 2
- end
- when 3
- if custom_attribute.type === :c
- values.each_with_index do |value, ca|
- custom_attribute[offset ] = value.r
- custom_attribute[offset + 1] = value.g
- custom_attribute[offset + 2] = value.b
-
- offset += 3
- end
- else
- values.each_with_index do |value, ca|
- custom_attribute[offset ] = value.x
- custom_attribute[offset + 1] = value.y
- custom_attribute[offset + 2] = value.z
-
- offset += 3
- end
- end
- when 4
- values.each_with_index do |value, ca|
- custom_attribute[offset ] = value.x
- custom_attribute[offset + 1] = value.y
- custom_attribute[offset + 2] = value.z
- custom_attribute[offset + 3] = value.w
-
- offset += 4
- end
- end
-
- glBindBuffer(GL_ARRAY_BUFFER, custom_attribute.buffer)
- glBufferData_easy(GL_ARRAY_BUFFER, custom_attribute.array, hint)
-
- custom_attribute.needs_update = false
- end
- end
- end
-
- def set_mesh_buffers(geometry_group, object, hint, dispose, material)
- return unless geometry_group[:_initted_arrays]
-
- needs_face_normals = material_needs_face_normals(material)
-
- vertex_index = 0
-
- offset = 0
- offset_uv = 0
- offset_uv2 = 0
- offset_face = 0
- offset_normal = 0
- offset_tangent = 0
- offset_line = 0
- offset_color = 0
- offset_skin = 0
- offset_morph_target = 0
- offset_custom = 0
-
- vertex_array = geometry_group[:_vertex_array]
- uv_array = geometry_group[:_uv_array]
- uv2_array = geometry_group[:_uv2_array]
- normal_array = geometry_group[:_normal_array]
- tangent_array = geometry_group[:_tangent_array]
- color_array = geometry_group[:_color_array]
-
- skin_index_array = geometry_group[:_skin_index_array]
- skin_weight_array = geometry_group[:_skin_weight_array]
-
- morph_targets_arrays = geometry_group[:_morph_targets_arrays]
- morph_normals_arrays = geometry_group[:_morph_normals_arrays]
-
- custom_attributes = geometry_group[:_opengl_custom_attributes_list]
-
- face_array = geometry_group[:_face_array]
- line_array = geometry_group[:_line_array]
-
- geometry = object.geometry # this is shared for all chunks
-
- dirty_vertices = geometry.vertices_need_update
- dirty_elements = geometry.elements_need_update
- dirty_uvs = geometry.uvs_need_update
- dirty_normals = geometry.normals_need_update
- dirty_tangents = geometry.tangents_need_update
- dirty_colors = geometry.colors_need_update
- dirty_morph_targets = geometry.morph_targets_need_update
-
- vertices = geometry.vertices
- chunk_faces3 = geometry_group[:faces3]
- obj_faces = geometry.faces
-
- obj_uvs = geometry.face_vertex_uvs[0]
- obj_uvs2 = geometry.face_vertex_uvs[1]
-
- obj_skin_indices = geometry.skin_indices
- obj_skin_weights = geometry.skin_weights
-
- morph_targets = geometry.morph_targets
- morph_normals = geometry.morph_normals
-
- if dirty_vertices
- chunk_faces3.each do |chf|
- face = obj_faces[chf]
-
- v1 = vertices[face.a]
- v2 = vertices[face.b]
- v3 = vertices[face.c]
-
- vertex_array[offset] = v1.x
- vertex_array[offset + 1] = v1.y
- vertex_array[offset + 2] = v1.z
-
- vertex_array[offset + 3] = v2.x
- vertex_array[offset + 4] = v2.y
- vertex_array[offset + 5] = v2.z
-
- vertex_array[offset + 6] = v3.x
- vertex_array[offset + 7] = v3.y
- vertex_array[offset + 8] = v3.z
-
- offset += 9
- end
-
- glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_vertex_buffer])
- glBufferData_easy(GL_ARRAY_BUFFER, vertex_array, hint)
- end
-
- if dirty_morph_targets
- morph_targets.each_index do |vk|
- chunk_faces.each do |chf|
- face = obj_faces[chf]
-
- # morph positions
-
- v1 = morph_targets[vk].vertices[face.a]
- v2 = morph_targets[vk].vertices[face.b]
- v3 = morph_targets[vk].vertices[face.c]
-
- vka = morph_targets_arrays[vk]
-
- vka[offset_morph_target] = v1.x
- vka[offset_morph_target + 1] = v1.y
- vka[offset_morph_target + 2] = v1.z
-
- vka[offset_morph_target + 3] = v2.x
- vka[offset_morph_target + 4] = v2.y
- vka[offset_morph_target + 5] = v2.z
-
- vka[offset_morph_target + 6] = v3.x
- vka[offset_morph_target + 7] = v3.y
- vka[offset_morph_target + 8] = v3.z
-
- # morph normals
-
- if material.morph_normals
- if needs_face_normals
- n1 = morph_normals[vk].face_normals[chf]
- n2 = n1
- n3 = n1
- else
- face_vertex_normals = morph_normals[vk].vertex_normals[chf]
-
- n1 = face_vertex_normals.a
- n2 = face_vertex_normals.b
- n3 = face_vertex_normals.c
- end
-
- nka = morph_normals_arrays[vk]
-
- nka[offset_morph_target] = n1.x
- nka[offset_morph_target + 1] = n1.y
- nka[offset_morph_target + 2] = n1.z
-
- nka[offset_morph_target + 3] = n2.x
- nka[offset_morph_target + 4] = n2.y
- nka[offset_morph_target + 5] = n2.z
-
- nka[offset_morph_target + 6] = n3.x
- nka[offset_morph_target + 7] = n3.y
- nka[offset_morph_target + 8] = n3.z
- end
-
- #
-
- offset_morph_target += 9
- end
-
- glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_morph_targets_buffers][vk])
- glBufferData_easy(GL_ARRAY_BUFFER, morph_targets_arrays[vk], hint)
-
- if material.morph_normals
- glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_morph_normals_buffers][vk])
- glBufferData_easy(GL_ARRAY_BUFFER, morph_normals_arrays[vk], hint)
- end
- end
- end
-
- if !obj_skin_weights.empty?
- chunk_faces3.each do |chf|
- face = obj_faces[chf]
-
- # weights
-
- sw1 = obj_skin_weights[face.a]
- sw2 = obj_skin_weights[face.b]
- sw3 = obj_skin_weights[face.c]
-
- skin_weight_array[offset_skin] = sw1.x
- skin_weight_array[offset_skin + 1] = sw1.y
- skin_weight_array[offset_skin + 2] = sw1.z
- skin_weight_array[offset_skin + 3] = sw1.w
-
- skin_weight_array[offset_skin + 4] = sw2.x
- skin_weight_array[offset_skin + 5] = sw2.y
- skin_weight_array[offset_skin + 6] = sw2.z
- skin_weight_array[offset_skin + 7] = sw2.w
-
- skin_weight_array[offset_skin + 8] = sw3.x
- skin_weight_array[offset_skin + 9] = sw3.y
- skin_weight_array[offset_skin + 10] = sw3.z
- skin_weight_array[offset_skin + 11] = sw3.w
-
- # indices
-
- si1 = obj_skin_indices[face.a]
- si2 = obj_skin_indices[face.b]
- si3 = obj_skin_indices[face.c]
-
- skin_indices_array[offset_skin] = si1.x
- skin_indices_array[offset_skin + 1] = si1.y
- skin_indices_array[offset_skin + 2] = si1.z
- skin_indices_array[offset_skin + 3] = si1.w
-
- skin_indices_array[offset_skin + 4] = si2.x
- skin_indices_array[offset_skin + 5] = si2.y
- skin_indices_array[offset_skin + 6] = si2.z
- skin_indices_array[offset_skin + 7] = si2.w
-
- skin_indices_array[offset_skin + 8] = si3.x
- skin_indices_array[offset_skin + 9] = si3.y
- skin_indices_array[offset_skin + 10] = si3.z
- skin_indices_array[offset_skin + 11] = si3.w
-
- offset_skin += 12
- end
-
- if offset_skin > 0
- glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_skin_indices_buffer])
- glBufferData_easy(GL_ARRAY_BUFFER, skin_index_array, hint)
-
- glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_skin_weights_buffer])
- glBufferData_easy(GL_ARRAY_BUFFER, skin_weight_array, hint)
- end
- end
-
- if dirty_colors
- chunk_faces3.each do |chf|
- face = obj_faces[chf]
-
- vertex_colors = face.vertex_colors
- face_color = face.color
-
- if vertex_colors.length == 3 && material.vertex_colors == VertexColors
- c1 = vertex_colors[0]
- c2 = vertex_colors[1]
- c3 = vertex_colors[2]
- else
- c1 = face_color
- c2 = face_color
- c3 = face_color
- end
-
- color_array[offset_color] = c1.r
- color_array[offset_color + 1] = c1.g
- color_array[offset_color + 2] = c1.b
-
- color_array[offset_color + 3] = c2.r
- color_array[offset_color + 4] = c2.g
- color_array[offset_color + 5] = c2.b
-
- color_array[offset_color + 6] = c3.r
- color_array[offset_color + 7] = c3.g
- color_array[offset_color + 8] = c3.b
-
- offset_color += 9
- end
-
- if offset_color > 0
- glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_color_buffer])
- glBufferData_easy(GL_ARRAY_BUFFER, color_array, hint)
- end
- end
-
- if dirty_tangents && geometry.has_tangents
- chunk_faces3.each do |chf|
- face = obj_faces[chf]
-
- vertex_tangents = face.vertex_tangents
-
- t1 = vertex_tangents[0]
- t2 = vertex_tangents[1]
- t3 = vertex_tangents[2]
-
- tangent_array[offset_tangent] = t1.x
- tangent_array[offset_tangent + 1] = t1.y
- tangent_array[offset_tangent + 2] = t1.z
- tangent_array[offset_tangent + 3] = t1.w
-
- tangent_array[offset_tangent + 4] = t2.x
- tangent_array[offset_tangent + 5] = t2.y
- tangent_array[offset_tangent + 6] = t2.z
- tangent_array[offset_tangent + 7] = t2.w
-
- tangent_array[offset_tangent + 8] = t3.x
- tangent_array[offset_tangent + 9] = t3.y
- tangent_array[offset_tangent + 10] = t3.z
- tangent_array[offset_tangent + 11] = t3.w
-
- offset_tangent += 12
- end
-
- glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_tangent_buffer])
- glBufferData_easy(GL_ARRAY_BUFFER, tangent_array, hint)
- end
-
- if dirty_normals
- chunk_faces3.each do |chf|
- face = obj_faces[chf]
-
- vertex_normals = face.vertex_normals
- face_normal = face.normal
-
- if vertex_normals.length == 3 && !needs_face_normals
- 3.times do |i|
- vn = vertex_normals[i]
-
- normal_array[offset_normal] = vn.x
- normal_array[offset_normal + 1] = vn.y
- normal_array[offset_normal + 2] = vn.z
-
- offset_normal += 3
- end
- else
- 3.times do |i|
- normal_array[offset_normal] = face_normal.x
- normal_array[offset_normal + 1] = face_normal.y
- normal_array[offset_normal + 2] = face_normal.z
-
- offset_normal += 3
- end
- end
- end
-
- glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_normal_buffer])
- glBufferData_easy(GL_ARRAY_BUFFER, normal_array, hint)
- end
-
- if dirty_uvs && obj_uvs
- chunk_faces3.each do |fi|
- uv = obj_uvs[fi]
-
- next if uv.nil?
-
- 3.times do |i|
- uvi = uv[i]
-
- uv_array[offset_uv] = uvi.x
- uv_array[offset_uv + 1] = uvi.y
-
- offset_uv += 2
- end
- end
-
- if offset_uv > 0
- glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_uv_buffer])
- glBufferData_easy(GL_ARRAY_BUFFER, uv_array, hint)
- end
- end
-
- if dirty_uvs && obj_uvs2
- chunk_faces3.each do |fi|
- uv2 = obj_uvs2[fi]
-
- next if uv2.nil?
-
- 3.times do |i|
- uv2i = uv2[i]
-
- uv2_array[offset_uv2] = uv2i.x
- uv2_array[offset_uv2 + 1] = uv2i.y
-
- offset_uv2 += 2
- end
- end
-
- if offset_uv2 > 0
- glBindBuffer(GL_ARRAY_BUFFER, geometry_group[:_opengl_uv2_buffer])
- glBufferData_easy(GL_ARRAY_BUFFER, uv2_array, hint)
- end
- end
-
- if dirty_elements
- chunk_faces3.each do |chf|
- face_array[offset_face] = vertex_index
- face_array[offset_face + 1] = vertex_index + 1
- face_array[offset_face + 2] = vertex_index + 2
-
- offset_face += 3
-
- line_array[offset_line] = vertex_index
- line_array[offset_line + 1] = vertex_index + 1
-
- line_array[offset_line + 2] = vertex_index
- line_array[offset_line + 3] = vertex_index + 2
-
- line_array[offset_line + 4] = vertex_index + 1
- line_array[offset_line + 5] = vertex_index + 2
-
- offset_line += 6
-
- vertex_index += 3
- end
-
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry_group[:_opengl_face_buffer])
- glBufferData_easy(GL_ELEMENT_ARRAY_BUFFER, face_array, hint)
-
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry_group[:_opengl_line_buffer])
- glBufferData_easy(GL_ELEMENT_ARRAY_BUFFER, line_array, hint)
- end
-
- if custom_attributes
- custom_attributes.each do |custom_attribute|
- next if !custom_attribute[:_original][:needs_update]
-
- offset_custom = 0
-
- if custom_attribute[:size] == 1
- if custom_attribute[:bound_to].nil? || custom_attribute[:bound_to] == :vertices
- chunk_faces3.each do |chf|
- face = obj_faces[chf]
-
- custom_attribute[:array][offset_custom] = custom_attribute[:value][face.a]
- custom_attribute[:array][offset_custom + 1] = custom_attribute[:value][face.b]
- custom_attribute[:array][offset_custom + 2] = custom_attribute[:value][face.c]
-
- offset_custom += 3
- end
- elsif custom_attribute[:bound_to] == :faces
- value = custom_attribute[:value][chf]
-
- custom_attribute[:array][offset_custom] = value
- custom_attribute[:array][offset_custom + 1] = value
- custom_attribute[:array][offset_custom + 2] = value
-
- offset_custom += 3
- end
- elsif custom_attribute[:size] == 2
- if custom_attribute[:bound_to].nil? || custom_attribute[:bound_to] == :vertices
- chunk_faces3.each do |chf|
- face = obj_faces[chf]
-
- v1 = custom_attribute[:value][face.a]
- v2 = custom_attribute[:value][face.b]
- v3 = custom_attribute[:value][face.c]
-
- custom_attribute[:array][offset_custom] = v1.x
- custom_attribute[:array][offset_custom + 1] = v1.y
-
- custom_attribute[:array][offset_custom + 2] = v2.x
- custom_attribute[:array][offset_custom + 3] = v2.y
-
- custom_attribute[:array][offset_custom + 4] = v3.x
- custom_attribute[:array][offset_custom + 5] = v3.y
-
- offset_custom += 6
- end
- elsif custom_attribute[:bound_to] == :faces
- chunk_faces3.each do |chf|
- value = custom_attribute[:value][chf]
-
- v1 = value
- v2 = value
- v3 = value
-
- custom_attribute[:array][offset_custom] = v1.x
- custom_attribute[:array][offset_custom + 1] = v1.y
-
- custom_attribute[:array][offset_custom + 2] = v2.x
- custom_attribute[:array][offset_custom + 3] = v2.y
-
- custom_attribute[:array][offset_custom + 4] = v3.x
- custom_attribute[:array][offset_custom + 5] = v3.y
-
- offset_custom += 6
- end
- end
- elsif custom_attribute[:size] == 3
- if custom_attribute[:bound_to].nil? || custom_attribute[:bound_to] == :vertices
- chunk_faces3.each do |chf|
- face = obj_faces[chf];
-
- v1 = custom_attribute[:value][face.a]
- v2 = custom_attribute[:value][face.b]
- v3 = custom_attribute[:value][face.c]
-
- custom_attribute[:array][offset_custom] = v1[0]
- custom_attribute[:array][offset_custom + 1] = v1[1]
- custom_attribute[:array][offset_custom + 2] = v1[2]
-
- custom_attribute[:array][offset_custom + 3] = v2[0]
- custom_attribute[:array][offset_custom + 4] = v2[1]
- custom_attribute[:array][offset_custom + 5] = v2[2]
-
- custom_attribute[:array][offset_custom + 6] = v3[0]
- custom_attribute[:array][offset_custom + 7] = v3[1]
- custom_attribute[:array][offset_custom + 8] = v3[2]
-
- offset_custom += 9
- end
- elsif custom_attribute[:bound_to] == :faces
- chunk_faces3.each do |chf|
- value = custom_attribute[:value][chf]
-
- v1 = value
- v2 = value
- v3 = value
-
- custom_attribute[:array][offset_custom] = v1[0]
- custom_attribute[:array][offset_custom + 1] = v1[1]
- custom_attribute[:array][offset_custom + 2] = v1[2]
-
- custom_attribute[:array][offset_custom + 3] = v2[0]
- custom_attribute[:array][offset_custom + 4] = v2[1]
- custom_attribute[:array][offset_custom + 5] = v2[2]
-
- custom_attribute[:array][offset_custom + 6] = v3[0]
- custom_attribute[:array][offset_custom + 7] = v3[1]
- custom_attribute[:array][offset_custom + 8] = v3[2]
-
- offset_custom += 9
- end
- elsif custom_attribute[:bound_to] == :face_vertices
- chunk_faces3.each do |chf|
- value = custom_attribute[:value][chf]
-
- v1 = value[0]
- v2 = value[1]
- v3 = value[2]
-
- custom_attribute[:array][offset_custom] = v1[0]
- custom_attribute[:array][offset_custom + 1] = v1[1]
- custom_attribute[:array][offset_custom + 2] = v1[2]
-
- custom_attribute[:array][offset_custom + 3] = v2[0]
- custom_attribute[:array][offset_custom + 4] = v2[1]
- custom_attribute[:array][offset_custom + 5] = v2[2]
-
- custom_attribute[:array][offset_custom + 6] = v3[0]
- custom_attribute[:array][offset_custom + 7] = v3[1]
- custom_attribute[:array][offset_custom + 8] = v3[2]
-
- offset_custom += 9
- end
- end
- elsif custom_attribute[:size] == 4
- if custom_attribute[:bound_to].nil? || custom_attribute[:bound_to] == :vertices
- chunk_faces3.each do |chf|
- face = obj_faces[chf]
-
- v1 = custom_attribute[:value][face.a]
- v2 = custom_attribute[:value][face.b]
- v3 = custom_attribute[:value][face.c]
-
- customAttribute.array[offset_custom] = v1.x
- customAttribute.array[offset_custom + 1 ] = v1.y
- customAttribute.array[offset_custom + 2 ] = v1.z
- customAttribute.array[offset_custom + 3 ] = v1.w
-
- customAttribute.array[offset_custom + 4 ] = v2.x
- customAttribute.array[offset_custom + 5 ] = v2.y
- customAttribute.array[offset_custom + 6 ] = v2.z
- customAttribute.array[offset_custom + 7 ] = v2.w
-
- customAttribute.array[offset_custom + 8 ] = v3.x
- customAttribute.array[offset_custom + 9 ] = v3.y
- customAttribute.array[offset_custom + 10] = v3.z
- customAttribute.array[offset_custom + 11] = v3.w
-
- offset_custom += 12
- end
- elsif custom_attribute[:bound_to] == :faces
- chunk_faces3.each do |chf|
- value = custom_attribute[:value][chf]
-
- v1 = value
- v2 = value
- v3 = value
-
- customAttribute.array[offset_custom] = v1.x
- customAttribute.array[offset_custom + 1 ] = v1.y
- customAttribute.array[offset_custom + 2 ] = v1.z
- customAttribute.array[offset_custom + 3 ] = v1.w
-
- customAttribute.array[offset_custom + 4 ] = v2.x
- customAttribute.array[offset_custom + 5 ] = v2.y
- customAttribute.array[offset_custom + 6 ] = v2.z
- customAttribute.array[offset_custom + 7 ] = v2.w
-
- customAttribute.array[offset_custom + 8 ] = v3.x
- customAttribute.array[offset_custom + 9 ] = v3.y
- customAttribute.array[offset_custom + 10] = v3.z
- customAttribute.array[offset_custom + 11] = v3.w
-
- offset_custom += 12
- end
- elsif custom_attribute[:bound_to] == :face_vertices
- chunk_faces3.each do |chf|
- value = custom_attribute[:value][chf]
-
- v1 = value[0]
- v2 = value[1]
- v3 = value[2]
-
- customAttribute.array[offset_custom] = v1.x
- customAttribute.array[offset_custom + 1 ] = v1.y
- customAttribute.array[offset_custom + 2 ] = v1.z
- customAttribute.array[offset_custom + 3 ] = v1.w
-
- customAttribute.array[offset_custom + 4 ] = v2.x
- customAttribute.array[offset_custom + 5 ] = v2.y
- customAttribute.array[offset_custom + 6 ] = v2.z
- customAttribute.array[offset_custom + 7 ] = v2.w
-
- customAttribute.array[offset_custom + 8 ] = v3.x
- customAttribute.array[offset_custom + 9 ] = v3.y
- customAttribute.array[offset_custom + 10] = v3.z
- customAttribute.array[offset_custom + 11] = v3.w
-
- offset_custom += 12
- end
- end
- end
-
- glBindBuffer(GL_ARRAY_BUFFER, custom_attribute[:buffer])
- glBufferData_easy(GL_ARRAY_BUFFER, custom_attribute[:array], hint)
- end
- end
-
- if dispose
- geometry_group.delete(:_initted_arrays)
- geometry_group.delete(:_color_array)
- geometry_group.delete(:_normal_array)
- geometry_group.delete(:_tangent_array)
- geometry_group.delete(:_uv_array)
- geometry_group.delete(:_uv2_array)
- geometry_group.delete(:_face_array)
- geometry_group.delete(:_vertex_array)
- geometry_group.delete(:_line_array)
- geometry_group.delete(:_skin_index_array)
- geometry_group.delete(:_skin_weight_array)
- end
- end
-
- def material_needs_face_normals(material)
- !material.is_a?(MeshPhongMaterial) && material.shading == FlatShading
- end
-
+ # FIXME: refactor
def set_program(camera, lights, fog, material, object)
@_used_texture_units = 0
+ material_impl = material.implementation(self)
+ object_impl = object.implementation(self)
if material.needs_update?
deallocate_material(material) if material.program
- init_material(material, lights, fog, object)
+ material_impl.init(lights, fog, object)
material.needs_update = false
end
if material.morph_targets
- if !object[:_opengl_morph_target_influences]
- object[:_opengl_morph_target_influences] = Array.new(@max_morph_targets) # Float32Array
+ if !object_impl.morph_target_influences
+ object_impl.morph_target_influences = Array.new(@max_morph_targets) # Float32Array
end
end
refresh_program = false
refresh_material = false
refresh_lights = false
program = material.program
- p_uniforms = program.uniforms
- m_uniforms = material[:_opengl_shader][:uniforms]
+ program_uniforms = program.uniforms
+ material_uniforms = material_impl.shader[:uniforms]
if program.id != @_current_program
glUseProgram(program.program)
@_current_program = program.id
@@ -2307,1070 +549,503 @@
refresh_material = true
end
if refresh_program || camera != @_current_camera
- glUniformMatrix4fv(p_uniforms['projectionMatrix'], 1, GL_FALSE, array_to_ptr_easy(camera.projection_matrix.elements))
-
- if @_logarithmic_depth_buffer
- glUniform1f(p_uniforms['logDepthBuffFC'], 2.0 / Math.log(camera.far + 1.0) / Math::LN2)
- end
-
- @_current_camera = camera if camera != @_current_camera
-
- # load material specific uniforms
- # (shader material also gets them for the sake of genericity)
-
- if material.is_a?(ShaderMaterial) || material.is_a?(MeshPhongMaterial) || material.env_map
- if !p_uniforms['cameraPosition'].nil?
- @_vector3.set_from_matrix_position(camera.matrix_world)
- glUniform3f(p_uniforms['cameraPosition'], @_vector3.x, @_vector3.y, @_vector3.z)
- end
- end
-
- if material.is_a?(MeshPhongMaterial) || material.is_a?(MeshLambertMaterial) || material.is_a?(MeshBasicMaterial) || material.is_a?(ShaderMaterial) || material.skinning
- if !p_uniforms['viewMatrix'].nil?
- glUniformMatrix4fv(p_uniforms['viewMatrix'], 1, GL_FALSE, array_to_ptr_easy(camera.matrix_world_inverse.elements))
- end
- end
+ @_current_camera = camera
+ update_camera_uniforms(program_uniforms, camera, material)
end
if material.skinning
- if object.bind_matrix && !p_uniforms.bind_matrix.nil?
- glUniformMatrix4fv(p_uniforms.bind_matrix, GL_FALSE, object.bind_matrix.elements)
- end
-
- if object.bind_matrix_inverse && !p_uniforms.bind_matrix_inverse.nil?
- glUniformMatrix4fv(p_uniforms.bind_matrix_inverse, GL_FALSE, object.bind_matrix_inverse.elements)
- end
-
- if _supports_bone_textures && object.skeleton && object.skeleton.use_vertex_texture
- if !p_uniforms.bone_texture.nil?
- texture_unit = get_texture_unit
-
- glUniform1i(p_uniforms.bone_texture, texture_unit)
- self.set_texture(object.skeleton.bone_texture, texture_unit)
- end
-
- if !p_uniforms.bone_texture_width.nil?
- glUniform1i(p_uniforms.bone_texture_width, object.skeleton.bone_texture_width)
- end
-
- if !p_uniforms.bone_texture_height.nil?
- glUniform1i(p_uniforms.bone_texture_height, object.skeleton.bone_texture_height)
- end
- elsif object.skeleton && object.skeleton.bone_matrices
- if !p_uniforms.bone_global_matrices.nil?
- glUniformMatrix4fv(p_uniforms.bone_global_matrices, GL_FALSE, object.skeleton.bone_matrices)
- end
- end
+ # TODO: when skinning is implemented. Then also refactor
+ # if object.bind_matrix && !program_uniforms['bindMatrix'].nil?
+ # glUniformMatrix4fv(program_uniforms['bindMatrix'], GL_FALSE, object.bind_matrix.elements)
+ # end
+ #
+ # if object.bind_matrix_inverse && !program_uniforms['bindMatrixInverse'].nil?
+ # glUniformMatrix4fv(program_uniforms['bindMatrixInverse'], GL_FALSE, object.bind_matrix_inverse.elements)
+ # end
+ #
+ # if @_supports_bone_textures && object.skeleton && object.skeleton.use_vertex_texture
+ # if !program_uniforms['boneTexture'].nil?
+ # texture_unit = get_texture_unit
+ #
+ # glUniform1i(program_uniforms['boneTexture'], texture_unit)
+ # object.skeleton.bone_texture.implementation(self).set(texture_unit)
+ # end
+ #
+ # if !program_uniforms['boneTextureWidth'].nil?
+ # glUniform1i(program_uniforms['boneTextureWidth'], object.skeleton.bone_texture_width)
+ # end
+ #
+ # if !program_uniforms['boneTextureHeight'].nil?
+ # glUniform1i(program_uniforms['boneTextureHeight'], object.skeleton.bone_texture_height)
+ # end
+ # elsif object.skeleton && object.skeleton.bone_matrices
+ # if !program_uniforms['boneGlobalMatrices'].nil?
+ # glUniformMatrix4fv(program_uniforms['boneGlobalMatrices'], GL_FALSE, object.skeleton.bone_matrices)
+ # end
+ # end
end
if refresh_material
- if fog && material.fog
- end
+ # TODO: when fog is implemented
+ # refresh_uniforms_fog(material_uniforms, fog) if fog && material.fog
- if material.is_a?(MeshPhongMaterial) || material.is_a?(MeshLambertMaterial) || material.lights
- if @_lights_need_update
+ if material_impl.needs_lights?
+ if @light_renderer.lights_need_update
refresh_lights = true
- setup_lights(lights)
- @_lights_need_update = false
+ @light_renderer.setup(lights)
end
if refresh_lights
- refresh_uniforms_lights(m_uniforms, @_lights)
- mark_uniforms_lights_needs_update(m_uniforms, true)
- else
- mark_uniforms_lights_needs_update(m_uniforms, false)
+ OpenGLHelper.refresh_uniforms_lights(material_uniforms, @light_renderer.cache)
end
- end
- if material.is_a?(MeshBasicMaterial) || material.is_a?(MeshLambertMaterial) || material.is_a?(MeshPhongMaterial)
- refresh_uniforms_common(m_uniforms, material)
+ OpenGLHelper.mark_uniforms_lights_needs_update(material_uniforms, refresh_lights)
end
- # refresh single material specific uniforms
+ material_impl.refresh_uniforms(material_uniforms)
# TODO: when all of these things exist
- case material
- when LineBasicMaterial
- refresh_uniforms_line(m_uniforms, material)
# when LineDashedMaterial
- # refresh_uniforms_line(m_uniforms, material)
- # refresh_uniforms_dash(m_uniforms, material)
+ # refresh_uniforms_line(material_uniforms, material)
+ # refresh_uniforms_dash(material_uniforms, material)
# when PointCloudMaterial
- # refresh_uniforms_particle(m_uniforms, material)
- when MeshPhongMaterial
- refresh_uniforms_phong(m_uniforms, material)
- when MeshLambertMaterial
- refresh_uniforms_lambert(m_uniforms, material)
+ # refresh_uniforms_particle(material_uniforms, material)
# when MeshDepthMaterial
- # m_uniforms.m_near.value = camera.near
- # m_uniforms.m_far.value = camera.far
- # m_uniforms.opacity.value = material.opacity
+ # material_uniforms.m_near.value = camera.near
+ # material_uniforms.m_far.value = camera.far
+ # material_uniforms.opacity.value = material.opacity
# when MeshNormalMaterial
- # m_uniforms.opactity.value = material.opacity
- end
+ # material_uniforms.opactity.value = material.opacity
- if object.receive_shadow && !material[:_shadow_pass]
- refresh_uniforms_shadow(m_uniforms, lights)
+ if object.receive_shadow && !material_impl.shadow_pass
+ OpenGLHelper.refresh_uniforms_shadow(material_uniforms, lights)
end
- # load common uniforms
-
- load_uniforms_generic(material[:uniforms_list])
+ load_uniforms_generic(material_impl.uniforms_list)
end
- load_uniforms_matrices(p_uniforms, object)
+ object.implementation(self).load_uniforms_matrices(program_uniforms)
- if !p_uniforms['modelMatrix'].nil?
- glUniformMatrix4fv(p_uniforms['modelMatrix'], 1, GL_FALSE, array_to_ptr_easy(object.matrix_world.elements))
+ if !program_uniforms['modelMatrix'].nil?
+ glUniformMatrix4fv(program_uniforms['modelMatrix'], 1, GL_FALSE, array_to_ptr_easy(object.matrix_world.elements))
end
program
end
- def init_material(material, lights, fog, object)
- material.add_event_listener(:dispose, @on_material_dispose)
-
- shader_id = @shader_ids[material.class]
-
- if shader_id
- shader = ShaderLib[shader_id]
- material[:_opengl_shader] = {
- uniforms: UniformsUtils.clone(shader.uniforms),
- vertex_shader: shader.vertex_shader,
- fragment_shader: shader.fragment_shader
- }
- else
- material[:_opengl_shader] = {
- uniforms: material.uniforms,
- vertex_shader: material.vertex_shader,
- fragment_shader: material.fragment_shader
- }
- end
-
- # heuristics to create shader paramaters ccording to lights in the scene
- # (not to blow over max_lights budget)
-
- max_light_count = allocate_lights(lights)
- max_shadows = allocate_shadows(lights)
- max_bones = allocate_bones(object)
-
- parameters = {
- precision: @_precision,
- supports_vertex_textures: @_supports_vertex_textures,
-
- map: !!material.map,
- env_map: !!material.env_map,
- env_map_mode: material.env_map && material.env_map.mapping,
- light_map: !!material.light_map,
- bump_map: !!material.light_map,
- normal_map: !!material.normal_map,
- specular_map: !!material.specular_map,
- alpha_map: !!material.alpha_map,
-
- combine: material.combine,
-
- vertex_colors: material.vertex_colors,
-
- fog: fog,
- use_fog: material.fog,
- # fog_exp: fog.is_a?(FogExp2), # TODO: when FogExp2 exists
-
- flat_shading: material.shading == FlatShading,
-
- size_attenuation: material.size_attenuation,
- logarithmic_depth_buffer: @_logarithmic_depth_buffer,
-
- skinning: material.skinning,
- max_bones: max_bones,
- use_vertex_texture: @_supports_bone_textures,
-
- morph_targets: material.morph_targets,
- morph_normals: material.morph_normals,
- max_morph_targets: @max_morph_targets,
- max_morph_normals: @max_morph_normals,
-
- max_dir_lights: max_light_count[:directional],
- max_point_lights: max_light_count[:point],
- max_spot_lights: max_light_count[:spot],
- max_hemi_lights: max_light_count[:hemi],
-
- max_shadows: max_shadows,
- shadow_map_enabled: @shadow_map_enabled && object.receive_shadow && max_shadows > 0,
- shadow_map_type: @shadow_map_type,
- shadow_map_debug: @shadow_map_debug,
- shadow_map_cascade: @shadow_map_cascade,
-
- alpha_test: material.alpha_test,
- metal: material.metal,
- wrap_around: material.wrap_around,
- double_sided: material.side == DoubleSide,
- flip_sided: material.side == BackSide
- }
-
- # generate code
-
- chunks = []
-
- if shader_id
- chunks << shader_id
- else
- chunks << material.fragment_shader
- chunks << material.vertex_shader
- end
-
- if !material.defines.nil?
- material.defines.each do |(name, define)|
- chunks << name
- chunks << define
- end
- end
-
- parameters.each do |(name, parameter)|
- chunks << name
- chunks << parameter
- end
-
- code = chunks.join
-
- program = nil
-
- # check if code has been already compiled
-
- @_programs.each do |program_info|
- if program_info.code == code
- program = program_info
- program.used_times += 1
- break
- end
- end
-
- if program.nil?
- program = OpenGLProgram.new(self, code, material, parameters)
- @_programs.push(program)
-
- @info[:memory][:programs] = @_programs.length
- end
-
- material.program = program
-
- attributes = program.attributes
-
- if material.morph_targets
- material.num_supported_morph_targets = 0
- base = 'morphTarget'
-
- @max_morph_targets.times do |i|
- id = base + i
- if attributes[id] >= 0
- material.num_supported_morph_targets += 1
- end
- end
- end
-
- if material.morph_normals
- material.num_supported_morph_normals = 0
- base = 'morphNormal'
-
- @max_morph_normals.times do |i|
- id = base + i
- if attributes[id] >= 0
- material.num_supported_morph_normals += 1
- end
- end
- end
-
- material[:uniforms_list] = []
-
- material[:_opengl_shader][:uniforms].each_key do |u|
- location = material.program.uniforms[u]
-
- if location
- material[:uniforms_list] << [material[:_opengl_shader][:uniforms][u], location]
- end
- end
- end
-
- def allocate_lights(lights)
- dir_lights = 0
- point_lights = 0
- spot_lights = 0
- hemi_lights = 0
-
- lights.each do |light|
- next if light.only_shadow || !light.visible
-
- dir_lights += 1 if light.is_a? DirectionalLight
- point_lights += 1 if light.is_a? PointLight
- spot_lights += 1 if light.is_a? SpotLight
- hemi_lights += 1 if light.is_a? HemisphereLight
- end
-
- {
- directional: dir_lights,
- point: point_lights,
- spot: spot_lights,
- hemi: hemi_lights
- }
- end
-
- def allocate_shadows(lights)
- max_shadows = 0
-
- lights.each do |light|
- next unless light.cast_shadow
-
- max_shadows += 1 if light.is_a?(SpotLight)
- max_shadows += 1 if light.is_a?(DirectionalLight) && !light.shadow_cascade
- end
-
- max_shadows
- end
-
- def allocate_bones(object = nil)
- if @_supports_bone_textures && object && object.skeleton && object.skeleton.use_vertex_texture
- return 1024
- end
-
- # default for when object is not specified
- # ( for example when prebuilding shader
- # to be used with multiple objects )
- #
- # - leave some extra space for other uniforms
- # - limit here is ANGLE's 254 max uniform vectors
- # (up to 54 should be safe)
-
- n_vertex_uniforms = (get_gl_parameter(GL_MAX_VERTEX_UNIFORM_COMPONENTS) / 4.0).floor
- n_vertex_matrices = ((n_vertex_uniforms - 20) / 4.0).floor
-
- max_bones = n_vertex_matrices
-
- # TODO: when SkinnedMesh exists
- # if !object.nil? && object.is_a?(SkinnedMesh)
- # max_bones = [object.skeleton.bones.length, max_bones].min
- #
- # if max_bones < object.skeleton.bones.length
- # puts "WARNING: OpenGLRenderer: too many bones - #{object.skeleton.bones.length}, this GPU supports just #{max_bones}"
- # end
- # end
-
- max_bones
- end
-
- def refresh_uniforms_common(uniforms, material)
-
- uniforms['opacity'].value = material.opacity
-
- uniforms['diffuse'].value = material.color
-
- uniforms['map'].value = material.map
- uniforms['lightMap'].value = material.light_map
- uniforms['specularMap'].value = material.specular_map
- uniforms['alphaMap'].value = material.alpha_map
-
- if material.bump_map
- uniforms['bumpMap'].value = material.bump_map
- uniforms['bumpScale'].value = material.bump_scale
- end
-
- if material.normal_map
- uniforms['normalMap'].value = material.normal_map
- uniforms['normalScale'].value.copy( material.normal_scale )
- end
-
- # uv repeat and offset setting priorities
- # 1. color map
- # 2. specular map
- # 3. normal map
- # 4. bump map
- # 5. alpha map
-
- uv_scale_map = nil
-
- if material.map
- uv_scale_map = material.map
- elsif material.specular_map
- uv_scale_map = material.specular_map
- elsif material.normal_map
- uv_scale_map = material.normal_map
- elsif material.bump_map
- uv_scale_map = material.bump_map
- elsif material.alpha_map
- uv_scale_map = material.alpha_map
- end
-
- if !uv_scale_map.nil?
- offset = uv_scale_map.offset
- repeat = uv_scale_map.repeat
-
- uniforms['offsetRepeat'].value.set(offset.x, offset.y, repeat.x, repeat.y)
- end
-
- uniforms['envMap'].value = material.env_map
- # TODO: when OpenGLRenderTargetCube exists
- # uniforms['flipEnvMap'].value = material.envMap.is_a?(OpenGLRenderTargetCube) ? 1 : - 1
-
- uniforms['reflectivity'].value = material.reflectivity
- uniforms['refractionRatio'].value = material.refraction_ratio
- end
-
- def refresh_uniforms_phong(uniforms, material)
- uniforms['shininess'].value = material.shininess
-
- uniforms['emissive'].value = material.emissive
- uniforms['specular'].value = material.specular
-
- if material.wrap_around
- uniforms['wrapRGB'].value.copy(material.wrap_rgb)
- end
- end
-
- def refresh_uniforms_shadow(uniforms, lights)
- if uniforms['shadowMatrix']
- lights.select(&:cast_shadow).select { |light|
- light.is_a?(SpotLight) || (light.is_a?(DirectionalLight) && !light.shadow_cascade)
- }.each_with_index { |light, i|
- uniforms['shadowMap'].value[i] = light.shadow_map
- uniforms['shadowMapSize'].value[i] = light.shadow_map_size
-
- uniforms['shadowMatrix'].value[i] = light.shadow_matrix
-
- uniforms['shadowDarkness'].value[i] = light.shadow_darkness
- uniforms['shadowBias'].value[i] = light.shadow_bias
- }
- end
- end
-
- def refresh_uniforms_line(uniforms, material)
- uniforms['diffuse'].value = material.color
- uniforms['opacity'].value = material.opacity
- end
-
+ # FIXME: REFACTOR!?!?!?!?!???
+ # MASSIVE CASE STATEMENT OMG!!!
def load_uniforms_generic(uniforms)
uniforms.each do |(uniform, location)|
# needs_update property is not added to all uniforms.
next if uniform.needs_update == false || location == -1
type = uniform.type
value = uniform.value
+ # AAAAAHHHHH!!!!! \o/ *flips table*
case type
- when :'1i'
+ when :int
glUniform1i(location, value)
- when :'1f'
+ when :ivec2
+ glUniform2i(location, value[0], value[1])
+ when :ivec3
+ glUniform3i(location, value[0], value[1], value[2])
+ when :ivec4
+ glUniform4i(location, value[0], value[1], value[2], value[3])
+ when :float
glUniform1f(location, value)
- when :'2f'
+ when :vec2
glUniform2f(location, value[0], value[1])
- when :'3f'
- glUniform2f(location, value[0], value[1], value[2])
- when :'4f'
+ when :vec3, :color
+ glUniform3f(location, value[0], value[1], value[2])
+ when :vec4
glUniform4f(location, value[0], value[1], value[2], value[3])
- when :'1iv'
+ when :'int[]'
glUniform1iv(location, value.length, array_to_ptr_easy(value))
- when :'2iv'
+ when :'ivec2[]'
glUniform2iv(location, value.length / 2, array_to_ptr_easy(value))
- when :'3iv'
+ when :'ivec3[]'
glUniform3iv(location, value.length / 3, array_to_ptr_easy(value))
- when :'4iv'
- glUniform3iv(location, value.length / 4, array_to_ptr_easy(value))
- when :'1fv'
+ when :'ivec4[]'
+ glUniform4iv(location, value.length / 4, array_to_ptr_easy(value))
+ when :'float[]'
glUniform1fv(location, value.length, array_to_ptr_easy(value))
- when :'2fv'
- glUniform2fv(location, value.length / 2, array_to_ptr_easy(value))
- when :'3fv'
- glUniform3fv(location, value.length / 3, array_to_ptr_easy(value))
- when :'4fv'
- glUniform3fv(location, value.length / 4, array_to_ptr_easy(value))
- when :Matrix3fv
- glUniformMatrix3fv(location, value / 9, GL_FALSE, array_to_ptr_easy(value))
- when :Matrix4fv
- glUniformMatrix4fv(location, value / 16, GL_FALSE, array_to_ptr_easy(value))
-
- #
-
- when :i
- # single integer
- glUniform1i(location, value)
- when :f
- # single float
- glUniform1f(location, value)
- when :v2
- # single Mittsu::Vector2
- glUniform2f(location, value.x, value.y)
- when :v3
- # single Mittsu::Vector3
- glUniform3f(location, value.x, value.y, value.z)
- when :v4
- # single Mittsu::Vector4
- glUniform4f(location, value.x, value.y, value.z, value.w)
- when :c
- # single Mittsu::Color
- glUniform3f(location, value.r, value.g, value.b)
- when :iv1
- # flat array of integers
- glUniform1iv(location, value.length, array_to_ptr_easy(value))
- when :iv
- # flat array of integers with 3 x N size
- glUniform3iv(location, value.length / 3, array_to_ptr_easy(value))
- when :fv1
- # flat array of floats
- glUniform1fv(location, value.length, array_to_ptr_easy(value))
- when :fv
- # flat array of float with 3 x N size
- glUniform3fv(location, value.length / 3, array_to_ptr_easy(value))
- when :v2v
- # array of Mittsu::Vector2
- uniform[:_array] ||= Array.new(2 * value.length) # Float32Array
-
- value.each_with_index do |v, i|
- offset = i * 2
- uniform[:_array][offset] = v.x
- uniform[:_array][offset + 1] = v.y
+ when :'vec2[]'
+ if value[0].is_a? Vector2
+ uniform.array ||= value.flat_map(&:to_a) # TODO: Float32Array
+ glUniform2fv(location, value.length, array_to_ptr_easy(uniform.array))
+ else
+ glUniform2fv(location, value.length / 2, array_to_ptr_easy(value))
end
-
- glUniform2fv(location, value.length * 2, array_to_ptr_easy(uniform[:_array]))
- when :v3v
- # array of Mittsu::Vector3
- uniform[:_array] ||= Array.new(3 * value.length) # Float32Array
-
- value.each_with_index do |v, i|
- offset = i * 3
- uniform[:_array][offset] = v.x
- uniform[:_array][offset + 1] = v.y
- uniform[:_array][offset + 2] = v.z
+ when :'vec3[]', :'color[]'
+ if value.first.is_a?(Vector3) || value.first.is_a?(Color)
+ uniform.array ||= value.flat_map(&:to_a) # TODO: Float32Array
+ glUniform3fv(location, value.length, array_to_ptr_easy(uniform.array))
+ else
+ glUniform3fv(location, value.length / 3, array_to_ptr_easy(value))
end
-
- glUniform3fv(location, value.length * 3, array_to_ptr_easy(uniform[:_array]))
- when :v4v
- # array of Mittsu::Vector4
- uniform[:_array] ||= Array.new(4 * value.length) # Float32Array
-
- value.each_with_index do |v, i|
- offset = i * 4
- uniform[:_array][offset] = v.x
- uniform[:_array][offset + 1] = v.y
- uniform[:_array][offset + 2] = v.z
- uniform[:_array][offset + 3] = v.w
+ when :'vec4[]'
+ if value.first.is_a? Vector4
+ uniform.array ||= value.flat_map(&:to_a) # TODO: Float32Array
+ glUniform4fv(location, value.length, array_to_ptr_easy(uniform.array))
+ else
+ glUniform4fv(location, value.length / 4, array_to_ptr_easy(value))
end
+ when :mat3
+ glUniformMatrix3fv(location, 1, GL_FALSE, array_to_ptr_easy(value.to_a))
+ when :mat4
+ glUniformMatrix4fv(location, 1, GL_FALSE, array_to_ptr_easy(value.to_a))
+ when :'mat3[]'
+ if value.first.is_a? Matrix3
+ uniform.array ||= Array.new(9 * value.length) # Float32Array
- glUniform4fv(location, value.length * 4, array_to_ptr_easy(uniform[:_array]))
- when :m3
- # single Mittsu::Matrix3
- glUniformMatrix3fv(location, 1, GL_FALSE, array_to_ptr_easy(value.elements))
- when :m3v
- # array of Mittsu::Matrix3
- uniform[:_array] ||= Array.new(9 * value.length) # Float32Array
+ value.each_with_index do |v, i|
+ value[i].flatten_to_array_offset(uniform.array, i * 9)
+ end
- value.each_with_index do |v, i|
- value[i].flatten_to_array_offset(uniform[:_array], i * 9)
+ glUniformMatrix3fv(location, value.length, GL_FALSE, array_to_ptr_easy(uniform.array))
+ else
+ glUniformMatrix3fv(location, value.length / 9, GL_FALSE, array_to_ptr_easy(value))
end
+ when :'mat4[]'
+ if value.first.is_a? Matrix4
+ uniform.array ||= Array.new(16 * value.length) # Float32Array
- glUniformMatrix3fv(location, value.length, GL_FALSE, array_to_ptr_easy(uniform[:_array]))
- when :m4
- # single Mittsu::Matrix4
- glUniformMatrix4vf(location, 1, GL_FALSE, array_to_ptr_easy(value.elements))
- when :m4v
- # array of Mittsu::Matrix4
- uniform[:_array] ||= Array.new(16 * value.length) # Float32Array
+ value.each_with_index do |v, i|
+ value[i].flatten_to_array_offset(uniform.array, i * 16)
+ end
- value.each_with_index do |v, i|
- value[i].flatten_to_array_offset(uniform[:_array], i * 16)
+ glUniformMatrix4fv(location, value.length, GL_FALSE, array_to_ptr_easy(uniform.array))
+ else
+ glUniformMatrix4fv(location, value.length / 16, GL_FALSE, array_to_ptr_easy(value))
end
-
- glUniformMatrix4fv(location, value.length, GL_FALSE, array_to_ptr_easy(uniform[:_array]))
- when :t
+ when :texture
# single Mittsu::Texture (2d or cube)
texture = value
texture_unit = get_texture_unit
glUniform1i(location, texture_unit)
next unless texture
- if texture.is_a?(CubeTexture) || (texture.is_a?(Array) && texture.image.length == 6)
- set_cube_texture(texture, texture_unit)
+ texture_impl = texture.implementation(self)
+ texture_impl.set(texture_unit)
# TODO: when OpenGLRenderTargetCube is defined
# elsif texture.is_a?(OpenGLRenderTargetCube)
# set_cube_texture_dynamic(texture, texture_unit)
- else
- set_texture(texture, texture_unit)
- end
- when :tv
+ when :'texture[]'
# array of Mittsu::Texture (2d)
- uniform[:_array] ||= []
+ uniform.array ||= []
uniform.value.each_index do |i|
- uniform[:_array][i] = get_texture_unit
+ uniform.array[i] = get_texture_unit
end
- glUniform1iv(location, uniform[:_array].length, array_to_ptr_easy(uniform[:_array]))
+ glUniform1iv(location, uniform.array.length, array_to_ptr_easy(uniform.array))
uniform.value.each_with_index do |tex, i|
- tex_unit = uniform[:_array][i]
+ tex_unit = uniform.array[i]
next unless tex
- set_texture(tex, tex_unit)
+ tex.implementation(self).set(tex_unit)
end
else
puts "WARNING: Mittsu::OpenGLRenderer: Unknown uniform type: #{type}"
end
end
end
- def load_uniforms_matrices(uniforms, object)
- glUniformMatrix4fv(uniforms['modelViewMatrix'], 1, GL_FALSE, array_to_ptr_easy(object[:_model_view_matrix].elements))
+ def get_texture_unit
+ texture_unit = @_used_texture_units
- if uniforms['normalMatrix']
- glUniformMatrix3fv(uniforms['normalMatrix'], 1, GL_FALSE, array_to_ptr_easy(object[:_normal_matrix].elements))
+ if texture_unit >= @_max_textures
+ puts "WARNING: OpenGLRenderer: trying to use #{texture_unit} texture units while this GPU supports only #{@_max_textures}"
end
+
+ @_used_texture_units += 1
+ texture_unit
end
- def setup_lights(lights)
- r, g, b = 0.0, 0.0, 0.0
+ def ensure_depth_buffer_writing
+ @state.set_depth_test(true)
+ @state.set_depth_write(true)
+ @state.set_color_write(true)
+ end
- zlights = @_lights
+ def reset_cache
+ @_current_geometry_program = ''
+ @_current_material_id = -1
+ @_current_camera = nil
+ @light_renderer.reset
+ end
- dir_colors = zlights[:directional][:colors]
- dir_positions = zlights[:directional][:positions]
+ def reset_objects_cache
+ @lights.clear
+ @opaque_objects.clear
+ @transparent_objects.clear
+ @sprites.clear
+ @lens_flares.clear
+ end
- point_colors = zlights[:point][:colors]
- point_positions = zlights[:point][:positions]
- point_distances = zlights[:point][:distances]
- point_decays = zlights[:point][:decays]
+ def reset_info
+ @info[:render][:calls] = 0
+ @info[:render][:vertices] = 0
+ @info[:render][:faces] = 0
+ @info[:render][:points] = 0
+ end
- spot_colors = zlights[:spot][:colors]
- spot_positions = zlights[:spot][:positions]
- spot_distances = zlights[:spot][:distances]
- spot_directions = zlights[:spot][:directions]
- spot_angles_cos = zlights[:spot][:angles_cos]
- spot_exponents = zlights[:spot][:exponents]
- spot_decays = zlights[:spot][:decays]
+ def reset_cache_for_this_frame
+ reset_cache
+ reset_objects_cache
+ reset_info
+ end
- hemi_sky_colors = zlights[:hemi][:sky_colors]
- hemi_ground_colors = zlights[:hemi][:ground_colors]
- hemi_positions = zlights[:hemi][:positions]
+ def update_screen_projection(camera)
+ camera.matrix_world_inverse.inverse(camera.matrix_world)
- dir_length = 0
- point_length = 0
- spot_length = 0
- hemi_length = 0
+ @proj_screen_matrix.multiply_matrices(camera.projection_matrix, camera.matrix_world_inverse)
+ @_frustum.set_from_matrix(@proj_screen_matrix)
+ end
- dir_count = 0
- point_count = 0
- spot_count = 0
- hemi_count = 0
+ def sort_objects_for_render
+ @opaque_objects.sort { |a,b| OpenGLHelper.painter_sort_stable(a,b) }
+ @transparent_objects.sort { |a,b| OpenGLHelper.reverse_painter_sort_stable(a,b) }
+ end
- dir_offset = 0
- point_offset = 0
- spot_offset = 0
- hemi_offset = 0
+ def set_matrices_for_immediate_objects(camera)
+ @_opengl_objects_immediate.each do |opengl_object|
+ object = opengl_object.object
- lights.each do |light|
-
- next if light.only_shadow
-
- color = light.color
- intensity = light.intensity
- distance = light.distance
-
- if light.is_a? AmbientLight
-
- next unless light.visible
-
- r += color.r
- g += color.g
- b += color.b
-
- elsif light.is_a? DirectionalLight
-
- dir_count += 1
-
- next unless light.visible
-
- @_direction.set_from_matrix_position(light.matrix_world)
- @_vector3.set_from_matrix_position(light.target.matrix_world)
- @_direction.sub(@_vector3)
- @_direction.normalize
-
- dir_offset = dir_length * 3
-
- dir_positions[dir_offset] = @_direction.x
- dir_positions[dir_offset + 1] = @_direction.y
- dir_positions[dir_offset + 2] = @_direction.z
-
- set_color_linear(dir_colors, dir_offset, color, intensity)
-
- dir_length += 1
-
- elsif light.is_a? PointLight
-
- point_count += 1
-
- next unless light.visible
-
- point_offset = point_length * 3;
-
- set_color_linear(point_colors, point_offset, color, intensity)
-
- @_vector3.set_from_matrix_position(light.matrix_world)
-
- point_positions[point_offset] = @_vector3.x
- point_positions[point_offset + 1] = @_vector3.y
- point_positions[point_offset + 2] = @_vector3.z
-
- # distance is 0 if decay is 0, because there is no attenuation at all.
- point_distances[point_length] = distance
- point_decays[point_length] = light.distance.zero? ? 0.0 : light.decay
-
- point_length += 1
-
- elsif light.is_a? SpotLight
-
- spot_count += 1
-
- next unless light.visible
-
- spot_offset = spot_length * 3
-
- set_color_linear(spot_colors, spot_offset, color, intensity)
-
- @_direction.set_from_matrix_position(light.matrix_world)
-
- spot_positions[spot_offset] = @_direction.x
- spot_positions[spot_offset + 1] = @_direction.y
- spot_positions[spot_offset + 2] = @_direction.z
-
- spot_distances[spot_length] = distance
-
- @_vector3.set_from_matrix_position(light.target.matrix_world)
- @_direction.sub(@_vector3)
- @_direction.normalize
-
- spot_directions[spot_offset] = @_direction.x
- spot_directions[spot_offset + 1] = @_direction.y
- spot_directions[spot_offset + 2] = @_direction.z
-
- spot_angles_cos[spot_length] = Math.cos(light.angle)
- spot_exponents[spot_length] = light.exponent;
- spot_decays[spot_length] = light.distance.zero? ? 0.0 : light.decay
-
- spot_length += 1;
-
- elsif light.is_a? HemisphereLight
-
- hemi_count += 1
-
- next unless light.visible
-
- @_direction.set_from_matrix_position(light.matrix_world)
- @_direction.normalize
-
- hemi_offset = hemi_length * 3
-
- hemi_positions[hemi_offset] = @_direction.x
- hemi_positions[hemi_offset + 1] = @_direction.y
- hemi_positions[hemi_offset + 2] = @_direction.z
-
- sky_color = light.color
- ground_color = light.ground_color
-
- set_color_linear(hemi_sky_colors, hemi_offset, sky_color, intensity )
- set_color_linear(hemi_ground_colors, hemi_offset, ground_color, intensity)
-
- hemi_length += 1
-
+ if object.visible
+ object.implementation(self).setup_matrices(camera)
+ unroll_immediate_buffer_material(opengl_object)
end
+ end
+ end
+ def render_main_pass(scene, camera)
+ if scene.override_material
+ render_with_override_material(scene, camera)
+ else
+ render_with_default_materials(scene, camera)
end
+ end
- # null eventual remains from removed lights
- # (this is to avoid if in shader)
+ def render_with_override_material(scene, camera)
+ override_material = scene.override_material
+ material_impl = override_material.implementation(self)
- (dir_length * 3).upto([dir_colors.length, dir_count * 3].max - 1).each { |i|
- dir_colors[i] = 0.0
- }
- (point_length * 3).upto([point_colors.length, point_count * 3].max - 1).each { |i|
- point_colors[i] = 0.0
- }
- (spot_length * 3).upto([spot_colors.length, spot_count * 3].max - 1).each { |i|
- spot_colors[i] = 0.0
- }
- (hemi_length * 3).upto([hemi_ground_colors.length, hemi_count * 3].max - 1).each { |i|
- hemi_ground_colors[i] = 0.0
- }
- (hemi_length * 3).upto([hemi_sky_colors.length, hemi_count * 3].max - 1).each { |i|
- hemi_sky_colors[i] = 0.0
- }
+ material_impl.set
- zlights[:directional][:length] = dir_length
- zlights[:point][:length] = point_length
- zlights[:spot][:length] = spot_length
- zlights[:hemi][:length] = hemi_length
+ render_objects(@opaque_objects, camera, @lights, scene.fog, override_material)
+ render_objects(@transparent_objects, camera, @lights, scene.fog, override_material)
+ render_objects_immediate(@_opengl_objects_immediate, nil, camera, @lights, scene.fog, override_material)
+ end
- zlights[:ambient][0] = r
- zlights[:ambient][1] = g
- zlights[:ambient][2] = b
+ def render_with_default_materials(scene, camera)
+ render_opaque_pass(scene, camera)
+ render_transparent_pass(scene, camera)
end
- def refresh_uniforms_lights(uniforms, lights)
- uniforms['ambientLightColor'].value = lights[:ambient]
+ def render_opaque_pass(scene, camera)
+ # front-to-back order
+ @state.set_blending(NoBlending)
- uniforms['directionalLightColor'].value = lights[:directional][:colors]
- uniforms['directionalLightDirection'].value = lights[:directional][:positions]
+ render_objects(@opaque_objects, camera, @lights, scene.fog, nil)
+ render_objects_immediate(@_opengl_objects_immediate, :opaque, camera, @lights, scene.fog, nil)
+ end
- uniforms['pointLightColor'].value = lights[:point][:colors]
- uniforms['pointLightPosition'].value = lights[:point][:positions]
- uniforms['pointLightDistance'].value = lights[:point][:distances]
- uniforms['pointLightDecay'].value = lights[:point][:decays]
+ def render_transparent_pass(scene, camera)
+ # back-to-front-order
+ render_objects(@transparent_objects, camera, @lights, scene.fog, nil)
+ render_objects_immediate(@_opengl_objects_immediate, :transparent, camera, @lights, scene.fog, nil)
+ end
- uniforms['spotLightColor'].value = lights[:spot][:colors]
- uniforms['spotLightPosition'].value = lights[:spot][:positions]
- uniforms['spotLightDistance'].value = lights[:spot][:distances]
- uniforms['spotLightDirection'].value = lights[:spot][:directions]
- uniforms['spotLightAngleCos'].value = lights[:spot][:angles_cos]
- uniforms['spotLightExponent'].value = lights[:spot][:exponents]
- uniforms['spotLightDecay'].value = lights[:spot][:decays]
+ def render_custom_plugins_pre_pass(scene, camera)
+ @shadow_map_plugin.render(scene, camera)
+ end
- uniforms['hemisphereLightSkyColor'].value = lights[:hemi][:sky_colors]
- uniforms['hemisphereLightGroundColor'].value = lights[:hemi][:ground_colors]
- uniforms['hemisphereLightDirection'].value = lights[:hemi][:positions]
+ def render_custom_plugins_post_pass(scene, camera)
+ # TODO: when these custom plugins are implemented
+ # @sprite_plugin.render(scene, camera)
+ # lens_flare_plugin.render(scene, camera, @_current_render_target.width, @_current_render_target.height)
end
- def mark_uniforms_lights_needs_update(uniforms, value)
- uniforms['ambientLightColor'].needs_update = value
+ def update_skeleton_objects(scene)
+ # TODO: when SkinnedMesh is defined
+ # scene.traverse do |object|
+ # if object.is_a? SkinnedMesh
+ # object.skeleton.update
+ # end
+ # end
+ end
- uniforms['directionalLightColor'].needs_update = value
- uniforms['directionalLightDirection'].needs_update = value
-
- uniforms['pointLightColor'].needs_update = value
- uniforms['pointLightPosition'].needs_update = value
- uniforms['pointLightDistance'].needs_update = value
- uniforms['pointLightDecay'].needs_update = value
-
- uniforms['spotLightColor'].needs_update = value
- uniforms['spotLightPosition'].needs_update = value
- uniforms['spotLightDistance'].needs_update = value
- uniforms['spotLightDirection'].needs_update = value
- uniforms['spotLightAngleCos'].needs_update = value
- uniforms['spotLightExponent'].needs_update = value
- uniforms['spotLightDecay'].needs_update = value
-
- uniforms['hemisphereLightSkyColor'].needs_update = value
- uniforms['hemisphereLightGroundColor'].needs_update = value
- uniforms['hemisphereLightDirection'].needs_update = value
+ def perform_auto_clear
+ clear(@auto_clear_color, @auto_clear_depth, @auto_clear_stencil)
end
- def refresh_uniforms_lambert(uniforms, material)
- uniforms['emissive'].value = material.emissive
+ def init_clearing
+ @_clear_color = Color.new(0x000000)
+ @_clear_alpha = 0.0
- if material.wrap_around
- uniforms['wrapRGB'].value.copy(material.wrap_rgb)
- end
+ @auto_clear = true
+ @auto_clear_color = true
+ @auto_clear_depth = true
+ @auto_clear_stencil = true
end
- def set_color_linear(array, offset, color, intensity)
- array[offset] = color.r * intensity
- array[offset + 1] = color.g * intensity
- array[offset + 2] = color.b * intensity
+ def init_gamma
+ @gamma_factor = 2.0 # backwards compat???
+ @gamma_input = false
+ @gamma_output = false
end
- def get_texture_unit
- texture_unit = @_used_texture_units
-
- if texture_unit >= @_max_textures
- puts "WARNING: OpenGLRenderer: trying to use #{texture_unit} texture units while this GPU supports only #{@_max_textures}"
- end
-
- @_used_texture_units += 1
- texture_unit
+ def init_shadow_properties
+ @shadow_map_enabled = false
+ @shadow_map_type = PCFShadowMap
+ @shadow_map_cull_face = CullFaceFront
+ @shadow_map_debug = false
+ @shadow_map_cascade = false
end
- def clamp_to_max_size(image, max_size)
- if image.width > max_size || image.height > max_size
- # TODO: scale the image ...
-
- puts "WARNING: Mittsu::OpenGLRenderer: image is too big (#{image.width} x #{image.height}). Resized to ??? x ???"
- end
- image
+ def init_morphs
+ @max_morph_targets = 8
+ @max_morph_normals = 4
end
- def param_mittsu_to_gl(p)
- case p
- when RepeatWrapping then GL_REPEAT
- when ClampToEdgeWrapping then GL_CLAMP_TO_EDGE
- when MirroredRepeatWrapping then GL_MIRRORED_REPEAT
+ def init_collections
+ @lights = []
- when NearestFilter then GL_NEAREST
- when NearestMipMapNearestFilter then GL_NEAREST_MIPMAP_NEAREST
- when NearestMipMapLinearFilter then GL_NEAREST_MIPMAP_LINEAR
+ @_opengl_objects = {}
+ @_opengl_objects_immediate = []
- when LinearFilter then GL_LINEAR
- when LinearMipMapNearestFilter then GL_LINEAR_MIPMAP_NEAREST
- when LinearMipMapLinearFilter then GL_LINEAR_MIPMAP_LINEAR
+ @opaque_objects = []
+ @transparent_objects = []
- when UnsignedByteType then GL_UNSIGNED_BYTE
- when UnsignedShort4444Type then GL_UNSIGNED_SHORT_4_4_4_4
- when UnsignedShort5551Type then GL_UNSIGNED_SHORT_5_5_5_1
- when UnsignedShort565Type then GL_UNSIGNED_SHORT_5_6_5
+ @sprites = []
+ @lens_flares = []
- when ByteType then GL_BYTE
- when ShortType then GL_SHORT
- when UnsignedShortType then GL_UNSIGNED_SHORT
- when IntType then GL_INT
- when UnsignedIntType then GL_UNSIGNED_INT
- when FloatType then GL_FLOAT
+ @programs = []
+ end
- when AlphaFormat then GL_ALPHA
- when RGBFormat then GL_RGB
- when RGBAFormat then GL_RGBA
- when LuminanceFormat then GL_LUMINANCE
- when LuminanceAlphaFormat then GL_LUMINANCE_ALPHA
+ def init_info
+ @info = {
+ memory: {
+ programs: 0,
+ geometries: 0,
+ textures: 0
+ },
+ render: {
+ calls: 0,
+ vertices: 0,
+ faces: 0,
+ points: 0
+ }
+ }
+ end
- when AddEquation then GL_FUNC_ADD
- when SubtractEquation then GL_FUNC_SUBTRACT
- when ReverseSubtractEquation then GL_FUNC_REVERSE_SUBTRACT
+ def init_state_cache
+ @_current_program = nil
+ @_current_render_target = default_target
+ @_current_material_id = -1
+ @_current_geometry_program = ''
+ @_current_camera = nil
- when ZeroFactor then GL_ZERO
- when OneFactor then GL_ONE
- when SrcColorFactor then GL_SRC_COLOR
- when OneMinusSrcColorFactor then GL_ONE_MINUS_SRC_COLOR
- when SrcAlphaFactor then GL_SRC_ALPHA
- when OneMinusSrcAlphaFactor then GL_ONE_MINUS_SRC_ALPHA
- when DstAlphaFactor then GL_DST_ALPHA
- when OneMinusDstAlphaFactor then GL_ONE_MINUS_DST_ALPHA
+ @_used_texture_units = 0
+ end
- when DstColorFactor then GL_DST_COLOR
- when OneMinusDstColorFactor then GL_ONE_MINUS_DST_COLOR
- when SrcAlphaSaturateFactor then GL_SRC_ALPHA_SATURATE
- else 0
- end
+ def init_camera_matrix_cache
+ @_frustum = Frustum.new
+ @proj_screen_matrix = Matrix4.new
+ @_vector3 = Vector3.new
end
- def set_texture_parameters(texture_type, texture, is_image_power_of_two)
- if is_image_power_of_two
- glTexParameteri(texture_type, GL_TEXTURE_WRAP_S, param_mittsu_to_gl(texture.wrap_s))
- glTexParameteri(texture_type, GL_TEXTURE_WRAP_T, param_mittsu_to_gl(texture.wrap_t))
+ def fetch_parameters(parameters)
+ @_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)
- glTexParameteri(texture_type, GL_TEXTURE_MAG_FILTER, param_mittsu_to_gl(texture.mag_filter))
- glTexParameteri(texture_type, GL_TEXTURE_MIN_FILTER, param_mittsu_to_gl(texture.min_filter))
- else
- glTexParameteri(texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
- glTexParameteri(texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
+ @width = parameters.fetch(:width, 800)
+ @height = parameters.fetch(:height, 600)
+ @title = parameters.fetch(:title, "Mittsu #{REVISION}")
+ end
- if texture.wrap_s != ClampToEdgeWrapping || texture.wrap_t != ClampToEdgeWrapping
- puts "WARNING: Mittsu::OpenGLRenderer: Texture is not power of two. Texture.wrap_s and Texture.wrap_t should be set to Mittsu::ClampToEdgeWrapping. (#{texture.source_file})"
- end
+ def get_gpu_capabilities
+ @_max_textures = glGetParameter(GL_MAX_TEXTURE_IMAGE_UNITS)
+ @_max_vertex_textures = glGetParameter(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS)
+ @_max_texture_size = glGetParameter(GL_MAX_TEXTURE_SIZE)
+ @_max_cubemap_size = glGetParameter(GL_MAX_CUBE_MAP_TEXTURE_SIZE)
- glTexParameteri(texture_type, GL_TEXTURE_MAG_FILTER, filter_fallback(texture.mag_filter))
- glTexParameteri(texture_type, GL_TEXTURE_MIN_FILTER, filter_fallback(texture.min_filter))
+ @_supports_vertex_textures = @_max_vertex_textures > 0
+ @_supports_bone_textures = @_supports_vertex_textures && false # TODO: extensions.get('OES_texture_float') ????
+ end
- if texture.min_filter != NearestFilter && texture.min_filter != LinearFilter
- puts "WARNING: Mittsu::OpenGLRenderer: Texture is not a power of two. Texture.min_filter should be set to Mittsu::NearestFilter or Mittsu::LinearFilter. (#{texture.source_file})"
- end
+ def init_plugins
+ @shadow_map_plugin = ShadowMapPlugin.new(self, @lights, @_opengl_objects, @_opengl_objects_immediate)
- # TODO: anisotropic extension ???
- end
+ # TODO: when these custom plugins are implemented
+ # @sprite_plugin = SpritePlugin.new(self, @sprites)
+ # @lens_flare_plugin = LensFlarePlugin.new(self, @lens_flares)
end
- def set_cube_texture(texture, slot)
- if texture.image.length == 6
- if texture.needs_update?
- if !texture.image[:_opengl_texture_cube]
- texture.add_event_listener(:dispose, @on_texture_dispose)
- texture.image[:_opengl_texture_cube] = glCreateTexture
- @info[:memory][:textures] += 1
- end
+ def create_window
+ # attributes = {
+ # alpha: _alpha,
+ # depth: _depth,
+ # stencil: _stencil,
+ # antialias: _antialias,
+ # premultiplied_alpha: _premultiplied_alpha,
+ # preserve_drawing_buffer: _preserve_drawing_buffer
+ # }
- glActiveTexture(GL_TEXTURE0 + slot)
- glBindTexture(GL_TEXTURE_CUBE_MAP, texture.image[:_opengl_texture_cube])
+ @window = GLFW::Window.new(@width, @height, @title)
- # glPixelStorei(GL_UNPACK_FLIP_Y_WEBGL, texture.flip_y)
+ default_target.set_viewport_size(*(@window.framebuffer_size))
- is_compressed = texture.is_a?(CompressedTexture)
- is_data_texture = texture.image[0].is_a?(DataTexture)
+ # TODO: handle losing opengl context??
+ end
- cube_image = [];
+ def switch_geometry_program(program, material, geometry_group)
+ wireframe_bit = material.wireframe ? 1 : 0
+ geometry_program = "#{geometry_group.id}_#{program.id}_#{wireframe_bit}"
- 6.times do |i|
- if @auto_scale_cubemaps && !is_compressed && !is_data_texture
- cube_image[i] = clamp_to_max_size(texture.image[i], @_max_cubemap_size)
- else
- cube_image[i] = is_data_texture ? texture.image[i].image : texture.image[i];
- end
- end
-
- image = cube_image[0]
- 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_CUBE_MAP, texture, is_image_power_of_two)
-
- 6.times do |i|
- if !is_compressed
- if is_data_texture
- glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl_format, cube_image[i].width, cube_image[i].height, 0, gl_format, gl_type, cube_image[i].data)
- else
- glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl_format, cube_image[i].width, cube_image[i].height, 0, gl_format, gl_type, cube_image[i].data)
- end
- else
- mipmaps = cube_image[i].mipmaps
-
- mipmaps.each_with_index do |mipmap, j|
- if texture.format != RGBAFormat && texture.format != RGBFormat
- if get_compressed_texture_formats.include?(gl_format)
- glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, j, gl_format, mipmap.width, mipmap.height, 0, mipmap.data)
- else
- puts "WARNING: Mittsu::OpenGLRenderer: Attempt to load unsupported compressed texture format in #set_cube_texture"
- end
- else
- glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, j, gl_format, mipmap.width, mipmap.height, 0, gl_format, gl_type, mipmap.data)
- end
- end
- end
- end
-
- if texture.generate_mipmaps && is_image_power_of_two
- glGenerateMipmap(GL_TEXTURE_CUBE_MAP)
- end
-
- texture.needs_update = false
-
- # TODO wat?
- # texture.on_update if texture.on_update
- else
- glActiveTexture(GL_TEXTURE0 + slot)
- glBindTexture(GL_TEXTURE_CUBE_MAP, texture.image[:_opengl_texture_cube])
- end
+ if geometry_program != @_current_geometry_program
+ @_current_geometry_program = geometry_program
+ true
+ else
+ false
end
end
- def setup_framebuffer(framebuffer, render_target, texture_target)
- glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, render_target[:_opengl_texture], 0)
+ def default_target
+ @_defualt_target ||= OpenGLDefaultTarget.new(self)
end
- def setup_renderbuffer(renderbuffer, render_target)
- glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer)
+ def update_camera_uniforms(uniforms, camera, material)
+ material_impl = material.implementation(self)
- if render_target.depth_buffer && !render_target.stencil_buffer
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, render_target.width, render_target.height)
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer)
+ glUniformMatrix4fv(uniforms['projectionMatrix'], 1, GL_FALSE, array_to_ptr_easy(camera.projection_matrix.elements))
- # TODO: investigate this (?):
- # THREE.js - For some reason this is not working. Defaulting to RGBA4.
- # } else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
- #
- # _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height );
- # _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
- elsif render_target.depth_buffer && render_target.stencil_buffer
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, render_target.width, render_target.height)
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuffer)
- else
- glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, render_target.width, render_target.height)
+ if @logarithmic_depth_buffer
+ glUniform1f(uniforms['logDepthBuffFC'], 2.0 / Math.log(camera.far + 1.0) / Math::LN2)
+ end
+
+ if material_impl.needs_camera_position_uniform? && !uniforms['cameraPosition'].nil?
+ @_vector3.set_from_matrix_position(camera.matrix_world)
+ glUniform3f(uniforms['cameraPosition'], @_vector3.x, @_vector3.y, @_vector3.z)
+ end
+
+ if material_impl.needs_view_matrix_uniform? && !uniforms['viewMatrix'].nil?
+ glUniformMatrix4fv(uniforms['viewMatrix'], 1, GL_FALSE, array_to_ptr_easy(camera.matrix_world_inverse.elements))
end
end
end
end