mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 17:52:08 -10:00
The shadow-mapped path's penumbra width was a fixed per-cascade value (shadow_smoothness_scale()'s ceiling), so every occluder blurred its shadow by the same amount regardless of how close it actually was to the receiver -- unlike the raytraced path, which gets that for free from traceShadowRayCone()'s real occluder distance. Close that gap with PCSS: search nearby texels for occluders, then scale the filter radius by how far away they were. The blocker search (pcssBlockerSearch() in shadows.sdr) needs raw, uncompared depth reads, which the existing PCF sampler can't do -- it's bound with a compare mode for the hardware shadow-compare instructions the filtered sampling itself relies on. So shadow maps now get a second sampler, shadow_map_raw, bound to the same texture with compare mode off; on OpenGL that needs a second sampler object and GL 3.3 (shadow_contact_hardening_supported()), Vulkan can always bind it. Where the raw sampler isn't available, or the new Graphics.ShadowContactHardening option is off, shadows_start_render() sets Shadow_penumbra_scale[cascade] to -1 as a sentinel and pcssPenumbraRadius() (shadows.sdr) falls straight back to the fixed radius, bit-identical to the pre-contact-hardening behavior and skipping the extra texture reads. When it is on, shadows_start_render() computes each cascade's penumbra scale from its own frustum extents and the sun's tanθ; the shader turns that plus the blocker search's average depth into a per-pixel radius, clamped to [1 texel, the old fixed ceiling]. A blocker search that finds nothing doesn't fall back to "no shadow" -- a sparse 8-tap search can miss a thin occluder, so it instead runs the full filter at the hardest (1-texel) radius, trading a touch of extra blur for not popping a real shadow to nothing. Drops RT_SHADOW_SUN_SIZE_CALIBRATION accordingly: that constant existed only to fake a blocker-distance-driven penumbra on the shadow-mapped path by pre-scaling the raytraced cone to match it, and the shadow-mapped path now has a real blocker search of its own, so the raytraced cone goes back to its physically correct size.
932 lines
22 KiB
C++
932 lines
22 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
|
|
#include "gropenglshader.h"
|
|
#include "graphics/material.h"
|
|
#include "gropenglstate.h"
|
|
#include "math/vecmat.h"
|
|
#ifdef USE_OPENGL_ES
|
|
#include "es_compatibility.h"
|
|
#endif
|
|
|
|
extern GLfloat GL_max_anisotropy;
|
|
|
|
|
|
opengl_state GL_state;
|
|
|
|
|
|
opengl_texture_state::~opengl_texture_state()
|
|
{
|
|
if (units != NULL) {
|
|
vm_free(units);
|
|
}
|
|
}
|
|
|
|
void opengl_texture_state::init(GLuint n_units)
|
|
{
|
|
Assert( n_units > 0 );
|
|
units = (opengl_texture_unit*) vm_malloc(n_units * sizeof(opengl_texture_unit));
|
|
num_texture_units = n_units;
|
|
|
|
for (unsigned int unit = 0; unit < num_texture_units; unit++) {
|
|
units[unit].enabled = GL_FALSE;
|
|
units[unit].bound_sampler = 0;
|
|
|
|
default_values(unit);
|
|
|
|
glActiveTexture(GL_TEXTURE0 + unit);
|
|
}
|
|
|
|
SetActiveUnit();
|
|
}
|
|
|
|
void opengl_texture_state::default_values(GLint unit, GLenum target)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + unit);
|
|
|
|
if (target == GL_INVALID_ENUM) {
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
|
|
|
units[unit].texture_target = GL_TEXTURE_2D;
|
|
units[unit].texture_id = 0;
|
|
}
|
|
}
|
|
|
|
void opengl_texture_state::SetTarget(GLenum tex_target)
|
|
{
|
|
if (units[active_texture_unit].texture_target != tex_target) {
|
|
|
|
if (units[active_texture_unit].texture_id) {
|
|
glBindTexture(units[active_texture_unit].texture_target, 0);
|
|
units[active_texture_unit].texture_id = 0;
|
|
}
|
|
|
|
// reset modes, since those were only valid for the previous texture target
|
|
default_values(active_texture_unit, tex_target);
|
|
units[active_texture_unit].texture_target = tex_target;
|
|
}
|
|
}
|
|
|
|
void opengl_texture_state::SetActiveUnit(GLuint id)
|
|
{
|
|
if (id >= num_texture_units) {
|
|
Int3();
|
|
id = 0;
|
|
}
|
|
|
|
glActiveTexture(GL_TEXTURE0 + id);
|
|
|
|
active_texture_unit = id;
|
|
}
|
|
|
|
void opengl_texture_state::Enable(GLuint tex_id)
|
|
{
|
|
if ( units[active_texture_unit].texture_id == tex_id ) {
|
|
return;
|
|
}
|
|
|
|
if (units[active_texture_unit].texture_id != tex_id) {
|
|
glBindTexture(units[active_texture_unit].texture_target, tex_id);
|
|
units[active_texture_unit].texture_id = tex_id;
|
|
}
|
|
}
|
|
|
|
void opengl_texture_state::Enable(GLuint unit, GLenum tex_target, GLuint tex_id) {
|
|
Assertion(unit < num_texture_units, "Invalid texture unit value!");
|
|
|
|
if (units[unit].texture_target == tex_target && units[unit].texture_id == tex_id) {
|
|
// The texture unit already uses this texture. There is no need to change it
|
|
return;
|
|
}
|
|
|
|
// Go the standard route
|
|
SetActiveUnit(unit);
|
|
SetTarget(tex_target);
|
|
Enable(tex_id);
|
|
}
|
|
|
|
void opengl_texture_state::BindSampler(GLuint unit, GLuint sampler)
|
|
{
|
|
Assertion(unit < num_texture_units, "Invalid texture unit value!");
|
|
|
|
if (units[unit].bound_sampler == sampler) {
|
|
return;
|
|
}
|
|
|
|
glBindSampler(unit, sampler);
|
|
units[unit].bound_sampler = sampler;
|
|
}
|
|
|
|
void opengl_texture_state::Delete(GLuint tex_id)
|
|
{
|
|
if (tex_id == 0) {
|
|
Int3();
|
|
return;
|
|
}
|
|
|
|
GLuint atu_save = active_texture_unit;
|
|
|
|
for (unsigned int i = 0; i < num_texture_units; i++) {
|
|
if (units[i].texture_id == tex_id) {
|
|
SetActiveUnit(i);
|
|
|
|
glBindTexture(units[i].texture_target, 0);
|
|
units[i].texture_id = 0;
|
|
|
|
default_values(i, units[i].texture_target);
|
|
|
|
if (i == atu_save) {
|
|
atu_save = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
SetActiveUnit(atu_save);
|
|
}
|
|
|
|
void opengl_state::init()
|
|
{
|
|
int i;
|
|
|
|
glDisable(GL_BLEND);
|
|
blend_Status = GL_FALSE;
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
depthtest_Status = GL_FALSE;
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
|
scissortest_Status = GL_FALSE;
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
cullface_Status = GL_FALSE;
|
|
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
polygonoffsetfill_Status = GL_FALSE;
|
|
|
|
polygon_offset_Factor = 0.0f;
|
|
polygon_offset_Unit = 0.0f;
|
|
|
|
normalize_Status = GL_FALSE;
|
|
|
|
for (i = 0; i < (int)(sizeof(clipplane_Status) / sizeof(GLboolean)); i++) {
|
|
//glDisable(GL_CLIP_PLANE0+i);
|
|
clipplane_Status[i] = GL_FALSE;
|
|
}
|
|
|
|
for (i = 0; i < (int)(sizeof(clipdistance_Status) / sizeof(GLboolean)); i++) {
|
|
//glDisable(GL_CLIP_DISTANCE0+i);
|
|
clipdistance_Status[i] = GL_FALSE;
|
|
}
|
|
|
|
glDepthMask(GL_FALSE);
|
|
depthmask_Status = GL_FALSE;
|
|
|
|
glFrontFace(GL_CCW);
|
|
frontface_Value = GL_CCW;
|
|
|
|
glCullFace(GL_BACK);
|
|
cullface_Value = GL_BACK;
|
|
|
|
glBlendFunc(GL_ONE, GL_ZERO);
|
|
blendfunc_Value.first = GL_ONE;
|
|
blendfunc_Value.first = GL_ZERO;
|
|
buffer_blendfunc_Value.fill(blendfunc_Value);
|
|
|
|
glDepthFunc(GL_LESS);
|
|
depthfunc_Value = GL_LESS;
|
|
|
|
glGetFloatv(GL_LINE_WIDTH, &line_width_Value);
|
|
|
|
current_program = 0;
|
|
glUseProgram(0);
|
|
|
|
current_framebuffer = {0, 0};
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
framebuffer_stack.clear();
|
|
|
|
stencilFunc = GL_ALWAYS;
|
|
stencilFuncRef = 0;
|
|
stencilFuncMask = 0xFFFFFFFF;
|
|
glStencilFunc(stencilFunc, stencilFuncRef, stencilFuncMask);
|
|
|
|
stencilMask = 0xFFFFFFFF;
|
|
glStencilMask(stencilMask);
|
|
|
|
stencilOpFrontStencilFail = GL_KEEP;
|
|
stencilOpFrontDepthFail = GL_KEEP;
|
|
stencilOpFrontPass = GL_KEEP;
|
|
|
|
stencilOpBackStencilFail = GL_KEEP;
|
|
stencilOpBackDepthFail = GL_KEEP;
|
|
stencilOpBackPass = GL_KEEP;
|
|
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
|
|
|
colormask_Status.x = true;
|
|
colormask_Status.y = true;
|
|
colormask_Status.z = true;
|
|
colormask_Status.w = true;
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
}
|
|
|
|
GLboolean opengl_state::Blend(GLint state)
|
|
{
|
|
GLboolean save_state = blend_Status;
|
|
|
|
if ( !((state == -1) || (state == blend_Status)) ) {
|
|
if (state) {
|
|
Assert( state == GL_TRUE );
|
|
glEnable(GL_BLEND);
|
|
blend_Status = GL_TRUE;
|
|
} else {
|
|
glDisable(GL_BLEND);
|
|
blend_Status = GL_FALSE;
|
|
}
|
|
}
|
|
|
|
return save_state;
|
|
}
|
|
|
|
GLboolean opengl_state::DepthTest(GLint state)
|
|
{
|
|
GLboolean save_state = depthtest_Status;
|
|
|
|
if ( !((state == -1) || (state == depthtest_Status)) ) {
|
|
if (state) {
|
|
Assert( state == GL_TRUE );
|
|
glEnable(GL_DEPTH_TEST);
|
|
depthtest_Status = GL_TRUE;
|
|
} else {
|
|
glDisable(GL_DEPTH_TEST);
|
|
depthtest_Status = GL_FALSE;
|
|
}
|
|
}
|
|
|
|
return save_state;
|
|
}
|
|
|
|
GLboolean opengl_state::ScissorTest(GLint state)
|
|
{
|
|
GLboolean save_state = scissortest_Status;
|
|
|
|
if ( !((state == -1) || (state == scissortest_Status)) ) {
|
|
if (state) {
|
|
Assert( state == GL_TRUE );
|
|
glEnable(GL_SCISSOR_TEST);
|
|
scissortest_Status = GL_TRUE;
|
|
} else {
|
|
glDisable(GL_SCISSOR_TEST);
|
|
scissortest_Status = GL_FALSE;
|
|
}
|
|
}
|
|
|
|
return save_state;
|
|
}
|
|
|
|
GLboolean opengl_state::StencilTest(GLint state)
|
|
{
|
|
GLboolean save_state = stenciltest_Status;
|
|
|
|
if ( !((state == -1) || (state == stenciltest_Status)) ) {
|
|
if (state) {
|
|
Assert( state == GL_TRUE );
|
|
glEnable(GL_STENCIL_TEST);
|
|
stenciltest_Status = GL_TRUE;
|
|
} else {
|
|
glDisable(GL_STENCIL_TEST);
|
|
stenciltest_Status = GL_FALSE;
|
|
}
|
|
}
|
|
|
|
return save_state;
|
|
}
|
|
|
|
GLboolean opengl_state::CullFace(GLint state)
|
|
{
|
|
GLboolean save_state = cullface_Status;
|
|
|
|
if ( !((state == -1) || (state == cullface_Status)) ) {
|
|
if (state) {
|
|
Assert( state == GL_TRUE );
|
|
glEnable(GL_CULL_FACE);
|
|
cullface_Status = GL_TRUE;
|
|
} else {
|
|
glDisable(GL_CULL_FACE);
|
|
cullface_Status = GL_FALSE;
|
|
}
|
|
}
|
|
|
|
return save_state;
|
|
}
|
|
|
|
void opengl_state::SetPolygonMode(GLenum face, GLenum mode)
|
|
{
|
|
if ( polygon_mode_Face != face || polygon_mode_Mode != mode ) {
|
|
glPolygonMode(face, mode);
|
|
|
|
polygon_mode_Face = face;
|
|
polygon_mode_Mode = mode;
|
|
}
|
|
}
|
|
|
|
void opengl_state::SetPolygonOffset(GLfloat factor, GLfloat units)
|
|
{
|
|
if ( polygon_offset_Factor != factor || polygon_offset_Unit != units) {
|
|
glPolygonOffset(factor, units);
|
|
|
|
polygon_offset_Factor = factor;
|
|
polygon_offset_Unit = units;
|
|
}
|
|
}
|
|
|
|
GLboolean opengl_state::PolygonOffsetFill(GLint state)
|
|
{
|
|
GLboolean save_state = polygonoffsetfill_Status;
|
|
|
|
if ( !((state == -1) || (state == polygonoffsetfill_Status)) ) {
|
|
if (state) {
|
|
Assert( state == GL_TRUE );
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
polygonoffsetfill_Status = GL_TRUE;
|
|
} else {
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
polygonoffsetfill_Status = GL_FALSE;
|
|
}
|
|
}
|
|
|
|
return save_state;
|
|
}
|
|
|
|
GLboolean opengl_state::ClipDistance(GLint num, bool state)
|
|
{
|
|
Assert( (num >= 0) && (num < (int)(sizeof(clipdistance_Status) / sizeof(GLboolean))) );
|
|
|
|
GLboolean save_state = clipdistance_Status[num];
|
|
|
|
#ifdef USE_OPENGL_ES
|
|
if (!GLAD_GL_EXT_clip_cull_distance) {
|
|
clipdistance_Status[num] = state;
|
|
return save_state;
|
|
}
|
|
#endif
|
|
|
|
if (state != clipdistance_Status[num]) {
|
|
if (state) {
|
|
glEnable(GL_CLIP_DISTANCE0+num);
|
|
} else {
|
|
glDisable(GL_CLIP_DISTANCE0+num);
|
|
}
|
|
clipdistance_Status[num] = state;
|
|
}
|
|
|
|
return save_state;
|
|
}
|
|
|
|
GLboolean opengl_state::DepthMask(GLint state)
|
|
{
|
|
GLboolean save_state = depthmask_Status;
|
|
|
|
if ( !((state == -1) || (state == depthmask_Status)) ) {
|
|
if (state) {
|
|
Assert( state == GL_TRUE );
|
|
glDepthMask(GL_TRUE);
|
|
depthmask_Status = GL_TRUE;
|
|
} else {
|
|
glDepthMask(GL_FALSE);
|
|
depthmask_Status = GL_FALSE;
|
|
}
|
|
}
|
|
|
|
return save_state;
|
|
}
|
|
|
|
bvec4 opengl_state::ColorMask(bool red, bool green, bool blue, bool alpha)
|
|
{
|
|
auto save_state = colormask_Status;
|
|
|
|
if (colormask_Status.x != red || colormask_Status.y != green || colormask_Status.z != blue
|
|
|| colormask_Status.w != alpha) {
|
|
glColorMask(red ? GL_TRUE : GL_FALSE,
|
|
green ? GL_TRUE : GL_FALSE,
|
|
blue ? GL_TRUE : GL_FALSE,
|
|
alpha ? GL_TRUE : GL_FALSE);
|
|
colormask_Status = { red, green, blue, alpha };
|
|
}
|
|
|
|
return save_state;
|
|
}
|
|
|
|
void opengl_state::BlendFunc(GLenum s_val, GLenum d_val)
|
|
{
|
|
if (s_val != blendfunc_Value.first || d_val != blendfunc_Value.second) {
|
|
glBlendFunc(s_val, d_val);
|
|
blendfunc_Value.first = s_val;
|
|
blendfunc_Value.second = d_val;
|
|
|
|
// This has set all the buffer blend modes as well
|
|
buffer_blendfunc_Value.fill(blendfunc_Value);
|
|
}
|
|
}
|
|
void opengl_state::BlendFunci(int buffer, GLenum s_val, GLenum d_val) {
|
|
Assertion(GLAD_GL_ARB_draw_buffers_blend != 0, "Buffer blend modes are not supported by this OpenGL implementation!");
|
|
Assertion(buffer >= 0 && buffer < (int) buffer_blendfunc_Value.size(), "Unsupported index %d specified for buffer blend mode!", buffer);
|
|
|
|
auto& state = buffer_blendfunc_Value[buffer];
|
|
if (state.first == s_val && state.second == d_val) {
|
|
// Already uses the correct blend mode
|
|
return;
|
|
}
|
|
|
|
glBlendFunciARB(buffer, s_val, d_val);
|
|
|
|
// Update the saved state
|
|
state.first = s_val;
|
|
state.second = d_val;
|
|
// Set the non-buffer values to an invalid value to make sure that the next call to BlendFunc resets the state.
|
|
blendfunc_Value.first = GL_INVALID_ENUM;
|
|
blendfunc_Value.second = GL_INVALID_ENUM;
|
|
}
|
|
|
|
void opengl_state::SetAlphaBlendMode(gr_alpha_blend ab)
|
|
{
|
|
switch (ab) {
|
|
case ALPHA_BLEND_ALPHA_BLEND_ALPHA:
|
|
GL_state.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
|
|
case ALPHA_BLEND_NONE:
|
|
GL_state.BlendFunc(GL_ONE, GL_ZERO);
|
|
break;
|
|
|
|
case ALPHA_BLEND_ADDITIVE:
|
|
GL_state.BlendFunc(GL_ONE, GL_ONE);
|
|
break;
|
|
|
|
case ALPHA_BLEND_ALPHA_ADDITIVE:
|
|
GL_state.BlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
break;
|
|
|
|
case ALPHA_BLEND_ALPHA_BLEND_SRC_COLOR:
|
|
GL_state.BlendFunc(/*GL_SRC_COLOR*/GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR);
|
|
break;
|
|
|
|
case ALPHA_BLEND_PREMULTIPLIED:
|
|
GL_state.BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
GL_state.Blend( (ab == ALPHA_BLEND_NONE) ? GL_FALSE : GL_TRUE );
|
|
}
|
|
|
|
void opengl_state::SetAlphaBlendModei(int buffer, gr_alpha_blend ab)
|
|
{
|
|
switch (ab) {
|
|
case ALPHA_BLEND_ALPHA_BLEND_ALPHA:
|
|
GL_state.BlendFunci(buffer, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
|
|
case ALPHA_BLEND_NONE:
|
|
GL_state.BlendFunci(buffer, GL_ONE, GL_ZERO);
|
|
break;
|
|
|
|
case ALPHA_BLEND_ADDITIVE:
|
|
GL_state.BlendFunci(buffer, GL_ONE, GL_ONE);
|
|
break;
|
|
|
|
case ALPHA_BLEND_ALPHA_ADDITIVE:
|
|
GL_state.BlendFunci(buffer, GL_SRC_ALPHA, GL_ONE);
|
|
break;
|
|
|
|
case ALPHA_BLEND_ALPHA_BLEND_SRC_COLOR:
|
|
GL_state.BlendFunci(buffer, /*GL_SRC_COLOR*/GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR);
|
|
break;
|
|
|
|
case ALPHA_BLEND_PREMULTIPLIED:
|
|
GL_state.BlendFunci(buffer, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void opengl_state::SetZbufferType(gr_zbuffer_type zt)
|
|
{
|
|
switch (zt) {
|
|
case ZBUFFER_TYPE_NONE:
|
|
GL_state.DepthFunc(GL_ALWAYS);
|
|
GL_state.DepthMask(GL_FALSE);
|
|
break;
|
|
|
|
case ZBUFFER_TYPE_READ:
|
|
GL_state.DepthFunc(GL_LESS);
|
|
GL_state.DepthMask(GL_FALSE);
|
|
break;
|
|
|
|
case ZBUFFER_TYPE_WRITE:
|
|
GL_state.DepthFunc(GL_ALWAYS);
|
|
GL_state.DepthMask(GL_TRUE);
|
|
break;
|
|
|
|
case ZBUFFER_TYPE_FULL:
|
|
GL_state.DepthFunc(GL_LESS);
|
|
GL_state.DepthMask(GL_TRUE);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
GL_state.DepthTest( (zt == ZBUFFER_TYPE_NONE) ? GL_FALSE : GL_TRUE );
|
|
}
|
|
|
|
void opengl_state::SetLineWidth(GLfloat width)
|
|
{
|
|
if ( width == line_width_Value ) {
|
|
return;
|
|
}
|
|
|
|
glLineWidth(width);
|
|
line_width_Value = width;
|
|
}
|
|
void opengl_state::UseProgram(GLuint program)
|
|
{
|
|
if (current_program == program) {
|
|
return;
|
|
}
|
|
|
|
current_program = program;
|
|
glUseProgram(program);
|
|
}
|
|
bool opengl_state::IsCurrentProgram(GLuint program) {
|
|
return current_program == program;
|
|
}
|
|
void opengl_state::BindFrameBuffer(GLuint name, GLenum mode) {
|
|
switch(mode) {
|
|
case GL_FRAMEBUFFER:
|
|
if (current_framebuffer.first != current_framebuffer.second || current_framebuffer.first != name) {
|
|
glBindFramebuffer(mode, name);
|
|
current_framebuffer = {name, name};
|
|
}
|
|
break;
|
|
case GL_READ_FRAMEBUFFER:
|
|
if (current_framebuffer.first != name) {
|
|
glBindFramebuffer(mode, name);
|
|
current_framebuffer.first = name;
|
|
}
|
|
break;
|
|
case GL_DRAW_FRAMEBUFFER:
|
|
if (current_framebuffer.second != name) {
|
|
glBindFramebuffer(mode, name);
|
|
current_framebuffer.second = name;
|
|
}
|
|
break;
|
|
default:
|
|
UNREACHABLE("Invalid BindFrameBuffer mode %d!", mode);
|
|
}
|
|
}
|
|
void opengl_state::BindFrameBufferBoth(GLuint read, GLuint draw) {
|
|
if (read != current_framebuffer.first) {
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, read);
|
|
current_framebuffer.first = read;
|
|
}
|
|
if (draw != current_framebuffer.second) {
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw);
|
|
current_framebuffer.second = draw;
|
|
}
|
|
}
|
|
void opengl_state::PushFramebufferState() {
|
|
framebuffer_stack.push_back(current_framebuffer);
|
|
}
|
|
void opengl_state::PopFramebufferState() {
|
|
Assertion(!framebuffer_stack.empty(), "Tried to pop the framebuffer state stack while it was empty!");
|
|
|
|
auto [restoreBufferRead, restoreBufferDraw] = framebuffer_stack.back();
|
|
framebuffer_stack.pop_back();
|
|
|
|
BindFrameBufferBoth(restoreBufferRead, restoreBufferDraw);
|
|
}
|
|
|
|
bool opengl_state::ValidForFlip() const {
|
|
//Check everything that may put the state into an invalid state for rendering
|
|
return framebuffer_stack.empty() && current_framebuffer.first == 0 && current_framebuffer.second == 0;
|
|
}
|
|
|
|
void opengl_state::BindVertexArray(GLuint vao) {
|
|
if (current_vao == vao) {
|
|
return;
|
|
}
|
|
|
|
glBindVertexArray(vao);
|
|
current_vao = vao;
|
|
|
|
Array.VertexArrayChanged();
|
|
}
|
|
void opengl_state::StencilFunc(GLenum func, GLint ref, GLuint mask) {
|
|
if (stencilFunc == func && stencilFuncRef == ref && stencilFuncMask == mask) {
|
|
return;
|
|
}
|
|
|
|
glStencilFunc(func, ref, mask);
|
|
|
|
stencilFunc = func;
|
|
stencilFuncRef = ref;
|
|
stencilFuncMask = mask;
|
|
}
|
|
void opengl_state::StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) {
|
|
if (face == GL_FRONT_AND_BACK) {
|
|
StencilOpSeparate(GL_FRONT, sfail, dpfail, dppass);
|
|
StencilOpSeparate(GL_BACK, sfail, dpfail, dppass);
|
|
return;
|
|
}
|
|
|
|
if (face == GL_FRONT) {
|
|
if (stencilOpFrontStencilFail == sfail && stencilOpFrontDepthFail == dpfail && stencilOpFrontPass == dppass) {
|
|
return;
|
|
}
|
|
|
|
glStencilOpSeparate(GL_FRONT, sfail, dpfail, dppass);
|
|
|
|
stencilOpFrontStencilFail = sfail;
|
|
stencilOpFrontDepthFail = dpfail;
|
|
stencilOpFrontPass = dppass;
|
|
} else {
|
|
if (stencilOpBackStencilFail == sfail && stencilOpBackDepthFail == dpfail && stencilOpBackPass == dppass) {
|
|
return;
|
|
}
|
|
|
|
glStencilOpSeparate(GL_BACK, sfail, dpfail, dppass);
|
|
|
|
stencilOpBackStencilFail = sfail;
|
|
stencilOpBackDepthFail = dpfail;
|
|
stencilOpBackPass = dppass;
|
|
}
|
|
}
|
|
void opengl_state::StencilMask(GLuint mask) {
|
|
if (stencilMask == mask) {
|
|
return;
|
|
}
|
|
|
|
glStencilMask(mask);
|
|
stencilMask = mask;
|
|
}
|
|
|
|
opengl_array_state::~opengl_array_state()
|
|
{
|
|
if ( client_texture_units != NULL ) {
|
|
vm_free(client_texture_units);
|
|
}
|
|
}
|
|
|
|
void opengl_array_state::init(GLuint n_units)
|
|
{
|
|
Assert( n_units > 0 );
|
|
client_texture_units = (opengl_client_texture_unit*) vm_malloc(n_units * sizeof(opengl_client_texture_unit));
|
|
num_client_texture_units = n_units;
|
|
active_client_texture_unit = 0;
|
|
|
|
for (unsigned int i = 0; i < num_client_texture_units; i++) {
|
|
client_texture_units[i].pointer = 0;
|
|
client_texture_units[i].size = 4;
|
|
client_texture_units[i].status = GL_FALSE;
|
|
client_texture_units[i].stride = 0;
|
|
client_texture_units[i].type = GL_FLOAT;
|
|
client_texture_units[i].buffer = 0;
|
|
client_texture_units[i].reset_ptr = false;
|
|
client_texture_units[i].used_for_draw = false;
|
|
}
|
|
|
|
array_buffer = 0;
|
|
element_array_buffer = 0;
|
|
texture_array_buffer = 0;
|
|
uniform_buffer = 0;
|
|
}
|
|
|
|
void opengl_array_state::EnableVertexAttrib(GLuint index)
|
|
{
|
|
opengl_vertex_attrib_unit *va_unit = &vertex_attrib_units[index];
|
|
|
|
va_unit->used_for_draw = true;
|
|
|
|
if ( va_unit->status_init && va_unit->status == GL_TRUE ) {
|
|
return;
|
|
}
|
|
|
|
glEnableVertexAttribArray(index);
|
|
va_unit->status = GL_TRUE;
|
|
va_unit->status_init = true;
|
|
}
|
|
|
|
void opengl_array_state::DisableVertexAttrib(GLuint index)
|
|
{
|
|
opengl_vertex_attrib_unit *va_unit = &vertex_attrib_units[index];
|
|
|
|
if ( va_unit->status_init && va_unit->status == GL_FALSE ) {
|
|
return;
|
|
}
|
|
|
|
glDisableVertexAttribArray(index);
|
|
va_unit->status = GL_FALSE;
|
|
va_unit->status_init = true;
|
|
}
|
|
|
|
void opengl_array_state::VertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLvoid *pointer)
|
|
{
|
|
opengl_vertex_attrib_unit *va_unit = &vertex_attrib_units[index];
|
|
|
|
if (
|
|
!va_unit->reset_ptr
|
|
&& va_unit->ptr_init
|
|
&& va_unit->normalized == normalized
|
|
&& va_unit->pointer == pointer
|
|
&& va_unit->size == size
|
|
&& va_unit->stride == stride
|
|
&& va_unit->type == type
|
|
&& va_unit->buffer == array_buffer
|
|
) {
|
|
return;
|
|
}
|
|
|
|
glVertexAttribPointer(index, size, type, normalized, stride, pointer);
|
|
|
|
va_unit->normalized = normalized;
|
|
va_unit->pointer = pointer;
|
|
va_unit->size = size;
|
|
va_unit->stride = stride;
|
|
va_unit->type = type;
|
|
va_unit->buffer = array_buffer;
|
|
va_unit->reset_ptr = false;
|
|
|
|
va_unit->ptr_init = true;
|
|
}
|
|
|
|
void opengl_array_state::ResetVertexAttribs()
|
|
{
|
|
for (auto &it : vertex_attrib_units) {
|
|
DisableVertexAttrib(it.first);
|
|
}
|
|
|
|
vertex_attrib_units.clear();
|
|
}
|
|
|
|
void opengl_array_state::BindPointersBegin()
|
|
{
|
|
for (unsigned int i = 0; i < num_client_texture_units; i++) {
|
|
client_texture_units[i].used_for_draw = false;
|
|
}
|
|
|
|
for (auto &it : vertex_attrib_units) {
|
|
it.second.used_for_draw = false;
|
|
}
|
|
}
|
|
|
|
void opengl_array_state::BindPointersEnd()
|
|
{
|
|
for (auto &it : vertex_attrib_units) {
|
|
if (!it.second.used_for_draw) {
|
|
DisableVertexAttrib(it.first);
|
|
}
|
|
}
|
|
}
|
|
|
|
void opengl_array_state::BindArrayBuffer(GLuint id)
|
|
{
|
|
if ( array_buffer_valid && array_buffer == id ) {
|
|
return;
|
|
}
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, id);
|
|
|
|
array_buffer = id;
|
|
array_buffer_valid = true;
|
|
|
|
for (unsigned int i = 0; i < num_client_texture_units; i++) {
|
|
client_texture_units[i].reset_ptr = true;
|
|
}
|
|
|
|
for (auto &it : vertex_attrib_units) {
|
|
it.second.reset_ptr = true;
|
|
}
|
|
}
|
|
|
|
void opengl_array_state::BindElementBuffer(GLuint id)
|
|
{
|
|
if ( element_array_buffer_valid && element_array_buffer == id ) {
|
|
return;
|
|
}
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id);
|
|
|
|
element_array_buffer = id;
|
|
element_array_buffer_valid = true;
|
|
}
|
|
|
|
void opengl_array_state::BindTextureBuffer(GLuint id)
|
|
{
|
|
if ( texture_array_buffer == id ) {
|
|
return;
|
|
}
|
|
|
|
glBindBuffer(GL_TEXTURE_BUFFER, id);
|
|
|
|
texture_array_buffer = id;
|
|
}
|
|
|
|
void opengl_array_state::BindUniformBufferBindingIndex(GLuint id, GLuint index)
|
|
{
|
|
if ( uniform_buffer_index_bindings[index] == id ) {
|
|
return;
|
|
}
|
|
|
|
glBindBufferBase(GL_UNIFORM_BUFFER, index, id);
|
|
|
|
uniform_buffer_index_bindings[index] = id;
|
|
}
|
|
|
|
void opengl_array_state::BindUniformBuffer(GLuint id)
|
|
{
|
|
if ( uniform_buffer == id ) {
|
|
return;
|
|
}
|
|
|
|
glBindBuffer(GL_UNIFORM_BUFFER, id);
|
|
|
|
uniform_buffer = id;
|
|
}
|
|
void opengl_array_state::VertexArrayChanged() {
|
|
array_buffer_valid = false;
|
|
element_array_buffer_valid = false;
|
|
|
|
for (auto& bindingInfo : vertex_buffer_bindings) {
|
|
bindingInfo.valid_data = false;
|
|
}
|
|
}
|
|
void opengl_array_state::BindVertexBuffer(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) {
|
|
if (bindingindex >= vertex_buffer_bindings.size()) {
|
|
// Make sure that we have the place for this information
|
|
vertex_buffer_bindings.resize(bindingindex + 1);
|
|
}
|
|
|
|
auto& bindingInfo = vertex_buffer_bindings[bindingindex];
|
|
|
|
if (bindingInfo.valid_data && bindingInfo.buffer == buffer && bindingInfo.offset == offset
|
|
&& bindingInfo.stride == stride) {
|
|
return;
|
|
}
|
|
|
|
glBindVertexBuffer(bindingindex, buffer, offset, stride);
|
|
|
|
bindingInfo.valid_data = true;
|
|
bindingInfo.buffer = buffer;
|
|
bindingInfo.stride = stride;
|
|
bindingInfo.offset = offset;
|
|
}
|
|
opengl_constant_state::opengl_constant_state() {
|
|
}
|
|
void opengl_constant_state::init() {
|
|
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uniform_buffer_offset_alignment);
|
|
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &_max_uniform_block_size);
|
|
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &_max_uniform_block_bindings);
|
|
|
|
if (GLAD_GL_EXT_texture_filter_anisotropic) {
|
|
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &_max_anisotropy);
|
|
} else {
|
|
_max_anisotropy = -1.0f;
|
|
}
|
|
}
|
|
GLint opengl_constant_state::GetUniformBufferOffsetAlignment() {
|
|
return _uniform_buffer_offset_alignment;
|
|
}
|
|
GLint opengl_constant_state::GetMaxUniformBlockSize() {
|
|
return _max_uniform_block_size;
|
|
}
|
|
GLint opengl_constant_state::GetMaxUniformBlockBindings() {
|
|
return _max_uniform_block_bindings;
|
|
}
|
|
GLfloat opengl_constant_state::GetMaxAnisotropy() {
|
|
return _max_anisotropy;
|
|
}
|
|
|
|
void gr_opengl_clear_states()
|
|
{
|
|
gr_zbias(0);
|
|
gr_zbuffer_set(ZBUFFER_TYPE_READ);
|
|
gr_set_cull(0);
|
|
gr_set_fill_mode(GR_FILL_MODE_SOLID);
|
|
|
|
opengl_shader_set_current();
|
|
}
|