Пример #1
0
     /// returns the normal vector
 StaticVector<ctype,3> normal(int tri) const {
     const StaticVector<ctype,3> a = vertices(triangles(tri).vertices[1]) - vertices(triangles(tri).vertices[0]);
     const StaticVector<ctype,3> b = vertices(triangles(tri).vertices[2]) - vertices(triangles(tri).vertices[0]);
     StaticVector<ctype,3> n = a.cross(b);
     n.normalize();
     return n;
 }
Пример #2
0
TEST(StaticVectorTest, push_get) {
	StaticVector<int> sVect;

	auto ref1 = sVect.push_back(23);
	auto ref2 = sVect.push_back(24);
	auto ref3 = sVect.push_back(25);

	ASSERT_EQ(23, ref1.get());
	ASSERT_EQ(24, ref2.get());
	ASSERT_EQ(25, ref3.get());

	ASSERT_EQ(size_t(3), sVect.activeSize());

	sVect.remove(ref2);

	ASSERT_EQ(size_t(2), sVect.activeSize());

	auto ref4 = sVect.push_back(45);
	ASSERT_EQ(45, ref4.get());
	ASSERT_EQ(size_t(3), sVect.activeSize());

	auto findInsert = [] ( int const& it ) -> bool {return it > 30;};
	auto ref5 = sVect.push(55, findInsert);
	ASSERT_EQ(55, ref5.get());

	// we expect it at positon 2
	ASSERT_EQ(55, sVect.get(2));
	ASSERT_EQ(45, sVect.get(3));

}
Пример #3
0
void ContactMapping<2,ctype>::computeDiscreteDomainDirections(const DirectionFunction<2,ctype>* direction,
                                                         std::vector<StaticVector<ctype,2> >& normals)
{
    size_t nElements = psurface_.domainSegments.size();

    if (!direction) {

        // //////////////////////////////////////////////////////////
        //  The contact directions are given as the vertex normals
        // //////////////////////////////////////////////////////////
        normals.resize(psurface_.domainVertices.size());
        for (size_t i=0; i<psurface_.domainVertices.size(); i++)
            normals[i] = StaticVector<ctype,2>(0);

        for (size_t i=0; i<nElements; i++) {

            // Compute segment normal
            int v0 = psurface_.domainSegments[i].points[0];
            int v1 = psurface_.domainSegments[i].points[1];

            StaticVector<ctype,2> segment;
            segment[0] = psurface_.domainVertices[v1][0] - psurface_.domainVertices[v0][0];
            segment[1] = psurface_.domainVertices[v1][1] - psurface_.domainVertices[v0][1];

            StaticVector<ctype,2> segmentNormal;
            segmentNormal[0] =  segment[1];
            segmentNormal[1] = -segment[0];

            segmentNormal /= segmentNormal.length();

            normals[psurface_.domainSegments[i].points[0]] += segmentNormal;
            normals[psurface_.domainSegments[i].points[1]] += segmentNormal;

        }

        for (size_t i=0; i<normals.size(); i++)
            normals[i] /= normals[i].length();

    } else {

        // Sample the provided analytical contact direction field
        normals.resize(psurface_.domainVertices.size());
        for (size_t i=0; i<psurface_.domainVertices.size(); i++) {

            if (dynamic_cast<const AnalyticDirectionFunction<2,ctype>*>(direction))
                normals[i] = (*dynamic_cast<const AnalyticDirectionFunction<2,ctype>*>(direction))(psurface_.domainVertices[i]);
            else if (dynamic_cast<const DiscreteDirectionFunction<2,ctype>*>(direction))
                normals[i] = (*dynamic_cast<const DiscreteDirectionFunction<2,ctype>*>(direction))(i);
            else
                throw(std::runtime_error("Domain direction function not properly set!"));

        }

    }

}
Пример #4
0
void ContactMapping<2,ctype>::computeDiscreteTargetDirections(const std::vector<std::array<int,2> >& elements,
                                                              const DirectionFunction<2,ctype>* direction,
                                                              std::vector<StaticVector<ctype,2> >& normals)
{
    // Build the normal field
    normals.resize(psurface_.targetVertices.size());
    for (size_t i=0; i<psurface_.targetVertices.size(); i++)
        normals[i] = StaticVector<ctype,2>(0);

    if (!direction) {

        for (int i=0; i<elements.size(); i++) {

            // Compute segment normal
            int v0 = elements[i][0];
            int v1 = elements[i][1];
            StaticVector<ctype,2> segment;
            segment[0] = psurface_.targetVertices[v1][0] - psurface_.targetVertices[v0][0];
            segment[1] = psurface_.targetVertices[v1][1] - psurface_.targetVertices[v0][1];

            StaticVector<ctype,2> segmentNormal;
            segmentNormal[0] =  segment[1];
            segmentNormal[1] = -segment[0];

            segmentNormal /= segmentNormal.length();

            normals[elements[i][0]] += segmentNormal;
            normals[elements[i][1]] += segmentNormal;

        }

        for (size_t i=0; i<normals.size(); i++)
            normals[i] /= normals[i].length();

    } else {

        // Sample the provided analytical contact direction field
        normals.resize(psurface_.targetVertices.size());
        for (size_t i=0; i<psurface_.targetVertices.size(); i++) {

            if (dynamic_cast<const AnalyticDirectionFunction<2,ctype>*>(direction))
                normals[i] = (*dynamic_cast<const AnalyticDirectionFunction<2,ctype>*>(direction))(psurface_.targetVertices[i]);
            else if (dynamic_cast<const DiscreteDirectionFunction<2,ctype>*>(direction))
                normals[i] = (*dynamic_cast<const DiscreteDirectionFunction<2,ctype>*>(direction))(i);
            else
                throw(std::runtime_error("Target direction function not properly set!"));

        }

    }

}
Пример #5
0
typename SurfaceBase<VertexType,EdgeType,TriangleType>::ctype SurfaceBase<VertexType,EdgeType,TriangleType>::minInteriorAngle(int n) const
{
    ctype minAngle = 2*M_PI;
    const std::array<int, 3>& p = triangles(n).vertices;

    for (int i=0; i<3; i++){
        StaticVector<ctype,3> a = vertices(p[(i+1)%3]) - vertices(p[i]);
        StaticVector<ctype,3> b = vertices(p[(i+2)%3]) - vertices(p[i]);

        ctype angle = acosf(a.dot(b) / (a.length() * b.length()));
        if (angle<minAngle)
            minAngle = angle;
    }

    return minAngle;
}
Пример #6
0
void AI_Shutdown( void )
{
    hubAreas.clear();

    AI_UnloadLevel();

    AiShutdownHooksHolder::Instance()->InvokeHooks();
}
Пример #7
0
    void completeGame(GoState& s, StaticVector<GoMove, MAX_GAME_LENGTH>& move_seq, RNG &rng) {
        bool game_over = false;

        unsigned int moves = 0;

        while (!game_over) {
            moves++;
            GoMove move = selectMove(s, rng);
            assert(s.isValidMove(move));

            if (move.isPass() && s.getPreviousMoveWasPass()) {
                game_over = true;
            }

            s.makeMove(move);
            move_seq.push_back(move);
        }
    }
