1
1
forked from 0ad/0ad

Some Linux compile fixes over the place.

Create TerrainRenderer, ShadowMap and WaterManager classes to divide
CRenderer's functions into more logical units.

This was SVN commit r3332.
This commit is contained in:
prefect 2006-01-07 01:04:26 +00:00
parent 1c43192f2d
commit b889826a3d
21 changed files with 1487 additions and 1203 deletions

View File

@ -15,6 +15,8 @@
#include <list>
#include <map>
#include "CStr.h"
#include "NUSpline.h"
/*
@ -32,7 +34,6 @@
class CVector3D;
class CVector4D;
class CCamera;
class CStr;
//For loading data
class CCinemaData

View File

@ -27,6 +27,7 @@
#include "Profile.h"
#include "ps/LoaderThunks.h"
#include "ps/Globals.h"
#include "renderer/WaterManager.h"
#include "Quaternion.h"
#include "Unit.h"
@ -80,7 +81,7 @@ CGameView::CGameView(CGame *pGame):
m_UnitView=NULL;
m_UnitAttach=NULL;
ONCE( ScriptingInit(); );
}
@ -135,7 +136,7 @@ void CGameView::RegisterInit(CGameAttributes *pAttribs)
RegMemFun(g_TexMan.GetSingletonPtr(), &CTextureManager::LoadTerrainTextures, L"LoadTerrainTextures", 15);
RegMemFun(g_ObjMan.GetSingletonPtr(), &CObjectManager::LoadObjects, L"LoadObjects", 1);
RegMemFun(g_Renderer.GetSingletonPtr(), &CRenderer::LoadAlphaMaps, L"LoadAlphaMaps", 45);
RegMemFun(g_Renderer.GetSingletonPtr(), &CRenderer::LoadWaterTextures, L"LoadWaterTextures", 600);
RegMemFun(g_Renderer.GetSingletonPtr()->GetWaterManager(), &WaterManager::LoadWaterTextures, L"LoadWaterTextures", 600);
}
@ -173,15 +174,15 @@ void CGameView::RenderModels(CUnitManager *pUnitMan, CProjectileManager *pProjec
CLOSManager* losMgr = m_pWorld->GetLOSManager();
const std::vector<CUnit*>& units=pUnitMan->GetUnits();
for (uint i=0;i<units.size();++i)
for (uint i=0;i<units.size();++i)
{
int status = losMgr->GetUnitStatus(units[i], g_Game->GetLocalPlayer());
CModel* model = units[i]->GetModel();
model->ValidatePosition();
if (frustum.IsBoxVisible(CVector3D(0,0,0), model->GetBounds())
&& status != UNIT_HIDDEN)
&& status != UNIT_HIDDEN)
{
if(units[i] != g_BuildingPlacer.m_actor)
{
@ -203,18 +204,18 @@ void CGameView::RenderModels(CUnitManager *pUnitMan, CProjectileManager *pProjec
}
const std::vector<CProjectile*>& projectiles=pProjectileMan->GetProjectiles();
for (uint i=0;i<projectiles.size();++i)
for (uint i=0;i<projectiles.size();++i)
{
CModel* model = projectiles[i]->GetModel();
model->ValidatePosition();
const CBound& bound = model->GetBounds();
CVector3D centre;
bound.GetCentre(centre);
if (frustum.IsBoxVisible(CVector3D(0,0,0), bound)
&& losMgr->GetStatus(centre.X, centre.Z, g_Game->GetLocalPlayer()) & LOS_VISIBLE)
&& losMgr->GetStatus(centre.X, centre.Z, g_Game->GetLocalPlayer()) & LOS_VISIBLE)
{
PROFILE( "submit projectiles" );
SubmitModelRecursive(projectiles[i]->GetModel());
@ -222,13 +223,13 @@ void CGameView::RenderModels(CUnitManager *pUnitMan, CProjectileManager *pProjec
}
}
//locks the camera in place
void CGameView::CameraLock(CVector3D Trans, bool smooth)
{
m_Terrain=g_Game->GetWorld()->GetTerrain();
float height=m_Terrain->getExactGroundLevel(
m_Camera.m_Orientation._14 + Trans.X, m_Camera.m_Orientation._34 + Trans.Z) +
m_Camera.m_Orientation._14 + Trans.X, m_Camera.m_Orientation._34 + Trans.Z) +
g_YMinOffset;
//is requested position within limits?
if (m_Camera.m_Orientation._24 + Trans.Y <= g_MaxZoomHeight)
@ -244,14 +245,14 @@ void CGameView::CameraLock(CVector3D Trans, bool smooth)
}
}
}
}
void CGameView::CameraLock(float x, float y, float z, bool smooth)
{
m_Terrain=g_Game->GetWorld()->GetTerrain();
float height=m_Terrain->getExactGroundLevel(
m_Camera.m_Orientation._14 + x, m_Camera.m_Orientation._34 + z) +
m_Camera.m_Orientation._14 + x, m_Camera.m_Orientation._34 + z) +
g_YMinOffset;
//is requested position within limits?
if (m_Camera.m_Orientation._24 + y <= g_MaxZoomHeight)
@ -269,8 +270,8 @@ void CGameView::CameraLock(float x, float y, float z, bool smooth)
}
}
void CGameView::SubmitModelRecursive(CModel* model)
{
g_Renderer.Submit(model);
@ -309,7 +310,7 @@ void CGameView::UnloadResources()
g_ObjMan.UnloadObjects();
g_TexMan.UnloadTerrainTextures();
g_Renderer.UnloadAlphaMaps();
g_Renderer.UnloadWaterTextures();
g_Renderer.GetWaterManager()->UnloadWaterTextures();
}
@ -335,7 +336,7 @@ void CGameView::ResetCameraOrientation()
m_Camera.SetProjection (1, 5000, DEGTORAD(20));
m_Camera.m_Orientation.SetXRotation(DEGTORAD(30));
m_Camera.m_Orientation.RotateY(DEGTORAD(-45));
target += CVector3D( 100.0f, 150.0f, -100.0f );
m_Camera.m_Orientation.Translate( target );
@ -350,7 +351,7 @@ void CGameView::Update(float DeltaTime)
{
if (!g_app_has_focus)
return;
if (m_UnitView)
{
// CQuaternion ToRotate = m_UnitViewProp->m_Rotation - m_Camera.m_Orientation.GetRotation();
@ -367,20 +368,20 @@ void CGameView::Update(float DeltaTime)
/*if( !m_UnitView->m_orderQueue.empty())
{
CEntityOrder Order = m_UnitView->m_orderQueue.front();
if (Order.m_type == CEntityOrder::ORDER_ATTACK_MELEE_NOPATHING ||
if (Order.m_type == CEntityOrder::ORDER_ATTACK_MELEE_NOPATHING ||
Order.m_type == CEntityOrder::ORDER_GATHER_NOPATHING)
{
CVector3D Focus = m_Camera.GetFocus();
CVector3D Target;
Target.X = (float) Order.m_data[0].location.x;
Target.Y = (float) m_Terrain->getExactGroundLevel(
Target.Y = (float) m_Terrain->getExactGroundLevel(
Order.m_data[0].location.x, Order.m_data[0].location.y );
Target.Z = (float) Order.m_data[0].location.y;
CVector3D Distance = Target - Focus;
float length = Distance.GetLength();
//We're looking too far out. Correct by moving up or down toward the true position.
if (length > 1.0f)
{
@ -388,9 +389,9 @@ void CGameView::Update(float DeltaTime)
CVector3D Down(0.0f, -1.0f, 0.0f);
//Find opposite side's length
float Angle = tan( acosf(Distance.Dot(Down)) );
float opp = length * Angle;
float hyp = 1 / sinf(Angle) * opp;
float opp = length * Angle;
float hyp = 1 / sinf(Angle) * opp;
CVector3D CamFace = m_Camera.m_Orientation.GetIn();
CVector3D ToFace = Target - m_Camera.m_Orientation.GetTranslation();
ToFace.Normalize();
@ -405,11 +406,11 @@ void CGameView::Update(float DeltaTime)
} //Check order type
} //Is order queue empty?
*/
m_Camera.UpdateFrustum();
return;
}
if (m_UnitAttach)
{
CVector3D ToMove = m_UnitAttach->m_position - m_Camera.GetFocus();
@ -418,7 +419,7 @@ void CGameView::Update(float DeltaTime)
m_Camera.UpdateFrustum();
return;
}
if (!m_TrackManager.m_TrackQueue.empty())
{
if(!m_TrackManager.Update(DeltaTime))
@ -428,7 +429,7 @@ void CGameView::Update(float DeltaTime)
m_Camera.UpdateFrustum();
return;
}
float delta = powf( m_ViewSnapSmoothness, DeltaTime );
m_Camera.m_Orientation.Translate( m_CameraDelta * ( 1.0f - delta ) );
m_CameraDelta *= delta;
@ -451,7 +452,7 @@ void CGameView::Update(float DeltaTime)
CVector3D rightwards = m_Camera.m_Orientation.GetLeft() * -1.0f; // upwards.Cross(forwards);
CVector3D upwards( 0.0f, 1.0f, 0.0f );
// rightwards.Normalize();
CVector3D forwards_horizontal = forwards;
forwards_horizontal.Y = 0.0f;
forwards_horizontal.Normalize();
@ -465,7 +466,7 @@ void CGameView::Update(float DeltaTime)
m_Camera.m_Orientation.Translate(position*-1);
// Sideways rotation
float rightways = 0.0f;
if( hotkeys[HOTKEY_CAMERA_ROTATE] )
rightways = (float)mouse_dx * m_ViewRotateSensitivity;
@ -494,7 +495,7 @@ void CGameView::Update(float DeltaTime)
CQuaternion temp;
temp.FromAxisAngle(rightwards, upways);
m_Camera.m_Orientation.Rotate(temp);
// Retranslate back to the right position
@ -505,13 +506,13 @@ void CGameView::Update(float DeltaTime)
{
CVector3D origin = m_Camera.m_Orientation.GetTranslation();
CVector3D delta = origin - m_CameraPivot;
CQuaternion rotateH, rotateV; CMatrix3D rotateM;
// Sideways rotation
float rightways = (float)mouse_dx * m_ViewRotateAboutTargetSensitivity;
rotateH.FromAxisAngle( upwards, rightways );
// Up/down rotation
@ -527,7 +528,7 @@ void CGameView::Update(float DeltaTime)
// Lock the inclination to a rather arbitrary values (for the sake of graphical decency)
float scan = sqrt( delta.X * delta.X + delta.Z * delta.Z ) / delta.Y;
if( ( scan >= 0.5f ) )
if( ( scan >= 0.5f ) )
{
// Move the camera to the origin (in preparation for rotation )
m_Camera.m_Orientation.Translate( origin * -1.0f );
@ -537,7 +538,7 @@ void CGameView::Update(float DeltaTime)
// Move the camera back to where it belongs
m_Camera.m_Orientation.Translate( m_CameraPivot + delta );
}
}
else if( hotkeys[HOTKEY_CAMERA_ROTATE_ABOUT_TARGET_KEYBOARD] )
{
@ -545,17 +546,17 @@ void CGameView::Update(float DeltaTime)
CVector3D origin = m_Camera.m_Orientation.GetTranslation();
CVector3D pivot = m_Camera.GetFocus();
CVector3D delta = origin - pivot;
CQuaternion rotateH, rotateV; CMatrix3D rotateM;
// Sideways rotation
float rightways = 0.0f;
if( hotkeys[HOTKEY_CAMERA_LEFT] )
rightways -= m_ViewRotateAboutTargetSensitivityKeyboard * DeltaTime;
if( hotkeys[HOTKEY_CAMERA_RIGHT] )
rightways += m_ViewRotateAboutTargetSensitivityKeyboard * DeltaTime;
rotateH.FromAxisAngle( upwards, rightways );
// Up/down rotation
@ -565,7 +566,7 @@ void CGameView::Update(float DeltaTime)
upways -= m_ViewRotateAboutTargetSensitivityKeyboard * DeltaTime;
if( hotkeys[HOTKEY_CAMERA_DOWN] )
upways += m_ViewRotateAboutTargetSensitivityKeyboard * DeltaTime;
rotateV.FromAxisAngle( rightwards, upways );
rotateH *= rotateV;
@ -576,7 +577,7 @@ void CGameView::Update(float DeltaTime)
// Lock the inclination to a rather arbitrary values (for the sake of graphical decency)
float scan = sqrt( delta.X * delta.X + delta.Z * delta.Z ) / delta.Y;
if( ( scan >= 0.5f ) )
if( ( scan >= 0.5f ) )
{
// Move the camera to the origin (in preparation for rotation )
m_Camera.m_Orientation.Translate( origin * -1.0f );
@ -586,7 +587,7 @@ void CGameView::Update(float DeltaTime)
// Move the camera back to where it belongs
m_Camera.m_Orientation.Translate( pivot + delta );
}
}
else if( hotkeys[HOTKEY_CAMERA_PAN] )
{
@ -595,15 +596,15 @@ void CGameView::Update(float DeltaTime)
CameraLock(rightwards * (m_ViewDragSensitivity * mouse_dx));
CameraLock(forwards_horizontal * (-m_ViewDragSensitivity * mouse_dy));
}
// Mouse movement
if( !hotkeys[HOTKEY_CAMERA_ROTATE] && !hotkeys[HOTKEY_CAMERA_ROTATE_ABOUT_TARGET] )
{
if (g_mouse_x >= g_xres-2 && g_mouse_x < g_xres)
CameraLock(rightwards * (m_ViewScrollSpeed * DeltaTime));
CameraLock(rightwards * (m_ViewScrollSpeed * DeltaTime));
else if (g_mouse_x <= 3 && g_mouse_x >= 0)
CameraLock(-rightwards * (m_ViewScrollSpeed * DeltaTime));
if (g_mouse_y >= g_yres-2 && g_mouse_y < g_yres)
CameraLock(-forwards_horizontal * (m_ViewScrollSpeed * DeltaTime));
else if (g_mouse_y <= 3 && g_mouse_y >= 0)
@ -619,7 +620,7 @@ void CGameView::Update(float DeltaTime)
CameraLock(rightwards * (m_ViewScrollSpeed * DeltaTime));
if( hotkeys[HOTKEY_CAMERA_LEFT] )
CameraLock(-rightwards * (m_ViewScrollSpeed * DeltaTime));
if( hotkeys[HOTKEY_CAMERA_DOWN] )
CameraLock(-forwards_horizontal * (m_ViewScrollSpeed * DeltaTime));
if( hotkeys[HOTKEY_CAMERA_UP] )
@ -645,8 +646,8 @@ void CGameView::Update(float DeltaTime)
m_Camera.UpdateFrustum ();
}
void CGameView::ToUnitView(CEntity* target, CModel* prop)
{
void CGameView::ToUnitView(CEntity* target, CModel* prop)
{
if( !target )
{
//prevent previous zooming
@ -654,7 +655,7 @@ void CGameView::ToUnitView(CEntity* target, CModel* prop)
ResetCamera();
SetCameraTarget( m_UnitView->m_position );
}
m_UnitView = target;
m_UnitView = target;
m_UnitViewProp = prop;
}
@ -739,11 +740,11 @@ InReaction CGameView::HandleEvent(const SDL_Event* ev)
case HOTKEY_CAMERA_ZOOM_WHEEL_IN:
m_ZoomDelta += m_ViewZoomSensitivityWheel;
return( IN_HANDLED );
case HOTKEY_CAMERA_ZOOM_WHEEL_OUT:
m_ZoomDelta -= m_ViewZoomSensitivityWheel;
return( IN_HANDLED );
default:
if( ( ev->user.code >= HOTKEY_CAMERA_BOOKMARK_0 ) && ( ev->user.code <= HOTKEY_CAMERA_BOOKMARK_9 ) )
@ -793,7 +794,7 @@ InReaction CGameView::HandleEvent(const SDL_Event* ev)
bool CGameView::JSI_StartCustomSelection(
JSContext* UNUSED(context), uint UNUSED(argc), jsval* UNUSED(argv))
{
StartCustomSelection();
StartCustomSelection();
return true;
}

View File

@ -17,7 +17,7 @@
#include "Profile.h"
#include "LOSManager.h"
#include "graphics/GameView.h"
#include "renderer/WaterManager.h"
bool g_TerrainModified = false;
@ -81,11 +81,11 @@ void CMiniMap::HandleMessage(const SGUIMessage &Message)
m_Camera->m_Orientation._14+=TransVector.X;
m_Camera->m_Orientation._34+=TransVector.Z;
//Lock Y coord. No risk of zoom exceeding limit-Y does not increase
//Lock Y coord. No risk of zoom exceeding limit-Y does not increase
float Height=MMTerrain->getExactGroundLevel(
m_Camera->m_Orientation._14, m_Camera->m_Orientation._34) + g_YMinOffset;
if (m_Camera->m_Orientation._24 < Height)
if (m_Camera->m_Orientation._24 < Height)
{
m_Camera->m_Orientation._24=Height;
}
@ -103,7 +103,7 @@ void CMiniMap::HandleMessage(const SGUIMessage &Message)
// which represents the view of the camera in the world.
void CMiniMap::DrawViewRect()
{
// Get correct world coordinates based off corner of screen start
// Get correct world coordinates based off corner of screen start
// at Bottom Left and going CW
CVector3D hitPt[4];
hitPt[0]=m_Camera->GetWorldCoordinates(0,g_Renderer.GetHeight());
@ -150,7 +150,7 @@ void CMiniMap::Draw()
// happens when the game is started, so abort until then.
if(!(GetGUI() && g_Game && g_Game->IsGameStarted()))
return;
// Set our globals in case they hadn't been set before
m_Camera = g_Game->GetView()->GetCamera();
m_Terrain = g_Game->GetWorld()->GetTerrain();
@ -271,7 +271,7 @@ void CMiniMap::Draw()
glEnd();
DrawViewRect();
// Reset everything back to normal
glPointSize(1.0f);
glDisable(GL_LINE_SMOOTH);
@ -315,6 +315,7 @@ void CMiniMap::RebuildTerrainTexture()
u32 y = 0;
u32 w = m_MapSize - 1;
u32 h = m_MapSize - 1;
float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight;
for(u32 j = 0; j < h; j++)
{
@ -327,7 +328,7 @@ void CMiniMap::RebuildTerrainTexture()
+ m_Terrain->getVertexGroundLevel((int)i+1, (int)j+1)
) / 4.0f;
if(avgHeight < g_Renderer.m_WaterHeight)
if(avgHeight < waterHeight)
{
*dataPtr++ = 0xff304080; // TODO: perhaps use the renderer's water color?
}

View File

@ -351,10 +351,6 @@ LibError ogl_get_gfx_info()
}
static void __stdcall emu_glDrawRangeElementsEXT(GLenum, GLuint, GLuint, GLsizei, GLenum, GLvoid*)
{
}
// call after each video mode change, since thereafter extension functions
// may have changed [address].
void oglInit()
@ -372,9 +368,6 @@ void oglInit()
importExtensionFunctions();
if(!pglDrawRangeElementsEXT)
pglDrawRangeElementsEXT = emu_glDrawRangeElementsEXT;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &ogl_max_tex_size);
glGetIntegerv(GL_MAX_TEXTURE_UNITS, &ogl_max_tex_units);
}

