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