Ticket #2025: pathfind.patch
File pathfind.patch, 148.2 KB (added by , 11 years ago) |
---|
-
source/maths/Sqrt.h
22 22 * 64-bit integer square root. 23 23 * Returns r such that r^2 <= n < (r+1)^2, for the complete u64 range. 24 24 */ 25 u32 isqrt64(u64 n);26 25 26 27 // TODO: This should be equivalent to (u32)sqrt((double)n), and in practice 28 // that seems to be true for all input, so do we actually need this integer-only 29 // implementation? i.e. are there any platforms / compiler settings where 30 // sqrt(double) won't give the correct answer? and is sqrt(double) faster? 31 // TODO: read, decide and benchmark 32 // http://omeg.pl/blog/2012/03/performance-of-various-square-root-computing-algorithms/ 33 // at least incredibly faster on win64 32bit build. (talking about 15% of cpu profile against 0.1% ...) 34 35 //#define NATIVE_SQRT 36 #ifndef NATIVE_SQRT 37 static inline u32 isqrt64(const u64 n) 38 { 39 u64 one = (u64)1 << 62; // highest power of four <= than the argument 40 while (one > n) 41 one >>= 2; 42 43 u64 op = n; 44 u64 res = 0; 45 while (one != 0) 46 { 47 if (op >= res + one) 48 { 49 op -= (res + one); 50 res += (one << 1); 51 } 52 res >>= 1; 53 one >>= 2; 54 } 55 return (u32)res; 56 } 57 #else 58 #define isqrt64(n) ((u32)sqrt((double)(n))) 59 #endif 60 27 61 #endif // INCLUDED_MATH_SQRT -
source/maths/Sqrt.cpp
18 18 #include "precompiled.h" 19 19 20 20 #include "Sqrt.h" 21 22 // Based on http://freaknet.org/martin/tape/gos/misc/personal/msc/sqrt/sqrt.html23 u32 isqrt64(u64 n)24 {25 u64 op = n;26 u64 res = 0;27 u64 one = (u64)1 << 62; // highest power of four <= than the argument28 29 while (one > op)30 one >>= 2;31 32 while (one != 0)33 {34 if (op >= res + one)35 {36 op -= (res + one);37 res += (one << 1);38 }39 res >>= 1;40 one >>= 2;41 }42 return (u32)res;43 }44 45 // TODO: This should be equivalent to (u32)sqrt((double)n), and in practice46 // that seems to be true for all input, so do we actually need this integer-only47 // implementation? i.e. are there any platforms / compiler settings where48 // sqrt(double) won't give the correct answer? and is sqrt(double) faster? -
source/maths/FixedVector2D.h
95 95 fixed Length() const 96 96 { 97 97 // Do intermediate calculations with 64-bit ints to avoid overflows 98 i64 x = (i64)X.GetInternalValue();99 i64 y = (i64)Y.GetInternalValue();100 u64 xx = (u64)(x * x);101 u64 yy = (u64)(y * y);102 u64 d2 = xx + yy;98 const i64 x = (i64)X.GetInternalValue(); 99 const i64 y = (i64)Y.GetInternalValue(); 100 const u64 xx = (u64)(x * x); 101 const u64 yy = (u64)(y * y); 102 const u64 d2 = xx + yy; 103 103 CheckUnsignedAdditionOverflow(d2, xx, L"Overflow in CFixedVector2D::Length() part 1") 104 104 105 u32 d = isqrt64(d2);105 const u32 d = isqrt64(d2); 106 106 107 107 CheckU32CastOverflow(d, i32, L"Overflow in CFixedVector2D::Length() part 2") 108 108 fixed r; 109 109 r.SetInternalValue((i32)d); 110 110 return r; 111 111 } 112 /** 113 * Returns the squared length of distance bewteen this vector and another 114 * Will not overflow if the result can be represented as type 'fixed'. 115 * faster than comparing two length (saving a squareroot operation) 116 */ 117 fixed computeDistanceLengthSquared(const CFixedVector2D& src) const 118 { 119 const i64 x = (i64)X.GetInternalValue() - (i64)src.X.GetInternalValue(); 120 const i64 y = (i64)Y.GetInternalValue() - (i64)src.Y.GetInternalValue(); 121 const u64 d2 = (u64)(x * x) + (u64)(y * y); 122 fixed r; 123 r.SetInternalValue((i32)(d2 & 0x0000ffff)); 124 return r; 125 } 126 127 /** 128 * Returns the squared length of distance bewteen this vector and another 129 * Will not overflow if the result can be represented as type 'fixed'. 130 */ 131 fixed computeDistanceLength(const CFixedVector2D& src) const 132 { 133 const i64 x = (i64)X.GetInternalValue() - (i64)src.X.GetInternalValue(); 134 const i64 y = (i64)Y.GetInternalValue() - (i64)src.Y.GetInternalValue(); 135 const u64 xx = (u64)(x * x); 136 const u64 yy = (u64)(y * y); 137 const u64 d2 = xx + yy; 138 CheckUnsignedAdditionOverflow(d2, xx, L"Overflow in CFixedVector2D::Length() part 1") 112 139 140 const u32 d = isqrt64(d2); 141 142 CheckU32CastOverflow(d, i32, L"Overflow in CFixedVector2D::Length() part 2") 143 fixed r; 144 r.SetInternalValue((i32)d); 145 return r; 146 } 113 147 /** 114 148 * Returns -1, 0, +1 depending on whether length is less/equal/greater 115 149 * than the argument. … … 188 222 Y = Y.MulDiv(n, l); 189 223 } 190 224 } 225 /** 226 * Compute the dot product of this vector with another. 227 */ 228 inline fixed SubFromAndDot(const fixed aX, const fixed aY, const CFixedVector2D& v) const 229 { 230 i64 x = (i64)(aX - X).GetInternalValue() * (i64)v.X.GetInternalValue(); 231 i64 y = (i64)(aY - Y).GetInternalValue() * (i64)v.Y.GetInternalValue(); 232 CheckSignedAdditionOverflow(i64, x, y, L"Overflow in CFixedVector2D::Dot() part 1", L"Underflow in CFixedVector2D::Dot() part 1") 233 i64 sum = x + y; 234 sum >>= fixed::fract_bits; 191 235 236 CheckCastOverflow(sum, i32, L"Overflow in CFixedVector2D::Dot() part 2", L"Underflow in CFixedVector2D::Dot() part 2") 237 fixed ret; 238 ret.SetInternalValue((i32)sum); 239 return ret; 240 } 192 241 /** 193 242 * Compute the dot product of this vector with another. 194 243 */ 195 fixed Dot(const CFixedVector2D& v)244 inline fixed SubAndDot(const CFixedVector2D& b, const CFixedVector2D& v) const 196 245 { 246 i64 x = (i64)(X - b.X).GetInternalValue() * (i64)v.X.GetInternalValue(); 247 i64 y = (i64)(Y - b.Y).GetInternalValue() * (i64)v.Y.GetInternalValue(); 248 CheckSignedAdditionOverflow(i64, x, y, L"Overflow in CFixedVector2D::Dot() part 1", L"Underflow in CFixedVector2D::Dot() part 1") 249 i64 sum = x + y; 250 sum >>= fixed::fract_bits; 251 252 CheckCastOverflow(sum, i32, L"Overflow in CFixedVector2D::Dot() part 2", L"Underflow in CFixedVector2D::Dot() part 2") 253 fixed ret; 254 ret.SetInternalValue((i32)sum); 255 return ret; 256 } 257 /** 258 * Compute the dot product of this vector with another. 259 */ 260 inline fixed Dot(const CFixedVector2D& v) const 261 { 197 262 i64 x = (i64)X.GetInternalValue() * (i64)v.X.GetInternalValue(); 198 263 i64 y = (i64)Y.GetInternalValue() * (i64)v.Y.GetInternalValue(); 199 264 CheckSignedAdditionOverflow(i64, x, y, L"Overflow in CFixedVector2D::Dot() part 1", L"Underflow in CFixedVector2D::Dot() part 1") … … 206 271 return ret; 207 272 } 208 273 209 CFixedVector2D Perpendicular() 274 CFixedVector2D Perpendicular() const 210 275 { 211 276 return CFixedVector2D(Y, -X); 212 277 } … … 214 279 /** 215 280 * Rotate the vector by the given angle (anticlockwise). 216 281 */ 217 CFixedVector2D Rotate(fixed angle) 282 CFixedVector2D Rotate(fixed angle) const 218 283 { 219 284 fixed s, c; 220 285 sincos_approx(angle, s, c); -
source/maths/Fixed.h
107 107 class CFixed 108 108 { 109 109 private: 110 T value;111 110 112 111 explicit CFixed(T v) : value(v) { } 113 112 114 113 public: 114 T value; 115 115 enum { fract_bits = fract_bits_ }; 116 116 117 117 CFixed() : value(0) { } … … 191 191 bool IsZero() const { return value == 0; } 192 192 193 193 /// Equality. 194 bool operator==( CFixedn) const { return (value == n.value); }194 bool operator==(const CFixed& n) const { return (value == n.value); } 195 195 196 196 /// Inequality. 197 bool operator!=( CFixedn) const { return (value != n.value); }197 bool operator!=(const CFixed& n) const { return (value != n.value); } 198 198 199 199 /// Numeric comparison. 200 bool operator<=( CFixedn) const { return (value <= n.value); }200 bool operator<=(const CFixed& n) const { return (value <= n.value); } 201 201 202 202 /// Numeric comparison. 203 bool operator<( CFixedn) const { return (value < n.value); }203 bool operator<(const CFixed& n) const { return (value < n.value); } 204 204 205 205 /// Numeric comparison. 206 bool operator>=( CFixedn) const { return (value >= n.value); }206 bool operator>=(const CFixed& n) const { return (value >= n.value); } 207 207 208 208 /// Numeric comparison. 209 bool operator>( CFixedn) const { return (value > n.value); }209 bool operator>(const CFixed& n) const { return (value > n.value); } 210 210 211 211 // Basic arithmetic: 212 212 213 213 /// Add a CFixed. Might overflow. 214 CFixed operator+( CFixedn) const214 CFixed operator+(const CFixed& n) const 215 215 { 216 216 CheckSignedAdditionOverflow(T, value, n.value, L"Overflow in CFixed::operator+(CFixed n)", L"Underflow in CFixed::operator+(CFixed n)") 217 217 return CFixed(value + n.value); 218 218 } 219 219 220 220 /// Subtract a CFixed. Might overflow. 221 CFixed operator-( CFixedn) const221 CFixed operator-(const CFixed& n) const 222 222 { 223 223 CheckSignedSubtractionOverflow(T, value, n.value, L"Overflow in CFixed::operator-(CFixed n)", L"Underflow in CFixed::operator-(CFixed n)") 224 224 return CFixed(value - n.value); 225 225 } 226 226 227 227 /// Add a CFixed. Might overflow. 228 CFixed& operator+=( CFixedn) { *this = *this + n; return *this; }228 CFixed& operator+=(const CFixed& n) { *this = *this + n; return *this; } 229 229 230 230 /// Subtract a CFixed. Might overflow. 231 CFixed& operator-=( CFixedn) { *this = *this - n; return *this; }231 CFixed& operator-=(const CFixed& n) { *this = *this - n; return *this; } 232 232 233 233 /// Negate a CFixed. 234 234 CFixed operator-() const … … 238 238 } 239 239 240 240 /// Divide by a CFixed. Must not have n.IsZero(). Might overflow. 241 CFixed operator/( CFixedn) const241 CFixed operator/(const CFixed& n) const 242 242 { 243 243 i64 t = (i64)value << fract_bits; 244 244 i64 result = t / (i64)n.value; … … 262 262 } 263 263 264 264 /// Mod by a fixed. Must not have n == 0. Result has the same sign as n. 265 CFixed operator%( CFixedn) const265 CFixed operator%(const CFixed& n) const 266 266 { 267 267 T t = value % n.value; 268 268 if (n.value > 0 && t < 0) … … 279 279 * Multiply by a CFixed. Likely to overflow if both numbers are large, 280 280 * so we use an ugly name instead of operator* to make it obvious. 281 281 */ 282 CFixed Multiply( CFixedn) const282 CFixed Multiply(const CFixed& n) const 283 283 { 284 284 i64 t = (i64)value * (i64)n.value; 285 285 t >>= fract_bits; … … 299 299 /** 300 300 * Compute this*m/d. Must not have d == 0. Won't overflow if the result can be represented as a CFixed. 301 301 */ 302 CFixed MulDiv( CFixed m, CFixedd) const302 CFixed MulDiv(const CFixed& m, const CFixed& d) const 303 303 { 304 304 i64 t = ((i64)value * (i64)m.value) / (i64)d.value; 305 305 CheckCastOverflow(t, T, L"Overflow in CFixed::Multiply(CFixed n)", L"Underflow in CFixed::Multiply(CFixed n)") -
source/tools/atlas/GameInterface/Brushes.cpp
52 52 --max_j_inclusive; 53 53 } 54 54 55 void ProcessTile( ssize_t i,ssize_t j)55 void ProcessTile(const ssize_t i, const ssize_t j) 56 56 { 57 57 ssize_t i0, j0; 58 58 m_Brush->GetBottomLeft(i0, j0); -
source/renderer/TerrainOverlay.h
123 123 * @param i <i>i</i> coordinate of tile being processed 124 124 * @param j <i>j</i> coordinate of tile being processed 125 125 */ 126 virtual void ProcessTile( ssize_t i,ssize_t j) = 0;126 virtual void ProcessTile(const ssize_t i, const ssize_t j) = 0; 127 127 128 128 /** 129 129 * Draw a filled quad on top of the current tile. -
source/simulation2/helpers/Geometry.h
47 47 * @return true if @p point is inside the square with rotated X axis unit vector @p u and rotated Z axis unit vector @p v, 48 48 * and half dimensions specified by @p halfSizes. 49 49 */ 50 bool PointIsInSquare( CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2DhalfSize);50 bool PointIsInSquare(const CFixedVector2D& point, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize); 51 51 52 CFixedVector2D GetHalfBoundingBox( CFixedVector2D u, CFixedVector2D v, CFixedVector2DhalfSize);52 CFixedVector2D GetHalfBoundingBox(const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize); 53 53 54 fixed DistanceToSquare( CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2DhalfSize);54 fixed DistanceToSquare(const CFixedVector2D& point, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize); 55 55 56 56 /** 57 57 * Given a circle of radius @p radius, and a chord of length @p chordLength on this circle, computes the central angle formed by … … 75 75 * @return 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 76 76 * dimensions @p halfSize, relative to the center of the square 77 77 */ 78 CFixedVector2D NearestPointOnSquare( CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2DhalfSize);78 CFixedVector2D NearestPointOnSquare(const CFixedVector2D& point, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize); 79 79 80 bool TestRaySquare( CFixedVector2D a, CFixedVector2D b, CFixedVector2D u, CFixedVector2D v, CFixedVector2DhalfSize);80 bool TestRaySquare(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize); 81 81 82 bool TestRayAASquare( CFixedVector2D a, CFixedVector2D b, CFixedVector2DhalfSize);82 bool TestRayAASquare(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& halfSize); 83 83 84 84 bool TestSquareSquare( 85 CFixedVector2D c0, CFixedVector2D u0, CFixedVector2D v0, CFixedVector2DhalfSize0,86 CFixedVector2D c1, CFixedVector2D u1, CFixedVector2D v1, CFixedVector2DhalfSize1);85 const CFixedVector2D& c0, const CFixedVector2D& u0, const CFixedVector2D& v0, const CFixedVector2D& halfSize0, 86 const CFixedVector2D& c1, const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1); 87 87 88 88 } // namespace 89 90 89 #endif // INCLUDED_HELPER_GEOMETRY -
source/simulation2/helpers/Spatial.h
72 72 return !(*this == rhs); 73 73 } 74 74 75 void Reset( entity_pos_t maxX, entity_pos_t maxZ,entity_pos_t divisionSize)75 void Reset(const entity_pos_t maxX, const entity_pos_t maxZ, const entity_pos_t divisionSize) 76 76 { 77 77 m_DivisionSize = divisionSize; 78 78 m_DivisionsW = (maxX / m_DivisionSize).ToInt_RoundToInfinity(); … … 85 85 * Add an item with the given 'to' size. 86 86 * The item must not already be present. 87 87 */ 88 void Add( T item, CFixedVector2D toMin, CFixedVector2DtoMax)88 void Add(const T& item, const CFixedVector2D& toMin, const CFixedVector2D& toMax) 89 89 { 90 90 ENSURE(toMin.X <= toMax.X && toMin.Y <= toMax.Y); 91 91 92 u32 i0 = GetI0(toMin.X);93 u32 j0 = GetJ0(toMin.Y);94 u32 i1 = GetI1(toMax.X);95 u32 j1 = GetJ1(toMax.Y);92 const u32 i0 = GetI0(toMin.X); 93 const u32 j0 = GetJ0(toMin.Y); 94 const u32 i1 = GetI1(toMax.X); 95 const u32 j1 = GetJ1(toMax.Y); 96 96 for (u32 j = j0; j <= j1; ++j) 97 97 { 98 98 for (u32 i = i0; i <= i1; ++i) … … 108 108 * The item should already be present. 109 109 * The size must match the size that was last used when adding the item. 110 110 */ 111 void Remove( T item, CFixedVector2D fromMin, CFixedVector2DfromMax)111 void Remove(const T& item, const CFixedVector2D& fromMin, const CFixedVector2D& fromMax) 112 112 { 113 113 ENSURE(fromMin.X <= fromMax.X && fromMin.Y <= fromMax.Y); 114 114 … … 139 139 /** 140 140 * Equivalent to Remove() then Add(), but potentially faster. 141 141 */ 142 void Move( T item, CFixedVector2D fromMin, CFixedVector2D fromMax, CFixedVector2D toMin, CFixedVector2DtoMax)142 void Move(const T& item, const CFixedVector2D& fromMin, const CFixedVector2D& fromMax, const CFixedVector2D& toMin, const CFixedVector2D& toMax) 143 143 { 144 144 // Skip the work if we're staying in the same divisions 145 145 if (GetIndex0(fromMin) == GetIndex0(toMin) && GetIndex1(fromMax) == GetIndex1(toMax)) … … 153 153 * Convenience function for Add() of individual points. 154 154 * (Note that points on a boundary may occupy multiple divisions.) 155 155 */ 156 void Add( T item, CFixedVector2Dto)156 void Add(const T& item, const CFixedVector2D& to) 157 157 { 158 158 Add(item, to, to); 159 159 } … … 161 161 /** 162 162 * Convenience function for Remove() of individual points. 163 163 */ 164 void Remove( T item, CFixedVector2Dfrom)164 void Remove(const T& item, const CFixedVector2D& from) 165 165 { 166 166 Remove(item, from, from); 167 167 } … … 169 169 /** 170 170 * Convenience function for Move() of individual points. 171 171 */ 172 void Move( T item, CFixedVector2D from, CFixedVector2Dto)172 void Move(const T& item, const CFixedVector2D& from, const CFixedVector2D& to) 173 173 { 174 174 Move(item, from, from, to, to); 175 175 } … … 178 178 * Returns a sorted list of unique items that includes all items 179 179 * within the given axis-aligned square range. 180 180 */ 181 std::vector<T> GetInRange(CFixedVector2D posMin, CFixedVector2D posMax)181 void GetInRange(const CFixedVector2D& posMin, const CFixedVector2D& posMax, std::vector<T> &ret) const 182 182 { 183 std::vector<T> ret;184 185 183 ENSURE(posMin.X <= posMax.X && posMin.Y <= posMax.Y); 186 184 187 u32 i0 = GetI0(posMin.X); 188 u32 j0 = GetJ0(posMin.Y); 189 u32 i1 = GetI1(posMax.X); 190 u32 j1 = GetJ1(posMax.Y); 191 for (u32 j = j0; j <= j1; ++j) 185 const u32 i0 = GetI0(posMin.X); 186 const u32 j0 = GetJ0(posMin.Y); 187 const u32 i1 = GetI1(posMax.X); 188 const u32 j1 = GetJ1(posMax.Y); 189 190 ret.reserve((i1 - i0)*(j1 - j0)*4); 191 for (u32 j = j0; j <= j1; j++) 192 192 { 193 for (u32 i = i0; i <= i1; ++i)193 for (u32 i = i0; i <= i1; i++) 194 194 { 195 std::vector<T>& div = m_Divisions.at(i + j*m_DivisionsW);195 const std::vector<T>& div = m_Divisions.at(i + j*m_DivisionsW); 196 196 ret.insert(ret.end(), div.begin(), div.end()); 197 197 198 } 198 199 } 199 200 … … 201 202 std::sort(ret.begin(), ret.end()); 202 203 ret.erase(std::unique(ret.begin(), ret.end()), ret.end()); 203 204 204 return ret;205 205 } 206 206 207 207 /** 208 208 * Returns a sorted list of unique items that includes all items 209 209 * within the given circular distance of the given point. 210 210 */ 211 std::vector<T> GetNear(CFixedVector2D pos, entity_pos_t range)211 void GetNear(const CFixedVector2D& pos, const entity_pos_t range, std::vector<T> &ents) const 212 212 { 213 213 // TODO: be cleverer and return a circular pattern of divisions, 214 214 // not this square over-approximation 215 215 216 return GetInRange(pos - CFixedVector2D(range, range), pos + CFixedVector2D(range, range) );216 return GetInRange(pos - CFixedVector2D(range, range), pos + CFixedVector2D(range, range), ents); 217 217 } 218 218 219 219 private: … … 221 221 // (avoiding out-of-bounds accesses, and rounding correctly so that 222 222 // points precisely between divisions are counted in both): 223 223 224 u32 GetI0( entity_pos_t x)224 u32 GetI0(const entity_pos_t x) const 225 225 { 226 226 return Clamp((x / m_DivisionSize).ToInt_RoundToInfinity()-1, 0, (int)m_DivisionsW-1); 227 227 } 228 228 229 u32 GetJ0( entity_pos_t z)229 u32 GetJ0(const entity_pos_t z) const 230 230 { 231 231 return Clamp((z / m_DivisionSize).ToInt_RoundToInfinity()-1, 0, (int)m_DivisionsH-1); 232 232 } 233 233 234 u32 GetI1( entity_pos_t x)234 u32 GetI1(const entity_pos_t x) const 235 235 { 236 236 return Clamp((x / m_DivisionSize).ToInt_RoundToNegInfinity(), 0, (int)m_DivisionsW-1); 237 237 } 238 238 239 u32 GetJ1( entity_pos_t z)239 u32 GetJ1(const entity_pos_t z) const 240 240 { 241 241 return Clamp((z / m_DivisionSize).ToInt_RoundToNegInfinity(), 0, (int)m_DivisionsH-1); 242 242 } 243 243 244 u32 GetIndex0( CFixedVector2D pos)244 u32 GetIndex0(const CFixedVector2D& pos) const 245 245 { 246 246 return GetI0(pos.X) + GetJ0(pos.Y)*m_DivisionsW; 247 247 } 248 248 249 u32 GetIndex1( CFixedVector2D pos)249 u32 GetIndex1(const CFixedVector2D& pos) const 250 250 { 251 251 return GetI1(pos.X) + GetJ1(pos.Y)*m_DivisionsW; 252 252 } -
source/simulation2/helpers/Grid.h
80 80 memset(m_Data, 0, m_W*m_H*sizeof(T)); 81 81 } 82 82 83 void set( int i,int j, const T& value)83 void set(const int i, const int j, const T& value) 84 84 { 85 85 #if GRID_BOUNDS_DEBUG 86 86 ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H); 87 87 #endif 88 88 m_Data[j*m_W + i] = value; 89 89 } 90 91 void add(const int i, const int j, T val) 92 { 93 #if GRID_BOUNDS_DEBUG 94 ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H); 95 #endif 96 m_Data[j*m_W + i] += val; 97 } 98 void getPtr(const int i, const int j) 99 { 100 #if GRID_BOUNDS_DEBUG 101 ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H); 102 #endif 103 return m_Data[j*m_W + i]; 104 } 90 105 91 T& get( int i,int j) const106 T& get(const int i, const int j) const 92 107 { 93 108 #if GRID_BOUNDS_DEBUG 94 109 ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H); … … 113 128 114 129 enum { BucketBits = 4, BucketSize = 1 << BucketBits }; 115 130 116 T* GetBucket( int i,int j)131 T* GetBucket(const int i, const int j) 117 132 { 118 size_t b = (j >> BucketBits) * m_BW + (i >> BucketBits);133 const size_t b = (j >> BucketBits) * m_BW + (i >> BucketBits); 119 134 if (!m_Data[b]) 120 135 { 121 m_Data[b] = new T[BucketSize*BucketSize]; 136 if (m_Reserve.empty()) 137 { 138 m_Data[b] = new T[BucketSize*BucketSize]; 139 } 140 else 141 { 142 m_Data[b] = m_Reserve.back(); 143 m_Reserve.pop_back(); 144 } 122 145 memset(m_Data[b], 0, BucketSize*BucketSize*sizeof(T)); 123 146 } 124 147 return m_Data[b]; … … 134 157 135 158 m_Data = new T*[m_BW*m_BH]; 136 159 memset(m_Data, 0, m_BW*m_BH*sizeof(T*)); 160 137 161 } 138 162 139 163 ~SparseGrid() 140 { 141 reset(); 164 { 165 for (size_t i = 0; i < (size_t)(m_BW*m_BH); ++i) 166 delete[] m_Data[i]; 142 167 delete[] m_Data; 143 168 } 144 169 145 170 void reset() 146 { 171 { 147 172 for (size_t i = 0; i < (size_t)(m_BW*m_BH); ++i) 148 delete[] m_Data[i]; 173 { 174 if (m_Data[i]) 175 { 176 m_Reserve.push_back(m_Data[i]); 177 } 178 } 149 179 150 180 memset(m_Data, 0, m_BW*m_BH*sizeof(T*)); 151 181 } 152 182 153 void set( int i,int j, const T& value)183 void set(const int i, const int j, const T& value) 154 184 { 155 185 #if GRID_BOUNDS_DEBUG 156 186 ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H); … … 158 188 GetBucket(i, j)[(j % BucketSize)*BucketSize + (i % BucketSize)] = value; 159 189 } 160 190 161 T& get( int i,int j)191 T& get(const int i, const int j) 162 192 { 163 193 #if GRID_BOUNDS_DEBUG 164 194 ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H); … … 169 199 u16 m_W, m_H; 170 200 u16 m_BW, m_BH; 171 201 T** m_Data; 202 std::deque < T* > m_Reserve; 172 203 173 204 size_t m_DirtyID; // if this is < the id maintained by ICmpObstructionManager then it needs to be updated 174 205 }; -
source/simulation2/helpers/Geometry.cpp
25 25 26 26 // TODO: all of these things could be optimised quite easily 27 27 28 bool Geometry::PointIsInSquare( CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2DhalfSize)28 bool Geometry::PointIsInSquare(const CFixedVector2D& point, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize) 29 29 { 30 fixed du = point.Dot(u);30 const fixed du = point.Dot(u); 31 31 if (-halfSize.X <= du && du <= halfSize.X) 32 32 { 33 fixed dv = point.Dot(v);33 const fixed dv = point.Dot(v); 34 34 if (-halfSize.Y <= dv && dv <= halfSize.Y) 35 35 return true; 36 36 } 37 37 return false; 38 38 } 39 39 40 CFixedVector2D Geometry::GetHalfBoundingBox( CFixedVector2D u, CFixedVector2D v, CFixedVector2DhalfSize)40 CFixedVector2D Geometry::GetHalfBoundingBox(const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize) 41 41 { 42 42 return CFixedVector2D( 43 43 u.X.Multiply(halfSize.X).Absolute() + v.X.Multiply(halfSize.Y).Absolute(), … … 50 50 return acosf(1.f - SQR(chordLength)/(2.f*SQR(radius))); // cfr. law of cosines 51 51 } 52 52 53 fixed Geometry::DistanceToSquare( CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2DhalfSize)53 fixed Geometry::DistanceToSquare(const CFixedVector2D& point, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize) 54 54 { 55 55 /* 56 56 * Relative to its own coordinate system, we have a square like: … … 79 79 */ 80 80 81 81 // du, dv are the location of the point in the square's coordinate system 82 fixed du = point.Dot(u);83 fixed dv = point.Dot(v);82 const fixed du = point.Dot(u); 83 const fixed dv = point.Dot(v); 84 84 85 fixed hw = halfSize.X;86 fixed hh = halfSize.Y;85 const fixed hw = halfSize.X; 86 const fixed hh = halfSize.Y; 87 87 88 88 // TODO: I haven't actually tested this 89 89 … … 116 116 } 117 117 } 118 118 119 CFixedVector2D Geometry::NearestPointOnSquare( CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2DhalfSize)119 CFixedVector2D Geometry::NearestPointOnSquare(const CFixedVector2D& point, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize) 120 120 { 121 121 /* 122 122 * Relative to its own coordinate system, we have a square like: … … 144 144 */ 145 145 146 146 // du, dv are the location of the point in the square's coordinate system 147 fixed du = point.Dot(u);148 fixed dv = point.Dot(v);147 const fixed du = point.Dot(u); 148 const fixed dv = point.Dot(v); 149 149 150 fixed hw = halfSize.X;151 fixed hh = halfSize.Y;150 const fixed hw = halfSize.X; 151 const fixed hh = halfSize.Y; 152 152 153 153 if (-hw < du && du < hw) // regions B, G; or regions D, E inside the square 154 154 { … … 190 190 } 191 191 } 192 192 193 bool Geometry::TestRaySquare( CFixedVector2D a, CFixedVector2D b, CFixedVector2D u, CFixedVector2D v, CFixedVector2DhalfSize)193 bool Geometry::TestRaySquare(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize) 194 194 { 195 195 /* 196 196 * We only consider collisions to be when the ray goes from outside to inside the shape (and possibly out again). … … 205 205 * (Points on the edge are considered 'inside'.) 206 206 */ 207 207 208 fixed hw = halfSize.X;209 fixed hh = halfSize.Y;208 const fixed hw = halfSize.X; 209 const fixed hh = halfSize.Y; 210 210 211 fixed au = a.Dot(u);212 fixed av = a.Dot(v);211 const fixed au = a.Dot(u); 212 const fixed av = a.Dot(v); 213 213 214 214 if (-hw <= au && au <= hw && -hh <= av && av <= hh) 215 215 return false; // a is inside 216 216 217 fixed bu = b.Dot(u);218 fixed bv = b.Dot(v);217 const fixed bu = b.Dot(u); 218 const fixed bv = b.Dot(v); 219 219 220 220 if (-hw <= bu && bu <= hw && -hh <= bv && bv <= hh) // TODO: isn't this subsumed by the next checks? 221 221 return true; // a is outside, b is inside … … 223 223 if ((au < -hw && bu < -hw) || (au > hw && bu > hw) || (av < -hh && bv < -hh) || (av > hh && bv > hh)) 224 224 return false; // ab is entirely above/below/side the square 225 225 226 CFixedVector2D abp = (b - a).Perpendicular();227 fixed s0 = abp.Dot((u.Multiply(hw) + v.Multiply(hh)) - a);228 fixed s1 = abp.Dot((u.Multiply(hw) - v.Multiply(hh)) - a);229 fixed s2 = abp.Dot((-u.Multiply(hw) - v.Multiply(hh)) - a);230 fixed s3 = abp.Dot((-u.Multiply(hw) + v.Multiply(hh)) - a);226 const CFixedVector2D abp ((b - a).Perpendicular()); 227 const fixed s0 = abp.Dot((u.Multiply(hw) + v.Multiply(hh)) - a); 228 const fixed s1 = abp.Dot((u.Multiply(hw) - v.Multiply(hh)) - a); 229 const fixed s2 = abp.Dot((-u.Multiply(hw) - v.Multiply(hh)) - a); 230 const fixed s3 = abp.Dot((-u.Multiply(hw) + v.Multiply(hh)) - a); 231 231 if (s0.IsZero() || s1.IsZero() || s2.IsZero() || s3.IsZero()) 232 232 return true; // ray intersects the corner 233 233 234 bool sign = (s0 < fixed::Zero());234 const bool sign = (s0 < fixed::Zero()); 235 235 if ((s1 < fixed::Zero()) != sign || (s2 < fixed::Zero()) != sign || (s3 < fixed::Zero()) != sign) 236 236 return true; // ray cuts through the square 237 237 238 238 return false; 239 239 } 240 240 241 bool Geometry::TestRayAASquare( CFixedVector2D a, CFixedVector2D b, CFixedVector2DhalfSize)241 bool Geometry::TestRayAASquare(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& halfSize) 242 242 { 243 243 // Exactly like TestRaySquare with u=(1,0), v=(0,1) 244 244 245 245 // Assume the compiler is clever enough to inline and simplify all this 246 246 // (TODO: stop assuming that) 247 CFixedVector2D u (fixed::FromInt(1), fixed::Zero());248 CFixedVector2D v (fixed::Zero(), fixed::FromInt(1));247 const CFixedVector2D u (fixed::FromInt(1), fixed::Zero()); 248 const CFixedVector2D v (fixed::Zero(), fixed::FromInt(1)); 249 249 250 fixed hw = halfSize.X;251 fixed hh = halfSize.Y;250 const fixed hw = halfSize.X; 251 const fixed hh = halfSize.Y; 252 252 253 fixed au = a.Dot(u);254 fixed av = a.Dot(v);253 const fixed au = a.Dot(u); 254 const fixed av = a.Dot(v); 255 255 256 256 if (-hw <= au && au <= hw && -hh <= av && av <= hh) 257 257 return false; // a is inside 258 258 259 fixed bu = b.Dot(u);260 fixed bv = b.Dot(v);259 const fixed bu = b.Dot(u); 260 const fixed bv = b.Dot(v); 261 261 262 262 if (-hw <= bu && bu <= hw && -hh <= bv && bv <= hh) // TODO: isn't this subsumed by the next checks? 263 263 return true; // a is outside, b is inside … … 265 265 if ((au < -hw && bu < -hw) || (au > hw && bu > hw) || (av < -hh && bv < -hh) || (av > hh && bv > hh)) 266 266 return false; // ab is entirely above/below/side the square 267 267 268 CFixedVector2D abp = (b - a).Perpendicular();269 fixed s0 = abp.Dot((u.Multiply(hw) + v.Multiply(hh)) - a);270 fixed s1 = abp.Dot((u.Multiply(hw) - v.Multiply(hh)) - a);271 fixed s2 = abp.Dot((-u.Multiply(hw) - v.Multiply(hh)) - a);272 fixed s3 = abp.Dot((-u.Multiply(hw) + v.Multiply(hh)) - a);268 const CFixedVector2D abp ((b - a).Perpendicular()); 269 const fixed s0 = abp.Dot((u.Multiply(hw) + v.Multiply(hh)) - a); 270 const fixed s1 = abp.Dot((u.Multiply(hw) - v.Multiply(hh)) - a); 271 const fixed s2 = abp.Dot((-u.Multiply(hw) - v.Multiply(hh)) - a); 272 const fixed s3 = abp.Dot((-u.Multiply(hw) + v.Multiply(hh)) - a); 273 273 if (s0.IsZero() || s1.IsZero() || s2.IsZero() || s3.IsZero()) 274 274 return true; // ray intersects the corner 275 275 276 bool sign = (s0 < fixed::Zero());276 const bool sign = (s0 < fixed::Zero()); 277 277 if ((s1 < fixed::Zero()) != sign || (s2 < fixed::Zero()) != sign || (s3 < fixed::Zero()) != sign) 278 278 return true; // ray cuts through the square 279 279 … … 284 284 * Separating axis test; returns true if the square defined by u/v/halfSize at the origin 285 285 * is not entirely on the clockwise side of a line in direction 'axis' passing through 'a' 286 286 */ 287 static bool SquareSAT( CFixedVector2D a, CFixedVector2D axis, CFixedVector2D u, CFixedVector2D v, CFixedVector2DhalfSize)287 static bool SquareSAT(const CFixedVector2D& a, const CFixedVector2D& axis, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize) 288 288 { 289 fixed hw = halfSize.X;290 fixed hh = halfSize.Y;289 const fixed hw = halfSize.X; 290 const fixed hh = halfSize.Y; 291 291 292 CFixedVector2D p = axis.Perpendicular();292 const CFixedVector2D p (axis.Perpendicular()); 293 293 if (p.Dot((u.Multiply(hw) + v.Multiply(hh)) - a) <= fixed::Zero()) 294 294 return true; 295 295 if (p.Dot((u.Multiply(hw) - v.Multiply(hh)) - a) <= fixed::Zero()) … … 303 303 } 304 304 305 305 bool Geometry::TestSquareSquare( 306 CFixedVector2D c0, CFixedVector2D u0, CFixedVector2D v0, CFixedVector2DhalfSize0,307 CFixedVector2D c1, CFixedVector2D u1, CFixedVector2D v1, CFixedVector2DhalfSize1)306 const CFixedVector2D& c0, const CFixedVector2D& u0, const CFixedVector2D& v0, const CFixedVector2D& halfSize0, 307 const CFixedVector2D& c1, const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1) 308 308 { 309 309 // TODO: need to test this carefully 310 310 311 CFixedVector2D corner0a = c0 + u0.Multiply(halfSize0.X) + v0.Multiply(halfSize0.Y);312 CFixedVector2D corner0b = c0 - u0.Multiply(halfSize0.X) - v0.Multiply(halfSize0.Y);313 CFixedVector2D corner1a = c1 + u1.Multiply(halfSize1.X) + v1.Multiply(halfSize1.Y);314 CFixedVector2D corner1b = c1 - u1.Multiply(halfSize1.X) - v1.Multiply(halfSize1.Y);311 const CFixedVector2D corner0a ( c0 + u0.Multiply(halfSize0.X) + v0.Multiply(halfSize0.Y)); 312 const CFixedVector2D corner0b ( c0 - u0.Multiply(halfSize0.X) - v0.Multiply(halfSize0.Y)); 313 const CFixedVector2D corner1a ( c1 + u1.Multiply(halfSize1.X) + v1.Multiply(halfSize1.Y)); 314 const CFixedVector2D corner1b ( c1 - u1.Multiply(halfSize1.X) - v1.Multiply(halfSize1.Y)); 315 315 316 316 // Do a SAT test for each square vs each edge of the other square 317 317 if (!SquareSAT(corner0a - c1, -u0, u1, v1, halfSize1)) -
source/simulation2/serialization/SerializeTemplates.h
86 86 } 87 87 } 88 88 }; 89 90 89 // We have to order the map before serializing to make things consistent 91 90 template<typename KS, typename VS> 92 91 struct SerializeUnorderedMap -
source/simulation2/system/InterfaceScripted.h
61 61 2, \ 62 62 JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT }, 63 63 64 64 65 #define DEFINE_INTERFACE_METHOD_3(scriptname, rettype, classname, methodname, arg1, arg2, arg3) \ 65 66 { scriptname, \ 66 67 ScriptInterface::callMethod<rettype, arg1, arg2, arg3, &class_##classname, classname, &classname::methodname>, \ … … 85 86 6, \ 86 87 JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT }, 87 88 89 88 90 #endif // INCLUDED_INTERFACE_SCRIPTED -
source/simulation2/components/ICmpPathfinder.h
101 101 * The waypoints correspond to the centers of horizontally/vertically adjacent tiles 102 102 * along the path. 103 103 */ 104 virtual void ComputePath( entity_pos_t x0, entity_pos_t z0, const Goal& goal, pass_class_t passClass,cost_class_t costClass, Path& ret) = 0;104 virtual void ComputePath(const entity_pos_t x0, const entity_pos_t z0, const Goal& goal, const pass_class_t passClass, const cost_class_t costClass, Path& ret) = 0; 105 105 106 106 /** 107 107 * Asynchronous version of ComputePath. … … 109 109 * Returns a unique non-zero number, which will match the 'ticket' in the result, 110 110 * so callers can recognise each individual request they make. 111 111 */ 112 virtual u32 ComputePathAsync( entity_pos_t x0, entity_pos_t z0, const Goal& goal, pass_class_t passClass, cost_class_t costClass,entity_id_t notify) = 0;112 virtual u32 ComputePathAsync(const entity_pos_t x0, const entity_pos_t z0, const Goal& goal, const pass_class_t passClass, const cost_class_t costClass, const entity_id_t notify) = 0; 113 113 114 114 /** 115 115 * If the debug overlay is enabled, render the path that will computed by ComputePath. 116 116 */ 117 virtual void SetDebugPath( entity_pos_t x0, entity_pos_t z0, const Goal& goal, pass_class_t passClass,cost_class_t costClass) = 0;117 virtual void SetDebugPath(const entity_pos_t x0, const entity_pos_t z0, const Goal& goal, const pass_class_t passClass, const cost_class_t costClass) = 0; 118 118 119 119 /** 120 120 * Compute a precise path from the given point to the goal, and return the set of waypoints. … … 122 122 * a unit of radius 'r' will be able to follow the path with no collisions. 123 123 * The path is restricted to a box of radius 'range' from the starting point. 124 124 */ 125 virtual void ComputeShortPath(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t r, entity_pos_t range, const Goal& goal,pass_class_t passClass, Path& ret) = 0;125 virtual void ComputeShortPath(const IObstructionTestFilter& filter, const entity_pos_t x0, const entity_pos_t z0, const entity_pos_t r, const entity_pos_t range, const Goal& goal, const pass_class_t passClass, Path& ret) = 0; 126 126 127 127 /** 128 128 * Asynchronous version of ComputeShortPath (using ControlGroupObstructionFilter). … … 130 130 * Returns a unique non-zero number, which will match the 'ticket' in the result, 131 131 * so callers can recognise each individual request they make. 132 132 */ 133 virtual u32 ComputeShortPathAsync( entity_pos_t x0, entity_pos_t z0, entity_pos_t r, entity_pos_t range, const Goal& goal, pass_class_t passClass, bool avoidMovingUnits, entity_id_t group,entity_id_t notify) = 0;133 virtual u32 ComputeShortPathAsync(const entity_pos_t x0, const entity_pos_t z0, const entity_pos_t r, const entity_pos_t range, const Goal& goal, const pass_class_t passClass, const bool avoidMovingUnits, const entity_id_t controller, const entity_id_t notify) = 0; 134 134 135 135 /** 136 136 * Find the speed factor (typically around 1.0) for a unit of the given cost class 137 137 * at the given position. 138 138 */ 139 virtual fixed GetMovementSpeed( entity_pos_t x0, entity_pos_t z0, cost_class_t costClass)= 0;139 virtual fixed GetMovementSpeed( const entity_pos_t x0, const entity_pos_t z0, const cost_class_t costClass) = 0; 140 140 141 141 /** 142 142 * Returns the coordinates of the point on the goal that is closest to pos in a straight line. 143 143 */ 144 virtual CFixedVector2D GetNearestPointOnGoal( CFixedVector2D pos, const Goal& goal)= 0;144 virtual CFixedVector2D GetNearestPointOnGoal(const CFixedVector2D& pos, const Goal& goal) const = 0; 145 145 146 146 /** 147 147 * Check whether the given movement line is valid and doesn't hit any obstructions -
source/simulation2/components/ICmpRangeManager.h
196 196 /** 197 197 * Returns whether the given vertex is visible (i.e. is within a unit's LOS). 198 198 */ 199 inline bool IsVisible( ssize_t i, ssize_t j)199 inline bool IsVisible(const ssize_t i, const ssize_t j) const 200 200 { 201 201 if (!(i >= 0 && j >= 0 && i < m_VerticesPerSide && j < m_VerticesPerSide)) 202 202 return false; … … 211 211 /** 212 212 * Returns whether the given vertex is explored (i.e. was (or still is) within a unit's LOS). 213 213 */ 214 inline bool IsExplored( ssize_t i, ssize_t j)214 inline bool IsExplored(const ssize_t i, const ssize_t j) const 215 215 { 216 216 if (!(i >= 0 && j >= 0 && i < m_VerticesPerSide && j < m_VerticesPerSide)) 217 217 return false; … … 227 227 * Returns whether the given vertex is visible (i.e. is within a unit's LOS). 228 228 * i and j must be in the range [0, verticesPerSide), else behaviour is undefined. 229 229 */ 230 inline bool IsVisible_UncheckedRange( ssize_t i, ssize_t j)230 inline bool IsVisible_UncheckedRange(const ssize_t i, const ssize_t j) const 231 231 { 232 232 #ifndef NDEBUG 233 233 ENSURE(i >= 0 && j >= 0 && i < m_VerticesPerSide && j < m_VerticesPerSide); … … 243 243 * Returns whether the given vertex is explored (i.e. was (or still is) within a unit's LOS). 244 244 * i and j must be in the range [0, verticesPerSide), else behaviour is undefined. 245 245 */ 246 inline bool IsExplored_UncheckedRange( ssize_t i, ssize_t j)246 inline bool IsExplored_UncheckedRange(const ssize_t i, const ssize_t j) const 247 247 { 248 248 #ifndef NDEBUG 249 249 ENSURE(i >= 0 && j >= 0 && i < m_VerticesPerSide && j < m_VerticesPerSide); … … 275 275 * TODO: This is a hack to allow preview entities in FoW to return fogged instead of hidden, 276 276 * see http://trac.wildfiregames.com/ticket/958 277 277 */ 278 virtual ELosVisibility GetLosVisibility( entity_id_t ent, player_id_t player,bool forceRetainInFog = false) = 0;278 virtual ELosVisibility GetLosVisibility(const entity_id_t ent, const player_id_t player, const bool forceRetainInFog = false) = 0; 279 279 280 280 /** 281 281 * GetLosVisibility wrapped for script calls. … … 287 287 * Set whether the whole map should be made visible to the given player. 288 288 * If player is -1, the map will be made visible to all players. 289 289 */ 290 virtual void SetLosRevealAll( player_id_t player,bool enabled) = 0;290 virtual void SetLosRevealAll(const player_id_t player, const bool enabled) = 0; 291 291 292 292 /** 293 293 * Returns whether the whole map has been made visible to the given player. 294 294 */ 295 virtual bool GetLosRevealAll( player_id_t player) = 0;295 virtual bool GetLosRevealAll(const player_id_t player) = 0; 296 296 297 297 /** 298 298 * Set the LOS to be restricted to a circular map. … … 312 312 /** 313 313 * Returns shared LOS mask for player. 314 314 */ 315 virtual u32 GetSharedLosMask( player_id_t player) = 0;315 virtual u32 GetSharedLosMask(const player_id_t player) = 0; 316 316 317 317 /** 318 318 * Get percent map explored statistics for specified player. 319 319 */ 320 virtual i32 GetPercentMapExplored( player_id_t player) = 0;320 virtual i32 GetPercentMapExplored(const player_id_t player) = 0; 321 321 322 322 323 323 /** -
source/simulation2/components/CCmpPosition.cpp
419 419 { 420 420 const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg); 421 421 422 float rotY = m_RotY.ToFloat();422 const float rotY = m_RotY.ToFloat(); 423 423 float delta = rotY - m_InterpolatedRotY; 424 // Wrap delta to -M_PI..M_PI 425 delta = fmodf(delta + (float)M_PI, 2*(float)M_PI); // range -2PI..2PI 426 if (delta < 0) delta += 2*(float)M_PI; // range 0..2PI 427 delta -= (float)M_PI; // range -M_PI..M_PI 428 // Clamp to max rate 429 float deltaClamped = clamp(delta, -m_RotYSpeed*msgData.deltaSimTime, +m_RotYSpeed*msgData.deltaSimTime); 430 // Calculate new orientation, in a peculiar way in order to make sure the 431 // result gets close to m_orientation (rather than being n*2*M_PI out) 432 m_InterpolatedRotY = rotY + deltaClamped - delta; 424 if (delta != 0.){ 425 // Wrap delta to -M_PI..M_PI 426 delta = fmodf(delta + (float)M_PI, 2*(float)M_PI); // range -2PI..2PI 427 if (delta < 0) delta += 2*(float)M_PI; // range 0..2PI 428 delta -= (float)M_PI; // range -M_PI..M_PI 429 // Clamp to max rate 430 const float deltaClamped = clamp(delta, -m_RotYSpeed*msgData.deltaSimTime, +m_RotYSpeed*msgData.deltaSimTime); 431 // Calculate new orientation, in a peculiar way in order to make sure the 432 // result gets close to m_orientation (rather than being n*2*M_PI out) 433 m_InterpolatedRotY = rotY + deltaClamped - delta; 434 } 433 435 434 436 break; 435 437 } -
source/simulation2/components/CCmpObstructionManager.cpp
84 84 serialize.NumberFixed_Unbounded("r", value.r); 85 85 serialize.NumberU8_Unbounded("flags", value.flags); 86 86 serialize.NumberU32_Unbounded("group", value.group); 87 } 87 }; 88 88 }; 89 89 90 90 /** … … 107 107 serialize.NumberU8_Unbounded("flags", value.flags); 108 108 serialize.NumberU32_Unbounded("group", value.group); 109 109 serialize.NumberU32_Unbounded("group2", value.group2); 110 } 110 }; 111 111 }; 112 112 113 113 class CCmpObstructionManager : public ICmpObstructionManager … … 116 116 static void ClassInit(CComponentManager& componentManager) 117 117 { 118 118 componentManager.SubscribeToMessageType(MT_RenderSubmit); // for debug overlays 119 } 119 }; 120 120 121 121 DEFAULT_COMPONENT_ALLOCATOR(ObstructionManager) 122 122 … … 517 517 /** 518 518 * Return whether the given point is within the world bounds by at least r 519 519 */ 520 bool IsInWorld(entity_pos_t x, entity_pos_t z, entity_pos_t r)520 inline bool IsInWorld(const entity_pos_t x, const entity_pos_t z, const entity_pos_t r) const 521 521 { 522 522 return (m_WorldX0+r <= x && x <= m_WorldX1-r && m_WorldZ0+r <= z && z <= m_WorldZ1-r); 523 523 } … … 525 525 /** 526 526 * Return whether the given point is within the world bounds 527 527 */ 528 bool IsInWorld(CFixedVector2D p)528 inline bool IsInWorld(const CFixedVector2D& p) const 529 529 { 530 530 return (m_WorldX0 <= p.X && p.X <= m_WorldX1 && m_WorldZ0 <= p.Y && p.Y <= m_WorldZ1); 531 531 } … … 541 541 if (!IsInWorld(x0, z0, r) || !IsInWorld(x1, z1, r)) 542 542 return true; 543 543 544 CFixedVector2D posMin (std::min(x0, x1) - r, std::min(z0, z1) - r); 545 CFixedVector2D posMax (std::max(x0, x1) + r, std::max(z0, z1) + r); 546 547 std::vector<u32> unitShapes = m_UnitSubdivision.GetInRange(posMin, posMax); 548 for (size_t i = 0; i < unitShapes.size(); ++i) 544 const CFixedVector2D posMin (std::min(x0, x1) - r, std::min(z0, z1) - r); 545 const CFixedVector2D posMax (std::max(x0, x1) + r, std::max(z0, z1) + r); 549 546 { 550 std::map<u32, UnitShape>::iterator it = m_UnitShapes.find(unitShapes[i]); 551 ENSURE(it != m_UnitShapes.end()); 547 std::vector<u32> unitShapes; 548 m_UnitSubdivision.GetInRange(posMin, posMax, unitShapes); 549 const std::vector<u32>::iterator itEnd = unitShapes.end(); 550 for (std::vector<u32>::iterator itShape = unitShapes.begin(); itShape != itEnd; ++itShape) 551 { 552 //std::map<u32, UnitShape>::iterator it = m_UnitShapes.find(unitShapes[i]); 553 std::map<u32, UnitShape>::iterator it = m_UnitShapes.find(*itShape); 554 ENSURE(it != m_UnitShapes.end()); 552 555 553 if (!filter.TestShape(UNIT_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, INVALID_ENTITY))554 continue;556 if (!filter.TestShape(UNIT_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, INVALID_ENTITY)) 557 continue; 555 558 556 CFixedVector2D center(it->second.x, it->second.z); 557 CFixedVector2D halfSize(it->second.r + r, it->second.r + r); 558 if (Geometry::TestRayAASquare(CFixedVector2D(x0, z0) - center, CFixedVector2D(x1, z1) - center, halfSize)) 559 return true; 559 const CFixedVector2D center(it->second.x, it->second.z); 560 const CFixedVector2D halfSize(it->second.r + r, it->second.r + r); 561 if (Geometry::TestRayAASquare(CFixedVector2D(x0, z0) - center, CFixedVector2D(x1, z1) - center, halfSize)) 562 return true; 563 } 560 564 } 561 562 std::vector<u32> staticShapes = m_StaticSubdivision.GetInRange(posMin, posMax);563 for (size_t i = 0; i < staticShapes.size(); ++i)564 565 { 565 std::map<u32, StaticShape>::iterator it = m_StaticShapes.find(staticShapes[i]); 566 ENSURE(it != m_StaticShapes.end()); 566 std::vector<u32> staticShapes; 567 m_StaticSubdivision.GetInRange(posMin, posMax, staticShapes); 568 const std::vector<u32>::iterator itEnd = staticShapes.end(); 569 for (std::vector<u32>::iterator itShape = staticShapes.begin(); itShape != itEnd; ++itShape) 570 { 571 std::map<u32, StaticShape>::iterator it = m_StaticShapes.find(*itShape); 572 ENSURE(it != m_StaticShapes.end()); 567 573 568 if (!filter.TestShape(STATIC_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, it->second.group2))569 continue;574 if (!filter.TestShape(STATIC_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, it->second.group2)) 575 continue; 570 576 571 CFixedVector2D center(it->second.x, it->second.z); 572 CFixedVector2D halfSize(it->second.hw + r, it->second.hh + r); 573 if (Geometry::TestRaySquare(CFixedVector2D(x0, z0) - center, CFixedVector2D(x1, z1) - center, it->second.u, it->second.v, halfSize)) 574 return true; 577 const CFixedVector2D center(it->second.x, it->second.z); 578 const CFixedVector2D halfSize(it->second.hw + r, it->second.hh + r); 579 if (Geometry::TestRaySquare(CFixedVector2D(x0, z0) - center, CFixedVector2D(x1, z1) - center, it->second.u, it->second.v, halfSize)) 580 return true; 581 } 575 582 } 576 583 577 584 return false; … … 590 597 591 598 fixed s, c; 592 599 sincos_approx(a, s, c); 593 CFixedVector2D u(c, -s);594 CFixedVector2D v(s, c);595 CFixedVector2D center(x, z);596 CFixedVector2D halfSize(w/2, h/2);600 const CFixedVector2D u(c, -s); 601 const CFixedVector2D v(s, c); 602 const CFixedVector2D center(x, z); 603 const CFixedVector2D halfSize(w/2, h/2); 597 604 598 605 // Check that all corners are within the world (which means the whole shape must be) 599 606 if (!IsInWorld(center + u.Multiply(halfSize.X) + v.Multiply(halfSize.Y)) || … … 607 614 return true; 608 615 } 609 616 610 for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it) 617 const std::map<u32, UnitShape>::iterator itUEnd = m_UnitShapes.end(); 618 for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != itUEnd; ++it) 611 619 { 612 620 if (!filter.TestShape(UNIT_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, INVALID_ENTITY)) 613 621 continue; … … 623 631 } 624 632 } 625 633 626 for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it) 634 const std::map<u32, StaticShape>::iterator itSEnd = m_StaticShapes.end(); 635 for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != itSEnd; ++it) 627 636 { 628 637 if (!filter.TestShape(STATIC_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, it->second.group2)) 629 638 continue; 630 639 631 CFixedVector2D center1(it->second.x, it->second.z);632 CFixedVector2D halfSize1(it->second.hw, it->second.hh);640 const CFixedVector2D center1(it->second.x, it->second.z); 641 const CFixedVector2D halfSize1(it->second.hw, it->second.hh); 633 642 if (Geometry::TestSquareSquare(center, u, v, halfSize, center1, it->second.u, it->second.v, halfSize1)) 634 643 { 635 644 if (out) … … 664 673 665 674 CFixedVector2D center(x, z); 666 675 667 for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it) 676 const std::map<u32, UnitShape>::iterator itUEnd = m_UnitShapes.end(); 677 for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != itUEnd; ++it) 668 678 { 669 679 if (!filter.TestShape(UNIT_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, INVALID_ENTITY)) 670 680 continue; 671 681 672 entity_pos_t r1 = it->second.r;682 const entity_pos_t r1 = it->second.r; 673 683 674 684 if (!(it->second.x + r1 < x - r || it->second.x - r1 > x + r || it->second.z + r1 < z - r || it->second.z - r1 > z + r)) 675 685 { … … 680 690 } 681 691 } 682 692 683 for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it) 693 const std::map<u32, StaticShape>::iterator itSEnd = m_StaticShapes.end(); 694 for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != itSEnd; ++it) 684 695 { 685 696 if (!filter.TestShape(STATIC_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, it->second.group2)) 686 697 continue; 687 698 688 CFixedVector2D center1(it->second.x, it->second.z);699 const CFixedVector2D center1(it->second.x, it->second.z); 689 700 if (Geometry::PointIsInSquare(center1 - center, it->second.u, it->second.v, CFixedVector2D(it->second.hw + r, it->second.hh + r))) 690 701 { 691 702 if (out) … … 750 761 // so we need to expand by at least 1/sqrt(2) of a tile 751 762 entity_pos_t expandFoundation = (entity_pos_t::FromInt(TERRAIN_TILE_SIZE) * 3) / 4; 752 763 753 for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it) 764 const std::map<u32, StaticShape>::iterator itSEnd = m_StaticShapes.end(); 765 for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != itSEnd; ++it) 754 766 { 755 767 CFixedVector2D center(it->second.x, it->second.z); 756 768 … … 776 788 777 789 if (it->second.flags & FLAG_BLOCK_FOUNDATION) 778 790 { 779 CFixedVector2D halfSize(it->second.hw + expandFoundation, it->second.hh + expandFoundation);780 CFixedVector2D halfBound = Geometry::GetHalfBoundingBox(it->second.u, it->second.v, halfSize);791 const CFixedVector2D halfSize(it->second.hw + expandFoundation, it->second.hh + expandFoundation); 792 const CFixedVector2D halfBound(Geometry::GetHalfBoundingBox(it->second.u, it->second.v, halfSize)); 781 793 782 794 u16 i0, j0, i1, j1; 783 795 NearestTile(center.X - halfBound.X, center.Y - halfBound.Y, i0, j0, grid.m_W, grid.m_H); … … 795 807 } 796 808 } 797 809 798 for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it) 810 const std::map<u32, UnitShape>::iterator itUEnd = m_UnitShapes.end(); 811 for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != itUEnd; ++it) 799 812 { 800 CFixedVector2D center(it->second.x, it->second.z);813 const CFixedVector2D center(it->second.x, it->second.z); 801 814 802 815 if (it->second.flags & FLAG_BLOCK_PATHFINDING) 803 816 { … … 813 826 814 827 if (it->second.flags & FLAG_BLOCK_FOUNDATION) 815 828 { 816 entity_pos_t r = it->second.r + expandFoundation;829 const entity_pos_t r = it->second.r + expandFoundation; 817 830 818 831 u16 i0, j0, i1, j1; 819 832 NearestTile(center.X - r, center.Y - r, i0, j0, grid.m_W, grid.m_H); … … 880 893 881 894 ENSURE(x0 <= x1 && z0 <= z1); 882 895 883 std::vector<u32> unitShapes = m_UnitSubdivision.GetInRange(CFixedVector2D(x0, z0), CFixedVector2D(x1, z1)); 884 for (size_t i = 0; i < unitShapes.size(); ++i) 896 std::vector<u32> unitShapes; 897 m_UnitSubdivision.GetInRange(CFixedVector2D(x0, z0), CFixedVector2D(x1, z1), unitShapes); 898 const std::vector<u32>::iterator itUEnd = unitShapes.end(); 899 for (std::vector<u32>::iterator itShape = unitShapes.begin(); itShape != itUEnd; ++itShape) 885 900 { 886 std::map<u32, UnitShape>::iterator it = m_UnitShapes.find( unitShapes[i]);901 std::map<u32, UnitShape>::iterator it = m_UnitShapes.find(*itShape); 887 902 ENSURE(it != m_UnitShapes.end()); 888 903 889 904 if (!filter.TestShape(UNIT_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, INVALID_ENTITY)) … … 901 916 squares.push_back(s); 902 917 } 903 918 904 std::vector<u32> staticShapes = m_StaticSubdivision.GetInRange(CFixedVector2D(x0, z0), CFixedVector2D(x1, z1)); 905 for (size_t i = 0; i < staticShapes.size(); ++i) 919 std::vector<u32> staticShapes; 920 m_StaticSubdivision.GetInRange(CFixedVector2D(x0, z0), CFixedVector2D(x1, z1), staticShapes); 921 const std::vector<u32>::iterator itSEnd = staticShapes.end(); 922 for (std::vector<u32>::iterator itShape = staticShapes.begin(); itShape != itSEnd; ++itShape) 906 923 { 907 std::map<u32, StaticShape>::iterator it = m_StaticShapes.find(staticShapes[i]); 924 //std::map<u32, StaticShape>::iterator it = m_StaticShapes.find(staticShapes[i]); 925 std::map<u32, StaticShape>::iterator it = m_StaticShapes.find(*itShape); 908 926 ENSURE(it != m_StaticShapes.end()); 909 927 910 928 if (!filter.TestShape(STATIC_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, it->second.group2)) … … 981 999 (m_WorldX1-m_WorldX0).ToFloat(), (m_WorldZ1-m_WorldZ0).ToFloat(), 982 1000 0, m_DebugOverlayLines.back(), true); 983 1001 984 for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it)985 1002 { 986 m_DebugOverlayLines.push_back(SOverlayLine()); 987 m_DebugOverlayLines.back().m_Color = ((it->second.flags & FLAG_MOVING) ? movingColour : defaultColour); 988 SimRender::ConstructSquareOnGround(GetSimContext(), it->second.x.ToFloat(), it->second.z.ToFloat(), it->second.r.ToFloat()*2, it->second.r.ToFloat()*2, 0, m_DebugOverlayLines.back(), true); 1003 const std::map<u32, UnitShape>::iterator itUEnd = m_UnitShapes.end(); 1004 for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != itUEnd; ++it) 1005 { 1006 m_DebugOverlayLines.push_back(SOverlayLine()); 1007 m_DebugOverlayLines.back().m_Color = ((it->second.flags & FLAG_MOVING) ? movingColour : defaultColour); 1008 SimRender::ConstructSquareOnGround(GetSimContext(), it->second.x.ToFloat(), it->second.z.ToFloat(), it->second.r.ToFloat()*2, it->second.r.ToFloat()*2, 0, m_DebugOverlayLines.back(), true); 1009 } 989 1010 } 990 991 for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it)992 1011 { 993 m_DebugOverlayLines.push_back(SOverlayLine()); 994 m_DebugOverlayLines.back().m_Color = defaultColour; 995 float a = atan2f(it->second.v.X.ToFloat(), it->second.v.Y.ToFloat()); 996 SimRender::ConstructSquareOnGround(GetSimContext(), it->second.x.ToFloat(), it->second.z.ToFloat(), it->second.hw.ToFloat()*2, it->second.hh.ToFloat()*2, a, m_DebugOverlayLines.back(), true); 1012 const std::map<u32, StaticShape>::iterator itSEnd = m_StaticShapes.end(); 1013 for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != itSEnd; ++it) 1014 { 1015 m_DebugOverlayLines.push_back(SOverlayLine()); 1016 m_DebugOverlayLines.back().m_Color = defaultColour; 1017 float a = atan2f(it->second.v.X.ToFloat(), it->second.v.Y.ToFloat()); 1018 SimRender::ConstructSquareOnGround(GetSimContext(), it->second.x.ToFloat(), it->second.z.ToFloat(), it->second.hw.ToFloat()*2, it->second.hh.ToFloat()*2, a, m_DebugOverlayLines.back(), true); 1019 } 997 1020 } 998 999 1021 m_DebugOverlayDirty = false; 1000 1022 } 1001 1023 -
source/simulation2/components/CCmpUnitMotion.cpp
521 521 /** 522 522 * Do the per-turn movement and other updates. 523 523 */ 524 void Move( fixed dt);524 void Move(const fixed dt); 525 525 526 526 /** 527 527 * Decide whether to approximate the given range from a square target as a circle, 528 528 * rather than as a square. 529 529 */ 530 bool ShouldTreatTargetAsCircle( entity_pos_t range, entity_pos_t hw, entity_pos_t hh,entity_pos_t circleRadius);530 bool ShouldTreatTargetAsCircle(const entity_pos_t range, const entity_pos_t hw, const entity_pos_t hh, const entity_pos_t circleRadius); 531 531 532 532 /** 533 533 * Computes the current location of our target entity (plus offset). … … 539 539 * Attempts to replace the current path with a straight line to the target 540 540 * entity, if it's close enough and the route is not obstructed. 541 541 */ 542 bool TryGoingStraightToTargetEntity( CFixedVector2Dfrom);542 bool TryGoingStraightToTargetEntity(const CFixedVector2D& from); 543 543 544 544 /** 545 545 * Returns whether the target entity has moved more than minDelta since our 546 546 * last path computations, and we're close enough to it to care. 547 547 */ 548 bool CheckTargetMovement( CFixedVector2D from,entity_pos_t minDelta);548 bool CheckTargetMovement(const CFixedVector2D& from, const entity_pos_t minDelta); 549 549 550 550 /** 551 551 * Returns whether the length of the given path, plus the distance from 552 552 * 'from' to the first waypoints, it shorter than minDistance. 553 553 */ 554 bool PathIsShort(const ICmpPathfinder::Path& path, CFixedVector2D from,entity_pos_t minDistance);554 bool PathIsShort(const ICmpPathfinder::Path& path, const CFixedVector2D& from, const entity_pos_t minDistance); 555 555 556 556 /** 557 557 * Rotate to face towards the target point, given the current pos 558 558 */ 559 void FaceTowardsPointFromPos( CFixedVector2D pos, entity_pos_t x,entity_pos_t z);559 void FaceTowardsPointFromPos(const CFixedVector2D& pos, const entity_pos_t x, const entity_pos_t z); 560 560 561 561 /** 562 562 * Returns an appropriate obstruction filter for use with path requests. … … 568 568 * Might go in a straight line immediately, or might start an asynchronous 569 569 * path request. 570 570 */ 571 void BeginPathing( CFixedVector2Dfrom, const ICmpPathfinder::Goal& goal);571 void BeginPathing(const CFixedVector2D& from, const ICmpPathfinder::Goal& goal); 572 572 573 573 /** 574 574 * Start an asynchronous long path query. 575 575 */ 576 void RequestLongPath( CFixedVector2Dfrom, const ICmpPathfinder::Goal& goal);576 void RequestLongPath(const CFixedVector2D& from, const ICmpPathfinder::Goal& goal); 577 577 578 578 /** 579 579 * Start an asynchronous short path query. 580 580 */ 581 void RequestShortPath( CFixedVector2Dfrom, const ICmpPathfinder::Goal& goal, bool avoidMovingUnits);581 void RequestShortPath(const CFixedVector2D& from, const ICmpPathfinder::Goal& goal, bool avoidMovingUnits); 582 582 583 583 /** 584 584 * Select a next long waypoint, given the current unit position. … … 753 753 } 754 754 } 755 755 756 void CCmpUnitMotion::Move( fixed dt)756 void CCmpUnitMotion::Move(const fixed dt) 757 757 { 758 758 PROFILE("Move"); 759 759 … … 802 802 if (!cmpPosition || !cmpPosition->IsInWorld()) 803 803 return; 804 804 805 CFixedVector2D initialPos = cmpPosition->GetPosition2D();805 const CFixedVector2D initialPos (cmpPosition->GetPosition2D()); 806 806 807 807 // If we're chasing a potentially-moving unit and are currently close 808 808 // enough to its current position, and we can head in a straight line … … 811 811 TryGoingStraightToTargetEntity(initialPos); 812 812 813 813 // Keep track of the current unit's position during the update 814 CFixedVector2D pos = initialPos;814 CFixedVector2D pos (initialPos); 815 815 816 816 // If in formation, run to keep up; otherwise just walk 817 817 // (TODO: support stamina, charging, etc) … … 824 824 // Find the speed factor of the underlying terrain 825 825 // (We only care about the tile we start on - it doesn't matter if we're moving 826 826 // partially onto a much slower/faster tile) 827 fixed terrainSpeed = cmpPathfinder->GetMovementSpeed(pos.X, pos.Y, m_CostClass);827 const fixed terrainSpeed = cmpPathfinder->GetMovementSpeed(pos.X, pos.Y, m_CostClass); 828 828 829 fixed maxSpeed = basicSpeed.Multiply(terrainSpeed);829 const fixed maxSpeed = basicSpeed.Multiply(terrainSpeed); 830 830 831 831 bool wasObstructed = false; 832 832 … … 841 841 break; 842 842 843 843 CFixedVector2D target(m_ShortPath.m_Waypoints.back().x, m_ShortPath.m_Waypoints.back().z); 844 CFixedVector2D offset = target - pos;844 CFixedVector2D offset (target - pos); 845 845 846 846 // Face towards the target 847 847 if (!offset.IsZero()) … … 924 924 // If we are close to reaching the end of the short path 925 925 // (or have reached it already), try to extend it 926 926 927 entity_pos_t minDistance = basicSpeed.Multiply(dt) * WAYPOINT_ADVANCE_LOOKAHEAD_TURNS;927 const entity_pos_t minDistance = basicSpeed.Multiply(dt) * WAYPOINT_ADVANCE_LOOKAHEAD_TURNS; 928 928 if (PathIsShort(m_ShortPath, pos, minDistance)) 929 929 { 930 930 // Start the path extension from the end of this short path 931 931 // (or our current position if no short path) 932 CFixedVector2D from = pos;932 CFixedVector2D from (pos); 933 933 if (!m_ShortPath.m_Waypoints.empty()) 934 934 from = CFixedVector2D(m_ShortPath.m_Waypoints[0].x, m_ShortPath.m_Waypoints[0].z); 935 935 … … 1010 1010 return true; 1011 1011 } 1012 1012 1013 bool CCmpUnitMotion::TryGoingStraightToTargetEntity( CFixedVector2Dfrom)1013 bool CCmpUnitMotion::TryGoingStraightToTargetEntity(const CFixedVector2D& from) 1014 1014 { 1015 1015 CFixedVector2D targetPos; 1016 1016 if (!ComputeTargetPosition(targetPos)) … … 1048 1048 return true; 1049 1049 } 1050 1050 1051 bool CCmpUnitMotion::CheckTargetMovement( CFixedVector2Dfrom, entity_pos_t minDelta)1051 bool CCmpUnitMotion::CheckTargetMovement(const CFixedVector2D& from, entity_pos_t minDelta) 1052 1052 { 1053 1053 CFixedVector2D targetPos; 1054 1054 if (!ComputeTargetPosition(targetPos)) … … 1087 1087 return true; 1088 1088 } 1089 1089 1090 bool CCmpUnitMotion::PathIsShort(const ICmpPathfinder::Path& path, CFixedVector2D from,entity_pos_t minDistance)1090 bool CCmpUnitMotion::PathIsShort(const ICmpPathfinder::Path& path, const CFixedVector2D& from, const entity_pos_t minDistance) 1091 1091 { 1092 CFixedVector2D pos = from;1092 CFixedVector2D pos (from); 1093 1093 entity_pos_t distLeft = minDistance; 1094 1094 1095 for (ssize_t i = (ssize_t)path.m_Waypoints.size()-1; i >= 0; --i) 1095 const std::vector<ICmpPathfinder::Waypoint>::const_reverse_iterator itEnd = path.m_Waypoints.rend(); 1096 for (std::vector<ICmpPathfinder::Waypoint>::const_reverse_iterator it = path.m_Waypoints.rbegin(); it != itEnd; ++it) 1096 1097 { 1098 const ICmpPathfinder::Waypoint& waypoint = *it; 1097 1099 // Check if the next path segment is longer than the requested minimum 1098 CFixedVector2D waypoint(path.m_Waypoints[i].x, path.m_Waypoints[i].z);1099 CFixedVector2D delta = waypoint - pos;1100 const CFixedVector2D waypoint_pos(waypoint.x, waypoint.z); 1101 const CFixedVector2D delta (waypoint_pos - pos); 1100 1102 if (delta.CompareLength(distLeft) > 0) 1101 1103 return false; 1102 1104 1103 1105 // Still short enough - prepare to check the next segment 1106 //todo: perf: reuse sqlenth from just above computation. 1104 1107 distLeft -= delta.Length(); 1105 pos = waypoint ;1108 pos = waypoint_pos; 1106 1109 } 1107 1110 1108 1111 // Reached the end of the path before exceeding minDistance 1109 1112 return true; 1110 1113 } 1111 1114 1112 void CCmpUnitMotion::FaceTowardsPoint( entity_pos_t x,entity_pos_t z)1115 void CCmpUnitMotion::FaceTowardsPoint(const entity_pos_t x, const entity_pos_t z) 1113 1116 { 1114 1117 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); 1115 1118 if (!cmpPosition || !cmpPosition->IsInWorld()) 1116 1119 return; 1117 1120 1118 CFixedVector2D pos = cmpPosition->GetPosition2D();1121 const CFixedVector2D pos (cmpPosition->GetPosition2D()); 1119 1122 FaceTowardsPointFromPos(pos, x, z); 1120 1123 } 1121 1124 1122 void CCmpUnitMotion::FaceTowardsPointFromPos( CFixedVector2D pos, entity_pos_t x,entity_pos_t z)1125 void CCmpUnitMotion::FaceTowardsPointFromPos(const CFixedVector2D& pos, const entity_pos_t x, const entity_pos_t z) 1123 1126 { 1124 1127 CFixedVector2D target(x, z); 1125 CFixedVector2D offset = target - pos;1128 CFixedVector2D offset (target - pos); 1126 1129 if (!offset.IsZero()) 1127 1130 { 1128 1131 entity_angle_t angle = atan2_approx(offset.X, offset.Y); … … 1147 1150 1148 1151 1149 1152 1150 void CCmpUnitMotion::BeginPathing( CFixedVector2Dfrom, const ICmpPathfinder::Goal& goal)1153 void CCmpUnitMotion::BeginPathing(const CFixedVector2D& from, const ICmpPathfinder::Goal& goal) 1151 1154 { 1152 1155 // Cancel any pending path requests 1153 1156 m_ExpectedPathTicket = 0; … … 1181 1184 RequestLongPath(from, goal); 1182 1185 } 1183 1186 1184 void CCmpUnitMotion::RequestLongPath( CFixedVector2Dfrom, const ICmpPathfinder::Goal& goal)1187 void CCmpUnitMotion::RequestLongPath(const CFixedVector2D& from, const ICmpPathfinder::Goal& goal) 1185 1188 { 1186 1189 CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY); 1187 1190 if (!cmpPathfinder) … … 1192 1195 m_ExpectedPathTicket = cmpPathfinder->ComputePathAsync(from.X, from.Y, goal, m_PassClass, m_CostClass, GetEntityId()); 1193 1196 } 1194 1197 1195 void CCmpUnitMotion::RequestShortPath( CFixedVector2D from, const ICmpPathfinder::Goal& goal,bool avoidMovingUnits)1198 void CCmpUnitMotion::RequestShortPath(const CFixedVector2D& from, const ICmpPathfinder::Goal& goal, const bool avoidMovingUnits) 1196 1199 { 1197 1200 CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY); 1198 1201 if (!cmpPathfinder) … … 1201 1204 m_ExpectedPathTicket = cmpPathfinder->ComputeShortPathAsync(from.X, from.Y, m_Radius, SHORT_PATH_SEARCH_RANGE, goal, m_PassClass, avoidMovingUnits, m_TargetEntity, GetEntityId()); 1202 1205 } 1203 1206 1204 bool CCmpUnitMotion::PickNextLongWaypoint(const CFixedVector2D& pos, bool avoidMovingUnits)1207 bool CCmpUnitMotion::PickNextLongWaypoint(const CFixedVector2D& pos, const bool avoidMovingUnits) 1205 1208 { 1206 1209 // If there's no long path, we can't pick the next waypoint from it 1207 1210 if (m_LongPath.m_Waypoints.empty()) … … 1252 1255 } 1253 1256 1254 1257 1255 bool CCmpUnitMotion::MoveToPointRange( entity_pos_t x, entity_pos_t z, entity_pos_t minRange,entity_pos_t maxRange)1258 bool CCmpUnitMotion::MoveToPointRange(const entity_pos_t x, const entity_pos_t z, const entity_pos_t minRange, const entity_pos_t maxRange) 1256 1259 { 1257 1260 PROFILE("MoveToPointRange"); 1258 1261 … … 1338 1341 return true; 1339 1342 } 1340 1343 1341 bool CCmpUnitMotion::IsInPointRange( entity_pos_t x, entity_pos_t z, entity_pos_t minRange,entity_pos_t maxRange)1344 bool CCmpUnitMotion::IsInPointRange(const entity_pos_t x, const entity_pos_t z, const entity_pos_t minRange, const entity_pos_t maxRange) 1342 1345 { 1343 1346 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); 1344 1347 if (!cmpPosition || !cmpPosition->IsInWorld()) 1345 1348 return false; 1346 1349 1347 CFixedVector2D pos = cmpPosition->GetPosition2D();1350 const CFixedVector2D pos (cmpPosition->GetPosition2D()); 1348 1351 1349 1352 bool hasObstruction = false; 1350 1353 CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); … … 1355 1358 if (minRange.IsZero() && maxRange.IsZero() && hasObstruction) 1356 1359 { 1357 1360 // Handle the non-ranged mode: 1358 CFixedVector2D halfSize(obstruction.hw, obstruction.hh);1359 entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize);1361 const CFixedVector2D halfSize(obstruction.hw, obstruction.hh); 1362 const entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize); 1360 1363 1361 1364 // See if we're too close to the target square 1362 1365 if (distance < minRange) … … 1370 1373 } 1371 1374 else 1372 1375 { 1373 entity_pos_t distance = (pos - CFixedVector2D(x, z)).Length();1376 const entity_pos_t distance = (pos - CFixedVector2D(x, z)).Length(); 1374 1377 1375 1378 if (distance < minRange) 1376 1379 { … … 1387 1390 } 1388 1391 } 1389 1392 1390 bool CCmpUnitMotion::ShouldTreatTargetAsCircle( entity_pos_t range, entity_pos_t hw, entity_pos_t hh,entity_pos_t circleRadius)1393 bool CCmpUnitMotion::ShouldTreatTargetAsCircle(const entity_pos_t range, const entity_pos_t hw, const entity_pos_t hh, const entity_pos_t circleRadius) 1391 1394 { 1392 1395 // Given a square, plus a target range we should reach, the shape at that distance 1393 1396 // is a round-cornered square which we can approximate as either a circle or as a square. … … 1402 1405 return (errCircle < errSquare); 1403 1406 } 1404 1407 1405 bool CCmpUnitMotion::MoveToTargetRange( entity_id_t target, entity_pos_t minRange,entity_pos_t maxRange)1408 bool CCmpUnitMotion::MoveToTargetRange(const entity_id_t target, const entity_pos_t minRange, const entity_pos_t maxRange) 1406 1409 { 1407 1410 PROFILE("MoveToTargetRange"); 1408 1411 … … 1546 1549 } 1547 1550 } 1548 1551 1549 bool CCmpUnitMotion::IsInTargetRange( entity_id_t target, entity_pos_t minRange,entity_pos_t maxRange)1552 bool CCmpUnitMotion::IsInTargetRange(const entity_id_t target, const entity_pos_t minRange, const entity_pos_t maxRange) 1550 1553 { 1551 1554 // This function closely mirrors MoveToTargetRange - it needs to return true 1552 1555 // after that Move has completed … … 1612 1615 } 1613 1616 } 1614 1617 1615 void CCmpUnitMotion::MoveToFormationOffset( entity_id_t target, entity_pos_t x,entity_pos_t z)1618 void CCmpUnitMotion::MoveToFormationOffset(const entity_id_t target, const entity_pos_t x, const entity_pos_t z) 1616 1619 { 1617 1620 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), target); 1618 1621 if (!cmpPosition || !cmpPosition->IsInWorld()) 1619 1622 return; 1620 1623 1621 CFixedVector2D pos = cmpPosition->GetPosition2D();1624 CFixedVector2D pos (cmpPosition->GetPosition2D()); 1622 1625 1623 1626 ICmpPathfinder::Goal goal; 1624 1627 goal.type = ICmpPathfinder::Goal::POINT; -
source/simulation2/components/tests/test_Pathfinder.h
68 68 CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself 69 69 70 70 LDR_BeginRegistering(); 71 mapReader->LoadMap(L"maps/scenarios/Median Oasis .pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL,71 mapReader->LoadMap(L"maps/scenarios/Median Oasis 01.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 72 72 &sim2, &sim2.GetSimContext(), -1, false); 73 73 LDR_EndRegistering(); 74 74 TS_ASSERT_OK(LDR_NonprogressiveLoad()); … … 90 90 printf("%d: %f %f\n", (int)i, path.m_Waypoints[i].x.ToDouble(), path.m_Waypoints[i].z.ToDouble()); 91 91 #endif 92 92 93 double t = timer_Time();94 93 95 94 srand(1234); 95 96 std::vector<ICmpPathfinder::Goal> goals; 97 98 std::vector<entity_pos_t> x0s; 99 std::vector<entity_pos_t> z0s; 96 100 for (size_t j = 0; j < 1024*2; ++j) 97 101 { 98 102 entity_pos_t x0 = entity_pos_t::FromInt(rand() % 512); … … 100 104 entity_pos_t x1 = x0 + entity_pos_t::FromInt(rand() % 64); 101 105 entity_pos_t z1 = z0 + entity_pos_t::FromInt(rand() % 64); 102 106 ICmpPathfinder::Goal goal = { ICmpPathfinder::Goal::POINT, x1, z1 }; 107 x0s.push_back(x0); 108 z0s.push_back(z0); 109 goals.push_back(goal); 110 } 103 111 104 ICmpPathfinder::Path path; 105 cmp->ComputePath(x0, z0, goal, cmp->GetPassabilityClass("default"), cmp->GetCostClass("default"), path); 112 double t = timer_Time(); 113 114 115 ICmpPathfinder::Path path; 116 for (size_t j = 0; j < 1024*2; ++j) 117 { 118 cmp->ComputePath(x0s[j], z0s[j], goals[j], cmp->GetPassabilityClass("default"), cmp->GetCostClass("default"), path); 106 119 } 107 120 108 121 t = timer_Time() - t; 109 printf(" [%f]", t);122 printf("\nTestCmpPathfinder ComputePath[%f]", t); 110 123 } 111 124 112 125 void test_performance_short_DISABLED() … … 124 137 CmpPtr<ICmpPathfinder> cmpPathfinder(sim2, SYSTEM_ENTITY); 125 138 126 139 srand(0); 140 127 141 for (size_t i = 0; i < 200; ++i) 128 142 { 129 143 fixed x = fixed::FromFloat(1.5f*range.ToFloat() * rand()/(float)RAND_MAX); … … 131 145 // printf("# %f %f\n", x.ToFloat(), z.ToFloat()); 132 146 cmpObstructionMan->AddUnitShape(INVALID_ENTITY, x, z, fixed::FromInt(2), 0, INVALID_ENTITY); 133 147 } 148 149 double t = timer_Time(); 134 150 135 NullObstructionFilter filter; 136 ICmpPathfinder::Goal goal = { ICmpPathfinder::Goal::POINT, range, range }; 137 ICmpPathfinder::Path path; 138 cmpPathfinder->ComputeShortPath(filter, range/3, range/3, fixed::FromInt(2), range, goal, 0, path); 139 for (size_t i = 0; i < path.m_Waypoints.size(); ++i) 140 printf("# %d: %f %f\n", (int)i, path.m_Waypoints[i].x.ToFloat(), path.m_Waypoints[i].z.ToFloat()); 151 for (size_t j = 0; j < 1024*2; ++j) 152 { 153 154 NullObstructionFilter filter; 155 ICmpPathfinder::Goal goal = { ICmpPathfinder::Goal::POINT, range, range }; 156 ICmpPathfinder::Path path; 157 158 cmpPathfinder->ComputeShortPath(filter, range/3, range/3, fixed::FromInt(2), range, goal, 0, path); 159 } 160 161 t = timer_Time() - t; 162 163 printf("\nTestCmpPathfinder ComputeShortPath[%f]\n", t); 164 { 165 NullObstructionFilter filter; 166 ICmpPathfinder::Goal goal = { ICmpPathfinder::Goal::POINT, range, range }; 167 ICmpPathfinder::Path path; 168 169 cmpPathfinder->ComputeShortPath(filter, range/3, range/3, fixed::FromInt(2), range, goal, 0, path); 170 for (size_t i = 0; i < path.m_Waypoints.size(); ++i) 171 printf("# %d: %f %f\n", (int)i, path.m_Waypoints[i].x.ToFloat(), path.m_Waypoints[i].z.ToFloat()); 172 } 141 173 } 142 174 }; -
source/simulation2/components/CCmpPathfinder_Vertex.cpp
63 63 * Therefore it must continue to a vertex in the BR quadrant (so this vertex is in 64 64 * *that* vertex's TL quadrant). 65 65 * 66 * That lets us significantly reduce the search space by quickly discarding vert exes66 * That lets us significantly reduce the search space by quickly discarding vertices 67 67 * from the wrong quadrants. 68 68 * 69 69 * (This causes badness if the path starts from inside the shape, so we add some hacks … … 82 82 static const u8 QUADRANT_ALL = QUADRANT_BLTR|QUADRANT_TLBR; 83 83 84 84 // A vertex around the corners of an obstruction 85 // (paths will be sequences of these vert exes)85 // (paths will be sequences of these vertices) 86 86 struct Vertex 87 87 { 88 88 enum … … 118 118 fixed c1; 119 119 }; 120 120 121 // When computing vert exes to insert into the search graph,122 // add a small delta so that the vert exes of an edge don't get interpreted121 // When computing vertices to insert into the search graph, 122 // add a small delta so that the vertices of an edge don't get interpreted 123 123 // as crossing the edge (given minor numerical inaccuracies) 124 124 static const entity_pos_t EDGE_EXPAND_DELTA = entity_pos_t::FromInt(1)/4; 125 125 … … 127 127 * Check whether a ray from 'a' to 'b' crosses any of the edges. 128 128 * (Edges are one-sided so it's only considered a cross if going from front to back.) 129 129 */ 130 inline static bool CheckVisibility( CFixedVector2D a, CFixedVector2D b, const std::vector<Edge>& edges)130 inline static bool CheckVisibility(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& abn, const std::vector<Edge>& edges) 131 131 { 132 CFixedVector2D abn = (b - a).Perpendicular(); 133 132 fixed s; 134 133 // Edges of general non-axis-aligned shapes 135 for (size_t i = 0; i < edges.size(); ++i) 134 const std::vector<Edge>::const_iterator itEnd = edges.end(); 135 for (std::vector<Edge>::const_iterator it = edges.begin(); it != itEnd; ++it) 136 136 { 137 CFixedVector2D p0 = edges[i].p0;138 CFixedVector2D p1 = edges[i].p1;137 const Edge e = *it; 138 const CFixedVector2D& p0 (e.p0); 139 139 140 CFixedVector2D d = (p1 - p0).Perpendicular(); 140 // The ray is crossing the infinitely-extended edge from in front to behind. 141 // Check the finite edge is crossing the infinitely-extended ray too. 142 // (Given the previous tests, it can only be crossing in one direction.) 143 s = p0.SubAndDot(a, abn); 144 if (s > fixed::Zero()) 145 continue; 141 146 147 const CFixedVector2D& p1 (e.p1); 148 149 s = p1.SubAndDot(a, abn); 150 if (s < fixed::Zero()) 151 continue; 152 153 const CFixedVector2D d ((p1 - p0).Perpendicular()); 154 142 155 // If 'a' is behind the edge, we can't cross 143 fixed q = (a - p0).Dot(d);144 if ( q< fixed::Zero())156 s = a.SubAndDot(p0, d); 157 if (s < fixed::Zero()) 145 158 continue; 146 159 147 160 // If 'b' is in front of the edge, we can't cross 148 fixed r = (b - p0).Dot(d); 149 if (r > fixed::Zero()) 150 continue; 151 152 // The ray is crossing the infinitely-extended edge from in front to behind. 153 // Check the finite edge is crossing the infinitely-extended ray too. 154 // (Given the previous tests, it can only be crossing in one direction.) 155 fixed s = (p0 - a).Dot(abn); 161 s = b.SubAndDot(p0, d); 156 162 if (s > fixed::Zero()) 157 163 continue; 158 164 159 fixed t = (p1 - a).Dot(abn);160 if (t < fixed::Zero())161 continue;162 163 165 return false; 164 166 } 165 167 … … 170 172 // (These are specialised versions of the general unaligned edge code. 171 173 // They assume the caller has already excluded edges for which 'a' is 172 174 // on the wrong side.) 173 174 inline static bool CheckVisibilityLeft(CFixedVector2D a, CFixedVector2D b, const std::vector<EdgeAA>& edges) 175 inline static bool CheckVisibilityLeft(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& abn, const std::vector<EdgeAA>& edges) 175 176 { 176 177 if (a.X >= b.X) 177 178 return true; 178 179 CFixedVector2D abn = (b - a).Perpendicular(); 180 181 for (size_t i = 0; i < edges.size(); ++i) 179 fixed s; 180 const std::vector<EdgeAA>::const_iterator itEnd = edges.end(); 181 for (std::vector<EdgeAA>::const_iterator it = edges.begin(); it != itEnd; ++it) 182 182 { 183 if (b.X < edges[i].p0.X) 184 continue; 185 186 CFixedVector2D p0 (edges[i].p0.X, edges[i].c1); 187 fixed s = (p0 - a).Dot(abn); 188 if (s > fixed::Zero()) 189 continue; 190 191 CFixedVector2D p1 (edges[i].p0.X, edges[i].p0.Y); 192 fixed t = (p1 - a).Dot(abn); 193 if (t < fixed::Zero()) 194 continue; 195 196 return false; 183 const EdgeAA& e = *it; 184 if ( b.X >= e.p0.X){ // diff line from right 185 s = a.SubFromAndDot(e.p0.X, e.c1, abn); 186 if (s.value <= 0){ 187 s = a.SubFromAndDot(e.p0.X, e.p0.Y, abn); 188 if (s.value >= 0){ 189 return false; 190 } 191 } 192 } 197 193 } 198 194 199 195 return true; 200 196 } 201 197 202 inline static bool CheckVisibilityRight( CFixedVector2D a, CFixedVector2D b,const std::vector<EdgeAA>& edges)198 inline static bool CheckVisibilityRight(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& abn,const std::vector<EdgeAA>& edges) 203 199 { 204 200 if (a.X <= b.X) 205 201 return true; 206 207 CFixedVector2D abn = (b - a).Perpendicular(); 208 209 for (size_t i = 0; i < edges.size(); ++i) 202 fixed s; 203 const std::vector<EdgeAA>::const_iterator itEnd = edges.end(); 204 for (std::vector<EdgeAA>::const_iterator it = edges.begin(); it != itEnd; ++it) 210 205 { 211 if (b.X > edges[i].p0.X) 212 continue; 213 214 CFixedVector2D p0 (edges[i].p0.X, edges[i].c1); 215 fixed s = (p0 - a).Dot(abn); 216 if (s > fixed::Zero()) 217 continue; 218 219 CFixedVector2D p1 (edges[i].p0.X, edges[i].p0.Y); 220 fixed t = (p1 - a).Dot(abn); 221 if (t < fixed::Zero()) 222 continue; 223 224 return false; 206 const EdgeAA& e = *it; 207 if ( b.X <= e.p0.X){ // diff line from left 208 s = a.SubFromAndDot(e.p0.X, e.c1, abn); 209 if (s.value <= 0){ 210 s = a.SubFromAndDot(e.p0.X, e.p0.Y, abn); 211 if (s.value >= 0){ 212 return false; 213 } 214 } 215 } 225 216 } 226 217 227 218 return true; 228 219 } 229 220 230 inline static bool CheckVisibilityBottom( CFixedVector2D a, CFixedVector2D b, const std::vector<EdgeAA>& edges)221 inline static bool CheckVisibilityBottom(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& abn, const std::vector<EdgeAA>& edges) 231 222 { 232 223 if (a.Y >= b.Y) 233 224 return true; 234 235 CFixedVector2D abn = (b - a).Perpendicular(); 236 237 for (size_t i = 0; i < edges.size(); ++i) 225 fixed s; 226 const std::vector<EdgeAA>::const_iterator itEnd = edges.end(); 227 for (std::vector<EdgeAA>::const_iterator it = edges.begin(); it != itEnd; ++it) 238 228 { 239 if (b.Y < edges[i].p0.Y) 240 continue; 241 242 CFixedVector2D p0 (edges[i].p0.X, edges[i].p0.Y); 243 fixed s = (p0 - a).Dot(abn); 244 if (s > fixed::Zero()) 245 continue; 246 247 CFixedVector2D p1 (edges[i].c1, edges[i].p0.Y); 248 fixed t = (p1 - a).Dot(abn); 249 if (t < fixed::Zero()) 250 continue; 251 252 return false; 229 const EdgeAA& e = *it; 230 if ( b.Y >= e.p0.Y){ // diff line from top 231 s = a.SubFromAndDot(e.p0.X, e.p0.Y, abn); 232 if (s.value <= 0){ 233 s = a.SubFromAndDot(e.c1, e.p0.Y, abn); 234 if (s.value >= 0){ 235 return false; 236 } 237 } 238 } 253 239 } 254 240 255 241 return true; 256 242 } 257 243 258 inline static bool CheckVisibilityTop( CFixedVector2D a, CFixedVector2D b,const std::vector<EdgeAA>& edges)244 inline static bool CheckVisibilityTop(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& abn,const std::vector<EdgeAA>& edges) 259 245 { 260 246 if (a.Y <= b.Y) 261 247 return true; 262 263 CFixedVector2D abn = (b - a).Perpendicular();264 265 for (s ize_t i = 0; i < edges.size(); ++i)248 249 fixed s; 250 const std::vector<EdgeAA>::const_iterator itEnd = edges.end(); 251 for (std::vector<EdgeAA>::const_iterator it = edges.begin(); it != itEnd; ++it) 266 252 { 267 if (b.Y > edges[i].p0.Y) 268 continue; 253 const EdgeAA& e = *it; 254 if ( b.Y <= e.p0.Y){ // diff line from bottom 255 s = a.SubFromAndDot(e.p0.X, e.p0.Y, abn); 256 if (s.value <= 0){ 257 s = a.SubFromAndDot(e.c1, e.p0.Y, abn); 258 if (s.value >= 0){ 259 return false; 260 } 261 } 262 } 263 } 269 264 270 CFixedVector2D p0 (edges[i].p0.X, edges[i].p0.Y);271 fixed s = (p0 - a).Dot(abn);272 if (s > fixed::Zero())273 continue;274 265 275 CFixedVector2D p1 (edges[i].c1, edges[i].p0.Y);276 fixed t = (p1 - a).Dot(abn);277 if (t < fixed::Zero())278 continue;279 280 return false;281 }282 283 266 return true; 284 267 } 285 268 286 269 287 static CFixedVector2D NearestPointOnGoal(CFixedVector2D pos, const CCmpPathfinder::Goal& goal) 270 271 static CFixedVector2D NearestPointOnGoal(const CFixedVector2D& pos, const CCmpPathfinder::Goal& goal) 288 272 { 289 CFixedVector2D g(goal.x, goal.z);273 const CFixedVector2D g(goal.x, goal.z); 290 274 291 275 switch (goal.type) 292 276 { … … 306 290 307 291 case CCmpPathfinder::Goal::SQUARE: 308 292 { 309 CFixedVector2D halfSize(goal.hw, goal.hh);310 CFixedVector2D d = pos - g;293 const CFixedVector2D halfSize(goal.hw, goal.hh); 294 const CFixedVector2D d(pos - g); 311 295 return g + Geometry::NearestPointOnSquare(d, goal.u, goal.v, halfSize); 312 296 } 313 297 … … 317 301 } 318 302 } 319 303 320 CFixedVector2D CCmpPathfinder::GetNearestPointOnGoal( CFixedVector2D pos, const CCmpPathfinder::Goal& goal)304 CFixedVector2D CCmpPathfinder::GetNearestPointOnGoal(const CFixedVector2D& pos, const CCmpPathfinder::Goal& goal) const 321 305 { 322 306 return NearestPointOnGoal(pos, goal); 323 307 // (It's intentional that we don't put the implementation inside this … … 333 317 enum { TOP, BOTTOM, LEFT, RIGHT } dir; 334 318 }; 335 319 336 static void AddTerrainEdges(std::vector<Edge>& edgesAA, std::vector<Vertex>& vertexes, 337 u16 i0, u16 j0, u16 i1, u16 j1, fixed r, 338 ICmpPathfinder::pass_class_t passClass, const Grid<TerrainTile>& terrain) 320 std::vector<TileEdge> tileEdges; 321 322 static void AddTerrainEdges(std::vector<Edge>& edgesAA, std::vector<Vertex>& vertices, 323 const u16 i0, const u16 j0, const u16 i1, const u16 j1, const fixed r, 324 const ICmpPathfinder::pass_class_t passClass, const Grid<TerrainTile>& terrain) 339 325 { 340 326 PROFILE("AddTerrainEdges"); 341 327 342 std::vector<TileEdge> tileEdges; 328 tileEdges.clear(); 329 // Find all edges between tiles of differently passability statuses 343 330 344 // Find all edges between tiles of differently passability statuses 331 bool any; 332 const u16 terrainW = terrain.m_W; 333 const u16 terrainmWOne = terrainW-1; 334 const u16 terrainmHOne = terrain.m_H-1; 335 const TerrainTile * const __restrict data = terrain.m_Data; 345 336 for (u16 j = j0; j <= j1; ++j) 346 337 { 338 const bool jInsideUp = j < terrainmHOne; 339 const bool jInsideDown = j != 0; 340 const u32 jPos = j*terrainW; 347 341 for (u16 i = i0; i <= i1; ++i) 348 342 { 349 if (!IS_TERRAIN_PASSABLE( terrain.get(i, j), passClass))343 if (!IS_TERRAIN_PASSABLE(data[i+jPos], passClass)) 350 344 { 351 boolany = false; // whether we're adding any edges of this tile345 any = false; // whether we're adding any edges of this tile 352 346 353 if (j > 0 && IS_TERRAIN_PASSABLE(terrain.get(i, j-1), passClass))347 if (jInsideDown && IS_TERRAIN_PASSABLE(data[i+jPos-terrainW], passClass)) 354 348 { 355 TileEdge e = { i, j, TileEdge::BOTTOM };349 const TileEdge e = { i, j, TileEdge::BOTTOM }; 356 350 tileEdges.push_back(e); 357 351 any = true; 358 352 } 359 353 360 if (j < terrain.m_H-1 && IS_TERRAIN_PASSABLE(terrain.get(i, j+1), passClass))354 if (jInsideUp && IS_TERRAIN_PASSABLE(data[i+jPos+terrainW], passClass)) 361 355 { 362 TileEdge e = { i, j, TileEdge::TOP };356 const TileEdge e = { i, j, TileEdge::TOP }; 363 357 tileEdges.push_back(e); 364 358 any = true; 365 359 } 366 360 367 if (i > 0 && IS_TERRAIN_PASSABLE(terrain.get(i-1, j), passClass))361 if (i != 0 && IS_TERRAIN_PASSABLE(data[i+jPos-1], passClass)) 368 362 { 369 TileEdge e = { i, j, TileEdge::LEFT };363 const TileEdge e = { i, j, TileEdge::LEFT }; 370 364 tileEdges.push_back(e); 371 365 any = true; 372 366 } 373 367 374 if (i < terrain .m_W-1 && IS_TERRAIN_PASSABLE(terrain.get(i+1, j), passClass))368 if (i < terrainmWOne && IS_TERRAIN_PASSABLE(data[i+jPos+1], passClass)) 375 369 { 376 TileEdge e = { i, j, TileEdge::RIGHT };370 const TileEdge e = { i, j, TileEdge::RIGHT }; 377 371 tileEdges.push_back(e); 378 372 any = true; 379 373 } … … 382 376 // (The inner edges are redundant but it's easier than trying to split the squares apart.) 383 377 if (any) 384 378 { 385 CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(i * (int)TERRAIN_TILE_SIZE) - r, fixed::FromInt(j * (int)TERRAIN_TILE_SIZE) - r);386 CFixedVector2D v1 = CFixedVector2D(fixed::FromInt((i+1) * (int)TERRAIN_TILE_SIZE) + r, fixed::FromInt((j+1) * (int)TERRAIN_TILE_SIZE) + r);387 Edge e = { v0, v1 };379 const CFixedVector2D v0 (fixed::FromInt(i * (int)TERRAIN_TILE_SIZE) - r, fixed::FromInt(j * (int)TERRAIN_TILE_SIZE) - r); 380 const CFixedVector2D v1 (fixed::FromInt((i+1) * (int)TERRAIN_TILE_SIZE) + r, fixed::FromInt((j+1) * (int)TERRAIN_TILE_SIZE) + r); 381 const Edge e = { v0, v1 }; 388 382 edgesAA.push_back(e); 389 383 } 390 384 } … … 396 390 // TODO: for efficiency (minimising the A* search space), we should coalesce adjoining edges 397 391 398 392 // Add all the tile outer edges to the search vertex lists 399 for (size_t n = 0; n < tileEdges.size(); ++n)400 {401 u16 i = tileEdges[n].i;402 u16 j = tileEdges[n].j;403 CFixedVector2D v0, v1;404 Vertex vert;405 vert.status = Vertex::UNEXPLORED;406 vert.quadOutward = QUADRANT_ALL;407 393 408 switch (tileEdges[n].dir) 394 Vertex vert; 395 vert.status = Vertex::UNEXPLORED; 396 vert.quadOutward = QUADRANT_ALL; 397 const std::vector<TileEdge>::const_iterator itEnd = tileEdges.end(); 398 for (std::vector<TileEdge>::const_iterator it = tileEdges.begin(); it != itEnd; ++it) 399 { 400 const TileEdge& tileEdge = *it; 401 const u16 i = tileEdge.i; 402 const u16 j = tileEdge.j; 403 404 switch (tileEdge.dir) 409 405 { 410 406 case TileEdge::BOTTOM: 411 407 { 412 v0 = CFixedVector2D(fixed::FromInt(i * (int)TERRAIN_TILE_SIZE) - r, fixed::FromInt(j * (int)TERRAIN_TILE_SIZE) - r);413 v1 = CFixedVector2D(fixed::FromInt((i+1) * (int)TERRAIN_TILE_SIZE) + r, fixed::FromInt(j * (int)TERRAIN_TILE_SIZE) - r);414 vert.p.X = v0.X - EDGE_EXPAND_DELTA; vert.p.Y = v0.Y - EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_TR; vert exes.push_back(vert);415 vert.p.X = v1.X + EDGE_EXPAND_DELTA; vert.p.Y = v1.Y - EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_TL; vert exes.push_back(vert);408 const CFixedVector2D v0 (fixed::FromInt(i * (int)TERRAIN_TILE_SIZE) - r, fixed::FromInt(j * (int)TERRAIN_TILE_SIZE) - r); 409 const CFixedVector2D v1 (fixed::FromInt((i+1) * (int)TERRAIN_TILE_SIZE) + r, fixed::FromInt(j * (int)TERRAIN_TILE_SIZE) - r); 410 vert.p.X = v0.X - EDGE_EXPAND_DELTA; vert.p.Y = v0.Y - EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_TR; vertices.push_back(vert); 411 vert.p.X = v1.X + EDGE_EXPAND_DELTA; vert.p.Y = v1.Y - EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_TL; vertices.push_back(vert); 416 412 break; 417 413 } 418 414 case TileEdge::TOP: 419 415 { 420 v0 = CFixedVector2D(fixed::FromInt((i+1) * (int)TERRAIN_TILE_SIZE) + r, fixed::FromInt((j+1) * (int)TERRAIN_TILE_SIZE) + r);421 v1 = CFixedVector2D(fixed::FromInt(i * (int)TERRAIN_TILE_SIZE) - r, fixed::FromInt((j+1) * (int)TERRAIN_TILE_SIZE) + r);422 vert.p.X = v0.X + EDGE_EXPAND_DELTA; vert.p.Y = v0.Y + EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_BL; vert exes.push_back(vert);423 vert.p.X = v1.X - EDGE_EXPAND_DELTA; vert.p.Y = v1.Y + EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_BR; vert exes.push_back(vert);416 const CFixedVector2D v0 (fixed::FromInt((i+1) * (int)TERRAIN_TILE_SIZE) + r, fixed::FromInt((j+1) * (int)TERRAIN_TILE_SIZE) + r); 417 const CFixedVector2D v1 (fixed::FromInt(i * (int)TERRAIN_TILE_SIZE) - r, fixed::FromInt((j+1) * (int)TERRAIN_TILE_SIZE) + r); 418 vert.p.X = v0.X + EDGE_EXPAND_DELTA; vert.p.Y = v0.Y + EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_BL; vertices.push_back(vert); 419 vert.p.X = v1.X - EDGE_EXPAND_DELTA; vert.p.Y = v1.Y + EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_BR; vertices.push_back(vert); 424 420 break; 425 421 } 426 422 case TileEdge::LEFT: 427 423 { 428 v0 = CFixedVector2D(fixed::FromInt(i * (int)TERRAIN_TILE_SIZE) - r, fixed::FromInt((j+1) * (int)TERRAIN_TILE_SIZE) + r);429 v1 = CFixedVector2D(fixed::FromInt(i * (int)TERRAIN_TILE_SIZE) - r, fixed::FromInt(j * (int)TERRAIN_TILE_SIZE) - r);430 vert.p.X = v0.X - EDGE_EXPAND_DELTA; vert.p.Y = v0.Y + EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_BR; vert exes.push_back(vert);431 vert.p.X = v1.X - EDGE_EXPAND_DELTA; vert.p.Y = v1.Y - EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_TR; vert exes.push_back(vert);424 const CFixedVector2D v0 (fixed::FromInt(i * (int)TERRAIN_TILE_SIZE) - r, fixed::FromInt((j+1) * (int)TERRAIN_TILE_SIZE) + r); 425 const CFixedVector2D v1 (fixed::FromInt(i * (int)TERRAIN_TILE_SIZE) - r, fixed::FromInt(j * (int)TERRAIN_TILE_SIZE) - r); 426 vert.p.X = v0.X - EDGE_EXPAND_DELTA; vert.p.Y = v0.Y + EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_BR; vertices.push_back(vert); 427 vert.p.X = v1.X - EDGE_EXPAND_DELTA; vert.p.Y = v1.Y - EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_TR; vertices.push_back(vert); 432 428 break; 433 429 } 434 430 case TileEdge::RIGHT: 435 431 { 436 v0 = CFixedVector2D(fixed::FromInt((i+1) * (int)TERRAIN_TILE_SIZE) + r, fixed::FromInt(j * (int)TERRAIN_TILE_SIZE) - r);437 v1 = CFixedVector2D(fixed::FromInt((i+1) * (int)TERRAIN_TILE_SIZE) + r, fixed::FromInt((j+1) * (int)TERRAIN_TILE_SIZE) + r);438 vert.p.X = v0.X + EDGE_EXPAND_DELTA; vert.p.Y = v0.Y - EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_TL; vert exes.push_back(vert);439 vert.p.X = v1.X + EDGE_EXPAND_DELTA; vert.p.Y = v1.Y + EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_BL; vert exes.push_back(vert);432 const CFixedVector2D v0 (fixed::FromInt((i+1) * (int)TERRAIN_TILE_SIZE) + r, fixed::FromInt(j * (int)TERRAIN_TILE_SIZE) - r); 433 const CFixedVector2D v1 (fixed::FromInt((i+1) * (int)TERRAIN_TILE_SIZE) + r, fixed::FromInt((j+1) * (int)TERRAIN_TILE_SIZE) + r); 434 vert.p.X = v0.X + EDGE_EXPAND_DELTA; vert.p.Y = v0.Y - EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_TL; vertices.push_back(vert); 435 vert.p.X = v1.X + EDGE_EXPAND_DELTA; vert.p.Y = v1.Y + EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_BL; vertices.push_back(vert); 440 436 break; 441 437 } 442 438 } … … 448 444 std::vector<EdgeAA>& edgesLeft, std::vector<EdgeAA>& edgesRight, 449 445 std::vector<EdgeAA>& edgesBottom, std::vector<EdgeAA>& edgesTop) 450 446 { 451 edgesLeft.reserve(edgesAA.size()); 452 edgesRight.reserve(edgesAA.size()); 453 edgesBottom.reserve(edgesAA.size()); 454 edgesTop.reserve(edgesAA.size()); 447 const size_t edgeSize = edgesAA.size(); 448 edgesLeft.reserve(edgeSize); 449 edgesRight.reserve(edgeSize); 450 edgesBottom.reserve(edgeSize); 451 edgesTop.reserve(edgeSize); 455 452 456 for (size_t i = 0; i < edgesAA.size(); ++i) 453 const std::vector<Edge>::const_iterator itEnd = edgesAA.end(); 454 for (std::vector<Edge>::const_iterator it = edgesAA.begin(); it != itEnd; ++it) 457 455 { 458 if (a.X <= edgesAA[i].p0.X) 456 const Edge& edge = *it; 457 if (a.X <= edge.p0.X) 459 458 { 460 EdgeAA e = { edge sAA[i].p0, edgesAA[i].p1.Y };459 EdgeAA e = { edge.p0, edge.p1.Y }; 461 460 edgesLeft.push_back(e); 462 461 } 463 if (a.X >= edge sAA[i].p1.X)462 if (a.X >= edge.p1.X) 464 463 { 465 EdgeAA e = { edge sAA[i].p1, edgesAA[i].p0.Y };464 EdgeAA e = { edge.p1, edge.p0.Y }; 466 465 edgesRight.push_back(e); 467 466 } 468 if (a.Y <= edge sAA[i].p0.Y)467 if (a.Y <= edge.p0.Y) 469 468 { 470 EdgeAA e = { edge sAA[i].p0, edgesAA[i].p1.X };469 EdgeAA e = { edge.p0, edge.p1.X }; 471 470 edgesBottom.push_back(e); 472 471 } 473 if (a.Y >= edge sAA[i].p1.Y)472 if (a.Y >= edge.p1.Y) 474 473 { 475 EdgeAA e = { edge sAA[i].p1, edgesAA[i].p0.X };474 EdgeAA e = { edge.p1, edge.p0.X }; 476 475 edgesTop.push_back(e); 477 476 } 478 477 } … … 485 484 { 486 485 CFixedVector2D src; 487 486 EdgeSort(CFixedVector2D src) : src(src) { } 488 bool operator()(const Edge& a, const Edge& b) 487 bool operator()(const Edge& a, const Edge& b) const 489 488 { 490 489 if ((a.p0 - src).CompareLength(b.p0 - src) < 0) 491 490 return true; … … 493 492 } 494 493 }; 495 494 495 // List of collision edges - paths must never cross these. 496 // (Edges are one-sided so intersections are fine in one direction, but not the other direction.) 497 std::vector<Edge> edges; 498 std::vector<Edge> edgesAA; // axis-aligned squares 499 // List of obstruction vertices (plus start/end points); we'll try to find paths through 500 // the graph defined by these vertices 501 std::vector<Vertex> vertices; 502 std::vector<ICmpObstructionManager::ObstructionSquare> squares; 503 504 std::vector<EdgeAA> edgesLeft; 505 std::vector<EdgeAA> edgesRight; 506 std::vector<EdgeAA> edgesBottom; 507 std::vector<EdgeAA> edgesTop; 508 496 509 void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter, 497 510 entity_pos_t x0, entity_pos_t z0, entity_pos_t r, 498 511 entity_pos_t range, const Goal& goal, pass_class_t passClass, Path& path) … … 530 543 } 531 544 } 532 545 533 // List of collision edges - paths must never cross these.534 // (Edges are one-sided so intersections are fine in one direction, but not the other direction.)535 std::vector<Edge> edges;536 std::vector<Edge> edgesAA; // axis-aligned squares537 546 547 edges.clear(); 548 edgesAA.clear(); 549 vertices.clear(); 550 squares.clear(); 551 538 552 // Create impassable edges at the max-range boundary, so we can't escape the region 539 553 // where we're meant to be searching 540 fixed rangeXMin = x0 - range;541 fixed rangeXMax = x0 + range;542 fixed rangeZMin = z0 - range;543 fixed rangeZMax = z0 + range;554 const fixed rangeXMin = x0 - range; 555 const fixed rangeXMax = x0 + range; 556 const fixed rangeZMin = z0 - range; 557 const fixed rangeZMax = z0 + range; 544 558 { 545 559 // (The edges are the opposite direction to usual, so it's an inside-out square) 546 560 Edge e0 = { CFixedVector2D(rangeXMin, rangeZMin), CFixedVector2D(rangeXMin, rangeZMax) }; … … 553 567 edges.push_back(e3); 554 568 } 555 569 556 // List of obstruction vertexes (plus start/end points); we'll try to find paths through557 // the graph defined by these vertexes558 std::vector<Vertex> vertexes;559 570 560 571 // Add the start point to the graph 561 CFixedVector2D posStart(x0, z0); 562 fixed hStart = (posStart - NearestPointOnGoal(posStart, goal)).Length(); 563 Vertex start = { posStart, fixed::Zero(), hStart, 0, Vertex::OPEN, QUADRANT_NONE, QUADRANT_ALL }; 564 vertexes.push_back(start); 572 const CFixedVector2D posStart(x0, z0); 573 const fixed hStart = (posStart - NearestPointOnGoal(posStart, goal)).Length(); 574 const Vertex start = { posStart, fixed::Zero(), hStart, 0, Vertex::OPEN, QUADRANT_NONE, QUADRANT_ALL }; 575 vertices.push_back(start); 576 565 577 const size_t START_VERTEX_ID = 0; 566 578 567 579 // Add the goal vertex to the graph. 568 580 // Since the goal isn't always a point, this a special magic virtual vertex which moves around - whenever 569 581 // we look at it from another vertex, it is moved to be the closest point on the goal shape to that vertex. 570 Vertex end = { CFixedVector2D(goal.x, goal.z), fixed::Zero(), fixed::Zero(), 0, Vertex::UNEXPLORED, QUADRANT_NONE, QUADRANT_ALL }; 571 vertexes.push_back(end); 582 { 583 const Vertex end = { CFixedVector2D(goal.x, goal.z), fixed::Zero(), fixed::Zero(), 0, Vertex::UNEXPLORED, QUADRANT_NONE, QUADRANT_ALL }; 584 vertices.push_back(end); 585 } 572 586 const size_t GOAL_VERTEX_ID = 1; 573 587 574 588 // Add terrain obstructions … … 576 590 u16 i0, j0, i1, j1; 577 591 NearestTile(rangeXMin, rangeZMin, i0, j0); 578 592 NearestTile(rangeXMax, rangeZMax, i1, j1); 579 AddTerrainEdges(edgesAA, vert exes, i0, j0, i1, j1, r, passClass, *m_Grid);593 AddTerrainEdges(edgesAA, vertices, i0, j0, i1, j1, r, passClass, *m_Grid); 580 594 } 581 595 582 596 // Find all the obstruction squares that might affect us 583 597 CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); 584 std::vector<ICmpObstructionManager::ObstructionSquare> squares;585 598 cmpObstructionManager->GetObstructionsInRange(filter, rangeXMin - r, rangeZMin - r, rangeXMax + r, rangeZMax + r, squares); 586 599 587 600 // Resize arrays to reduce reallocations 588 vert exes.reserve(vertexes.size() + squares.size()*4);601 vertices.reserve(vertices.size() + squares.size()*4); 589 602 edgesAA.reserve(edgesAA.size() + squares.size()); // (assume most squares are AA) 590 603 591 // Convert each obstruction square into collision edges and search graph vertexes 592 for (size_t i = 0; i < squares.size(); ++i) 604 // Convert each obstruction square into collision edges and search graph vertices 605 606 Vertex vert; 607 const std::vector<ICmpObstructionManager::ObstructionSquare>::const_iterator itEnd = squares.end(); 608 for (std::vector<ICmpObstructionManager::ObstructionSquare>::const_iterator it = squares.begin(); it != itEnd; ++it) 593 609 { 594 CFixedVector2D center(squares[i].x, squares[i].z); 595 CFixedVector2D u = squares[i].u; 596 CFixedVector2D v = squares[i].v; 610 const ICmpObstructionManager::ObstructionSquare& square = *it; 597 611 598 // Expand the vertexes by the moving unit's collision radius, to find the 612 const CFixedVector2D center(square.x, square.z); 613 const CFixedVector2D& u = square.u; 614 const CFixedVector2D& v = square.v; 615 616 // Expand the vertices by the moving unit's collision radius, to find the 599 617 // closest we can get to it 600 618 601 CFixedVector2D hd0(squares[i].hw + r + EDGE_EXPAND_DELTA, squares[i].hh + r + EDGE_EXPAND_DELTA);602 CFixedVector2D hd1(squares[i].hw + r + EDGE_EXPAND_DELTA, -(squares[i].hh + r + EDGE_EXPAND_DELTA));619 const CFixedVector2D hd0(square.hw + r + EDGE_EXPAND_DELTA, square.hh + r + EDGE_EXPAND_DELTA); 620 const CFixedVector2D hd1(square.hw + r + EDGE_EXPAND_DELTA, -(square.hh + r + EDGE_EXPAND_DELTA)); 603 621 604 622 // Check whether this is an axis-aligned square 605 bool aa = (u.X == fixed::FromInt(1) && u.Y == fixed::Zero() && v.X == fixed::Zero() && v.Y == fixed::FromInt(1));623 const bool aa = (u.X == fixed::FromInt(1) && u.Y == fixed::Zero() && v.X == fixed::Zero() && v.Y == fixed::FromInt(1)); 606 624 607 Vertex vert;608 625 vert.status = Vertex::UNEXPLORED; 609 626 vert.quadInward = QUADRANT_NONE; 610 627 vert.quadOutward = QUADRANT_ALL; 611 vert.p.X = center.X - hd0.Dot(u); vert.p.Y = center.Y + hd0.Dot(v); if (aa) vert.quadInward = QUADRANT_BR; vert exes.push_back(vert);612 vert.p.X = center.X - hd1.Dot(u); vert.p.Y = center.Y + hd1.Dot(v); if (aa) vert.quadInward = QUADRANT_TR; vert exes.push_back(vert);613 vert.p.X = center.X + hd0.Dot(u); vert.p.Y = center.Y - hd0.Dot(v); if (aa) vert.quadInward = QUADRANT_TL; vert exes.push_back(vert);614 vert.p.X = center.X + hd1.Dot(u); vert.p.Y = center.Y - hd1.Dot(v); if (aa) vert.quadInward = QUADRANT_BL; vert exes.push_back(vert);628 vert.p.X = center.X - hd0.Dot(u); vert.p.Y = center.Y + hd0.Dot(v); if (aa) vert.quadInward = QUADRANT_BR; vertices.push_back(vert); 629 vert.p.X = center.X - hd1.Dot(u); vert.p.Y = center.Y + hd1.Dot(v); if (aa) vert.quadInward = QUADRANT_TR; vertices.push_back(vert); 630 vert.p.X = center.X + hd0.Dot(u); vert.p.Y = center.Y - hd0.Dot(v); if (aa) vert.quadInward = QUADRANT_TL; vertices.push_back(vert); 631 vert.p.X = center.X + hd1.Dot(u); vert.p.Y = center.Y - hd1.Dot(v); if (aa) vert.quadInward = QUADRANT_BL; vertices.push_back(vert); 615 632 616 633 // Add the edges: 617 634 618 CFixedVector2D h0(squares[i].hw + r, squares[i].hh + r); 619 CFixedVector2D h1(squares[i].hw + r, -(squares[i].hh + r)); 635 const CFixedVector2D h1(square.hw + r, -(square.hh + r)); 636 637 const fixed h1DotU(h1.Dot(u)); 638 const fixed h1DotV(h1.Dot(v)); 620 639 621 CFixedVector2D ev0(center.X - h0.Dot(u), center.Y + h0.Dot(v)); 622 CFixedVector2D ev1(center.X - h1.Dot(u), center.Y + h1.Dot(v)); 623 CFixedVector2D ev2(center.X + h0.Dot(u), center.Y - h0.Dot(v)); 624 CFixedVector2D ev3(center.X + h1.Dot(u), center.Y - h1.Dot(v)); 640 const CFixedVector2D ev1(center.X - h1DotU, center.Y + h1DotV); 641 const CFixedVector2D ev3(center.X + h1DotU, center.Y - h1DotV); 625 642 if (aa) 626 643 { 627 644 Edge e = { ev1, ev3 }; … … 629 646 } 630 647 else 631 648 { 649 const CFixedVector2D h0(square.hw + r, square.hh + r); 650 651 const fixed h0DotU(h0.Dot(u)); 652 const fixed h0DotV(h0.Dot(v)); 653 654 const CFixedVector2D ev0(center.X - h0DotU, center.Y + h0DotV); 655 const CFixedVector2D ev2(center.X + h0DotU, center.Y - h0DotV); 656 632 657 Edge e0 = { ev0, ev1 }; 633 658 Edge e1 = { ev1, ev2 }; 634 659 Edge e2 = { ev2, ev3 }; 635 660 Edge e3 = { ev3, ev0 }; 661 636 662 edges.push_back(e0); 637 663 edges.push_back(e1); 638 664 edges.push_back(e2); 639 665 edges.push_back(e3); 640 666 } 641 667 642 // TODO: should clip out vert exes and edges that are outside the range,668 // TODO: should clip out vertices and edges that are outside the range, 643 669 // to reduce the search space 644 670 } 645 671 646 ENSURE(vert exes.size() < 65536); // we store array indexes as u16672 ENSURE(vertices.size() < 65536); // we store array indexes as u16 647 673 648 674 if (m_DebugOverlay) 649 675 { … … 684 710 // Since we are just measuring Euclidean distance the heuristic is admissible, 685 711 // so we never have to re-examine a node once it's been moved to the closed set. 686 712 687 // To save time in common cases, we don't precompute a graph of valid edges between vert exes;713 // To save time in common cases, we don't precompute a graph of valid edges between vertices; 688 714 // we do it lazily instead. When the search algorithm reaches a vertex, we examine every other 689 715 // vertex and see if we can reach it without hitting any collision edges, and ignore the ones 690 716 // we can't reach. Since the algorithm can only reach a vertex once (and then it'll be marked … … 703 729 { 704 730 // Move best tile from open to closed 705 731 PriorityQueue::Item curr = open.pop(); 706 vert exes[curr.id].status = Vertex::CLOSED;732 vertices[curr.id].status = Vertex::CLOSED; 707 733 708 734 // If we've reached the destination, stop 709 735 if (curr.id == GOAL_VERTEX_ID) … … 712 738 break; 713 739 } 714 740 741 const Vertex &VertexCurr = vertices[curr.id]; 742 const CFixedVector2D& vertexPosition = VertexCurr.p; 743 715 744 // Sort the edges so ones nearer this vertex are checked first by CheckVisibility, 716 745 // since they're more likely to block the rays 717 std::sort(edgesAA.begin(), edgesAA.end(), EdgeSort( vertexes[curr.id].p));746 std::sort(edgesAA.begin(), edgesAA.end(), EdgeSort(VertexCurr.p)); 718 747 719 std::vector<EdgeAA> edgesLeft;720 std::vector<EdgeAA> edgesRight;721 std::vector<EdgeAA> edgesBottom;722 std::vector<EdgeAA> edgesTop;723 SplitAAEdges( vertexes[curr.id].p, edgesAA, edgesLeft, edgesRight, edgesBottom, edgesTop);748 edgesLeft.clear(); 749 edgesRight.clear(); 750 edgesBottom.clear(); 751 edgesTop.clear(); 752 SplitAAEdges(VertexCurr.p, edgesAA, edgesLeft, edgesRight, edgesBottom, edgesTop); 724 753 725 754 // Check the lines to every other vertex 726 for (size_t n = 0; n < vertexes.size(); ++n) 755 size_t n = 0; 756 const std::vector<Vertex>::iterator itVEnd = vertices.end(); 757 for (std::vector<Vertex>::iterator itv = vertices.begin(); itv != itVEnd; ++itv, n++) 727 758 { 728 if (vertexes[n].status == Vertex::CLOSED) 759 Vertex& vertexN = *itv; 760 if (vertexN.status == Vertex::CLOSED) 729 761 continue; 730 762 731 763 // If this is the magical goal vertex, move it to near the current vertex 732 764 CFixedVector2D npos; 733 765 if (n == GOAL_VERTEX_ID) 734 766 { 735 npos = NearestPointOnGoal(vertex es[curr.id].p, goal);767 npos = NearestPointOnGoal(vertexPosition, goal); 736 768 737 // To prevent integer overflows later on, we need to ensure all vert exes are769 // To prevent integer overflows later on, we need to ensure all vertices are 738 770 // 'close' to the source. The goal might be far away (not a good idea but 739 771 // sometimes it happens), so clamp it to the current search range 740 772 npos.X = clamp(npos.X, rangeXMin, rangeXMax); … … 742 774 } 743 775 else 744 776 { 745 npos = vertex es[n].p;777 npos = vertexN.p; 746 778 } 747 779 748 780 // Work out which quadrant(s) we're approaching the new vertex from 749 u8 quad = 0; 750 if (vertexes[curr.id].p.X <= npos.X && vertexes[curr.id].p.Y <= npos.Y) quad |= QUADRANT_BL; 751 if (vertexes[curr.id].p.X >= npos.X && vertexes[curr.id].p.Y >= npos.Y) quad |= QUADRANT_TR; 752 if (vertexes[curr.id].p.X <= npos.X && vertexes[curr.id].p.Y >= npos.Y) quad |= QUADRANT_TL; 753 if (vertexes[curr.id].p.X >= npos.X && vertexes[curr.id].p.Y <= npos.Y) quad |= QUADRANT_BR; 781 u8 quad = 0; 754 782 783 if (vertexPosition.X <= npos.X && vertexPosition.Y <= npos.Y) quad |= QUADRANT_BL; 784 if (vertexPosition.X >= npos.X && vertexPosition.Y >= npos.Y) quad |= QUADRANT_TR; 785 if (vertexPosition.X <= npos.X && vertexPosition.Y >= npos.Y) quad |= QUADRANT_TL; 786 if (vertexPosition.X >= npos.X && vertexPosition.Y <= npos.Y) quad |= QUADRANT_BR; 787 755 788 // Check that the new vertex is in the right quadrant for the old vertex 756 if (!( vertexes[curr.id].quadOutward & quad))789 if (!(VertexCurr.quadOutward & quad)) 757 790 { 758 791 // Hack: Always head towards the goal if possible, to avoid missing it if it's 759 792 // inside another unit … … 763 796 } 764 797 } 765 798 766 bool visible = 767 CheckVisibilityLeft(vertexes[curr.id].p, npos, edgesLeft) && 768 CheckVisibilityRight(vertexes[curr.id].p, npos, edgesRight) && 769 CheckVisibilityBottom(vertexes[curr.id].p, npos, edgesBottom) && 770 CheckVisibilityTop(vertexes[curr.id].p, npos, edgesTop) && 771 CheckVisibility(vertexes[curr.id].p, npos, edges); 799 const CFixedVector2D abn ((npos - vertexPosition).Perpendicular()); 800 bool visible = CheckVisibilityLeft(vertexPosition, npos, abn, edgesLeft) && 801 CheckVisibilityRight(vertexPosition, npos, abn, edgesRight) && 802 CheckVisibilityBottom(vertexPosition, npos, abn, edgesBottom) && 803 CheckVisibilityTop(vertexPosition, npos, abn, edgesTop) && 804 CheckVisibility(vertexPosition, npos, abn, edges); 805 806 772 807 773 808 /* 774 809 // Render the edges that we examine 775 810 m_DebugOverlayShortPathLines.push_back(SOverlayLine()); 776 811 m_DebugOverlayShortPathLines.back().m_Color = visible ? CColor(0, 1, 0, 0.5) : CColor(1, 0, 0, 0.5); 777 812 std::vector<float> xz; 778 xz.push_back( vertexes[curr.id].p.X.ToFloat());779 xz.push_back( vertexes[curr.id].p.Y.ToFloat());813 xz.push_back(VertexCurr.p.X.ToFloat()); 814 xz.push_back(VertexCurr.p.Y.ToFloat()); 780 815 xz.push_back(npos.X.ToFloat()); 781 816 xz.push_back(npos.Y.ToFloat()); 782 817 SimRender::ConstructLineOnGround(GetSimContext(), xz, m_DebugOverlayShortPathLines.back(), false); … … 784 819 785 820 if (visible) 786 821 { 787 fixed g = vertexes[curr.id].g + (vertexes[curr.id].p- npos).Length();822 const fixed g = VertexCurr.g + (vertexPosition - npos).Length(); 788 823 789 824 // If this is a new tile, compute the heuristic distance 790 if (vertex es[n].status == Vertex::UNEXPLORED)825 if (vertexN.status == Vertex::UNEXPLORED) 791 826 { 792 827 // Add it to the open list: 793 vertex es[n].status = Vertex::OPEN;794 vertex es[n].g = g;795 vertex es[n].h = DistanceToGoal(npos, goal);796 vertex es[n].pred = curr.id;828 vertexN.status = Vertex::OPEN; 829 vertexN.g = g; 830 vertexN.h = DistanceToGoal(npos, goal); 831 vertexN.pred = curr.id; 797 832 798 833 // If this is an axis-aligned shape, the path must continue in the same quadrant 799 834 // direction (but not go into the inside of the shape). 800 835 // Hack: If we started *inside* a shape then perhaps headed to its corner (e.g. the unit 801 836 // was very near another unit), don't restrict further pathing. 802 if (vertex es[n].quadInward && !(curr.id == START_VERTEX_ID && g < fixed::FromInt(8)))803 vertex es[n].quadOutward = ((~vertexes[n].quadInward) & quad) & 0xF;837 if (vertexN.quadInward && !(curr.id == START_VERTEX_ID && g < fixed::FromInt(8))) 838 vertexN.quadOutward = ((~vertexN.quadInward) & quad) & 0xF; 804 839 805 840 if (n == GOAL_VERTEX_ID) 806 vertex es[n].p = npos; // remember the new best goal position841 vertexN.p = npos; // remember the new best goal position 807 842 808 PriorityQueue::Item t = { (u16)n, g + vertex es[n].h };843 PriorityQueue::Item t = { (u16)n, g + vertexN.h }; 809 844 open.push(t); 810 845 811 846 // Remember the heuristically best vertex we've seen so far, in case we never actually reach the target 812 if (vertex es[n].h < hBest)847 if (vertexN.h < hBest) 813 848 { 814 849 idBest = (u16)n; 815 hBest = vertex es[n].h;850 hBest = vertexN.h; 816 851 } 817 852 } 818 853 else // must be OPEN 819 854 { 820 855 // If we've already seen this tile, and the new path to this tile does not have a 821 856 // better cost, then stop now 822 if (g >= vertex es[n].g)857 if (g >= vertexN.g) 823 858 continue; 824 859 825 860 // Otherwise, we have a better path, so replace the old one with the new cost/parent 826 vertex es[n].g = g;827 vertex es[n].pred = curr.id;861 vertexN.g = g; 862 vertexN.pred = curr.id; 828 863 829 864 // If this is an axis-aligned shape, the path must continue in the same quadrant 830 865 // direction (but not go into the inside of the shape). 831 if (vertex es[n].quadInward)832 vertex es[n].quadOutward = ((~vertexes[n].quadInward) & quad) & 0xF;866 if (vertexN.quadInward) 867 vertexN.quadOutward = ((~vertexN.quadInward) & quad) & 0xF; 833 868 834 869 if (n == GOAL_VERTEX_ID) 835 vertex es[n].p = npos; // remember the new best goal position870 vertexN.p = npos; // remember the new best goal position 836 871 837 open.promote((u16)n, g + vertex es[n].h);872 open.promote((u16)n, g + vertexN.h); 838 873 } 839 874 } 840 875 } 841 876 } 842 877 843 878 // Reconstruct the path (in reverse) 844 for (u16 id = idBest; id != START_VERTEX_ID; id = vert exes[id].pred)879 for (u16 id = idBest; id != START_VERTEX_ID; id = vertices[id].pred) 845 880 { 846 Waypoint w = { vert exes[id].p.X, vertexes[id].p.Y };881 Waypoint w = { vertices[id].p.X, vertices[id].p.Y }; 847 882 path.m_Waypoints.push_back(w); 848 883 } 849 884 … … 867 902 868 903 UpdateGrid(); 869 904 870 std::vector<Edge> edgesAA;871 std::vector<Vertex> vertexes;905 edgesAA.clear(); 906 vertices.clear(); 872 907 873 908 u16 i0, j0, i1, j1; 874 909 NearestTile(std::min(x0, x1) - r, std::min(z0, z1) - r, i0, j0); 875 910 NearestTile(std::max(x0, x1) + r, std::max(z0, z1) + r, i1, j1); 876 AddTerrainEdges(edgesAA, vert exes, i0, j0, i1, j1, r, passClass, *m_Grid);911 AddTerrainEdges(edgesAA, vertices, i0, j0, i1, j1, r, passClass, *m_Grid); 877 912 878 913 CFixedVector2D a(x0, z0); 879 914 CFixedVector2D b(x1, z1); 880 915 881 std::vector<EdgeAA> edgesLeft;882 std::vector<EdgeAA> edgesRight;883 std::vector<EdgeAA> edgesBottom;884 std::vector<EdgeAA> edgesTop;916 edgesLeft.clear(); 917 edgesRight.clear(); 918 edgesBottom.clear(); 919 edgesTop.clear(); 885 920 SplitAAEdges(a, edgesAA, edgesLeft, edgesRight, edgesBottom, edgesTop); 886 921 922 923 const CFixedVector2D abn ((b - a).Perpendicular()); 887 924 bool visible = 888 CheckVisibilityLeft(a, b, edgesLeft) &&889 CheckVisibilityRight(a, b, edgesRight) &&890 CheckVisibilityBottom(a, b, edgesBottom) &&891 CheckVisibilityTop(a, b, edgesTop);925 CheckVisibilityLeft(a, b, abn, edgesLeft) && 926 CheckVisibilityRight(a, b, abn, edgesRight) && 927 CheckVisibilityBottom(a, b, abn, edgesBottom) && 928 CheckVisibilityTop(a, b, abn, edgesTop); 892 929 930 931 932 933 893 934 return visible; 894 935 } -
source/simulation2/components/CCmpPathfinder.cpp
238 238 } 239 239 240 240 241 fixed CCmpPathfinder::GetMovementSpeed( entity_pos_t x0, entity_pos_t z0, u8 costClass)241 fixed CCmpPathfinder::GetMovementSpeed(const entity_pos_t x0, const entity_pos_t z0, u8 costClass) 242 242 { 243 243 UpdateGrid(); 244 244 … … 275 275 return m_UnitCostClassTags[name]; 276 276 } 277 277 278 fixed CCmpPathfinder::DistanceToGoal( CFixedVector2D pos, const CCmpPathfinder::Goal& goal)278 fixed CCmpPathfinder::DistanceToGoal( const CFixedVector2D& pos, const CCmpPathfinder::Goal& goal) 279 279 { 280 280 switch (goal.type) 281 281 { -
source/simulation2/components/CCmpPathfinder_Tile.cpp
20 20 * Tile-based algorithm for CCmpPathfinder. 21 21 * This is a fairly naive algorithm and could probably be improved substantially 22 22 * (hopefully without needing to change the interface much). 23 * TODO: 24 ** use squared length for all distance comparisons, saves a sqrt per comparison: huge speed up 25 ** use pointers for edge so that memory movement are smaller, prealloc edge using memory pools so that data is contiguous: huge speed up 23 26 */ 24 27 25 28 #include "precompiled.h" … … 53 56 STATUS_CLOSED = 2 54 57 }; 55 58 56 bool IsUnexplored() { return status == STATUS_UNEXPLORED; }57 bool IsOpen() { return status == STATUS_OPEN; }58 bool IsClosed() { return status == STATUS_CLOSED; }59 bool IsUnexplored() const{ return status == STATUS_UNEXPLORED; } 60 bool IsOpen()const { return status == STATUS_OPEN; } 61 bool IsClosed()const { return status == STATUS_CLOSED; } 59 62 void SetStatusOpen() { status = STATUS_OPEN; } 60 63 void SetStatusClosed() { status = STATUS_CLOSED; } 61 64 62 65 // Get pi,pj coords of predecessor to this tile on best path, given i,j coords of this tile 63 u16 GetPredI( u16 i){ return (u16)(i + dpi); }64 u16 GetPredJ( u16 j){ return (u16)(j + dpj); }66 u16 GetPredI(const u16 i) const { return (u16)(i + dpi); } 67 u16 GetPredJ(const u16 j) const { return (u16)(j + dpj); } 65 68 // Set the pi,pj coords of predecessor, given i,j coords of this tile 66 69 void SetPred(u16 pi_, u16 pj_, u16 i, u16 j) 67 70 { … … 82 85 u32 h; // h (heuristic cost to goal) (TODO: is it really better for performance to store this instead of recomputing?) 83 86 84 87 #if PATHFIND_DEBUG 85 u32 GetStep() { return step; }86 void SetStep( u32 s) { step = s; }88 u32 GetStep() const{ return step; } 89 void SetStep(const u32 s) { step = s; } 87 90 private: 88 91 u32 step; // step at which this tile was last processed (for debug rendering) 89 92 #else 90 u32 GetStep() { return 0; }91 void SetStep( u32) { }93 u32 GetStep()const { return 0; } 94 void SetStep(const u32) { } 92 95 #endif 93 96 94 97 }; … … 113 116 m_Pathfinder.UpdateGrid(); 114 117 } 115 118 116 virtual void ProcessTile( ssize_t i,ssize_t j)119 virtual void ProcessTile(const ssize_t i, const ssize_t j) 117 120 { 118 121 if (m_Pathfinder.m_Grid && !IS_PASSABLE(m_Pathfinder.m_Grid->get((int)i, (int)j), m_Pathfinder.m_DebugPassClass)) 119 122 RenderTile(CColor(1, 0, 0, 0.6f), false); … … 215 218 #endif 216 219 }; 217 220 218 static bool AtGoal( u16 i,u16 j, const ICmpPathfinder::Goal& goal)221 static bool AtGoal(const u16 i, const u16 j, const ICmpPathfinder::Goal& goal) 219 222 { 220 223 // Allow tiles slightly more than sqrt(2) from the actual goal, 221 224 // i.e. adjacent diagonally to the target tile 222 fixed tolerance = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*3/2);225 const fixed tolerance = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*3/2); 223 226 224 227 entity_pos_t x, z; 225 228 CCmpPathfinder::TileCenter(i, j, x, z); 226 fixed dist = CCmpPathfinder::DistanceToGoal(CFixedVector2D(x, z), goal);229 const fixed dist = CCmpPathfinder::DistanceToGoal(CFixedVector2D(x, z), goal); 227 230 return (dist < tolerance); 228 231 } 229 232 230 233 // Calculate heuristic cost from tile i,j to destination 231 234 // (This ought to be an underestimate for correctness) 232 static u32 CalculateHeuristic( u16 i, u16 j, u16 iGoal, u16 jGoal,u16 rGoal)235 static u32 CalculateHeuristic(const u16 i, const u16 j, const u16 iGoal, const u16 jGoal, const u16 rGoal) 233 236 { 234 237 #if USE_DIAGONAL_MOVEMENT 235 CFixedVector2D pos (fixed::FromInt(i), fixed::FromInt(j));236 CFixedVector2D goal (fixed::FromInt(iGoal), fixed::FromInt(jGoal));237 fixed dist = (pos - goal).Length();238 const CFixedVector2D pos (fixed::FromInt(i), fixed::FromInt(j)); 239 const CFixedVector2D goal (fixed::FromInt(iGoal), fixed::FromInt(jGoal)); 240 const fixed dist = pos.computeDistanceLength(goal); 238 241 // TODO: the heuristic could match the costs better - it's not really Euclidean movement 239 242 240 243 fixed rdist = dist - fixed::FromInt(rGoal); … … 255 258 } 256 259 257 260 // Calculate movement cost from predecessor tile pi,pj to tile i,j 258 static u32 CalculateCostDelta(u16 pi, u16 pj, u16 i, u16 j, PathfindTileGrid* tempGrid, u32 tileCost) 261 static u32 CalculateCostDelta(const u16 pi, const u16 pj, const PathfindTile &p, 262 const u16 i, const u16 j, 263 PathfindTileGrid* const tempGrid, 264 const u32 tileCost) 259 265 { 260 266 u32 dg = tileCost; 261 267 … … 269 275 // likely to be reasonably stable) and reducing the cost, and use a Euclidean heuristic. 270 276 // At least this makes paths look a bit nicer for now... 271 277 272 PathfindTile& p = tempGrid->get(pi, pj);273 u16 ppi = p.GetPredI(pi);274 u16 ppj = p.GetPredJ(pj);278 //PathfindTile& p = tempGrid->get(pi, pj); 279 const u16 ppi = p.GetPredI(pi); 280 const u16 ppj = p.GetPredJ(pj); 275 281 if (ppi != i && ppj != j) 282 { 276 283 dg = (dg << 16) / 92682; // dg*sqrt(2)/2 284 } 277 285 else 278 286 { 279 PathfindTile& pp = tempGrid->get(ppi, ppj);280 int di = abs(i - pp.GetPredI(ppi));281 int dj = abs(j - pp.GetPredJ(ppj));287 const PathfindTile& pp = tempGrid->get(ppi, ppj); 288 const int di = abs(i - pp.GetPredI(ppi)); 289 const int dj = abs(j - pp.GetPredJ(ppj)); 282 290 if ((di == 1 && dj == 2) || (di == 2 && dj == 1)) 283 291 dg = (dg << 16) / 79742; // dg*(sqrt(5)-sqrt(2)) 284 292 } … … 288 296 } 289 297 290 298 // Do the A* processing for a neighbour tile i,j. 291 static void ProcessNeighbour(u16 pi, u16 pj, u16 i, u16 j, u32 pg, PathfinderState& state) 299 static void ProcessNeighbour(const u16 pi, const u16 pj, const PathfindTile &pTile, 300 const u16 i, const u16 j, const u32 pg, 301 PathfinderState& state) 292 302 { 293 303 #if PATHFIND_STATS 294 304 state.numProcessed++; 295 305 #endif 296 306 297 307 // Reject impassable tiles 298 TerrainTile tileTag = state.terrain->get(i, j);308 const TerrainTile tileTag = state.terrain->get(i, j); 299 309 if (!IS_PASSABLE(tileTag, state.passClass) && !state.ignoreImpassable) 300 310 return; 301 311 302 u32 dg = CalculateCostDelta(pi, pj, i, j, state.tiles, state.moveCosts.at(GET_COST_CLASS(tileTag)));312 const u32 dg = CalculateCostDelta(pi, pj, pTile, i, j, state.tiles, state.moveCosts.at(GET_COST_CLASS(tileTag))); 303 313 304 u32 g = pg + dg; // cost to this tile = cost to predecessor + delta from predecessor314 const u32 g = pg + dg; // cost to this tile = cost to predecessor + delta from predecessor 305 315 306 316 PathfindTile& n = state.tiles->get(i, j); 307 317 … … 363 373 #endif 364 374 } 365 375 366 void CCmpPathfinder::ComputePath( entity_pos_t x0, entity_pos_t z0, const Goal& goal, pass_class_t passClass,cost_class_t costClass, Path& path)376 void CCmpPathfinder::ComputePath(const entity_pos_t x0, const entity_pos_t z0, const Goal& goal, const pass_class_t passClass, const cost_class_t costClass, Path& path) 367 377 { 368 378 UpdateGrid(); 369 379 … … 384 394 return; 385 395 } 386 396 397 387 398 // If the target is a circle, we want to aim for the edge of it (so e.g. if we're inside 388 399 // a large circle then the heuristics will aim us directly outwards); 389 400 // otherwise just aim at the center point. (We'll never try moving outwards to a square shape.) … … 397 408 398 409 state.steps = 0; 399 410 400 state.tiles = new PathfindTileGrid(m_MapSize, m_MapSize); 411 if (m_DebugGrid) 412 { 413 state.tiles = m_DebugGrid; 414 state.tiles->reset(); 415 } 416 else 417 { 418 state.tiles = new PathfindTileGrid(m_MapSize, m_MapSize); 419 } 401 420 state.terrain = m_Grid; 402 421 403 422 state.iBest = i0; … … 433 452 434 453 // Move best tile from open to closed 435 454 PriorityQueue::Item curr = state.open.pop(); 436 u16 i = curr.id.first; 437 u16 j = curr.id.second; 438 state.tiles->get(i, j).SetStatusClosed(); 455 const u16 i = curr.id.first; 456 const u16 j = curr.id.second; 457 PathfindTile &pTile = state.tiles->get(i, j); 458 pTile.SetStatusClosed(); 439 459 440 460 // If we've reached the destination, stop 441 461 if (AtGoal(i, j, goal)) … … 450 470 // take it and forbid any further use of impassable tiles 451 471 if (state.ignoreImpassable) 452 472 { 453 if (i >0 && IS_PASSABLE(state.terrain->get(i-1, j), state.passClass))473 if (i != 0 && IS_PASSABLE(state.terrain->get(i-1, j), state.passClass)) 454 474 state.ignoreImpassable = false; 455 475 else if (i < m_MapSize-1 && IS_PASSABLE(state.terrain->get(i+1, j), state.passClass)) 456 476 state.ignoreImpassable = false; 457 else if (j >0 && IS_PASSABLE(state.terrain->get(i, j-1), state.passClass))477 else if (j != 0 && IS_PASSABLE(state.terrain->get(i, j-1), state.passClass)) 458 478 state.ignoreImpassable = false; 459 479 else if (j < m_MapSize-1 && IS_PASSABLE(state.terrain->get(i, j+1), state.passClass)) 460 480 state.ignoreImpassable = false; 461 481 } 462 482 463 u32 g = state.tiles->get(i, j).cost;464 if (i >0)465 ProcessNeighbour(i, j, (u16)(i-1), j, g, state);483 const u32 g = pTile.cost; 484 if (i != 0) 485 ProcessNeighbour(i, j, pTile, (u16)(i-1), j, g, state); 466 486 if (i < m_MapSize-1) 467 ProcessNeighbour(i, j, (u16)(i+1), j, g, state);468 if (j >0)469 ProcessNeighbour(i, j, i, (u16)(j-1), g, state);487 ProcessNeighbour(i, j, pTile, (u16)(i+1), j, g, state); 488 if (j != 0) 489 ProcessNeighbour(i, j, pTile, i, (u16)(j-1), g, state); 470 490 if (j < m_MapSize-1) 471 ProcessNeighbour(i, j, i, (u16)(j+1), g, state);491 ProcessNeighbour(i, j, pTile, i, (u16)(j+1), g, state); 472 492 } 473 493 474 494 // Reconstruct the path (in reverse) … … 487 507 } 488 508 489 509 // Save this grid for debug display 490 delete m_DebugGrid;491 510 m_DebugGrid = state.tiles; 492 511 m_DebugSteps = state.steps; 493 512 -
source/simulation2/components/CCmpTerritoryManager.cpp
56 56 57 57 TerritoryOverlay(CCmpTerritoryManager& manager); 58 58 virtual void StartRender(); 59 virtual void ProcessTile( ssize_t i,ssize_t j);59 virtual void ProcessTile(const ssize_t i, const ssize_t j); 60 60 }; 61 61 62 62 class CCmpTerritoryManager : public ICmpTerritoryManager … … 279 279 280 280 typedef PriorityQueueHeap<std::pair<u16, u16>, u32, std::greater<u32> > OpenQueue; 281 281 282 static void ProcessNeighbour( u32 falloff, u16 i, u16 j,u32 pg, bool diagonal,282 static void ProcessNeighbour(const u32 falloff, const u16 i, const u16 j, const u32 pg, bool diagonal, 283 283 Grid<u32>& grid, OpenQueue& queue, const Grid<u8>& costGrid) 284 284 { 285 285 u32 dg = falloff * costGrid.get(i, j); … … 291 291 if (pg <= grid.get(i, j) + dg) 292 292 return; 293 293 294 u32 g = pg - dg; // cost to this tile = cost to predecessor - falloff from predecessor294 const u32 g = pg - dg; // cost to this tile = cost to predecessor - falloff from predecessor 295 295 296 296 grid.set(i, j, g); 297 297 OpenQueue::Item tile = { std::make_pair(i, j), g }; 298 298 queue.push(tile); 299 299 } 300 300 301 static void FloodFill(Grid<u32>& grid, Grid<u8>& costGrid, OpenQueue& openTiles, u32 falloff)301 static void FloodFill(Grid<u32>& grid, Grid<u8>& costGrid, OpenQueue& openTiles, const u32 falloff) 302 302 { 303 303 u16 tilesW = grid.m_W; 304 304 u16 tilesH = grid.m_H; … … 308 308 OpenQueue::Item tile = openTiles.pop(); 309 309 310 310 // Process neighbours (if they're not off the edge of the map) 311 u16 x = tile.id.first;312 u16 z = tile.id.second;311 const u16 x = tile.id.first; 312 const u16 z = tile.id.second; 313 313 if (x > 0) 314 314 ProcessNeighbour(falloff, (u16)(x-1), z, tile.rank, false, grid, openTiles, costGrid); 315 315 if (x < tilesW-1) … … 343 343 if (!cmpTerrain->IsLoaded()) 344 344 return; 345 345 346 u16 tilesW = cmpTerrain->GetTilesPerSide();347 u16 tilesH = cmpTerrain->GetTilesPerSide();346 const u16 tilesW = cmpTerrain->GetTilesPerSide(); 347 const u16 tilesH = cmpTerrain->GetTilesPerSide(); 348 348 349 349 m_Territories = new Grid<u8>(tilesW, tilesH); 350 350 … … 352 352 Grid<u8> influenceGrid(tilesW, tilesH); 353 353 354 354 CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY); 355 ICmpPathfinder::pass_class_t passClassDefault = cmpPathfinder->GetPassabilityClass("default");356 ICmpPathfinder::pass_class_t passClassUnrestricted = cmpPathfinder->GetPassabilityClass("unrestricted");355 const ICmpPathfinder::pass_class_t passClassDefault = cmpPathfinder->GetPassabilityClass("default"); 356 const ICmpPathfinder::pass_class_t passClassUnrestricted = cmpPathfinder->GetPassabilityClass("unrestricted"); 357 357 358 358 const Grid<u16>& passGrid = cmpPathfinder->GetPassabilityGrid(); 359 359 for (u16 j = 0; j < tilesH; ++j) 360 360 { 361 361 for (u16 i = 0; i < tilesW; ++i) 362 362 { 363 u16 g = passGrid.get(i, j);363 const u16 g = passGrid.get(i, j); 364 364 u8 cost; 365 365 if (g & passClassUnrestricted) 366 366 cost = 255; // off the world; use maximum cost … … 416 416 std::vector<std::pair<player_id_t, Grid<u32> > > playerGrids; 417 417 // TODO: this is a large waste of memory; we don't really need to store 418 418 // all the intermediate grids 419 420 for (std::map<player_id_t, std::vector<entity_id_t> >::iterator it = influenceEntities.begin(); it != influenceEntities.end(); ++it) 419 420 Grid<u32> entityTempGrid(tilesW, tilesH); 421 for (std::map<player_id_t, std::vector<entity_id_t> >::iterator it = influenceEntities.begin(); 422 it != influenceEntities.end(); ++it) 421 423 { 422 424 Grid<u32> playerGrid(tilesW, tilesH); 423 425 424 426 std::vector<entity_id_t>& ents = it->second; 425 for (std::vector<entity_id_t>::iterator eit = ents.begin(); eit != ents.end(); ++eit) 427 std::vector<entity_id_t>::iterator entEnd = ents.end(); 428 for (std::vector<entity_id_t>::iterator eit = ents.begin(); eit != entEnd; ++eit) 426 429 { 427 430 // Compute the influence map of the current entity, then add it to the player grid 431 entityTempGrid.reset(); 428 432 429 Grid<u32> entityGrid(tilesW, tilesH);430 431 433 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *eit); 432 434 CFixedVector2D pos = cmpPosition->GetPosition2D(); 433 435 u16 i = (u16)clamp((pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1); … … 442 444 // when doing all the sums 443 445 444 446 // Initialise the tile under the entity 445 entity Grid.set(i, j, weight);447 entityTempGrid.set(i, j, weight); 446 448 OpenQueue openTiles; 447 449 OpenQueue::Item tile = { std::make_pair((u16)i, (i16)j), weight }; 448 450 openTiles.push(tile); 449 451 450 452 // Expand influences outwards 451 FloodFill(entity Grid, influenceGrid, openTiles, falloff);453 FloodFill(entityTempGrid, influenceGrid, openTiles, falloff); 452 454 453 455 // TODO: we should do a sparse grid and only add the non-zero regions, for performance 454 for (u16 j = 0; j < entityGrid.m_H; ++j) 455 for (u16 i = 0; i < entityGrid.m_W; ++i) 456 playerGrid.set(i, j, playerGrid.get(i, j) + entityGrid.get(i, j)); 456 for (u16 j = 0; j < tilesH; ++j) 457 { 458 for (u16 i = 0; i < tilesW; ++i) 459 { 460 playerGrid.add(i, j, entityTempGrid.get(i, j)); 461 } 462 } 457 463 } 458 464 459 465 playerGrids.push_back(std::make_pair(it->first, playerGrid)); 460 466 } 461 467 462 468 // Set m_Territories to the player ID with the highest influence for each tile 469 const size_t playerGridsSize = playerGrids.size(); 463 470 for (u16 j = 0; j < tilesH; ++j) 464 471 { 465 472 for (u16 i = 0; i < tilesW; ++i) 466 473 { 467 474 u32 bestWeight = 0; 468 for (size_t k = 0; k < playerGrids .size(); ++k)475 for (size_t k = 0; k < playerGridsSize; ++k) 469 476 { 470 477 u32 w = playerGrids[k].second.get(i, j); 471 478 if (w > bestWeight) … … 480 487 481 488 // Detect territories connected to a 'root' influence (typically a civ center) 482 489 // belonging to their player, and mark them with the connected flag 490 Grid<u8>& grid = *m_Territories; 491 const u16 maxi = (u16)(grid.m_W-1); 492 const u16 maxj = (u16)(grid.m_H-1); 493 494 std::vector<std::pair<u16, u16> > tileStack; 495 // TODO: pre-allocate once and for all 496 //tileStack.reserve(500); 483 497 for (std::vector<entity_id_t>::iterator it = rootInfluenceEntities.begin(); it != rootInfluenceEntities.end(); ++it) 484 498 { 485 499 // (These components must be valid else the entities wouldn't be added to this list) 486 500 CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), *it); 487 501 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *it); 488 502 489 CFixedVector2D pos = cmpPosition->GetPosition2D();490 u16 i = (u16)clamp((pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1);491 u16 j = (u16)clamp((pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesH-1);503 const CFixedVector2D pos = cmpPosition->GetPosition2D(); 504 const u16 i = (u16)clamp((pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1); 505 const u16 j = (u16)clamp((pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesH-1); 492 506 493 u8 owner = (u8)cmpOwnership->GetOwner();507 const u8 owner = (u8)cmpOwnership->GetOwner(); 494 508 495 509 if (m_Territories->get(i, j) != owner) 496 510 continue; 497 511 498 512 // TODO: would be nice to refactor some of the many flood fill 499 513 // algorithms in this component 514 tileStack.clear(); 515 #define MARK_AND_PUSH(ki, kj) STMT(grid.set(ki, kj, owner | TERRITORY_CONNECTED_MASK); tileStack.push_back(std::make_pair(ki, kj)); ) 500 516 501 Grid<u8>& grid = *m_Territories;502 503 u16 maxi = (u16)(grid.m_W-1);504 u16 maxj = (u16)(grid.m_H-1);505 506 std::vector<std::pair<u16, u16> > tileStack;507 508 #define MARK_AND_PUSH(i, j) STMT(grid.set(i, j, owner | TERRITORY_CONNECTED_MASK); tileStack.push_back(std::make_pair(i, j)); )509 510 517 MARK_AND_PUSH(i, j); 511 518 while (!tileStack.empty()) 512 519 { 513 int ti = tileStack.back().first;514 int tj = tileStack.back().second;520 const int ti = tileStack.back().first; 521 const int tj = tileStack.back().second; 515 522 tileStack.pop_back(); 516 523 517 524 if (ti > 0 && grid.get(ti-1, tj) == owner) … … 743 750 m_TerritoryManager.CalculateTerritories(); 744 751 } 745 752 746 void TerritoryOverlay::ProcessTile( ssize_t i,ssize_t j)753 void TerritoryOverlay::ProcessTile(const ssize_t i, const ssize_t j) 747 754 { 748 755 if (!m_TerritoryManager.m_Territories) 749 756 return; -
source/simulation2/components/CCmpPathfinder_Common.h
240 240 241 241 virtual const Grid<u16>& GetPassabilityGrid(); 242 242 243 virtual void ComputePath( entity_pos_t x0, entity_pos_t z0, const Goal& goal, pass_class_t passClass,cost_class_t costClass, Path& ret);243 virtual void ComputePath(const entity_pos_t x0, const entity_pos_t z0, const Goal& goal, const pass_class_t passClass, const cost_class_t costClass, Path& ret); 244 244 245 virtual u32 ComputePathAsync( entity_pos_t x0, entity_pos_t z0, const Goal& goal, pass_class_t passClass, cost_class_t costClass,entity_id_t notify);245 virtual u32 ComputePathAsync(const entity_pos_t x0, const entity_pos_t z0, const Goal& goal, const pass_class_t passClass, const cost_class_t costClass, const entity_id_t notify); 246 246 247 virtual void ComputeShortPath(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t r, entity_pos_t range, const Goal& goal,pass_class_t passClass, Path& ret);247 virtual void ComputeShortPath(const IObstructionTestFilter& filter, const entity_pos_t x0, const entity_pos_t z0, const entity_pos_t r, const entity_pos_t range, const Goal& goal, const pass_class_t passClass, Path& ret); 248 248 249 virtual u32 ComputeShortPathAsync( entity_pos_t x0, entity_pos_t z0, entity_pos_t r, entity_pos_t range, const Goal& goal, pass_class_t passClass, bool avoidMovingUnits, entity_id_t controller,entity_id_t notify);249 virtual u32 ComputeShortPathAsync(const entity_pos_t x0, const entity_pos_t z0, const entity_pos_t r, const entity_pos_t range, const Goal& goal, const pass_class_t passClass, const bool avoidMovingUnits, const entity_id_t controller, const entity_id_t notify); 250 250 251 virtual void SetDebugPath( entity_pos_t x0, entity_pos_t z0, const Goal& goal, pass_class_t passClass,cost_class_t costClass);251 virtual void SetDebugPath(const entity_pos_t x0, const entity_pos_t z0, const Goal& goal, const pass_class_t passClass, const cost_class_t costClass); 252 252 253 253 virtual void ResetDebugPath(); 254 254 255 255 virtual void SetDebugOverlay(bool enabled); 256 256 257 virtual fixed GetMovementSpeed( entity_pos_t x0, entity_pos_t z0, cost_class_t costClass);257 virtual fixed GetMovementSpeed( const entity_pos_t x0, const entity_pos_t z0, const cost_class_t costClass) ; 258 258 259 virtual CFixedVector2D GetNearestPointOnGoal( CFixedVector2D pos, const Goal& goal);259 virtual CFixedVector2D GetNearestPointOnGoal(const CFixedVector2D& pos, const Goal& goal) const; 260 260 261 261 virtual bool CheckMovement(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, pass_class_t passClass); 262 262 … … 275 275 /** 276 276 * Returns the tile containing the given position 277 277 */ 278 void NearestTile( entity_pos_t x, entity_pos_t z, u16& i, u16& j)278 void NearestTile( const entity_pos_t x, const entity_pos_t z, u16& i, u16& j) const 279 279 { 280 280 i = (u16)clamp((x / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), 0, m_MapSize-1); 281 281 j = (u16)clamp((z / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), 0, m_MapSize-1); … … 284 284 /** 285 285 * Returns the position of the center of the given tile 286 286 */ 287 static void TileCenter( u16 i, u16 j, entity_pos_t& x, entity_pos_t& z)287 static void TileCenter(const u16 i, const u16 j, entity_pos_t& x, entity_pos_t& z) 288 288 { 289 289 x = entity_pos_t::FromInt(i*(int)TERRAIN_TILE_SIZE + (int)TERRAIN_TILE_SIZE/2); 290 290 z = entity_pos_t::FromInt(j*(int)TERRAIN_TILE_SIZE + (int)TERRAIN_TILE_SIZE/2); 291 291 } 292 292 293 static fixed DistanceToGoal( CFixedVector2Dpos, const CCmpPathfinder::Goal& goal);293 static fixed DistanceToGoal(const CFixedVector2D& pos, const CCmpPathfinder::Goal& goal); 294 294 295 295 /** 296 296 * Regenerates the grid based on the current obstruction list, if necessary … … 298 298 void UpdateGrid(); 299 299 300 300 void RenderSubmit(SceneCollector& collector); 301 public: 302 303 private: 301 304 }; 302 305 303 306 #endif // INCLUDED_CCMPPATHFINDER_COMMON -
source/simulation2/components/CCmpRangeManager.cpp
57 57 * Convert an owner ID (-1 = unowned, 0 = gaia, 1..30 = players) 58 58 * into a 32-bit mask for quick set-membership tests. 59 59 */ 60 static u32 CalcOwnerMask( player_id_t owner)60 static u32 CalcOwnerMask(const player_id_t owner) 61 61 { 62 62 if (owner >= -1 && owner < 31) 63 63 return 1 << (1+owner); … … 68 68 /** 69 69 * Returns LOS mask for given player. 70 70 */ 71 static u32 CalcPlayerLosMask( player_id_t player)71 static u32 CalcPlayerLosMask(const player_id_t player) 72 72 { 73 73 if (player > 0 && player <= 16) 74 74 return ICmpRangeManager::LOS_MASK << (2*(player-1)); … … 78 78 /** 79 79 * Returns shared LOS mask for given list of players. 80 80 */ 81 static u32 CalcSharedLosMask( std::vector<player_id_t>players)81 static u32 CalcSharedLosMask(const std::vector<player_id_t> &players) 82 82 { 83 83 u32 playerMask = 0; 84 84 for (size_t i = 0; i < players.size(); i++) … … 722 722 // no entities will move until we've finished checking all the ranges 723 723 std::vector<std::pair<entity_id_t, CMessageRangeUpdate> > messages; 724 724 725 std::vector<entity_id_t> r; 726 std::vector<entity_id_t> added; 727 std::vector<entity_id_t> removed; 728 725 729 for (std::map<tag_t, Query>::iterator it = m_Queries.begin(); it != m_Queries.end(); ++it) 726 730 { 727 731 Query& q = it->second; … … 733 737 if (!cmpSourcePosition || !cmpSourcePosition->IsInWorld()) 734 738 continue; 735 739 736 std::vector<entity_id_t> r;740 r.clear(); 737 741 r.reserve(q.lastMatch.size()); 738 742 739 743 PerformQuery(q, r); 740 744 745 added.clear(); 746 removed.clear(); 741 747 // Compute the changes vs the last match 742 std::vector<entity_id_t> added;743 std::vector<entity_id_t> removed;744 748 std::set_difference(r.begin(), r.end(), q.lastMatch.begin(), q.lastMatch.end(), std::back_inserter(added)); 745 749 std::set_difference(q.lastMatch.begin(), q.lastMatch.end(), r.begin(), r.end(), std::back_inserter(removed)); 746 750 747 751 if (added.empty() && removed.empty()) 748 752 continue; 749 753 754 750 755 // Return the 'added' list sorted by distance from the entity 751 756 // (Don't bother sorting 'removed' because they might not even have positions or exist any more) 752 CFixedVector2D pos = cmpSourcePosition->GetPosition2D();757 const CFixedVector2D pos (cmpSourcePosition->GetPosition2D()); 753 758 std::stable_sort(added.begin(), added.end(), EntityDistanceOrdering(m_EntityData, pos)); 754 759 755 760 messages.push_back(std::make_pair(q.source, CMessageRangeUpdate(it->first))); … … 799 804 CmpPtr<ICmpPosition> cmpSourcePosition(GetSimContext(), q.source); 800 805 if (!cmpSourcePosition || !cmpSourcePosition->IsInWorld()) 801 806 return; 802 CFixedVector2D pos = cmpSourcePosition->GetPosition2D();807 const CFixedVector2D pos (cmpSourcePosition->GetPosition2D()); 803 808 804 809 // Special case: range -1.0 means check all entities ignoring distance 805 810 if (q.maxRange == entity_pos_t::FromInt(-1)) … … 815 820 else 816 821 { 817 822 // Get a quick list of entities that are potentially in range 818 std::vector<entity_id_t> ents = m_Subdivision.GetNear(pos, q.maxRange); 823 std::vector<entity_id_t> ents; 824 m_Subdivision.GetNear(pos, q.maxRange, ents); 819 825 820 826 for (size_t i = 0; i < ents.size(); ++i) 821 827 { … … 826 832 continue; 827 833 828 834 // Restrict based on precise distance 829 int distVsMax = (CFixedVector2D(it->second.x, it->second.z) - pos).CompareLength(q.maxRange);835 const int distVsMax = (CFixedVector2D(it->second.x, it->second.z) - pos).CompareLength(q.maxRange); 830 836 if (distVsMax > 0) 831 837 continue; 832 838 833 839 if (!q.minRange.IsZero()) 834 840 { 835 int distVsMin = (CFixedVector2D(it->second.x, it->second.z) - pos).CompareLength(q.minRange);841 const int distVsMin = (CFixedVector2D(it->second.x, it->second.z) - pos).CompareLength(q.minRange); 836 842 if (distVsMin < 0) 837 843 continue; 838 844 } … … 970 976 971 977 // LOS implementation: 972 978 973 virtual CLosQuerier GetLosQuerier( player_id_t player)979 virtual CLosQuerier GetLosQuerier(const player_id_t player) 974 980 { 975 981 if (GetLosRevealAll(player)) 976 982 return CLosQuerier(0xFFFFFFFFu, m_LosStateRevealed, m_TerrainVerticesPerSide); … … 978 984 return CLosQuerier(GetSharedLosMask(player), m_LosState, m_TerrainVerticesPerSide); 979 985 } 980 986 981 virtual ELosVisibility GetLosVisibility( entity_id_t ent, player_id_t player,bool forceRetainInFog)987 virtual ELosVisibility GetLosVisibility(const entity_id_t ent, const player_id_t player, const bool forceRetainInFog) 982 988 { 983 989 // (We can't use m_EntityData since this needs to handle LOCAL entities too) 984 990 … … 987 993 if (!cmpPosition || !cmpPosition->IsInWorld()) 988 994 return VIS_HIDDEN; 989 995 990 CFixedVector2D pos = cmpPosition->GetPosition2D();996 const CFixedVector2D pos (cmpPosition->GetPosition2D()); 991 997 992 int i = (pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest();993 int j = (pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest();998 const int i = (pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); 999 const int j = (pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); 994 1000 995 1001 // Reveal flag makes all positioned entities visible 996 1002 if (GetLosRevealAll(player)) … … 1019 1025 return VIS_HIDDEN; 1020 1026 } 1021 1027 1022 virtual void SetLosRevealAll( player_id_t player,bool enabled)1028 virtual void SetLosRevealAll(const player_id_t player, const bool enabled) 1023 1029 { 1024 1030 m_LosRevealAll[player] = enabled; 1025 1031 } 1026 1032 1027 virtual bool GetLosRevealAll( player_id_t player)1033 virtual bool GetLosRevealAll(const player_id_t player) 1028 1034 { 1029 1035 std::map<player_id_t, bool>::const_iterator it; 1030 1036 … … 1041 1047 return false; 1042 1048 } 1043 1049 1044 virtual void SetLosCircular( bool enabled)1050 virtual void SetLosCircular(const bool enabled) 1045 1051 { 1046 1052 m_LosCircular = enabled; 1047 1053 … … 1058 1064 m_SharedLosMasks[player] = CalcSharedLosMask(players); 1059 1065 } 1060 1066 1061 virtual u32 GetSharedLosMask( player_id_t player)1067 virtual u32 GetSharedLosMask(const player_id_t player) 1062 1068 { 1063 1069 std::map<player_id_t, u32>::const_iterator it = m_SharedLosMasks.find(player); 1064 1070 ENSURE(it != m_SharedLosMasks.end()); … … 1098 1104 * Returns whether the given vertex is outside the normal bounds of the world 1099 1105 * (i.e. outside the range of a circular map) 1100 1106 */ 1101 inline bool LosIsOffWorld( ssize_t i, ssize_t j)1107 inline bool LosIsOffWorld(const ssize_t i, const ssize_t j) const 1102 1108 { 1103 1109 // WARNING: CCmpObstructionManager::Rasterise needs to be kept in sync with this 1104 1110 const ssize_t edgeSize = 3; // number of vertexes around the edge that will be off-world … … 1107 1113 { 1108 1114 // With a circular map, vertex is off-world if hypot(i - size/2, j - size/2) >= size/2: 1109 1115 1110 ssize_t dist2 = (i - m_TerrainVerticesPerSide/2)*(i - m_TerrainVerticesPerSide/2)1116 const ssize_t dist2 = (i - m_TerrainVerticesPerSide/2)*(i - m_TerrainVerticesPerSide/2) 1111 1117 + (j - m_TerrainVerticesPerSide/2)*(j - m_TerrainVerticesPerSide/2); 1112 1118 1113 ssize_t r = m_TerrainVerticesPerSide/2 - edgeSize + 1;1119 const ssize_t r = m_TerrainVerticesPerSide/2 - edgeSize + 1; 1114 1120 // subtract a bit from the radius to ensure nice 1115 1121 // SoD blurring around the edges of the map 1116 1122 … … 1128 1134 /** 1129 1135 * Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive). 1130 1136 */ 1131 inline void LosAddStripHelper( u8 owner, i32 i0, i32 i1, i32 j, u16*counts)1137 inline void LosAddStripHelper(const u8 owner, const i32 i0, const i32 i1, const i32 j, u16* const __restrict counts) 1132 1138 { 1133 1139 if (i1 < i0) 1134 1140 return; 1135 1141 1136 i32 idx0 = j*m_TerrainVerticesPerSide + i0;1137 i32 idx1 = j*m_TerrainVerticesPerSide + i1;1142 const i32 idx0 = j*m_TerrainVerticesPerSide + i0; 1143 const i32 idx1 = j*m_TerrainVerticesPerSide + i1; 1138 1144 1139 1145 for (i32 idx = idx0; idx <= idx1; ++idx) 1140 1146 { 1141 1147 // Increasing from zero to non-zero - move from unexplored/explored to visible+explored 1142 1148 if (counts[idx] == 0) 1143 1149 { 1144 i32 i = i0 + idx - idx0;1150 const i32 i = i0 + idx - idx0; 1145 1151 if (!LosIsOffWorld(i, j)) 1146 1152 m_LosState[idx] |= ((LOS_VISIBLE | LOS_EXPLORED) << (2*(owner-1))); 1147 1153 } … … 1154 1160 /** 1155 1161 * Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive). 1156 1162 */ 1157 inline void LosRemoveStripHelper( u8 owner, i32 i0, i32 i1, i32 j, u16*counts)1163 inline void LosRemoveStripHelper(const u8 owner, const i32 i0, const i32 i1, const i32 j, u16* const __restrict counts) 1158 1164 { 1159 1165 if (i1 < i0) 1160 1166 return; 1161 1167 1162 i32 idx0 = j*m_TerrainVerticesPerSide + i0;1163 i32 idx1 = j*m_TerrainVerticesPerSide + i1;1168 const i32 idx0 = j*m_TerrainVerticesPerSide + i0; 1169 const i32 idx1 = j*m_TerrainVerticesPerSide + i1; 1164 1170 1165 1171 for (i32 idx = idx0; idx <= idx1; ++idx) 1166 1172 { … … 1182 1188 * Assumes owner is in the valid range. 1183 1189 */ 1184 1190 template<bool adding> 1185 void LosUpdateHelper( u8 owner, entity_pos_t visionRange, CFixedVector2Dpos)1191 void LosUpdateHelper(const u8 owner, const entity_pos_t visionRange, const CFixedVector2D& pos) 1186 1192 { 1187 1193 if (m_TerrainVerticesPerSide == 0) // do nothing if not initialised yet 1188 1194 return; … … 1208 1214 1209 1215 // Compute top/bottom coordinates, and clamp to exclude the 1-tile border around the map 1210 1216 // (so that we never render the sharp edge of the map) 1211 i32 j0 = ((pos.Y - visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToInfinity();1212 i32 j1 = ((pos.Y + visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity();1213 i32 j0clamp = std::max(j0, 1);1214 i32 j1clamp = std::min(j1, m_TerrainVerticesPerSide-2);1217 const i32 j0 = ((pos.Y - visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToInfinity(); 1218 const i32 j1 = ((pos.Y + visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(); 1219 const i32 j0clamp = std::max(j0, 1); 1220 const i32 j1clamp = std::min(j1, m_TerrainVerticesPerSide-2); 1215 1221 1216 1222 // Translate world coordinates into fractional tile-space coordinates 1217 entity_pos_t x = pos.X / (int)TERRAIN_TILE_SIZE;1218 entity_pos_t y = pos.Y / (int)TERRAIN_TILE_SIZE;1219 entity_pos_t r = visionRange / (int)TERRAIN_TILE_SIZE;1220 entity_pos_t r2 = r.Square();1223 const entity_pos_t x = pos.X / (int)TERRAIN_TILE_SIZE; 1224 const entity_pos_t y = pos.Y / (int)TERRAIN_TILE_SIZE; 1225 const entity_pos_t r = visionRange / (int)TERRAIN_TILE_SIZE; 1226 const entity_pos_t r2 = r.Square(); 1221 1227 1222 1228 // Compute the integers on either side of x 1223 i32 xfloor = (x - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity();1224 i32 xceil = (x + entity_pos_t::Epsilon()).ToInt_RoundToInfinity();1229 const i32 xfloor = (x - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity(); 1230 const i32 xceil = (x + entity_pos_t::Epsilon()).ToInt_RoundToInfinity(); 1225 1231 1226 1232 // Initialise the strip (i0, i1) to a rough guess 1227 1233 i32 i0 = xfloor; … … 1257 1263 1258 1264 // Clamp the strip to exclude the 1-tile border, 1259 1265 // then add or remove the strip as requested 1260 i32 i0clamp = std::max(i0, 1);1261 i32 i1clamp = std::min(i1, m_TerrainVerticesPerSide-2);1266 const i32 i0clamp = std::max(i0, 1); 1267 const i32 i1clamp = std::min(i1, m_TerrainVerticesPerSide-2); 1262 1268 if (adding) 1263 1269 LosAddStripHelper(owner, i0clamp, i1clamp, j, countsData); 1264 1270 else … … 1271 1277 * by removing visibility around the 'from' position 1272 1278 * and then adding visibility around the 'to' position. 1273 1279 */ 1274 void LosUpdateHelperIncremental( u8 owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2Dto)1280 void LosUpdateHelperIncremental(const u8 owner, const entity_pos_t visionRange, const CFixedVector2D& from, const CFixedVector2D& to) 1275 1281 { 1276 1282 if (m_TerrainVerticesPerSide == 0) // do nothing if not initialised yet 1277 1283 return; … … 1284 1290 if (counts.empty()) 1285 1291 counts.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); 1286 1292 1287 u16* co untsData = &counts[0];1293 u16* const __restrict countsData = &counts[0]; 1288 1294 1289 1295 // See comments in LosUpdateHelper. 1290 1296 // This does exactly the same, except computing the strips for … … 1293 1299 // so we can compute the difference between the removed/added strips 1294 1300 // and only have to touch tiles that have a net change.) 1295 1301 1296 i32 j0_from = ((from.Y - visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToInfinity();1297 i32 j1_from = ((from.Y + visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity();1298 i32 j0_to = ((to.Y - visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToInfinity();1299 i32 j1_to = ((to.Y + visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity();1300 i32 j0clamp = std::max(std::min(j0_from, j0_to), 1);1301 i32 j1clamp = std::min(std::max(j1_from, j1_to), m_TerrainVerticesPerSide-2);1302 const i32 j0_from = ((from.Y - visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToInfinity(); 1303 const i32 j1_from = ((from.Y + visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(); 1304 const i32 j0_to = ((to.Y - visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToInfinity(); 1305 const i32 j1_to = ((to.Y + visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(); 1306 const i32 j0clamp = std::max(std::min(j0_from, j0_to), 1); 1307 const i32 j1clamp = std::min(std::max(j1_from, j1_to), m_TerrainVerticesPerSide-2); 1302 1308 1303 entity_pos_t x_from = from.X / (int)TERRAIN_TILE_SIZE;1304 entity_pos_t y_from = from.Y / (int)TERRAIN_TILE_SIZE;1305 entity_pos_t x_to = to.X / (int)TERRAIN_TILE_SIZE;1306 entity_pos_t y_to = to.Y / (int)TERRAIN_TILE_SIZE;1307 entity_pos_t r = visionRange / (int)TERRAIN_TILE_SIZE;1308 entity_pos_t r2 = r.Square();1309 const entity_pos_t x_from = from.X / (int)TERRAIN_TILE_SIZE; 1310 const entity_pos_t y_from = from.Y / (int)TERRAIN_TILE_SIZE; 1311 const entity_pos_t x_to = to.X / (int)TERRAIN_TILE_SIZE; 1312 const entity_pos_t y_to = to.Y / (int)TERRAIN_TILE_SIZE; 1313 const entity_pos_t r = visionRange / (int)TERRAIN_TILE_SIZE; 1314 const entity_pos_t r2 = r.Square(); 1309 1315 1310 i32 xfloor_from = (x_from - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity();1311 i32 xceil_from = (x_from + entity_pos_t::Epsilon()).ToInt_RoundToInfinity();1312 i32 xfloor_to = (x_to - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity();1313 i32 xceil_to = (x_to + entity_pos_t::Epsilon()).ToInt_RoundToInfinity();1316 const i32 xfloor_from = (x_from - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity(); 1317 const i32 xceil_from = (x_from + entity_pos_t::Epsilon()).ToInt_RoundToInfinity(); 1318 const i32 xfloor_to = (x_to - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity(); 1319 const i32 xceil_to = (x_to + entity_pos_t::Epsilon()).ToInt_RoundToInfinity(); 1314 1320 1315 1321 i32 i0_from = xfloor_from; 1316 1322 i32 i1_from = xceil_from; … … 1319 1325 1320 1326 for (i32 j = j0clamp; j <= j1clamp; ++j) 1321 1327 { 1322 entity_pos_t dy_from = entity_pos_t::FromInt(j) - y_from;1323 entity_pos_t dy2_from = dy_from.Square();1328 const entity_pos_t dy_from = entity_pos_t::FromInt(j) - y_from; 1329 const entity_pos_t dy2_from = dy_from.Square(); 1324 1330 while (dy2_from + (entity_pos_t::FromInt(i0_from-1) - x_from).Square() <= r2) 1325 1331 --i0_from; 1326 1332 while (i0_from < xceil_from && dy2_from + (entity_pos_t::FromInt(i0_from) - x_from).Square() > r2) … … 1330 1336 while (i1_from > xfloor_from && dy2_from + (entity_pos_t::FromInt(i1_from) - x_from).Square() > r2) 1331 1337 --i1_from; 1332 1338 1333 entity_pos_t dy_to = entity_pos_t::FromInt(j) - y_to;1334 entity_pos_t dy2_to = dy_to.Square();1339 const entity_pos_t dy_to = entity_pos_t::FromInt(j) - y_to; 1340 const entity_pos_t dy2_to = dy_to.Square(); 1335 1341 while (dy2_to + (entity_pos_t::FromInt(i0_to-1) - x_to).Square() <= r2) 1336 1342 --i0_to; 1337 1343 while (i0_to < xceil_to && dy2_to + (entity_pos_t::FromInt(i0_to) - x_to).Square() > r2) … … 1361 1367 // Check whether this strip moved at all 1362 1368 if (!(i0_to == i0_from && i1_to == i1_from)) 1363 1369 { 1364 i32 i0clamp_from = std::max(i0_from, 1);1365 i32 i1clamp_from = std::min(i1_from, m_TerrainVerticesPerSide-2);1366 i32 i0clamp_to = std::max(i0_to, 1);1367 i32 i1clamp_to = std::min(i1_to, m_TerrainVerticesPerSide-2);1370 const i32 i0clamp_from = std::max(i0_from, 1); 1371 const i32 i1clamp_from = std::min(i1_from, m_TerrainVerticesPerSide-2); 1372 const i32 i0clamp_to = std::max(i0_to, 1); 1373 const i32 i1clamp_to = std::min(i1_to, m_TerrainVerticesPerSide-2); 1368 1374 1369 1375 // Check whether one strip is negative width, 1370 1376 // and we can just add/remove the entire other strip … … 1396 1402 } 1397 1403 } 1398 1404 1399 void LosAdd( player_id_t owner, entity_pos_t visionRange, CFixedVector2Dpos)1405 void LosAdd(const player_id_t owner, const entity_pos_t visionRange, const CFixedVector2D& pos) 1400 1406 { 1401 1407 if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) 1402 1408 return; … … 1404 1410 LosUpdateHelper<true>((u8)owner, visionRange, pos); 1405 1411 } 1406 1412 1407 void LosRemove( player_id_t owner, entity_pos_t visionRange, CFixedVector2Dpos)1413 void LosRemove(const player_id_t owner, const entity_pos_t visionRange, const CFixedVector2D& pos) 1408 1414 { 1409 1415 if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) 1410 1416 return; … … 1412 1418 LosUpdateHelper<false>((u8)owner, visionRange, pos); 1413 1419 } 1414 1420 1415 void LosMove( player_id_t owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2Dto)1421 void LosMove(const player_id_t owner, const entity_pos_t visionRange, const CFixedVector2D& from, const CFixedVector2D& to) 1416 1422 { 1417 1423 if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) 1418 1424 return; … … 1432 1438 } 1433 1439 } 1434 1440 1435 virtual i32 GetPercentMapExplored( player_id_t player)1441 virtual i32 GetPercentMapExplored(const player_id_t player) 1436 1442 { 1437 1443 i32 exploredVertices = 0; 1438 1444 i32 overallVisibleVertices = 0;