View File

@ -25,6 +25,17 @@ inline T clamp(T value, T min, T max)
else return value;
}
static inline int RoundUpToPowerOf2(int x)
{
if ((x & (x-1))==0) return x;
int d=x;
while (d & (d-1)) {
d&=(d-1);
}
return d<<1;
}
#if 0
/*

View File

@ -1,4 +1,4 @@
#include "Precompiled.h"
#include "precompiled.h"
#include "NUSpline.h"
#include "Matrix3D.h"

View File

@ -1,6 +1,10 @@
//Desc: Contains classes for smooth splines
//Borrowed from Game Programming Gems4. (Slightly changed to better suit our purposes
//(and compatability). Any references to external material can be found there
#ifndef NUSPLINE_H
#define NUSPLINE_H
#define MAX_SPLINE_NODES 30
#include <stdlib.h>
#include "Vector3D.h"
@ -47,4 +51,6 @@ public:
void BuildSpline(){ RNSpline::BuildSpline(); Smooth(); Smooth(); Smooth(); }
void Smooth(){ SNSpline::Smooth(); Constrain(); }
void Constrain();
};
};
#endif // NUSPLINE_H

View File

@ -41,6 +41,7 @@
#include "graphics/MaterialManager.h"
#include "graphics/MeshManager.h"
#include "renderer/Renderer.h"
#include "renderer/VertexBufferManager.h"
#include "maths/MathUtil.h"
#include "simulation/BaseEntityCollection.h"

View File

@ -6,17 +6,13 @@
#include "Pyrogenesis.h"
#include "lib/res/graphics/ogl_tex.h"
#include "Renderer.h"
#include "PatchRData.h"
#include "renderer/PatchRData.h"
#include "AlphaMapCalculator.h"
#include "ps/CLogger.h"
#include "ps/Profile.h"
#include "MathUtil.h"
#include "LOSManager.h"
///////////////////////////////////////////////////////////////////
// shared list of all submitted patches this frame
std::vector<CPatch*> CPatchRData::m_Patches;
const int BlendOffsets[8][2] = {
{ 0, -1 },
@ -397,7 +393,7 @@ void CPatchRData::BuildVertices()
// upload to vertex buffer
if (!m_VBBase) {
m_VBBase=g_VBMan.Allocate(sizeof(SBaseVertex),vsize*vsize,true);
}
}
m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase,m_Vertices);
}
@ -557,22 +553,6 @@ void CPatchRData::RenderBlends()
}
}
void CPatchRData::RenderOutlines()
{
for (uint i=0;i<m_Patches.size();++i) {
CPatchRData* patchdata=(CPatchRData*) m_Patches[i]->GetRenderData();
patchdata->RenderOutline();
}
}
void CPatchRData::RenderStreamsAll(u32 streamflags)
{
for (uint i=0;i<m_Patches.size();++i) {
CPatchRData* patchdata=(CPatchRData*) m_Patches[i]->GetRenderData();
patchdata->RenderStreams(streamflags);
}
}
void CPatchRData::RenderOutline()
{
int i;
@ -606,285 +586,4 @@ void CPatchRData::RenderOutline()
glEnd();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// SubmitBaseBatches: submit base batches for this patch to the vertex buffer
void CPatchRData::SubmitBaseBatches()
{
debug_assert(m_VBBase);
for (uint i=0;i<m_Splats.size();i++) {
const SSplat& splat=m_Splats[i];
m_VBBase->m_Owner->AppendBatch(m_VBBase,splat.m_Texture,splat.m_IndexCount,&m_Indices[splat.m_IndexStart]);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// SubmitBlendBatches: submit next set of blend batches for this patch to the vertex buffer;
void CPatchRData::SubmitBlendBatches()
{
for (uint i=0;i<m_BlendSplats.size();i++) {
const SSplat& splat=m_BlendSplats[i];
m_VBBlends->m_Owner->AppendBatch(m_VBBlends, splat.m_Texture, splat.m_IndexCount, &m_BlendIndices[splat.m_IndexStart]);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// RenderBaseSplats: render all base passes of all patches; assumes vertex, texture and color
// client states are enabled
void CPatchRData::RenderBaseSplats()
{
uint i;
// set up texture environment for base pass
MICROLOG(L"base splat textures");
pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0);
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);
// Set alpha to 1.0
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);
float one[4] = { 1.f, 1.f, 1.f, 1.f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one);
#if 1
// submit base batches for each patch to the vertex buffer
for (i=0;i<m_Patches.size();++i) {
CPatchRData* patchdata=(CPatchRData*) m_Patches[i]->GetRenderData();
patchdata->SubmitBaseBatches();
}
// render base passes for each patch
const std::list<CVertexBuffer*>& buffers=g_VBMan.GetBufferList();
std::list<CVertexBuffer*>::const_iterator iter;
for (iter=buffers.begin();iter!=buffers.end();++iter) {
CVertexBuffer* buffer=*iter;
// any batches in this VB?
const std::vector<CVertexBuffer::Batch*>& batches=buffer->GetBatches();
if (batches.size()>0) {
u8* base=buffer->Bind();
// setup data pointers
u32 stride=sizeof(SBaseVertex);
glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBaseVertex,m_Position));
glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBaseVertex,m_Color));
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBaseVertex,m_UVs[0]));
// render each batch
for (i=0;i<batches.size();++i) {
const CVertexBuffer::Batch* batch=batches[i];
if (batch->m_IndexData.size()>0) {
ogl_tex_bind(batch->m_Texture);
for (uint j=0;j<batch->m_IndexData.size();j++) {
glDrawElements(GL_QUADS, (GLsizei)batch->m_IndexData[j].first, GL_UNSIGNED_SHORT, batch->m_IndexData[j].second);
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris+=(u32)batch->m_IndexData[j].first/2;
}
}
}
}
}
// everything rendered; empty out batch lists
MICROLOG(L"clear");
g_VBMan.ClearBatchIndices();
#else
for (i=0;i<m_Patches.size();++i) {
CPatchRData* patchdata=(CPatchRData*) m_Patches[i]->GetRenderData();
patchdata->RenderBase();
}
#endif
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// RenderBlendSplats: render all blend passes of all patches; assumes vertex, texture and color
// client states are enabled
void CPatchRData::RenderBlendSplats()
{
uint i;
// switch on the composite alpha map texture
(void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1);
// switch on second uv set
pglClientActiveTextureARB(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// setup additional texenv required by blend pass
pglActiveTextureARB(GL_TEXTURE1);
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_PREVIOUS);
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_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA);
// switch on blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
// no need to write to the depth buffer a second time
glDepthMask(0);
#if 1
// submit blend batches for each patch to the vertex buffer
for (i=0;i<m_Patches.size();++i) {
CPatchRData* patchdata=(CPatchRData*) m_Patches[i]->GetRenderData();
patchdata->SubmitBlendBatches();
}
// render blend passes for each patch
const std::list<CVertexBuffer*>& buffers=g_VBMan.GetBufferList();
std::list<CVertexBuffer*>::const_iterator iter;
for (iter=buffers.begin();iter!=buffers.end();++iter) {
CVertexBuffer* buffer=*iter;
// any batches in this VB?
const std::vector<CVertexBuffer::Batch*>& batches=buffer->GetBatches();
if (batches.size()>0) {
u8* base=buffer->Bind();
// setup data pointers
u32 stride=sizeof(SBlendVertex);
glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_Position));
glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBlendVertex,m_Color));
pglClientActiveTextureARB(GL_TEXTURE1);
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_AlphaUVs[0]));
pglClientActiveTextureARB(GL_TEXTURE0);
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_UVs[0]));
// render each batch
for (i=0;i<batches.size();++i) {
const CVertexBuffer::Batch* batch=batches[i];
if (batch->m_IndexData.size()>0) {
ogl_tex_bind(batch->m_Texture);
for (uint j = 0; j < batch->m_IndexData.size(); j++) {
glDrawElements(GL_QUADS, (GLsizei)batch->m_IndexData[j].first, GL_UNSIGNED_SHORT, batch->m_IndexData[j].second);
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris+=(u32)batch->m_IndexData[j].first/2;
}
}
}
}
}
// everything rendered; empty out batch lists
g_VBMan.ClearBatchIndices();
#else
// render blend passes for each patch
for (i=0;i<m_Patches.size();++i) {
CPatchRData* patchdata=(CPatchRData*) m_Patches[i]->GetRenderData();
patchdata->RenderBlends();
}
#endif
// restore depth writes
glDepthMask(1);
// restore default state: switch off blending
glDisable(GL_BLEND);
// switch off texture unit 1 and second uv set
pglClientActiveTextureARB(GL_TEXTURE1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
g_Renderer.BindTexture(1,0);
// switch back to texture unit 0
pglClientActiveTextureARB(GL_TEXTURE0);
pglActiveTextureARB(GL_TEXTURE0);
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Submit: submit a patch to render this frame
void CPatchRData::Submit(CPatch* patch)
{
CPatchRData* data=(CPatchRData*) patch->GetRenderData();
if (data==0) {
// no renderdata for patch, create it now
data=new CPatchRData(patch);
patch->SetRenderData(data);
}
data->Update();
m_Patches.push_back(patch);
}
/////////////////////////////////////////////////////////////////////////////////////////////
// ApplyShadowMap: apply given shadow map to all terrain patches; assume the texture matrix
// has been correctly setup on unit 1 to handle the projection
void CPatchRData::ApplyShadowMap(GLuint shadowmaphandle)
{
uint i;
g_Renderer.BindTexture(0,shadowmaphandle);
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_TEXTURE);
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_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glColor3f(1,1,1);
glEnable(GL_BLEND);
glBlendFunc(GL_DST_COLOR,GL_ZERO);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
#if 1
// submit base batches for each patch to the vertex buffer
for (i=0;i<m_Patches.size();++i) {
CPatchRData* patchdata=(CPatchRData*) m_Patches[i]->GetRenderData();
patchdata->m_VBBase->m_Owner->AppendBatch(patchdata->m_VBBase,0,patchdata->m_ShadowMapIndices.size(),
&patchdata->m_ShadowMapIndices[0]);
}
// render base passes for each patch
const std::list<CVertexBuffer*>& buffers=g_VBMan.GetBufferList();
std::list<CVertexBuffer*>::const_iterator iter;
for (iter=buffers.begin();iter!=buffers.end();++iter) {
CVertexBuffer* buffer=*iter;
// any batches in this VB?
const std::vector<CVertexBuffer::Batch*>& batches=buffer->GetBatches();
if (batches.size()>0) {
u8* base=buffer->Bind();
// setup data pointers
u32 stride=sizeof(SBaseVertex);
glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBaseVertex,m_Position));
glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBaseVertex,m_Color));
glTexCoordPointer(3,GL_FLOAT,sizeof(SBaseVertex),base+offsetof(SBaseVertex,m_Position));
// render batch (can only be one per buffer, since all batches are flagged as using a null texture)
const CVertexBuffer::Batch* batch=batches[0];
for (uint j=0;j<batch->m_IndexData.size();j++) {
glDrawElements(GL_QUADS,(GLsizei)batch->m_IndexData[j].first,GL_UNSIGNED_SHORT,batch->m_IndexData[j].second);
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris+=(u32)batch->m_IndexData[j].first/2;
}
}
}
// everything rendered; empty out batch lists
g_VBMan.ClearBatchIndices();
#else
for (uint i=0;i<m_Patches.size();++i) {
CPatchRData* patchdata=(CPatchRData*) m_Patches[i]->GetRenderData();;
patchdata->RenderStreams(STREAM_POS|STREAM_POSTOUV0);
}
#endif
glDisable(GL_BLEND);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

View File

@ -15,8 +15,8 @@ class CPatch;
class CPatchRData : public CRenderData
{
public:
CPatchRData(CPatch* patch);
~CPatchRData();
CPatchRData(CPatch* patch);
~CPatchRData();
void Update();
void RenderBase();
@ -24,27 +24,6 @@ public:
void RenderOutline();
void RenderStreams(u32 streamflags);
// submit a patch to render this frame
static void Submit(CPatch* patch);
// clear per frame patch list
static void ClearSubmissions() { m_Patches.clear(); }
// render the base pass of all patches
static void RenderBaseSplats();
// render the blend pass of all patches
static void RenderBlendSplats();
// render the outlines of all patches
static void RenderOutlines();
// render given streams of all patches; don't fiddle with renderstate
static void RenderStreamsAll(u32 streamflags);
// apply given shadow map to all terrain patches
static void ApplyShadowMap(GLuint handle);
// submit base batches for this patch to the vertex buffer
void SubmitBaseBatches();
// submit next set of blend batches for this patch to the vertex buffer;
void SubmitBlendBatches();
private:
struct SSplat {
SSplat() : m_Texture(0), m_IndexCount(0) {}
@ -129,9 +108,6 @@ private:
// splats used in blend pass
std::vector<SSplat> m_BlendSplats;
// list of all submitted patches
static std::vector<CPatch*> m_Patches;
};

View File

@ -22,7 +22,6 @@
#include "Matrix3D.h"
#include "MathUtil.h"
#include "Camera.h"
#include "PatchRData.h"
#include "Texture.h"
#include "LightEnv.h"
#include "Terrain.h"
@ -52,7 +51,10 @@
#include "renderer/PlayerRenderer.h"
#include "renderer/RenderModifiers.h"
#include "renderer/RenderPathVertexShader.h"
#include "renderer/ShadowMap.h"
#include "renderer/TerrainRenderer.h"
#include "renderer/TransparencyRenderer.h"
#include "renderer/WaterManager.h"
#define LOG_CATEGORY "graphics"
@ -71,7 +73,7 @@ class CRendererStatsTable : public AbstractProfileTable
{
public:
CRendererStatsTable(const CRenderer::Stats& st);
// Implementation of AbstractProfileTable interface
CStr GetName();
CStr GetTitle();
@ -83,17 +85,17 @@ public:
private:
/// Reference to the renderer singleton's stats
const CRenderer::Stats& Stats;
/// Column descriptions
std::vector<ProfileColumn> columnDescriptions;
enum {
Row_Counter = 0,
Row_DrawCalls,
Row_TerrainTris,
Row_ModelTris,
Row_BlendSplats,
// Must be last to count number of rows
NumberRows
};
@ -134,7 +136,7 @@ const std::vector<ProfileColumn>& CRendererStatsTable::GetColumns()
CStr CRendererStatsTable::GetCellText(uint row, uint col)
{
char buf[256];
switch(row)
{
case Row_Counter:
@ -142,31 +144,31 @@ CStr CRendererStatsTable::GetCellText(uint row, uint col)
return "counter";
snprintf(buf, sizeof(buf), "%d", Stats.m_Counter);
return buf;
case Row_DrawCalls:
if (col == 0)
return "# draw calls";
snprintf(buf, sizeof(buf), "%d", Stats.m_DrawCalls);
return buf;
case Row_TerrainTris:
if (col == 0)
return "# terrain tris";
snprintf(buf, sizeof(buf), "%d", Stats.m_TerrainTris);
return buf;
case Row_ModelTris:
if (col == 0)
return "# model tris";
snprintf(buf, sizeof(buf), "%d", Stats.m_ModelTris);
return buf;
case Row_BlendSplats:
if (col == 0)
return "# blend splats";
snprintf(buf, sizeof(buf), "%d", Stats.m_BlendSplats);
return buf;
default:
return "???";
}
@ -189,10 +191,27 @@ struct CRendererInternals
{
/// Table to display renderer stats in-game via profile system
CRendererStatsTable profileTable;
/// Water manager
WaterManager waterManager;
/// Terrain renderer
TerrainRenderer* terrainRenderer;
/// Shadow map
ShadowMap* shadow;
CRendererInternals()
: profileTable(g_Renderer.m_Stats)
{
terrainRenderer = new TerrainRenderer();
shadow = new ShadowMap();
}
~CRendererInternals()
{
delete shadow;
delete terrainRenderer;
}
};
@ -201,8 +220,10 @@ struct CRendererInternals
CRenderer::CRenderer()
{
m = new CRendererInternals;
m_WaterManager = &m->waterManager;
g_ProfileViewer.AddRootTable(&m->profileTable);
m_Width=0;
m_Height=0;
m_Depth=0;
@ -210,13 +231,12 @@ CRenderer::CRenderer()
m_TerrainRenderMode=SOLID;
m_ModelRenderMode=SOLID;
m_ClearColor[0]=m_ClearColor[1]=m_ClearColor[2]=m_ClearColor[3]=0;
m_ShadowMap=0;
m_SortAllTransparent = false;
m_FastNormals = true;
m_VertexShader = 0;
m_Options.m_NoVBO=false;
m_Options.m_Shadows=true;
m_Options.m_ShadowColor=RGBAColor(0.4f,0.4f,0.4f,1.0f);
@ -279,23 +299,6 @@ CRenderer::CRenderer()
m_Models.ModTransparent = RenderModifierPtr(new TransparentRenderModifier);
m_Models.ModTransparentShadow = RenderModifierPtr(new TransparentShadowRenderModifier);
// water
m_RenderWater = true;
m_WaterHeight = 5.0f;
m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f);
m_WaterFullDepth = 4.0f;
m_WaterMaxAlpha = 0.85f;
m_WaterAlphaOffset = -0.05f;
m_SWaterTrans=0;
m_TWaterTrans=0;
m_SWaterSpeed=0.0015f;
m_TWaterSpeed=0.0015f;
m_SWaterScrollCounter=0;
m_TWaterScrollCounter=0;
m_WaterCurrentTex=0;
cur_loading_water_tex = 0;
ONCE( ScriptingInit(); );
}
@ -382,19 +385,19 @@ bool CRenderer::Open(int width, int height, int depth)
if (m_Options.m_RenderPath == RP_DEFAULT)
SetRenderPath(m_Options.m_RenderPath);
return true;
}
// resize renderer view
void CRenderer::Resize(int width,int height)
{
if (m_ShadowMap && (width>m_Width || height>m_Height)) {
glDeleteTextures(1,(GLuint*) &m_ShadowMap);
m_ShadowMap=0;
}
m_Width=width;
m_Height=height;
// need to recreate the shadow map object to resize the shadow texture
delete m->shadow;
m->shadow = new ShadowMap;
m_Width = width;
m_Height = height;
}
//////////////////////////////////////////////////////////////////////////////////////////
@ -492,7 +495,7 @@ void CRenderer::SetRenderPath(RenderPath rp)
else
rp = RP_FIXED;
}
if (rp == RP_VERTEXSHADER)
{
if (!m_Models.NormalHWLit || !m_Models.PlayerHWLit)
@ -501,7 +504,7 @@ void CRenderer::SetRenderPath(RenderPath rp)
rp = RP_FIXED;
}
}
m_Options.m_RenderPath = rp;
}
@ -524,7 +527,7 @@ CRenderer::RenderPath CRenderer::GetRenderPathByName(CStr name)
return RP_VERTEXSHADER;
if (name == "default")
return RP_DEFAULT;
LOG(WARNING, LOG_CATEGORY, "Unknown render path name '%hs', assuming 'default'", name.c_str());
return RP_DEFAULT;
}
@ -535,7 +538,7 @@ CRenderer::RenderPath CRenderer::GetRenderPathByName(CStr name)
void CRenderer::SetFastPlayerColor(bool fast)
{
m_FastPlayerColor = fast;
if (m_FastPlayerColor)
{
if (!FastPlayerColorRender::IsAvailable())
@ -544,7 +547,7 @@ void CRenderer::SetFastPlayerColor(bool fast)
m_FastPlayerColor = false;
}
}
if (m_FastPlayerColor)
m_Models.ModPlayer = RenderModifierPtr(new FastPlayerColorRender);
else
@ -565,10 +568,10 @@ void CRenderer::BeginFrame()
if (m_VertexShader)
m_VertexShader->BeginFrame();
// zero out all the per-frame stats
m_Stats.Reset();
// calculate coefficients for terrain and unit lighting
m_SHCoeffsUnits.Clear();
m_SHCoeffsTerrain.Clear();
@ -596,354 +599,14 @@ void CRenderer::SetClearColor(u32 color)
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;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// ConstructLightTransform: build transformation matrix for light at given position casting in
// 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);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// CalcShadowMatrices: calculate required matrices for shadow map generation - the light's
// projection and transformation matrices
void CRenderer::CalcShadowMatrices()
{
int i;
// get bounds of shadow casting objects
const CBound& bounds=m_ShadowBound;
// get centre of bounds
CVector3D centre;
bounds.GetCentre(centre);
// get sunlight direction
// ??? RC more optimal light placement?
CVector3D lightpos=centre-(m_LightEnv->m_SunDir * 1000);
// make light transformation matrix
ConstructLightTransform(lightpos,m_LightEnv->m_SunDir,m_LightTransform);
// 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
znear=(znear<m_Camera.GetNearPlane()+0.01f) ? m_Camera.GetNearPlane() : 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);
m_LightProjection._34=-(zfar+znear)/(zfar-znear);
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]);
}
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
// 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;
}
}
}
// now we want to rotate the camera such that the longest axis lies the diagonal at 45 degrees -
// 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??
// 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);
m_LightProjection._34=-(zfar+znear)/(zfar-znear);
m_LightProjection._44=1;
#endif
}
void CRenderer::CreateShadowMap()
{
// get shadow map size as next power of two up from view width and height
m_ShadowMapWidth=m_Width;
m_ShadowMapWidth=RoundUpToPowerOf2(m_ShadowMapWidth);
m_ShadowMapHeight=m_Height;
m_ShadowMapHeight=RoundUpToPowerOf2(m_ShadowMapHeight);
// create texture object - initially filled with white, so clamp to edge clamps to correct color
glGenTextures(1,(GLuint*) &m_ShadowMap);
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;
// set texture parameters
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
}
void CRenderer::RenderShadowMap()
{
PROFILE( "render shadow map" );
// create shadow map if we haven't already got one
if (!m_ShadowMap) CreateShadowMap();
// clear buffers
glClearColor(1,1,1,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// build required matrices
CalcShadowMatrices();
// setup viewport
glViewport(0,0,m_Width,m_Height);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadMatrixf(&m_LightProjection._11);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixf(&m_LightTransform._11);
#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
glEnable(GL_SCISSOR_TEST);
glScissor(1,1,m_Width-2,m_Height-2);
m->shadow->SetupFrame(m_ShadowBound);
m->shadow->BeginRender();
// TODO HACK fold this into ShadowMap
glColor4fv(m_Options.m_ShadowColor);
glDisable(GL_CULL_FACE);
@ -967,56 +630,22 @@ void CRenderer::RenderShadowMap()
glColor3f(1.0f,1.0f,1.0f);
glDisable(GL_SCISSOR_TEST);
// copy result into shadow map texture
BindTexture(0,m_ShadowMap);
glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,m_Width,m_Height);
// restore matrix stack
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
#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
m->shadow->EndRender();
}
//TODO: Fold into TerrainRenderer
void CRenderer::ApplyShadowMap()
{
PROFILE( "applying shadows" );
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
const CMatrix3D& texturematrix = m->shadow->GetTextureMatrix();
GLuint shadowmap = m->shadow->GetTexture();
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(&texturematrix._11);
glMatrixMode(GL_MODELVIEW);
CPatchRData::ApplyShadowMap(m_ShadowMap);
m->terrainRenderer->ApplyShadowMap(shadowmap);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
@ -1035,7 +664,7 @@ void CRenderer::RenderPatches()
// render all the patches, including blend pass
MICROLOG(L"render patch submissions");
RenderPatchSubmissions();
m->terrainRenderer->RenderTerrain();
if (m_TerrainRenderMode==WIREFRAME) {
// switch wireframe off again
@ -1055,19 +684,15 @@ void CRenderer::RenderPatches()
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
// .. and some client states
glEnableClientState(GL_VERTEX_ARRAY);
CPatchRData::RenderStreamsAll(STREAM_POS);
// render tiles edges
m->terrainRenderer->RenderPatches();
// set color for outline
glColor3f(0,0,1);
glLineWidth(4.0f);
// render outline of each patch
CPatchRData::RenderOutlines();
// .. and switch off the client states
glDisableClientState(GL_VERTEX_ARRAY);
m->terrainRenderer->RenderOutlines();
// .. and restore the renderstates
glDisable(GL_BLEND);
@ -1078,135 +703,6 @@ void CRenderer::RenderPatches()
}
}
void CRenderer::RenderWater()
{
PROFILE(" render water ");
if(!m_RenderWater)
{
return;
}
//Fresnel effect
CCamera* Camera=g_Game->GetView()->GetCamera();
CVector3D CamFace=Camera->m_Orientation.GetIn();
CamFace.Normalize();
float FresnelScalar = CamFace.Dot( CVector3D(0.0f, -1.0f, 0.0f) );
//Invert and set boundaries
FresnelScalar = (1 - FresnelScalar) * 0.4f + 0.6f;
const int DX[] = {1,1,0,0};
const int DZ[] = {0,1,1,0};
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
int mapSize = terrain->GetVerticesPerSide();
CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
double time = get_time();
double period = 1.6;
int curTex = (int)(time*60/period) % 60;
ogl_tex_bind(m_WaterTexture[curTex], 0);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
float tx = -fmod(time, 20.0)/20.0;
float ty = fmod(time, 35.0)/35.0;
glTranslatef(tx, ty, 0);
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_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_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);
glBegin(GL_QUADS);
for(size_t i=0; i<m_VisiblePatches.size(); i++)
{
CPatch* patch = m_VisiblePatches[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);
int z = (patch->m_Z*PATCH_SIZE + dz);
// 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 terrainHeight = terrain->getVertexGroundLevel(x + DX[j], z + DZ[j]);
if(terrainHeight < m_WaterHeight)
{
shouldRender = true;
break;
}
}
if(!shouldRender)
{
continue;
}
for(int j=0; j<4; j++)
{
int ix = x + DX[j];
int iz = z + DZ[j];
float vertX = ix * CELL_SIZE;
float vertZ = iz * CELL_SIZE;
float terrainHeight = terrain->getVertexGroundLevel(ix, iz);
float alpha = clamp((m_WaterHeight - terrainHeight) / m_WaterFullDepth + m_WaterAlphaOffset,
-100.0f, m_WaterMaxAlpha);
float losMod = 1.0f;
for(int k=0; k<4; k++)
{
int tx = ix - DX[k];
int tz = iz - DZ[k];
if(tx >= 0 && tz >= 0 && tx <= mapSize-2 && tz <= mapSize-2)
{
ELOSStatus s = losMgr->GetStatus(tx, tz, g_Game->GetLocalPlayer());
if(s == LOS_EXPLORED && losMod > 0.7f)
losMod = 0.7f;
else if(s==LOS_UNEXPLORED && losMod > 0.0f)
losMod = 0.0f;
}
}
glColor4f(m_WaterColor.r*losMod, m_WaterColor.g*losMod, m_WaterColor.b*losMod, alpha * FresnelScalar);
pglMultiTexCoord2fARB(GL_TEXTURE0, vertX/16.0f, vertZ/16.0f);
glVertex3f(vertX, m_WaterHeight, vertZ);
}
} //end of x loop
} //end of z loop
}
glEnd();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}
void CRenderer::RenderModels()
{
PROFILE( "render models ");
@ -1298,6 +794,10 @@ void CRenderer::FlushFrame()
m_Models.TranspSortAll->PrepareModels();
PROFILE_END("prepare models");
PROFILE_START("prepare terrain");
m->terrainRenderer->PrepareForRendering();
PROFILE_END("prepare terrain");
if (!m_ShadowRendered) {
if (m_Options.m_Shadows) {
MICROLOG(L"render shadows");
@ -1334,15 +834,17 @@ void CRenderer::FlushFrame()
// 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();
if (m_WaterManager->m_RenderWater)
{
MICROLOG(L"render water");
m->terrainRenderer->RenderWater();
oglCheck();
}
// empty lists
MICROLOG(L"empty lists");
CPatchRData::ClearSubmissions();
m_VisiblePatches.clear();
m->terrainRenderer->EndFrame();
// Finish model renderers
m_Models.NormalFF->EndFrame();
m_Models.PlayerFF->EndFrame();
@ -1405,8 +907,7 @@ void CRenderer::SetViewport(const SViewPort &vp)
void CRenderer::Submit(CPatch* patch)
{
CPatchRData::Submit(patch);
m_VisiblePatches.push_back(patch);
m->terrainRenderer->Submit(patch);
}
void CRenderer::Submit(CModel* model)
@ -1418,9 +919,9 @@ void CRenderer::Submit(CModel* model)
// Tricky: The call to GetBounds() above can invalidate the position
model->ValidatePosition();
bool canUseInstancing = false;
if (model->GetModelDef()->GetNumBones() == 0)
canUseInstancing = true;
@ -1471,24 +972,6 @@ void CRenderer::Submit(COverlay* UNUSED(overlay))
{
}
void CRenderer::RenderPatchSubmissions()
{
// switch on required client states
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// render everything
CPatchRData::RenderBaseSplats();
CPatchRData::RenderBlendSplats();
// switch off all client states
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// LoadTexture: try and load the given texture; set clamp/repeat flags on texture object if necessary
@ -1723,58 +1206,6 @@ void CRenderer::UnloadAlphaMaps()
int CRenderer::LoadWaterTextures()
{
const uint num_textures = ARRAY_SIZE(m_WaterTexture);
// yield after this time is reached. balances increased progress bar
// smoothness vs. slowing down loading.
const double end_time = get_time() + 100e-3;
// initialize to 0 in case something fails below
// (we then abort the loop, but don't want undefined values in here)
if (cur_loading_water_tex == 0)
{
for (uint i = 0; i < num_textures; i++)
m_WaterTexture[i] = 0;
}
while (cur_loading_water_tex < num_textures)
{
char waterName[VFS_MAX_PATH];
// TODO: add a member variable and setter for this. (can't make this
// a parameter because this function is called via delay-load code)
const char* water_type = "animation2";
snprintf(waterName, ARRAY_SIZE(waterName), "art/textures/terrain/types/water/%s/water%02d.dds", water_type, cur_loading_water_tex+1);
Handle ht = ogl_tex_load(waterName);
if (ht <= 0)
{
LOG(ERROR, LOG_CATEGORY, "LoadWaterTextures failed on \"%s\"", waterName);
return ht;
}
m_WaterTexture[cur_loading_water_tex]=ht;
RETURN_ERR(ogl_tex_upload(ht));
cur_loading_water_tex++;
LDR_CHECK_TIMEOUT(cur_loading_water_tex, num_textures);
}
return 0;
}
void CRenderer::UnloadWaterTextures()
{
for (uint i = 0; i < ARRAY_SIZE(m_WaterTexture); i++)
{
ogl_tex_free(m_WaterTexture[i]);
m_WaterTexture[i] = 0;
}
cur_loading_water_tex = 0; // so they will be reloaded if LoadWaterTextures is called again
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Scripting Interface
@ -1786,10 +1217,10 @@ jsval CRenderer::JSI_GetFastPlayerColor(JSContext*)
void CRenderer::JSI_SetFastPlayerColor(JSContext* ctx, jsval newval)
{
bool fast;
if (!ToPrimitive(ctx, newval, fast))
return;
SetFastPlayerColor(fast);
}
@ -1801,10 +1232,10 @@ jsval CRenderer::JSI_GetRenderPath(JSContext*)
void CRenderer::JSI_SetRenderPath(JSContext* ctx, jsval newval)
{
CStr name;
if (!ToPrimitive(ctx, newval, name))
return;
SetRenderPath(GetRenderPathByName(name));
}

View File

@ -5,7 +5,7 @@
// Contact: rich@wildfiregames.com
//
// Description: OpenGL renderer class; a higher level interface
// on top of OpenGL to handle rendering the basic visual games
// on top of OpenGL to handle rendering the basic visual games
// types - terrain, models, sprites, particles etc
//
///////////////////////////////////////////////////////////////////////////////
@ -17,7 +17,6 @@
#include "ogl.h"
#include "Camera.h"
#include "Frustum.h"
#include "PatchRData.h"
#include "SHCoeffs.h"
#include "Terrain.h"
#include "Singleton.h"
@ -40,6 +39,7 @@ class CTexture;
class CTerrain;
class RenderPathVertexShader;
class WaterManager;
// rendering modes
@ -82,28 +82,6 @@ struct CRendererInternals;
class CRenderer : public Singleton<CRenderer>, public CJSObject<CRenderer>
{
private:
std::vector<CPatch*> m_VisiblePatches;
public:
Handle m_WaterTexture[60];
int m_WaterCurrentTex;
CColor m_WaterColor;
bool m_RenderWater;
bool m_WaterScroll;
float m_WaterHeight;
float m_WaterMaxAlpha;
float m_WaterFullDepth;
float m_WaterAlphaOffset;
float m_SWaterSpeed;
float m_TWaterSpeed;
float m_SWaterTrans;
float m_TWaterTrans;
float m_SWaterScrollCounter;
float m_TWaterScrollCounter;
float m_WaterTexTimer;
public:
// various enumerations and renderer related constants
enum { NumAlphaMaps=14 };
@ -115,19 +93,19 @@ public:
OPT_SHADOWCOLOR,
OPT_LODBIAS
};
enum RenderPath {
// If no rendering path is configured explicitly, the renderer
// will choose the path when Open() is called.
RP_DEFAULT,
// Classic fixed function.
RP_FIXED,
// Use (GL 2.0) vertex shaders for T&L when possible.
RP_VERTEXSHADER
};
// stats class - per frame counts of number of draw calls, poly counts etc
struct Stats {
// set all stats to zero
@ -141,7 +119,7 @@ public:
m_BlendSplats+=rhs.m_BlendSplats;
return *this;
}
// count of the number of stats added together
// count of the number of stats added together
size_t m_Counter;
// number of draw calls per frame - total DrawElements + Begin/End immediate mode loops
size_t m_DrawCalls;
@ -153,7 +131,7 @@ public:
size_t m_BlendSplats;
};
// renderer options
// renderer options
struct Options {
bool m_NoVBO;
bool m_Shadows;
@ -172,11 +150,11 @@ public:
// resize renderer view
void Resize(int width,int height);
// set/get boolean renderer option
// set/get boolean renderer option
void SetOptionBool(enum Option opt,bool value);
bool GetOptionBool(enum Option opt) const;
// set/get RGBA color renderer option
// set/get RGBA color renderer option
void SetOptionColor(enum Option opt,const RGBAColor& value);
void SetOptionFloat(enum Option opt, float val);
const RGBAColor& GetOptionColor(enum Option opt) const;
@ -184,7 +162,7 @@ public:
RenderPath GetRenderPath() const { return m_Options.m_RenderPath; }
static CStr GetRenderPathName(RenderPath rp);
static RenderPath GetRenderPathByName(CStr name);
// return view width
int GetWidth() const { return m_Width; }
// return view height
@ -196,7 +174,7 @@ public:
void BeginFrame();
// force rendering of any batched objects
void FlushFrame();
// signal frame end : implicitly flushes batched objects
// signal frame end : implicitly flushes batched objects
void EndFrame();
// set color used to clear screen in BeginFrame()
@ -219,9 +197,9 @@ public:
void Submit(CParticleSys* psys);
void Submit(COverlay* overlay);
// basic primitive rendering operations in 2 and 3D; handy for debugging stuff, but also useful in
// editor tools (eg for highlighting specific terrain patches)
// note:
// basic primitive rendering operations in 2 and 3D; handy for debugging stuff, but also useful in
// editor tools (eg for highlighting specific terrain patches)
// note:
// * all 3D vertices specified in world space
// * primitive operations rendered immediatedly, never batched
// * primitives rendered in current material (set via SetMaterial)
@ -266,21 +244,22 @@ public:
int LoadAlphaMaps();
void UnloadAlphaMaps();
// load textures for the active water type.
// return a negative error code if anything along the way fails.
// called via delay-load mechanism.
int LoadWaterTextures();
void UnloadWaterTextures();
// return stats accumulated for current frame
const Stats& GetStats() { return m_Stats; }
// return the current light environment
const CLightEnv &GetLightEnv() { return *m_LightEnv; }
// return the current camera
const CCamera& GetCamera() const { return m_Camera; }
/**
* GetWaterManager: Return the renderer's water manager.
*
* @return the WaterManager object used by the renderer
*/
WaterManager* GetWaterManager() { return m_WaterManager; }
/**
* SetFastPlayerColor: Tell the renderer which path to take for
* player colored models. Both paths should provide the same visual
@ -291,7 +270,7 @@ public:
* is printed and the slow path is used instead.
*/
void SetFastPlayerColor(bool fast);
protected:
friend struct CRendererInternals;
friend class CVertexBuffer;
@ -303,6 +282,7 @@ protected:
friend class RenderPathVertexShader;
friend class HWLightingModelRenderer;
friend class InstancingModelRenderer;
friend class TerrainRenderer;
// scripting
jsval JSI_GetFastPlayerColor(JSContext*);
@ -310,25 +290,17 @@ protected:
jsval JSI_GetRenderPath(JSContext*);
void JSI_SetRenderPath(JSContext* ctx, jsval newval);
static void ScriptingInit();
// patch rendering stuff
void RenderPatchSubmissions();
void RenderPatches();
void RenderWater();
// model rendering stuff
void RenderModels();
void RenderTransparentModels();
// shadow rendering stuff
void CreateShadowMap();
void RenderShadowMap();
void ApplyShadowMap();
void BuildTransformation(const CVector3D& pos,const CVector3D& right,const CVector3D& up,
const CVector3D& dir,CMatrix3D& result);
void ConstructLightTransform(const CVector3D& pos,const CVector3D& lightdir,CMatrix3D& result);
void CalcShadowMatrices();
void CalcShadowBounds(CBound& bounds);
// RENDERER DATA:
/// Private data that is not needed by inline functions
@ -361,18 +333,10 @@ protected:
CSHCoeffs m_SHCoeffsTerrain;
// ogl_tex handle of composite alpha map (all the alpha maps packed into one texture)
Handle m_hCompositeAlphaMap;
// handle of shadow map
u32 m_ShadowMap;
// width, height of shadow map
u32 m_ShadowMapWidth,m_ShadowMapHeight;
// object space bound of shadow casting objects
CBound m_ShadowBound;
// per-frame flag: has the shadow map been rendered this frame?
bool m_ShadowRendered;
// projection matrix of shadow casting light
CMatrix3D m_LightProjection;
// transformation matrix of shadow casting light
CMatrix3D m_LightTransform;
// object space bound of shadow casting objects
CBound m_ShadowBound;
// coordinates of each (untransformed) alpha map within the packed texture
struct {
float u0,u1,v0,v1;
@ -390,14 +354,20 @@ protected:
Stats m_Stats;
// active textures on each unit
GLuint m_ActiveTextures[MaxTextureUnits];
// Additional state that is only available when the vertex shader
// render path is used (according to m_Options.m_RenderPath)
RenderPathVertexShader* m_VertexShader;
/// If false, use a multipass fallback for player colors.
bool m_FastPlayerColor;
/**
* m_WaterManager: the WaterManager object used for water textures and settings
* (e.g. water color, water height)
*/
WaterManager* m_WaterManager;
/**
* m_SortAllTransparent: If true, all transparent models are
* rendered using the TransparencyRenderer which performs sorting.
@ -406,8 +376,8 @@ protected:
* batching renderer when possible.
*/
bool m_SortAllTransparent;
/**
* m_FastNormals: Use faster normal transformation in the
* software transform by multiplying with the bone matrix itself
@ -415,9 +385,6 @@ protected:
*/
bool m_FastNormals;
// State used by LoadWaterTextures with progressive loading
uint cur_loading_water_tex;
// Various model renderers
struct Models {
ModelRenderer* NormalFF;
@ -426,11 +393,11 @@ protected:
ModelRenderer* PlayerHWLit;
ModelRenderer* NormalInstancing;
ModelRenderer* PlayerInstancing;
ModelRenderer* TranspFF;
ModelRenderer* TranspHWLit;
ModelRenderer* TranspSortAll;
ModelVertexRendererPtr VertexFF;
ModelVertexRendererPtr VertexHWLit;
ModelVertexRendererPtr VertexInstancing;

View File

@ -0,0 +1,430 @@
/**
* =========================================================================
* File : ShadowMap.cpp
* Project : Pyrogenesis
* Description : Shadow mapping related texture and matrix management
*
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#include "precompiled.h"
#include "ogl.h"
#include "graphics/LightEnv.h"
#include "maths/Bound.h"
#include "maths/MathUtil.h"
#include "maths/Matrix3D.h"
#include "renderer/Renderer.h"
#include "renderer/ShadowMap.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// ShadowMap implementation
/**
* Struct ShadowMapInternals: Internal data for the ShadowMap implementation
*/
struct ShadowMapInternals
{
// handle of shadow map
GLuint Texture;
// width, height of shadow map
u32 Width, Height;
// object space bound of shadow casting objects
CBound m_ShadowBound;
// project light space into projected light space
CMatrix3D LightProjection;
// transform world space into light space
CMatrix3D LightTransform;
// transform world space into texture space
CMatrix3D TextureMatrix;
// Helper functions
void BuildTransformation(
const CVector3D& pos,const CVector3D& right,const CVector3D& up,
const CVector3D& dir,CMatrix3D& result);
void ConstructLightTransform(const CVector3D& pos,const CVector3D& dir,CMatrix3D& result);
void CalcShadowMatrices(const CBound& bound);
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// Construction/Destruction
ShadowMap::ShadowMap()
{
m = new ShadowMapInternals;
m->Texture = 0;
}
ShadowMap::~ShadowMap()
{
if (m->Texture)
glDeleteTextures(1, &m->Texture);
delete m;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BuildTransformation: build transformation matrix from a position and standard basis vectors
// TODO: Shouldn't this be part of CMatrix3D?
void ShadowMapInternals::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;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// ConstructLightTransform: build transformation matrix for light at given position casting in
// given direction
void ShadowMapInternals::ConstructLightTransform(const CVector3D& pos,const CVector3D& dir,CMatrix3D& result)
{
CVector3D right,up;
CVector3D viewdir = g_Renderer.GetCamera().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);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// CalcShadowMatrices: calculate required matrices for shadow map generation - the light's
// projection and transformation matrices
void ShadowMapInternals::CalcShadowMatrices(const CBound& bounds)
{
const CLightEnv& lightenv = g_Renderer.GetLightEnv();
const CCamera& camera = g_Renderer.GetCamera();
int i;
// get centre of bounds
CVector3D centre;
bounds.GetCentre(centre);
// get sunlight direction
// ??? RC more optimal light placement?
CVector3D lightpos=centre-(lightenv.m_SunDir * 1000);
// make light transformation matrix
ConstructLightTransform(lightpos, lightenv.m_SunDir, LightTransform);
// transform shadow bounds to light space, calculate near and far bounds
CVector3D vp[8];
LightTransform.Transform(CVector3D(bounds[0].X,bounds[0].Y,bounds[0].Z),vp[0]);
LightTransform.Transform(CVector3D(bounds[1].X,bounds[0].Y,bounds[0].Z),vp[1]);
LightTransform.Transform(CVector3D(bounds[0].X,bounds[1].Y,bounds[0].Z),vp[2]);
LightTransform.Transform(CVector3D(bounds[1].X,bounds[1].Y,bounds[0].Z),vp[3]);
LightTransform.Transform(CVector3D(bounds[0].X,bounds[0].Y,bounds[1].Z),vp[4]);
LightTransform.Transform(CVector3D(bounds[1].X,bounds[0].Y,bounds[1].Z),vp[5]);
LightTransform.Transform(CVector3D(bounds[0].X,bounds[1].Y,bounds[1].Z),vp[6]);
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
znear=(znear<camera.GetNearPlane()+0.01f) ? camera.GetNearPlane() : znear-0.01f;
zfar+=0.01f;
LightProjection.SetZero();
LightProjection._11=2/(right-left);
LightProjection._22=2/(top-bottom);
LightProjection._33=2/(zfar-znear);
LightProjection._14=-(right+left)/(right-left);
LightProjection._24=-(top+bottom)/(top-bottom);
LightProjection._34=-(zfar+znear)/(zfar-znear);
LightProjection._44=1;
// calculate texture matrix
CMatrix3D tmp2;
CMatrix3D texturematrix;
float dx=0.5f*float(g_Renderer.GetWidth())/float(Width);
float dy=0.5f*float(g_Renderer.GetHeight())/float(Height);
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 * LightProjection; // transform light -> projected light space (-1 to 1)
TextureMatrix = TextureMatrix * LightTransform; // transform world -> light space
#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]);
}
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
// 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;
}
}
}
// now we want to rotate the camera such that the longest axis lies the diagonal at 45 degrees -
// 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??
// 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);
m_LightProjection._34=-(zfar+znear)/(zfar-znear);
m_LightProjection._44=1;
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Prepare for the next frame: Matrix calculations and texture creation if necessary
void ShadowMap::SetupFrame(const CBound& visibleBounds)
{
m->CalcShadowMatrices(visibleBounds);
if (!m->Texture)
{
// get shadow map size as next power of two up from view width and height
m->Width = g_Renderer.GetWidth();
m->Width = RoundUpToPowerOf2(m->Width);
m->Height = g_Renderer.GetHeight();
m->Height = RoundUpToPowerOf2(m->Height);
// create texture object - initially filled with white, so clamp to edge clamps to correct color
glGenTextures(1, &m->Texture);
g_Renderer.BindTexture(0, m->Texture);
u32 size = m->Width*m->Height;
u32* buf=new u32[size];
for (uint i=0;i<size;i++) buf[i]=0x00ffffff;
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,m->Width,m->Height,0,GL_RGBA,GL_UNSIGNED_BYTE,buf);
delete[] buf;
// set texture parameters
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Set up to render into shadow map texture
void ShadowMap::BeginRender()
{
// HACK HACK: this depends in non-obvious ways on the behaviour of the caller
CRenderer& renderer = g_Renderer;
int renderWidth = renderer.GetWidth();
int renderHeight = renderer.GetHeight();
// clear buffers
glClearColor(1,1,1,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// setup viewport
glViewport(0, 0, renderWidth, renderHeight);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadMatrixf(&m->LightProjection._11);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixf(&m->LightTransform._11);
glEnable(GL_SCISSOR_TEST);
glScissor(1,1, renderWidth-2, renderHeight-2);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Finish rendering into shadow map texture
void ShadowMap::EndRender()
{
glDisable(GL_SCISSOR_TEST);
// copy result into shadow map texture
g_Renderer.BindTexture(0, m->Texture);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight());
// restore matrix stack
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Retrieve the texture handle and texture matrix for shadowing
GLuint ShadowMap::GetTexture()
{
return m->Texture;
}
const CMatrix3D& ShadowMap::GetTextureMatrix()
{
return m->TextureMatrix;
}

View File

@ -0,0 +1,77 @@
/**
* =========================================================================
* File : ShadowMap.h
* Project : Pyrogenesis
* Description : Shadow mapping related texture and matrix management
*
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#ifndef SHADOWMAP_H
#define SHADOWMAP_H
#include "ogl.h"
class CBound;
class CMatrix3D;
struct ShadowMapInternals;
/**
* Class ShadowMap: Maintain the shadow map texture and perform necessary OpenGL setup,
* including matrix calculations.
*
* The class will automatically generate a texture the first time the shadow map is rendered into.
* The texture will not be resized afterwards.
*
* @todo use depth textures for self-shadowing
*/
class ShadowMap
{
public:
ShadowMap();
~ShadowMap();
/**
* SetupFrame: Setup shadow map texture and matrices for this frame.
*
* @param visibleBounds bound around objects that are visible on the screen
*/
void SetupFrame(const CBound& visibleBounds);
/**
* BeginRender: Set OpenGL state for rendering into the shadow map texture.
*
* @todo this depends in non-obvious ways on the behaviour of the call-site
*/
void BeginRender();
/**
* EndRender: Finish rendering into the shadow map.
*
* @todo this depends in non-obvious ways on the behaviour of the call-site
*/
void EndRender();
/**
* GetTexture: Retrieve the OpenGL texture object name that contains the shadow map.
*
* @return the texture name of the shadow map texture
*/
GLuint GetTexture();
/**
* GetTextureMatrix: Retrieve the world-space to shadow map texture coordinates
* transformation matrix.
*
* @return the matrix that transforms world-space coordinates into homogenous
* shadow map texture coordinates
*/
const CMatrix3D& GetTextureMatrix();
private:
ShadowMapInternals* m;
};
#endif // SHADOWMAP_H

View File

@ -0,0 +1,402 @@
/**
* =========================================================================
* File : TerrainRenderer.cpp
* Project : Pyrogenesis
* Description : Terrain rendering (everything related to patches and
* : water) is encapsulated in TerrainRenderer
*
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#include "precompiled.h"
#include "graphics/Camera.h"
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
#include "maths/MathUtil.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "simulation/LOSManager.h"
#include "renderer/PatchRData.h"
#include "renderer/Renderer.h"
#include "renderer/TerrainRenderer.h"
#include "renderer/WaterManager.h"
///////////////////////////////////////////////////////////////////////////////////////////////
// TerrainRenderer implementation
/**
* TerrainRenderer keeps track of which phase it is in, to detect
* when Submit, PrepareForRendering etc. are called in the wrong order.
*/
enum Phase {
Phase_Submit,
Phase_Render
};
/**
* Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class.
*/
struct TerrainRendererInternals
{
/// Which phase (submitting or rendering patches) are we in right now?
Phase phase;
/**
* VisiblePatches: Patches that were submitted for this frame
*
* @todo Merge this list with CPatchRData list
*/
std::vector<CPatch*> visiblePatches;
};
///////////////////////////////////////////////////////////////////
// Construction/Destruction
TerrainRenderer::TerrainRenderer()
{
m = new TerrainRendererInternals();
m->phase = Phase_Submit;
}
TerrainRenderer::~TerrainRenderer()
{
delete m;
}
///////////////////////////////////////////////////////////////////
// Submit a patch for rendering
void TerrainRenderer::Submit(CPatch* patch)
{
debug_assert(m->phase == Phase_Submit);
CPatchRData* data=(CPatchRData*) patch->GetRenderData();
if (data == 0)
{
// no renderdata for patch, create it now
data = new CPatchRData(patch);
patch->SetRenderData(data);
}
data->Update();
m->visiblePatches.push_back(patch);
}
///////////////////////////////////////////////////////////////////
// Prepare for rendering
void TerrainRenderer::PrepareForRendering()
{
debug_assert(m->phase == Phase_Submit);
m->phase = Phase_Render;
}
///////////////////////////////////////////////////////////////////
// Clear submissions lists
void TerrainRenderer::EndFrame()
{
debug_assert(m->phase == Phase_Render);
m->visiblePatches.clear();
m->phase = Phase_Submit;
}
///////////////////////////////////////////////////////////////////
// Query if patches have been submitted this frame
bool TerrainRenderer::HaveSubmissions()
{
return m->visiblePatches.size() > 0;
}
///////////////////////////////////////////////////////////////////
// Full-featured terrain rendering with blending and everything
void TerrainRenderer::RenderTerrain()
{
debug_assert(m->phase == Phase_Render);
// switch on required client states
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// render everything
// set up texture environment for base pass
MICROLOG(L"base splat textures");
pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0);
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);
// Set alpha to 1.0
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);
float one[4] = { 1.f, 1.f, 1.f, 1.f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one);
for(uint i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderBase();
}
// render blends
// switch on the composite alpha map texture
(void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1);
// switch on second uv set
pglClientActiveTextureARB(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// setup additional texenv required by blend pass
pglActiveTextureARB(GL_TEXTURE1);
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_PREVIOUS);
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_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA);
// switch on blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
// no need to write to the depth buffer a second time
glDepthMask(0);
// render blend passes for each patch
for(uint i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderBlends();
}
// restore OpenGL state
glDepthMask(1);
glDisable(GL_BLEND);
pglClientActiveTextureARB(GL_TEXTURE1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
g_Renderer.BindTexture(1,0);
pglClientActiveTextureARB(GL_TEXTURE0);
pglActiveTextureARB(GL_TEXTURE0);
// switch off all client states
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render un-textured patches as polygons
void TerrainRenderer::RenderPatches()
{
debug_assert(m->phase == Phase_Render);
glEnableClientState(GL_VERTEX_ARRAY);
for(uint i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderStreams(STREAM_POS);
}
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render outlines of submitted patches as lines
void TerrainRenderer::RenderOutlines()
{
glEnableClientState(GL_VERTEX_ARRAY);
for(uint i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderOutline();
}
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render water that is part of the terrain
void TerrainRenderer::RenderWater()
{
PROFILE(" render water ");
//Fresnel effect
CCamera* Camera=g_Game->GetView()->GetCamera();
CVector3D CamFace=Camera->m_Orientation.GetIn();
CamFace.Normalize();
float FresnelScalar = CamFace.Dot( CVector3D(0.0f, -1.0f, 0.0f) );
//Invert and set boundaries
FresnelScalar = (1 - FresnelScalar) * 0.4f + 0.6f;
const int DX[] = {1,1,0,0};
const int DZ[] = {0,1,1,0};
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
int mapSize = terrain->GetVerticesPerSide();
CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
double time = get_time();
double period = 1.6;
int curTex = (int)(time*60/period) % 60;
ogl_tex_bind(WaterMgr->m_WaterTexture[curTex], 0);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
float tx = -fmod(time, 20.0)/20.0;
float ty = fmod(time, 35.0)/35.0;
glTranslatef(tx, ty, 0);
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_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_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, g_Renderer.m_Options.m_LodBias);
glBegin(GL_QUADS);
for(size_t i=0; i<m->visiblePatches.size(); i++)
{
CPatch* patch = m->visiblePatches[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);
int z = (patch->m_Z*PATCH_SIZE + dz);
// 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 terrainHeight = terrain->getVertexGroundLevel(x + DX[j], z + DZ[j]);
if(terrainHeight < WaterMgr->m_WaterHeight)
{
shouldRender = true;
break;
}
}
if(!shouldRender)
{
continue;
}
for(int j=0; j<4; j++)
{
int ix = x + DX[j];
int iz = z + DZ[j];
float vertX = ix * CELL_SIZE;
float vertZ = iz * CELL_SIZE;
float terrainHeight = terrain->getVertexGroundLevel(ix, iz);
float alpha = clamp(
(WaterMgr->m_WaterHeight - terrainHeight) / WaterMgr->m_WaterFullDepth + WaterMgr->m_WaterAlphaOffset,
-100.0f, WaterMgr->m_WaterMaxAlpha);
float losMod = 1.0f;
for(int k=0; k<4; k++)
{
int tx = ix - DX[k];
int tz = iz - DZ[k];
if(tx >= 0 && tz >= 0 && tx <= mapSize-2 && tz <= mapSize-2)
{
ELOSStatus s = losMgr->GetStatus(tx, tz, g_Game->GetLocalPlayer());
if(s == LOS_EXPLORED && losMod > 0.7f)
losMod = 0.7f;
else if(s==LOS_UNEXPLORED && losMod > 0.0f)
losMod = 0.0f;
}
}
glColor4f(WaterMgr->m_WaterColor.r*losMod, WaterMgr->m_WaterColor.g*losMod, WaterMgr->m_WaterColor.b*losMod, alpha * FresnelScalar);
pglMultiTexCoord2fARB(GL_TEXTURE0, vertX/16.0f, vertZ/16.0f);
glVertex3f(vertX, WaterMgr->m_WaterHeight, vertZ);
}
} //end of x loop
} //end of z loop
}
glEnd();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}
///////////////////////////////////////////////////////////////////
//
void TerrainRenderer::ApplyShadowMap(GLuint shadowmaphandle)
{
debug_assert(m->phase == Phase_Render);
g_Renderer.BindTexture(0, shadowmaphandle);
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_TEXTURE);
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_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glColor3f(1,1,1);
glEnable(GL_BLEND);
glBlendFunc(GL_DST_COLOR,GL_ZERO);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
for (uint i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderStreams(STREAM_POS|STREAM_POSTOUV0);
}
glDisable(GL_BLEND);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