Пример #8
0
void ContactMapping<2,ctype>::build(const std::vector<std::array<ctype,2> >& coords1,  ///< The vertex coordinates of the first surface
               const std::vector<std::array<int,2> >& tri1,       ///< The triangles of the first surface
               const std::vector<std::array<ctype,2> >& coords2,  ///< The vertices of the second surface
               const std::vector<std::array<int,2> >& tri2,
                                    const DirectionFunction<2,ctype>* domainDirection,
                                    const DirectionFunction<2,ctype>* targetDirection
               )
{
    int numVertices1 = coords1.size();
    int numVertices2 = coords2.size();
    int nTri1  = tri1.size();
    int nTri2  = tri2.size();

#if 0
    printf("----- 1 -----\n");

    for (int i=0; i<nTri1; i++)
        printf("-- %d %d\n", tri1[2*i], tri1[2*i+1]);

    printf("----- 2 -----\n");

    for (int i=0; i<nTri2; i++)
        printf("-- %d %d\n", tri2[2*i], tri2[2*i+1]);
#endif

    // //////////////////////////////////////////////////
    //   Build domain surface and its normal field
    // //////////////////////////////////////////////////

    psurface_.domainVertices.resize(numVertices1);
    for (int i=0; i<numVertices1; i++)
        for (int j=0; j<2; j++)
            psurface_.domainVertices[i][j] = coords1[i][j];

    // Build the domain segments
    psurface_.domainSegments.clear();  // may contain old stuff from previous runs
    psurface_.domainSegments.resize(nTri1);
    for (int i=0; i<nTri1; i++) {
        psurface_.domainSegments[i].points[0] = tri1[i][0];
        psurface_.domainSegments[i].points[1] = tri1[i][1];
    }

    // ///////////////////////////////
    //   Build the domain normal field
    // ///////////////////////////////
    std::vector<StaticVector<ctype, 2> > domainNormals;
    std::vector<StaticVector<ctype, 2> > targetNormals;

    computeDiscreteDomainDirections(domainDirection, domainNormals);

    // //////////////////////////////////////////////////
    //   Build range surface and its normal field
    // //////////////////////////////////////////////////

    // first mark the vertices that are actually used
    psurface_.targetVertices.resize(numVertices2);
    for (int i=0; i<numVertices2; i++)
        for (int j=0; j<2; j++)
            psurface_.targetVertices[i][j] = coords2[i][j];

    // /////////////////////////////////////////////////////
    //   Build the segments-per-vertex arrays
    // /////////////////////////////////////////////////////

    std::vector<std::array<int, 2> > segPerVertex1(psurface_.domainVertices.size());
    for (size_t i=0; i<segPerVertex1.size(); i++)
        segPerVertex1[i][0] = segPerVertex1[i][1] = -1;

    for (int i=0; i<nTri1; i++) {

        //printf("segment %d:  %d %d  --  %d %d\n", i, tri2[2*i], tri2[2*i+1], used2[tri2[2*i]],used2[tri2[2*i+1]]);
        for (int j=0; j<2; j++) {

            int p = tri1[i][j];
            if (segPerVertex1[p][0]==-1)
                segPerVertex1[p][0] = i;
            else
                segPerVertex1[p][1] = i;

        }

    }

    // use this to construct the neighbor relationships between segments
    for (size_t i=0; i<psurface_.domainSegments.size(); i++) {

        int vertex0 = psurface_.domainSegments[i].points[0];
        int other0 = (segPerVertex1[vertex0][0] == i) ? segPerVertex1[vertex0][1] : segPerVertex1[vertex0][0];
        psurface_.domainSegments[i].neighbor[0] = other0;

        int vertex1 = psurface_.domainSegments[i].points[1];
        int other1 = (segPerVertex1[vertex1][0] == i) ? segPerVertex1[vertex1][1] : segPerVertex1[vertex1][0];
        psurface_.domainSegments[i].neighbor[1] = other1;

        //printf("Segment %d neighbors:  %d  %d\n", i, other0, other1);
    }



    // Build the segments-per-vertex arrays for the target vertices
    std::vector<std::array<int, 2> > segPerVertex2(psurface_.targetVertices.size());
    for (size_t i=0; i<segPerVertex2.size(); i++)
        segPerVertex2[i][0] = segPerVertex2[i][1] = -1;

    for (int i=0; i<nTri2; i++) {

        //printf("segment %d:  %d %d  --  %d %d\n", i, tri2[2*i], tri2[2*i+1], tri2[2*i], tri2[2*i+1]);
        for (int j=0; j<2; j++) {

            int p = tri2[i][j];
            if (segPerVertex2[p][0]==-1)
                segPerVertex2[p][0] = i;
            else
                segPerVertex2[p][1] = i;

        }

    }

    computeDiscreteTargetDirections(tri2, targetDirection, targetNormals);

    // ///////////////////////////////////////////////////////////////////////
    //   Project the vertices of the target surface onto the domain surface
    // ///////////////////////////////////////////////////////////////////////
    const ctype eps = 1e-10;

    for (size_t i=0; i<psurface_.targetVertices.size(); i++) {

        ctype bestLocalPos = std::numeric_limits<ctype>::max();  // init to something
        int bestSegment = -1;
        ctype bestDist = std::numeric_limits<ctype>::max();

        for (int j=0; j<psurface_.domainSegments.size(); j++) {

            const StaticVector<ctype,2>& p0 = psurface_.domainVertices[psurface_.domainSegments[j].points[0]];
            const StaticVector<ctype,2>& p1 = psurface_.domainVertices[psurface_.domainSegments[j].points[1]];

            const StaticVector<ctype,2>& n0 = domainNormals[psurface_.domainSegments[j].points[0]];
            const StaticVector<ctype,2>& n1 = domainNormals[psurface_.domainSegments[j].points[1]];

            ctype local; // the unknown...

            if (NormalProjector<ctype>::computeInverseNormalProjection(p0, p1, n0, n1,
                                                                        psurface_.targetVertices[i], local)) {

                // We want that the line from the domain surface to its projection
                // approaches the target surface from the front side, i.e., it should
                // not pass through the body represented by the target surface.
                // We do a simplified test by comparing the connecting segment
                // with the normal at the target surface and the normal at the
                // domain surface
                /** \todo Rewrite this once we have expression templates */
                StaticVector<ctype,2> base;
                StaticVector<ctype, 2> baseNormal;
                StaticVector<ctype, 2> segment;

                for (int k=0; k<2; k++) {
                    base[k]       = (1-local)*p0[k] + local*p1[k];
                    baseNormal[k] = (1-local)*n0[k] + local*n1[k];
                    segment[k]    = psurface_.targetVertices[i][k] - base[k];
                }

                ctype distance = segment.length2();

                if (segment.dot(targetNormals[i]) > -0.0001
                    && segment.dot(baseNormal) > -0.0001
                    && distance > 1e-8) {
                    //printf("aborting %g %g %g\n", segment * targetNormals[i], segment * baseNormal, distance);
                    continue;
                }

                // There may be several inverse orthogonal projections.
                // We want the shortest one.

                if (distance < bestDist) {

                    bestDist = distance;
                    bestLocalPos = local;
                    bestSegment  = j;

                }

            }

        }

        // /////////////////////////////////////////////
        //   We have found a valid projection
        // /////////////////////////////////////////////
        if (bestSegment != -1) {

            typename PSurface<1,ctype>::DomainSegment& bS = psurface_.domainSegments[bestSegment];

            if (bestLocalPos < eps) {

                // Insert as new first element
                bS.nodes.insert(bS.nodes.begin(), typename PSurface<1,ctype>::Node(0, 1, true, true, segPerVertex2[i][0], segPerVertex2[i][1]));

                // Look for left neighbor segment
                if (psurface_.domainSegments[bestSegment].neighbor[0] != -1) {

                    psurface_.domainSegments[psurface_.domainSegments[bestSegment].neighbor[0]].nodes.push_back( typename PSurface<1,ctype>::Node(1, 0, true, true,
                                                                                                  segPerVertex2[i][0], segPerVertex2[i][1]) );

                }

            } else if (bestLocalPos > 1-eps) {

                typename PSurface<1,ctype>::Node newNode(1, 0, true, true, segPerVertex2[i][0], segPerVertex2[i][1]);
                bS.nodes.push_back(newNode);

                // Look for right neighbor segment
                if (psurface_.domainSegments[bestSegment].neighbor[1] != -1) {

                    typename PSurface<1,ctype>::DomainSegment& rightNeighborSegment = psurface_.domainSegments[psurface_.domainSegments[bestSegment].neighbor[1]];
                    rightNeighborSegment.nodes.insert(rightNeighborSegment.nodes.begin(),
                                                      typename PSurface<1,ctype>::Node(0, 1, true, true, segPerVertex2[i][0], segPerVertex2[i][1]));

                }
            } else {
                int nNodes = bS.nodes.size();

                bS.nodes.resize(nNodes+1);
                int j=nNodes-1;
                for (; j>=0; j--) {
                    if (bS.nodes[j].domainLocalPosition > bestLocalPos)
                        bS.nodes[j+1] = bS.nodes[j];
                    else
                        break;
                }

                bS.nodes[j+1] = typename PSurface<1,ctype>::Node(bestLocalPos, 0, false, true, segPerVertex2[i][0], segPerVertex2[i][1]);

            }

        }

    }

    // //////////////////////////////////////////////////////////////////////
    //   Insert missing nodes that belong to vertices of the domain segment
    // //////////////////////////////////////////////////////////////////////

    for (int i=0; i<psurface_.domainSegments.size(); i++) {

        typename PSurface<1,ctype>::DomainSegment& cS = psurface_.domainSegments[i];

        // Insert node belonging to domain vertex to the segment to the left of the vertex
        if (cS.nodes.size()==0
            || !cS.nodes[0].isNodeOnVertex
            || (cS.nodes.size()==1 && cS.nodes[0].isNodeOnVertex && cS.nodes[0].domainLocalPosition > 1-eps)) {

            ctype rangeLocalPosition;
            int rangeSegment;

            if (NormalProjector<ctype>::normalProjection(psurface_.domainVertices[cS.points[0]], domainNormals[cS.points[0]],
                                                          rangeSegment, rangeLocalPosition,
                                                          tri2, coords2)) {

                typename PSurface<1,ctype>::Node newNode(0, rangeLocalPosition, true, false, rangeSegment, rangeSegment);

                cS.nodes.insert(cS.nodes.begin(), newNode);

            }

        }

        // Insert node belonging to domain vertex to the segment to the right of the vertex
        if (cS.nodes.size()==0
            || !cS.nodes.back().isNodeOnVertex
            || (cS.nodes.size()==1 && cS.nodes[0].isNodeOnVertex && cS.nodes[0].domainLocalPosition < eps)) {

            ctype rangeLocalPosition;
            int rangeSegment;

            if (NormalProjector<ctype>::normalProjection(psurface_.domainVertices[cS.points[1]], domainNormals[cS.points[1]],
                                                          rangeSegment, rangeLocalPosition,
                                                          tri2, coords2)) {

                typename PSurface<1,ctype>::Node newNode(1, rangeLocalPosition, true, false, rangeSegment, rangeSegment);

                cS.nodes.push_back(newNode);
            }

        }

    }

#if 0
    for (int i=0; i<psurface_.domainSegments.size(); i++) {
        printf(" --- segment %d ---   (%d  -->  %d)\n", i,
               psurface_.domainSegments[i].points[0],psurface_.domainSegments[i].points[1]);
        for (int j=0; j<psurface_.domainSegments[i].nodes.size(); j++)
            std::cout << psurface_.domainSegments[i].nodes[j];

        std::cout << std::endl;
    }
#endif

    // /////////////////////////////////////////////////////
    //   Insert edges
    // /////////////////////////////////////////////////////

    /** \todo Only works if the relevant domain is a single connected component */
    for (int i=0; i<psurface_.domainSegments.size(); i++) {

        std::vector<typename PSurface<1,ctype>::Node>& nodes = psurface_.domainSegments[i].nodes;

        ////////////////////////////////
        for (int j=0; j<int(nodes.size())-1; j++) {

            if (nodes[j].rangeSegments[0] == nodes[j+1].rangeSegments[0])
                nodes[j].rightRangeSegment = nodes[j].rangeSegments[0];
            else if (nodes[j].rangeSegments[0] == nodes[j+1].rangeSegments[1])
                nodes[j].rightRangeSegment = nodes[j].rangeSegments[0];
            else if (nodes[j].rangeSegments[1] == nodes[j+1].rangeSegments[0])
                nodes[j].rightRangeSegment = nodes[j].rangeSegments[1];
            else if (nodes[j].rangeSegments[1] == nodes[j+1].rangeSegments[1])
                nodes[j].rightRangeSegment = nodes[j].rangeSegments[1];
            else
                throw(std::runtime_error("Segment of the PSurface<1> data structure is inconsistent!"));

            if (nodes[j].rightRangeSegment == -1)
                throw(std::runtime_error("Segment of the PSurface<1> data structure is inconsistent!"));

        }

    }

}
Пример #9
0
ctype CircularPatch<ctype>::distanceTo(const StaticVector<ctype,3> &p) const
{
    int i, j;
    ctype bestDist = std::numeric_limits<ctype>::max();

    // check point against triangles
    for (j=0; j<size(); j++){

        const DomainTriangle<ctype>& cT = par->triangles(triangles[j]);

        StaticVector<ctype,3> triPoints[3];
        triPoints[0] = par->vertices(cT.vertices[0]);
        triPoints[1] = par->vertices(cT.vertices[1]);
        triPoints[2] = par->vertices(cT.vertices[2]);

        // local base
        StaticVector<ctype,3> a = triPoints[1] - triPoints[0];
        StaticVector<ctype,3> b = triPoints[2] - triPoints[0];
        StaticVector<ctype,3> c = a.cross(b);
        c.normalize();

        StaticVector<ctype,3> x = p - triPoints[0];

        // write x in the new base  (Cramer's rule)
        StaticMatrix<ctype,3> numerator(a, b, c);
        StaticMatrix<ctype,3> alphaMat(x, b, c);
        StaticMatrix<ctype,3> betaMat(a, x, c);
        StaticMatrix<ctype,3> gammaMat(a, b, x);

        ctype alpha = alphaMat.det()/numerator.det();
        ctype beta  = betaMat.det()/numerator.det();
        ctype gamma = gammaMat.det()/numerator.det();

        // check whether orthogonal projection onto the ab plane is in triangle
        bool isIn = alpha>=0 && beta>=0 && (1-alpha-beta)>=0;

        if (isIn && fabs(gamma)<bestDist){

            //              printf("a(%1.2f %1.2f %1.2f) b(%1.2f %1.2f %1.2f)  c(%1.2f %1.2f %1.2f)  x(%1.2f %1.2f %1.2f)\n",
            //                     a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z, x.x, x.y, x.z);

            //              printf("tri: %d, alpha = %f, beta = %f, gamma = %f\n", j, alpha, beta, gamma);

            bestDist = fabs(gamma);
        }
    }

    // check point against edges
    for (i=0; i<size(); i++){
        for (j=0; j<3; j++){

            const DomainTriangle<ctype>& cT = par->triangles(triangles[i]);

            StaticVector<ctype,3> from = par->vertices(cT.vertices[j]);
            StaticVector<ctype,3> to   = par->vertices(cT.vertices[(j+1)%3]);

            StaticVector<ctype,3> edge = to - from;

            ctype projectLength = edge.dot(p - from)/edge.length();
            StaticVector<ctype,3> projection = edge/edge.length() * projectLength;

            ctype orthoDist = ((p-from) - projection).length();

            if (projectLength>=0 && projectLength<=edge.length() && orthoDist<bestDist)
                bestDist = orthoDist;
        }
    }

    // check point against vertices
    for (i=0; i<size(); i++){
        for (j=0; j<3; j++){
            ctype dist = (p - par->vertices(par->triangles(triangles[i]).vertices[j])).length();
            if (dist < bestDist){
                bestDist = dist;
            }
        }
    }

    return bestDist;
}
Пример #10
0
static void FindHubAreas()
{
    if (!hubAreas.empty())
        return;

    AiAasWorld *aasWorld = AiAasWorld::Instance();
    if (!aasWorld->IsLoaded())
        return;

    // Select not more than hubAreas.capacity() grounded areas that have highest connectivity to other areas.

    struct AreaAndReachCount
    {
        int area, reachCount;
        AreaAndReachCount(int area_, int reachCount_): area(area_), reachCount(reachCount_) {}
        // Ensure that area with lowest reachCount will be evicted in pop_heap(), so use >
        bool operator<(const AreaAndReachCount &that) const { return reachCount > that.reachCount; }
    };

    StaticVector<AreaAndReachCount, hubAreas.capacity() + 1> bestAreasHeap;
    for (int i = 1; i < aasWorld->NumAreas(); ++i)
    {
        const auto &areaSettings = aasWorld->AreaSettings()[i];
        if (!(areaSettings.areaflags & AREA_GROUNDED))
            continue;
        if (areaSettings.areaflags & AREA_DISABLED)
            continue;
        if (areaSettings.contents & (AREACONTENTS_DONOTENTER|AREACONTENTS_LAVA|AREACONTENTS_SLIME|AREACONTENTS_WATER))
            continue;

        // Reject degenerate areas, pass only relatively large areas
        const auto &area = aasWorld->Areas()[i];
        if (area.maxs[0] - area.mins[0] < 128.0f)
            continue;
        if (area.maxs[1] - area.mins[1] < 128.0f)
            continue;

        // Count as useful only several kinds of reachabilities
        int usefulReachCount = 0;
        int reachNum = areaSettings.firstreachablearea;
        int lastReachNum = areaSettings.firstreachablearea + areaSettings.numreachableareas - 1;
        while (reachNum <= lastReachNum)
        {
            const auto &reach = aasWorld->Reachabilities()[reachNum];
            if (reach.traveltype == TRAVEL_WALK || reach.traveltype == TRAVEL_WALKOFFLEDGE)
                usefulReachCount++;
            ++reachNum;
        }

        // Reject early to avoid more expensive call to push_heap()
        if (!usefulReachCount)
            continue;

        bestAreasHeap.push_back(AreaAndReachCount(i, usefulReachCount));
        std::push_heap(bestAreasHeap.begin(), bestAreasHeap.end());

        // bestAreasHeap size should be always less than its capacity:
        // 1) to ensure that there is a free room for next area;
        // 2) to ensure that hubAreas capacity will not be exceeded.
        if (bestAreasHeap.size() == bestAreasHeap.capacity())
        {
            std::pop_heap(bestAreasHeap.begin(), bestAreasHeap.end());
            bestAreasHeap.pop_back();
        }
    }
    static_assert(bestAreasHeap.capacity() == hubAreas.capacity() + 1, "");
    for (const auto &areaAndReachCount: bestAreasHeap)
        hubAreas.push_back(areaAndReachCount.area);
}
Пример #11
0
    /// gives the surface area
    ctype area(int tri) const {
        StaticVector<ctype,3> a = vertices(triangles(tri).vertices[1]) - vertices(triangles(tri).vertices[0]);
        StaticVector<ctype,3> b = vertices(triangles(tri).vertices[2]) - vertices(triangles(tri).vertices[0]);

        return fabs(0.5 * (a.cross(b)).length());
    }
