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"
|
|
|
|
#include "TransparencyRenderer.h"
|
2005-01-24 07:14:13 +01:00
|
|
|
#include "PlayerRenderer.h"
|
2004-05-30 02:46:58 +02:00
|
|
|
#include "Terrain.h"
|
|
|
|
#include "Matrix3D.h"
|
2005-09-30 02:59:42 +02:00
|
|
|
#include "MathUtil.h"
|
2004-05-30 02:46:58 +02:00
|
|
|
#include "Camera.h"
|
|
|
|
#include "PatchRData.h"
|
|
|
|
#include "Texture.h"
|
|
|
|
#include "LightEnv.h"
|
2005-09-18 05:47:15 +02:00
|
|
|
#include "Terrain.h"
|
2004-06-11 00:24:03 +02:00
|
|
|
#include "CLogger.h"
|
2004-09-26 06:05:35 +02:00
|
|
|
#include "ps/Game.h"
|
2005-05-20 19:09:47 +02:00
|
|
|
#include "Profile.h"
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
#include "Model.h"
|
|
|
|
#include "ModelDef.h"
|
|
|
|
|
|
|
|
#include "ogl.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-03-18 23:02:20 +01:00
|
|
|
#include "timer.h"
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2004-08-15 22:57:31 +02:00
|
|
|
#define LOG_CATEGORY "graphics"
|
|
|
|
|
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
|
|
|
{
|
|
|
|
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;
|
|
|
|
m_ShadowMap=0;
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
m_Options.m_NoVBO=false;
|
|
|
|
m_Options.m_Shadows=true;
|
2004-06-07 21:53:58 +02:00
|
|
|
m_Options.m_ShadowColor=RGBAColor(0.4f,0.4f,0.4f,1.0f);
|
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
|
|
|
|
|
|
|
m_RenderWater = true;
|
|
|
|
m_WaterHeight = 5.0f;
|
2005-09-28 07:53:22 +02:00
|
|
|
m_WaterColor = CColor(0.2f, 0.3f, 0.6f, 1.0f);
|
|
|
|
m_WaterFullDepth = 8.0f;
|
|
|
|
m_WaterMaxAlpha = 0.95f;
|
2005-09-18 08:28:23 +02:00
|
|
|
m_WaterAlphaOffset = -0.05f;
|
2005-09-27 20:17:45 +02:00
|
|
|
|
|
|
|
m_SWaterTrans=0;
|
|
|
|
m_TWaterTrans=0;
|
|
|
|
m_SWaterSpeed=0.0015f;
|
|
|
|
m_TWaterSpeed=0.0015f;
|
|
|
|
m_SWaterScrollCounter=0;
|
|
|
|
m_TWaterScrollCounter=0;
|
|
|
|
m_WaterCurrentTex=0;
|
2005-09-29 07:00:20 +02:00
|
|
|
|
2005-09-27 20:17:45 +02:00
|
|
|
for (int x=0; x<60; x++)
|
|
|
|
{
|
|
|
|
char waterName[1000];
|
2005-09-28 01:14:08 +02:00
|
|
|
sprintf(waterName, "art/textures/terrain/types/water/animation2/water%02d.dds", x+1);
|
2005-09-27 20:17:45 +02:00
|
|
|
m_WaterTexture[x]=ogl_tex_load(waterName);
|
|
|
|
if (m_WaterTexture[x] <= 0)
|
|
|
|
{
|
|
|
|
LOG(ERROR, LOG_CATEGORY, "LoadTexture failed on \"%s\"", waterName);
|
|
|
|
ogl_tex_free(m_WaterTexture[x]);
|
|
|
|
}
|
|
|
|
else
|
2005-09-29 07:00:20 +02:00
|
|
|
ogl_tex_upload(m_WaterTexture[x]);
|
2005-09-27 20:17:45 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CRenderer::Open(int width, int height, int depth)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
|
|
|
// query card capabilities
|
|
|
|
EnumCaps();
|
|
|
|
|
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
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::Close()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// resize renderer view
|
|
|
|
void CRenderer::Resize(int width,int height)
|
|
|
|
{
|
2004-06-11 00:24:03 +02:00
|
|
|
if (m_ShadowMap && (width>m_Width || height>m_Height)) {
|
|
|
|
glDeleteTextures(1,(GLuint*) &m_ShadowMap);
|
|
|
|
m_ShadowMap=0;
|
|
|
|
}
|
2004-06-07 21:53:58 +02: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;
|
|
|
|
case OPT_SHADOWS:
|
|
|
|
m_Options.m_Shadows=value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
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;
|
|
|
|
}
|
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
|
|
|
|
void CRenderer::SetOptionColor(enum Option opt,const RGBAColor& value)
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
|
|
|
switch (opt) {
|
|
|
|
case OPT_SHADOWCOLOR:
|
|
|
|
m_Options.m_ShadowColor=value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
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
|
|
|
|
const RGBAColor& CRenderer::GetOptionColor(enum Option opt) const
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
2004-06-07 21:53:58 +02:00
|
|
|
static const RGBAColor defaultColor(1.0f,1.0f,1.0f,1.0f);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
switch (opt) {
|
|
|
|
case OPT_SHADOWCOLOR:
|
|
|
|
return m_Options.m_ShadowColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
return defaultColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// BeginFrame: signal frame start
|
|
|
|
void CRenderer::BeginFrame()
|
|
|
|
{
|
2004-12-28 00:27:26 +01:00
|
|
|
#ifndef SCED
|
2004-09-28 18:01:11 +02:00
|
|
|
if(!g_Game || !g_Game->IsGameStarted())
|
|
|
|
return;
|
2004-12-28 00:27:26 +01:00
|
|
|
#endif
|
2004-09-26 06:05:35 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// bump frame counter
|
|
|
|
m_FrameCounter++;
|
|
|
|
|
|
|
|
// zero out all the per-frame stats
|
|
|
|
m_Stats.Reset();
|
|
|
|
|
|
|
|
// calculate coefficients for terrain and unit lighting
|
|
|
|
m_SHCoeffsUnits.Clear();
|
|
|
|
m_SHCoeffsTerrain.Clear();
|
|
|
|
|
|
|
|
if (m_LightEnv) {
|
2004-10-30 23:57:46 +02:00
|
|
|
m_SHCoeffsUnits.AddDirectionalLight(m_LightEnv->m_SunDir, m_LightEnv->m_SunColor);
|
|
|
|
m_SHCoeffsTerrain.AddDirectionalLight(m_LightEnv->m_SunDir, m_LightEnv->m_SunColor);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
m_SHCoeffsUnits.AddAmbientLight(m_LightEnv->m_UnitsAmbientColor);
|
2004-06-07 21:53:58 +02:00
|
|
|
m_SHCoeffsTerrain.AddAmbientLight(m_LightEnv->m_TerrainAmbientColor);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// init per frame stuff
|
2004-06-11 00:24:03 +02:00
|
|
|
m_ShadowRendered=false;
|
2004-06-07 21:53:58 +02:00
|
|
|
m_ShadowBound.SetEmpty();
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int RoundUpToPowerOf2(int x)
|
|
|
|
{
|
|
|
|
if ((x & (x-1))==0) return x;
|
|
|
|
int d=x;
|
|
|
|
while (d & (d-1)) {
|
|
|
|
d&=(d-1);
|
|
|
|
}
|
|
|
|
return d<<1;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// BuildTransformation: build transformation matrix from a position and standard basis vectors
|
|
|
|
void CRenderer::BuildTransformation(const CVector3D& pos,const CVector3D& right,const CVector3D& up,
|
|
|
|
const CVector3D& dir,CMatrix3D& result)
|
|
|
|
{
|
|
|
|
// build basis
|
|
|
|
result._11=right.X;
|
|
|
|
result._12=right.Y;
|
|
|
|
result._13=right.Z;
|
|
|
|
result._14=0;
|
|
|
|
|
|
|
|
result._21=up.X;
|
|
|
|
result._22=up.Y;
|
|
|
|
result._23=up.Z;
|
|
|
|
result._24=0;
|
|
|
|
|
|
|
|
result._31=dir.X;
|
|
|
|
result._32=dir.Y;
|
|
|
|
result._33=dir.Z;
|
|
|
|
result._34=0;
|
|
|
|
|
|
|
|
result._41=0;
|
|
|
|
result._42=0;
|
|
|
|
result._43=0;
|
|
|
|
result._44=1;
|
|
|
|
|
|
|
|
CMatrix3D trans;
|
|
|
|
trans.SetTranslation(-pos.X,-pos.Y,-pos.Z);
|
|
|
|
result=result*trans;
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2004-06-07 21:53:58 +02:00
|
|
|
// ConstructLightTransform: build transformation matrix for light at given position casting in
|
2004-05-30 02:46:58 +02:00
|
|
|
// given direction
|
|
|
|
void CRenderer::ConstructLightTransform(const CVector3D& pos,const CVector3D& dir,CMatrix3D& result)
|
|
|
|
{
|
|
|
|
CVector3D right,up;
|
|
|
|
|
|
|
|
CVector3D viewdir=m_Camera.m_Orientation.GetIn();
|
|
|
|
if (fabs(dir.Y)>0.01f) {
|
|
|
|
up=CVector3D(viewdir.X,(-dir.Z*viewdir.Z-dir.X*dir.X)/dir.Y,viewdir.Z);
|
|
|
|
} else {
|
|
|
|
up=CVector3D(0,0,1);
|
|
|
|
}
|
|
|
|
|
|
|
|
up.Normalize();
|
|
|
|
right=dir.Cross(up);
|
|
|
|
right.Normalize();
|
|
|
|
BuildTransformation(pos,right,up,dir,result);
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2004-06-07 21:53:58 +02:00
|
|
|
// CalcShadowMatrices: calculate required matrices for shadow map generation - the light's
|
2004-05-30 02:46:58 +02:00
|
|
|
// projection and transformation matrices
|
|
|
|
void CRenderer::CalcShadowMatrices()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// get bounds of shadow casting objects
|
2004-06-07 21:53:58 +02:00
|
|
|
const CBound& bounds=m_ShadowBound;
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// get centre of bounds
|
|
|
|
CVector3D centre;
|
|
|
|
bounds.GetCentre(centre);
|
|
|
|
|
|
|
|
// get sunlight direction
|
|
|
|
// ??? RC more optimal light placement?
|
2004-10-30 23:57:46 +02:00
|
|
|
CVector3D lightpos=centre-(m_LightEnv->m_SunDir * 1000);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// make light transformation matrix
|
2004-10-30 23:57:46 +02:00
|
|
|
ConstructLightTransform(lightpos,m_LightEnv->m_SunDir,m_LightTransform);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// transform shadow bounds to light space, calculate near and far bounds
|
|
|
|
CVector3D vp[8];
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[0].X,bounds[0].Y,bounds[0].Z),vp[0]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[1].X,bounds[0].Y,bounds[0].Z),vp[1]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[0].X,bounds[1].Y,bounds[0].Z),vp[2]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[1].X,bounds[1].Y,bounds[0].Z),vp[3]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[0].X,bounds[0].Y,bounds[1].Z),vp[4]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[1].X,bounds[0].Y,bounds[1].Z),vp[5]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[0].X,bounds[1].Y,bounds[1].Z),vp[6]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[1].X,bounds[1].Y,bounds[1].Z),vp[7]);
|
|
|
|
|
|
|
|
float left=vp[0].X;
|
|
|
|
float right=vp[0].X;
|
|
|
|
float top=vp[0].Y;
|
|
|
|
float bottom=vp[0].Y;
|
|
|
|
float znear=vp[0].Z;
|
|
|
|
float zfar=vp[0].Z;
|
|
|
|
|
|
|
|
for (i=1;i<8;i++) {
|
|
|
|
if (vp[i].X<left) left=vp[i].X;
|
|
|
|
else if (vp[i].X>right) right=vp[i].X;
|
|
|
|
|
|
|
|
if (vp[i].Y<bottom) bottom=vp[i].Y;
|
|
|
|
else if (vp[i].Y>top) top=vp[i].Y;
|
|
|
|
|
|
|
|
if (vp[i].Z<znear) znear=vp[i].Z;
|
|
|
|
else if (vp[i].Z>zfar) zfar=vp[i].Z;
|
|
|
|
}
|
|
|
|
|
|
|
|
// shift near and far clip planes slightly to avoid artifacts with points
|
|
|
|
// exactly on the clip planes
|
2004-06-11 00:24:03 +02:00
|
|
|
znear=(znear<m_Camera.GetNearPlane()+0.01f) ? m_Camera.GetNearPlane() : znear-0.01f;
|
2004-05-30 02:46:58 +02:00
|
|
|
zfar+=0.01f;
|
|
|
|
|
|
|
|
m_LightProjection.SetZero();
|
|
|
|
m_LightProjection._11=2/(right-left);
|
|
|
|
m_LightProjection._22=2/(top-bottom);
|
|
|
|
m_LightProjection._33=2/(zfar-znear);
|
|
|
|
m_LightProjection._14=-(right+left)/(right-left);
|
|
|
|
m_LightProjection._24=-(top+bottom)/(top-bottom);
|
2004-06-07 21:53:58 +02:00
|
|
|
m_LightProjection._34=-(zfar+znear)/(zfar-znear);
|
2004-05-30 02:46:58 +02:00
|
|
|
m_LightProjection._44=1;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// TODO, RC - trim against frustum?
|
|
|
|
// get points of view frustum in world space
|
|
|
|
CVector3D frustumPts[8];
|
|
|
|
m_Camera.GetFrustumPoints(frustumPts);
|
|
|
|
|
|
|
|
// transform to light space
|
|
|
|
for (i=0;i<8;i++) {
|
|
|
|
m_LightTransform.Transform(frustumPts[i],vp[i]);
|
|
|
|
}
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
float left1=vp[0].X;
|
|
|
|
float right1=vp[0].X;
|
|
|
|
float top1=vp[0].Y;
|
|
|
|
float bottom1=vp[0].Y;
|
|
|
|
float znear1=vp[0].Z;
|
|
|
|
float zfar1=vp[0].Z;
|
|
|
|
|
|
|
|
for (int i=1;i<8;i++) {
|
|
|
|
if (vp[i].X<left1) left1=vp[i].X;
|
|
|
|
else if (vp[i].X>right1) right1=vp[i].X;
|
|
|
|
|
|
|
|
if (vp[i].Y<bottom1) bottom1=vp[i].Y;
|
|
|
|
else if (vp[i].Y>top1) top1=vp[i].Y;
|
|
|
|
|
|
|
|
if (vp[i].Z<znear1) znear1=vp[i].Z;
|
|
|
|
else if (vp[i].Z>zfar1) zfar1=vp[i].Z;
|
|
|
|
}
|
|
|
|
|
|
|
|
left=max(left,left1);
|
|
|
|
right=min(right,right1);
|
|
|
|
top=min(top,top1);
|
|
|
|
bottom=max(bottom,bottom1);
|
|
|
|
znear=max(znear,znear1);
|
|
|
|
zfar=min(zfar,zfar1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// experimental stuff, do not use ..
|
|
|
|
// TODO, RC - desperately need to improve resolution here if we're using shadow maps; investigate
|
|
|
|
// feasibility of PSMs
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// transform light space bounds to image space - TODO, RC: safe to just use 3d transform here?
|
|
|
|
CVector4D vph[8];
|
|
|
|
for (i=0;i<8;i++) {
|
|
|
|
CVector4D tmp(vp[i].X,vp[i].Y,vp[i].Z,1.0f);
|
|
|
|
m_LightProjection.Transform(tmp,vph[i]);
|
|
|
|
vph[i][0]/=vph[i][2];
|
|
|
|
vph[i][1]/=vph[i][2];
|
|
|
|
}
|
|
|
|
|
|
|
|
// find the two points furthest apart
|
|
|
|
int p0,p1;
|
|
|
|
float maxdistsqrd=-1;
|
|
|
|
for (i=0;i<8;i++) {
|
|
|
|
for (int j=i+1;j<8;j++) {
|
|
|
|
float dx=vph[i][0]-vph[j][0];
|
|
|
|
float dy=vph[i][1]-vph[j][1];
|
|
|
|
float distsqrd=dx*dx+dy*dy;
|
|
|
|
if (distsqrd>maxdistsqrd) {
|
|
|
|
p0=i;
|
|
|
|
p1=j;
|
|
|
|
maxdistsqrd=distsqrd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-06-07 21:53:58 +02:00
|
|
|
// now we want to rotate the camera such that the longest axis lies the diagonal at 45 degrees -
|
2004-05-30 02:46:58 +02:00
|
|
|
// get angle between points
|
|
|
|
float angle=atan2(vph[p0][1]-vph[p1][1],vph[p0][0]-vph[p1][0]);
|
|
|
|
float rotation=-angle;
|
|
|
|
|
|
|
|
// build rotation matrix
|
|
|
|
CQuaternion quat;
|
|
|
|
quat.FromAxisAngle(lightdir,rotation);
|
|
|
|
CMatrix3D m;
|
|
|
|
quat.ToMatrix(m);
|
|
|
|
|
|
|
|
// rotate up vector by given rotation
|
|
|
|
CVector3D up(m_LightTransform._21,m_LightTransform._22,m_LightTransform._23);
|
|
|
|
up=m.Rotate(up);
|
|
|
|
up.Normalize(); // TODO, RC - required??
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// rebuild right vector
|
|
|
|
CVector3D rightvec;
|
|
|
|
rightvec=lightdir.Cross(up);
|
|
|
|
rightvec.Normalize();
|
|
|
|
BuildTransformation(lightpos,rightvec,up,lightdir,m_LightTransform);
|
|
|
|
|
|
|
|
// retransform points
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[0].X,bounds[0].Y,bounds[0].Z),vp[0]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[1].X,bounds[0].Y,bounds[0].Z),vp[1]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[0].X,bounds[1].Y,bounds[0].Z),vp[2]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[1].X,bounds[1].Y,bounds[0].Z),vp[3]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[0].X,bounds[0].Y,bounds[1].Z),vp[4]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[1].X,bounds[0].Y,bounds[1].Z),vp[5]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[0].X,bounds[1].Y,bounds[1].Z),vp[6]);
|
|
|
|
m_LightTransform.Transform(CVector3D(bounds[1].X,bounds[1].Y,bounds[1].Z),vp[7]);
|
|
|
|
|
|
|
|
// recalculate projection
|
|
|
|
left=vp[0].X;
|
|
|
|
right=vp[0].X;
|
|
|
|
top=vp[0].Y;
|
|
|
|
bottom=vp[0].Y;
|
|
|
|
znear=vp[0].Z;
|
|
|
|
zfar=vp[0].Z;
|
|
|
|
|
|
|
|
for (i=1;i<8;i++) {
|
|
|
|
if (vp[i].X<left) left=vp[i].X;
|
|
|
|
else if (vp[i].X>right) right=vp[i].X;
|
|
|
|
|
|
|
|
if (vp[i].Y<bottom) bottom=vp[i].Y;
|
|
|
|
else if (vp[i].Y>top) top=vp[i].Y;
|
|
|
|
|
|
|
|
if (vp[i].Z<znear) znear=vp[i].Z;
|
|
|
|
else if (vp[i].Z>zfar) zfar=vp[i].Z;
|
|
|
|
}
|
|
|
|
|
|
|
|
// shift near and far clip planes slightly to avoid artifacts with points
|
|
|
|
// exactly on the clip planes
|
|
|
|
znear-=0.01f;
|
|
|
|
zfar+=0.01f;
|
|
|
|
|
|
|
|
m_LightProjection.SetZero();
|
|
|
|
m_LightProjection._11=2/(right-left);
|
|
|
|
m_LightProjection._22=2/(top-bottom);
|
|
|
|
m_LightProjection._33=2/(zfar-znear);
|
|
|
|
m_LightProjection._14=-(right+left)/(right-left);
|
|
|
|
m_LightProjection._24=-(top+bottom)/(top-bottom);
|
2004-06-07 21:53:58 +02:00
|
|
|
m_LightProjection._34=-(zfar+znear)/(zfar-znear);
|
2004-05-30 02:46:58 +02:00
|
|
|
m_LightProjection._44=1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::CreateShadowMap()
|
|
|
|
{
|
2004-06-07 21:53:58 +02:00
|
|
|
// get shadow map size as next power of two up from view width and height
|
|
|
|
m_ShadowMapWidth=m_Width;
|
2004-06-11 00:24:03 +02:00
|
|
|
m_ShadowMapWidth=RoundUpToPowerOf2(m_ShadowMapWidth);
|
|
|
|
m_ShadowMapHeight=m_Height;
|
|
|
|
m_ShadowMapHeight=RoundUpToPowerOf2(m_ShadowMapHeight);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2004-06-07 21:53:58 +02:00
|
|
|
// create texture object - initially filled with white, so clamp to edge clamps to correct color
|
2004-05-30 02:46:58 +02:00
|
|
|
glGenTextures(1,(GLuint*) &m_ShadowMap);
|
2004-06-11 00:24:03 +02:00
|
|
|
BindTexture(0,(GLuint) m_ShadowMap);
|
|
|
|
|
|
|
|
u32 size=m_ShadowMapWidth*m_ShadowMapHeight;
|
|
|
|
u32* buf=new u32[size];
|
|
|
|
for (uint i=0;i<size;i++) buf[i]=0x00ffffff;
|
|
|
|
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,m_ShadowMapWidth,m_ShadowMapHeight,0,GL_RGBA,GL_UNSIGNED_BYTE,buf);
|
|
|
|
delete[] buf;
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// set texture parameters
|
2004-06-07 21:53:58 +02:00
|
|
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
2004-05-30 02:46:58 +02:00
|
|
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::RenderShadowMap()
|
|
|
|
{
|
2005-05-20 19:09:47 +02:00
|
|
|
PROFILE( "render shadow map" );
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// create shadow map if we haven't already got one
|
|
|
|
if (!m_ShadowMap) CreateShadowMap();
|
|
|
|
|
|
|
|
// clear buffers
|
2004-06-11 00:24:03 +02:00
|
|
|
glClearColor(1,1,1,0);
|
2004-06-07 21:53:58 +02:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// build required matrices
|
|
|
|
CalcShadowMatrices();
|
|
|
|
|
|
|
|
// setup viewport
|
2004-06-07 21:53:58 +02:00
|
|
|
glViewport(0,0,m_Width,m_Height);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadMatrixf(&m_LightProjection._11);
|
|
|
|
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadMatrixf(&m_LightTransform._11);
|
|
|
|
|
2004-07-24 16:04:40 +02:00
|
|
|
#if 0
|
|
|
|
// debug aid - render actual bounds of shadow casting objects; helps see where
|
|
|
|
// the lights projection/transform can be optimised
|
|
|
|
glColor3f(1.0,0.0,0.0);
|
|
|
|
const CBound& bounds=m_ShadowBound;
|
|
|
|
|
|
|
|
glBegin(GL_LINE_LOOP);
|
|
|
|
glVertex3f(bounds[0].X,bounds[0].Y,bounds[0].Z);
|
|
|
|
glVertex3f(bounds[0].X,bounds[0].Y,bounds[1].Z);
|
|
|
|
glVertex3f(bounds[0].X,bounds[1].Y,bounds[1].Z);
|
|
|
|
glVertex3f(bounds[0].X,bounds[1].Y,bounds[0].Z);
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
glBegin(GL_LINE_LOOP);
|
|
|
|
glVertex3f(bounds[1].X,bounds[0].Y,bounds[0].Z);
|
|
|
|
glVertex3f(bounds[1].X,bounds[0].Y,bounds[1].Z);
|
|
|
|
glVertex3f(bounds[1].X,bounds[1].Y,bounds[1].Z);
|
|
|
|
glVertex3f(bounds[1].X,bounds[1].Y,bounds[0].Z);
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
glBegin(GL_LINE_LOOP);
|
|
|
|
glVertex3f(bounds[0].X,bounds[0].Y,bounds[0].Z);
|
|
|
|
glVertex3f(bounds[0].X,bounds[0].Y,bounds[1].Z);
|
|
|
|
glVertex3f(bounds[1].X,bounds[0].Y,bounds[1].Z);
|
|
|
|
glVertex3f(bounds[1].X,bounds[0].Y,bounds[0].Z);
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
glBegin(GL_LINE_LOOP);
|
|
|
|
glVertex3f(bounds[0].X,bounds[1].Y,bounds[0].Z);
|
|
|
|
glVertex3f(bounds[0].X,bounds[1].Y,bounds[1].Z);
|
|
|
|
glVertex3f(bounds[1].X,bounds[1].Y,bounds[1].Z);
|
|
|
|
glVertex3f(bounds[1].X,bounds[1].Y,bounds[0].Z);
|
|
|
|
glEnd();
|
|
|
|
#endif // 0
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// setup client states
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
|
2004-06-07 21:53:58 +02:00
|
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
glScissor(1,1,m_Width-2,m_Height-2);
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2005-06-14 05:33:16 +02:00
|
|
|
glActiveTextureARB(GL_TEXTURE0);
|
2004-05-30 02:46:58 +02:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
|
2004-06-11 00:24:03 +02:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
|
2004-05-30 02:46:58 +02:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
|
2004-10-21 17:04:19 +02:00
|
|
|
|
|
|
|
// Set the proper LOD bias
|
|
|
|
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, m_Options.m_LodBias);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
glColor4fv(m_Options.m_ShadowColor);
|
|
|
|
|
|
|
|
glDisable(GL_CULL_FACE);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// render models
|
2004-10-06 20:44:47 +02:00
|
|
|
CModelRData::RenderModels(STREAM_POS,MODELFLAG_CASTSHADOWS);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2005-01-30 07:27:07 +01:00
|
|
|
// call on the player renderer to render all of the player shadows.
|
|
|
|
g_PlayerRenderer.RenderShadows();
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// call on the transparency renderer to render all the transparent stuff
|
|
|
|
g_TransparencyRenderer.RenderShadows();
|
2004-06-11 00:24:03 +02:00
|
|
|
|
|
|
|
glEnable(GL_CULL_FACE);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
glColor3f(1.0f,1.0f,1.0f);
|
|
|
|
|
2004-06-07 21:53:58 +02:00
|
|
|
glDisable(GL_SCISSOR_TEST);
|
2004-05-30 02:46:58 +02:00
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
|
|
|
|
// copy result into shadow map texture
|
|
|
|
BindTexture(0,m_ShadowMap);
|
2004-06-07 21:53:58 +02:00
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,m_Width,m_Height);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// restore matrix stack
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
|
2004-07-24 16:04:40 +02:00
|
|
|
#if 0
|
|
|
|
// debug aid - dump generated shadow map to file; helps verify shadow map
|
|
|
|
// space being well used (not that it is at the minute .. (TODO, RC))
|
|
|
|
unsigned char* data=new unsigned char[m_ShadowMapWidth*m_ShadowMapHeight*3];
|
|
|
|
glGetTexImage(GL_TEXTURE_2D,0,GL_BGR_EXT,GL_UNSIGNED_BYTE,data);
|
|
|
|
saveTGA("d:\\test4.tga",m_ShadowMapWidth,m_ShadowMapHeight,24,data);
|
|
|
|
delete[] data;
|
|
|
|
#endif // 0
|
|
|
|
#if 0
|
|
|
|
unsigned char* data=new unsigned char[m_Width*m_Height*4];
|
|
|
|
glReadBuffer(GL_BACK);
|
|
|
|
glReadPixels(0,0,m_Width,m_Height,GL_BGRA_EXT,GL_UNSIGNED_BYTE,data);
|
|
|
|
saveTGA("d:\\test3.tga",m_Width,m_Height,32,data);
|
|
|
|
delete[] data;
|
|
|
|
#endif // 0
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::ApplyShadowMap()
|
|
|
|
{
|
2005-05-20 19:09:47 +02:00
|
|
|
PROFILE( "applying shadows" );
|
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
CMatrix3D tmp2;
|
|
|
|
CMatrix3D texturematrix;
|
|
|
|
|
|
|
|
float dx=0.5f*float(m_Width)/float(m_ShadowMapWidth);
|
|
|
|
float dy=0.5f*float(m_Height)/float(m_ShadowMapHeight);
|
|
|
|
texturematrix.SetTranslation(dx,dy,0); // transform (-0.5, 0.5) to (0,1) - texture space
|
|
|
|
tmp2.SetScaling(dx,dy,0); // scale (-1,1) to (-0.5,0.5)
|
|
|
|
texturematrix=texturematrix*tmp2;
|
|
|
|
|
|
|
|
texturematrix=texturematrix*m_LightProjection; // transform light -> projected light space (-1 to 1)
|
|
|
|
texturematrix=texturematrix*m_LightTransform; // transform world -> light space
|
|
|
|
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
glLoadMatrixf(&texturematrix._11);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
|
2004-06-07 21:53:58 +02:00
|
|
|
CPatchRData::ApplyShadowMap(m_ShadowMap);
|
2004-06-11 00:24:03 +02:00
|
|
|
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
glLoadIdentity();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::RenderPatches()
|
|
|
|
{
|
2005-05-20 19:09:47 +02:00
|
|
|
PROFILE(" render patches ");
|
|
|
|
|
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");
|
2004-05-30 02:46:58 +02:00
|
|
|
RenderPatchSubmissions();
|
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);
|
|
|
|
|
|
|
|
// .. and some client states
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
2004-10-06 20:44:47 +02:00
|
|
|
CPatchRData::RenderStreamsAll(STREAM_POS);
|
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
|
2004-10-06 20:44:47 +02:00
|
|
|
CPatchRData::RenderOutlines();
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// .. and switch off the client states
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
|
|
|
|
// .. and restore the renderstates
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
glDepthMask(1);
|
|
|
|
|
|
|
|
// restore fill mode, and we're done
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
|
2004-06-11 00:24:03 +02:00
|
|
|
}
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2005-09-18 05:47:15 +02:00
|
|
|
void CRenderer::RenderWater()
|
|
|
|
{
|
|
|
|
PROFILE(" render water ");
|
|
|
|
|
|
|
|
if(!m_RenderWater)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const int DX[] = {1,1,0,0};
|
|
|
|
const int DZ[] = {0,1,1,0};
|
|
|
|
|
|
|
|
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
|
|
|
|
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glDepthMask(false);
|
|
|
|
|
2005-09-27 20:17:45 +02:00
|
|
|
float time = (float) get_time();
|
|
|
|
|
|
|
|
float period = 1.6f;
|
|
|
|
int curTex = (int)(fmod(time, period)*(60/period));
|
|
|
|
ogl_tex_bind(m_WaterTexture[curTex], 0);
|
|
|
|
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
glLoadIdentity();
|
|
|
|
float tx = -fmod(time, 20.0f)/20.0f;
|
|
|
|
float ty = fmod(time, 35.0f)/35.0f;
|
|
|
|
glTranslatef(tx, ty, 0);
|
|
|
|
|
|
|
|
glActiveTextureARB(GL_TEXTURE0);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
2005-09-28 01:14:08 +02:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
|
2005-09-27 20:17:45 +02:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
|
2005-09-28 01:14:08 +02:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
|
2005-09-27 20:17:45 +02:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
|
|
|
|
|
|
|
|
// Set the proper LOD bias
|
|
|
|
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, m_Options.m_LodBias);
|
|
|
|
|
|
|
|
|
2005-09-18 05:47:15 +02:00
|
|
|
glBegin(GL_QUADS);
|
|
|
|
|
|
|
|
for(size_t i=0; i<m_WaterPatches.size(); i++)
|
|
|
|
{
|
|
|
|
CPatch* patch = m_WaterPatches[i];
|
|
|
|
|
|
|
|
for(int dx=0; dx<PATCH_SIZE; dx++)
|
|
|
|
{
|
|
|
|
for(int dz=0; dz<PATCH_SIZE; dz++)
|
|
|
|
{
|
|
|
|
int x = (patch->m_X*PATCH_SIZE + dx) * CELL_SIZE;
|
|
|
|
int z = (patch->m_Z*PATCH_SIZE + dz) * CELL_SIZE;
|
|
|
|
|
|
|
|
// is any corner of the tile below the water height? if not, no point rendering it
|
|
|
|
bool shouldRender = false;
|
|
|
|
for(int j=0; j<4; j++)
|
|
|
|
{
|
|
|
|
float vertX = x + DX[j]*CELL_SIZE;
|
|
|
|
float vertZ = z + DZ[j]*CELL_SIZE;
|
|
|
|
float terrainHeight = terrain->getExactGroundLevel(vertX, vertZ);
|
|
|
|
if(terrainHeight < m_WaterHeight)
|
|
|
|
{
|
|
|
|
shouldRender = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!shouldRender)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int j=0; j<4; j++)
|
|
|
|
{
|
|
|
|
float vertX = x + DX[j]*CELL_SIZE;
|
|
|
|
float vertZ = z + DZ[j]*CELL_SIZE;
|
|
|
|
float terrainHeight = terrain->getExactGroundLevel(vertX, vertZ);
|
2005-09-18 08:28:23 +02:00
|
|
|
float alpha = clamp((m_WaterHeight - terrainHeight) / m_WaterFullDepth + m_WaterAlphaOffset,
|
|
|
|
-100.0f, m_WaterMaxAlpha);
|
2005-09-27 20:17:45 +02:00
|
|
|
glColor4f(m_WaterColor.r, m_WaterColor.g, m_WaterColor.b, alpha);
|
|
|
|
glMultiTexCoord2fARB(GL_TEXTURE0, vertX/16.0f, vertZ/16.0f);
|
2005-09-18 05:47:15 +02:00
|
|
|
glVertex3f(vertX, m_WaterHeight, vertZ);
|
|
|
|
}
|
2005-09-27 20:17:45 +02:00
|
|
|
} //end of x loop
|
|
|
|
} //end of z loop
|
|
|
|
}
|
|
|
|
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
/*
|
|
|
|
//Don't add of we don't need to
|
|
|
|
if (m_WaterScroll == true)
|
|
|
|
{
|
|
|
|
m_SWaterScrollCounter+=get_time();
|
|
|
|
m_TWaterScrollCounter+=get_time();
|
|
|
|
|
|
|
|
if(m_SWaterScrollCounter >=.02)
|
|
|
|
{
|
|
|
|
m_SWaterScrollCounter=0;
|
|
|
|
m_SWaterTrans+=m_SWaterSpeed;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_TWaterScrollCounter >=.02)
|
|
|
|
{
|
|
|
|
m_TWaterScrollCounter=0;
|
|
|
|
m_TWaterTrans+=m_TWaterSpeed;
|
2005-09-18 05:47:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-27 20:17:45 +02:00
|
|
|
//--------Reset counters--------
|
|
|
|
//will someone play this long though?
|
|
|
|
|
|
|
|
if(m_SWaterTrans >= 4)
|
|
|
|
{
|
|
|
|
m_SWaterTrans=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_TWaterTrans >= 4)
|
|
|
|
{
|
|
|
|
m_TWaterTrans=0;
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
|
|
glLoadIdentity();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
2005-09-18 05:47:15 +02:00
|
|
|
|
|
|
|
glDepthMask(true);
|
|
|
|
glDisable(GL_BLEND);
|
2005-09-27 20:17:45 +02:00
|
|
|
glDisable(GL_TEXTURE_2D);
|
2005-09-18 05:47:15 +02:00
|
|
|
}
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
void CRenderer::RenderModelSubmissions()
|
|
|
|
{
|
2004-06-11 00:24:03 +02:00
|
|
|
// set up texture environment for base pass - modulate texture and primary color
|
2005-06-14 05:33:16 +02:00
|
|
|
glActiveTextureARB(GL_TEXTURE0);
|
2004-06-11 00:24:03 +02:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
|
2004-06-15 04:43:38 +02:00
|
|
|
|
2004-10-21 17:04:19 +02:00
|
|
|
// Set the proper LOD bias
|
|
|
|
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, m_Options.m_LodBias);
|
2004-10-17 23:01:00 +02:00
|
|
|
|
2005-01-24 07:14:13 +01:00
|
|
|
// pass one through as alpha; transparent textures handled specially by CTransparencyRenderer
|
|
|
|
// (gl_constant means the colour comes from the gl_texture_env_color)
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2005-01-24 07:14:13 +01:00
|
|
|
float color[] = { 1.0, 1.0, 1.0, 1.0 };
|
|
|
|
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2005-01-24 07:14:13 +01:00
|
|
|
// setup client states
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
2005-01-23 23:02:36 +01:00
|
|
|
|
2005-01-24 07:14:13 +01:00
|
|
|
// render models
|
|
|
|
CModelRData::RenderModels(STREAM_POS|STREAM_COLOR|STREAM_UV0);
|
2005-01-23 23:02:36 +01:00
|
|
|
|
2005-01-24 07:14:13 +01:00
|
|
|
// switch off client states
|
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
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
|
|
|
|
|
|
|
// render all the models
|
|
|
|
RenderModelSubmissions();
|
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) {
|
|
|
|
// 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.75f);
|
|
|
|
glLineWidth(1.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);
|
|
|
|
|
|
|
|
// .. and some client states
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
|
|
|
|
// render each model
|
2004-06-07 21:53:58 +02:00
|
|
|
CModelRData::RenderModels(STREAM_POS);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
// .. and switch off the client states
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
|
|
|
|
// .. and restore the renderstates
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
glDepthMask(1);
|
|
|
|
|
|
|
|
// restore fill mode, and we're done
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// SortModelsByTexture: sorting class used for batching models with identical textures
|
|
|
|
struct SortModelsByTexture {
|
|
|
|
typedef CModel* SortObj;
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
bool operator()(const SortObj& lhs,const SortObj& rhs) {
|
|
|
|
return lhs->GetTexture()<rhs->GetTexture() ? true : false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// FlushFrame: force rendering of any batched objects
|
|
|
|
void CRenderer::FlushFrame()
|
2004-06-07 21:53:58 +02:00
|
|
|
{
|
2004-12-28 00:27:26 +01:00
|
|
|
#ifndef SCED
|
2004-10-21 17:04:19 +02:00
|
|
|
if(!g_Game || !g_Game->IsGameStarted())
|
|
|
|
return;
|
2004-12-28 00:27:26 +01:00
|
|
|
#endif
|
2004-09-26 06:05:35 +02:00
|
|
|
|
2004-08-06 17:01:23 +02:00
|
|
|
oglCheck();
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// sort all the transparent stuff
|
|
|
|
g_TransparencyRenderer.Sort();
|
2005-01-30 07:27:07 +01:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
if (!m_ShadowRendered) {
|
2004-07-15 21:29:56 +02:00
|
|
|
if (m_Options.m_Shadows) {
|
|
|
|
MICROLOG(L"render shadows");
|
|
|
|
RenderShadowMap();
|
|
|
|
}
|
2004-05-30 02:46:58 +02:00
|
|
|
// clear buffers
|
|
|
|
glClearColor(m_ClearColor[0],m_ClearColor[1],m_ClearColor[2],m_ClearColor[3]);
|
2004-06-07 21:53:58 +02:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2004-08-06 17:01:23 +02:00
|
|
|
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();
|
|
|
|
|
2005-09-18 05:47:15 +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();
|
|
|
|
|
2004-07-15 21:29:56 +02:00
|
|
|
if (m_Options.m_Shadows && !m_ShadowRendered) {
|
|
|
|
MICROLOG(L"apply shadows");
|
|
|
|
ApplyShadowMap();
|
2004-08-06 17:01:23 +02:00
|
|
|
oglCheck();
|
2004-07-15 21:29:56 +02:00
|
|
|
}
|
2004-06-11 00:24:03 +02:00
|
|
|
m_ShadowRendered=true;
|
2004-08-06 17:01:23 +02:00
|
|
|
|
2005-01-30 07:27:07 +01:00
|
|
|
MICROLOG(L"render player models");
|
|
|
|
g_PlayerRenderer.Render();
|
|
|
|
oglCheck();
|
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
// call on the transparency renderer to render all the transparent stuff
|
2004-07-15 21:29:56 +02:00
|
|
|
MICROLOG(L"render transparent");
|
2004-06-11 00:24:03 +02:00
|
|
|
g_TransparencyRenderer.Render();
|
2004-08-06 17:01:23 +02:00
|
|
|
oglCheck();
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2005-09-18 05:47:15 +02:00
|
|
|
// render water (note: we're assuming there's no transparent stuff over water...
|
|
|
|
// we could also do this above render transparent if we assume there's no transparent
|
|
|
|
// stuff underwater)
|
|
|
|
MICROLOG(L"render water");
|
|
|
|
RenderWater();
|
|
|
|
oglCheck();
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// empty lists
|
2004-07-15 21:29:56 +02:00
|
|
|
MICROLOG(L"empty lists");
|
2004-05-30 02:46:58 +02:00
|
|
|
g_TransparencyRenderer.Clear();
|
2005-01-24 07:14:13 +01:00
|
|
|
g_PlayerRenderer.Clear();
|
2004-06-07 21:53:58 +02:00
|
|
|
CPatchRData::ClearSubmissions();
|
|
|
|
CModelRData::ClearSubmissions();
|
2005-09-18 05:47:15 +02:00
|
|
|
m_WaterPatches.clear();
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2004-06-07 21:53:58 +02:00
|
|
|
// EndFrame: signal frame end; implicitly flushes batched objects
|
2004-05-30 02:46:58 +02:00
|
|
|
void CRenderer::EndFrame()
|
|
|
|
{
|
2004-12-28 00:27:26 +01:00
|
|
|
#ifndef SCED
|
2004-10-21 17:04:19 +02:00
|
|
|
if(!g_Game || !g_Game->IsGameStarted())
|
|
|
|
return;
|
2004-12-28 00:27:26 +01:00
|
|
|
#endif
|
2004-09-26 06:05:35 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
FlushFrame();
|
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
|
|
|
}
|
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// SetCamera: setup projection and transform of camera and adjust viewport to current view
|
2004-05-30 02:46:58 +02:00
|
|
|
void CRenderer::SetCamera(CCamera& camera)
|
|
|
|
{
|
|
|
|
CMatrix3D view;
|
|
|
|
camera.m_Orientation.GetInverse(view);
|
2004-06-07 21:53:58 +02:00
|
|
|
const CMatrix3D& proj=camera.GetProjection();
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
2004-06-07 21:53:58 +02:00
|
|
|
glLoadMatrixf(&proj._11);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
2004-06-07 21:53:58 +02:00
|
|
|
glLoadMatrixf(&view._11);
|
2004-05-30 02:46:58 +02:00
|
|
|
|
2004-08-05 15:07:51 +02:00
|
|
|
SetViewport(camera.GetViewPort());
|
2004-05-30 02:46:58 +02:00
|
|
|
|
|
|
|
m_Camera=camera;
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
{
|
2004-06-11 00:24:03 +02:00
|
|
|
CPatchRData::Submit(patch);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2005-09-18 05:47:15 +02:00
|
|
|
void CRenderer::SubmitWater(CPatch* patch)
|
|
|
|
{
|
|
|
|
m_WaterPatches.push_back(patch);
|
|
|
|
}
|
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
void CRenderer::Submit(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" );
|
2004-06-11 00:24:03 +02:00
|
|
|
m_ShadowBound+=model->GetBounds();
|
|
|
|
}
|
|
|
|
|
|
|
|
CModelRData::Submit(model);
|
2004-05-30 02:46:58 +02:00
|
|
|
}
|
|
|
|
|
2005-08-09 17:55:44 +02:00
|
|
|
void CRenderer::Submit(CSprite* UNUSED(sprite))
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2005-08-09 17:55:44 +02:00
|
|
|
void CRenderer::Submit(CParticleSys* UNUSED(psys))
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2005-08-09 17:55:44 +02:00
|
|
|
void CRenderer::Submit(COverlay* UNUSED(overlay))
|
2004-05-30 02:46:58 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void CRenderer::RenderPatchSubmissions()
|
|
|
|
{
|
2004-06-07 21:53:58 +02:00
|
|
|
// switch on required client states
|
2004-05-30 02:46:58 +02:00
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
2004-06-07 21:53:58 +02:00
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
// render everything
|
|
|
|
CPatchRData::RenderBaseSplats();
|
2004-06-07 21:53:58 +02:00
|
|
|
CPatchRData::RenderBlendSplats();
|
2004-06-11 00:24:03 +02:00
|
|
|
|
2004-05-30 02:46:58 +02:00
|
|
|
// switch off all client states
|
2004-06-11 00:24:03 +02:00
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
2004-05-30 02:46:58 +02:00
|
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
2004-06-11 00:24:03 +02:00
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
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
|
|
|
{
|
|
|
|
#if 0
|
2005-06-14 05:33:16 +02:00
|
|
|
glActiveTextureARB(GL_TEXTURE0+unit);
|
2004-06-07 21:53:58 +02:00
|
|
|
if (tex==m_ActiveTextures[unit]) return;
|
|
|
|
|
2004-06-11 00:24:03 +02:00
|
|
|
if (tex) {
|
|
|
|
glBindTexture(GL_TEXTURE_2D,tex);
|
2004-06-07 21:53:58 +02:00
|
|
|
if (!m_ActiveTextures[unit]) {
|
2004-06-11 00:24:03 +02:00
|
|
|
glEnable(GL_TEXTURE_2D);
|
2004-06-07 21:53:58 +02:00
|
|
|
}
|
|
|
|
} else if (m_ActiveTextures[unit]) {
|
2004-05-30 02:46:58 +02:00
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
}
|
2004-06-11 00:24:03 +02:00
|
|
|
m_ActiveTextures[unit]=tex;
|
|
|
|
#endif
|
|
|
|
|
2005-06-14 05:33:16 +02:00
|
|
|
glActiveTextureARB(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;
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline void CopyTriple(unsigned char* dst,const unsigned char* src)
|
|
|
|
{
|
|
|
|
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-09-02 04:54:02 +02:00
|
|
|
//
|
|
|
|
// load all textures and store Handle in array
|
|
|
|
//
|
2004-05-30 02:46:58 +02:00
|
|
|
Handle textures[NumAlphaMaps];
|
2005-09-02 04:54:02 +02:00
|
|
|
PathPackage pp;
|
|
|
|
(void)pp_set_dir(&pp, "art/textures/terrain/alphamaps/special");
|
|
|
|
const char* fnames[NumAlphaMaps] = {
|
|
|
|
"blendcircle.png",
|
|
|
|
"blendlshape.png",
|
|
|
|
"blendedge.png",
|
|
|
|
"blendedgecorner.png",
|
|
|
|
"blendedgetwocorners.png",
|
|
|
|
"blendfourcorners.png",
|
|
|
|
"blendtwooppositecorners.png",
|
|
|
|
"blendlshapecorner.png",
|
|
|
|
"blendtwocorners.png",
|
|
|
|
"blendcorner.png",
|
|
|
|
"blendtwoedges.png",
|
|
|
|
"blendthreecorners.png",
|
|
|
|
"blendushape.png",
|
|
|
|
"blendbad.png"
|
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
|
|
|
{
|
|
|
|
(void)pp_append_file(&pp, fnames[i]);
|
|
|
|
textures[i] = ogl_tex_load(pp.path);
|
|
|
|
WARN_ERR(textures[i]);
|
|
|
|
|
|
|
|
// 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-09-02 04:54:02 +02:00
|
|
|
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-09-21 18:42:56 +02:00
|
|
|
m_hCompositeAlphaMap = ogl_tex_wrap(&t, "(alpha map composite)");
|
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-09-29 19:33:30 +02:00
|
|
|
(void)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
|
|
|
|
|
|
|
return 0;
|
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);
|
2004-12-16 13:41:54 +01:00
|
|
|
}
|