/*
 * ===============================================================================
 *
 * 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;
                    }
                }
            }
        }
/*
 * ================
 * idCollisionModelManagerLocal::WriteNodes
 * ================
 */
        void idCollisionModelManagerLocal::WriteNodes(idFile *fp, cm_node_t *node)
        {
            fp->WriteFloatString("\t( %d %f )\n", node->planeType, node->planeDist);
            if (node->planeType != -1)
            {
                WriteNodes(fp, node->children[0]);
                WriteNodes(fp, node->children[1]);
            }
        }
/*
 * ================
 * 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);
        }
/*
 * ================
 * idCollisionModelManagerLocal::CountPolygonMemory
 * ================
 */
        int idCollisionModelManagerLocal::CountPolygonMemory(cm_node_t *node) const
 int CM_GetNodeContents(cm_node_t *node);
/*
 * ===============================================================================
 *
 * Writing of collision model file
 *
 * ===============================================================================
 */

        void CM_GetNodeBounds(idBounds *bounds, cm_node_t *node);