#include "gropengldeferred.h" #include "graphics/util/primitives.h" #include "ShaderProgram.h" #include "gropengldraw.h" #include "gropenglstate.h" #include "gropengltnl.h" #include "graphics/2d.h" #include "graphics/light.h" #include "graphics/matrix.h" #include "graphics/shadows.h" #include "graphics/util/UniformAligner.h" #include "graphics/util/UniformBuffer.h" #include "graphics/util/uniform_structs.h" #include "lighting/lighting.h" #include "lighting/lighting_profiles.h" #include "mission/mission_flags.h" #include "mission/missionparse.h" #include "nebula/neb.h" #include "nebula/volumetrics.h" #include "mod_table/mod_table.h" #include "render/3d.h" #include "tracing/tracing.h" #ifdef USE_OPENGL_ES #include "es_compatibility.h" #endif #include void gr_opengl_deferred_init() { gr_opengl_deferred_light_cylinder_init(16); gr_opengl_deferred_light_sphere_init(16, 16); } void gr_opengl_deferred_shutdown() {} void opengl_clear_deferred_buffers() { GR_DEBUG_SCOPE("Clear deferred buffers"); GLboolean depth = GL_state.DepthTest(GL_FALSE); GLboolean depth_mask = GL_state.DepthMask(GL_FALSE); GLboolean blend = GL_state.Blend(GL_FALSE); GLboolean cull = GL_state.CullFace(GL_FALSE); GL_state.ColorMask(true, true, true, true); opengl_shader_set_current( gr_opengl_maybe_create_shader(SDR_TYPE_DEFERRED_CLEAR, 0) ); opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f); opengl_shader_set_current(); GL_state.ColorMask(true, true, true, false); GL_state.DepthTest(depth); GL_state.DepthMask(depth_mask); GL_state.Blend(blend); GL_state.CullFace(cull); } void gr_opengl_deferred_lighting_begin(bool clearNonColorBufs) { if (!light_deferred_enabled()) return; static const float black[] = {0, 0, 0, 1.0f}; GR_DEBUG_SCOPE("Deferred lighting begin"); Deferred_lighting = true; GL_state.ColorMask(true, true, true, true); GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5 }; if (Cmdline_msaa_enabled > 0) { //Ensure MSAA Mode if necessary GL_state.BindFrameBuffer(Scene_framebuffer_ms); glDrawBuffer(GL_COLOR_ATTACHMENT4); opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_COPY, 0)); GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_color_texture); Current_shader->program->Uniforms.setTextureUniform("tex", 0); GL_state.SetAlphaBlendMode(gr_alpha_blend::ALPHA_BLEND_NONE); GL_state.SetZbufferType(ZBUFFER_TYPE_NONE); opengl_draw_full_screen_textured(0, 0, 1, 1); } else { // Copy the existing color data into the emissive part of the G-buffer since everything that already existed is // treated as emissive glDrawBuffer(GL_COLOR_ATTACHMENT4); glReadBuffer(GL_COLOR_ATTACHMENT0); glBlitFramebuffer(0, 0, gr_screen.max_w, gr_screen.max_h, 0, 0, gr_screen.max_w, gr_screen.max_h, GL_COLOR_BUFFER_BIT, GL_NEAREST); } glDrawBuffers(6, buffers); glClearBufferfv(GL_COLOR, 0, black); if (clearNonColorBufs) { glClearBufferfv(GL_COLOR, 1, black); glClearBufferfv(GL_COLOR, 2, black); glClearBufferfv(GL_COLOR, 3, black); glClearBufferfv(GL_COLOR, 5, black); } } void gr_opengl_deferred_lighting_msaa() { if (!Deferred_lighting) return; if (Cmdline_msaa_enabled <= 0) return; GR_DEBUG_SCOPE("MSAA Pass"); GL_state.BindFrameBuffer(Scene_framebuffer); GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4}; glDrawBuffers(5, buffers); int msaa_resolve_flags = 0; switch (Cmdline_msaa_enabled) { case 4: msaa_resolve_flags = SDR_FLAG_MSAA_SAMPLES_4; break; case 8: msaa_resolve_flags = SDR_FLAG_MSAA_SAMPLES_8; break; case 16: msaa_resolve_flags = SDR_FLAG_MSAA_SAMPLES_16; break; default: Error(LOCATION, "Disallowed MSAA shader sample count %d!", Cmdline_msaa_enabled); break; } opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_MSAA_RESOLVE, msaa_resolve_flags)); GL_state.Texture.Enable(0, GL_TEXTURE_2D_MULTISAMPLE, Scene_color_texture_ms); GL_state.Texture.Enable(1, GL_TEXTURE_2D_MULTISAMPLE, Scene_position_texture_ms); GL_state.Texture.Enable(2, GL_TEXTURE_2D_MULTISAMPLE, Scene_normal_texture_ms); GL_state.Texture.Enable(3, GL_TEXTURE_2D_MULTISAMPLE, Scene_specular_texture_ms); GL_state.Texture.Enable(4, GL_TEXTURE_2D_MULTISAMPLE, Scene_emissive_texture_ms); GL_state.Texture.Enable(5, GL_TEXTURE_2D_MULTISAMPLE, Scene_depth_texture_ms); Current_shader->program->Uniforms.setTextureUniform("texColor", 0); Current_shader->program->Uniforms.setTextureUniform("texPos", 1); Current_shader->program->Uniforms.setTextureUniform("texNormal", 2); Current_shader->program->Uniforms.setTextureUniform("texSpecular", 3); Current_shader->program->Uniforms.setTextureUniform("texEmissive", 4); Current_shader->program->Uniforms.setTextureUniform("texDepth", 5); opengl_set_generic_uniform_data( [&](graphics::generic_data::msaa_data* data) { data->samples = Cmdline_msaa_enabled; data->fov = g3_get_hfov(Proj_fov); }); GL_state.SetAlphaBlendMode(gr_alpha_blend::ALPHA_BLEND_NONE); GL_state.SetZbufferType(ZBUFFER_TYPE_WRITE); opengl_draw_full_screen_textured(0, 0, 1, 1); } void gr_opengl_deferred_lighting_end() { if(!Deferred_lighting) return; GR_DEBUG_SCOPE("Deferred lighting end"); Deferred_lighting = false; glDrawBuffer(GL_COLOR_ATTACHMENT0); GL_state.ColorMask(true, true, true, false); } static GLuint deferred_light_cylinder_vbo = 0; static GLuint deferred_light_cylinder_ibo = 0; static GLushort deferred_light_cylinder_vcount = 0; static GLuint deferred_light_cylinder_icount = 0; static GLuint deferred_light_sphere_vbo = 0; static GLuint deferred_light_sphere_ibo = 0; static GLushort deferred_light_sphere_vcount = 0; static GLuint deferred_light_sphere_icount = 0; extern SCP_vector Lights; extern int Num_lights; namespace ltp = lighting_profiles; using namespace ltp; static bool override_fog = false; graphics::deferred_light_data* // common conversion operations to translate a game light data structure into a render-ready light uniform. prepare_light_uniforms(light& l, graphics::util::UniformAligner& uniformAligner, const ltp::profile* lp) { graphics::deferred_light_data* light_data = uniformAligner.addTypedElement(); light_data->lightType = static_cast(l.type); float intensity = (Lighting_mode == lighting_mode::COCKPIT) ? lp->cockpit_light_intensity_modifier.handle(l.intensity) : l.intensity; vec3d diffuse; diffuse.xyz.x = l.r * intensity; diffuse.xyz.y = l.g * intensity; diffuse.xyz.z = l.b * intensity; light_data->diffuseLightColor = diffuse; // Set a default value for all lights. Only the first directional light will change this. light_data->enable_shadows = false; light_data->sourceRadius = l.source_radius; return light_data; } void gr_opengl_deferred_lighting_finish() { GR_DEBUG_SCOPE("Deferred lighting finish"); TRACE_SCOPE(tracing::ApplyLights); if (!light_deferred_enabled()) { return; } GL_state.SetAlphaBlendMode(ALPHA_BLEND_ADDITIVE); gr_zbuffer_set(GR_ZBUFF_NONE); // GL_state.DepthFunc(GL_GREATER); // GL_state.DepthMask(GL_FALSE); opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_DEFERRED_LIGHTING, ENVMAP > 0 ? SDR_FLAG_ENV_MAP : 0)); // Render on top of the composite buffer texture glDrawBuffer(GL_COLOR_ATTACHMENT5); glReadBuffer(GL_COLOR_ATTACHMENT4); glBlitFramebuffer(0, 0, gr_screen.max_w, gr_screen.max_h, 0, 0, gr_screen.max_w, gr_screen.max_h, GL_COLOR_BUFFER_BIT, GL_NEAREST); GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_color_texture); GL_state.Texture.Enable(1, GL_TEXTURE_2D, Scene_normal_texture); GL_state.Texture.Enable(2, GL_TEXTURE_2D, Scene_position_texture); GL_state.Texture.Enable(3, GL_TEXTURE_2D, Scene_specular_texture); if (Shadow_quality != ShadowQuality::Disabled) { GL_state.Texture.Enable(4, GL_TEXTURE_2D_ARRAY, Shadow_map_depth_texture); } if (ENVMAP > 0) { Current_shader->program->Uniforms.setTextureUniform("sEnvmap", 5); Current_shader->program->Uniforms.setTextureUniform("sIrrmap", 6); float u_scale, v_scale; uint32_t array_index; gr_opengl_tcache_set(ENVMAP, TCACHE_TYPE_CUBEMAP, &u_scale, &v_scale, &array_index, 5); gr_opengl_tcache_set(IRRMAP, TCACHE_TYPE_CUBEMAP, &u_scale, &v_scale, &array_index, 6); Assertion(array_index == 0, "Cube map arrays are not supported yet!"); } // We need to use stable sorting here to make sure that the relative ordering of the same light types is the same as // the rest of the code. Otherwise the shadow mapping would be applied while rendering the wrong light which would // lead to flickering lights in some circumstances std::stable_sort(Lights.begin(), Lights.end(), light_compare_by_type); using namespace graphics; // We need to precompute how many elements we are going to need size_t num_data_elements = Lights.size() + 1; // Get a uniform buffer for our data auto light_buffer = gr_get_uniform_buffer(uniform_block_type::Lights, num_data_elements); auto& light_uniform_aligner = light_buffer.aligner(); auto matrix_buffer = gr_get_uniform_buffer(uniform_block_type::Matrices, num_data_elements); auto& matrix_uniform_aligner = matrix_buffer.aligner(); // This is the light which is responsible for shadows and volumetric nebula lighting const light* global_light = nullptr; vec3d global_light_diffuse; // To allow reduced bind calls, we sort lights into subsets based on rendering methods. // It might seem optimal to create these subsets as lights are added, or some other method, // but this keeps the graphics implementation methods better contained and profiling currently // (dec 2023) shows negligable cost of doing it this way. SCP_vector full_frame_lights = SCP_vector(); SCP_vector sphere_lights = SCP_vector(); SCP_vector cylinder_lights = SCP_vector(); for (auto& l : Lights) { switch (l.type) { case Light_Type::Directional: full_frame_lights.push_back(l); break; case Light_Type::Cone: FALLTHROUGH; case Light_Type::Point: sphere_lights.push_back(l); break; case Light_Type::Tube: cylinder_lights.push_back(l); break; case Light_Type::Ambient: Warning(LOCATION, "Multiple ambient lights are not supported!"); } } { GR_DEBUG_SCOPE("Build buffer data"); auto lp = ltp::current(); auto header = light_uniform_aligner.getHeader(); if (Shadow_quality != ShadowQuality::Disabled) { vm_inverse_matrix4(&header->inv_view_matrix, &Shadow_view_matrix_render); shadow_cascade_params_bind_deferred(); } header->invScreenWidth = 1.0f / gr_screen.max_w; header->invScreenHeight = 1.0f / gr_screen.max_h; header->nearPlane = gr_near_plane; { //Prepare ambient light light& l = full_frame_lights.emplace_back(); vec3d ambient; gr_get_ambient_light(&ambient); l.r = ambient.xyz.x; l.g = ambient.xyz.y; l.b = ambient.xyz.z; l.type = Light_Type::Ambient; l.intensity = 1.f; l.source_radius = 0.f; } // Only the first directional light uses shaders so we need to know when we already saw that light bool first_directional = true; for (auto& l : full_frame_lights) { auto light_data = prepare_light_uniforms(l, light_uniform_aligner, lp); if (l.type == Light_Type::Directional ) { if (Shadow_quality != ShadowQuality::Disabled) { light_data->enable_shadows = first_directional ? 1 : 0; } // Global light direction should match shadow light direction if (first_directional) { global_light = &l; global_light_diffuse = light_data->diffuseLightColor; first_directional = false; } vec4 light_dir; light_dir.xyzw.x = -l.vec.xyz.x; light_dir.xyzw.y = -l.vec.xyz.y; light_dir.xyzw.z = -l.vec.xyz.z; light_dir.xyzw.w = 0.0f; vec4 view_dir; vm_vec_transform(&view_dir, &light_dir, &gr_view_matrix); light_data->lightDir.xyz.x = view_dir.xyzw.x; light_data->lightDir.xyz.y = view_dir.xyzw.y; light_data->lightDir.xyz.z = view_dir.xyzw.z; } } for (auto& l : sphere_lights) { auto light_data = prepare_light_uniforms(l, light_uniform_aligner, lp); if (l.type == Light_Type::Cone) { light_data->dualCone = (l.flags & LF_DUAL_CONE) ? 1 : 0; light_data->coneAngle = l.cone_angle; light_data->coneInnerAngle = l.cone_inner_angle; light_data->coneDir = l.vec2; } float rad = (Lighting_mode == lighting_mode::COCKPIT) ? lp->cockpit_light_radius_modifier.handle(MAX(l.rada, l.radb)) : MAX(l.rada, l.radb); light_data->lightRadius = rad; // A small padding factor is added to guard against potentially clipping the edges of the light with facets // of the volume mesh. light_data->scale.xyz.x = rad * 1.05f; light_data->scale.xyz.y = rad * 1.05f; light_data->scale.xyz.z = rad * 1.05f; } for (auto& l : cylinder_lights) { auto light_data = prepare_light_uniforms(l, light_uniform_aligner, lp); float rad = (Lighting_mode == lighting_mode::COCKPIT) ? lp->cockpit_light_radius_modifier.handle(l.radb) : l.radb; light_data->lightRadius = rad; light_data->lightType = LT_TUBE; vec3d a; vm_vec_sub(&a, &l.vec, &l.vec2); auto length = vm_vec_mag(&a); // Tube light volumes must be extended past the length of their requested light vector // to allow smooth fall-off from all angles. Since the light volume starts at the mesh // origin we must extend it here. Later the position will be adjusted as well. length += light_data->lightRadius * 2.0f; // A small padding factor is added to guard against potentially clipping the edges of the light with facets // of the volume mesh. light_data->scale.xyz.x = rad * 1.05f; light_data->scale.xyz.y = rad * 1.05f; light_data->scale.xyz.z = length; } // Uniform data has been assembled, upload it to the GPU and issue the draw calls light_buffer.submitData(); } { for (size_t i = 0; i(); matrix_data->modelViewMatrix = gr_env_texture_matrix; } for (auto& l : sphere_lights) { auto matrix_data = matrix_uniform_aligner.addTypedElement(); g3_start_instance_matrix(&l.vec, &vmd_identity_matrix, true); matrix_data->modelViewMatrix = gr_model_view_matrix; matrix_data->projMatrix = gr_projection_matrix; g3_done_instance(true); } for (auto& l : cylinder_lights ) { auto matrix_data = matrix_uniform_aligner.addTypedElement(); vec3d dir, newPos; matrix orient; vm_vec_normalized_dir(&dir, &l.vec, &l.vec2); vm_vector_2_matrix_norm(&orient, &dir, nullptr, nullptr); // Tube light volumes must be extended past the length of their requested light vector // to allow smooth fall-off from all angles. Since the light volume starts at the mesh // origin we must extend it, which has been done above, and then move it backwards one radius. vm_vec_scale_sub(&newPos, &l.vec2, &dir, l.radb); g3_start_instance_matrix(&newPos, &orient, true); matrix_data->modelViewMatrix = gr_model_view_matrix; matrix_data->projMatrix = gr_projection_matrix; g3_done_instance(true); } matrix_buffer.submitData(); } { GR_DEBUG_SCOPE("Render light geometry"); gr_bind_uniform_buffer(uniform_block_type::DeferredGlobals, light_buffer.getBufferOffset(0), sizeof(graphics::deferred_global_data), light_buffer.bufferHandle()); gr_bind_uniform_buffer(uniform_block_type::Matrices, matrix_buffer.getBufferOffset(0), sizeof(graphics::matrix_uniforms), matrix_buffer.bufferHandle()); size_t element_index = 0; vertex_layout vertex_declare; vertex_declare.add_vertex_component(vertex_format_data::POSITION3, sizeof(float) * 3, 0); for (size_t i = 0; iget_enabled() && !override_fog; if (bDrawFullNeb) { GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE); gr_zbuffer_set(GR_ZBUFF_NONE); opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_SCENE_FOG, 0)); GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_composite_texture); GL_state.Texture.Enable(1, GL_TEXTURE_2D, Scene_depth_texture); float fog_near, fog_density; neb2_get_adjusted_fog_values(&fog_near, &fog_density); unsigned char r, g, b; neb2_get_fog_color(&r, &g, &b); Current_shader->program->Uniforms.setTextureUniform("tex", 0); Current_shader->program->Uniforms.setTextureUniform("depth_tex", 1); opengl_set_generic_uniform_data([&](graphics::generic_data::fog_data* data) { data->fog_start = fog_near; data->fog_density = fog_density; data->fog_color.xyz.x = r / 255.f; data->fog_color.xyz.y = g / 255.f; data->fog_color.xyz.z = b / 255.f; data->zNear = Min_draw_distance; data->zFar = Max_draw_distance; data->clip_inf_dist = Neb2_fog_skybox_clip_distance; data->clip_dist = Neb2_fog_clip_distance; }); opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f); if (bDrawNebVolumetrics) { glReadBuffer(GL_COLOR_ATTACHMENT0); glDrawBuffer(GL_COLOR_ATTACHMENT5); glBlitFramebuffer(0, 0, gr_screen.max_w, gr_screen.max_h, 0, 0, gr_screen.max_w, gr_screen.max_h, GL_COLOR_BUFFER_BIT, GL_NEAREST); glDrawBuffer(GL_COLOR_ATTACHMENT0); glReadBuffer(GL_COLOR_ATTACHMENT0); } } if (bDrawNebVolumetrics) { GR_DEBUG_SCOPE("Volumetric Nebulae"); TRACE_SCOPE(tracing::Volumetrics); const volumetric_nebula& neb = *The_mission.volumetrics; Assertion(neb.isVolumeBitmapValid(), "The volumetric nebula was not properly initialized!"); gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance); gr_set_view_matrix(&Eye_position, &Eye_matrix); GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE); gr_zbuffer_set(GR_ZBUFF_NONE); opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_VOLUMETRIC_FOG, (neb.getEdgeSmoothing() ? SDR_FLAG_VOLUMETRICS_DO_EDGE_SMOOTHING : 0) | (neb.getNoiseActive() ? SDR_FLAG_VOLUMETRICS_NOISE : 0) )); GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_composite_texture); GL_state.Texture.Enable(1, GL_TEXTURE_2D, Scene_emissive_texture); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); GL_state.Texture.Enable(2, GL_TEXTURE_2D, Scene_depth_texture); { //The following are not required, but the graphics API still returns them float u_scale, v_scale; uint32_t array_index; gr_set_texture_addressing(TMAP_ADDRESS_CLAMP); // only set the filter parameter if the bind succeeded, or else it is applied to whatever // texture was previously bound to this unit if (gr_opengl_tcache_set(neb.getVolumeBitmapHandle(), TCACHE_TYPE_3DTEX, &u_scale, &v_scale, &array_index, 3)) { glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } if (neb.getNoiseActive()) { gr_set_texture_addressing(TMAP_ADDRESS_WRAP); if (gr_opengl_tcache_set(neb.getNoiseVolumeBitmapHandle(), TCACHE_TYPE_3DTEX, &u_scale, &v_scale, &array_index, 4)) { glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } } } opengl_set_generic_uniform_data([&](graphics::generic_data::volumetric_fog_data* data) { vm_inverse_matrix4(&data->p_inv, &gr_projection_matrix); vm_inverse_matrix4(&data->v_inv, &gr_view_matrix); data->zNear = Min_draw_distance; data->zFar = Max_draw_distance; data->cameraPos = Eye_position; data->globalLightDirection = global_light ? global_light->vec : vec3d(ZERO_VECTOR); data->globalLightDiffuse = global_light_diffuse; data->nebPos = neb.getPos(); data->nebSize = neb.getSize(); data->stepsize = neb.getStepsize(); data->opacitydistance = neb.getOpacityDistance(); data->alphalimit = neb.getAlphaLim(); data->nebColor[0] = std::get<0>(neb.getNebulaColor()); data->nebColor[1] = std::get<1>(neb.getNebulaColor()); data->nebColor[2] = std::get<2>(neb.getNebulaColor()); data->udfScale = neb.getUDFScale(); data->emissiveSpreadFactor = neb.getEmissiveSpread(); data->emissiveIntensity = neb.getEmissiveIntensity(); data->emissiveFalloff = neb.getEmissiveFalloff(); data->henyeyGreensteinCoeff = neb.getHenyeyGreensteinCoeff(); data->directionalLightSampleSteps = neb.getGlobalLightSteps(); data->directionalLightStepSize = neb.getGlobalLightStepsize(); data->noiseColor[0] = std::get<0>(neb.getNoiseColor()); data->noiseColor[1] = std::get<1>(neb.getNoiseColor()); data->noiseColor[2] = std::get<2>(neb.getNoiseColor()); data->noiseColorScale1 = std::get<0>(neb.getNoiseColorScale()); data->noiseColorScale2 = std::get<1>(neb.getNoiseColorScale()); data->noiseColorIntensity = neb.getNoiseColorIntensity(); data->aspect = gr_screen.clip_aspect; data->fov = g3_get_hfov(Proj_fov); }); { GR_DEBUG_SCOPE("Volumetric Nebulae Draw"); opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f); } GL_state.Texture.Enable(Scene_emissive_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gr_end_view_matrix(); gr_end_proj_matrix(); } if(!bDrawFullNeb && !bDrawNebVolumetrics) { // Transfer the resolved lighting back to the color texture // TODO: Maybe this could be improved so that it doesn't require the copy back operation? glReadBuffer(GL_COLOR_ATTACHMENT5); glBlitFramebuffer(0, 0, gr_screen.max_w, gr_screen.max_h, 0, 0, gr_screen.max_w, gr_screen.max_h, GL_COLOR_BUFFER_BIT, GL_NEAREST); glReadBuffer(GL_COLOR_ATTACHMENT0); } gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance); gr_set_view_matrix(&Eye_position, &Eye_matrix); // reset state gr_clear_states(); } void gr_opengl_override_fog(bool set_override) { override_fog = set_override; } void gr_opengl_draw_deferred_light_sphere(const vec3d *position) { g3_start_instance_matrix(position, &vmd_identity_matrix, true); gr_matrix_set_uniforms(); opengl_draw_sphere(); g3_done_instance(true); } void gr_opengl_deferred_light_cylinder_init(int segments) { auto mesh = graphics::util::generate_cylinder_mesh(segments); deferred_light_cylinder_vcount = static_cast(mesh.vertex_count); deferred_light_cylinder_icount = mesh.index_count; glGetError(); glGenBuffers(1, &deferred_light_cylinder_vbo); // make sure we have one if (deferred_light_cylinder_vbo) { glBindBuffer(GL_ARRAY_BUFFER, deferred_light_cylinder_vbo); glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(float), mesh.vertices.data(), GL_STATIC_DRAW); // just in case if ( opengl_check_for_errors() ) { glDeleteBuffers(1, &deferred_light_cylinder_vbo); deferred_light_cylinder_vbo = 0; return; } glBindBuffer(GL_ARRAY_BUFFER, 0); } glGenBuffers(1, &deferred_light_cylinder_ibo); // make sure we have one if (deferred_light_cylinder_ibo) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, deferred_light_cylinder_ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices.size() * sizeof(ushort), mesh.indices.data(), GL_STATIC_DRAW); // just in case if ( opengl_check_for_errors() ) { glDeleteBuffers(1, &deferred_light_cylinder_ibo); deferred_light_cylinder_ibo = 0; return; } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } } void gr_opengl_deferred_light_sphere_init(int rings, int segments) { auto mesh = graphics::util::generate_sphere_mesh(rings, segments); deferred_light_sphere_vcount = static_cast(mesh.vertex_count); deferred_light_sphere_icount = mesh.index_count; glGetError(); glGenBuffers(1, &deferred_light_sphere_vbo); // make sure we have one if (deferred_light_sphere_vbo) { glBindBuffer(GL_ARRAY_BUFFER, deferred_light_sphere_vbo); glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(float), mesh.vertices.data(), GL_STATIC_DRAW); // just in case if ( opengl_check_for_errors() ) { glDeleteBuffers(1, &deferred_light_sphere_vbo); deferred_light_sphere_vbo = 0; return; } glBindBuffer(GL_ARRAY_BUFFER, 0); } glGenBuffers(1, &deferred_light_sphere_ibo); // make sure we have one if (deferred_light_sphere_ibo) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, deferred_light_sphere_ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices.size() * sizeof(ushort), mesh.indices.data(), GL_STATIC_DRAW); // just in case if ( opengl_check_for_errors() ) { glDeleteBuffers(1, &deferred_light_sphere_ibo); deferred_light_sphere_ibo = 0; return; } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } } void opengl_draw_sphere() { vertex_layout vertex_declare; vertex_declare.add_vertex_component(vertex_format_data::POSITION3, sizeof(float) * 3, 0); opengl_bind_vertex_layout(vertex_declare, deferred_light_sphere_vbo, deferred_light_sphere_ibo); glDrawRangeElements(GL_TRIANGLES, 0, deferred_light_sphere_vcount, deferred_light_sphere_icount, GL_UNSIGNED_SHORT, 0); } void gr_opengl_draw_deferred_light_cylinder(const vec3d *position, const matrix *orient) { g3_start_instance_matrix(position, orient, true); gr_matrix_set_uniforms(); vertex_layout vertex_declare; vertex_declare.add_vertex_component(vertex_format_data::POSITION3, sizeof(float) * 3, 0); opengl_bind_vertex_layout(vertex_declare, deferred_light_cylinder_vbo, deferred_light_cylinder_ibo); glDrawRangeElements(GL_TRIANGLES, 0, deferred_light_cylinder_vcount, deferred_light_cylinder_icount, GL_UNSIGNED_SHORT, 0); g3_done_instance(true); }