# 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,6 +269,14 @@ function init(window, bottomWindow)
|
||||
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 nb = new wxNotebook(bottomWindow, -1);
|
||||
|
@ -51,7 +51,7 @@ CTerrainTextureEntry::CTerrainTextureEntry(CTerrainPropertiesPtr props, const Vf
|
||||
for (;it!=m_Groups.end();++it)
|
||||
(*it)->AddTerrain(this);
|
||||
|
||||
m_Tag = fs::basename(path);
|
||||
m_Tag = CStr(CStrW(fs::basename(path)));
|
||||
}
|
||||
|
||||
CTerrainTextureEntry::~CTerrainTextureEntry()
|
||||
|
@ -40,7 +40,7 @@ public:
|
||||
|
||||
private:
|
||||
// Tag = file name stripped of path and extension (grass_dark_1)
|
||||
CStrW m_Tag;
|
||||
CStr m_Tag;
|
||||
|
||||
// The property sheet used by this texture
|
||||
CTerrainPropertiesPtr m_pProperties;
|
||||
|
@ -229,6 +229,8 @@ void Render()
|
||||
|
||||
ogl_WarnIfError();
|
||||
|
||||
g_Renderer.RenderTextOverlays();
|
||||
|
||||
// Temp GUI message GeeTODO
|
||||
PROFILE_START("render gui");
|
||||
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.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -17,25 +17,28 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include "ps/Pyrogenesis.h"
|
||||
|
||||
#include "graphics/GameView.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/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/components/ICmpRangeManager.h"
|
||||
|
||||
const ssize_t BlendOffsets[8][2] = {
|
||||
const ssize_t BlendOffsets[9][2] = {
|
||||
{ 0, -1 },
|
||||
{ -1, -1 },
|
||||
{ -1, 0 },
|
||||
@ -43,13 +46,10 @@ const ssize_t BlendOffsets[8][2] = {
|
||||
{ 0, 1 },
|
||||
{ 1, 1 },
|
||||
{ 1, 0 },
|
||||
{ 1, -1 }
|
||||
{ 1, -1 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// CPatchRData constructor
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
struct STmpSplat {
|
||||
/**
|
||||
* Represents a blend for a single tile, texture and shape.
|
||||
*/
|
||||
struct STileBlend
|
||||
{
|
||||
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()
|
||||
@ -98,200 +140,260 @@ void CPatchRData::BuildBlends()
|
||||
m_BlendVertices.clear();
|
||||
m_BlendVertexIndices.clear();
|
||||
|
||||
CTerrain* terrain=m_Patch->m_Parent;
|
||||
CTerrain* terrain = m_Patch->m_Parent;
|
||||
|
||||
// temporary list of splats
|
||||
std::vector<STmpSplat> splats;
|
||||
// set of textures used for splats
|
||||
std::set<CTerrainTextureEntry*> splatTextures;
|
||||
std::vector<STileBlendStack> blendStacks;
|
||||
blendStacks.reserve(PATCH_SIZE*PATCH_SIZE);
|
||||
|
||||
// for each tile in patch ..
|
||||
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 each tile in patch ..
|
||||
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;
|
||||
|
||||
// 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->GetTextureEntry() != mp->GetTextureEntry()) {
|
||||
if (nmp->GetPriority() > mp->GetPriority() || (nmp->GetPriority() == mp->GetPriority() && nmp->GetTextureEntry() > mp->GetTextureEntry())) {
|
||||
STex tex;
|
||||
tex.m_Texture=nmp->GetTextureEntry();
|
||||
tex.m_Priority=nmp->GetPriority();
|
||||
if (std::find(neighbourTextures.begin(),neighbourTextures.end(),tex)==neighbourTextures.end()) {
|
||||
neighbourTextures.push_back(tex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<STileBlend> blends;
|
||||
blends.reserve(9);
|
||||
|
||||
// Compute a blend for every tile in the 3x3 square around this tile
|
||||
for (size_t n = 0; n < 9; ++n)
|
||||
{
|
||||
ssize_t ox = gx + BlendOffsets[n][1];
|
||||
ssize_t oz = gz + BlendOffsets[n][0];
|
||||
|
||||
CMiniPatch* nmp = terrain->GetTile(ox, oz);
|
||||
if (!nmp)
|
||||
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 ..
|
||||
size_t count=neighbourTextures.size();
|
||||
for (size_t k=0;k<count;++k) {
|
||||
// Sort the blends, highest priority first
|
||||
std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority());
|
||||
|
||||
// now build the grid of blends dependent on whether the tile adjacent to the current tile
|
||||
// uses the current neighbour texture
|
||||
BlendShape8 shape;
|
||||
for (size_t m=0;m<8;m++) {
|
||||
ssize_t ox=gx+BlendOffsets[m][1];
|
||||
ssize_t oz=gz+BlendOffsets[m][0];
|
||||
STileBlendStack blendStack;
|
||||
blendStack.i = i;
|
||||
blendStack.j = j;
|
||||
|
||||
// get texture on adjacent tile
|
||||
CTerrainTextureEntry* atex=GetTerrainTileTexture(terrain,ox,oz);
|
||||
// fill 0/1 into shape array
|
||||
shape[m]=(atex==neighbourTextures[k].m_Texture) ? 0 : 1;
|
||||
}
|
||||
// Put the blends into the tile's stack, merging any adjacent blends with the same texture
|
||||
for (size_t k = 0; k < blends.size(); ++k)
|
||||
{
|
||||
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
|
||||
unsigned int alphamapflags;
|
||||
int alphamap=CAlphaMapCalculator::Calculate(shape,alphamapflags);
|
||||
// Remove blends that are after (i.e. lower priority than) the current tile
|
||||
// (including the current tile), since we don't want to render them on top of
|
||||
// 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)
|
||||
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;
|
||||
}
|
||||
blendStacks.push_back(blendStack);
|
||||
}
|
||||
}
|
||||
|
||||
if (alphamapflags & BLENDMAP_FLIPV) {
|
||||
// flip v
|
||||
float t=v0;
|
||||
v0=v1;
|
||||
v1=t;
|
||||
}
|
||||
// Given the blend stack per tile, we want to batch together as many blends as possible.
|
||||
// Group them into a series of layers (each of which has a single texture):
|
||||
|
||||
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;
|
||||
}
|
||||
std::vector<SBlendLayer> blendLayers;
|
||||
|
||||
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;
|
||||
while (true)
|
||||
{
|
||||
if (!blendLayers.empty())
|
||||
{
|
||||
// Try to grab as many tiles as possible that match our current layer,
|
||||
// from off the blend stacks of all the tiles
|
||||
|
||||
ssize_t vsize=PATCH_SIZE+1;
|
||||
CTerrainTextureEntry* tex = blendLayers.back().m_Texture;
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
for (size_t k = 0; k < blendStacks.size(); ++k)
|
||||
{
|
||||
if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex)
|
||||
{
|
||||
SBlendLayer::Tile t = { blendStacks[k].i, blendStacks[k].j, blendStacks[k].blends.back().m_TileMask };
|
||||
blendLayers.back().m_Tiles.push_back(t);
|
||||
blendStacks[k].blends.pop_back();
|
||||
}
|
||||
// (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
|
||||
if (m_VBBlends) {
|
||||
// release existing vertex buffer chunk
|
||||
// Release existing vertex buffer chunk
|
||||
if (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
|
||||
m_BlendSplats.resize(splatTextures.size());
|
||||
size_t splatCount=0;
|
||||
if (m_BlendVertices.size())
|
||||
{
|
||||
// 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);
|
||||
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];
|
||||
splat.m_IndexStart=m_BlendIndices.size();
|
||||
splat.m_Texture=tex;
|
||||
|
||||
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++;
|
||||
}
|
||||
// Update the indices to include the base offset
|
||||
for (size_t k = 0; k < m_BlendIndices.size(); ++k)
|
||||
m_BlendIndices[k] += base;
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// must have allocated some vertices before trying to build corresponding indices
|
||||
@ -559,7 +661,8 @@ void CPatchRData::RenderBlends()
|
||||
{
|
||||
debug_assert(m_UpdateFlags==0);
|
||||
|
||||
if (m_BlendVertices.size()==0) return;
|
||||
if (m_BlendVertices.empty())
|
||||
return;
|
||||
|
||||
u8* base=m_VBBlends->m_Owner->Bind();
|
||||
|
||||
@ -629,3 +732,38 @@ void CPatchRData::RenderOutline()
|
||||
}
|
||||
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 RenderOutline();
|
||||
void RenderStreams(int streamflags, bool losColor);
|
||||
void RenderPriorities();
|
||||
|
||||
private:
|
||||
struct SSplat {
|
||||
@ -86,6 +87,8 @@ private:
|
||||
// build this renderdata object
|
||||
void Build();
|
||||
|
||||
ssize_t AddBlend(u16 i, u16 j, u8 shape);
|
||||
|
||||
void BuildBlends();
|
||||
void BuildIndices();
|
||||
void BuildVertices();
|
||||
|
@ -25,9 +25,13 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "Renderer.h"
|
||||
|
||||
#include "lib/bits.h" // is_pow2
|
||||
#include "lib/res/graphics/ogl_tex.h"
|
||||
#include "Renderer.h"
|
||||
#include "maths/Matrix3D.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "ps/CLogger.h"
|
||||
@ -358,6 +362,7 @@ CRenderer::CRenderer()
|
||||
m_SortAllTransparent = false;
|
||||
m_DisplayFrustum = false;
|
||||
m_DisableCopyShadow = false;
|
||||
m_DisplayTerrainPriorities = false;
|
||||
m_FastPlayerColor = true;
|
||||
m_SkipSubmit = false;
|
||||
m_RenderTerritories = true;
|
||||
@ -387,12 +392,16 @@ CRenderer::CRenderer()
|
||||
AddLocalProperty(L"waterShininess", &m->waterManager.m_Shininess, false);
|
||||
AddLocalProperty(L"waterSpecularStrength", &m->waterManager.m_SpecularStrength, false);
|
||||
AddLocalProperty(L"waterWaviness", &m->waterManager.m_Waviness, false);
|
||||
|
||||
RegisterFileReloadFunc(ReloadChangedFileCB, this);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// CRenderer destructor
|
||||
CRenderer::~CRenderer()
|
||||
{
|
||||
UnregisterFileReloadFunc(ReloadChangedFileCB, this);
|
||||
|
||||
// model rendering
|
||||
for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType)
|
||||
{
|
||||
@ -1302,7 +1311,12 @@ void CRenderer::RenderSubmissions()
|
||||
m->overlayRenderer.RenderForegroundOverlays(m_ViewCamera);
|
||||
PROFILE_END("render fg overlays");
|
||||
ogl_WarnIfError();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// EndFrame: signal frame end
|
||||
void CRenderer::EndFrame()
|
||||
{
|
||||
// empty lists
|
||||
m->terrainRenderer->EndFrame();
|
||||
m->overlayRenderer.EndFrame();
|
||||
@ -1315,18 +1329,12 @@ void CRenderer::RenderSubmissions()
|
||||
if (m->Model.Player != m->Model.PlayerInstancing)
|
||||
m->Model.PlayerInstancing->EndFrame();
|
||||
m->Model.Transp->EndFrame();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// EndFrame: signal frame end
|
||||
void CRenderer::EndFrame()
|
||||
{
|
||||
ogl_tex_bind(0, 0);
|
||||
|
||||
static bool once=false;
|
||||
if (!once && glGetError()) {
|
||||
LOGERROR(L"CRenderer::EndFrame: GL errors occurred");
|
||||
once=true;
|
||||
if (glGetError())
|
||||
{
|
||||
ONCE(LOGERROR(L"CRenderer::EndFrame: GL errors occurred"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1355,6 +1363,18 @@ void CRenderer::DisplayFrustum()
|
||||
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
|
||||
// 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
|
||||
//
|
||||
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] = {
|
||||
L"blendcircle.dds",
|
||||
L"blendlshape.dds",
|
||||
L"blendedge.dds",
|
||||
L"blendedgecorner.dds",
|
||||
L"blendedgetwocorners.dds",
|
||||
L"blendfourcorners.dds",
|
||||
L"blendtwooppositecorners.dds",
|
||||
L"blendlshapecorner.dds",
|
||||
L"blendtwocorners.dds",
|
||||
L"blendcorner.dds",
|
||||
L"blendtwoedges.dds",
|
||||
L"blendthreecorners.dds",
|
||||
L"blendushape.dds",
|
||||
L"blendbad.dds"
|
||||
L"blendcircle.png",
|
||||
L"blendlshape.png",
|
||||
L"blendedge.png",
|
||||
L"blendedgecorner.png",
|
||||
L"blendedgetwocorners.png",
|
||||
L"blendfourcorners.png",
|
||||
L"blendtwooppositecorners.png",
|
||||
L"blendlshapecorner.png",
|
||||
L"blendtwocorners.png",
|
||||
L"blendcorner.png",
|
||||
L"blendtwoedges.png",
|
||||
L"blendthreecorners.png",
|
||||
L"blendushape.png",
|
||||
L"blendbad.png"
|
||||
};
|
||||
size_t base = 0; // texture width/height (see below)
|
||||
// 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]);
|
||||
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.
|
||||
// (the packing algo assumes this)
|
||||
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
|
||||
|
||||
|
@ -216,20 +216,12 @@ public:
|
||||
*/
|
||||
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)
|
||||
// note:
|
||||
// * all 3D vertices specified in world space
|
||||
// * primitive operations rendered immediatedly, never batched
|
||||
// * primitives rendered in current material (set via SetMaterial)
|
||||
// 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);
|
||||
/**
|
||||
* Render text overlays on top of the scene.
|
||||
* Assumes the caller has set up the GL environment for orthographic rendering
|
||||
* with texturing and blending.
|
||||
*/
|
||||
void RenderTextOverlays();
|
||||
|
||||
// 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)
|
||||
@ -247,6 +239,9 @@ public:
|
||||
// get the mode to render subsequent models
|
||||
ERenderMode GetModelRenderMode() const { return m_ModelRenderMode; }
|
||||
|
||||
// debugging
|
||||
void SetDisplayTerrainPriorities(bool enabled) { m_DisplayTerrainPriorities = enabled; }
|
||||
|
||||
// bind a GL texture object to active unit
|
||||
void BindTexture(int unit,GLuint tex);
|
||||
|
||||
@ -365,6 +360,9 @@ protected:
|
||||
// enable oblique frustum clipping with the given clip plane
|
||||
void SetObliqueFrustumClipping(const CVector4D& clipPlane, int sign);
|
||||
|
||||
// hotloading
|
||||
static LibError ReloadChangedFileCB(void* param, const VfsPath& path);
|
||||
|
||||
// RENDERER DATA:
|
||||
/// Private data that is not needed by inline functions
|
||||
CRendererInternals* m;
|
||||
@ -453,6 +451,11 @@ protected:
|
||||
*/
|
||||
bool m_DisableCopyShadow;
|
||||
|
||||
/**
|
||||
* Enable rendering of terrain tile priority text overlay, for debugging.
|
||||
*/
|
||||
bool m_DisplayTerrainPriorities;
|
||||
|
||||
public:
|
||||
/**
|
||||
* m_ShadowZBias: Z bias used when rendering shadows into a depth texture.
|
||||
|
@ -125,21 +125,12 @@ protected:
|
||||
*/
|
||||
void RenderTileOutline(const CColor& colour, int line_width, bool draw_hidden, ssize_t i, ssize_t j);
|
||||
|
||||
|
||||
public:
|
||||
/**
|
||||
* Draw all TerrainOverlay objects that exist.
|
||||
*/
|
||||
static void RenderOverlays();
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Kai: added function to draw out line segments for triangulation
|
||||
*/
|
||||
static void RenderEntityEdges();
|
||||
#endif
|
||||
|
||||
|
||||
private:
|
||||
/// Copying not allowed.
|
||||
TerrainOverlay(const TerrainOverlay&);
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Font.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/World.h"
|
||||
@ -655,3 +656,19 @@ void TerrainRenderer::RenderWater()
|
||||
glDisable(GL_BLEND);
|
||||
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();
|
||||
|
||||
/**
|
||||
* Render priority text for all submitted patches, for debugging.
|
||||
*/
|
||||
void RenderPriorities();
|
||||
|
||||
private:
|
||||
TerrainRendererInternals* m;
|
||||
};
|
||||
|
@ -39,8 +39,10 @@ struct Brush
|
||||
|
||||
float Get(ssize_t x, ssize_t y) const
|
||||
{
|
||||
debug_assert(x >= 0 && x < m_W && y >= 0 && y < m_H);
|
||||
return m_Data[x + y*m_W];
|
||||
if (x >= 0 && x < m_W && y >= 0 && y < m_H)
|
||||
return m_Data[x + y*m_W];
|
||||
else
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
ssize_t m_W, m_H;
|
||||
|
@ -153,25 +153,31 @@ BEGIN_COMMAND(PaintTerrain)
|
||||
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)
|
||||
{
|
||||
// Ignore out-of-bounds tiles
|
||||
if (size_t(x) >= size_t(m_VertsPerSide-1) || size_t(y) >= size_t(m_VertsPerSide-1))
|
||||
return;
|
||||
|
||||
// Priority system: If the new tile should have a high 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));
|
||||
set(x,y, TerrainTile(tex, priority));
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -223,12 +229,28 @@ BEGIN_COMMAND(PaintTerrain)
|
||||
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 dx = 0; dx < g_CurrentBrush.m_W; ++dx)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (name == L"passability")
|
||||
|
@ -79,6 +79,7 @@ public:
|
||||
virtual CSimulation2* GetSimulation2();
|
||||
virtual bool WantsHighFramerate();
|
||||
|
||||
virtual void SetParam(const std::wstring& name, bool value);
|
||||
virtual void SetParam(const std::wstring& name, const std::wstring& value);
|
||||
|
||||
void SetSpeedMultiplier(float speed);
|
||||
|
Loading…
Reference in New Issue
Block a user