Rendering marker lines between buildings and rally points
Added command button to focus on rally point (implements #524) This was SVN commit r10704.
This commit is contained in:
parent
fbb0e3995f
commit
db864f10c3
@ -8,8 +8,23 @@
|
||||
</variant>
|
||||
</group>
|
||||
<group>
|
||||
<variant name="waypoint_hellenes">
|
||||
<variant name="hele">
|
||||
<texture>props/banner_greek.png</texture>
|
||||
</variant>
|
||||
<variant name="pers">
|
||||
<texture>props/banner_persian.png</texture>
|
||||
</variant>
|
||||
<variant name="celt">
|
||||
<texture>props/banner_celt.png</texture>
|
||||
</variant>
|
||||
<variant name="cart">
|
||||
<texture>props/banner_carthage.png</texture>
|
||||
</variant>
|
||||
<variant name="iber">
|
||||
<texture>props/banner_iberians.png</texture>
|
||||
</variant>
|
||||
<variant name="rome">
|
||||
<texture>props/banner_romans.png</texture>
|
||||
</variant>
|
||||
</group>
|
||||
</actor>
|
||||
|
BIN
binaries/data/mods/public/art/textures/misc/rallypoint_line.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/misc/rallypoint_line.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/misc/rallypoint_line_mask.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/misc/rallypoint_line_mask.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/ui/session/icons/single/focus-rally.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/icons/single/focus-rally.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -135,7 +135,6 @@ function getActionInfo(action, target)
|
||||
return entState && entState.rallyPoint;
|
||||
});
|
||||
|
||||
|
||||
if (!target)
|
||||
{
|
||||
if (action == "set-rallypoint" && haveRallyPoints)
|
||||
@ -1114,6 +1113,24 @@ function performCommand(entity, commandName)
|
||||
break;
|
||||
case "unload-all":
|
||||
unloadAll(entity);
|
||||
break;
|
||||
case "focus-rally":
|
||||
// if the selected building has a rally point set, move the camera to it; otherwise, move to the building itself
|
||||
// (since that's where units will spawn without a rally point)
|
||||
var focusTarget = null;
|
||||
if (entState.rallyPoint && entState.rallyPoint.position)
|
||||
{
|
||||
focusTarget = entState.rallyPoint.position;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (entState.position)
|
||||
focusTarget = entState.position;
|
||||
}
|
||||
|
||||
if (focusTarget !== null)
|
||||
Engine.CameraMoveTo(focusTarget.x, focusTarget.z);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -270,7 +270,8 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
|
||||
break;
|
||||
|
||||
case COMMAND:
|
||||
if (item == "unload-all")
|
||||
// here, "item" is an object with properties .name (command name), .tooltip and .icon (relative to session/icons/single)
|
||||
if (item.name == "unload-all")
|
||||
{
|
||||
var count = unitEntState.garrisonHolder.entities.length;
|
||||
getGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (count > 0 ? count : "");
|
||||
@ -280,7 +281,7 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
|
||||
getGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = "";
|
||||
}
|
||||
|
||||
tooltip = toTitleCase(item);
|
||||
tooltip = (item.tooltip ? item.tooltip : toTitleCase(item.name));
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -334,7 +335,7 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
|
||||
{
|
||||
//icon.cell_id = i;
|
||||
//icon.cell_id = getCommandCellId(item);
|
||||
icon.sprite = "stretched:session/icons/single/" + getCommandImage(item);
|
||||
icon.sprite = "stretched:session/icons/single/" + item.icon;
|
||||
|
||||
}
|
||||
else if (template.icon)
|
||||
@ -477,7 +478,7 @@ function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, s
|
||||
var commands = getEntityCommandsList(entState);
|
||||
if (commands.length)
|
||||
setupUnitPanel("Command", usedPanels, entState, commands,
|
||||
function (item) { performCommand(entState.id, item); } );
|
||||
function (item) { performCommand(entState.id, item.name); } );
|
||||
|
||||
if (entState.garrisonHolder)
|
||||
{
|
||||
|
@ -189,23 +189,6 @@ function getFormationCellId(formationName)
|
||||
}
|
||||
}
|
||||
|
||||
function getCommandImage(commandName)
|
||||
{
|
||||
switch (commandName)
|
||||
{
|
||||
case "delete":
|
||||
return "kill_small.png";
|
||||
case "unload-all":
|
||||
return "garrison-out.png";
|
||||
case "garrison":
|
||||
return "garrison.png";
|
||||
case "repair":
|
||||
return "repair.png";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function getEntityFormationsList(entState)
|
||||
{
|
||||
var civ = g_Players[entState.player].civ;
|
||||
@ -234,12 +217,47 @@ function getEntityCommandsList(entState)
|
||||
{
|
||||
var commands = [];
|
||||
if (entState.garrisonHolder)
|
||||
commands.push("unload-all");
|
||||
{
|
||||
commands.push({
|
||||
"name": "unload-all",
|
||||
"tooltip": "Unload All",
|
||||
"icon": "garrison-out.png"
|
||||
});
|
||||
}
|
||||
|
||||
commands.push({
|
||||
"name": "delete",
|
||||
"tooltip": "Delete",
|
||||
"icon": "kill_small.png"
|
||||
});
|
||||
|
||||
if (isUnit(entState))
|
||||
commands.push("garrison");
|
||||
{
|
||||
commands.push({
|
||||
"name": "garrison",
|
||||
"tooltip": "Garrison",
|
||||
"icon": "garrison.png"
|
||||
});
|
||||
}
|
||||
|
||||
if (entState.buildEntities)
|
||||
commands.push("repair");
|
||||
commands.push("delete");
|
||||
{
|
||||
commands.push({
|
||||
"name": "repair",
|
||||
"tooltip": "Repair",
|
||||
"icon": "repair.png"
|
||||
});
|
||||
}
|
||||
|
||||
if (entState.rallyPoint)
|
||||
{
|
||||
commands.push({
|
||||
"name": "focus-rally",
|
||||
"tooltip": "Focus on Rally Point",
|
||||
"icon": "focus-rally.png"
|
||||
});
|
||||
}
|
||||
|
||||
return commands;
|
||||
}
|
||||
|
||||
|
@ -3,18 +3,25 @@ PARAM objectColor = program.local[0];
|
||||
TEMP base;
|
||||
TEMP mask;
|
||||
TEMP color;
|
||||
TEMP los;
|
||||
|
||||
|
||||
// Combine base texture and color, using mask texture
|
||||
TEX base, fragment.texcoord[0], texture[0], 2D;
|
||||
TEX mask, fragment.texcoord[0], texture[1], 2D;
|
||||
LRP color.rgb, mask, objectColor, base;
|
||||
|
||||
// Multiply by LOS texture
|
||||
TEX los, fragment.texcoord[1], texture[2], 2D;
|
||||
MUL result.color.rgb, color, los.a;
|
||||
#ifdef IGNORE_LOS
|
||||
MOV result.color.rgb, color;
|
||||
#else
|
||||
// Multiply RGB by LOS texture (alpha channel)
|
||||
TEMP los;
|
||||
TEX los, fragment.texcoord[1], texture[2], 2D;
|
||||
MUL result.color.rgb, color, los.a;
|
||||
#endif
|
||||
|
||||
// Use alpha from base texture
|
||||
// Use alpha from base texture, combined with the object color alpha.
|
||||
// The latter is usually 1, so this basically comes down to base.a
|
||||
MUL result.color.a, objectColor.a, base.a;
|
||||
|
||||
|
||||
END
|
||||
|
@ -26,6 +26,10 @@ GuiInterface.prototype.Init = function()
|
||||
/*
|
||||
* All of the functions defined below are called via Engine.GuiInterfaceCall(name, arg)
|
||||
* from GUI scripts, and executed here with arguments (player, arg).
|
||||
*
|
||||
* CAUTION: The input to the functions in this module is not network-synchronised, so it
|
||||
* mustn't affect the simulation state (i.e. the data that is serialised and can affect
|
||||
* the behaviour of the rest of the simulation) else it'll cause out-of-sync errors.
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -223,7 +227,7 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
|
||||
var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint);
|
||||
if (cmpRallyPoint)
|
||||
{
|
||||
ret.rallyPoint = { };
|
||||
ret.rallyPoint = {'position': cmpRallyPoint.GetPosition()}; // undefined or {x,z} object
|
||||
}
|
||||
|
||||
var cmpGarrisonHolder = Engine.QueryInterface(ent, IID_GarrisonHolder);
|
||||
@ -418,62 +422,62 @@ GuiInterface.prototype.SetStatusBars = function(player, cmd)
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays the rally point of a building
|
||||
* Displays the rally point of a given list of entities (carried in cmd.entities).
|
||||
*
|
||||
* The 'cmd' object may carry its own x/z coordinate pair indicating the location where the rally point should
|
||||
* be rendered, in order to support instantaneously rendering a rally point marker at a specified location
|
||||
* instead of incurring a delay while PostNetworkCommand processes the set-rallypoint command (see input.js).
|
||||
* If cmd doesn't carry a custom location, then the position to render the marker at will be read from the
|
||||
* RallyPoint component.
|
||||
*/
|
||||
GuiInterface.prototype.DisplayRallyPoint = function(player, cmd)
|
||||
{
|
||||
// If there are rally points already displayed, destroy them
|
||||
for each (var ent in this.rallyPoints)
|
||||
{
|
||||
// Hide it first (the destruction won't be instantaneous)
|
||||
var cmpPosition = Engine.QueryInterface(ent, IID_Position);
|
||||
cmpPosition.MoveOutOfWorld();
|
||||
var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
|
||||
var cmpPlayer = Engine.QueryInterface(cmpPlayerMan.GetPlayerByID(player), IID_Player);
|
||||
|
||||
Engine.DestroyEntity(ent);
|
||||
// If there are some rally points already displayed, first hide them
|
||||
for each (var ent in this.entsRallyPointsDisplayed)
|
||||
{
|
||||
var cmpRallyPointRenderer = Engine.QueryInterface(ent, IID_RallyPointRenderer);
|
||||
if (cmpRallyPointRenderer)
|
||||
cmpRallyPointRenderer.SetDisplayed(false);
|
||||
}
|
||||
|
||||
this.rallyPoints = [];
|
||||
|
||||
var positions = [];
|
||||
// DisplayRallyPoints is called passing a list of entities for which
|
||||
// rally points must be displayed
|
||||
// Show the rally points for the passed entities
|
||||
for each (var ent in cmd.entities)
|
||||
{
|
||||
var cmpRallyPointRenderer = Engine.QueryInterface(ent, IID_RallyPointRenderer);
|
||||
if (!cmpRallyPointRenderer)
|
||||
continue;
|
||||
|
||||
// entity must have a rally point component to display a rally point marker
|
||||
// (regardless of whether cmd specifies a custom location)
|
||||
var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint);
|
||||
if (!cmpRallyPoint)
|
||||
continue;
|
||||
|
||||
// Verify the owner
|
||||
var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
|
||||
if (!cmpOwnership || cmpOwnership.GetOwner() != player)
|
||||
continue;
|
||||
if (!(cmpPlayer && cmpPlayer.CanControlAllUnits()))
|
||||
if (!cmpOwnership || cmpOwnership.GetOwner() != player)
|
||||
continue;
|
||||
|
||||
// If the command was passed an explicit position, use that and
|
||||
// override the real rally point position; otherwise use the real position
|
||||
var pos;
|
||||
if (cmd.x && cmd.z)
|
||||
pos = {"x": cmd.x, "z": cmd.z};
|
||||
pos = cmd;
|
||||
else
|
||||
pos = cmpRallyPoint.GetPosition();
|
||||
pos = cmpRallyPoint.GetPosition(); // may return undefined
|
||||
|
||||
if (pos)
|
||||
{
|
||||
// TODO: it'd probably be nice if we could draw some kind of line
|
||||
// between the building and pos, to make the marker easy to find even
|
||||
// if it's a long way from the building
|
||||
cmpRallyPointRenderer.SetPosition({'x': pos.x, 'y': pos.z}); // SetPosition takes a CFixedVector2D which has X/Y components, not X/Z
|
||||
|
||||
positions.push(pos);
|
||||
}
|
||||
cmpRallyPointRenderer.SetDisplayed(true);
|
||||
}
|
||||
|
||||
// Add rally point entity for each building
|
||||
for each (var pos in positions)
|
||||
{
|
||||
var rallyPoint = Engine.AddLocalEntity("actor|props/special/common/waypoint_flag.xml");
|
||||
var cmpPosition = Engine.QueryInterface(rallyPoint, IID_Position);
|
||||
cmpPosition.JumpTo(pos.x, pos.z);
|
||||
this.rallyPoints.push(rallyPoint);
|
||||
}
|
||||
// Remember which entities have their rally points displayed so we can hide them again
|
||||
this.entsRallyPointsDisplayed = cmd.entities;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -13,7 +13,7 @@ RallyPoint.prototype.SetPosition = function(x, z)
|
||||
this.pos = {
|
||||
"x": x,
|
||||
"z": z
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
RallyPoint.prototype.Unset = function()
|
||||
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="special/actor">
|
||||
<VisualActor>
|
||||
<Actor>props/special/common/waypoint_flag.xml</Actor>
|
||||
</VisualActor>
|
||||
<Vision>
|
||||
<AlwaysVisible>true</AlwaysVisible>
|
||||
</Vision>
|
||||
</Entity>
|
@ -34,6 +34,9 @@
|
||||
<Position>
|
||||
<Floating>true</Floating>
|
||||
</Position>
|
||||
<RallyPointRenderer>
|
||||
<LinePassabilityClass>ship</LinePassabilityClass>
|
||||
</RallyPointRenderer>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>interface/select/building/sel_dock.xml</select>
|
||||
|
@ -53,7 +53,19 @@
|
||||
<DisableBlockPathfinding>false</DisableBlockPathfinding>
|
||||
</Obstruction>
|
||||
<OverlayRenderer/>
|
||||
<RallyPoint/>
|
||||
<RallyPoint />
|
||||
<RallyPointRenderer>
|
||||
<MarkerTemplate>special/rallypoint</MarkerTemplate>
|
||||
<LineTexture>art/textures/misc/rallypoint_line.png</LineTexture>
|
||||
<LineTextureMask>art/textures/misc/rallypoint_line_mask.png</LineTextureMask>
|
||||
<LineThickness>0.2</LineThickness>
|
||||
<LineColour r="35" g="86" b="188" />
|
||||
<LineDashColour r="158" g="11" b="15" />
|
||||
<LineStartCap>square</LineStartCap>
|
||||
<LineEndCap>round</LineEndCap>
|
||||
<LineCostClass>default</LineCostClass>
|
||||
<LinePassabilityClass>default</LinePassabilityClass>
|
||||
</RallyPointRenderer>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>interface/select/building/sel_universal.xml</select>
|
||||
|
@ -36,6 +36,9 @@
|
||||
<Position>
|
||||
<Floating>true</Floating>
|
||||
</Position>
|
||||
<RallyPointRenderer>
|
||||
<LinePassabilityClass>ship</LinePassabilityClass>
|
||||
</RallyPointRenderer>
|
||||
<ResourceDropsite>
|
||||
<Types>food wood stone metal</Types>
|
||||
</ResourceDropsite>
|
||||
|
@ -899,7 +899,7 @@ void CGameView::Update(float DeltaTime)
|
||||
m->ViewCamera.UpdateFrustum();
|
||||
}
|
||||
|
||||
void CGameView::MoveCameraTarget(const CVector3D& target, bool minimap)
|
||||
void CGameView::MoveCameraTarget(const CVector3D& target)
|
||||
{
|
||||
// Maintain the same orientation and level of zoom, if we can
|
||||
// (do this by working out the point the camera is looking at, saving
|
||||
@ -912,11 +912,6 @@ void CGameView::MoveCameraTarget(const CVector3D& target, bool minimap)
|
||||
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());
|
||||
|
||||
|
@ -79,7 +79,7 @@ public:
|
||||
|
||||
InReaction HandleEvent(const SDL_Event_* ev);
|
||||
|
||||
void MoveCameraTarget(const CVector3D& target, bool minimap = false);
|
||||
void MoveCameraTarget(const CVector3D& target);
|
||||
void ResetCameraTarget(const CVector3D& target);
|
||||
void ResetCameraAngleZoom();
|
||||
void CameraFollow(entity_id_t entity, bool firstPerson);
|
||||
|
37
source/graphics/Overlay.cpp
Normal file
37
source/graphics/Overlay.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "ps/CStr.h"
|
||||
#include "Overlay.h"
|
||||
|
||||
SOverlayTexturedLine::LineCapType SOverlayTexturedLine::StrToLineCapType(const std::wstring& str)
|
||||
{
|
||||
if (str == L"round")
|
||||
return LINECAP_ROUND;
|
||||
else if (str == L"sharp")
|
||||
return LINECAP_SHARP;
|
||||
else if (str == L"square")
|
||||
return LINECAP_SQUARE;
|
||||
else if (str == L"flat")
|
||||
return LINECAP_FLAT;
|
||||
else {
|
||||
debug_warn(L"[Overlay] Unrecognized line cap type identifier");
|
||||
return LINECAP_FLAT;
|
||||
}
|
||||
}
|
@ -49,16 +49,43 @@ struct SOverlayLine
|
||||
*/
|
||||
struct SOverlayTexturedLine
|
||||
{
|
||||
SOverlayTexturedLine() : m_Terrain(NULL), m_Thickness(1.0f) { }
|
||||
enum LineCapType
|
||||
{
|
||||
LINECAP_FLAT, ///< no line ending; abrupt stop of the line (aka. butt ending)
|
||||
|
||||
/**
|
||||
* Semi-circular line ending. The texture is mapped by curving the left vertical edge around the semi-circle's rim. That is,
|
||||
* the center point has UV coordinates (0.5;0.5), and the rim vertices all have U coordinate 0 and a V coordinate that ranges
|
||||
* from 0 to 1 as the rim is traversed.
|
||||
*/
|
||||
LINECAP_ROUND,
|
||||
LINECAP_SHARP, ///< sharp point ending
|
||||
LINECAP_SQUARE, ///< square end that extends half the line width beyond the line end
|
||||
};
|
||||
|
||||
SOverlayTexturedLine()
|
||||
: m_Terrain(NULL), m_Thickness(1.0f), m_Closed(false), m_AlwaysVisible(false), m_StartCapType(LINECAP_FLAT), m_EndCapType(LINECAP_FLAT)
|
||||
{}
|
||||
|
||||
CTerrain* m_Terrain;
|
||||
CTexturePtr m_TextureBase;
|
||||
CTexturePtr m_TextureMask;
|
||||
CColor m_Color;
|
||||
std::vector<float> m_Coords; // (x, z) vertex coordinate pairs; y is computed automatically; shape is automatically closed
|
||||
float m_Thickness; // world-space units
|
||||
CColor m_Color; ///< Color to apply to the line texture
|
||||
std::vector<float> m_Coords; ///< (x, z) vertex coordinate pairs; y is computed automatically
|
||||
float m_Thickness; ///< Half-width of the line, in world-space units
|
||||
|
||||
shared_ptr<CRenderData> m_RenderData; // cached renderer data (shared_ptr so that copies/deletes are automatic)
|
||||
bool m_Closed; ///< Should this line be treated as a closed loop? (if set, the end cap settings are ignored)
|
||||
bool m_AlwaysVisible; ///< Should this line be rendered even under the SoD?
|
||||
LineCapType m_StartCapType; ///< LineCapType to be used at the start of the line
|
||||
LineCapType m_EndCapType; ///< LineCapType to be used at the end of the line
|
||||
|
||||
shared_ptr<CRenderData> m_RenderData; ///< Cached renderer data (shared_ptr so that copies/deletes are automatic)
|
||||
|
||||
/**
|
||||
* Converts a string line cap type into its corresponding LineCap enum value, and returns the resulting value.
|
||||
* If the input string is unrecognized, a warning is issued and a default value is returned.
|
||||
*/
|
||||
static LineCapType StrToLineCapType(const std::wstring& str);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -80,7 +80,7 @@ bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& ba
|
||||
|
||||
if (strncmp(name, "fixed:", 6) == 0)
|
||||
{
|
||||
program = CShaderProgramPtr(CShaderProgram::ConstructFFP(name+6));
|
||||
program = CShaderProgramPtr(CShaderProgram::ConstructFFP(name+6, baseDefines));
|
||||
if (!program)
|
||||
return false;
|
||||
program->Reload();
|
||||
|
@ -71,7 +71,7 @@ public:
|
||||
/**
|
||||
* Construct an instance of a pre-defined fixed-function pipeline setup.
|
||||
*/
|
||||
static CShaderProgram* ConstructFFP(const std::string& id);
|
||||
static CShaderProgram* ConstructFFP(const std::string& id, const std::map<CStr, CStr>& defines);
|
||||
|
||||
typedef const char* attrib_id_t;
|
||||
typedef const char* texture_id_t;
|
||||
|
@ -100,8 +100,10 @@ class CShaderProgramFFP_OverlayLine : public CShaderProgramFFP
|
||||
ID_objectColor
|
||||
};
|
||||
|
||||
bool m_IgnoreLos;
|
||||
|
||||
public:
|
||||
CShaderProgramFFP_OverlayLine() :
|
||||
CShaderProgramFFP_OverlayLine(const std::map<CStr, CStr>& defines) :
|
||||
CShaderProgramFFP(STREAM_POS | STREAM_UV0 | STREAM_UV1)
|
||||
{
|
||||
m_UniformIndexes["losTransform"] = ID_losTransform;
|
||||
@ -111,6 +113,13 @@ public:
|
||||
m_UniformIndexes["baseTex"] = 0;
|
||||
m_UniformIndexes["maskTex"] = 1;
|
||||
m_UniformIndexes["losTex"] = 2;
|
||||
|
||||
m_IgnoreLos = (defines.find(CStr("IGNORE_LOS")) != defines.end());
|
||||
}
|
||||
|
||||
bool IsIgnoreLos()
|
||||
{
|
||||
return m_IgnoreLos;
|
||||
}
|
||||
|
||||
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
|
||||
@ -145,7 +154,7 @@ public:
|
||||
// RGB channels:
|
||||
// Unit 0: Load base texture
|
||||
// Unit 1: Load mask texture; interpolate with objectColor & base
|
||||
// Unit 2: Load LOS texture; multiply
|
||||
// Unit 2: (Load LOS texture; multiply) if not #IGNORE_LOS, pass through otherwise
|
||||
// Alpha channel:
|
||||
// Unit 0: Load base texture
|
||||
// Unit 1: Multiply by objectColor
|
||||
@ -163,12 +172,15 @@ public:
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
pglActiveTextureARB(GL_TEXTURE1);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
||||
// Uniform() sets GL_TEXTURE_ENV_COLOR
|
||||
|
||||
// load mask texture; interpolate with objectColor and base; GL_INTERPOLATE takes 3 arguments:
|
||||
// a0 * a2 + a1 * (1 - a2)
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_CONSTANT);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
|
||||
@ -183,26 +195,41 @@ public:
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PREVIOUS);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
pglActiveTextureARB(GL_TEXTURE2);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
||||
|
||||
glEnable(GL_TEXTURE_GEN_S);
|
||||
glEnable(GL_TEXTURE_GEN_T);
|
||||
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
||||
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
||||
// Uniform() sets GL_OBJECT_PLANE values
|
||||
|
||||
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);
|
||||
bool ignoreLos = IsIgnoreLos();
|
||||
if (ignoreLos)
|
||||
{
|
||||
// RGB pass through
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
// multiply RGB with LoS texture alpha channel
|
||||
glEnable(GL_TEXTURE_GEN_S);
|
||||
glEnable(GL_TEXTURE_GEN_T);
|
||||
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
||||
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
||||
// Uniform() sets GL_OBJECT_PLANE values
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// alpha pass through
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
virtual void Unbind()
|
||||
@ -221,10 +248,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/*static*/ CShaderProgram* CShaderProgram::ConstructFFP(const std::string& id)
|
||||
/*static*/ CShaderProgram* CShaderProgram::ConstructFFP(const std::string& id, const std::map<CStr, CStr>& defines)
|
||||
{
|
||||
if (id == "overlayline")
|
||||
return new CShaderProgramFFP_OverlayLine();
|
||||
return new CShaderProgramFFP_OverlayLine(defines);
|
||||
|
||||
LOGERROR(L"CShaderProgram::ConstructFFP: Invalid id '%hs'", id.c_str());
|
||||
return NULL;
|
||||
|
@ -160,7 +160,7 @@ void CMiniMap::SetCameraPos()
|
||||
CVector3D target;
|
||||
GetMouseWorldCoordinates(target.X, target.Z);
|
||||
target.Y = terrain->GetExactGroundLevel(target.X, target.Z);
|
||||
g_Game->GetView()->MoveCameraTarget(target, true);
|
||||
g_Game->GetView()->MoveCameraTarget(target);
|
||||
}
|
||||
|
||||
float CMiniMap::GetAngle()
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "ps/CConsole.h"
|
||||
#include "ps/Errors.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/World.h"
|
||||
#include "ps/Hotkey.h"
|
||||
#include "ps/Overlay.h"
|
||||
#include "ps/ProfileViewer.h"
|
||||
@ -400,6 +401,23 @@ void CameraFollowFPS(void* UNUSED(cbdata), entity_id_t entityid)
|
||||
g_Game->GetView()->CameraFollow(entityid, true);
|
||||
}
|
||||
|
||||
/// Move camera to a 2D location
|
||||
void CameraMoveTo(void* UNUSED(cbdata), entity_pos_t x, entity_pos_t z)
|
||||
{
|
||||
// called from JS; must not fail
|
||||
if(!(g_Game && g_Game->GetWorld() && g_Game->GetView() && g_Game->GetWorld()->GetTerrain()))
|
||||
return;
|
||||
|
||||
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
|
||||
|
||||
CVector3D target;
|
||||
target.X = x.ToFloat();
|
||||
target.Z = z.ToFloat();
|
||||
target.Y = terrain->GetExactGroundLevel(target.X, target.Z);
|
||||
|
||||
g_Game->GetView()->MoveCameraTarget(target);
|
||||
}
|
||||
|
||||
entity_id_t GetFollowedEntity(void* UNUSED(cbdata))
|
||||
{
|
||||
if (g_Game && g_Game->GetView())
|
||||
@ -519,6 +537,7 @@ void QuickLoad(void* UNUSED(cbdata))
|
||||
{
|
||||
g_Game->GetTurnManager()->QuickLoad();
|
||||
}
|
||||
|
||||
void SetBoundingBoxDebugOverlay(void* UNUSED(cbdata), bool enabled)
|
||||
{
|
||||
ICmpSelectable::ms_EnableDebugOverlays = enabled;
|
||||
@ -574,6 +593,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
|
||||
scriptInterface.RegisterFunction<CScriptVal, &GetMapSettings>("GetMapSettings");
|
||||
scriptInterface.RegisterFunction<void, entity_id_t, &CameraFollow>("CameraFollow");
|
||||
scriptInterface.RegisterFunction<void, entity_id_t, &CameraFollowFPS>("CameraFollowFPS");
|
||||
scriptInterface.RegisterFunction<void, entity_pos_t, entity_pos_t, &CameraMoveTo>("CameraMoveTo");
|
||||
scriptInterface.RegisterFunction<entity_id_t, &GetFollowedEntity>("GetFollowedEntity");
|
||||
scriptInterface.RegisterFunction<bool, std::string, &HotkeyIsPressed_>("HotkeyIsPressed");
|
||||
scriptInterface.RegisterFunction<void, std::wstring, &DisplayErrorDialog>("DisplayErrorDialog");
|
||||
|
@ -127,6 +127,32 @@ public:
|
||||
return CVector2D(X / mag, Y / mag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version of this vector rotated counterclockwise by @p angle radians.
|
||||
*/
|
||||
CVector2D Rotated(float angle)
|
||||
{
|
||||
float c = cosf(angle);
|
||||
float s = sinf(angle);
|
||||
return CVector2D(
|
||||
c*X - s*Y,
|
||||
s*X + c*Y
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates this vector counterclockwise by @p angle radians.
|
||||
*/
|
||||
void Rotate(float angle)
|
||||
{
|
||||
float c = cosf(angle);
|
||||
float s = sinf(angle);
|
||||
float newX = c*X - s*Y;
|
||||
float newY = s*X + c*Y;
|
||||
X = newX;
|
||||
Y = newY;
|
||||
}
|
||||
|
||||
public:
|
||||
float X, Y;
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -19,9 +19,11 @@
|
||||
|
||||
#include "OverlayRenderer.h"
|
||||
|
||||
#include "maths/MathUtil.h"
|
||||
#include "maths/Quaternion.h"
|
||||
#include "maths/Vector2D.h"
|
||||
#include "graphics/LOSTexture.h"
|
||||
#include "graphics/Overlay.h"
|
||||
#include "graphics/ShaderManager.h"
|
||||
#include "graphics/Terrain.h"
|
||||
#include "graphics/TextureManager.h"
|
||||
#include "lib/ogl.h"
|
||||
@ -44,9 +46,8 @@ class CTexturedLineRData : public CRenderData
|
||||
{
|
||||
public:
|
||||
CTexturedLineRData(SOverlayTexturedLine* line) :
|
||||
m_Line(line), m_VB(NULL), m_VBIndices(NULL)
|
||||
{
|
||||
}
|
||||
m_Line(line), m_VB(NULL), m_VBIndices(NULL), m_Raise(.2f)
|
||||
{ }
|
||||
|
||||
~CTexturedLineRData()
|
||||
{
|
||||
@ -58,18 +59,40 @@ public:
|
||||
|
||||
struct SVertex
|
||||
{
|
||||
SVertex(CVector3D pos, short u, short v) : m_Position(pos) { m_UVs[0] = u; m_UVs[1] = v; }
|
||||
SVertex(CVector3D pos, float u, float v) : m_Position(pos) { m_UVs[0] = u; m_UVs[1] = v; }
|
||||
CVector3D m_Position;
|
||||
GLshort m_UVs[2];
|
||||
GLfloat m_UVs[2];
|
||||
float _padding[3]; // 5 floats up till now, so pad with another 3 floats to get a power of 2
|
||||
};
|
||||
cassert(sizeof(SVertex) == 16);
|
||||
cassert(sizeof(SVertex) == 32);
|
||||
|
||||
void Update();
|
||||
|
||||
SOverlayTexturedLine* m_Line;
|
||||
/**
|
||||
* Creates a line cap of the specified type @p endCapType at the end of the segment going in direction @p normal, and appends
|
||||
* the vertices to @p verticesOut in GL_TRIANGLES order.
|
||||
*
|
||||
* @param corner1 One of the two butt-end corner points of the line to which the cap should be attached.
|
||||
* @param corner2 One of the two butt-end corner points of the line to which the cap should be attached.
|
||||
* @param normal Normal vector indicating the direction of the segment to which the cap should be attached.
|
||||
* @param endCapType The type of end cap to produce.
|
||||
* @param verticesOut Output vector of vertices for passing to the renderer.
|
||||
* @param indicesOut Output vector of vertex indices for passing to the renderer.
|
||||
*/
|
||||
void CreateLineCap(const CVector3D& corner1, const CVector3D& corner2, const CVector3D& normal,
|
||||
SOverlayTexturedLine::LineCapType endCapType, std::vector<SVertex>& verticesOut, std::vector<u16>& indicesOut);
|
||||
|
||||
/// Small utility function; grabs the centroid of the positions of two vertices
|
||||
inline CVector3D Centroid(const SVertex& v1, const SVertex& v2)
|
||||
{
|
||||
return (v1.m_Position + v2.m_Position) * 0.5;
|
||||
}
|
||||
|
||||
SOverlayTexturedLine* m_Line;
|
||||
CVertexBuffer::VBChunk* m_VB;
|
||||
CVertexBuffer::VBChunk* m_VBIndices;
|
||||
|
||||
float m_Raise; // small vertical offset of line from terrain to prevent visual glitches
|
||||
};
|
||||
|
||||
OverlayRenderer::OverlayRenderer()
|
||||
@ -180,69 +203,38 @@ void OverlayRenderer::RenderOverlaysAfterWater()
|
||||
else
|
||||
shaderName = "fixed:overlayline";
|
||||
|
||||
CShaderManager& shaderManager = g_Renderer.GetShaderManager();
|
||||
CShaderProgramPtr shaderTexLine(shaderManager.LoadProgram(shaderName, std::map<CStr, CStr>()));
|
||||
|
||||
shaderTexLine->Bind();
|
||||
|
||||
int streamflags = shaderTexLine->GetStreamFlags();
|
||||
|
||||
if (streamflags & STREAM_POS)
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
if (streamflags & STREAM_UV0)
|
||||
{
|
||||
pglClientActiveTextureARB(GL_TEXTURE0);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
}
|
||||
|
||||
if (streamflags & STREAM_UV1)
|
||||
{
|
||||
pglClientActiveTextureARB(GL_TEXTURE1);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
}
|
||||
std::map<CStr, CStr> defAlwaysVisible;
|
||||
defAlwaysVisible.insert(std::make_pair(CStr("IGNORE_LOS"), CStr("1")));
|
||||
|
||||
CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
|
||||
shaderTexLine->BindTexture("losTex", los.GetTexture());
|
||||
shaderTexLine->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
|
||||
|
||||
for (size_t i = 0; i < m->texlines.size(); ++i)
|
||||
{
|
||||
SOverlayTexturedLine* line = m->texlines[i];
|
||||
if (!line->m_RenderData)
|
||||
continue;
|
||||
CShaderManager& shaderManager = g_Renderer.GetShaderManager();
|
||||
CShaderProgramPtr shaderTexLineNormal(shaderManager.LoadProgram(shaderName, std::map<CStr, CStr>()));
|
||||
CShaderProgramPtr shaderTexLineAlwaysVisible(shaderManager.LoadProgram(shaderName, defAlwaysVisible));
|
||||
|
||||
shaderTexLine->BindTexture("baseTex", line->m_TextureBase->GetHandle());
|
||||
shaderTexLine->BindTexture("maskTex", line->m_TextureMask->GetHandle());
|
||||
shaderTexLine->Uniform("objectColor", line->m_Color);
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
CTexturedLineRData* rdata = static_cast<CTexturedLineRData*>(line->m_RenderData.get());
|
||||
shaderTexLineNormal->Bind();
|
||||
shaderTexLineNormal->BindTexture("losTex", los.GetTexture());
|
||||
shaderTexLineNormal->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
|
||||
|
||||
GLsizei stride = sizeof(CTexturedLineRData::SVertex);
|
||||
CTexturedLineRData::SVertex* base = reinterpret_cast<CTexturedLineRData::SVertex*>(rdata->m_VB->m_Owner->Bind());
|
||||
// batch render only the non-always-visible overlay lines using the normal shader
|
||||
RenderTexturedOverlayLines(shaderTexLineNormal, false);
|
||||
|
||||
if (streamflags & STREAM_POS)
|
||||
glVertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
|
||||
shaderTexLineNormal->Unbind();
|
||||
|
||||
if (streamflags & STREAM_UV0)
|
||||
{
|
||||
pglClientActiveTextureARB(GL_TEXTURE0);
|
||||
glTexCoordPointer(2, GL_SHORT, stride, &base->m_UVs[0]);
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
if (streamflags & STREAM_UV1)
|
||||
{
|
||||
pglClientActiveTextureARB(GL_TEXTURE1);
|
||||
glTexCoordPointer(2, GL_SHORT, stride, &base->m_UVs[0]);
|
||||
}
|
||||
shaderTexLineAlwaysVisible->Bind();
|
||||
// TODO: losTex and losTransform are unused in the always visible shader, but I'm not sure if it's worthwhile messing
|
||||
// with it just to remove these calls
|
||||
shaderTexLineAlwaysVisible->BindTexture("losTex", los.GetTexture());
|
||||
shaderTexLineAlwaysVisible->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
|
||||
|
||||
u8* indexBase = rdata->m_VBIndices->m_Owner->Bind();
|
||||
glDrawElements(GL_QUAD_STRIP, rdata->m_VBIndices->m_Count, GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*rdata->m_VBIndices->m_Index);
|
||||
// batch render only the always-visible overlay lines using the LoS-ignored shader
|
||||
RenderTexturedOverlayLines(shaderTexLineAlwaysVisible, true);
|
||||
|
||||
g_Renderer.GetStats().m_OverlayTris += rdata->m_VBIndices->m_Count - 2;
|
||||
}
|
||||
|
||||
shaderTexLine->Unbind();
|
||||
shaderTexLineAlwaysVisible->Unbind();
|
||||
|
||||
// TODO: the shader should probably be responsible for unbinding its textures
|
||||
g_Renderer.BindTexture(1, 0);
|
||||
@ -260,6 +252,69 @@ void OverlayRenderer::RenderOverlaysAfterWater()
|
||||
}
|
||||
}
|
||||
|
||||
void OverlayRenderer::RenderTexturedOverlayLines(CShaderProgramPtr shaderTexLine, bool alwaysVisible)
|
||||
{
|
||||
int streamflags = shaderTexLine->GetStreamFlags();
|
||||
|
||||
if (streamflags & STREAM_POS)
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
if (streamflags & STREAM_UV0)
|
||||
{
|
||||
pglClientActiveTextureARB(GL_TEXTURE0);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
}
|
||||
|
||||
if (streamflags & STREAM_UV1)
|
||||
{
|
||||
pglClientActiveTextureARB(GL_TEXTURE1);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m->texlines.size(); ++i)
|
||||
{
|
||||
SOverlayTexturedLine* line = m->texlines[i];
|
||||
|
||||
// render only those lines matching the requested alwaysVisible status
|
||||
if (!line->m_RenderData || line->m_AlwaysVisible != alwaysVisible)
|
||||
continue;
|
||||
|
||||
shaderTexLine->BindTexture("baseTex", line->m_TextureBase->GetHandle());
|
||||
shaderTexLine->BindTexture("maskTex", line->m_TextureMask->GetHandle());
|
||||
shaderTexLine->Uniform("objectColor", line->m_Color);
|
||||
|
||||
CTexturedLineRData* rdata = static_cast<CTexturedLineRData*>(line->m_RenderData.get());
|
||||
if (!rdata->m_VB || !rdata->m_VBIndices)
|
||||
continue; // might have failed to allocate
|
||||
|
||||
// -- render main line quad strip ----------------------
|
||||
|
||||
GLsizei stride = sizeof(CTexturedLineRData::SVertex);
|
||||
CTexturedLineRData::SVertex* vertexBase = reinterpret_cast<CTexturedLineRData::SVertex*>(rdata->m_VB->m_Owner->Bind());
|
||||
|
||||
if (streamflags & STREAM_POS)
|
||||
glVertexPointer(3, GL_FLOAT, stride, &vertexBase->m_Position[0]);
|
||||
|
||||
if (streamflags & STREAM_UV0)
|
||||
{
|
||||
pglClientActiveTextureARB(GL_TEXTURE0);
|
||||
glTexCoordPointer(2, GL_FLOAT, stride, &vertexBase->m_UVs[0]);
|
||||
}
|
||||
|
||||
if (streamflags & STREAM_UV1)
|
||||
{
|
||||
pglClientActiveTextureARB(GL_TEXTURE1);
|
||||
glTexCoordPointer(2, GL_FLOAT, stride, &vertexBase->m_UVs[0]);
|
||||
}
|
||||
|
||||
u8* indexBase = rdata->m_VBIndices->m_Owner->Bind();
|
||||
glDrawElements(GL_TRIANGLES, rdata->m_VBIndices->m_Count, GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*rdata->m_VBIndices->m_Index);
|
||||
g_Renderer.GetStats().m_OverlayTris += rdata->m_VBIndices->m_Count/3;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OverlayRenderer::RenderForegroundOverlays(const CCamera& viewCamera)
|
||||
{
|
||||
PROFILE3_GPU("overlays (fg)");
|
||||
@ -293,7 +348,6 @@ void OverlayRenderer::RenderForegroundOverlays(const CCamera& viewCamera)
|
||||
};
|
||||
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(float)*3, &pos[0].X);
|
||||
|
||||
glDrawArrays(GL_QUADS, 0, (GLsizei)4);
|
||||
}
|
||||
|
||||
@ -312,34 +366,40 @@ void CTexturedLineRData::Update()
|
||||
g_VBMan.Release(m_VB);
|
||||
m_VB = NULL;
|
||||
}
|
||||
|
||||
if (m_VBIndices)
|
||||
{
|
||||
g_VBMan.Release(m_VBIndices);
|
||||
m_VBIndices = NULL;
|
||||
}
|
||||
|
||||
CTerrain* terrain = m_Line->m_Terrain;
|
||||
CmpPtr<ICmpWaterManager> cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
||||
|
||||
float v = 0.f;
|
||||
std::vector<SVertex> vertices;
|
||||
std::vector<u16> indices;
|
||||
|
||||
short v = 0;
|
||||
size_t n = m_Line->m_Coords.size() / 2; // number of line points
|
||||
bool closed = m_Line->m_Closed;
|
||||
|
||||
size_t n = m_Line->m_Coords.size() / 2;
|
||||
ENSURE(n >= 1);
|
||||
|
||||
CTerrain* terrain = m_Line->m_Terrain;
|
||||
|
||||
// TODO: this assumes paths are closed loops; probably should extend this to
|
||||
// handle non-closed paths too
|
||||
ENSURE(n >= 2); // minimum needed to avoid errors (also minimum value to make sense, can't draw a line between 1 point)
|
||||
|
||||
// In each iteration, p1 is the position of vertex i, p0 is i-1, p2 is i+1.
|
||||
// To avoid slightly expensive terrain computations we cycle these around and
|
||||
// recompute p2 at the end of each iteration.
|
||||
CVector3D p0 = CVector3D(m_Line->m_Coords[(n-1)*2], 0, m_Line->m_Coords[(n-1)*2+1]);
|
||||
CVector3D p1 = CVector3D(m_Line->m_Coords[0], 0, m_Line->m_Coords[1]);
|
||||
CVector3D p2 = CVector3D(m_Line->m_Coords[(1 % n)*2], 0, m_Line->m_Coords[(1 % n)*2+1]);
|
||||
|
||||
CVector3D p0;
|
||||
CVector3D p1(m_Line->m_Coords[0], 0, m_Line->m_Coords[1]);
|
||||
CVector3D p2(m_Line->m_Coords[(1 % n)*2], 0, m_Line->m_Coords[(1 % n)*2+1]);
|
||||
|
||||
if (closed)
|
||||
// grab the ending point so as to close the loop
|
||||
p0 = CVector3D(m_Line->m_Coords[(n-1)*2], 0, m_Line->m_Coords[(n-1)*2+1]);
|
||||
else
|
||||
// we don't want to loop around and use the direction towards the other end of the line, so create an artificial p0 that
|
||||
// extends the p2 -> p1 direction, and use that point instead
|
||||
p0 = p1 + (p1 - p2);
|
||||
|
||||
bool p1floating = false;
|
||||
bool p2floating = false;
|
||||
|
||||
@ -386,23 +446,66 @@ void CTexturedLineRData::Update()
|
||||
if (fabs(l) > 0.000001f) // avoid unlikely divide-by-zero
|
||||
b *= m_Line->m_Thickness / l;
|
||||
|
||||
// Raise off the terrain a little bit
|
||||
const float raised = 0.2f;
|
||||
// Push vertices and indices in GL_TRIANGLES order
|
||||
//
|
||||
// NOTE: in order for OpenGL to successfully render these, the winding order needs to be correct. Basically, it means
|
||||
// that every pair of triangles sharing a side must specify the vertices of that side in the opposite order from the
|
||||
// other triangle.
|
||||
// (see http://www.opengl.org/resources/code/samples/sig99/advanced99/notes/node16.html for an illustration)
|
||||
//
|
||||
// What the code below does is push the indices for a quad composed of two triangles in each iteration. The two triangles
|
||||
// of each quad are indexed using the winding orders (BR, BL, TR) and (TR, BL, TR) (where BR is bottom-right of this
|
||||
// iteration's quad, TR top-right etc).
|
||||
SVertex vertex1(p1 + b + norm*m_Raise, 0.f, v);
|
||||
SVertex vertex2(p1 - b + norm*m_Raise, 1.f, v);
|
||||
vertices.push_back(vertex1);
|
||||
vertices.push_back(vertex2);
|
||||
|
||||
vertices.push_back(SVertex(p1 + b + norm*raised, 0, v));
|
||||
indices.push_back(vertices.size() - 1);
|
||||
u16 index1 = vertices.size() - 2; // index of vertex1 in this iteration (TR of this quad)
|
||||
u16 index2 = vertices.size() - 1; // index of the vertex2 in this iteration (TL of this quad)
|
||||
|
||||
vertices.push_back(SVertex(p1 - b + norm*raised, 1, v));
|
||||
indices.push_back(vertices.size() - 1);
|
||||
if (i == 0)
|
||||
{
|
||||
// initial two vertices to continue building triangles from (n must be >= 2 for this to work)
|
||||
indices.push_back(index1);
|
||||
indices.push_back(index2);
|
||||
}
|
||||
else
|
||||
{
|
||||
u16 index1Prev = vertices.size() - 4; // index of the vertex1 in the previous iteration (BR of this quad)
|
||||
u16 index2Prev = vertices.size() - 3; // index of the vertex2 in the previous iteration (BL of this quad)
|
||||
ENSURE(index1Prev >= 0 && index1Prev < vertices.size());
|
||||
ENSURE(index2Prev >= 0 && index2Prev < vertices.size());
|
||||
// Add two corner points from last iteration and join with one of our own corners to create triangle 1
|
||||
// (don't need to do this if i == 1 because i == 0 are the first two ones, they don't need to be copied)
|
||||
if (i > 1)
|
||||
{
|
||||
indices.push_back(index1Prev);
|
||||
indices.push_back(index2Prev);
|
||||
}
|
||||
indices.push_back(index1); // complete triangle 1
|
||||
|
||||
// Alternate V coordinate for debugging
|
||||
// create triangle 2, specifying the adjacent side's vertices in the opposite order from triangle 1
|
||||
indices.push_back(index1);
|
||||
indices.push_back(index2Prev);
|
||||
indices.push_back(index2);
|
||||
}
|
||||
|
||||
// alternate V coordinate for debugging
|
||||
v = 1 - v;
|
||||
|
||||
// Cycle the p's and compute the new p2
|
||||
// cycle the p's and compute the new p2
|
||||
p0 = p1;
|
||||
p1 = p2;
|
||||
p1floating = p2floating;
|
||||
p2 = CVector3D(m_Line->m_Coords[((i+2) % n)*2], 0, m_Line->m_Coords[((i+2) % n)*2+1]);
|
||||
|
||||
// if in closed mode, wrap around the coordinate array for p2 -- otherwise, extend linearly
|
||||
if (!closed && i == n-2)
|
||||
// next iteration is the last point of the line, so create an artificial p2 that extends the p0 -> p1 direction
|
||||
p2 = p1 + (p1 - p0);
|
||||
else
|
||||
p2 = CVector3D(m_Line->m_Coords[((i+2) % n)*2], 0, m_Line->m_Coords[((i+2) % n)*2+1]);
|
||||
|
||||
p2.Y = terrain->GetExactGroundLevel(p2.X, p2.Z);
|
||||
if (p2.Y < w)
|
||||
{
|
||||
@ -413,17 +516,196 @@ void CTexturedLineRData::Update()
|
||||
p2floating = false;
|
||||
}
|
||||
|
||||
// Close the path
|
||||
indices.push_back(0);
|
||||
indices.push_back(1);
|
||||
if (closed)
|
||||
{
|
||||
// close the path
|
||||
indices.push_back(vertices.size()-2);
|
||||
indices.push_back(vertices.size()-1);
|
||||
indices.push_back(0);
|
||||
|
||||
indices.push_back(0);
|
||||
indices.push_back(vertices.size()-1);
|
||||
indices.push_back(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create start and end caps. On either end, this is done by taking the centroid between the last and second-to-last pair of
|
||||
// vertices that was generated along the path (i.e. the vertex1's and vertex2's from above), taking a directional vector
|
||||
// between them, and drawing the line cap in the plane given by the two butt-end corner points plus said vector.
|
||||
std::vector<u16> capIndices;
|
||||
std::vector<SVertex> capVertices;
|
||||
|
||||
// create end cap
|
||||
CreateLineCap(
|
||||
// the order of these vertices is important here, swapping them produces caps at the wrong side
|
||||
vertices[vertices.size()-2].m_Position, // top-right vertex of last quad
|
||||
vertices[vertices.size()-1].m_Position, // top-left vertex of last quad
|
||||
// directional vector between centroids of last vertex pair and second-to-last vertex pair
|
||||
(Centroid(vertices[vertices.size()-2], vertices[vertices.size()-1]) - Centroid(vertices[vertices.size()-4], vertices[vertices.size()-3])).Normalized(),
|
||||
m_Line->m_EndCapType,
|
||||
capVertices,
|
||||
capIndices
|
||||
);
|
||||
|
||||
for (unsigned i = 0; i < capIndices.size(); i++)
|
||||
capIndices[i] += vertices.size();
|
||||
vertices.insert(vertices.end(), capVertices.begin(), capVertices.end());
|
||||
indices.insert(indices.end(), capIndices.begin(), capIndices.end());
|
||||
|
||||
capIndices.clear();
|
||||
capVertices.clear();
|
||||
|
||||
// create start cap
|
||||
CreateLineCap(
|
||||
// the order of these vertices is important here, swapping them produces caps at the wrong side
|
||||
vertices[1].m_Position,
|
||||
vertices[0].m_Position,
|
||||
// directional vector between centroids of first vertex pair and second vertex pair
|
||||
(Centroid(vertices[1], vertices[0]) - Centroid(vertices[3], vertices[2])).Normalized(),
|
||||
m_Line->m_StartCapType,
|
||||
capVertices,
|
||||
capIndices
|
||||
);
|
||||
|
||||
for (unsigned i = 0; i < capIndices.size(); i++)
|
||||
capIndices[i] += vertices.size();
|
||||
vertices.insert(vertices.end(), capVertices.begin(), capVertices.end());
|
||||
indices.insert(indices.end(), capIndices.begin(), capIndices.end());
|
||||
}
|
||||
|
||||
ENSURE(indices.size() % 3 == 0); // GL_TRIANGLES indices, so must be multiple of 3
|
||||
|
||||
m_VB = g_VBMan.Allocate(sizeof(SVertex), vertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
|
||||
m_VB->m_Owner->UpdateChunkVertices(m_VB, &vertices[0]);
|
||||
if (m_VB)
|
||||
{
|
||||
// allocation might fail (e.g. due to too many vertices)
|
||||
m_VB->m_Owner->UpdateChunkVertices(m_VB, &vertices[0]); // copy data into VBO
|
||||
|
||||
// Update the indices to include the base offset of the vertex data
|
||||
for (size_t k = 0; k < indices.size(); ++k)
|
||||
indices[k] += m_VB->m_Index;
|
||||
for (size_t k = 0; k < indices.size(); ++k)
|
||||
indices[k] += m_VB->m_Index;
|
||||
|
||||
m_VBIndices = g_VBMan.Allocate(sizeof(u16), indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
|
||||
if (m_VBIndices)
|
||||
m_VBIndices->m_Owner->UpdateChunkVertices(m_VBIndices, &indices[0]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CTexturedLineRData::CreateLineCap(const CVector3D& corner1, const CVector3D& corner2, const CVector3D& lineDirectionNormal,
|
||||
SOverlayTexturedLine::LineCapType endCapType, std::vector<SVertex>& verticesOut,
|
||||
std::vector<u16>& indicesOut)
|
||||
{
|
||||
if (endCapType == SOverlayTexturedLine::LINECAP_FLAT)
|
||||
return; // no action needed, this is the default
|
||||
|
||||
// When not in closed mode, we've created artificial points for the start- and endpoints that extend the line in the
|
||||
// direction of the first and the last segment, respectively. Thus, we know both the start and endpoints have perpendicular
|
||||
// butt endings, i.e. the end corner vertices on either side of the line extend perpendicularly from the segment direction.
|
||||
// That is to say, when viewed from the top, we will have something like
|
||||
// .
|
||||
// this: and not like this: /|
|
||||
// ____. / |
|
||||
// | / .
|
||||
// | /
|
||||
// ____. /
|
||||
//
|
||||
|
||||
int roundCapPoints = 8; // amount of points to sample along the semicircle for rounded caps (including corner points)
|
||||
float radius = m_Line->m_Thickness;
|
||||
|
||||
CVector3D centerPoint = (corner1 + corner2) * 0.5f;
|
||||
SVertex centerVertex(centerPoint, 0.5f, 0.5f);
|
||||
u16 indexOffset = verticesOut.size(); // index offset in verticesOut from where we start adding our vertices
|
||||
|
||||
switch (endCapType)
|
||||
{
|
||||
case SOverlayTexturedLine::LINECAP_SHARP:
|
||||
{
|
||||
roundCapPoints = 3; // creates only one point directly ahead
|
||||
radius *= 1.5f; // make it a bit sharper (note that we don't use the radius for the butt-end corner points so it should be ok)
|
||||
centerVertex.m_UVs[0] = 0.480f; // slight visual correction to make the texture match up better at the corner points
|
||||
}
|
||||
// fall-through
|
||||
case SOverlayTexturedLine::LINECAP_ROUND:
|
||||
{
|
||||
// Draw a rounded line cap in the 3D plane of the line specified by the two corner points and the normal vector of the
|
||||
// line's direction. The terrain normal at the centroid between the two corner points is perpendicular to this plane.
|
||||
// The way this works is by taking a vector from the corner points' centroid to one of the corner points (which is then
|
||||
// of radius length), and rotate it around the terrain normal vector in that centroid. This will rotate the vector in
|
||||
// the line's plane, producing the desired rounded cap.
|
||||
|
||||
// To please OpenGL's winding order, this angle needs to be negated depending on whether we start rotating from
|
||||
// the (center -> corner1) or (center -> corner2) vector. For the (center -> corner2) vector, we apparently need to use
|
||||
// the negated angle.
|
||||
float stepAngle = -(float)(M_PI/(roundCapPoints-1));
|
||||
|
||||
// Push the vertices in triangle fan order (easy to generate GL_TRIANGLES indices for afterwards)
|
||||
// Note that we're manually adding the corner vertices instead of having them be generated by the rotating vector.
|
||||
// This is because we want to support an overly large radius to make the sharp line ending look sharper.
|
||||
verticesOut.push_back(centerVertex);
|
||||
verticesOut.push_back(SVertex(corner2, 0.f, 0.f));
|
||||
|
||||
// Get the base vector that we will incrementally rotate in the cap plane to produce the radial sample points.
|
||||
// Normally corner2 - centerPoint would suffice for this since it is of radius length, but we want to support custom
|
||||
// radii to support tuning the 'sharpness' of sharp end caps (see above)
|
||||
CVector3D rotationBaseVector = (corner2 - centerPoint).Normalized() * radius;
|
||||
// Calculate the normal vector of the plane in which we're going to be drawing the line cap. This is the vector that
|
||||
// is perpendicular to both baseVector and the 'lineDirectionNormal' vector indicating the direction of the line.
|
||||
// Note that we shouldn't use terrain->CalcExactNormal() here because if the line is being rendered on top of water,
|
||||
// then CalcExactNormal will return the normal vector of the terrain that's underwater (which can be quite funky).
|
||||
CVector3D capPlaneNormal = lineDirectionNormal.Cross(rotationBaseVector).Normalized();
|
||||
|
||||
for (int i = 1; i < roundCapPoints - 1; ++i)
|
||||
{
|
||||
// Rotate the centerPoint -> corner vector by i*stepAngle radians around the cap plane normal at the center point.
|
||||
CQuaternion quatRotation;
|
||||
quatRotation.FromAxisAngle(capPlaneNormal, i * stepAngle);
|
||||
CVector3D worldPos3D = centerPoint + quatRotation.Rotate(rotationBaseVector);
|
||||
|
||||
// Let v range from 0 to 1 as we move along the semi-circle, keep u fixed at 0 (i.e. curve the left vertical edge
|
||||
// of the texture around the edge of the semicircle)
|
||||
float u = 0.f;
|
||||
float v = clamp((i/(float)(roundCapPoints-1)), 0.f, 1.f); // pos, u, v
|
||||
verticesOut.push_back(SVertex(worldPos3D, u, v));
|
||||
}
|
||||
|
||||
// connect back to the other butt-end corner point to complete the semicircle
|
||||
verticesOut.push_back(SVertex(corner1, 0.f, 1.f));
|
||||
|
||||
// now push indices in GL_TRIANGLES order; vertices[indexOffset] is the center vertex, vertices[indexOffset + 1] is the
|
||||
// first corner point, then a bunch of radial samples, and then at the end we have the other corner point again. So:
|
||||
for (int i=1; i < roundCapPoints; ++i)
|
||||
{
|
||||
indicesOut.push_back(indexOffset); // center vertex
|
||||
indicesOut.push_back(indexOffset + i);
|
||||
indicesOut.push_back(indexOffset + i + 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SOverlayTexturedLine::LINECAP_SQUARE:
|
||||
{
|
||||
// Extend the (corner1 -> corner2) vector along the direction normal and draw a square line ending consisting of
|
||||
// three triangles (sort of like a triangle fan)
|
||||
// NOTE: The order in which the vertices are pushed out determines the visibility, as they
|
||||
// are rendered only one-sided; the wrong order of vertices will make the cap visible only from the bottom.
|
||||
verticesOut.push_back(centerVertex);
|
||||
verticesOut.push_back(SVertex(corner2, 0.f, 0.f));
|
||||
verticesOut.push_back(SVertex(corner2 + (lineDirectionNormal * (m_Line->m_Thickness)), 0.f, 0.33333f)); // extend butt corner point 2 along the normal vector
|
||||
verticesOut.push_back(SVertex(corner1 + (lineDirectionNormal * (m_Line->m_Thickness)), 0.f, 0.66666f)); // extend butt corner point 1 along the normal vector
|
||||
verticesOut.push_back(SVertex(corner1, 0.f, 1.0f)); // push butt corner point 1
|
||||
|
||||
for (int i=1; i < 4; ++i)
|
||||
{
|
||||
indicesOut.push_back(indexOffset); // center point
|
||||
indicesOut.push_back(indexOffset + i);
|
||||
indicesOut.push_back(indexOffset + i + 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_VBIndices = g_VBMan.Allocate(sizeof(u16), indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
|
||||
m_VBIndices->m_Owner->UpdateChunkVertices(m_VBIndices, &indices[0]);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -18,6 +18,8 @@
|
||||
#ifndef INCLUDED_OVERLAYRENDERER
|
||||
#define INCLUDED_OVERLAYRENDERER
|
||||
|
||||
#include "graphics/ShaderManager.h"
|
||||
|
||||
struct SOverlayLine;
|
||||
struct SOverlayTexturedLine;
|
||||
struct SOverlaySprite;
|
||||
@ -83,6 +85,15 @@ public:
|
||||
*/
|
||||
void RenderForegroundOverlays(const CCamera& viewCamera);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Helper method; renders those overlay lines currently registered in the internals (i.e. in m->texlines) for which the
|
||||
* always visible flag equals @alwaysVisible. Used for batch rendering the overlay lines by their alwaysVisible status,
|
||||
* because this requires a separate shader to be used.
|
||||
*/
|
||||
void RenderTexturedOverlayLines(CShaderProgramPtr shader, bool alwaysVisible);
|
||||
|
||||
private:
|
||||
OverlayRendererInternals* m;
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 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
|
||||
@ -134,18 +134,6 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class RenderModifierRenderer: Interface to a model renderer that can render
|
||||
* its models via a RenderModifier that sets up fragment stages.
|
||||
*/
|
||||
class RenderModifierRenderer : public ModelRenderer
|
||||
{
|
||||
public:
|
||||
RenderModifierRenderer() { }
|
||||
virtual ~RenderModifierRenderer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class PlainRenderModifier: RenderModifier that simply uses opaque textures
|
||||
* modulated by primary color. It is used for normal, no-frills models.
|
||||
|
@ -503,11 +503,11 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const sh
|
||||
m_nativeScope = JS_DefineObject(m_cx, m_glob, nativeScopeName, NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY
|
||||
| JSPROP_PERMANENT);
|
||||
|
||||
JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
JS_DefineFunction(m_cx, m_glob, "log", ::logmsg, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
JS_DefineFunction(m_cx, m_glob, "warn", ::warn, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
JS_DefineFunction(m_cx, m_glob, "error", ::error, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
JS_DefineFunction(m_cx, m_glob, "deepcopy", ::deepcopy, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
JS_DefineFunction(m_cx, m_glob, "log", ::logmsg, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
JS_DefineFunction(m_cx, m_glob, "warn", ::warn, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
JS_DefineFunction(m_cx, m_glob, "error", ::error, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
JS_DefineFunction(m_cx, m_glob, "deepcopy", ::deepcopy, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
|
||||
Register("ProfileStart", ::ProfileStart, 1);
|
||||
Register("ProfileStop", ::ProfileStop, 0);
|
||||
|
@ -118,6 +118,9 @@ COMPONENT(Position) // must be before VisualActor
|
||||
INTERFACE(ProjectileManager)
|
||||
COMPONENT(ProjectileManager)
|
||||
|
||||
INTERFACE(RallyPointRenderer)
|
||||
COMPONENT(RallyPointRenderer)
|
||||
|
||||
INTERFACE(RangeManager)
|
||||
COMPONENT(RangeManager)
|
||||
|
||||
|
1080
source/simulation2/components/CCmpRallyPointRenderer.cpp
Normal file
1080
source/simulation2/components/CCmpRallyPointRenderer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -103,6 +103,9 @@ public:
|
||||
|
||||
TerritoryOverlay* m_DebugOverlay;
|
||||
|
||||
bool m_EnableLineDebugOverlays; ///< Enable node debugging overlays for boundary lines?
|
||||
std::vector<SOverlayLine> m_DebugBoundaryLineNodes;
|
||||
|
||||
virtual void Init(const CParamNode& UNUSED(paramNode))
|
||||
{
|
||||
m_Territories = NULL;
|
||||
@ -110,7 +113,7 @@ public:
|
||||
// m_DebugOverlay = new TerritoryOverlay(*this);
|
||||
m_BoundaryLinesDirty = true;
|
||||
m_TriggerEvent = true;
|
||||
|
||||
m_EnableLineDebugOverlays = false;
|
||||
m_DirtyID = 1;
|
||||
|
||||
m_AnimTime = 0.0;
|
||||
@ -742,6 +745,7 @@ void CCmpTerritoryManager::UpdateBoundaryLines()
|
||||
PROFILE("update boundary lines");
|
||||
|
||||
m_BoundaryLines.clear();
|
||||
m_DebugBoundaryLineNodes.clear();
|
||||
|
||||
if (!CRenderer::IsInitialised())
|
||||
return;
|
||||
@ -780,23 +784,40 @@ void CCmpTerritoryManager::UpdateBoundaryLines()
|
||||
m_BoundaryLines.push_back(SBoundaryLine());
|
||||
m_BoundaryLines.back().connected = boundaries[i].connected;
|
||||
m_BoundaryLines.back().color = color;
|
||||
|
||||
m_BoundaryLines.back().overlay.m_Terrain = terrain;
|
||||
m_BoundaryLines.back().overlay.m_TextureBase = textureBase;
|
||||
m_BoundaryLines.back().overlay.m_TextureMask = textureMask;
|
||||
m_BoundaryLines.back().overlay.m_Color = color;
|
||||
m_BoundaryLines.back().overlay.m_Thickness = m_BorderThickness;
|
||||
m_BoundaryLines.back().overlay.m_Closed = true;
|
||||
|
||||
SimRender::SmoothPointsAverage(boundaries[i].points, true);
|
||||
SimRender::SmoothPointsAverage(boundaries[i].points, m_BoundaryLines.back().overlay.m_Closed);
|
||||
|
||||
SimRender::InterpolatePointsRNS(boundaries[i].points, true, m_BorderSeparation);
|
||||
SimRender::InterpolatePointsRNS(boundaries[i].points, m_BoundaryLines.back().overlay.m_Closed, m_BorderSeparation);
|
||||
|
||||
std::vector<float>& points = m_BoundaryLines.back().overlay.m_Coords;
|
||||
for (size_t j = 0; j < boundaries[i].points.size(); ++j)
|
||||
{
|
||||
points.push_back(boundaries[i].points[j].X);
|
||||
points.push_back(boundaries[i].points[j].Y);
|
||||
|
||||
if (m_EnableLineDebugOverlays)
|
||||
{
|
||||
const int numHighlightNodes = 7; // highlight the X last nodes on either end to see where they meet (if closed)
|
||||
SOverlayLine overlayNode;
|
||||
if (j > boundaries[i].points.size() - 1 - numHighlightNodes)
|
||||
overlayNode.m_Color = CColor(1.f, 0.f, 0.f, 1.f);
|
||||
else if (j < numHighlightNodes)
|
||||
overlayNode.m_Color = CColor(0.f, 1.f, 0.f, 1.f);
|
||||
else
|
||||
overlayNode.m_Color = CColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
overlayNode.m_Thickness = 1;
|
||||
SimRender::ConstructCircleOnGround(GetSimContext(), boundaries[i].points[j].X, boundaries[i].points[j].Y, 0.1f, overlayNode, true);
|
||||
m_DebugBoundaryLineNodes.push_back(overlayNode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -825,6 +846,10 @@ void CCmpTerritoryManager::RenderSubmit(SceneCollector& collector)
|
||||
{
|
||||
for (size_t i = 0; i < m_BoundaryLines.size(); ++i)
|
||||
collector.Submit(&m_BoundaryLines[i].overlay);
|
||||
|
||||
for (size_t i = 0; i < m_DebugBoundaryLineNodes.size(); ++i)
|
||||
collector.Submit(&m_DebugBoundaryLineNodes[i]);
|
||||
|
||||
}
|
||||
|
||||
player_id_t CCmpTerritoryManager::GetOwner(entity_pos_t x, entity_pos_t z)
|
||||
|
@ -361,6 +361,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetUnitEntitySelection(const CStr& selection)
|
||||
{
|
||||
if (m_Unit)
|
||||
{
|
||||
m_Unit->SetEntitySelection(selection);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SelectMovementAnimation(fixed runThreshold)
|
||||
{
|
||||
m_AnimRunThreshold = runThreshold;
|
||||
|
@ -53,6 +53,11 @@ public:
|
||||
return m_Script.Call<CColor>("GetColour");
|
||||
}
|
||||
|
||||
virtual std::wstring GetCiv()
|
||||
{
|
||||
return m_Script.Call<std::wstring>("GetCiv");
|
||||
}
|
||||
|
||||
virtual CFixedVector3D GetStartingCameraPos()
|
||||
{
|
||||
return m_Script.Call<CFixedVector3D>("GetStartingCameraPos");
|
||||
|
@ -36,6 +36,7 @@ public:
|
||||
virtual void SetColour(u8 r, u8 g, u8 b) = 0;
|
||||
|
||||
virtual CColor GetColour() = 0;
|
||||
virtual std::wstring GetCiv() = 0;
|
||||
virtual CFixedVector3D GetStartingCameraPos() = 0;
|
||||
virtual CFixedVector3D GetStartingCameraRot() = 0;
|
||||
|
||||
|
28
source/simulation2/components/ICmpRallyPointRenderer.cpp
Normal file
28
source/simulation2/components/ICmpRallyPointRenderer.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "ICmpRallyPointRenderer.h"
|
||||
#include "simulation2/system/InterfaceScripted.h"
|
||||
|
||||
class CFixedVector2D;
|
||||
|
||||
BEGIN_INTERFACE_WRAPPER(RallyPointRenderer)
|
||||
DEFINE_INTERFACE_METHOD_1("SetDisplayed", void, ICmpRallyPointRenderer, SetDisplayed, bool)
|
||||
DEFINE_INTERFACE_METHOD_1("SetPosition", void, ICmpRallyPointRenderer, SetPosition, CFixedVector2D)
|
||||
END_INTERFACE_WRAPPER(RallyPointRenderer)
|
42
source/simulation2/components/ICmpRallyPointRenderer.h
Normal file
42
source/simulation2/components/ICmpRallyPointRenderer.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_ICMPRALLYPOINT
|
||||
#define INCLUDED_ICMPRALLYPOINT
|
||||
|
||||
#include "maths/FixedVector2D.h"
|
||||
#include "simulation2/helpers/Position.h"
|
||||
#include "simulation2/system/Interface.h"
|
||||
|
||||
/**
|
||||
* Rally Point.
|
||||
* Holds the position of a unit's rally point, and renders it to screen.
|
||||
*/
|
||||
class ICmpRallyPointRenderer : public IComponent
|
||||
{
|
||||
public:
|
||||
|
||||
/// Sets whether the rally point marker and line should be displayed.
|
||||
virtual void SetDisplayed(bool displayed) = 0;
|
||||
|
||||
/// Sets the position at which the rally point marker should be displayed.
|
||||
virtual void SetPosition(CFixedVector2D position) = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(RallyPointRenderer)
|
||||
};
|
||||
|
||||
#endif // INCLUDED_ICMPRALLYPOINT
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "simulation2/system/Interface.h"
|
||||
|
||||
#include "ps/CStr.h"
|
||||
#include "maths/BoundingBoxOriented.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
#include "maths/Fixed.h"
|
||||
@ -90,6 +91,11 @@ public:
|
||||
*/
|
||||
virtual void SelectAnimation(std::string name, bool once, fixed speed, std::wstring soundgroup) = 0;
|
||||
|
||||
/**
|
||||
* Sets the specified entity selection on the underlying unit.
|
||||
*/
|
||||
virtual void SetUnitEntitySelection(const CStr& selection) = 0;
|
||||
|
||||
/**
|
||||
* Start playing the walk/run animations, scaled to the unit's movement speed.
|
||||
* @param runThreshold movement speed at which to switch to the run animation
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -30,12 +30,38 @@ class CFixedVector2D;
|
||||
namespace Geometry
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns true if @p point is inside the square with rotated X axis unit vector @p u and rotated Z axis unit vector @p v,
|
||||
* and half dimensions specified by @p halfSizes. Currently assumes the @p u and @p v vectors are perpendicular. Can also
|
||||
* be used for rectangles.
|
||||
*
|
||||
* @param point point vector of the point that is to be tested relative to the origin (center) of the shape.
|
||||
* @param u rotated X axis unit vector relative to the absolute XZ plane. Indicates the orientation of the rectangle. If not rotated,
|
||||
* this value is the absolute X axis unit vector (1,0). If rotated by angle theta, this should be (cos theta, -sin theta), as
|
||||
* the absolute Z axis points down in the unit circle.
|
||||
* @param v rotated Z axis unit vector relative to the absolute XZ plane. Indicates the orientation of the rectangle. If not rotated,
|
||||
* this value is the absolute Z axis unit vector (0,1). If rotated by angle theta, this should be (sin theta, cos theta), as
|
||||
* the absolute Z axis points down in the unit circle.
|
||||
* @param halfSizes Holds half the dimensions of the shape along the u and v vectors, respectively.
|
||||
*/
|
||||
bool PointIsInSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize);
|
||||
|
||||
CFixedVector2D GetHalfBoundingBox(CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize);
|
||||
|
||||
fixed DistanceToSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize);
|
||||
|
||||
/**
|
||||
* Returns the point that is closest to @p point on the edge of the square specified by orientation unit vectors @p u and @p v and half
|
||||
* dimensions @p halfSize, relative to the center of the square. Currently assumes the @p u and @p v vectors are perpendicular.
|
||||
* Can also be used for rectangles.
|
||||
*
|
||||
* @param point point vector of the point we want to get the nearest edge point for, relative to the origin (center) of the shape.
|
||||
* @param u rotated X axis unit vector, relative to the absolute XZ plane. Indicates the orientation of the shape. If not rotated,
|
||||
* this value is the absolute X axis unit vector (1,0). If rotated by angle theta, this should be (cos theta, -sin theta).
|
||||
* @param v rotated Z axis unit vector, relative to the absolute XZ plane. Indicates the orientation of the shape. If not rotated,
|
||||
* this value is the absolute Z axis unit vector (0,1). If rotated by angle theta, this should be (sin theta, cos theta).
|
||||
* @param halfSizes Holds half the dimensions of the shape along the u and v vectors, respectively.
|
||||
*/
|
||||
CFixedVector2D NearestPointOnSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize);
|
||||
|
||||
bool TestRaySquare(CFixedVector2D a, CFixedVector2D b, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -343,9 +343,10 @@ static CVector2D EvaluateSpline(float t, CVector2D a0, CVector2D a1, CVector2D a
|
||||
return p + CVector2D(dp.Y*-offset, dp.X*offset);
|
||||
}
|
||||
|
||||
void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset)
|
||||
void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset, int segmentSamples)
|
||||
{
|
||||
PROFILE("InterpolatePointsRNS");
|
||||
ENSURE(segmentSamples > 0);
|
||||
|
||||
std::vector<CVector2D> newPoints;
|
||||
|
||||
@ -357,20 +358,54 @@ void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed
|
||||
// curve with fewer points
|
||||
|
||||
size_t n = points.size();
|
||||
if (n < 1)
|
||||
return; // can't do anything unless we have two points
|
||||
|
||||
size_t imax = closed ? n : n-1; // TODO: we probably need to do a bit more to handle non-closed paths
|
||||
if (closed)
|
||||
{
|
||||
if (n < 1)
|
||||
return; // we need at least a single point to not crash
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n < 2)
|
||||
return; // in non-closed mode, we need at least n=2 to not crash
|
||||
}
|
||||
|
||||
newPoints.reserve(imax*4);
|
||||
size_t imax = closed ? n : n-1;
|
||||
newPoints.reserve(imax*segmentSamples);
|
||||
|
||||
// these are primarily used inside the loop, but for open paths we need them outside the loop once to compute the last point
|
||||
CVector2D a0;
|
||||
CVector2D a1;
|
||||
CVector2D a2;
|
||||
CVector2D a3;
|
||||
|
||||
for (size_t i = 0; i < imax; ++i)
|
||||
{
|
||||
// Get the relevant points for this spline segment
|
||||
CVector2D p0 = points[(i-1+n)%n];
|
||||
|
||||
// Get the relevant points for this spline segment; each step interpolates the segment between p1 and p2; p0 and p3 are the points
|
||||
// before p1 and after p2, respectively; they're needed to compute tangents and whatnot.
|
||||
CVector2D p0; // normally points[(i-1+n)%n], but it's a bit more complicated due to open/closed paths -- see below
|
||||
CVector2D p1 = points[i];
|
||||
CVector2D p2 = points[(i+1)%n];
|
||||
CVector2D p3 = points[(i+2)%n];
|
||||
CVector2D p3; // normally points[(i+2)%n], but it's a bit more complicated due to open/closed paths -- see below
|
||||
|
||||
if (!closed && (i == 0))
|
||||
// p0's point index is out of bounds, and we can't wrap around because we're in non-closed mode -- create an artificial point
|
||||
// that extends p1 -> p0 (i.e. the first segment's direction)
|
||||
p0 = points[0] + (points[0] - points[1]);
|
||||
else
|
||||
// standard wrap-around case
|
||||
p0 = points[(i-1+n)%n]; // careful; don't use (i-1)%n here, as the result is machine-dependent for negative operands (e.g. if i==0, the result could be either -1 or n-1)
|
||||
|
||||
|
||||
if (!closed && (i == n-2))
|
||||
// p3's point index is out of bounds; create an artificial point that extends p_(n-2) -> p_(n-1) (i.e. the last segment's direction)
|
||||
// (note that p2's index should not be out of bounds, because in non-closed mode imax is reduced by 1)
|
||||
p3 = points[n-1] + (points[n-1] - points[n-2]);
|
||||
else
|
||||
// standard wrap-around case
|
||||
p3 = points[(i+2)%n];
|
||||
|
||||
|
||||
// Do the RNS computation (based on GPG4 "Nonuniform Splines")
|
||||
float l1 = (p2 - p1).Length(); // length of spline segment (i)..(i+1)
|
||||
@ -381,17 +416,108 @@ void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed
|
||||
CVector2D v2 = (s1 + s2).Normalized() * l1; // spline velocity at i+1
|
||||
|
||||
// Compute standard cubic spline parameters
|
||||
CVector2D a0 = p1*2 + p2*-2 + v1 + v2;
|
||||
CVector2D a1 = p1*-3 + p2*3 + v1*-2 + v2*-1;
|
||||
CVector2D a2 = v1;
|
||||
CVector2D a3 = p1;
|
||||
a0 = p1*2 + p2*-2 + v1 + v2;
|
||||
a1 = p1*-3 + p2*3 + v1*-2 + v2*-1;
|
||||
a2 = v1;
|
||||
a3 = p1;
|
||||
|
||||
// Interpolate at regular points across the interval
|
||||
for (int sample = 0; sample < segmentSamples; sample++)
|
||||
newPoints.push_back(EvaluateSpline(sample/((float) segmentSamples), a0, a1, a2, a3, offset));
|
||||
|
||||
// Interpolate at various points
|
||||
newPoints.push_back(EvaluateSpline(0.f, a0, a1, a2, a3, offset));
|
||||
newPoints.push_back(EvaluateSpline(1.f/4.f, a0, a1, a2, a3, offset));
|
||||
newPoints.push_back(EvaluateSpline(2.f/4.f, a0, a1, a2, a3, offset));
|
||||
newPoints.push_back(EvaluateSpline(3.f/4.f, a0, a1, a2, a3, offset));
|
||||
}
|
||||
|
||||
if (!closed)
|
||||
// if the path is open, we should take care to include the last control point
|
||||
// NOTE: we can't just do push_back(points[n-1]) here because that ignores the offset
|
||||
newPoints.push_back(EvaluateSpline(1.f, a0, a1, a2, a3, offset));
|
||||
|
||||
points.swap(newPoints);
|
||||
}
|
||||
|
||||
void SimRender::ConstructDashedLine(const std::vector<CVector2D>& keyPoints, SDashedLine& dashedLineOut, const float dashLength, const float blankLength)
|
||||
{
|
||||
// sanity checks
|
||||
if (dashLength <= 0)
|
||||
return;
|
||||
|
||||
if (blankLength <= 0)
|
||||
return;
|
||||
|
||||
if (keyPoints.size() < 2)
|
||||
return;
|
||||
|
||||
dashedLineOut.m_Points.clear();
|
||||
dashedLineOut.m_StartIndices.clear();
|
||||
|
||||
// walk the line, counting the total length so far at each node point. When the length exceeds dashLength, cut the last segment at the
|
||||
// required length and continue for blankLength along the line to start a new dash segment.
|
||||
|
||||
// TODO: we should probably extend this function to also allow for closed lines. I was thinking of slightly scaling the dash/blank length
|
||||
// so that it fits the length of the curve, but that requires knowing the length of the curve upfront which is sort of expensive to compute
|
||||
// (O(n) and lots of square roots).
|
||||
|
||||
bool buildingDash = true; // true if we're building a dash, false if a blank
|
||||
float curDashLength = 0; // builds up the current dash/blank's length as we walk through the line nodes
|
||||
CVector2D dashLastPoint = keyPoints[0]; // last point of the current dash/blank being built.
|
||||
|
||||
// register the first starting node of the first dash
|
||||
dashedLineOut.m_Points.push_back(keyPoints[0]);
|
||||
dashedLineOut.m_StartIndices.push_back(0);
|
||||
|
||||
// index of the next key point on the path. Must always point to a node that is further along the path than dashLastPoint, so we can
|
||||
// properly take a direction vector along the path.
|
||||
size_t i = 0;
|
||||
|
||||
while(i < keyPoints.size() - 1)
|
||||
{
|
||||
// get length of this segment
|
||||
CVector2D segmentVector = keyPoints[i + 1] - dashLastPoint; // vector from our current point along the path to nextNode
|
||||
float segmentLength = segmentVector.Length();
|
||||
|
||||
float targetLength = (buildingDash ? dashLength : blankLength);
|
||||
if (curDashLength + segmentLength > targetLength)
|
||||
{
|
||||
// segment is longer than the dash length we still have to go, so we'll need to cut it; create a cut point along the segment
|
||||
// line that is of just the required length to complete the dash, then make it the base point for the next dash/blank.
|
||||
float cutLength = targetLength - curDashLength;
|
||||
CVector2D cutPoint = dashLastPoint + (segmentVector.Normalized() * cutLength);
|
||||
|
||||
// start a new dash or blank in the next iteration
|
||||
curDashLength = 0;
|
||||
buildingDash = !buildingDash; // flip from dash to blank and vice-versa
|
||||
dashLastPoint = cutPoint;
|
||||
|
||||
// don't increment i, we haven't fully traversed this segment yet so we still need to use the same point to take the
|
||||
// direction vector with in the next iteration
|
||||
|
||||
// this cut point is either the end of the current dash or the beginning of a new dash; either way, we're gonna need it
|
||||
// in the points array.
|
||||
dashedLineOut.m_Points.push_back(cutPoint);
|
||||
|
||||
if (buildingDash)
|
||||
{
|
||||
// if we're gonna be building a new dash, then cutPoint is now the base point of that new dash, so let's register its
|
||||
// index as a start index of a dash.
|
||||
dashedLineOut.m_StartIndices.push_back(dashedLineOut.m_Points.size() - 1);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// the segment from lastDashPoint to keyPoints[i+1] doesn't suffice to complete the dash, so we need to add keyPoints[i+1]
|
||||
// to this dash's points and continue from there
|
||||
|
||||
if (buildingDash)
|
||||
// still building the dash, add it to the output (we don't need to store the blanks)
|
||||
dashedLineOut.m_Points.push_back(keyPoints[i+1]);
|
||||
|
||||
curDashLength += segmentLength;
|
||||
dashLastPoint = keyPoints[i+1];
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
* Helper functions related to rendering
|
||||
*/
|
||||
|
||||
#include "maths/Vector2D.h"
|
||||
|
||||
class CSimContext;
|
||||
class CVector2D;
|
||||
class CVector3D;
|
||||
@ -31,6 +33,26 @@ class CBoundingBoxAligned;
|
||||
class CBoundingBoxOriented;
|
||||
struct SOverlayLine;
|
||||
|
||||
struct SDashedLine
|
||||
{
|
||||
/// Packed array of consecutive dashes' points. Use m_StartIndices to navigate it.
|
||||
std::vector<CVector2D> m_Points;
|
||||
|
||||
/**
|
||||
* Start indices in m_Points of each dash. Dash n starts at point m_StartIndices[n] and ends at the point with index
|
||||
* m_StartIndices[n+1] - 1, or at the end of the m_Points vector. Use the GetEndIndex(n) convenience method to abstract away the
|
||||
* difference and get the (exclusive) end index of dash n.
|
||||
*/
|
||||
std::vector<size_t> m_StartIndices;
|
||||
|
||||
/// Returns the (exclusive) end point index (i.e. index within m_Points) of dash n.
|
||||
size_t GetEndIndex(size_t i)
|
||||
{
|
||||
// for the last dash, there is no next starting index, so we need to use the end index of the m_Points array instead
|
||||
return (i < m_StartIndices.size() - 1 ? m_StartIndices[i+1] : m_Points.size());
|
||||
}
|
||||
};
|
||||
|
||||
namespace SimRender
|
||||
{
|
||||
|
||||
@ -95,8 +117,19 @@ void SmoothPointsAverage(std::vector<CVector2D>& points, bool closed);
|
||||
* the direction of the curve.
|
||||
* If @p closed then the points are treated as a closed path (the last is connected
|
||||
* to the first).
|
||||
* @param segmentSamples Amount of intermediate points to sample between every two control points.
|
||||
*/
|
||||
void InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset);
|
||||
void InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset, int segmentSamples = 4);
|
||||
|
||||
/**
|
||||
* Creates a dashed line from the line specified by @points so that each dash is of length
|
||||
* @p dashLength, and each blank inbetween is of length @p blankLength. The dashed line returned as a list of smaller lines
|
||||
* in @p dashedLineOut.
|
||||
*
|
||||
* @param dashLength Length of a single dash. Must be strictly positive.
|
||||
* @param blankLength Length of a single blank between dashes. Must be strictly positive.
|
||||
*/
|
||||
void ConstructDashedLine(const std::vector<CVector2D>& linePoints, SDashedLine& dashedLineOut, const float dashLength, const float blankLength);
|
||||
|
||||
} // namespace
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user