2004-06-03 04:20:48 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Name: Renderer.cpp
|
|
|
|
// Author: Rich Cross
|
|
|
|
// Contact: rich@wildfiregames.com
|
|
|
|
//
|
|
|
|
// Description: OpenGL renderer class; a higher level interface
|
2004-06-07 21:53:58 +02:00
|
|
|
// on top of OpenGL to handle rendering the basic visual games
|
2004-05-30 02:46:58 +02:00
|
|
|
// types - terrain, models, sprites, particles etc
|
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
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>
|
|
|
|
#include "Renderer.h"
|
2006-06-02 04:10:27 +02:00
|
|
|
#include "graphics/Terrain.h"
|
|
|
|
#include "maths/Matrix3D.h"
|
|
|
|
#include "maths/MathUtil.h"
|
|
|
|
#include "graphics/Camera.h"
|
|
|
|
#include "graphics/Texture.h"
|
|
|
|
#include "graphics/LightEnv.h"
|
|
|
|
#include "graphics/Terrain.h"
|
2006-06-02 05:56:24 +02:00
|
|
|
#include "ps/Pyrogenesis.h" // MICROLOG
|
2006-06-02 04:10:27 +02:00
|
|
|
#include "ps/CLogger.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"
|
|
|
|
#include "ps/Game.h"
|
|
|
|
#include "ps/World.h"
|
|
|
|
#include "ps/Player.h"
|
|
|
|
#include "simulation/LOSManager.h"
|
2006-09-07 00:26:27 +02:00
|
|
|
#include "simulation/TerritoryManager.h"
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2006-06-02 04:10:27 +02:00
|
|
|
#include "graphics/Model.h"
|
|
|
|
#include "graphics/ModelDef.h"
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2006-06-02 04:10:27 +02:00
|
|
|
#include "lib/ogl.h"
|
2006-04-22 18:26:16 +02:00
|
|
|
#include "lib/path_util.h"
|
2005-10-21 09:47:38 +02:00
|
|
|
#include "lib/res/res.h"
|
2005-09-02 04:54:02 +02:00
|
|
|
#include "lib/res/file/file.h"
|
2005-08-12 19:06:53 +02:00
|
|
|
#include "lib/res/graphics/tex.h"
|
|
|
|
#include "lib/res/graphics/ogl_tex.h"
|
2005-10-12 19:19:07 +02:00
|
|
|
#include "ps/Loader.h"
|
2005-11-19 17:15:34 +01:00
|
|
|
#include "ps/ProfileViewer.h"
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2006-02-11 10:40:04 +01:00
|
|
|
#include "graphics/ParticleEngine.h"
|
|
|
|
#include "graphics/DefaultEmitter.h"
|
2005-10-25 03:43:07 +02:00
|
|
|
#include "renderer/FixedFunctionModelRenderer.h"
|
|
|
|
#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"
|
|
|
|
#include "renderer/PlayerRenderer.h"
|
|
|
|
#include "renderer/RenderModifiers.h"
|
2005-10-05 18:42:09 +02:00
|
|
|
#include "renderer/RenderPathVertexShader.h"
|
2006-01-07 02:04:26 +01:00
|
|
|
#include "renderer/ShadowMap.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"
|
2005-10-25 03:43:07 +02:00
|
|
|
#include "renderer/TransparencyRenderer.h"
|
2006-01-07 02:04:26 +01:00
|
|
|
#include "renderer/WaterManager.h"
|
2006-05-16 06:41:37 +02:00
|
|
|
#include "renderer/SkyManager.h"
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2004-08-15 22:57:31 +02:00
|
|
|
#define LOG_CATEGORY "graphics"
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
class CRendererStatsTable : public AbstractProfileTable
|
|
|
|
{
|
|
|
|
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 {
|
|
|
|
Row_Counter = 0,
|
|
|
|
Row_DrawCalls,
|
|
|
|
Row_TerrainTris,
|
|
|
|
Row_ModelTris,
|
|
|
|
Row_BlendSplats,
|
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
|
|
|
|
};
|
2005-12-12 20:19:30 +01:00
|
|
|
|
2006-05-17 18:47:49 +02:00
|
|
|
NO_COPY_CTOR(CRendererStatsTable);
|
2005-11-19 17:15:34 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// 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_Counter:
|
|
|
|
if (col == 0)
|
|
|
|
return "counter";
|
|
|
|
snprintf(buf, sizeof(buf), "%d", Stats.m_Counter);
|
|
|
|
return buf;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
case Row_DrawCalls:
|
|
|
|
if (col == 0)
|
|
|
|
return "# draw calls";
|
|
|
|
snprintf(buf, sizeof(buf), "%d", Stats.m_DrawCalls);
|
|
|
|
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";
|
|
|
|
snprintf(buf, sizeof(buf), "%d", Stats.m_TerrainTris);
|
|
|
|
return buf;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
case Row_ModelTris:
|
|
|
|
if (col == 0)
|
|
|
|
return "# model tris";
|
|
|
|
snprintf(buf, sizeof(buf), "%d", Stats.m_ModelTris);
|
|
|
|
return buf;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
case Row_BlendSplats:
|
|
|
|
if (col == 0)
|
|
|
|
return "# blend splats";
|
|
|
|
snprintf(buf, sizeof(buf), "%d", Stats.m_BlendSplats);
|
|
|
|
return buf;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
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
|
|
|
|
|
2006-03-26 01:54:20 +01:00
|
|
|
enum {
|
|
|
|
AmbientDiffuse = 0,
|
|
|
|
OnlyDiffuse,
|
|
|
|
|
|
|
|
NumVertexTypes
|
|
|
|
};
|
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
/**
|
|
|
|
* Struct CRendererInternals: Truly hide data that is supposed to be hidden
|
|
|
|
* in this structure so it won't even appear in header files.
|
|
|
|
*/
|
|
|
|
struct CRendererInternals
|
|
|
|
{
|
2006-03-28 21:45:44 +02:00
|
|
|
/// true if CRenderer::Open has been called
|
|
|
|
bool IsOpen;
|
|
|
|
|
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
|
|
|
|
|
|
|
/// Water manager
|
|
|
|
WaterManager waterManager;
|
|
|
|
|
2006-05-16 06:41:37 +02:00
|
|
|
/// Sky manager
|
|
|
|
SkyManager skyManager;
|
|
|
|
|
2006-01-07 02:04:26 +01:00
|
|
|
/// Terrain renderer
|
|
|
|
TerrainRenderer* terrainRenderer;
|
|
|
|
|
|
|
|
/// Shadow map
|
|
|
|
ShadowMap* shadow;
|
2006-01-22 20:12:30 +01:00
|
|
|
|
2006-03-26 01:54:20 +01:00
|
|
|
/// Various model renderers
|
|
|
|
struct Models {
|
|
|
|
// The following model renderers are aliases for the appropriate real_*
|
|
|
|
// model renderers (depending on hardware availability and current settings)
|
|
|
|
// and must be used for actual model submission and rendering
|
|
|
|
ModelRenderer* Normal;
|
|
|
|
ModelRenderer* NormalInstancing;
|
|
|
|
ModelRenderer* Player;
|
|
|
|
ModelRenderer* PlayerInstancing;
|
|
|
|
ModelRenderer* Transp;
|
|
|
|
|
|
|
|
// "Palette" of available ModelRenderers. Do not use these directly for
|
|
|
|
// rendering and submission; use the aliases above instead.
|
|
|
|
ModelRenderer* pal_NormalFF[NumVertexTypes];
|
|
|
|
ModelRenderer* pal_PlayerFF[NumVertexTypes];
|
|
|
|
ModelRenderer* pal_NormalHWLit[NumVertexTypes];
|
|
|
|
ModelRenderer* pal_PlayerHWLit[NumVertexTypes];
|
|
|
|
ModelRenderer* pal_NormalInstancing[NumVertexTypes];
|
|
|
|
ModelRenderer* pal_PlayerInstancing[NumVertexTypes];
|
|
|
|
ModelRenderer* pal_TranspFF[NumVertexTypes];
|
|
|
|
ModelRenderer* pal_TranspHWLit[NumVertexTypes];
|
|
|
|
ModelRenderer* pal_TranspSortAll;
|
|
|
|
|
|
|
|
ModelVertexRendererPtr VertexFF[NumVertexTypes];
|
|
|
|
ModelVertexRendererPtr VertexHWLit[NumVertexTypes];
|
|
|
|
ModelVertexRendererPtr VertexInstancing[NumVertexTypes];
|
|
|
|
ModelVertexRendererPtr VertexPolygonSort;
|
|
|
|
|
|
|
|
// generic RenderModifiers that are supposed to be used directly
|
|
|
|
RenderModifierPtr ModWireframe;
|
|
|
|
RenderModifierPtr ModSolidColor;
|
|
|
|
RenderModifierPtr ModTransparentShadow;
|
|
|
|
RenderModifierPtr ModTransparentDepthShadow;
|
|
|
|
|
|
|
|
// RenderModifiers that are selected from the palette below
|
|
|
|
RenderModifierPtr ModNormal;
|
|
|
|
RenderModifierPtr ModPlayer;
|
|
|
|
RenderModifierPtr ModTransparent;
|
|
|
|
|
|
|
|
// Palette of available RenderModifiers
|
|
|
|
RenderModifierPtr ModPlain;
|
|
|
|
LitRenderModifierPtr ModPlainLit;
|
|
|
|
RenderModifierPtr ModPlayerUnlit;
|
|
|
|
LitRenderModifierPtr ModPlayerLit;
|
|
|
|
RenderModifierPtr ModTransparentUnlit;
|
|
|
|
LitRenderModifierPtr ModTransparentLit;
|
|
|
|
} Model;
|
|
|
|
|
|
|
|
|
2005-11-19 17:15:34 +01:00
|
|
|
CRendererInternals()
|
2006-03-28 21:45:44 +02:00
|
|
|
: IsOpen(false), profileTable(g_Renderer.m_Stats)
|
2005-11-19 17:15:34 +01:00
|
|
|
{
|
2006-01-07 02:04:26 +01:00
|
|
|
terrainRenderer = new TerrainRenderer();
|
|
|
|
shadow = new ShadowMap();
|
2006-03-28 21:45:44 +02:00
|
|
|
|
|
|
|
for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType)
|
|
|
|
{
|
|
|
|
Model.pal_NormalFF[vertexType] = 0;
|
|
|
|
Model.pal_PlayerFF[vertexType] = 0;
|
|
|
|
Model.pal_TranspFF[vertexType] = 0;
|
|
|
|
Model.pal_NormalHWLit[vertexType] = 0;
|
|
|
|
Model.pal_PlayerHWLit[vertexType] = 0;
|
|
|
|
Model.pal_TranspHWLit[vertexType] = 0;
|
|
|
|
Model.pal_NormalInstancing[vertexType] = 0;
|
|
|
|
Model.pal_PlayerInstancing[vertexType] = 0;
|
|
|
|
}
|
|
|
|
Model.pal_TranspSortAll = 0;
|
|
|
|
|
|
|
|
Model.Normal = 0;
|
|
|
|
Model.NormalInstancing = 0;
|
|
|
|
Model.Player = 0;
|
|
|
|
Model.PlayerInstancing = 0;
|
|
|
|
Model.Transp = 0;
|
2006-01-07 02:04:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
~CRendererInternals()
|
|
|
|
{
|
|
|
|
delete shadow;
|
|
|
|
delete terrainRenderer;
|
2005-11-19 17:15:34 +01:00
|
|
|
}
|
2006-03-31 04:19:11 +02:00
|
|
|
|
|
|
|
bool CanUseRenderPathVertexShader()
|
|
|
|
{
|
|
|
|
for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType)
|
|
|
|
{
|
|
|
|
if (!Model.pal_NormalHWLit[vertexType] ||
|
|
|
|
!Model.pal_PlayerHWLit[vertexType])
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2006-09-14 22:06:21 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Load the OpenGL projection and modelview matrices and the viewport according
|
|
|
|
* to the given camera.
|
|
|
|
*/
|
|
|
|
void SetOpenGLCamera(const CCamera& camera)
|
|
|
|
{
|
|
|
|
CMatrix3D view;
|
|
|
|
camera.m_Orientation.GetInverse(view);
|
|
|
|
const CMatrix3D& proj = camera.GetProjection();
|
|
|
|
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glLoadMatrixf(&proj._11);
|
|
|
|
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glLoadMatrixf(&view._11);
|
|
|
|
|
|
|
|
const SViewPort &vp = camera.GetViewPort();
|
|
|
|
glViewport(vp.m_X,vp.m_Y,vp.m_Width,vp.m_Height);
|
|
|
|
}
|
2005-11-19 17:15:34 +01: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
|
|
|
{
|
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
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
m_Width=0;
|
|
|
|
m_Height=0;
|
|
|
|
m_Depth=0;
|
|
|
|
m_FrameCounter=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
|
|
|
|
2005-10-25 04:22:22 +02:00
|
|
|
m_SortAllTransparent = false;
|
2005-10-28 21:25:47 +02:00
|
|
|
m_FastNormals = true;
|
2006-01-22 20:12:30 +01:00
|
|
|
m_DisplayFrustum = false;
|
2006-02-16 00:50:24 +01:00
|
|
|
m_DisableCopyShadow = false;
|
2006-03-26 19:36:33 +02:00
|
|
|
m_FastPlayerColor = true;
|
2006-09-23 18:04:54 +02:00
|
|
|
m_SkipSubmit = false;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-05 18:42:09 +02:00
|
|
|
m_VertexShader = 0;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
m_Options.m_NoVBO=false;
|
2006-03-26 19:36:33 +02:00
|
|
|
m_Options.m_NoFramebufferObject = false;
|
2004-05-30 02:46:58 +02:00
|
|
|
m_Options.m_Shadows=true;
|
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;
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2006-07-17 02:59:32 +02:00
|
|
|
m_ShadowZBias = 0.02f;
|
2006-03-26 19:36:33 +02:00
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
for (uint i=0;i<MaxTextureUnits;i++) {
|
|
|
|
m_ActiveTextures[i]=0;
|
|
|
|
}
|
2005-09-18 05:47:15 +02:00
|
|
|
|
2005-10-10 16:15:28 +02:00
|
|
|
ONCE( ScriptingInit(); );
|
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);
|
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
|
|
|
{
|
2005-10-25 03:43:07 +02:00
|
|
|
// model rendering
|
2006-03-26 01:54:20 +01:00
|
|
|
for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType)
|
|
|
|
{
|
|
|
|
delete m->Model.pal_NormalFF[vertexType];
|
|
|
|
delete m->Model.pal_PlayerFF[vertexType];
|
|
|
|
delete m->Model.pal_TranspFF[vertexType];
|
|
|
|
delete m->Model.pal_NormalHWLit[vertexType];
|
|
|
|
delete m->Model.pal_PlayerHWLit[vertexType];
|
|
|
|
delete m->Model.pal_TranspHWLit[vertexType];
|
|
|
|
delete m->Model.pal_NormalInstancing[vertexType];
|
|
|
|
delete m->Model.pal_PlayerInstancing[vertexType];
|
|
|
|
}
|
|
|
|
delete m->Model.pal_TranspSortAll;
|
2005-10-25 03:43:07 +02:00
|
|
|
|
|
|
|
// general
|
2005-10-06 18:07:36 +02:00
|
|
|
delete m_VertexShader;
|
|
|
|
m_VertexShader = 0;
|
2005-10-12 06:16:41 +02:00
|
|
|
|
2006-03-15 19:32:24 +01:00
|
|
|
CParticleEngine::GetInstance()->cleanup();
|
|
|
|
|
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
|
|
|
|
m_Caps.m_VBO=false;
|
|
|
|
m_Caps.m_TextureBorderClamp=false;
|
|
|
|
m_Caps.m_GenerateMipmaps=false;
|
2005-10-01 00:23:48 +02:00
|
|
|
m_Caps.m_VertexShader=false;
|
2006-02-11 19:04:32 +01:00
|
|
|
m_Caps.m_DepthTextureShadows = false;
|
2006-03-26 19:36:33 +02:00
|
|
|
m_Caps.m_FramebufferObject = false;
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// now start querying extensions
|
|
|
|
if (!m_Options.m_NoVBO) {
|
2005-03-10 17:57:42 +01:00
|
|
|
if (oglHaveExtension("GL_ARB_vertex_buffer_object")) {
|
2004-05-30 02:46:58 +02:00
|
|
|
m_Caps.m_VBO=true;
|
|
|
|
}
|
|
|
|
}
|
2005-03-10 17:57:42 +01:00
|
|
|
if (oglHaveExtension("GL_ARB_texture_border_clamp")) {
|
2004-05-30 02:46:58 +02:00
|
|
|
m_Caps.m_TextureBorderClamp=true;
|
|
|
|
}
|
2005-03-10 17:57:42 +01:00
|
|
|
if (oglHaveExtension("GL_SGIS_generate_mipmap")) {
|
2004-05-30 02:46:58 +02:00
|
|
|
m_Caps.m_GenerateMipmaps=true;
|
|
|
|
}
|
2005-10-01 00:23:48 +02:00
|
|
|
if (0 == oglHaveExtensions(0, "GL_ARB_shader_objects", "GL_ARB_shading_language_100", 0))
|
|
|
|
{
|
|
|
|
if (oglHaveExtension("GL_ARB_vertex_shader"))
|
|
|
|
m_Caps.m_VertexShader=true;
|
|
|
|
}
|
2006-02-15 01:45:16 +01:00
|
|
|
|
2006-02-11 19:04:32 +01:00
|
|
|
if (0 == oglHaveExtensions(0, "GL_ARB_shadow", "GL_ARB_depth_texture", 0)) {
|
2006-02-15 01:45:16 +01:00
|
|
|
// According to Delphi3d.net, all relevant graphics chips that support depth textures
|
|
|
|
// (i.e. Geforce3+, Radeon9500+, even i915) also have >= 4 TMUs, so this restriction
|
|
|
|
// isn't actually a restriction, and it helps with integrating depth texture
|
|
|
|
// shadows into rendering paths.
|
|
|
|
if (ogl_max_tex_units >= 4)
|
|
|
|
m_Caps.m_DepthTextureShadows = true;
|
2006-02-11 19:04:32 +01:00
|
|
|
}
|
2006-03-26 19:36:33 +02:00
|
|
|
if (!m_Options.m_NoFramebufferObject)
|
|
|
|
{
|
|
|
|
if (oglHaveExtension("GL_EXT_framebuffer_object"))
|
|
|
|
m_Caps.m_FramebufferObject = true;
|
|
|
|
}
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2005-10-01 00:23:48 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
bool CRenderer::Open(int width, int height, int depth)
|
|
|
|
{
|
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();
|
|
|
|
m->shadow->SetUseDepthTexture(true);
|
|
|
|
|
|
|
|
m_VertexShader = new RenderPathVertexShader;
|
|
|
|
if (!m_VertexShader->Init())
|
|
|
|
{
|
|
|
|
delete m_VertexShader;
|
|
|
|
m_VertexShader = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// model rendering
|
|
|
|
m->Model.VertexFF[AmbientDiffuse] = ModelVertexRendererPtr(new FixedFunctionModelRenderer(false));
|
|
|
|
m->Model.VertexFF[OnlyDiffuse] = ModelVertexRendererPtr(new FixedFunctionModelRenderer(true));
|
|
|
|
if (HWLightingModelRenderer::IsAvailable())
|
|
|
|
{
|
|
|
|
m->Model.VertexHWLit[AmbientDiffuse] = ModelVertexRendererPtr(new HWLightingModelRenderer(false));
|
|
|
|
m->Model.VertexHWLit[OnlyDiffuse] = ModelVertexRendererPtr(new HWLightingModelRenderer(true));
|
|
|
|
}
|
|
|
|
if (InstancingModelRenderer::IsAvailable())
|
|
|
|
{
|
|
|
|
m->Model.VertexInstancing[AmbientDiffuse] = ModelVertexRendererPtr(new InstancingModelRenderer(false));
|
|
|
|
m->Model.VertexInstancing[OnlyDiffuse] = ModelVertexRendererPtr(new InstancingModelRenderer(true));
|
|
|
|
}
|
|
|
|
m->Model.VertexPolygonSort = ModelVertexRendererPtr(new PolygonSortModelRenderer);
|
|
|
|
|
|
|
|
for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType)
|
|
|
|
{
|
|
|
|
m->Model.pal_NormalFF[vertexType] = new BatchModelRenderer(m->Model.VertexFF[vertexType]);
|
|
|
|
m->Model.pal_PlayerFF[vertexType] = new BatchModelRenderer(m->Model.VertexFF[vertexType]);
|
|
|
|
m->Model.pal_TranspFF[vertexType] = new SortModelRenderer(m->Model.VertexFF[vertexType]);
|
|
|
|
if (m->Model.VertexHWLit[vertexType])
|
|
|
|
{
|
|
|
|
m->Model.pal_NormalHWLit[vertexType] = new BatchModelRenderer(m->Model.VertexHWLit[vertexType]);
|
|
|
|
m->Model.pal_PlayerHWLit[vertexType] = new BatchModelRenderer(m->Model.VertexHWLit[vertexType]);
|
|
|
|
m->Model.pal_TranspHWLit[vertexType] = new SortModelRenderer(m->Model.VertexHWLit[vertexType]);
|
|
|
|
}
|
|
|
|
if (m->Model.VertexInstancing[vertexType])
|
|
|
|
{
|
|
|
|
m->Model.pal_NormalInstancing[vertexType] = new BatchModelRenderer(m->Model.VertexInstancing[vertexType]);
|
|
|
|
m->Model.pal_PlayerInstancing[vertexType] = new BatchModelRenderer(m->Model.VertexInstancing[vertexType]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m->Model.pal_TranspSortAll = new SortModelRenderer(m->Model.VertexPolygonSort);
|
|
|
|
|
|
|
|
m->Model.ModWireframe = RenderModifierPtr(new WireframeRenderModifier);
|
|
|
|
m->Model.ModPlain = RenderModifierPtr(new PlainRenderModifier);
|
|
|
|
m->Model.ModPlainLit = LitRenderModifierPtr(new PlainLitRenderModifier);
|
|
|
|
SetFastPlayerColor(true);
|
|
|
|
m->Model.ModPlayerLit = LitRenderModifierPtr(new LitPlayerColorRender);
|
|
|
|
m->Model.ModSolidColor = RenderModifierPtr(new SolidColorRenderModifier);
|
|
|
|
m->Model.ModTransparentUnlit = RenderModifierPtr(new TransparentRenderModifier);
|
|
|
|
m->Model.ModTransparentLit = LitRenderModifierPtr(new LitTransparentRenderModifier);
|
|
|
|
m->Model.ModTransparentShadow = RenderModifierPtr(new TransparentShadowRenderModifier);
|
|
|
|
m->Model.ModTransparentDepthShadow = RenderModifierPtr(new TransparentDepthShadowModifier);
|
|
|
|
|
|
|
|
// Particle engine
|
|
|
|
CParticleEngine::GetInstance()->initParticleSystem();
|
|
|
|
CEmitter *pEmitter = new CDefaultEmitter(1000, -1);
|
|
|
|
CParticleEngine::GetInstance()->addEmitter(pEmitter);
|
|
|
|
|
|
|
|
// Dimensions
|
2004-05-30 02:46:58 +02:00
|
|
|
m_Width = width;
|
|
|
|
m_Height = height;
|
|
|
|
m_Depth = depth;
|
|
|
|
|
|
|
|
// 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);
|
2004-08-15 22:57:31 +02:00
|
|
|
LOG(NORMAL, LOG_CATEGORY, "CRenderer::Open: depth bits %d",bits);
|
2004-06-11 00:24:03 +02:00
|
|
|
glGetIntegerv(GL_STENCIL_BITS,&bits);
|
2004-08-15 22:57:31 +02:00
|
|
|
LOG(NORMAL, LOG_CATEGORY, "CRenderer::Open: stencil bits %d",bits);
|
2004-06-11 00:24:03 +02:00
|
|
|
glGetIntegerv(GL_ALPHA_BITS,&bits);
|
2004-08-15 22:57:31 +02:00
|
|
|
LOG(NORMAL, LOG_CATEGORY, "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
|
|
|
|
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
|
2006-05-31 18:42:50 +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:
|
|
|
|
m_Options.m_NoVBO=value;
|
|
|
|
break;
|
2006-03-26 19:36:33 +02:00
|
|
|
case OPT_NOFRAMEBUFFEROBJECT:
|
|
|
|
m_Options.m_NoFramebufferObject=value;
|
|
|
|
break;
|
2004-05-30 02:46:58 +02:00
|
|
|
case OPT_SHADOWS:
|
|
|
|
m_Options.m_Shadows=value;
|
|
|
|
break;
|
2006-05-17 05:53:54 +02:00
|
|
|
case OPT_FANCYWATER:
|
|
|
|
m_Options.m_FancyWater=value;
|
|
|
|
break;
|
2005-10-07 17:24:29 +02:00
|
|
|
default:
|
|
|
|
debug_warn("CRenderer::SetOptionBool: unknown option");
|
|
|
|
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;
|
2006-03-26 19:36:33 +02:00
|
|
|
case OPT_NOFRAMEBUFFEROBJECT:
|
|
|
|
return m_Options.m_NoFramebufferObject;
|
2004-05-30 02:46:58 +02:00
|
|
|
case OPT_SHADOWS:
|
|
|
|
return m_Options.m_Shadows;
|
2006-05-17 05:53:54 +02:00
|
|
|
case OPT_FANCYWATER:
|
|
|
|
return m_Options.m_FancyWater;
|
2005-10-07 17:24:29 +02:00
|
|
|
default:
|
|
|
|
debug_warn("CRenderer::GetOptionBool: unknown option");
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
2004-06-07 21:53:58 +02:00
|
|
|
// SetOptionColor: set color renderer option
|
2006-09-26 03:44:20 +02:00
|
|
|
// void CRenderer::SetOptionColor(Option UNUSED(opt),const RGBAColor& UNUSED(value))
|
|
|
|
// {
|
|
|
|
// // switch (opt) {
|
|
|
|
// // default:
|
|
|
|
// debug_warn("CRenderer::SetOptionColor: unknown option");
|
|
|
|
// // break;
|
|
|
|
// // }
|
|
|
|
// }
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2004-10-17 23:01:00 +02:00
|
|
|
void CRenderer::SetOptionFloat(enum Option opt, float val)
|
|
|
|
{
|
2004-10-21 17:04:19 +02:00
|
|
|
switch(opt)
|
|
|
|
{
|
|
|
|
case OPT_LODBIAS:
|
|
|
|
m_Options.m_LodBias = val;
|
|
|
|
break;
|
2005-10-07 17:24:29 +02:00
|
|
|
default:
|
|
|
|
debug_warn("CRenderer::SetOptionFloat: unknown option");
|
|
|
|
break;
|
2004-10-21 17:04:19 +02:00
|
|
|
}
|
2004-10-17 23:01:00 +02:00
|
|
|
}
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
2004-06-07 21:53:58 +02:00
|
|
|
// GetOptionColor: get color renderer option
|
2006-09-26 03:44:20 +02:00
|
|
|
// const RGBAColor& CRenderer::GetOptionColor(Option UNUSED(opt)) const
|
|
|
|
// {
|
|
|
|
// static const RGBAColor defaultColor(1.0f,1.0f,1.0f,1.0f);
|
|
|
|
//
|
|
|
|
// // switch (opt) {
|
|
|
|
// // default:
|
|
|
|
// debug_warn("CRenderer::GetOptionColor: unknown option");
|
|
|
|
// // break;
|
|
|
|
// // }
|
|
|
|
//
|
|
|
|
// return defaultColor;
|
|
|
|
// }
|
2004-05-30 02:46:58 +02:00
|
|
|
|
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
|
|
|
{
|
2006-03-31 04:19:11 +02:00
|
|
|
if (m->CanUseRenderPathVertexShader())
|
2005-10-25 03:43:07 +02:00
|
|
|
rp = RP_VERTEXSHADER;
|
|
|
|
else
|
|
|
|
rp = RP_FIXED;
|
|
|
|
}
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
if (rp == RP_VERTEXSHADER)
|
|
|
|
{
|
2006-03-31 04:19:11 +02:00
|
|
|
if (!m->CanUseRenderPathVertexShader())
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
|
|
|
LOG(WARNING, LOG_CATEGORY, "Falling back to fixed function\n");
|
|
|
|
rp = RP_FIXED;
|
|
|
|
}
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-05 18:42:09 +02:00
|
|
|
m_Options.m_RenderPath = rp;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CStr CRenderer::GetRenderPathName(RenderPath rp)
|
|
|
|
{
|
|
|
|
switch(rp) {
|
|
|
|
case RP_DEFAULT: return "default";
|
|
|
|
case RP_FIXED: return "fixed";
|
|
|
|
case RP_VERTEXSHADER: return "vertexshader";
|
|
|
|
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;
|
|
|
|
if (name == "vertexshader")
|
|
|
|
return RP_VERTEXSHADER;
|
|
|
|
if (name == "default")
|
|
|
|
return RP_DEFAULT;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-05 18:42:09 +02:00
|
|
|
LOG(WARNING, LOG_CATEGORY, "Unknown render path name '%hs', assuming 'default'", name.c_str());
|
|
|
|
return RP_DEFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// SetFastPlayerColor
|
|
|
|
void CRenderer::SetFastPlayerColor(bool fast)
|
|
|
|
{
|
|
|
|
m_FastPlayerColor = fast;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
if (m_FastPlayerColor)
|
|
|
|
{
|
|
|
|
if (!FastPlayerColorRender::IsAvailable())
|
|
|
|
{
|
|
|
|
LOG(WARNING, LOG_CATEGORY, "Falling back to slower player color rendering.");
|
|
|
|
m_FastPlayerColor = false;
|
|
|
|
}
|
|
|
|
}
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
if (m_FastPlayerColor)
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.ModPlayerUnlit = RenderModifierPtr(new FastPlayerColorRender);
|
2005-10-25 03:43:07 +02:00
|
|
|
else
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.ModPlayerUnlit = RenderModifierPtr(new SlowPlayerColorRender);
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// BeginFrame: signal frame start
|
|
|
|
void CRenderer::BeginFrame()
|
|
|
|
{
|
|
|
|
// bump frame counter
|
|
|
|
m_FrameCounter++;
|
|
|
|
|
2005-10-05 18:42:09 +02:00
|
|
|
if (m_VertexShader)
|
|
|
|
m_VertexShader->BeginFrame();
|
2006-01-07 02:04:26 +01:00
|
|
|
|
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
|
|
|
|
2006-03-26 01:54:20 +01:00
|
|
|
// choose model renderers for this frame
|
|
|
|
int vertexType;
|
|
|
|
|
|
|
|
if (m_Options.m_Shadows && m->shadow->GetUseDepthTexture())
|
|
|
|
{
|
|
|
|
vertexType = OnlyDiffuse;
|
|
|
|
m->Model.ModNormal = m->Model.ModPlainLit;
|
|
|
|
m->Model.ModPlainLit->SetShadowMap(m->shadow);
|
|
|
|
m->Model.ModPlainLit->SetLightEnv(m_LightEnv);
|
|
|
|
|
|
|
|
m->Model.ModPlayer = m->Model.ModPlayerLit;
|
|
|
|
m->Model.ModPlayerLit->SetShadowMap(m->shadow);
|
|
|
|
m->Model.ModPlayerLit->SetLightEnv(m_LightEnv);
|
|
|
|
|
|
|
|
m->Model.ModTransparent = m->Model.ModTransparentLit;
|
|
|
|
m->Model.ModTransparentLit->SetShadowMap(m->shadow);
|
|
|
|
m->Model.ModTransparentLit->SetLightEnv(m_LightEnv);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vertexType = AmbientDiffuse;
|
|
|
|
m->Model.ModNormal = m->Model.ModPlain;
|
|
|
|
m->Model.ModPlayer = m->Model.ModPlayerUnlit;
|
|
|
|
m->Model.ModTransparent = m->Model.ModTransparentUnlit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_Options.m_RenderPath == RP_VERTEXSHADER)
|
|
|
|
{
|
2006-03-28 21:45:44 +02:00
|
|
|
debug_assert(m->Model.pal_NormalHWLit[vertexType] != 0);
|
|
|
|
|
2006-03-26 01:54:20 +01:00
|
|
|
if (m->Model.pal_NormalInstancing)
|
|
|
|
m->Model.NormalInstancing = m->Model.pal_NormalInstancing[vertexType];
|
|
|
|
else
|
|
|
|
m->Model.NormalInstancing = m->Model.pal_NormalHWLit[vertexType];
|
|
|
|
m->Model.Normal = m->Model.pal_NormalHWLit[vertexType];
|
|
|
|
|
|
|
|
if (m->Model.pal_PlayerInstancing)
|
|
|
|
m->Model.PlayerInstancing = m->Model.pal_PlayerInstancing[vertexType];
|
|
|
|
else
|
|
|
|
m->Model.PlayerInstancing = m->Model.pal_PlayerHWLit[vertexType];
|
|
|
|
m->Model.Player = m->Model.pal_PlayerHWLit[vertexType];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m->Model.NormalInstancing = m->Model.pal_NormalFF[vertexType];
|
|
|
|
m->Model.Normal = m->Model.pal_NormalFF[vertexType];
|
|
|
|
|
|
|
|
m->Model.PlayerInstancing = m->Model.pal_PlayerFF[vertexType];
|
|
|
|
m->Model.Player = m->Model.pal_PlayerFF[vertexType];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_SortAllTransparent)
|
|
|
|
m->Model.Transp = m->Model.pal_TranspSortAll;
|
|
|
|
else if (m_Options.m_RenderPath == RP_VERTEXSHADER)
|
|
|
|
m->Model.Transp = m->Model.pal_TranspHWLit[vertexType];
|
|
|
|
else
|
|
|
|
m->Model.Transp = m->Model.pal_TranspFF[vertexType];
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2006-03-26 01:54:20 +01:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// SetClearColor: set color used to clear screen in BeginFrame()
|
|
|
|
void CRenderer::SetClearColor(u32 color)
|
2004-06-07 21:53:58 +02:00
|
|
|
{
|
2004-05-30 02:46:58 +02:00
|
|
|
m_ClearColor[0]=float(color & 0xff)/255.0f;
|
|
|
|
m_ClearColor[1]=float((color>>8) & 0xff)/255.0f;
|
|
|
|
m_ClearColor[2]=float((color>>16) & 0xff)/255.0f;
|
|
|
|
m_ClearColor[3]=float((color>>24) & 0xff)/255.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::RenderShadowMap()
|
|
|
|
{
|
2005-05-20 19:09:47 +02:00
|
|
|
PROFILE( "render shadow map" );
|
|
|
|
|
2006-01-07 02:04:26 +01:00
|
|
|
m->shadow->BeginRender();
|
2006-01-22 20:12:30 +01:00
|
|
|
|
2006-02-15 01:45:16 +01:00
|
|
|
float shadowTransp = m_LightEnv->GetTerrainShadowTransparency();
|
|
|
|
glColor3f(shadowTransp, shadowTransp, shadowTransp);
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2006-02-13 15:18:20 +01:00
|
|
|
// Figure out transparent rendering strategy
|
2006-03-26 01:54:20 +01:00
|
|
|
RenderModifierPtr transparentShadows = m->Model.ModTransparentShadow;
|
2006-02-13 15:18:20 +01:00
|
|
|
|
|
|
|
if (m->shadow->GetUseDepthTexture())
|
2006-03-26 01:54:20 +01:00
|
|
|
transparentShadows = m->Model.ModTransparentDepthShadow;
|
2006-02-13 15:18:20 +01:00
|
|
|
|
|
|
|
// Render all closed models (i.e. models where rendering back faces will produce
|
|
|
|
// the correct result)
|
|
|
|
glCullFace(GL_FRONT);
|
|
|
|
|
|
|
|
if (m->shadow->GetUseDepthTexture())
|
|
|
|
m->terrainRenderer->RenderPatches();
|
|
|
|
|
|
|
|
glCullFace(GL_BACK);
|
|
|
|
|
|
|
|
// Render models that aren't closed
|
2004-06-11 00:24:03 +02:00
|
|
|
glDisable(GL_CULL_FACE);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.Normal->Render(m->Model.ModSolidColor, MODELFLAG_CASTSHADOWS);
|
|
|
|
if (m->Model.Normal != m->Model.NormalInstancing)
|
|
|
|
m->Model.NormalInstancing->Render(m->Model.ModSolidColor, MODELFLAG_CASTSHADOWS);
|
|
|
|
m->Model.Player->Render(m->Model.ModSolidColor, MODELFLAG_CASTSHADOWS);
|
|
|
|
if (m->Model.Player != m->Model.PlayerInstancing)
|
|
|
|
m->Model.PlayerInstancing->Render(m->Model.ModSolidColor, MODELFLAG_CASTSHADOWS);
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.Transp->Render(transparentShadows, MODELFLAG_CASTSHADOWS);
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2006-02-11 19:04:32 +01:00
|
|
|
glEnable(GL_CULL_FACE);
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2006-02-11 19:04:32 +01:00
|
|
|
glColor3f(1.0, 1.0, 1.0);
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2006-02-11 19:04:32 +01:00
|
|
|
m->shadow->EndRender();
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::RenderPatches()
|
|
|
|
{
|
2006-04-19 02:56:24 +02:00
|
|
|
PROFILE( "render patches" );
|
2005-05-20 19:09:47 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// switch on wireframe if we need it
|
|
|
|
if (m_TerrainRenderMode==WIREFRAME) {
|
2004-07-15 21:29:56 +02:00
|
|
|
MICROLOG(L"wireframe on");
|
2004-05-30 02:46:58 +02:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
|
2004-06-07 21:53:58 +02:00
|
|
|
}
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// render all the patches, including blend pass
|
2004-07-15 21:29:56 +02:00
|
|
|
MICROLOG(L"render patch submissions");
|
2006-02-13 15:18:20 +01:00
|
|
|
m->terrainRenderer->RenderTerrain(m_Options.m_Shadows ? m->shadow : 0);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
if (m_TerrainRenderMode==WIREFRAME) {
|
|
|
|
// switch wireframe off again
|
2004-07-15 21:29:56 +02:00
|
|
|
MICROLOG(L"wireframe off");
|
2004-05-30 02:46:58 +02:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
|
|
|
|
} else if (m_TerrainRenderMode==EDGED_FACES) {
|
|
|
|
// 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 ..
|
|
|
|
glDepthMask(0);
|
|
|
|
SetTexture(0,0);
|
|
|
|
glColor4f(1,1,1,0.35f);
|
|
|
|
glLineWidth(2.0f);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
2006-01-07 02:04:26 +01:00
|
|
|
// render tiles edges
|
|
|
|
m->terrainRenderer->RenderPatches();
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// set color for outline
|
|
|
|
glColor3f(0,0,1);
|
|
|
|
glLineWidth(4.0f);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
|
|
|
// render outline of each patch
|
2006-01-07 02:04:26 +01:00
|
|
|
m->terrainRenderer->RenderOutlines();
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// .. and restore the renderstates
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
glDepthMask(1);
|
|
|
|
|
|
|
|
// restore fill mode, and we're done
|
2006-02-13 01:59:59 +01:00
|
|
|
glLineWidth(1.0f);
|
2004-05-30 02:46:58 +02:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
|
2004-06-11 00:24:03 +02:00
|
|
|
}
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::RenderModels()
|
2004-06-11 00:24:03 +02:00
|
|
|
{
|
2005-05-20 19:09:47 +02:00
|
|
|
PROFILE( "render models ");
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// switch on wireframe if we need it
|
|
|
|
if (m_ModelRenderMode==WIREFRAME) {
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
|
2004-06-07 21:53:58 +02:00
|
|
|
}
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.Normal->Render(m->Model.ModNormal, 0);
|
|
|
|
m->Model.Player->Render(m->Model.ModPlayer, 0);
|
|
|
|
if (m->Model.Normal != m->Model.NormalInstancing)
|
|
|
|
m->Model.NormalInstancing->Render(m->Model.ModNormal, 0);
|
|
|
|
if (m->Model.Player != m->Model.PlayerInstancing)
|
|
|
|
m->Model.PlayerInstancing->Render(m->Model.ModPlayer, 0);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
if (m_ModelRenderMode==WIREFRAME) {
|
|
|
|
// switch wireframe off again
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
|
|
|
|
} else if (m_ModelRenderMode==EDGED_FACES) {
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.Normal->Render(m->Model.ModWireframe, 0);
|
|
|
|
m->Model.Player->Render(m->Model.ModWireframe, 0);
|
|
|
|
if (m->Model.Normal != m->Model.NormalInstancing)
|
|
|
|
m->Model.NormalInstancing->Render(m->Model.ModWireframe, 0);
|
|
|
|
if (m->Model.Player != m->Model.PlayerInstancing)
|
|
|
|
m->Model.PlayerInstancing->Render(m->Model.ModWireframe, 0);
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
}
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
void CRenderer::RenderTransparentModels()
|
|
|
|
{
|
|
|
|
PROFILE( "render transparent models ");
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
// switch on wireframe if we need it
|
|
|
|
if (m_ModelRenderMode==WIREFRAME) {
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
|
|
|
|
}
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.Transp->Render(m->Model.ModTransparent, 0);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
if (m_ModelRenderMode==WIREFRAME) {
|
|
|
|
// switch wireframe off again
|
2004-05-30 02:46:58 +02:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
|
2005-10-25 03:43:07 +02:00
|
|
|
} else if (m_ModelRenderMode==EDGED_FACES) {
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.Transp->Render(m->Model.ModWireframe, 0);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-28 04:13:32 +02:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetModelViewProjectionMatrix: save the current OpenGL model-view-projection matrix
|
|
|
|
CMatrix3D CRenderer::GetModelViewProjectionMatrix()
|
|
|
|
{
|
|
|
|
CMatrix3D proj;
|
|
|
|
CMatrix3D view;
|
|
|
|
|
|
|
|
glGetFloatv( GL_PROJECTION_MATRIX, &proj._11 );
|
|
|
|
glGetFloatv( GL_MODELVIEW_MATRIX, &view._11 );
|
|
|
|
|
|
|
|
return( proj * view );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// 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
|
|
|
|
// - cp is a clip plane in camera space (cp.dot(v) = 0 for any vector v on the plane)
|
|
|
|
// - sign is 1 or -1, to specify the side to clip on
|
|
|
|
void CRenderer::SetObliqueFrustumClipping(const CVector4D& cp, int sign)
|
|
|
|
{
|
|
|
|
float matrix[16];
|
|
|
|
CVector4D q;
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
CMatrix3D viewMatrix;
|
|
|
|
m_ViewCamera.m_Orientation.GetInverse(viewMatrix);
|
|
|
|
CMatrix3D normalMatrix = viewMatrix;
|
|
|
|
normalMatrix._14 = 0;
|
|
|
|
normalMatrix._24 = 0;
|
|
|
|
normalMatrix._34 = 0;
|
|
|
|
normalMatrix._44 = 1;
|
|
|
|
normalMatrix._41 = 0;
|
|
|
|
normalMatrix._42 = 0;
|
|
|
|
normalMatrix._43 = 0;
|
|
|
|
|
|
|
|
// Convert the normal to camera space
|
|
|
|
CVector4D planeNormal(cp.m_X, cp.m_Y, cp.m_Z, 0);
|
|
|
|
planeNormal = normalMatrix.Transform(planeNormal);
|
|
|
|
planeNormal.normalize();
|
|
|
|
|
|
|
|
// Find a point on the plane: we'll take the normal times -D
|
|
|
|
float oldD = cp.m_W;
|
|
|
|
CVector4D pointOnPlane(-oldD * cp.m_X, -oldD * cp.m_Y, -oldD * cp.m_Z, 1);
|
|
|
|
pointOnPlane = viewMatrix.Transform(pointOnPlane);
|
|
|
|
float newD = -pointOnPlane.dot(planeNormal);
|
|
|
|
|
|
|
|
// Now create a clip plane from the new normal and new D
|
|
|
|
CVector4D camPlane = planeNormal;
|
|
|
|
camPlane.m_W = newD;
|
|
|
|
|
|
|
|
// Grab the current projection matrix from OpenGL
|
|
|
|
glGetFloatv(GL_PROJECTION_MATRIX, matrix);
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
q.m_X = (sgn(camPlane.m_X) + matrix[8]) / matrix[0];
|
|
|
|
q.m_Y = (sgn(camPlane.m_Y) + matrix[9]) / matrix[5];
|
|
|
|
q.m_Z = -1.0f;
|
|
|
|
q.m_W = (1.0f + matrix[10]) / matrix[14];
|
|
|
|
|
|
|
|
// Calculate the scaled plane vector
|
|
|
|
CVector4D c = camPlane * (sign * 2.0f / camPlane.dot(q));
|
|
|
|
|
|
|
|
// Replace the third row of the projection matrix
|
|
|
|
matrix[2] = c.m_X;
|
|
|
|
matrix[6] = c.m_Y;
|
|
|
|
matrix[10] = c.m_Z + 1.0f;
|
|
|
|
matrix[14] = c.m_W;
|
|
|
|
|
|
|
|
// Load it back into OpenGL
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glLoadMatrixf(matrix);
|
|
|
|
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// RenderReflections: render the water reflections to the reflection texture
|
|
|
|
void CRenderer::RenderReflections()
|
|
|
|
{
|
|
|
|
MICROLOG(L"render reflections");
|
|
|
|
|
|
|
|
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.Translate(0, -wm.m_WaterHeight, 0);
|
|
|
|
m_ViewCamera.m_Orientation.Scale(1, -1, 1);
|
|
|
|
m_ViewCamera.m_Orientation.Translate(0, wm.m_WaterHeight, 0);
|
|
|
|
SViewPort vp;
|
|
|
|
vp.m_Height = wm.m_ReflectionTextureSize;
|
|
|
|
vp.m_Width = wm.m_ReflectionTextureSize;
|
|
|
|
vp.m_X = 0;
|
|
|
|
vp.m_Y = 0;
|
|
|
|
m_ViewCamera.SetViewPort(&vp);
|
2006-06-01 00:37:23 +02:00
|
|
|
m_ViewCamera.SetProjection(1, 5000, DEGTORAD(21)); // Slightly higher than view FOV of 20
|
|
|
|
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);
|
|
|
|
SetObliqueFrustumClipping(camPlane, -1);
|
|
|
|
|
|
|
|
// Save the model-view-projection matrix so the shaders can use it for projective texturing
|
|
|
|
wm.m_ReflectionMatrix = GetModelViewProjectionMatrix();
|
|
|
|
|
|
|
|
// Disable backface culling so trees render properly (it might also be possible to flip
|
|
|
|
// the culling direction here, but this seems to lead to problems)
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
|
|
|
|
// Make the depth buffer work backwards; there seems to be some oddness with
|
|
|
|
// oblique frustum clipping and the "sign" parameter here
|
|
|
|
glClearDepth(0);
|
|
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
glDepthFunc(GL_GEQUAL);
|
|
|
|
|
|
|
|
// Render sky, terrain and models
|
|
|
|
m->skyManager.RenderSky();
|
|
|
|
oglCheck();
|
|
|
|
RenderPatches();
|
|
|
|
oglCheck();
|
|
|
|
RenderModels();
|
|
|
|
oglCheck();
|
|
|
|
RenderTransparentModels();
|
|
|
|
oglCheck();
|
|
|
|
|
|
|
|
// Copy the image to a texture
|
|
|
|
pglActiveTextureARB(GL_TEXTURE0_ARB);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, wm.m_ReflectionTexture);
|
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
|
|
|
|
wm.m_ReflectionTextureSize, wm.m_ReflectionTextureSize);
|
|
|
|
|
|
|
|
//Reset old camera and re-enable backface culling
|
2006-09-14 22:06:21 +02:00
|
|
|
m_ViewCamera = normalCamera;
|
|
|
|
m->SetOpenGLCamera(m_ViewCamera);
|
|
|
|
|
2006-05-28 04:13:32 +02:00
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
//glClearDepth(1);
|
|
|
|
//glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
//glDepthFunc(GL_LEQUAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// RenderRefractions: render the water refractions to the refraction texture
|
|
|
|
void CRenderer::RenderRefractions()
|
|
|
|
{
|
|
|
|
MICROLOG(L"render refractions");
|
|
|
|
|
|
|
|
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.
|
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;
|
|
|
|
m_ViewCamera.SetViewPort(&vp);
|
2006-06-01 00:37:23 +02:00
|
|
|
m_ViewCamera.SetProjection(1, 5000, DEGTORAD(21)); // Slightly higher than view FOV of 20
|
|
|
|
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);
|
|
|
|
SetObliqueFrustumClipping(camPlane, -1);
|
|
|
|
|
|
|
|
// Save the model-view-projection matrix so the shaders can use it for projective texturing
|
|
|
|
wm.m_RefractionMatrix = GetModelViewProjectionMatrix();
|
|
|
|
|
|
|
|
// Make the depth buffer work backwards; there seems to be some oddness with
|
|
|
|
// oblique frustum clipping and the "sign" parameter here
|
|
|
|
glClearDepth(0);
|
|
|
|
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);
|
|
|
|
glDepthFunc(GL_GEQUAL);
|
|
|
|
|
|
|
|
// Render terrain and models
|
|
|
|
RenderPatches();
|
|
|
|
oglCheck();
|
|
|
|
RenderModels();
|
|
|
|
oglCheck();
|
|
|
|
RenderTransparentModels();
|
|
|
|
oglCheck();
|
|
|
|
|
|
|
|
// Copy the image to a texture
|
|
|
|
pglActiveTextureARB(GL_TEXTURE0_ARB);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, wm.m_RefractionTexture);
|
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
|
|
|
|
wm.m_RefractionTextureSize, wm.m_RefractionTextureSize);
|
|
|
|
|
|
|
|
//Reset old camera and re-enable backface culling
|
2006-09-14 22:06:21 +02:00
|
|
|
m_ViewCamera = normalCamera;
|
|
|
|
m->SetOpenGLCamera(m_ViewCamera);
|
|
|
|
|
2006-05-28 04:13:32 +02:00
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glClearDepth(1);
|
|
|
|
glDepthFunc(GL_LEQUAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
{
|
2004-08-06 17:01:23 +02:00
|
|
|
oglCheck();
|
|
|
|
|
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
|
2005-10-25 04:00:09 +02:00
|
|
|
PROFILE_START("prepare models");
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.Normal->PrepareModels();
|
|
|
|
m->Model.Player->PrepareModels();
|
|
|
|
if (m->Model.Normal != m->Model.NormalInstancing)
|
|
|
|
m->Model.NormalInstancing->PrepareModels();
|
|
|
|
if (m->Model.Player != m->Model.PlayerInstancing)
|
|
|
|
m->Model.PlayerInstancing->PrepareModels();
|
|
|
|
m->Model.Transp->PrepareModels();
|
2005-10-25 04:00:09 +02:00
|
|
|
PROFILE_END("prepare models");
|
2005-01-30 07:27:07 +01:00
|
|
|
|
2006-01-07 02:04:26 +01:00
|
|
|
PROFILE_START("prepare terrain");
|
|
|
|
m->terrainRenderer->PrepareForRendering();
|
|
|
|
PROFILE_END("prepare terrain");
|
|
|
|
|
# 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
|
|
|
if (m_Options.m_Shadows)
|
|
|
|
{
|
2006-02-11 19:04:32 +01:00
|
|
|
MICROLOG(L"render shadows");
|
|
|
|
RenderShadowMap();
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2006-02-11 19:04:32 +01:00
|
|
|
// 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);
|
|
|
|
|
2004-08-06 17:01:23 +02:00
|
|
|
oglCheck();
|
|
|
|
|
# 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
|
|
|
if (m_WaterManager->m_RenderWater && m_Options.m_FancyWater)
|
2006-05-28 04:13:32 +02:00
|
|
|
{
|
|
|
|
// render reflected and refracted scenes, then re-clear the screen
|
|
|
|
RenderReflections();
|
|
|
|
RenderRefractions();
|
|
|
|
glClearColor(m_ClearColor[0],m_ClearColor[1],m_ClearColor[2],m_ClearColor[3]);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
}
|
|
|
|
|
2006-05-16 06:41:37 +02:00
|
|
|
// render sky; this is done before everything so that
|
|
|
|
// (a) we can use a box around the camera instead of placing it "infinitely far away"
|
|
|
|
// (we just disable depth write so that this doesn't affect future rendering)
|
|
|
|
// (b) transparent objects properly overlap the sky
|
|
|
|
if (m_SkyManager->m_RenderSky)
|
|
|
|
{
|
|
|
|
MICROLOG(L"render sky");
|
|
|
|
m->skyManager.RenderSky();
|
|
|
|
oglCheck();
|
|
|
|
}
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// render submitted patches and models
|
2004-07-15 21:29:56 +02:00
|
|
|
MICROLOG(L"render patches");
|
2004-05-30 02:46:58 +02:00
|
|
|
RenderPatches();
|
2004-08-06 17:01:23 +02:00
|
|
|
oglCheck();
|
|
|
|
|
2006-09-22 19:43:00 +02:00
|
|
|
if (g_Game)
|
|
|
|
{
|
|
|
|
g_Game->GetWorld()->GetTerritoryManager()->renderTerritories();
|
|
|
|
oglCheck();
|
|
|
|
}
|
2006-09-07 00:26:27 +02:00
|
|
|
|
2006-04-17 22:02:51 +02:00
|
|
|
// render debug-related terrain overlays
|
|
|
|
TerrainOverlay::RenderOverlays();
|
2006-09-07 00:26:27 +02:00
|
|
|
oglCheck();
|
2006-04-17 22:02:51 +02:00
|
|
|
|
2004-07-15 21:29:56 +02:00
|
|
|
MICROLOG(L"render models");
|
2004-06-07 21:53:58 +02:00
|
|
|
RenderModels();
|
2004-08-06 17:01:23 +02:00
|
|
|
oglCheck();
|
|
|
|
|
2006-07-09 00:40:01 +02:00
|
|
|
// render transparent stuff, so it can overlap models/terrain
|
2004-07-15 21:29:56 +02:00
|
|
|
MICROLOG(L"render transparent");
|
2005-10-25 03:43:07 +02:00
|
|
|
RenderTransparentModels();
|
2004-08-06 17:01:23 +02:00
|
|
|
oglCheck();
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2006-05-29 02:49:09 +02:00
|
|
|
// render water
|
# 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
|
|
|
if (m_WaterManager->m_RenderWater && g_Game)
|
2006-01-07 02:04:26 +01:00
|
|
|
{
|
|
|
|
MICROLOG(L"render water");
|
|
|
|
m->terrainRenderer->RenderWater();
|
|
|
|
oglCheck();
|
2006-07-09 00:40:01 +02:00
|
|
|
|
2006-05-29 02:49:09 +02:00
|
|
|
// render transparent stuff again, so it can overlap the water
|
|
|
|
MICROLOG(L"render transparent 2");
|
|
|
|
RenderTransparentModels();
|
|
|
|
oglCheck();
|
2006-07-09 00:40:01 +02:00
|
|
|
|
|
|
|
// TODO: Maybe think of a better way to deal with transparent objects;
|
|
|
|
// they can appear both under and above water (seaweed vs. trees), but doing
|
|
|
|
// 2 renders causes (a) inefficiency and (b) darker over-water objects (e.g.
|
|
|
|
// trees) than usual because the transparent bits get overwritten twice.
|
|
|
|
// This doesn't look particularly bad, but it is noticeable if you try
|
|
|
|
// turning the water off. On the other hand every user will have water
|
|
|
|
// on all the time, so it might not be worth worrying about.
|
2006-01-07 02:04:26 +01:00
|
|
|
}
|
2005-09-18 05:47:15 +02:00
|
|
|
|
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);
|
|
|
|
|
2006-02-11 10:40:04 +01:00
|
|
|
//// Particle Engine Rendering.
|
|
|
|
MICROLOG(L"render particles");
|
|
|
|
CParticleEngine::GetInstance()->renderParticles();
|
|
|
|
oglCheck();
|
|
|
|
|
2006-01-22 20:12:30 +01:00
|
|
|
// render debug lines
|
|
|
|
if (m_DisplayFrustum)
|
|
|
|
{
|
|
|
|
MICROLOG(L"display frustum");
|
2006-01-29 18:34:45 +01:00
|
|
|
DisplayFrustum();
|
2006-02-11 01:26:40 +01:00
|
|
|
m->shadow->RenderDebugDisplay();
|
2006-01-22 20:12:30 +01:00
|
|
|
oglCheck();
|
|
|
|
}
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// empty lists
|
2004-07-15 21:29:56 +02:00
|
|
|
MICROLOG(L"empty lists");
|
2006-01-07 02:04:26 +01:00
|
|
|
m->terrainRenderer->EndFrame();
|
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
// Finish model renderers
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.Normal->EndFrame();
|
|
|
|
m->Model.Player->EndFrame();
|
|
|
|
if (m->Model.Normal != m->Model.NormalInstancing)
|
|
|
|
m->Model.NormalInstancing->EndFrame();
|
|
|
|
if (m->Model.Player != m->Model.PlayerInstancing)
|
|
|
|
m->Model.PlayerInstancing->EndFrame();
|
|
|
|
m->Model.Transp->EndFrame();
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2006-01-22 20:12:30 +01:00
|
|
|
// EndFrame: signal frame end
|
2004-05-30 02:46:58 +02:00
|
|
|
void CRenderer::EndFrame()
|
|
|
|
{
|
2004-06-11 00:24:03 +02:00
|
|
|
g_Renderer.SetTexture(0,0);
|
|
|
|
|
|
|
|
static bool once=false;
|
|
|
|
if (!once && glGetError()) {
|
2004-08-15 22:57:31 +02:00
|
|
|
LOG(ERROR, LOG_CATEGORY, "CRenderer::EndFrame: GL errors occurred");
|
2004-06-11 00:24:03 +02:00
|
|
|
once=true;
|
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()
|
|
|
|
{
|
|
|
|
glDepthMask(0);
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2006-09-14 22:06:21 +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)
|
|
|
|
{
|
|
|
|
glViewport(vp.m_X,vp.m_Y,vp.m_Width,vp.m_Height);
|
|
|
|
}
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
void CRenderer::Submit(CPatch* patch)
|
|
|
|
{
|
2006-01-07 02:04:26 +01:00
|
|
|
m->terrainRenderer->Submit(patch);
|
2005-09-18 05:47:15 +02:00
|
|
|
}
|
|
|
|
|
2006-09-24 13:25:11 +02:00
|
|
|
void CRenderer::SubmitNonRecursive(CModel* model)
|
2004-06-11 00:24:03 +02:00
|
|
|
{
|
2004-10-06 20:44:47 +02:00
|
|
|
if (model->GetFlags() & MODELFLAG_CASTSHADOWS) {
|
2005-05-20 19:09:47 +02:00
|
|
|
PROFILE( "updating shadow bounds" );
|
2006-01-29 18:34:45 +01:00
|
|
|
m->shadow->AddShadowedBound(model->GetBounds());
|
2004-06-11 00:24:03 +02:00
|
|
|
}
|
|
|
|
|
2005-10-30 02:22:22 +02:00
|
|
|
// Tricky: The call to GetBounds() above can invalidate the position
|
|
|
|
model->ValidatePosition();
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-30 02:22:22 +02:00
|
|
|
bool canUseInstancing = false;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-30 02:22:22 +02:00
|
|
|
if (model->GetModelDef()->GetNumBones() == 0)
|
|
|
|
canUseInstancing = true;
|
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
if (model->GetMaterial().IsPlayer())
|
|
|
|
{
|
2006-03-26 01:54:20 +01:00
|
|
|
if (canUseInstancing)
|
|
|
|
m->Model.PlayerInstancing->Submit(model);
|
2005-10-25 03:43:07 +02:00
|
|
|
else
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.Player->Submit(model);
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
2005-10-30 02:22:22 +02:00
|
|
|
else if (model->GetMaterial().UsesAlpha())
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.Transp->Submit(model);
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-03-26 01:54:20 +01:00
|
|
|
if (canUseInstancing)
|
|
|
|
m->Model.NormalInstancing->Submit(model);
|
2005-10-25 03:43:07 +02:00
|
|
|
else
|
2006-03-26 01:54:20 +01:00
|
|
|
m->Model.Normal->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
|
|
|
|
void CRenderer::RenderScene(Scene *scene)
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
2006-09-24 13:25:11 +02:00
|
|
|
CFrustum frustum = m_CullCamera.GetFrustum();
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2006-09-24 13:25:11 +02:00
|
|
|
MICROLOG(L"collect objects");
|
|
|
|
scene->EnumerateObjects(frustum, this);
|
|
|
|
|
|
|
|
oglCheck();
|
|
|
|
|
|
|
|
MICROLOG(L"flush objects");
|
|
|
|
RenderSubmissions();
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// LoadTexture: try and load the given texture; set clamp/repeat flags on texture object if necessary
|
|
|
|
bool CRenderer::LoadTexture(CTexture* texture,u32 wrapflags)
|
|
|
|
{
|
2004-12-16 13:01:47 +01:00
|
|
|
const Handle errorhandle = -1;
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
Handle h=texture->GetHandle();
|
2005-09-29 07:00:20 +02:00
|
|
|
// already tried to load this texture
|
|
|
|
if (h)
|
|
|
|
{
|
|
|
|
// nothing to do here - just return success according to
|
|
|
|
// whether this is a valid handle or not
|
2004-12-16 13:01:47 +01:00
|
|
|
return h==errorhandle ? true : false;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
2005-09-29 07:00:20 +02:00
|
|
|
|
|
|
|
h=ogl_tex_load(texture->GetName());
|
|
|
|
if (h <= 0)
|
|
|
|
{
|
|
|
|
LOG(ERROR, LOG_CATEGORY, "LoadTexture failed on \"%s\"",(const char*) texture->GetName());
|
|
|
|
texture->SetHandle(errorhandle);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!wrapflags)
|
|
|
|
wrapflags = GL_CLAMP_TO_EDGE;
|
|
|
|
(void)ogl_tex_set_wrap(h, wrapflags);
|
|
|
|
(void)ogl_tex_set_filter(h, GL_LINEAR_MIPMAP_LINEAR);
|
|
|
|
|
|
|
|
// (this also verifies that the texture is a power-of-two)
|
|
|
|
if(ogl_tex_upload(h) < 0)
|
|
|
|
{
|
|
|
|
LOG(ERROR, LOG_CATEGORY, "LoadTexture failed on \"%s\" : upload failed",(const char*) texture->GetName());
|
|
|
|
ogl_tex_free(h);
|
|
|
|
texture->SetHandle(errorhandle);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
texture->SetHandle(h);
|
|
|
|
return true;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
2004-06-11 00:24:03 +02:00
|
|
|
|
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
|
2004-05-30 02:46:58 +02: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
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D,tex);
|
|
|
|
if (tex) {
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
} else {
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
}
|
|
|
|
m_ActiveTextures[unit]=tex;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// SetTexture: set the given unit to reference the given texture; pass a null texture to disable texturing on any unit
|
|
|
|
void CRenderer::SetTexture(int unit,CTexture* texture)
|
2004-06-11 00:24:03 +02:00
|
|
|
{
|
2005-09-02 04:54:02 +02:00
|
|
|
Handle h = texture? texture->GetHandle() : 0;
|
2006-03-17 04:59:49 +01:00
|
|
|
|
|
|
|
// Errored textures will give a handle of -1
|
|
|
|
if (h == -1)
|
|
|
|
h = 0;
|
|
|
|
|
2005-09-02 04:54:02 +02:00
|
|
|
ogl_tex_bind(h, unit);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// IsTextureTransparent: return true if given texture is transparent, else false - note texture must be loaded
|
|
|
|
// beforehand
|
|
|
|
bool CRenderer::IsTextureTransparent(CTexture* texture)
|
|
|
|
{
|
|
|
|
if (!texture) return false;
|
|
|
|
Handle h=texture->GetHandle();
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2005-09-29 07:00:20 +02:00
|
|
|
uint flags = 0; // assume no alpha on failure
|
2005-09-02 04:54:02 +02:00
|
|
|
(void)ogl_tex_get_format(h, &flags, 0);
|
|
|
|
return (flags & TEX_ALPHA) != 0;
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2005-10-30 00:29:55 +02:00
|
|
|
static inline void CopyTriple(unsigned char* dst,const unsigned char* src)
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
|
|
|
dst[0]=src[0];
|
|
|
|
dst[1]=src[1];
|
|
|
|
dst[2]=src[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
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
|
|
|
{
|
2005-10-21 09:47:38 +02:00
|
|
|
const char* const key = "(alpha map composite)";
|
|
|
|
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};
|
2005-09-02 04:54:02 +02:00
|
|
|
PathPackage pp;
|
2006-04-22 18:26:16 +02:00
|
|
|
(void)path_package_set_dir(&pp, "art/textures/terrain/alphamaps/special");
|
2005-09-02 04:54:02 +02:00
|
|
|
const char* fnames[NumAlphaMaps] = {
|
2005-11-18 17:23:39 +01:00
|
|
|
"blendcircle.dds",
|
|
|
|
"blendlshape.dds",
|
|
|
|
"blendedge.dds",
|
|
|
|
"blendedgecorner.dds",
|
|
|
|
"blendedgetwocorners.dds",
|
|
|
|
"blendfourcorners.dds",
|
|
|
|
"blendtwooppositecorners.dds",
|
|
|
|
"blendlshapecorner.dds",
|
|
|
|
"blendtwocorners.dds",
|
|
|
|
"blendcorner.dds",
|
|
|
|
"blendtwoedges.dds",
|
|
|
|
"blendthreecorners.dds",
|
|
|
|
"blendushape.dds",
|
|
|
|
"blendbad.dds"
|
2005-03-18 23:02:20 +01:00
|
|
|
};
|
2005-09-29 07:00:20 +02:00
|
|
|
uint 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)
|
2005-09-29 07:00:20 +02:00
|
|
|
uint bpp = 0;
|
|
|
|
for(uint i=0;i<NumAlphaMaps;i++)
|
2005-09-02 04:54:02 +02:00
|
|
|
{
|
2006-04-22 18:26:16 +02:00
|
|
|
(void)path_package_append_file(&pp, fnames[i]);
|
2005-10-21 09:47:38 +02:00
|
|
|
// note: these individual textures can be discarded afterwards;
|
|
|
|
// we cache the composite.
|
|
|
|
textures[i] = ogl_tex_load(pp.path, RES_NO_CACHE);
|
2005-10-12 06:16:41 +02:00
|
|
|
RETURN_ERR(textures[i]);
|
2005-09-02 04:54:02 +02:00
|
|
|
|
2005-11-18 17:23:39 +01:00
|
|
|
// quick hack: we require plain RGB(A) format, so convert to that.
|
|
|
|
// ideally the texture would be in uncompressed form; then this wouldn't
|
|
|
|
// be necessary.
|
|
|
|
uint flags;
|
|
|
|
ogl_tex_get_format(textures[i], &flags, 0);
|
|
|
|
ogl_tex_transform_to(textures[i], flags & ~TEX_DXT);
|
|
|
|
|
2005-09-02 04:54:02 +02:00
|
|
|
// get its size and make sure they are all equal.
|
|
|
|
// (the packing algo assumes this)
|
2005-09-29 07:00:20 +02:00
|
|
|
uint this_width = 0, this_bpp = 0; // fail-safe
|
2005-09-02 04:54:02 +02:00
|
|
|
(void)ogl_tex_get_size(textures[i], &this_width, 0, &this_bpp);
|
|
|
|
// .. 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)
|
|
|
|
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.
|
|
|
|
//
|
2005-09-29 07:00:20 +02:00
|
|
|
uint tile_w = 2+base+2; // 2 pixel border (avoids bilinear filtering artifacts)
|
|
|
|
uint total_w = RoundUpToPowerOf2(tile_w * NumAlphaMaps);
|
|
|
|
uint total_h = base; debug_assert(is_pow2(total_h));
|
2005-09-02 04:54:02 +02:00
|
|
|
u8* data=new u8[total_w*total_h*3];
|
2004-05-30 02:46:58 +02:00
|
|
|
// for each tile on row
|
2005-09-29 07:00:20 +02:00
|
|
|
for(uint 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
|
2005-09-02 04:54:02 +02:00
|
|
|
const u8* src = 0;
|
|
|
|
(void)ogl_tex_get_data(textures[i], (void**)&src);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2005-09-29 07:00:20 +02:00
|
|
|
uint srcstep=bpp/8;
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// get destination of copy
|
2005-09-02 04:54:02 +02:00
|
|
|
u8* dst=data+3*(i*tile_w);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// for each row of image
|
2005-09-29 07:00:20 +02:00
|
|
|
for (uint j=0;j<base;j++) {
|
2004-05-30 02:46:58 +02:00
|
|
|
// duplicate first pixel
|
|
|
|
CopyTriple(dst,src);
|
|
|
|
dst+=3;
|
|
|
|
CopyTriple(dst,src);
|
|
|
|
dst+=3;
|
|
|
|
|
|
|
|
// copy a row
|
2005-09-29 07:00:20 +02:00
|
|
|
for (uint k=0;k<base;k++) {
|
2004-05-30 02:46:58 +02:00
|
|
|
CopyTriple(dst,src);
|
|
|
|
dst+=3;
|
|
|
|
src+=srcstep;
|
|
|
|
}
|
|
|
|
// duplicate last pixel
|
|
|
|
CopyTriple(dst,(src-srcstep));
|
|
|
|
dst+=3;
|
|
|
|
CopyTriple(dst,(src-srcstep));
|
|
|
|
dst+=3;
|
|
|
|
|
|
|
|
// advance write pointer for next row
|
2005-09-02 04:54:02 +02:00
|
|
|
dst+=3*(total_w-tile_w);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2005-09-02 04:54:02 +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);
|
2004-05-30 02:46:58 +02:00
|
|
|
m_AlphaMapCoords[i].v0=0.0f;
|
|
|
|
m_AlphaMapCoords[i].v1=1.0f;
|
|
|
|
}
|
|
|
|
|
2005-09-29 07:00:20 +02:00
|
|
|
for (uint 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;
|
|
|
|
(void)tex_wrap(total_w, total_h, 24, 0, data, &t);
|
2005-10-21 09:47:38 +02:00
|
|
|
m_hCompositeAlphaMap = ogl_tex_wrap(&t, key);
|
2005-09-29 07:00:20 +02:00
|
|
|
(void)ogl_tex_set_filter(m_hCompositeAlphaMap, GL_LINEAR);
|
|
|
|
(void)ogl_tex_set_wrap (m_hCompositeAlphaMap, GL_CLAMP_TO_EDGE);
|
2005-10-12 06:16:41 +02:00
|
|
|
int ret = ogl_tex_upload(m_hCompositeAlphaMap, 0, 0, GL_INTENSITY);
|
2004-05-30 02:46:58 +02:00
|
|
|
delete[] data;
|
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
|
|
|
|
2005-10-10 16:15:28 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Scripting Interface
|
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
jsval CRenderer::JSI_GetFastPlayerColor(JSContext*)
|
|
|
|
{
|
|
|
|
return ToJSVal(m_FastPlayerColor);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::JSI_SetFastPlayerColor(JSContext* ctx, jsval newval)
|
|
|
|
{
|
|
|
|
bool fast;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
if (!ToPrimitive(ctx, newval, fast))
|
|
|
|
return;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
SetFastPlayerColor(fast);
|
|
|
|
}
|
|
|
|
|
|
|
|
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-11 19:04:32 +01:00
|
|
|
jsval CRenderer::JSI_GetUseDepthTexture(JSContext*)
|
|
|
|
{
|
|
|
|
return ToJSVal(m->shadow->GetUseDepthTexture());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::JSI_SetUseDepthTexture(JSContext* ctx, jsval newval)
|
|
|
|
{
|
|
|
|
bool depthTexture;
|
|
|
|
|
|
|
|
if (!ToPrimitive(ctx, newval, depthTexture))
|
|
|
|
return;
|
|
|
|
|
|
|
|
m->shadow->SetUseDepthTexture(depthTexture);
|
|
|
|
}
|
|
|
|
|
2006-02-16 00:50:24 +01:00
|
|
|
jsval CRenderer::JSI_GetDepthTextureBits(JSContext*)
|
|
|
|
{
|
|
|
|
return ToJSVal(m->shadow->GetDepthTextureBits());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::JSI_SetDepthTextureBits(JSContext* ctx, jsval newval)
|
|
|
|
{
|
|
|
|
int depthTextureBits;
|
|
|
|
|
|
|
|
if (!ToPrimitive(ctx, newval, depthTextureBits))
|
|
|
|
return;
|
|
|
|
|
|
|
|
m->shadow->SetDepthTextureBits(depthTextureBits);
|
|
|
|
}
|
|
|
|
|
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"fastPlayerColor", &CRenderer::JSI_GetFastPlayerColor, &CRenderer::JSI_SetFastPlayerColor);
|
|
|
|
AddProperty(L"renderpath", &CRenderer::JSI_GetRenderPath, &CRenderer::JSI_SetRenderPath);
|
2006-02-11 19:04:32 +01:00
|
|
|
AddProperty(L"useDepthTexture", &CRenderer::JSI_GetUseDepthTexture, &CRenderer::JSI_SetUseDepthTexture);
|
2005-10-25 04:22:22 +02:00
|
|
|
AddProperty(L"sortAllTransparent", &CRenderer::m_SortAllTransparent);
|
2005-10-28 21:25:47 +02:00
|
|
|
AddProperty(L"fastNormals", &CRenderer::m_FastNormals);
|
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);
|
2006-02-16 00:50:24 +01:00
|
|
|
AddProperty(L"disableCopyShadow", &CRenderer::m_DisableCopyShadow);
|
|
|
|
AddProperty(L"depthTextureBits", &CRenderer::JSI_GetDepthTextureBits, &CRenderer::JSI_SetDepthTextureBits);
|
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");
|
|
|
|
}
|
|
|
|
|