WINTERMUTE: Render shadow volume using VBOs and shaders
This commit is contained in:
parent
781608e2e9
commit
3864ad1dd8
17 changed files with 575 additions and 145 deletions
|
@ -576,7 +576,7 @@ bool AdObject3D::skipTo(int x, int y, bool tolerant) {
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
ShadowVolume *AdObject3D::getShadowVolume() {
|
||||
if (_shadowVolume == nullptr) {
|
||||
_shadowVolume = new ShadowVolume(_gameRef);
|
||||
_shadowVolume = _gameRef->_renderer3D->createShadowVolume();
|
||||
}
|
||||
|
||||
return _shadowVolume;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "engines/wintermute/base/gfx/opengl/camera3d.h"
|
||||
#include "engines/wintermute/base/gfx/opengl/mesh3ds_opengl.h"
|
||||
#include "engines/wintermute/base/gfx/opengl/meshx_opengl.h"
|
||||
#include "engines/wintermute/base/gfx/opengl/shadow_volume_opengl.h"
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
#include "math/glmath.h"
|
||||
|
||||
|
@ -614,4 +615,8 @@ MeshX *BaseRenderOpenGL3D::createMeshX() {
|
|||
return new MeshXOpenGL(_gameRef);
|
||||
}
|
||||
|
||||
ShadowVolume *BaseRenderOpenGL3D::createShadowVolume() {
|
||||
return new ShadowVolumeOpenGL(_gameRef);
|
||||
}
|
||||
|
||||
} // namespace Wintermute
|
||||
|
|
|
@ -179,6 +179,7 @@ public:
|
|||
|
||||
Mesh3DS *createMesh3DS() override;
|
||||
MeshX *createMeshX() override;
|
||||
ShadowVolume *createShadowVolume() override;
|
||||
|
||||
private:
|
||||
Math::Matrix4 _lastProjectionMatrix;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "engines/wintermute/base/gfx/opengl/camera3d.h"
|
||||
#include "engines/wintermute/base/gfx/opengl/mesh3ds_opengl_shader.h"
|
||||
#include "engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h"
|
||||
#include "engines/wintermute/base/gfx/opengl/shadow_volume_opengl_shader.h"
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
#include "math/glmath.h"
|
||||
|
||||
|
@ -279,6 +280,9 @@ bool BaseRenderOpenGL3DShader::setProjection2D() {
|
|||
_projectionMatrix2d(3, 0) = -1.0f;
|
||||
_projectionMatrix2d(3, 1) = -1.0f;
|
||||
_projectionMatrix2d(3, 2) = -(farPlane + nearPlane) / (farPlane - nearPlane);
|
||||
|
||||
_shadowMaskShader->use();
|
||||
_shadowMaskShader->setUniform("projMatrix", _projectionMatrix2d);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -302,6 +306,9 @@ void BaseRenderOpenGL3DShader::pushWorldTransform(const Math::Matrix4 &transform
|
|||
_modelXShader->use();
|
||||
_modelXShader->setUniform("modelMatrix", newTop);
|
||||
_modelXShader->setUniform("normalMatrix", newInvertedTranspose);
|
||||
|
||||
_shadowVolumeShader->use();
|
||||
_shadowVolumeShader->setUniform("modelMatrix", newTop);
|
||||
}
|
||||
|
||||
void BaseRenderOpenGL3DShader::popWorldTransform() {
|
||||
|
@ -317,6 +324,9 @@ void BaseRenderOpenGL3DShader::popWorldTransform() {
|
|||
_modelXShader->use();
|
||||
_modelXShader->setUniform("modelMatrix", currentTransform);
|
||||
_modelXShader->setUniform("normalMatrix", currentInvertedTranspose);
|
||||
|
||||
_shadowVolumeShader->use();
|
||||
_shadowVolumeShader->setUniform("modelMatrix", currentTransform);
|
||||
}
|
||||
|
||||
bool BaseRenderOpenGL3DShader::windowedBlt() {
|
||||
|
@ -350,6 +360,12 @@ bool BaseRenderOpenGL3DShader::initRenderer(int width, int height, bool windowed
|
|||
static const char *geometryAttributes[] = { "position", nullptr };
|
||||
_geometryShader = OpenGL::Shader::fromFiles("geometry", geometryAttributes);
|
||||
|
||||
static const char *shadowVolumeAttributes[] = { "position", nullptr };
|
||||
_shadowVolumeShader = OpenGL::Shader::fromFiles("shadow_volume", shadowVolumeAttributes);
|
||||
|
||||
static const char *shadowMaskAttributes[] = { "position", nullptr };
|
||||
_shadowMaskShader = OpenGL::Shader::fromFiles("shadow_mask", shadowMaskAttributes);
|
||||
|
||||
_transformStack.push_back(Math::Matrix4());
|
||||
_transformStack.back().setToIdentity();
|
||||
|
||||
|
@ -496,6 +512,10 @@ bool BaseRenderOpenGL3DShader::setup3D(Camera3D *camera, bool force) {
|
|||
_geometryShader->setUniform("viewMatrix", _lastViewMatrix);
|
||||
_geometryShader->setUniform("projMatrix", _projectionMatrix3d);
|
||||
|
||||
_shadowVolumeShader->use();
|
||||
_shadowVolumeShader->setUniform("viewMatrix", _lastViewMatrix);
|
||||
_shadowVolumeShader->setUniform("projMatrix", _projectionMatrix3d);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -657,4 +677,8 @@ MeshX *BaseRenderOpenGL3DShader::createMeshX() {
|
|||
return new MeshXOpenGLShader(_gameRef, _modelXShader);
|
||||
}
|
||||
|
||||
ShadowVolume *BaseRenderOpenGL3DShader::createShadowVolume() {
|
||||
return new ShadowVolumeOpenGLShader(_gameRef, _shadowVolumeShader, _shadowMaskShader);
|
||||
}
|
||||
|
||||
} // namespace Wintermute
|
||||
|
|
|
@ -180,6 +180,7 @@ public:
|
|||
|
||||
Mesh3DS *createMesh3DS() override;
|
||||
MeshX *createMeshX() override;
|
||||
ShadowVolume *createShadowVolume() override;
|
||||
|
||||
private:
|
||||
Math::Matrix4 _lastViewMatrix;
|
||||
|
@ -199,6 +200,8 @@ private:
|
|||
OpenGL::Shader *_fadeShader;
|
||||
OpenGL::Shader *_modelXShader;
|
||||
OpenGL::Shader *_geometryShader;
|
||||
OpenGL::Shader *_shadowVolumeShader;
|
||||
OpenGL::Shader *_shadowMaskShader;
|
||||
};
|
||||
|
||||
} // namespace Wintermute
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace Wintermute {
|
|||
class BaseSurfaceOpenGL3D;
|
||||
class Mesh3DS;
|
||||
class MeshX;
|
||||
class ShadowVolume;
|
||||
|
||||
class BaseRenderer3D : public BaseRenderer {
|
||||
public:
|
||||
|
@ -74,6 +75,7 @@ public:
|
|||
|
||||
virtual Mesh3DS *createMesh3DS() = 0;
|
||||
virtual MeshX *createMeshX() = 0;
|
||||
virtual ShadowVolume *createShadowVolume() = 0;
|
||||
|
||||
virtual bool drawSprite(BaseSurfaceOpenGL3D &tex, const Rect32 &rect, float zoomX, float zoomY, const Vector2 &pos,
|
||||
uint32 color, bool alphaDisable, Graphics::TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) = 0;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
in vec4 Color;
|
||||
|
||||
OUTPUT
|
||||
|
||||
void main()
|
||||
{
|
||||
outColor = Color;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
in vec2 position;
|
||||
|
||||
uniform highp mat4 projMatrix;
|
||||
uniform vec4 color;
|
||||
|
||||
out vec4 Color;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projMatrix * vec4(position, 0.0f, 1.0f);
|
||||
Color = color;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
void main()
|
||||
{
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
in vec3 position;
|
||||
|
||||
uniform highp mat4 modelMatrix;
|
||||
uniform highp mat4 viewMatrix;
|
||||
uniform highp mat4 projMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projMatrix * viewMatrix * modelMatrix * vec4(position, 1.0f);
|
||||
}
|
|
@ -53,143 +53,6 @@ void ShadowVolume::addVertex(const Math::Vector3d &vertex) {
|
|||
_vertices.add(vertex);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolume::render() {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glVertexPointer(3, GL_FLOAT, 0, _vertices.data());
|
||||
glDrawArrays(GL_TRIANGLES, 0, _vertices.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolume::renderToStencilBuffer() {
|
||||
// Disable z-buffer writes (note: z-testing still occurs), and enable the
|
||||
// stencil-buffer
|
||||
glDepthMask(GL_FALSE);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
// Set up stencil compare fuction, reference value, and masks.
|
||||
// Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true.
|
||||
// Note: since we set up the stencil-test to always pass, the STENCILFAIL
|
||||
// renderstate is really not needed.
|
||||
glStencilFunc(GL_ALWAYS, 0x1, 0xFFFFFFFF);
|
||||
|
||||
glShadeModel(GL_FLAT);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// Make sure that no pixels get drawn to the frame buffer
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ZERO, GL_ONE);
|
||||
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
|
||||
|
||||
// Draw back-side of shadow volume in stencil/z only
|
||||
glCullFace(GL_FRONT);
|
||||
render();
|
||||
|
||||
// // Decrement stencil buffer value
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
|
||||
|
||||
// Draw front-side of shadow volume in stencil/z only
|
||||
glCullFace(GL_BACK);
|
||||
render();
|
||||
|
||||
// // Restore render states
|
||||
glEnable(GL_LIGHTING);
|
||||
glFrontFace(GL_CCW);
|
||||
glShadeModel(GL_SMOOTH);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolume::renderToScene() {
|
||||
initMask();
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Only write where stencil val >= 1 (count indicates # of shadows that overlap that pixel)
|
||||
glStencilFunc(GL_LEQUAL, 0x1, 0xFFFFFFFF);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
|
||||
glDisable(GL_FOG);
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
|
||||
_gameRef->_renderer3D->setProjection2D();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
// Draw a big, gray square
|
||||
glInterleavedArrays(GL_C4UB_V3F, 0, _shadowMask);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// Restore render states
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
_gameRef->_renderer3D->setup3D(nullptr, true);
|
||||
|
||||
// clear stencil buffer
|
||||
glClearStencil(0);
|
||||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolume::initMask() {
|
||||
Rect32 viewport = _gameRef->_renderer->getViewPort();
|
||||
|
||||
_shadowMask[0].x = viewport.left;
|
||||
_shadowMask[0].y = viewport.bottom;
|
||||
_shadowMask[0].z = 0.0f;
|
||||
|
||||
_shadowMask[1].x = viewport.left;
|
||||
_shadowMask[1].y = viewport.top;
|
||||
_shadowMask[1].z = 0.0f;
|
||||
|
||||
_shadowMask[2].x = viewport.right;
|
||||
_shadowMask[2].y = viewport.bottom;
|
||||
_shadowMask[2].z = 0.0f;
|
||||
|
||||
_shadowMask[3].x = viewport.right;
|
||||
_shadowMask[3].y = viewport.top;
|
||||
_shadowMask[3].z = 0.0f;
|
||||
|
||||
byte a = RGBCOLGetA(_color);
|
||||
byte r = RGBCOLGetR(_color);
|
||||
byte g = RGBCOLGetG(_color);
|
||||
byte b = RGBCOLGetB(_color);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
_shadowMask[i].r = r;
|
||||
_shadowMask[i].g = g;
|
||||
_shadowMask[i].b = b;
|
||||
_shadowMask[i].a = a;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolume::setColor(uint32 color) {
|
||||
if (color != _color) {
|
||||
|
|
|
@ -58,17 +58,17 @@ public:
|
|||
void addVertex(const Math::Vector3d &vertex);
|
||||
bool reset();
|
||||
|
||||
bool renderToStencilBuffer();
|
||||
bool renderToScene();
|
||||
virtual bool renderToStencilBuffer() = 0;
|
||||
virtual bool renderToScene() = 0;
|
||||
|
||||
bool setColor(uint32 color);
|
||||
|
||||
private:
|
||||
bool render();
|
||||
ShadowVertex _shadowMask[4];
|
||||
uint32 _color;
|
||||
bool initMask();
|
||||
protected:
|
||||
BaseArray<Math::Vector3d> _vertices; // Vertex data for rendering shadow volume
|
||||
uint32 _color;
|
||||
|
||||
private:
|
||||
virtual bool initMask() = 0;
|
||||
};
|
||||
|
||||
} // namespace Wintermute
|
||||
|
|
182
engines/wintermute/base/gfx/opengl/shadow_volume_opengl.cpp
Normal file
182
engines/wintermute/base/gfx/opengl/shadow_volume_opengl.cpp
Normal file
|
@ -0,0 +1,182 @@
|
|||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is based on WME.
|
||||
* http://dead-code.org/redir.php?target=wme
|
||||
* Copyright (c) 2003-2013 Jan Nedoma and contributors
|
||||
*/
|
||||
|
||||
#include "engines/wintermute/base/base_game.h"
|
||||
#include "engines/wintermute/base/gfx/opengl/base_render_opengl3d.h"
|
||||
#include "engines/wintermute/base/gfx/opengl/shadow_volume_opengl.h"
|
||||
#include "engines/wintermute/dcgf.h"
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ShadowVolumeOpenGL::ShadowVolumeOpenGL(BaseGame *inGame) : ShadowVolume(inGame) {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ShadowVolumeOpenGL::~ShadowVolumeOpenGL() {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolumeOpenGL::render() {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glVertexPointer(3, GL_FLOAT, 0, _vertices.data());
|
||||
glDrawArrays(GL_TRIANGLES, 0, _vertices.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolumeOpenGL::renderToStencilBuffer() {
|
||||
// Disable z-buffer writes (note: z-testing still occurs), and enable the
|
||||
// stencil-buffer
|
||||
glDepthMask(GL_FALSE);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
// Set up stencil compare fuction, reference value, and masks.
|
||||
// Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true.
|
||||
// Note: since we set up the stencil-test to always pass, the STENCILFAIL
|
||||
// renderstate is really not needed.
|
||||
glStencilFunc(GL_ALWAYS, 0x1, 0xFFFFFFFF);
|
||||
|
||||
glShadeModel(GL_FLAT);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// Make sure that no pixels get drawn to the frame buffer
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ZERO, GL_ONE);
|
||||
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
|
||||
|
||||
// Draw back-side of shadow volume in stencil/z only
|
||||
glCullFace(GL_FRONT);
|
||||
render();
|
||||
|
||||
// // Decrement stencil buffer value
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
|
||||
|
||||
// Draw front-side of shadow volume in stencil/z only
|
||||
glCullFace(GL_BACK);
|
||||
render();
|
||||
|
||||
// // Restore render states
|
||||
glEnable(GL_LIGHTING);
|
||||
glFrontFace(GL_CCW);
|
||||
glShadeModel(GL_SMOOTH);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolumeOpenGL::renderToScene() {
|
||||
initMask();
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Only write where stencil val >= 1 (count indicates # of shadows that overlap that pixel)
|
||||
glStencilFunc(GL_LEQUAL, 0x1, 0xFFFFFFFF);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
|
||||
glDisable(GL_FOG);
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
|
||||
_gameRef->_renderer3D->setProjection2D();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
// Draw a big, gray square
|
||||
glInterleavedArrays(GL_C4UB_V3F, 0, _shadowMask);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// Restore render states
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
_gameRef->_renderer3D->setup3D(nullptr, true);
|
||||
|
||||
// clear stencil buffer
|
||||
glClearStencil(0);
|
||||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolumeOpenGL::initMask() {
|
||||
Rect32 viewport = _gameRef->_renderer->getViewPort();
|
||||
|
||||
_shadowMask[0].x = viewport.left;
|
||||
_shadowMask[0].y = viewport.bottom;
|
||||
_shadowMask[0].z = 0.0f;
|
||||
|
||||
_shadowMask[1].x = viewport.left;
|
||||
_shadowMask[1].y = viewport.top;
|
||||
_shadowMask[1].z = 0.0f;
|
||||
|
||||
_shadowMask[2].x = viewport.right;
|
||||
_shadowMask[2].y = viewport.bottom;
|
||||
_shadowMask[2].z = 0.0f;
|
||||
|
||||
_shadowMask[3].x = viewport.right;
|
||||
_shadowMask[3].y = viewport.top;
|
||||
_shadowMask[3].z = 0.0f;
|
||||
|
||||
byte a = RGBCOLGetA(_color);
|
||||
byte r = RGBCOLGetR(_color);
|
||||
byte g = RGBCOLGetG(_color);
|
||||
byte b = RGBCOLGetB(_color);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
_shadowMask[i].r = r;
|
||||
_shadowMask[i].g = g;
|
||||
_shadowMask[i].b = b;
|
||||
_shadowMask[i].a = a;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Wintermute
|
52
engines/wintermute/base/gfx/opengl/shadow_volume_opengl.h
Normal file
52
engines/wintermute/base/gfx/opengl/shadow_volume_opengl.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is based on WME.
|
||||
* http://dead-code.org/redir.php?target=wme
|
||||
* Copyright (c) 2003-2013 Jan Nedoma and contributors
|
||||
*/
|
||||
|
||||
#ifndef WINTERMUTE_SHADOW_VOLUME_OPENGL_H
|
||||
#define WINTERMUTE_SHADOW_VOLUME_OPENGL_H
|
||||
|
||||
#include "engines/wintermute/base/gfx/opengl/shadow_volume.h"
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
class ShadowVolumeOpenGL : public ShadowVolume {
|
||||
public:
|
||||
ShadowVolumeOpenGL(BaseGame *inGame);
|
||||
virtual ~ShadowVolumeOpenGL();
|
||||
|
||||
bool renderToStencilBuffer() override;
|
||||
bool renderToScene() override;
|
||||
|
||||
private:
|
||||
bool render();
|
||||
ShadowVertex _shadowMask[4];
|
||||
bool initMask();
|
||||
};
|
||||
|
||||
} // namespace Wintermute
|
||||
|
||||
#endif
|
|
@ -0,0 +1,205 @@
|
|||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is based on WME.
|
||||
* http://dead-code.org/redir.php?target=wme
|
||||
* Copyright (c) 2003-2013 Jan Nedoma and contributors
|
||||
*/
|
||||
|
||||
#include "engines/wintermute/base/base_game.h"
|
||||
#include "engines/wintermute/base/gfx/opengl/base_render_opengl3d.h"
|
||||
#include "engines/wintermute/base/gfx/opengl/shadow_volume_opengl_shader.h"
|
||||
#include "engines/wintermute/dcgf.h"
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
#include "common/pack-start.h"
|
||||
|
||||
struct ShadowVertexShader {
|
||||
float x;
|
||||
float y;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
#include "common/pack-end.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ShadowVolumeOpenGLShader::ShadowVolumeOpenGLShader(BaseGame *inGame, OpenGL::Shader *volumeShader, OpenGL::Shader *maskShader)
|
||||
: ShadowVolume(inGame), _color(0x7f000000), _volumeShader(volumeShader), _maskShader(maskShader) {
|
||||
ShadowVertexShader shadowMask[4];
|
||||
Rect32 viewport = _gameRef->_renderer->getViewPort();
|
||||
|
||||
shadowMask[0].x = viewport.left;
|
||||
shadowMask[0].y = viewport.bottom;
|
||||
|
||||
shadowMask[1].x = viewport.left;
|
||||
shadowMask[1].y = viewport.top;
|
||||
|
||||
shadowMask[2].x = viewport.right;
|
||||
shadowMask[2].y = viewport.bottom;
|
||||
|
||||
shadowMask[3].x = viewport.right;
|
||||
shadowMask[3].y = viewport.top;
|
||||
|
||||
glGenBuffers(1, &_shadowMaskVertexBuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _shadowMaskVertexBuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(ShadowVertexShader), shadowMask, GL_DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ShadowVolumeOpenGLShader::~ShadowVolumeOpenGLShader() {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolumeOpenGLShader::render() {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDrawArrays(GL_TRIANGLES, 0, _vertices.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolumeOpenGLShader::renderToStencilBuffer() {
|
||||
// since the vertex count of the volume might change,
|
||||
// we just create a new buffer per frame
|
||||
// we might as well use the number of vertices of the mesh as an upper bound
|
||||
// or get rid of this completely by moving everything onto the gpu
|
||||
glDeleteBuffers(1, &_shadowVolumeVertexBuffer);
|
||||
glGenBuffers(1, &_shadowVolumeVertexBuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _shadowVolumeVertexBuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, 12 * _vertices.size(), _vertices.data(), GL_STATIC_DRAW);
|
||||
|
||||
_volumeShader->enableVertexAttribute("position", _shadowVolumeVertexBuffer, 3, GL_FLOAT, false, 12, 0);
|
||||
_volumeShader->use(true);
|
||||
|
||||
// Disable z-buffer writes (note: z-testing still occurs), and enable the
|
||||
// stencil-buffer
|
||||
glDepthMask(GL_FALSE);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
// Set up stencil compare fuction, reference value, and masks.
|
||||
// Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true.
|
||||
// Note: since we set up the stencil-test to always pass, the STENCILFAIL
|
||||
// renderstate is really not needed.
|
||||
glStencilFunc(GL_ALWAYS, 0x1, 0xFFFFFFFF);
|
||||
|
||||
glShadeModel(GL_FLAT);
|
||||
|
||||
// Make sure that no pixels get drawn to the frame buffer
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ZERO, GL_ONE);
|
||||
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
|
||||
|
||||
// Draw back-side of shadow volume in stencil/z only
|
||||
glCullFace(GL_FRONT);
|
||||
render();
|
||||
|
||||
// // Decrement stencil buffer value
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
|
||||
|
||||
// Draw front-side of shadow volume in stencil/z only
|
||||
glCullFace(GL_BACK);
|
||||
render();
|
||||
|
||||
// // Restore render states
|
||||
glFrontFace(GL_CCW);
|
||||
glShadeModel(GL_SMOOTH);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolumeOpenGLShader::renderToScene() {
|
||||
initMask();
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Only write where stencil val >= 1 (count indicates # of shadows that overlap that pixel)
|
||||
glStencilFunc(GL_LEQUAL, 0x1, 0xFFFFFFFF);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
|
||||
_gameRef->_renderer3D->setProjection2D();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
_maskShader->enableVertexAttribute("position", _shadowMaskVertexBuffer, 2, GL_FLOAT, false, 8, 0);
|
||||
_maskShader->use(true);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// Restore render states
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
_gameRef->_renderer3D->setup3D(nullptr, true);
|
||||
|
||||
// clear stencil buffer
|
||||
glClearStencil(0);
|
||||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ShadowVolumeOpenGLShader::initMask() {
|
||||
Rect32 viewport = _gameRef->_renderer->getViewPort();
|
||||
|
||||
ShadowVertexShader shadowMask[4];
|
||||
|
||||
shadowMask[0].x = viewport.left;
|
||||
shadowMask[0].y = viewport.bottom;
|
||||
|
||||
shadowMask[1].x = viewport.left;
|
||||
shadowMask[1].y = viewport.top;
|
||||
|
||||
shadowMask[2].x = viewport.right;
|
||||
shadowMask[2].y = viewport.bottom;
|
||||
|
||||
shadowMask[3].x = viewport.right;
|
||||
shadowMask[3].y = viewport.top;
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _shadowMaskVertexBuffer);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * sizeof(ShadowVertexShader), shadowMask);
|
||||
|
||||
Math::Vector4d color;
|
||||
|
||||
color.x() = RGBCOLGetR(_color) / 255.0f;
|
||||
color.y() = RGBCOLGetG(_color) / 255.0f;
|
||||
color.z() = RGBCOLGetB(_color) / 255.0f;
|
||||
color.w() = RGBCOLGetA(_color) / 255.0f;
|
||||
|
||||
_maskShader->use();
|
||||
_maskShader->setUniform("color", color);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Wintermute
|
|
@ -0,0 +1,58 @@
|
|||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is based on WME.
|
||||
* http://dead-code.org/redir.php?target=wme
|
||||
* Copyright (c) 2003-2013 Jan Nedoma and contributors
|
||||
*/
|
||||
|
||||
#ifndef WINTERMUTE_SHADOW_VOLUME_OPENGL_SHADER_H
|
||||
#define WINTERMUTE_SHADOW_VOLUME_OPENGL_SHADER_H
|
||||
|
||||
#include "engines/wintermute/base/gfx/opengl/shadow_volume.h"
|
||||
#include "graphics/opengl/shader.h"
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
class ShadowVolumeOpenGLShader : public ShadowVolume {
|
||||
public:
|
||||
ShadowVolumeOpenGLShader(BaseGame *inGame, OpenGL::Shader *volumeShader, OpenGL::Shader *maskShader);
|
||||
virtual ~ShadowVolumeOpenGLShader();
|
||||
|
||||
bool renderToStencilBuffer() override;
|
||||
bool renderToScene() override;
|
||||
|
||||
private:
|
||||
bool render();
|
||||
uint32 _color;
|
||||
bool initMask() override;
|
||||
GLuint _shadowVolumeVertexBuffer;
|
||||
GLuint _shadowMaskVertexBuffer;
|
||||
OpenGL::Shader *_volumeShader;
|
||||
OpenGL::Shader *_maskShader;
|
||||
};
|
||||
|
||||
} // namespace Wintermute
|
||||
|
||||
#endif
|
|
@ -87,6 +87,8 @@ MODULE_OBJS := \
|
|||
base/gfx/opengl/mesh3ds_opengl_shader.o \
|
||||
base/gfx/opengl/loader3ds.o \
|
||||
base/gfx/opengl/shadow_volume.o \
|
||||
base/gfx/opengl/shadow_volume_opengl.o \
|
||||
base/gfx/opengl/shadow_volume_opengl_shader.o \
|
||||
base/gfx/x/active_animation.o \
|
||||
base/gfx/x/animation.o \
|
||||
base/gfx/x/animation_channel.o \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue