/// <summary> /// R_DrawSkyChain /// </summary> private static void DrawSkyChain(msurface_t s) { DisableMultitexture(); // used when gl_texsort is on Drawer.Bind(_SolidSkyTexture); _SpeedScale = (float)Host.RealTime * 8; _SpeedScale -= (int)_SpeedScale & ~127; for (msurface_t fa = s; fa != null; fa = fa.texturechain) { EmitSkyPolys(fa); } GL.Enable(EnableCap.Blend); Drawer.Bind(_AlphaSkyTexture); _SpeedScale = (float)Host.RealTime * 16; _SpeedScale -= (int)_SpeedScale & ~127; for (msurface_t fa = s; fa != null; fa = fa.texturechain) { EmitSkyPolys(fa); } GL.Disable(EnableCap.Blend); }
/// <summary> /// R_RenderBrushPoly /// </summary> private static void RenderBrushPoly(msurface_t fa) { _BrushPolys++; if ((fa.flags & Surf.SURF_DRAWSKY) != 0) { // warp texture, no lightmaps EmitBothSkyLayers(fa); return; } texture_t t = TextureAnimation(fa.texinfo.texture); Drawer.Bind(t.gl_texturenum); if ((fa.flags & Surf.SURF_DRAWTURB) != 0) { // warp texture, no lightmaps EmitWaterPolys(fa); return; } if ((fa.flags & Surf.SURF_UNDERWATER) != 0) { DrawGLWaterPoly(fa.polys); } else { DrawGLPoly(fa.polys); } // add the poly to the proper lightmap chain fa.polys.chain = _LightMapPolys[fa.lightmaptexturenum]; _LightMapPolys[fa.lightmaptexturenum] = fa.polys; // check for lightmap modification bool modified = false; for (int maps = 0; maps < BspFile.MAXLIGHTMAPS && fa.styles[maps] != 255; maps++) { if (_LightStyleValue[fa.styles[maps]] != fa.cached_light[maps]) { modified = true; break; } } if (modified || fa.dlightframe == _FrameCount || // dynamic this frame fa.cached_dlight) // dynamic previously { if (_Dynamic.Value != 0) { _LightMapModified[fa.lightmaptexturenum] = true; UpdateRect(fa, ref _LightMapRectChange[fa.lightmaptexturenum]); int offset = fa.lightmaptexturenum * _LightMapBytes * BLOCK_WIDTH * BLOCK_HEIGHT; offset += fa.light_t * BLOCK_WIDTH * _LightMapBytes + fa.light_s * _LightMapBytes; BuildLightMap(fa, new ByteArraySegment(_LightMaps, offset), BLOCK_WIDTH * _LightMapBytes); } } }
/// <summary> /// GL_SubdivideSurface /// Breaks a polygon up along axial 64 unit boundaries /// so that turbulent and sky warps can be done reasonably. /// </summary> public static void SubdivideSurface(msurface_t fa) { _WarpFace = fa; // // convert edges back to a normal polygon // int numverts = 0; Vector3[] verts = new Vector3[fa.numedges + 1]; // + 1 for wrap case model_t loadmodel = Mod.Model; for (int i = 0; i < fa.numedges; i++) { int lindex = loadmodel.surfedges[fa.firstedge + i]; if (lindex > 0) { verts[numverts] = loadmodel.vertexes[loadmodel.edges[lindex].v[0]].position; } else { verts[numverts] = loadmodel.vertexes[loadmodel.edges[-lindex].v[1]].position; } numverts++; } SubdividePolygon(numverts, verts); }
private static void UpdateRect(msurface_t fa, ref glRect_t theRect) { if (fa.light_t < theRect.t) { if (theRect.h != 0) { theRect.h += (byte)(theRect.t - fa.light_t); } theRect.t = (byte)fa.light_t; } if (fa.light_s < theRect.l) { if (theRect.w != 0) { theRect.w += (byte)(theRect.l - fa.light_s); } theRect.l = (byte)fa.light_s; } int smax = (fa.extents[0] >> 4) + 1; int tmax = (fa.extents[1] >> 4) + 1; if ((theRect.w + theRect.l) < (fa.light_s + smax)) { theRect.w = (byte)((fa.light_s - theRect.l) + smax); } if ((theRect.h + theRect.t) < (fa.light_t + tmax)) { theRect.h = (byte)((fa.light_t - theRect.t) + tmax); } }
/// <summary> /// R_MirrorChain /// </summary> private static void MirrorChain(msurface_t s) { if (_IsMirror) { return; } _IsMirror = true; _MirrorPlane = s.plane; }
static void DrawTextureChains() { if (_glTexSort.Value == 0) { DisableMultitexture(); if (_SkyChain != null) { DrawSkyChain(_SkyChain); _SkyChain = null; } return; } model_t world = Client.cl.worldmodel; for (int i = 0; i < world.numtextures; i++) { texture_t t = world.textures[i]; if (t == null) { continue; } msurface_t s = t.texturechain; if (s == null) { continue; } if (i == _SkyTextureNum) { DrawSkyChain(s); } else if (i == _MirrorTextureNum && _MirrorAlpha.Value != 1.0f) { MirrorChain(s); continue; } else { if ((s.flags & Surf.SURF_DRAWTURB) != 0 && _WaterAlpha.Value != 1.0f) { continue; // draw translucent water later } for (; s != null; s = s.texturechain) { RenderBrushPoly(s); } } t.texturechain = null; } }
/// <summary> /// GL_CreateSurfaceLightmap /// </summary> static void CreateSurfaceLightmap(msurface_t surf) { if ((surf.flags & (Surf.SURF_DRAWSKY | Surf.SURF_DRAWTURB)) != 0) { return; } int smax = (surf.extents[0] >> 4) + 1; int tmax = (surf.extents[1] >> 4) + 1; surf.lightmaptexturenum = AllocBlock(smax, tmax, ref surf.light_s, ref surf.light_t); int offset = surf.lightmaptexturenum * _LightMapBytes * BLOCK_WIDTH * BLOCK_HEIGHT; offset += (surf.light_t * BLOCK_WIDTH + surf.light_s) * _LightMapBytes; BuildLightMap(surf, new ByteArraySegment(_LightMaps, offset), BLOCK_WIDTH * _LightMapBytes); }
/// <summary> /// EmitBothSkyLayers /// Does a sky warp on the pre-fragmented glpoly_t chain /// This will be called for brushmodels, the world /// will have them chained together. /// </summary> private static void EmitBothSkyLayers(msurface_t fa) { DisableMultitexture(); Drawer.Bind(_SolidSkyTexture); _SpeedScale = (float)Host.RealTime * 8; _SpeedScale -= (int)_SpeedScale & ~127; EmitSkyPolys(fa); GL.Enable(EnableCap.Blend); Drawer.Bind(_AlphaSkyTexture); _SpeedScale = (float)Host.RealTime * 16; _SpeedScale -= (int)_SpeedScale & ~127; EmitSkyPolys(fa); GL.Disable(EnableCap.Blend); }
/// <summary> /// R_RenderDynamicLightmaps /// Multitexture /// </summary> static void RenderDynamicLightmaps(msurface_t fa) { _BrushPolys++; if ((fa.flags & (Surf.SURF_DRAWSKY | Surf.SURF_DRAWTURB)) != 0) { return; } fa.polys.chain = _LightMapPolys[fa.lightmaptexturenum]; _LightMapPolys[fa.lightmaptexturenum] = fa.polys; // check for lightmap modification bool flag = false; for (int maps = 0; maps < BspFile.MAXLIGHTMAPS && fa.styles[maps] != 255; maps++) { if (_LightStyleValue[fa.styles[maps]] != fa.cached_light[maps]) { flag = true; break; } } if (flag || fa.dlightframe == _FrameCount || // dynamic this frame fa.cached_dlight) // dynamic previously { if (_Dynamic.Value != 0) { _LightMapModified[fa.lightmaptexturenum] = true; UpdateRect(fa, ref _LightMapRectChange[fa.lightmaptexturenum]); int offset = fa.lightmaptexturenum * _LightMapBytes * BLOCK_WIDTH * BLOCK_HEIGHT + fa.light_t * BLOCK_WIDTH * _LightMapBytes + fa.light_s * _LightMapBytes; BuildLightMap(fa, new ByteArraySegment(_LightMaps, offset), BLOCK_WIDTH * _LightMapBytes); } } }
/// <summary> /// EmitWaterPolys /// Does a water warp on the pre-fragmented glpoly_t chain /// </summary> static void EmitWaterPolys(msurface_t fa) { for (glpoly_t p = fa.polys; p != null; p = p.next) { GL.Begin(PrimitiveType.Polygon); for (int i = 0; i < p.numverts; i++) { float[] v = p.verts[i]; float os = v[3]; float ot = v[4]; float s = os + _TurbSin[(int)((ot * 0.125 + Host.RealTime) * TURBSCALE) & 255]; s *= (1.0f / 64); float t = ot + _TurbSin[(int)((os * 0.125 + Host.RealTime) * TURBSCALE) & 255]; t *= (1.0f / 64); GL.TexCoord2(s, t); GL.Vertex3(v); } GL.End(); } }
/// <summary> /// EmitSkyPolys /// </summary> static void EmitSkyPolys(msurface_t fa) { for (glpoly_t p = fa.polys; p != null; p = p.next) { GL.Begin(PrimitiveType.Polygon); for (int i = 0; i < p.numverts; i++) { float[] v = p.verts[i]; Vector3 dir = new Vector3(v[0] - Render.Origin.X, v[1] - Render.Origin.Y, v[2] - Render.Origin.Z); dir.Z *= 3; // flatten the sphere dir.Normalize(); dir *= 6 * 63; float s = (_SpeedScale + dir.X) / 128.0f; float t = (_SpeedScale + dir.Y) / 128.0f; GL.TexCoord2(s, t); GL.Vertex3(v); } GL.End(); } }
/// <summary> /// R_MarkLights /// </summary> static void MarkLights(dlight_t light, int bit, mnodebase_t node) { if (node.contents < 0) { return; } mnode_t n = (mnode_t)node; mplane_t splitplane = n.plane; float dist = Vector3.Dot(light.origin, splitplane.normal) - splitplane.dist; if (dist > light.radius) { MarkLights(light, bit, n.children[0]); return; } if (dist < -light.radius) { MarkLights(light, bit, n.children[1]); return; } // mark the polygons for (int i = 0; i < n.numsurfaces; i++) { msurface_t surf = Client.cl.worldmodel.surfaces[n.firstsurface + i]; if (surf.dlightframe != _DlightFrameCount) { surf.dlightbits = 0; surf.dlightframe = _DlightFrameCount; } surf.dlightbits |= bit; } MarkLights(light, bit, n.children[0]); MarkLights(light, bit, n.children[1]); }
/// <summary> /// R_DrawSequentialPoly /// Systems that have fast state and texture changes can /// just do everything as it passes with no need to sort /// </summary> static void DrawSequentialPoly(msurface_t s) { // // normal lightmaped poly // if ((s.flags & (Surf.SURF_DRAWSKY | Surf.SURF_DRAWTURB | Surf.SURF_UNDERWATER)) == 0) { RenderDynamicLightmaps(s); glpoly_t p = s.polys; texture_t t = TextureAnimation(s.texinfo.texture); if (Vid.glMTexable) { // Binds world to texture env 0 Drawer.SelectTexture(MTexTarget.TEXTURE0_SGIS); Drawer.Bind(t.gl_texturenum); GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Replace); // Binds lightmap to texenv 1 EnableMultitexture(); // Same as SelectTexture (TEXTURE1) Drawer.Bind(_LightMapTextures + s.lightmaptexturenum); int i = s.lightmaptexturenum; if (_LightMapModified[i]) { CommitLightmap(i); } GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Blend); GL.Begin(PrimitiveType.Polygon); for (i = 0; i < p.numverts; i++) { float[] v = p.verts[i]; GL.MultiTexCoord2(TextureUnit.Texture0, v[3], v[4]); GL.MultiTexCoord2(TextureUnit.Texture1, v[5], v[6]); GL.Vertex3(v); } GL.End(); return; } else { Drawer.Bind(t.gl_texturenum); GL.Begin(PrimitiveType.Polygon); for (int i = 0; i < p.numverts; i++) { float[] v = p.verts[i]; GL.TexCoord2(v[3], v[4]); GL.Vertex3(v); } GL.End(); Drawer.Bind(_LightMapTextures + s.lightmaptexturenum); GL.Enable(EnableCap.Blend); GL.Begin(PrimitiveType.Polygon); for (int i = 0; i < p.numverts; i++) { float[] v = p.verts[i]; GL.TexCoord2(v[5], v[6]); GL.Vertex3(v); } GL.End(); GL.Disable(EnableCap.Blend); } return; } // // subdivided water surface warp // if ((s.flags & Surf.SURF_DRAWTURB) != 0) { DisableMultitexture(); Drawer.Bind(s.texinfo.texture.gl_texturenum); EmitWaterPolys(s); return; } // // subdivided sky warp // if ((s.flags & Surf.SURF_DRAWSKY) != 0) { DisableMultitexture(); Drawer.Bind(_SolidSkyTexture); _SpeedScale = (float)Host.RealTime * 8; _SpeedScale -= (int)_SpeedScale & ~127; EmitSkyPolys(s); GL.Enable(EnableCap.Blend); Drawer.Bind(_AlphaSkyTexture); _SpeedScale = (float)Host.RealTime * 16; _SpeedScale -= (int)_SpeedScale & ~127; EmitSkyPolys(s); GL.Disable(EnableCap.Blend); return; } // // underwater warped with lightmap // RenderDynamicLightmaps(s); if (Vid.glMTexable) { texture_t t = TextureAnimation(s.texinfo.texture); Drawer.SelectTexture(MTexTarget.TEXTURE0_SGIS); Drawer.Bind(t.gl_texturenum); GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Replace); EnableMultitexture(); Drawer.Bind(_LightMapTextures + s.lightmaptexturenum); int i = s.lightmaptexturenum; if (_LightMapModified[i]) { CommitLightmap(i); } GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Blend); GL.Begin(PrimitiveType.TriangleFan); glpoly_t p = s.polys; float[] nv = new float[3]; for (i = 0; i < p.numverts; i++) { float[] v = p.verts[i]; GL.MultiTexCoord2(TextureUnit.Texture0, v[3], v[4]); GL.MultiTexCoord2(TextureUnit.Texture1, v[5], v[6]); nv[0] = (float)(v[0] + 8 * Math.Sin(v[1] * 0.05 + Host.RealTime) * Math.Sin(v[2] * 0.05 + Host.RealTime)); nv[1] = (float)(v[1] + 8 * Math.Sin(v[0] * 0.05 + Host.RealTime) * Math.Sin(v[2] * 0.05 + Host.RealTime)); nv[2] = v[2]; GL.Vertex3(nv); } GL.End(); } else { glpoly_t p = s.polys; texture_t t = TextureAnimation(s.texinfo.texture); Drawer.Bind(t.gl_texturenum); DrawGLWaterPoly(p); Drawer.Bind(_LightMapTextures + s.lightmaptexturenum); GL.Enable(EnableCap.Blend); DrawGLWaterPolyLightmap(p); GL.Disable(EnableCap.Blend); } }
/// <summary> /// BuildSurfaceDisplayList /// </summary> static void BuildSurfaceDisplayList(msurface_t fa) { // reconstruct the polygon medge_t[] pedges = _CurrentModel.edges; int lnumverts = fa.numedges; // // draw texture // glpoly_t poly = new glpoly_t(); poly.AllocVerts(lnumverts); poly.next = fa.polys; poly.flags = fa.flags; fa.polys = poly; ushort[] r_pedge_v; Vector3 vec; for (int i = 0; i < lnumverts; i++) { int lindex = _CurrentModel.surfedges[fa.firstedge + i]; if (lindex > 0) { r_pedge_v = pedges[lindex].v; vec = _CurrentVertBase[r_pedge_v[0]].position; } else { r_pedge_v = pedges[-lindex].v; vec = _CurrentVertBase[r_pedge_v[1]].position; } float s = Mathlib.DotProduct(ref vec, ref fa.texinfo.vecs[0]) + fa.texinfo.vecs[0].W; s /= fa.texinfo.texture.width; float t = Mathlib.DotProduct(ref vec, ref fa.texinfo.vecs[1]) + fa.texinfo.vecs[1].W; t /= fa.texinfo.texture.height; poly.verts[i][0] = vec.X; poly.verts[i][1] = vec.Y; poly.verts[i][2] = vec.Z; poly.verts[i][3] = s; poly.verts[i][4] = t; // // lightmap texture coordinates // s = Mathlib.DotProduct(ref vec, ref fa.texinfo.vecs[0]) + fa.texinfo.vecs[0].W; s -= fa.texturemins[0]; s += fa.light_s * 16; s += 8; s /= BLOCK_WIDTH * 16; t = Mathlib.DotProduct(ref vec, ref fa.texinfo.vecs[1]) + fa.texinfo.vecs[1].W; t -= fa.texturemins[1]; t += fa.light_t * 16; t += 8; t /= BLOCK_HEIGHT * 16; poly.verts[i][5] = s; poly.verts[i][6] = t; } // // remove co-linear points - Ed // if (_glKeepTJunctions.Value == 0 && (fa.flags & Surf.SURF_UNDERWATER) == 0) { for (int i = 0; i < lnumverts; ++i) { if (IsCollinear(poly.verts[(i + lnumverts - 1) % lnumverts], poly.verts[i], poly.verts[(i + 1) % lnumverts])) { int j; for (j = i + 1; j < lnumverts; ++j) { //int k; for (int k = 0; k < Mod.VERTEXSIZE; ++k) { poly.verts[j - 1][k] = poly.verts[j][k]; } } --lnumverts; ++_ColinElim; // retry next vertex next time, which is now current vertex --i; } } } poly.numverts = lnumverts; }
/// <summary> /// R_BuildLightMap /// Combine and scale multiple lightmaps into the 8.8 format in blocklights /// </summary> static void BuildLightMap(msurface_t surf, ByteArraySegment dest, int stride) { surf.cached_dlight = (surf.dlightframe == _FrameCount); int smax = (surf.extents[0] >> 4) + 1; int tmax = (surf.extents[1] >> 4) + 1; int size = smax * tmax; int srcOffset = surf.sampleofs; byte[] lightmap = surf.sample_base;// surf.samples; // set to full bright if no light data if (_FullBright.Value != 0 || Client.cl.worldmodel.lightdata == null) { for (int i = 0; i < size; i++) { _BlockLights[i] = 255 * 256; } } else { // clear to no light for (int i = 0; i < size; i++) { _BlockLights[i] = 0; } // add all the lightmaps if (lightmap != null) { for (int maps = 0; maps < BspFile.MAXLIGHTMAPS && surf.styles[maps] != 255; maps++) { int scale = _LightStyleValue[surf.styles[maps]]; surf.cached_light[maps] = scale; // 8.8 fraction for (int i = 0; i < size; i++) { _BlockLights[i] += (uint)(lightmap[srcOffset + i] * scale); } srcOffset += size; // lightmap += size; // skip to next lightmap } } // add all the dynamic lights if (surf.dlightframe == _FrameCount) { AddDynamicLights(surf); } } // bound, invert, and shift //store: int blOffset = 0; int destOffset = dest.StartIndex; byte[] data = dest.Data; switch (Drawer.LightMapFormat) { case PixelFormat.Rgba: stride -= (smax << 2); for (int i = 0; i < tmax; i++, destOffset += stride) // dest += stride { for (int j = 0; j < smax; j++) { uint t = _BlockLights[blOffset++]; // *bl++; t >>= 7; if (t > 255) { t = 255; } data[destOffset + 3] = (byte)(255 - t); //dest[3] = 255 - t; destOffset += 4; } } break; case PixelFormat.Alpha: case PixelFormat.Luminance: //case GL_INTENSITY: for (int i = 0; i < tmax; i++, destOffset += stride) { for (int j = 0; j < smax; j++) { uint t = _BlockLights[blOffset++]; // *bl++; t >>= 7; if (t > 255) { t = 255; } data[destOffset + j] = (byte)(255 - t); // dest[j] = 255 - t; } } break; default: Sys.Error("Bad lightmap format"); break; } }
/// <summary> /// R_AddDynamicLights /// </summary> static void AddDynamicLights(msurface_t surf) { int smax = (surf.extents[0] >> 4) + 1; int tmax = (surf.extents[1] >> 4) + 1; mtexinfo_t tex = surf.texinfo; dlight_t[] dlights = Client.DLights; for (int lnum = 0; lnum < Client.MAX_DLIGHTS; lnum++) { if ((surf.dlightbits & (1 << lnum)) == 0) { continue; // not lit by this light } float rad = dlights[lnum].radius; float dist = Vector3.Dot(dlights[lnum].origin, surf.plane.normal) - surf.plane.dist; rad -= Math.Abs(dist); float minlight = dlights[lnum].minlight; if (rad < minlight) { continue; } minlight = rad - minlight; Vector3 impact = dlights[lnum].origin - surf.plane.normal * dist; float local0 = Vector3.Dot(impact, tex.vecs[0].Xyz) + tex.vecs[0].W; float local1 = Vector3.Dot(impact, tex.vecs[1].Xyz) + tex.vecs[1].W; local0 -= surf.texturemins[0]; local1 -= surf.texturemins[1]; for (int t = 0; t < tmax; t++) { int td = (int)(local1 - t * 16); if (td < 0) { td = -td; } for (int s = 0; s < smax; s++) { int sd = (int)(local0 - s * 16); if (sd < 0) { sd = -sd; } if (sd > td) { dist = sd + (td >> 1); } else { dist = td + (sd >> 1); } if (dist < minlight) { _BlockLights[t * smax + s] += (uint)((rad - dist) * 256); } } } } }
/// <summary> /// R_DrawWaterSurfaces /// </summary> static void DrawWaterSurfaces() { if (_WaterAlpha.Value == 1.0f && _glTexSort.Value != 0) { return; } // // go back to the world matrix // GL.LoadMatrix(ref _WorldMatrix); if (_WaterAlpha.Value < 1.0) { GL.Enable(EnableCap.Blend); GL.Color4(1, 1, 1, _WaterAlpha.Value); GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Modulate); } if (_glTexSort.Value == 0) { if (_WaterChain == null) { return; } for (msurface_t s = _WaterChain; s != null; s = s.texturechain) { Drawer.Bind(s.texinfo.texture.gl_texturenum); EmitWaterPolys(s); } _WaterChain = null; } else { for (int i = 0; i < Client.cl.worldmodel.numtextures; i++) { texture_t t = Client.cl.worldmodel.textures[i]; if (t == null) { continue; } msurface_t s = t.texturechain; if (s == null) { continue; } if ((s.flags & Surf.SURF_DRAWTURB) == 0) { continue; } // set modulate mode explicitly Drawer.Bind(t.gl_texturenum); for (; s != null; s = s.texturechain) { EmitWaterPolys(s); } t.texturechain = null; } } if (_WaterAlpha.Value < 1.0) { GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Replace); GL.Color4(1f, 1, 1, 1); GL.Disable(EnableCap.Blend); } }
/// <summary> /// R_RecursiveWorldNode /// </summary> static void RecursiveWorldNode(mnodebase_t node) { if (node.contents == Contents.CONTENTS_SOLID) { return; // solid } if (node.visframe != _VisFrameCount) { return; } if (CullBox(ref node.mins, ref node.maxs)) { return; } int c; // if a leaf node, draw stuff if (node.contents < 0) { mleaf_t pleaf = (mleaf_t)node; msurface_t[] marks = pleaf.marksurfaces; int mark = pleaf.firstmarksurface; c = pleaf.nummarksurfaces; if (c != 0) { do { marks[mark].visframe = _FrameCount; mark++; } while (--c != 0); } // deal with model fragments in this leaf if (pleaf.efrags != null) { StoreEfrags(pleaf.efrags); } return; } // node is just a decision point, so go down the apropriate sides mnode_t n = (mnode_t)node; // find which side of the node we are on mplane_t plane = n.plane; double dot; switch (plane.type) { case Planes.PLANE_X: dot = _ModelOrg.X - plane.dist; break; case Planes.PLANE_Y: dot = _ModelOrg.Y - plane.dist; break; case Planes.PLANE_Z: dot = _ModelOrg.Z - plane.dist; break; default: dot = Vector3.Dot(_ModelOrg, plane.normal) - plane.dist; break; } int side = (dot >= 0 ? 0 : 1); // recurse down the children, front side first RecursiveWorldNode(n.children[side]); // draw stuff c = n.numsurfaces; if (c != 0) { msurface_t[] surf = Client.cl.worldmodel.surfaces; int offset = n.firstsurface; if (dot < 0 - QDef.BACKFACE_EPSILON) { side = Surf.SURF_PLANEBACK; } else if (dot > QDef.BACKFACE_EPSILON) { side = 0; } for (; c != 0; c--, offset++) { if (surf[offset].visframe != _FrameCount) { continue; } // don't backface underwater surfaces, because they warp if ((surf[offset].flags & Surf.SURF_UNDERWATER) == 0 && ((dot < 0) ^ ((surf[offset].flags & Surf.SURF_PLANEBACK) != 0))) { continue; // wrong side } // if sorting by texture, just store it out if (_glTexSort.Value != 0) { if (!_IsMirror || surf[offset].texinfo.texture != Client.cl.worldmodel.textures[_MirrorTextureNum]) { surf[offset].texturechain = surf[offset].texinfo.texture.texturechain; surf[offset].texinfo.texture.texturechain = surf[offset]; } } else if ((surf[offset].flags & Surf.SURF_DRAWSKY) != 0) { surf[offset].texturechain = _SkyChain; _SkyChain = surf[offset]; } else if ((surf[offset].flags & Surf.SURF_DRAWTURB) != 0) { surf[offset].texturechain = _WaterChain; _WaterChain = surf[offset]; } else { DrawSequentialPoly(surf[offset]); } } } // recurse down the back side RecursiveWorldNode(n.children[side == 0 ? 1 : 0]); }