Changeset View
Changeset View
Standalone View
Standalone View
source/simulation2/components/CCmpItineraryPointRenderer.cpp
/* Copyright (C) 2017 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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "precompiled.h" | |||||
#include "CCmpItineraryPointRenderer.h" | |||||
void CCmpItineraryPointRenderer::ClassInit(CComponentManager& componentManager) | |||||
{ | |||||
componentManager.SubscribeToMessageType(MT_OwnershipChanged); | |||||
componentManager.SubscribeToMessageType(MT_TurnStart); | |||||
componentManager.SubscribeToMessageType(MT_Destroy); | |||||
componentManager.SubscribeToMessageType(MT_PositionChanged); | |||||
} | |||||
void CCmpItineraryPointRenderer::MergeVisibilitySegments(std::vector<SVisibilitySegment>& segments) | |||||
{ | |||||
// Scan for single-point segments; if they are inbetween two other segments, delete them and merge the surrounding segments. | |||||
// If they're at either end of the path, include them in their bordering segment (but only if those bordering segments aren't | |||||
// themselves single-point segments, because then we would want those to get absorbed by its surrounding ones first). | |||||
// first scan for absorptions of single-point surrounded segments (i.e. excluding edge segments) | |||||
size_t numSegments = segments.size(); | |||||
// WARNING: FOR LOOP TRICKERY AHEAD! | |||||
for (size_t i = 1; i < numSegments - 1;) | |||||
{ | |||||
SVisibilitySegment& segment = segments[i]; | |||||
if (segment.IsSinglePoint()) | |||||
{ | |||||
// since the segments' visibility alternates, the surrounding ones should have the same visibility | |||||
ENSURE(segments[i - 1].m_Visible == segments[i + 1].m_Visible); | |||||
segments[i - 1].m_EndIndex = segments[i + 1].m_EndIndex; // make previous segment span all the way across to the next | |||||
segments.erase(segments.begin() + i); // erase this segment ... | |||||
segments.erase(segments.begin() + i); // and the next (we removed [i], so [i+1] is now at position [i]) | |||||
numSegments -= 2; // we removed 2 segments, so update the loop condition | |||||
// in the next iteration, i should still point to the segment right after the one that got expanded, which is now | |||||
// at position i; so don't increment i here | |||||
} | |||||
else | |||||
{ | |||||
++i; | |||||
} | |||||
} | |||||
ENSURE(numSegments == segments.size()); | |||||
// check to see if the first segment needs to be merged with its neighbour | |||||
if (segments.size() >= 2 && segments[0].IsSinglePoint()) | |||||
{ | |||||
int firstSegmentStartIndex = segments.front().m_StartIndex; | |||||
ENSURE(firstSegmentStartIndex == 0); | |||||
ENSURE(!segments[1].IsSinglePoint()); // at this point, the second segment should never be a single-point segment | |||||
segments.erase(segments.begin()); | |||||
segments.front().m_StartIndex = firstSegmentStartIndex; | |||||
} | |||||
// check to see if the last segment needs to be merged with its neighbour | |||||
if (segments.size() >= 2 && segments[segments.size() - 1].IsSinglePoint()) | |||||
{ | |||||
int lastSegmentEndIndex = segments.back().m_EndIndex; | |||||
ENSURE(!segments[segments.size() - 2].IsSinglePoint()); // at this point, the second-to-last segment should never be a single-point segment | |||||
segments.erase(segments.end()); | |||||
segments.back().m_EndIndex = lastSegmentEndIndex; | |||||
} | |||||
// -------------------------------------------------------------------------------------------------------- | |||||
// at this point, every segment should have at least 2 points | |||||
for (size_t i = 0; i < segments.size(); ++i) | |||||
{ | |||||
ENSURE(!segments[i].IsSinglePoint()); | |||||
ENSURE(segments[i].m_EndIndex > segments[i].m_StartIndex); | |||||
} | |||||
} | |||||
void CCmpItineraryPointRenderer::Init(const CParamNode& paramNode) | |||||
{ | |||||
m_Displayed = false; | |||||
m_SmoothPath = true; | |||||
m_LastOwner = INVALID_PLAYER; | |||||
m_LastMarkerCount = 0; | |||||
m_EnableDebugNodeOverlay = false; | |||||
// --------------------------------------------------------------------------------------------- | |||||
// load some XML configuration data (schema guarantees that all these nodes are valid) | |||||
m_MarkerTemplate = paramNode.GetChild("MarkerTemplate").ToString(); | |||||
const CParamNode& lineColor = paramNode.GetChild("LineColour"); | |||||
m_LineColor = CColor( | |||||
lineColor.GetChild("@r").ToInt() / 255.f, | |||||
lineColor.GetChild("@g").ToInt() / 255.f, | |||||
lineColor.GetChild("@b").ToInt() / 255.f, | |||||
1.f | |||||
); | |||||
const CParamNode& lineDashColor = paramNode.GetChild("LineDashColour"); | |||||
m_LineDashColor = CColor( | |||||
lineDashColor.GetChild("@r").ToInt() / 255.f, | |||||
lineDashColor.GetChild("@g").ToInt() / 255.f, | |||||
lineDashColor.GetChild("@b").ToInt() / 255.f, | |||||
1.f | |||||
); | |||||
m_LineThickness = paramNode.GetChild("LineThickness").ToFixed().ToFloat(); | |||||
m_LineTexturePath = paramNode.GetChild("LineTexture").ToString(); | |||||
m_LineTextureMaskPath = paramNode.GetChild("LineTextureMask").ToString(); | |||||
m_LineStartCapType = SOverlayTexturedLine::StrToLineCapType(paramNode.GetChild("LineStartCap").ToString()); | |||||
m_LineEndCapType = SOverlayTexturedLine::StrToLineCapType(paramNode.GetChild("LineEndCap").ToString()); | |||||
m_LineCostClass = paramNode.GetChild("LineCostClass").ToUTF8(); | |||||
m_LinePassabilityClass = paramNode.GetChild("LinePassabilityClass").ToUTF8(); | |||||
// --------------------------------------------------------------------------------------------- | |||||
// load some textures | |||||
if (CRenderer::IsInitialised()) | |||||
{ | |||||
CTextureProperties texturePropsBase(m_LineTexturePath); | |||||
texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); | |||||
texturePropsBase.SetMaxAnisotropy(4.f); | |||||
m_Texture = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase); | |||||
CTextureProperties texturePropsMask(m_LineTextureMaskPath); | |||||
texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); | |||||
texturePropsMask.SetMaxAnisotropy(4.f); | |||||
m_TextureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask); | |||||
} | |||||
} | |||||
void CCmpItineraryPointRenderer::Deinit() {} | |||||
void CCmpItineraryPointRenderer::Deserialize(const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) | |||||
{ | |||||
Init(paramNode); | |||||
} | |||||
void CCmpItineraryPointRenderer::Serialize(ISerializer& UNUSED(serialize)) | |||||
{ | |||||
// Do not serialize anything; this is a rendering-only component, it does not and should not affect simulation state | |||||
} | |||||
void CCmpItineraryPointRenderer::AddPosition(CFixedVector2D pos, bool recompute) | |||||
{ | |||||
m_ItineraryPoints.push_back(pos); | |||||
UpdateMarkers(); | |||||
if (recompute) | |||||
RecomputeAllItineraryPointPaths(); | |||||
else | |||||
RecomputeItineraryPointPath_wrapper(m_ItineraryPoints.size() - 1); | |||||
UpdateMessageSubscriptions(); | |||||
} | |||||
void CCmpItineraryPointRenderer::AddPosition_wrapper(const CFixedVector2D& pos) | |||||
{ | |||||
AddPosition(pos, false); | |||||
} | |||||
void CCmpItineraryPointRenderer::ConstructAllOverlayLines() | |||||
{ | |||||
m_TexturedOverlayLines.clear(); | |||||
for (size_t i = 0; i < m_Path.size(); ++i) | |||||
ConstructOverlayLines(i); | |||||
} | |||||
bool CCmpItineraryPointRenderer::IsSet() const | |||||
{ | |||||
return !m_ItineraryPoints.empty(); | |||||
} | |||||
void CCmpItineraryPointRenderer::RenderSubmit(SceneCollector& collector) | |||||
{ | |||||
// we only get here if the rally point is set and should be displayed | |||||
for (std::vector<std::vector<SOverlayTexturedLine> >::iterator it = m_TexturedOverlayLines.begin(); | |||||
it != m_TexturedOverlayLines.end(); ++it) | |||||
{ | |||||
for (std::vector<SOverlayTexturedLine>::iterator iter = (*it).begin(); iter != (*it).end(); ++iter) | |||||
{ | |||||
if (!(*iter).m_Coords.empty()) | |||||
collector.Submit(&(*iter)); | |||||
} | |||||
} | |||||
if (m_EnableDebugNodeOverlay && !m_DebugNodeOverlays.empty()) | |||||
{ | |||||
for (size_t i = 0; i < m_DebugNodeOverlays.size(); ++i) | |||||
for (size_t j = 0; j < m_DebugNodeOverlays[i].size(); ++j) | |||||
collector.Submit(&m_DebugNodeOverlays[i][j]); | |||||
} | |||||
} | |||||
void CCmpItineraryPointRenderer::SetDisplayed(bool displayed) | |||||
{ | |||||
if (m_Displayed != displayed) | |||||
{ | |||||
m_Displayed = displayed; | |||||
// move the markers out of oblivion and back into the real world, or vice-versa | |||||
UpdateMarkers(); | |||||
// Check for changes to the SoD and update the overlay lines accordingly. We need to do this here because this method | |||||
// only takes effect when the display flag is active; we need to pick up changes to the SoD that might have occurred | |||||
// while this rally point was not being displayed. | |||||
UpdateOverlayLines(); | |||||
UpdateMessageSubscriptions(); | |||||
} | |||||
} | |||||
void CCmpItineraryPointRenderer::SetPosition(const CFixedVector2D& pos) | |||||
{ | |||||
if (!(m_ItineraryPoints.size() == 1 && m_ItineraryPoints.front() == pos)) | |||||
{ | |||||
m_ItineraryPoints.clear(); | |||||
AddPosition(pos, true); | |||||
// Don't need to UpdateMessageSubscriptions here since AddPosition already calls it | |||||
} | |||||
} | |||||
void CCmpItineraryPointRenderer::UpdateOverlayLines() | |||||
{ | |||||
// We should only do this if the rally point is currently being displayed and set inside the world, otherwise it's a massive | |||||
// waste of time to calculate all this stuff (this method is called every turn) | |||||
if (!m_Displayed || !CCmpItineraryPointRenderer::IsSet()) | |||||
return; | |||||
// see if there have been any changes to the SoD by grabbing the visibility edge points and comparing them to the previous ones | |||||
std::vector<std::vector<SVisibilitySegment> > newVisibilitySegments; | |||||
for (size_t i = 0; i < m_Path.size(); ++i) | |||||
{ | |||||
std::vector<SVisibilitySegment> tmp; | |||||
newVisibilitySegments.push_back(tmp); | |||||
GetVisibilitySegments(newVisibilitySegments[i], i); | |||||
} | |||||
// Check if the full path changed, then reconstruct all overlay lines, otherwise check if a segment changed and update that. | |||||
if (m_VisibilitySegments.size() != newVisibilitySegments.size()) | |||||
{ | |||||
m_VisibilitySegments = newVisibilitySegments; // save the new visibility segments to compare against next time | |||||
ConstructAllOverlayLines(); | |||||
} | |||||
else | |||||
{ | |||||
for (size_t i = 0; i < m_VisibilitySegments.size(); ++i) | |||||
{ | |||||
if (m_VisibilitySegments[i] != newVisibilitySegments[i]) | |||||
{ | |||||
// The visibility segments have changed, reconstruct the overlay lines to match. NOTE: The path itself doesn't | |||||
// change, only the overlay lines we construct from it. | |||||
m_VisibilitySegments[i] = newVisibilitySegments[i]; // save the new visibility segments to compare against next time | |||||
ConstructOverlayLines(i); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
Wildfire Games · Phabricator