2012-01-29 02:22:22 +01:00
|
|
|
/* Copyright (C) 2012 Wildfire Games.
|
2009-04-18 19:00:33 +02:00
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. 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.
|
|
|
|
*
|
|
|
|
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2009-04-18 19:51:05 +02:00
|
|
|
/*
|
|
|
|
* higher level interface on top of OpenGL to render basic objects:
|
|
|
|
* terrain, models, sprites, particles etc.
|
2007-05-07 18:33:24 +02:00
|
|
|
*/
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2004-06-03 20:38:14 +02:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
#include <map>
|
|
|
|
#include <set>
|
|
|
|
#include <algorithm>
|
2011-01-29 17:31:48 +01:00
|
|
|
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
|
|
|
|
#include "Renderer.h"
|
|
|
|
|
2007-05-09 23:01:11 +02:00
|
|
|
#include "lib/bits.h" // is_pow2
|
2007-12-22 19:15:52 +01:00
|
|
|
#include "lib/res/graphics/ogl_tex.h"
|
2011-04-29 21:10:34 +02:00
|
|
|
#include "lib/allocators/shared_ptr.h"
|
2006-06-02 04:10:27 +02:00
|
|
|
#include "maths/Matrix3D.h"
|
|
|
|
#include "maths/MathUtil.h"
|
|
|
|
#include "ps/CLogger.h"
|
2012-01-29 02:22:22 +01:00
|
|
|
#include "ps/ConfigDB.h"
|
2004-09-26 06:05:35 +02:00
|
|
|
#include "ps/Game.h"
|
2006-06-02 04:10:27 +02:00
|
|
|
#include "ps/Profile.h"
|
2007-12-22 19:15:52 +01:00
|
|
|
#include "ps/Filesystem.h"
|
2006-06-02 04:10:27 +02:00
|
|
|
#include "ps/World.h"
|
2007-12-20 21:21:45 +01:00
|
|
|
#include "ps/Loader.h"
|
|
|
|
#include "ps/ProfileViewer.h"
|
|
|
|
#include "graphics/Camera.h"
|
2011-03-26 21:17:21 +01:00
|
|
|
#include "graphics/GameView.h"
|
2007-12-20 21:21:45 +01:00
|
|
|
#include "graphics/LightEnv.h"
|
2012-04-03 20:44:46 +02:00
|
|
|
#include "graphics/MaterialManager.h"
|
2007-12-20 21:21:45 +01:00
|
|
|
#include "graphics/Model.h"
|
|
|
|
#include "graphics/ModelDef.h"
|
2011-04-03 21:15:15 +02:00
|
|
|
#include "graphics/ParticleManager.h"
|
2011-03-26 21:17:21 +01:00
|
|
|
#include "graphics/ShaderManager.h"
|
|
|
|
#include "graphics/Terrain.h"
|
|
|
|
#include "graphics/Texture.h"
|
|
|
|
#include "graphics/TextureManager.h"
|
2005-10-25 03:43:07 +02:00
|
|
|
#include "renderer/HWLightingModelRenderer.h"
|
2005-10-30 02:22:22 +02:00
|
|
|
#include "renderer/InstancingModelRenderer.h"
|
2005-10-25 03:43:07 +02:00
|
|
|
#include "renderer/ModelRenderer.h"
|
2010-01-09 20:20:14 +01:00
|
|
|
#include "renderer/OverlayRenderer.h"
|
2011-04-03 21:15:15 +02:00
|
|
|
#include "renderer/ParticleRenderer.h"
|
2005-10-25 03:43:07 +02:00
|
|
|
#include "renderer/RenderModifiers.h"
|
2006-01-07 02:04:26 +01:00
|
|
|
#include "renderer/ShadowMap.h"
|
2010-01-09 20:20:14 +01:00
|
|
|
#include "renderer/SkyManager.h"
|
2006-04-17 22:02:51 +02:00
|
|
|
#include "renderer/TerrainOverlay.h"
|
2006-01-07 02:04:26 +01:00
|
|
|
#include "renderer/TerrainRenderer.h"
|
2011-03-13 19:58:09 +01:00
|
|
|
#include "renderer/VertexBufferManager.h"
|
2006-01-07 02:04:26 +01:00
|
|
|
#include "renderer/WaterManager.h"
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CRendererStatsTable - Profile display of rendering stats
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class CRendererStatsTable: Implementation of AbstractProfileTable to
|
|
|
|
* display the renderer stats in-game.
|
|
|
|
*
|
|
|
|
* Accesses CRenderer::m_Stats by keeping the reference passed to the
|
|
|
|
* constructor.
|
|
|
|
*/
|
2009-01-03 19:40:28 +01:00
|
|
|
class CRendererStatsTable : public AbstractProfileTable
|
2005-11-19 17:15:34 +01:00
|
|
|
{
|
2009-01-03 19:40:28 +01:00
|
|
|
NONCOPYABLE(CRendererStatsTable);
|
2005-11-19 17:15:34 +01:00
|
|
|
public:
|
|
|
|
CRendererStatsTable(const CRenderer::Stats& st);
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
// Implementation of AbstractProfileTable interface
|
|
|
|
CStr GetName();
|
|
|
|
CStr GetTitle();
|
2006-09-01 08:59:29 +02:00
|
|
|
size_t GetNumberRows();
|
2005-11-19 17:15:34 +01:00
|
|
|
const std::vector<ProfileColumn>& GetColumns();
|
2006-09-01 08:59:29 +02:00
|
|
|
CStr GetCellText(size_t row, size_t col);
|
|
|
|
AbstractProfileTable* GetChild(size_t row);
|
2005-11-19 17:15:34 +01:00
|
|
|
|
|
|
|
private:
|
|
|
|
/// Reference to the renderer singleton's stats
|
|
|
|
const CRenderer::Stats& Stats;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
/// Column descriptions
|
|
|
|
std::vector<ProfileColumn> columnDescriptions;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
enum {
|
2011-04-29 14:26:31 +02:00
|
|
|
Row_DrawCalls = 0,
|
2005-11-19 17:15:34 +01:00
|
|
|
Row_TerrainTris,
|
2011-07-13 01:48:05 +02:00
|
|
|
Row_WaterTris,
|
2005-11-19 17:15:34 +01:00
|
|
|
Row_ModelTris,
|
2011-07-30 02:56:45 +02:00
|
|
|
Row_OverlayTris,
|
2005-11-19 17:15:34 +01:00
|
|
|
Row_BlendSplats,
|
2011-04-29 14:26:31 +02:00
|
|
|
Row_Particles,
|
2011-03-13 19:58:09 +01:00
|
|
|
Row_VBReserved,
|
|
|
|
Row_VBAllocated,
|
2012-04-03 20:44:46 +02:00
|
|
|
Row_ShadersLoaded,
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
// Must be last to count number of rows
|
|
|
|
NumberRows
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
// Construction
|
|
|
|
CRendererStatsTable::CRendererStatsTable(const CRenderer::Stats& st)
|
|
|
|
: Stats(st)
|
|
|
|
{
|
|
|
|
columnDescriptions.push_back(ProfileColumn("Name", 230));
|
|
|
|
columnDescriptions.push_back(ProfileColumn("Value", 100));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implementation of AbstractProfileTable interface
|
|
|
|
CStr CRendererStatsTable::GetName()
|
|
|
|
{
|
|
|
|
return "renderer";
|
|
|
|
}
|
|
|
|
|
|
|
|
CStr CRendererStatsTable::GetTitle()
|
|
|
|
{
|
|
|
|
return "Renderer statistics";
|
|
|
|
}
|
|
|
|
|
2006-09-01 08:59:29 +02:00
|
|
|
size_t CRendererStatsTable::GetNumberRows()
|
2005-11-19 17:15:34 +01:00
|
|
|
{
|
|
|
|
return NumberRows;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::vector<ProfileColumn>& CRendererStatsTable::GetColumns()
|
|
|
|
{
|
|
|
|
return columnDescriptions;
|
|
|
|
}
|
|
|
|
|
2006-09-01 08:59:29 +02:00
|
|
|
CStr CRendererStatsTable::GetCellText(size_t row, size_t col)
|
2005-11-19 17:15:34 +01:00
|
|
|
{
|
|
|
|
char buf[256];
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
switch(row)
|
|
|
|
{
|
|
|
|
case Row_DrawCalls:
|
|
|
|
if (col == 0)
|
|
|
|
return "# draw calls";
|
2009-11-06 11:59:10 +01:00
|
|
|
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_DrawCalls);
|
2005-11-19 17:15:34 +01:00
|
|
|
return buf;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
case Row_TerrainTris:
|
|
|
|
if (col == 0)
|
|
|
|
return "# terrain tris";
|
2009-11-06 11:59:10 +01:00
|
|
|
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_TerrainTris);
|
2005-11-19 17:15:34 +01:00
|
|
|
return buf;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
case Row_WaterTris:
|
|
|
|
if (col == 0)
|
|
|
|
return "# water tris";
|
|
|
|
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_WaterTris);
|
|
|
|
return buf;
|
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
case Row_ModelTris:
|
|
|
|
if (col == 0)
|
|
|
|
return "# model tris";
|
2009-11-06 11:59:10 +01:00
|
|
|
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_ModelTris);
|
2005-11-19 17:15:34 +01:00
|
|
|
return buf;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2011-07-30 02:56:45 +02:00
|
|
|
case Row_OverlayTris:
|
|
|
|
if (col == 0)
|
|
|
|
return "# overlay tris";
|
|
|
|
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_OverlayTris);
|
|
|
|
return buf;
|
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
case Row_BlendSplats:
|
|
|
|
if (col == 0)
|
|
|
|
return "# blend splats";
|
2009-11-06 11:59:10 +01:00
|
|
|
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_BlendSplats);
|
2005-11-19 17:15:34 +01:00
|
|
|
return buf;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2011-04-29 14:26:31 +02:00
|
|
|
case Row_Particles:
|
|
|
|
if (col == 0)
|
|
|
|
return "# particles";
|
|
|
|
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_Particles);
|
|
|
|
return buf;
|
|
|
|
|
2011-03-13 19:58:09 +01:00
|
|
|
case Row_VBReserved:
|
|
|
|
if (col == 0)
|
|
|
|
return "VB bytes reserved";
|
|
|
|
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_VBMan.GetBytesReserved());
|
|
|
|
return buf;
|
|
|
|
|
|
|
|
case Row_VBAllocated:
|
|
|
|
if (col == 0)
|
|
|
|
return "VB bytes allocated";
|
|
|
|
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_VBMan.GetBytesAllocated());
|
|
|
|
return buf;
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
case Row_ShadersLoaded:
|
|
|
|
if (col == 0)
|
|
|
|
return "shader effects loaded";
|
|
|
|
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_Renderer.GetShaderManager().GetNumEffectsLoaded());
|
|
|
|
return buf;
|
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
default:
|
|
|
|
return "???";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-01 08:59:29 +02:00
|
|
|
AbstractProfileTable* CRendererStatsTable::GetChild(size_t UNUSED(row))
|
2005-11-19 17:15:34 +01:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CRenderer implementation
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Struct CRendererInternals: Truly hide data that is supposed to be hidden
|
|
|
|
* in this structure so it won't even appear in header files.
|
|
|
|
*/
|
2009-01-03 19:40:28 +01:00
|
|
|
struct CRendererInternals
|
2005-11-19 17:15:34 +01:00
|
|
|
{
|
2009-01-03 19:40:28 +01:00
|
|
|
NONCOPYABLE(CRendererInternals);
|
|
|
|
public:
|
2006-03-28 21:45:44 +02:00
|
|
|
/// true if CRenderer::Open has been called
|
|
|
|
bool IsOpen;
|
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
/// true if shaders need to be reloaded
|
|
|
|
bool ShadersDirty;
|
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
/// Table to display renderer stats in-game via profile system
|
|
|
|
CRendererStatsTable profileTable;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
/// Shader manager
|
|
|
|
CShaderManager shaderManager;
|
|
|
|
|
2006-01-07 02:04:26 +01:00
|
|
|
/// Water manager
|
|
|
|
WaterManager waterManager;
|
|
|
|
|
2006-05-16 06:41:37 +02:00
|
|
|
/// Sky manager
|
|
|
|
SkyManager skyManager;
|
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
/// Texture manager
|
|
|
|
CTextureManager textureManager;
|
|
|
|
|
2006-01-07 02:04:26 +01:00
|
|
|
/// Terrain renderer
|
2012-04-03 20:44:46 +02:00
|
|
|
TerrainRenderer terrainRenderer;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
/// Overlay renderer
|
|
|
|
OverlayRenderer overlayRenderer;
|
|
|
|
|
2011-04-03 21:15:15 +02:00
|
|
|
/// Particle manager
|
|
|
|
CParticleManager particleManager;
|
|
|
|
|
|
|
|
/// Particle renderer
|
|
|
|
ParticleRenderer particleRenderer;
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
/// Material manager
|
|
|
|
CMaterialManager materialManager;
|
|
|
|
|
2006-01-07 02:04:26 +01:00
|
|
|
/// Shadow map
|
2012-04-03 20:44:46 +02:00
|
|
|
ShadowMap shadow;
|
2006-01-22 20:12:30 +01:00
|
|
|
|
2006-03-26 01:54:20 +01:00
|
|
|
/// Various model renderers
|
2012-04-03 20:44:46 +02:00
|
|
|
struct Models
|
|
|
|
{
|
|
|
|
// NOTE: The current renderer design (with ModelRenderer, ModelVertexRenderer,
|
|
|
|
// RenderModifier, etc) is mostly a relic of an older design that implemented
|
|
|
|
// the different materials and rendering modes through extensive subclassing
|
|
|
|
// and hooking objects together in various combinations.
|
|
|
|
// The new design uses the CShaderManager API to abstract away the details
|
|
|
|
// of rendering, and uses a data-driven approach to materials, so there are
|
|
|
|
// now a small number of generic subclasses instead of many specialised subclasses,
|
|
|
|
// but most of the old infrastructure hasn't been refactored out yet and leads to
|
|
|
|
// some unwanted complexity.
|
|
|
|
|
|
|
|
// Submitted models are split on two axes:
|
|
|
|
// - Normal vs Transp[arent] - alpha-blended models are stored in a separate
|
|
|
|
// list so we can draw them above/below the alpha-blended water plane correctly
|
2012-04-12 17:43:59 +02:00
|
|
|
// - Skinned vs Unskinned - with hardware lighting we don't need to
|
2012-04-03 20:44:46 +02:00
|
|
|
// duplicate mesh data per model instance (except for skinned models),
|
|
|
|
// so non-skinned models get different ModelVertexRenderers
|
|
|
|
|
2012-04-12 17:43:59 +02:00
|
|
|
ModelRendererPtr NormalSkinned;
|
|
|
|
ModelRendererPtr NormalUnskinned; // == NormalSkinned if unskinned shader instancing not supported
|
|
|
|
ModelRendererPtr TranspSkinned;
|
|
|
|
ModelRendererPtr TranspUnskinned; // == TranspSkinned if unskinned shader instancing not supported
|
2012-04-03 20:44:46 +02:00
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
ModelVertexRendererPtr VertexRendererShader;
|
|
|
|
ModelVertexRendererPtr VertexInstancingShader;
|
2012-04-12 17:43:59 +02:00
|
|
|
ModelVertexRendererPtr VertexGPUSkinningShader;
|
2006-03-26 01:54:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
LitRenderModifierPtr ModShader;
|
2006-03-26 01:54:20 +01:00
|
|
|
} Model;
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
CShaderDefines globalContext;
|
2006-03-26 01:54:20 +01:00
|
|
|
|
2011-04-03 21:15:15 +02:00
|
|
|
CRendererInternals() :
|
|
|
|
IsOpen(false), ShadersDirty(true), profileTable(g_Renderer.m_Stats), textureManager(g_VFS, false, false)
|
2005-11-19 17:15:34 +01:00
|
|
|
{
|
|
|
|
}
|
2006-03-31 04:19:11 +02:00
|
|
|
|
2006-09-14 22:06:21 +02:00
|
|
|
/**
|
2011-04-07 00:07:13 +02:00
|
|
|
* Load the OpenGL projection and modelview matrices and the viewport according
|
|
|
|
* to the given camera.
|
|
|
|
*/
|
2006-09-14 22:06:21 +02:00
|
|
|
void SetOpenGLCamera(const CCamera& camera)
|
|
|
|
{
|
|
|
|
CMatrix3D view;
|
|
|
|
camera.m_Orientation.GetInverse(view);
|
|
|
|
const CMatrix3D& proj = camera.GetProjection();
|
|
|
|
|
2012-02-13 16:06:25 +01:00
|
|
|
#if CONFIG2_GLES
|
2012-02-13 17:41:17 +01:00
|
|
|
#warning TODO: fix CRenderer camera handling for GLES (do not use global matrixes)
|
2012-02-13 16:06:25 +01:00
|
|
|
#else
|
2006-09-14 22:06:21 +02:00
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glLoadMatrixf(&proj._11);
|
|
|
|
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glLoadMatrixf(&view._11);
|
2012-02-13 16:06:25 +01:00
|
|
|
#endif
|
2006-09-14 22:06:21 +02:00
|
|
|
|
|
|
|
const SViewPort &vp = camera.GetViewPort();
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
glViewport((GLint)vp.m_X,(GLint)vp.m_Y,(GLsizei)vp.m_Width,(GLsizei)vp.m_Height);
|
2006-09-14 22:06:21 +02:00
|
|
|
}
|
2011-03-18 17:57:54 +01:00
|
|
|
|
|
|
|
/**
|
2012-04-03 20:44:46 +02:00
|
|
|
* Renders all non-alpha-blended models with the given context.
|
2011-03-18 17:57:54 +01:00
|
|
|
*/
|
2012-04-03 20:44:46 +02:00
|
|
|
void CallModelRenderers(const CShaderDefines& context, int flags)
|
2011-03-18 17:57:54 +01:00
|
|
|
{
|
2012-04-12 17:43:59 +02:00
|
|
|
CShaderDefines contextSkinned = context;
|
|
|
|
if (g_Renderer.m_Options.m_GPUSkinning)
|
|
|
|
{
|
|
|
|
contextSkinned.Add("USE_INSTANCING", "1");
|
|
|
|
contextSkinned.Add("USE_GPU_SKINNING", "1");
|
|
|
|
}
|
|
|
|
Model.NormalSkinned->Render(Model.ModShader, contextSkinned, flags);
|
2011-03-18 17:57:54 +01:00
|
|
|
|
2012-04-12 17:43:59 +02:00
|
|
|
if (Model.NormalUnskinned != Model.NormalSkinned)
|
|
|
|
{
|
|
|
|
CShaderDefines contextUnskinned = context;
|
|
|
|
contextUnskinned.Add("USE_INSTANCING", "1");
|
|
|
|
Model.NormalUnskinned->Render(Model.ModShader, contextUnskinned, flags);
|
|
|
|
}
|
2011-03-18 17:57:54 +01:00
|
|
|
}
|
2011-07-13 01:48:05 +02:00
|
|
|
|
|
|
|
/**
|
2012-04-03 20:44:46 +02:00
|
|
|
* Renders all alpha-blended models with the given context.
|
|
|
|
*/
|
|
|
|
void CallTranspModelRenderers(const CShaderDefines& context, int flags)
|
|
|
|
{
|
2012-04-12 17:43:59 +02:00
|
|
|
CShaderDefines contextSkinned = context;
|
|
|
|
if (g_Renderer.m_Options.m_GPUSkinning)
|
|
|
|
{
|
|
|
|
contextSkinned.Add("USE_INSTANCING", "1");
|
|
|
|
contextSkinned.Add("USE_GPU_SKINNING", "1");
|
|
|
|
}
|
|
|
|
Model.TranspSkinned->Render(Model.ModShader, contextSkinned, flags);
|
2012-04-03 20:44:46 +02:00
|
|
|
|
2012-04-12 17:43:59 +02:00
|
|
|
if (Model.TranspUnskinned != Model.TranspSkinned)
|
|
|
|
{
|
|
|
|
CShaderDefines contextUnskinned = context;
|
|
|
|
contextUnskinned.Add("USE_INSTANCING", "1");
|
|
|
|
Model.TranspUnskinned->Render(Model.ModShader, contextUnskinned, flags);
|
|
|
|
}
|
2012-04-03 20:44:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Filters all non-alpha-blended models.
|
2011-07-13 01:48:05 +02:00
|
|
|
*/
|
|
|
|
void FilterModels(CModelFilter& filter, int passed, int flags = 0)
|
|
|
|
{
|
2012-04-12 17:43:59 +02:00
|
|
|
Model.NormalSkinned->Filter(filter, passed, flags);
|
|
|
|
if (Model.NormalUnskinned != Model.NormalSkinned)
|
|
|
|
Model.NormalUnskinned->Filter(filter, passed, flags);
|
2012-04-03 20:44:46 +02:00
|
|
|
}
|
2011-07-13 01:48:05 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
/**
|
|
|
|
* Filters all alpha-blended models.
|
|
|
|
*/
|
|
|
|
void FilterTranspModels(CModelFilter& filter, int passed, int flags = 0)
|
|
|
|
{
|
2012-04-12 17:43:59 +02:00
|
|
|
Model.TranspSkinned->Filter(filter, passed, flags);
|
|
|
|
if (Model.TranspUnskinned != Model.TranspSkinned)
|
|
|
|
Model.TranspUnskinned->Filter(filter, passed, flags);
|
2011-07-13 01:48:05 +02:00
|
|
|
}
|
2005-11-19 17:15:34 +01:00
|
|
|
};
|
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
2010-06-20 00:07:27 +02:00
|
|
|
// CRenderer constructor
|
2004-06-07 21:53:58 +02:00
|
|
|
CRenderer::CRenderer()
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
2005-11-19 17:15:34 +01:00
|
|
|
m = new CRendererInternals;
|
2006-01-07 02:04:26 +01:00
|
|
|
m_WaterManager = &m->waterManager;
|
2006-05-16 06:41:37 +02:00
|
|
|
m_SkyManager = &m->skyManager;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
g_ProfileViewer.AddRootTable(&m->profileTable);
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2012-04-24 00:57:51 +02:00
|
|
|
m_Width = 0;
|
|
|
|
m_Height = 0;
|
|
|
|
m_TerrainRenderMode = SOLID;
|
|
|
|
m_ModelRenderMode = SOLID;
|
|
|
|
m_ClearColor[0] = m_ClearColor[1] = m_ClearColor[2] = m_ClearColor[3] = 0;
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2006-01-22 20:12:30 +01:00
|
|
|
m_DisplayFrustum = false;
|
2011-01-29 17:31:48 +01:00
|
|
|
m_DisplayTerrainPriorities = false;
|
2006-09-23 18:04:54 +02:00
|
|
|
m_SkipSubmit = false;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2010-06-20 00:07:27 +02:00
|
|
|
m_Options.m_NoVBO = false;
|
2005-10-05 18:42:09 +02:00
|
|
|
m_Options.m_RenderPath = RP_DEFAULT;
|
2006-09-08 00:16:15 +02:00
|
|
|
m_Options.m_FancyWater = false;
|
2010-06-20 00:07:27 +02:00
|
|
|
m_Options.m_Shadows = false;
|
2011-03-07 01:34:03 +01:00
|
|
|
m_Options.m_ShadowAlphaFix = true;
|
2011-03-26 21:17:21 +01:00
|
|
|
m_Options.m_ARBProgramShadow = true;
|
2011-07-13 01:48:05 +02:00
|
|
|
m_Options.m_ShadowPCF = false;
|
2012-04-24 00:57:51 +02:00
|
|
|
m_Options.m_Particles = false;
|
2012-01-29 02:22:22 +01:00
|
|
|
m_Options.m_PreferGLSL = false;
|
2012-04-10 23:03:22 +02:00
|
|
|
m_Options.m_ForceAlphaTest = false;
|
2012-04-12 17:43:59 +02:00
|
|
|
m_Options.m_GPUSkinning = false;
|
2012-01-29 02:22:22 +01:00
|
|
|
|
|
|
|
// TODO: be more consistent in use of the config system
|
|
|
|
CFG_GET_USER_VAL("preferglsl", Bool, m_Options.m_PreferGLSL);
|
2012-04-10 23:03:22 +02:00
|
|
|
CFG_GET_USER_VAL("forcealphatest", Bool, m_Options.m_ForceAlphaTest);
|
2012-04-12 17:43:59 +02:00
|
|
|
CFG_GET_USER_VAL("gpuskinning", Bool, m_Options.m_GPUSkinning);
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2012-02-13 21:53:24 +01:00
|
|
|
#if CONFIG2_GLES
|
|
|
|
// Override config option since GLES only supports GLSL
|
|
|
|
m_Options.m_PreferGLSL = true;
|
|
|
|
#endif
|
|
|
|
|
2006-07-17 02:59:32 +02:00
|
|
|
m_ShadowZBias = 0.02f;
|
2007-02-09 18:04:55 +01:00
|
|
|
m_ShadowMapSize = 0;
|
2006-03-26 19:36:33 +02:00
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
m_LightEnv = NULL;
|
|
|
|
|
2011-04-05 22:32:03 +02:00
|
|
|
m_CurrentScene = NULL;
|
|
|
|
|
2008-10-18 19:58:04 +02:00
|
|
|
m_hCompositeAlphaMap = 0;
|
2005-09-18 05:47:15 +02:00
|
|
|
|
2011-07-16 21:52:48 +02:00
|
|
|
m_Stats.Reset();
|
2012-04-24 00:57:51 +02:00
|
|
|
AddLocalProperty(L"particles", &m_Options.m_Particles, false);
|
2006-09-23 14:48:06 +02:00
|
|
|
AddLocalProperty(L"fancyWater", &m_Options.m_FancyWater, false);
|
|
|
|
AddLocalProperty(L"horizonHeight", &m->skyManager.m_HorizonHeight, false);
|
|
|
|
AddLocalProperty(L"waterMurkiness", &m->waterManager.m_Murkiness, false);
|
|
|
|
AddLocalProperty(L"waterReflTintStrength", &m->waterManager.m_ReflectionTintStrength, false);
|
|
|
|
AddLocalProperty(L"waterRepeatPeriod", &m->waterManager.m_RepeatPeriod, false);
|
|
|
|
AddLocalProperty(L"waterShininess", &m->waterManager.m_Shininess, false);
|
|
|
|
AddLocalProperty(L"waterSpecularStrength", &m->waterManager.m_SpecularStrength, false);
|
|
|
|
AddLocalProperty(L"waterWaviness", &m->waterManager.m_Waviness, false);
|
2011-01-29 17:31:48 +01:00
|
|
|
|
|
|
|
RegisterFileReloadFunc(ReloadChangedFileCB, this);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CRenderer destructor
|
2004-06-07 21:53:58 +02:00
|
|
|
CRenderer::~CRenderer()
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
2011-01-29 17:31:48 +01:00
|
|
|
UnregisterFileReloadFunc(ReloadChangedFileCB, this);
|
|
|
|
|
2005-10-31 19:57:03 +01:00
|
|
|
// we no longer UnloadAlphaMaps / UnloadWaterTextures here -
|
|
|
|
// that is the responsibility of the module that asked for
|
|
|
|
// them to be loaded (i.e. CGameView).
|
2005-11-19 17:15:34 +01:00
|
|
|
delete m;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-06-07 21:53:58 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
2004-05-30 02:46:58 +02:00
|
|
|
// EnumCaps: build card cap bits
|
|
|
|
void CRenderer::EnumCaps()
|
|
|
|
{
|
|
|
|
// assume support for nothing
|
2011-02-03 02:12:24 +01:00
|
|
|
m_Caps.m_VBO = false;
|
2011-03-26 21:17:21 +01:00
|
|
|
m_Caps.m_ARBProgram = false;
|
|
|
|
m_Caps.m_ARBProgramShadow = false;
|
2011-02-03 02:12:24 +01:00
|
|
|
m_Caps.m_VertexShader = false;
|
|
|
|
m_Caps.m_FragmentShader = false;
|
|
|
|
m_Caps.m_Shadows = false;
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// now start querying extensions
|
|
|
|
if (!m_Options.m_NoVBO) {
|
2007-05-02 14:07:08 +02:00
|
|
|
if (ogl_HaveExtension("GL_ARB_vertex_buffer_object")) {
|
2004-05-30 02:46:58 +02:00
|
|
|
m_Caps.m_VBO=true;
|
|
|
|
}
|
|
|
|
}
|
2011-02-03 02:12:24 +01:00
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
if (0 == ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", NULL))
|
|
|
|
{
|
|
|
|
m_Caps.m_ARBProgram = true;
|
|
|
|
if (ogl_HaveExtension("GL_ARB_fragment_program_shadow"))
|
|
|
|
m_Caps.m_ARBProgramShadow = true;
|
|
|
|
}
|
|
|
|
|
2009-08-04 18:08:41 +02:00
|
|
|
if (0 == ogl_HaveExtensions(0, "GL_ARB_shader_objects", "GL_ARB_shading_language_100", NULL))
|
2005-10-01 00:23:48 +02:00
|
|
|
{
|
2007-05-02 14:07:08 +02:00
|
|
|
if (ogl_HaveExtension("GL_ARB_vertex_shader"))
|
2011-03-26 21:17:21 +01:00
|
|
|
m_Caps.m_VertexShader = true;
|
2010-03-21 15:18:15 +01:00
|
|
|
if (ogl_HaveExtension("GL_ARB_fragment_shader"))
|
2011-03-26 21:17:21 +01:00
|
|
|
m_Caps.m_FragmentShader = true;
|
2005-10-01 00:23:48 +02:00
|
|
|
}
|
2006-02-15 01:45:16 +01:00
|
|
|
|
2012-03-19 22:10:14 +01:00
|
|
|
#if CONFIG2_GLES
|
|
|
|
m_Caps.m_Shadows = true;
|
|
|
|
#else
|
2011-04-07 00:08:51 +02:00
|
|
|
if (0 == ogl_HaveExtensions(0, "GL_ARB_shadow", "GL_ARB_depth_texture", "GL_EXT_framebuffer_object", NULL))
|
2011-02-03 02:12:24 +01:00
|
|
|
{
|
2006-02-15 01:45:16 +01:00
|
|
|
if (ogl_max_tex_units >= 4)
|
2011-04-07 00:08:51 +02:00
|
|
|
m_Caps.m_Shadows = true;
|
2006-03-26 19:36:33 +02:00
|
|
|
}
|
2012-03-19 22:10:14 +01:00
|
|
|
#endif
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2012-04-24 18:46:32 +02:00
|
|
|
CShaderDefines CRenderer::GetSystemShaderDefines()
|
|
|
|
{
|
|
|
|
CShaderDefines defines;
|
|
|
|
|
|
|
|
if (GetRenderPath() == RP_SHADER && m_Caps.m_ARBProgram)
|
|
|
|
defines.Add("SYS_HAS_ARB", "1");
|
|
|
|
|
|
|
|
if (GetRenderPath() == RP_SHADER && m_Caps.m_VertexShader && m_Caps.m_FragmentShader)
|
|
|
|
defines.Add("SYS_HAS_GLSL", "1");
|
|
|
|
|
|
|
|
if (m_Options.m_PreferGLSL)
|
|
|
|
defines.Add("SYS_PREFER_GLSL", "1");
|
|
|
|
|
|
|
|
return defines;
|
|
|
|
}
|
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
void CRenderer::ReloadShaders()
|
|
|
|
{
|
2011-04-30 15:01:45 +02:00
|
|
|
ENSURE(m->IsOpen);
|
2011-04-23 20:51:47 +02:00
|
|
|
|
2012-04-24 18:46:32 +02:00
|
|
|
m->globalContext = GetSystemShaderDefines();
|
2011-03-26 21:17:21 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
if (m_Caps.m_Shadows && m_Options.m_Shadows)
|
2011-03-26 21:17:21 +01:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
m->globalContext.Add("USE_SHADOW", "1");
|
2011-03-26 21:17:21 +01:00
|
|
|
if (m_Caps.m_ARBProgramShadow && m_Options.m_ARBProgramShadow)
|
2012-04-03 20:44:46 +02:00
|
|
|
m->globalContext.Add("USE_FP_SHADOW", "1");
|
2011-07-13 01:48:05 +02:00
|
|
|
if (m_Options.m_ShadowPCF)
|
2012-04-03 20:44:46 +02:00
|
|
|
m->globalContext.Add("USE_SHADOW_PCF", "1");
|
2012-03-19 22:10:14 +01:00
|
|
|
#if !CONFIG2_GLES
|
2012-04-03 20:44:46 +02:00
|
|
|
m->globalContext.Add("USE_SHADOW_SAMPLER", "1");
|
2012-03-19 22:10:14 +01:00
|
|
|
#endif
|
2011-03-26 21:17:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_LightEnv)
|
2012-04-03 20:44:46 +02:00
|
|
|
m->globalContext.Add(("LIGHTING_MODEL_" + m_LightEnv->GetLightingModel()).c_str(), "1");
|
|
|
|
|
|
|
|
m->Model.ModShader = LitRenderModifierPtr(new ShaderRenderModifier());
|
|
|
|
|
|
|
|
bool cpuLighting = (GetRenderPath() == RP_FIXED);
|
|
|
|
m->Model.VertexRendererShader = ModelVertexRendererPtr(new ShaderModelVertexRenderer(cpuLighting));
|
2012-04-12 17:43:59 +02:00
|
|
|
m->Model.VertexInstancingShader = ModelVertexRendererPtr(new InstancingModelRenderer(false));
|
2012-04-03 20:44:46 +02:00
|
|
|
|
2012-04-12 17:43:59 +02:00
|
|
|
if (GetRenderPath() == RP_SHADER && m_Options.m_GPUSkinning) // TODO: should check caps and GLSL etc too
|
|
|
|
{
|
|
|
|
m->Model.VertexGPUSkinningShader = ModelVertexRendererPtr(new InstancingModelRenderer(true));
|
|
|
|
m->Model.NormalSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexGPUSkinningShader));
|
|
|
|
m->Model.TranspSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexGPUSkinningShader));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m->Model.VertexGPUSkinningShader.reset();
|
|
|
|
m->Model.NormalSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader));
|
|
|
|
m->Model.TranspSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader));
|
|
|
|
}
|
2012-04-03 20:44:46 +02:00
|
|
|
|
|
|
|
// Use instancing renderers in shader mode
|
|
|
|
if (GetRenderPath() == RP_SHADER)
|
|
|
|
{
|
2012-04-12 17:43:59 +02:00
|
|
|
m->Model.NormalUnskinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
|
|
|
|
m->Model.TranspUnskinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
|
2012-04-03 20:44:46 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-04-12 17:43:59 +02:00
|
|
|
m->Model.NormalUnskinned = m->Model.NormalSkinned;
|
|
|
|
m->Model.TranspUnskinned = m->Model.TranspSkinned;
|
2012-04-03 20:44:46 +02:00
|
|
|
}
|
2011-03-26 21:17:21 +01:00
|
|
|
|
|
|
|
m->ShadersDirty = false;
|
|
|
|
}
|
2005-10-01 00:23:48 +02:00
|
|
|
|
2010-06-03 21:07:59 +02:00
|
|
|
bool CRenderer::Open(int width, int height)
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
2006-03-28 21:45:44 +02:00
|
|
|
m->IsOpen = true;
|
|
|
|
|
2006-03-26 19:36:33 +02:00
|
|
|
// Must query card capabilities before creating renderers that depend
|
|
|
|
// on card capabilities.
|
|
|
|
EnumCaps();
|
2011-03-26 21:17:21 +01:00
|
|
|
|
2006-03-26 19:36:33 +02:00
|
|
|
// Dimensions
|
2004-05-30 02:46:58 +02:00
|
|
|
m_Width = width;
|
|
|
|
m_Height = height;
|
|
|
|
|
|
|
|
// set packing parameters
|
|
|
|
glPixelStorei(GL_PACK_ALIGNMENT,1);
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
|
|
|
|
|
|
|
|
// setup default state
|
|
|
|
glDepthFunc(GL_LEQUAL);
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glCullFace(GL_BACK);
|
|
|
|
glFrontFace(GL_CCW);
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
GLint bits;
|
|
|
|
glGetIntegerv(GL_DEPTH_BITS,&bits);
|
2010-12-05 09:41:55 +01:00
|
|
|
LOGMESSAGE(L"CRenderer::Open: depth bits %d",bits);
|
2004-06-11 00:24:03 +02:00
|
|
|
glGetIntegerv(GL_STENCIL_BITS,&bits);
|
2010-12-05 09:41:55 +01:00
|
|
|
LOGMESSAGE(L"CRenderer::Open: stencil bits %d",bits);
|
2004-06-11 00:24:03 +02:00
|
|
|
glGetIntegerv(GL_ALPHA_BITS,&bits);
|
2010-12-05 09:41:55 +01:00
|
|
|
LOGMESSAGE(L"CRenderer::Open: alpha bits %d",bits);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2006-03-28 21:45:44 +02:00
|
|
|
// Validate the currently selected render path
|
|
|
|
SetRenderPath(m_Options.m_RenderPath);
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2012-04-23 20:55:32 +02:00
|
|
|
// Let component renderers perform one-time initialization after graphics capabilities and
|
|
|
|
// the shader path have been determined.
|
|
|
|
m->overlayRenderer.Initialize();
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// resize renderer view
|
|
|
|
void CRenderer::Resize(int width,int height)
|
|
|
|
{
|
2006-01-07 02:04:26 +01:00
|
|
|
// need to recreate the shadow map object to resize the shadow texture
|
2012-04-03 20:44:46 +02:00
|
|
|
m->shadow.RecreateTexture();
|
2006-01-07 02:04:26 +01:00
|
|
|
|
|
|
|
m_Width = width;
|
|
|
|
m_Height = height;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
2004-06-07 21:53:58 +02:00
|
|
|
// SetOptionBool: set boolean renderer option
|
2004-05-30 02:46:58 +02:00
|
|
|
void CRenderer::SetOptionBool(enum Option opt,bool value)
|
|
|
|
{
|
|
|
|
switch (opt) {
|
|
|
|
case OPT_NOVBO:
|
2012-04-24 00:57:51 +02:00
|
|
|
m_Options.m_NoVBO = value;
|
2004-05-30 02:46:58 +02:00
|
|
|
break;
|
|
|
|
case OPT_SHADOWS:
|
2012-04-24 00:57:51 +02:00
|
|
|
m_Options.m_Shadows = value;
|
2011-04-23 20:51:47 +02:00
|
|
|
MakeShadersDirty();
|
2004-05-30 02:46:58 +02:00
|
|
|
break;
|
2006-05-17 05:53:54 +02:00
|
|
|
case OPT_FANCYWATER:
|
2012-04-24 00:57:51 +02:00
|
|
|
m_Options.m_FancyWater = value;
|
2006-05-17 05:53:54 +02:00
|
|
|
break;
|
2011-07-13 01:48:05 +02:00
|
|
|
case OPT_SHADOWPCF:
|
2012-04-24 00:57:51 +02:00
|
|
|
m_Options.m_ShadowPCF = value;
|
2012-04-03 20:44:46 +02:00
|
|
|
MakeShadersDirty();
|
2011-07-13 01:48:05 +02:00
|
|
|
break;
|
2012-04-24 00:57:51 +02:00
|
|
|
case OPT_PARTICLES:
|
|
|
|
m_Options.m_Particles = value;
|
|
|
|
break;
|
2005-10-07 17:24:29 +02:00
|
|
|
default:
|
2009-11-03 22:46:35 +01:00
|
|
|
debug_warn(L"CRenderer::SetOptionBool: unknown option");
|
2005-10-07 17:24:29 +02:00
|
|
|
break;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
2004-06-07 21:53:58 +02:00
|
|
|
// GetOptionBool: get boolean renderer option
|
2004-05-30 02:46:58 +02:00
|
|
|
bool CRenderer::GetOptionBool(enum Option opt) const
|
|
|
|
{
|
|
|
|
switch (opt) {
|
|
|
|
case OPT_NOVBO:
|
|
|
|
return m_Options.m_NoVBO;
|
|
|
|
case OPT_SHADOWS:
|
|
|
|
return m_Options.m_Shadows;
|
2006-05-17 05:53:54 +02:00
|
|
|
case OPT_FANCYWATER:
|
|
|
|
return m_Options.m_FancyWater;
|
2011-07-13 01:48:05 +02:00
|
|
|
case OPT_SHADOWPCF:
|
|
|
|
return m_Options.m_ShadowPCF;
|
2012-04-24 00:57:51 +02:00
|
|
|
case OPT_PARTICLES:
|
|
|
|
return m_Options.m_Particles;
|
2005-10-07 17:24:29 +02:00
|
|
|
default:
|
2009-11-03 22:46:35 +01:00
|
|
|
debug_warn(L"CRenderer::GetOptionBool: unknown option");
|
2005-10-07 17:24:29 +02:00
|
|
|
break;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2005-10-05 18:42:09 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// SetRenderPath: Select the preferred render path.
|
|
|
|
// This may only be called before Open(), because the layout of vertex arrays and other
|
|
|
|
// data may depend on the chosen render path.
|
|
|
|
void CRenderer::SetRenderPath(RenderPath rp)
|
|
|
|
{
|
2006-03-28 21:45:44 +02:00
|
|
|
if (!m->IsOpen)
|
|
|
|
{
|
|
|
|
// Delay until Open() is called.
|
|
|
|
m_Options.m_RenderPath = rp;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Renderer has been opened, so validate the selected renderpath
|
2005-10-25 03:43:07 +02:00
|
|
|
if (rp == RP_DEFAULT)
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
2012-02-25 18:29:27 +01:00
|
|
|
if (m_Caps.m_ARBProgram || (m_Caps.m_VertexShader && m_Caps.m_FragmentShader && m_Options.m_PreferGLSL))
|
2011-03-26 21:17:21 +01:00
|
|
|
rp = RP_SHADER;
|
2005-10-25 03:43:07 +02:00
|
|
|
else
|
|
|
|
rp = RP_FIXED;
|
|
|
|
}
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
if (rp == RP_SHADER)
|
|
|
|
{
|
2012-02-25 18:29:27 +01:00
|
|
|
if (!(m_Caps.m_ARBProgram || (m_Caps.m_VertexShader && m_Caps.m_FragmentShader && m_Options.m_PreferGLSL)))
|
2011-03-26 21:17:21 +01:00
|
|
|
{
|
|
|
|
LOGWARNING(L"Falling back to fixed function\n");
|
|
|
|
rp = RP_FIXED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-10-05 18:42:09 +02:00
|
|
|
m_Options.m_RenderPath = rp;
|
2011-03-26 21:17:21 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
MakeShadersDirty();
|
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
// We might need to regenerate some render data after changing path
|
|
|
|
if (g_Game)
|
|
|
|
g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_COLOR);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CStr CRenderer::GetRenderPathName(RenderPath rp)
|
|
|
|
{
|
|
|
|
switch(rp) {
|
|
|
|
case RP_DEFAULT: return "default";
|
|
|
|
case RP_FIXED: return "fixed";
|
2011-03-26 21:17:21 +01:00
|
|
|
case RP_SHADER: return "shader";
|
2005-10-05 18:42:09 +02:00
|
|
|
default: return "(invalid)";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 16:37:58 +02:00
|
|
|
CRenderer::RenderPath CRenderer::GetRenderPathByName(const CStr& name)
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
|
|
|
if (name == "fixed")
|
|
|
|
return RP_FIXED;
|
2011-03-26 21:17:21 +01:00
|
|
|
if (name == "shader")
|
|
|
|
return RP_SHADER;
|
2005-10-05 18:42:09 +02:00
|
|
|
if (name == "default")
|
|
|
|
return RP_DEFAULT;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2010-12-05 09:41:55 +01:00
|
|
|
LOGWARNING(L"Unknown render path name '%hs', assuming 'default'", name.c_str());
|
2005-10-05 18:42:09 +02:00
|
|
|
return RP_DEFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// BeginFrame: signal frame start
|
|
|
|
void CRenderer::BeginFrame()
|
|
|
|
{
|
2011-03-03 23:38:01 +01:00
|
|
|
PROFILE("begin frame");
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// zero out all the per-frame stats
|
|
|
|
m_Stats.Reset();
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2011-04-07 00:07:13 +02:00
|
|
|
// choose model renderers for this frame
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
if (m->ShadersDirty)
|
|
|
|
ReloadShaders();
|
2011-03-26 21:17:21 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->Model.ModShader->SetShadowMap(&m->shadow);
|
|
|
|
m->Model.ModShader->SetLightEnv(m_LightEnv);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// SetClearColor: set color used to clear screen in BeginFrame()
|
2008-07-19 02:36:42 +02:00
|
|
|
void CRenderer::SetClearColor(SColor4ub color)
|
2004-06-07 21:53:58 +02:00
|
|
|
{
|
2008-07-19 02:36:42 +02:00
|
|
|
m_ClearColor[0] = float(color.R) / 255.0f;
|
|
|
|
m_ClearColor[1] = float(color.G) / 255.0f;
|
|
|
|
m_ClearColor[2] = float(color.B) / 255.0f;
|
|
|
|
m_ClearColor[3] = float(color.A) / 255.0f;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
void CRenderer::RenderShadowMap(const CShaderDefines& context)
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
2011-11-09 14:09:01 +01:00
|
|
|
PROFILE3_GPU("shadow map");
|
2005-05-20 19:09:47 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->shadow.BeginRender();
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2011-02-19 00:41:27 +01:00
|
|
|
{
|
|
|
|
PROFILE("render patches");
|
2012-04-03 20:44:46 +02:00
|
|
|
m->terrainRenderer.RenderPatches();
|
2011-02-19 00:41:27 +01:00
|
|
|
}
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
CShaderDefines contextCast = context;
|
|
|
|
contextCast.Add("MODE_SHADOWCAST", "1");
|
|
|
|
|
2011-02-19 00:41:27 +01:00
|
|
|
{
|
|
|
|
PROFILE("render models");
|
2012-04-03 20:44:46 +02:00
|
|
|
m->CallModelRenderers(contextCast, MODELFLAG_CASTSHADOWS);
|
2011-02-19 00:41:27 +01:00
|
|
|
}
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2011-02-19 00:41:27 +01:00
|
|
|
{
|
|
|
|
PROFILE("render transparent models");
|
2011-07-13 15:51:40 +02:00
|
|
|
// disable face-culling for two-sided models
|
|
|
|
glDisable(GL_CULL_FACE);
|
2012-04-03 20:44:46 +02:00
|
|
|
m->CallTranspModelRenderers(contextCast, MODELFLAG_CASTSHADOWS);
|
2011-07-13 15:51:40 +02:00
|
|
|
glEnable(GL_CULL_FACE);
|
2011-02-19 00:41:27 +01:00
|
|
|
}
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->shadow.EndRender();
|
2012-03-19 22:10:14 +01:00
|
|
|
|
|
|
|
m->SetOpenGLCamera(m_ViewCamera);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
void CRenderer::RenderPatches(const CShaderDefines& context, const CFrustum* frustum)
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
2011-11-09 14:09:01 +01:00
|
|
|
PROFILE3_GPU("patches");
|
2005-05-20 19:09:47 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
bool filtered = false;
|
|
|
|
if (frustum)
|
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
if (!m->terrainRenderer.CullPatches(frustum))
|
2011-07-13 01:48:05 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
filtered = true;
|
|
|
|
}
|
|
|
|
|
2012-02-13 16:06:25 +01:00
|
|
|
#if CONFIG2_GLES
|
|
|
|
#warning TODO: implement wireface/edged rendering mode GLES
|
|
|
|
#else
|
2004-05-30 02:46:58 +02:00
|
|
|
// switch on wireframe if we need it
|
2011-03-26 21:17:21 +01:00
|
|
|
if (m_TerrainRenderMode == WIREFRAME)
|
|
|
|
{
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
2004-06-07 21:53:58 +02:00
|
|
|
}
|
2012-02-13 16:06:25 +01:00
|
|
|
#endif
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// render all the patches, including blend pass
|
2011-03-26 21:17:21 +01:00
|
|
|
if (GetRenderPath() == RP_SHADER)
|
2012-04-03 20:44:46 +02:00
|
|
|
m->terrainRenderer.RenderTerrainShader(context, (m_Caps.m_Shadows && m_Options.m_Shadows) ? &m->shadow : 0, filtered);
|
2011-03-26 21:17:21 +01:00
|
|
|
else
|
2012-04-03 20:44:46 +02:00
|
|
|
m->terrainRenderer.RenderTerrain(filtered);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
|
2012-02-13 16:06:25 +01:00
|
|
|
#if !CONFIG2_GLES
|
2011-03-26 21:17:21 +01:00
|
|
|
if (m_TerrainRenderMode == WIREFRAME)
|
|
|
|
{
|
2004-05-30 02:46:58 +02:00
|
|
|
// switch wireframe off again
|
2011-03-26 21:17:21 +01:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
|
|
}
|
|
|
|
else if (m_TerrainRenderMode == EDGED_FACES)
|
|
|
|
{
|
2004-05-30 02:46:58 +02:00
|
|
|
// edged faces: need to make a second pass over the data:
|
|
|
|
// first switch on wireframe
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// setup some renderstate ..
|
2012-02-13 15:02:14 +01:00
|
|
|
pglActiveTextureARB(GL_TEXTURE0);
|
2011-03-26 21:17:21 +01:00
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
glColor3f(0.5f, 0.5f, 1.0f);
|
2004-05-30 02:46:58 +02:00
|
|
|
glLineWidth(2.0f);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2006-01-07 02:04:26 +01:00
|
|
|
// render tiles edges
|
2012-04-03 20:44:46 +02:00
|
|
|
m->terrainRenderer.RenderPatches(filtered);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// set color for outline
|
2011-03-26 21:17:21 +01:00
|
|
|
glColor3f(0, 0, 1);
|
2004-05-30 02:46:58 +02:00
|
|
|
glLineWidth(4.0f);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
|
|
|
// render outline of each patch
|
2012-04-03 20:44:46 +02:00
|
|
|
m->terrainRenderer.RenderOutlines(filtered);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// .. and restore the renderstates
|
2006-02-13 01:59:59 +01:00
|
|
|
glLineWidth(1.0f);
|
2011-03-26 21:17:21 +01:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
2004-06-11 00:24:03 +02:00
|
|
|
}
|
2012-02-13 16:06:25 +01:00
|
|
|
#endif
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
class CModelCuller : public CModelFilter
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CModelCuller(const CFrustum& frustum) : m_Frustum(frustum) { }
|
|
|
|
|
|
|
|
bool Filter(CModel *model)
|
|
|
|
{
|
2011-11-25 07:36:13 +01:00
|
|
|
return m_Frustum.IsBoxVisible(CVector3D(0, 0, 0), model->GetWorldBoundsRec());
|
2011-07-13 01:48:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
const CFrustum& m_Frustum;
|
|
|
|
};
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
void CRenderer::RenderModels(const CShaderDefines& context, const CFrustum* frustum)
|
2004-06-11 00:24:03 +02:00
|
|
|
{
|
2011-11-09 14:09:01 +01:00
|
|
|
PROFILE3_GPU("models");
|
2005-05-20 19:09:47 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
int flags = 0;
|
|
|
|
if (frustum)
|
|
|
|
{
|
|
|
|
flags = MODELFLAG_FILTERED;
|
|
|
|
CModelCuller culler(*frustum);
|
|
|
|
m->FilterModels(culler, flags);
|
|
|
|
}
|
|
|
|
|
2012-02-13 16:06:25 +01:00
|
|
|
#if !CONFIG2_GLES
|
2011-03-26 21:17:21 +01:00
|
|
|
if (m_ModelRenderMode == WIREFRAME)
|
|
|
|
{
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
2004-06-07 21:53:58 +02:00
|
|
|
}
|
2012-02-13 16:06:25 +01:00
|
|
|
#endif
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->CallModelRenderers(context, flags);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2012-02-13 16:06:25 +01:00
|
|
|
#if !CONFIG2_GLES
|
2011-03-26 21:17:21 +01:00
|
|
|
if (m_ModelRenderMode == WIREFRAME)
|
|
|
|
{
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
|
|
}
|
|
|
|
else if (m_ModelRenderMode == EDGED_FACES)
|
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
CShaderDefines contextWireframe = context;
|
|
|
|
contextWireframe.Add("MODE_WIREFRAME", "1");
|
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->CallModelRenderers(contextWireframe, flags);
|
2011-03-26 21:17:21 +01:00
|
|
|
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
2012-02-13 16:06:25 +01:00
|
|
|
#endif
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
void CRenderer::RenderTransparentModels(const CShaderDefines& context, ETransparentMode transparentMode, const CFrustum* frustum)
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2011-11-09 14:09:01 +01:00
|
|
|
PROFILE3_GPU("transparent models");
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
int flags = 0;
|
|
|
|
if (frustum)
|
|
|
|
{
|
|
|
|
flags = MODELFLAG_FILTERED;
|
|
|
|
CModelCuller culler(*frustum);
|
2012-04-03 20:44:46 +02:00
|
|
|
m->FilterTranspModels(culler, flags);
|
2011-07-13 01:48:05 +02:00
|
|
|
}
|
|
|
|
|
2012-02-13 16:06:25 +01:00
|
|
|
#if !CONFIG2_GLES
|
2005-10-25 03:43:07 +02:00
|
|
|
// switch on wireframe if we need it
|
2011-03-26 21:17:21 +01:00
|
|
|
if (m_ModelRenderMode == WIREFRAME)
|
|
|
|
{
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
2012-02-13 16:06:25 +01:00
|
|
|
#endif
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2011-07-13 15:51:40 +02:00
|
|
|
// disable face culling for two-sided models in sub-renders
|
|
|
|
if (flags)
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
CShaderDefines contextOpaque = context;
|
|
|
|
contextOpaque.Add("ALPHABLEND_PASS_OPAQUE", "1");
|
|
|
|
|
|
|
|
CShaderDefines contextBlend = context;
|
|
|
|
contextBlend.Add("ALPHABLEND_PASS_BLEND", "1");
|
|
|
|
|
|
|
|
if (transparentMode == TRANSPARENT || transparentMode == TRANSPARENT_OPAQUE)
|
|
|
|
m->CallTranspModelRenderers(contextOpaque, flags);
|
|
|
|
|
|
|
|
if (transparentMode == TRANSPARENT || transparentMode == TRANSPARENT_BLEND)
|
|
|
|
m->CallTranspModelRenderers(contextBlend, flags);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2011-07-13 15:51:40 +02:00
|
|
|
if (flags)
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
|
2012-02-13 16:06:25 +01:00
|
|
|
#if !CONFIG2_GLES
|
2011-03-26 21:17:21 +01:00
|
|
|
if (m_ModelRenderMode == WIREFRAME)
|
|
|
|
{
|
2005-10-25 03:43:07 +02:00
|
|
|
// switch wireframe off again
|
2011-03-26 21:17:21 +01:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
|
|
}
|
|
|
|
else if (m_ModelRenderMode == EDGED_FACES)
|
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
CShaderDefines contextWireframe = contextOpaque;
|
|
|
|
contextWireframe.Add("MODE_WIREFRAME", "1");
|
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->CallTranspModelRenderers(contextWireframe, flags);
|
2011-03-26 21:17:21 +01:00
|
|
|
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
2012-02-13 16:06:25 +01:00
|
|
|
#endif
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2006-05-28 04:13:32 +02:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// SetObliqueFrustumClipping: change the near plane to the given clip plane (in world space)
|
|
|
|
// Based on code from Game Programming Gems 5, from http://www.terathon.com/code/oblique.html
|
2011-07-13 01:48:05 +02:00
|
|
|
// - worldPlane is a clip plane in world space (worldPlane.Dot(v) >= 0 for any vector v passing the clipping test)
|
|
|
|
void CRenderer::SetObliqueFrustumClipping(const CVector4D& worldPlane)
|
2006-05-28 04:13:32 +02:00
|
|
|
{
|
|
|
|
// First, we'll convert the given clip plane to camera space, then we'll
|
|
|
|
// Get the view matrix and normal matrix (top 3x3 part of view matrix)
|
2011-07-13 01:48:05 +02:00
|
|
|
CMatrix3D normalMatrix = m_ViewCamera.m_Orientation.GetTranspose();
|
|
|
|
CVector4D camPlane = normalMatrix.Transform(worldPlane);
|
|
|
|
|
2012-02-13 16:06:25 +01:00
|
|
|
CMatrix3D matrix = m_ViewCamera.GetProjection();
|
2011-07-13 01:48:05 +02:00
|
|
|
|
|
|
|
// Calculate the clip-space corner point opposite the clipping plane
|
|
|
|
// as (sgn(camPlane.x), sgn(camPlane.y), 1, 1) and
|
|
|
|
// transform it into camera space by multiplying it
|
|
|
|
// by the inverse of the projection matrix
|
2012-02-13 16:06:25 +01:00
|
|
|
|
|
|
|
CVector4D q;
|
2012-04-08 17:55:06 +02:00
|
|
|
q.X = (sgn(camPlane.X) - matrix[8]/matrix[11]) / matrix[0];
|
|
|
|
q.Y = (sgn(camPlane.Y) - matrix[9]/matrix[11]) / matrix[5];
|
|
|
|
q.Z = 1.0f/matrix[11];
|
|
|
|
q.W = (1.0f - matrix[10]/matrix[11]) / matrix[14];
|
2006-05-28 04:13:32 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
// Calculate the scaled plane vector
|
|
|
|
CVector4D c = camPlane * (2.0f * matrix[11] / camPlane.Dot(q));
|
|
|
|
|
|
|
|
// Replace the third row of the projection matrix
|
2012-04-08 17:55:06 +02:00
|
|
|
matrix[2] = c.X;
|
|
|
|
matrix[6] = c.Y;
|
|
|
|
matrix[10] = c.Z - matrix[11];
|
|
|
|
matrix[14] = c.W;
|
2011-07-13 01:48:05 +02:00
|
|
|
|
2012-02-13 16:06:25 +01:00
|
|
|
// Load it back into the camera
|
|
|
|
m_ViewCamera.SetProjection(matrix);
|
|
|
|
m->SetOpenGLCamera(m_ViewCamera);
|
2006-05-28 04:13:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// RenderReflections: render the water reflections to the reflection texture
|
2012-04-03 20:44:46 +02:00
|
|
|
SScreenRect CRenderer::RenderReflections(const CShaderDefines& context, const CBoundingBoxAligned& scissor)
|
2006-05-28 04:13:32 +02:00
|
|
|
{
|
2011-11-09 14:09:01 +01:00
|
|
|
PROFILE3_GPU("water reflections");
|
2006-05-28 04:13:32 +02:00
|
|
|
|
|
|
|
WaterManager& wm = m->waterManager;
|
|
|
|
|
|
|
|
// Remember old camera
|
|
|
|
CCamera normalCamera = m_ViewCamera;
|
|
|
|
|
2006-06-01 00:37:23 +02:00
|
|
|
// Temporarily change the camera to one that is reflected.
|
|
|
|
// Also, for texturing purposes, make it render to a view port the size of the
|
|
|
|
// water texture, stretch the image according to our aspect ratio so it covers
|
|
|
|
// the whole screen despite being rendered into a square, and cover slightly more
|
|
|
|
// of the view so we can see wavy reflections of slightly off-screen objects.
|
2006-05-28 04:13:32 +02:00
|
|
|
m_ViewCamera.m_Orientation.Scale(1, -1, 1);
|
2011-07-13 01:48:05 +02:00
|
|
|
m_ViewCamera.m_Orientation.Translate(0, 2*wm.m_WaterHeight, 0);
|
|
|
|
m_ViewCamera.UpdateFrustum(scissor);
|
|
|
|
m_ViewCamera.ClipFrustum(CVector4D(0, 1, 0, -wm.m_WaterHeight));
|
|
|
|
|
2006-05-28 04:13:32 +02:00
|
|
|
SViewPort vp;
|
|
|
|
vp.m_Height = wm.m_ReflectionTextureSize;
|
|
|
|
vp.m_Width = wm.m_ReflectionTextureSize;
|
|
|
|
vp.m_X = 0;
|
|
|
|
vp.m_Y = 0;
|
2010-06-03 21:07:59 +02:00
|
|
|
m_ViewCamera.SetViewPort(vp);
|
2011-11-18 00:34:01 +01:00
|
|
|
m_ViewCamera.SetProjection(normalCamera.GetNearPlane(), normalCamera.GetFarPlane(), normalCamera.GetFOV()*1.05f); // Slightly higher than view FOV
|
2006-06-01 00:37:23 +02:00
|
|
|
CMatrix3D scaleMat;
|
|
|
|
scaleMat.SetScaling(m_Height/float(std::max(1, m_Width)), 1.0f, 1.0f);
|
|
|
|
m_ViewCamera.m_ProjMat = scaleMat * m_ViewCamera.m_ProjMat;
|
2006-09-14 22:06:21 +02:00
|
|
|
|
|
|
|
m->SetOpenGLCamera(m_ViewCamera);
|
2006-05-28 04:13:32 +02:00
|
|
|
|
|
|
|
CVector4D camPlane(0, 1, 0, -wm.m_WaterHeight);
|
2011-07-13 01:48:05 +02:00
|
|
|
SetObliqueFrustumClipping(camPlane);
|
2006-05-28 04:13:32 +02:00
|
|
|
|
|
|
|
// Save the model-view-projection matrix so the shaders can use it for projective texturing
|
2012-02-13 16:06:25 +01:00
|
|
|
wm.m_ReflectionMatrix = m_ViewCamera.GetViewProjection();
|
2006-05-28 04:13:32 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
SScreenRect screenScissor;
|
|
|
|
screenScissor.x1 = (GLint)floor((scissor[0].X*0.5f+0.5f)*vp.m_Width);
|
|
|
|
screenScissor.y1 = (GLint)floor((scissor[0].Y*0.5f+0.5f)*vp.m_Height);
|
|
|
|
screenScissor.x2 = (GLint)ceil((scissor[1].X*0.5f+0.5f)*vp.m_Width);
|
|
|
|
screenScissor.y2 = (GLint)ceil((scissor[1].Y*0.5f+0.5f)*vp.m_Height);
|
2006-05-28 04:13:32 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
if (screenScissor.x1 < screenScissor.x2 && screenScissor.y1 < screenScissor.y2)
|
|
|
|
{
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
glScissor(screenScissor.x1, screenScissor.y1, screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
|
2006-05-28 04:13:32 +02:00
|
|
|
|
2012-03-19 22:10:14 +01:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
2006-05-28 04:13:32 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
glFrontFace(GL_CW);
|
2006-05-28 04:13:32 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
// Render sky, terrain and models
|
|
|
|
m->skyManager.RenderSky();
|
|
|
|
ogl_WarnIfError();
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderPatches(context, &m_ViewCamera.GetFrustum());
|
2011-07-13 01:48:05 +02:00
|
|
|
ogl_WarnIfError();
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderModels(context, &m_ViewCamera.GetFrustum());
|
2011-07-13 01:48:05 +02:00
|
|
|
ogl_WarnIfError();
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderTransparentModels(context, TRANSPARENT_BLEND, &m_ViewCamera.GetFrustum());
|
2011-07-13 01:48:05 +02:00
|
|
|
ogl_WarnIfError();
|
|
|
|
|
|
|
|
glFrontFace(GL_CCW);
|
|
|
|
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
|
|
|
|
|
|
|
// Copy the image to a texture
|
2012-02-13 16:06:25 +01:00
|
|
|
pglActiveTextureARB(GL_TEXTURE0);
|
2011-07-13 01:48:05 +02:00
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, wm.m_ReflectionTexture);
|
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
|
|
screenScissor.x1, screenScissor.y1,
|
|
|
|
screenScissor.x1, screenScissor.y1,
|
|
|
|
screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset old camera
|
2006-09-14 22:06:21 +02:00
|
|
|
m_ViewCamera = normalCamera;
|
|
|
|
m->SetOpenGLCamera(m_ViewCamera);
|
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
return screenScissor;
|
2006-05-28 04:13:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// RenderRefractions: render the water refractions to the refraction texture
|
2012-04-03 20:44:46 +02:00
|
|
|
SScreenRect CRenderer::RenderRefractions(const CShaderDefines& context, const CBoundingBoxAligned &scissor)
|
2006-05-28 04:13:32 +02:00
|
|
|
{
|
2011-11-09 14:09:01 +01:00
|
|
|
PROFILE3_GPU("water refractions");
|
2006-05-28 04:13:32 +02:00
|
|
|
|
|
|
|
WaterManager& wm = m->waterManager;
|
|
|
|
|
|
|
|
// Remember old camera
|
|
|
|
CCamera normalCamera = m_ViewCamera;
|
|
|
|
|
2006-06-01 00:37:23 +02:00
|
|
|
// Temporarily change the camera to make it render to a view port the size of the
|
|
|
|
// water texture, stretch the image according to our aspect ratio so it covers
|
|
|
|
// the whole screen despite being rendered into a square, and cover slightly more
|
|
|
|
// of the view so we can see wavy refractions of slightly off-screen objects.
|
2011-07-13 01:48:05 +02:00
|
|
|
m_ViewCamera.UpdateFrustum(scissor);
|
|
|
|
m_ViewCamera.ClipFrustum(CVector4D(0, -1, 0, wm.m_WaterHeight));
|
|
|
|
|
2006-05-28 04:13:32 +02:00
|
|
|
SViewPort vp;
|
|
|
|
vp.m_Height = wm.m_RefractionTextureSize;
|
|
|
|
vp.m_Width = wm.m_RefractionTextureSize;
|
|
|
|
vp.m_X = 0;
|
|
|
|
vp.m_Y = 0;
|
2010-06-03 21:07:59 +02:00
|
|
|
m_ViewCamera.SetViewPort(vp);
|
2011-11-18 00:34:01 +01:00
|
|
|
m_ViewCamera.SetProjection(normalCamera.GetNearPlane(), normalCamera.GetFarPlane(), normalCamera.GetFOV()*1.05f); // Slightly higher than view FOV
|
2006-06-01 00:37:23 +02:00
|
|
|
CMatrix3D scaleMat;
|
|
|
|
scaleMat.SetScaling(m_Height/float(std::max(1, m_Width)), 1.0f, 1.0f);
|
|
|
|
m_ViewCamera.m_ProjMat = scaleMat * m_ViewCamera.m_ProjMat;
|
2011-07-13 01:48:05 +02:00
|
|
|
|
2006-09-14 22:06:21 +02:00
|
|
|
m->SetOpenGLCamera(m_ViewCamera);
|
2006-05-28 04:13:32 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
CVector4D camPlane(0, -1, 0, wm.m_WaterHeight);
|
|
|
|
SetObliqueFrustumClipping(camPlane);
|
2006-05-28 04:13:32 +02:00
|
|
|
|
|
|
|
// Save the model-view-projection matrix so the shaders can use it for projective texturing
|
2012-02-13 16:06:25 +01:00
|
|
|
wm.m_RefractionMatrix = m_ViewCamera.GetViewProjection();
|
2006-05-28 04:13:32 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
SScreenRect screenScissor;
|
|
|
|
screenScissor.x1 = (GLint)floor((scissor[0].X*0.5f+0.5f)*vp.m_Width);
|
|
|
|
screenScissor.y1 = (GLint)floor((scissor[0].Y*0.5f+0.5f)*vp.m_Height);
|
|
|
|
screenScissor.x2 = (GLint)ceil((scissor[1].X*0.5f+0.5f)*vp.m_Width);
|
|
|
|
screenScissor.y2 = (GLint)ceil((scissor[1].Y*0.5f+0.5f)*vp.m_Height);
|
|
|
|
if (screenScissor.x1 < screenScissor.x2 && screenScissor.y1 < screenScissor.y2)
|
|
|
|
{
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
glScissor(screenScissor.x1, screenScissor.y1, screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
|
2006-05-28 04:13:32 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // a neutral gray to blend in with shores
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
2006-05-28 04:13:32 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
// Render terrain and models
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderPatches(context, &m_ViewCamera.GetFrustum());
|
2011-07-13 01:48:05 +02:00
|
|
|
ogl_WarnIfError();
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderModels(context, &m_ViewCamera.GetFrustum());
|
2011-07-13 01:48:05 +02:00
|
|
|
ogl_WarnIfError();
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderTransparentModels(context, TRANSPARENT_BLEND, &m_ViewCamera.GetFrustum());
|
2011-07-13 01:48:05 +02:00
|
|
|
ogl_WarnIfError();
|
|
|
|
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
2006-05-28 04:13:32 +02:00
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
// Copy the image to a texture
|
2012-02-13 16:06:25 +01:00
|
|
|
pglActiveTextureARB(GL_TEXTURE0);
|
2011-07-13 01:48:05 +02:00
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, wm.m_RefractionTexture);
|
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
|
|
screenScissor.x1, screenScissor.y1,
|
|
|
|
screenScissor.x1, screenScissor.y1,
|
|
|
|
screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset old camera
|
2006-09-14 22:06:21 +02:00
|
|
|
m_ViewCamera = normalCamera;
|
|
|
|
m->SetOpenGLCamera(m_ViewCamera);
|
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
return screenScissor;
|
2006-05-28 04:13:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
void CRenderer::RenderSilhouettes(const CShaderDefines& context)
|
2011-03-18 17:57:54 +01:00
|
|
|
{
|
2011-11-09 14:09:01 +01:00
|
|
|
PROFILE3_GPU("silhouettes");
|
2011-03-18 17:57:54 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
CShaderDefines contextOccluder = context;
|
|
|
|
contextOccluder.Add("MODE_SILHOUETTEOCCLUDER", "1");
|
|
|
|
|
|
|
|
CShaderDefines contextDisplay = context;
|
|
|
|
contextDisplay.Add("MODE_SILHOUETTEDISPLAY", "1");
|
|
|
|
|
2011-03-18 17:57:54 +01:00
|
|
|
// Render silhouettes of units hidden behind terrain or occluders.
|
|
|
|
// To avoid breaking the standard rendering of alpha-blended objects, this
|
|
|
|
// has to be done in a separate pass.
|
|
|
|
// First we render all occluders into depth, then render all units with
|
|
|
|
// inverted depth test so any behind an occluder will get drawn in a constant
|
|
|
|
// colour.
|
|
|
|
|
|
|
|
float silhouetteAlpha = 0.75f;
|
|
|
|
|
2011-03-18 19:07:18 +01:00
|
|
|
// Silhouette blending requires an almost-universally-supported extension;
|
|
|
|
// fall back to non-blended if unavailable
|
|
|
|
if (!ogl_HaveExtension("GL_EXT_blend_color"))
|
|
|
|
silhouetteAlpha = 1.f;
|
|
|
|
|
2011-03-18 17:57:54 +01:00
|
|
|
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
|
|
|
|
|
|
glColorMask(0, 0, 0, 0);
|
|
|
|
|
|
|
|
// Render occluders:
|
|
|
|
|
|
|
|
{
|
|
|
|
PROFILE("render patches");
|
2011-03-25 00:54:08 +01:00
|
|
|
|
|
|
|
// To prevent units displaying silhouettes when parts of their model
|
|
|
|
// protrude into the ground, only occlude with the back faces of the
|
|
|
|
// terrain (so silhouettes will still display when behind hills)
|
|
|
|
glCullFace(GL_FRONT);
|
2012-04-03 20:44:46 +02:00
|
|
|
m->terrainRenderer.RenderPatches();
|
2011-03-25 00:54:08 +01:00
|
|
|
glCullFace(GL_BACK);
|
2011-03-18 17:57:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
PROFILE("render model occluders");
|
2012-04-03 20:44:46 +02:00
|
|
|
m->CallModelRenderers(contextOccluder, MODELFLAG_SILHOUETTE_OCCLUDER);
|
2011-03-18 17:57:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
PROFILE("render transparent occluders");
|
2012-04-03 20:44:46 +02:00
|
|
|
m->CallTranspModelRenderers(contextOccluder, MODELFLAG_SILHOUETTE_OCCLUDER);
|
2011-03-18 17:57:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
glDepthFunc(GL_GEQUAL);
|
|
|
|
glColorMask(1, 1, 1, 1);
|
|
|
|
|
|
|
|
// Render more efficiently if alpha == 1
|
|
|
|
if (silhouetteAlpha == 1.f)
|
|
|
|
{
|
|
|
|
// Ideally we'd render objects back-to-front so nearer silhouettes would
|
|
|
|
// appear on top, but sorting has non-zero cost. So we'll keep the depth
|
|
|
|
// write enabled, to do the opposite - far objects will consistently appear
|
|
|
|
// on top.
|
|
|
|
glDepthMask(0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Since we can't sort, we'll use the stencil buffer to ensure we only draw
|
|
|
|
// a pixel once (using the colour of whatever model happens to be drawn first).
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
|
2011-03-18 19:07:18 +01:00
|
|
|
pglBlendColorEXT(0, 0, 0, silhouetteAlpha);
|
2011-03-18 17:57:54 +01:00
|
|
|
|
|
|
|
glEnable(GL_STENCIL_TEST);
|
2011-03-18 19:07:18 +01:00
|
|
|
glStencilFunc(GL_NOTEQUAL, 1, (GLuint)-1);
|
2011-03-18 17:57:54 +01:00
|
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: For performance, we probably ought to do a quick raycasting check
|
|
|
|
// to see which units are likely blocked by occluders and not bother
|
|
|
|
// rendering any of the others
|
|
|
|
|
|
|
|
{
|
|
|
|
PROFILE("render models");
|
2012-04-03 20:44:46 +02:00
|
|
|
m->CallModelRenderers(contextDisplay, MODELFLAG_SILHOUETTE_DISPLAY);
|
2011-03-18 17:57:54 +01:00
|
|
|
// (This won't render transparent objects with SILHOUETTE_DISPLAY - will
|
|
|
|
// we have any units that need that?)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore state
|
|
|
|
glDepthFunc(GL_LEQUAL);
|
2011-03-18 19:07:18 +01:00
|
|
|
if (silhouetteAlpha == 1.f)
|
|
|
|
{
|
|
|
|
glDepthMask(1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
pglBlendColorEXT(0, 0, 0, 0);
|
|
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
}
|
2011-03-18 17:57:54 +01:00
|
|
|
}
|
|
|
|
|
2011-04-03 21:15:15 +02:00
|
|
|
void CRenderer::RenderParticles()
|
|
|
|
{
|
|
|
|
// Only supported in shader modes
|
|
|
|
if (GetRenderPath() != RP_SHADER)
|
|
|
|
return;
|
|
|
|
|
2011-11-09 14:09:01 +01:00
|
|
|
PROFILE3_GPU("particles");
|
2011-04-03 21:15:15 +02:00
|
|
|
|
|
|
|
m->particleRenderer.RenderParticles();
|
|
|
|
|
2012-02-13 16:06:25 +01:00
|
|
|
#if !CONFIG2_GLES
|
2011-04-03 21:15:15 +02:00
|
|
|
if (m_ModelRenderMode == EDGED_FACES)
|
|
|
|
{
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
glColor3f(0.0f, 0.5f, 0.0f);
|
|
|
|
|
|
|
|
m->particleRenderer.RenderParticles(true);
|
|
|
|
|
2012-02-25 18:29:27 +01:00
|
|
|
CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect("gui_solid");
|
2012-02-12 21:45:31 +01:00
|
|
|
shaderTech->BeginPass();
|
|
|
|
CShaderProgramPtr shader = shaderTech->GetShader();
|
2012-02-12 14:20:49 +01:00
|
|
|
shader->Uniform("color", 0.0f, 1.0f, 0.0f, 1.0f);
|
|
|
|
shader->Uniform("transform", m_ViewCamera.GetViewProjection());
|
|
|
|
|
|
|
|
m->particleRenderer.RenderBounds(shader);
|
2011-04-29 14:26:31 +02:00
|
|
|
|
2012-02-12 21:45:31 +01:00
|
|
|
shaderTech->EndPass();
|
2011-04-29 14:26:31 +02:00
|
|
|
|
2011-04-03 21:15:15 +02:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
|
|
}
|
2012-02-13 16:06:25 +01:00
|
|
|
#endif
|
2011-04-03 21:15:15 +02:00
|
|
|
}
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2006-09-24 13:25:11 +02:00
|
|
|
// RenderSubmissions: force rendering of any batched objects
|
|
|
|
void CRenderer::RenderSubmissions()
|
2004-06-07 21:53:58 +02:00
|
|
|
{
|
2011-11-04 02:35:50 +01:00
|
|
|
PROFILE3("render submissions");
|
2011-03-03 23:38:01 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
CShaderDefines context = m->globalContext;
|
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2004-08-06 17:01:23 +02:00
|
|
|
|
2006-09-14 22:06:21 +02:00
|
|
|
// Set the camera
|
|
|
|
m->SetOpenGLCamera(m_ViewCamera);
|
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
// Prepare model renderers
|
2011-11-04 02:35:50 +01:00
|
|
|
{
|
|
|
|
PROFILE3("prepare models");
|
2012-04-12 17:43:59 +02:00
|
|
|
m->Model.NormalSkinned->PrepareModels();
|
|
|
|
m->Model.TranspSkinned->PrepareModels();
|
|
|
|
if (m->Model.NormalUnskinned != m->Model.NormalSkinned)
|
|
|
|
m->Model.NormalUnskinned->PrepareModels();
|
|
|
|
if (m->Model.TranspUnskinned != m->Model.TranspSkinned)
|
|
|
|
m->Model.TranspUnskinned->PrepareModels();
|
2011-11-04 02:35:50 +01:00
|
|
|
}
|
2005-01-30 07:27:07 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->terrainRenderer.PrepareForRendering();
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
m->overlayRenderer.PrepareForRendering();
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->particleRenderer.PrepareForRendering(context);
|
2011-04-03 21:15:15 +02:00
|
|
|
|
2011-04-07 00:07:13 +02:00
|
|
|
if (m_Caps.m_Shadows && m_Options.m_Shadows && GetRenderPath() == RP_SHADER)
|
# Added tool for viewing models and animations outside the game.
Atlas: Added ActorViewer. Moved GL canvas into separate class for shared
use. Disabled message-handling callback while blocked on the game, and
stopped creating dialog boxes inside the game thread in order to avoid
deadlocks (hopefully). Support multiple Views (for independent sets of
camera/update/render code). Recalculate territory boundaries when
necessary. Changed default list of animations to match those currently
used by actors.
# Tidied up more code.
Moved some more #includes out of .h files, to minimise unnecessary
compilation.
MathUtil: Deleted unused/unuseful macros (M_PI (use PI instead), M_PI_2
(use PI/2), MAX3, ABS (use abs)).
ObjectManager: Removed some ScEd-specific things.
Unit: Moved creation out of UnitManager, so units can be created without
adding to the manager. Changed CStr8 to the more conventional CStr.
app_hooks: Removed warning for setting multiple times.
win: Restored SEH catcher.
GameSetup, GameView: Removed RenderNoCull, because it doesn't seem to do
what it says it does ("force renderer to load everything") since we're
loading-on-demand most stuff and it doesn't seem especially useful since
we'd prefer to minimise loading times (but feel free to correct me if
I'm wrong). (And because it crashes when things need to be initialised
in a different order, so it's easier to remove than to understand and
fix it.)
PatchRData, Renderer: Work sensibly when there's no game (hence no LOS
manager, water, etc).
LOSManager: Use entity position instead of actor position when possible.
TerritoryManager: Allow delayed recalculations (so Atlas can issue lots
of move+recalculate commands per frame).
Cinematic: Non-pointer wxTimer, so it doesn't leak and doesn't have to
be deleted manually.
This was SVN commit r4261.
2006-08-28 19:36:42 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderShadowMap(context);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2011-11-09 14:09:01 +01:00
|
|
|
{
|
|
|
|
PROFILE3_GPU("clear buffers");
|
|
|
|
glClearColor(m_ClearColor[0], m_ClearColor[1], m_ClearColor[2], m_ClearColor[3]);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
|
|
}
|
2006-02-11 19:04:32 +01:00
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2004-08-06 17:01:23 +02:00
|
|
|
|
2011-11-25 07:36:13 +01:00
|
|
|
CBoundingBoxAligned waterScissor;
|
2011-07-13 01:48:05 +02:00
|
|
|
if (m_WaterManager->m_RenderWater)
|
2006-05-28 04:13:32 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
waterScissor = m->terrainRenderer.ScissorWater(m_ViewCamera.GetViewProjection());
|
2011-07-13 01:48:05 +02:00
|
|
|
if (waterScissor.GetVolume() > 0 && m_WaterManager->WillRenderFancyWater())
|
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
SScreenRect reflectionScissor = RenderReflections(context, waterScissor);
|
|
|
|
SScreenRect refractionScissor = RenderRefractions(context, waterScissor);
|
2011-11-09 14:09:01 +01:00
|
|
|
|
|
|
|
PROFILE3_GPU("water scissor");
|
2011-07-13 01:48:05 +02:00
|
|
|
SScreenRect dirty;
|
|
|
|
dirty.x1 = std::min(reflectionScissor.x1, refractionScissor.x1);
|
|
|
|
dirty.y1 = std::min(reflectionScissor.y1, refractionScissor.y1);
|
|
|
|
dirty.x2 = std::max(reflectionScissor.x2, refractionScissor.x2);
|
|
|
|
dirty.y2 = std::max(reflectionScissor.y2, refractionScissor.y2);
|
|
|
|
if (dirty.x1 < dirty.x2 && dirty.y1 < dirty.y2)
|
|
|
|
{
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
glScissor(dirty.x1, dirty.y1, dirty.x2 - dirty.x1, dirty.y2 - dirty.y1);
|
|
|
|
glClearColor(m_ClearColor[0], m_ClearColor[1], m_ClearColor[2], m_ClearColor[3]);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
|
|
|
}
|
|
|
|
}
|
2006-05-28 04:13:32 +02:00
|
|
|
}
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// render submitted patches and models
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderPatches(context);
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2004-08-06 17:01:23 +02:00
|
|
|
|
2006-04-17 22:02:51 +02:00
|
|
|
// render debug-related terrain overlays
|
2012-04-24 18:46:32 +02:00
|
|
|
ITerrainOverlay::RenderOverlaysBeforeWater();
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2006-04-17 22:02:51 +02:00
|
|
|
|
2011-07-30 02:56:45 +02:00
|
|
|
// render other debug-related overlays before water (so they can be seen when underwater)
|
|
|
|
m->overlayRenderer.RenderOverlaysBeforeWater();
|
2010-05-28 01:23:53 +02:00
|
|
|
ogl_WarnIfError();
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderModels(context);
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2004-08-06 17:01:23 +02:00
|
|
|
|
2006-05-29 02:49:09 +02:00
|
|
|
// render water
|
2011-07-13 01:48:05 +02:00
|
|
|
if (m_WaterManager->m_RenderWater && g_Game && waterScissor.GetVolume() > 0)
|
2006-01-07 02:04:26 +01:00
|
|
|
{
|
2011-07-13 01:48:05 +02:00
|
|
|
// render transparent stuff, but only the solid parts that can occlude block water
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderTransparentModels(context, TRANSPARENT_OPAQUE);
|
2011-07-13 01:48:05 +02:00
|
|
|
ogl_WarnIfError();
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->terrainRenderer.RenderWater();
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2011-07-13 01:48:05 +02:00
|
|
|
|
|
|
|
// render transparent stuff again, but only the blended parts that overlap water
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderTransparentModels(context, TRANSPARENT_BLEND);
|
2011-07-13 01:48:05 +02:00
|
|
|
ogl_WarnIfError();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// render transparent stuff, so it can overlap models/terrain
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderTransparentModels(context, TRANSPARENT);
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2006-01-07 02:04:26 +01:00
|
|
|
}
|
2005-09-18 05:47:15 +02:00
|
|
|
|
2012-04-24 18:46:32 +02:00
|
|
|
// render debug-related terrain overlays
|
|
|
|
ITerrainOverlay::RenderOverlaysAfterWater();
|
|
|
|
ogl_WarnIfError();
|
|
|
|
|
2011-07-30 02:56:45 +02:00
|
|
|
// render some other overlays after water (so they can be displayed on top of water)
|
|
|
|
m->overlayRenderer.RenderOverlaysAfterWater();
|
|
|
|
ogl_WarnIfError();
|
|
|
|
|
2011-04-03 21:15:15 +02:00
|
|
|
// particles are transparent so render after water
|
2012-04-24 00:57:51 +02:00
|
|
|
if (m_Options.m_Particles)
|
|
|
|
{
|
|
|
|
RenderParticles();
|
|
|
|
ogl_WarnIfError();
|
|
|
|
}
|
2011-04-03 21:15:15 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
RenderSilhouettes(context);
|
2011-03-18 17:57:54 +01:00
|
|
|
|
2012-02-13 16:06:25 +01:00
|
|
|
#if !CONFIG2_GLES
|
2006-05-28 23:58:56 +02:00
|
|
|
// Clean up texture blend mode so particles and other things render OK
|
|
|
|
// (really this should be cleaned up by whoever set it)
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
2012-02-13 16:06:25 +01:00
|
|
|
#endif
|
2006-05-28 23:58:56 +02:00
|
|
|
|
2006-01-22 20:12:30 +01:00
|
|
|
// render debug lines
|
|
|
|
if (m_DisplayFrustum)
|
|
|
|
{
|
2006-01-29 18:34:45 +01:00
|
|
|
DisplayFrustum();
|
2012-04-03 20:44:46 +02:00
|
|
|
m->shadow.RenderDebugBounds();
|
|
|
|
m->shadow.RenderDebugTexture();
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2006-01-22 20:12:30 +01:00
|
|
|
}
|
|
|
|
|
2010-10-02 14:41:29 +02:00
|
|
|
// render overlays that should appear on top of all other objects
|
|
|
|
m->overlayRenderer.RenderForegroundOverlays(m_ViewCamera);
|
|
|
|
ogl_WarnIfError();
|
2011-01-29 17:31:48 +01:00
|
|
|
}
|
2010-10-02 14:41:29 +02:00
|
|
|
|
2011-01-29 17:31:48 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// EndFrame: signal frame end
|
|
|
|
void CRenderer::EndFrame()
|
|
|
|
{
|
2011-11-04 02:35:50 +01:00
|
|
|
PROFILE3("end frame");
|
2011-03-03 23:38:01 +01:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// empty lists
|
2012-04-03 20:44:46 +02:00
|
|
|
m->terrainRenderer.EndFrame();
|
2010-01-09 20:20:14 +01:00
|
|
|
m->overlayRenderer.EndFrame();
|
2011-04-03 21:15:15 +02:00
|
|
|
m->particleRenderer.EndFrame();
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
// Finish model renderers
|
2012-04-12 17:43:59 +02:00
|
|
|
m->Model.NormalSkinned->EndFrame();
|
|
|
|
m->Model.TranspSkinned->EndFrame();
|
|
|
|
if (m->Model.NormalUnskinned != m->Model.NormalSkinned)
|
|
|
|
m->Model.NormalUnskinned->EndFrame();
|
|
|
|
if (m->Model.TranspUnskinned != m->Model.TranspSkinned)
|
|
|
|
m->Model.TranspUnskinned->EndFrame();
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
ogl_tex_bind(0, 0);
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2011-01-29 17:31:48 +01:00
|
|
|
{
|
2011-11-04 02:35:50 +01:00
|
|
|
PROFILE3("error check");
|
|
|
|
if (glGetError())
|
|
|
|
{
|
|
|
|
ONCE(LOGERROR(L"CRenderer::EndFrame: GL errors occurred"));
|
|
|
|
}
|
2004-06-07 21:53:58 +02:00
|
|
|
}
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2006-01-29 18:34:45 +01:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// DisplayFrustum: debug displays
|
|
|
|
// - white: cull camera frustum
|
|
|
|
// - red: bounds of shadow casting objects
|
|
|
|
void CRenderer::DisplayFrustum()
|
|
|
|
{
|
2012-02-13 16:06:25 +01:00
|
|
|
#if CONFIG2_GLES
|
|
|
|
#warning TODO: implement CRenderer::DisplayFrustum for GLES
|
|
|
|
#else
|
2006-01-29 18:34:45 +01:00
|
|
|
glDepthMask(0);
|
|
|
|
glDisable(GL_CULL_FACE);
|
2012-02-12 14:20:49 +01:00
|
|
|
glDisable(GL_TEXTURE_2D);
|
2006-01-29 18:34:45 +01:00
|
|
|
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glColor4ub(255,255,255,64);
|
|
|
|
m_CullCamera.Render(2);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
glColor3ub(255,255,255);
|
|
|
|
m_CullCamera.Render(2);
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
|
|
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glDepthMask(1);
|
2012-02-13 16:06:25 +01:00
|
|
|
#endif
|
2006-01-29 18:34:45 +01:00
|
|
|
}
|
|
|
|
|
2011-01-29 17:31:48 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Text overlay rendering
|
|
|
|
void CRenderer::RenderTextOverlays()
|
|
|
|
{
|
2011-11-09 14:09:01 +01:00
|
|
|
PROFILE3_GPU("text overlays");
|
2011-01-29 17:31:48 +01:00
|
|
|
|
|
|
|
if (m_DisplayTerrainPriorities)
|
2012-04-03 20:44:46 +02:00
|
|
|
m->terrainRenderer.RenderPriorities();
|
2011-01-29 17:31:48 +01:00
|
|
|
|
|
|
|
ogl_WarnIfError();
|
|
|
|
}
|
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2006-09-14 22:06:21 +02:00
|
|
|
// SetSceneCamera: setup projection and transform of camera and adjust viewport to current view
|
|
|
|
// The camera always represents the actual camera used to render a scene, not any virtual camera
|
|
|
|
// used for shadow rendering or reflections.
|
|
|
|
void CRenderer::SetSceneCamera(const CCamera& viewCamera, const CCamera& cullCamera)
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
2006-01-22 20:12:30 +01:00
|
|
|
m_ViewCamera = viewCamera;
|
|
|
|
m_CullCamera = cullCamera;
|
|
|
|
|
2011-04-07 00:07:13 +02:00
|
|
|
if (m_Caps.m_Shadows && m_Options.m_Shadows && GetRenderPath() == RP_SHADER)
|
2012-04-03 20:44:46 +02:00
|
|
|
m->shadow.SetupFrame(m_CullCamera, m_LightEnv->GetSunDir());
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2006-09-14 22:06:21 +02:00
|
|
|
|
2004-08-05 15:07:51 +02:00
|
|
|
void CRenderer::SetViewport(const SViewPort &vp)
|
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
glViewport((GLint)vp.m_X,(GLint)vp.m_Y,(GLsizei)vp.m_Width,(GLsizei)vp.m_Height);
|
2004-08-05 15:07:51 +02:00
|
|
|
}
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
void CRenderer::Submit(CPatch* patch)
|
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
m->terrainRenderer.Submit(patch);
|
2005-09-18 05:47:15 +02:00
|
|
|
}
|
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
void CRenderer::Submit(SOverlayLine* overlay)
|
|
|
|
{
|
|
|
|
m->overlayRenderer.Submit(overlay);
|
|
|
|
}
|
|
|
|
|
2011-07-30 02:56:45 +02:00
|
|
|
void CRenderer::Submit(SOverlayTexturedLine* overlay)
|
|
|
|
{
|
|
|
|
m->overlayRenderer.Submit(overlay);
|
|
|
|
}
|
|
|
|
|
2010-10-02 14:41:29 +02:00
|
|
|
void CRenderer::Submit(SOverlaySprite* overlay)
|
|
|
|
{
|
|
|
|
m->overlayRenderer.Submit(overlay);
|
|
|
|
}
|
|
|
|
|
2012-04-22 06:04:02 +02:00
|
|
|
void CRenderer::Submit(SOverlayQuad* overlay)
|
|
|
|
{
|
|
|
|
m->overlayRenderer.Submit(overlay);
|
|
|
|
}
|
|
|
|
|
2011-03-13 20:22:05 +01:00
|
|
|
void CRenderer::Submit(CModelDecal* decal)
|
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
m->terrainRenderer.Submit(decal);
|
2011-03-13 20:22:05 +01:00
|
|
|
}
|
|
|
|
|
2011-04-03 21:15:15 +02:00
|
|
|
void CRenderer::Submit(CParticleEmitter* emitter)
|
|
|
|
{
|
|
|
|
m->particleRenderer.Submit(emitter);
|
|
|
|
}
|
|
|
|
|
2006-09-24 13:25:11 +02:00
|
|
|
void CRenderer::SubmitNonRecursive(CModel* model)
|
2004-06-11 00:24:03 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
if (model->GetFlags() & MODELFLAG_CASTSHADOWS)
|
|
|
|
{
|
|
|
|
m->shadow.AddShadowedBound(model->GetWorldBounds());
|
2004-06-11 00:24:03 +02:00
|
|
|
}
|
|
|
|
|
2011-11-25 07:36:13 +01:00
|
|
|
// Tricky: The call to GetWorldBounds() above can invalidate the position
|
2005-10-30 02:22:22 +02:00
|
|
|
model->ValidatePosition();
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2012-04-12 17:43:59 +02:00
|
|
|
bool requiresSkinning = (model->GetModelDef()->GetNumBones() != 0);
|
2005-10-30 02:22:22 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
if (model->GetMaterial().UsesAlphaBlending())
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-12 17:43:59 +02:00
|
|
|
if (requiresSkinning)
|
|
|
|
m->Model.TranspSkinned->Submit(model);
|
2005-10-25 03:43:07 +02:00
|
|
|
else
|
2012-04-12 17:43:59 +02:00
|
|
|
m->Model.TranspUnskinned->Submit(model);
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-04-12 17:43:59 +02:00
|
|
|
if (requiresSkinning)
|
|
|
|
m->Model.NormalSkinned->Submit(model);
|
2005-10-25 03:43:07 +02:00
|
|
|
else
|
2012-04-12 17:43:59 +02:00
|
|
|
m->Model.NormalUnskinned->Submit(model);
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-24 13:25:11 +02:00
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// Render the given scene
|
2011-04-05 22:32:03 +02:00
|
|
|
void CRenderer::RenderScene(Scene& scene)
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
2011-04-05 22:32:03 +02:00
|
|
|
m_CurrentScene = &scene;
|
|
|
|
|
2006-09-24 13:25:11 +02:00
|
|
|
CFrustum frustum = m_CullCamera.GetFrustum();
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2011-04-05 22:32:03 +02:00
|
|
|
scene.EnumerateObjects(frustum, this);
|
2006-09-24 13:25:11 +02:00
|
|
|
|
2011-04-03 21:15:15 +02:00
|
|
|
m->particleManager.RenderSubmit(*this, frustum);
|
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2006-09-24 13:25:11 +02:00
|
|
|
|
|
|
|
RenderSubmissions();
|
2011-04-05 22:32:03 +02:00
|
|
|
|
|
|
|
m_CurrentScene = NULL;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2011-04-05 22:32:03 +02:00
|
|
|
Scene& CRenderer::GetScene()
|
|
|
|
{
|
2011-04-30 15:01:45 +02:00
|
|
|
ENSURE(m_CurrentScene);
|
2011-04-05 22:32:03 +02:00
|
|
|
return *m_CurrentScene;
|
|
|
|
}
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
2004-06-07 21:53:58 +02:00
|
|
|
// BindTexture: bind a GL texture object to current active unit
|
2012-03-19 22:10:14 +01:00
|
|
|
void CRenderer::BindTexture(int unit, GLuint tex)
|
2004-06-11 00:24:03 +02:00
|
|
|
{
|
2005-10-30 02:18:15 +01:00
|
|
|
pglActiveTextureARB(GL_TEXTURE0+unit);
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2012-03-19 22:10:14 +01:00
|
|
|
glBindTexture(GL_TEXTURE_2D, tex);
|
|
|
|
#if !CONFIG2_GLES
|
2004-06-11 00:24:03 +02:00
|
|
|
if (tex) {
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
} else {
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
}
|
2012-03-19 22:10:14 +01:00
|
|
|
#endif
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2004-06-07 21:53:58 +02:00
|
|
|
// LoadAlphaMaps: load the 14 default alpha maps, pack them into one composite texture and
|
2005-09-02 04:54:02 +02:00
|
|
|
// calculate the coordinate of each alphamap within this packed texture
|
2005-05-03 23:36:57 +02:00
|
|
|
int CRenderer::LoadAlphaMaps()
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
const wchar_t* const key = L"(alpha map composite)";
|
2005-10-21 09:47:38 +02:00
|
|
|
Handle ht = ogl_tex_find(key);
|
|
|
|
// alpha map texture had already been created and is still in memory:
|
|
|
|
// reuse it, do not load again.
|
|
|
|
if(ht > 0)
|
|
|
|
{
|
|
|
|
m_hCompositeAlphaMap = ht;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-02 04:54:02 +02:00
|
|
|
//
|
|
|
|
// load all textures and store Handle in array
|
|
|
|
//
|
2005-10-30 00:29:55 +02:00
|
|
|
Handle textures[NumAlphaMaps] = {0};
|
2011-01-29 17:31:48 +01:00
|
|
|
VfsPath path(L"art/textures/terrain/alphamaps/standard");
|
2009-11-03 22:46:35 +01:00
|
|
|
const wchar_t* fnames[NumAlphaMaps] = {
|
2011-01-29 17:31:48 +01:00
|
|
|
L"blendcircle.png",
|
|
|
|
L"blendlshape.png",
|
|
|
|
L"blendedge.png",
|
|
|
|
L"blendedgecorner.png",
|
|
|
|
L"blendedgetwocorners.png",
|
|
|
|
L"blendfourcorners.png",
|
|
|
|
L"blendtwooppositecorners.png",
|
|
|
|
L"blendlshapecorner.png",
|
|
|
|
L"blendtwocorners.png",
|
|
|
|
L"blendcorner.png",
|
|
|
|
L"blendtwoedges.png",
|
|
|
|
L"blendthreecorners.png",
|
|
|
|
L"blendushape.png",
|
|
|
|
L"blendbad.png"
|
2005-03-18 23:02:20 +01:00
|
|
|
};
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
size_t base = 0; // texture width/height (see below)
|
2005-09-02 04:54:02 +02:00
|
|
|
// for convenience, we require all alpha maps to be of the same BPP
|
|
|
|
// (avoids another ogl_tex_get_size call, and doesn't hurt)
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
size_t bpp = 0;
|
|
|
|
for(size_t i=0;i<NumAlphaMaps;i++)
|
2005-09-02 04:54:02 +02:00
|
|
|
{
|
2005-10-21 09:47:38 +02:00
|
|
|
// note: these individual textures can be discarded afterwards;
|
|
|
|
// we cache the composite.
|
2011-03-23 14:36:20 +01:00
|
|
|
textures[i] = ogl_tex_load(g_VFS, path / fnames[i]);
|
2011-05-03 14:38:42 +02:00
|
|
|
RETURN_STATUS_IF_ERR(textures[i]);
|
2005-09-02 04:54:02 +02:00
|
|
|
|
|
|
|
// get its size and make sure they are all equal.
|
|
|
|
// (the packing algo assumes this)
|
2011-04-30 13:46:23 +02:00
|
|
|
size_t this_width = 0, this_height = 0, this_bpp = 0; // fail-safe
|
|
|
|
(void)ogl_tex_get_size(textures[i], &this_width, &this_height, &this_bpp);
|
|
|
|
if(this_width != this_height)
|
|
|
|
DEBUG_DISPLAY_ERROR(L"Alpha maps are not square");
|
2005-09-02 04:54:02 +02:00
|
|
|
// .. first iteration: establish size
|
|
|
|
if(i == 0)
|
|
|
|
{
|
|
|
|
base = this_width;
|
|
|
|
bpp = this_bpp;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
2005-09-02 04:54:02 +02:00
|
|
|
// .. not first: make sure texture size matches
|
|
|
|
else if(base != this_width || bpp != this_bpp)
|
2008-01-19 12:33:11 +01:00
|
|
|
DEBUG_DISPLAY_ERROR(L"Alpha maps are not identically sized (including pixel depth)");
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2005-09-02 04:54:02 +02:00
|
|
|
//
|
|
|
|
// copy each alpha map (tile) into one buffer, arrayed horizontally.
|
|
|
|
//
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
size_t tile_w = 2+base+2; // 2 pixel border (avoids bilinear filtering artifacts)
|
|
|
|
size_t total_w = round_up_to_pow2(tile_w * NumAlphaMaps);
|
2011-04-30 15:01:45 +02:00
|
|
|
size_t total_h = base; ENSURE(is_pow2(total_h));
|
2011-04-29 21:10:34 +02:00
|
|
|
shared_ptr<u8> data;
|
2011-04-30 13:46:23 +02:00
|
|
|
AllocateAligned(data, total_w*total_h, maxSectorSize);
|
2004-05-30 02:46:58 +02:00
|
|
|
// for each tile on row
|
2011-04-30 13:46:23 +02:00
|
|
|
for (size_t i = 0; i < NumAlphaMaps; i++)
|
2005-09-02 04:54:02 +02:00
|
|
|
{
|
2004-05-30 02:46:58 +02:00
|
|
|
// get src of copy
|
2008-07-19 02:36:42 +02:00
|
|
|
u8* src = 0;
|
|
|
|
(void)ogl_tex_get_data(textures[i], &src);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2011-04-30 13:46:23 +02:00
|
|
|
size_t srcstep = bpp/8;
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// get destination of copy
|
2011-04-30 13:46:23 +02:00
|
|
|
u8* dst = data.get() + (i*tile_w);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// for each row of image
|
2011-04-30 13:46:23 +02:00
|
|
|
for (size_t j = 0; j < base; j++)
|
|
|
|
{
|
2004-05-30 02:46:58 +02:00
|
|
|
// duplicate first pixel
|
2011-04-30 13:46:23 +02:00
|
|
|
*dst++ = *src;
|
|
|
|
*dst++ = *src;
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// copy a row
|
2011-04-30 13:46:23 +02:00
|
|
|
for (size_t k = 0; k < base; k++)
|
|
|
|
{
|
|
|
|
*dst++ = *src;
|
|
|
|
src += srcstep;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
2011-04-30 13:46:23 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// duplicate last pixel
|
2011-04-30 13:46:23 +02:00
|
|
|
*dst++ = *(src-srcstep);
|
|
|
|
*dst++ = *(src-srcstep);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// advance write pointer for next row
|
2011-04-30 13:46:23 +02:00
|
|
|
dst += total_w-tile_w;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2011-04-30 13:46:23 +02:00
|
|
|
m_AlphaMapCoords[i].u0 = float(i*tile_w+2) / float(total_w);
|
|
|
|
m_AlphaMapCoords[i].u1 = float((i+1)*tile_w-2) / float(total_w);
|
|
|
|
m_AlphaMapCoords[i].v0 = 0.0f;
|
|
|
|
m_AlphaMapCoords[i].v1 = 1.0f;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2011-04-30 13:46:23 +02:00
|
|
|
for (size_t i = 0; i < NumAlphaMaps; i++)
|
2005-10-30 00:29:55 +02:00
|
|
|
(void)ogl_tex_free(textures[i]);
|
2004-12-16 13:41:54 +01:00
|
|
|
|
2005-09-02 04:54:02 +02:00
|
|
|
// upload the composite texture
|
2005-09-20 00:48:20 +02:00
|
|
|
Tex t;
|
2011-04-30 13:46:23 +02:00
|
|
|
(void)tex_wrap(total_w, total_h, 8, TEX_GREY, data, 0, &t);
|
2010-07-04 12:15:53 +02:00
|
|
|
m_hCompositeAlphaMap = ogl_tex_wrap(&t, g_VFS, key);
|
2005-09-29 07:00:20 +02:00
|
|
|
(void)ogl_tex_set_filter(m_hCompositeAlphaMap, GL_LINEAR);
|
2011-07-30 02:56:45 +02:00
|
|
|
(void)ogl_tex_set_wrap (m_hCompositeAlphaMap, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
|
2012-02-13 16:06:25 +01:00
|
|
|
int ret = ogl_tex_upload(m_hCompositeAlphaMap, GL_ALPHA, 0, 0);
|
2005-05-03 23:36:57 +02:00
|
|
|
|
2005-10-12 06:16:41 +02:00
|
|
|
return ret;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
2004-12-16 13:41:54 +01:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// UnloadAlphaMaps: frees the resources allocates by LoadAlphaMaps
|
|
|
|
void CRenderer::UnloadAlphaMaps()
|
|
|
|
{
|
2005-09-20 00:48:20 +02:00
|
|
|
ogl_tex_free(m_hCompositeAlphaMap);
|
2005-12-02 03:08:26 +01:00
|
|
|
m_hCompositeAlphaMap = 0;
|
2004-12-16 13:41:54 +01:00
|
|
|
}
|
2005-10-10 16:15:28 +02:00
|
|
|
|
|
|
|
|
2005-10-12 06:16:41 +02:00
|
|
|
|
2011-05-03 14:38:42 +02:00
|
|
|
Status CRenderer::ReloadChangedFileCB(void* param, const VfsPath& path)
|
2011-01-29 17:31:48 +01:00
|
|
|
{
|
|
|
|
CRenderer* renderer = static_cast<CRenderer*>(param);
|
|
|
|
|
|
|
|
// If an alpha map changed, and we already loaded them, then reload them
|
2011-03-23 14:36:20 +01:00
|
|
|
if (boost::algorithm::starts_with(path.string(), L"art/textures/terrain/alphamaps/"))
|
2011-01-29 17:31:48 +01:00
|
|
|
{
|
|
|
|
if (renderer->m_hCompositeAlphaMap)
|
|
|
|
{
|
|
|
|
renderer->UnloadAlphaMaps();
|
|
|
|
renderer->LoadAlphaMaps();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return INFO::OK;
|
|
|
|
}
|
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
void CRenderer::MakeShadersDirty()
|
|
|
|
{
|
|
|
|
m->ShadersDirty = true;
|
|
|
|
}
|
|
|
|
|
2005-10-10 16:15:28 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Scripting Interface
|
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
jsval CRenderer::JSI_GetRenderPath(JSContext*)
|
|
|
|
{
|
|
|
|
return ToJSVal(GetRenderPathName(m_Options.m_RenderPath));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::JSI_SetRenderPath(JSContext* ctx, jsval newval)
|
|
|
|
{
|
|
|
|
CStr name;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
if (!ToPrimitive(ctx, newval, name))
|
|
|
|
return;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
SetRenderPath(GetRenderPathByName(name));
|
|
|
|
}
|
|
|
|
|
2006-02-16 00:50:24 +01:00
|
|
|
jsval CRenderer::JSI_GetDepthTextureBits(JSContext*)
|
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
return ToJSVal(m->shadow.GetDepthTextureBits());
|
2006-02-16 00:50:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::JSI_SetDepthTextureBits(JSContext* ctx, jsval newval)
|
|
|
|
{
|
|
|
|
int depthTextureBits;
|
|
|
|
|
|
|
|
if (!ToPrimitive(ctx, newval, depthTextureBits))
|
|
|
|
return;
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->shadow.SetDepthTextureBits(depthTextureBits);
|
2006-02-16 00:50:24 +01:00
|
|
|
}
|
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
jsval CRenderer::JSI_GetShadows(JSContext*)
|
|
|
|
{
|
|
|
|
return ToJSVal(m_Options.m_Shadows);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::JSI_SetShadows(JSContext* ctx, jsval newval)
|
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
ToPrimitive(ctx, newval, m_Options.m_Shadows);
|
2011-03-26 21:17:21 +01:00
|
|
|
ReloadShaders();
|
|
|
|
}
|
|
|
|
|
2011-03-07 01:34:03 +01:00
|
|
|
jsval CRenderer::JSI_GetShadowAlphaFix(JSContext*)
|
|
|
|
{
|
|
|
|
return ToJSVal(m_Options.m_ShadowAlphaFix);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::JSI_SetShadowAlphaFix(JSContext* ctx, jsval newval)
|
|
|
|
{
|
|
|
|
if (!ToPrimitive(ctx, newval, m_Options.m_ShadowAlphaFix))
|
|
|
|
return;
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->shadow.RecreateTexture();
|
2011-03-07 01:34:03 +01:00
|
|
|
}
|
|
|
|
|
2011-07-13 01:48:05 +02:00
|
|
|
jsval CRenderer::JSI_GetShadowPCF(JSContext*)
|
|
|
|
{
|
|
|
|
return ToJSVal(m_Options.m_ShadowPCF);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::JSI_SetShadowPCF(JSContext* ctx, jsval newval)
|
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
ToPrimitive(ctx, newval, m_Options.m_ShadowPCF);
|
2011-07-13 01:48:05 +02:00
|
|
|
ReloadShaders();
|
|
|
|
}
|
|
|
|
|
2012-01-29 02:22:22 +01:00
|
|
|
jsval CRenderer::JSI_GetPreferGLSL(JSContext*)
|
|
|
|
{
|
|
|
|
return ToJSVal(m_Options.m_PreferGLSL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::JSI_SetPreferGLSL(JSContext* ctx, jsval newval)
|
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
ToPrimitive(ctx, newval, m_Options.m_PreferGLSL);
|
|
|
|
ReloadShaders();
|
2012-01-29 02:22:22 +01:00
|
|
|
}
|
|
|
|
|
2006-05-17 04:24:17 +02:00
|
|
|
jsval CRenderer::JSI_GetSky(JSContext*)
|
|
|
|
{
|
|
|
|
return ToJSVal(m->skyManager.GetSkySet());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::JSI_SetSky(JSContext* ctx, jsval newval)
|
|
|
|
{
|
|
|
|
CStrW skySet;
|
2006-05-28 23:58:56 +02:00
|
|
|
if (!ToPrimitive<CStrW>(ctx, newval, skySet)) return;
|
2006-05-17 04:24:17 +02:00
|
|
|
m->skyManager.SetSkySet(skySet);
|
|
|
|
}
|
|
|
|
|
2005-10-10 16:15:28 +02:00
|
|
|
void CRenderer::ScriptingInit()
|
|
|
|
{
|
2005-10-25 03:43:07 +02:00
|
|
|
AddProperty(L"renderpath", &CRenderer::JSI_GetRenderPath, &CRenderer::JSI_SetRenderPath);
|
2006-01-22 20:12:30 +01:00
|
|
|
AddProperty(L"displayFrustum", &CRenderer::m_DisplayFrustum);
|
2006-02-13 15:18:20 +01:00
|
|
|
AddProperty(L"shadowZBias", &CRenderer::m_ShadowZBias);
|
2007-02-09 18:04:55 +01:00
|
|
|
AddProperty(L"shadowMapSize", &CRenderer::m_ShadowMapSize);
|
2011-03-26 21:17:21 +01:00
|
|
|
AddProperty(L"shadows", &CRenderer::JSI_GetShadows, &CRenderer::JSI_SetShadows);
|
2006-02-16 00:50:24 +01:00
|
|
|
AddProperty(L"depthTextureBits", &CRenderer::JSI_GetDepthTextureBits, &CRenderer::JSI_SetDepthTextureBits);
|
2011-03-07 01:34:03 +01:00
|
|
|
AddProperty(L"shadowAlphaFix", &CRenderer::JSI_GetShadowAlphaFix, &CRenderer::JSI_SetShadowAlphaFix);
|
2011-07-13 01:48:05 +02:00
|
|
|
AddProperty(L"shadowPCF", &CRenderer::JSI_GetShadowPCF, &CRenderer::JSI_SetShadowPCF);
|
2012-01-29 02:22:22 +01:00
|
|
|
AddProperty(L"preferGLSL", &CRenderer::JSI_GetPreferGLSL, &CRenderer::JSI_SetPreferGLSL);
|
2006-09-23 18:04:54 +02:00
|
|
|
AddProperty(L"skipSubmit", &CRenderer::m_SkipSubmit);
|
2006-05-17 04:24:17 +02:00
|
|
|
AddProperty(L"skySet", &CRenderer::JSI_GetSky, &CRenderer::JSI_SetSky);
|
2005-10-10 16:15:28 +02:00
|
|
|
|
|
|
|
CJSObject<CRenderer>::ScriptingInit("Renderer");
|
|
|
|
}
|
|
|
|
|
2007-05-07 18:33:24 +02:00
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
CTextureManager& CRenderer::GetTextureManager()
|
|
|
|
{
|
|
|
|
return m->textureManager;
|
|
|
|
}
|
2011-03-26 21:17:21 +01:00
|
|
|
|
|
|
|
CShaderManager& CRenderer::GetShaderManager()
|
|
|
|
{
|
|
|
|
return m->shaderManager;
|
|
|
|
}
|
2011-04-03 21:15:15 +02:00
|
|
|
|
|
|
|
CParticleManager& CRenderer::GetParticleManager()
|
|
|
|
{
|
|
|
|
return m->particleManager;
|
|
|
|
}
|
2012-04-03 20:44:46 +02:00
|
|
|
|
2012-04-24 18:46:32 +02:00
|
|
|
TerrainRenderer& CRenderer::GetTerrainRenderer()
|
|
|
|
{
|
|
|
|
return m->terrainRenderer;
|
|
|
|
}
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
CMaterialManager& CRenderer::GetMaterialManager()
|
|
|
|
{
|
|
|
|
return m->materialManager;
|
|
|
|
}
|