0ad/source/renderer/PatchRData.cpp
Ykkrosh 35e91718c5 # Added tool for viewing models and animations outside the game.
Atlas: Added ActorViewer. Moved GL canvas into separate class for shared
use. Disabled message-handling callback while blocked on the game, and
stopped creating dialog boxes inside the game thread in order to avoid
deadlocks (hopefully). Support multiple Views (for independent sets of
camera/update/render code). Recalculate territory boundaries when
necessary. Changed default list of animations to match those currently
used by actors.
# Tidied up more code.
Moved some more #includes out of .h files, to minimise unnecessary
compilation.
MathUtil: Deleted unused/unuseful macros (M_PI (use PI instead), M_PI_2
(use PI/2), MAX3, ABS (use abs)).
ObjectManager: Removed some ScEd-specific things.
Unit: Moved creation out of UnitManager, so units can be created without
adding to the manager. Changed CStr8 to the more conventional CStr.
app_hooks: Removed warning for setting multiple times.
win: Restored SEH catcher.
GameSetup, GameView: Removed RenderNoCull, because it doesn't seem to do
what it says it does ("force renderer to load everything") since we're
loading-on-demand most stuff and it doesn't seem especially useful since
we'd prefer to minimise loading times (but feel free to correct me if
I'm wrong). (And because it crashes when things need to be initialised
in a different order, so it's easier to remove than to understand and
fix it.)
PatchRData, Renderer: Work sensibly when there's no game (hence no LOS
manager, water, etc).
LOSManager: Use entity position instead of actor position when possible.
TerritoryManager: Allow delayed recalculations (so Atlas can issue lots
of move+recalculate commands per frame).
Cinematic: Non-pointer wxTimer, so it doesn't leak and doesn't have to
be deleted manually.

This was SVN commit r4261.
2006-08-28 17:36:42 +00:00

594 lines
16 KiB
C++

