Ticket #3588: geometry1.2.patch

File geometry1.2.patch, 11.1 KB (added by fsincos, 8 years ago)

Initial implementation of replacing CheckVisibility with a TestRaySquare-based version.

  • source/simulation2/components/CCmpPathfinder_Vertex.cpp

    diff --git a/source/simulation2/components/CCmpPathfinder_Vertex.cpp b/source/simulation2/components/CCmpPathfinder_Vertex.cpp
    index 5ab11b6..17d43a1 100644
    a b  
    3939#include "simulation2/components/ICmpObstructionManager.h"
    4040#include "simulation2/helpers/PriorityQueue.h"
    4141#include "simulation2/helpers/Render.h"
     42#include "simulation2/helpers/Geometry.h"
    4243
    4344/* Quadrant optimisation:
    4445 * (loosely based on GPG2 "Optimizing Points-of-Visibility Pathfinding")
    inline static bool CheckVisibilityTop(const CFixedVector2D& a, const CFixedVecto  
    289290    return true;
    290291}
    291292
     293// Unaligned squares are handled here.
     294inline static bool CheckVisibilitySquares(const CFixedVector2D& a, const CFixedVector2D& b, const std::vector<ICmpObstructionManager::ObstructionSquare> squares)
     295{
     296    for (size_t i = 0; i < squares.size(); ++i)
     297    {
     298        const CFixedVector2D center = CFixedVector2D(squares[i].x, squares[i].z);
     299        const CFixedVector2D halfSize = CFixedVector2D(squares[i].hw, squares[i].hh);
     300        if (Geometry::TestRaySquare(a - center, b - center, squares[i].u, squares[i].v, halfSize))
     301            return false;
     302    }
     303    return true;
     304}
     305
    292306typedef PriorityQueueHeap<u16, fixed, fixed> VertexPriorityQueue;
    293307
    294308/**
    static void AddTerrainEdges(std::vector<Edge>& edges, std::vector<Vertex>& verte  
    483497static void SplitAAEdges(const CFixedVector2D& a,
    484498        const std::vector<Edge>& edges,
    485499        const std::vector<Square>& squares,
    486         std::vector<Edge>& edgesUnaligned,
    487500        std::vector<EdgeAA>& edgesLeft, std::vector<EdgeAA>& edgesRight,
    488501        std::vector<EdgeAA>& edgesBottom, std::vector<EdgeAA>& edgesTop)
    489502{
    static void SplitAAEdges(const CFixedVector2D& a,  
    536549                edgesTop.emplace_back(EdgeAA{ edge.p0, edge.p1.X });
    537550            }
    538551        }
    539         else
    540             edgesUnaligned.push_back(edge);
    541552    }
    542553}
    543554
    void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,  
    648659    entity_pos_t pathfindClearance = clearance;
    649660   
    650661    // Convert each obstruction square into collision edges and search graph vertexes
     662    std::vector<ICmpObstructionManager::ObstructionSquare> squaresUnaligned;
    651663    for (size_t i = 0; i < squares.size(); ++i)
    652664    {
    653665        CFixedVector2D center(squares[i].x, squares[i].z);
    void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,  
    677689
    678690        // Add the edges:
    679691
    680         CFixedVector2D h0(squares[i].hw + pathfindClearance, squares[i].hh + pathfindClearance);
    681         CFixedVector2D h1(squares[i].hw + pathfindClearance, -(squares[i].hh + pathfindClearance));
     692        if(aa)
     693        {
     694            CFixedVector2D h0(squares[i].hw + pathfindClearance, squares[i].hh + pathfindClearance);
     695            CFixedVector2D h1(squares[i].hw + pathfindClearance, -(squares[i].hh + pathfindClearance));
    682696
    683         CFixedVector2D ev0(center.X - h0.Dot(u), center.Y + h0.Dot(v));
    684         CFixedVector2D ev1(center.X - h1.Dot(u), center.Y + h1.Dot(v));
    685         CFixedVector2D ev2(center.X + h0.Dot(u), center.Y - h0.Dot(v));
    686         CFixedVector2D ev3(center.X + h1.Dot(u), center.Y - h1.Dot(v));
    687         if (aa)
     697            CFixedVector2D ev1(center.X - h1.Dot(u), center.Y + h1.Dot(v));
     698            CFixedVector2D ev3(center.X + h1.Dot(u), center.Y - h1.Dot(v));
    688699            edgeSquares.emplace_back(Square{ ev1, ev3 });
    689         else
    690         {
    691             edges.emplace_back(Edge{ ev0, ev1 });
    692             edges.emplace_back(Edge{ ev1, ev2 });
    693             edges.emplace_back(Edge{ ev2, ev3 });
    694             edges.emplace_back(Edge{ ev3, ev0 });
    695700        }
     701        else
     702            squaresUnaligned.push_back(squares[i]);
    696703
    697704        // TODO: should clip out vertexes and edges that are outside the range,
    698705        // to reduce the search space
    void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,  
    771778            xz.push_back(s.p0.Y.ToFloat());
    772779            SimRender::ConstructLineOnGround(GetSimContext(), xz, m_DebugOverlayShortPathLines.back(), true);
    773780        }
     781        // Render the unaligned squares
     782        for (size_t i = 0; i < squaresUnaligned.size(); ++i)
     783        {
     784            m_DebugOverlayShortPathLines.push_back(SOverlayLine());
     785            m_DebugOverlayShortPathLines.back().m_Color = CColor(0, 1, 1, 1);
     786            std::vector<float> xz;
     787            ICmpObstructionManager::ObstructionSquare s = squaresUnaligned[i];
     788            CFixedVector2D h0(s.hw + clearance, s.hh + clearance);
     789            CFixedVector2D h1(s.hw + clearance, -(s.hh + clearance));
     790            CFixedVector2D u = s.u;
     791            CFixedVector2D v = s.v;
     792            CFixedVector2D center = CFixedVector2D(s.x, s.z);
     793
     794            CFixedVector2D ev0(center.X - h0.Dot(u), center.Y + h0.Dot(v));
     795            CFixedVector2D ev1(center.X - h1.Dot(u), center.Y + h1.Dot(v));
     796            CFixedVector2D ev2(center.X + h0.Dot(u), center.Y - h0.Dot(v));
     797            CFixedVector2D ev3(center.X + h1.Dot(u), center.Y - h1.Dot(v));
     798            xz.push_back(ev0.X.ToFloat());
     799            xz.push_back(ev0.Y.ToFloat());
     800            xz.push_back(ev1.X.ToFloat());
     801            xz.push_back(ev1.Y.ToFloat());
     802            xz.push_back(ev2.X.ToFloat());
     803            xz.push_back(ev2.Y.ToFloat());
     804            xz.push_back(ev3.X.ToFloat());
     805            xz.push_back(ev3.Y.ToFloat());
     806            xz.push_back(ev0.X.ToFloat());
     807            xz.push_back(ev0.Y.ToFloat());
     808            SimRender::ConstructLineOnGround(GetSimContext(), xz, m_DebugOverlayShortPathLines.back(), true);
     809        }
    774810    }
    775811
    776812    // Do an A* search over the vertex/visibility graph:
    void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,  
    810846        // since they're more likely to block the rays
    811847        std::sort(edgeSquares.begin(), edgeSquares.end(), SquareSort(vertexes[curr.id].p));
    812848
    813         std::vector<Edge> edgesUnaligned;
    814849        std::vector<EdgeAA> edgesLeft;
    815850        std::vector<EdgeAA> edgesRight;
    816851        std::vector<EdgeAA> edgesBottom;
    817852        std::vector<EdgeAA> edgesTop;
    818         SplitAAEdges(vertexes[curr.id].p, edges, edgeSquares, edgesUnaligned, edgesLeft, edgesRight, edgesBottom, edgesTop);
     853        SplitAAEdges(vertexes[curr.id].p, edges, edgeSquares, edgesLeft, edgesRight, edgesBottom, edgesTop);
    819854
    820855        // Check the lines to every other vertex
    821856        for (size_t n = 0; n < vertexes.size(); ++n)
    void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,  
    871906                CheckVisibilityRight(vertexes[curr.id].p, npos, edgesRight) &&
    872907                CheckVisibilityBottom(vertexes[curr.id].p, npos, edgesBottom) &&
    873908                CheckVisibilityTop(vertexes[curr.id].p, npos, edgesTop) &&
    874                 CheckVisibility(vertexes[curr.id].p, npos, edgesUnaligned);
     909                CheckVisibilitySquares(vertexes[curr.id].p, npos, squaresUnaligned);
    875910
    876911            /*
    877912            // Render the edges that we examine
  • source/simulation2/helpers/Geometry.cpp

    diff --git a/source/simulation2/helpers/Geometry.cpp b/source/simulation2/helpers/Geometry.cpp
    index 4173a7a..c6e1b6a 100644
    a b  
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 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
    bool Geometry::TestRaySquare(const CFixedVector2D& a, const CFixedVector2D& b, c  
    196196    /*
    197197     * We only consider collisions to be when the ray goes from outside to inside the shape (and possibly out again).
    198198     * Various cases to consider:
    199      *   'a' inside, 'b' inside -> no collision
    200      *   'a' inside, 'b' outside -> no collision
    201      *   'a' outside, 'b' inside -> collision
    202      *   'a' outside, 'b' outside -> depends; use separating axis theorem:
     199     *   'a' inside -> no collision
     200     *   use separating axis theorem:
    203201     *     if the ray's bounding box is outside the square -> no collision
    204202     *     if the whole square is on the same side of the ray -> no collision
    205203     *     otherwise -> collision
    bool Geometry::TestRaySquare(const CFixedVector2D& a, const CFixedVector2D& b, c  
    218216    fixed bu = b.Dot(u);
    219217    fixed bv = b.Dot(v);
    220218
    221     if (-hw <= bu && bu <= hw && -hh <= bv && bv <= hh) // TODO: isn't this subsumed by the next checks?
    222         return true; // a is outside, b is inside
    223 
    224219    if ((au < -hw && bu < -hw) || (au > hw && bu > hw) || (av < -hh && bv < -hh) || (av > hh && bv > hh))
    225220        return false; // ab is entirely above/below/side the square
    226221
    227     CFixedVector2D abp = (b - a).Perpendicular();
    228     fixed s0 = abp.Dot((u.Multiply(hw) + v.Multiply(hh)) - a);
    229     fixed s1 = abp.Dot((u.Multiply(hw) - v.Multiply(hh)) - a);
    230     fixed s2 = abp.Dot((-u.Multiply(hw) - v.Multiply(hh)) - a);
    231     fixed s3 = abp.Dot((-u.Multiply(hw) + v.Multiply(hh)) - a);
    232     if (s0.IsZero() || s1.IsZero() || s2.IsZero() || s3.IsZero())
    233         return true; // ray intersects the corner
    234 
    235     bool sign = (s0 < fixed::Zero());
    236     if ((s1 < fixed::Zero()) != sign || (s2 < fixed::Zero()) != sign || (s3 < fixed::Zero()) != sign)
    237         return true; // ray cuts through the square
    238 
    239     return false;
     222    // Check if all points are on one side of a->b.
     223    // This can be simplified by using the following equivalence:
     224    // (x +- y +- z) are all > 0 or all < 0 <=> |x| > |y| + |z|
     225    // and the linearity of scalar products.
     226    // Furthermore, we already computed
     227    // u.Dot((b-a).Perpendicular()) = (u.Perpendicular()).Dot(b-a) = av - bv, v.Dot(...) similarly
     228    // Also, a.Dot(a.Perpendicular()) = 0.
     229    fixed s0 = a.Dot(b.Perpendicular()).Absolute();
     230    fixed s1 = (av - bv).Absolute().Multiply(hw) + (bu - au).Absolute().Multiply(hh);
     231    return (s0 <= s1);
    240232}
    241233
    242234// Exactly like TestRaySquare with u=(1,0), v=(0,1)
    bool Geometry::TestRayAASquare(const CFixedVector2D& a, const CFixedVector2D& b,  
    248240    if (-hw <= a.X && a.X <= hw && -hh <= a.Y && a.Y <= hh)
    249241        return false; // a is inside
    250242
    251     if (-hw <= b.X && b.X <= hw && -hh <= b.Y && b.Y <= hh) // TODO: isn't this subsumed by the next checks?
    252         return true; // a is outside, b is inside
    253 
    254243    if ((a.X < -hw && b.X < -hw) || (a.X > hw && b.X > hw) || (a.Y < -hh && b.Y < -hh) || (a.Y > hh && b.Y > hh))
    255244        return false; // ab is entirely above/below/side the square
    256245
    257246    CFixedVector2D abp = (b - a).Perpendicular();
    258     fixed s0 = abp.Dot(CFixedVector2D(hw, hh) - a);
    259     fixed s1 = abp.Dot(CFixedVector2D(hw, -hh) - a);
    260     fixed s2 = abp.Dot(CFixedVector2D(-hw, -hh) - a);
    261     fixed s3 = abp.Dot(CFixedVector2D(-hw, hh) - a);
    262     if (s0.IsZero() || s1.IsZero() || s2.IsZero() || s3.IsZero())
    263         return true; // ray intersects the corner
    264 
    265     bool sign = (s0 < fixed::Zero());
    266     if ((s1 < fixed::Zero()) != sign || (s2 < fixed::Zero()) != sign || (s3 < fixed::Zero()) != sign)
    267         return true; // ray cuts through the square
    268 
    269     return false;
     247    fixed s0 = a.Dot(abp).Absolute();
     248    fixed s1 = abp.X.Absolute().Multiply(hw) + abp.Y.Absolute().Multiply(hh);
     249    return (s0 <= s1);
    270250}
    271251
    272252/**
    static bool SquareSAT(const CFixedVector2D& a, const CFixedVector2D& axis, const  
    279259    fixed hh = halfSize.Y;
    280260
    281261    CFixedVector2D p = axis.Perpendicular();
    282     if (p.Dot((u.Multiply(hw) + v.Multiply(hh)) - a) <= fixed::Zero())
    283         return true;
    284     if (p.Dot((u.Multiply(hw) - v.Multiply(hh)) - a) <= fixed::Zero())
     262    // We only check the "counterclockwisest" point.
     263    // But first we check if the center is on the wrong side.
     264    fixed s0 = p.Dot(a);
     265    if (s0 >= fixed::Zero())
    285266        return true;
    286     if (p.Dot((-u.Multiply(hw) - v.Multiply(hh)) - a) <= fixed::Zero())
    287         return true;
    288     if (p.Dot((-u.Multiply(hw) + v.Multiply(hh)) - a) <= fixed::Zero())
    289         return true;
    290 
    291     return false;
     267    s0 += u.Dot(p).Absolute().Multiply(hw) + v.Dot(p).Absolute().Multiply(hh);
     268    return (s0 >= fixed::Zero());
    292269}
    293270
    294271bool Geometry::TestSquareSquare(