Ticket #2430: newSubdivision.2.patch
File newSubdivision.2.patch, 15.9 KB (added by , 9 years ago) |
---|
-
source/simulation2/components/CCmpObstruction.cpp
470 470 pos = cmpPosition->GetPreviousPosition2D(); 471 471 else 472 472 pos = cmpPosition->GetPosition2D(); 473 if (m_Type == STATIC) 474 out = cmpObstructionManager->GetStaticShapeObstruction(pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1); 475 else if (m_Type == UNIT) 473 if (m_Type == UNIT) 476 474 out = cmpObstructionManager->GetUnitShapeObstruction(pos.X, pos.Y, m_Size0); 477 475 else 478 476 out = cmpObstructionManager->GetStaticShapeObstruction(pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1); … … 487 485 return entity_pos_t::Zero(); 488 486 } 489 487 488 virtual entity_pos_t GetSize() 489 { 490 if (m_Type == UNIT) 491 return m_Size0; 492 else 493 return CFixedVector2D(m_Size0 / 2, m_Size1 / 2).Length(); 494 } 495 490 496 virtual bool IsControlPersistent() 491 497 { 492 498 return m_ControlPersist; -
source/simulation2/components/CCmpRangeManager.cpp
32 32 #include "simulation2/components/ICmpVision.h" 33 33 #include "simulation2/components/ICmpWaterManager.h" 34 34 #include "simulation2/helpers/Render.h" 35 #include "simulation2/helpers/Spatial.h"36 35 37 36 #include "graphics/Overlay.h" 38 37 #include "graphics/Terrain.h" … … 172 171 */ 173 172 struct EntityData 174 173 { 175 EntityData() : visibilities(0), retainInFog(0), owner(-1), inWorld(0), flags(1), scriptedVisibility(0) { }174 EntityData() : visibilities(0), size(0), retainInFog(0), owner(-1), inWorld(0), flags(1), scriptedVisibility(0) { } 176 175 entity_pos_t x, z; 177 176 entity_pos_t visionRange; 178 177 u32 visibilities; // 2-bit visibility, per player 178 u32 size; 179 179 u8 retainInFog; // boolean 180 180 i8 owner; 181 181 u8 inWorld; // boolean … … 183 183 u8 scriptedVisibility; // boolean, see ComputeLosVisibility 184 184 }; 185 185 186 cassert(sizeof(EntityData) == 2 4);186 cassert(sizeof(EntityData) == 28); 187 187 188 188 /** 189 189 * Serialization helper template for Query … … 236 236 serialize.NumberFixed_Unbounded("z", value.z); 237 237 serialize.NumberFixed_Unbounded("vision", value.visionRange); 238 238 serialize.NumberU32_Unbounded("visibilities", value.visibilities); 239 serialize.NumberU32_Unbounded("size", value.size); 239 240 serialize.NumberU8("retain in fog", value.retainInFog, 0, 1); 240 241 serialize.NumberI8_Unbounded("owner", value.owner); 241 242 serialize.NumberU8("in world", value.inWorld, 0, 1); … … 314 315 std::map<tag_t, Query> m_Queries; 315 316 EntityMap<EntityData> m_EntityData; 316 317 317 SpatialSubdivision m_Subdivision; // spatial index of m_EntityData318 FastSpatialSubdivision m_Subdivision; // spatial index of m_EntityData 318 319 319 320 // LOS state: 320 321 static const player_id_t MAX_LOS_PLAYER_ID = 16; … … 461 462 if (cmpVisibility) 462 463 entdata.retainInFog = (cmpVisibility->GetRetainInFog() ? 1 : 0); 463 464 465 // Store the size 466 CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent); 467 if (cmpObstruction) 468 entdata.size = cmpObstruction->GetSize().ToInt_RoundToInfinity(); 469 464 470 // Remember this entity 465 471 m_EntityData.insert(ent, entdata); 466 472 break; … … 482 488 { 483 489 CFixedVector2D from(it->second.x, it->second.z); 484 490 CFixedVector2D to(msgData.x, msgData.z); 485 m_Subdivision.Move(ent, from, to );491 m_Subdivision.Move(ent, from, to, it->second.size); 486 492 LosMove(it->second.owner, it->second.visionRange, from, to); 487 493 i32 oldLosTile = PosToLosTilesHelper(it->second.x, it->second.z); 488 494 i32 newLosTile = PosToLosTilesHelper(msgData.x, msgData.z); … … 495 501 else 496 502 { 497 503 CFixedVector2D to(msgData.x, msgData.z); 498 m_Subdivision.Add(ent, to );504 m_Subdivision.Add(ent, to, it->second.size); 499 505 LosAdd(it->second.owner, it->second.visionRange, to); 500 506 AddToTile(PosToLosTilesHelper(msgData.x, msgData.z), ent); 501 507 } … … 509 515 if (it->second.inWorld) 510 516 { 511 517 CFixedVector2D from(it->second.x, it->second.z); 512 m_Subdivision.Remove(ent, from );518 m_Subdivision.Remove(ent, from, it->second.size); 513 519 LosRemove(it->second.owner, it->second.visionRange, from); 514 520 RemoveFromTile(PosToLosTilesHelper(it->second.x, it->second.z), ent); 515 521 } … … 559 565 560 566 if (it->second.inWorld) 561 567 { 562 m_Subdivision.Remove(ent, CFixedVector2D(it->second.x, it->second.z) );568 m_Subdivision.Remove(ent, CFixedVector2D(it->second.x, it->second.z), it->second.size); 563 569 RemoveFromTile(PosToLosTilesHelper(it->second.x, it->second.z), ent); 564 570 } 565 571 … … 640 646 641 647 std::vector<std::vector<u16> > oldPlayerCounts = m_LosPlayerCounts; 642 648 std::vector<u32> oldStateRevealed = m_LosStateRevealed; 643 SpatialSubdivision oldSubdivision = m_Subdivision;649 FastSpatialSubdivision oldSubdivision = m_Subdivision; 644 650 std::vector<std::set<entity_id_t> > oldLosTiles = m_LosTiles; 645 651 646 652 ResetDerivedData(true); … … 671 677 debug_warn(L"inconsistent los tiles"); 672 678 } 673 679 674 SpatialSubdivision* GetSubdivision()680 FastSpatialSubdivision* GetSubdivision() 675 681 { 676 return & 682 return &m_Subdivision; 677 683 } 678 684 679 685 // Reinitialise subdivisions and LOS data, based on entity data … … 744 750 745 751 void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1) 746 752 { 747 // Use 8x8 tile subdivisions 748 // (TODO: find the optimal number instead of blindly guessing) 749 m_Subdivision.Reset(x1, z1, entity_pos_t::FromInt(8*TERRAIN_TILE_SIZE)); 753 m_Subdivision.Reset(x1, z1); 750 754 751 755 for (EntityMap<EntityData>::const_iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it) 752 756 { 753 757 if (it->second.inWorld) 754 m_Subdivision.Add(it->first, CFixedVector2D(it->second.x, it->second.z) );758 m_Subdivision.Add(it->first, CFixedVector2D(it->second.x, it->second.z), it->second.size); 755 759 } 756 760 } 757 761 … … 1347 1351 } 1348 1352 1349 1353 // render subdivision grid 1350 float divSize = m_Subdivision.GetDivisionSize().ToFloat(); 1351 int width = m_Subdivision.GetWidth(); 1352 int height = m_Subdivision.GetHeight(); 1353 for (int x = 0; x < width; ++x) 1354 float divSize = m_Subdivision.GetDivisionSize(); 1355 int size = m_Subdivision.GetWidth(); 1356 for (int x = 0; x < size; ++x) 1354 1357 { 1355 for (int y = 0; y < height; ++y)1358 for (int y = 0; y < size; ++y) 1356 1359 { 1357 1360 m_DebugOverlayLines.push_back(SOverlayLine()); 1358 1361 m_DebugOverlayLines.back().m_Color = subdivColor; -
source/simulation2/components/ICmpObstruction.h
52 52 */ 53 53 virtual bool GetPreviousObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) = 0; 54 54 55 virtual entity_pos_t GetSize() = 0; 56 55 57 virtual entity_pos_t GetUnitRadius() = 0; 56 58 57 59 virtual bool IsControlPersistent() = 0; -
source/simulation2/components/ICmpRangeManager.h
77 77 * Access the spatial subdivision kept by the range manager. 78 78 * @return pointer to spatial subdivision structure. 79 79 */ 80 virtual SpatialSubdivision* GetSubdivision() = 0;80 virtual FastSpatialSubdivision* GetSubdivision() = 0; 81 81 82 82 /** 83 83 * Set the bounds of the world. -
source/simulation2/helpers/Selection.cpp
28 28 #include "simulation2/components/ICmpSelectable.h" 29 29 #include "simulation2/components/ICmpVisual.h" 30 30 #include "simulation2/components/ICmpUnitRenderer.h" 31 #include "simulation2/helpers/Spatial.h"32 31 #include "ps/CLogger.h" 33 32 #include "ps/Profiler2.h" 34 33 -
source/simulation2/helpers/Spatial.h
1 /* Copyright (C) 201 0Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … 32 32 * Move/Remove are called with the same coordinates originally passed 33 33 * to Add (since this class doesn't remember which divisions an item 34 34 * occupies). 35 *36 * (TODO: maybe an adaptive quadtree would be better than fixed sizes?)37 35 */ 38 36 class SpatialSubdivision 39 37 { … … 66 64 uint32_t m_DivisionsW; 67 65 uint32_t m_DivisionsH; 68 66 69 friend struct SerializeSubDivisionGrid;70 67 friend struct SerializeSpatialSubdivision; 71 68 72 69 public: … … 346 343 } 347 344 }; 348 345 346 347 /** 348 * A basic square subdivision scheme for finding entities in range 349 * More efficient than SpatialSubdivision, but a bit less precise 350 * (so the querier will get more entities to perform tests on). 351 * 352 * Items are stored in vectors in fixed-size divisions. 353 * 354 * Items have a size (min/max values of their axis-aligned bounding box). 355 * If that size is higher than a subdivision's size, they're stored in the "general" vector 356 * This means that if too many objects have a size that's big, it'll end up being slow 357 * We want subdivisions to be as small as possible yet contain as many items as possible. 358 * 359 * It is the caller's responsibility to ensure items are only added once, aren't removed 360 * unless they've been added, etc, and that Move/Remove are called with the same coordinates 361 * originally passed to Add (since this class doesn't remember which divisions an item 362 * occupies). 363 * 364 * TODO: If a unit size were to change, it would need to be updated (that doesn't happen for now) 365 */ 366 class FastSpatialSubdivision 367 { 368 private: 369 static const int SUBDIVISION_SIZE = 20; // bigger than most buildings and entities 370 371 std::vector<entity_id_t> m_OverSizedData; 372 std::vector<entity_id_t>* m_SpatialDivisionsData; // fixed size array of subdivisions 373 size_t m_ArrayWidth; // number of columns in m_SpatialDivisionsData 374 375 inline size_t Index(fixed position) 376 { 377 return (position / SUBDIVISION_SIZE).ToInt_RoundToZero(); 378 } 379 380 inline size_t SubdivisionIdx(CFixedVector2D position) 381 { 382 return Index(position.X) + Index(position.Y)*m_ArrayWidth; 383 } 384 385 /** 386 * Efficiently erase from a vector by swapping with the last element and popping it. 387 * Returns true if the element was found and erased, else returns false. 388 */ 389 bool EraseFrom(std::vector<entity_id_t>& vector, entity_id_t item) 390 { 391 auto it = std::find(vector.begin(), vector.end(), item); 392 if (it == vector.end()) 393 return false; 394 395 if ((int)vector.size() > 1) 396 *it = vector.back(); 397 vector.pop_back(); 398 return true; 399 } 400 401 public: 402 FastSpatialSubdivision() : 403 m_SpatialDivisionsData(NULL), m_ArrayWidth(0) 404 { 405 } 406 407 FastSpatialSubdivision(const FastSpatialSubdivision& other) : 408 m_SpatialDivisionsData(NULL), m_ArrayWidth(0) 409 { 410 Reset(other.m_ArrayWidth); 411 std::copy(&other.m_SpatialDivisionsData[0], &other.m_SpatialDivisionsData[m_ArrayWidth*m_ArrayWidth], m_SpatialDivisionsData); 412 } 413 414 ~FastSpatialSubdivision() 415 { 416 if (m_SpatialDivisionsData) 417 delete[] m_SpatialDivisionsData; 418 } 419 420 void Reset(size_t arrayWidth) 421 { 422 if (m_SpatialDivisionsData) 423 SAFE_ARRAY_DELETE(m_SpatialDivisionsData); 424 425 m_ArrayWidth = arrayWidth; 426 m_SpatialDivisionsData = new std::vector<entity_id_t>[m_ArrayWidth*m_ArrayWidth]; 427 m_OverSizedData.clear(); 428 } 429 430 void Reset(fixed w, fixed h) 431 { 432 Reset(std::max(Index(w), Index(h)) + 1); 433 } 434 435 FastSpatialSubdivision& operator=(const FastSpatialSubdivision& other) 436 { 437 if (this != &other) 438 { 439 Reset(other.m_ArrayWidth); 440 std::copy(&other.m_SpatialDivisionsData[0], &other.m_SpatialDivisionsData[m_ArrayWidth*m_ArrayWidth], m_SpatialDivisionsData); 441 } 442 return *this; 443 } 444 445 bool operator==(const FastSpatialSubdivision& other) 446 { 447 if (m_ArrayWidth != other.m_ArrayWidth) 448 return false; 449 if (m_OverSizedData != other.m_OverSizedData) 450 return false; 451 for (size_t idx = 0; idx < m_ArrayWidth*m_ArrayWidth; ++idx) 452 if (m_SpatialDivisionsData[idx] != other.m_SpatialDivisionsData[idx]) 453 return false; 454 return true; 455 } 456 457 inline bool operator!=(const FastSpatialSubdivision& rhs) 458 { 459 return !(*this == rhs); 460 } 461 462 /** 463 * Add an item. 464 */ 465 void Add(entity_id_t item, CFixedVector2D position, u32 size) 466 { 467 if (size > SUBDIVISION_SIZE) 468 { 469 if (std::find(m_OverSizedData.begin(), m_OverSizedData.end(), item) == m_OverSizedData.end()) 470 m_OverSizedData.push_back(item); 471 } 472 else 473 { 474 std::vector<entity_id_t>& subdivision = m_SpatialDivisionsData[SubdivisionIdx(position)]; 475 if (std::find(subdivision.begin(), subdivision.end(), item) == subdivision.end()) 476 subdivision.push_back(item); 477 } 478 } 479 480 /** 481 * Remove an item. 482 * Position must be where we expect to find it, or we won't find it. 483 */ 484 void Remove(entity_id_t item, CFixedVector2D position, u32 size) 485 { 486 if (size > SUBDIVISION_SIZE) 487 EraseFrom(m_OverSizedData, item); 488 else 489 { 490 std::vector<entity_id_t>& subdivision = m_SpatialDivisionsData[SubdivisionIdx(position)]; 491 EraseFrom(subdivision, item); 492 } 493 } 494 495 /** 496 * Equivalent to Remove() then Add(), but slightly faster. 497 * In particular for big objects nothing needs to be done. 498 */ 499 void Move(entity_id_t item, CFixedVector2D oldPosition, CFixedVector2D newPosition, u32 size) 500 { 501 if (size > SUBDIVISION_SIZE) 502 return; 503 if (SubdivisionIdx(newPosition) == SubdivisionIdx(oldPosition)) 504 return; 505 506 std::vector<entity_id_t>& oldSubdivision = m_SpatialDivisionsData[SubdivisionIdx(oldPosition)]; 507 if (EraseFrom(oldSubdivision, item)) 508 { 509 std::vector<entity_id_t>& newSubdivision = m_SpatialDivisionsData[SubdivisionIdx(newPosition)]; 510 newSubdivision.push_back(item); 511 } 512 } 513 514 /** 515 * Returns a sorted list of items that are either in the square or close to it. 516 * It's the responsibility of the querier to do proper distance checking. 517 */ 518 void GetInRange(std::vector<entity_id_t>& out, CFixedVector2D posMin, CFixedVector2D posMax) 519 { 520 size_t minX = Index(posMin.X); 521 size_t minY = Index(posMin.Y); 522 size_t maxX = Index(posMax.X) + 1; 523 size_t maxY = Index(posMax.Y) + 1; 524 525 // Now expand the subdivisions by one so we make sure we've got all elements potentially in range. 526 // Also make sure min >= 0 and max <= width 527 minX = minX > 0 ? minX-1 : 0; 528 minY = minY > 0 ? minY-1 : 0; 529 maxX = maxX < m_ArrayWidth ? maxX+1 : m_ArrayWidth; 530 maxY = maxY < m_ArrayWidth ? maxY+1 : m_ArrayWidth; 531 532 ENSURE(out.empty() && "GetInRange: out is not clean"); 533 534 // Add oversized items, they can be anywhere 535 out.insert(out.end(), m_OverSizedData.begin(), m_OverSizedData.end()); 536 537 for (size_t Y = minY; Y < maxY; ++Y) 538 { 539 for (size_t X = minX; X < maxX; ++X) 540 { 541 std::vector<entity_id_t>& subdivision = m_SpatialDivisionsData[X + Y*m_ArrayWidth]; 542 if (!subdivision.empty()) 543 out.insert(out.end(), subdivision.begin(), subdivision.end()); 544 } 545 } 546 547 std::sort(out.begin(), out.end()); 548 } 549 550 /** 551 * Returns a sorted list of items that are either in the circle or close to it. 552 * It's the responsibility of the querier to do proper distance checking. 553 */ 554 void GetNear(std::vector<entity_id_t>& out, CFixedVector2D pos, entity_pos_t range) 555 { 556 // Because the subdivision size is rather big wrt typical ranges, 557 // this square over-approximation is hopefully not too bad. 558 CFixedVector2D r(range, range); 559 GetInRange(out, pos - r, pos + r); 560 } 561 562 size_t GetDivisionSize() const 563 { 564 return SUBDIVISION_SIZE; 565 } 566 567 size_t GetWidth() const 568 { 569 return m_ArrayWidth; 570 } 571 }; 572 349 573 #endif // INCLUDED_SPATIAL