Ticket #524: rallypoints_wip_25aug11.patch
File rallypoints_wip_25aug11.patch, 136.5 KB (added by , 13 years ago) |
---|
-
binaries/data/mods/public/art/actors/props/special/common/waypoint_flag.xml
diff --git a/binaries/data/mods/public/art/actors/props/special/common/waypoint_flag.xml b/binaries/data/mods/public/art/actors/props/special/common/waypoint_flag.xml index fcc167a..1ee5bdc 100644
a b 11 11 <variant name="waypoint_hellenes"> 12 12 <texture>props/banner_greek.png</texture> 13 13 </variant> 14 <variant name="waypoint_persians"> 15 <texture>props/banner_persian.png</texture> 16 </variant> 17 <variant name="waypoint_celts"> 18 <texture>props/banner_celt.png</texture> 19 </variant> 20 <variant name="waypoint_carthaginians"> 21 <texture>props/banner_carthage.png</texture> 22 </variant> 23 <variant name="waypoint_iberians"> 24 <texture>props/banner_iberians.png</texture> 25 </variant> 26 <variant name="waypoint_romans"> 27 <texture>props/banner_romans.png</texture> 28 </variant> 14 29 </group> 15 30 </actor> -
new file inaries/data/mods/public/art/textures/misc/rallypoint_line.png
diff --git a/binaries/data/mods/public/art/textures/misc/rallypoint_line.png b/binaries/data/mods/public/art/textures/misc/rallypoint_line.png new file mode 100644 index 0000000000000000000000000000000000000000..55c1100618b0fa701fe2ee22c16430987a80cc89 GIT binary patch literal 2849 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
-
new file 0
literal 0 HcmV?d00001 diff --git a/binaries/data/mods/public/art/textures/misc/rallypoint_line_mask.png b/binaries/data/mods/public/art/textures/misc/rallypoint_line_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..b1824fc318c54ca2d9b31a1e4f9acf1d3d5696db GIT binary patch literal 2826 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
-
new file 0
literal 0 HcmV?d00001 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/single/focus-rally.png b/binaries/data/mods/public/art/textures/ui/session/icons/single/focus-rally.png new file mode 100644 index 0000000000000000000000000000000000000000..31a341a52d386d3176e14fdd96ead1ac209f2ad3 GIT binary patch literal 3816 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
-
binaries/data/mods/public/gui/session/input.js
literal 0 HcmV?d00001 diff --git a/binaries/data/mods/public/gui/session/input.js b/binaries/data/mods/public/gui/session/input.js index f082daf..7dc3ddc 100644
a b function performCommand(entity, commandName) 1109 1109 case "unload-all": 1110 1110 unloadAll(entity); 1111 1111 break; 1112 case "focus-rally": 1113 // make the camera move to the building's rally point 1114 1115 // grab the rally point position (if any) 1116 var entState = Engine.GuiInterfaceCall("GetEntityState", entity); 1117 if (entState.rallyPoint) 1118 { 1119 // the rally point isn't necessarily set 1120 if (entState.rallyPoint.position) 1121 { 1122 Engine.CameraMoveTo(entState.rallyPoint.position.x, entState.rallyPoint.position.z); 1123 } 1124 else 1125 { 1126 // no rally point set, jump to the building itself instead (since units will now spawn right next to the 1127 // building) 1128 if (entState.position) 1129 Engine.CameraMoveTo(entState.position.x, entState.position.z); 1130 } 1131 } 1132 1133 break; 1112 1134 default: 1113 1135 break; 1114 1136 } -
binaries/data/mods/public/gui/session/unit_commands.js
diff --git a/binaries/data/mods/public/gui/session/unit_commands.js b/binaries/data/mods/public/gui/session/unit_commands.js index d546de9..ebb1575 100644
a b function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback) 225 225 break; 226 226 227 227 case COMMAND: 228 if (item == "unload-all") 228 // here, "item" is an object with properties .name (command name), .tooltip and .icon (relative to session/icons/single) 229 if (item.name == "unload-all") 229 230 { 230 231 var count = unitEntState.garrisonHolder.entities.length; 231 232 getGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (count > 0 ? count : ""); … … function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback) 235 236 getGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = ""; 236 237 } 237 238 238 tooltip = toTitleCase(item);239 tooltip = (item.tooltip ? item.tooltip : toTitleCase(item.name)); // use item.tooltip if available, otherwise default to title-cased command name 239 240 break; 240 241 241 242 default: … … function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback) 289 290 { 290 291 //icon.cell_id = i; 291 292 //icon.cell_id = getCommandCellId(item); 292 icon.sprite = "stretched:session/icons/single/" + getCommandImage(item);293 icon.sprite = "stretched:session/icons/single/" + item.icon; 293 294 294 295 } 295 296 else if (template.icon) … … function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, s 373 374 var commands = getEntityCommandsList(entState); 374 375 if (commands.length) 375 376 setupUnitPanel("Command", usedPanels, entState, commands, 376 function (item) { performCommand(entState.id, item ); } );377 function (item) { performCommand(entState.id, item.name); } ); 377 378 378 379 if (entState.garrisonHolder) 379 380 { -
binaries/data/mods/public/gui/session/utility_functions.js
diff --git a/binaries/data/mods/public/gui/session/utility_functions.js b/binaries/data/mods/public/gui/session/utility_functions.js index a25ee94..a86b1fe 100644
a b function getFormationCellId(formationName) 253 253 } 254 254 } 255 255 256 function getCommandImage(commandName)257 {258 switch (commandName)259 {260 case "delete":261 return "kill_small.png";262 case "unload-all":263 return "garrison-out.png";264 case "garrison":265 return "garrison.png";266 case "repair":267 return "repair.png";268 default:269 return "";270 }271 }272 273 256 function getEntityFormationsList(entState) 274 257 { 275 258 var civ = g_Players[entState.player].civ; … … function getEntityCommandsList(entState) 298 281 { 299 282 var commands = []; 300 283 if (entState.garrisonHolder) 301 commands.push("unload-all"); 302 commands.push("delete"); 284 { 285 commands.push({ 286 "name": "unload-all", 287 "tooltip": "Unload All", 288 "icon": "garrison-out.png" 289 }); 290 } 291 292 commands.push({ 293 "name": "delete", 294 "tooltip": "Delete", 295 "icon": "kill_small.png" 296 }); 297 303 298 if (isUnit(entState)) 304 commands.push("garrison"); 299 { 300 commands.push({ 301 "name": "garrison", 302 "tooltip": "Garrison", 303 "icon": "garrison.png" 304 }); 305 } 306 305 307 if (entState.buildEntities) 306 commands.push("repair"); 308 { 309 commands.push({ 310 "name": "repair", 311 "tooltip": "Repair", 312 "icon": "repair.png" 313 }); 314 } 315 316 if(entState.rallyPoint) 317 { 318 commands.push({ 319 "name": "focus-rally", 320 "tooltip": "Focus on Rally Point", 321 "icon": "focus-rally.png" 322 }); 323 } 324 307 325 return commands; 308 326 } 309 327 -
binaries/data/mods/public/shaders/overlayline.fp
diff --git a/binaries/data/mods/public/shaders/overlayline.fp b/binaries/data/mods/public/shaders/overlayline.fp index 368ce42..3d78109 100644
a b PARAM objectColor = program.local[0]; 3 3 TEMP base; 4 4 TEMP mask; 5 5 TEMP color; 6 TEMP los; 6 7 7 8 8 // Combine base texture and color, using mask texture 9 9 TEX base, fragment.texcoord[0], texture[0], 2D; 10 10 TEX mask, fragment.texcoord[0], texture[1], 2D; 11 11 LRP color.rgb, mask, objectColor, base; 12 12 13 #ifdef IGNORE_LOS 14 MOV result.color.rgb, color; 15 MOV result.color.a, 1; // full-bright 16 #else 17 TEMP los; 13 18 // Multiply by LOS texture 14 19 TEX los, fragment.texcoord[1], texture[2], 2D; 15 20 MUL result.color.rgb, color, los.a; 16 21 17 22 // Use alpha from base texture 23 //MOV result.color.a, base.a; 18 24 MUL result.color.a, objectColor.a, base.a; 25 #endif 26 19 27 20 28 END -
binaries/data/mods/public/simulation/components/GuiInterface.js
diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js index ac55d7c..03caee8 100644
a b GuiInterface.prototype.GetEntityState = function(player, ent) 219 219 if (cmpRallyPoint) 220 220 { 221 221 ret.rallyPoint = { }; 222 if (cmpRallyPoint.IsSet()) // can't return a rally point position if none is set ... 223 ret.rallyPoint.position = cmpRallyPoint.GetPosition(); 222 224 } 223 225 224 226 var cmpGarrisonHolder = Engine.QueryInterface(ent, IID_GarrisonHolder); … … GuiInterface.prototype.SetStatusBars = function(player, cmd) 376 378 */ 377 379 GuiInterface.prototype.DisplayRallyPoint = function(player, cmd) 378 380 { 379 // If there are rally points already displayed, destroythem380 for each (var ent in this. rallyPoints)381 // If there are some rally points already displayed, first hide them 382 for each (var ent in this.entsRallyPointsDisplayed) 381 383 { 382 // Hide it first (the destruction won't be instantaneous)383 var cmpPosition = Engine.QueryInterface(ent, IID_Position);384 cmpPosition.MoveOutOfWorld();384 var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint); 385 if (!cmpRallyPoint) 386 continue; 385 387 386 Engine.DestroyEntity(ent);388 cmpRallyPoint.SetDisplayed(false); 387 389 } 388 390 389 this.rallyPoints = []; 390 391 var positions = []; 392 // DisplayRallyPoints is called passing a list of entities for which 393 // rally points must be displayed 391 // Show the rally points for the passed entities 394 392 for each (var ent in cmd.entities) 395 393 { 396 394 var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint); … … GuiInterface.prototype.DisplayRallyPoint = function(player, cmd) 402 400 if (!cmpOwnership || cmpOwnership.GetOwner() != player) 403 401 continue; 404 402 405 // If the command was passed an explicit position, use that and 406 // override the real rally point position; otherwise use the real position 407 var pos; 403 // If the command was passed an explicit position, first set it 408 404 if (cmd.x && cmd.z) 409 pos = {"x": cmd.x, "z": cmd.z}; 410 else 411 pos = cmpRallyPoint.GetPosition(); 405 cmpRallyPoint.SetPosition(cmd); 412 406 413 if (pos) 414 { 415 // TODO: it'd probably be nice if we could draw some kind of line 416 // between the building and pos, to make the marker easy to find even 417 // if it's a long way from the building 407 cmpRallyPoint.SetDisplayed(true); 418 408 419 positions.push(pos);420 }421 409 } 422 410 423 // Add rally point entity for each building 424 for each (var pos in positions) 425 { 426 var rallyPoint = Engine.AddLocalEntity("actor|props/special/common/waypoint_flag.xml"); 427 var cmpPosition = Engine.QueryInterface(rallyPoint, IID_Position); 428 cmpPosition.JumpTo(pos.x, pos.z); 429 this.rallyPoints.push(rallyPoint); 430 } 411 // Remember which entities have their rally points displayed so we can hide them again 412 this.entsRallyPointsDisplayed = cmd.entities; 413 431 414 }; 432 415 433 416 /** -
deleted file binaries/data/mods/public/simulation/components/RallyPoint.js
diff --git a/binaries/data/mods/public/simulation/components/RallyPoint.js b/binaries/data/mods/public/simulation/components/RallyPoint.js deleted file mode 100644 index 3c3816a..0000000
+ - 1 function RallyPoint() {}2 3 RallyPoint.prototype.Schema =4 "<a:component/><empty/>";5 6 RallyPoint.prototype.Init = function()7 {8 this.pos = undefined;9 };10 11 RallyPoint.prototype.SetPosition = function(x, z)12 {13 this.pos = {14 "x": x,15 "z": z16 }17 };18 19 RallyPoint.prototype.Unset = function()20 {21 this.pos = undefined;22 };23 24 RallyPoint.prototype.GetPosition = function()25 {26 return this.pos;27 };28 29 Engine.RegisterComponentType(IID_RallyPoint, "RallyPoint", RallyPoint); -
deleted file binaries/data/mods/public/simulation/components/interfaces/RallyPoint.js
diff --git a/binaries/data/mods/public/simulation/components/interfaces/RallyPoint.js b/binaries/data/mods/public/simulation/components/interfaces/RallyPoint.js deleted file mode 100644 index ac0f079..0000000
+ - 1 Engine.RegisterInterface("RallyPoint"); -
binaries/data/mods/public/simulation/helpers/Commands.js
diff --git a/binaries/data/mods/public/simulation/helpers/Commands.js b/binaries/data/mods/public/simulation/helpers/Commands.js index 57dc2b9..a3b3dae 100644
a b function ProcessCommand(player, cmd) 229 229 { 230 230 var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint); 231 231 if (cmpRallyPoint) 232 cmpRallyPoint.SetPosition(cmd .x, cmd.z);232 cmpRallyPoint.SetPosition(cmd); 233 233 } 234 234 break; 235 235 -
new file inaries/data/mods/public/simulation/templates/special/rallypoint.xml
diff --git a/binaries/data/mods/public/simulation/templates/special/rallypoint.xml b/binaries/data/mods/public/simulation/templates/special/rallypoint.xml new file mode 100644 index 0000000..c73b5d8
- + 1 <?xml version="1.0" encoding="utf-8"?> 2 <Entity parent="special/actor"> 3 <VisualActor> 4 <Actor>props/special/common/waypoint_flag.xml</Actor> 5 </VisualActor> 6 <Vision> 7 <AlwaysVisible>true</AlwaysVisible> 8 </Vision> 9 </Entity> -
binaries/data/mods/public/simulation/templates/template_structure.xml
diff --git a/binaries/data/mods/public/simulation/templates/template_structure.xml b/binaries/data/mods/public/simulation/templates/template_structure.xml index a346fd9..8de5ade 100644
a b 53 53 <DisableBlockPathfinding>false</DisableBlockPathfinding> 54 54 </Obstruction> 55 55 <OverlayRenderer/> 56 <RallyPoint/> 56 <RallyPoint> 57 <MarkerTemplate>special/rallypoint</MarkerTemplate> 58 <LineThickness>0.2</LineThickness> 59 <LineColour r="35" g="86" b="188"></LineColour> 60 <LineDashColour r="158" g="11" b="15"></LineDashColour> 61 <LineStartCap>square</LineStartCap> 62 <LineEndCap>round</LineEndCap> 63 </RallyPoint> 57 64 <Sound> 58 65 <SoundGroups> 59 66 <select>interface/select/building/sel_universal.xml</select> -
new file source/graphics/Overlay.cpp
diff --git a/source/graphics/Overlay.cpp b/source/graphics/Overlay.cpp new file mode 100644 index 0000000..1acf8f7
- + 1 /* Copyright (C) 2011 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "precompiled.h" 19 20 #include "Overlay.h" 21 22 bool SOverlayTexturedLine::StrToLineCap(const std::wstring& str, SOverlayTexturedLine::LineCap& out) 23 { 24 if (str.compare(L"flat") == 0) 25 { 26 out = LINECAP_FLAT; 27 return true; 28 } 29 else if (str.compare(L"round") == 0) 30 { 31 out = LINECAP_ROUND; 32 return true; 33 } 34 else if (str.compare(L"sharp") == 0) 35 { 36 out = LINECAP_SHARP; 37 return true; 38 } 39 else if (str.compare(L"square") == 0) 40 { 41 out = LINECAP_SQUARE; 42 return true; 43 } 44 else 45 return false; 46 } -
source/graphics/Overlay.h
diff --git a/source/graphics/Overlay.h b/source/graphics/Overlay.h index e07b13b..871f7a6 100644
a b struct SOverlayLine 44 44 */ 45 45 struct SOverlayTexturedLine 46 46 { 47 SOverlayTexturedLine() : m_Terrain(NULL), m_Thickness(1.0f) { } 47 48 enum LineCap 49 { 50 LINECAP_FLAT, ///< no line ending; abrupt stop of the line (aka. butt ending) 51 52 /** 53 * Semi-circular line ending. The texture is mapped by curving the left vertical edge around the semi-circle's rim. That is, 54 * 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 55 * from 0 to 1 as the rim is traversed. 56 */ 57 LINECAP_ROUND, 58 LINECAP_SHARP, ///< sharp point ending 59 LINECAP_SQUARE, ///< square end that extends half the line width beyond the line end 60 }; 61 62 SOverlayTexturedLine() 63 : m_Terrain(NULL), m_Thickness(1.0f), m_Closed(false), m_AlwaysVisible(false), m_StartCap(LINECAP_FLAT), m_EndCap(LINECAP_FLAT) 64 {} 48 65 49 66 CTerrain* m_Terrain; 50 67 CTexturePtr m_TextureBase; 51 68 CTexturePtr m_TextureMask; 52 CColor m_Color; 53 std::vector<float> m_Coords; // (x, z) vertex coordinate pairs; y is computed automatically; shape is automatically closed 54 float m_Thickness; // world-space units 69 CColor m_Color; // color to apply to the line texture 70 std::vector<float> m_Coords; // (x, z) vertex coordinate pairs; y is computed automatically 71 float m_Thickness; // half-width of the line, in world-space units 72 73 bool m_Closed; // should this line be treated as a closed loop? (if set, the end cap settings are ignored) 74 bool m_AlwaysVisible; // should this line be rendered even under the SoD? 75 LineCap m_StartCap; // LineEndCap to be used at the start of the line 76 LineCap m_EndCap; // LineEndCap to be used at the end of the line 55 77 56 78 shared_ptr<CRenderData> m_RenderData; // cached renderer data (shared_ptr so that copies/deletes are automatic) 79 80 /** 81 * Interprets @p str as a LineCap value, and writes the matching value to @p out, if any. Returns true if a value was written to @p out, 82 * false otherwise. 83 */ 84 static bool StrToLineCap(const std::wstring& str, LineCap& out); 85 57 86 }; 58 87 59 88 /** -
source/gui/scripting/ScriptFunctions.cpp
diff --git a/source/gui/scripting/ScriptFunctions.cpp b/source/gui/scripting/ScriptFunctions.cpp index 0054246..e6c0dcc 100644
a b 34 34 #include "ps/CConsole.h" 35 35 #include "ps/Errors.h" 36 36 #include "ps/Game.h" 37 #include "ps/World.h" 37 38 #include "ps/Hotkey.h" 38 39 #include "ps/Overlay.h" 39 40 #include "ps/ProfileViewer.h" … … void CameraFollowFPS(void* UNUSED(cbdata), entity_id_t entityid) 346 347 g_Game->GetView()->CameraFollow(entityid, true); 347 348 } 348 349 350 void MoveCameraTarget2D(void* UNUSED(cbdata), entity_pos_t x, entity_pos_t z) 351 { 352 // this must not fail 353 if(!(g_Game && g_Game->GetWorld() && g_Game->GetView() && g_Game->GetWorld()->GetTerrain())) 354 return; 355 356 CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); 357 358 CVector3D target; 359 target.X = x.ToFloat(); 360 target.Z = z.ToFloat(); 361 target.Y = terrain->GetExactGroundLevel(target.X, target.Z); 362 g_Game->GetView()->MoveCameraTarget(target, true); // perform this in minimap mode since this is an "instant" camera movement similar to what you would get for clicking the minimap (also prevents height glitches) 363 } 364 349 365 entity_id_t GetFollowedEntity(void* UNUSED(cbdata)) 350 366 { 351 367 if (g_Game && g_Game->GetView()) … … void GuiScriptingInit(ScriptInterface& scriptInterface) 499 515 scriptInterface.RegisterFunction<CScriptVal, &GetMapSettings>("GetMapSettings"); 500 516 scriptInterface.RegisterFunction<void, entity_id_t, &CameraFollow>("CameraFollow"); 501 517 scriptInterface.RegisterFunction<void, entity_id_t, &CameraFollowFPS>("CameraFollowFPS"); 518 scriptInterface.RegisterFunction<void, entity_pos_t, entity_pos_t, &MoveCameraTarget2D>("CameraMoveTo"); // renamed slightly to better match the rest of the CameraXxx functions 502 519 scriptInterface.RegisterFunction<entity_id_t, &GetFollowedEntity>("GetFollowedEntity"); 503 520 scriptInterface.RegisterFunction<bool, std::string, &HotkeyIsPressed_>("HotkeyIsPressed"); 504 521 scriptInterface.RegisterFunction<void, std::wstring, &DisplayErrorDialog>("DisplayErrorDialog"); -
source/maths/Vector2D.h
diff --git a/source/maths/Vector2D.h b/source/maths/Vector2D.h index c61d31b..dfab3d4 100644
a b public: 127 127 return CVector2D(X / mag, Y / mag); 128 128 } 129 129 130 /** 131 * Returns a version of this vector rotated by @p angle radians counterclockwise. 132 */ 133 CVector2D Rotated(float angle) 134 { 135 float c = cos(angle); 136 float s = sin(angle); 137 return CVector2D( 138 c*X - s*Y, 139 s*X + c*Y 140 ); 141 } 142 143 /** 144 * Rotates this vector by @p angle radians counterclockwise. 145 */ 146 void Rotate(float angle) 147 { 148 float c = cos(angle); 149 float s = sin(angle); 150 float newX = c*X - s*Y; 151 float newY = s*X + c*Y; 152 X = newX; 153 Y = newY; 154 } 155 130 156 public: 131 157 float X, Y; 132 158 }; -
source/renderer/OverlayRenderer.cpp
diff --git a/source/renderer/OverlayRenderer.cpp b/source/renderer/OverlayRenderer.cpp index d5eb0cc..ddb108f 100644
a b 1 /* Copyright (C) 201 0Wildfire Games.1 /* Copyright (C) 2011 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … 19 19 20 20 #include "OverlayRenderer.h" 21 21 22 #include "maths/MathUtil.h" 22 23 #include "graphics/LOSTexture.h" 23 24 #include "graphics/Overlay.h" 24 #include "graphics/ShaderManager.h"25 25 #include "graphics/Terrain.h" 26 26 #include "graphics/TextureManager.h" 27 27 #include "lib/ogl.h" 28 #include "maths/Vector2D.h" 28 29 #include "ps/Game.h" 29 30 #include "ps/Profile.h" 31 #include "ps/CLogger.h" 30 32 #include "renderer/Renderer.h" 31 33 #include "renderer/VertexBuffer.h" 32 34 #include "renderer/VertexBufferManager.h" … … class CTexturedLineRData : public CRenderData 44 46 { 45 47 public: 46 48 CTexturedLineRData(SOverlayTexturedLine* line) : 47 m_Line(line), m_VB(NULL), m_VBIndices(NULL) 49 m_Line(line), 50 m_VB(NULL), 51 m_VBIndices(NULL), 52 m_VBStartCap(NULL), 53 m_VBStartCapIndices(NULL), 54 m_VBEndCap(NULL), 55 m_VBEndCapIndices(NULL), 56 m_Raise(.2f) 48 57 { 49 58 } 50 59 … … public: 54 63 g_VBMan.Release(m_VB); 55 64 if (m_VBIndices) 56 65 g_VBMan.Release(m_VBIndices); 66 if (m_VBStartCap) 67 g_VBMan.Release(m_VBStartCap); 68 if (m_VBStartCapIndices) 69 g_VBMan.Release(m_VBStartCapIndices); 70 if (m_VBEndCap) 71 g_VBMan.Release(m_VBEndCap); 72 if (m_VBEndCapIndices) 73 g_VBMan.Release(m_VBEndCapIndices); 57 74 } 58 75 59 76 struct SVertex 60 77 { 61 SVertex(CVector3D pos, short u, short v) : m_Position(pos) { m_UVs[0] = u; m_UVs[1] = v; }78 SVertex(CVector3D pos, float u, float v) : m_Position(pos) { m_UVs[0] = u; m_UVs[1] = v; } 62 79 CVector3D m_Position; 63 GLshort m_UVs[2]; 80 //GLshort m_UVs[2]; 81 GLfloat m_UVs[2]; 64 82 }; 65 cassert(sizeof(SVertex) == 16);83 //cassert(sizeof(SVertex) == 16); 66 84 67 85 void Update(); 68 86 87 /** 88 * Creates a line cap of the specified type @p endCapType at the end of the segment going in direction @p normal, and write the vertices and indices 89 * to @p verticesOut and @p indicesOut, respectively. The drawing mode to use for the resulting vertices/indices array (quad strip, 90 * triangle strip, ...) is written to @p drawModeOut. 91 * 92 * @param corner1 One of the two butt-end corner points of the line to which the cap should be attached. 93 * @param corner2 One of the two butt-end corner points of the line to which the cap should be attached. 94 * @param normal Normal vector indicating the direction of the segment to which the cap should be attached. 95 * @param endCapType The type of end cap to produce. 96 * @param verticesOut Output vector of vertices to draw using @p drawModeOut. These will be entered into a vertex buffer and handed to the 97 * renderer along with the indices array. 98 * @param indicesOut Output vector of vertex indices to draw using @p drawModeOut. These will be entered into a vertex index buffer and 99 * handed to the rendered along with the vertex array. 100 * @param drawModeOut The rendering mode to be used by the renderer to draw the end cap, e.g. GL_QUAD_STRIP, GL_TRIANGLE_STRIP, etc. 101 */ 102 void CreateLineCap(const CVector3D& corner1, const CVector3D& corner2, const CVector3D& normal, SOverlayTexturedLine::LineCap endCapType, 103 std::vector<SVertex>& verticesOut, std::vector<u16>& indicesOut, GLenum& drawModeOut); 104 105 /// Small utility function; grabs the centroid of the positions of two vertices 106 inline CVector3D Centroid(const SVertex& v1, const SVertex& v2) 107 { 108 return (v1.m_Position + v2.m_Position) * 0.5; 109 } 110 69 111 SOverlayTexturedLine* m_Line; 70 112 71 113 CVertexBuffer::VBChunk* m_VB; 72 114 CVertexBuffer::VBChunk* m_VBIndices; 115 116 // vertex buffers for custom line end caps (at both the start and end of the line) 117 CVertexBuffer::VBChunk* m_VBStartCap; 118 CVertexBuffer::VBChunk* m_VBStartCapIndices; 119 GLenum m_StartCapDrawMode; // quad strip, triangle fan, ... 120 121 CVertexBuffer::VBChunk* m_VBEndCap; 122 CVertexBuffer::VBChunk* m_VBEndCapIndices; 123 GLenum m_EndCapDrawMode; 124 125 float m_Raise; // small vertical offset of line from terrain to prevent visual glitches 126 73 127 }; 74 128 75 129 OverlayRenderer::OverlayRenderer() … … void OverlayRenderer::RenderOverlaysBeforeWater() 164 218 glDisable(GL_BLEND); 165 219 } 166 220 221 void OverlayRenderer::RenderTexturedOverlayLines(const std::vector<size_t>& indices, CShaderProgramPtr shaderTexLine) 222 { 223 224 //for (size_t i = 0; i < m->texlines.size(); ++i) 225 for (std::vector<size_t>::const_iterator idxIt = indices.begin(); idxIt != indices.end(); idxIt++) 226 { 227 SOverlayTexturedLine* line = m->texlines[(*idxIt)]; 228 if (!line->m_RenderData) 229 continue; 230 231 shaderTexLine->BindTexture("baseTex", line->m_TextureBase->GetHandle()); 232 shaderTexLine->BindTexture("maskTex", line->m_TextureMask->GetHandle()); 233 shaderTexLine->Uniform("objectColor", line->m_Color); 234 235 CTexturedLineRData* rdata = static_cast<CTexturedLineRData*>(line->m_RenderData.get()); 236 237 // -- render main line quad strip ---------------------- 238 239 GLsizei stride = sizeof(CTexturedLineRData::SVertex); 240 CTexturedLineRData::SVertex* base = reinterpret_cast<CTexturedLineRData::SVertex*>(rdata->m_VB->m_Owner->Bind()); 241 242 glVertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]); 243 glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs[0]); 244 245 u8* indexBase = rdata->m_VBIndices->m_Owner->Bind(); 246 glDrawElements(GL_QUAD_STRIP, rdata->m_VBIndices->m_Count, GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*rdata->m_VBIndices->m_Index); 247 248 g_Renderer.GetStats().m_OverlayTris += rdata->m_VBIndices->m_Count - 2; 249 250 // -- render end cap (if any) -------------------------- 251 252 if (rdata->m_VBEndCap && rdata->m_VBEndCapIndices) // only draw end cap if we've created vertex/index buffers for it 253 { 254 CTexturedLineRData::SVertex* endCapBase = reinterpret_cast<CTexturedLineRData::SVertex*>(rdata->m_VBEndCap->m_Owner->Bind()); 255 256 glVertexPointer(3, GL_FLOAT, stride, &endCapBase->m_Position[0]); 257 glTexCoordPointer(2, GL_FLOAT, stride, &endCapBase->m_UVs[0]); 258 259 u8* endCapIndexBase = rdata->m_VBEndCapIndices->m_Owner->Bind(); 260 glDrawElements(rdata->m_EndCapDrawMode, rdata->m_VBEndCapIndices->m_Count, GL_UNSIGNED_SHORT, endCapIndexBase + sizeof(u16)*rdata->m_VBEndCapIndices->m_Index); 261 262 g_Renderer.GetStats().m_OverlayTris += rdata->m_VBEndCapIndices->m_Count - 2; // valid for both quad strip and triangle strip 263 264 } 265 266 // -- render start cap (if any) ------------------------ 267 268 if (rdata->m_VBStartCap && rdata->m_VBStartCapIndices) // only draw start cap if we've created vertex/index buffers for it 269 { 270 CTexturedLineRData::SVertex* startCapBase = reinterpret_cast<CTexturedLineRData::SVertex*>(rdata->m_VBStartCap->m_Owner->Bind()); 271 272 glVertexPointer(3, GL_FLOAT, stride, &startCapBase->m_Position[0]); 273 glTexCoordPointer(2, GL_FLOAT, stride, &startCapBase->m_UVs[0]); 274 275 u8* startCapIndexBase = rdata->m_VBStartCapIndices->m_Owner->Bind(); 276 glDrawElements(rdata->m_StartCapDrawMode, rdata->m_VBStartCapIndices->m_Count, GL_UNSIGNED_SHORT, startCapIndexBase + sizeof(u16)*rdata->m_VBStartCapIndices->m_Index); 277 278 g_Renderer.GetStats().m_OverlayTris += rdata->m_VBStartCapIndices->m_Count - 2; // valid for both quad strip and triangle strip 279 } 280 281 } 282 283 } 284 167 285 void OverlayRenderer::RenderOverlaysAfterWater() 168 286 { 169 287 PROFILE("render overlays (after water)"); … … void OverlayRenderer::RenderOverlaysAfterWater() 182 300 glEnableClientState(GL_VERTEX_ARRAY); 183 301 glEnableClientState(GL_TEXTURE_COORD_ARRAY); 184 302 185 CShaderManager& shaderManager = g_Renderer.GetShaderManager(); 186 CShaderProgramPtr shaderTexLine(shaderManager.LoadProgram("overlayline", std::map<CStr, CStr>())); 303 CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture(); 187 304 188 shaderTexLine->Bind(); 305 std::map<CStr, CStr> defAlwaysVisible; 306 defAlwaysVisible.insert(std::make_pair(CStr("IGNORE_LOS"), CStr("1"))); 189 307 190 C LOSTexture& los = g_Renderer.GetScene().GetLOSTexture();191 shaderTexLine->BindTexture("losTex", los.GetTexture());192 shaderTexLine->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);308 CShaderManager& shaderManager = g_Renderer.GetShaderManager(); 309 CShaderProgramPtr shaderTexLineNormal(shaderManager.LoadProgram("overlayline", std::map<CStr, CStr>())); 310 CShaderProgramPtr shaderTexLineAlwaysVisible(shaderManager.LoadProgram("overlayline", defAlwaysVisible)); 193 311 194 for (size_t i = 0; i < m->texlines.size(); ++i) 312 // ---------------------------------------------------------------------------------------- 313 // group the overlay lines by whether they need to be always visible 314 std::vector<size_t> alwaysVisibleTexLineIndices; 315 std::vector<size_t> normalTexLineIndices; 316 317 for (size_t i = 0; i < m->texlines.size(); i++) 195 318 { 196 SOverlayTexturedLine* line = m->texlines[i]; 197 if (!line->m_RenderData) 198 continue; 319 if (m->texlines[i]->m_AlwaysVisible) 320 { 321 alwaysVisibleTexLineIndices.push_back(i); 322 } 323 else 324 { 325 normalTexLineIndices.push_back(i); 326 } 327 } 199 328 200 shaderTexLine->BindTexture("baseTex", line->m_TextureBase->GetHandle()); 201 shaderTexLine->BindTexture("maskTex", line->m_TextureMask->GetHandle()); 202 shaderTexLine->Uniform("objectColor", line->m_Color); 329 // render go go 203 330 204 CTexturedLineRData* rdata = static_cast<CTexturedLineRData*>(line->m_RenderData.get()); 331 shaderTexLineNormal->Bind(); 332 shaderTexLineNormal->BindTexture("losTex", los.GetTexture()); 333 shaderTexLineNormal->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f); 205 334 206 GLsizei stride = sizeof(CTexturedLineRData::SVertex); 207 CTexturedLineRData::SVertex* base = reinterpret_cast<CTexturedLineRData::SVertex*>(rdata->m_VB->m_Owner->Bind()); 335 RenderTexturedOverlayLines(normalTexLineIndices, shaderTexLineNormal); 208 336 209 glVertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]); 210 glTexCoordPointer(2, GL_SHORT, stride, &base->m_UVs[0]); 337 shaderTexLineNormal->Unbind(); 211 338 212 u8* indexBase = rdata->m_VBIndices->m_Owner->Bind(); 213 glDrawElements(GL_QUAD_STRIP, rdata->m_VBIndices->m_Count, GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*rdata->m_VBIndices->m_Index); 339 // ---------------------------------------------------------------------------------------- 214 340 215 g_Renderer.GetStats().m_OverlayTris += rdata->m_VBIndices->m_Count - 2; 216 } 341 shaderTexLineAlwaysVisible->Bind(); 342 // TODO: losTex and losTransform are unused in the always visible shader, but I'm not sure if it's worthwhile messing 343 // with it just to remove these calls 344 shaderTexLineAlwaysVisible->BindTexture("losTex", los.GetTexture()); 345 shaderTexLineAlwaysVisible->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f); 346 347 RenderTexturedOverlayLines(alwaysVisibleTexLineIndices, shaderTexLineAlwaysVisible); 348 349 shaderTexLineAlwaysVisible->Unbind(); 217 350 218 shaderTexLine->Unbind();351 // ---------------------------------------------------------------------------------------- 219 352 220 353 // TODO: the shader should probably be responsible for unbinding its textures 221 354 g_Renderer.BindTexture(1, 0); … … void CTexturedLineRData::Update() 289 422 m_VBIndices = NULL; 290 423 } 291 424 425 if (m_VBStartCap) 426 { 427 g_VBMan.Release(m_VBStartCap); 428 m_VBStartCap = NULL; 429 } 430 431 if (m_VBStartCapIndices) 432 { 433 g_VBMan.Release(m_VBStartCapIndices); 434 m_VBStartCapIndices = NULL; 435 } 436 437 if (m_VBEndCap) 438 { 439 g_VBMan.Release(m_VBEndCap); 440 m_VBEndCap = NULL; 441 } 442 443 if (m_VBEndCapIndices) 444 { 445 g_VBMan.Release(m_VBEndCapIndices); 446 m_VBEndCapIndices = NULL; 447 } 448 292 449 CmpPtr<ICmpWaterManager> cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY); 293 450 294 451 std::vector<SVertex> vertices; 295 452 std::vector<u16> indices; 296 453 297 short v = 0;454 float v = 0.f; 298 455 299 size_t n = m_Line->m_Coords.size() / 2; 300 ENSURE(n >= 1);456 size_t n = m_Line->m_Coords.size() / 2; // number of line points 457 bool closed = m_Line->m_Closed; 301 458 302 CTerrain* terrain = m_Line->m_Terrain; 459 if (closed) 460 ENSURE(n >= 1); // granted, it's kind of hard to draw a closed loop through 1 point, but this is the minimum to avoid errors 461 else 462 ENSURE(n >= 2); 303 463 304 // TODO: this assumes paths are closed loops; probably should extend this to 305 // handle non-closed paths too 464 CTerrain* terrain = m_Line->m_Terrain; 306 465 307 466 // In each iteration, p1 is the position of vertex i, p0 is i-1, p2 is i+1. 308 467 // To avoid slightly expensive terrain computations we cycle these around and 309 468 // recompute p2 at the end of each iteration. 310 CVector3D p0 = CVector3D(m_Line->m_Coords[(n-1)*2], 0, m_Line->m_Coords[(n-1)*2+1]); 311 CVector3D p1 = CVector3D(m_Line->m_Coords[0], 0, m_Line->m_Coords[1]); 312 CVector3D p2 = CVector3D(m_Line->m_Coords[(1 % n)*2], 0, m_Line->m_Coords[(1 % n)*2+1]); 469 470 CVector3D p0; 471 CVector3D p1(m_Line->m_Coords[0], 0, m_Line->m_Coords[1]); 472 CVector3D p2(m_Line->m_Coords[(1 % n)*2], 0, m_Line->m_Coords[(1 % n)*2+1]); 473 474 if (closed) 475 // grab the ending point so as to close the loop 476 p0 = CVector3D(m_Line->m_Coords[(n-1)*2], 0, m_Line->m_Coords[(n-1)*2+1]); 477 else 478 // we don't want to loop around and use the direction towards the other end of the line, so create an artificial p0 that 479 // extends the p2 -> p1 direction, and use that point instead 480 p0 = p1 + (p1 - p2); 481 313 482 bool p1floating = false; 314 483 bool p2floating = false; 315 484 … … void CTexturedLineRData::Update() 341 510 { 342 511 // For vertex i, compute bisector of lines (i-1)..(i) and (i)..(i+1) 343 512 // perpendicular to terrain normal 513 // Push vertices in GL_QUAD_STRIP order 344 514 345 515 // Normal is vertical if on water, else computed from terrain 346 516 CVector3D norm; … … void CTexturedLineRData::Update() 356 526 if (fabs(l) > 0.000001f) // avoid unlikely divide-by-zero 357 527 b *= m_Line->m_Thickness / l; 358 528 359 // Raise off the terrain a little bit 360 const float raised = 0.2f; 361 362 vertices.push_back(SVertex(p1 + b + norm*raised, 0, v)); 529 vertices.push_back(SVertex(p1 + b + norm*m_Raise, 0.f, v)); 363 530 indices.push_back(vertices.size() - 1); 364 531 365 vertices.push_back(SVertex(p1 - b + norm* raised, 1, v));532 vertices.push_back(SVertex(p1 - b + norm*m_Raise, 1.f, v)); 366 533 indices.push_back(vertices.size() - 1); 367 534 368 535 // Alternate V coordinate for debugging … … void CTexturedLineRData::Update() 372 539 p0 = p1; 373 540 p1 = p2; 374 541 p1floating = p2floating; 542 543 // if in closed mode, wrap around the coordinate array for p2 -- otherwise, extend linearly 544 if (!closed && i == n-2) 545 // next iteration is the last point of the line, so create an artificial p2 that extends the p0 -> p1 direction 546 p2 = p1 + (p1 - p0); 547 else 375 548 p2 = CVector3D(m_Line->m_Coords[((i+2) % n)*2], 0, m_Line->m_Coords[((i+2) % n)*2+1]); 549 376 550 p2.Y = terrain->GetExactGroundLevel(p2.X, p2.Z); 377 551 if (p2.Y < w) 378 552 { … … void CTexturedLineRData::Update() 383 557 p2floating = false; 384 558 } 385 559 560 561 if (closed) 562 { 386 563 // Close the path 387 564 indices.push_back(0); 388 565 indices.push_back(1); 566 } 389 567 390 568 m_VB = g_VBMan.Allocate(sizeof(SVertex), vertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER); 391 569 m_VB->m_Owner->UpdateChunkVertices(m_VB, &vertices[0]); … … void CTexturedLineRData::Update() 396 574 397 575 m_VBIndices = g_VBMan.Allocate(sizeof(u16), indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER); 398 576 m_VBIndices->m_Owner->UpdateChunkVertices(m_VBIndices, &indices[0]); 577 578 if (!closed) 579 { 580 // ------------------------------------------------------------------- 581 // create start cap 582 583 std::vector<u16> startCapIndices; 584 std::vector<SVertex> startCapVertices; 585 586 CreateLineCap( 587 vertices[1].m_Position, // the order of these vertices is actually important here, swapping them produces caps at the wrong side 588 vertices[0].m_Position, 589 (Centroid(vertices[1], vertices[0]) - Centroid(vertices[3], vertices[2])).Normalized(), 590 m_Line->m_StartCap, 591 startCapVertices, 592 startCapIndices, 593 m_StartCapDrawMode 594 ); 595 596 if (!startCapVertices.empty() && !startCapIndices.empty()) 597 { 598 m_VBStartCap = g_VBMan.Allocate(sizeof(SVertex), startCapVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER); 599 m_VBStartCap->m_Owner->UpdateChunkVertices(m_VBStartCap, &startCapVertices[0]); 600 601 // Update the indices to include the base offset of the vertex data 602 for (size_t k = 0; k < startCapVertices.size(); ++k) 603 startCapIndices[k] += m_VBStartCap->m_Index; 604 605 m_VBStartCapIndices = g_VBMan.Allocate(sizeof(u16), startCapIndices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER); 606 m_VBStartCapIndices->m_Owner->UpdateChunkVertices(m_VBStartCapIndices, &startCapIndices[0]); 607 608 } 609 610 // ------------------------------------------------------------------- 611 // create end cap 612 613 std::vector<u16> endcapIndices; 614 std::vector<SVertex> endcapVertices; 615 616 CreateLineCap( 617 vertices[2*n-2].m_Position, // second-to-last line vertex 618 vertices[2*n-1].m_Position, // last line vertex 619 (Centroid(vertices[2*n-2], vertices[2*n-1]) - Centroid(vertices[2*n-4], vertices[2*n-3])).Normalized(), 620 m_Line->m_EndCap, 621 endcapVertices, 622 endcapIndices, 623 m_EndCapDrawMode 624 ); 625 626 if (!endcapVertices.empty() && !endcapIndices.empty()) 627 { 628 m_VBEndCap = g_VBMan.Allocate(sizeof(SVertex), endcapVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER); 629 m_VBEndCap->m_Owner->UpdateChunkVertices(m_VBEndCap, &endcapVertices[0]); 630 631 // Update the indices to include the base offset of the vertex data 632 for (size_t k = 0; k < endcapVertices.size(); ++k) 633 endcapIndices[k] += m_VBEndCap->m_Index; 634 635 m_VBEndCapIndices = g_VBMan.Allocate(sizeof(u16), endcapIndices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER); 636 m_VBEndCapIndices->m_Owner->UpdateChunkVertices(m_VBEndCapIndices, &endcapIndices[0]); 637 638 } 639 640 } 641 } 642 643 void CTexturedLineRData::CreateLineCap(const CVector3D& corner1, const CVector3D& corner2, const CVector3D& normal, SOverlayTexturedLine::LineCap endCapType, 644 std::vector<SVertex>& verticesOut, std::vector<u16>& indicesOut, GLenum& drawModeOut) 645 { 646 647 if (endCapType == SOverlayTexturedLine::LINECAP_FLAT) 648 return; // no action needed, this is the default 649 650 CTerrain* terrain = m_Line->m_Terrain; 651 CmpPtr<ICmpWaterManager> cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY); 652 653 // when not in closed mode, we've created artificial points for the start- and endpoints that extend the line in the 654 // direction of the first and the last segment, respectively. Thus, we know both the start and endpoints have perpendicular 655 // butt endings, i.e. the end corner vertices on either side of the line extend perpendicularly from the segment direction. 656 // That is to say, we will have something like 657 // . 658 // this: and not like this: /| 659 // ____. / | 660 // | / . 661 // | / 662 // ____. / 663 // 664 665 int roundCapPoints = 8; // = how many points to sample along the semicircle for rounded caps (including corner points) 666 float radius = m_Line->m_Thickness; 667 float texCenterU = 0.5f; // U coordinate of butt end centroid 668 float texCenterV = 0.5f; // V coordinate of butt end centroid 669 670 CVector3D centerPoint = (corner1 + corner2) * 0.5f; // middle between last two vertices (i.e. "butt" corner points) 671 /*centerPoint.X = (corner1.X + corner2.X)/2.f; 672 centerPoint.Y = (corner1.Y + corner2.Y)/2.f; 673 centerPoint.Z = (corner1.Z + corner2.Z)/2.f;*/ 674 675 switch (endCapType) 676 { 677 678 case SOverlayTexturedLine::LINECAP_SHARP: 679 { 680 roundCapPoints = 3; // creates only one point directly ahead 681 radius *= 1.2f; // make it a bit sharper (note that we don't use this for the butt corner points so it should be ok) 682 texCenterU = 0.485f; // slight visual correction to make the texture edges match up better (they're slightly too thin with only 3 points and a U coord of 0.5f) 683 } 684 case SOverlayTexturedLine::LINECAP_ROUND: 685 { 686 687 // TODO: this currently uses a complicated method where it grabs the points across the arc of the rounded cap, and then 688 // fetches the height of the map at that point. The idea was to make it "stick" to the terrain better, but it turns out 689 // to look bad on funky terrain. It'd probably be better to adopt the same method the square line caps use, and just 690 // extend the line plane. However, that requires rotating the (centerPoint -> cornerPoint) vector along an arbitrary 3D axis, 691 // and I'm not sure how to do that yet. (--vts) 692 693 drawModeOut = GL_TRIANGLE_FAN; 694 695 // depending on whether we start rotating from the (center -> corner1) or (center -> corner2) vector, 696 // this angle needs to be inverted. For the (center -> corner1) vector, apparently we need to use the positive angle; 697 // there's probably some reason for why that one is the positive one and not vice-versa, but I don't want to be bothered 698 // to find out why as it probably has to do with right-hand-rules and whatnot. 699 float stepAngle = (float)(M_PI/(roundCapPoints-1)); 700 701 // add center point vertex 702 CVector2D centerPoint2D(centerPoint.X, centerPoint.Z); 703 704 verticesOut.push_back(SVertex(centerPoint, texCenterU, texCenterV)); 705 indicesOut.push_back(verticesOut.size() - 1); 706 707 // rotate a 2D unit vector from center to one of the butt corner points stepwise 180 degrees to sample semicircular points, then 708 // multiply by the radius. For each resulting point, grab the actual terrain height. To prevent visual glitches, we reuse the two 709 // butt corner vertices directly instead of recomputing them as part of the semicircle. 710 CVector2D baseVector((corner2 - centerPoint).X, (corner2 - centerPoint).Z); 711 baseVector.Normalize(); 712 713 // connect to first butt corner point 714 verticesOut.push_back(SVertex(corner2, 0.f, 0.f)); 715 indicesOut.push_back(verticesOut.size() - 1); 716 717 //for (int i=0; i<roundCapPoints; i++) 718 for (int i=1; i<roundCapPoints-1; i++) // note: excludes the first and last iteration, since we're adding those points manually 719 { 720 CVector2D currentVector = baseVector.Rotated(i * stepAngle); 721 722 // 2D sample point location in absolute world space 723 CVector2D worldPos2D = centerPoint2D + (currentVector * radius); 724 725 // now grab the 3D location 726 CVector3D norm; 727 float groundLevel = terrain->GetExactGroundLevel(worldPos2D.X, worldPos2D.Y); 728 float w = cmpWaterManager->GetExactWaterLevel(worldPos2D.X, worldPos2D.Y); 729 730 // we want this to float on water, so if the actual terrain ground level is under water, then we artificially raise it 731 // to the water level 732 if (groundLevel < w) 733 { 734 norm = CVector3D(0, 1, 0); 735 groundLevel = w; 736 } 737 else 738 norm = m_Line->m_Terrain->CalcExactNormal(worldPos2D.X, worldPos2D.Y); 739 740 CVector3D worldPos3D(worldPos2D.X, groundLevel, worldPos2D.Y); 741 worldPos3D += norm * m_Raise; 742 743 // 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 744 // around the edge of the semicircle) 745 float u = 0.f; 746 float v = clamp((i/(float)(roundCapPoints-1)), 0.f, 1.f); 747 verticesOut.push_back(SVertex(worldPos3D, u, v)); // pos, u, v 748 indicesOut.push_back(verticesOut.size() - 1); 749 } 750 751 // connect back to the other butt corner point to complete the semicircle 752 verticesOut.push_back(SVertex(corner1, 0.f, 1.f)); 753 indicesOut.push_back(verticesOut.size() - 1); 754 755 } 756 break; 757 758 case SOverlayTexturedLine::LINECAP_SQUARE: 759 { 760 // extend the (corner1 -> corner2) vector along the direction normal and draw a triangle fan with 3 triangles 761 // NOTE: the order in which the vertices are pushed out determines the visibility of the rendered fan, as they 762 // are rendered only one-sided; the wrong order of vertices will only make the fan visible from the bottom. 763 764 drawModeOut = GL_TRIANGLE_FAN; 765 766 // push center point 767 verticesOut.push_back(SVertex(centerPoint, texCenterU, texCenterV)); 768 indicesOut.push_back(verticesOut.size() - 1); 769 770 // push butt corner point 2 771 verticesOut.push_back(SVertex(corner2, 0.f, 0.f)); 772 indicesOut.push_back(verticesOut.size() - 1); 773 774 // extend butt corner point 2 along the normal vector 775 verticesOut.push_back(SVertex(corner2 + (normal * (m_Line->m_Thickness)), 0.f, 0.333f)); 776 indicesOut.push_back(verticesOut.size() - 1); 777 778 // extend butt corner point 1 along the normal vector 779 verticesOut.push_back(SVertex(corner1 + (normal * (m_Line->m_Thickness)), 0.f, 0.666f)); 780 indicesOut.push_back(verticesOut.size() - 1); 781 782 // push butt corner point 1 783 verticesOut.push_back(SVertex(corner1, 0.f, 1.0f)); 784 indicesOut.push_back(verticesOut.size() - 1); 785 786 } 787 break; 788 789 default: 790 break; 791 792 } 793 399 794 } -
source/renderer/OverlayRenderer.h
diff --git a/source/renderer/OverlayRenderer.h b/source/renderer/OverlayRenderer.h index a48ae0a..7845cb4 100644
a b 1 /* Copyright (C) 201 0Wildfire Games.1 /* Copyright (C) 2011 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … 18 18 #ifndef INCLUDED_OVERLAYRENDERER 19 19 #define INCLUDED_OVERLAYRENDERER 20 20 21 #include "graphics/ShaderManager.h" 22 21 23 struct SOverlayLine; 22 24 struct SOverlayTexturedLine; 23 25 struct SOverlaySprite; … … public: 84 86 void RenderForegroundOverlays(const CCamera& viewCamera); 85 87 86 88 private: 89 90 /** 91 * Helper method; renders the overlay lines currently registered in the internals (i.e. in m->texlines) in the order 92 * specified by @p indices. Used for splitting the textured overlay lines by their visibility status, and then batch 93 * rendering them with the right shader by passing the correct indices to this method. 94 */ 95 void RenderTexturedOverlayLines(const std::vector<size_t>& indices, CShaderProgramPtr shader); 96 97 private: 87 98 OverlayRendererInternals* m; 88 99 }; 89 100 -
source/renderer/RenderModifiers.h
diff --git a/source/renderer/RenderModifiers.h b/source/renderer/RenderModifiers.h index 01bc887..ecf8930 100644
a b 1 /* Copyright (C) 20 09Wildfire Games.1 /* Copyright (C) 2011 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … private: 137 137 /** 138 138 * Class RenderModifierRenderer: Interface to a model renderer that can render 139 139 * its models via a RenderModifier that sets up fragment stages. 140 * TODO: this appears to be currently unused, should be considered for deletion 140 141 */ 141 142 class RenderModifierRenderer : public ModelRenderer 142 143 { -
source/scriptinterface/ScriptInterface.cpp
diff --git a/source/scriptinterface/ScriptInterface.cpp b/source/scriptinterface/ScriptInterface.cpp index c175890..c2daa21 100644
a b JSBool logmsg(JSContext* cx, uintN argc, jsval* vp) 326 326 return JS_TRUE; 327 327 } 328 328 329 JSBool logmsgrender(JSContext* cx, uintN argc, jsval* vp) 330 { 331 if (argc < 1) 332 { 333 JS_SET_RVAL(cx, vp, JSVAL_VOID); 334 return JS_TRUE; 335 } 336 337 std::wstring str; 338 if (!ScriptInterface::FromJSVal(cx, JS_ARGV(cx, vp)[0], str)) 339 return JS_FALSE; 340 LOGMESSAGERENDER(L"%ls", str.c_str()); 341 JS_SET_RVAL(cx, vp, JSVAL_VOID); 342 return JS_TRUE; 343 } 344 329 345 JSBool warn(JSContext* cx, uintN argc, jsval* vp) 330 346 { 331 347 if (argc < 1) … … ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const sh 505 521 506 522 JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); 507 523 JS_DefineFunction(m_cx, m_glob, "log", ::logmsg, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); 524 JS_DefineFunction(m_cx, m_glob, "logr", ::logmsgrender, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); 508 525 JS_DefineFunction(m_cx, m_glob, "warn", ::warn, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); 509 526 JS_DefineFunction(m_cx, m_glob, "error", ::error, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); 510 527 JS_DefineFunction(m_cx, m_glob, "deepcopy", ::deepcopy, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); -
source/simulation2/TypeList.h
diff --git a/source/simulation2/TypeList.h b/source/simulation2/TypeList.h index a7ac94b..795a586 100644
a b COMPONENT(Position) // must be before VisualActor 114 114 INTERFACE(ProjectileManager) 115 115 COMPONENT(ProjectileManager) 116 116 117 INTERFACE(RallyPoint) 118 COMPONENT(RallyPoint) 119 117 120 INTERFACE(RangeManager) 118 121 COMPONENT(RangeManager) 119 122 -
new file source/simulation2/components/CCmpRallyPoint.cpp
diff --git a/source/simulation2/components/CCmpRallyPoint.cpp b/source/simulation2/components/CCmpRallyPoint.cpp new file mode 100644 index 0000000..a259346
- + 1 /* Copyright (C) 2011 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "precompiled.h" 19 20 #include "simulation2/system/Component.h" 21 #include "ICmpRallyPoint.h" 22 23 #include "simulation2/MessageTypes.h" 24 #include "simulation2/helpers/Render.h" 25 #include "simulation2/helpers/Geometry.h" 26 27 #include "simulation2/components/ICmpPosition.h" 28 #include "simulation2/components/ICmpPathFinder.h" 29 #include "simulation2/components/ICmpFootprint.h" 30 #include "simulation2/components/ICmpRangeManager.h" 31 #include "simulation2/components/ICmpPlayer.h" 32 #include "simulation2/components/ICmpOwnership.h" 33 #include "simulation2/components/ICmpTerrain.h" 34 #include "simulation2/components/ICmpPlayerManager.h" 35 #include "simulation2/components/ICmpPlayer.h" 36 #include "simulation2/components/ICmpObstructionManager.h" 37 38 #include "ps/CLogger.h" 39 #include "graphics/Overlay.h" 40 #include "graphics/Terrain.h" 41 #include "graphics/TextureManager.h" 42 #include "maths/Fixed.h" 43 #include "maths/MathUtil.h" 44 #include "maths/Matrix3D.h" 45 #include "maths/Vector2D.h" 46 #include "maths/Vector3D.h" 47 #include "renderer/Scene.h" 48 #include "renderer/Renderer.h" 49 #include "renderer/TerrainOverlay.h" 50 51 struct SVisibilitySegment 52 { 53 bool m_Visible; 54 size_t m_StartIndex; 55 size_t m_EndIndex; // inclusive 56 57 SVisibilitySegment(bool visible, size_t startIndex, size_t endIndex) 58 : m_Visible(visible), m_StartIndex(startIndex), m_EndIndex(endIndex) 59 {} 60 61 bool operator==(const SVisibilitySegment& other) const 62 { 63 return (m_Visible == other.m_Visible && m_StartIndex == other.m_StartIndex && m_EndIndex == other.m_EndIndex); 64 } 65 66 bool operator!=(const SVisibilitySegment& other) const 67 { 68 return !(*this == other); 69 } 70 71 bool IsSinglePoint() 72 { 73 return (m_StartIndex == m_EndIndex); 74 } 75 }; 76 77 class CCmpRallyPoint : public ICmpRallyPoint 78 { 79 // import some types for less verbosity 80 typedef ICmpPathfinder::Waypoint Waypoint; 81 typedef ICmpPathfinder::Path Path; 82 typedef ICmpPathfinder::Goal Goal; 83 typedef ICmpRangeManager::CLosQuerier CLosQuerier; 84 typedef SOverlayTexturedLine::LineCap LineCap; 85 86 public: 87 static void ClassInit(CComponentManager& componentManager) 88 { 89 componentManager.SubscribeToMessageType(MT_RenderSubmit); 90 componentManager.SubscribeToMessageType(MT_OwnershipChanged); 91 componentManager.SubscribeToMessageType(MT_TurnStart); 92 // TODO: should probably also listen to movement messages 93 } 94 95 DEFAULT_COMPONENT_ALLOCATOR(RallyPoint) 96 97 protected: 98 99 /// Actual position of the rally point (in fixed-point world coordinates for stability) 100 RallyPointPosition m_RallyPoint; 101 /// Full path to the rally point as returned by the pathfinder, with some post-processing applied to reduce zig/zagging. 102 //Path m_FullPath; 103 std::vector<CVector2D> m_FullPath; 104 std::deque<SVisibilitySegment> m_VisibilitySegments; 105 106 bool m_Displayed; ///< Should we render the rally point and its path line? (set from JS when e.g. the unit is selected/deselected) 107 bool m_SmoothPath; ///< Smooth the path before rendering? 108 109 entity_id_t m_MarkerEntity; ///< Entity ID of the rally point marker. Allocated when first displayed. 110 std::wstring m_MarkerTemplate; ///< Template name of the rally point marker. 111 112 /// Marker connector line settings (loaded from XML) 113 float m_LineThickness; 114 CColor m_LineColor; 115 CColor m_LineDashColor; 116 LineCap m_LineStartCap; 117 LineCap m_LineEndCap; 118 std::wstring m_LineTexturePath; 119 std::wstring m_LineTextureMaskPath; 120 121 CTexturePtr m_Texture; 122 CTexturePtr m_TextureMask; 123 124 //SOverlayTexturedLine* m_TexturedOverlayLine; 125 ///< Textured overlay lines for the marker line. There can be multiple because we need to do dashes inside the SoD. 126 std::vector<SOverlayTexturedLine> m_TexturedOverlayLines; 127 128 /// Draw little overlay circles to indicate where the exact path points are? 129 bool m_EnableDebugNodeOverlay; 130 std::vector<SOverlayLine> m_DebugNodeOverlays; 131 132 public: 133 134 CCmpRallyPoint() 135 { 136 m_Displayed = false; 137 m_SmoothPath = true; 138 m_MarkerEntity = INVALID_ENTITY; 139 m_EnableDebugNodeOverlay = false; 140 } 141 142 ~CCmpRallyPoint() 143 { 144 //if(m_TexturedOverlayLine) delete m_TexturedOverlayLine; 145 } 146 147 static std::string GetSchema() 148 { 149 return 150 "<a:component />" 151 "<a:help>Displays a rally point where created units will gather when spawned.</a:help>" 152 "<a:example>" 153 "<MarkerTemplate>special/rallypoint</MarkerTemplate>" 154 "<LineThickness>0.75</LineThickness>" 155 "<LineStartCap>round</LineStartCap>" 156 "<LineEndCap>square</LineEndCap>" 157 "<LineColour r='20' g='128' b='240'></LineColour>" 158 "</a:example>" 159 "<element name='MarkerTemplate' a:help='Template name for the rally point marker entity (typically a waypoint flag actor).'>" 160 "<text/>" 161 "</element>" 162 "<optional>" 163 "<element name='LineColour'>" 164 "<attribute name='r'>" 165 "<data type='integer'><param name='minInclusive'>0</param><param name='maxInclusive'>255</param></data>" 166 "</attribute>" 167 "<attribute name='g'>" 168 "<data type='integer'><param name='minInclusive'>0</param><param name='maxInclusive'>255</param></data>" 169 "</attribute>" 170 "<attribute name='b'>" 171 "<data type='integer'><param name='minInclusive'>0</param><param name='maxInclusive'>255</param></data>" 172 "</attribute>" 173 "</element>" 174 "</optional>" 175 "<optional>" 176 "<element name='LineStartCap'>" 177 "<choice>" 178 "<value a:help='Abrupt line ending; line endings are not closed.'>none</value>" 179 "<value a:help='Semi-circular line end cap. The texture"s left vertical edge is curved around the rim of the semicircle.'>round</value>" 180 "<value a:help='Sharp, pointy line end. The texture"s left vertical edge is mapped along the outline.'>sharp</value>" 181 "<value a:help='Square line end cap. The texture"s left vertical edge is mapped along the outline.'>square</value>" 182 "</choice>" 183 "</element>" 184 "</optional>" 185 "<optional>" 186 "<element name='LineEndCap'>" 187 "<choice>" 188 "<value a:help='Abrupt line ending; line endings are not closed.'>none</value>" 189 "<value a:help='Semi-circular line end cap. The texture"s left vertical edge is curved around the rim of the semicircle.'>round</value>" 190 "<value a:help='Sharp, pointy line end. The texture"s left vertical edge is mapped along the outline.'>sharp</value>" 191 "<value a:help='Square line end cap. The texture"s left vertical edge is mapped along the outline.'>square</value>" 192 "</choice>" 193 "</element>" 194 "</optional>" 195 "<optional>" 196 "<element name='LineThickness' a:help='Thickness of the marker line connecting the entity to the rally point marker.'>" 197 "<data type='decimal'/>" 198 "</element>" 199 "</optional>" 200 "<optional>" 201 "<element name='LineDashColour'>" 202 "<attribute name='r'>" 203 "<data type='integer'><param name='minInclusive'>0</param><param name='maxInclusive'>255</param></data>" 204 "</attribute>" 205 "<attribute name='g'>" 206 "<data type='integer'><param name='minInclusive'>0</param><param name='maxInclusive'>255</param></data>" 207 "</attribute>" 208 "<attribute name='b'>" 209 "<data type='integer'><param name='minInclusive'>0</param><param name='maxInclusive'>255</param></data>" 210 "</attribute>" 211 "</element>" 212 "</optional>"; 213 } 214 215 virtual void Init(const CParamNode& paramNode); 216 217 virtual void Deinit() 218 { 219 } 220 221 virtual void Serialize(ISerializer& UNUSED(serialize)) 222 { 223 // TODO should probably serialize the rally point location here 224 } 225 226 virtual void Deserialize(const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) 227 { 228 Init(paramNode); 229 } 230 231 virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) 232 { 233 switch (msg.GetType()) 234 { 235 case MT_RenderSubmit: 236 { 237 if(m_Displayed && IsSet()) 238 { 239 const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg); 240 RenderSubmit(msgData.collector); 241 } 242 break; 243 } 244 case MT_OwnershipChanged: 245 { 246 CreateMarker(); 247 RepositionMarker(); // the new marker doesn't have a position yet, so let's make sure to update it 248 } 249 case MT_TurnStart: 250 { 251 UpdateOverlayLines(); // check for changes to the SoD and update the overlay lines accordingly 252 } 253 } 254 } 255 256 virtual RallyPointPosition GetPosition() 257 { 258 return m_RallyPoint; 259 } 260 261 virtual void SetPosition(RallyPointPosition pos) 262 { 263 bool posChanged = (pos.X != m_RallyPoint.X || pos.Y != m_RallyPoint.Y); 264 m_RallyPoint = pos; 265 266 //LOGMESSAGERENDER(L"[entity %d] CCmpRallyPoint::SetPosition()", GetEntityId()); 267 if(posChanged) 268 { 269 RecomputeRallyPointPath(); 270 RepositionMarker(); 271 } 272 } 273 274 virtual void Unset() 275 { 276 SetPosition(RallyPointPosition()); // reset to zero 277 } 278 279 bool IsSet() 280 { 281 return !m_RallyPoint.IsZero(); 282 } 283 284 virtual void SetDisplayed(bool displayed) 285 { 286 //LOGMESSAGERENDER(L"[entity %d] CCmpRallyPoint::SetDisplayed(%s)", GetEntityId(), displayed ? L"true" : L"false"); 287 bool displayChanged = (m_Displayed != displayed); 288 m_Displayed = displayed; 289 290 if(displayChanged) 291 { 292 RepositionMarker(); // move the marker out of oblivion and back into the real world, or vice-versa 293 UpdateOverlayLines(); // check for changes to the SoD and update the overlay lines accordingly 294 } 295 } 296 297 private: 298 299 /** 300 * (Re)creates the rally point marker entity. Called upon initialization and whenever the ownership of this entity changes, as the marker 301 * depends on the owner's civilization. If a marker entity already exists, it will be destroyed first. 302 */ 303 void CreateMarker(); 304 305 /** 306 * Repositions the rally point marker; moves it outside of the world (ie. hides it), or positions it at the rally point. Executed 307 * whenever either the position of the rally point changes (including whether it is set or not), or the display flag changes. 308 * 309 * TODO: this is executed from JS-executed methods, which must not fail under any circumstances. We'd probably have to change this to 310 * detect the changes in the rendering code rather than execute them directly here. 311 **/ 312 void RepositionMarker(); 313 314 /** 315 * Recomputes the full path from this entity to the rally point, and does all the necessary post-processing to make it prettier. 316 * Called whenever the rally point position changes. 317 */ 318 void RecomputeRallyPointPath(); 319 320 /** 321 * Sets up the overlay lines for rendering according to the current full path and visibility segments. Does all the necessary splitting 322 * of the line into solid and dashed pieces (for the SoD). Should be called whenever the SoD has changed. If no full path is currently 323 * set, this method does nothing. 324 */ 325 void ConstructOverlayLines(); 326 327 /** 328 * Checks for changes to the SoD to the previously saved state, and reconstructs the overlay lines to match if necessary. Does nothing 329 * if the rally point line is not currently set to be displayed, so that we don't waste compute cycles trying to update rally point 330 * overlays for every building all the time if they're not even gonna be visible anyway. 331 */ 332 void UpdateOverlayLines(); 333 334 /** 335 * Removes waypoints from m_rallyPointPath that are obstructed by the originating building's footprint, and links up the rally point path 336 * nicely to the edge of the building's footprint. Only needed if the pathfinder can possibly return obstructed tile waypoints, i.e. when 337 * pathfinding is started from an obstructed tile. 338 */ 339 void FixFootprintWaypoints(std::vector<CVector2D>& coords, CmpPtr<ICmpPosition> cmpPosition, CmpPtr<ICmpFootprint> cmpFootprint); 340 341 /** 342 * Removes waypoints that are inside the shroud of darkness, i.e. where the player shouldn't be able to get any information about the 343 * positions of various buildings and whatnot from the rally point path. 344 */ 345 void FixInvisibleWaypoints(std::vector<CVector2D>& coords); 346 347 /** 348 * Simplifies the path by removing waypoints that lie between two points that are visible from one another. This is primarily intended to 349 * reduce some unnecessary curviness of the path; the pathfinder returns a mathematically (near-)optimal path, which will happily curve 350 * and bend to reduce costs. Visually, it doesn't make sense for a rally point path to curve and bend when it could just as well have gone 351 * in a straight line, so that's why we have this. 352 * 353 * @p coords array of path coordinates to simplify 354 * @p maxSegmentLinks if non-zero, indicates the maximum amount of consecutive node-to-node links that can be eliminated to form a single 355 * link. If this value is set to e.g. 1, then no reductions will be performed. A value of 3 means that at most 3 356 * consecutive node links will be joined into a single link. 357 */ 358 void ReduceSegmentsByVisibility(std::vector<CVector2D>& coords, unsigned maxSegmentLinks = 0); 359 360 /** 361 * Returns a list of indices of waypoints in the current path (m_FullPath) where the LOS visibility changes, ordered from building to rally 362 * point. Used to construct the overlay line segments and track changes to the shroud of darkness. 363 */ 364 void GetVisibilitySegments(std::deque<SVisibilitySegment>& out); 365 366 /** 367 * Helper function to GetVisibilitySegments, factored out for testing. Merges single-point segments with its neighbouring segments. 368 * You should not have to call this method directly. 369 */ 370 static void MergeVisibilitySegments(std::deque<SVisibilitySegment>& segments); 371 372 void RenderSubmit(SceneCollector& collector); 373 374 }; 375 376 REGISTER_COMPONENT_TYPE(RallyPoint) 377 378 void CCmpRallyPoint::Init(const CParamNode& paramNode) 379 { 380 // set some defaults 381 m_LineThickness = .3f; 382 m_LineStartCap = SOverlayTexturedLine::LINECAP_FLAT; 383 m_LineEndCap = SOverlayTexturedLine::LINECAP_FLAT; 384 m_LineTexturePath = std::wstring(L"art/textures/misc/rallypoint_line.png"); 385 m_LineTextureMaskPath = std::wstring(L"art/textures/misc/rallypoint_line_mask.png"); 386 // implicit constructor defaults for m_LineColor and m_MarkerTemplate 387 388 // --------------------------------------------------------------------------------------------- 389 // load some XML configuration data 390 391 if (paramNode.GetChild("MarkerTemplate").IsOk()) 392 m_MarkerTemplate = paramNode.GetChild("MarkerTemplate").ToString(); 393 394 if (paramNode.GetChild("LineThickness").IsOk()) 395 m_LineThickness = paramNode.GetChild("LineThickness").ToFixed().ToFloat(); 396 397 const CParamNode& lineColor = paramNode.GetChild("LineColour"); 398 if (lineColor.IsOk() && lineColor.GetChild("@r").IsOk() && lineColor.GetChild("@g").IsOk() && lineColor.GetChild("@b").IsOk()) 399 { 400 m_LineColor = CColor( 401 lineColor.GetChild("@r").ToInt()/255.f, 402 lineColor.GetChild("@g").ToInt()/255.f, 403 lineColor.GetChild("@b").ToInt()/255.f, 404 1.f 405 ); 406 } 407 408 const CParamNode& lineDashColor = paramNode.GetChild("LineDashColour"); 409 if (lineDashColor.IsOk() && lineDashColor.GetChild("@r").IsOk() && lineDashColor.GetChild("@g").IsOk() && lineDashColor.GetChild("@b").IsOk()) 410 { 411 m_LineDashColor = CColor( 412 lineDashColor.GetChild("@r").ToInt()/255.f, 413 lineDashColor.GetChild("@g").ToInt()/255.f, 414 lineDashColor.GetChild("@b").ToInt()/255.f, 415 1.f 416 ); 417 } 418 else 419 { 420 m_LineDashColor = m_LineColor; // if not specified, use the same colour as the regular line segments 421 } 422 423 if (paramNode.GetChild("LineTexture").IsOk()) 424 m_LineTexturePath = paramNode.GetChild("LineTexture").ToString(); 425 426 if (paramNode.GetChild("LineMaskTexture").IsOk()) 427 m_LineTextureMaskPath = paramNode.GetChild("LineMaskTexture").ToString(); 428 429 if (paramNode.GetChild("LineStartCap").IsOk()) 430 { 431 const std::wstring& xmlLineStartCap = paramNode.GetChild("LineStartCap").ToString(); 432 if (!SOverlayTexturedLine::StrToLineCap(xmlLineStartCap, m_LineStartCap)) 433 LOGERROR(L"Unrecognized LineStartCap value \"%s\"", xmlLineStartCap); // this shouldn't happen by virtue of the XML validation 434 } 435 436 if (paramNode.GetChild("LineEndCap").IsOk()) 437 { 438 const std::wstring& xmlLineEndCap = paramNode.GetChild("LineEndCap").ToString(); 439 if (!SOverlayTexturedLine::StrToLineCap(xmlLineEndCap, m_LineEndCap)) 440 LOGERROR(L"Unrecognized LineEndCap value \"%s\"", xmlLineEndCap); // this shouldn't happen by virtue of the XML validation 441 } 442 443 // --------------------------------------------------------------------------------------------- 444 // load some textures 445 446 CTextureProperties texturePropsBase(m_LineTexturePath); 447 texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); 448 texturePropsBase.SetMaxAnisotropy(8.f); 449 m_Texture = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase); 450 451 CTextureProperties texturePropsMask(m_LineTextureMaskPath); 452 texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); 453 texturePropsMask.SetMaxAnisotropy(8.f); 454 m_TextureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask); 455 456 // --------------------------------------------------------------------------------------------- 457 458 CreateMarker(); // TODO: evaluate how much load this puts on the entity IDs, if it's too high then we can do this on-demand) 459 460 } 461 462 void CCmpRallyPoint::CreateMarker() 463 { 464 465 CComponentManager& componentMgr = GetSimContext().GetComponentManager(); 466 467 // if a marker entity already exists, kill it first 468 if (m_MarkerEntity != INVALID_ENTITY) 469 { 470 CmpPtr<ICmpPosition> markerCmpPosition(GetSimContext(), m_MarkerEntity); 471 if (!markerCmpPosition.null()) 472 markerCmpPosition->MoveOutOfWorld(); 473 474 componentMgr.DestroyComponentsSoon(m_MarkerEntity); // queue entity for destruction 475 m_MarkerEntity = INVALID_ENTITY; // make sure any code below doesn't try to access the soon-to-be-destroyed entity anymore 476 } 477 478 // allocate a new entity for the marker 479 if (!m_MarkerTemplate.empty()) 480 { 481 m_MarkerEntity = componentMgr.AllocateNewLocalEntity(); 482 if (m_MarkerEntity != INVALID_ENTITY) 483 m_MarkerEntity = componentMgr.AddEntity(m_MarkerTemplate, m_MarkerEntity); 484 } 485 486 // TODO: in the future, when variants are working properly for actors, we should set the variant corresponding to the 487 // owning player's civilization. 488 CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), GetEntityId()); 489 if (!cmpOwnership.null()) 490 { 491 player_id_t ownerId = cmpOwnership->GetOwner(); 492 if (ownerId != INVALID_PLAYER) 493 { 494 CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSimContext(), SYSTEM_ENTITY); 495 ENSURE(!cmpPlayerManager.null()); 496 497 CmpPtr<ICmpPlayer> cmpPlayer(GetSimContext(), cmpPlayerManager->GetPlayerByID(ownerId)); 498 if (!cmpPlayer.null()) 499 { 500 //LOGMESSAGERENDER(L"[entity %d] CCmpRallyPoint: owner civilization = %s", GetEntityId(), cmpPlayer->GetCiv()); 501 } 502 } 503 } 504 505 } 506 507 void CCmpRallyPoint::RepositionMarker() 508 { 509 if (m_MarkerEntity == INVALID_ENTITY) 510 return; // there is no valid marker entity, no use trying to position it 511 512 CmpPtr<ICmpPosition> markerCmpPosition(GetSimContext(), m_MarkerEntity); 513 if (!markerCmpPosition.null()) 514 { 515 if(m_Displayed && IsSet()) 516 { 517 markerCmpPosition->JumpTo(m_RallyPoint.X, m_RallyPoint.Y); 518 } 519 else 520 { 521 markerCmpPosition->MoveOutOfWorld(); // hide it 522 } 523 } 524 } 525 526 void CCmpRallyPoint::RecomputeRallyPointPath() 527 { 528 529 m_FullPath.clear(); 530 m_VisibilitySegments.clear(); 531 532 if (!IsSet()) 533 return; // no use computing a path if the rally point isn't set 534 535 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); 536 if (cmpPosition.null() || !cmpPosition->IsInWorld()) 537 return; // no point going on if this entity doesn't have a position or is outside of the world 538 539 CmpPtr<ICmpFootprint> cmpFootprint(GetSimContext(), GetEntityId()); 540 CmpPtr<ICmpPathfinder> cmpPathFinder(GetSimContext(), SYSTEM_ENTITY); 541 //CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY); 542 543 544 // ------------------------------------------------------------------------------------------------- 545 546 entity_pos_t pathStartX = cmpPosition->GetPosition2D().X; 547 entity_pos_t pathStartY = cmpPosition->GetPosition2D().Y; 548 549 // Find a long path to the goal point -- this uses the tile-based pathfinder, which will return a 550 // list of waypoints (i.e. a Path) from the building to the goal, where each waypoint is centered 551 // at a tile. We'll have to do some post-processing on the path to get it smooth. 552 Path path; 553 std::vector<Waypoint>& waypoints = path.m_Waypoints; 554 555 Goal goal = { Goal::POINT, m_RallyPoint.X, m_RallyPoint.Y }; 556 cmpPathFinder->ComputePath( 557 pathStartX, 558 pathStartY, 559 goal, 560 cmpPathFinder->GetPassabilityClass("default"), 561 cmpPathFinder->GetCostClass("default"), 562 path 563 ); 564 565 if (path.m_Waypoints.size() < 2) 566 return; // not likely to happen, but can't hurt to check 567 568 // from here on, we choose to represent the waypoints as CVector2D floats to avoid to have to convert back and forth 569 // between fixed-point Waypoint/CFixedVector2D and various other float-based formats used by interpolation and whatnot. 570 // Since we'll only be further using these points for rendering purposes, using floats should be fine. 571 572 // make sure to add the actual goal point as the last point (the long pathfinder only finds paths to 573 // the tile closest to the goal, so we need to complete the last bit from the closest tile to the rally 574 // point itself) 575 // NOTE: the points are returned in reverse order (from the goal to the start point), so we need to 576 // actually insert it at the front of the coordinate list. Hence, we'll do this first before appending the rest of the 577 // fixed waypoints as CVector2Ds. 578 579 Waypoint& lastWaypoint = waypoints.back(); 580 if (lastWaypoint.x != goal.x || lastWaypoint.z != goal.z) 581 m_FullPath.push_back(CVector2D(goal.x.ToFloat(), goal.z.ToFloat())); 582 583 // add the rest of the waypoints 584 for (size_t i = 0; i < waypoints.size(); ++i) 585 m_FullPath.push_back(CVector2D(waypoints[i].x.ToFloat(), waypoints[i].z.ToFloat())); 586 587 // ------------------------------------------------------------------------------------------- 588 // postprocessing 589 590 // linearize the path; 591 // pass through the waypoints , averaging each waypoint with its next one except the last one. Because the path 592 // goes from the marker to this entity and we want to keep the point at the marker's exact position, loop backwards through the 593 // waypoints so that the marker waypoint is maintained. 594 // TODO: see if we can do this at the same time as the waypoint -> coord conversion above 595 for(size_t i = m_FullPath.size() - 1; i > 0; --i) 596 m_FullPath[i] = (m_FullPath[i] + m_FullPath[i-1]) / 2.0f; 597 598 // if there's a footprint, remove any points returned by the pathfinder that may be on obstructed footprint tiles 599 if (!cmpFootprint.null()) 600 FixFootprintWaypoints(m_FullPath, cmpPosition, cmpFootprint); 601 602 // eliminate some consecutive waypoints that are visible from eachother. 603 ReduceSegmentsByVisibility(m_FullPath, 6); // reduce across a maximum distance of approx. 6 tiles 604 605 //// <DEBUG> /////////////////////////////////////////////// 606 if (m_EnableDebugNodeOverlay) 607 m_DebugNodeOverlays.clear(); 608 609 if (m_EnableDebugNodeOverlay && m_SmoothPath) 610 { 611 // create separate control point overlays so we can differentiate when using smoothing (offset them a little higher from the 612 // terrain so we can still see them after the interpolated points are added) 613 for (size_t j = 0; j < m_FullPath.size(); ++j) 614 { 615 SOverlayLine overlayLine; 616 overlayLine.m_Color = CColor(1.0f, 0.0f, 0.0f, 1.0f); 617 overlayLine.m_Thickness = 2; 618 //SimRender::ConstructCircleOnGround(GetSimContext(), coords[j].X, coords[j].Y, 0.1f, overlayLine, true, 0.30f); 619 SimRender::ConstructSquareOnGround(GetSimContext(), m_FullPath[j].X, m_FullPath[j].Y, 0.2f, 0.2f, 1.0f, overlayLine, true); 620 m_DebugNodeOverlays.push_back(overlayLine); 621 } 622 } 623 //// </DEBUG> ////////////////////////////////////////////// 624 625 if (m_SmoothPath) 626 // the number of points to interpolate goes together with the maximum amount of node links allowed to be joined together by 627 // the visibility reduction. The more node links that can be joined together, the more interpolated points you probably need 628 // to generate to be able to deal with terrain height changes 629 SimRender::InterpolatePointsRNS(m_FullPath, false, 0, 8); // no offset, keep line at its exact path 630 631 // ------------------------------------------------------------------------------------------- 632 633 // find which point is the last visible point before going into the SoD, so we have a point to compare to on the next turn 634 GetVisibilitySegments(m_VisibilitySegments); 635 636 // build overlay lines for the new path 637 ConstructOverlayLines(); 638 639 } 640 641 void CCmpRallyPoint::ConstructOverlayLines() 642 { 643 644 // we need to create a new SOverlayTexturedLine every time we want to change the coordinates after having passed it to the renderer, 645 // because it does some fancy vertex buffering thing and caches them internally instead of recomputing them on every pass (which is 646 // only sensible) 647 648 m_TexturedOverlayLines.clear(); 649 650 if (m_FullPath.size() < 2) 651 return; 652 653 CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY); 654 SOverlayTexturedLine::LineCap dashesLineCap = SOverlayTexturedLine::LINECAP_ROUND; // line caps to use for the dashed segments (and any other segment's edges that border it) 655 656 // assuming the path endpoint is in the visible LOS area (which it should be, otherwise you shouldn't have been able to set the rally point), 657 // alternate between full overlay lines (using every path point) and dashed lines in between for the SoD. 658 659 //bool dashed = false; 660 //size_t endPointIdx = m_FullPath.size() - 1; 661 662 // Move through the SoD visibility edge points, and maintain a startPointIdx and endPointIdx of each dashed/non-dashed segment along the way. 663 // It's possible that there are no visibility edge points (meaning that the entire path is inside the visible area), so let's start with -1 to 664 // indicate "no current edge point" and then check each time whether a next one exists. If there is a next one, use its index as the end point 665 // for the segment we're drawing; otherwise, go all the way through to the end of the path (i.e. startPointIdx = 0). 666 // 667 // NOTE: this algorithm needed surprisingly little debugging, so I wouldn't be surprised if there're still some bugs in it. 668 //int currentEdgePointIdx = -1; 669 //while (currentEdgePointIdx < int(m_VisibilitySegments.size())) 670 for (std::deque<SVisibilitySegment>::const_iterator it = m_VisibilitySegments.begin(); it != m_VisibilitySegments.end(); ++it) 671 { 672 /*size_t startPointIdx = 0; // if there are no more visibility edge points after this, then the current segment should go right up until the end of the path (in reverse order, so node with idx 0; the rally point marker) 673 if (currentEdgePointIdx + 1 < int(m_VisibilitySegments.size())) 674 startPointIdx = m_VisibilitySegments[currentEdgePointIdx+1]; 675 676 ENSURE(startPointIdx < m_FullPath.size()); // size_t is unsigned, so >= 0 is always true 677 ENSURE(endPointIdx < m_FullPath.size());*/ 678 679 const SVisibilitySegment& segment = (*it); 680 681 if (segment.m_Visible) 682 { 683 684 // does this segment border on the building or rally point flag on either side? 685 bool bordersBuilding = (segment.m_EndIndex == m_FullPath.size() - 1); 686 bool bordersFlag = (segment.m_StartIndex == 0); 687 688 // construct solid textured overlay line along a subset of the full path points from startPointIdx to endPointIdx 689 SOverlayTexturedLine overlayLine; 690 overlayLine.m_Thickness = m_LineThickness; 691 overlayLine.m_Terrain = cmpTerrain->GetCTerrain(); 692 overlayLine.m_TextureBase = m_Texture; 693 overlayLine.m_TextureMask = m_TextureMask; 694 overlayLine.m_Color = m_LineColor; 695 overlayLine.m_Closed = false; 696 // we should take care to only use m_LineXCap for the actual end points at the building and the rally point; any intermediate 697 // end points (i.e., that border a dashed segment) should have the dashed cap 698 // the path line is actually in reverse order as well, so let's swap out the start and end caps 699 overlayLine.m_StartCap = (bordersFlag ? m_LineEndCap : dashesLineCap); 700 overlayLine.m_EndCap = (bordersBuilding ? m_LineStartCap : dashesLineCap); 701 overlayLine.m_AlwaysVisible = true; 702 703 // push overlay line coordinates 704 ENSURE(segment.m_EndIndex > segment.m_StartIndex); 705 for (size_t j = segment.m_StartIndex; j <= segment.m_EndIndex; ++j) // end index is inclusive here 706 { 707 overlayLine.m_Coords.push_back(m_FullPath[j].X); 708 overlayLine.m_Coords.push_back(m_FullPath[j].Y); 709 } 710 711 m_TexturedOverlayLines.push_back(overlayLine); 712 } 713 else 714 { 715 // construct dashed line from startPointIdx to endPointIdx, add textured overlay lines for it to the render list 716 std::vector<CVector2D> straightLine; 717 straightLine.push_back(m_FullPath[segment.m_StartIndex]); 718 straightLine.push_back(m_FullPath[segment.m_EndIndex]); 719 720 // We always want to have the dashed line end at either point with a full dash (i.e. not a cleared space), so that the dashed 721 // area is visually obvious. That implies that we want at least So, let's do some calculations to see what size we should make 722 // the dashes and clears. 723 724 float maxDashSize = 3.f; 725 float maxClearSize = 3.f; 726 727 float dashSize = maxDashSize; 728 float clearSize = maxClearSize; 729 float pairDashRatio = (dashSize / (dashSize + clearSize)); // ratio of the dash's length to a (dash + clear) pair's length 730 731 float distance = (m_FullPath[segment.m_StartIndex] - m_FullPath[segment.m_EndIndex]).Length(); // straight-line distance between the points 732 733 // see how many pairs (dash + clear) can fit into the distance unmodified. Then check the remaining distance; if it's not exactly 734 // a dash size's worth (and it likely won't be), then adjust the dash/clear sizes slightly so that it is. 735 int numFitUnmodified = floor(distance/(dashSize + clearSize)); 736 float remainderDistance = distance - (numFitUnmodified * (dashSize + clearSize)); 737 738 // Now we want to make remainderDistance equal exactly one dash size (i.e. maxDashSize) by scaling dashSize and clearSize slightly. 739 // We have (remainderDistance - maxDashSize) of space to distribute over numFitUnmodified instances of (dashSize + clearSize) to make 740 // it fit, so each (dashSize + clearSize) pair needs to adjust its length by (remainderDistance - maxDashSize)/numFitUnmodified 741 // (which will be positive or negative accordingly). This number can then be distributed further proportionally among the dash's 742 // length and the clear's length. 743 744 // we always want to have at least one dash/clear pair (i.e., "|===| |===|"); also, we need to avoid division by zero below. 745 numFitUnmodified = std::max(1, numFitUnmodified); 746 747 float pairwiseLengthDifference = (remainderDistance - maxDashSize)/numFitUnmodified; // can be either positive or negative 748 dashSize += pairDashRatio * pairwiseLengthDifference; 749 clearSize += (1 - pairDashRatio) * pairwiseLengthDifference; 750 751 // ------------------------------------------------------------------------------------------------ 752 753 SDashedLine dashedLine; 754 SimRender::ConstructDashedLine(straightLine, dashedLine, dashSize, clearSize); 755 756 // build overlay lines for dashes 757 size_t numDashes = dashedLine.m_StartIndices.size(); 758 for (size_t i=0; i < numDashes; i++) 759 { 760 SOverlayTexturedLine dashOverlay; 761 762 dashOverlay.m_Thickness = m_LineThickness; 763 dashOverlay.m_Terrain = cmpTerrain->GetCTerrain(); 764 dashOverlay.m_TextureBase = m_Texture; 765 dashOverlay.m_TextureMask = m_TextureMask; 766 dashOverlay.m_Color = m_LineDashColor; 767 dashOverlay.m_Closed = false; 768 // the path line is actually in reverse order, so let's swap out the start and end caps too 769 dashOverlay.m_StartCap = dashesLineCap; 770 dashOverlay.m_EndCap = dashesLineCap; 771 dashOverlay.m_AlwaysVisible = true; 772 // TODO: maybe adjust the elevation of the dashes to be a little lower, so that it slides underneath the actual path 773 774 size_t dashStartIndex = dashedLine.m_StartIndices[i]; 775 size_t dashEndIndex = dashedLine.GetEndIndex(i); 776 ENSURE(dashEndIndex > dashStartIndex); 777 778 for (size_t n = dashStartIndex; n < dashEndIndex; n++) 779 { 780 dashOverlay.m_Coords.push_back(dashedLine.m_Points[n].X); 781 dashOverlay.m_Coords.push_back(dashedLine.m_Points[n].Y); 782 } 783 784 m_TexturedOverlayLines.push_back(dashOverlay); 785 } 786 787 } 788 789 //dashed = !dashed; 790 //endPointIdx = startPointIdx; // don't link up at the exact same point, that's gonna leave an ugly overlap between two end caps 791 792 //++currentEdgePointIdx; 793 794 } 795 796 //// <DEBUG> ////////////////////////////////////////////// 797 if (m_EnableDebugNodeOverlay) 798 { 799 for (size_t j = 0; j < m_FullPath.size(); ++j) 800 { 801 SOverlayLine overlayLine; 802 overlayLine.m_Color = CColor(1.0f, 1.0f, 1.0f, 1.0f); 803 overlayLine.m_Thickness = 1; 804 SimRender::ConstructCircleOnGround(GetSimContext(), m_FullPath[j].X, m_FullPath[j].Y, 0.075f, overlayLine, true); 805 m_DebugNodeOverlays.push_back(overlayLine); 806 } 807 } 808 //// </DEBUG> ////////////////////////////////////////////// 809 } 810 811 void CCmpRallyPoint::UpdateOverlayLines() 812 { 813 814 // we should really only do this if the rally point is currently being displayed and set inside the world, otherwise it's a massive 815 // waste of time to calculate all this stuff every turn if there's nothing to show for it anyway 816 817 if (!m_Displayed || !IsSet()) 818 return; 819 820 // see if there have been any changes to the SoD by grabbing the visibility edge points and comparing them to the previous ones 821 std::deque<SVisibilitySegment> newVisibilitySegments; 822 GetVisibilitySegments(newVisibilitySegments); 823 824 // compare the two indices vectors; as soon as an element is different (and provided the full path hasn't changed), then the SoD 825 // has changed and we should recreate the overlay lines 826 /*bool same = (m_VisibilitySegments.size() == currentVisibilitySegments.size()); // initial check 827 if (same) 828 { 829 std::deque<SVisibilitySegment>::const_iterator oldIt = m_VisibilitySegments.begin(); 830 std::deque<SVisibilitySegment>::const_iterator newIt = currentVisibilitySegments.begin(); 831 832 while (newIt != currentVisibilitySegments.end()) 833 { 834 same = same && (*newIt == *oldIt); 835 if (!same) break; 836 837 ++newIt; 838 ++oldIt; 839 } 840 }*/ 841 bool same = (m_VisibilitySegments == newVisibilitySegments); 842 843 if (!same) 844 { 845 // the visibility segments have changed, so we want to reconstruct the overlay lines to match. Note that the path itself doesn't 846 // change, only the overlay lines we construct from them. 847 //LOGMESSAGERENDER(L"LOS visibility segments changed, updating overlays (current: %d segments, new: %d)", m_VisibilitySegments.size(), newVisibilitySegments.size()); 848 m_VisibilitySegments = newVisibilitySegments; // save the new visibility segments to compare against next time 849 ConstructOverlayLines(); 850 } 851 852 } 853 854 void CCmpRallyPoint::FixFootprintWaypoints(std::vector<CVector2D>& coords, CmpPtr<ICmpPosition> cmpPosition, CmpPtr<ICmpFootprint> cmpFootprint) 855 { 856 ENSURE(!cmpPosition.null()); 857 ENSURE(!cmpFootprint.null()); 858 859 // ----------------------------------------------------------------------------------------------------- 860 861 //std::vector<Waypoint>& waypoints = m_FullPath.m_Waypoints; // for ease of access 862 // TODO: nasty fixed/float conversions everywhere 863 864 // grab the shape and dimensions of the footprint 865 entity_pos_t footprintSize0, footprintSize1, footprintHeight; 866 ICmpFootprint::EShape footprintShape; 867 cmpFootprint->GetShape(footprintShape, footprintSize0, footprintSize1, footprintHeight); 868 869 // grab the center of the footprint 870 CFixedVector2D center = cmpPosition->GetPosition2D(); 871 872 // ----------------------------------------------------------------------------------------------------- 873 874 switch (footprintShape) 875 { 876 case ICmpFootprint::SQUARE: 877 { 878 // in this case, footprintSize0 and 1 respectively indicate the (unrotated) size along the X and Z axes 879 880 // the building's footprint could be rotated any which way, so let's get the rotation around the Y axis 881 // and the rotated unit vectors in the X/Z plane of the shape's footprint 882 // (the Footprint itself holds only the outline, the Position holds the orientation) 883 884 fixed s, c; // sine and cosine of the Y axis rotation angle (aka the yaw) 885 fixed a = cmpPosition->GetRotation().Y; 886 sincos_approx(a, s, c); 887 CFixedVector2D u(c, -s); // unit vector along the rotated X axis 888 CFixedVector2D v(s, c); // unit vector along the rotated Z axis 889 CFixedVector2D halfSize(footprintSize0/2, footprintSize1/2); 890 891 // starting from the start position, check if any points are within the footprint of the building 892 // (this is possible if the pathfinder was started from a point located within the footprint) 893 for(int i = (int)(coords.size() - 1); i >= 0; i--) 894 { 895 const CVector2D& wp = coords[i]; 896 if (Geometry::PointIsInSquare(CFixedVector2D(fixed::FromFloat(wp.X), fixed::FromFloat(wp.Y)) - center, u, v, halfSize)) 897 { 898 coords.erase(coords.begin() + i); 899 } 900 else 901 { 902 break; // point no longer inside footprint, from this point on neither will any of the following be 903 } 904 } 905 906 // add a point right on the edge of the footprint (nearest to the last waypoint) so that it links up nicely with the rest of the path 907 CFixedVector2D lastWaypoint(fixed::FromFloat(coords.back().X), fixed::FromFloat(coords.back().Y)); 908 CFixedVector2D footprintEdgePoint = Geometry::NearestPointOnSquare(lastWaypoint - center, u, v, halfSize); // relative to the shape origin (center) 909 CVector2D footprintEdge((center.X + footprintEdgePoint.X).ToFloat(), (center.Y + footprintEdgePoint.Y).ToFloat()); 910 coords.push_back(footprintEdge); 911 912 } 913 break; 914 case ICmpFootprint::CIRCLE: 915 { 916 // in this case, both footprintSize0 and 1 indicate the circle's radius 917 918 for(int i = (int)(coords.size() - 1); i >= 0; i--) 919 { 920 const CVector2D& wp = coords[i]; 921 fixed pointDistance = (CFixedVector2D(fixed::FromFloat(wp.X), fixed::FromFloat(wp.Y)) - center).Length(); 922 if (pointDistance <= footprintSize0) 923 { 924 coords.erase(coords.begin() + i); 925 } 926 else 927 { 928 break; // point no longer inside footprint, from this point on neither will any of the following be 929 } 930 } 931 932 // add a point right on the edge of the footprint so that it links up nicely with the rest of the path 933 /*CFixedVector2D radiusEdgePoint = CFixedVector2D(waypoints.back().x, waypoints.back().z) - center; 934 radiusEdgePoint.Normalize(footprintSize1); // resize vector to footprint circle radius to get point on edge in same direction 935 Waypoint footprintEdge = { center.X + radiusEdgePoint.X, center.Y + radiusEdgePoint.Y }; 936 waypoints.push_back(footprintEdge);*/ 937 938 CFixedVector2D radiusEdgePoint(fixed::FromFloat(coords.back().X), fixed::FromFloat(coords.back().Y)); 939 radiusEdgePoint.Normalize(footprintSize1); 940 CVector2D footprintEdge((center.X + radiusEdgePoint.X).ToFloat(), (center.Y + radiusEdgePoint.Y).ToFloat()); 941 coords.push_back(footprintEdge); 942 943 } 944 break; 945 } 946 947 } 948 949 void CCmpRallyPoint::FixInvisibleWaypoints(std::vector<CVector2D>& coords) 950 { 951 CmpPtr<ICmpRangeManager> cmpRangeMgr(GetSimContext(), SYSTEM_ENTITY); 952 953 player_id_t currentPlayer = GetSimContext().GetCurrentDisplayedPlayer(); 954 CLosQuerier losQuerier(cmpRangeMgr->GetLosQuerier(currentPlayer)); 955 956 //for (std::vector<Waypoint>::iterator it = waypoints.begin(); it != waypoints.end();) 957 for(std::vector<CVector2D>::iterator it = coords.begin(); it != coords.end();) 958 { 959 int i = (fixed::FromFloat(it->X) / (int)CELL_SIZE).ToInt_RoundToNearest(); 960 int j = (fixed::FromFloat(it->Y) / (int)CELL_SIZE).ToInt_RoundToNearest(); 961 962 bool explored = losQuerier.IsExplored(i, j); 963 if (!explored) 964 { 965 it = coords.erase(it); 966 } 967 else 968 { 969 it++; 970 } 971 } 972 973 } 974 975 void CCmpRallyPoint::ReduceSegmentsByVisibility(std::vector<CVector2D>& coords, unsigned maxSegmentLinks) 976 { 977 978 CmpPtr<ICmpPathfinder> cmpPathFinder(GetSimContext(), SYSTEM_ENTITY); 979 CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY); 980 981 if (coords.size() < 3) 982 return; 983 984 // the basic idea is this: starting from a base node, keep checking each individual point along the path to see if there's a visible 985 // line between it and the base point. If so, keep going, otherwise, make the last visible point the new base node and start the same 986 // process from there on until the entire line is checked. The output is the array of base nodes. 987 988 std::vector<CVector2D> newCoords; 989 StationaryObstructionFilter obstructionFilter; // TODO: is this the right one to use? 990 entity_pos_t lineRadius = fixed::FromFloat(m_LineThickness); 991 ICmpPathfinder::pass_class_t passabilityClass = cmpPathFinder->GetPassabilityClass("default"); 992 993 newCoords.push_back(coords[0]); // save the first base node 994 995 size_t baseNodeIdx = 0; 996 size_t curNodeIdx = 1; 997 998 // kind of ugly, but prevents code duplication below 999 #define UPDATE_BASENODE_XYZ() STMT(baseNodeX = fixed::FromFloat(coords[baseNodeIdx].X);\ 1000 baseNodeZ = fixed::FromFloat(coords[baseNodeIdx].Y);\ 1001 baseNodeY = cmpTerrain->GetExactGroundLevel(coords[baseNodeIdx].X, coords[baseNodeIdx].Y);) 1002 1003 float baseNodeY; 1004 entity_pos_t baseNodeX; 1005 entity_pos_t baseNodeZ; 1006 1007 UPDATE_BASENODE_XYZ(); // set initial base node coords 1008 1009 while (curNodeIdx < coords.size()) 1010 { 1011 ENSURE(curNodeIdx > baseNodeIdx); // this needs to be true at all times, otherwise we're checking visibility between a point and itself 1012 1013 entity_pos_t curNodeX = fixed::FromFloat(coords[curNodeIdx].X); 1014 entity_pos_t curNodeZ = fixed::FromFloat(coords[curNodeIdx].Y); 1015 float curNodeY = cmpTerrain->GetExactGroundLevel(coords[curNodeIdx].X, coords[curNodeIdx].Y); 1016 1017 // find out whether curNode is visible from baseNode (careful; this is in 2D only; terrain height differences are ignored!) 1018 bool curNodeVisible = cmpPathFinder->CheckMovement(obstructionFilter, baseNodeX, baseNodeZ, curNodeX, curNodeZ, lineRadius, passabilityClass); 1019 1020 // since height differences are ignored by CheckMovement, let's call two points visible from one another only if they're at roughly the same terrain elevation 1021 curNodeVisible = curNodeVisible && fabsf(curNodeY - baseNodeY) < 3.f; // TODO: this could probably use some tuning 1022 if (maxSegmentLinks > 0) 1023 curNodeVisible = curNodeVisible && ((curNodeIdx - baseNodeIdx) <= maxSegmentLinks); // max. amount of node-to-node links to be eliminated (unsigned subtraction is valid because curNodeIdx is always > baseNodeIdx) 1024 1025 if (!curNodeVisible) 1026 { 1027 // current node is not visible from the base node, so the previous one was the last visible point from baseNode and should 1028 // hence become the new base node for further iterations. 1029 1030 // if curNodeIdx is adjacent to the current baseNode (which is possible due to steep height differences, e.g. hills), then 1031 // we should take care not to stay stuck at the current base node 1032 if (curNodeIdx > baseNodeIdx + 1) 1033 { 1034 baseNodeIdx = curNodeIdx - 1; 1035 } 1036 else 1037 { 1038 // curNodeIdx == baseNodeIdx + 1 1039 baseNodeIdx = curNodeIdx; 1040 curNodeIdx++; // move the next candidate node one forward so that we don't test a point against itself in the next iteration 1041 } 1042 1043 newCoords.push_back(coords[baseNodeIdx]); // add new base node to output list 1044 1045 UPDATE_BASENODE_XYZ(); 1046 1047 } 1048 1049 curNodeIdx++; 1050 1051 } 1052 1053 #undef UPDATE_BASENODE_XYZ 1054 1055 // we always need to add the last point back to the array; if e.g. all the points up to the last one are all visible from the current 1056 // base node, then the loop above just ends and no endpoint is ever added to the list. 1057 ENSURE(curNodeIdx == coords.size()); 1058 newCoords.push_back(coords[coords.size() - 1]); 1059 1060 coords.swap(newCoords); 1061 1062 } 1063 1064 void CCmpRallyPoint::GetVisibilitySegments(std::deque<SVisibilitySegment>& out) 1065 { 1066 1067 out.clear(); 1068 1069 if (m_FullPath.size() < 2) 1070 return; 1071 1072 CmpPtr<ICmpRangeManager> cmpRangeMgr(GetSimContext(), SYSTEM_ENTITY); 1073 1074 player_id_t currentPlayer = GetSimContext().GetCurrentDisplayedPlayer(); 1075 CLosQuerier losQuerier(cmpRangeMgr->GetLosQuerier(currentPlayer)); 1076 1077 // temporary storage for the raw segments (we need to do some post-processing before writing to @p out) 1078 //std::vector<SVisibilitySegment> workingSegments; 1079 1080 // go through the path node list, comparing each node's visibility with the previous one. If it changes, end the current segment and start 1081 // a new one at the next point. 1082 1083 bool lastVisible = losQuerier.IsExplored( 1084 (fixed::FromFloat(m_FullPath[0].X) / (int) CELL_SIZE).ToInt_RoundToNearest(), 1085 (fixed::FromFloat(m_FullPath[0].Y) / (int) CELL_SIZE).ToInt_RoundToNearest() 1086 ); 1087 size_t curSegmentStartIndex = 0; // starting node index of the current segment 1088 1089 for (size_t k = 1; k < m_FullPath.size(); ++k) 1090 { 1091 // grab tile indices for this coord 1092 int i = (fixed::FromFloat(m_FullPath[k].X) / (int)CELL_SIZE).ToInt_RoundToNearest(); 1093 int j = (fixed::FromFloat(m_FullPath[k].Y) / (int)CELL_SIZE).ToInt_RoundToNearest(); 1094 1095 bool nodeVisible = losQuerier.IsExplored(i, j); 1096 if (nodeVisible != lastVisible) 1097 { 1098 // visibility changed; write out the segment that was just completed and get ready for the new one 1099 out.push_back(SVisibilitySegment(lastVisible, curSegmentStartIndex, k - 1)); 1100 1101 //curSegmentStartIndex = k; // new segment starts here 1102 curSegmentStartIndex = k - 1; 1103 lastVisible = nodeVisible; 1104 } 1105 1106 } 1107 1108 // terminate the last segment 1109 out.push_back(SVisibilitySegment(lastVisible, curSegmentStartIndex, m_FullPath.size() - 1)); 1110 1111 //ENSURE(out[0].m_StartIndex == 0); 1112 //ENSURE(out[out.size()-1].m_EndIndex == m_FullPath.size()-1); 1113 1114 // --------------------------------------------------------------------------------------------------- 1115 MergeVisibilitySegments(out); 1116 1117 } 1118 1119 void CCmpRallyPoint::MergeVisibilitySegments(std::deque<SVisibilitySegment>& segments) 1120 { 1121 // now scan for single-point segments; if they are inbetween two other segments, delete it and merge the surrounding segments; 1122 // if they're at either end of the path, include them in their bordering segment (but only if those bordering segments aren't also themselves 1123 // single-point segments, because then we would want those to get absorbed by its surrounding ones). 1124 1125 // first scan for absorptions of single-point surrounded segments (i.e. excluding edge segments) 1126 size_t numSegments = segments.size(); 1127 1128 // YE BE WARNED: HERE BE FOR LOOP TRICKERY 1129 for (size_t i = 1; i < numSegments - 1;) 1130 { 1131 SVisibilitySegment& segment = segments[i]; 1132 if (segment.IsSinglePoint()) 1133 { 1134 ENSURE(segments[i-1].m_Visible == segments[i+1].m_Visible); // since the segments' visibility alternates, the surrounding ones should have the same visibility 1135 segments[i-1].m_EndIndex = segments[i+1].m_EndIndex; // make previous segment span all the way across to the next 1136 segments.erase(segments.begin() + i); // erase this segment ... 1137 segments.erase(segments.begin() + i); // and the next (note; since we removed [i], [i+1] is now at position [i]) 1138 numSegments -= 2; // we removed 2 segments, so update the loop condition 1139 // in the next iteration, i should still point to this index (which will now be the index of the segment right after the one that got expanded), 1140 // so don't increment i here 1141 } 1142 else 1143 { 1144 ++i; 1145 } 1146 } 1147 1148 ENSURE(numSegments == segments.size()); 1149 1150 // check to see if the first segment needs to be merged with its neighbour 1151 if (segments.size() >= 2 && segments[0].IsSinglePoint()) 1152 { 1153 int firstSegmentStartIndex = segments.front().m_StartIndex; 1154 ENSURE(firstSegmentStartIndex == 0); 1155 ENSURE(!segments[1].IsSinglePoint()); // at this point, the second segment should never be a single-point segment 1156 1157 segments.erase(segments.begin()); 1158 segments.front().m_StartIndex = firstSegmentStartIndex; 1159 1160 } 1161 1162 // check to see if the last segment needs to be merged with its neighbour 1163 if (segments.size() >= 2 && segments[segments.size()-1].IsSinglePoint()) 1164 { 1165 int lastSegmentEndIndex = segments.back().m_EndIndex; 1166 ENSURE(!segments[segments.size()-2].IsSinglePoint()); // at this point, the second-to-last segment should never be a single-point segment 1167 1168 segments.erase(segments.end()); 1169 segments.back().m_EndIndex = lastSegmentEndIndex; 1170 } 1171 1172 // -------------------------------------------------------------------------------------------------------- 1173 // at this point, every segment should have at least 2 points 1174 for (size_t i = 0; i < segments.size(); ++i) 1175 { 1176 ENSURE(!segments[i].IsSinglePoint()); 1177 ENSURE(segments[i].m_EndIndex > segments[i].m_StartIndex); 1178 } 1179 1180 } 1181 1182 void CCmpRallyPoint::RenderSubmit(SceneCollector& collector) 1183 { 1184 1185 // we only get here if the rally point is set and should be displayed 1186 for (size_t i = 0; i < m_TexturedOverlayLines.size(); ++i) 1187 { 1188 if (!m_TexturedOverlayLines[i].m_Coords.empty()) 1189 collector.Submit(&m_TexturedOverlayLines[i]); 1190 } 1191 1192 if (m_EnableDebugNodeOverlay && !m_DebugNodeOverlays.empty()) 1193 { 1194 for (size_t i = 0; i < m_DebugNodeOverlays.size(); i++) 1195 collector.Submit(&m_DebugNodeOverlays[i]); 1196 } 1197 1198 } -
source/simulation2/components/CCmpSelectable.cpp
diff --git a/source/simulation2/components/CCmpSelectable.cpp b/source/simulation2/components/CCmpSelectable.cpp index e9aa3d2..27bb2a6 100644
a b 1 /* Copyright (C) 201 0Wildfire Games.1 /* Copyright (C) 2011 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … 22 22 23 23 #include "ICmpPosition.h" 24 24 #include "ICmpFootprint.h" 25 #include "ICmpVisual.h" 25 26 #include "simulation2/MessageTypes.h" 26 27 #include "simulation2/helpers/Render.h" 27 28 … … public: 155 156 // (This is only called if a > 0) 156 157 157 158 collector.Submit(&m_Overlay); 159 160 // TODO: use this to draw debug overlays for the bounding boxes 161 /*CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), GetEntityId()); 162 if (!cmpVisual.null()) 163 { 164 CBound bound = cmpVisual->GetBounds(); 165 CVector3D tmin = bound[0]; 166 CVector3D tmax = bound[1]; 167 168 static SOverlayLine* overlayLine = new SOverlayLine(); 169 overlayLine->m_Thickness = 2; 170 overlayLine->m_Color = CColor(1.0, 0.0, 0.0, 1.0); 171 overlayLine->m_Coords.clear(); 172 // floor square 173 overlayLine->m_Coords.push_back(tmin.X); overlayLine->m_Coords.push_back(tmin.Y); overlayLine->m_Coords.push_back(tmin.Z); 174 overlayLine->m_Coords.push_back(tmax.X); overlayLine->m_Coords.push_back(tmin.Y); overlayLine->m_Coords.push_back(tmin.Z); 175 overlayLine->m_Coords.push_back(tmax.X); overlayLine->m_Coords.push_back(tmin.Y); overlayLine->m_Coords.push_back(tmax.Z); 176 overlayLine->m_Coords.push_back(tmin.X); overlayLine->m_Coords.push_back(tmin.Y); overlayLine->m_Coords.push_back(tmax.Z); 177 overlayLine->m_Coords.push_back(tmin.X); overlayLine->m_Coords.push_back(tmin.Y); overlayLine->m_Coords.push_back(tmin.Z); 178 // roof square 179 overlayLine->m_Coords.push_back(tmin.X); overlayLine->m_Coords.push_back(tmax.Y); overlayLine->m_Coords.push_back(tmin.Z); 180 overlayLine->m_Coords.push_back(tmax.X); overlayLine->m_Coords.push_back(tmax.Y); overlayLine->m_Coords.push_back(tmin.Z); 181 overlayLine->m_Coords.push_back(tmax.X); overlayLine->m_Coords.push_back(tmax.Y); overlayLine->m_Coords.push_back(tmax.Z); 182 overlayLine->m_Coords.push_back(tmin.X); overlayLine->m_Coords.push_back(tmax.Y); overlayLine->m_Coords.push_back(tmax.Z); 183 overlayLine->m_Coords.push_back(tmin.X); overlayLine->m_Coords.push_back(tmax.Y); overlayLine->m_Coords.push_back(tmin.Z); 184 // supports 185 collector.Submit(overlayLine); 186 }*/ 187 158 188 } 159 189 }; 160 190 -
source/simulation2/components/CCmpTerritoryManager.cpp
diff --git a/source/simulation2/components/CCmpTerritoryManager.cpp b/source/simulation2/components/CCmpTerritoryManager.cpp index e000f34..8e131f2 100644
a b public: 103 103 104 104 TerritoryOverlay* m_DebugOverlay; 105 105 106 bool m_EnableLineDebugOverlays; ///< Enable node debugging overlays for boundary lines? 107 std::vector<SOverlayLine> m_DebugBoundaryLineNodes; 108 106 109 virtual void Init(const CParamNode& UNUSED(paramNode)) 107 110 { 108 111 m_Territories = NULL; … … public: 110 113 // m_DebugOverlay = new TerritoryOverlay(*this); 111 114 m_BoundaryLinesDirty = true; 112 115 m_TriggerEvent = true; 113 116 m_EnableLineDebugOverlays = false; 114 117 m_DirtyID = 1; 115 118 116 119 m_AnimTime = 0.0; … … void CCmpTerritoryManager::UpdateBoundaryLines() 742 745 PROFILE("update boundary lines"); 743 746 744 747 m_BoundaryLines.clear(); 748 m_DebugBoundaryLineNodes.clear(); 745 749 746 750 std::vector<CCmpTerritoryManager::TerritoryBoundary> boundaries = ComputeBoundaries(); 747 751 … … void CCmpTerritoryManager::UpdateBoundaryLines() 777 781 m_BoundaryLines.push_back(SBoundaryLine()); 778 782 m_BoundaryLines.back().connected = boundaries[i].connected; 779 783 m_BoundaryLines.back().color = color; 780 781 784 m_BoundaryLines.back().overlay.m_Terrain = terrain; 782 785 m_BoundaryLines.back().overlay.m_TextureBase = textureBase; 783 786 m_BoundaryLines.back().overlay.m_TextureMask = textureMask; 784 787 m_BoundaryLines.back().overlay.m_Color = color; 785 788 m_BoundaryLines.back().overlay.m_Thickness = m_BorderThickness; 789 m_BoundaryLines.back().overlay.m_Closed = true; 786 790 787 SimRender::SmoothPointsAverage(boundaries[i].points, true);791 SimRender::SmoothPointsAverage(boundaries[i].points, m_BoundaryLines.back().overlay.m_Closed); 788 792 789 SimRender::InterpolatePointsRNS(boundaries[i].points, true, m_BorderSeparation);793 SimRender::InterpolatePointsRNS(boundaries[i].points, m_BoundaryLines.back().overlay.m_Closed, m_BorderSeparation); 790 794 791 795 std::vector<float>& points = m_BoundaryLines.back().overlay.m_Coords; 792 796 for (size_t j = 0; j < boundaries[i].points.size(); ++j) 793 797 { 794 798 points.push_back(boundaries[i].points[j].X); 795 799 points.push_back(boundaries[i].points[j].Y); 800 801 if (m_EnableLineDebugOverlays) 802 { 803 SOverlayLine overlayNode; 804 if (j > boundaries[i].points.size() - 1 - 7) 805 overlayNode.m_Color = CColor(1.f, 0.f, 0.f, 1.f); 806 else if (j < 7) 807 overlayNode.m_Color = CColor(0.f, 1.f, 0.f, 1.f); 808 else 809 overlayNode.m_Color = CColor(1.0f, 1.0f, 1.0f, 1.0f); 810 811 overlayNode.m_Thickness = 1; 812 SimRender::ConstructCircleOnGround(GetSimContext(), boundaries[i].points[j].X, boundaries[i].points[j].Y, 0.1f, overlayNode, true); 813 m_DebugBoundaryLineNodes.push_back(overlayNode); 814 } 796 815 } 816 797 817 } 798 818 } 799 819 … … void CCmpTerritoryManager::RenderSubmit(SceneCollector& collector) 822 842 { 823 843 for (size_t i = 0; i < m_BoundaryLines.size(); ++i) 824 844 collector.Submit(&m_BoundaryLines[i].overlay); 845 846 for( size_t i = 0; i < m_DebugBoundaryLineNodes.size(); ++i) 847 collector.Submit(&m_DebugBoundaryLineNodes[i]); 848 825 849 } 826 850 827 851 player_id_t CCmpTerritoryManager::GetOwner(entity_pos_t x, entity_pos_t z) -
new file source/simulation2/components/ICmpRallyPoint.cpp
diff --git a/source/simulation2/components/ICmpRallyPoint.cpp b/source/simulation2/components/ICmpRallyPoint.cpp new file mode 100644 index 0000000..3d0fd4d
- + 1 /* Copyright (C) 2011 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "precompiled.h" 19 20 #include "ICmpRallyPoint.h" 21 #include "simulation2/system/InterfaceScripted.h" 22 23 BEGIN_INTERFACE_WRAPPER(RallyPoint) 24 DEFINE_INTERFACE_METHOD_0("GetPosition", RallyPointPosition, ICmpRallyPoint, GetPosition) 25 DEFINE_INTERFACE_METHOD_1("SetPosition", void, ICmpRallyPoint, SetPosition, RallyPointPosition) 26 DEFINE_INTERFACE_METHOD_0("IsSet", bool, ICmpRallyPoint, IsSet) 27 DEFINE_INTERFACE_METHOD_0("Unset", void, ICmpRallyPoint, Unset) 28 DEFINE_INTERFACE_METHOD_1("SetDisplayed", void, ICmpRallyPoint, SetDisplayed, bool) 29 //DEFINE_INTERFACE_METHOD_0("GetMarkerEntityId", entity_id_t, ICmpRallyPoint, GetMarkerEntityId) 30 END_INTERFACE_WRAPPER(RallyPoint) -
new file source/simulation2/components/ICmpRallyPoint.h
diff --git a/source/simulation2/components/ICmpRallyPoint.h b/source/simulation2/components/ICmpRallyPoint.h new file mode 100644 index 0000000..0e9fa9c
- + 1 /* Copyright (C) 2010 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #ifndef INCLUDED_ICMPRALLYPOINT 19 #define INCLUDED_ICMPRALLYPOINT 20 21 #include "simulation2/system/Interface.h" 22 #include "simulation2/helpers/Position.h" 23 24 #include "maths/FixedVector2D.h" 25 26 // trivial position subclass for JS conversions (so we can use x and z instead of x and y) 27 class RallyPointPosition : public CFixedVector2D {}; 28 29 /** 30 * Rally Point. 31 * Holds the position of a unit's rally point, and renders it to screen. 32 */ 33 class ICmpRallyPoint : public IComponent 34 { 35 public: 36 37 virtual RallyPointPosition GetPosition() = 0; 38 virtual void SetPosition(RallyPointPosition pos) = 0; 39 virtual void SetDisplayed(bool displayed) = 0; 40 //virtual entity_id_t GetMarkerEntityId() = 0; 41 virtual void Unset() = 0; 42 virtual bool IsSet() = 0; 43 44 DECLARE_INTERFACE_TYPE(RallyPoint) 45 }; 46 47 #endif // INCLUDED_ICMPRALLYPOINT -
source/simulation2/helpers/Geometry.h
diff --git a/source/simulation2/helpers/Geometry.h b/source/simulation2/helpers/Geometry.h index 0fc046c..4e5ede1 100644
a b 1 /* Copyright (C) 201 0Wildfire Games.1 /* Copyright (C) 2011 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … class CFixedVector2D; 30 30 namespace Geometry 31 31 { 32 32 33 /** 34 * 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, 35 * and half dimensions specified by @p halfSizes. Currently assumes the @p u and @p v vectors are perpendicular. Can also 36 * be used for rectangles. 37 * 38 * @param point point vector of the point that is to be tested relative to the origin (center) of the shape. 39 * @param u rotated X axis unit vector relative to the absolute XZ plane. Indicates the orientation of the rectangle. If not rotated, 40 * this value is the absolute X axis unit vector (1,0). If rotated by angle theta, this should be (cos theta, -sin theta), as 41 * the absolute Z axis points down in the unit circle. 42 * @param v rotated Z axis unit vector relative to the absolute XZ plane. Indicates the orientation of the rectangle. If not rotated, 43 * this value is the absolute Z axis unit vector (0,1). If rotated by angle theta, this should be (sin theta, cos theta), as 44 * the absolute Z axis points down in the unit circle. 45 * @param halfSizes Holds half the dimensions of the shape along the u and v vectors, respectively. 46 */ 33 47 bool PointIsInSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize); 34 48 35 49 CFixedVector2D GetHalfBoundingBox(CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize); 36 50 37 51 fixed DistanceToSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize); 38 52 53 /** 54 * 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 55 * dimensions @p halfSize, relative to the center of the square. Currently assumes the @p u and @p v vectors are perpendicular. 56 * Can also be used for rectangles. 57 * 58 * @param point point vector of the point we want to get the nearest edge point for, relative to the origin (center) of the shape. 59 * @param u rotated X axis unit vector, relative to the absolute XZ plane. Indicates the orientation of the shape. If not rotated, 60 * this value is the absolute X axis unit vector (1,0). If rotated by angle theta, this should be (cos theta, -sin theta). 61 * @param v rotated Z axis unit vector, relative to the absolute XZ plane. Indicates the orientation of the shape. If not rotated, 62 * this value is the absolute Z axis unit vector (0,1). If rotated by angle theta, this should be (sin theta, cos theta). 63 * @param halfSizes Holds half the dimensions of the shape along the u and v vectors, respectively. 64 */ 39 65 CFixedVector2D NearestPointOnSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize); 40 66 41 67 bool TestRaySquare(CFixedVector2D a, CFixedVector2D b, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize); -
source/simulation2/helpers/Render.cpp
diff --git a/source/simulation2/helpers/Render.cpp b/source/simulation2/helpers/Render.cpp index db63115..44675e8 100644
a b 1 /* Copyright (C) 201 0Wildfire Games.1 /* Copyright (C) 2011 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … static CVector2D EvaluateSpline(float t, CVector2D a0, CVector2D a1, CVector2D a 201 201 return p + CVector2D(dp.Y*-offset, dp.X*offset); 202 202 } 203 203 204 void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset )204 void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset, int segmentSamples) 205 205 { 206 206 PROFILE("InterpolatePointsRNS"); 207 ENSURE(segmentSamples > 0); 207 208 208 209 std::vector<CVector2D> newPoints; 210 const float fSegmentSamples = (float) segmentSamples; 209 211 210 212 // (This does some redundant computations for adjacent vertices, 211 213 // but it's fairly fast (<1ms typically) so we don't worry about it yet) … … void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed 215 217 // curve with fewer points 216 218 217 219 size_t n = points.size(); 220 221 if (closed) 222 { 218 223 if (n < 1) 219 return; // can't do anything unless we have two points 224 return; // we need at least a single point to not crash 225 } 226 else 227 { 228 if (n < 2) 229 return; // in non-closed mode, we need at least n=2 to not crash 230 } 220 231 221 size_t imax = closed ? n : n-1; // TODO: we probably need to do a bit more to handle non-closed paths 232 size_t imax = closed ? n : n-1; 233 newPoints.reserve(imax*segmentSamples); 222 234 223 newPoints.reserve(imax*4); 235 // these are primarily used inside the loop, but for open paths we need them outside the loop once to compute the last point 236 CVector2D a0; 237 CVector2D a1; 238 CVector2D a2; 239 CVector2D a3; 224 240 225 241 for (size_t i = 0; i < imax; ++i) 226 242 { 227 // Get the relevant points for this spline segment 228 CVector2D p0 = points[(i-1+n)%n]; 243 244 // Get the relevant points for this spline segment; each step interpolates the segment between p1 and p2; p0 and p3 are the points 245 // before p1 and after p2, respectively; they're needed to compute tangents and whatnot. 246 CVector2D p0; // normally points[(i-1+n)%n], but it's a bit more complicated due to open/closed paths -- see below 229 247 CVector2D p1 = points[i]; 230 248 CVector2D p2 = points[(i+1)%n]; 231 CVector2D p3 = points[(i+2)%n]; 249 CVector2D p3; // normally points[(i+2)%n], but it's a bit more complicated due to open/closed paths -- see below 250 251 if (!closed && (i == 0)) 252 // 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 253 // that extends p1 -> p0 (i.e. the first segment's direction) 254 p0 = points[0] + (points[0] - points[1]); 255 else 256 // standard wrap-around case 257 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) 258 259 260 if (!closed && (i == n-2)) 261 // 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) 262 // (note that p2's index should not be out of bounds, because in non-closed mode imax is reduced by 1) 263 p3 = points[n-1] + (points[n-1] - points[n-2]); 264 else 265 // standard wrap-around case 266 p3 = points[(i+2)%n]; 267 232 268 233 269 // Do the RNS computation (based on GPG4 "Nonuniform Splines") 234 270 float l1 = (p2 - p1).Length(); // length of spline segment (i)..(i+1) … … void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed 239 275 CVector2D v2 = (s1 + s2).Normalized() * l1; // spline velocity at i+1 240 276 241 277 // Compute standard cubic spline parameters 242 CVector2D a0 = p1*2 + p2*-2 + v1 + v2; 243 CVector2D a1 = p1*-3 + p2*3 + v1*-2 + v2*-1; 244 CVector2D a2 = v1; 245 CVector2D a3 = p1; 278 a0 = p1*2 + p2*-2 + v1 + v2; 279 a1 = p1*-3 + p2*3 + v1*-2 + v2*-1; 280 a2 = v1; 281 a3 = p1; 282 283 // Interpolate at regular points across the interval 284 for (int sample = 0; sample < segmentSamples; sample++) 285 newPoints.push_back(EvaluateSpline(sample/fSegmentSamples, a0, a1, a2, a3, offset)); 246 286 247 // Interpolate at various points248 newPoints.push_back(EvaluateSpline(0.f, a0, a1, a2, a3, offset));249 newPoints.push_back(EvaluateSpline(1.f/4.f, a0, a1, a2, a3, offset));250 newPoints.push_back(EvaluateSpline(2.f/4.f, a0, a1, a2, a3, offset));251 newPoints.push_back(EvaluateSpline(3.f/4.f, a0, a1, a2, a3, offset));252 287 } 253 288 289 if (!closed) 290 // if the path is open, we should take care to include the last control point 291 // NOTE: we can't just do push_back(points[n-1]) here because that ignores the offset 292 newPoints.push_back(EvaluateSpline(1.f, a0, a1, a2, a3, offset)); 293 254 294 points.swap(newPoints); 255 295 } 296 297 void SimRender::ConstructDashedLine(const std::vector<CVector2D>& keyPoints, SDashedLine& dashedLineOut, const float dashLength, const float blankLength) 298 { 299 // sanity checks 300 if (dashLength <= 0) 301 return; 302 303 if (blankLength <= 0) 304 return; 305 306 if (keyPoints.size() < 2) 307 return; 308 309 dashedLineOut.m_Points.clear(); 310 dashedLineOut.m_StartIndices.clear(); 311 312 // walk the line, counting the total length so far at each node point. When the length exceeds dashLength, cut the last segment at the 313 // required length and continue for blankLength along the line to start a new dash segment. 314 315 // TODO: we should probably extend this function to also allow for closed lines. I was thinking of slightly scaling the dash/blank length 316 // 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 317 // (O(n) and lots of square roots). 318 319 bool buildingDash = true; // true if we're building a dash, false if a blank 320 float curDashLength = 0; // builds up the current dash/blank's length as we walk through the line nodes 321 CVector2D dashLastPoint = keyPoints[0]; // last point of the current dash/blank being built. 322 323 // register the first starting node of the first dash 324 dashedLineOut.m_Points.push_back(keyPoints[0]); 325 dashedLineOut.m_StartIndices.push_back(0); 326 327 // 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 328 // properly take a direction vector along the path. 329 size_t i = 0; 330 331 while(i < keyPoints.size() - 1) 332 { 333 // get length of this segment 334 CVector2D segmentVector = keyPoints[i + 1] - dashLastPoint; // vector from our current point along the path to nextNode 335 float segmentLength = segmentVector.Length(); 336 337 float targetLength = (buildingDash ? dashLength : blankLength); 338 if (curDashLength + segmentLength > targetLength) 339 { 340 // 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 341 // line that is of just the required length to complete the dash, then make it the base point for the next dash/blank. 342 float cutLength = targetLength - curDashLength; 343 CVector2D cutPoint = dashLastPoint + (segmentVector.Normalized() * cutLength); 344 345 // start a new dash or blank in the next iteration 346 curDashLength = 0; 347 buildingDash = !buildingDash; // flip from dash to blank and vice-versa 348 dashLastPoint = cutPoint; 349 350 // don't increment i, we haven't fully traversed this segment yet so we still need to use the same point to take the 351 // direction vector with in the next iteration 352 353 // 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 354 // in the points array. 355 dashedLineOut.m_Points.push_back(cutPoint); 356 357 if (buildingDash) 358 { 359 // 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 360 // index as a start index of a dash. 361 dashedLineOut.m_StartIndices.push_back(dashedLineOut.m_Points.size() - 1); 362 } 363 364 } 365 else 366 { 367 // the segment from lastDashPoint to keyPoints[i+1] doesn't suffice to complete the dash, so we need to add keyPoints[i+1] 368 // to this dash's points and continue from there 369 370 if (buildingDash) 371 // still building the dash, add it to the output (we don't need to store the blanks) 372 dashedLineOut.m_Points.push_back(keyPoints[i+1]); 373 374 curDashLength += segmentLength; 375 dashLastPoint = keyPoints[i+1]; 376 i++; 377 378 } 379 380 } 381 382 } -
source/simulation2/helpers/Render.h
diff --git a/source/simulation2/helpers/Render.h b/source/simulation2/helpers/Render.h index 0129b01..b991ed9 100644
a b 23 23 * Helper functions related to rendering 24 24 */ 25 25 26 #include "maths/Vector2D.h" 27 26 28 class CSimContext; 27 class CVector2D;28 29 struct SOverlayLine; 29 30 31 32 33 struct SDashedLine 34 { 35 std::vector<CVector2D> m_Points; ///< Packed array of consecutive dashes' points. Use m_StartIndices to navigate it. 36 37 /** 38 * Start indices in m_Points of each dash. Dash n starts at point m_StartIndices[n] and ends at the point with index 39 * m_StartIndices[n+1] - 1, or at the end of the m_Points vector. Use the GetEndIndex(n) convenience method to abstract away the 40 * difference and get the (exclusive) end index of dash n. 41 */ 42 std::vector<size_t> m_StartIndices; 43 44 /// Returns the (exclusive) end point index (i.e. index within m_Points) of dash n. 45 size_t GetEndIndex(size_t i) 46 { 47 // for the last dash, there is no next starting index, so we need to use the end index of the m_Points array instead 48 return (i < m_StartIndices.size() - 1 ? m_StartIndices[i+1] : m_Points.size()); 49 } 50 }; 51 30 52 namespace SimRender 31 53 { 32 54 … … void SmoothPointsAverage(std::vector<CVector2D>& points, bool closed); 68 90 * the direction of the curve. 69 91 * If @p closed then the points are treated as a closed path (the last is connected 70 92 * to the first). 93 * @param segmentSamples Amount of intermediate points to sample between every two control points. 94 */ 95 void InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset, int segmentSamples = 4); 96 97 /** 98 * Creates a dashed line from the line specified by @points so that each dash is of length 99 * @p dashLength, and each blank inbetween is of length @p blankLength. The dashed line returned as a list of smaller lines 100 * in @p dashedLineOut. 101 * 102 * @param dashLength Length of a single dash. Must be strictly positive. 103 * @param blankLength Length of a single blank between dashes. Must be strictly positive. 71 104 */ 72 void InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset);105 void ConstructDashedLine(const std::vector<CVector2D>& linePoints, SDashedLine& dashedLineOut, const float dashLength, const float blankLength); 73 106 74 107 } // namespace 75 108 -
source/simulation2/scripting/EngineScriptConversions.cpp
diff --git a/source/simulation2/scripting/EngineScriptConversions.cpp b/source/simulation2/scripting/EngineScriptConversions.cpp index b7cc512..3399416 100644
a b 29 29 #include "simulation2/helpers/Grid.h" 30 30 #include "simulation2/system/IComponent.h" 31 31 #include "simulation2/system/ParamNode.h" 32 #include "simulation2/components/ICmpRallyPoint.h" 32 33 33 34 #define FAIL(msg) STMT(JS_ReportError(cx, msg); return false) 34 35 … … template<> jsval ScriptInterface::ToJSVal<CFixedVector2D>(JSContext* cx, const C 210 211 return OBJECT_TO_JSVAL(obj); 211 212 } 212 213 214 // rally point conversion; a RallyPointPosition is merely a simple CFixedVector2D with a custom name so that we 215 // can create this convertor for it. This is the same thing as the CFixedVector2D conversion, except that it 216 // renames the y component to z in javascript to prevent breaking any code that relies on it (and vice-versa for 217 // FromJSVal below) 218 template<> jsval ScriptInterface::ToJSVal<RallyPointPosition>(JSContext* cx, const RallyPointPosition& val) 219 { 220 JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL); 221 if (!obj) 222 return JSVAL_VOID; 223 224 jsval x = ToJSVal(cx, val.X); 225 jsval z = ToJSVal(cx, val.Y); 226 227 JS_SetProperty(cx, obj, "x", &x); 228 JS_SetProperty(cx, obj, "z", &z); // rename "y" to "z" for JavaScript 229 230 return OBJECT_TO_JSVAL(obj); 231 } 232 233 template<> bool ScriptInterface::FromJSVal<RallyPointPosition>(JSContext* cx, jsval v, RallyPointPosition& out) 234 { 235 if (!JSVAL_IS_OBJECT(v)) 236 return false; // TODO: report type error 237 JSObject* obj = JSVAL_TO_OBJECT(v); 238 239 jsval p; 240 241 if (!JS_GetProperty(cx, obj, "x", &p)) return false; // TODO: report type errors 242 if (!FromJSVal(cx, p, out.X)) return false; 243 244 if (!JS_GetProperty(cx, obj, "z", &p)) return false; // rename "z" to "y" from javascript 245 if (!FromJSVal(cx, p, out.Y)) return false; 246 247 return true; 248 } 249 213 250 template<> jsval ScriptInterface::ToJSVal<Grid<u16> >(JSContext* cx, const Grid<u16>& val) 214 251 { 215 252 JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);