Line data Source code
1 : /* Copyright (C) 2022 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_CCMPRALLYPOINTRENDERER
19 : #define INCLUDED_CCMPRALLYPOINTRENDERER
20 :
21 : #include "ICmpRallyPointRenderer.h"
22 :
23 : #include "graphics/Overlay.h"
24 : #include "graphics/TextureManager.h"
25 : #include "ps/CLogger.h"
26 : #include "renderer/Renderer.h"
27 : #include "simulation2/MessageTypes.h"
28 : #include "simulation2/components/ICmpFootprint.h"
29 : #include "simulation2/components/ICmpIdentity.h"
30 : #include "simulation2/components/ICmpObstructionManager.h"
31 : #include "simulation2/components/ICmpOwnership.h"
32 : #include "simulation2/components/ICmpPathfinder.h"
33 : #include "simulation2/components/ICmpPlayer.h"
34 : #include "simulation2/components/ICmpPlayerManager.h"
35 : #include "simulation2/components/ICmpPosition.h"
36 : #include "simulation2/components/ICmpTerrain.h"
37 : #include "simulation2/components/ICmpVisual.h"
38 : #include "simulation2/components/ICmpWaterManager.h"
39 : #include "simulation2/helpers/Render.h"
40 : #include "simulation2/helpers/Geometry.h"
41 : #include "simulation2/system/Component.h"
42 :
43 : struct SVisibilitySegment
44 : {
45 : bool m_Visible;
46 : size_t m_StartIndex;
47 : size_t m_EndIndex; // Inclusive
48 :
49 0 : SVisibilitySegment(bool visible, size_t startIndex, size_t endIndex)
50 0 : : m_Visible(visible), m_StartIndex(startIndex), m_EndIndex(endIndex)
51 0 : {}
52 :
53 0 : bool operator==(const SVisibilitySegment& other) const
54 : {
55 0 : return m_Visible == other.m_Visible && m_StartIndex == other.m_StartIndex && m_EndIndex == other.m_EndIndex;
56 : }
57 :
58 : bool operator!=(const SVisibilitySegment& other) const
59 : {
60 : return !(*this == other);
61 : }
62 :
63 0 : bool IsSinglePoint() const
64 : {
65 0 : return m_StartIndex == m_EndIndex;
66 : }
67 : };
68 :
69 0 : class CCmpRallyPointRenderer final : public ICmpRallyPointRenderer
70 : {
71 : public:
72 : static std::string GetSchema();
73 : static void ClassInit(CComponentManager& componentManager);
74 :
75 : void Init(const CParamNode& paramNode) override;
76 : void Deinit() override;
77 :
78 : void Serialize(ISerializer& UNUSED(serialize)) override;
79 : void Deserialize(const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) override;
80 :
81 : void HandleMessage(const CMessage& msg, bool UNUSED(global)) override;
82 :
83 : /*
84 : * Must be called whenever m_Displayed or the size of m_RallyPoints change,
85 : * to determine whether we need to respond to render messages.
86 : */
87 : virtual void UpdateMessageSubscriptions();
88 :
89 : void AddPosition_wrapper(const CFixedVector2D& pos) override;
90 :
91 : void SetPosition(const CFixedVector2D& pos) override;
92 :
93 : void UpdatePosition(u32 rallyPointId, const CFixedVector2D& pos) override;
94 :
95 : void SetDisplayed(bool displayed) override;
96 :
97 : void Reset() override;
98 :
99 : void UpdateColor() override;
100 :
101 : /**
102 : * Returns true if at least one display rally point is set; i.e., if we have a point to render our marker/line at.
103 : */
104 : bool IsSet() const override;
105 :
106 0 : DEFAULT_COMPONENT_ALLOCATOR(RallyPointRenderer)
107 :
108 : protected:
109 : /**
110 : * Display position of the rally points. Note that this are merely the display positions; they not necessarily the same as the
111 : * actual positions used in the simulation at any given time. In particular, we need this separate copy to support
112 : * instantaneously rendering the rally point markers/lines when the user sets one in-game (instead of waiting until the
113 : * network-synchronization code sets it on the RallyPoint component, which might take up to half a second).
114 : */
115 : std::vector<CFixedVector2D> m_RallyPoints;
116 : /**
117 : * Full path to the rally points as returned by the pathfinder, with some post-processing applied to reduce zig/zagging.
118 : */
119 : std::vector<std::vector<CVector2D> > m_Path;
120 : /**
121 : * Visibility segments of the rally point paths; splits the path into SoD/non-SoD segments.
122 : */
123 : std::vector<std::vector<SVisibilitySegment> > m_VisibilitySegments;
124 : /**
125 : * Should we render the rally points and the path lines? (set from JS when e.g. the unit is selected/deselected)
126 : */
127 : bool m_Displayed;
128 : /**
129 : * Smooth the path before rendering?
130 : */
131 : bool m_SmoothPath;
132 : /**
133 : * Entity IDs of the rally point markers.
134 : */
135 : std::vector<entity_id_t> m_MarkerEntityIds;
136 :
137 : size_t m_LastMarkerCount;
138 : /**
139 : * Last seen owner of this entity (used to keep track of ownership changes).
140 : */
141 : player_id_t m_LastOwner;
142 : /**
143 : * Template name of the rally point markers.
144 : */
145 : std::wstring m_MarkerTemplate;
146 :
147 : /**
148 : * Marker connector line settings (loaded from XML)
149 : */
150 : float m_LineThickness;
151 : CColor m_LineColor;
152 : CColor m_LineDashColor;
153 : SOverlayTexturedLine::LineCapType m_LineStartCapType;
154 : SOverlayTexturedLine::LineCapType m_LineEndCapType;
155 : std::wstring m_LineTexturePath;
156 : std::wstring m_LineTextureMaskPath;
157 : /**
158 : * Pathfinder passability class to use for computing the (long-range) marker line path.
159 : */
160 : std::string m_LinePassabilityClass;
161 :
162 : CTexturePtr m_Texture;
163 : CTexturePtr m_TextureMask;
164 :
165 : /**
166 : * Textured overlay lines to be used for rendering the marker line. There can be multiple because we may need to render
167 : * dashes for segments that are inside the SoD.
168 : */
169 : std::vector<std::vector<SOverlayTexturedLine> > m_TexturedOverlayLines;
170 :
171 : /**
172 : * Draw little overlay circles to indicate where the exact path points are.
173 : */
174 : bool m_EnableDebugNodeOverlay;
175 : std::vector<std::vector<SOverlayLine> > m_DebugNodeOverlays;
176 :
177 : private:
178 : /**
179 : * Helper function for AddPosition_wrapper and SetPosition.
180 : */
181 : void AddPosition(CFixedVector2D pos, bool recompute);
182 :
183 : /**
184 : * Helper function to set the line color to its owner's color.
185 : */
186 : void UpdateLineColor();
187 :
188 : /**
189 : * Repositions the rally point markers; moves them outside of the world (ie. hides them), or positions them at the currently
190 : * set rally points. Also updates the actor's variation according to the entity's current owning player's civilization.
191 : *
192 : * Should be called whenever either the position of a rally point changes (including whether it is set or not), or the display
193 : * flag changes, or the ownership of the entity changes.
194 : */
195 : void UpdateMarkers();
196 :
197 : /**
198 : * Recomputes all the full paths from this entity to the rally point and from the rally point to the next, and does all the necessary
199 : * post-processing to make them prettier.
200 : *
201 : * Should be called whenever all rally points' position changes.
202 : */
203 : void RecomputeAllRallyPointPaths();
204 :
205 : /**
206 : * Recomputes the full path for m_Path[ @p index], and does all the necessary post-processing to make it prettier.
207 : *
208 : * Should be called whenever either the starting position or the rally point's position changes.
209 : */
210 : void RecomputeRallyPointPath_wrapper(size_t index);
211 :
212 : /**
213 : * Recomputes the full path from this entity/the previous rally point to the next rally point, and does all the necessary
214 : * post-processing to make it prettier. This doesn't check if we have a valid position or if a rally point is set.
215 : *
216 : * You shouldn't need to call this method directly.
217 : */
218 : void RecomputeRallyPointPath(size_t index, CmpPtr<ICmpPosition>& cmpPosition, CmpPtr<ICmpFootprint>& cmpFootprint, CmpPtr<ICmpPathfinder> cmpPathfinder);
219 :
220 : /**
221 : * Checks for changes to the SoD to the previously saved state, and reconstructs the visibility segments and overlay lines to
222 : * match if necessary. Does nothing if the rally point lines are not currently set to be displayed, or if no rally point is set.
223 : */
224 : void UpdateOverlayLines();
225 :
226 : /**
227 : * Sets up all overlay lines for rendering according to the current full path and visibility segments. Splits the line into solid
228 : * and dashed pieces (for the SoD). Should be called whenever the SoD has changed. If no full path is currently set, this method
229 : * does nothing.
230 : */
231 : void ConstructAllOverlayLines();
232 :
233 : /**
234 : * Sets up the overlay lines for rendering according to the full path and visibility segments at @p index. Splits the line into
235 : * solid and dashed pieces (for the SoD). Should be called whenever the SoD of the path at @p index has changed.
236 : */
237 : void ConstructOverlayLines(size_t index);
238 :
239 : /**
240 : * Get the point on the footprint edge that's as close from "start" as possible.
241 : */
242 : void GetClosestsEdgePointFrom(CFixedVector2D& result, CFixedVector2D& start, CmpPtr<ICmpPosition> cmpPosition, CmpPtr<ICmpFootprint> cmpFootprint) const;
243 :
244 : /**
245 : * Returns a list of indices of waypoints in the current path (m_Path[index]) where the LOS visibility changes, ordered from
246 : * building/previous rally point to rally point. Used to construct the overlay line segments and track changes to the SoD.
247 : */
248 : void GetVisibilitySegments(std::vector<SVisibilitySegment>& out, size_t index) const;
249 :
250 : /**
251 : * Simplifies the path by removing waypoints that lie between two points that are visible from one another. This is primarily
252 : * intended to reduce some unnecessary curviness of the path; the pathfinder returns a mathematically (near-)optimal path, which
253 : * will happily curve and bend to reduce costs. Visually, it doesn't make sense for a rally point path to curve and bend when it
254 : * could just as well have gone in a straight line; that's why we have this, to make it look more natural.
255 : *
256 : * @p coords array of path coordinates to simplify
257 : * @p maxSegmentLinks if non-zero, indicates the maximum amount of consecutive node-to-node links that can be joined into a
258 : * single link. If this value is set to e.g. 1, then no reductions will be performed. A value of 3 means that
259 : * at most 3 consecutive node links will be joined into a single link.
260 : * @p floating whether to consider nodes who are under the water level as floating on top of the water
261 : */
262 : void ReduceSegmentsByVisibility(std::vector<CVector2D>& coords, unsigned maxSegmentLinks = 0, bool floating = true) const;
263 :
264 : /**
265 : * Helper function to GetVisibilitySegments, factored out for testing. Merges single-point segments with its neighbouring
266 : * segments. You should not have to call this method directly.
267 : */
268 : static void MergeVisibilitySegments(std::vector<SVisibilitySegment>& segments);
269 :
270 : void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
271 : };
272 :
273 116 : REGISTER_COMPONENT_TYPE(RallyPointRenderer)
274 :
275 : #endif // INCLUDED_CCMPRALLYPOINTRENDERER
|