Index: ps/trunk/binaries/data/mods/public/simulation/components/interfaces/Settlement.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/interfaces/Settlement.js (revision 9888)
+++ ps/trunk/binaries/data/mods/public/simulation/components/interfaces/Settlement.js (nonexistent)
@@ -1 +0,0 @@
-Engine.RegisterInterface("Settlement");
Index: ps/trunk/source/gui/MiniMap.cpp
===================================================================
--- ps/trunk/source/gui/MiniMap.cpp (revision 9888)
+++ ps/trunk/source/gui/MiniMap.cpp (revision 9889)
@@ -1,549 +1,515 @@
/* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include
#include "MiniMap.h"
#include "graphics/GameView.h"
#include "graphics/LOSTexture.h"
#include "graphics/MiniPatch.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureEntry.h"
#include "graphics/TerrainTextureManager.h"
+#include "graphics/TerritoryTexture.h"
#include "lib/ogl.h"
#include "lib/external_libraries/sdl.h"
#include "lib/bits.h"
#include "lib/timer.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "ps/World.h"
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpMinimap.h"
+#include "simulation2/components/ICmpTerritoryManager.h"
bool g_GameRestarted = false;
static unsigned int ScaleColor(unsigned int color, float x)
{
unsigned int r = unsigned(float(color & 0xff) * x);
unsigned int g = unsigned(float((color>>8) & 0xff) * x);
unsigned int b = unsigned(float((color>>16) & 0xff) * x);
return (0xff000000 | r | g<<8 | b<<16);
}
CMiniMap::CMiniMap() :
m_TerrainTexture(0), m_TerrainData(0), m_MapSize(0), m_Terrain(0), m_TerrainDirty(true), m_MapScale(1.f)
{
AddSetting(GUIST_CColor, "fov_wedge_color");
AddSetting(GUIST_CStrW, "tooltip");
AddSetting(GUIST_CStr, "tooltip_style");
m_Clicking = false;
m_Hovering = false;
}
CMiniMap::~CMiniMap()
{
Destroy();
}
void CMiniMap::HandleMessage(SGUIMessage &Message)
{
switch(Message.type)
{
case GUIM_MOUSE_PRESS_LEFT:
{
if (m_Hovering)
{
SetCameraPos();
m_Clicking = true;
}
break;
}
case GUIM_MOUSE_RELEASE_LEFT:
{
if(m_Hovering && m_Clicking)
{
SetCameraPos();
}
m_Clicking = false;
break;
}
case GUIM_MOUSE_DBLCLICK_LEFT:
{
if(m_Hovering && m_Clicking)
{
SetCameraPos();
}
m_Clicking = false;
break;
}
case GUIM_MOUSE_ENTER:
{
m_Hovering = true;
break;
}
case GUIM_MOUSE_LEAVE:
{
m_Clicking = false;
m_Hovering = false;
break;
}
case GUIM_MOUSE_RELEASE_RIGHT:
{
CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT, 1);
break;
}
case GUIM_MOUSE_DBLCLICK_RIGHT:
{
CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT, 2);
break;
}
case GUIM_MOUSE_MOTION:
{
if (m_Hovering && m_Clicking)
{
SetCameraPos();
}
break;
}
case GUIM_MOUSE_WHEEL_DOWN:
case GUIM_MOUSE_WHEEL_UP:
Message.Skip();
break;
default:
break;
} // switch
}
void CMiniMap::GetMouseWorldCoordinates(float& x, float& z)
{
// Determine X and Z according to proportion of mouse position and minimap
CPos mousePos = GetMousePos();
float px = (mousePos.x - m_CachedActualSize.left) / m_CachedActualSize.GetWidth();
float py = (m_CachedActualSize.bottom - mousePos.y) / m_CachedActualSize.GetHeight();
float angle = GetAngle();
// Scale world coordinates for shrunken square map
x = CELL_SIZE * m_MapSize * (m_MapScale * (cos(angle)*(px-0.5) - sin(angle)*(py-0.5)) + 0.5);
z = CELL_SIZE * m_MapSize * (m_MapScale * (cos(angle)*(py-0.5) + sin(angle)*(px-0.5)) + 0.5);
}
void CMiniMap::SetCameraPos()
{
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
CVector3D target;
GetMouseWorldCoordinates(target.X, target.Z);
target.Y = terrain->GetExactGroundLevel(target.X, target.Z);
g_Game->GetView()->MoveCameraTarget(target, true);
}
float CMiniMap::GetAngle()
{
CVector3D cameraIn = m_Camera->m_Orientation.GetIn();
return -atan2(cameraIn.X, cameraIn.Z);
}
void CMiniMap::FireWorldClickEvent(int button, int clicks)
{
float x, z;
GetMouseWorldCoordinates(x, z);
CScriptValRooted coords;
g_ScriptingHost.GetScriptInterface().Eval("({})", coords);
g_ScriptingHost.GetScriptInterface().SetProperty(coords.get(), "x", x, false);
g_ScriptingHost.GetScriptInterface().SetProperty(coords.get(), "z", z, false);
ScriptEvent("worldclick", coords);
UNUSED2(button);
UNUSED2(clicks);
}
// render view rect : John M. Mena
// This sets up and draws the rectangle on the mini-map
// which represents the view of the camera in the world.
void CMiniMap::DrawViewRect()
{
// Compute the camera frustum intersected with a fixed-height plane.
// TODO: Currently we hard-code the height, so this'll be dodgy when maps aren't the
// expected height - how can we make it better without the view rect wobbling in
// size while the player scrolls?
float h = 16384.f * HEIGHT_SCALE;
CVector3D hitPt[4];
hitPt[0]=m_Camera->GetWorldCoordinates(0, g_Renderer.GetHeight(), h);
hitPt[1]=m_Camera->GetWorldCoordinates(g_Renderer.GetWidth(), g_Renderer.GetHeight(), h);
hitPt[2]=m_Camera->GetWorldCoordinates(g_Renderer.GetWidth(), 0, h);
hitPt[3]=m_Camera->GetWorldCoordinates(0, 0, h);
float ViewRect[4][2];
for (int i=0;i<4;i++) {
// convert to minimap space
float px=hitPt[i].X;
float pz=hitPt[i].Z;
ViewRect[i][0]=(m_CachedActualSize.GetWidth()*px/float(CELL_SIZE*m_MapSize));
ViewRect[i][1]=(m_CachedActualSize.GetHeight()*pz/float(CELL_SIZE*m_MapSize));
}
// Enable Scissoring as to restrict the rectangle
// to only the mini-map below by retrieving the mini-maps
// screen coords.
const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom;
glScissor((int)x, g_Renderer.GetHeight()-(int)y, (int)m_CachedActualSize.GetWidth(), (int)m_CachedActualSize.GetHeight());
glEnable(GL_SCISSOR_TEST);
glEnable(GL_LINE_SMOOTH);
glLineWidth(2.0f);
glColor3f(1.0f, 0.3f, 0.3f);
// Draw the viewing rectangle with the ScEd's conversion algorithm
glBegin(GL_LINE_LOOP);
glVertex2f(ViewRect[0][0], -ViewRect[0][1]);
glVertex2f(ViewRect[1][0], -ViewRect[1][1]);
glVertex2f(ViewRect[2][0], -ViewRect[2][1]);
glVertex2f(ViewRect[3][0], -ViewRect[3][1]);
glEnd();
// restore state
glDisable(GL_SCISSOR_TEST);
glDisable(GL_LINE_SMOOTH);
glLineWidth(1.0f);
}
struct MinimapUnitVertex
{
u8 r, g, b, a;
float x, y;
};
void CMiniMap::DrawTexture(float coordMax, float angle, float x, float y, float x2, float y2, float z)
{
// Rotate the texture coordinates (0,0)-(coordMax,coordMax) around their center point (m,m)
// Scale square maps to fit in circular minimap area
const float s = sin(angle) * m_MapScale;
const float c = cos(angle) * m_MapScale;
const float m = coordMax / 2.f;
glBegin(GL_QUADS);
glTexCoord2f(m*(-c + s + 1.f), m*(-c + -s + 1.f));
glVertex3f(x, y, z);
glTexCoord2f(m*(c + s + 1.f), m*(-c + s + 1.f));
glVertex3f(x2, y, z);
glTexCoord2f(m*(c + -s + 1.f), m*(c + s + 1.f));
glVertex3f(x2, y2, z);
glTexCoord2f(m*(-c + -s + 1.f), m*(c + -s + 1.f));
glVertex3f(x, y2, z);
glEnd();
}
void CMiniMap::Draw()
{
PROFILE("minimap");
// The terrain isn't actually initialized until the map is loaded, which
// happens when the game is started, so abort until then.
if(!(GetGUI() && g_Game && g_Game->IsGameStarted()))
return;
CSimulation2* sim = g_Game->GetSimulation2();
CmpPtr cmpRangeManager(*sim, SYSTEM_ENTITY);
ENSURE(!cmpRangeManager.null());
// Set our globals in case they hadn't been set before
m_Camera = g_Game->GetView()->GetCamera();
m_Terrain = g_Game->GetWorld()->GetTerrain();
m_Width = (u32)(m_CachedActualSize.right - m_CachedActualSize.left);
m_Height = (u32)(m_CachedActualSize.bottom - m_CachedActualSize.top);
m_MapSize = m_Terrain->GetVerticesPerSide();
m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize);
m_MapScale = (cmpRangeManager->GetLosCircular() ? 1.f : 1.414f);
if(!m_TerrainTexture || g_GameRestarted)
CreateTextures();
// only update 2x / second
// (note: since units only move a few pixels per second on the minimap,
// we can get away with infrequent updates; this is slow)
static double last_time;
const double cur_time = timer_Time();
if(cur_time - last_time > 0.5)
{
last_time = cur_time;
if(m_TerrainDirty)
RebuildTerrainTexture();
}
const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom;
const float x2 = m_CachedActualSize.right, y2 = m_CachedActualSize.top;
const float z = GetBufferedZ();
const float texCoordMax = (float)(m_MapSize - 1) / (float)m_TextureSize;
const float angle = GetAngle();
// Draw the main textured quad
g_Renderer.BindTexture(0, m_TerrainTexture);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
DrawTexture(texCoordMax, angle, x, y, x2, y2, z);
- /* // TODO: reimplement with new sim system
- // Shade territories by player
- CTerritoryManager* territoryMgr = g_Game->GetWorld()->GetTerritoryManager();
- std::vector& territories = territoryMgr->GetTerritories();
-
- PROFILE_START("minimap territory shade");
-
- glDisable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- for( size_t i=0; iowner->GetPlayerID() == 0 )
- continue;
- std::vector& boundary = territories[i]->boundary;
- SPlayerColour col = territories[i]->owner->GetColour();
- glColor4f(col.r, col.g, col.b, 0.25f);
- glBegin(GL_POLYGON);
- for( size_t j=0; jGetTilesPerSide() * CELL_SIZE);
- float fy = boundary[j].y / (m_Terrain->GetTilesPerSide() * CELL_SIZE);
- glVertex3f( x*(1-fx) + x2*fx, y*(1-fy) + y2*fy, z );
- }
- glEnd();
- }
- glDisable(GL_BLEND);
-
- PROFILE_END("minimap territory shade");
// Draw territory boundaries
- glEnable(GL_LINE_SMOOTH);
- glLineWidth(1.0f);
+ CTerritoryTexture& territoryTexture = g_Game->GetView()->GetTerritoryTexture();
+ territoryTexture.BindTexture(0);
glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glColor4f(0.8f, 0.8f, 0.8f, 0.8f);
- for( size_t i=0; i& boundary = territories[i]->boundary;
- glBegin(GL_LINE_LOOP);
- for( size_t j=0; jGetTilesPerSide() * CELL_SIZE);
- float fy = boundary[j].y / (m_Terrain->GetTilesPerSide() * CELL_SIZE);
- glVertex3f( x*(1-fx) + x2*fx, y*(1-fy) + y2*fy, z );
- }
- glEnd();
- }
- glLineWidth(1.0f);
- glDisable(GL_LINE_SMOOTH);
+ glMatrixMode(GL_TEXTURE);
+ glLoadMatrixf(territoryTexture.GetMinimapTextureMatrix());
+ glMatrixMode(GL_MODELVIEW);
+
+ DrawTexture(1.0f, angle, x, y, x2, y2, z);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
glDisable(GL_BLEND);
- */
+
// Draw the LOS quad in black, using alpha values from the LOS texture
CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture();
losTexture.BindTexture(0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor3f(0.0f, 0.0f, 0.0f);
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(losTexture.GetMinimapTextureMatrix());
glMatrixMode(GL_MODELVIEW);
DrawTexture(1.0f, angle, x, y, x2, y2, z);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glDisable(GL_BLEND);
// Set up the matrix for drawing points and lines
glPushMatrix();
glTranslatef(x, y, z);
// Rotate around the center of the map
glTranslatef((x2-x)/2.f, (y2-y)/2.f, 0.f);
// Scale square maps to fit in circular minimap area
float unitScale = (cmpRangeManager->GetLosCircular() ? 1.f : m_MapScale/2.f);
glScalef(unitScale, unitScale, 1.f);
glRotatef(angle * 180.f/M_PI, 0.f, 0.f, 1.f);
glTranslatef(-(x2-x)/2.f, -(y2-y)/2.f, 0.f);
PROFILE_START("minimap units");
// Don't enable GL_POINT_SMOOTH because it's far too slow
// (~70msec/frame on a GF4 rendering a thousand points)
glPointSize(3.f);
float sx = (float)m_Width / ((m_MapSize - 1) * CELL_SIZE);
float sy = (float)m_Height / ((m_MapSize - 1) * CELL_SIZE);
CSimulation2::InterfaceList ents = sim->GetEntitiesWithInterface(IID_Minimap);
std::vector vertexArray;
vertexArray.reserve(ents.size());
for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
{
MinimapUnitVertex v;
ICmpMinimap* cmpMinimap = static_cast(it->second);
entity_pos_t posX, posZ;
if (cmpMinimap->GetRenderData(v.r, v.g, v.b, posX, posZ))
{
ICmpRangeManager::ELosVisibility vis = cmpRangeManager->GetLosVisibility(it->first, g_Game->GetPlayerID());
if (vis != ICmpRangeManager::VIS_HIDDEN)
{
v.a = 255;
v.x = posX.ToFloat()*sx;
v.y = -posZ.ToFloat()*sy;
vertexArray.push_back(v);
}
}
}
if (!vertexArray.empty())
{
glInterleavedArrays(GL_C4UB_V2F, sizeof(MinimapUnitVertex), &vertexArray[0]);
glDrawArrays(GL_POINTS, 0, (GLsizei)vertexArray.size());
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
PROFILE_END("minimap units");
DrawViewRect();
glPopMatrix();
// Reset everything back to normal
glPointSize(1.0f);
glEnable(GL_TEXTURE_2D);
}
void CMiniMap::CreateTextures()
{
Destroy();
// Create terrain texture
glGenTextures(1, &m_TerrainTexture);
g_Renderer.BindTexture(0, m_TerrainTexture);
// Initialise texture with solid black, for the areas we don't
// overwrite with glTexSubImage2D later
u32* texData = new u32[m_TextureSize * m_TextureSize];
for (ssize_t i = 0; i < m_TextureSize * m_TextureSize; ++i)
texData[i] = 0xFF000000;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_TextureSize, m_TextureSize, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, texData);
delete[] texData;
m_TerrainData = new u32[(m_MapSize - 1) * (m_MapSize - 1)];
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
// Rebuild and upload both of them
RebuildTerrainTexture();
}
void CMiniMap::RebuildTerrainTexture()
{
u32 x = 0;
u32 y = 0;
u32 w = m_MapSize - 1;
u32 h = m_MapSize - 1;
float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight;
m_TerrainDirty = false;
for(u32 j = 0; j < h; j++)
{
u32 *dataPtr = m_TerrainData + ((y + j) * (m_MapSize - 1)) + x;
for(u32 i = 0; i < w; i++)
{
float avgHeight = ( m_Terrain->GetVertexGroundLevel((int)i, (int)j)
+ m_Terrain->GetVertexGroundLevel((int)i+1, (int)j)
+ m_Terrain->GetVertexGroundLevel((int)i, (int)j+1)
+ m_Terrain->GetVertexGroundLevel((int)i+1, (int)j+1)
) / 4.0f;
if(avgHeight < waterHeight)
{
*dataPtr++ = 0xff304080; // TODO: perhaps use the renderer's water color?
}
else
{
int hmap = ((int)m_Terrain->GetHeightMap()[(y + j) * m_MapSize + x + i]) >> 8;
int val = (hmap / 3) + 170;
u32 color = 0xFFFFFFFF;
CMiniPatch *mp = m_Terrain->GetTile(x + i, y + j);
if(mp)
{
CTerrainTextureEntry *tex = mp->GetTextureEntry();
if(tex)
{
// If the texture can't be loaded yet, set the dirty flags
// so we'll try regenerating the terrain texture again soon
if(!tex->GetTexture()->TryLoad())
m_TerrainDirty = true;
color = tex->GetBaseColor();
}
}
*dataPtr++ = ScaleColor(color, float(val) / 255.0f);
}
}
}
// Upload the texture
g_Renderer.BindTexture(0, m_TerrainTexture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize - 1, m_MapSize - 1, GL_BGRA_EXT, GL_UNSIGNED_BYTE, m_TerrainData);
}
void CMiniMap::Destroy()
{
if(m_TerrainTexture)
{
glDeleteTextures(1, &m_TerrainTexture);
m_TerrainTexture = 0;
}
delete[] m_TerrainData;
m_TerrainData = 0;
}
Index: ps/trunk/source/tools/atlas/GameInterface/ActorViewer.cpp
===================================================================
--- ps/trunk/source/tools/atlas/GameInterface/ActorViewer.cpp (revision 9888)
+++ ps/trunk/source/tools/atlas/GameInterface/ActorViewer.cpp (revision 9889)
@@ -1,399 +1,406 @@
/* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "ActorViewer.h"
#include "View.h"
#include "graphics/ColladaManager.h"
#include "graphics/LOSTexture.h"
#include "graphics/Model.h"
#include "graphics/ObjectManager.h"
#include "graphics/ParticleManager.h"
#include "graphics/Patch.h"
#include "graphics/SkeletonAnimManager.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureEntry.h"
#include "graphics/TerrainTextureManager.h"
+#include "graphics/TerritoryTexture.h"
#include "graphics/UnitManager.h"
#include "maths/MathUtil.h"
#include "ps/Font.h"
#include "ps/GameSetup/Config.h"
#include "ps/ProfileViewer.h"
#include "renderer/Renderer.h"
#include "renderer/Scene.h"
#include "renderer/SkyManager.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/components/ICmpTerrain.h"
#include "simulation2/components/ICmpUnitMotion.h"
#include "simulation2/components/ICmpVisual.h"
struct ActorViewerImpl : public Scene
{
NONCOPYABLE(ActorViewerImpl);
public:
ActorViewerImpl() :
Entity(INVALID_ENTITY),
Terrain(),
ColladaManager(),
MeshManager(ColladaManager),
SkeletonAnimManager(ColladaManager),
UnitManager(),
Simulation2(&UnitManager, &Terrain),
ObjectManager(MeshManager, SkeletonAnimManager, Simulation2),
- LOSTexture(Simulation2)
+ LOSTexture(Simulation2),
+ TerritoryTexture(Simulation2)
{
UnitManager.SetObjectManager(ObjectManager);
}
entity_id_t Entity;
CStrW CurrentUnitID;
CStrW CurrentUnitAnim;
float CurrentSpeed;
bool WalkEnabled;
bool GroundEnabled;
bool ShadowsEnabled;
SColor4ub Background;
CTerrain Terrain;
CColladaManager ColladaManager;
CMeshManager MeshManager;
CSkeletonAnimManager SkeletonAnimManager;
CObjectManager ObjectManager;
CUnitManager UnitManager;
CSimulation2 Simulation2;
CLOSTexture LOSTexture;
+ CTerritoryTexture TerritoryTexture;
// Simplistic implementation of the Scene interface
virtual void EnumerateObjects(const CFrustum& frustum, SceneCollector* c)
{
if (GroundEnabled)
{
for (ssize_t pj = 0; pj < Terrain.GetPatchesPerSide(); ++pj)
for (ssize_t pi = 0; pi < Terrain.GetPatchesPerSide(); ++pi)
c->Submit(Terrain.GetPatch(pi, pj));
}
Simulation2.RenderSubmit(*c, frustum, false);
}
virtual CLOSTexture& GetLOSTexture()
{
return LOSTexture;
}
+ virtual CTerritoryTexture& GetTerritoryTexture()
+ {
+ return TerritoryTexture;
+ }
};
ActorViewer::ActorViewer()
: m(*new ActorViewerImpl())
{
m.WalkEnabled = false;
m.GroundEnabled = true;
m.ShadowsEnabled = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS);
m.Background = SColor4ub(0, 0, 0, 255);
// Create a tiny empty piece of terrain, just so we can put shadows
// on it without having to think too hard
m.Terrain.Initialize(2, NULL);
CTerrainTextureEntry* tex = g_TexMan.FindTexture("whiteness");
if (tex)
{
for (ssize_t pi = 0; pi < m.Terrain.GetPatchesPerSide(); ++pi)
{
for (ssize_t pj = 0; pj < m.Terrain.GetPatchesPerSide(); ++pj)
{
CPatch* patch = m.Terrain.GetPatch(pi, pj);
for (ssize_t i = 0; i < PATCH_SIZE; ++i)
{
for (ssize_t j = 0; j < PATCH_SIZE; ++j)
{
CMiniPatch& mp = patch->m_MiniPatches[i][j];
mp.Tex = tex;
mp.Priority = 0;
}
}
}
}
}
else
{
debug_warn(L"Failed to load whiteness texture");
}
// Start the simulation
m.Simulation2.LoadDefaultScripts();
m.Simulation2.ResetState();
// Tell the simulation we've already loaded the terrain
CmpPtr cmpTerrain(m.Simulation2, SYSTEM_ENTITY);
if (!cmpTerrain.null())
cmpTerrain->ReloadTerrain();
CmpPtr cmpRangeManager(m.Simulation2, SYSTEM_ENTITY);
if (!cmpRangeManager.null())
cmpRangeManager->SetLosRevealAll(-1, true);
}
ActorViewer::~ActorViewer()
{
delete &m;
}
CSimulation2* ActorViewer::GetSimulation2()
{
return &m.Simulation2;
}
entity_id_t ActorViewer::GetEntity()
{
return m.Entity;
}
void ActorViewer::UnloadObjects()
{
m.ObjectManager.UnloadObjects();
}
void ActorViewer::SetActor(const CStrW& name, const CStrW& animation)
{
bool needsAnimReload = false;
CStrW id = name;
// Recreate the entity, if we don't have one or if the new one is different
if (m.Entity == INVALID_ENTITY || id != m.CurrentUnitID)
{
// Delete the old entity (if any)
if (m.Entity != INVALID_ENTITY)
{
m.Simulation2.DestroyEntity(m.Entity);
m.Simulation2.FlushDestroyedEntities();
m.Entity = INVALID_ENTITY;
}
// If there's no actor to display, return with nothing loaded
if (id.empty())
return;
m.Entity = m.Simulation2.AddEntity(L"preview|" + id);
if (m.Entity == INVALID_ENTITY)
return;
CmpPtr cmpPosition(m.Simulation2, m.Entity);
if (!cmpPosition.null())
{
ssize_t c = CELL_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2;
cmpPosition->JumpTo(entity_pos_t::FromInt(c), entity_pos_t::FromInt(c));
cmpPosition->SetYRotation(entity_angle_t::FromFloat((float)M_PI));
}
needsAnimReload = true;
}
if (animation != m.CurrentUnitAnim)
needsAnimReload = true;
if (needsAnimReload)
{
CStr anim = animation.ToUTF8().LowerCase();
// Emulate the typical simulation animation behaviour
float speed;
float repeattime = 0.f;
if (anim == "walk")
{
CmpPtr cmpUnitMotion(m.Simulation2, m.Entity);
if (!cmpUnitMotion.null())
speed = cmpUnitMotion->GetWalkSpeed().ToFloat();
else
speed = 7.f; // typical unit speed
m.CurrentSpeed = speed;
}
else if (anim == "run")
{
CmpPtr cmpUnitMotion(m.Simulation2, m.Entity);
if (!cmpUnitMotion.null())
speed = cmpUnitMotion->GetRunSpeed().ToFloat();
else
speed = 12.f; // typical unit speed
m.CurrentSpeed = speed;
}
else if (anim == "melee")
{
speed = 1.f; // speed will be ignored if we have a repeattime
m.CurrentSpeed = 0.f;
CStr code = "var cmp = Engine.QueryInterface("+CStr::FromUInt(m.Entity)+", IID_Attack); " +
"if (cmp) cmp.GetTimers(cmp.GetBestAttack()).repeat; else 0;";
m.Simulation2.GetScriptInterface().Eval(code.c_str(), repeattime);
}
else
{
// Play the animation at normal speed, but movement speed is zero
speed = 1.f;
m.CurrentSpeed = 0.f;
}
CStr sound;
if (anim == "melee")
sound = "attack";
else if (anim == "build")
sound = "build";
else if (anim.Find("gather_") == 0)
sound = anim;
std::wstring soundgroup;
if (!sound.empty())
{
CStr code = "var cmp = Engine.QueryInterface("+CStr::FromUInt(m.Entity)+", IID_Sound); " +
"if (cmp) cmp.GetSoundGroup('"+sound+"'); else '';";
m.Simulation2.GetScriptInterface().Eval(code.c_str(), soundgroup);
}
CmpPtr cmpVisual(m.Simulation2, m.Entity);
if (!cmpVisual.null())
{
// TODO: SetEntitySelection(anim)
cmpVisual->SelectAnimation(anim, false, speed, soundgroup);
if (repeattime)
cmpVisual->SetAnimationSyncRepeat(repeattime);
}
}
m.CurrentUnitID = id;
m.CurrentUnitAnim = animation;
}
void ActorViewer::SetBackgroundColour(const SColor4ub& colour)
{
m.Background = colour;
m.Terrain.SetBaseColour(colour);
}
void ActorViewer::SetWalkEnabled(bool enabled) { m.WalkEnabled = enabled; }
void ActorViewer::SetGroundEnabled(bool enabled) { m.GroundEnabled = enabled; }
void ActorViewer::SetShadowsEnabled(bool enabled) { m.ShadowsEnabled = enabled; }
void ActorViewer::SetStatsEnabled(bool enabled)
{
if (enabled)
g_ProfileViewer.ShowTable("renderer");
else
g_ProfileViewer.ShowTable("");
}
void ActorViewer::Render()
{
m.Terrain.MakeDirty(RENDERDATA_UPDATE_COLOR);
g_Renderer.SetClearColor(m.Background);
// Disable shadows locally (avoid clobbering global state)
bool oldShadows = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS);
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, m.ShadowsEnabled);
bool oldSky = g_Renderer.GetSkyManager()->m_RenderSky;
g_Renderer.GetSkyManager()->m_RenderSky = false;
g_Renderer.BeginFrame();
// Find the centre of the interesting region, in the middle of the patch
// and half way up the model (assuming there is one)
CVector3D centre;
CmpPtr cmpVisual(m.Simulation2, m.Entity);
if (!cmpVisual.null())
cmpVisual->GetBounds().GetCentre(centre);
else
centre.Y = 0.f;
centre.X = centre.Z = CELL_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2;
CCamera camera = View::GetView_Actor()->GetCamera();
camera.m_Orientation.Translate(centre.X, centre.Y, centre.Z);
camera.UpdateFrustum();
g_Renderer.SetSceneCamera(camera, camera);
g_Renderer.RenderScene(m);
// ....
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glPushAttrib(GL_ENABLE_BIT);
glEnable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glEnable(GL_TEXTURE_2D);
g_ProfileViewer.RenderProfile();
glPopAttrib();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
g_Renderer.EndFrame();
// Restore the old renderer state
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, oldShadows);
g_Renderer.GetSkyManager()->m_RenderSky = oldSky;
ogl_WarnIfError();
}
void ActorViewer::Update(float dt)
{
m.Simulation2.Update((int)(dt*1000));
m.Simulation2.Interpolate(dt, 0);
g_Renderer.GetParticleManager().Interpolate(dt);
if (m.WalkEnabled && m.CurrentSpeed)
{
CmpPtr cmpPosition(m.Simulation2, m.Entity);
if (!cmpPosition.null())
{
// Move the model by speed*dt forwards
float z = cmpPosition->GetPosition().Z.ToFloat();
z -= m.CurrentSpeed*dt;
// Wrap at the edges, so it doesn't run off into the horizon
ssize_t c = CELL_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2;
if (z < c - CELL_SIZE*PATCH_SIZE * 0.1f)
z = c + CELL_SIZE*PATCH_SIZE * 0.1f;
cmpPosition->JumpTo(cmpPosition->GetPosition().X, entity_pos_t::FromFloat(z));
}
}
}
Index: ps/trunk/source/graphics/LOSTexture.cpp
===================================================================
--- ps/trunk/source/graphics/LOSTexture.cpp (revision 9888)
+++ ps/trunk/source/graphics/LOSTexture.cpp (revision 9889)
@@ -1,270 +1,269 @@
/* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "LOSTexture.h"
#include "graphics/Terrain.h"
#include "lib/bits.h"
#include "ps/Game.h"
#include "ps/Profile.h"
-#include "ps/World.h"
#include "renderer/Renderer.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/components/ICmpTerrain.h"
/*
The LOS bitmap is computed with one value per map vertex, based on
CCmpRangeManager's visibility information.
The bitmap is then blurred using an NxN filter (in particular a
7-tap Binomial filter as an efficient integral approximation of a Gaussian).
To implement the blur efficiently without using extra memory for a second copy
of the bitmap, we generate the bitmap with (N-1)/2 pixels of padding on each side,
then the blur shifts the image back into the corner.
The blurred bitmap is then uploaded into a GL texture for use by the renderer.
*/
// Blur with a NxN filter, where N = g_BlurSize must be an odd number.
static const size_t g_BlurSize = 7;
CLOSTexture::CLOSTexture(CSimulation2& simulation) :
m_Simulation(simulation), m_Dirty(true), m_Texture(0), m_MapSize(0), m_TextureSize(0)
{
}
CLOSTexture::~CLOSTexture()
{
if (m_Texture)
DeleteTexture();
}
void CLOSTexture::DeleteTexture()
{
glDeleteTextures(1, &m_Texture);
m_Texture = 0;
}
void CLOSTexture::MakeDirty()
{
m_Dirty = true;
}
void CLOSTexture::BindTexture(int unit)
{
if (m_Dirty)
{
RecomputeTexture(unit);
m_Dirty = false;
}
g_Renderer.BindTexture(unit, m_Texture);
}
GLuint CLOSTexture::GetTexture()
{
if (m_Dirty)
{
RecomputeTexture(0);
m_Dirty = false;
}
return m_Texture;
}
const float* CLOSTexture::GetTextureMatrix()
{
ENSURE(!m_Dirty);
return &m_TextureMatrix._11;
}
const float* CLOSTexture::GetMinimapTextureMatrix()
{
ENSURE(!m_Dirty);
return &m_MinimapTextureMatrix._11;
}
void CLOSTexture::ConstructTexture(int unit)
{
CmpPtr cmpTerrain(m_Simulation, SYSTEM_ENTITY);
if (cmpTerrain.null())
return;
m_MapSize = cmpTerrain->GetVerticesPerSide();
m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize + g_BlurSize - 1);
glGenTextures(1, &m_Texture);
g_Renderer.BindTexture(unit, m_Texture);
// Initialise texture with SoD colour, for the areas we don't
// overwrite with glTexSubImage2D later
u8* texData = new u8[m_TextureSize * m_TextureSize];
memset(texData, 0x00, m_TextureSize * m_TextureSize);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, m_TextureSize, m_TextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texData);
delete[] texData;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
{
// Texture matrix: We want to map
// world pos (0, y, 0) (i.e. first vertex)
// onto texcoord (0.5/texsize, 0.5/texsize) (i.e. middle of first texel);
// world pos ((mapsize-1)*cellsize, y, (mapsize-1)*cellsize) (i.e. last vertex)
// onto texcoord ((mapsize-0.5) / texsize, (mapsize-0.5) / texsize) (i.e. middle of last texel)
float s = (m_MapSize-1) / (float)(m_TextureSize * (m_MapSize-1) * CELL_SIZE);
float t = 0.5f / m_TextureSize;
m_TextureMatrix.SetZero();
m_TextureMatrix._11 = s;
m_TextureMatrix._23 = s;
m_TextureMatrix._14 = t;
m_TextureMatrix._24 = t;
m_TextureMatrix._44 = 1;
}
{
// Minimap matrix: We want to map UV (0,0)-(1,1) onto (0,0)-(mapsize/texsize, mapsize/texsize)
float s = m_MapSize / (float)m_TextureSize;
m_MinimapTextureMatrix.SetZero();
m_MinimapTextureMatrix._11 = s;
m_MinimapTextureMatrix._22 = s;
m_MinimapTextureMatrix._44 = 1;
}
}
void CLOSTexture::RecomputeTexture(int unit)
{
// If the map was resized, delete and regenerate the texture
if (m_Texture)
{
CmpPtr cmpTerrain(m_Simulation, SYSTEM_ENTITY);
if (!cmpTerrain.null() && m_MapSize != (ssize_t)cmpTerrain->GetVerticesPerSide())
DeleteTexture();
}
if (!m_Texture)
ConstructTexture(unit);
PROFILE("recompute LOS texture");
std::vector losData;
losData.resize(GetBitmapSize(m_MapSize, m_MapSize));
CmpPtr cmpRangeManager(m_Simulation, SYSTEM_ENTITY);
if (cmpRangeManager.null())
return;
ICmpRangeManager::CLosQuerier los (cmpRangeManager->GetLosQuerier(g_Game->GetPlayerID()));
GenerateBitmap(los, &losData[0], m_MapSize, m_MapSize);
g_Renderer.BindTexture(unit, m_Texture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize + g_BlurSize - 1, m_MapSize + g_BlurSize - 1, GL_ALPHA, GL_UNSIGNED_BYTE, &losData[0]);
}
size_t CLOSTexture::GetBitmapSize(size_t w, size_t h)
{
return (w + g_BlurSize - 1) * (h + g_BlurSize - 1);
}
void CLOSTexture::GenerateBitmap(ICmpRangeManager::CLosQuerier los, u8* losData, size_t w, size_t h)
{
const size_t rowSize = w + g_BlurSize-1; // size of losData rows
u8 *dataPtr = losData;
// Initialise the top padding
for (size_t j = 0; j < g_BlurSize/2; ++j)
for (size_t i = 0; i < rowSize; ++i)
*dataPtr++ = 0;
for (size_t j = 0; j < h; ++j)
{
// Initialise the left padding
for (size_t i = 0; i < g_BlurSize/2; ++i)
*dataPtr++ = 0;
// Fill in the visibility data
for (size_t i = 0; i < w; ++i)
{
if (los.IsVisible_UncheckedRange(i, j))
*dataPtr++ = 255;
else if (los.IsExplored_UncheckedRange(i, j))
*dataPtr++ = 127;
else
*dataPtr++ = 0;
}
// Initialise the right padding
for (size_t i = 0; i < g_BlurSize/2; ++i)
*dataPtr++ = 0;
}
// Initialise the bottom padding
for (size_t j = 0; j < g_BlurSize/2; ++j)
for (size_t i = 0; i < rowSize; ++i)
*dataPtr++ = 0;
// Horizontal blur:
for (size_t j = g_BlurSize/2; j < h + g_BlurSize/2; ++j)
{
for (size_t i = 0; i < w; ++i)
{
u8* d = &losData[i+j*rowSize];
*d = (
1*d[0] +
6*d[1] +
15*d[2] +
20*d[3] +
15*d[4] +
6*d[5] +
1*d[6]
) / 64;
}
}
// Vertical blur:
for (size_t j = 0; j < h; ++j)
{
for (size_t i = 0; i < w; ++i)
{
u8* d = &losData[i+j*rowSize];
*d = (
1*d[0*rowSize] +
6*d[1*rowSize] +
15*d[2*rowSize] +
20*d[3*rowSize] +
15*d[4*rowSize] +
6*d[5*rowSize] +
1*d[6*rowSize]
) / 64;
}
}
}
Index: ps/trunk/source/graphics/TerritoryTexture.h
===================================================================
--- ps/trunk/source/graphics/TerritoryTexture.h (nonexistent)
+++ ps/trunk/source/graphics/TerritoryTexture.h (revision 9889)
@@ -0,0 +1,89 @@
+/* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "lib/ogl.h"
+
+#include "maths/Matrix3D.h"
+#include "simulation2/helpers/Grid.h"
+
+class CSimulation2;
+
+/**
+ * Maintains the territory boundary texture, used for
+ * rendering and for the minimap.
+ */
+class CTerritoryTexture
+{
+ NONCOPYABLE(CTerritoryTexture);
+
+public:
+ CTerritoryTexture(CSimulation2& simulation);
+ ~CTerritoryTexture();
+
+ /**
+ * Recomputes the territory texture if necessary, and binds it to the requested
+ * texture unit.
+ * Also switches the current active texture unit, and enables texturing on it.
+ * The texture is in 32-bit BGRA format.
+ */
+ void BindTexture(int unit);
+
+ /**
+ * Recomputes the territory texture if necessary, and returns the texture handle.
+ * Also potentially switches the current active texture unit, and enables texturing on it.
+ * The texture is in 32-bit BGRA format.
+ */
+ GLuint GetTexture();
+
+ /**
+ * Returns a matrix to map (x,y,z) world coordinates onto (u,v) texture
+ * coordinates, in the form expected by glLoadMatrixf.
+ * This must only be called after BindTexture.
+ */
+ const float* GetTextureMatrix();
+
+ /**
+ * Returns a matrix to map (0,0)-(1,1) texture coordinates onto texture
+ * coordinates, in the form expected by glLoadMatrixf.
+ * This must only be called after BindTexture.
+ */
+ const float* GetMinimapTextureMatrix();
+
+private:
+ /**
+ * Returns true if the territory state has changed since the last call to this function
+ */
+ bool UpdateDirty();
+
+ void DeleteTexture();
+ void ConstructTexture(int unit);
+ void RecomputeTexture(int unit);
+
+ void GenerateBitmap(const Grid& territories, u8* bitmap, ssize_t w, ssize_t h);
+
+ CSimulation2& m_Simulation;
+
+ size_t m_DirtyID;
+
+ GLuint m_Texture;
+
+ ssize_t m_MapSize; // tiles per side
+ GLsizei m_TextureSize; // texels per side
+
+ CMatrix3D m_TextureMatrix;
+ CMatrix3D m_MinimapTextureMatrix;
+};
Property changes on: ps/trunk/source/graphics/TerritoryTexture.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/graphics/GameView.cpp
===================================================================
--- ps/trunk/source/graphics/GameView.cpp (revision 9888)
+++ ps/trunk/source/graphics/GameView.cpp (revision 9889)
@@ -1,1035 +1,1043 @@
/* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "GameView.h"
#include "graphics/Camera.h"
#include "graphics/CinemaTrack.h"
#include "graphics/ColladaManager.h"
#include "graphics/HFTracer.h"
#include "graphics/LightEnv.h"
#include "graphics/LOSTexture.h"
#include "graphics/Model.h"
#include "graphics/ObjectManager.h"
#include "graphics/Patch.h"
#include "graphics/SkeletonAnimManager.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureManager.h"
+#include "graphics/TerritoryTexture.h"
#include "graphics/Unit.h"
#include "graphics/UnitManager.h"
#include "lib/input.h"
#include "lib/timer.h"
#include "maths/Bound.h"
#include "maths/MathUtil.h"
#include "maths/Matrix3D.h"
#include "maths/Quaternion.h"
#include "ps/ConfigDB.h"
#include "ps/Game.h"
#include "ps/Globals.h"
#include "ps/Hotkey.h"
#include "ps/Joystick.h"
#include "ps/Loader.h"
#include "ps/LoaderThunks.h"
#include "ps/Profile.h"
#include "ps/Pyrogenesis.h"
#include "ps/World.h"
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
#include "scripting/ScriptableObject.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpRangeManager.h"
extern int g_xres, g_yres;
const float CGameView::defaultFOV = DEGTORAD(20.f);
const float CGameView::defaultNear = 16.f;
const float CGameView::defaultFar = 4096.f;
const float CGameView::defaultCullFOV = CGameView::defaultFOV + DEGTORAD(6.0f); //add 6 degrees to the default FOV for use with the culling frustum
// Maximum distance outside the edge of the map that the camera's
// focus point can be moved
static const float CAMERA_EDGE_MARGIN = 2.0f*CELL_SIZE;
/**
* A value with exponential decay towards the target value.
*/
class CSmoothedValue
{
public:
CSmoothedValue(float value, float smoothness, float minDelta)
: m_Target(value), m_Current(value), m_Smoothness(smoothness), m_MinDelta(minDelta)
{
}
float GetSmoothedValue()
{
return m_Current;
}
void SetValueSmoothly(float value)
{
m_Target = value;
}
void AddSmoothly(float value)
{
m_Target += value;
}
void Add(float value)
{
m_Target += value;
m_Current += value;
}
float GetValue()
{
return m_Target;
}
void SetValue(float value)
{
m_Target = value;
m_Current = value;
}
float Update(float time)
{
if (fabs(m_Target - m_Current) < m_MinDelta)
return 0.0f;
double p = pow((double)m_Smoothness, 10.0 * (double)time);
// (add the factor of 10 so that smoothnesses don't have to be tiny numbers)
double delta = (m_Target - m_Current) * (1.0 - p);
m_Current += delta;
return (float)delta;
}
void ClampSmoothly(float min, float max)
{
m_Target = Clamp(m_Target, (double)min, (double)max);
}
// Wrap so 'target' is in the range [min, max]
void Wrap(float min, float max)
{
double t = fmod(m_Target - min, (double)(max - min));
if (t < 0)
t += max - min;
t += min;
m_Current += t - m_Target;
m_Target = t;
}
private:
double m_Target; // the value which m_Current is tending towards
double m_Current;
// (We use double because the extra precision is worthwhile here)
float m_MinDelta; // cutoff where we stop moving (to avoid ugly shimmering effects)
public:
float m_Smoothness;
};
class CGameViewImpl : public CJSObject
{
NONCOPYABLE(CGameViewImpl);
public:
CGameViewImpl(CGame* game)
: Game(game),
ColladaManager(), MeshManager(ColladaManager), SkeletonAnimManager(ColladaManager),
ObjectManager(MeshManager, SkeletonAnimManager, *game->GetSimulation2()),
LOSTexture(*game->GetSimulation2()),
+ TerritoryTexture(*game->GetSimulation2()),
ViewCamera(),
CullCamera(),
LockCullCamera(false),
ConstrainCamera(true),
Culling(true),
FollowEntity(INVALID_ENTITY),
FollowFirstPerson(false),
// Dummy values (these will be filled in by the config file)
ViewScrollSpeed(0),
ViewRotateXSpeed(0),
ViewRotateXMin(0),
ViewRotateXMax(0),
ViewRotateXDefault(0),
ViewRotateYSpeed(0),
ViewRotateYSpeedWheel(0),
ViewRotateYDefault(0),
ViewDragSpeed(0),
ViewZoomSpeed(0),
ViewZoomSpeedWheel(0),
ViewZoomMin(0),
ViewZoomMax(0),
ViewZoomDefault(0),
JoystickPanX(-1),
JoystickPanY(-1),
JoystickRotateX(-1),
JoystickRotateY(-1),
JoystickZoomIn(-1),
JoystickZoomOut(-1),
PosX(0, 0, 0.01f),
PosY(0, 0, 0.01f),
PosZ(0, 0, 0.01f),
Zoom(0, 0, 0.1f),
RotateX(0, 0, 0.001f),
RotateY(0, 0, 0.001f)
{
}
CGame* Game;
CColladaManager ColladaManager;
CMeshManager MeshManager;
CSkeletonAnimManager SkeletonAnimManager;
CObjectManager ObjectManager;
CLOSTexture LOSTexture;
+ CTerritoryTexture TerritoryTexture;
/**
* this camera controls the eye position when rendering
*/
CCamera ViewCamera;
/**
* this camera controls the frustum that is used for culling
* and shadow calculations
*
* Note that all code that works with camera movements should only change
* m_ViewCamera. The render functions automatically sync the cull camera to
* the view camera depending on the value of m_LockCullCamera.
*/
CCamera CullCamera;
/**
* When @c true, the cull camera is locked in place.
* When @c false, the cull camera follows the view camera.
*
* Exposed to JS as gameView.lockCullCamera
*/
bool LockCullCamera;
/**
* When @c true, culling is enabled so that only models that have a chance of
* being visible are sent to the renderer.
* Otherwise, the entire world is sent to the renderer.
*
* Exposed to JS as gameView.culling
*/
bool Culling;
/**
* Whether the camera movement should be constrained by min/max limits
* and terrain avoidance.
*/
bool ConstrainCamera;
/**
* Cache global lighting environment. This is used to check whether the
* environment has changed during the last frame, so that vertex data can be updated etc.
*/
CLightEnv CachedLightEnv;
CCinemaManager TrackManager;
/**
* Entity for the camera to follow, or INVALID_ENTITY if none.
*/
entity_id_t FollowEntity;
/**
* Whether to follow FollowEntity in first-person mode.
*/
bool FollowFirstPerson;
////////////////////////////////////////
// Settings
float ViewScrollSpeed;
float ViewRotateXSpeed;
float ViewRotateXMin;
float ViewRotateXMax;
float ViewRotateXDefault;
float ViewRotateYSpeed;
float ViewRotateYSpeedWheel;
float ViewRotateYDefault;
float ViewDragSpeed;
float ViewZoomSpeed;
float ViewZoomSpeedWheel;
float ViewZoomMin;
float ViewZoomMax;
float ViewZoomDefault;
int JoystickPanX;
int JoystickPanY;
int JoystickRotateX;
int JoystickRotateY;
int JoystickZoomIn;
int JoystickZoomOut;
////////////////////////////////////////
// Camera Controls State
CSmoothedValue PosX;
CSmoothedValue PosY;
CSmoothedValue PosZ;
CSmoothedValue Zoom;
CSmoothedValue RotateX; // inclination around x axis (relative to camera)
CSmoothedValue RotateY; // rotation around y (vertical) axis
static void ScriptingInit();
};
static void SetupCameraMatrixSmooth(CGameViewImpl* m, CMatrix3D* orientation)
{
orientation->SetIdentity();
orientation->RotateX(m->RotateX.GetSmoothedValue());
orientation->RotateY(m->RotateY.GetSmoothedValue());
orientation->Translate(m->PosX.GetSmoothedValue(), m->PosY.GetSmoothedValue(), m->PosZ.GetSmoothedValue());
}
static void SetupCameraMatrixSmoothRot(CGameViewImpl* m, CMatrix3D* orientation)
{
orientation->SetIdentity();
orientation->RotateX(m->RotateX.GetSmoothedValue());
orientation->RotateY(m->RotateY.GetSmoothedValue());
orientation->Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
}
static void SetupCameraMatrixNonSmooth(CGameViewImpl* m, CMatrix3D* orientation)
{
orientation->SetIdentity();
orientation->RotateX(m->RotateX.GetValue());
orientation->RotateY(m->RotateY.GetValue());
orientation->Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
}
CGameView::CGameView(CGame *pGame):
m(new CGameViewImpl(pGame))
{
SViewPort vp;
vp.m_X=0;
vp.m_Y=0;
vp.m_Width=g_xres;
vp.m_Height=g_yres;
m->ViewCamera.SetViewPort(vp);
m->ViewCamera.SetProjection(defaultNear, defaultFar, defaultFOV);
SetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);
m->ViewCamera.UpdateFrustum();
m->CullCamera = m->ViewCamera;
g_Renderer.SetSceneCamera(m->ViewCamera, m->CullCamera);
}
CGameView::~CGameView()
{
UnloadResources();
delete m;
}
void CGameView::SetViewport(const SViewPort& vp)
{
m->ViewCamera.SetViewPort(vp);
m->ViewCamera.SetProjection(defaultNear, defaultFar, defaultFOV);
}
CObjectManager& CGameView::GetObjectManager() const
{
return m->ObjectManager;
}
JSObject* CGameView::GetScript()
{
return m->GetScript();
}
/*static*/ void CGameView::ScriptingInit()
{
return CGameViewImpl::ScriptingInit();
}
CCamera* CGameView::GetCamera()
{
return &m->ViewCamera;
}
CCinemaManager* CGameView::GetCinema()
{
return &m->TrackManager;
};
CLOSTexture& CGameView::GetLOSTexture()
{
return m->LOSTexture;
}
+CTerritoryTexture& CGameView::GetTerritoryTexture()
+{
+ return m->TerritoryTexture;
+}
+
void CGameViewImpl::ScriptingInit()
{
AddProperty(L"culling", &CGameViewImpl::Culling);
AddProperty(L"lockCullCamera", &CGameViewImpl::LockCullCamera);
AddProperty(L"constrainCamera", &CGameViewImpl::ConstrainCamera);
CJSObject::ScriptingInit("GameView");
}
int CGameView::Initialize()
{
CFG_GET_SYS_VAL("view.scroll.speed", Float, m->ViewScrollSpeed);
CFG_GET_SYS_VAL("view.rotate.x.speed", Float, m->ViewRotateXSpeed);
CFG_GET_SYS_VAL("view.rotate.x.min", Float, m->ViewRotateXMin);
CFG_GET_SYS_VAL("view.rotate.x.max", Float, m->ViewRotateXMax);
CFG_GET_SYS_VAL("view.rotate.x.default", Float, m->ViewRotateXDefault);
CFG_GET_SYS_VAL("view.rotate.y.speed", Float, m->ViewRotateYSpeed);
CFG_GET_SYS_VAL("view.rotate.y.speed.wheel", Float, m->ViewRotateYSpeedWheel);
CFG_GET_SYS_VAL("view.rotate.y.default", Float, m->ViewRotateYDefault);
CFG_GET_SYS_VAL("view.drag.speed", Float, m->ViewDragSpeed);
CFG_GET_SYS_VAL("view.zoom.speed", Float, m->ViewZoomSpeed);
CFG_GET_SYS_VAL("view.zoom.speed.wheel", Float, m->ViewZoomSpeedWheel);
CFG_GET_SYS_VAL("view.zoom.min", Float, m->ViewZoomMin);
CFG_GET_SYS_VAL("view.zoom.max", Float, m->ViewZoomMax);
CFG_GET_SYS_VAL("view.zoom.default", Float, m->ViewZoomDefault);
CFG_GET_SYS_VAL("joystick.camera.pan.x", Int, m->JoystickPanX);
CFG_GET_SYS_VAL("joystick.camera.pan.y", Int, m->JoystickPanY);
CFG_GET_SYS_VAL("joystick.camera.rotate.x", Int, m->JoystickRotateX);
CFG_GET_SYS_VAL("joystick.camera.rotate.y", Int, m->JoystickRotateY);
CFG_GET_SYS_VAL("joystick.camera.zoom.in", Int, m->JoystickZoomIn);
CFG_GET_SYS_VAL("joystick.camera.zoom.out", Int, m->JoystickZoomOut);
CFG_GET_SYS_VAL("view.pos.smoothness", Float, m->PosX.m_Smoothness);
CFG_GET_SYS_VAL("view.pos.smoothness", Float, m->PosY.m_Smoothness);
CFG_GET_SYS_VAL("view.pos.smoothness", Float, m->PosZ.m_Smoothness);
CFG_GET_SYS_VAL("view.zoom.smoothness", Float, m->Zoom.m_Smoothness);
CFG_GET_SYS_VAL("view.rotate.x.smoothness", Float, m->RotateX.m_Smoothness);
CFG_GET_SYS_VAL("view.rotate.y.smoothness", Float, m->RotateY.m_Smoothness);
m->RotateX.SetValue(DEGTORAD(m->ViewRotateXDefault));
m->RotateY.SetValue(DEGTORAD(m->ViewRotateYDefault));
return 0;
}
void CGameView::RegisterInit()
{
// CGameView init
RegMemFun(this, &CGameView::Initialize, L"CGameView init", 1);
// previously done by CGameView::InitResources
RegMemFun(g_TexMan.GetSingletonPtr(), &CTerrainTextureManager::LoadTerrainTextures, L"LoadTerrainTextures", 60);
RegMemFun(g_Renderer.GetSingletonPtr(), &CRenderer::LoadAlphaMaps, L"LoadAlphaMaps", 5);
RegMemFun(g_Renderer.GetSingletonPtr()->GetWaterManager(), &WaterManager::LoadWaterTextures, L"LoadWaterTextures", 80);
}
void CGameView::BeginFrame()
{
if (m->LockCullCamera == false)
{
// Set up cull camera
m->CullCamera = m->ViewCamera;
// One way to fix shadows popping in at the edge of the screen is to widen the culling frustum so that
// objects aren't culled as early. The downside is that objects will get rendered even though they appear
// off screen, which is somewhat inefficient. A better solution would be to decouple shadow map rendering
// from model rendering; as it is now, a shadow map is only rendered if its associated model is to be
// rendered.
// (See http://trac.wildfiregames.com/ticket/504)
m->CullCamera.SetProjection(defaultNear, defaultFar, defaultCullFOV);
m->CullCamera.UpdateFrustum();
}
g_Renderer.SetSceneCamera(m->ViewCamera, m->CullCamera);
CheckLightEnv();
m->Game->CachePlayerColours();
}
void CGameView::Render()
{
g_Renderer.RenderScene(*this);
}
///////////////////////////////////////////////////////////
// This callback is part of the Scene interface
// Submit all objects visible in the given frustum
void CGameView::EnumerateObjects(const CFrustum& frustum, SceneCollector* c)
{
PROFILE_START("submit terrain");
CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();
const ssize_t patchesPerSide = pTerrain->GetPatchesPerSide();
// find out which patches will be drawn
for (ssize_t j=0; jGetPatch(i,j); // can't fail
// If the patch is underwater, calculate a bounding box that also contains the water plane
CBound bounds = patch->GetBounds();
float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight + 0.001f;
if(bounds[1].Y < waterHeight) {
bounds[1].Y = waterHeight;
}
if (!m->Culling || frustum.IsBoxVisible (CVector3D(0,0,0), bounds)) {
//c->Submit(patch);
// set the renderstate for this patch
patch->setDrawState(true);
// set the renderstate for the neighbors
CPatch *nPatch;
nPatch = pTerrain->GetPatch(i-1,j-1);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i,j-1);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i+1,j-1);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i-1,j);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i+1,j);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i-1,j+1);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i,j+1);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i+1,j+1);
if(nPatch) nPatch->setDrawState(true);
}
}
}
// draw the patches
for (ssize_t j=0; jGetPatch(i,j); // can't fail
if(patch->getDrawState() == true)
{
c->Submit(patch);
patch->setDrawState(false);
}
}
}
PROFILE_END("submit terrain");
PROFILE_START("submit sim components");
m->Game->GetSimulation2()->RenderSubmit(*c, frustum, m->Culling);
PROFILE_END("submit sim components");
}
void CGameView::CheckLightEnv()
{
if (m->CachedLightEnv == g_LightEnv)
return;
if (m->CachedLightEnv.GetLightingModel() != g_LightEnv.GetLightingModel())
g_Renderer.MakeShadersDirty();
m->CachedLightEnv = g_LightEnv;
CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();
if (!pTerrain)
return;
PROFILE("update light env");
pTerrain->MakeDirty(RENDERDATA_UPDATE_COLOR);
const std::vector& units = m->Game->GetWorld()->GetUnitManager().GetUnits();
for (size_t i = 0; i < units.size(); ++i)
units[i]->GetModel().SetDirtyRec(RENDERDATA_UPDATE_COLOR);
}
void CGameView::UnloadResources()
{
g_TexMan.UnloadTerrainTextures();
g_Renderer.UnloadAlphaMaps();
g_Renderer.GetWaterManager()->UnloadWaterTextures();
}
static void ClampDistance(CGameViewImpl* m, bool smooth)
{
if (!m->ConstrainCamera)
return;
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);
CVector3D forwards = targetCam.m_Orientation.GetIn();
CVector3D delta = targetCam.GetFocus() - targetCam.m_Orientation.GetTranslation();
float dist = delta.Dot(forwards);
float clampedDist = Clamp(dist, m->ViewZoomMin, m->ViewZoomMax);
float diff = clampedDist - dist;
if (!diff)
return;
if (smooth)
{
m->PosX.AddSmoothly(forwards.X * -diff);
m->PosY.AddSmoothly(forwards.Y * -diff);
m->PosZ.AddSmoothly(forwards.Z * -diff);
}
else
{
m->PosX.Add(forwards.X * -diff);
m->PosY.Add(forwards.Y * -diff);
m->PosZ.Add(forwards.Z * -diff);
}
}
void CGameView::Update(float DeltaTime)
{
if (!g_app_has_focus)
return;
// TODO: this is probably not an ideal place for this, it should probably go
// in a CCmpWaterManager or some such thing (once such a thing exists)
if (!m->Game->m_Paused)
g_Renderer.GetWaterManager()->m_WaterTexTimer += DeltaTime;
if (m->TrackManager.IsActive() && m->TrackManager.IsPlaying())
{
if (! m->TrackManager.Update(DeltaTime))
{
// ResetCamera();
}
return;
}
// Calculate mouse movement
static int mouse_last_x = 0;
static int mouse_last_y = 0;
int mouse_dx = g_mouse_x - mouse_last_x;
int mouse_dy = g_mouse_y - mouse_last_y;
mouse_last_x = g_mouse_x;
mouse_last_y = g_mouse_y;
if (HotkeyIsPressed("camera.rotate.cw"))
m->RotateY.AddSmoothly(m->ViewRotateYSpeed * DeltaTime);
if (HotkeyIsPressed("camera.rotate.ccw"))
m->RotateY.AddSmoothly(-m->ViewRotateYSpeed * DeltaTime);
if (HotkeyIsPressed("camera.rotate.up"))
m->RotateX.AddSmoothly(-m->ViewRotateXSpeed * DeltaTime);
if (HotkeyIsPressed("camera.rotate.down"))
m->RotateX.AddSmoothly(m->ViewRotateXSpeed * DeltaTime);
float moveRightward = 0.f;
float moveForward = 0.f;
if (HotkeyIsPressed("camera.pan"))
{
moveRightward += m->ViewDragSpeed * mouse_dx;
moveForward += m->ViewDragSpeed * -mouse_dy;
}
if (g_mouse_active)
{
if (g_mouse_x >= g_xres - 2 && g_mouse_x < g_xres)
moveRightward += m->ViewScrollSpeed * DeltaTime;
else if (g_mouse_x <= 3 && g_mouse_x >= 0)
moveRightward -= m->ViewScrollSpeed * DeltaTime;
if (g_mouse_y >= g_yres - 2 && g_mouse_y < g_yres)
moveForward -= m->ViewScrollSpeed * DeltaTime;
else if (g_mouse_y <= 3 && g_mouse_y >= 0)
moveForward += m->ViewScrollSpeed * DeltaTime;
}
if (HotkeyIsPressed("camera.right"))
moveRightward += m->ViewScrollSpeed * DeltaTime;
if (HotkeyIsPressed("camera.left"))
moveRightward -= m->ViewScrollSpeed * DeltaTime;
if (HotkeyIsPressed("camera.up"))
moveForward += m->ViewScrollSpeed * DeltaTime;
if (HotkeyIsPressed("camera.down"))
moveForward -= m->ViewScrollSpeed * DeltaTime;
if (g_Joystick.IsEnabled())
{
// This could all be improved with extra speed and sensitivity settings
// (maybe use pow to allow finer control?), and inversion settings
moveRightward += g_Joystick.GetAxisValue(m->JoystickPanX) * m->ViewScrollSpeed * DeltaTime;
moveForward -= g_Joystick.GetAxisValue(m->JoystickPanY) * m->ViewScrollSpeed * DeltaTime;
m->RotateX.AddSmoothly(g_Joystick.GetAxisValue(m->JoystickRotateX) * m->ViewRotateXSpeed * DeltaTime);
m->RotateY.AddSmoothly(-g_Joystick.GetAxisValue(m->JoystickRotateY) * m->ViewRotateYSpeed * DeltaTime);
// Use a +1 bias for zoom because I want this to work with trigger buttons that default to -1
m->Zoom.AddSmoothly((g_Joystick.GetAxisValue(m->JoystickZoomIn) + 1.0f) / 2.0f * m->ViewZoomSpeed * DeltaTime);
m->Zoom.AddSmoothly(-(g_Joystick.GetAxisValue(m->JoystickZoomOut) + 1.0f) / 2.0f * m->ViewZoomSpeed * DeltaTime);
}
if (moveRightward || moveForward)
{
// Break out of following mode when the user starts scrolling
m->FollowEntity = INVALID_ENTITY;
float s = sin(m->RotateY.GetSmoothedValue());
float c = cos(m->RotateY.GetSmoothedValue());
m->PosX.AddSmoothly(c * moveRightward);
m->PosZ.AddSmoothly(-s * moveRightward);
m->PosX.AddSmoothly(s * moveForward);
m->PosZ.AddSmoothly(c * moveForward);
}
if (m->FollowEntity)
{
CmpPtr cmpPosition(*(m->Game->GetSimulation2()), m->FollowEntity);
if (!cmpPosition.null() && cmpPosition->IsInWorld())
{
// Get the most recent interpolated position
float frameOffset = m->Game->GetSimulation2()->GetLastFrameOffset();
CMatrix3D transform = cmpPosition->GetInterpolatedTransform(frameOffset, false);
CVector3D pos = transform.GetTranslation();
if (m->FollowFirstPerson)
{
float x, z, angle;
cmpPosition->GetInterpolatedPosition2D(frameOffset, x, z, angle);
float height = 4.f;
m->ViewCamera.m_Orientation.SetIdentity();
m->ViewCamera.m_Orientation.RotateX((float)M_PI/24.f);
m->ViewCamera.m_Orientation.RotateY(angle);
m->ViewCamera.m_Orientation.Translate(pos.X, pos.Y + height, pos.Z);
m->ViewCamera.UpdateFrustum();
return;
}
else
{
// Move the camera to match the unit
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);
CVector3D pivot = targetCam.GetFocus();
CVector3D delta = pos - pivot;
m->PosX.AddSmoothly(delta.X);
m->PosY.AddSmoothly(delta.Y);
m->PosZ.AddSmoothly(delta.Z);
}
}
else
{
// The unit disappeared (died or garrisoned etc), so stop following it
m->FollowEntity = INVALID_ENTITY;
}
}
if (HotkeyIsPressed("camera.zoom.in"))
m->Zoom.AddSmoothly(m->ViewZoomSpeed * DeltaTime);
if (HotkeyIsPressed("camera.zoom.out"))
m->Zoom.AddSmoothly(-m->ViewZoomSpeed * DeltaTime);
float zoomDelta = m->Zoom.Update(DeltaTime);
if (zoomDelta)
{
CVector3D forwards = m->ViewCamera.m_Orientation.GetIn();
m->PosX.AddSmoothly(forwards.X * zoomDelta);
m->PosY.AddSmoothly(forwards.Y * zoomDelta);
m->PosZ.AddSmoothly(forwards.Z * zoomDelta);
}
if (m->ConstrainCamera)
m->RotateX.ClampSmoothly(DEGTORAD(m->ViewRotateXMin), DEGTORAD(m->ViewRotateXMax));
ClampDistance(m, true);
// Ensure the ViewCamera focus is inside the map with the chosen margins
// if not so - apply margins to the camera
if (m->ConstrainCamera)
{
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);
CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();
CVector3D pivot = targetCam.GetFocus();
CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
CVector3D desiredPivot = pivot;
CmpPtr cmpRangeManager(*m->Game->GetSimulation2(), SYSTEM_ENTITY);
if (!cmpRangeManager.null() && cmpRangeManager->GetLosCircular())
{
// Clamp to a circular region around the center of the map
float r = pTerrain->GetMaxX() / 2;
CVector3D center(r, desiredPivot.Y, r);
float dist = (desiredPivot - center).Length();
if (dist > r + CAMERA_EDGE_MARGIN)
desiredPivot = center + (desiredPivot - center).Normalized() * (r + CAMERA_EDGE_MARGIN);
}
else
{
// Clamp to the square edges of the map
desiredPivot.X = Clamp(desiredPivot.X, pTerrain->GetMinX() - CAMERA_EDGE_MARGIN, pTerrain->GetMaxX() + CAMERA_EDGE_MARGIN);
desiredPivot.Z = Clamp(desiredPivot.Z, pTerrain->GetMinZ() - CAMERA_EDGE_MARGIN, pTerrain->GetMaxZ() + CAMERA_EDGE_MARGIN);
}
// Update the position so that pivot is within the margin
m->PosX.SetValueSmoothly(desiredPivot.X + delta.X);
m->PosZ.SetValueSmoothly(desiredPivot.Z + delta.Z);
}
m->PosX.Update(DeltaTime);
m->PosY.Update(DeltaTime);
m->PosZ.Update(DeltaTime);
// Handle rotation around the Y (vertical) axis
{
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixSmooth(m, &targetCam.m_Orientation);
float rotateYDelta = m->RotateY.Update(DeltaTime);
if (rotateYDelta)
{
// We've updated RotateY, and need to adjust Pos so that it's still
// facing towards the original focus point (the terrain in the center
// of the screen).
CVector3D upwards(0.0f, 1.0f, 0.0f);
CVector3D pivot = targetCam.GetFocus();
CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
CQuaternion q;
q.FromAxisAngle(upwards, rotateYDelta);
CVector3D d = q.Rotate(delta) - delta;
m->PosX.Add(d.X);
m->PosY.Add(d.Y);
m->PosZ.Add(d.Z);
}
}
// Handle rotation around the X (sideways, relative to camera) axis
{
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixSmooth(m, &targetCam.m_Orientation);
float rotateXDelta = m->RotateX.Update(DeltaTime);
if (rotateXDelta)
{
CVector3D rightwards = targetCam.m_Orientation.GetLeft() * -1.0f;
CVector3D pivot = m->ViewCamera.GetFocus();
CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
CQuaternion q;
q.FromAxisAngle(rightwards, rotateXDelta);
CVector3D d = q.Rotate(delta) - delta;
m->PosX.Add(d.X);
m->PosY.Add(d.Y);
m->PosZ.Add(d.Z);
}
}
/* This is disabled since it doesn't seem necessary:
// Ensure the camera's near point is never inside the terrain
if (m->ConstrainCamera)
{
CMatrix3D target;
target.SetIdentity();
target.RotateX(m->RotateX.GetValue());
target.RotateY(m->RotateY.GetValue());
target.Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
CVector3D nearPoint = target.GetTranslation() + target.GetIn() * defaultNear;
float ground = m->Game->GetWorld()->GetTerrain()->GetExactGroundLevel(nearPoint.X, nearPoint.Z);
float limit = ground + 16.f;
if (nearPoint.Y < limit)
m->PosY.AddSmoothly(limit - nearPoint.Y);
}
*/
m->RotateY.Wrap(-(float)M_PI, (float)M_PI);
// Update the camera matrix
SetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);
m->ViewCamera.UpdateFrustum();
}
void CGameView::MoveCameraTarget(const CVector3D& target, bool minimap)
{
// Maintain the same orientation and level of zoom, if we can
// (do this by working out the point the camera is looking at, saving
// the difference between that position and the camera point, and restoring
// that difference to our new target)
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixNonSmooth(m, &targetCam.m_Orientation);
CVector3D pivot = targetCam.GetFocus();
CVector3D delta = target - pivot;
//If minimap movement, maintain previous zoom level by not changing Y position
// - this prevents strange behavior when moving across changes in terrain height
if (!minimap)
m->PosY.SetValueSmoothly(delta.Y + m->PosY.GetValue());
m->PosX.SetValueSmoothly(delta.X + m->PosX.GetValue());
m->PosZ.SetValueSmoothly(delta.Z + m->PosZ.GetValue());
ClampDistance(m, false);
// Break out of following mode so the camera really moves to the target
m->FollowEntity = INVALID_ENTITY;
}
void CGameView::ResetCameraTarget(const CVector3D& target)
{
CMatrix3D orientation;
orientation.SetIdentity();
orientation.RotateX(DEGTORAD(m->ViewRotateXDefault));
orientation.RotateY(DEGTORAD(m->ViewRotateYDefault));
CVector3D delta = orientation.GetIn() * m->ViewZoomDefault;
m->PosX.SetValue(target.X - delta.X);
m->PosY.SetValue(target.Y - delta.Y);
m->PosZ.SetValue(target.Z - delta.Z);
m->RotateX.SetValue(DEGTORAD(m->ViewRotateXDefault));
m->RotateY.SetValue(DEGTORAD(m->ViewRotateYDefault));
ClampDistance(m, false);
SetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);
m->ViewCamera.UpdateFrustum();
// Break out of following mode so the camera really moves to the target
m->FollowEntity = INVALID_ENTITY;
}
void CGameView::ResetCameraAngleZoom()
{
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixNonSmooth(m, &targetCam.m_Orientation);
// Compute the zoom adjustment to get us back to the default
CVector3D forwards = targetCam.m_Orientation.GetIn();
CVector3D delta = targetCam.GetFocus() - targetCam.m_Orientation.GetTranslation();
float dist = delta.Dot(forwards);
m->Zoom.AddSmoothly(dist - m->ViewZoomDefault);
// Reset orientations to default
m->RotateX.SetValueSmoothly(DEGTORAD(m->ViewRotateXDefault));
m->RotateY.SetValueSmoothly(DEGTORAD(m->ViewRotateYDefault));
}
void CGameView::CameraFollow(entity_id_t entity, bool firstPerson)
{
m->FollowEntity = entity;
m->FollowFirstPerson = firstPerson;
}
entity_id_t CGameView::GetFollowedEntity()
{
return m->FollowEntity;
}
InReaction game_view_handler(const SDL_Event_* ev)
{
// put any events that must be processed even if inactive here
if(!g_app_has_focus || !g_Game || !g_Game->IsGameStarted())
return IN_PASS;
CGameView *pView=g_Game->GetView();
return pView->HandleEvent(ev);
}
InReaction CGameView::HandleEvent(const SDL_Event_* ev)
{
switch(ev->ev.type)
{
case SDL_HOTKEYDOWN:
std::string hotkey = static_cast(ev->ev.user.data1);
if (hotkey == "wireframe")
{
if (g_Renderer.GetModelRenderMode() == SOLID)
{
g_Renderer.SetTerrainRenderMode(EDGED_FACES);
g_Renderer.SetModelRenderMode(EDGED_FACES);
}
else if (g_Renderer.GetModelRenderMode() == EDGED_FACES)
{
g_Renderer.SetTerrainRenderMode(WIREFRAME);
g_Renderer.SetModelRenderMode(WIREFRAME);
}
else
{
g_Renderer.SetTerrainRenderMode(SOLID);
g_Renderer.SetModelRenderMode(SOLID);
}
return IN_HANDLED;
}
// Mouse wheel must be treated using events instead of polling,
// because SDL auto-generates a sequence of mousedown/mouseup events
// and we never get to see the "down" state inside Update().
else if (hotkey == "camera.zoom.wheel.in")
{
m->Zoom.AddSmoothly(m->ViewZoomSpeedWheel);
return IN_HANDLED;
}
else if (hotkey == "camera.zoom.wheel.out")
{
m->Zoom.AddSmoothly(-m->ViewZoomSpeedWheel);
return IN_HANDLED;
}
else if (hotkey == "camera.rotate.wheel.cw")
{
m->RotateY.AddSmoothly(m->ViewRotateYSpeedWheel);
return IN_HANDLED;
}
else if (hotkey == "camera.rotate.wheel.ccw")
{
m->RotateY.AddSmoothly(-m->ViewRotateYSpeedWheel);
return IN_HANDLED;
}
else if (hotkey == "camera.reset")
{
ResetCameraAngleZoom();
return IN_HANDLED;
}
}
return IN_PASS;
}
Index: ps/trunk/source/graphics/GameView.h
===================================================================
--- ps/trunk/source/graphics/GameView.h (revision 9888)
+++ ps/trunk/source/graphics/GameView.h (revision 9889)
@@ -1,99 +1,99 @@
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#ifndef INCLUDED_GAMEVIEW
#define INCLUDED_GAMEVIEW
#include "renderer/Scene.h"
#include "simulation2/system/Entity.h"
#include "lib/input.h" // InReaction - can't forward-declare enum
class CGame;
class CObjectManager;
class CCamera;
class CCinemaManager;
-class CLOSTexture;
class CVector3D;
struct SViewPort;
struct JSObject;
class CGameViewImpl;
class CGameView : private Scene
{
NONCOPYABLE(CGameView);
public:
static const float defaultFOV, defaultCullFOV, defaultNear, defaultFar;
private:
CGameViewImpl* m;
// Check whether lighting environment has changed and update vertex data if necessary
void CheckLightEnv();
public:
//BEGIN: Implementation of Scene
virtual void EnumerateObjects(const CFrustum& frustum, SceneCollector* c);
virtual CLOSTexture& GetLOSTexture();
+ virtual CTerritoryTexture& GetTerritoryTexture();
//END: Implementation of Scene
private:
// InitResources(): Load all graphics resources (textures, actor objects and
// alpha maps) required by the game
//void InitResources();
// UnloadResources(): Unload all graphics resources loaded by InitResources
void UnloadResources();
public:
CGameView(CGame *pGame);
~CGameView();
void SetViewport(const SViewPort& vp);
void RegisterInit();
int Initialize();
CObjectManager& GetObjectManager() const;
// Update: Update all the view information (i.e. rotate camera, scroll,
// whatever). This will *not* change any World information - only the
// *presentation*
void Update(float DeltaTime);
void BeginFrame();
void Render();
InReaction HandleEvent(const SDL_Event_* ev);
void MoveCameraTarget(const CVector3D& target, bool minimap = false);
void ResetCameraTarget(const CVector3D& target);
void ResetCameraAngleZoom();
void CameraFollow(entity_id_t entity, bool firstPerson);
entity_id_t GetFollowedEntity();
CCamera *GetCamera();
CCinemaManager* GetCinema();
JSObject* GetScript();
static void ScriptingInit();
};
extern InReaction game_view_handler(const SDL_Event_* ev);
#endif
Index: ps/trunk/source/graphics/TerritoryTexture.cpp
===================================================================
--- ps/trunk/source/graphics/TerritoryTexture.cpp (nonexistent)
+++ ps/trunk/source/graphics/TerritoryTexture.cpp (revision 9889)
@@ -0,0 +1,260 @@
+/* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "precompiled.h"
+
+#include "TerritoryTexture.h"
+
+#include "graphics/Terrain.h"
+#include "lib/bits.h"
+#include "ps/Profile.h"
+#include "renderer/Renderer.h"
+#include "simulation2/Simulation2.h"
+#include "simulation2/components/ICmpTerrain.h"
+#include "simulation2/components/ICmpTerritoryManager.h"
+
+// TODO: There's a lot of duplication with CLOSTexture - might be nice to refactor a bit
+
+CTerritoryTexture::CTerritoryTexture(CSimulation2& simulation) :
+ m_Simulation(simulation), m_DirtyID(0), m_Texture(0), m_MapSize(0), m_TextureSize(0)
+{
+}
+
+CTerritoryTexture::~CTerritoryTexture()
+{
+ if (m_Texture)
+ DeleteTexture();
+}
+
+void CTerritoryTexture::DeleteTexture()
+{
+ glDeleteTextures(1, &m_Texture);
+ m_Texture = 0;
+}
+
+bool CTerritoryTexture::UpdateDirty()
+{
+ CmpPtr cmpTerritoryManager(m_Simulation, SYSTEM_ENTITY);
+ if (cmpTerritoryManager.null())
+ return false;
+
+ return cmpTerritoryManager->NeedUpdate(&m_DirtyID);
+}
+
+void CTerritoryTexture::BindTexture(int unit)
+{
+ if (UpdateDirty())
+ RecomputeTexture(unit);
+
+ g_Renderer.BindTexture(unit, m_Texture);
+}
+
+GLuint CTerritoryTexture::GetTexture()
+{
+ if (UpdateDirty())
+ RecomputeTexture(0);
+
+ return m_Texture;
+}
+
+const float* CTerritoryTexture::GetTextureMatrix()
+{
+ ENSURE(!UpdateDirty());
+ return &m_TextureMatrix._11;
+}
+
+const float* CTerritoryTexture::GetMinimapTextureMatrix()
+{
+ ENSURE(!UpdateDirty());
+ return &m_MinimapTextureMatrix._11;
+}
+
+void CTerritoryTexture::ConstructTexture(int unit)
+{
+ CmpPtr cmpTerrain(m_Simulation, SYSTEM_ENTITY);
+ if (cmpTerrain.null())
+ return;
+
+ m_MapSize = cmpTerrain->GetVerticesPerSide() - 1;
+
+ m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize);
+
+ glGenTextures(1, &m_Texture);
+ g_Renderer.BindTexture(unit, m_Texture);
+
+ // Initialise texture with transparency, for the areas we don't
+ // overwrite with glTexSubImage2D later
+ u8* texData = new u8[m_TextureSize * m_TextureSize * 4];
+ memset(texData, 0x00, m_TextureSize * m_TextureSize * 4);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_TextureSize, m_TextureSize, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, texData);
+ delete[] texData;
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ {
+ // Texture matrix: We want to map
+ // world pos (0, y, 0) (i.e. bottom-left of first tile)
+ // onto texcoord (0, 0) (i.e. bottom-left of first texel);
+ // world pos (mapsize*cellsize, y, mapsize*cellsize) (i.e. top-right of last tile)
+ // onto texcoord (mapsize / texsize, mapsize / texsize) (i.e. top-right of last texel)
+
+ float s = 1.f / (float)(m_TextureSize * CELL_SIZE);
+ float t = 0.f;
+ m_TextureMatrix.SetZero();
+ m_TextureMatrix._11 = s;
+ m_TextureMatrix._23 = s;
+ m_TextureMatrix._14 = t;
+ m_TextureMatrix._24 = t;
+ m_TextureMatrix._44 = 1;
+ }
+
+ {
+ // Minimap matrix: We want to map UV (0,0)-(1,1) onto (0,0)-(mapsize/texsize, mapsize/texsize)
+
+ float s = m_MapSize / (float)m_TextureSize;
+ m_MinimapTextureMatrix.SetZero();
+ m_MinimapTextureMatrix._11 = s;
+ m_MinimapTextureMatrix._22 = s;
+ m_MinimapTextureMatrix._44 = 1;
+ }
+}
+
+void CTerritoryTexture::RecomputeTexture(int unit)
+{
+ // If the map was resized, delete and regenerate the texture
+ if (m_Texture)
+ {
+ CmpPtr cmpTerrain(m_Simulation, SYSTEM_ENTITY);
+ if (!cmpTerrain.null() && m_MapSize != (ssize_t)cmpTerrain->GetVerticesPerSide())
+ DeleteTexture();
+ }
+
+ if (!m_Texture)
+ ConstructTexture(unit);
+
+ PROFILE("recompute territory texture");
+
+ std::vector bitmap;
+ bitmap.resize(m_MapSize * m_MapSize * 4);
+
+ CmpPtr cmpTerritoryManager(m_Simulation, SYSTEM_ENTITY);
+ if (cmpTerritoryManager.null())
+ return;
+
+ const Grid territories = cmpTerritoryManager->GetTerritoryGrid();
+
+ GenerateBitmap(territories, &bitmap[0], m_MapSize, m_MapSize);
+
+ g_Renderer.BindTexture(unit, m_Texture);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize, m_MapSize, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &bitmap[0]);
+}
+
+void CTerritoryTexture::GenerateBitmap(const Grid& territories, u8* bitmap, ssize_t w, ssize_t h)
+{
+ int alphaMax = 0xC0;
+ int alphaFalloff = 0x20;
+
+ u8* p = bitmap;
+ for (ssize_t j = 0; j < h; ++j)
+ {
+ for (ssize_t i = 0; i < w; ++i)
+ {
+ u8 val = territories.get(i, j);
+ switch (val)
+ {
+ // TODO: use player colours or something
+ case 1: *p++ = 0x00; *p++ = 0x00; *p++ = 0xFF; break;
+ case 2: *p++ = 0x00; *p++ = 0xFF; *p++ = 0x00; break;
+ case 3: *p++ = 0xFF; *p++ = 0x00; *p++ = 0x00; break;
+ case 4: *p++ = 0x00; *p++ = 0xFF; *p++ = 0xFF; break;
+ case 5: *p++ = 0xFF; *p++ = 0xFF; *p++ = 0x00; break;
+ case 6: *p++ = 0xFF; *p++ = 0x00; *p++ = 0xFF; break;
+ default: *p++ = 0xFF; *p++ = 0xFF; *p++ = 0xFF; break;
+ }
+
+ if ((i > 0 && territories.get(i-1, j) != val)
+ || (i < w-1 && territories.get(i+1, j) != val)
+ || (j > 0 && territories.get(i, j-1) != val)
+ || (j < h-1 && territories.get(i, j+1) != val)
+// || (i > 0 && j > 0 && territories.get(i-1, j-1) != val)
+// || (i < w-1 && j > 0 && territories.get(i+1, j-1) != val)
+// || (i > 0 && j > h-1 && territories.get(i-1, j+1) != val)
+// || (i < w-1 && j < h-1 && territories.get(i+1, j+1) != val)
+ )
+ {
+ *p++ = alphaMax;
+ }
+ else
+ {
+ *p++ = 0x00;
+ }
+ }
+ }
+
+ // Do a low-quality cheap blur effect
+
+ for (ssize_t j = 0; j < h; ++j)
+ {
+ int a;
+
+ a = 0;
+ for (ssize_t i = 0; i < w; ++i)
+ {
+ a = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]);
+ bitmap[(j*w+i)*4 + 3] = a;
+ }
+
+ a = 0;
+ for (ssize_t i = w-1; i >= 0; --i)
+ {
+ a = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]);
+ bitmap[(j*w+i)*4 + 3] = a;
+ }
+ }
+
+ for (ssize_t i = 0; i < w; ++i)
+ {
+ int a;
+
+ a = 0;
+ for (ssize_t j = 0; j < w; ++j)
+ {
+ a = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]);
+ bitmap[(j*w+i)*4 + 3] = a;
+ }
+
+ a = 0;
+ for (ssize_t j = w-1; j >= 0; --j)
+ {
+ a = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]);
+ bitmap[(j*w+i)*4 + 3] = a;
+ }
+ }
+
+ // Add a gap between the boundaries, by deleting the max-alpha tiles
+ for (ssize_t j = 0; j < h; ++j)
+ {
+ for (ssize_t i = 0; i < w; ++i)
+ {
+ if (bitmap[(j*w+i)*4 + 3] == alphaMax)
+ bitmap[(j*w+i)*4 + 3] = 0;
+ }
+ }
+}
Property changes on: ps/trunk/source/graphics/TerritoryTexture.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/renderer/Scene.h
===================================================================
--- ps/trunk/source/renderer/Scene.h (revision 9888)
+++ ps/trunk/source/renderer/Scene.h (revision 9889)
@@ -1,121 +1,127 @@
/* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/**
* File : Scene.h
* Project : graphics
* Description : This file contains the interfaces that are used to send a
* : scene to the renderer, and for the renderer to query objects
* : in that scene.
*
* @note This file would fit just as well into the graphics/ subdirectory.
**/
#ifndef INCLUDED_SCENE
#define INCLUDED_SCENE
class CFrustum;
class CModel;
class CModelAbstract;
class CModelDecal;
class CParticleEmitter;
class CPatch;
class CLOSTexture;
+class CTerritoryTexture;
struct SOverlayLine;
struct SOverlaySprite;
class SceneCollector;
/**
* This interface describes a scene to the renderer.
*
* @see CRenderer::RenderScene
*/
class Scene
{
public:
virtual ~Scene() {}
/**
* Send all objects that can be seen when rendering the given frustum
* to the scene collector.
* @param frustum The frustum that will be used for rendering.
* @param c The scene collector that should receive objects inside the frustum
* that are visible.
*/
virtual void EnumerateObjects(const CFrustum& frustum, SceneCollector* c) = 0;
/**
* Return the LOS texture to be used for rendering this scene.
*/
virtual CLOSTexture& GetLOSTexture() = 0;
+
+ /**
+ * Return the territory texture to be used for rendering this scene.
+ */
+ virtual CTerritoryTexture& GetTerritoryTexture() = 0;
};
/**
* This interface accepts renderable objects.
*
* @see Scene::EnumerateObjects
*/
class SceneCollector
{
public:
virtual ~SceneCollector() {}
/**
* Submit a terrain patch that is part of the scene.
*/
virtual void Submit(CPatch* patch) = 0;
/**
* Submit a line-based overlay.
*/
virtual void Submit(SOverlayLine* overlay) = 0;
/**
* Submit a sprite overlay.
*/
virtual void Submit(SOverlaySprite* overlay) = 0;
/**
* Submit a terrain decal.
*/
virtual void Submit(CModelDecal* decal) = 0;
/**
* Submit a particle emitter.
*/
virtual void Submit(CParticleEmitter* emitter) = 0;
/**
* Submit a model that is part of the scene,
* without submitting attached models.
*/
virtual void SubmitNonRecursive(CModel* model) = 0;
/**
* Submit a model that is part of the scene,
* including attached sub-models.
*
* @note This function is implemented using SubmitNonRecursive,
* so you shouldn't have to reimplement it.
*/
virtual void SubmitRecursive(CModelAbstract* model);
};
#endif // INCLUDED_SCENE
Index: ps/trunk/source/renderer/TerrainRenderer.cpp
===================================================================
--- ps/trunk/source/renderer/TerrainRenderer.cpp (revision 9888)
+++ ps/trunk/source/renderer/TerrainRenderer.cpp (revision 9889)
@@ -1,858 +1,863 @@
/* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
* Terrain rendering (everything related to patches and water) is
* encapsulated in TerrainRenderer
*/
#include "precompiled.h"
#include "graphics/Camera.h"
#include "graphics/Decal.h"
#include "graphics/LightEnv.h"
#include "graphics/LOSTexture.h"
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
#include "graphics/GameView.h"
#include "graphics/Model.h"
#include "graphics/ShaderManager.h"
+#include "graphics/TerritoryTexture.h"
#include "maths/MathUtil.h"
#include "ps/Filesystem.h"
#include "ps/CLogger.h"
#include "ps/Font.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "ps/World.h"
#include "renderer/DecalRData.h"
#include "renderer/PatchRData.h"
#include "renderer/Renderer.h"
#include "renderer/ShadowMap.h"
#include "renderer/TerrainRenderer.h"
#include "renderer/VertexArray.h"
#include "renderer/WaterManager.h"
#include "lib/res/graphics/ogl_shader.h"
///////////////////////////////////////////////////////////////////////////////////////////////
// TerrainRenderer implementation
/**
* TerrainRenderer keeps track of which phase it is in, to detect
* when Submit, PrepareForRendering etc. are called in the wrong order.
*/
enum Phase {
Phase_Submit,
Phase_Render
};
/**
* Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class.
*/
struct TerrainRendererInternals
{
/// Which phase (submitting or rendering patches) are we in right now?
Phase phase;
/// Patches that were submitted for this frame
std::vector visiblePatches;
std::vector filteredPatches;
/// Decals that were submitted for this frame
std::vector visibleDecals;
std::vector filteredDecals;
/// Fancy water shader
Handle fancyWaterShader;
};
///////////////////////////////////////////////////////////////////
// Construction/Destruction
TerrainRenderer::TerrainRenderer()
{
m = new TerrainRendererInternals();
m->phase = Phase_Submit;
m->fancyWaterShader = 0;
}
TerrainRenderer::~TerrainRenderer()
{
if( m->fancyWaterShader )
{
ogl_program_free( m->fancyWaterShader );
}
delete m;
}
///////////////////////////////////////////////////////////////////
// Submit a patch for rendering
void TerrainRenderer::Submit(CPatch* patch)
{
ENSURE(m->phase == Phase_Submit);
CPatchRData* data = (CPatchRData*)patch->GetRenderData();
if (data == 0)
{
// no renderdata for patch, create it now
data = new CPatchRData(patch);
patch->SetRenderData(data);
}
data->Update();
m->visiblePatches.push_back(data);
}
///////////////////////////////////////////////////////////////////
// Submit a decal for rendering
void TerrainRenderer::Submit(CModelDecal* decal)
{
ENSURE(m->phase == Phase_Submit);
CDecalRData* data = (CDecalRData*)decal->GetRenderData();
if (data == 0)
{
// no renderdata for decal, create it now
data = new CDecalRData(decal);
decal->SetRenderData(data);
}
data->Update();
m->visibleDecals.push_back(data);
}
///////////////////////////////////////////////////////////////////
// Prepare for rendering
void TerrainRenderer::PrepareForRendering()
{
ENSURE(m->phase == Phase_Submit);
m->phase = Phase_Render;
}
///////////////////////////////////////////////////////////////////
// Clear submissions lists
void TerrainRenderer::EndFrame()
{
ENSURE(m->phase == Phase_Render || m->phase == Phase_Submit);
m->visiblePatches.clear();
m->visibleDecals.clear();
m->phase = Phase_Submit;
}
///////////////////////////////////////////////////////////////////
// Culls patches and decals against a frustum.
bool TerrainRenderer::CullPatches(const CFrustum* frustum)
{
m->filteredPatches.clear();
for (std::vector::iterator it = m->visiblePatches.begin(); it != m->visiblePatches.end(); it++)
{
if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetPatch()->GetBounds()))
m->filteredPatches.push_back(*it);
}
m->filteredDecals.clear();
for (std::vector::iterator it = m->visibleDecals.begin(); it != m->visibleDecals.end(); it++)
{
if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetDecal()->GetBounds()))
m->filteredDecals.push_back(*it);
}
return !m->filteredPatches.empty() || !m->filteredDecals.empty();
}
///////////////////////////////////////////////////////////////////
// Full-featured terrain rendering with blending and everything
void TerrainRenderer::RenderTerrain(bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
std::vector& visibleDecals = filtered ? m->filteredDecals : m->visibleDecals;
if (visiblePatches.empty() && visibleDecals.empty())
return;
// render the solid black sides of the map first
g_Renderer.BindTexture(0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glColor3f(0, 0, 0);
PROFILE_START("render terrain sides");
for (size_t i = 0; i < visiblePatches.size(); ++i)
visiblePatches[i]->RenderSides();
PROFILE_END("render terrain sides");
// switch on required client states
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// render everything fullbright
// set up texture environment for base pass
pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
// Set alpha to 1.0
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
static const float one[4] = { 1.f, 1.f, 1.f, 1.f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one);
PROFILE_START("render terrain base");
CPatchRData::RenderBases(visiblePatches);
PROFILE_END("render terrain base");
// render blends
// switch on the composite alpha map texture
(void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1);
// switch on second uv set
pglClientActiveTextureARB(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// setup additional texenv required by blend pass
pglActiveTextureARB(GL_TEXTURE1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA);
// switch on blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
// no need to write to the depth buffer a second time
glDepthMask(0);
// The decal color array contains lighting data, which we don't want in this non-shader mode
glDisableClientState(GL_COLOR_ARRAY);
// render blend passes for each patch
PROFILE_START("render terrain blends");
CPatchRData::RenderBlends(visiblePatches);
PROFILE_END("render terrain blends");
// Disable second texcoord array
pglClientActiveTextureARB(GL_TEXTURE1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
// Render terrain decals
g_Renderer.BindTexture(1, 0);
pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
PROFILE_START("render terrain decals");
for (size_t i = 0; i < visibleDecals.size(); ++i)
visibleDecals[i]->Render(CShaderProgramPtr());
PROFILE_END("render terrain decals");
// Now apply lighting
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
pglClientActiveTextureARB(GL_TEXTURE0);
glEnableClientState(GL_COLOR_ARRAY); // diffuse lighting colours
glBlendFunc(GL_DST_COLOR, GL_ZERO);
// GL_TEXTURE_ENV_COLOR requires four floats, so we shouldn't use the RGBColor directly
float terrainAmbientColor[4] = {
lightEnv.m_TerrainAmbientColor.X,
lightEnv.m_TerrainAmbientColor.Y,
lightEnv.m_TerrainAmbientColor.Z,
1.f
};
CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture();
int streamflags = STREAM_POS|STREAM_COLOR;
pglActiveTextureARB(GL_TEXTURE0);
// We're not going to use a texture here, but we have to have a valid texture
// bound else the texture unit will be disabled.
// We should still have a bound splat texture from some earlier rendering,
// so assume that's still valid to use.
// (TODO: That's a bit of an ugly hack.)
// No shadows: (Ambient + Diffuse) * LOS
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor);
losTexture.BindTexture(1);
pglClientActiveTextureARB(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
streamflags |= STREAM_POSTOUV1;
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(losTexture.GetTextureMatrix());
glMatrixMode(GL_MODELVIEW);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0);
PROFILE_START("render terrain streams");
CPatchRData::RenderStreams(visiblePatches, streamflags);
PROFILE_END("render terrain streams");
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
// restore OpenGL state
g_Renderer.BindTexture(1, 0);
pglClientActiveTextureARB(GL_TEXTURE1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
pglClientActiveTextureARB(GL_TEXTURE0);
pglActiveTextureARB(GL_TEXTURE0);
glDepthMask(1);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
///////////////////////////////////////////////////////////////////
/**
* Set up all the uniforms for a shader pass.
*/
void TerrainRenderer::PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shadow)
{
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
if (shadow)
{
shader->BindTexture("shadowTex", shadow->GetTexture());
shader->Uniform("shadowTransform", shadow->GetTextureMatrix());
const float* offsets = shadow->GetFilterOffsets();
shader->Uniform("shadowOffsets1", offsets[0], offsets[1], offsets[2], offsets[3]);
shader->Uniform("shadowOffsets2", offsets[4], offsets[5], offsets[6], offsets[7]);
}
CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
shader->BindTexture("losTex", los.GetTexture());
shader->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
+ CTerritoryTexture& territory = g_Renderer.GetScene().GetTerritoryTexture();
+ shader->BindTexture("territoryTex", territory.GetTexture());
+ shader->Uniform("territoryTransform", territory.GetTextureMatrix()[0], territory.GetTextureMatrix()[12], 0.f, 0.f);
+
shader->Uniform("ambient", lightEnv.m_TerrainAmbientColor);
shader->Uniform("sunColor", lightEnv.m_SunColor);
}
void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow, bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
std::vector& visibleDecals = filtered ? m->filteredDecals : m->visibleDecals;
if (visiblePatches.empty() && visibleDecals.empty())
return;
CShaderManager& shaderManager = g_Renderer.GetShaderManager();
typedef std::map Defines;
Defines defBasic;
if (shadow)
{
defBasic["USE_SHADOW"] = "1";
if (g_Renderer.m_Caps.m_ARBProgramShadow && g_Renderer.m_Options.m_ARBProgramShadow)
defBasic["USE_FP_SHADOW"] = "1";
if (g_Renderer.m_Options.m_ShadowPCF)
defBasic["USE_SHADOW_PCF"] = "1";
}
defBasic["LIGHTING_MODEL_" + g_Renderer.GetLightEnv().GetLightingModel()] = "1";
CShaderProgramPtr shaderBase(shaderManager.LoadProgram("terrain_base", defBasic));
CShaderProgramPtr shaderBlend(shaderManager.LoadProgram("terrain_blend", defBasic));
CShaderProgramPtr shaderDecal(shaderManager.LoadProgram("terrain_decal", defBasic));
// render the solid black sides of the map first
g_Renderer.BindTexture(0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glColor3f(0, 0, 0);
PROFILE_START("render terrain sides");
for (size_t i = 0; i < visiblePatches.size(); ++i)
visiblePatches[i]->RenderSides();
PROFILE_END("render terrain sides");
// switch on required client states
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY); // diffuse lighting colours
shaderBase->Bind();
PrepareShader(shaderBase, shadow);
PROFILE_START("render terrain base");
CPatchRData::RenderBases(visiblePatches);
PROFILE_END("render terrain base");
shaderBase->Unbind();
// render blends
shaderBlend->Bind();
PrepareShader(shaderBlend, shadow);
// switch on the composite alpha map texture
(void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1);
// switch on second uv set
pglClientActiveTextureARB(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// switch on blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// no need to write to the depth buffer a second time
glDepthMask(0);
// render blend passes for each patch
PROFILE_START("render terrain blends");
CPatchRData::RenderBlends(visiblePatches);
PROFILE_END("render terrain blends");
// Disable second texcoord array
pglClientActiveTextureARB(GL_TEXTURE1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
shaderBlend->Unbind();
// Render terrain decals
shaderDecal->Bind();
PrepareShader(shaderDecal, shadow);
g_Renderer.BindTexture(1, 0);
pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0);
PROFILE_START("render terrain decals");
for (size_t i = 0; i < visibleDecals.size(); ++i)
visibleDecals[i]->Render(shaderDecal);
PROFILE_END("render terrain decals");
shaderDecal->Unbind();
// restore OpenGL state
g_Renderer.BindTexture(1, 0);
g_Renderer.BindTexture(2, 0);
g_Renderer.BindTexture(3, 0);
pglClientActiveTextureARB(GL_TEXTURE0);
pglActiveTextureARB(GL_TEXTURE0);
glDepthMask(1);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render un-textured patches as polygons
void TerrainRenderer::RenderPatches(bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
if (visiblePatches.empty())
return;
glEnableClientState(GL_VERTEX_ARRAY);
CPatchRData::RenderStreams(visiblePatches, STREAM_POS);
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render outlines of submitted patches as lines
void TerrainRenderer::RenderOutlines(bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
if (visiblePatches.empty())
return;
glEnableClientState(GL_VERTEX_ARRAY);
for (size_t i = 0; i < visiblePatches.size(); ++i)
visiblePatches[i]->RenderOutline();
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Scissor rectangle of water patches
CBound TerrainRenderer::ScissorWater(const CMatrix3D &viewproj)
{
CBound scissor;
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* data = m->visiblePatches[i];
const CBound& waterBounds = data->GetWaterBounds();
if (waterBounds.IsEmpty())
continue;
CVector4D v1 = viewproj.Transform(CVector4D(waterBounds[0].X, waterBounds[1].Y, waterBounds[0].Z, 1.0f));
CVector4D v2 = viewproj.Transform(CVector4D(waterBounds[1].X, waterBounds[1].Y, waterBounds[0].Z, 1.0f));
CVector4D v3 = viewproj.Transform(CVector4D(waterBounds[0].X, waterBounds[1].Y, waterBounds[1].Z, 1.0f));
CVector4D v4 = viewproj.Transform(CVector4D(waterBounds[1].X, waterBounds[1].Y, waterBounds[1].Z, 1.0f));
CBound screenBounds;
#define ADDBOUND(v1, v2, v3, v4) \
if (v1[2] >= -v1[3]) \
screenBounds += CVector3D(v1[0], v1[1], v1[2]) * (1.0f / v1[3]); \
else \
{ \
float t = v1[2] + v1[3]; \
if (v2[2] > -v2[3]) \
{ \
CVector4D c2 = v1 + (v2 - v1) * (t / (t - (v2[2] + v2[3]))); \
screenBounds += CVector3D(c2[0], c2[1], c2[2]) * (1.0f / c2[3]); \
} \
if (v3[2] > -v3[3]) \
{ \
CVector4D c3 = v1 + (v3 - v1) * (t / (t - (v3[2] + v3[3]))); \
screenBounds += CVector3D(c3[0], c3[1], c3[2]) * (1.0f / c3[3]); \
} \
if (v4[2] > -v4[3]) \
{ \
CVector4D c4 = v1 + (v4 - v1) * (t / (t - (v4[2] + v4[3]))); \
screenBounds += CVector3D(c4[0], c4[1], c4[2]) * (1.0f / c4[3]); \
} \
}
ADDBOUND(v1, v2, v3, v4);
ADDBOUND(v2, v1, v3, v4);
ADDBOUND(v3, v1, v2, v4);
ADDBOUND(v4, v1, v2, v3);
#undef ADDBOUND
if (screenBounds[0].X >= 1.0f || screenBounds[1].X <= -1.0f || screenBounds[0].Y >= 1.0f || screenBounds[1].Y <= -1.0f)
continue;
scissor += screenBounds;
}
return CBound(CVector3D(clamp(scissor[0].X, -1.0f, 1.0f), clamp(scissor[0].Y, -1.0f, 1.0f), -1.0f),
CVector3D(clamp(scissor[1].X, -1.0f, 1.0f), clamp(scissor[1].Y, -1.0f, 1.0f), 1.0f));
}
// Render fancy water
bool TerrainRenderer::RenderFancyWater()
{
PROFILE("render fancy water");
// If we're using fancy water, make sure its shader is loaded
if (!m->fancyWaterShader)
{
Handle h = ogl_program_load(g_VFS, L"shaders/water_high.xml");
if (h < 0)
{
LOGERROR(L"Failed to load water shader. Falling back to non-fancy water.\n");
g_Renderer.m_Options.m_FancyWater = false;
return false;
}
else
{
m->fancyWaterShader = h;
}
}
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
double time = WaterMgr->m_WaterTexTimer;
double period = 1.6;
int curTex = (int)(time*60/period) % 60;
WaterMgr->m_NormalMap[curTex]->Bind();
// Shift the texture coordinates by these amounts to make the water "flow"
float tx = -fmod(time, 81.0)/81.0;
float ty = -fmod(time, 34.0)/34.0;
float repeatPeriod = WaterMgr->m_RepeatPeriod;
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
const CCamera& camera = g_Renderer.GetViewCamera();
CVector3D camPos = camera.m_Orientation.GetTranslation();
// Bind reflection and refraction textures on texture units 1 and 2
pglActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_ReflectionTexture);
pglActiveTextureARB(GL_TEXTURE2_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_RefractionTexture);
losTexture.BindTexture(3);
// Bind water shader and set arguments
ogl_program_use(m->fancyWaterShader);
GLint ambient = ogl_program_get_uniform_location(m->fancyWaterShader, "ambient");
GLint sunDir = ogl_program_get_uniform_location(m->fancyWaterShader, "sunDir");
GLint sunColor = ogl_program_get_uniform_location(m->fancyWaterShader, "sunColor");
GLint cameraPos = ogl_program_get_uniform_location(m->fancyWaterShader, "cameraPos");
GLint shininess = ogl_program_get_uniform_location(m->fancyWaterShader, "shininess");
GLint specularStrength = ogl_program_get_uniform_location(m->fancyWaterShader, "specularStrength");
GLint waviness = ogl_program_get_uniform_location(m->fancyWaterShader, "waviness");
GLint murkiness = ogl_program_get_uniform_location(m->fancyWaterShader, "murkiness");
GLint fullDepth = ogl_program_get_uniform_location(m->fancyWaterShader, "fullDepth");
GLint tint = ogl_program_get_uniform_location(m->fancyWaterShader, "tint");
GLint reflectionTint = ogl_program_get_uniform_location(m->fancyWaterShader, "reflectionTint");
GLint reflectionTintStrength = ogl_program_get_uniform_location(m->fancyWaterShader, "reflectionTintStrength");
GLint translation = ogl_program_get_uniform_location(m->fancyWaterShader, "translation");
GLint repeatScale = ogl_program_get_uniform_location(m->fancyWaterShader, "repeatScale");
GLint reflectionMatrix = ogl_program_get_uniform_location(m->fancyWaterShader, "reflectionMatrix");
GLint refractionMatrix = ogl_program_get_uniform_location(m->fancyWaterShader, "refractionMatrix");
GLint losMatrix = ogl_program_get_uniform_location(m->fancyWaterShader, "losMatrix");
GLint normalMap = ogl_program_get_uniform_location(m->fancyWaterShader, "normalMap");
GLint reflectionMap = ogl_program_get_uniform_location(m->fancyWaterShader, "reflectionMap");
GLint refractionMap = ogl_program_get_uniform_location(m->fancyWaterShader, "refractionMap");
GLint losMap = ogl_program_get_uniform_location(m->fancyWaterShader, "losMap");
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
pglUniform3fvARB(ambient, 1, &lightEnv.m_TerrainAmbientColor.X);
pglUniform3fvARB(sunDir, 1, &lightEnv.GetSunDir().X);
pglUniform3fvARB(sunColor, 1, &lightEnv.m_SunColor.X);
pglUniform1fARB(shininess, WaterMgr->m_Shininess);
pglUniform1fARB(specularStrength, WaterMgr->m_SpecularStrength);
pglUniform1fARB(waviness, WaterMgr->m_Waviness);
pglUniform1fARB(murkiness, WaterMgr->m_Murkiness);
pglUniform1fARB(fullDepth, WaterMgr->m_WaterFullDepth);
pglUniform3fvARB(tint, 1, WaterMgr->m_WaterTint.FloatArray());
pglUniform1fARB(reflectionTintStrength, WaterMgr->m_ReflectionTintStrength);
pglUniform3fvARB(reflectionTint, 1, WaterMgr->m_ReflectionTint.FloatArray());
pglUniform2fARB(translation, tx, ty);
pglUniform1fARB(repeatScale, 1.0f / repeatPeriod);
pglUniformMatrix4fvARB(reflectionMatrix, 1, false, &WaterMgr->m_ReflectionMatrix._11);
pglUniformMatrix4fvARB(refractionMatrix, 1, false, &WaterMgr->m_RefractionMatrix._11);
pglUniformMatrix4fvARB(losMatrix, 1, false, losTexture.GetTextureMatrix());
pglUniform1iARB(normalMap, 0); // texture unit 0
pglUniform1iARB(reflectionMap, 1); // texture unit 1
pglUniform1iARB(refractionMap, 2); // texture unit 2
pglUniform1iARB(losMap, 3); // texture unit 3
pglUniform3fvARB(cameraPos, 1, &camPos.X);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* data = m->visiblePatches[i];
data->RenderWater();
}
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
// Unbind the LOS/refraction/reflection textures and the shader
g_Renderer.BindTexture(3, 0);
g_Renderer.BindTexture(2, 0);
g_Renderer.BindTexture(1, 0);
pglActiveTextureARB(GL_TEXTURE0_ARB);
ogl_program_use(0);
glDisable(GL_BLEND);
return true;
}
void TerrainRenderer::RenderSimpleWater()
{
PROFILE("render simple water");
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
double time = WaterMgr->m_WaterTexTimer;
double period = 1.6f;
int curTex = (int)(time*60/period) % 60;
WaterMgr->m_WaterTexture[curTex]->Bind();
// Shift the texture coordinates by these amounts to make the water "flow"
float tx = -fmod(time, 81.0)/81.0;
float ty = -fmod(time, 34.0)/34.0;
float repeatPeriod = 16.0f;
// Perform the shifting by using texture coordinate generation
GLfloat texgenS0[4] = { 1/repeatPeriod, 0, 0, tx };
GLfloat texgenT0[4] = { 0, 0, 1/repeatPeriod, ty };
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS0);
glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT0);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
// Set up texture environment to multiply vertex RGB by texture RGB and use vertex alpha
GLfloat waterColor[4] = { WaterMgr->m_WaterColor.r, WaterMgr->m_WaterColor.g, WaterMgr->m_WaterColor.b, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, waterColor);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// Multiply by LOS texture
losTexture.BindTexture(1);
const float *losMatrix = losTexture.GetTextureMatrix();
GLfloat texgenS1[4] = { losMatrix[0], losMatrix[4], losMatrix[8], losMatrix[12] };
GLfloat texgenT1[4] = { losMatrix[1], losMatrix[5], losMatrix[9], losMatrix[13] };
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS1);
glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT1);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* data = m->visiblePatches[i];
data->RenderWater();
}
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
g_Renderer.BindTexture(1, 0);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
pglActiveTextureARB(GL_TEXTURE0_ARB);
// Clean up the texture matrix and blend mode
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}
///////////////////////////////////////////////////////////////////
// Render water that is part of the terrain
void TerrainRenderer::RenderWater()
{
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
if (!WaterMgr->WillRenderFancyWater() || !RenderFancyWater())
RenderSimpleWater();
}
void TerrainRenderer::RenderPriorities()
{
PROFILE("render priorities");
ENSURE(m->phase == Phase_Render);
CFont font(L"mono-stroke-10");
font.Bind();
glColor3f(1, 1, 0);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
m->visiblePatches[i]->RenderPriorities();
}
Index: ps/trunk/source/simulation2/TypeList.h
===================================================================
--- ps/trunk/source/simulation2/TypeList.h (revision 9888)
+++ ps/trunk/source/simulation2/TypeList.h (revision 9889)
@@ -1,139 +1,148 @@
-/* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
// MESSAGE: message types
// INTERFACE: component interface types
// COMPONENT: component types
// Components intended only for use in test cases:
// (The tests rely on the enum IDs, so don't change the order of these)
INTERFACE(Test1)
COMPONENT(Test1A)
COMPONENT(Test1B)
COMPONENT(Test1Scripted)
INTERFACE(Test2)
COMPONENT(Test2A)
COMPONENT(Test2Scripted)
// Message types:
MESSAGE(TurnStart)
MESSAGE(Update)
MESSAGE(Update_MotionFormation)
MESSAGE(Update_MotionUnit)
MESSAGE(Update_Final)
MESSAGE(Interpolate) // non-deterministic (use with caution)
MESSAGE(RenderSubmit) // non-deterministic (use with caution)
MESSAGE(ProgressiveLoad) // non-deterministic (use with caution)
MESSAGE(Create)
MESSAGE(Destroy)
MESSAGE(OwnershipChanged)
MESSAGE(PositionChanged)
MESSAGE(MotionChanged)
MESSAGE(RangeUpdate)
MESSAGE(TerrainChanged)
MESSAGE(PathResult)
// TemplateManager must come before all other (non-test) components,
// so that it is the first to be (de)serialized
INTERFACE(TemplateManager)
COMPONENT(TemplateManager)
// Special component for script component types with no native interface
INTERFACE(UnknownScript)
COMPONENT(UnknownScript)
// In alphabetical order:
INTERFACE(AIInterface)
COMPONENT(AIInterfaceScripted)
INTERFACE(AIManager)
COMPONENT(AIManager)
INTERFACE(CommandQueue)
COMPONENT(CommandQueue)
INTERFACE(Decay)
COMPONENT(Decay)
INTERFACE(Footprint)
COMPONENT(Footprint)
INTERFACE(GuiInterface)
COMPONENT(GuiInterfaceScripted)
INTERFACE(Identity)
COMPONENT(IdentityScripted)
INTERFACE(Minimap)
COMPONENT(Minimap)
INTERFACE(Motion)
COMPONENT(MotionBall)
COMPONENT(MotionScripted)
INTERFACE(Obstruction)
COMPONENT(Obstruction)
INTERFACE(ObstructionManager)
COMPONENT(ObstructionManager)
INTERFACE(OverlayRenderer)
COMPONENT(OverlayRenderer)
INTERFACE(Ownership)
COMPONENT(Ownership)
INTERFACE(Pathfinder)
COMPONENT(Pathfinder)
INTERFACE(Player)
COMPONENT(PlayerScripted)
INTERFACE(PlayerManager)
COMPONENT(PlayerManagerScripted)
INTERFACE(Position)
COMPONENT(Position) // must be before VisualActor
INTERFACE(ProjectileManager)
COMPONENT(ProjectileManager)
INTERFACE(RangeManager)
COMPONENT(RangeManager)
INTERFACE(Selectable)
COMPONENT(Selectable)
+INTERFACE(Settlement)
+COMPONENT(SettlementScripted)
+
INTERFACE(SoundManager)
COMPONENT(SoundManager)
INTERFACE(Terrain)
COMPONENT(Terrain)
+INTERFACE(TerritoryInfluence)
+COMPONENT(TerritoryInfluence)
+
+INTERFACE(TerritoryManager)
+COMPONENT(TerritoryManager)
+
INTERFACE(UnitMotion)
COMPONENT(UnitMotion) // must be after Obstruction
COMPONENT(UnitMotionScripted)
INTERFACE(Vision)
COMPONENT(Vision)
INTERFACE(Visual)
COMPONENT(VisualActor)
INTERFACE(WaterManager)
COMPONENT(WaterManager)
Index: ps/trunk/source/simulation2/components/CCmpTerritoryInfluence.cpp
===================================================================
--- ps/trunk/source/simulation2/components/CCmpTerritoryInfluence.cpp (nonexistent)
+++ ps/trunk/source/simulation2/components/CCmpTerritoryInfluence.cpp (revision 9889)
@@ -0,0 +1,67 @@
+/* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "precompiled.h"
+
+#include "simulation2/system/Component.h"
+#include "ICmpTerritoryInfluence.h"
+
+class CCmpTerritoryInfluence : public ICmpTerritoryInfluence
+{
+public:
+ static void ClassInit(CComponentManager& UNUSED(componentManager))
+ {
+ }
+
+ DEFAULT_COMPONENT_ALLOCATOR(TerritoryInfluence)
+
+ u8 m_Cost;
+
+ static std::string GetSchema()
+ {
+ return
+ ""
+ ""
+ "";
+ }
+
+ virtual void Init(const CParamNode& paramNode)
+ {
+ m_Cost = paramNode.GetChild("OverrideCost").ToInt();
+ }
+
+ virtual void Deinit()
+ {
+ }
+
+ virtual void Serialize(ISerializer& UNUSED(serialize))
+ {
+ }
+
+ virtual void Deserialize(const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
+ {
+ Init(paramNode);
+ }
+
+ virtual u8 GetCost()
+ {
+ return m_Cost;
+ }
+
+};
+
+REGISTER_COMPONENT_TYPE(TerritoryInfluence)
Property changes on: ps/trunk/source/simulation2/components/CCmpTerritoryInfluence.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/simulation2/components/CCmpTerritoryManager.cpp
===================================================================
--- ps/trunk/source/simulation2/components/CCmpTerritoryManager.cpp (nonexistent)
+++ ps/trunk/source/simulation2/components/CCmpTerritoryManager.cpp (revision 9889)
@@ -0,0 +1,401 @@
+/* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "precompiled.h"
+
+#include "simulation2/system/Component.h"
+#include "ICmpTerritoryManager.h"
+
+#include "graphics/Terrain.h"
+#include "maths/MathUtil.h"
+#include "ps/Overlay.h"
+#include "renderer/TerrainOverlay.h"
+#include "simulation2/MessageTypes.h"
+#include "simulation2/components/ICmpObstruction.h"
+#include "simulation2/components/ICmpObstructionManager.h"
+#include "simulation2/components/ICmpPathfinder.h"
+#include "simulation2/components/ICmpPosition.h"
+#include "simulation2/components/ICmpSettlement.h"
+#include "simulation2/components/ICmpTerrain.h"
+#include "simulation2/components/ICmpTerritoryInfluence.h"
+#include "simulation2/helpers/Geometry.h"
+#include "simulation2/helpers/Grid.h"
+#include "simulation2/helpers/PriorityQueue.h"
+
+class CCmpTerritoryManager;
+
+class TerritoryOverlay : public TerrainOverlay
+{
+ NONCOPYABLE(TerritoryOverlay);
+public:
+ CCmpTerritoryManager& m_TerritoryManager;
+
+ TerritoryOverlay(CCmpTerritoryManager& manager) : m_TerritoryManager(manager) { }
+ virtual void StartRender();
+ virtual void ProcessTile(ssize_t i, ssize_t j);
+};
+
+class CCmpTerritoryManager : public ICmpTerritoryManager
+{
+public:
+ static void ClassInit(CComponentManager& componentManager)
+ {
+ componentManager.SubscribeGloballyToMessageType(MT_OwnershipChanged);
+ componentManager.SubscribeGloballyToMessageType(MT_PositionChanged);
+ componentManager.SubscribeToMessageType(MT_TerrainChanged);
+ }
+
+ DEFAULT_COMPONENT_ALLOCATOR(TerritoryManager)
+
+ static std::string GetSchema()
+ {
+ return "";
+ }
+
+ Grid* m_Territories;
+ TerritoryOverlay* m_DebugOverlay;
+
+ virtual void Init(const CParamNode& UNUSED(paramNode))
+ {
+ m_Territories = NULL;
+ m_DebugOverlay = NULL;
+// m_DebugOverlay = new TerritoryOverlay(*this);
+
+ m_DirtyID = 1;
+ }
+
+ virtual void Deinit()
+ {
+ SAFE_DELETE(m_Territories);
+ SAFE_DELETE(m_DebugOverlay);
+ }
+
+ virtual void Serialize(ISerializer& serialize)
+ {
+ // TODO
+ }
+
+ virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
+ {
+ Init(paramNode);
+ }
+
+ virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
+ {
+ switch (msg.GetType())
+ {
+ case MT_OwnershipChanged:
+ {
+ const CMessageOwnershipChanged& msgData = static_cast (msg);
+ MakeDirtyIfRelevantEntity(msgData.entity);
+ break;
+ }
+ case MT_PositionChanged:
+ {
+ const CMessagePositionChanged& msgData = static_cast (msg);
+ MakeDirtyIfRelevantEntity(msgData.entity);
+ break;
+ }
+ case MT_TerrainChanged:
+ {
+ MakeDirty();
+ break;
+ }
+ }
+ }
+
+ // Check whether the entity is either a settlement or territory influence;
+ // ignore any others
+ void MakeDirtyIfRelevantEntity(entity_id_t ent)
+ {
+ CmpPtr cmpSettlement(GetSimContext(), ent);
+ if (!cmpSettlement.null())
+ MakeDirty();
+
+ CmpPtr cmpTerritoryInfluence(GetSimContext(), ent);
+ if (!cmpTerritoryInfluence.null())
+ MakeDirty();
+ }
+
+ virtual const Grid& GetTerritoryGrid()
+ {
+ CalculateTerritories();
+ return *m_Territories;
+ }
+
+ // To support lazy updates of territory render data,
+ // we maintain a DirtyID here and increment it whenever territories change;
+ // if a caller has a lower DirtyID then it needs to be updated.
+
+ size_t m_DirtyID;
+
+ void MakeDirty()
+ {
+ SAFE_DELETE(m_Territories);
+ ++m_DirtyID;
+ }
+
+ virtual bool NeedUpdate(size_t* dirtyID)
+ {
+ ENSURE(*dirtyID <= m_DirtyID);
+ if (*dirtyID < m_DirtyID)
+ {
+ *dirtyID = m_DirtyID;
+ return true;
+ }
+ return false;
+ }
+
+ void CalculateTerritories();
+
+ /**
+ * Updates @p grid based on the obstruction shapes of all entities with
+ * a TerritoryInfluence component. Grid cells are 0 if no influence,
+ * or 1+c if the influence have cost c (assumed between 0 and 254).
+ */
+ void RasteriseInfluences(Grid& grid);
+};
+
+REGISTER_COMPONENT_TYPE(TerritoryManager)
+
+
+/*
+
+We compute territory influences with a kind of best-first search:
+ 1) Initialise an 'open' list with tiles that contain settlements (annotated with
+ territory ID) with initial cost 0
+ 2) Pick the lowest cost tile from 'item'
+ 3) For each neighbour which has not already been assigned to a territory,
+ assign it to this territory and compute its new cost (effectively the
+ distance from the associated settlement) and add to 'open'
+ 4) Go to 2 until 'open' is empty
+
+*/
+
+typedef PriorityQueueHeap, u32> OpenQueue;
+
+static void ProcessNeighbour(u8 pid, u16 i, u16 j, u32 pg, bool diagonal,
+ Grid& grid, OpenQueue& queue, const Grid& influenceGrid)
+{
+ // Ignore tiles that are already claimed
+ u8 id = grid.get(i, j);
+ if (id)
+ return;
+
+ // Base cost for moving onto this tile
+ u32 dg = diagonal ? 362 : 256;
+
+ // Adjust cost based on this tile's influences
+ dg *= influenceGrid.get(i, j);
+
+ u32 g = pg + dg; // cost to this tile = cost to predecessor + delta from predecessor
+
+ grid.set(i, j, pid);
+ OpenQueue::Item tile = { std::make_pair(i, j), g };
+ queue.push(tile);
+}
+
+void CCmpTerritoryManager::CalculateTerritories()
+{
+ PROFILE("CalculateTerritories");
+
+ if (m_Territories)
+ return;
+
+ CmpPtr cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
+ uint32_t tilesW = cmpTerrain->GetVerticesPerSide() - 1;
+ uint32_t tilesH = cmpTerrain->GetVerticesPerSide() - 1;
+
+ Grid influenceGrid(tilesW, tilesH);
+
+ RasteriseInfluences(influenceGrid);
+
+ SAFE_DELETE(m_Territories);
+ m_Territories = new Grid(tilesW, tilesH);
+
+ CmpPtr cmpPathfinder(GetSimContext(), SYSTEM_ENTITY);
+ ICmpPathfinder::pass_class_t passClass = cmpPathfinder->GetPassabilityClass("default");
+ const Grid& passGrid = cmpPathfinder->GetPassabilityGrid();
+
+ // Adjust influenceGrid so it contains terrain-passability-dependent costs,
+ // unless overridden by existing values in influenceGrid
+ for (u32 j = 0; j < tilesH; ++j)
+ {
+ for (u32 i = 0; i < tilesW; ++i)
+ {
+ u8 cost;
+ u8 inflCost = influenceGrid.get(i, j);
+ if (inflCost)
+ {
+ cost = inflCost-1; // undo RasteriseInfluences's offset
+ }
+ else
+ {
+ if (passGrid.get(i, j) & passClass)
+ cost = 100;
+ else
+ cost = 1;
+ }
+ influenceGrid.set(i, j, cost);
+ }
+ }
+
+ OpenQueue openTiles;
+
+ // Initialise open list with all settlements
+
+ CComponentManager::InterfaceList settlements = GetSimContext().GetComponentManager().GetEntitiesWithInterface(IID_Settlement);
+ u8 id = 1;
+ for (CComponentManager::InterfaceList::iterator it = settlements.begin(); it != settlements.end(); ++it)
+ {
+ entity_id_t settlement = it->first;
+ CmpPtr cmpPosition(GetSimContext(), settlement);
+ if (cmpPosition.null() || !cmpPosition->IsInWorld())
+ continue;
+
+ // TODO: maybe we need to ignore settlements with owner -1,
+ // since they're probably destroyed
+
+ CFixedVector2D pos = cmpPosition->GetPosition2D();
+ int i = clamp((pos.X / (int)CELL_SIZE).ToInt_RoundToNegInfinity(), 0, (int)tilesW-1);
+ int j = clamp((pos.Y / (int)CELL_SIZE).ToInt_RoundToNegInfinity(), 0, (int)tilesH-1);
+
+ // Must avoid duplicates in the priority queue; ignore the settlement
+ // if there's already one on that tile
+ if (!m_Territories->get(i, j))
+ {
+ m_Territories->set(i, j, id);
+ OpenQueue::Item tile = { std::make_pair((u16)i, (i16)j), 0 };
+ openTiles.push(tile);
+ }
+
+ id += 1;
+ }
+
+ while (!openTiles.empty())
+ {
+ OpenQueue::Item tile = openTiles.pop();
+
+ // Get current tile's territory ID
+ u8 tid = m_Territories->get(tile.id.first, tile.id.second);
+
+ // Process neighbours (if they're not off the edge of the map)
+ u16 x = tile.id.first;
+ u16 z = tile.id.second;
+ if (x > 0)
+ ProcessNeighbour(tid, x-1, z, tile.rank, false, *m_Territories, openTiles, influenceGrid);
+ if (x < tilesW-1)
+ ProcessNeighbour(tid, x+1, z, tile.rank, false, *m_Territories, openTiles, influenceGrid);
+ if (z > 0)
+ ProcessNeighbour(tid, x, z-1, tile.rank, false, *m_Territories, openTiles, influenceGrid);
+ if (z < tilesH-1)
+ ProcessNeighbour(tid, x, z+1, tile.rank, false, *m_Territories, openTiles, influenceGrid);
+ if (x > 0 && z > 0)
+ ProcessNeighbour(tid, x-1, z-1, tile.rank, true, *m_Territories, openTiles, influenceGrid);
+ if (x > 0 && z < tilesH-1)
+ ProcessNeighbour(tid, x-1, z+1, tile.rank, true, *m_Territories, openTiles, influenceGrid);
+ if (x < tilesW-1 && z > 0)
+ ProcessNeighbour(tid, x+1, z-1, tile.rank, true, *m_Territories, openTiles, influenceGrid);
+ if (x < tilesW-1 && z < tilesH-1)
+ ProcessNeighbour(tid, x+1, z+1, tile.rank, true, *m_Territories, openTiles, influenceGrid);
+ }
+}
+
+/**
+ * Compute the tile indexes on the grid nearest to a given point
+ */
+static void NearestTile(entity_pos_t x, entity_pos_t z, u16& i, u16& j, u16 w, u16 h)
+{
+ i = clamp((x / (int)CELL_SIZE).ToInt_RoundToZero(), 0, w-1);
+ j = clamp((z / (int)CELL_SIZE).ToInt_RoundToZero(), 0, h-1);
+}
+
+/**
+ * Returns the position of the center of the given tile
+ */
+static void TileCenter(u16 i, u16 j, entity_pos_t& x, entity_pos_t& z)
+{
+ x = entity_pos_t::FromInt(i*(int)CELL_SIZE + CELL_SIZE/2);
+ z = entity_pos_t::FromInt(j*(int)CELL_SIZE + CELL_SIZE/2);
+}
+
+// TODO: would be nice not to duplicate those two functions from CCmpObstructionManager.cpp
+
+
+void CCmpTerritoryManager::RasteriseInfluences(Grid& grid)
+{
+ CComponentManager::InterfaceList infls = GetSimContext().GetComponentManager().GetEntitiesWithInterface(IID_TerritoryInfluence);
+ for (CComponentManager::InterfaceList::iterator it = infls.begin(); it != infls.end(); ++it)
+ {
+ ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast(it->second);
+
+ CmpPtr cmpObstruction(GetSimContext(), it->first);
+ if (cmpObstruction.null())
+ continue;
+
+ ICmpObstructionManager::ObstructionSquare square;
+ if (!cmpObstruction->GetObstructionSquare(square))
+ continue;
+
+ u8 cost = cmpTerritoryInfluence->GetCost();
+
+ CFixedVector2D halfSize(square.hw, square.hh);
+ CFixedVector2D halfBound = Geometry::GetHalfBoundingBox(square.u, square.v, halfSize);
+
+ u16 i0, j0, i1, j1;
+ NearestTile(square.x - halfBound.X, square.z - halfBound.Y, i0, j0, grid.m_W, grid.m_H);
+ NearestTile(square.x + halfBound.X, square.z + halfBound.Y, i1, j1, grid.m_W, grid.m_H);
+ for (u16 j = j0; j <= j1; ++j)
+ {
+ for (u16 i = i0; i <= i1; ++i)
+ {
+ entity_pos_t x, z;
+ TileCenter(i, j, x, z);
+ if (Geometry::PointIsInSquare(CFixedVector2D(x - square.x, z - square.z), square.u, square.v, halfSize))
+ grid.set(i, j, cost+1);
+ }
+ }
+
+ }
+}
+
+
+void TerritoryOverlay::StartRender()
+{
+ m_TerritoryManager.CalculateTerritories();
+}
+
+void TerritoryOverlay::ProcessTile(ssize_t i, ssize_t j)
+{
+ if (!m_TerritoryManager.m_Territories)
+ return;
+
+ u8 id = m_TerritoryManager.m_Territories->get(i, j);
+
+ float a = 0.2f;
+ switch (id)
+ {
+ case 0: break;
+ case 1: RenderTile(CColor(1, 0, 0, a), false); break;
+ case 2: RenderTile(CColor(0, 1, 0, a), false); break;
+ case 3: RenderTile(CColor(0, 0, 1, a), false); break;
+ case 4: RenderTile(CColor(1, 1, 0, a), false); break;
+ case 5: RenderTile(CColor(0, 1, 1, a), false); break;
+ case 6: RenderTile(CColor(1, 0, 1, a), false); break;
+ default: RenderTile(CColor(1, 1, 1, a), false); break;
+ }
+}
Property changes on: ps/trunk/source/simulation2/components/CCmpTerritoryManager.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/simulation2/components/ICmpSettlement.h
===================================================================
--- ps/trunk/source/simulation2/components/ICmpSettlement.h (nonexistent)
+++ ps/trunk/source/simulation2/components/ICmpSettlement.h (revision 9889)
@@ -0,0 +1,29 @@
+/* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_ICMPSETTLEMENT
+#define INCLUDED_ICMPSETTLEMENT
+
+#include "simulation2/system/Interface.h"
+
+class ICmpSettlement : public IComponent
+{
+public:
+ DECLARE_INTERFACE_TYPE(Settlement)
+};
+
+#endif // INCLUDED_ICMPSETTLEMENT
Property changes on: ps/trunk/source/simulation2/components/ICmpSettlement.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/simulation2/components/ICmpSettlement.cpp
===================================================================
--- ps/trunk/source/simulation2/components/ICmpSettlement.cpp (nonexistent)
+++ ps/trunk/source/simulation2/components/ICmpSettlement.cpp (revision 9889)
@@ -0,0 +1,35 @@
+/* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "precompiled.h"
+
+#include "ICmpSettlement.h"
+
+#include "simulation2/system/InterfaceScripted.h"
+#include "simulation2/scripting/ScriptComponent.h"
+
+
+BEGIN_INTERFACE_WRAPPER(Settlement)
+END_INTERFACE_WRAPPER(Settlement)
+
+class CCmpSettlementScripted : public ICmpSettlement
+{
+public:
+ DEFAULT_SCRIPT_WRAPPER(SettlementScripted)
+};
+
+REGISTER_COMPONENT_SCRIPT_WRAPPER(SettlementScripted)
Property changes on: ps/trunk/source/simulation2/components/ICmpSettlement.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/simulation2/components/ICmpTerritoryInfluence.h
===================================================================
--- ps/trunk/source/simulation2/components/ICmpTerritoryInfluence.h (nonexistent)
+++ ps/trunk/source/simulation2/components/ICmpTerritoryInfluence.h (revision 9889)
@@ -0,0 +1,31 @@
+/* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_ICMPTERRITORYINFLUENCE
+#define INCLUDED_ICMPTERRITORYINFLUENCE
+
+#include "simulation2/system/Interface.h"
+
+class ICmpTerritoryInfluence : public IComponent
+{
+public:
+ virtual u8 GetCost() = 0;
+
+ DECLARE_INTERFACE_TYPE(TerritoryInfluence)
+};
+
+#endif // INCLUDED_ICMPTERRITORYINFLUENCE
Property changes on: ps/trunk/source/simulation2/components/ICmpTerritoryInfluence.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/simulation2/components/ICmpTerritoryManager.h
===================================================================
--- ps/trunk/source/simulation2/components/ICmpTerritoryManager.h (nonexistent)
+++ ps/trunk/source/simulation2/components/ICmpTerritoryManager.h (revision 9889)
@@ -0,0 +1,35 @@
+/* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_ICMPTERRITORYMANAGER
+#define INCLUDED_ICMPTERRITORYMANAGER
+
+#include "simulation2/system/Interface.h"
+
+#include "simulation2/helpers/Grid.h"
+
+class ICmpTerritoryManager : public IComponent
+{
+public:
+ virtual bool NeedUpdate(size_t* dirtyID) = 0;
+
+ virtual const Grid& GetTerritoryGrid() = 0;
+
+ DECLARE_INTERFACE_TYPE(TerritoryManager)
+};
+
+#endif // INCLUDED_ICMPTERRITORYMANAGER
Property changes on: ps/trunk/source/simulation2/components/ICmpTerritoryManager.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/simulation2/components/ICmpTerritoryInfluence.cpp
===================================================================
--- ps/trunk/source/simulation2/components/ICmpTerritoryInfluence.cpp (nonexistent)
+++ ps/trunk/source/simulation2/components/ICmpTerritoryInfluence.cpp (revision 9889)
@@ -0,0 +1,25 @@
+/* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "precompiled.h"
+
+#include "ICmpTerritoryInfluence.h"
+
+#include "simulation2/system/InterfaceScripted.h"
+
+BEGIN_INTERFACE_WRAPPER(TerritoryInfluence)
+END_INTERFACE_WRAPPER(TerritoryInfluence)
Property changes on: ps/trunk/source/simulation2/components/ICmpTerritoryInfluence.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/simulation2/components/ICmpTerritoryManager.cpp
===================================================================
--- ps/trunk/source/simulation2/components/ICmpTerritoryManager.cpp (nonexistent)
+++ ps/trunk/source/simulation2/components/ICmpTerritoryManager.cpp (revision 9889)
@@ -0,0 +1,25 @@
+/* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "precompiled.h"
+
+#include "ICmpTerritoryManager.h"
+
+#include "simulation2/system/InterfaceScripted.h"
+
+BEGIN_INTERFACE_WRAPPER(TerritoryManager)
+END_INTERFACE_WRAPPER(TerritoryManager)
Property changes on: ps/trunk/source/simulation2/components/ICmpTerritoryManager.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/simulation2/components/ICmpObstruction.h
===================================================================
--- ps/trunk/source/simulation2/components/ICmpObstruction.h (revision 9888)
+++ ps/trunk/source/simulation2/components/ICmpObstruction.h (revision 9889)
@@ -1,71 +1,76 @@
/* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#ifndef INCLUDED_ICMPOBSTRUCTION
#define INCLUDED_ICMPOBSTRUCTION
#include "simulation2/system/Interface.h"
#include "simulation2/components/ICmpObstructionManager.h"
/**
* Flags an entity as obstructing movement for other units,
* and handles the processing of collision queries.
*/
class ICmpObstruction : public IComponent
{
public:
virtual ICmpObstructionManager::tag_t GetObstruction() = 0;
+ /**
+ * Gets the square corresponding to this obstruction shape.
+ * @return true and updates @p out on success;
+ * false on failure (e.g. object not in the world).
+ */
virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) = 0;
virtual entity_pos_t GetUnitRadius() = 0;
/**
* Test whether this entity is colliding with any obstruction that are set to
* block the creation of foundations.
* @return true if there is a collision
*/
virtual bool CheckFoundationCollisions() = 0;
/**
* Returns a list of entities that are colliding with this entity, and that
* are set to block construction.
* @return true if there is a collision
*/
virtual std::vector GetConstructionCollisions() = 0;
virtual void SetActive(bool active) = 0;
virtual void SetMovingFlag(bool enabled) = 0;
virtual void SetDisableBlockMovementPathfinding(bool disabled) = 0;
virtual bool GetBlockMovementFlag() = 0;
/**
* Change the control group that the entity belongs to.
* Control groups are used to let units ignore collisions with other units from
* the same group. Default is the entity's own ID.
*/
virtual void SetControlGroup(entity_id_t group) = 0;
DECLARE_INTERFACE_TYPE(Obstruction)
};
#endif // INCLUDED_ICMPOBSTRUCTION
Index: ps/trunk/source/simulation2/Simulation2.cpp
===================================================================
--- ps/trunk/source/simulation2/Simulation2.cpp (revision 9888)
+++ ps/trunk/source/simulation2/Simulation2.cpp (revision 9889)
@@ -1,654 +1,655 @@
/* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "Simulation2.h"
#include "simulation2/MessageTypes.h"
#include "simulation2/system/ComponentManager.h"
#include "simulation2/system/ParamNode.h"
#include "simulation2/system/SimContext.h"
#include "simulation2/components/ICmpAIManager.h"
#include "simulation2/components/ICmpCommandQueue.h"
#include "simulation2/components/ICmpTemplateManager.h"
#include "lib/timer.h"
#include "lib/file/vfs/vfs_util.h"
#include "maths/MathUtil.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/Profile.h"
#include "ps/Pyrogenesis.h"
#include "ps/XML/Xeromyces.h"
#include
#if MSC_VERSION
#include
#define getpid _getpid // use the non-deprecated function name
#endif
class CSimulation2Impl
{
public:
CSimulation2Impl(CUnitManager* unitManager, CTerrain* terrain) :
m_SimContext(), m_ComponentManager(m_SimContext), m_EnableOOSLog(false)
{
m_SimContext.m_UnitManager = unitManager;
m_SimContext.m_Terrain = terrain;
m_ComponentManager.LoadComponentTypes();
RegisterFileReloadFunc(ReloadChangedFileCB, this);
// m_EnableOOSLog = true; // TODO: this should be a command-line flag or similar
}
~CSimulation2Impl()
{
UnregisterFileReloadFunc(ReloadChangedFileCB, this);
}
void ResetState(bool skipScriptedComponents, bool skipAI)
{
m_ComponentManager.ResetState();
m_DeltaTime = 0.0;
m_LastFrameOffset = 0.0f;
m_TurnNumber = 0;
CParamNode noParam;
CComponentManager::ComponentTypeId cid;
// Add native system components:
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_TemplateManager, noParam);
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_CommandQueue, noParam);
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_ObstructionManager, noParam);
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_Pathfinder, noParam);
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_ProjectileManager, noParam);
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_RangeManager, noParam);
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_SoundManager, noParam);
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_Terrain, noParam);
+ m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_TerritoryManager, noParam);
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_WaterManager, noParam);
if (!skipAI)
{
m_ComponentManager.AddComponent(SYSTEM_ENTITY, CID_AIManager, noParam);
}
// Add scripted system components:
if (!skipScriptedComponents)
{
#define LOAD_SCRIPTED_COMPONENT(name) \
cid = m_ComponentManager.LookupCID(name); \
if (cid == CID__Invalid) \
LOGERROR(L"Can't find component type " L##name); \
m_ComponentManager.AddComponent(SYSTEM_ENTITY, cid, noParam)
LOAD_SCRIPTED_COMPONENT("AIInterface");
LOAD_SCRIPTED_COMPONENT("EndGameManager");
LOAD_SCRIPTED_COMPONENT("GuiInterface");
LOAD_SCRIPTED_COMPONENT("PlayerManager");
LOAD_SCRIPTED_COMPONENT("Timer");
#undef LOAD_SCRIPTED_COMPONENT
}
}
bool LoadScripts(const VfsPath& path);
Status ReloadChangedFile(const VfsPath& path);
static Status ReloadChangedFileCB(void* param, const VfsPath& path)
{
return static_cast(param)->ReloadChangedFile(path);
}
int ProgressiveLoad();
bool Update(int turnLength, const std::vector& commands);
void Interpolate(float frameLength, float frameOffset);
void DumpState();
CSimContext m_SimContext;
CComponentManager m_ComponentManager;
double m_DeltaTime;
float m_LastFrameOffset;
std::wstring m_StartupScript;
CScriptValRooted m_MapSettings;
std::set m_LoadedScripts;
uint32_t m_TurnNumber;
bool m_EnableOOSLog;
};
bool CSimulation2Impl::LoadScripts(const VfsPath& path)
{
VfsPaths pathnames;
if (vfs::GetPathnames(g_VFS, path, L"*.js", pathnames) < 0)
return false;
bool ok = true;
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
{
VfsPath filename = *it;
m_LoadedScripts.insert(filename);
LOGMESSAGE(L"Loading simulation script '%ls'", filename.string().c_str());
if (! m_ComponentManager.LoadScript(filename))
ok = false;
}
return ok;
}
Status CSimulation2Impl::ReloadChangedFile(const VfsPath& path)
{
const VfsPath& filename = path;
// Ignore if this file wasn't loaded as a script
// (TODO: Maybe we ought to load in any new .js files that are created in the right directories)
if (m_LoadedScripts.find(filename) == m_LoadedScripts.end())
return INFO::OK;
// If the file doesn't exist (e.g. it was deleted), don't bother loading it since that'll give an error message.
// (Also don't bother trying to 'unload' it from the component manager, because that's not possible)
if (!VfsFileExists(path))
return INFO::OK;
LOGMESSAGE(L"Reloading simulation script '%ls'", filename.string().c_str());
if (!m_ComponentManager.LoadScript(filename, true))
return ERR::FAIL;
return INFO::OK;
}
int CSimulation2Impl::ProgressiveLoad()
{
// yield after this time is reached. balances increased progress bar
// smoothness vs. slowing down loading.
const double end_time = timer_Time() + 200e-3;
int ret;
do
{
bool progressed = false;
int total = 0;
int progress = 0;
CMessageProgressiveLoad msg(&progressed, &total, &progress);
m_ComponentManager.BroadcastMessage(msg);
if (!progressed || total == 0)
return 0; // we have nothing left to load
ret = Clamp(100*progress / total, 1, 100);
}
while (timer_Time() < end_time);
return ret;
}
bool CSimulation2Impl::Update(int turnLength, const std::vector& commands)
{
fixed turnLengthFixed = fixed::FromInt(turnLength) / 1000;
// TODO: the update process is pretty ugly, with lots of messages and dependencies
// between different components. Ought to work out a nicer way to do this.
CMessageTurnStart msgTurnStart;
m_ComponentManager.BroadcastMessage(msgTurnStart);
CmpPtr cmpPathfinder(m_SimContext, SYSTEM_ENTITY);
if (!cmpPathfinder.null())
cmpPathfinder->FinishAsyncRequests();
// Push AI commands onto the queue before we use them
CmpPtr cmpAIManager(m_SimContext, SYSTEM_ENTITY);
if (!cmpAIManager.null())
cmpAIManager->PushCommands();
CmpPtr cmpCommandQueue(m_SimContext, SYSTEM_ENTITY);
if (!cmpCommandQueue.null())
cmpCommandQueue->FlushTurn(commands);
// Process newly generated move commands so the UI feels snappy
if (!cmpPathfinder.null())
cmpPathfinder->ProcessSameTurnMoves();
// Send all the update phases
{
CMessageUpdate msgUpdate(turnLengthFixed);
m_ComponentManager.BroadcastMessage(msgUpdate);
}
{
CMessageUpdate_MotionFormation msgUpdate(turnLengthFixed);
m_ComponentManager.BroadcastMessage(msgUpdate);
}
// Process move commands for formations (group proxy)
if (!cmpPathfinder.null())
cmpPathfinder->ProcessSameTurnMoves();
{
CMessageUpdate_MotionUnit msgUpdate(turnLengthFixed);
m_ComponentManager.BroadcastMessage(msgUpdate);
}
{
CMessageUpdate_Final msgUpdate(turnLengthFixed);
m_ComponentManager.BroadcastMessage(msgUpdate);
}
// Process moves resulting from group proxy movement (unit needs to catch up or realign) and any others
if (!cmpPathfinder.null())
cmpPathfinder->ProcessSameTurnMoves();
// Clean up any entities destroyed during the simulation update
m_ComponentManager.FlushDestroyedComponents();
// if (m_TurnNumber == 0)
// m_ComponentManager.GetScriptInterface().DumpHeap();
// Run the GC occasionally
// (TODO: we ought to schedule this for a frame where we're not
// running the sim update, to spread the load)
if (m_TurnNumber % 10 == 0)
m_ComponentManager.GetScriptInterface().MaybeGC();
if (m_EnableOOSLog)
DumpState();
// Start computing AI for the next turn
if (!cmpAIManager.null())
cmpAIManager->StartComputation();
++m_TurnNumber;
return true; // TODO: don't bother with bool return
}
void CSimulation2Impl::Interpolate(float frameLength, float frameOffset)
{
m_LastFrameOffset = frameOffset;
CMessageInterpolate msg(frameLength, frameOffset);
m_ComponentManager.BroadcastMessage(msg);
// Clean up any entities destroyed during interpolate (e.g. local corpses)
m_ComponentManager.FlushDestroyedComponents();
}
void CSimulation2Impl::DumpState()
{
PROFILE("DumpState");
std::wstringstream name;
name << L"sim_log/" << getpid() << L"/" << std::setw(5) << std::setfill(L'0') << m_TurnNumber << L".txt";
OsPath path = psLogDir() / name.str();
CreateDirectories(path.Parent(), 0700);
std::ofstream file (OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc);
file << "State hash: " << std::hex;
std::string hashRaw;
m_ComponentManager.ComputeStateHash(hashRaw, false);
for (size_t i = 0; i < hashRaw.size(); ++i)
file << std::setfill('0') << std::setw(2) << (int)(unsigned char)hashRaw[i];
file << std::dec << "\n";
file << "\n";
m_ComponentManager.DumpDebugState(file);
std::ofstream binfile (OsString(path.ChangeExtension(L".dat")).c_str(), std::ofstream::out | std::ofstream::trunc | std::ofstream::binary);
m_ComponentManager.SerializeState(binfile);
}
////////////////////////////////////////////////////////////////
CSimulation2::CSimulation2(CUnitManager* unitManager, CTerrain* terrain) :
m(new CSimulation2Impl(unitManager, terrain))
{
}
CSimulation2::~CSimulation2()
{
delete m;
}
// Forward all method calls to the appropriate CSimulation2Impl/CComponentManager methods:
void CSimulation2::EnableOOSLog()
{
m->m_EnableOOSLog = true;
}
entity_id_t CSimulation2::AddEntity(const std::wstring& templateName)
{
return m->m_ComponentManager.AddEntity(templateName, m->m_ComponentManager.AllocateNewEntity());
}
entity_id_t CSimulation2::AddEntity(const std::wstring& templateName, entity_id_t preferredId)
{
return m->m_ComponentManager.AddEntity(templateName, m->m_ComponentManager.AllocateNewEntity(preferredId));
}
entity_id_t CSimulation2::AddLocalEntity(const std::wstring& templateName)
{
return m->m_ComponentManager.AddEntity(templateName, m->m_ComponentManager.AllocateNewLocalEntity());
}
void CSimulation2::DestroyEntity(entity_id_t ent)
{
m->m_ComponentManager.DestroyComponentsSoon(ent);
}
void CSimulation2::FlushDestroyedEntities()
{
m->m_ComponentManager.FlushDestroyedComponents();
}
IComponent* CSimulation2::QueryInterface(entity_id_t ent, int iid) const
{
return m->m_ComponentManager.QueryInterface(ent, iid);
}
void CSimulation2::PostMessage(entity_id_t ent, const CMessage& msg) const
{
m->m_ComponentManager.PostMessage(ent, msg);
}
void CSimulation2::BroadcastMessage(const CMessage& msg) const
{
m->m_ComponentManager.BroadcastMessage(msg);
}
CSimulation2::InterfaceList CSimulation2::GetEntitiesWithInterface(int iid)
{
return m->m_ComponentManager.GetEntitiesWithInterface(iid);
}
const CSimulation2::InterfaceListUnordered& CSimulation2::GetEntitiesWithInterfaceUnordered(int iid)
{
return m->m_ComponentManager.GetEntitiesWithInterfaceUnordered(iid);
}
const CSimContext& CSimulation2::GetSimContext() const
{
return m->m_SimContext;
}
ScriptInterface& CSimulation2::GetScriptInterface() const
{
return m->m_ComponentManager.GetScriptInterface();
}
void CSimulation2::InitGame(const CScriptVal& data)
{
CScriptVal ret; // ignored
GetScriptInterface().CallFunction(GetScriptInterface().GetGlobalObject(), "InitGame", data, ret);
}
bool CSimulation2::Update(int turnLength)
{
std::vector commands;
return m->Update(turnLength, commands);
}
bool CSimulation2::Update(int turnLength, const std::vector& commands)
{
return m->Update(turnLength, commands);
}
void CSimulation2::Interpolate(float frameLength, float frameOffset)
{
m->Interpolate(frameLength, frameOffset);
}
void CSimulation2::RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling)
{
CMessageRenderSubmit msg(collector, frustum, culling);
m->m_ComponentManager.BroadcastMessage(msg);
}
float CSimulation2::GetLastFrameOffset() const
{
return m->m_LastFrameOffset;
}
bool CSimulation2::LoadScripts(const VfsPath& path)
{
return m->LoadScripts(path);
}
bool CSimulation2::LoadDefaultScripts()
{
return (
m->LoadScripts(L"simulation/components/interfaces/") &&
m->LoadScripts(L"simulation/helpers/") &&
m->LoadScripts(L"simulation/components/")
);
}
void CSimulation2::SetStartupScript(const std::wstring& code)
{
m->m_StartupScript = code;
}
const std::wstring& CSimulation2::GetStartupScript()
{
return m->m_StartupScript;
}
void CSimulation2::SetMapSettings(const std::string& settings)
{
m->m_MapSettings = m->m_ComponentManager.GetScriptInterface().ParseJSON(settings);
}
void CSimulation2::SetMapSettings(const CScriptValRooted& settings)
{
m->m_MapSettings = settings;
}
std::string CSimulation2::GetMapSettingsString()
{
return m->m_ComponentManager.GetScriptInterface().StringifyJSON(m->m_MapSettings.get());
}
CScriptVal CSimulation2::GetMapSettings()
{
return m->m_MapSettings.get();
}
void CSimulation2::LoadPlayerSettings()
{
GetScriptInterface().CallFunctionVoid(GetScriptInterface().GetGlobalObject(), "LoadPlayerSettings", m->m_MapSettings);
}
void CSimulation2::LoadMapSettings()
{
// Initialize here instead of in Update()
GetScriptInterface().CallFunctionVoid(GetScriptInterface().GetGlobalObject(), "LoadMapSettings", m->m_MapSettings);
if (!m->m_StartupScript.empty())
GetScriptInterface().LoadScript(L"map startup script", m->m_StartupScript);
}
int CSimulation2::ProgressiveLoad()
{
return m->ProgressiveLoad();
}
Status CSimulation2::ReloadChangedFile(const VfsPath& path)
{
return m->ReloadChangedFile(path);
}
void CSimulation2::ResetState(bool skipScriptedComponents, bool skipAI)
{
m->ResetState(skipScriptedComponents, skipAI);
}
bool CSimulation2::ComputeStateHash(std::string& outHash, bool quick)
{
return m->m_ComponentManager.ComputeStateHash(outHash, quick);
}
bool CSimulation2::DumpDebugState(std::ostream& stream)
{
return m->m_ComponentManager.DumpDebugState(stream);
}
bool CSimulation2::SerializeState(std::ostream& stream)
{
return m->m_ComponentManager.SerializeState(stream);
}
bool CSimulation2::DeserializeState(std::istream& stream)
{
// TODO: need to make sure the required SYSTEM_ENTITY components get constructed
return m->m_ComponentManager.DeserializeState(stream);
}
std::string CSimulation2::GenerateSchema()
{
return m->m_ComponentManager.GenerateSchema();
}
std::vector CSimulation2::GetRMSData()
{
VfsPath path(L"maps/random/");
VfsPaths pathnames;
std::vector data;
// Find all ../maps/random/*.json
Status ret = vfs::GetPathnames(g_VFS, path, L"*.json", pathnames);
if (ret == INFO::OK)
{
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
{
// Load JSON file
CVFSFile file;
PSRETURN ret = file.Load(g_VFS, *it);
if (ret != PSRETURN_OK)
{
LOGERROR(L"Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
}
else
{
data.push_back(std::string(file.GetBuffer(), file.GetBuffer() + file.GetBufferSize()));
}
}
}
else
{
// Some error reading directory
wchar_t error[200];
LOGERROR(L"Error reading directory '%ls': %ls", path.string().c_str(), StatusDescription(ret, error, ARRAY_SIZE(error)));
}
return data;
}
std::vector CSimulation2::GetCivData()
{
VfsPath path(L"civs/");
VfsPaths pathnames;
std::vector data;
// Load all JSON files in civs directory
Status ret = vfs::GetPathnames(g_VFS, path, L"*.json", pathnames);
if (ret == INFO::OK)
{
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
{
// Load JSON file
CVFSFile file;
PSRETURN ret = file.Load(g_VFS, *it);
if (ret != PSRETURN_OK)
{
LOGERROR(L"CSimulation2::GetCivData: Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
}
else
{
data.push_back(std::string(file.GetBuffer(), file.GetBuffer() + file.GetBufferSize()));
}
}
}
else
{
// Some error reading directory
wchar_t error[200];
LOGERROR(L"CSimulation2::GetCivData: Error reading directory '%ls': %ls", path.string().c_str(), StatusDescription(ret, error, ARRAY_SIZE(error)));
}
return data;
}
std::string CSimulation2::GetPlayerDefaults()
{
return ReadJSON(L"simulation/data/player_defaults.json");
}
std::string CSimulation2::GetMapSizes()
{
return ReadJSON(L"simulation/data/map_sizes.json");
}
std::string CSimulation2::ReadJSON(VfsPath path)
{
std::string data;
if (!VfsFileExists(path))
{
LOGERROR(L"File '%ls' does not exist", path.string().c_str());
}
else
{
// Load JSON file
CVFSFile file;
PSRETURN ret = file.Load(g_VFS, path);
if (ret != PSRETURN_OK)
{
LOGERROR(L"Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
}
else
{
data = std::string(file.GetBuffer(), file.GetBuffer() + file.GetBufferSize());
}
}
return data;
}
std::string CSimulation2::GetAIData()
{
ScriptInterface& scriptInterface = GetScriptInterface();
std::vector aiData = ICmpAIManager::GetAIs(scriptInterface);
// Build single JSON string with array of AI data
CScriptValRooted ais;
if (!scriptInterface.Eval("({})", ais) || !scriptInterface.SetProperty(ais.get(), "AIData", aiData))
return std::string();
return scriptInterface.StringifyJSON(ais.get());
}
Index: ps/trunk/binaries/data/mods/public/simulation/templates/special/territory_block.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/special/territory_block.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/special/territory_block.xml (revision 9889)
@@ -0,0 +1,33 @@
+
+
+
+ 0
+ upright
+ false
+ 6.0
+
+
+
+ 9.0
+
+
+
+
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+
+
+
+ false
+ true
+ structures/hellenes/wall_med.xml
+
+
+ 100
+
+
Index: ps/trunk/binaries/data/mods/public/simulation/templates/special/territory_pull.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/special/territory_pull.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/special/territory_pull.xml (revision 9889)
@@ -0,0 +1,33 @@
+
+
+
+ 0
+ upright
+ false
+ 6.0
+
+
+
+ 9.0
+
+
+
+
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+
+
+
+ false
+ true
+ structures/hellenes/wall_med.xml
+
+
+ 0
+
+
Index: ps/trunk/binaries/data/mods/public/shaders/terrain_common.fp
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/terrain_common.fp (revision 9888)
+++ ps/trunk/binaries/data/mods/public/shaders/terrain_common.fp (revision 9889)
@@ -1,94 +1,98 @@
!!ARBfp1.0
#ifdef USE_FP_SHADOW
OPTION ARB_fragment_program_shadow;
#endif
#ifdef LIGHTING_MODEL_old
#define CLAMP_LIGHTING
#endif
#ifdef CLAMP_LIGHTING // for compat with old scenarios that expect clamped lighting
#define MAD_MAYBE_SAT MAD_SAT
#else
#define MAD_MAYBE_SAT MAD
#endif
PARAM ambient = program.local[0];
#ifdef DECAL
PARAM shadingColor = program.local[1];
#endif
#ifdef USE_SHADOW_PCF
PARAM shadowOffsets1 = program.local[2];
PARAM shadowOffsets2 = program.local[3];
TEMP offset;
#endif
TEMP tex;
TEMP temp;
TEMP diffuse;
TEMP color;
#ifdef BLEND
// Use alpha from blend texture
// TODO: maybe we should invert the texture instead of doing SUB here?
TEX tex.a, fragment.texcoord[1], texture[1], 2D;
SUB result.color.a, 1.0, tex.a;
#endif
// Load diffuse colour
TEX color, fragment.texcoord[0], texture[0], 2D;
#ifdef DECAL
// Use alpha from main texture
MOV result.color.a, color;
#endif
// Compute color = texture * (ambient + diffuse*shadow)
// (diffuse is 2*fragment.color due to clamp-avoidance in the vertex program)
#ifdef USE_SHADOW
#ifdef USE_FP_SHADOW
#ifdef USE_SHADOW_PCF
MOV offset.zw, fragment.texcoord[2];
ADD offset.xy, fragment.texcoord[2], shadowOffsets1;
TEX temp.x, offset, texture[2], SHADOW2D;
ADD offset.xy, fragment.texcoord[2], shadowOffsets1.zwzw;
TEX temp.y, offset, texture[2], SHADOW2D;
ADD offset.xy, fragment.texcoord[2], shadowOffsets2;
TEX temp.z, offset, texture[2], SHADOW2D;
ADD offset.xy, fragment.texcoord[2], shadowOffsets2.zwzw;
TEX temp.w, offset, texture[2], SHADOW2D;
DP4 temp, temp, 0.25;
#else
TEX temp, fragment.texcoord[2], texture[2], SHADOW2D;
#endif
#else
TEX tex, fragment.texcoord[2], texture[2], 2D;
MOV_SAT temp.z, fragment.texcoord[2].z;
SGE temp, tex.x, temp.z;
#endif
#ifdef CLAMP_LIGHTING
MAD_SAT diffuse.rgb, fragment.color, 2.0, ambient;
LRP temp.rgb, temp, diffuse, ambient;
#else
MUL diffuse.rgb, fragment.color, 2.0;
MAD temp.rgb, diffuse, temp, ambient;
#endif
MUL color.rgb, color, temp;
#else
MAD_MAYBE_SAT temp.rgb, fragment.color, 2.0, ambient;
MUL color.rgb, color, temp;
#endif
+// Blend with the territory boundary texture
+TEX tex, fragment.texcoord[4], texture[4], 2D;
+LRP color.rgb, tex.a, tex, color;
+
// Multiply everything by the LOS texture
TEX tex.a, fragment.texcoord[3], texture[3], 2D;
MUL color.rgb, color, tex.a;
#ifdef DECAL
MUL result.color.rgb, color, shadingColor;
#else
MOV result.color.rgb, color;
#endif
END
Index: ps/trunk/binaries/data/mods/public/shaders/terrain_decal.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/terrain_decal.xml (revision 9888)
+++ ps/trunk/binaries/data/mods/public/shaders/terrain_decal.xml (revision 9889)
@@ -1,23 +1,25 @@
+
+
Index: ps/trunk/binaries/data/mods/public/shaders/terrain_base.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/terrain_base.xml (revision 9888)
+++ ps/trunk/binaries/data/mods/public/shaders/terrain_base.xml (revision 9889)
@@ -1,20 +1,22 @@
+
+
Index: ps/trunk/binaries/data/mods/public/shaders/terrain_blend.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/terrain_blend.xml (revision 9888)
+++ ps/trunk/binaries/data/mods/public/shaders/terrain_blend.xml (revision 9889)
@@ -1,23 +1,25 @@
+
+
Index: ps/trunk/binaries/data/mods/public/shaders/terrain_common.vp
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/terrain_common.vp (revision 9888)
+++ ps/trunk/binaries/data/mods/public/shaders/terrain_common.vp (revision 9889)
@@ -1,42 +1,44 @@
!!ARBvp1.0
PARAM sunColor = program.local[0];
PARAM losTransform = program.local[1];
PARAM shadowTransform[4] = { program.local[2..5] };
+PARAM territoryTransform = program.local[6];
TEMP lighting;
//// Compute position and normal:
ATTRIB position = vertex.position;
DP4 result.position.x, state.matrix.mvp.row[0], position;
DP4 result.position.y, state.matrix.mvp.row[1], position;
DP4 result.position.z, state.matrix.mvp.row[2], position;
DP4 result.position.w, state.matrix.mvp.row[3], position;
//// Compute lighting:
// Diffuse factor is precomputed in vertex attribute
// Scale diffuse to allow overbrightness (since result.color will be clamped to [0, 1])
MUL lighting, vertex.color, 0.5;
// Apply light colour
MUL result.color, lighting, sunColor;
//// Texture coordinates:
MOV result.texcoord[0], vertex.texcoord[0];
#ifdef BLEND
MOV result.texcoord[1], vertex.texcoord[1];
#endif
#ifdef USE_SHADOW
DP4 result.texcoord[2].x, shadowTransform[0], position;
DP4 result.texcoord[2].y, shadowTransform[1], position;
DP4 result.texcoord[2].z, shadowTransform[2], position;
DP4 result.texcoord[2].w, shadowTransform[3], position;
#endif
MAD result.texcoord[3], position.xzzz, losTransform.x, losTransform.y;
+MAD result.texcoord[4], position.xzzz, territoryTransform.x, territoryTransform.y;
END