Ticket #930: ticket930-poc.patch

File ticket930-poc.patch, 10.0 KB (added by Philip Taylor, 12 years ago)

incomplete proof-of-concept for alternative design

  • source/simulation2/components/CCmpObstructionManager.cpp

     
    250250        UnitShape shape = { ent, x, z, r, flags, group };
    251251        u32 id = m_UnitShapeNext++;
    252252        m_UnitShapes[id] = shape;
    253         MakeDirtyUnit(flags);
     253        MakeDirtyUnit(shape);
    254254
    255255        m_UnitSubdivision.Add(id, CFixedVector2D(x - r, z - r), CFixedVector2D(x + r, z + r));
    256256
     
    267267        StaticShape shape = { ent, x, z, u, v, w/2, h/2, flags };
    268268        u32 id = m_StaticShapeNext++;
    269269        m_StaticShapes[id] = shape;
    270         MakeDirtyStatic(flags);
     270        MakeDirtyStatic(shape);
    271271
    272272        CFixedVector2D center(x, z);
    273273        CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(w/2, h/2));
     
    312312            shape.x = x;
    313313            shape.z = z;
    314314
    315             MakeDirtyUnit(shape.flags);
     315            MakeDirtyUnit(shape);
    316316        }
    317317        else
    318318        {
     
    336336            shape.u = u;
    337337            shape.v = v;
    338338
    339             MakeDirtyStatic(shape.flags);
     339            MakeDirtyStatic(shape);
    340340        }
    341341    }
    342342
     
    378378                CFixedVector2D(shape.x - shape.r, shape.z - shape.r),
    379379                CFixedVector2D(shape.x + shape.r, shape.z + shape.r));
    380380
    381             MakeDirtyUnit(shape.flags);
     381            MakeDirtyUnit(shape);
    382382            m_UnitShapes.erase(TAG_TO_INDEX(tag));
    383383        }
    384384        else
     
    389389            CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
    390390            m_StaticSubdivision.Remove(TAG_TO_INDEX(tag), center - bbHalfSize, center + bbHalfSize);
    391391
    392             MakeDirtyStatic(shape.flags);
     392            MakeDirtyStatic(shape);
    393393            m_StaticShapes.erase(TAG_TO_INDEX(tag));
    394394        }
    395395    }
     
    420420
    421421    virtual bool Rasterise(Grid<u8>& grid);
    422422    virtual void GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares);
     423    void GetShapesInRange(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::map<u32, UnitShape>& outUnitShapes, std::map<u32, StaticShape>& outStaticShapes);
    423424    virtual bool FindMostImportantObstruction(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, ObstructionSquare& square);
    424425
    425426    virtual void SetPassabilityCircular(bool enabled)
     
    444445    // if a grid has a lower DirtyID then it needs to be updated.
    445446
    446447    size_t m_DirtyID;
     448    entity_pos_t m_DirtyX0;
     449    entity_pos_t m_DirtyZ0;
     450    entity_pos_t m_DirtyX1;
     451    entity_pos_t m_DirtyZ1;
     452    // XXX: this probably breaks if used with multiple independently-dirtyable grids
    447453
    448454    /**
    449455     * Mark all previous Rasterise()d grids as dirty, and the debug display.
     
    453459    {
    454460        ++m_DirtyID;
    455461        m_DebugOverlayDirty = true;
     462        m_DirtyX0 = m_WorldX0;
     463        m_DirtyZ0 = m_WorldZ0;
     464        m_DirtyX1 = m_WorldX1;
     465        m_DirtyZ1 = m_WorldZ1;
    456466    }
    457467
    458468    /**
     
    468478     * Mark all previous Rasterise()d grids as dirty, if they depend on this shape.
    469479     * Call this when a static shape has changed.
    470480     */
    471     void MakeDirtyStatic(flags_t flags)
     481    void MakeDirtyStatic(const StaticShape& shape)
    472482    {
    473         if (flags & (FLAG_BLOCK_PATHFINDING|FLAG_BLOCK_FOUNDATION))
     483        if (shape.flags & (FLAG_BLOCK_PATHFINDING|FLAG_BLOCK_FOUNDATION))
     484        {
    474485            ++m_DirtyID;
     486            CFixedVector2D halfSize(shape.hw, shape.hh);
     487            CFixedVector2D halfBound = Geometry::GetHalfBoundingBox(shape.u, shape.v, halfSize);
     488            m_DirtyX0 = std::min(m_DirtyX0, shape.x - halfBound.X);
     489            m_DirtyZ0 = std::min(m_DirtyZ0, shape.z - halfBound.Y);
     490            m_DirtyX1 = std::max(m_DirtyX1, shape.x + halfBound.X);
     491            m_DirtyZ1 = std::max(m_DirtyZ1, shape.z + halfBound.Y);
     492        }
    475493
    476494        m_DebugOverlayDirty = true;
    477495    }
     
    480498     * Mark all previous Rasterise()d grids as dirty, if they depend on this shape.
    481499     * Call this when a unit shape has changed.
    482500     */
    483     void MakeDirtyUnit(flags_t flags)
     501    void MakeDirtyUnit(const UnitShape& shape)
    484502    {
    485         if (flags & (FLAG_BLOCK_PATHFINDING|FLAG_BLOCK_FOUNDATION))
     503        if (shape.flags & (FLAG_BLOCK_PATHFINDING|FLAG_BLOCK_FOUNDATION))
     504        {
    486505            ++m_DirtyID;
     506            entity_pos_t r = shape.r;
     507            m_DirtyX0 = std::min(m_DirtyX0, shape.x - r);
     508            m_DirtyZ0 = std::min(m_DirtyZ0, shape.z - r);
     509            m_DirtyX1 = std::max(m_DirtyX1, shape.x + r);
     510            m_DirtyZ1 = std::max(m_DirtyZ1, shape.z + r);
     511        }
    487512
    488513        m_DebugOverlayDirty = true;
    489514    }
     
    708733    if (!IsDirty(grid))
    709734        return false;
    710735
    711     PROFILE("Rasterise");
     736    PROFILE3("Rasterise");
     737    TIMER(L"Rasterise");
    712738
    713739    grid.m_DirtyID = m_DirtyID;
    714740
     
    716742    // What we should perhaps do is have some kind of quadtree storing Shapes so it's
    717743    // quick to invalidate and update small numbers of tiles
    718744
    719     grid.reset();
     745//  grid.reset();
    720746
    721747    // For tile-based pathfinding:
    722748    // Since we only count tiles whose centers are inside the square,
     
    733759    // so we need to expand by at least 1/sqrt(2) of a tile
    734760    entity_pos_t expandFoundation = (entity_pos_t::FromInt(CELL_SIZE) * 3) / 4;
    735761
    736     for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it)
     762
     763//  debug_printf(L"# Rasterising region %f %f %f %f\n", m_DirtyX0.ToFloat(), m_DirtyZ0.ToFloat(), m_DirtyX1.ToFloat(), m_DirtyZ1.ToFloat());
     764
     765    // XXX: if it's a large region (e.g. the whole map changed), we should just
     766    // reset the entire grid and redraw every shape, instead of doing GetShapesInRange etc
     767
     768    // XXX: we should do a fancier dirty-rectangles, so if there were two small changes
     769    // on opposite sides of the map we won't have to re-rasterise all the tiles in between
     770
     771    u16 dirtyI0, dirtyJ0, dirtyI1, dirtyJ1;
     772    entity_pos_t dirtyExpand = std::max(expandPathfinding, expandFoundation) * 2; // expand by hopefully enough to avoid missing some edges (XXX: this might not be right)
     773    NearestTile(m_DirtyX0 - dirtyExpand, m_DirtyZ0 - dirtyExpand, dirtyI0, dirtyJ0, grid.m_W, grid.m_H);
     774    NearestTile(m_DirtyX1 + dirtyExpand, m_DirtyZ1 + dirtyExpand, dirtyI1, dirtyJ1, grid.m_W, grid.m_H);
     775
     776    grid.reset(dirtyI0, dirtyJ0, dirtyI1, dirtyJ1);
     777
     778    std::map<u32, StaticShape> staticShapes;
     779    std::map<u32, UnitShape> unitShapes;
     780    GetShapesInRange(m_DirtyX0 - dirtyExpand*2, m_DirtyZ0 - dirtyExpand*2, m_DirtyX1 + dirtyExpand*2, m_DirtyZ1 + dirtyExpand*2, unitShapes, staticShapes);
     781    // XXX: should make GetShapesInRange only return shapes with the appropriate flags
     782    // XXX: to minimise memory allocation, should return shape lists as std::vector<u32>
     783    // instead of creating a std::map and copying the actual shape structs.
     784    // Or we could just use m_StaticSubdivision.GetInRange directly here.
     785
     786    // Reset dirty region to empty
     787    m_DirtyX0 = m_WorldX1;
     788    m_DirtyZ0 = m_WorldZ1;
     789    m_DirtyX1 = m_WorldX0;
     790    m_DirtyZ1 = m_WorldZ0;
     791
     792    for (std::map<u32, StaticShape>::iterator it = staticShapes.begin(); it != staticShapes.end(); ++it)
    737793    {
    738794        CFixedVector2D center(it->second.x, it->second.z);
    739795
     
    778834        }
    779835    }
    780836
    781     for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it)
     837    for (std::map<u32, UnitShape>::iterator it = unitShapes.begin(); it != unitShapes.end(); ++it)
    782838    {
    783839        CFixedVector2D center(it->second.x, it->second.z);
    784840
     
    816872
    817873    if (m_PassabilityCircular)
    818874    {
    819         for (u16 j = 0; j < grid.m_H; ++j)
     875        for (u16 j = dirtyJ0; j < dirtyJ1; ++j)
    820876        {
    821             for (u16 i = 0; i < grid.m_W; ++i)
     877            for (u16 i = dirtyI0; i < dirtyI1; ++i)
    822878            {
    823879                // Based on CCmpRangeManager::LosIsOffWorld
    824880                // but tweaked since it's tile-based instead.
     
    840896        NearestTile(m_WorldX0, m_WorldZ0, i0, j0, grid.m_W, grid.m_H);
    841897        NearestTile(m_WorldX1, m_WorldZ1, i1, j1, grid.m_W, grid.m_H);
    842898
     899        // XXX: use dirty region
    843900        for (u16 j = 0; j < grid.m_H; ++j)
    844901            for (u16 i = 0; i < i0+edgeSize; ++i)
    845902                grid.set(i, j, edgeFlags);
     
    906963    }
    907964}
    908965
     966void CCmpObstructionManager::GetShapesInRange(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::map<u32, UnitShape>& outUnitShapes, std::map<u32, StaticShape>& outStaticShapes)
     967{
     968    ENSURE(x0 <= x1 && z0 <= z1);
     969
     970    std::vector<u32> unitShapes = m_UnitSubdivision.GetInRange(CFixedVector2D(x0, z0), CFixedVector2D(x1, z1));
     971    for (size_t i = 0; i < unitShapes.size(); ++i)
     972    {
     973        std::map<u32, UnitShape>::iterator it = m_UnitShapes.find(unitShapes[i]);
     974        ENSURE(it != m_UnitShapes.end());
     975
     976        entity_pos_t r = it->second.r;
     977
     978        // Skip this object if it's completely outside the requested range
     979        if (it->second.x + r < x0 || it->second.x - r > x1 || it->second.z + r < z0 || it->second.z - r > z1)
     980            continue;
     981
     982        outUnitShapes.insert(*it);
     983    }
     984
     985    std::vector<u32> staticShapes = m_StaticSubdivision.GetInRange(CFixedVector2D(x0, z0), CFixedVector2D(x1, z1));
     986    for (size_t i = 0; i < staticShapes.size(); ++i)
     987    {
     988        std::map<u32, StaticShape>::iterator it = m_StaticShapes.find(staticShapes[i]);
     989        ENSURE(it != m_StaticShapes.end());
     990
     991        entity_pos_t r = it->second.hw + it->second.hh; // overestimate the max dist of an edge from the center
     992
     993        // Skip this object if its overestimated bounding box is completely outside the requested range
     994        if (it->second.x + r < x0 || it->second.x - r > x1 || it->second.z + r < z0 || it->second.z - r > z1)
     995            continue;
     996
     997        outStaticShapes.insert(*it);
     998    }
     999}
     1000
    9091001bool CCmpObstructionManager::FindMostImportantObstruction(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, ObstructionSquare& square)
    9101002{
    9111003    std::vector<ObstructionSquare> squares;
  • source/simulation2/helpers/Grid.h

     
    8080            memset(m_Data, 0, m_W*m_H*sizeof(T));
    8181    }
    8282
     83    // Reset all tiles i0 <= i <= i1, j0 <= j <= j1
     84    void reset(int i0, int j0, int i1, int j1)
     85    {
     86        ENSURE(0 <= i0 && i0 < m_W && 0 <= i1 && i1 < m_W && 0 <= j0 && j0 < m_H && 0 <= j1 && j1 < m_H);
     87        for (int j = j0; j <= j1; ++j)
     88            for (int i = i0; i <= i1; ++i)
     89                m_Data[j*m_W + i] = 0;
     90    }
     91
    8392    void set(int i, int j, const T& value)
    8493    {
    8594#if GRID_BOUNDS_DEBUG