/*
 * ===============================================================================
 *
 * Trace through the spatial subdivision
 *
 * ===============================================================================
 */

/*
 * ================
 * idCollisionModelManagerLocal::TraceTrmThroughNode
 * ================
 */
        void idCollisionModelManagerLocal::TraceTrmThroughNode(cm_traceWork_t *tw, cm_node_t *node)
        {
            cm_polygonRef_t *pref;
            cm_brushRef_t *  bref;

            // position test
            if (tw->positionTest)
            {
                // if already stuck in solid
                if (tw->trace.fraction == 0.0f)
                {
                    return;
                }
                // test if any of the trm vertices is inside a brush
                for (bref = node->brushes; bref; bref = bref->next)
                {
                    if (idCollisionModelManagerLocal::TestTrmVertsInBrush(tw, bref->b))
                    {
                        return;
                    }
                }
                // if just testing a point we're done
                if (tw->pointTrace)
                {
                    return;
                }
                // test if the trm is stuck in any polygons
                for (pref = node->polygons; pref; pref = pref->next)
                {
                    if (idCollisionModelManagerLocal::TestTrmInPolygon(tw, pref->p))
                    {
                        return;
                    }
                }
            }
            else if (tw->rotation)
            {
                // rotate through all polygons in this leaf
                for (pref = node->polygons; pref; pref = pref->next)
                {
                    if (idCollisionModelManagerLocal::RotateTrmThroughPolygon(tw, pref->p))
                    {
                        return;
                    }
                }
            }
            else
            {
                // trace through all polygons in this leaf
                for (pref = node->polygons; pref; pref = pref->next)
                {
                    if (idCollisionModelManagerLocal::TranslateTrmThroughPolygon(tw, pref->p))
                    {
                        return;
                    }
                }
            }
        }
示例#2
0
/*
 * ================
 * CM_AddContact
 * ================
 */
        ID_INLINE void CM_AddContact(cm_traceWork_t *tw)
        {
            if (tw->numContacts >= tw->maxContacts)
            {
                return;
            }
            // copy contact information from trace_t
            tw->contacts[tw->numContacts] = tw->trace.c;
            tw->numContacts++;
            // set fraction back to 1 to find all other contacts
            tw->trace.fraction = 1.0f;
        }
/*
 * ================
 * idCollisionModelManagerLocal::TraceThroughModel
 * ================
 */
        void idCollisionModelManagerLocal::TraceThroughModel(cm_traceWork_t *tw)
        {
            float      d;
            int        i, numSteps;
            idVec3     start, end;
            idRotation rot;

            if (!tw->rotation)
            {
                // trace through spatial subdivision and then through leafs
                idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r(tw, tw->model->node, 0, 1, tw->start, tw->end);
            }
            else
            {
                // approximate the rotation with a series of straight line movements
                // total length covered along circle
                d = tw->radius * DEG2RAD(tw->angle);
                // if more than one step
                if (d > CIRCLE_APPROXIMATION_LENGTH)
                {
                    // number of steps for the approximation
                    numSteps = (int)(CIRCLE_APPROXIMATION_LENGTH / d);
                    // start of approximation
                    start = tw->start;
                    // trace circle approximation steps through the BSP tree
                    for (i = 0; i < numSteps; i++)
                    {
                        // calculate next point on approximated circle
                        rot.Set(tw->origin, tw->axis, tw->angle * ((float)(i + 1) / numSteps));
                        end = start * rot;
                        // trace through spatial subdivision and then through leafs
                        idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r(tw, tw->model->node, 0, 1, start, end);
                        // no need to continue if something was hit already
                        if (tw->trace.fraction < 1.0f)
                        {
                            return;
                        }
                        start = end;
                    }
                }
                else
                {
                    start = tw->start;
                }
                // last step of the approximation
                idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r(tw, tw->model->node, 0, 1, start, tw->end);
            }
        }
/*
 * ================
 * idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r
 * ================
 */
//#define NO_SPATIAL_SUBDIVISION

        void idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r(cm_traceWork_t *tw, cm_node_t *node, float p1f, float p2f, idVec3&p1, idVec3&p2)
        {
            float  t1, t2, offset;
            float  frac, frac2;
            float  idist;
            idVec3 mid;
            int    side;
            float  midf;

            if (!node)
            {
                return;
            }

            if (tw->quickExit)
            {
                return;         // stop immediately
            }

            if (tw->trace.fraction <= p1f)
            {
                return;         // already hit something nearer
            }

            // if we need to test this node for collisions
            if (node->polygons || (tw->positionTest && node->brushes))
            {
                // trace through node with collision data
                idCollisionModelManagerLocal::TraceTrmThroughNode(tw, node);
            }
            // if already stuck in solid
            if (tw->positionTest && tw->trace.fraction == 0.0f)
            {
                return;
            }
            // if this is a leaf node
            if (node->planeType == -1)
            {
                return;
            }
#ifdef NO_SPATIAL_SUBDIVISION
            idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r(tw, node->children[0], p1f, p2f, p1, p2);
            idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r(tw, node->children[1], p1f, p2f, p1, p2);
            return;
#endif
            // distance from plane for trace start and end
            t1 = p1[node->planeType] - node->planeDist;
            t2 = p2[node->planeType] - node->planeDist;
            // adjust the plane distance appropriately for mins/maxs
            offset = tw->extents[node->planeType];
            // see which sides we need to consider
            if (t1 >= offset && t2 >= offset)
            {
                idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r(tw, node->children[0], p1f, p2f, p1, p2);
                return;
            }

            if (t1 < -offset && t2 < -offset)
            {
                idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r(tw, node->children[1], p1f, p2f, p1, p2);
                return;
            }

            if (t1 < t2)
            {
                idist = 1.0f / (t1 - t2);
                side  = 1;
                frac2 = (t1 + offset) * idist;
                frac  = (t1 - offset) * idist;
            }
            else if (t1 > t2)
            {
                idist = 1.0f / (t1 - t2);
                side  = 0;
                frac2 = (t1 - offset) * idist;
                frac  = (t1 + offset) * idist;
            }
            else
            {
                side  = 0;
                frac  = 1.0f;
                frac2 = 0.0f;
            }

            // move up to the node
            if (frac < 0.0f)
            {
                frac = 0.0f;
            }
            else if (frac > 1.0f)
            {
                frac = 1.0f;
            }

            midf = p1f + (p2f - p1f) * frac;

            mid[0] = p1[0] + frac * (p2[0] - p1[0]);
            mid[1] = p1[1] + frac * (p2[1] - p1[1]);
            mid[2] = p1[2] + frac * (p2[2] - p1[2]);

            idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r(tw, node->children[side], p1f, midf, p1, mid);


            // go past the node
            if (frac2 < 0.0f)
            {
                frac2 = 0.0f;
            }
            else if (frac2 > 1.0f)
            {
                frac2 = 1.0f;
            }

            midf = p1f + (p2f - p1f) * frac2;

            mid[0] = p1[0] + frac2 * (p2[0] - p1[0]);
            mid[1] = p1[1] + frac2 * (p2[1] - p1[1]);
            mid[2] = p1[2] + frac2 * (p2[2] - p1[2]);

            idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r(tw, node->children[side ^ 1], midf, p2f, mid, p2);
        }