int R_LightVec(Vector3 start, Vector3 end, bool useLightStyles, ref Vector3 c) { c.X = c.Y = c.Z = 0; int skysurf = -1; LightVecState state = new LightVecState(); state.skySurf = -1; state.start = start; state.end = end; state.hitFrac = 1.0f; int retSurfId = RecursiveLightPoint(0, 0f, 1f, ref c, state); if (retSurfId == -1 && skysurf != -1) return skysurf; return retSurfId; }
int RecursiveLightPoint(int n, float start, float end, ref Vector3 c, LightVecState state) { while (n >= 0) { dnode_t node = world.nodes[n]; cplane_t plane = node.plane; float startDotn = Vector3.Dot(state.start, plane.normal); float deltaDotn = Vector3.Dot(state.end - state.start, plane.normal); float front = startDotn + start * deltaDotn - plane.dist; float back = deltaDotn + end * deltaDotn - plane.dist; bool side = front < 0; // If they're both on the same side of the plane, don't bother to split // just check the appropriate child int surfid = 0; if ((back < 0) == side) { n = node.children[side ? 1 : 0]; continue; //surfid = RecursiveLightPoint(node.children[side ? 1 : 0], start, end, ref c, state); //return surfid; } // calculate mid point float frac = front / (front - back); float mid = start * (1.0f - frac) + end * frac; // go down front side surfid = RecursiveLightPoint(node.children[side ? 1 : 0], start, mid, ref c, state); if (surfid != -1) return surfid; // hit something // check for impact on this node surfid = FindIntersectionSurfaceAtNode(n, mid, ref c, state); if (surfid != -1) return surfid; // go down back side surfid = RecursiveLightPoint(node.children[!side ? 1 : 0], mid, end, ref c, state); return surfid; break; } // didn't hit anything if (n < 0) { // FIXME: Should we always do this? It could get expensive... // Check all the faces at the leaves return FindIntersectionSurfaceAtLeaf(world.leafs[-1 - n], start, end, ref c, state); } return -1; }
int FindIntersectionSurfaceAtLeaf(dleaf_t leaf, float start, float end, ref Vector3 c, LightVecState state) { int clostestSurf = -1; for (int i = 0; i < leaf.numleaffaces; i++) { int surfid = world.leafFaces[leaf.firstleafface + i]; face_t face = world.faces_t[surfid]; // Don't add surfaces that have displacement; they are handled above // In fact, don't even set the vis frame; we need it unset for translucent // displacement code if (face.dispinfo != -1) continue; if ((face.face.texinfo.flags & SurfFlags.SURF_NODRAW) == SurfFlags.SURF_NODRAW) continue; cplane_t plane = face.face.plane_t; // Backface cull... if (Vector3.Dot(plane.normal, (state.end - state.start)) > 0.0f) continue; float startdotn = Vector3.Dot(state.start, plane.normal); float deltadotn = Vector3.Dot((state.end - state.start), plane.normal); float front = startdotn + start * deltadotn - plane.dist; float back = startdotn + end * deltadotn - plane.dist; bool side = front < 0.0f; // Blow it off if it doesn't split the plane... if ((back < 0.0f) == side) continue; // Don't test a surface that is farther away from the closest found intersection float frac = front / (front - back); if (frac >= state.hitFrac) continue; float mid = start * (1.0f - frac) + end * frac; // Check this surface to see if there's an intersection if (FindIntersectionAtSurface(surfid, mid, ref c, state)) { clostestSurf = surfid; } } // Return the closest surface hit return clostestSurf; }
int FindIntersectionSurfaceAtNode(int n, float t, ref Vector3 c, LightVecState state) { dnode_t node = world.nodes[n]; int surfid = node.firstface; for (int i = 0; i < node.numfaces; i++, surfid++) { // Don't immediately return when we hit sky; // we may actually hit another surface SurfFlags flags = world.faces[world.leafFaces[surfid]].texinfo.flags; if ((flags & SurfFlags.SURF_SKY) == SurfFlags.SURF_SKY) { state.skySurf = surfid; continue; } // Don't let water surfaces affect us if ((flags & SurfFlags.SURF_WARP) == SurfFlags.SURF_WARP) continue; if(FindIntersectionAtSurface(surfid, t, ref c, state)) return surfid; } return -1; }
//----------------------------------------------------------------------------- // Tests a particular surface //----------------------------------------------------------------------------- bool FindIntersectionAtSurface(int surfid, float f, ref Vector3 c, LightVecState state) { // no lightmaps on this surface? punt... // FIXME: should be water surface? SurfFlags flags = world.faces[world.leafFaces[surfid]].texinfo.flags; if ((flags & SurfFlags.SURF_NOLIGHT) == SurfFlags.SURF_NOLIGHT) // SURFDRAW_NOLIGHT return false; // Compute the actual point Vector3 pt = ViewParams.VectorMA(state.start, f, state.end - state.start); texinfo_t tex = world.faces[world.leafFaces[surfid]].texinfo; // See where in lightmap space our intersection point is float s = Vector3.Dot(pt, tex.lightmapVecs[0]) + tex.lightmapVecs2[0]; float t = Vector3.Dot(pt, tex.lightmapVecs[1]) + tex.lightmapVecs2[1]; // Not in the bounds of our lightmap? punt... int[] luxMins = world.faces[world.leafFaces[surfid]].face_t.LightmapTextureMinsInLuxels; if (s < luxMins[0] || t < luxMins[1]) return false; // assuming a square lightmap (FIXME: which ain't always the case), // lets see if it lies in that rectangle. If not, punt... float ds = s - luxMins[0]; float dt = t - luxMins[1]; int[] luxExtends = world.faces[world.leafFaces[surfid]].face_t.LightmapTextureSizeInLuxels; if (ds > luxExtends[0] || dt > luxExtends[1]) return false; // Store off the hit distance... state.hitFrac = f; // You heard the man! // TODO ComputeLightmapColorFromAverage(world.faces[world.leafFaces[surfid]].face_t, state.useLightstyles, ref c); return true; }