View File

@ -0,0 +1,112 @@
/**
* =========================================================================
* File : TerrainRenderer.h
* Project : Pyrogenesis
* Description : Terrain rendering (everything related to patches and
* : water) is encapsulated in TerrainRenderer
*
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#ifndef TERRAINRENDERER_H
#define TERRAINRENDERER_H
class CPatch;
class WaterManager;
struct TerrainRendererInternals;
/**
* Class TerrainRenderer: Render everything related to the terrain,
* especially patches and water.
*/
class TerrainRenderer
{
public:
TerrainRenderer();
~TerrainRenderer();
/**
* Submit: Add a patch for rendering in this frame.
*
* preconditions : PrepareForRendering must not have been called
* for this frame yet.
* The patch must not have been submitted in this frame yet (i.e. you
* can only submit a frame once).
*
* @param patch the patch
*/
void Submit(CPatch* patch);
/**
* PrepareForRendering: Prepare internal data structures like vertex
* buffers for rendering.
*
* All patches must have been submitted before the call to
* PrepareForRendering.
* PrepareForRendering must be called before any rendering calls.
*/
void PrepareForRendering();
/**
* EndFrame: Remove all patches from the list of submitted patches.
*/
void EndFrame();
/**
* HaveSubmissions: Query whether any patches have been submitted
* for this frame.
*
* @return @c true if a patch has been submitted for this frame,
* @c false otherwise.
*/
bool HaveSubmissions();
/**
* RenderTerrain: Render textured terrain (including blends between
* different terrain types).
*
* preconditions : PrepareForRendering must have been called this
* frame before calling RenderTerrain.
*/
void RenderTerrain();
/**
* RenderPatches: Render all patches un-textured as polygons.
*
* preconditions : PrepareForRendering must have been called this
* frame before calling RenderPatches.
*/
void RenderPatches();
/**
* RenderOutlines: Render the outline of patches as lines.
*
* preconditions : PrepareForRendering must have been called this
* frame before calling RenderOutline.
*/
void RenderOutlines();
/**
* RenderWater: Render water for all patches that have been submitted
* this frame.
*
* preconditions : PrepareForRendering must have been called this
* frame before calling RenderOutline.
*/
void RenderWater();
/**
* ApplyShadowMap: transition measure during refactoring
*
* @todo fix this
* @deprecated
*/
void ApplyShadowMap(GLuint handle);
private:
TerrainRendererInternals* m;
};
#endif // TERRAINRENDERER_H