#include "precompiled.h"
#include <set>
#include <algorithm>
#include "ps/Pyrogenesis.h"
#include "lib/res/graphics/ogl_tex.h"
#include "graphics/LightEnv.h"
#include "Renderer.h"
#include "renderer/PatchRData.h"
#include "AlphaMapCalculator.h"
#include "ps/CLogger.h"
#include "ps/Profile.h"
#include "maths/MathUtil.h"
#include "simulation/LOSManager.h"
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
const int BlendOffsets[8][2] = {
{ 0, -1 },
{ -1, -1 },
{ -1, 0 },
{ -1, 1 },
{ 0, 1 },
{ 1, 1 },
{ 1, 0 },
{ 1, -1 }
};
///////////////////////////////////////////////////////////////////
// CPatchRData constructor
CPatchRData::CPatchRData(CPatch* patch) : m_Patch(patch), m_VBBase(0), m_VBBlends(0), m_Vertices(0)
{
debug_assert(patch);
Build();
}
///////////////////////////////////////////////////////////////////
// CPatchRData destructor
CPatchRData::~CPatchRData()
{
// delete copy of vertex data
delete[] m_Vertices;
// release vertex buffer chunks
if (m_VBBase) g_VBMan.Release(m_VBBase);
if (m_VBBlends) g_VBMan.Release(m_VBBlends);
}
static Handle GetTerrainTileTexture(CTerrain* terrain,int gx,int gz)
{
CMiniPatch* mp=terrain->GetTile(gx,gz);
return mp ? mp->Tex1 : 0;
}
const float uvFactor = 0.125f / sqrt(2.f);
static void CalculateUV(float uv[2], int x, int z)
{
// The UV axes are offset 45 degrees from XZ
uv[0] = ( x-z)*uvFactor;
uv[1] = (-x-z)*uvFactor;
}
struct STmpSplat {
Handle m_Texture;
u16 m_Indices[4];
};
void CPatchRData::BuildBlends()
{
m_BlendIndices.clear();
m_BlendSplats.clear();
m_BlendVertices.clear();
m_BlendVertexIndices.clear();
CTerrain* terrain=m_Patch->m_Parent;
// temporary list of splats
std::vector<STmpSplat> splats;
// set of textures used for splats
std::set<Handle> splatTextures;
// for each tile in patch ..
for (int j=0;j<PATCH_SIZE;j++) {
for (int i=0;i<PATCH_SIZE;i++) {
u32 gx,gz;
CMiniPatch* mp=&m_Patch->m_MiniPatches[j][i];
mp->GetTileIndex(gx,gz);
// build list of textures of higher priority than current tile that are used by neighbouring tiles
std::vector<STex> neighbourTextures;
for (int m=-1;m<=1;m++) {
for (int k=-1;k<=1;k++) {
CMiniPatch* nmp=terrain->GetTile(gx+k,gz+m);
if (nmp && nmp->Tex1 != mp->Tex1) {
if (nmp->Tex1Priority>mp->Tex1Priority || (nmp->Tex1Priority==mp->Tex1Priority && nmp->Tex1>mp->Tex1)) {
STex tex;
tex.m_Handle=nmp->Tex1;
tex.m_Priority=nmp->Tex1Priority;
if (std::find(neighbourTextures.begin(),neighbourTextures.end(),tex)==neighbourTextures.end()) {
neighbourTextures.push_back(tex);
}
}
}
}
}
if (neighbourTextures.size()>0) {
// sort textures from lowest to highest priority
std::sort(neighbourTextures.begin(),neighbourTextures.end());
// for each of the neighbouring textures ..
uint count=(uint)neighbourTextures.size();
for (uint k=0;k<count;++k) {
// now build the grid of blends dependent on whether the tile adjacent to the current tile
// uses the current neighbour texture
BlendShape8 shape;
for (int m=0;m<8;m++) {
int ox=gx+BlendOffsets[m][1];
int oz=gz+BlendOffsets[m][0];
// get texture on adjacent tile
Handle atex=GetTerrainTileTexture(terrain,ox,oz);
// fill 0/1 into shape array
shape[m]=(atex==neighbourTextures[k].m_Handle) ? 0 : 1;
}
// calculate the required alphamap and the required rotation of the alphamap from blendshape
unsigned int alphamapflags;
int alphamap=CAlphaMapCalculator::Calculate(shape,alphamapflags);
// now actually render the blend tile (if we need one)
if (alphamap!=-1) {
float u0=g_Renderer.m_AlphaMapCoords[alphamap].u0;
float u1=g_Renderer.m_AlphaMapCoords[alphamap].u1;
float v0=g_Renderer.m_AlphaMapCoords[alphamap].v0;
float v1=g_Renderer.m_AlphaMapCoords[alphamap].v1;
if (alphamapflags & BLENDMAP_FLIPU) {
// flip u
float t=u0;
u0=u1;
u1=t;
}
if (alphamapflags & BLENDMAP_FLIPV) {
// flip v
float t=v0;
v0=v1;
v1=t;
}
int base=0;
if (alphamapflags & BLENDMAP_ROTATE90) {
// rotate 1
base=1;
} else if (alphamapflags & BLENDMAP_ROTATE180) {
// rotate 2
base=2;
} else if (alphamapflags & BLENDMAP_ROTATE270) {
// rotate 3
base=3;
}
SBlendVertex vtx[4];
vtx[(base+0)%4].m_AlphaUVs[0]=u0;
vtx[(base+0)%4].m_AlphaUVs[1]=v0;
vtx[(base+1)%4].m_AlphaUVs[0]=u1;
vtx[(base+1)%4].m_AlphaUVs[1]=v0;
vtx[(base+2)%4].m_AlphaUVs[0]=u1;
vtx[(base+2)%4].m_AlphaUVs[1]=v1;
vtx[(base+3)%4].m_AlphaUVs[0]=u0;
vtx[(base+3)%4].m_AlphaUVs[1]=v1;
int vsize=PATCH_SIZE+1;
SBlendVertex dst;
int vindex=(int)m_BlendVertices.size();
const SBaseVertex& vtx0=m_Vertices[(j*vsize)+i];
CalculateUV(dst.m_UVs, gx, gz);
dst.m_AlphaUVs[0]=vtx[0].m_AlphaUVs[0];
dst.m_AlphaUVs[1]=vtx[0].m_AlphaUVs[1];
dst.m_LOSColor=vtx0.m_LOSColor;
dst.m_Position=vtx0.m_Position;
m_BlendVertices.push_back(dst);
m_BlendVertexIndices.push_back((j*vsize)+i);
const SBaseVertex& vtx1=m_Vertices[(j*vsize)+i+1];
CalculateUV(dst.m_UVs, gx+1, gz);
dst.m_AlphaUVs[0]=vtx[1].m_AlphaUVs[0];
dst.m_AlphaUVs[1]=vtx[1].m_AlphaUVs[1];
dst.m_LOSColor=vtx1.m_LOSColor;
dst.m_Position=vtx1.m_Position;
m_BlendVertices.push_back(dst);
m_BlendVertexIndices.push_back((j*vsize)+i+1);
const SBaseVertex& vtx2=m_Vertices[((j+1)*vsize)+i+1];
CalculateUV(dst.m_UVs, gx+1, gz+1);
dst.m_AlphaUVs[0]=vtx[2].m_AlphaUVs[0];
dst.m_AlphaUVs[1]=vtx[2].m_AlphaUVs[1];
dst.m_LOSColor=vtx2.m_LOSColor;
dst.m_Position=vtx2.m_Position;
m_BlendVertices.push_back(dst);
m_BlendVertexIndices.push_back(((j+1)*vsize)+i+1);
const SBaseVertex& vtx3=m_Vertices[((j+1)*vsize)+i];
CalculateUV(dst.m_UVs, gx, gz+1);
dst.m_AlphaUVs[0]=vtx[3].m_AlphaUVs[0];
dst.m_AlphaUVs[1]=vtx[3].m_AlphaUVs[1];
dst.m_LOSColor=vtx3.m_LOSColor;
dst.m_Position=vtx3.m_Position;
m_BlendVertices.push_back(dst);
m_BlendVertexIndices.push_back(((j+1)*vsize)+i);
// build a splat for this quad
STmpSplat splat;
splat.m_Texture=neighbourTextures[k].m_Handle;
splat.m_Indices[0]=(u16)(vindex);
splat.m_Indices[1]=(u16)(vindex+1);
splat.m_Indices[2]=(u16)(vindex+2);
splat.m_Indices[3]=(u16)(vindex+3);
splats.push_back(splat);
// add this texture to set of unique splat textures
splatTextures.insert(splat.m_Texture);
}
}
}
}
}
// build vertex data
if (m_VBBlends) {
// release existing vertex buffer chunk
g_VBMan.Release(m_VBBlends);
m_VBBlends=0;
}
if (m_BlendVertices.size()) {
m_VBBlends=g_VBMan.Allocate(sizeof(SBlendVertex),m_BlendVertices.size(),true);
m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends,&m_BlendVertices[0]);
// now build outgoing splats
m_BlendSplats.resize(splatTextures.size());
int splatCount=0;
debug_assert(m_VBBlends->m_Index < 65536);
unsigned short base = (unsigned short)m_VBBlends->m_Index;
std::set<Handle>::iterator iter=splatTextures.begin();
for (;iter!=splatTextures.end();++iter) {
Handle tex=*iter;
SSplat& splat=m_BlendSplats[splatCount];
splat.m_IndexStart=(u32)m_BlendIndices.size();
splat.m_Texture=tex;
for (uint k=0;k<(uint)splats.size();k++) {
if (splats[k].m_Texture==tex) {
m_BlendIndices.push_back(splats[k].m_Indices[0]+base);
m_BlendIndices.push_back(splats[k].m_Indices[1]+base);
m_BlendIndices.push_back(splats[k].m_Indices[2]+base);
m_BlendIndices.push_back(splats[k].m_Indices[3]+base);
splat.m_IndexCount+=4;
}
}
splatCount++;
}
}
}
void CPatchRData::BuildIndices()
{
// must have allocated some vertices before trying to build corresponding indices
debug_assert(m_VBBase);
// number of vertices in each direction in each patch
int vsize=PATCH_SIZE+1;
// release existing indices and bins
m_Indices.clear();
m_ShadowMapIndices.clear();
m_Splats.clear();
// build grid of textures on this patch and boundaries of adjacent patches
std::vector<Handle> textures;
Handle texgrid[PATCH_SIZE][PATCH_SIZE];
for (int j=0;j<PATCH_SIZE;j++) {
for (int i=0;i<PATCH_SIZE;i++) {
Handle h=m_Patch->m_MiniPatches[j][i].Tex1;
texgrid[j][i]=h;
if (std::find(textures.begin(),textures.end(),h)==textures.end()) {
textures.push_back(h);
}
}
}
// now build base splats from interior textures
m_Splats.resize(textures.size());
// build indices for base splats
u32 base=(u32)m_VBBase->m_Index;
for (uint i=0;i<(uint)m_Splats.size();i++) {
Handle h=textures[i];
SSplat& splat=m_Splats[i];
splat.m_Texture=h;
splat.m_IndexStart=(u32)m_Indices.size();
for (int j=0;j<PATCH_SIZE;j++) {
for (int i=0;i<PATCH_SIZE;i++) {
if (texgrid[j][i]==h){
m_Indices.push_back(((j+0)*vsize+(i+0))+base);
m_Indices.push_back(((j+0)*vsize+(i+1))+base);
m_Indices.push_back(((j+1)*vsize+(i+1))+base);
m_Indices.push_back(((j+1)*vsize+(i+0))+base);
}
}
}
splat.m_IndexCount=(u32)m_Indices.size()-splat.m_IndexStart;
}
// build indices for the shadow map pass
for (int j=0;j<PATCH_SIZE;j++) {
for (int i=0;i<PATCH_SIZE;i++) {
m_ShadowMapIndices.push_back(((j+0)*vsize+(i+0))+base);
m_ShadowMapIndices.push_back(((j+0)*vsize+(i+1))+base);
m_ShadowMapIndices.push_back(((j+1)*vsize+(i+1))+base);
m_ShadowMapIndices.push_back(((j+1)*vsize+(i+0))+base);
}
}
}
void CPatchRData::BuildVertices()
{
// create both vertices and lighting colors
CVector3D normal;
// number of vertices in each direction in each patch
int vsize=PATCH_SIZE+1;
if (!m_Vertices) {
m_Vertices=new SBaseVertex[vsize*vsize];
}
SBaseVertex* vertices=m_Vertices;
// get index of this patch
u32 px=m_Patch->m_X;
u32 pz=m_Patch->m_Z;
CTerrain* terrain=m_Patch->m_Parent;
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
// build vertices
for (int j=0;j<vsize;j++) {
for (int i=0;i<vsize;i++) {
int ix=px*PATCH_SIZE+i;
int iz=pz*PATCH_SIZE+j;
int v=(j*vsize)+i;
// calculate vertex data
terrain->CalcPosition(ix,iz,vertices[v].m_Position);
*(uint32_t*)&vertices[v].m_LOSColor = 0; // will be set to the proper value in Update()
CalculateUV(vertices[v].m_UVs, ix, iz);
// Calculate diffuse lighting for this vertex
// Ambient is added by the lighting pass (since ambient is the same
// for all vertices, it need not be stored in the vertex structure)
terrain->CalcNormal(ix,iz,normal);
RGBColor diffuse;
lightEnv.EvaluateDirect(normal, diffuse);
*(u32*)&vertices[v].m_DiffuseColor = ConvertRGBColorTo4ub(diffuse);
}
}
// 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);
}
void CPatchRData::Build()
{
BuildVertices();
BuildIndices();
BuildBlends();
}
void CPatchRData::Update()
{
if (m_UpdateFlags!=0) {
// TODO,RC 11/04/04 - need to only rebuild necessary bits of renderdata rather
// than everything; it's complicated slightly because the blends are dependent
// on both vertex and index data
BuildVertices();
BuildIndices();
BuildBlends();
m_UpdateFlags=0;
}
// Update vertex colors, which are affected by LOS
u32 px=m_Patch->m_X;
u32 pz=m_Patch->m_Z;
CTerrain* terrain=m_Patch->m_Parent;
int mapSize=terrain->GetVerticesPerSide();
int vsize=PATCH_SIZE+1;
if (g_Game)
{
CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
// this is very similar to BuildVertices(), but just for color
for (int j=0;j<vsize;j++) {
for (int i=0;i<vsize;i++) {
int ix=px*PATCH_SIZE+i;
int iz=pz*PATCH_SIZE+j;
int v=(j*vsize)+i;
const int DX[] = {1,1,0,0};
const int DZ[] = {0,1,1,0};
SColor4ub losMod(255, 255, 255, 255);
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.R > 178)
losMod = SColor4ub(178, 178, 178, 255);
else if(s==LOS_UNEXPLORED && losMod.R > 0)
losMod = SColor4ub(0, 0, 0, 255);
}
}
m_Vertices[v].m_LOSColor = losMod;
}
}
}
else
{
for (int j = 0; j < vsize; ++j)
{
for (int i = 0; i < vsize; ++i)
{
int v = (j*vsize)+i;
m_Vertices[v].m_LOSColor = SColor4ub(255, 255, 255, 255);
}
}
}
// upload base vertices into their vertex buffer
m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase,m_Vertices);
// update blend colors by copying them from vertex colors
for(uint i=0; i<m_BlendVertices.size(); i++)
{
m_BlendVertices[i].m_LOSColor = m_Vertices[m_BlendVertexIndices[i]].m_LOSColor;
}
// upload blend vertices into their vertex buffer too
if(m_BlendVertices.size())
{
m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends,&m_BlendVertices[0]);
}
}
void CPatchRData::RenderBase(bool losColor)
{
debug_assert(m_UpdateFlags==0);
SBaseVertex *base=(SBaseVertex *)m_VBBase->m_Owner->Bind();
// setup data pointers
u32 stride=sizeof(SBaseVertex);
glVertexPointer(3,GL_FLOAT,stride,&base->m_Position[0]);
glColorPointer(4,GL_UNSIGNED_BYTE,stride,losColor ? &base->m_LOSColor : &base->m_DiffuseColor);
glTexCoordPointer(2,GL_FLOAT,stride,&base->m_UVs[0]);
// render each splat
for (uint i=0;i<(uint)m_Splats.size();i++) {
SSplat& splat=m_Splats[i];
ogl_tex_bind(splat.m_Texture);
glDrawElements(GL_QUADS,splat.m_IndexCount,GL_UNSIGNED_SHORT,&m_Indices[splat.m_IndexStart]);
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris+=splat.m_IndexCount/2;
}
}
void CPatchRData::RenderStreams(u32 streamflags, bool losColor)
{
debug_assert(m_UpdateFlags==0);
SBaseVertex* base=(SBaseVertex *)m_VBBase->m_Owner->Bind();
// setup data pointers
u32 stride=sizeof(SBaseVertex);
glVertexPointer(3, GL_FLOAT, stride, &base->m_Position);
if (streamflags & STREAM_UV0) {
glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs);
} else if (streamflags & STREAM_POSTOUV0) {
glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
}
if (streamflags & STREAM_COLOR)
{
glColorPointer(4,GL_UNSIGNED_BYTE,stride,losColor ? &base->m_LOSColor : &base->m_DiffuseColor);
}
// render all base splats at once
glDrawElements(GL_QUADS,(GLsizei)m_Indices.size(),GL_UNSIGNED_SHORT,&m_Indices[0]);
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris+=(u32)m_Indices.size()/2;
}
void CPatchRData::RenderBlends()
{
debug_assert(m_UpdateFlags==0);
if (m_BlendVertices.size()==0) return;
u8* base=m_VBBlends->m_Owner->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_LOSColor));
pglClientActiveTextureARB(GL_TEXTURE0);
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_UVs[0]));
pglClientActiveTextureARB(GL_TEXTURE1);
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_AlphaUVs[0]));
for (uint i=0;i<(uint)m_BlendSplats.size();i++) {
SSplat& splat=m_BlendSplats[i];
ogl_tex_bind(splat.m_Texture);
glDrawElements(GL_QUADS,splat.m_IndexCount,GL_UNSIGNED_SHORT,&m_BlendIndices[splat.m_IndexStart]);
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_BlendSplats++;
g_Renderer.m_Stats.m_TerrainTris+=splat.m_IndexCount/2;
}
}
void CPatchRData::RenderOutline()
{
int i;
uint vsize=PATCH_SIZE+1;
glBegin(GL_LINES);
for (i=0;i<PATCH_SIZE;i++) {
glVertex3fv(&m_Vertices[i].m_Position.X);
glVertex3fv(&m_Vertices[i+1].m_Position.X);
}
glEnd();
glBegin(GL_LINES);
for (i=0;i<PATCH_SIZE;i++) {
glVertex3fv(&m_Vertices[PATCH_SIZE+(i*(PATCH_SIZE+1))].m_Position.X);
glVertex3fv(&m_Vertices[PATCH_SIZE+((i+1)*(PATCH_SIZE+1))].m_Position.X);
}
glEnd();
glBegin(GL_LINES);
for (i=1;i<PATCH_SIZE;i++) {
glVertex3fv(&m_Vertices[(vsize*vsize)-i].m_Position.X);
glVertex3fv(&m_Vertices[(vsize*vsize)-(i+1)].m_Position.X);
}
glEnd();
glBegin(GL_LINES);
for (i=1;i<PATCH_SIZE;i++) {
glVertex3fv(&m_Vertices[(vsize*(vsize-1))-(i*vsize)].m_Position.X);
glVertex3fv(&m_Vertices[(vsize*(vsize-1))-((i+1)*vsize)].m_Position.X);
}
glEnd();
}