Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
afdfa15134
@ -1,20 +1,25 @@
|
||||
// From http://http.developer.nvidia.com/GPUGems3/gpugems3_ch40.html
|
||||
|
||||
uniform layout(size1x16) restrict readonly image2D source;
|
||||
uniform layout(size1x32) restrict readonly image2D depth;
|
||||
uniform layout(size1x16) volatile restrict writeonly image2D dest;
|
||||
uniform float sigma = 5.;
|
||||
|
||||
layout (local_size_x = 8, local_size_y = 8) in;
|
||||
|
||||
shared float local_src[8 + 2 * 8][8];
|
||||
shared float local_depth[8 + 2 * 8][8];
|
||||
|
||||
void main()
|
||||
{
|
||||
int x = int(gl_LocalInvocationID.x), y = int(gl_LocalInvocationID.y);
|
||||
ivec2 uv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);
|
||||
local_src[x][y] = imageLoad(source, ivec2(uv) - ivec2(8, 0)).x;
|
||||
local_depth[x][y] = imageLoad(depth, ivec2(uv) - ivec2(8, 0)).x;
|
||||
local_src[x + 8][y] = imageLoad(source, ivec2(uv)).x;
|
||||
local_depth[x + 8][y] = imageLoad(depth, ivec2(uv)).x;
|
||||
local_src[x + 16][y] = imageLoad(source, ivec2(uv) + ivec2(8, 0)).x;
|
||||
local_depth[x + 16][y] = imageLoad(depth, ivec2(uv) + ivec2(8, 0)).x;
|
||||
|
||||
barrier();
|
||||
|
||||
@ -23,14 +28,20 @@ void main()
|
||||
g1 = exp(-0.5 / (sigma * sigma));
|
||||
g2 = g1 * g1;
|
||||
float sum = local_src[x + 8][y] * g0;
|
||||
float pixel_depth = local_depth[x + 8][y];
|
||||
g0 *= g1;
|
||||
g1 *= g2;
|
||||
float tmp_weight, total_weight = g0;
|
||||
for (int j = 1; j < 8; j++) {
|
||||
sum += local_src[8 + x - j][y] * g0;
|
||||
sum += local_src[8 + x + j][y] * g0;
|
||||
tmp_weight = max(0.0, 1.0 - .001 * abs(local_depth[8 + x - j][y] - pixel_depth));
|
||||
total_weight += g0 * tmp_weight;
|
||||
sum += local_src[8 + x - j][y] * g0 * tmp_weight;
|
||||
tmp_weight = max(0.0, 1.0 - .001 * abs(local_depth[8 + x + j][y] - pixel_depth));
|
||||
total_weight += g0 * tmp_weight;
|
||||
sum += local_src[8 + x + j][y] * g0 * tmp_weight;
|
||||
g0 *= g1;
|
||||
g1 *= g2;
|
||||
}
|
||||
imageStore(dest, ivec2(uv), vec4(sum));
|
||||
imageStore(dest, ivec2(uv), vec4(sum / total_weight));
|
||||
}
|
||||
|
37
data/shaders/bilateralH.frag
Normal file
37
data/shaders/bilateralH.frag
Normal file
@ -0,0 +1,37 @@
|
||||
// From http://http.developer.nvidia.com/GPUGems3/gpugems3_ch40.html
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D depth;
|
||||
uniform vec2 pixel;
|
||||
uniform float sigma = 5.;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 uv = gl_FragCoord.xy * pixel;
|
||||
float X = uv.x;
|
||||
float Y = uv.y;
|
||||
|
||||
float g0, g1, g2;
|
||||
g0 = 1.0 / (sqrt(2.0 * 3.14) * sigma);
|
||||
g1 = exp(-0.5 / (sigma * sigma));
|
||||
g2 = g1 * g1;
|
||||
vec4 sum = texture(tex, vec2(X, Y)) * g0;
|
||||
float pixel_depth = texture(depth, vec2(X, Y)).x;
|
||||
g0 *= g1;
|
||||
g1 *= g2;
|
||||
float tmp_weight, total_weight = g0;
|
||||
for (int i = 1; i < 9; i++) {
|
||||
tmp_weight = max(0.0, 1.0 - .001 * abs(texture(depth, vec2(X - i * pixel.x, Y)).x - pixel_depth));
|
||||
sum += texture(tex, vec2(X - i * pixel.x, Y)) * g0 * tmp_weight;
|
||||
total_weight += g0 * tmp_weight;
|
||||
tmp_weight = max(0.0, 1.0 - .001 * abs(texture(depth, vec2(X + i * pixel.x, Y)).x - pixel_depth));
|
||||
sum += texture(tex, vec2(X + i * pixel.x, Y)) * g0 * tmp_weight;
|
||||
total_weight += g0 * tmp_weight;
|
||||
g0 *= g1;
|
||||
g1 *= g2;
|
||||
}
|
||||
|
||||
FragColor = sum / total_weight;
|
||||
}
|
@ -1,20 +1,25 @@
|
||||
// From http://http.developer.nvidia.com/GPUGems3/gpugems3_ch40.html
|
||||
|
||||
uniform layout(size1x16) restrict readonly image2D source;
|
||||
uniform layout(size1x32) restrict readonly image2D depth;
|
||||
uniform layout(size1x16) volatile restrict writeonly image2D dest;
|
||||
uniform float sigma = 5.;
|
||||
|
||||
layout (local_size_x = 8, local_size_y = 8) in;
|
||||
|
||||
shared float local_src[8][8 + 2 * 8];
|
||||
shared float local_depth[8][8 + 2 * 8];
|
||||
|
||||
void main()
|
||||
{
|
||||
int x = int(gl_LocalInvocationID.x), y = int(gl_LocalInvocationID.y);
|
||||
ivec2 uv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);
|
||||
local_src[x][y] = imageLoad(source, ivec2(uv) - ivec2(0, 8)).x;
|
||||
local_depth[x][y] = imageLoad(depth, ivec2(uv) - ivec2(0, 8)).x;
|
||||
local_src[x][y + 8] = imageLoad(source, ivec2(uv)).x;
|
||||
local_depth[x][y + 8] = imageLoad(depth, ivec2(uv)).x;
|
||||
local_src[x][y + 16] = imageLoad(source, ivec2(uv) + ivec2(0, 8)).x;
|
||||
local_depth[x][y + 16] = imageLoad(depth, ivec2(uv) + ivec2(0, 8)).x;
|
||||
|
||||
barrier();
|
||||
|
||||
@ -23,14 +28,21 @@ void main()
|
||||
g1 = exp(-0.5 / (sigma * sigma));
|
||||
g2 = g1 * g1;
|
||||
float sum = local_src[x][y + 8] * g0;
|
||||
float pixel_depth = local_depth[x][y + 8];
|
||||
g0 *= g1;
|
||||
g1 *= g2;
|
||||
float tmp_weight, total_weight = g0;
|
||||
for (int j = 1; j < 8; j++) {
|
||||
sum += local_src[x][y + 8 + j] * g0;
|
||||
sum += local_src[x][y + 8 - j] * g0;
|
||||
tmp_weight = max(0.0, 1.0 - .001 * abs(local_depth[x][y + 8 + j] - pixel_depth));
|
||||
sum += local_src[x][y + 8 + j] * g0 * tmp_weight;
|
||||
total_weight += g0 * tmp_weight;
|
||||
tmp_weight = max(0.0, 1.0 - .001 * abs(local_depth[x][y + 8 - j] - pixel_depth));
|
||||
sum += local_src[x][y + 8 - j] * g0 * tmp_weight;
|
||||
total_weight += g0 * tmp_weight;
|
||||
g0 *= g1;
|
||||
g1 *= g2;
|
||||
|
||||
}
|
||||
imageStore(dest, ivec2(uv), vec4(sum));
|
||||
imageStore(dest, ivec2(uv), vec4(sum / total_weight));
|
||||
}
|
||||
|
38
data/shaders/bilateralV.frag
Normal file
38
data/shaders/bilateralV.frag
Normal file
@ -0,0 +1,38 @@
|
||||
// From http://http.developer.nvidia.com/GPUGems3/gpugems3_ch40.html
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D depth;
|
||||
uniform vec2 pixel;
|
||||
uniform float sigma = 5.;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 uv = gl_FragCoord.xy * pixel;
|
||||
float X = uv.x;
|
||||
float Y = uv.y;
|
||||
|
||||
float g0, g1, g2;
|
||||
g0 = 1.0 / (sqrt(2.0 * 3.14) * sigma);
|
||||
g1 = exp(-0.5 / (sigma * sigma));
|
||||
g2 = g1 * g1;
|
||||
vec4 sum = texture(tex, vec2(X, Y)) * g0;
|
||||
float pixel_depth = texture(depth, vec2(X, Y)).x;
|
||||
g0 *= g1;
|
||||
g1 *= g2;
|
||||
float tmp_weight, total_weight = g0;
|
||||
for (int i = 1; i < 9; i++) {
|
||||
tmp_weight = max(0.0, 1.0 - .001 * abs(texture(depth, vec2(X, Y - i * pixel.y)).x - pixel_depth));
|
||||
sum += texture(tex, vec2(X, Y - i * pixel.y)) * g0 * tmp_weight;
|
||||
total_weight += g0 * tmp_weight;
|
||||
tmp_weight = max(0.0, 1.0 - .001 * abs(texture(depth, vec2(X, Y + i * pixel.y)).x - pixel_depth));
|
||||
sum += texture(tex, vec2(X, Y + i * pixel.y)) * g0 * tmp_weight;
|
||||
total_weight += g0 * tmp_weight;
|
||||
g0 *= g1;
|
||||
g1 *= g2;
|
||||
}
|
||||
|
||||
FragColor = sum / total_weight;
|
||||
}
|
||||
|
18
data/shaders/frustrum.vert
Normal file
18
data/shaders/frustrum.vert
Normal file
@ -0,0 +1,18 @@
|
||||
layout (std140) uniform MatrixesData
|
||||
{
|
||||
mat4 ViewMatrix;
|
||||
mat4 ProjectionMatrix;
|
||||
mat4 InverseViewMatrix;
|
||||
mat4 InverseProjectionMatrix;
|
||||
mat4 ShadowViewProjMatrixes[4];
|
||||
vec2 screen;
|
||||
};
|
||||
|
||||
uniform int idx;
|
||||
|
||||
in vec3 Position;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
gl_Position = ShadowViewProjMatrixes[idx] * vec4(Position, 1.);
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
// From http://http.developer.nvidia.com/GPUGems3/gpugems3_ch40.html
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform vec2 pixel;
|
||||
uniform float sigma = 5.;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 uv = gl_FragCoord.xy * pixel;
|
||||
float X = uv.x;
|
||||
float Y = uv.y;
|
||||
|
||||
float g0, g1, g2;
|
||||
g0 = 1.0 / (sqrt(2.0 * 3.14) * sigma);
|
||||
g1 = exp(-0.5 / (sigma * sigma));
|
||||
g2 = g1 * g1;
|
||||
vec4 sum = texture(tex, vec2(X, Y)) * g0;
|
||||
g0 *= g1;
|
||||
g1 *= g2;
|
||||
for (int i = 1; i < 9; i++) {
|
||||
sum += texture(tex, vec2(X - i * pixel.x, Y)) * g0;
|
||||
sum += texture(tex, vec2(X + i * pixel.x, Y)) * g0;
|
||||
g0 *= g1;
|
||||
g1 *= g2;
|
||||
}
|
||||
|
||||
FragColor = sum;
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
// From http://http.developer.nvidia.com/GPUGems3/gpugems3_ch40.html
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform vec2 pixel;
|
||||
uniform float sigma = 5.;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 uv = gl_FragCoord.xy * pixel;
|
||||
float X = uv.x;
|
||||
float Y = uv.y;
|
||||
|
||||
float g0, g1, g2;
|
||||
g0 = 1.0 / (sqrt(2.0 * 3.14) * sigma);
|
||||
g1 = exp(-0.5 / (sigma * sigma));
|
||||
g2 = g1 * g1;
|
||||
vec4 sum = texture(tex, vec2(X, Y)) * g0;
|
||||
g0 *= g1;
|
||||
g1 *= g2;
|
||||
for (int i = 1; i < 9; i++) {
|
||||
sum += texture(tex, vec2(X, Y - i * pixel.y)) * g0;
|
||||
sum += texture(tex, vec2(X, Y + i * pixel.y)) * g0;
|
||||
g0 *= g1;
|
||||
g1 *= g2;
|
||||
}
|
||||
|
||||
FragColor = sum;
|
||||
}
|
||||
|
10
data/shaders/layertexturequad.frag
Normal file
10
data/shaders/layertexturequad.frag
Normal file
@ -0,0 +1,10 @@
|
||||
uniform sampler2DArray tex;
|
||||
uniform int layer;
|
||||
|
||||
in vec2 uv;
|
||||
out vec4 FragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = texture(tex, vec3(uv, float(layer)));
|
||||
}
|
@ -24,12 +24,12 @@ layout (std140) uniform MatrixesData
|
||||
|
||||
out float AO;
|
||||
|
||||
const float sigma = 1.;
|
||||
const float sigma = 2.;
|
||||
const float tau = 7.;
|
||||
const float beta = 0.001;
|
||||
const float epsilon = .00001;
|
||||
const float radius = 1.;
|
||||
const float k = 1.5;
|
||||
const float k = 5.;
|
||||
|
||||
#define SAMPLES 16
|
||||
|
||||
|
@ -443,7 +443,10 @@ void IrrDriver::initDevice()
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &GLMajorVersion);
|
||||
glGetIntegerv(GL_MINOR_VERSION, &GLMinorVersion);
|
||||
}
|
||||
Log::info("IrrDriver", "OPENGL VERSION IS %d.%d", GLMajorVersion, GLMinorVersion);
|
||||
Log::info("IrrDriver", "OpenGL version: %d.%d", GLMajorVersion, GLMinorVersion);
|
||||
Log::info("IrrDriver", "OpenGL vendor: %s", glGetString(GL_VENDOR));
|
||||
Log::info("IrrDriver", "OpenGL renderer: %s", glGetString(GL_RENDERER));
|
||||
Log::info("IrrDriver", "OpenGL version string: %s", glGetString(GL_VERSION));
|
||||
m_glsl = (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion >= 1));
|
||||
|
||||
// Parse extensions
|
||||
|
@ -345,6 +345,7 @@ private:
|
||||
class STKMeshSceneNode *m_sun_interposer;
|
||||
scene::CLensFlareSceneNode *m_lensflare;
|
||||
scene::ICameraSceneNode *m_suncam;
|
||||
float m_shadows_cam[4][24];
|
||||
|
||||
std::vector<GlowData> m_glowing;
|
||||
|
||||
@ -380,6 +381,7 @@ private:
|
||||
void renderSSAO();
|
||||
void renderLights(unsigned pointlightCount);
|
||||
void renderDisplacement();
|
||||
void renderShadowsDebug();
|
||||
void doScreenShot();
|
||||
public:
|
||||
IrrDriver();
|
||||
|
@ -415,7 +415,11 @@ void PostProcessing::renderGaussian17TapBlur(FrameBuffer &in_fbo, FrameBuffer &a
|
||||
setTexture(0, in_fbo.getRTT()[0], GL_LINEAR, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
setTexture(1, irr_driver->getDepthStencilTexture(), GL_LINEAR, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glUniform1i(FullScreenShader::Gaussian17TapHShader::uniform_tex, 0);
|
||||
glUniform1i(FullScreenShader::Gaussian17TapHShader::uniform_depth, 1);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
@ -425,9 +429,11 @@ void PostProcessing::renderGaussian17TapBlur(FrameBuffer &in_fbo, FrameBuffer &a
|
||||
|
||||
glUseProgram(FullScreenShader::ComputeGaussian17TapHShader::Program);
|
||||
glBindImageTexture(0, in_fbo.getRTT()[0], 0, false, 0, GL_READ_ONLY, GL_R16F);
|
||||
glBindImageTexture(1, auxiliary.getRTT()[0], 0, false, 0, GL_WRITE_ONLY, GL_R16F);
|
||||
glBindImageTexture(1, irr_driver->getFBO(FBO_LINEAR_DEPTH).getRTT()[0], 1, false, 0, GL_READ_ONLY, GL_R32F);
|
||||
glBindImageTexture(2, auxiliary.getRTT()[0], 0, false, 0, GL_WRITE_ONLY, GL_R16F);
|
||||
glUniform1i(FullScreenShader::ComputeGaussian17TapHShader::uniform_source, 0);
|
||||
glUniform1i(FullScreenShader::ComputeGaussian17TapHShader::uniform_dest, 1);
|
||||
glUniform1i(FullScreenShader::ComputeGaussian17TapHShader::uniform_depth, 1);
|
||||
glUniform1i(FullScreenShader::ComputeGaussian17TapHShader::uniform_dest, 2);
|
||||
glDispatchCompute(in_fbo.getWidth() / 8, in_fbo.getHeight() / 8, 1);
|
||||
}
|
||||
#endif
|
||||
@ -446,7 +452,11 @@ void PostProcessing::renderGaussian17TapBlur(FrameBuffer &in_fbo, FrameBuffer &a
|
||||
setTexture(0, auxiliary.getRTT()[0], GL_LINEAR, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
setTexture(1, irr_driver->getDepthStencilTexture(), GL_LINEAR, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glUniform1i(FullScreenShader::Gaussian17TapVShader::uniform_tex, 0);
|
||||
glUniform1i(FullScreenShader::Gaussian17TapVShader::uniform_depth, 1);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
@ -455,9 +465,11 @@ void PostProcessing::renderGaussian17TapBlur(FrameBuffer &in_fbo, FrameBuffer &a
|
||||
{
|
||||
glUseProgram(FullScreenShader::ComputeGaussian17TapVShader::Program);
|
||||
glBindImageTexture(0, auxiliary.getRTT()[0], 0, false, 0, GL_READ_ONLY, GL_R16F);
|
||||
glBindImageTexture(1, in_fbo.getRTT()[0], 0, false, 0, GL_WRITE_ONLY, GL_R16F);
|
||||
glBindImageTexture(1, irr_driver->getFBO(FBO_LINEAR_DEPTH).getRTT()[0], 1, false, 0, GL_READ_ONLY, GL_R32F);
|
||||
glBindImageTexture(2, in_fbo.getRTT()[0], 0, false, 0, GL_WRITE_ONLY, GL_R16F);
|
||||
glUniform1i(FullScreenShader::ComputeGaussian17TapVShader::uniform_source, 0);
|
||||
glUniform1i(FullScreenShader::ComputeGaussian17TapVShader::uniform_dest, 1);
|
||||
glUniform1i(FullScreenShader::ComputeGaussian17TapVShader::uniform_depth, 1);
|
||||
glUniform1i(FullScreenShader::ComputeGaussian17TapVShader::uniform_dest, 2);
|
||||
glDispatchCompute(in_fbo.getWidth() / 8, in_fbo.getHeight() / 8, 1);
|
||||
}
|
||||
#endif
|
||||
@ -479,6 +491,21 @@ void PostProcessing::renderPassThrough(GLuint tex)
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
void PostProcessing::renderTextureLayer(unsigned tex, unsigned layer)
|
||||
{
|
||||
glUseProgram(FullScreenShader::LayerPassThroughShader::Program);
|
||||
glBindVertexArray(FullScreenShader::LayerPassThroughShader::vao);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glUniform1i(FullScreenShader::LayerPassThroughShader::uniform_texture, 0);
|
||||
glUniform1i(FullScreenShader::LayerPassThroughShader::uniform_layer, layer);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
void PostProcessing::renderGlow(unsigned tex)
|
||||
{
|
||||
|
||||
|
@ -90,6 +90,7 @@ public:
|
||||
|
||||
/** Render tex. Used for blit/texture resize */
|
||||
void renderPassThrough(unsigned tex);
|
||||
void renderTextureLayer(unsigned tex, unsigned layer);
|
||||
void applyMLAA();
|
||||
|
||||
void renderMotionBlur(unsigned cam, FrameBuffer &in_fbo, FrameBuffer &out_fbo);
|
||||
|
@ -197,6 +197,10 @@ void IrrDriver::renderGLSL(float dt)
|
||||
glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y);
|
||||
m_post_processing->renderPassThrough(m_rtts->getRSM().getRTT()[0]);
|
||||
}
|
||||
else if (irr_driver->getShadowViz())
|
||||
{
|
||||
renderShadowsDebug();
|
||||
}
|
||||
else
|
||||
fbo->BlitToDefault(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y);
|
||||
}
|
||||
@ -685,8 +689,6 @@ void IrrDriver::renderParticles()
|
||||
|
||||
void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, size_t width, size_t height)
|
||||
{
|
||||
static int tick = 0;
|
||||
tick++;
|
||||
m_scene_manager->drawAll(scene::ESNRP_CAMERA);
|
||||
irr_driver->setProjMatrix(irr_driver->getVideoDriver()->getTransform(video::ETS_PROJECTION));
|
||||
irr_driver->setViewMatrix(irr_driver->getVideoDriver()->getTransform(video::ETS_VIEW));
|
||||
@ -730,6 +732,34 @@ void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, siz
|
||||
camnode->setFarValue(FarValues[i]);
|
||||
camnode->setNearValue(NearValues[i]);
|
||||
camnode->render();
|
||||
const scene::SViewFrustum *frustrum = camnode->getViewFrustum();
|
||||
float tmp[24] = {
|
||||
frustrum->getFarLeftDown().X,
|
||||
frustrum->getFarLeftDown().Y,
|
||||
frustrum->getFarLeftDown().Z,
|
||||
frustrum->getFarLeftUp().X,
|
||||
frustrum->getFarLeftUp().Y,
|
||||
frustrum->getFarLeftUp().Z,
|
||||
frustrum->getFarRightDown().X,
|
||||
frustrum->getFarRightDown().Y,
|
||||
frustrum->getFarRightDown().Z,
|
||||
frustrum->getFarRightUp().X,
|
||||
frustrum->getFarRightUp().Y,
|
||||
frustrum->getFarRightUp().Z,
|
||||
frustrum->getNearLeftDown().X,
|
||||
frustrum->getNearLeftDown().Y,
|
||||
frustrum->getNearLeftDown().Z,
|
||||
frustrum->getNearLeftUp().X,
|
||||
frustrum->getNearLeftUp().Y,
|
||||
frustrum->getNearLeftUp().Z,
|
||||
frustrum->getNearRightDown().X,
|
||||
frustrum->getNearRightDown().Y,
|
||||
frustrum->getNearRightDown().Z,
|
||||
frustrum->getNearRightUp().X,
|
||||
frustrum->getNearRightUp().Y,
|
||||
frustrum->getNearRightUp().Z,
|
||||
};
|
||||
memcpy(m_shadows_cam[i], tmp, 24 * sizeof(float));
|
||||
const core::aabbox3df smallcambox = camnode->
|
||||
getViewFrustum()->getBoundingBox();
|
||||
core::aabbox3df trackbox(vmin->toIrrVector(), vmax->toIrrVector() -
|
||||
@ -739,8 +769,32 @@ void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, siz
|
||||
core::aabbox3df box = smallcambox;
|
||||
box = box.intersect(trackbox);
|
||||
|
||||
|
||||
SunCamViewMatrix.transformBoxEx(trackbox);
|
||||
float xmin = INFINITY, xmax = -INFINITY;
|
||||
float ymin = INFINITY, ymax = -INFINITY;
|
||||
float zmin = INFINITY, zmax = -INFINITY;
|
||||
const vector3df vectors[] =
|
||||
{
|
||||
frustrum->getFarLeftDown(),
|
||||
frustrum->getFarLeftUp(),
|
||||
frustrum->getFarRightDown(),
|
||||
frustrum->getFarRightUp(),
|
||||
frustrum->getNearLeftDown(),
|
||||
frustrum->getNearLeftUp(),
|
||||
frustrum->getNearRightDown(),
|
||||
frustrum->getNearRightUp()
|
||||
};
|
||||
for (unsigned j = 0; j < 8; j++)
|
||||
{
|
||||
vector3df vector;
|
||||
SunCamViewMatrix.transformVect(vector, vectors[j]);
|
||||
xmin = MIN2(xmin, vector.X);
|
||||
xmax = MAX2(xmax, vector.X);
|
||||
ymin = MIN2(ymin, vector.Y);
|
||||
ymax = MAX2(ymax, vector.Y);
|
||||
zmin = MIN2(zmin, vector.Z);
|
||||
zmax = MAX2(zmax, vector.Z);
|
||||
}
|
||||
/* SunCamViewMatrix.transformBoxEx(trackbox);
|
||||
SunCamViewMatrix.transformBoxEx(box);
|
||||
|
||||
core::vector3df extent = box.getExtent();
|
||||
@ -750,12 +804,12 @@ void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, siz
|
||||
|
||||
// Snap to texels
|
||||
const float units_per_w = w / 1024;
|
||||
const float units_per_h = h / 1024;
|
||||
const float units_per_h = h / 1024;*/
|
||||
|
||||
float left = box.MinEdge.X;
|
||||
float right = box.MaxEdge.X;
|
||||
float up = box.MaxEdge.Y;
|
||||
float down = box.MinEdge.Y;
|
||||
float left = xmin;
|
||||
float right = xmax;
|
||||
float up = ymin;
|
||||
float down = ymax;
|
||||
|
||||
core::matrix4 tmp_matrix;
|
||||
|
||||
@ -768,15 +822,26 @@ void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, siz
|
||||
}
|
||||
|
||||
tmp_matrix.buildProjectionMatrixOrthoLH(left, right,
|
||||
up, down,
|
||||
30, z);
|
||||
down, up,
|
||||
30, zmax);
|
||||
m_suncam->setProjectionMatrix(tmp_matrix, true);
|
||||
m_suncam->render();
|
||||
|
||||
sun_ortho_matrix.push_back(getVideoDriver()->getTransform(video::ETS_PROJECTION) * getVideoDriver()->getTransform(video::ETS_VIEW));
|
||||
}
|
||||
if ((tick % 100) == 2)
|
||||
rsm_matrix = sun_ortho_matrix[3];
|
||||
|
||||
{
|
||||
core::aabbox3df trackbox(vmin->toIrrVector(), vmax->toIrrVector() -
|
||||
core::vector3df(0, 30, 0));
|
||||
SunCamViewMatrix.transformBoxEx(trackbox);
|
||||
core::matrix4 tmp_matrix;
|
||||
tmp_matrix.buildProjectionMatrixOrthoLH(trackbox.MinEdge.X, trackbox.MaxEdge.X,
|
||||
trackbox.MaxEdge.Y, trackbox.MinEdge.Y,
|
||||
30, trackbox.MaxEdge.Z);
|
||||
m_suncam->setProjectionMatrix(tmp_matrix, true);
|
||||
m_suncam->render();
|
||||
rsm_matrix = getVideoDriver()->getTransform(video::ETS_PROJECTION) * getVideoDriver()->getTransform(video::ETS_VIEW);
|
||||
}
|
||||
rh_extend = core::vector3df(128, 64, 128);
|
||||
core::vector3df campos = camnode->getAbsolutePosition();
|
||||
core::vector3df translation(8 * floor(campos.X / 8), 8 * floor(campos.Y / 8), 8 * floor(campos.Z / 8));
|
||||
@ -847,6 +912,36 @@ void IrrDriver::renderShadows()
|
||||
}
|
||||
}
|
||||
|
||||
static void renderWireFrameFrustrum(float *tmp, unsigned i)
|
||||
{
|
||||
glUseProgram(MeshShader::ViewFrustrumShader::Program);
|
||||
glBindVertexArray(MeshShader::ViewFrustrumShader::frustrumvao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, SharedObject::frustrumvbo);
|
||||
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, 8 * 3 * sizeof(float), (void *)tmp);
|
||||
MeshShader::ViewFrustrumShader::setUniforms(video::SColor(255, 0, 255, 0), i);
|
||||
glDrawElements(GL_LINES, 24, GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
|
||||
|
||||
void IrrDriver::renderShadowsDebug()
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glViewport(0, UserConfigParams::m_height / 2, UserConfigParams::m_width / 2, UserConfigParams::m_height / 2);
|
||||
m_post_processing->renderTextureLayer(m_rtts->getShadowDepthTex(), 0);
|
||||
renderWireFrameFrustrum(m_shadows_cam[0], 0);
|
||||
glViewport(UserConfigParams::m_width / 2, UserConfigParams::m_height / 2, UserConfigParams::m_width / 2, UserConfigParams::m_height / 2);
|
||||
m_post_processing->renderTextureLayer(m_rtts->getShadowDepthTex(), 1);
|
||||
renderWireFrameFrustrum(m_shadows_cam[1], 1);
|
||||
glViewport(0, 0, UserConfigParams::m_width / 2, UserConfigParams::m_height / 2);
|
||||
m_post_processing->renderTextureLayer(m_rtts->getShadowDepthTex(), 2);
|
||||
renderWireFrameFrustrum(m_shadows_cam[2], 2);
|
||||
glViewport(UserConfigParams::m_width / 2, 0, UserConfigParams::m_width / 2, UserConfigParams::m_height / 2);
|
||||
m_post_processing->renderTextureLayer(m_rtts->getShadowDepthTex(), 3);
|
||||
renderWireFrameFrustrum(m_shadows_cam[3], 3);
|
||||
glViewport(0, 0, UserConfigParams::m_width, UserConfigParams::m_height);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void IrrDriver::renderGlow(std::vector<GlowData>& glows)
|
||||
|
@ -265,20 +265,19 @@ RTT::~RTT()
|
||||
glDeleteTextures(1, &DepthStencilTexture);
|
||||
if (UserConfigParams::m_shadows)
|
||||
{
|
||||
delete m_shadow_FBO;
|
||||
glDeleteTextures(1, &shadowColorTex);
|
||||
glDeleteTextures(1, &shadowDepthTex);
|
||||
}
|
||||
if (UserConfigParams::m_gi)
|
||||
{
|
||||
delete m_RH_FBO;
|
||||
delete m_RSM;
|
||||
glDeleteTextures(1, &RSM_Color);
|
||||
glDeleteTextures(1, &RSM_Normal);
|
||||
glDeleteTextures(1, &RSM_Depth);
|
||||
glDeleteTextures(1, &RH_Red);
|
||||
glDeleteTextures(1, &RH_Green);
|
||||
glDeleteTextures(1, &RH_Blue);
|
||||
|
||||
delete m_shadow_FBO;
|
||||
delete m_RH_FBO;
|
||||
delete m_RSM;
|
||||
}
|
||||
}
|
||||
|
@ -169,6 +169,26 @@ static void initCubeVBO()
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * 6 * sizeof(int), indices, GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
GLuint SharedObject::frustrumvbo = 0;
|
||||
GLuint SharedObject::frustrumindexes = 0;
|
||||
|
||||
static void initFrustrumVBO()
|
||||
{
|
||||
glGenBuffers(1, &SharedObject::frustrumvbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, SharedObject::frustrumvbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, 8 * 3 * sizeof(float), 0, GL_DYNAMIC_DRAW);
|
||||
|
||||
int indices[24] = {
|
||||
0, 1, 1, 3, 3, 2, 2, 0,
|
||||
4, 5, 5, 7, 7, 6, 6, 4,
|
||||
0, 4, 1, 5, 2, 6, 3, 7,
|
||||
};
|
||||
|
||||
glGenBuffers(1, &SharedObject::frustrumindexes);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedObject::frustrumindexes);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 12 * 2 * sizeof(int), indices, GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
GLuint SharedObject::ViewProjectionMatrixesUBO;
|
||||
|
||||
static void initShadowVPMUBO()
|
||||
@ -271,6 +291,7 @@ void Shaders::loadShaders()
|
||||
initQuadBuffer();
|
||||
initBillboardVBO();
|
||||
initCubeVBO();
|
||||
initFrustrumVBO();
|
||||
initShadowVPMUBO();
|
||||
FullScreenShader::BloomBlendShader::init();
|
||||
FullScreenShader::BloomShader::init();
|
||||
@ -286,6 +307,7 @@ void Shaders::loadShaders()
|
||||
FullScreenShader::Gaussian6VBlurShader::init();
|
||||
FullScreenShader::GlowShader::init();
|
||||
FullScreenShader::PassThroughShader::init();
|
||||
FullScreenShader::LayerPassThroughShader::init();
|
||||
FullScreenShader::LinearizeDepthShader::init();
|
||||
FullScreenShader::SSAOShader::init();
|
||||
FullScreenShader::SunLightShader::init();
|
||||
@ -336,6 +358,7 @@ void Shaders::loadShaders()
|
||||
MeshShader::InstancedRefShadowShader::init();
|
||||
MeshShader::GrassShadowShader::init();
|
||||
MeshShader::SkyboxShader::init();
|
||||
MeshShader::ViewFrustrumShader::init();
|
||||
ParticleShader::FlipParticleRender::init();
|
||||
ParticleShader::HeightmapSimulationShader::init();
|
||||
ParticleShader::SimpleParticleRender::init();
|
||||
@ -1791,6 +1814,41 @@ namespace MeshShader
|
||||
glUniformMatrix4fv(uniform_MM, 1, GL_FALSE, ModelMatrix.pointer());
|
||||
glUniform1i(uniform_tex, TU_tex);
|
||||
}
|
||||
|
||||
GLuint ViewFrustrumShader::Program;
|
||||
GLuint ViewFrustrumShader::attrib_position;
|
||||
GLuint ViewFrustrumShader::uniform_color;
|
||||
GLuint ViewFrustrumShader::uniform_idx;
|
||||
GLuint ViewFrustrumShader::frustrumvao;
|
||||
|
||||
void ViewFrustrumShader::init()
|
||||
{
|
||||
Program = LoadProgram(
|
||||
GL_VERTEX_SHADER, file_manager->getAsset("shaders/frustrum.vert").c_str(),
|
||||
GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/coloredquad.frag").c_str());
|
||||
attrib_position = glGetAttribLocation(Program, "Position");
|
||||
if (!UserConfigParams::m_ubo_disabled)
|
||||
{
|
||||
GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
|
||||
glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
|
||||
}
|
||||
uniform_color = glGetUniformLocation(Program, "color");
|
||||
uniform_idx = glGetUniformLocation(Program, "idx");
|
||||
|
||||
glGenVertexArrays(1, &frustrumvao);
|
||||
glBindVertexArray(frustrumvao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, SharedObject::frustrumvbo);
|
||||
glEnableVertexAttribArray(attrib_position);
|
||||
glVertexAttribPointer(attrib_position, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedObject::frustrumindexes);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void ViewFrustrumShader::setUniforms(const video::SColor &color, unsigned idx)
|
||||
{
|
||||
glUniform4i(uniform_color, color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
|
||||
glUniform1i(uniform_idx, idx);
|
||||
}
|
||||
}
|
||||
|
||||
namespace LightShader
|
||||
@ -2437,27 +2495,31 @@ namespace FullScreenShader
|
||||
|
||||
GLuint Gaussian17TapHShader::Program;
|
||||
GLuint Gaussian17TapHShader::uniform_tex;
|
||||
GLuint Gaussian17TapHShader::uniform_depth;
|
||||
GLuint Gaussian17TapHShader::uniform_pixel;
|
||||
GLuint Gaussian17TapHShader::vao;
|
||||
void Gaussian17TapHShader::init()
|
||||
{
|
||||
Program = LoadProgram(
|
||||
GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
|
||||
GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gaussian17taph.frag").c_str());
|
||||
GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/bilateralH.frag").c_str());
|
||||
uniform_tex = glGetUniformLocation(Program, "tex");
|
||||
uniform_pixel = glGetUniformLocation(Program, "pixel");
|
||||
uniform_depth = glGetUniformLocation(Program, "depth");
|
||||
vao = createFullScreenVAO(Program);
|
||||
}
|
||||
|
||||
GLuint ComputeGaussian17TapHShader::Program;
|
||||
GLuint ComputeGaussian17TapHShader::uniform_source;
|
||||
GLuint ComputeGaussian17TapHShader::uniform_depth;
|
||||
GLuint ComputeGaussian17TapHShader::uniform_dest;
|
||||
void ComputeGaussian17TapHShader::init()
|
||||
{
|
||||
#if WIN32
|
||||
Program = LoadProgram(
|
||||
GL_COMPUTE_SHADER, file_manager->getAsset("shaders/gaussian.comp").c_str());
|
||||
GL_COMPUTE_SHADER, file_manager->getAsset("shaders/bilateralH.comp").c_str());
|
||||
uniform_source = glGetUniformLocation(Program, "source");
|
||||
uniform_depth = glGetUniformLocation(Program, "depth");
|
||||
uniform_dest = glGetUniformLocation(Program, "dest");
|
||||
#endif
|
||||
}
|
||||
@ -2492,27 +2554,31 @@ namespace FullScreenShader
|
||||
|
||||
GLuint Gaussian17TapVShader::Program;
|
||||
GLuint Gaussian17TapVShader::uniform_tex;
|
||||
GLuint Gaussian17TapVShader::uniform_depth;
|
||||
GLuint Gaussian17TapVShader::uniform_pixel;
|
||||
GLuint Gaussian17TapVShader::vao;
|
||||
void Gaussian17TapVShader::init()
|
||||
{
|
||||
Program = LoadProgram(
|
||||
GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
|
||||
GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gaussian17tapv.frag").c_str());
|
||||
GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/bilateralV.frag").c_str());
|
||||
uniform_tex = glGetUniformLocation(Program, "tex");
|
||||
uniform_pixel = glGetUniformLocation(Program, "pixel");
|
||||
uniform_depth = glGetUniformLocation(Program, "depth");
|
||||
vao = createFullScreenVAO(Program);
|
||||
}
|
||||
|
||||
GLuint ComputeGaussian17TapVShader::Program;
|
||||
GLuint ComputeGaussian17TapVShader::uniform_source;
|
||||
GLuint ComputeGaussian17TapVShader::uniform_depth;
|
||||
GLuint ComputeGaussian17TapVShader::uniform_dest;
|
||||
void ComputeGaussian17TapVShader::init()
|
||||
{
|
||||
#if WIN32
|
||||
Program = LoadProgram(
|
||||
GL_COMPUTE_SHADER, file_manager->getAsset("shaders/gaussianv.comp").c_str());
|
||||
GL_COMPUTE_SHADER, file_manager->getAsset("shaders/bilateralV.comp").c_str());
|
||||
uniform_source = glGetUniformLocation(Program, "source");
|
||||
uniform_depth = glGetUniformLocation(Program, "depth");
|
||||
uniform_dest = glGetUniformLocation(Program, "dest");
|
||||
#endif
|
||||
}
|
||||
@ -2557,6 +2623,20 @@ namespace FullScreenShader
|
||||
vao = createVAO(Program);
|
||||
}
|
||||
|
||||
GLuint LayerPassThroughShader::Program;
|
||||
GLuint LayerPassThroughShader::uniform_texture;
|
||||
GLuint LayerPassThroughShader::uniform_layer;
|
||||
GLuint LayerPassThroughShader::vao;
|
||||
void LayerPassThroughShader::init()
|
||||
{
|
||||
Program = LoadProgram(
|
||||
GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
|
||||
GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/layertexturequad.frag").c_str());
|
||||
uniform_texture = glGetUniformLocation(Program, "tex");
|
||||
uniform_layer = glGetUniformLocation(Program, "layer");
|
||||
vao = createVAO(Program);
|
||||
}
|
||||
|
||||
GLuint LinearizeDepthShader::Program;
|
||||
GLuint LinearizeDepthShader::uniform_zn;
|
||||
GLuint LinearizeDepthShader::uniform_zf;
|
||||
|
@ -29,7 +29,7 @@ class SharedObject
|
||||
{
|
||||
public:
|
||||
static GLuint billboardvbo;
|
||||
static GLuint cubevbo, cubeindexes;
|
||||
static GLuint cubevbo, cubeindexes, frustrumvbo, frustrumindexes;
|
||||
static GLuint ViewProjectionMatrixesUBO;
|
||||
};
|
||||
|
||||
@ -424,6 +424,18 @@ public:
|
||||
static void setUniforms(const core::matrix4 &ModelMatrix, const core::vector2df &screen, unsigned TU_tex);
|
||||
};
|
||||
|
||||
class ViewFrustrumShader
|
||||
{
|
||||
public:
|
||||
static GLuint Program;
|
||||
static GLuint attrib_position;
|
||||
static GLuint uniform_color, uniform_idx;
|
||||
static GLuint frustrumvao;
|
||||
|
||||
static void init();
|
||||
static void setUniforms(const video::SColor &color, unsigned idx);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#define MAXLIGHT 32
|
||||
@ -635,7 +647,7 @@ class Gaussian17TapHShader
|
||||
{
|
||||
public:
|
||||
static GLuint Program;
|
||||
static GLuint uniform_tex, uniform_pixel;
|
||||
static GLuint uniform_tex, uniform_depth, uniform_pixel;
|
||||
static GLuint vao;
|
||||
|
||||
static void init();
|
||||
@ -645,7 +657,7 @@ class ComputeGaussian17TapHShader
|
||||
{
|
||||
public:
|
||||
static GLuint Program;
|
||||
static GLuint uniform_source, uniform_dest;
|
||||
static GLuint uniform_source, uniform_depth, uniform_dest;
|
||||
|
||||
static void init();
|
||||
};
|
||||
@ -674,7 +686,7 @@ class Gaussian17TapVShader
|
||||
{
|
||||
public:
|
||||
static GLuint Program;
|
||||
static GLuint uniform_tex, uniform_pixel;
|
||||
static GLuint uniform_tex, uniform_depth, uniform_pixel;
|
||||
static GLuint vao;
|
||||
|
||||
static void init();
|
||||
@ -684,7 +696,7 @@ class ComputeGaussian17TapVShader
|
||||
{
|
||||
public:
|
||||
static GLuint Program;
|
||||
static GLuint uniform_source, uniform_dest;
|
||||
static GLuint uniform_source, uniform_depth, uniform_dest;
|
||||
|
||||
static void init();
|
||||
};
|
||||
@ -720,6 +732,16 @@ public:
|
||||
static void init();
|
||||
};
|
||||
|
||||
class LayerPassThroughShader
|
||||
{
|
||||
public:
|
||||
static GLuint Program;
|
||||
static GLuint uniform_layer, uniform_texture;
|
||||
static GLuint vao;
|
||||
|
||||
static void init();
|
||||
};
|
||||
|
||||
class LinearizeDepthShader
|
||||
{
|
||||
public:
|
||||
|
@ -340,7 +340,10 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models, bool always_anim
|
||||
|
||||
node = irr_driver->addAnimatedMesh(m_mesh);
|
||||
// as animated mesh are not cheap to render use frustum box culling
|
||||
node->setAutomaticCulling(scene::EAC_FRUSTUM_BOX);
|
||||
if (irr_driver->isGLSL())
|
||||
node->setAutomaticCulling(scene::EAC_OFF);
|
||||
else
|
||||
node->setAutomaticCulling(scene::EAC_FRUSTUM_BOX);
|
||||
|
||||
if (always_animated)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user