View File

@ -2,8 +2,8 @@
* =========================================================================
* File : TransparencyRenderer.h
* Project : Pyrogenesis
* Description : ModelRenderer implementation that sorts models and/or
* : polygons based on distance from viewer, for transparency
* Description : ModelRenderer implementation that sorts models and/or
* : polygons based on distance from viewer, for transparency
* : rendering.
*
* @author Rich Cross <rich@wildfiregames.com>
@ -41,10 +41,10 @@
struct PSModelDef : public CModelDefRPrivate
{
PSModelDef(CModelDefPtr mdef);
/// Static vertex array
VertexArray m_Array;
/// UV is static
VertexArray::Attribute m_UV;
};
@ -55,14 +55,14 @@ PSModelDef::PSModelDef(CModelDefPtr mdef)
m_UV.type = GL_FLOAT;
m_UV.elems = 2;
m_Array.AddAttribute(&m_UV);
m_Array.SetNumVertices(mdef->GetNumVertices());
m_Array.Layout();
VertexArrayIterator<float[2]> UVit = m_UV.GetIterator<float[2]>();
ModelRenderer::BuildUV(mdef, UVit);
m_Array.Upload();
m_Array.FreeBackingStore();
}
@ -75,7 +75,7 @@ struct PSModel
{
PSModel(CModel* model);
~PSModel();
/**
* BackToFrontIndexSort: Sort polygons by distance to camera for
* transparency rendering and fill the indices array appropriately.
@ -85,13 +85,13 @@ struct PSModel
* @return Square of the estimated distance to the nearest triangle.
*/
float BackToFrontIndexSort(const CMatrix3D& objToCam);
/// Back-link to the model
CModel* m_Model;
/// Dynamic per-CModel vertex array
VertexArray m_Array;
/// Position and lighting are recalculated on CPU every frame
VertexArray::Attribute m_Position;
VertexArray::Attribute m_Color;
@ -104,7 +104,7 @@ PSModel::PSModel(CModel* model)
: m_Model(model), m_Array(true)
{
CModelDefPtr mdef = m_Model->GetModelDef();
m_Position.type = GL_FLOAT;
m_Position.elems = 3;
m_Array.AddAttribute(&m_Position);
@ -112,10 +112,10 @@ PSModel::PSModel(CModel* model)
m_Color.type = GL_UNSIGNED_BYTE;
m_Color.elems = 4;
m_Array.AddAttribute(&m_Color);
m_Array.SetNumVertices(mdef->GetNumVertices());
m_Array.Layout();
m_Indices = new u16[mdef->GetNumFaces()*3];
}
@ -140,7 +140,7 @@ float PSModel::BackToFrontIndexSort(const CMatrix3D& worldToCam)
CModelDefPtr mdef = m_Model->GetModelDef();
size_t numFaces = mdef->GetNumFaces();
const SModelFace* faces = mdef->GetFaces();
if (IndexSorter.size() < numFaces)
IndexSorter.resize(numFaces);
@ -162,7 +162,7 @@ float PSModel::BackToFrontIndexSort(const CMatrix3D& worldToCam)
}
std::sort(IndexSorter.begin(),IndexSorter.begin()+numFaces,SortFacesByDist());
// now build index list
u32 idxidx = 0;
for (size_t i = 0; i < numFaces; ++i) {
@ -203,13 +203,13 @@ void* PolygonSortModelRenderer::CreateModelData(CModel* model)
{
CModelDefPtr mdef = model->GetModelDef();
PSModelDef* psmdef = (PSModelDef*)mdef->GetRenderData(m);
if (!psmdef)
{
psmdef = new PSModelDef(mdef);
mdef->SetRenderData(m, psmdef);
}
return new PSModel(model);
}
@ -240,14 +240,14 @@ void PolygonSortModelRenderer::UpdateModelData(CModel* model, void* data, u32 up
// upload everything to vertex buffer
psmdl->m_Array.Upload();
}
// resort model indices from back to front, according to camera position - and store
// the returned sqrd distance to the centre of the nearest triangle
PROFILE_START( "sorting transparent" );
CMatrix3D worldToCam;
g_Renderer.GetCamera().m_Orientation.GetInverse(worldToCam);
psmdl->BackToFrontIndexSort(worldToCam);
PROFILE_END( "sorting transparent" );
}
@ -257,7 +257,7 @@ void PolygonSortModelRenderer::UpdateModelData(CModel* model, void* data, u32 up
void PolygonSortModelRenderer::DestroyModelData(CModel* UNUSED(model), void* data)
{
PSModel* psmdl = (PSModel*)data;
delete psmdl;
}
@ -277,7 +277,7 @@ void PolygonSortModelRenderer::EndPass(uint streamflags)
{
if (streamflags & STREAM_UV0) glDisableClientState(GL_TEXTURE_COORD_ARRAY);
if (streamflags & STREAM_COLOR) glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
@ -288,12 +288,12 @@ void PolygonSortModelRenderer::PrepareModelDef(uint streamflags, CModelDefPtr de
if (streamflags & STREAM_UV0)
{
PSModelDef* psmdef = (PSModelDef*)def->GetRenderData(m);
debug_assert(psmdef);
u8* base = psmdef->m_Array.Bind();
GLsizei stride = (GLsizei)psmdef->m_Array.GetStride();
glTexCoordPointer(2, GL_FLOAT, stride, base + psmdef->m_UV.offset);
}
}
@ -304,14 +304,14 @@ void PolygonSortModelRenderer::RenderModel(uint streamflags, CModel* model, void
{
CModelDefPtr mdef = model->GetModelDef();
PSModel* psmdl = (PSModel*)data;
// Setup per-CModel arrays
u8* base = psmdl->m_Array.Bind();
GLsizei stride = (GLsizei)psmdl->m_Array.GetStride();
glVertexPointer(3, GL_FLOAT, stride, base + psmdl->m_Position.offset);
if (streamflags & STREAM_COLOR)
glColorPointer(3, psmdl->m_Color.type, stride, base + psmdl->m_Color.offset);
glColorPointer(3, psmdl->m_Color.type, stride, base + psmdl->m_Color.offset);
// render the lot
size_t numFaces = mdef->GetNumFaces();
@ -335,13 +335,13 @@ struct SModel : public CModelRData
{
SModel(SortModelRendererInternals* tri, CModel* model);
~SModel();
// Back-link to the Model renderer
SortModelRendererInternals* m_SMRI;
// Private data of the ModelVertexRenderer
void* m_Data;
// Distance to camera (for sorting)
float m_Distance;
};
@ -354,7 +354,7 @@ struct SortModelRendererInternals
{
/// Vertex renderer used for transform and lighting
ModelVertexRendererPtr vertexRenderer;
/// List of submitted models.
std::vector<SModel*> models;
};
@ -392,7 +392,7 @@ void SortModelRenderer::Submit(CModel* model)
{
CModelRData* rdata = (CModelRData*)model->GetRenderData();
SModel* smdl;
if (rdata && rdata->GetKey() == m)
{
smdl = (SModel*)rdata;
@ -405,7 +405,7 @@ void SortModelRenderer::Submit(CModel* model)
model->SetDirty(~0u);
g_Renderer.LoadTexture(model->GetTexture(), GL_CLAMP_TO_EDGE);
}
m->models.push_back(smdl);
}
@ -420,26 +420,26 @@ struct SortModelsByDist {
void SortModelRenderer::PrepareModels()
{
CMatrix3D worldToCam;
if (m->models.size() == 0)
return;
g_Renderer.m_Camera.m_Orientation.GetInverse(worldToCam);
for(std::vector<SModel*>::iterator it = m->models.begin(); it != m->models.end(); ++it)
{
SModel* smdl = *it;
CModel* model = smdl->GetModel();
debug_assert(model->GetRenderData() == smdl);
m->vertexRenderer->UpdateModelData(model, smdl->m_Data, smdl->m_UpdateFlags);
smdl->m_UpdateFlags = 0;
CVector3D modelpos = model->GetTransform().GetTranslation();
modelpos = worldToCam.Transform(modelpos);
smdl->m_Distance = modelpos.Z;
}
@ -467,51 +467,51 @@ bool SortModelRenderer::HaveSubmissions()
void SortModelRenderer::Render(RenderModifierPtr modifier, u32 flags)
{
uint pass = 0;
if (m->models.size() == 0)
return;
do
{
u32 streamflags = modifier->BeginPass(pass);
CModelDefPtr lastmdef;
CTexture* lasttex = 0;
m->vertexRenderer->BeginPass(streamflags);
for(std::vector<SModel*>::iterator it = m->models.begin(); it != m->models.end(); ++it)
{
SModel* smdl = *it;
CModel* mdl = smdl->GetModel();
if (flags & !(mdl->GetFlags() & flags))
continue;
debug_assert(smdl->GetKey() == m);
CModelDefPtr mdef = mdl->GetModelDef();
CTexture* tex = mdl->GetTexture();
// Prepare per-CModelDef data if changed
if (mdef != lastmdef)
{
m->vertexRenderer->PrepareModelDef(streamflags, mdef);
lastmdef = mdef;
}
// Prepare necessary RenderModifier stuff
if (tex != lasttex)
{
modifier->PrepareTexture(pass, tex);
lasttex = tex;
}
modifier->PrepareModel(pass, mdl);
// Render the model
m->vertexRenderer->RenderModel(streamflags, mdl, smdl->m_Data);
}
m->vertexRenderer->EndPass(streamflags);
} while(!modifier->EndPass(pass++));
}
@ -547,13 +547,13 @@ u32 TransparentRenderModifier::BeginPass(uint pass)
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER,0.975f);
// render everything with color writes off to setup depth buffer correctly
glColorMask(0,0,0,0);
return STREAM_POS|STREAM_UV0;
}
else
@ -574,10 +574,10 @@ u32 TransparentRenderModifier::BeginPass(uint pass)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
return STREAM_POS|STREAM_COLOR|STREAM_UV0;
}
}
@ -586,11 +586,11 @@ bool TransparentRenderModifier::EndPass(uint pass)
{
if (pass == 0)
return false; // multi-pass
glDisable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glDepthMask(1);
return true;
}
@ -619,7 +619,7 @@ TransparentShadowRenderModifier::~TransparentShadowRenderModifier()
u32 TransparentShadowRenderModifier::BeginPass(uint pass)
{
debug_assert(pass == 0);
glDepthMask(0);
glEnable(GL_BLEND);
@ -643,7 +643,7 @@ bool TransparentShadowRenderModifier::EndPass(uint UNUSED(pass))
{
glDepthMask(1);
glDisable(GL_BLEND);
return true;
}

View File

@ -0,0 +1,106 @@
/**
* =========================================================================
* File : WaterManager.cpp
* Project : Pyrogenesis
* Description : Water settings (speed, height) and texture management
*
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#include "precompiled.h"
#include "lib/timer.h"
#include "lib/res/file/vfs.h"
#include "lib/res/graphics/tex.h"
#include "lib/res/graphics/ogl_tex.h"
#include "ps/CLogger.h"
#include "ps/Loader.h"
#include "renderer/WaterManager.h"
#define LOG_CATEGORY "graphics"
///////////////////////////////////////////////////////////////////////////////////////////////
// WaterManager implementation
///////////////////////////////////////////////////////////////////
// Construction/Destruction
WaterManager::WaterManager()
{
// water
m_RenderWater = true;
m_WaterHeight = 5.0f;
m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f);
m_WaterFullDepth = 4.0f;
m_WaterMaxAlpha = 0.85f;
m_WaterAlphaOffset = -0.05f;
m_SWaterTrans=0;
m_TWaterTrans=0;
m_SWaterSpeed=0.0015f;
m_TWaterSpeed=0.0015f;
m_SWaterScrollCounter=0;
m_TWaterScrollCounter=0;
m_WaterCurrentTex=0;
for (uint i = 0; i < ARRAY_SIZE(m_WaterTexture); i++)
m_WaterTexture[i] = 0;
cur_loading_water_tex = 0;
}
WaterManager::~WaterManager()
{
// Cleanup if the caller messed up
UnloadWaterTextures();
}
///////////////////////////////////////////////////////////////////
// Progressive load of water textures
int WaterManager::LoadWaterTextures()
{
const uint num_textures = ARRAY_SIZE(m_WaterTexture);
// yield after this time is reached. balances increased progress bar
// smoothness vs. slowing down loading.
const double end_time = get_time() + 100e-3;
while (cur_loading_water_tex < num_textures)
{
char waterName[VFS_MAX_PATH];
// TODO: add a member variable and setter for this. (can't make this
// a parameter because this function is called via delay-load code)
static const char* const water_type = "animation2";
snprintf(waterName, ARRAY_SIZE(waterName), "art/textures/terrain/types/water/%s/water%02d.dds", water_type, cur_loading_water_tex+1);
Handle ht = ogl_tex_load(waterName);
if (ht <= 0)
{
LOG(ERROR, LOG_CATEGORY, "LoadWaterTextures failed on \"%s\"", waterName);
return ht;
}
m_WaterTexture[cur_loading_water_tex] = ht;
RETURN_ERR(ogl_tex_upload(ht));
cur_loading_water_tex++;
LDR_CHECK_TIMEOUT(cur_loading_water_tex, num_textures);
}
return 0;
}
///////////////////////////////////////////////////////////////////
// Unload water textures
void WaterManager::UnloadWaterTextures()
{
for(uint i = 0; i < ARRAY_SIZE(m_WaterTexture); i++)
{
ogl_tex_free(m_WaterTexture[i]);
m_WaterTexture[i] = 0;
}
cur_loading_water_tex = 0; // so they will be reloaded if LoadWaterTextures is called again
}

View File

@ -0,0 +1,68 @@
/**
* =========================================================================
* File : WaterManager.h
* Project : Pyrogenesis
* Description : Water settings (speed, height) and texture management
*
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#ifndef WATERMANAGER_H
#define WATERMANAGER_H
#include "ps/Overlay.h"
/**
* Class WaterManager: Maintain water settings and textures.
*
* This could be extended to provide more advanced water rendering effects
* (refractive/reflective water) in the future.
*/
class WaterManager
{
public:
Handle m_WaterTexture[60];
int m_WaterCurrentTex;
CColor m_WaterColor;
bool m_RenderWater;
bool m_WaterScroll;
float m_WaterHeight;
float m_WaterMaxAlpha;
float m_WaterFullDepth;
float m_WaterAlphaOffset;
float m_SWaterSpeed;
float m_TWaterSpeed;
float m_SWaterTrans;
float m_TWaterTrans;
float m_SWaterScrollCounter;
float m_TWaterScrollCounter;
float m_WaterTexTimer;
public:
WaterManager();
~WaterManager();
/**
* LoadWaterTextures: Load water textures from within the
* progressive load framework.
*
* @return 0 if loading has completed, a value from 1 to 100 (in percent of completion)
* if more textures need to be loaded and a negative error value on failure.
*/
int LoadWaterTextures();
/**
* UnloadWaterTextures: Free any loaded water textures and reset the internal state
* so that another call to LoadWaterTextures will begin progressive loading.
*/
void UnloadWaterTextures();
private:
/// State of progressive loading (in # of loaded textures)
uint cur_loading_water_tex;
};
#endif // WATERMANAGER_H

View File

@ -36,6 +36,7 @@
#include "scripting/JSInterface_Console.h"
#include "scripting/JSInterface_VFS.h"
#include "scripting/JSConversions.h"
#include "renderer/WaterManager.h"
#ifndef NO_GUI
# include "gui/scripting/JSInterface_IGUIObject.h"
#endif
@ -181,7 +182,7 @@ JSBool getEntityTemplate( JSContext* cx, JSObject*, uint argc, jsval* argv, jsva
JS_ReportError( cx, "No such template: %s", CStr8(templateName).c_str() );
return( JS_TRUE );
}
*rval = OBJECT_TO_JSVAL( v->GetScript() );
return( JS_TRUE );
}
@ -201,7 +202,7 @@ JSBool issueCommand( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rv
if (JS_GetClass(JSVAL_TO_OBJECT(argv[0])) == &CEntity::JSI_class)
entities.push_back( (ToNative<CEntity>(argv[0])) ->me);
else
else
entities = *EntityCollection::RetrieveSet(cx, JSVAL_TO_OBJECT(argv[0]));
CNetMessage *msg = CNetMessage::CommandFromJSArgs(entities, cx, argc-1, argv+1);
@ -389,7 +390,7 @@ JSBool cancelInterval( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval*
}
// Cause the scheduled task (timeout or interval) with the given ID to
// Cause the scheduled task (timeout or interval) with the given ID to
// no longer be called.
// params:
// returns:
@ -431,7 +432,7 @@ JSBool createServer(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rva
g_Game = new CGame();
if( !g_NetServer )
g_NetServer = new CNetServer(g_Game, &g_GameAttributes);
*rval = OBJECT_TO_JSVAL(g_NetServer->GetScript());
return( JS_TRUE );
}
@ -817,7 +818,7 @@ JSBool toggleWater( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, js
{
REQUIRE_NO_PARAMS( toggleWater );
debug_printf("Toggling water!\n");
g_Renderer.m_RenderWater = !g_Renderer.m_RenderWater;
g_Renderer.GetWaterManager()->m_RenderWater = !g_Renderer.GetWaterManager()->m_RenderWater;
*rval = JSVAL_VOID;
return( JS_TRUE );
}
@ -833,7 +834,7 @@ JSBool setWaterHeight( JSContext* cx, JSObject* UNUSED(globalObject), uint argc,
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Renderer.m_WaterHeight = newHeight;
g_Renderer.GetWaterManager()->m_WaterHeight = newHeight;
g_TerrainModified = true;
*rval = JSVAL_VOID;
return( JS_TRUE );
@ -843,7 +844,7 @@ JSBool setWaterHeight( JSContext* cx, JSObject* UNUSED(globalObject), uint argc,
JSBool getWaterHeight( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
REQUIRE_NO_PARAMS( getWaterHeight );
*rval = ToJSVal(g_Renderer.m_WaterHeight);
*rval = ToJSVal(g_Renderer.GetWaterManager()->m_WaterHeight);
return( JS_TRUE );
}
@ -860,7 +861,7 @@ JSBool setWaterColor( JSContext* cx, JSObject* UNUSED(globalObject), uint argc,
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Renderer.m_WaterColor = CColor(r, g, b, 1.0f);
g_Renderer.GetWaterManager()->m_WaterColor = CColor(r, g, b, 1.0f);
*rval = JSVAL_VOID;
return( JS_TRUE );
}
@ -876,7 +877,7 @@ JSBool setWaterMaxAlpha( JSContext* cx, JSObject* UNUSED(globalObject), uint arg
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Renderer.m_WaterMaxAlpha = val;
g_Renderer.GetWaterManager()->m_WaterMaxAlpha = val;
*rval = JSVAL_VOID;
return( JS_TRUE );
}
@ -892,7 +893,7 @@ JSBool setWaterFullDepth( JSContext* cx, JSObject* UNUSED(globalObject), uint ar
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Renderer.m_WaterFullDepth = val;
g_Renderer.GetWaterManager()->m_WaterFullDepth = val;
*rval = JSVAL_VOID;
return( JS_TRUE );
}
@ -908,7 +909,7 @@ JSBool setWaterAlphaOffset( JSContext* cx, JSObject* UNUSED(globalObject), uint
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Renderer.m_WaterAlphaOffset = val;
g_Renderer.GetWaterManager()->m_WaterAlphaOffset = val;
*rval = JSVAL_VOID;
return( JS_TRUE );
}
@ -973,7 +974,7 @@ JSBool revealMap( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsva
// we simplify this a bit with a macro:
#define JS_FUNC(script_name, cpp_function, min_params) { #script_name, cpp_function, min_params, 0, 0 },
JSFunctionSpec ScriptFunctionTable[] =
JSFunctionSpec ScriptFunctionTable[] =
{
// Console
JS_FUNC(writeConsole, JSI_Console::writeConsole, 1) // external
@ -996,7 +997,7 @@ JSFunctionSpec ScriptFunctionTable[] =
JS_FUNC(setWaterFullDepth, setWaterFullDepth, 0)
JS_FUNC(setWaterAlphaOffset, setWaterAlphaOffset, 0)
// GUI
// GUI
#ifndef NO_GUI
JS_FUNC(getGUIObjectByName, JSI_IGUIObject::getByName, 1) // external
JS_FUNC(getGUIGlobal, getGUIGlobal, 0)

View File

@ -19,6 +19,7 @@
#include "scripting/JSInterface_Vector3D.h"
#include "MathUtil.h"
#include "CConsole.h"
#include "renderer/WaterManager.h"
extern CConsole* g_Console;
extern int g_xres, g_yres;
@ -878,7 +879,7 @@ float CEntity::getAnchorLevel( float x, float z )
}
else
{
return max( groundLevel, g_Renderer.m_WaterHeight );
return max( groundLevel, g_Renderer.GetWaterManager()->m_WaterHeight );
}
}