mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-08 17:48:46 -10:00
610 lines
18 KiB
C++
610 lines
18 KiB
C++
|
|
|
|
#include "gropengldeferred.h"
|
|
|
|
#include "ShaderProgram.h"
|
|
#include "gropengldraw.h"
|
|
#include "gropenglstate.h"
|
|
#include "gropengltnl.h"
|
|
|
|
#include "graphics/2d.h"
|
|
#include "graphics/matrix.h"
|
|
#include "graphics/util/UniformAligner.h"
|
|
#include "graphics/util/UniformBuffer.h"
|
|
#include "graphics/util/uniform_structs.h"
|
|
#include "lighting/lighting.h"
|
|
#include "mission/mission_flags.h"
|
|
#include "mission/missionparse.h"
|
|
#include "nebula/neb.h"
|
|
#include "render/3d.h"
|
|
#include "tracing/tracing.h"
|
|
|
|
#include <math/bitarray.h>
|
|
|
|
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()
|
|
{
|
|
if ( Cmdline_no_deferred_lighting)
|
|
return;
|
|
|
|
GR_DEBUG_SCOPE("Deferred lighting begin");
|
|
|
|
Deferred_lighting = true;
|
|
GL_state.ColorMask(true, true, true, true);
|
|
|
|
// 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);
|
|
|
|
GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4 };
|
|
glDrawBuffers(5, buffers);
|
|
|
|
static const float black[] = { 0, 0, 0, 1.0f };
|
|
glClearBufferfv(GL_COLOR, 0, black);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
extern SCP_vector<light> Lights;
|
|
extern int Num_lights;
|
|
extern float static_point_factor;
|
|
extern float static_light_factor;
|
|
extern float static_tube_factor;
|
|
|
|
void gr_opengl_deferred_lighting_finish()
|
|
{
|
|
GR_DEBUG_SCOPE("Deferred lighting finish");
|
|
TRACE_SCOPE(tracing::ApplyLights);
|
|
|
|
if (Cmdline_no_deferred_lighting) {
|
|
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, 0));
|
|
|
|
// Render on top of the emissive buffer texture
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT4);
|
|
|
|
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_texture);
|
|
}
|
|
|
|
// 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 = 0;
|
|
for (auto& l : Lights) {
|
|
++num_data_elements;
|
|
if (l.type == Light_Type::Tube) {
|
|
++num_data_elements;
|
|
}
|
|
}
|
|
|
|
// Get a uniform buffer for our data
|
|
auto buffer = gr_get_uniform_buffer(uniform_block_type::Lights, num_data_elements);
|
|
auto& uniformAligner = buffer.aligner();
|
|
|
|
{
|
|
GR_DEBUG_SCOPE("Build buffer data");
|
|
|
|
auto header = uniformAligner.getHeader<deferred_global_data>();
|
|
if (Shadow_quality != ShadowQuality::Disabled) {
|
|
// Avoid this overhead when we are not going to use these values
|
|
header->shadow_mv_matrix = Shadow_view_matrix;
|
|
for (size_t i = 0; i < MAX_SHADOW_CASCADES; ++i) {
|
|
header->shadow_proj_matrix[i] = Shadow_proj_matrix[i];
|
|
}
|
|
header->veryneardist = Shadow_cascade_distances[0];
|
|
header->neardist = Shadow_cascade_distances[1];
|
|
header->middist = Shadow_cascade_distances[2];
|
|
header->fardist = Shadow_cascade_distances[3];
|
|
|
|
vm_inverse_matrix4(&header->inv_view_matrix, &gr_view_matrix);
|
|
}
|
|
|
|
header->invScreenWidth = 1.0f / gr_screen.max_w;
|
|
header->invScreenHeight = 1.0f / gr_screen.max_h;
|
|
|
|
// 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 : Lights) {
|
|
auto light_data = uniformAligner.addTypedElement<deferred_light_data>();
|
|
|
|
light_data->lightType = static_cast<int>(l.type);
|
|
|
|
vec3d diffuse;
|
|
diffuse.xyz.x = l.r * l.intensity;
|
|
diffuse.xyz.y = l.g * l.intensity;
|
|
diffuse.xyz.z = l.b * l.intensity;
|
|
|
|
vec3d spec;
|
|
spec.xyz.x = l.spec_r * l.intensity;
|
|
spec.xyz.y = l.spec_g * l.intensity;
|
|
spec.xyz.z = l.spec_b * l.intensity;
|
|
|
|
light_data->diffuseLightColor = diffuse;
|
|
light_data->specLightColor = spec;
|
|
|
|
// Set a default value for all lights. Only the first directional light will change this.
|
|
light_data->enable_shadows = false;
|
|
|
|
switch (l.type) {
|
|
case Light_Type::Directional:
|
|
if (Shadow_quality != ShadowQuality::Disabled) {
|
|
light_data->enable_shadows = first_directional ? 1 : 0;
|
|
}
|
|
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;
|
|
|
|
vm_vec_scale(&light_data->specLightColor, static_light_factor);
|
|
|
|
first_directional = false;
|
|
break;
|
|
case Light_Type::Cone:
|
|
light_data->dualCone = l.dual_cone ? 1.0f : 0.0f;
|
|
light_data->coneAngle = l.cone_angle;
|
|
light_data->coneInnerAngle = l.cone_inner_angle;
|
|
light_data->coneDir = l.vec2;
|
|
FALLTHROUGH;
|
|
case Light_Type::Point:
|
|
vm_vec_scale(&light_data->specLightColor, static_point_factor);
|
|
|
|
light_data->lightRadius = MAX(l.rada, l.radb) * 1.25f;
|
|
light_data->scale.xyz.x = MAX(l.rada, l.radb) * 1.28f;
|
|
light_data->scale.xyz.y = MAX(l.rada, l.radb) * 1.28f;
|
|
light_data->scale.xyz.z = MAX(l.rada, l.radb) * 1.28f;
|
|
break;
|
|
case Light_Type::Tube: {
|
|
light_data->lightRadius = l.radb * 1.5f;
|
|
light_data->lightType = LT_TUBE;
|
|
|
|
vec3d a;
|
|
vm_vec_sub(&a, &l.vec, &l.vec2);
|
|
auto length = vm_vec_mag(&a);
|
|
|
|
light_data->scale.xyz.x = l.radb * 1.53f;
|
|
light_data->scale.xyz.y = l.radb * 1.53f;
|
|
light_data->scale.xyz.z = length;
|
|
|
|
vm_vec_scale(&light_data->specLightColor, static_tube_factor);
|
|
|
|
// Tube lights consist of two different types of lights with almost the same properties
|
|
light_data = uniformAligner.addTypedElement<deferred_light_data>();
|
|
light_data->diffuseLightColor = diffuse;
|
|
light_data->specLightColor = spec;
|
|
|
|
vm_vec_scale(&light_data->specLightColor, static_tube_factor);
|
|
|
|
light_data->lightRadius = l.radb * 1.5f;
|
|
light_data->lightType = LT_POINT;
|
|
|
|
light_data->scale.xyz.x = l.radb * 1.53f;
|
|
light_data->scale.xyz.y = l.radb * 1.53f;
|
|
light_data->scale.xyz.z = l.radb * 1.53f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Uniform data has been assembled, upload it to the GPU and issue the draw calls
|
|
buffer.submitData();
|
|
}
|
|
{
|
|
GR_DEBUG_SCOPE("Render light geometry");
|
|
gr_bind_uniform_buffer(uniform_block_type::DeferredGlobals, buffer.getBufferOffset(0),
|
|
sizeof(graphics::deferred_global_data), buffer.bufferHandle());
|
|
|
|
size_t element_index = 0;
|
|
for (auto& l : Lights) {
|
|
GR_DEBUG_SCOPE("Deferred apply single light");
|
|
|
|
switch (l.type) {
|
|
case Light_Type::Directional:
|
|
gr_bind_uniform_buffer(uniform_block_type::Lights, buffer.getAlignerElementOffset(element_index),
|
|
sizeof(graphics::deferred_light_data), buffer.bufferHandle());
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f);
|
|
++element_index;
|
|
break;
|
|
case Light_Type::Cone:
|
|
case Light_Type::Point:
|
|
gr_bind_uniform_buffer(uniform_block_type::Lights, buffer.getAlignerElementOffset(element_index),
|
|
sizeof(graphics::deferred_light_data), buffer.bufferHandle());
|
|
gr_opengl_draw_deferred_light_sphere(&l.vec);
|
|
++element_index;
|
|
break;
|
|
case Light_Type::Tube:
|
|
gr_bind_uniform_buffer(uniform_block_type::Lights, buffer.getAlignerElementOffset(element_index),
|
|
sizeof(graphics::deferred_light_data), buffer.bufferHandle());
|
|
|
|
vec3d a;
|
|
matrix orient;
|
|
vm_vec_sub(&a, &l.vec, &l.vec2);
|
|
vm_vector_2_matrix(&orient, &a, NULL, NULL);
|
|
|
|
gr_opengl_draw_deferred_light_cylinder(&l.vec2, &orient);
|
|
++element_index;
|
|
|
|
// The next two draws use the same uniform block element
|
|
gr_bind_uniform_buffer(uniform_block_type::Lights, buffer.getAlignerElementOffset(element_index),
|
|
sizeof(graphics::deferred_light_data), buffer.bufferHandle());
|
|
|
|
gr_opengl_draw_deferred_light_sphere(&l.vec);
|
|
gr_opengl_draw_deferred_light_sphere(&l.vec2);
|
|
++element_index;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
gr_end_view_matrix();
|
|
gr_end_proj_matrix();
|
|
|
|
// Now reset back to drawing into the color buffer
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
|
if (The_mission.flags[Mission::Mission_Flags::Fullneb] && Neb2_render_mode != NEB2_RENDER_NONE) {
|
|
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_emissive_texture);
|
|
GL_state.Texture.Enable(1, GL_TEXTURE_2D, Scene_depth_texture);
|
|
|
|
float fog_near, fog_far;
|
|
neb2_get_adjusted_fog_values(&fog_near, &fog_far, nullptr);
|
|
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>([&](graphics::generic_data::fog_data* data) {
|
|
data->fog_start = fog_near;
|
|
data->fog_scale = 1.0f / (fog_far - fog_near);
|
|
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;
|
|
});
|
|
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f);
|
|
} else {
|
|
// 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_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);
|
|
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_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);
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
void gr_opengl_deferred_light_cylinder_init(int segments) // Generate a VBO of a cylinder of radius and height 1.0f, based on code at http://www.ogre3d.org/tikiwiki/ManualSphereMeshes
|
|
{
|
|
unsigned int nVertex = (segments + 1) * 2 * 3 + 6; // Can someone verify this?
|
|
unsigned int nIndex = deferred_light_cylinder_icount = 12 * (segments + 1) - 6; //This too
|
|
float *Vertices = (float*)vm_malloc(sizeof(float) * nVertex);
|
|
float *pVertex = Vertices;
|
|
ushort *Indices = (ushort*)vm_malloc(sizeof(ushort) * nIndex);
|
|
ushort *pIndex = Indices;
|
|
|
|
float fDeltaSegAngle = (2.0f * PI / segments);
|
|
unsigned short wVerticeIndex = 0 ;
|
|
|
|
*pVertex++ = 0.0f;
|
|
*pVertex++ = 0.0f;
|
|
*pVertex++ = 0.0f;
|
|
wVerticeIndex ++;
|
|
*pVertex++ = 0.0f;
|
|
*pVertex++ = 0.0f;
|
|
*pVertex++ = 1.0f;
|
|
wVerticeIndex ++;
|
|
|
|
for( int ring = 0; ring <= 1; ring++ ) {
|
|
float z0 = (float)ring;
|
|
|
|
// Generate the group of segments for the current ring
|
|
for(int seg = 0; seg <= segments; seg++) {
|
|
float x0 = sinf(seg * fDeltaSegAngle);
|
|
float y0 = cosf(seg * fDeltaSegAngle);
|
|
|
|
// Add one vertex to the strip which makes up the cylinder
|
|
*pVertex++ = x0;
|
|
*pVertex++ = y0;
|
|
*pVertex++ = z0;
|
|
|
|
if (!ring) {
|
|
*pIndex++ = wVerticeIndex + (ushort)segments + 1;
|
|
*pIndex++ = wVerticeIndex;
|
|
*pIndex++ = wVerticeIndex + (ushort)segments;
|
|
*pIndex++ = wVerticeIndex + (ushort)segments + 1;
|
|
*pIndex++ = wVerticeIndex + 1;
|
|
*pIndex++ = wVerticeIndex;
|
|
if(seg != segments)
|
|
{
|
|
*pIndex++ = wVerticeIndex + 1;
|
|
*pIndex++ = wVerticeIndex;
|
|
*pIndex++ = 0;
|
|
}
|
|
wVerticeIndex ++;
|
|
}
|
|
else
|
|
{
|
|
if(seg != segments)
|
|
{
|
|
*pIndex++ = wVerticeIndex + 1;
|
|
*pIndex++ = wVerticeIndex;
|
|
*pIndex++ = 1;
|
|
wVerticeIndex ++;
|
|
}
|
|
}
|
|
}; // end for seg
|
|
} // end for ring
|
|
|
|
deferred_light_cylinder_vcount = wVerticeIndex;
|
|
|
|
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, nVertex * sizeof(float), Vertices, 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);
|
|
|
|
vm_free(Vertices);
|
|
Vertices = NULL;
|
|
}
|
|
|
|
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, nIndex * sizeof(ushort), Indices, 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);
|
|
|
|
vm_free(Indices);
|
|
Indices = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
void gr_opengl_deferred_light_sphere_init(int rings, int segments) // Generate a VBO of a sphere of radius 1.0f, based on code at http://www.ogre3d.org/tikiwiki/ManualSphereMeshes
|
|
{
|
|
unsigned int nVertex = (rings + 1) * (segments+1) * 3;
|
|
unsigned int nIndex = deferred_light_sphere_icount = 6 * rings * (segments + 1);
|
|
float *Vertices = (float*)vm_malloc(sizeof(float) * nVertex);
|
|
float *pVertex = Vertices;
|
|
ushort *Indices = (ushort*)vm_malloc(sizeof(ushort) * nIndex);
|
|
ushort *pIndex = Indices;
|
|
|
|
float fDeltaRingAngle = (PI / rings);
|
|
float fDeltaSegAngle = (2.0f * PI / segments);
|
|
unsigned short wVerticeIndex = 0 ;
|
|
|
|
// Generate the group of rings for the sphere
|
|
for( int ring = 0; ring <= rings; ring++ ) {
|
|
float r0 = sinf (ring * fDeltaRingAngle);
|
|
float y0 = cosf (ring * fDeltaRingAngle);
|
|
|
|
// Generate the group of segments for the current ring
|
|
for(int seg = 0; seg <= segments; seg++) {
|
|
float x0 = r0 * sinf(seg * fDeltaSegAngle);
|
|
float z0 = r0 * cosf(seg * fDeltaSegAngle);
|
|
|
|
// Add one vertex to the strip which makes up the sphere
|
|
*pVertex++ = x0;
|
|
*pVertex++ = y0;
|
|
*pVertex++ = z0;
|
|
|
|
if (ring != rings) {
|
|
// each vertex (except the last) has six indices pointing to it
|
|
*pIndex++ = wVerticeIndex + (ushort)segments + 1;
|
|
*pIndex++ = wVerticeIndex;
|
|
*pIndex++ = wVerticeIndex + (ushort)segments;
|
|
*pIndex++ = wVerticeIndex + (ushort)segments + 1;
|
|
*pIndex++ = wVerticeIndex + 1;
|
|
*pIndex++ = wVerticeIndex;
|
|
wVerticeIndex ++;
|
|
}
|
|
}; // end for seg
|
|
} // end for ring
|
|
|
|
deferred_light_sphere_vcount = wVerticeIndex;
|
|
|
|
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, nVertex * sizeof(float), Vertices, 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);
|
|
|
|
vm_free(Vertices);
|
|
Vertices = NULL;
|
|
}
|
|
|
|
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, nIndex * sizeof(ushort), Indices, 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);
|
|
|
|
vm_free(Indices);
|
|
Indices = NULL;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|