Пример #12
0
void Bot::RegisterVisibleEnemies()
{
    if(G_ISGHOSTING(self) || GS_MatchState() == MATCH_STATE_COUNTDOWN || GS_ShootingDisabled())
        return;

    CheckIsInThinkFrame(__FUNCTION__);

    // Compute look dir before loop
    vec3_t lookDir;
    AngleVectors(self->s.angles, lookDir, nullptr, nullptr);

    float fov = 110.0f + 69.0f * Skill();
    float dotFactor = cosf((float)DEG2RAD(fov / 2));

    struct EntAndDistance
    {
        int entNum;
        float distance;

        EntAndDistance(int entNum_, float distance_): entNum(entNum_), distance(distance_) {}
        bool operator<(const EntAndDistance &that) const { return distance < that.distance; }
    };

    // Do not call inPVS() and G_Visible() for potential targets inside a loop for all clients.
    // In worst case when all bots may see each other we get N^2 traces and PVS tests
    // First, select all candidate targets along with distance to a bot.
    // Then choose not more than BotBrain::maxTrackedEnemies nearest enemies for calling OnEnemyViewed()
    // It may cause data loss (far enemies may have higher logical priority),
    // but in a common good case (when there are few visible enemies) it preserves data,
    // and in the worst case mentioned above it does not act weird from player POV and prevents server hang up.
    // Note: non-client entities also may be candidate targets.
    StaticVector<EntAndDistance, MAX_EDICTS> candidateTargets;

    for (int i = 1; i < game.numentities; ++i)
    {
        edict_t *ent = game.edicts + i;
        if (botBrain.MayNotBeFeasibleEnemy(ent))
            continue;

        // Reject targets quickly by fov
        Vec3 toTarget(ent->s.origin);
        toTarget -= self->s.origin;
        float squareDistance = toTarget.SquaredLength();
        if (squareDistance < 1)
            continue;
        float invDistance = Q_RSqrt(squareDistance);
        toTarget *= invDistance;
        if (toTarget.Dot(lookDir) < dotFactor)
            continue;

        // It seams to be more instruction cache-friendly to just add an entity to a plain array
        // and sort it once after the loop instead of pushing an entity in a heap on each iteration
        candidateTargets.emplace_back(EntAndDistance(ENTNUM(ent), 1.0f / invDistance));
    }

    std::sort(candidateTargets.begin(), candidateTargets.end());

    // Select inPVS/visible targets first to aid instruction cache, do not call callbacks in loop
    StaticVector<edict_t *, MAX_CLIENTS> targetsInPVS;
    StaticVector<edict_t *, MAX_CLIENTS> visibleTargets;

    static_assert(AiBaseEnemyPool::MAX_TRACKED_ENEMIES <= MAX_CLIENTS, "targetsInPVS capacity may be exceeded");

    for (int i = 0, end = std::min(candidateTargets.size(), botBrain.MaxTrackedEnemies()); i < end; ++i)
    {
        edict_t *ent = game.edicts + candidateTargets[i].entNum;
        if (trap_inPVS(self->s.origin, ent->s.origin))
            targetsInPVS.push_back(ent);
    }

    for (auto ent: targetsInPVS)
        if (G_Visible(self, ent))
            visibleTargets.push_back(ent);

    // Call bot brain callbacks on visible targets
    for (auto ent: visibleTargets)
        botBrain.OnEnemyViewed(ent);

    botBrain.AfterAllEnemiesViewed();

    CheckAlertSpots(visibleTargets);
}
Пример #13
0
SelectedNavEntity BotItemsSelector::SuggestGoalNavEntity( const SelectedNavEntity &currSelectedNavEntity ) {
	UpdateInternalItemAndGoalWeights();

	StaticVector<NavEntityAndWeight, MAX_NAVENTS> rawWeightCandidates;
	const auto levelTime = level.time;
	auto *navEntitiesRegistry = NavEntitiesRegistry::Instance();
	for( auto it = navEntitiesRegistry->begin(), end = navEntitiesRegistry->end(); it != end; ++it ) {
		const NavEntity *navEnt = *it;
		if( navEnt->IsDisabled() ) {
			continue;
		}

		// We cannot just set a zero internal weight for a temporarily disabled nav entity
		// (it might be overridden by an external weight, and we should not modify external weights
		// as script users expect them remaining the same unless explicitly changed via script API)
		if( disabledForSelectionUntil[navEnt->Id()] >= levelTime ) {
			continue;
		}

		// Since movable goals have been introduced (and clients qualify as movable goals), prevent picking itself as a goal.
		if( navEnt->Id() == ENTNUM( self ) ) {
			continue;
		}

		if( navEnt->Item() && !G_Gametype_CanPickUpItem( navEnt->Item() ) ) {
			continue;
		}

		// Reject an entity quickly if it looks like blocked by an enemy that is close to the entity.
		// Note than passing this test does not guarantee that entire path to the entity is not blocked by enemies.
		if( self->ai->botRef->routeCache->AreaDisabled( navEnt->AasAreaNum() ) ) {
			continue;
		}

		// This is a coarse and cheap test, helps to reject recently picked armors and powerups
		unsigned spawnTime = navEnt->SpawnTime();
		// A feasible spawn time (non-zero) always >= level.time.
		if( !spawnTime || level.time - spawnTime > 15000 ) {
			continue;
		}

		float weight = GetEntityWeight( navEnt->Id() );
		if( weight > 0 ) {
			rawWeightCandidates.push_back( NavEntityAndWeight( navEnt, weight ) );
		}
	}

	// Sort all pre-selected candidates by their raw weights
	std::sort( rawWeightCandidates.begin(), rawWeightCandidates.end() );

	// Try checking whether the bot is in some floor cluster to give a greater weight for items in the same cluster
	int currFloorClusterNum = 0;
	const auto &entityPhysicsState = self->ai->botRef->EntityPhysicsState();
	const auto *aasFloorClusterNums = AiAasWorld::Instance()->AreaFloorClusterNums();
	if( aasFloorClusterNums[entityPhysicsState->CurrAasAreaNum()] ) {
		currFloorClusterNum = aasFloorClusterNums[entityPhysicsState->CurrAasAreaNum()];
	} else if( aasFloorClusterNums[entityPhysicsState->DroppedToFloorAasAreaNum()] ) {
		currFloorClusterNum = aasFloorClusterNums[entityPhysicsState->DroppedToFloorAasAreaNum()];
	}

	const NavEntity *currGoalNavEntity = currSelectedNavEntity.navEntity;
	float currGoalEntWeight = 0.0f;
	float currGoalEntCost = 0.0f;
	const NavEntity *bestNavEnt = nullptr;
	float bestWeight = 0.000001f;
	float bestNavEntCost = 0.0f;
	// Test not more than 16 best pre-selected by raw weight candidates.
	// (We try to avoid too many expensive FindTravelTimeToGoalArea() calls,
	// thats why we start from the best item to avoid wasting these calls for low-priority items)
	for( unsigned i = 0, end = std::min( rawWeightCandidates.size(), 16U ); i < end; ++i ) {
		const NavEntity *navEnt = rawWeightCandidates[i].goal;
		float weight = rawWeightCandidates[i].weight;

		unsigned moveDuration = 1;
		unsigned waitDuration = 1;

		if( self->ai->botRef->CurrAreaNum() != navEnt->AasAreaNum() ) {
			// We ignore cost of traveling in goal area, since:
			// 1) to estimate it we have to retrieve reachability to goal area from last area before the goal area
			// 2) it is relative low compared to overall travel cost, and movement in areas is cheap anyway
			moveDuration = self->ai->botRef->botBrain.FindTravelTimeToGoalArea( navEnt->AasAreaNum() ) * 10U;
			// AAS functions return 0 as a "none" value, 1 as a lowest feasible value
			if( !moveDuration ) {
				continue;
			}

			if( navEnt->IsDroppedEntity() ) {
				// Do not pick an entity that is likely to dispose before it may be reached
				if( navEnt->Timeout() <= level.time + moveDuration ) {
					continue;
				}
			}
		}

		unsigned spawnTime = navEnt->SpawnTime();
		// The entity is not spawned and respawn time is unknown
		if( !spawnTime ) {
			continue;
		}

		// Entity origin may be reached at this time
		unsigned reachTime = level.time + moveDuration;
		if( reachTime < spawnTime ) {
			waitDuration = spawnTime - reachTime;
		}

		if( waitDuration > navEnt->MaxWaitDuration() ) {
			continue;
		}

		float moveCost = MOVE_TIME_WEIGHT * moveDuration * navEnt->CostInfluence();
		float cost = 0.0001f + moveCost + WAIT_TIME_WEIGHT * waitDuration * navEnt->CostInfluence();

		weight = ( 1000 * weight ) / cost;

		// If the bot is inside a floor cluster
		if( currFloorClusterNum ) {
			// Greatly increase weight for items in the same floor cluster
			if( currFloorClusterNum == aasFloorClusterNums[navEnt->AasAreaNum()] ) {
				weight *= 4.0f;
			}
		}

		// Store current weight of the current goal entity
		if( currGoalNavEntity == navEnt ) {
			currGoalEntWeight = weight;
			// Waiting time is handled by the planner for wait actions separately.
			currGoalEntCost = moveCost;
		}

		if( weight > bestWeight ) {
			bestNavEnt = navEnt;
			bestWeight = weight;
			// Waiting time is handled by the planner for wait actions separately.
			bestNavEntCost = moveCost;
		}
	}

	if( !bestNavEnt ) {
		Debug( "Can't find a feasible long-term goal nav. entity\n" );
		return SelectedNavEntity( nullptr, std::numeric_limits<float>::max(), 0.0f, level.time + 200 );
	}

	// If it is time to pick a new goal (not just re-evaluate current one), do not be too sticky to the current goal
	const float currToBestWeightThreshold = currGoalNavEntity != nullptr ? 0.6f : 0.8f;

	if( currGoalNavEntity && currGoalNavEntity == bestNavEnt ) {
		constexpr const char *format = "current goal entity %s is kept as still having best weight %.3f\n";
		Debug( format, currGoalNavEntity->Name(), bestWeight );
		return SelectedNavEntity( bestNavEnt, bestNavEntCost, GetGoalWeight( bestNavEnt->Id() ), level.time + 4000 );
	} else if( currGoalEntWeight > 0 && currGoalEntWeight / bestWeight > currToBestWeightThreshold ) {
		constexpr const char *format =
			"current goal entity %s is kept as having weight %.3f good enough to not consider picking another one\n";
		// If currGoalEntWeight > 0, currLongTermGoalEnt is guaranteed to be non-null
		Debug( format, currGoalNavEntity->Name(), currGoalEntWeight );
		return SelectedNavEntity( currGoalNavEntity, currGoalEntCost, GetGoalWeight( bestNavEnt->Id() ), level.time + 2500 );
	} else {
		if( currGoalNavEntity ) {
			const char *format = "suggested %s weighted %.3f as a long-term goal instead of %s weighted now as %.3f\n";
			Debug( format, bestNavEnt->Name(), bestWeight, currGoalNavEntity->Name(), currGoalEntWeight );
		} else {
			Debug( "suggested %s weighted %.3f as a new long-term goal\n", bestNavEnt->Name(), bestWeight );
		}
		return SelectedNavEntity( bestNavEnt, bestNavEntCost, GetGoalWeight( bestNavEnt->Id() ), level.time + 2500 );
	}
}