Ticket #2430: newSubdivision.2.patch

File newSubdivision.2.patch, 15.9 KB (added by Itms, 9 years ago)

Addressed style comments

  • source/simulation2/components/CCmpObstruction.cpp

     
    470470            pos = cmpPosition->GetPreviousPosition2D();
    471471        else
    472472            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)
    476474            out = cmpObstructionManager->GetUnitShapeObstruction(pos.X, pos.Y, m_Size0);
    477475        else
    478476            out = cmpObstructionManager->GetStaticShapeObstruction(pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1);
     
    487485            return entity_pos_t::Zero();
    488486    }
    489487
     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
    490496    virtual bool IsControlPersistent()
    491497    {
    492498        return m_ControlPersist;
  • source/simulation2/components/CCmpRangeManager.cpp

     
    3232#include "simulation2/components/ICmpVision.h"
    3333#include "simulation2/components/ICmpWaterManager.h"
    3434#include "simulation2/helpers/Render.h"
    35 #include "simulation2/helpers/Spatial.h"
    3635
    3736#include "graphics/Overlay.h"
    3837#include "graphics/Terrain.h"
     
    172171 */
    173172struct EntityData
    174173{
    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) { }
    176175    entity_pos_t x, z;
    177176    entity_pos_t visionRange;
    178177    u32 visibilities; // 2-bit visibility, per player
     178    u32 size;
    179179    u8 retainInFog; // boolean
    180180    i8 owner;
    181181    u8 inWorld; // boolean
     
    183183    u8 scriptedVisibility; // boolean, see ComputeLosVisibility
    184184};
    185185
    186 cassert(sizeof(EntityData) == 24);
     186cassert(sizeof(EntityData) == 28);
    187187
    188188/**
    189189 * Serialization helper template for Query
     
    236236        serialize.NumberFixed_Unbounded("z", value.z);
    237237        serialize.NumberFixed_Unbounded("vision", value.visionRange);
    238238        serialize.NumberU32_Unbounded("visibilities", value.visibilities);
     239        serialize.NumberU32_Unbounded("size", value.size);
    239240        serialize.NumberU8("retain in fog", value.retainInFog, 0, 1);
    240241        serialize.NumberI8_Unbounded("owner", value.owner);
    241242        serialize.NumberU8("in world", value.inWorld, 0, 1);
     
    314315    std::map<tag_t, Query> m_Queries;
    315316    EntityMap<EntityData> m_EntityData;
    316317
    317     SpatialSubdivision m_Subdivision; // spatial index of m_EntityData
     318    FastSpatialSubdivision m_Subdivision; // spatial index of m_EntityData
    318319
    319320    // LOS state:
    320321    static const player_id_t MAX_LOS_PLAYER_ID = 16;
     
    461462            if (cmpVisibility)
    462463                entdata.retainInFog = (cmpVisibility->GetRetainInFog() ? 1 : 0);
    463464
     465            // Store the size
     466            CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent);
     467            if (cmpObstruction)
     468                entdata.size = cmpObstruction->GetSize().ToInt_RoundToInfinity();
     469
    464470            // Remember this entity
    465471            m_EntityData.insert(ent, entdata);
    466472            break;
     
    482488                {
    483489                    CFixedVector2D from(it->second.x, it->second.z);
    484490                    CFixedVector2D to(msgData.x, msgData.z);
    485                     m_Subdivision.Move(ent, from, to);
     491                    m_Subdivision.Move(ent, from, to, it->second.size);
    486492                    LosMove(it->second.owner, it->second.visionRange, from, to);
    487493                    i32 oldLosTile = PosToLosTilesHelper(it->second.x, it->second.z);
    488494                    i32 newLosTile = PosToLosTilesHelper(msgData.x, msgData.z);
     
    495501                else
    496502                {
    497503                    CFixedVector2D to(msgData.x, msgData.z);
    498                     m_Subdivision.Add(ent, to);
     504                    m_Subdivision.Add(ent, to, it->second.size);
    499505                    LosAdd(it->second.owner, it->second.visionRange, to);
    500506                    AddToTile(PosToLosTilesHelper(msgData.x, msgData.z), ent);
    501507                }
     
    509515                if (it->second.inWorld)
    510516                {
    511517                    CFixedVector2D from(it->second.x, it->second.z);
    512                     m_Subdivision.Remove(ent, from);
     518                    m_Subdivision.Remove(ent, from, it->second.size);
    513519                    LosRemove(it->second.owner, it->second.visionRange, from);
    514520                    RemoveFromTile(PosToLosTilesHelper(it->second.x, it->second.z), ent);
    515521                }
     
    559565
    560566            if (it->second.inWorld)
    561567            {
    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);
    563569                RemoveFromTile(PosToLosTilesHelper(it->second.x, it->second.z), ent);
    564570            }
    565571
     
    640646
    641647        std::vector<std::vector<u16> > oldPlayerCounts = m_LosPlayerCounts;
    642648        std::vector<u32> oldStateRevealed = m_LosStateRevealed;
    643         SpatialSubdivision oldSubdivision = m_Subdivision;
     649        FastSpatialSubdivision oldSubdivision = m_Subdivision;
    644650        std::vector<std::set<entity_id_t> > oldLosTiles = m_LosTiles;
    645651
    646652        ResetDerivedData(true);
     
    671677            debug_warn(L"inconsistent los tiles");
    672678    }
    673679
    674     SpatialSubdivision* GetSubdivision()
     680    FastSpatialSubdivision* GetSubdivision()
    675681    {
    676         return & m_Subdivision;
     682        return &m_Subdivision;
    677683    }
    678684
    679685    // Reinitialise subdivisions and LOS data, based on entity data
     
    744750
    745751    void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1)
    746752    {
    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);
    750754
    751755        for (EntityMap<EntityData>::const_iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it)
    752756        {
    753757            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);
    755759        }
    756760    }
    757761
     
    13471351            }
    13481352
    13491353            // 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)
    13541357            {
    1355                 for (int y = 0; y < height; ++y)
     1358                for (int y = 0; y < size; ++y)
    13561359                {
    13571360                    m_DebugOverlayLines.push_back(SOverlayLine());
    13581361                    m_DebugOverlayLines.back().m_Color = subdivColor;
  • source/simulation2/components/ICmpObstruction.h

     
    5252     */
    5353    virtual bool GetPreviousObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) = 0;
    5454
     55    virtual entity_pos_t GetSize() = 0;
     56
    5557    virtual entity_pos_t GetUnitRadius() = 0;
    5658
    5759    virtual bool IsControlPersistent() = 0;
  • source/simulation2/components/ICmpRangeManager.h

     
    7777     * Access the spatial subdivision kept by the range manager.
    7878     * @return pointer to spatial subdivision structure.
    7979     */
    80     virtual SpatialSubdivision* GetSubdivision() = 0;
     80    virtual FastSpatialSubdivision* GetSubdivision() = 0;
    8181
    8282    /**
    8383     * Set the bounds of the world.
  • source/simulation2/helpers/Selection.cpp

     
    2828#include "simulation2/components/ICmpSelectable.h"
    2929#include "simulation2/components/ICmpVisual.h"
    3030#include "simulation2/components/ICmpUnitRenderer.h"
    31 #include "simulation2/helpers/Spatial.h"
    3231#include "ps/CLogger.h"
    3332#include "ps/Profiler2.h"
    3433
  • source/simulation2/helpers/Spatial.h

     
    1 /* Copyright (C) 2010 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    3232 * Move/Remove are called with the same coordinates originally passed
    3333 * to Add (since this class doesn't remember which divisions an item
    3434 * occupies).
    35  *
    36  * (TODO: maybe an adaptive quadtree would be better than fixed sizes?)
    3735 */
    3836class SpatialSubdivision
    3937{
     
    6664    uint32_t m_DivisionsW;
    6765    uint32_t m_DivisionsH;
    6866
    69     friend struct SerializeSubDivisionGrid;
    7067    friend struct SerializeSpatialSubdivision;
    7168
    7269public:
     
    346343    }
    347344};
    348345
     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 */
     366class FastSpatialSubdivision
     367{
     368private:
     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
     401public:
     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
    349573#endif // INCLUDED_SPATIAL