///* //================== //CM_TraceThroughTree //Traverse all the contacted leafs from the start to the end position. //If the trace is a point, they will be exactly in order, but for larger //trace volumes it is possible to hit something in a later leaf with //a smaller intercept fraction. //================== //*/ //void TraceThroughTree(traceWork_t tw, int num, float p1f, float p2f, Vector3 p1, Vector3 p2) //{ // //TraceTest(0); // if (tw.trace.fraction <= p1f) // return; // already hit something nearer // // if < 0, we are in a leaf node // if (num < 0) // { // TraceThroughLeaf(tw, leafs[-1-num]); // return; // } // // // // find the point distances to the seperating plane // // and the offset for the size of the box // // // dnode_t node = nodes[num]; // cplane_t plane = node.plane; // // adjust the plane distance apropriately for mins/maxs // float t1, t2, offset; // if (plane.type < 3) // { // t1 = p1[plane.type] - plane.dist; // t2 = p2[plane.type] - plane.dist; // offset = tw.extents[plane.type]; // } // else // { // t1 = Vector3.Dot(plane.normal, p1) - plane.dist; // t2 = Vector3.Dot(plane.normal, p2) - plane.dist; // if (tw.isPoint) // offset = 0; // else // { // offset = (float)(Math.Abs(tw.extents[0] * plane.normal[0]) + Math.Abs(tw.extents[1] * plane.normal[1]) + Math.Abs(tw.extents[2] * plane.normal[2])); // // this is silly // //offset = 2048; // } // } // // see which sides we need to consider // if (t1 > offset + 1 && t2 > offset + 1) // { // TraceThroughTree(tw, node.children[0], p1f, p2f, p1, p2); // return; // } // if (t1 < -offset - 1 && t2 < -offset - 1) // { // TraceThroughTree(tw, node.children[1], p1f, p2f, p1, p2); // return; // } // // put the crosspoint SURFACE_CLIP_EPSILON pixels on the near side // float idist, frac, frac2; // int side; // if (t1 < t2) // { // idist = 1.0f / (t1 - t2); // side = 1; // frac2 = (t1 + offset + EPSILON) * idist; // frac = (t1 - offset + EPSILON) * idist; // } // else if (t1 > t2) // { // idist = 1.0f / (t1 - t2); // side = 0; // frac2 = (t1 - offset - EPSILON) * idist; // frac = (t1 + offset + EPSILON) * idist; // } // else // { // side = 0; // frac = 1; // frac2 = 0; // } // // move up to the node // if (frac < 0f) // frac = 0; // if (frac > 1f) // frac = 1; // float midf = p1f + ((p2f - p1f) * frac); // Vector3 mid = p1 + (p2 - p1) * frac; // TraceThroughTree(tw, node.children[side], p1f, midf, p1, mid); // // go past the node // if (frac2 < 0f) // frac2 = 0; // if (frac2 > 1f) // frac2 = 1f; // midf = p1f + ((p2f - p1f) * frac2); // mid = p1 + (frac2 * (p2 - p1)); // TraceThroughTree(tw, node.children[side ^ 1], midf, p2f, mid, p2); //} void TraceThroughLeaf(traceWork_t tw, int num) { dleaf_t leaf = leafs[num]; dbrush_t b; // trace line against all brushes in the leaf int brushnum = leaf.numleafbrushes; if (brushnum == 0) brushnum++; for (int k = 0; k < brushnum; k++) { b = brushes[leafbrushes[leaf.firstleafbrush + k]]; if (b.checkcount == checkcount) continue; // already checked this brush in another leaf b.checkcount = checkcount; // Assert that the brush contains the needed contents if (((int)b.contents & tw.contents) == 0) continue; // Check bounds of brush if (!BoundsIntersect(tw.bounds[0], tw.bounds[1], b.boundsmin, b.boundsmax)) continue; //ClipBoxToBrush(tw, b); TraceThroughBrush(tw, b); // Return now if we were blocked 100% if (tw.trace.fraction == 0.0f) return; } if (tw.trace.fraction == 0.0f) return; //// Check displacements //if (Renderer.Instance.SourceMap != null) //{ // World world = Renderer.Instance.SourceMap.world; // leaf = world.leafs[num]; // if (leaf.DisplacementIndexes != null) // { // KeyValuePair<int,int>[] indx = leaf.DisplacementIndexes; // for (int i = 0; i < indx.Length; i++) // { // DispCollTree dispTree = world.dispCollTrees[indx[i].Value]; // if ((dispTree.m_Contents & tw.contents) == 0) // continue; // TraceToDispTree(dispTree, tw.start, tw.end, tw.size[0], tw.size[1], 0f, 1f, tw.trace, false); // if (tw.trace.fraction == 0f) // break; // //if (face2.lastVisCount != VisCount) // //{ // // face2.lastVisCount = VisCount; // // visibleRenderItems[face2.item.material.MaterialID].Add(face2.item); // //} // } // } //} if (tw.trace.fraction != 1.0f) { // // determine whether or not we are in solid // Vector3 traceDir = tw.end - tw.start; if (Vector3.Dot(tw.trace.plane.normal, traceDir) > 0.0f) { tw.trace.allsolid = true; tw.trace.startsolid = true; } } }
void TraceThroughBrush(traceWork_t tw, dbrush_t brush) { float enterFrac = -9999f; float leaveFrac = 1f; cplane_t clipplane = null; if (brush.numsides <= 0) return; c_brush_traces++; bool getout = false; bool getoutTemp = false; bool startout = false; bool startoutTemp = false; dbrushside_t leadside = null; cplane_t plane = null; // // compare the trace against all planes of the brush // find the latest time the trace crosses a plane towards the interior // and the earliest time the trace crosses a plane towards the exterior // for (int i = 0; i < brush.numsides; ++i) { dbrushside_t side = brushsides[brush.firstside + i]; plane = side.plane; // sanity check if (side.plane.normal.X == 0f && side.plane.normal.Y == 0f && side.plane.normal.Z == 0f) continue; float dist; if (!tw.isPoint) dist = plane.dist - Vector3.Dot(tw.offsets[plane.signbits], plane.normal); else // ray { dist = plane.dist; // dont trace rays against bevel planes if (side.bevel > 0) continue; } float d1 = Vector3.Dot(tw.start, plane.normal) - dist; float d2 = Vector3.Dot(tw.end, plane.normal) - dist; // if completely in front of face, no intersection if (d1 > 0.0f) { startout = true; // d1 > 0.f && d2 > 0.f if (d2 > 0.0f) return; } else { // d1 <= 0.f && d2 <= 0.f if (d2 <= 0.0f) continue; // d2 > 0.f getout = true; // endpoint is not in solid } //startout = startoutTemp; //getout = getoutTemp; // crosses face if (d1 > d2) // enter { // enter // JAY: This could be negative if d1 is less than the epsilon. // If the trace is short (d1-d2 is small) then it could produce a large // negative fraction. I can't believe this didn't break Q2! float f = (d1 - EPSILON); if (f < 0.0f) f = 0.0f; f = f / (d1 - d2); if (f > enterFrac) { enterFrac = f; clipplane = plane; leadside = side; } } else // leave { float f = (d1 + EPSILON) / (d1 - d2); //if (f > 1) // f = 1; if (f < leaveFrac) { leaveFrac = f; } } } ////when this happens, we entered the brush *after* leaving the previous brush. //// Therefore, we're still outside! //// NOTE: We only do this test against points because fractionleftsolid is //// not possible to compute for brush sweeps without a *lot* more computation //// So, client code will never get fractionleftsolid for box sweeps //if (tw.isPoint && startout) //{ // // Add a little sludge. The sludge should already be in the fractionleftsolid // // (for all intents and purposes is a leavefrac value) and enterfrac values. // // Both of these values have +/- DIST_EPSILON values calculated in. Thus, I // // think the test should be against "0.0." If we experience new "left solid" // // problems you may want to take a closer look here! // // if ((trace->fractionleftsolid - enterfrac) > -1e-6) // if (tw.trace.fractionleftsolid - enterFrac > 0.0f) // startout = false; //} // // all planes have been checked, and the trace was not // completely outside the brush // if (!startout) { // original point was inside brush tw.trace.startsolid = true; tw.trace.contents = (int)brush.contents; if (!getout) { tw.trace.allsolid = true; //tw.trace.plane = plane; tw.trace.fraction = 0f; //tw.trace.fractionleftsolid = 1.0f; } else { // if leavefrac == 1, this means it's never been updated or we're in allsolid // the allsolid case was handled above //if ((leaveFrac != 1.0f) && (leaveFrac > tw.trace.fractionleftsolid)) //{ // tw.trace.fractionleftsolid = leaveFrac; // // This could occur if a previous trace didn't start us in solid // if (tw.trace.fraction <= leaveFrac) // { // tw.trace.fraction = 1.0f; // } //} } return; } // We haven't hit anything at all until we've left... if (enterFrac < leaveFrac) { if (enterFrac > -9999.0f && enterFrac < tw.trace.fraction) { if (enterFrac < 0) enterFrac = 0; tw.trace.fraction = enterFrac; tw.trace.plane = clipplane; if(leadside.texinfo != -1) tw.trace.surfaceFlags = (int)texinfos[leadside.texinfo].flags; //tw.trace.surfaceFlags = leadside.bevel; tw.trace.contents = (int)brush.contents; } } }
void RecursiveHullCheck(traceWork_t tw, int num, float p1f, float p2f, Vector3 p1, Vector3 p2) { if (tw.trace.fraction <= p1f) return; // already hit something nearer dnode_t node = null; cplane_t plane; float t1 = 0f, t2 = 0f, offset = 0f; // find the point distances to the seperating plane // and the offset for the size of the box // NJS: Hoisted loop invariant comparison to trace_ispoint if (tw.isPoint) { while (num >= 0) { node = nodes[num]; plane = node.plane; if (plane.type < 3) { t1 = p1[plane.type] - plane.dist; t2 = p2[plane.type] - plane.dist; offset = tw.extents[plane.type]; } else { t1 = Vector3.Dot(plane.normal, p1) - plane.dist; t2 = Vector3.Dot(plane.normal, p2) - plane.dist; offset = 0; } // see which sides we need to consider if (t1 > offset && t2 > offset) { num = node.children[0]; continue; } if (t1 < -offset && t2 < -offset) { num = node.children[1]; continue; } break; } } else { while (num >= 0) { node = nodes[num]; plane = node.plane; //Vector3 pl = new Vector3(0.5f, 0.5f, 0); //Vector3 pos = new Vector3(100, 200, -45); //float res = Vector3.Dot(pos,pl); //float sside = res +368; if (plane.type < 3) { t1 = p1[plane.type] - plane.dist; t2 = p2[plane.type] - plane.dist; offset = tw.extents[plane.type]; } else { t1 = Vector3.Dot(plane.normal, p1) - plane.dist; t2 = Vector3.Dot(plane.normal, p2) - plane.dist; offset = (float)(Math.Abs(tw.extents[0]*plane.normal[0]) + Math.Abs(tw.extents[1]*plane.normal[1]) + Math.Abs(tw.extents[2]*plane.normal[2])); } // see which sides we need to consider if (t1 > offset && t2 > offset) { num = node.children[0]; continue; } if (t1 < -offset && t2 < -offset) { num = node.children[1]; continue; } break; } } // if < 0, we are in a leaf node if (num < 0) { //TraceToLeaf(tw, -1 - num, p1f, p2f); TraceThroughLeaf(tw, -1 - num); return; } // put the crosspoint DIST_EPSILON pixels on the near side float idist, frac, frac2; int side; if (t1 < t2) { idist = 1.0f / (t1 - t2); side = 1; frac2 = (t1 + offset + EPSILON) * idist; frac = (t1 - offset - EPSILON) * idist; } else if (t1 > t2) { idist = 1.0f / (t1 - t2); side = 0; frac2 = (t1 - offset - EPSILON) * idist; frac = (t1 + offset + EPSILON) * idist; } else { side = 0; frac = 1; frac2 = 0; } // move up to the node frac = Clamp(frac, 0, 1); float midf = p1f + (p2f - p1f) * frac; Vector3 mid = p1 + (p2 - p1) * frac; RecursiveHullCheck(tw, node.children[side], p1f, midf, p1, mid); // go past the node frac2 = Clamp(frac2, 0, 1); midf = p1f + (p2f - p1f) * frac2; mid = p1 + (p2 - p1) * frac2; RecursiveHullCheck(tw, node.children[side ^ 1], midf, p2f, mid, p2); }
trace_t Trace(Vector3 start, Vector3 end, Vector3 mins, Vector3 maxs, int model, Vector3 origin, int brushmask) { //cmodel_t cmod = ClipHandleToModel(model); // for multi-check avoidance checkcount++; // for statistics, may be zeroed c_traces++; this.tw = new traceWork_t(); traceWork_t tw = this.tw; tw.trace = new trace_t(); tw.trace.fraction = 1f; // assume it goes the entire distance until shown otherwise tw.modelOrigin = origin; if (nodes == null || nodes.Length == 0) { return tw.trace; // map not loaded, shouldn't happen } // set basic parms tw.contents = brushmask; // adjust so that mins and maxs are always symetric, which // avoids some complications with plane expanding of rotated // bmodels Vector3 offset = (mins+maxs) * 0.5f; tw.size[0] = mins - offset; tw.size[1] = maxs - offset; tw.start = start + offset; tw.end = end + offset; tw.maxOffset = tw.size[1][0] + tw.size[1][1] + tw.size[1][2]; // tw.offsets[signbits] = vector to apropriate corner from origin tw.offsets[0][0] = tw.size[0][0]; tw.offsets[0][1] = tw.size[0][1]; tw.offsets[0][2] = tw.size[0][2]; tw.offsets[1][0] = tw.size[1][0]; tw.offsets[1][1] = tw.size[0][1]; tw.offsets[1][2] = tw.size[0][2]; tw.offsets[2][0] = tw.size[0][0]; tw.offsets[2][1] = tw.size[1][1]; tw.offsets[2][2] = tw.size[0][2]; tw.offsets[3][0] = tw.size[1][0]; tw.offsets[3][1] = tw.size[1][1]; tw.offsets[3][2] = tw.size[0][2]; tw.offsets[4][0] = tw.size[0][0]; tw.offsets[4][1] = tw.size[0][1]; tw.offsets[4][2] = tw.size[1][2]; tw.offsets[5][0] = tw.size[1][0]; tw.offsets[5][1] = tw.size[0][1]; tw.offsets[5][2] = tw.size[1][2]; tw.offsets[6][0] = tw.size[0][0]; tw.offsets[6][1] = tw.size[1][1]; tw.offsets[6][2] = tw.size[1][2]; tw.offsets[7][0] = tw.size[1][0]; tw.offsets[7][1] = tw.size[1][1]; tw.offsets[7][2] = tw.size[1][2]; // // calculate bounds // for (int i = 0; i < 3; i++) { if (tw.start[i] < tw.end[i]) { tw.bounds[0][i] = tw.start[i] + tw.size[0][i]; tw.bounds[1][i] = tw.end[i] + tw.size[1][i]; } else { tw.bounds[0][i] = tw.end[i] + tw.size[0][i]; tw.bounds[1][i] = tw.start[i] + tw.size[1][i]; } } // // check for position test special case // if (start.Equals(end)) { if (model > 0) { int test = 2; //TestInLeaf(tw, cmod.leaf); } else { PositionTest(tw); } } else { // // check for point special case // if (tw.size[0].Equals(Vector3.Zero)) { tw.isPoint = true; tw.extents = Vector3.Zero; } else { tw.isPoint = false; tw.extents = tw.size[1]; } // // general sweeping through world // if (model > 0) { int test = 2; //TraceThroughLeaf(tw, cmod.leaf); } else { RecursiveHullCheck(tw, 0, 0, 1, tw.start, tw.end); //TraceThroughTree(tw, 0, 0, 1, tw.start, tw.end); } } // generate endpos from the original, unmodified start/end if (tw.trace.fraction == 1f) { tw.trace.endpos = end; } else { tw.trace.endpos = start + (tw.trace.fraction * (end - start)); } // If allsolid is set (was entirely inside something solid), the plane is not valid. // If fraction == 1.0, we never hit anything, and thus the plane is not valid. // Otherwise, the normal on the plane should have unit length Debug.Assert(tw.trace.allsolid || tw.trace.fraction == 1f || tw.trace.plane.normal.LengthSquared() > 0.9999f); return tw.trace; }
//int FindParentNode(int nodeid) //{ // //System.Console.WriteLine("Parent for " + nodeid); // if (nodeid == 0) // return 0; // for (int i = 0; i < nodes.Length; i++) // { // if (nodes[i].children[0] == nodeid) // return FindParentNode(i); // else if (nodes[i].children[1] == nodeid) // return FindParentNode(i); // } // return -1; //} void PositionTest(traceWork_t tw) { // identify the leafs we are touching ll.bounds[0] = tw.start - tw.size[0]; ll.bounds[1] = tw.end - tw.size[1]; for (int i = 0; i < 3; i++) { ll.bounds[0][i] -= 1; ll.bounds[1][i] += 1; } ll.count = 0; ll.maxcount = 1024; ll.list = lleafs; //ll.StoreLeaf += new StoreLeafDelegate(StoreLeafs); ll.lastLeaf = 0; ll.overflowed = false; checkcount++; BoxLeafnums(ll, 0); checkcount++; // test the contents of the leafs for (int i = 0; i < ll.count; i++) { TraceThroughLeaf(tw, lleafs[i]); if (tw.trace.allsolid) break; } }