# Fix terrain blending logic.
Add terrain priority rendering mode in Atlas. Fix terrain painting in Atlas to be less profligate with priority values. Support hotloading of blend textures. Clean up some trivial things. This was SVN commit r8872.
This commit is contained in:
parent
d0bc4253c9
commit
03c75100ab
@ -269,7 +269,15 @@ function init(window, bottomWindow)
|
|||||||
Atlas.Message.SetViewParamS(Atlas.RenderView.GAME, "passability", evt.string);
|
Atlas.Message.SetViewParamS(Atlas.RenderView.GAME, "passability", evt.string);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
visualiseSettingsSizer.add(new wxStaticText(window, -1, 'Priorities'), 0, wxAlignment.RIGHT);
|
||||||
|
var passabilityClasses = Atlas.Message.GetTerrainPassabilityClasses().classnames;
|
||||||
|
var priorityCheckbox = new wxCheckBox(window, -1, "");
|
||||||
|
visualiseSettingsSizer.add(priorityCheckbox);
|
||||||
|
priorityCheckbox.onCheckBox = function (evt) {
|
||||||
|
Atlas.Message.SetViewParamB(Atlas.RenderView.GAME, "priorities", evt.checked);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
var terrainGroups = Atlas.Message.GetTerrainGroups();
|
var terrainGroups = Atlas.Message.GetTerrainGroups();
|
||||||
var nb = new wxNotebook(bottomWindow, -1);
|
var nb = new wxNotebook(bottomWindow, -1);
|
||||||
bottomWindow.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
bottomWindow.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||||
|
@ -51,7 +51,7 @@ CTerrainTextureEntry::CTerrainTextureEntry(CTerrainPropertiesPtr props, const Vf
|
|||||||
for (;it!=m_Groups.end();++it)
|
for (;it!=m_Groups.end();++it)
|
||||||
(*it)->AddTerrain(this);
|
(*it)->AddTerrain(this);
|
||||||
|
|
||||||
m_Tag = fs::basename(path);
|
m_Tag = CStr(CStrW(fs::basename(path)));
|
||||||
}
|
}
|
||||||
|
|
||||||
CTerrainTextureEntry::~CTerrainTextureEntry()
|
CTerrainTextureEntry::~CTerrainTextureEntry()
|
||||||
|
@ -40,7 +40,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// Tag = file name stripped of path and extension (grass_dark_1)
|
// Tag = file name stripped of path and extension (grass_dark_1)
|
||||||
CStrW m_Tag;
|
CStr m_Tag;
|
||||||
|
|
||||||
// The property sheet used by this texture
|
// The property sheet used by this texture
|
||||||
CTerrainPropertiesPtr m_pProperties;
|
CTerrainPropertiesPtr m_pProperties;
|
||||||
|
@ -229,6 +229,8 @@ void Render()
|
|||||||
|
|
||||||
ogl_WarnIfError();
|
ogl_WarnIfError();
|
||||||
|
|
||||||
|
g_Renderer.RenderTextOverlays();
|
||||||
|
|
||||||
// Temp GUI message GeeTODO
|
// Temp GUI message GeeTODO
|
||||||
PROFILE_START("render gui");
|
PROFILE_START("render gui");
|
||||||
if(g_DoRenderGui) g_GUI->Draw();
|
if(g_DoRenderGui) g_GUI->Draw();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2010 Wildfire Games.
|
/* Copyright (C) 2011 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@ -17,25 +17,28 @@
|
|||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "ps/Pyrogenesis.h"
|
|
||||||
|
#include "graphics/GameView.h"
|
||||||
#include "graphics/LightEnv.h"
|
#include "graphics/LightEnv.h"
|
||||||
#include "Renderer.h"
|
|
||||||
#include "renderer/PatchRData.h"
|
|
||||||
#include "AlphaMapCalculator.h"
|
|
||||||
#include "ps/CLogger.h"
|
|
||||||
#include "ps/Profile.h"
|
|
||||||
#include "ps/Game.h"
|
|
||||||
#include "ps/World.h"
|
|
||||||
#include "maths/MathUtil.h"
|
|
||||||
#include "graphics/Patch.h"
|
#include "graphics/Patch.h"
|
||||||
#include "graphics/Terrain.h"
|
#include "graphics/Terrain.h"
|
||||||
|
#include "lib/res/graphics/unifont.h"
|
||||||
|
#include "maths/MathUtil.h"
|
||||||
|
#include "ps/CLogger.h"
|
||||||
|
#include "ps/Game.h"
|
||||||
|
#include "ps/Profile.h"
|
||||||
|
#include "ps/Pyrogenesis.h"
|
||||||
|
#include "ps/World.h"
|
||||||
|
#include "ps/GameSetup/Config.h"
|
||||||
|
#include "renderer/AlphaMapCalculator.h"
|
||||||
|
#include "renderer/PatchRData.h"
|
||||||
|
#include "renderer/Renderer.h"
|
||||||
#include "simulation2/Simulation2.h"
|
#include "simulation2/Simulation2.h"
|
||||||
#include "simulation2/components/ICmpRangeManager.h"
|
#include "simulation2/components/ICmpRangeManager.h"
|
||||||
|
|
||||||
const ssize_t BlendOffsets[8][2] = {
|
const ssize_t BlendOffsets[9][2] = {
|
||||||
{ 0, -1 },
|
{ 0, -1 },
|
||||||
{ -1, -1 },
|
{ -1, -1 },
|
||||||
{ -1, 0 },
|
{ -1, 0 },
|
||||||
@ -43,13 +46,10 @@ const ssize_t BlendOffsets[8][2] = {
|
|||||||
{ 0, 1 },
|
{ 0, 1 },
|
||||||
{ 1, 1 },
|
{ 1, 1 },
|
||||||
{ 1, 0 },
|
{ 1, 0 },
|
||||||
{ 1, -1 }
|
{ 1, -1 },
|
||||||
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////
|
||||||
// CPatchRData constructor
|
// CPatchRData constructor
|
||||||
CPatchRData::CPatchRData(CPatch* patch) : m_Patch(patch), m_VBBase(0), m_VBBlends(0), m_Vertices(0)
|
CPatchRData::CPatchRData(CPatch* patch) : m_Patch(patch), m_VBBase(0), m_VBBlends(0), m_Vertices(0)
|
||||||
@ -69,15 +69,6 @@ CPatchRData::~CPatchRData()
|
|||||||
if (m_VBBlends) g_VBMan.Release(m_VBBlends);
|
if (m_VBBlends) g_VBMan.Release(m_VBBlends);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static CTerrainTextureEntry* GetTerrainTileTexture(CTerrain* terrain, ssize_t gx, ssize_t gz)
|
|
||||||
{
|
|
||||||
CMiniPatch* mp = terrain->GetTile(gx, gz);
|
|
||||||
if (!mp)
|
|
||||||
return 0;
|
|
||||||
return mp->GetTextureEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
const float uvFactor = 0.125f / sqrt(2.f);
|
const float uvFactor = 0.125f / sqrt(2.f);
|
||||||
static void CalculateUV(float uv[2], ssize_t x, ssize_t z)
|
static void CalculateUV(float uv[2], ssize_t x, ssize_t z)
|
||||||
{
|
{
|
||||||
@ -86,9 +77,60 @@ static void CalculateUV(float uv[2], ssize_t x, ssize_t z)
|
|||||||
uv[1] = (-x-z)*uvFactor;
|
uv[1] = (-x-z)*uvFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct STmpSplat {
|
/**
|
||||||
|
* Represents a blend for a single tile, texture and shape.
|
||||||
|
*/
|
||||||
|
struct STileBlend
|
||||||
|
{
|
||||||
CTerrainTextureEntry* m_Texture;
|
CTerrainTextureEntry* m_Texture;
|
||||||
u16 m_Indices[4];
|
int m_Priority;
|
||||||
|
u16 m_TileMask; // bit n set if this blend contains neighbour tile BlendOffsets[n]
|
||||||
|
|
||||||
|
struct DecreasingPriority
|
||||||
|
{
|
||||||
|
bool operator()(const STileBlend& a, const STileBlend& b) const
|
||||||
|
{
|
||||||
|
if (a.m_Priority > b.m_Priority)
|
||||||
|
return true;
|
||||||
|
if (a.m_Priority < b.m_Priority)
|
||||||
|
return false;
|
||||||
|
if (a.m_Texture && b.m_Texture)
|
||||||
|
return a.m_Texture->GetTag() > b.m_Texture->GetTag();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CurrentTile
|
||||||
|
{
|
||||||
|
bool operator()(const STileBlend& a) const
|
||||||
|
{
|
||||||
|
return a.m_TileMask & (1 << 8);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the ordered collection of blends drawn on a particular tile.
|
||||||
|
*/
|
||||||
|
struct STileBlendStack
|
||||||
|
{
|
||||||
|
u8 i, j;
|
||||||
|
std::vector<STileBlend> blends; // back of vector is lowest-priority texture
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a batched collection of blends using the same texture.
|
||||||
|
*/
|
||||||
|
struct SBlendLayer
|
||||||
|
{
|
||||||
|
struct Tile
|
||||||
|
{
|
||||||
|
u8 i, j;
|
||||||
|
u8 shape;
|
||||||
|
};
|
||||||
|
|
||||||
|
CTerrainTextureEntry* m_Texture;
|
||||||
|
std::vector<Tile> m_Tiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
void CPatchRData::BuildBlends()
|
void CPatchRData::BuildBlends()
|
||||||
@ -98,200 +140,260 @@ void CPatchRData::BuildBlends()
|
|||||||
m_BlendVertices.clear();
|
m_BlendVertices.clear();
|
||||||
m_BlendVertexIndices.clear();
|
m_BlendVertexIndices.clear();
|
||||||
|
|
||||||
CTerrain* terrain=m_Patch->m_Parent;
|
CTerrain* terrain = m_Patch->m_Parent;
|
||||||
|
|
||||||
// temporary list of splats
|
std::vector<STileBlendStack> blendStacks;
|
||||||
std::vector<STmpSplat> splats;
|
blendStacks.reserve(PATCH_SIZE*PATCH_SIZE);
|
||||||
// set of textures used for splats
|
|
||||||
std::set<CTerrainTextureEntry*> splatTextures;
|
|
||||||
|
|
||||||
// for each tile in patch ..
|
// For each tile in patch ..
|
||||||
for (ssize_t j=0;j<PATCH_SIZE;j++) {
|
for (ssize_t j = 0; j < PATCH_SIZE; ++j)
|
||||||
for (ssize_t i=0;i<PATCH_SIZE;i++) {
|
{
|
||||||
CMiniPatch* mp=&m_Patch->m_MiniPatches[j][i];
|
for (ssize_t i = 0; i < PATCH_SIZE; ++i)
|
||||||
|
{
|
||||||
ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
|
ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
|
||||||
ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
|
ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
|
||||||
|
|
||||||
// build list of textures of higher priority than current tile that are used by neighbouring tiles
|
std::vector<STileBlend> blends;
|
||||||
std::vector<STex> neighbourTextures;
|
blends.reserve(9);
|
||||||
for (int m=-1;m<=1;m++) {
|
|
||||||
for (int k=-1;k<=1;k++) {
|
// Compute a blend for every tile in the 3x3 square around this tile
|
||||||
CMiniPatch* nmp=terrain->GetTile(gx+k,gz+m);
|
for (size_t n = 0; n < 9; ++n)
|
||||||
if (nmp && nmp->GetTextureEntry() != mp->GetTextureEntry()) {
|
{
|
||||||
if (nmp->GetPriority() > mp->GetPriority() || (nmp->GetPriority() == mp->GetPriority() && nmp->GetTextureEntry() > mp->GetTextureEntry())) {
|
ssize_t ox = gx + BlendOffsets[n][1];
|
||||||
STex tex;
|
ssize_t oz = gz + BlendOffsets[n][0];
|
||||||
tex.m_Texture=nmp->GetTextureEntry();
|
|
||||||
tex.m_Priority=nmp->GetPriority();
|
CMiniPatch* nmp = terrain->GetTile(ox, oz);
|
||||||
if (std::find(neighbourTextures.begin(),neighbourTextures.end(),tex)==neighbourTextures.end()) {
|
if (!nmp)
|
||||||
neighbourTextures.push_back(tex);
|
continue;
|
||||||
}
|
|
||||||
}
|
STileBlend blend;
|
||||||
}
|
blend.m_Texture = nmp->GetTextureEntry();
|
||||||
}
|
blend.m_Priority = nmp->GetPriority();
|
||||||
|
blend.m_TileMask = 1 << n;
|
||||||
|
blends.push_back(blend);
|
||||||
}
|
}
|
||||||
if (neighbourTextures.size()>0) {
|
|
||||||
// sort textures from lowest to highest priority
|
|
||||||
std::sort(neighbourTextures.begin(),neighbourTextures.end());
|
|
||||||
|
|
||||||
// for each of the neighbouring textures ..
|
// Sort the blends, highest priority first
|
||||||
size_t count=neighbourTextures.size();
|
std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority());
|
||||||
for (size_t k=0;k<count;++k) {
|
|
||||||
|
|
||||||
// now build the grid of blends dependent on whether the tile adjacent to the current tile
|
STileBlendStack blendStack;
|
||||||
// uses the current neighbour texture
|
blendStack.i = i;
|
||||||
BlendShape8 shape;
|
blendStack.j = j;
|
||||||
for (size_t m=0;m<8;m++) {
|
|
||||||
ssize_t ox=gx+BlendOffsets[m][1];
|
|
||||||
ssize_t oz=gz+BlendOffsets[m][0];
|
|
||||||
|
|
||||||
// get texture on adjacent tile
|
// Put the blends into the tile's stack, merging any adjacent blends with the same texture
|
||||||
CTerrainTextureEntry* atex=GetTerrainTileTexture(terrain,ox,oz);
|
for (size_t k = 0; k < blends.size(); ++k)
|
||||||
// fill 0/1 into shape array
|
{
|
||||||
shape[m]=(atex==neighbourTextures[k].m_Texture) ? 0 : 1;
|
if (!blendStack.blends.empty() && blendStack.blends.back().m_Texture == blends[k].m_Texture)
|
||||||
}
|
blendStack.blends.back().m_TileMask |= blends[k].m_TileMask;
|
||||||
|
else
|
||||||
|
blendStack.blends.push_back(blends[k]);
|
||||||
|
}
|
||||||
|
|
||||||
// calculate the required alphamap and the required rotation of the alphamap from blendshape
|
// Remove blends that are after (i.e. lower priority than) the current tile
|
||||||
unsigned int alphamapflags;
|
// (including the current tile), since we don't want to render them on top of
|
||||||
int alphamap=CAlphaMapCalculator::Calculate(shape,alphamapflags);
|
// the tile's base texture
|
||||||
|
blendStack.blends.erase(
|
||||||
|
std::find_if(blendStack.blends.begin(), blendStack.blends.end(), STileBlend::CurrentTile()),
|
||||||
|
blendStack.blends.end());
|
||||||
|
|
||||||
// now actually render the blend tile (if we need one)
|
blendStacks.push_back(blendStack);
|
||||||
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) {
|
// Given the blend stack per tile, we want to batch together as many blends as possible.
|
||||||
// flip v
|
// Group them into a series of layers (each of which has a single texture):
|
||||||
float t=v0;
|
|
||||||
v0=v1;
|
|
||||||
v1=t;
|
|
||||||
}
|
|
||||||
|
|
||||||
int base=0;
|
std::vector<SBlendLayer> blendLayers;
|
||||||
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];
|
while (true)
|
||||||
vtx[(base+0)%4].m_AlphaUVs[0]=u0;
|
{
|
||||||
vtx[(base+0)%4].m_AlphaUVs[1]=v0;
|
if (!blendLayers.empty())
|
||||||
vtx[(base+1)%4].m_AlphaUVs[0]=u1;
|
{
|
||||||
vtx[(base+1)%4].m_AlphaUVs[1]=v0;
|
// Try to grab as many tiles as possible that match our current layer,
|
||||||
vtx[(base+2)%4].m_AlphaUVs[0]=u1;
|
// from off the blend stacks of all the tiles
|
||||||
vtx[(base+2)%4].m_AlphaUVs[1]=v1;
|
|
||||||
vtx[(base+3)%4].m_AlphaUVs[0]=u0;
|
|
||||||
vtx[(base+3)%4].m_AlphaUVs[1]=v1;
|
|
||||||
|
|
||||||
ssize_t vsize=PATCH_SIZE+1;
|
CTerrainTextureEntry* tex = blendLayers.back().m_Texture;
|
||||||
|
|
||||||
SBlendVertex dst;
|
for (size_t k = 0; k < blendStacks.size(); ++k)
|
||||||
const size_t vindex=m_BlendVertices.size();
|
{
|
||||||
|
if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex)
|
||||||
const SBaseVertex& vtx0=m_Vertices[(j*vsize)+i];
|
{
|
||||||
CalculateUV(dst.m_UVs, gx, gz);
|
SBlendLayer::Tile t = { blendStacks[k].i, blendStacks[k].j, blendStacks[k].blends.back().m_TileMask };
|
||||||
dst.m_AlphaUVs[0]=vtx[0].m_AlphaUVs[0];
|
blendLayers.back().m_Tiles.push_back(t);
|
||||||
dst.m_AlphaUVs[1]=vtx[0].m_AlphaUVs[1];
|
blendStacks[k].blends.pop_back();
|
||||||
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_Texture;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// (We've already merged adjacent entries of the same texture in each stack,
|
||||||
|
// so we don't need to bother looping to check the next entry in this stack again)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've grabbed as many tiles as possible; now we need to start a new layer.
|
||||||
|
// The new layer's texture could come from the back of any non-empty stack;
|
||||||
|
// choose the longest stack as a heuristic to reduce the number of layers
|
||||||
|
CTerrainTextureEntry* bestTex = NULL;
|
||||||
|
size_t bestStackSize = 0;
|
||||||
|
|
||||||
|
for (size_t k = 0; k < blendStacks.size(); ++k)
|
||||||
|
{
|
||||||
|
if (blendStacks[k].blends.size() > bestStackSize)
|
||||||
|
{
|
||||||
|
bestStackSize = blendStacks[k].blends.size();
|
||||||
|
bestTex = blendStacks[k].blends.back().m_Texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all our stacks were empty, we're done
|
||||||
|
if (bestStackSize == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Otherwise add the new layer, then loop back and start filling it in
|
||||||
|
|
||||||
|
SBlendLayer layer;
|
||||||
|
layer.m_Texture = bestTex;
|
||||||
|
blendLayers.push_back(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now build outgoing splats
|
||||||
|
m_BlendSplats.resize(blendLayers.size());
|
||||||
|
|
||||||
|
for (size_t k = 0; k < blendLayers.size(); ++k)
|
||||||
|
{
|
||||||
|
SSplat& splat = m_BlendSplats[k];
|
||||||
|
splat.m_IndexStart = m_BlendIndices.size();
|
||||||
|
splat.m_Texture = blendLayers[k].m_Texture;
|
||||||
|
|
||||||
|
for (size_t t = 0; t < blendLayers[k].m_Tiles.size(); ++t)
|
||||||
|
{
|
||||||
|
SBlendLayer::Tile& tile = blendLayers[k].m_Tiles[t];
|
||||||
|
|
||||||
|
ssize_t index = AddBlend(tile.i, tile.j, tile.shape);
|
||||||
|
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
// (These indices will get incremented by the VB base offset later)
|
||||||
|
m_BlendIndices.push_back(index + 0);
|
||||||
|
m_BlendIndices.push_back(index + 1);
|
||||||
|
m_BlendIndices.push_back(index + 2);
|
||||||
|
m_BlendIndices.push_back(index + 3);
|
||||||
|
splat.m_IndexCount += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// build vertex data
|
// Release existing vertex buffer chunk
|
||||||
if (m_VBBlends) {
|
if (m_VBBlends)
|
||||||
// release existing vertex buffer chunk
|
{
|
||||||
g_VBMan.Release(m_VBBlends);
|
g_VBMan.Release(m_VBBlends);
|
||||||
m_VBBlends=0;
|
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
|
if (m_BlendVertices.size())
|
||||||
m_BlendSplats.resize(splatTextures.size());
|
{
|
||||||
size_t splatCount=0;
|
// Construct vertex buffer
|
||||||
|
|
||||||
|
m_VBBlends = g_VBMan.Allocate(sizeof(SBlendVertex), m_BlendVertices.size(), true);
|
||||||
|
m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends, &m_BlendVertices[0]);
|
||||||
|
|
||||||
debug_assert(m_VBBlends->m_Index < 65536);
|
debug_assert(m_VBBlends->m_Index < 65536);
|
||||||
unsigned short base = (unsigned short)m_VBBlends->m_Index;
|
unsigned short base = (unsigned short)m_VBBlends->m_Index;
|
||||||
std::set<CTerrainTextureEntry*>::iterator iter=splatTextures.begin();
|
|
||||||
for (;iter!=splatTextures.end();++iter) {
|
|
||||||
CTerrainTextureEntry* tex=*iter;
|
|
||||||
|
|
||||||
SSplat& splat=m_BlendSplats[splatCount];
|
// Update the indices to include the base offset
|
||||||
splat.m_IndexStart=m_BlendIndices.size();
|
for (size_t k = 0; k < m_BlendIndices.size(); ++k)
|
||||||
splat.m_Texture=tex;
|
m_BlendIndices[k] += base;
|
||||||
|
|
||||||
for (size_t k=0;k<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++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssize_t CPatchRData::AddBlend(u16 i, u16 j, u8 shape)
|
||||||
|
{
|
||||||
|
ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
|
||||||
|
ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
|
||||||
|
|
||||||
|
// uses the current neighbour texture
|
||||||
|
BlendShape8 shape8;
|
||||||
|
for (size_t m = 0; m < 8; ++m)
|
||||||
|
shape8[m] = (shape & (1 << m)) ? 0 : 1;
|
||||||
|
|
||||||
|
// calculate the required alphamap and the required rotation of the alphamap from blendshape
|
||||||
|
unsigned int alphamapflags;
|
||||||
|
int alphamap = CAlphaMapCalculator::Calculate(shape8, alphamapflags);
|
||||||
|
|
||||||
|
// now actually render the blend tile (if we need one)
|
||||||
|
if (alphamap == -1)
|
||||||
|
return -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)
|
||||||
|
std::swap(u0, u1);
|
||||||
|
|
||||||
|
if (alphamapflags & BLENDMAP_FLIPV)
|
||||||
|
std::swap(v0, v1);
|
||||||
|
|
||||||
|
int base = 0;
|
||||||
|
if (alphamapflags & BLENDMAP_ROTATE90)
|
||||||
|
base = 1;
|
||||||
|
else if (alphamapflags & BLENDMAP_ROTATE180)
|
||||||
|
base = 2;
|
||||||
|
else if (alphamapflags & BLENDMAP_ROTATE270)
|
||||||
|
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;
|
||||||
|
|
||||||
|
ssize_t vsize = PATCH_SIZE + 1;
|
||||||
|
|
||||||
|
SBlendVertex dst;
|
||||||
|
const size_t vindex = 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);
|
||||||
|
|
||||||
|
return vindex;
|
||||||
|
}
|
||||||
|
|
||||||
void CPatchRData::BuildIndices()
|
void CPatchRData::BuildIndices()
|
||||||
{
|
{
|
||||||
// must have allocated some vertices before trying to build corresponding indices
|
// must have allocated some vertices before trying to build corresponding indices
|
||||||
@ -559,7 +661,8 @@ void CPatchRData::RenderBlends()
|
|||||||
{
|
{
|
||||||
debug_assert(m_UpdateFlags==0);
|
debug_assert(m_UpdateFlags==0);
|
||||||
|
|
||||||
if (m_BlendVertices.size()==0) return;
|
if (m_BlendVertices.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
u8* base=m_VBBlends->m_Owner->Bind();
|
u8* base=m_VBBlends->m_Owner->Bind();
|
||||||
|
|
||||||
@ -629,3 +732,38 @@ void CPatchRData::RenderOutline()
|
|||||||
}
|
}
|
||||||
glEnd();
|
glEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPatchRData::RenderPriorities()
|
||||||
|
{
|
||||||
|
CTerrain* terrain = m_Patch->m_Parent;
|
||||||
|
CCamera* camera = g_Game->GetView()->GetCamera();
|
||||||
|
|
||||||
|
for (ssize_t j = 0; j < PATCH_SIZE; ++j)
|
||||||
|
{
|
||||||
|
for (ssize_t i = 0; i < PATCH_SIZE; ++i)
|
||||||
|
{
|
||||||
|
ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
|
||||||
|
ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
|
||||||
|
|
||||||
|
CVector3D pos;
|
||||||
|
terrain->CalcPosition(gx, gz, pos);
|
||||||
|
|
||||||
|
// Move a bit towards the center of the tile
|
||||||
|
pos.X += CELL_SIZE/4.f;
|
||||||
|
pos.Z += CELL_SIZE/4.f;
|
||||||
|
|
||||||
|
float x, y;
|
||||||
|
camera->GetScreenCoordinates(pos, x, y);
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(x, g_yres - y, 0.f);
|
||||||
|
|
||||||
|
// Draw the text upside-down, because it's aligned with
|
||||||
|
// the GUI (which uses the top-left as (0,0))
|
||||||
|
glScalef(1.0f, -1.0f, 1.0f);
|
||||||
|
|
||||||
|
glwprintf(L"%d", m_Patch->m_MiniPatches[j][i].Priority);
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -41,6 +41,7 @@ public:
|
|||||||
void RenderBlends();
|
void RenderBlends();
|
||||||
void RenderOutline();
|
void RenderOutline();
|
||||||
void RenderStreams(int streamflags, bool losColor);
|
void RenderStreams(int streamflags, bool losColor);
|
||||||
|
void RenderPriorities();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct SSplat {
|
struct SSplat {
|
||||||
@ -86,6 +87,8 @@ private:
|
|||||||
// build this renderdata object
|
// build this renderdata object
|
||||||
void Build();
|
void Build();
|
||||||
|
|
||||||
|
ssize_t AddBlend(u16 i, u16 j, u8 shape);
|
||||||
|
|
||||||
void BuildBlends();
|
void BuildBlends();
|
||||||
void BuildIndices();
|
void BuildIndices();
|
||||||
void BuildVertices();
|
void BuildVertices();
|
||||||
|
@ -25,9 +25,13 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
|
#include "Renderer.h"
|
||||||
|
|
||||||
#include "lib/bits.h" // is_pow2
|
#include "lib/bits.h" // is_pow2
|
||||||
#include "lib/res/graphics/ogl_tex.h"
|
#include "lib/res/graphics/ogl_tex.h"
|
||||||
#include "Renderer.h"
|
|
||||||
#include "maths/Matrix3D.h"
|
#include "maths/Matrix3D.h"
|
||||||
#include "maths/MathUtil.h"
|
#include "maths/MathUtil.h"
|
||||||
#include "ps/CLogger.h"
|
#include "ps/CLogger.h"
|
||||||
@ -358,6 +362,7 @@ CRenderer::CRenderer()
|
|||||||
m_SortAllTransparent = false;
|
m_SortAllTransparent = false;
|
||||||
m_DisplayFrustum = false;
|
m_DisplayFrustum = false;
|
||||||
m_DisableCopyShadow = false;
|
m_DisableCopyShadow = false;
|
||||||
|
m_DisplayTerrainPriorities = false;
|
||||||
m_FastPlayerColor = true;
|
m_FastPlayerColor = true;
|
||||||
m_SkipSubmit = false;
|
m_SkipSubmit = false;
|
||||||
m_RenderTerritories = true;
|
m_RenderTerritories = true;
|
||||||
@ -387,12 +392,16 @@ CRenderer::CRenderer()
|
|||||||
AddLocalProperty(L"waterShininess", &m->waterManager.m_Shininess, false);
|
AddLocalProperty(L"waterShininess", &m->waterManager.m_Shininess, false);
|
||||||
AddLocalProperty(L"waterSpecularStrength", &m->waterManager.m_SpecularStrength, false);
|
AddLocalProperty(L"waterSpecularStrength", &m->waterManager.m_SpecularStrength, false);
|
||||||
AddLocalProperty(L"waterWaviness", &m->waterManager.m_Waviness, false);
|
AddLocalProperty(L"waterWaviness", &m->waterManager.m_Waviness, false);
|
||||||
|
|
||||||
|
RegisterFileReloadFunc(ReloadChangedFileCB, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
// CRenderer destructor
|
// CRenderer destructor
|
||||||
CRenderer::~CRenderer()
|
CRenderer::~CRenderer()
|
||||||
{
|
{
|
||||||
|
UnregisterFileReloadFunc(ReloadChangedFileCB, this);
|
||||||
|
|
||||||
// model rendering
|
// model rendering
|
||||||
for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType)
|
for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType)
|
||||||
{
|
{
|
||||||
@ -1302,7 +1311,12 @@ void CRenderer::RenderSubmissions()
|
|||||||
m->overlayRenderer.RenderForegroundOverlays(m_ViewCamera);
|
m->overlayRenderer.RenderForegroundOverlays(m_ViewCamera);
|
||||||
PROFILE_END("render fg overlays");
|
PROFILE_END("render fg overlays");
|
||||||
ogl_WarnIfError();
|
ogl_WarnIfError();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// EndFrame: signal frame end
|
||||||
|
void CRenderer::EndFrame()
|
||||||
|
{
|
||||||
// empty lists
|
// empty lists
|
||||||
m->terrainRenderer->EndFrame();
|
m->terrainRenderer->EndFrame();
|
||||||
m->overlayRenderer.EndFrame();
|
m->overlayRenderer.EndFrame();
|
||||||
@ -1315,18 +1329,12 @@ void CRenderer::RenderSubmissions()
|
|||||||
if (m->Model.Player != m->Model.PlayerInstancing)
|
if (m->Model.Player != m->Model.PlayerInstancing)
|
||||||
m->Model.PlayerInstancing->EndFrame();
|
m->Model.PlayerInstancing->EndFrame();
|
||||||
m->Model.Transp->EndFrame();
|
m->Model.Transp->EndFrame();
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// EndFrame: signal frame end
|
|
||||||
void CRenderer::EndFrame()
|
|
||||||
{
|
|
||||||
ogl_tex_bind(0, 0);
|
ogl_tex_bind(0, 0);
|
||||||
|
|
||||||
static bool once=false;
|
if (glGetError())
|
||||||
if (!once && glGetError()) {
|
{
|
||||||
LOGERROR(L"CRenderer::EndFrame: GL errors occurred");
|
ONCE(LOGERROR(L"CRenderer::EndFrame: GL errors occurred"));
|
||||||
once=true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1355,6 +1363,18 @@ void CRenderer::DisplayFrustum()
|
|||||||
glDepthMask(1);
|
glDepthMask(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Text overlay rendering
|
||||||
|
void CRenderer::RenderTextOverlays()
|
||||||
|
{
|
||||||
|
PROFILE("render text overlays");
|
||||||
|
|
||||||
|
if (m_DisplayTerrainPriorities)
|
||||||
|
m->terrainRenderer->RenderPriorities();
|
||||||
|
|
||||||
|
ogl_WarnIfError();
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// SetSceneCamera: setup projection and transform of camera and adjust viewport to current view
|
// SetSceneCamera: setup projection and transform of camera and adjust viewport to current view
|
||||||
// The camera always represents the actual camera used to render a scene, not any virtual camera
|
// The camera always represents the actual camera used to render a scene, not any virtual camera
|
||||||
@ -1479,22 +1499,22 @@ int CRenderer::LoadAlphaMaps()
|
|||||||
// load all textures and store Handle in array
|
// load all textures and store Handle in array
|
||||||
//
|
//
|
||||||
Handle textures[NumAlphaMaps] = {0};
|
Handle textures[NumAlphaMaps] = {0};
|
||||||
VfsPath path(L"art/textures/terrain/alphamaps/special");
|
VfsPath path(L"art/textures/terrain/alphamaps/standard");
|
||||||
const wchar_t* fnames[NumAlphaMaps] = {
|
const wchar_t* fnames[NumAlphaMaps] = {
|
||||||
L"blendcircle.dds",
|
L"blendcircle.png",
|
||||||
L"blendlshape.dds",
|
L"blendlshape.png",
|
||||||
L"blendedge.dds",
|
L"blendedge.png",
|
||||||
L"blendedgecorner.dds",
|
L"blendedgecorner.png",
|
||||||
L"blendedgetwocorners.dds",
|
L"blendedgetwocorners.png",
|
||||||
L"blendfourcorners.dds",
|
L"blendfourcorners.png",
|
||||||
L"blendtwooppositecorners.dds",
|
L"blendtwooppositecorners.png",
|
||||||
L"blendlshapecorner.dds",
|
L"blendlshapecorner.png",
|
||||||
L"blendtwocorners.dds",
|
L"blendtwocorners.png",
|
||||||
L"blendcorner.dds",
|
L"blendcorner.png",
|
||||||
L"blendtwoedges.dds",
|
L"blendtwoedges.png",
|
||||||
L"blendthreecorners.dds",
|
L"blendthreecorners.png",
|
||||||
L"blendushape.dds",
|
L"blendushape.png",
|
||||||
L"blendbad.dds"
|
L"blendbad.png"
|
||||||
};
|
};
|
||||||
size_t base = 0; // texture width/height (see below)
|
size_t base = 0; // texture width/height (see below)
|
||||||
// for convenience, we require all alpha maps to be of the same BPP
|
// for convenience, we require all alpha maps to be of the same BPP
|
||||||
@ -1507,13 +1527,6 @@ int CRenderer::LoadAlphaMaps()
|
|||||||
textures[i] = ogl_tex_load(g_VFS, path/fnames[i]);
|
textures[i] = ogl_tex_load(g_VFS, path/fnames[i]);
|
||||||
RETURN_ERR(textures[i]);
|
RETURN_ERR(textures[i]);
|
||||||
|
|
||||||
// quick hack: we require plain RGB(A) format, so convert to that.
|
|
||||||
// ideally the texture would be in uncompressed form; then this wouldn't
|
|
||||||
// be necessary.
|
|
||||||
size_t flags;
|
|
||||||
ogl_tex_get_format(textures[i], &flags, 0);
|
|
||||||
ogl_tex_transform_to(textures[i], flags & ~TEX_DXT);
|
|
||||||
|
|
||||||
// get its size and make sure they are all equal.
|
// get its size and make sure they are all equal.
|
||||||
// (the packing algo assumes this)
|
// (the packing algo assumes this)
|
||||||
size_t this_width = 0, this_bpp = 0; // fail-safe
|
size_t this_width = 0, this_bpp = 0; // fail-safe
|
||||||
@ -1602,6 +1615,23 @@ void CRenderer::UnloadAlphaMaps()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LibError CRenderer::ReloadChangedFileCB(void* param, const VfsPath& path)
|
||||||
|
{
|
||||||
|
CRenderer* renderer = static_cast<CRenderer*>(param);
|
||||||
|
|
||||||
|
// If an alpha map changed, and we already loaded them, then reload them
|
||||||
|
if (boost::algorithm::starts_with(path.string(), L"art/textures/terrain/alphamaps/"))
|
||||||
|
{
|
||||||
|
if (renderer->m_hCompositeAlphaMap)
|
||||||
|
{
|
||||||
|
renderer->UnloadAlphaMaps();
|
||||||
|
renderer->LoadAlphaMaps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return INFO::OK;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Scripting Interface
|
// Scripting Interface
|
||||||
|
|
||||||
|
@ -216,20 +216,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
void RenderScene(Scene* scene);
|
void RenderScene(Scene* scene);
|
||||||
|
|
||||||
// basic primitive rendering operations in 2 and 3D; handy for debugging stuff, but also useful in
|
/**
|
||||||
// editor tools (eg for highlighting specific terrain patches)
|
* Render text overlays on top of the scene.
|
||||||
// note:
|
* Assumes the caller has set up the GL environment for orthographic rendering
|
||||||
// * all 3D vertices specified in world space
|
* with texturing and blending.
|
||||||
// * primitive operations rendered immediatedly, never batched
|
*/
|
||||||
// * primitives rendered in current material (set via SetMaterial)
|
void RenderTextOverlays();
|
||||||
// void RenderLine(const SVertex2D* vertices);
|
|
||||||
// void RenderLineLoop(int len,const SVertex2D* vertices);
|
|
||||||
// void RenderTri(const SVertex2D* vertices);
|
|
||||||
// void RenderQuad(const SVertex2D* vertices);
|
|
||||||
// void RenderLine(const SVertex3D* vertices);
|
|
||||||
// void RenderLineLoop(int len,const SVertex3D* vertices);
|
|
||||||
// void RenderTri(const SVertex3D* vertices);
|
|
||||||
// void RenderQuad(const SVertex3D* vertices);
|
|
||||||
|
|
||||||
// set the current lighting environment; (note: the passed pointer is just copied to a variable within the renderer,
|
// set the current lighting environment; (note: the passed pointer is just copied to a variable within the renderer,
|
||||||
// so the lightenv passed must be scoped such that it is not destructed until after the renderer is no longer rendering)
|
// so the lightenv passed must be scoped such that it is not destructed until after the renderer is no longer rendering)
|
||||||
@ -247,6 +239,9 @@ public:
|
|||||||
// get the mode to render subsequent models
|
// get the mode to render subsequent models
|
||||||
ERenderMode GetModelRenderMode() const { return m_ModelRenderMode; }
|
ERenderMode GetModelRenderMode() const { return m_ModelRenderMode; }
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
void SetDisplayTerrainPriorities(bool enabled) { m_DisplayTerrainPriorities = enabled; }
|
||||||
|
|
||||||
// bind a GL texture object to active unit
|
// bind a GL texture object to active unit
|
||||||
void BindTexture(int unit,GLuint tex);
|
void BindTexture(int unit,GLuint tex);
|
||||||
|
|
||||||
@ -365,6 +360,9 @@ protected:
|
|||||||
// enable oblique frustum clipping with the given clip plane
|
// enable oblique frustum clipping with the given clip plane
|
||||||
void SetObliqueFrustumClipping(const CVector4D& clipPlane, int sign);
|
void SetObliqueFrustumClipping(const CVector4D& clipPlane, int sign);
|
||||||
|
|
||||||
|
// hotloading
|
||||||
|
static LibError ReloadChangedFileCB(void* param, const VfsPath& path);
|
||||||
|
|
||||||
// RENDERER DATA:
|
// RENDERER DATA:
|
||||||
/// Private data that is not needed by inline functions
|
/// Private data that is not needed by inline functions
|
||||||
CRendererInternals* m;
|
CRendererInternals* m;
|
||||||
@ -453,6 +451,11 @@ protected:
|
|||||||
*/
|
*/
|
||||||
bool m_DisableCopyShadow;
|
bool m_DisableCopyShadow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable rendering of terrain tile priority text overlay, for debugging.
|
||||||
|
*/
|
||||||
|
bool m_DisplayTerrainPriorities;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* m_ShadowZBias: Z bias used when rendering shadows into a depth texture.
|
* m_ShadowZBias: Z bias used when rendering shadows into a depth texture.
|
||||||
|
@ -124,7 +124,6 @@ protected:
|
|||||||
* Draw an outlined quad on top of the given tile.
|
* Draw an outlined quad on top of the given tile.
|
||||||
*/
|
*/
|
||||||
void RenderTileOutline(const CColor& colour, int line_width, bool draw_hidden, ssize_t i, ssize_t j);
|
void RenderTileOutline(const CColor& colour, int line_width, bool draw_hidden, ssize_t i, ssize_t j);
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@ -132,14 +131,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
static void RenderOverlays();
|
static void RenderOverlays();
|
||||||
|
|
||||||
#if 0
|
|
||||||
/**
|
|
||||||
* Kai: added function to draw out line segments for triangulation
|
|
||||||
*/
|
|
||||||
static void RenderEntityEdges();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Copying not allowed.
|
/// Copying not allowed.
|
||||||
TerrainOverlay(const TerrainOverlay&);
|
TerrainOverlay(const TerrainOverlay&);
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include "ps/Filesystem.h"
|
#include "ps/Filesystem.h"
|
||||||
#include "ps/CLogger.h"
|
#include "ps/CLogger.h"
|
||||||
|
#include "ps/Font.h"
|
||||||
#include "ps/Game.h"
|
#include "ps/Game.h"
|
||||||
#include "ps/Profile.h"
|
#include "ps/Profile.h"
|
||||||
#include "ps/World.h"
|
#include "ps/World.h"
|
||||||
@ -655,3 +656,19 @@ void TerrainRenderer::RenderWater()
|
|||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TerrainRenderer::RenderPriorities()
|
||||||
|
{
|
||||||
|
PROFILE("render priorities");
|
||||||
|
|
||||||
|
CFont font(L"mono-stroke-10");
|
||||||
|
font.Bind();
|
||||||
|
|
||||||
|
glColor3f(1, 1, 0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
|
||||||
|
{
|
||||||
|
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
|
||||||
|
patchdata->RenderPriorities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -111,6 +111,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
void RenderWater();
|
void RenderWater();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render priority text for all submitted patches, for debugging.
|
||||||
|
*/
|
||||||
|
void RenderPriorities();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TerrainRendererInternals* m;
|
TerrainRendererInternals* m;
|
||||||
};
|
};
|
||||||
|
@ -39,8 +39,10 @@ struct Brush
|
|||||||
|
|
||||||
float Get(ssize_t x, ssize_t y) const
|
float Get(ssize_t x, ssize_t y) const
|
||||||
{
|
{
|
||||||
debug_assert(x >= 0 && x < m_W && y >= 0 && y < m_H);
|
if (x >= 0 && x < m_W && y >= 0 && y < m_H)
|
||||||
return m_Data[x + y*m_W];
|
return m_Data[x + y*m_W];
|
||||||
|
else
|
||||||
|
return 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t m_W, m_H;
|
ssize_t m_W, m_H;
|
||||||
|
@ -153,25 +153,31 @@ BEGIN_COMMAND(PaintTerrain)
|
|||||||
m_VertsPerSide = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
|
m_VertsPerSide = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UpdatePriority(ssize_t x, ssize_t y, CTerrainTextureEntry* tex, ssize_t priorityScale, ssize_t& priority)
|
||||||
|
{
|
||||||
|
// Ignore out-of-bounds tiles
|
||||||
|
if (size_t(x) >= size_t(m_VertsPerSide-1) || size_t(y) >= size_t(m_VertsPerSide-1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
CMiniPatch* tile = m_Terrain->GetTile(x, y);
|
||||||
|
if (!tile)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If this tile matches the current texture, we just want to match its
|
||||||
|
// priority; otherwise we want to exceed its priority
|
||||||
|
if (tile->GetTextureEntry() == tex)
|
||||||
|
priority = std::max(priority, tile->GetPriority()*priorityScale);
|
||||||
|
else
|
||||||
|
priority = std::max(priority, tile->GetPriority()*priorityScale + 1);
|
||||||
|
}
|
||||||
|
|
||||||
void PaintTile(ssize_t x, ssize_t y, CTerrainTextureEntry* tex, ssize_t priority)
|
void PaintTile(ssize_t x, ssize_t y, CTerrainTextureEntry* tex, ssize_t priority)
|
||||||
{
|
{
|
||||||
// Ignore out-of-bounds tiles
|
// Ignore out-of-bounds tiles
|
||||||
if (size_t(x) >= size_t(m_VertsPerSide-1) || size_t(y) >= size_t(m_VertsPerSide-1))
|
if (size_t(x) >= size_t(m_VertsPerSide-1) || size_t(y) >= size_t(m_VertsPerSide-1))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Priority system: If the new tile should have a high priority,
|
set(x,y, TerrainTile(tex, priority));
|
||||||
// set it to one plus the maximum priority of all surrounding tiles
|
|
||||||
// (so that it's definitely the highest).
|
|
||||||
// Similar for low priority.
|
|
||||||
ssize_t greatest = 0;
|
|
||||||
ssize_t scale = (priority == ePaintTerrainPriority::HIGH ? +1 : -1);
|
|
||||||
CMiniPatch* tile;
|
|
||||||
#define TILE(dx, dy) tile = m_Terrain->GetTile(x dx, y dy); if (tile && tile->GetPriority()*scale > greatest) greatest = tile->GetPriority()*scale;
|
|
||||||
TILE(-1, -1) TILE(+0, -1) TILE(+1, -1)
|
|
||||||
TILE(-1, +0) TILE(+1, +0)
|
|
||||||
TILE(-1, +1) TILE(+0, +1) TILE(+1, +1)
|
|
||||||
#undef TILE
|
|
||||||
set(x,y, TerrainTile(tex, (greatest+1)*scale));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -223,12 +229,28 @@ BEGIN_COMMAND(PaintTerrain)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Priority system: If the new tile should have a high priority,
|
||||||
|
// set it to one plus the maximum priority of all surrounding tiles
|
||||||
|
// that aren't included in the brush (so that it's definitely the highest).
|
||||||
|
// Similar for low priority.
|
||||||
|
ssize_t priorityScale = (msg->priority == ePaintTerrainPriority::HIGH ? +1 : -1);
|
||||||
|
ssize_t priority = 0;
|
||||||
|
|
||||||
|
for (ssize_t dy = -1; dy < g_CurrentBrush.m_H+1; ++dy)
|
||||||
|
{
|
||||||
|
for (ssize_t dx = -1; dx < g_CurrentBrush.m_W+1; ++dx)
|
||||||
|
{
|
||||||
|
if (!(g_CurrentBrush.Get(dx, dy) > 0.5f)) // ignore tiles that will be painted over
|
||||||
|
m_TerrainDelta.UpdatePriority(x0+dx, y0+dy, texentry, priorityScale, priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (ssize_t dy = 0; dy < g_CurrentBrush.m_H; ++dy)
|
for (ssize_t dy = 0; dy < g_CurrentBrush.m_H; ++dy)
|
||||||
{
|
{
|
||||||
for (ssize_t dx = 0; dx < g_CurrentBrush.m_W; ++dx)
|
for (ssize_t dx = 0; dx < g_CurrentBrush.m_W; ++dx)
|
||||||
{
|
{
|
||||||
if (g_CurrentBrush.Get(dx, dy) > 0.5f) // TODO: proper solid brushes
|
if (g_CurrentBrush.Get(dx, dy) > 0.5f) // TODO: proper solid brushes
|
||||||
m_TerrainDelta.PaintTile(x0+dx, y0+dy, texentry, msg->priority);
|
m_TerrainDelta.PaintTile(x0+dx, y0+dy, texentry, priority*priorityScale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,6 +240,12 @@ void ViewGame::Render()
|
|||||||
Atlas_GLSwapBuffers((void*)g_GameLoop->glCanvas);
|
Atlas_GLSwapBuffers((void*)g_GameLoop->glCanvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewGame::SetParam(const std::wstring& name, bool value)
|
||||||
|
{
|
||||||
|
if (name == L"priorities")
|
||||||
|
g_Renderer.SetDisplayTerrainPriorities(value);
|
||||||
|
}
|
||||||
|
|
||||||
void ViewGame::SetParam(const std::wstring& name, const std::wstring& value)
|
void ViewGame::SetParam(const std::wstring& name, const std::wstring& value)
|
||||||
{
|
{
|
||||||
if (name == L"passability")
|
if (name == L"passability")
|
||||||
|
@ -79,6 +79,7 @@ public:
|
|||||||
virtual CSimulation2* GetSimulation2();
|
virtual CSimulation2* GetSimulation2();
|
||||||
virtual bool WantsHighFramerate();
|
virtual bool WantsHighFramerate();
|
||||||
|
|
||||||
|
virtual void SetParam(const std::wstring& name, bool value);
|
||||||
virtual void SetParam(const std::wstring& name, const std::wstring& value);
|
virtual void SetParam(const std::wstring& name, const std::wstring& value);
|
||||||
|
|
||||||
void SetSpeedMultiplier(float speed);
|
void SetSpeedMultiplier(float speed);
|
||||||
|
Loading…
Reference in New Issue
Block a user