// The ambientCache is on the stack, so we don't want to leave a reference to it that would try to be freed later. Create the ambientCache immediately. static void R_FinishDeform(DrawSurf drawSurf, SrfTriangles newTri, DrawVert *ac) { if (newTri == null) { return; } // generate current normals, tangents, and bitangents We might want to support the possibility of deform functions generating // explicit normals, and we might also want to allow the cached deformInfo optimization for these. // FIXME: this doesn't work, because the deformed surface is just the ambient one, and there isn't an opportunity to generate light interactions if (drawSurf.material.ReceivesLighting) { newTri.verts = ac; R_DeriveTangents(newTri, false); newTri.verts = null; } newTri.ambientCache = vertexCache.AllocFrameTemp(ac, newTri.numVerts * DrawVert.SizeOf, false); fixed(void *newTri_indexes_ = newTri.indexes) newTri.indexCache = vertexCache.AllocFrameTemp(newTri_indexes_, newTri.numIndexes * sizeof(GlIndex), true); drawSurf.geoFrontEnd = newTri; drawSurf.ambientCache = newTri.ambientCache; drawSurf.indexCache = newTri.indexCache; drawSurf.numIndexes = newTri.numIndexes; drawSurf.numShadowIndexesNoFrontCaps = newTri.numShadowIndexesNoFrontCaps; drawSurf.numShadowIndexesNoCaps = newTri.numShadowIndexesNoCaps; drawSurf.shadowCapPlaneBits = newTri.shadowCapPlaneBits; }
// Determines which triangles of the surface are facing towards the light origin. // The facing array should be allocated with one extra index than the number of surface triangles, which will be used to handle dangling edge silhouettes. static void R_CalcInteractionFacing(IRenderEntity ent, SrfTriangles tri, IRenderLight light, ref SrfCullInfo cullInfo) { if (cullInfo.facing != null) { return; } Vector3 localLightOrigin; R_GlobalPointToLocal(ent.modelMatrix, light.globalLightOrigin, out localLightOrigin); var numFaces = tri.numIndexes / 3; if (tri.facePlanes == null || !tri.facePlanesCalculated) { R_DeriveFacePlanes(tri); } cullInfo.facing = (byte *)R_StaticAlloc((numFaces + 1) * sizeof(byte)); // calculate back face culling var planeSide = stackalloc float[numFaces + floatX.ALLOC16]; planeSide = (float *)_alloca16(planeSide); // exact geometric cull against face fixed(Plane *facePlanesP = tri.facePlanes) Simd.Dotcp(planeSide, localLightOrigin, facePlanesP, numFaces); Simd.CmpGE(cullInfo.facing, planeSide, 0f, numFaces); cullInfo.facing[numFaces] = 1; // for dangling edges to reference }
// We want to cull a little on the sloppy side, because the pre-clipping of geometry to the lights in dmap will give many cases that are right // at the border we throw things out on the border, because if any one vertex is clearly inside, the entire triangle will be accepted. static void R_CalcInteractionCullBits(IRenderEntity ent, SrfTriangles tri, IRenderLight light, ref SrfCullInfo cullInfo) { int i, frontBits; if (cullInfo.cullBits != null) { return; } frontBits = 0; // cull the triangle surface bounding box for (i = 0; i < 6; i++) { R_GlobalPlaneToLocal(ent.modelMatrix, -light.frustum[i], out cullInfo.localClipPlanes[i]); // get front bits for the whole surface if (tri.bounds.PlaneDistance(cullInfo.localClipPlanes[i]) >= IInteraction.LIGHT_CLIP_EPSILON) { frontBits |= 1 << i; } } // if the surface is completely inside the light frustum if (frontBits == ((1 << 6) - 1)) { cullInfo.cullBits = IInteraction.LIGHT_CULL_ALL_FRONT; return; } cullInfo.cullBits = (byte *)R_StaticAlloc(tri.numVerts * sizeof(byte)); Simd.Memset(cullInfo.cullBits, 0, tri.numVerts * sizeof(byte)); var planeSide = stackalloc float[tri.numVerts + floatX.ALLOC16]; planeSide = (float *)_alloca16(planeSide); for (i = 0; i < 6; i++) { // if completely infront of this clipping plane if ((frontBits & (1 << i)) != 0) { continue; fixed(DrawVert *vertsD = tri.verts) Simd.Dotpd(planeSide, cullInfo.localClipPlanes[i], vertsD, tri.numVerts); Simd.CmpLTb(cullInfo.cullBits, (byte)i, planeSide, IInteraction.LIGHT_CLIP_EPSILON, tri.numVerts); } }
// are dangling edges that are outside the light frustum still making planes? public static SrfTriangles R_CreateVertexProgramTurboShadowVolume(RenderEntityLocal ent, SrfTriangles tri, RenderLightLocal light, SrfCullInfo cullInfo) { int i, j; SrfTriangles newTri; SilEdge sil; GlIndex[] indexes; byte[] facing; R_CalcInteractionFacing(ent, tri, light, cullInfo); if (r_useShadowProjectedCull.Bool) { R_CalcInteractionCullBits(ent, tri, light, cullInfo); } var numFaces = tri.numIndexes / 3; var numShadowingFaces = 0; facing = cullInfo.facing; // if all the triangles are inside the light frustum if (cullInfo.cullBits == LIGHT_CULL_ALL_FRONT || !r_useShadowProjectedCull.Bool) { // count the number of shadowing faces for (i = 0; i < numFaces; i++) { numShadowingFaces += facing[i]; } numShadowingFaces = numFaces - numShadowingFaces; } else { // make all triangles that are outside the light frustum "facing", so they won't cast shadows indexes = tri.indexes; byte *modifyFacing = cullInfo.facing; byte *cullBits = cullInfo.cullBits; for (j = i = 0; i < tri.numIndexes; i += 3, j++) { if (modifyFacing[j] == 0) { var i1 = indexes[i + 0]; var i2 = indexes[i + 1]; var i3 = indexes[i + 2]; if ((cullBits[i1] & cullBits[i2] & cullBits[i3]) != 0) { modifyFacing[j] = 1; } else { numShadowingFaces++; } } } } // no faces are inside the light frustum and still facing the right way if (numShadowingFaces == 0) { return(null); } // shadowVerts will be NULL on these surfaces, so the shadowVerts will be taken from the ambient surface newTri = R_AllocStaticTriSurf(); newTri.numVerts = tri.numVerts * 2; // alloc the max possible size #if USE_TRI_DATA_ALLOCATOR R_AllocStaticTriSurfIndexes(newTri, (numShadowingFaces + tri.numSilEdges) * 6); GlIndex tempIndexes = newTri.indexes; GlIndex shadowIndexes = newTri.indexes; #else GlIndex tempIndexes = new GlIndex[tri.numSilEdges * 6]; GlIndex shadowIndexes = tempIndexes; #endif // create new triangles along sil planes for (sil = tri.silEdges, i = tri.numSilEdges; i > 0; i--, sil++) { int f1 = facing[sil.p1], f2 = facing[sil.p2]; if ((f1 ^ f2) == 0) { continue; } int v1 = sil.v1 << 1, v2 = sil.v2 << 1; // set the two triangle winding orders based on facing without using a poorly-predictable branch shadowIndexes[0] = v1; shadowIndexes[1] = v2 ^ f1; shadowIndexes[2] = v2 ^ f2; shadowIndexes[3] = v1 ^ f2; shadowIndexes[4] = v1 ^ f1; shadowIndexes[5] = v2 ^ 1; shadowIndexes += 6; } int numShadowIndexes = shadowIndexes - tempIndexes; // we aren't bothering to separate front and back caps on these newTri.numIndexes = newTri.numShadowIndexesNoFrontCaps = numShadowIndexes + numShadowingFaces * 6; newTri.numShadowIndexesNoCaps = numShadowIndexes; newTri.shadowCapPlaneBits = SHADOW_CAP_INFINITE; #if USE_TRI_DATA_ALLOCATOR // decrease the size of the memory block to only store the used indexes R_ResizeStaticTriSurfIndexes(newTri, newTri.numIndexes); #else // allocate memory for the indexes R_AllocStaticTriSurfIndexes(newTri, newTri.numIndexes); // copy the indexes we created for the sil planes Simd.Memcpy(newTri.indexes, tempIndexes, numShadowIndexes * sizeof(tempIndexes[0])); #endif // these have no effect, because they extend to infinity newTri.bounds.Clear(); // put some faces on the model and some on the distant projection indexes = tri.indexes; shadowIndexes = newTri.indexes + numShadowIndexes; for (i = 0, j = 0; i < tri.numIndexes; i += 3, j++) { if (facing[j] != 0) { continue; } var i0 = indexes[i + 0] << 1; shadowIndexes[2] = i0; shadowIndexes[3] = i0 ^ 1; var i1 = indexes[i + 1] << 1; shadowIndexes[1] = i1; shadowIndexes[4] = i1 ^ 1; var i2 = indexes[i + 2] << 1; shadowIndexes[0] = i2; shadowIndexes[5] = i2 ^ 1; shadowIndexes += 6; } return(newTri); }
// Calculates two axis for the surface sutch that a point dotted against the axis will give a 0.0 to 1.0 range in S and T when inside the gui surface public static void R_SurfaceToTextureAxis(SrfTriangles tri, ref Vector3 origin, Vector3 *axis) { float *d0 = stackalloc float[5], d1 = stackalloc float[5]; var bounds = stackalloc (float x, float y)[2];
// If we resort the vertexes so all silverts come first, we can save some work here. public unsafe static LocalTrace R_LocalTrace(Vector3 start, Vector3 end, float radius, SrfTriangles tri) { int i, j; Plane[] planes = new Plane[4]; LocalTrace hit = new(); int c_testEdges, c_testPlanes, c_intersect; Vector3 startDir; byte totalOr; float radiusSqr; #if TEST_TRACE Timer trace_timer = new(); trace_timer.Start(); #endif hit.fraction = 1f; // create two planes orthogonal to each other that intersect along the trace startDir = end - start; startDir.Normalize(); startDir.NormalVectors(out planes[0].Normal, out planes[1].Normal); planes[0].d = -start * planes[0].Normal; planes[1].d = -start * planes[1].Normal; // create front and end planes so the trace is on the positive sides of both planes[2] = startDir; planes[2].d = -start * planes[2].Normal; planes[3] = -startDir; planes[3].d = -end * planes[3].Normal; // catagorize each point against the four planes var cullBits = stackalloc byte[tri.numVerts]; Simd.TracePointCull(cullBits, totalOr, radius, planes, tri.verts, tri.numVerts); // if we don't have points on both sides of both the ray planes, no intersection if (((totalOr ^ (totalOr >> 4)) & 3) != 0) /*common.Printf("nothing crossed the trace planes\n");*/ return { (hit); } // if we don't have any points between front and end, no intersection if (((totalOr ^ (totalOr >> 1)) & 4) != 0) /*common.Printf("trace didn't reach any triangles\n");*/ return { (hit); } // scan for triangles that cross both planes c_testPlanes = c_testEdges = c_intersect = 0; radiusSqr = Square(radius); startDir = end - start; if (tri.facePlanes == null || !tri.facePlanesCalculated) { R_DeriveFacePlanes(tri); } for (i = 0, j = 0; i < tri.numIndexes; i += 3, j++) { float d1, d2, f, d, edgeLengthSqr; byte triOr; Vector3 cross, edge; Vector3[] dir = new Vector3[3]; // get sidedness info for the triangle triOr = cullBits[tri.indexes[i + 0]]; triOr |= cullBits[tri.indexes[i + 1]]; triOr |= cullBits[tri.indexes[i + 2]]; // if we don't have points on both sides of both the ray planes, no intersection if (((triOr ^ (triOr >> 4)) & 3) != 0) { continue; } // if we don't have any points between front and end, no intersection if (((triOr ^ (triOr >> 1)) & 4) != 0) { continue; } c_testPlanes++; ref Plane plane = ref tri.facePlanes[j]; d1 = plane.Distance(start); d2 = plane.Distance(end); if (d1 <= d2) { continue; // comning at it from behind or parallel } if (d1 < 0f) { continue; // starts past it } if (d2 > 0f) { continue; // finishes in front of it } f = d1 / (d1 - d2); if (f < 0f) { continue; // shouldn't happen } if (f >= hit.fraction) { continue; // have already hit something closer } c_testEdges++; // find the exact point of impact with the plane var point = start + f * startDir; // see if the point is within the three edges if radius > 0 the triangle is expanded with a circle in the triangle plane dir[0] = tri.verts[tri.indexes[i + 0]].xyz - point; dir[1] = tri.verts[tri.indexes[i + 1]].xyz - point; cross = dir[0].Cross(dir[1]); d = plane.Normal * cross; if (d > 0f) { if (radiusSqr <= 0f) { continue; } edge = tri.verts[tri.indexes[i + 0]].xyz - tri.verts[tri.indexes[i + 1]].xyz; edgeLengthSqr = edge.LengthSqr; if (cross.LengthSqr > edgeLengthSqr * radiusSqr) { continue; } d = edge * dir[0]; if (d < 0f) { edge = tri.verts[tri.indexes[i + 0]].xyz - tri.verts[tri.indexes[i + 2]].xyz; d = edge * dir[0]; if (d < 0f && dir[0].LengthSqr > radiusSqr) { continue; } } else if (d > edgeLengthSqr) { edge = tri.verts[tri.indexes[i + 1]].xyz - tri.verts[tri.indexes[i + 2]].xyz; d = edge * dir[1]; if (d < 0f && dir[1].LengthSqr > radiusSqr) { continue; } } } dir[2] = tri.verts[tri.indexes[i + 2]].xyz - point; cross = dir[1].Cross(dir[2]); d = plane.Normal * cross; if (d > 0f) { if (radiusSqr <= 0f) { continue; } edge = tri.verts[tri.indexes[i + 1]].xyz - tri.verts[tri.indexes[i + 2]].xyz; edgeLengthSqr = edge.LengthSqr; if (cross.LengthSqr > edgeLengthSqr * radiusSqr) { continue; } d = edge * dir[1]; if (d < 0f) { edge = tri.verts[tri.indexes[i + 1]].xyz - tri.verts[tri.indexes[i + 0]].xyz; d = edge * dir[1]; if (d < 0f && dir[1].LengthSqr > radiusSqr) { continue; } } else if (d > edgeLengthSqr) { edge = tri.verts[tri.indexes[i + 2]].xyz - tri.verts[tri.indexes[i + 0]].xyz; d = edge * dir[2]; if (d < 0f && dir[2].LengthSqr > radiusSqr) { continue; } } } cross = dir[2].Cross(dir[0]); d = plane.Normal * cross; if (d > 0f) { if (radiusSqr <= 0f) { continue; } edge = tri.verts[tri.indexes[i + 2]].xyz - tri.verts[tri.indexes[i + 0]].xyz; edgeLengthSqr = edge.LengthSqr; if (cross.LengthSqr > edgeLengthSqr * radiusSqr) { continue; } d = edge * dir[2]; if (d < 0f) { edge = tri.verts[tri.indexes[i + 2]].xyz - tri.verts[tri.indexes[i + 1]].xyz; d = edge * dir[2]; if (d < 0f && dir[2].LengthSqr > radiusSqr) { continue; } } else if (d > edgeLengthSqr) { edge = tri.verts[tri.indexes[i + 0]].xyz - tri.verts[tri.indexes[i + 1]].xyz; d = edge * dir[0]; if (d < 0f && dir[0].LengthSqr > radiusSqr) { continue; } } } // we hit it c_intersect++; hit.fraction = f; hit.normal = plane.Normal; hit.point = point; hit.indexes[0] = tri.indexes[i]; hit.indexes[1] = tri.indexes[i + 1]; hit.indexes[2] = tri.indexes[i + 2]; }