/// <summary> /// CreateSurfaceLightmap /// --------------------- /// Creates a lightmap surface /// </summary> public void CreateSurfaceLightmap(CModel.SModel _SModel, ref CModel.SMSurface surf) { int smax; int tmax; int bytepos; if ( (surf.flags & CModel.EMSurface.SURF_DRAWSKY) == CModel.EMSurface.SURF_DRAWSKY | (surf.flags & CModel.EMSurface.SURF_DRAWTURB) == CModel.EMSurface.SURF_DRAWTURB ) { return; } smax = (surf.extents[0] >> 4) + 1; tmax = (surf.extents[1] >> 4) + 1; if (LM_AllocBlock(smax, tmax, ref surf.light_s, ref surf.light_t) == false) { LM_UploadBlock(false); LM_InitBlock(); if (LM_AllocBlock(smax, tmax, ref surf.light_s, ref surf.light_t) == false) { CMain.Error(CMain.EErrorParm.ERR_FATAL, "Consecutive calls to LM_AllocBlock(" + smax + "," + tmax + ") failed\n"); } } surf.lightmaptexturenum = CProgram.gQ2Game.gCMain.gCImage.current_lightmap_texture; bytepos = (surf.light_t * BLOCK_WIDTH + surf.light_s) * LIGHTMAP_BYTES; CProgram.gQ2Game.gCMain.gCLight.SetCacheState(ref surf); CProgram.gQ2Game.gCMain.gCLight.BuildLightMap(_SModel, ref surf, bytepos, ref lms.lightmap_buffer, BLOCK_WIDTH * LIGHTMAP_BYTES); }
/// <summary> /// SubdivideSurface /// ---------------- /// Breaks a polygon up along axial 64 unit boundaries /// so that turbulent and sky warps can be done reasonably. /// </summary> public void SubdivideSurface(ref CModel.SMSurface surf) { float[] verts; int numverts; List <CModel.SGLPoly> polys; if ((surf.flags & CModel.EMSurface.SURF_DRAWTURB) != CModel.EMSurface.SURF_DRAWTURB) { return; } verts = new float[64 * 3]; numverts = surf.polys[0].numverts; for (int i = 0; i < surf.polys[0].numverts; i++) { verts[(i * 3) + 0] = surf.polys[0].verts[i].vertex.Position.X; verts[(i * 3) + 1] = surf.polys[0].verts[i].vertex.Position.Y; verts[(i * 3) + 2] = surf.polys[0].verts[i].vertex.Position.Z; } surf.polys = null; polys = new List <CModel.SGLPoly>(); SubdividePolygon(surf, ref polys, numverts, verts); surf.polys = polys.ToArray(); polys.Clear(); }
public void SetCacheState(ref CModel.SMSurface surf) { surf.cached_light = new float[CQ2BSP.MAXLIGHTMAPS]; for (int maps = 0; maps < CQ2BSP.MAXLIGHTMAPS && surf.styles[maps] != 255; maps++) { surf.cached_light[maps] = CClient.cl.RefDef.lightstyles[surf.styles[maps]].white; } }
/// <summary> /// BuildSurfaceIndex /// ------------------ /// Convert the surface's vertex format from triangle fan to triangle list /// This is used to speed up surface warping by using an index buffer /// </summary> public int[] BuildSurfaceIndex(CModel.SMSurface surf) { List <int> ib = new List <int>(); ib.Clear(); for (int i = 0; i < surf.polys.Length; i++) { for (int j = 2; j < surf.polys[i].verts.Length; j++) { ib.Add(surf.polys[i].verts[0].offset); ib.Add(surf.polys[i].verts[j - 1].offset); ib.Add(surf.polys[i].verts[j].offset); } } return(ib.ToArray()); }
/// <summary> /// BuildSurfaceIndex /// ------------------ /// Convert the surface's vertex format from triangle fan to triangle list /// This is used to speed up surface warping by using an index buffer /// </summary> public void BuildSurfaceIndex(ref CModel.SMSurface surf) { List <int> ib; //if ((surf.flags & CModel.EMSurface.SURF_DRAWTURB) != CModel.EMSurface.SURF_DRAWTURB) // return; ib = new List <int>(); ib.Clear(); for (int i = 0; i < surf.polys.Length; i++) { for (int j = 2; j < surf.polys[i].verts.Length; j++) { ib.Add(surf.polys[i].verts[0].offset); ib.Add(surf.polys[i].verts[j - 1].offset); ib.Add(surf.polys[i].verts[j].offset); } } surf.ibData = ib.ToArray(); // setup the index buffer if (CProgram.gQ2Game.gGraphicsDevice.GraphicsProfile == GraphicsProfile.HiDef) { surf.ibSurface = new IndexBuffer(CProgram.gQ2Game.gGraphicsDevice, IndexElementSize.ThirtyTwoBits /*typeof(int)*/, surf.ibData.Length, BufferUsage.WriteOnly); surf.ibSurface.SetData(surf.ibData); } else { short[] ibData16 = new short[surf.ibData.Length]; for (int i = 0; i < surf.ibData.Length; i++) { ibData16[i] = (short)surf.ibData[i]; } surf.ibSurface = new IndexBuffer(CProgram.gQ2Game.gGraphicsDevice, IndexElementSize.SixteenBits, ibData16.Length, BufferUsage.WriteOnly); surf.ibSurface.SetData(ibData16); } //surf.ibSurface.SetData(surf.ibData); }
/// <summary> /// R_BuildLightMap /// --------------- /// Combine and scale multiple lightmaps into the floating format in blocklights /// </summary> public void BuildLightMap(CModel.SModel _SModel, ref CModel.SMSurface surf, int dest_pos, ref byte[] dest, int stride) { int smax, tmax; int r, g, b, a, max; int size; int lightmap; float[] scale; int nummaps; float[] bl; CLocal.SLightStyle style; int bl_pos; float modulate = 1.0f; if ( (_SModel.texinfo[surf.texinfo].flags & CQ2BSP.ESurface.SURF_SKY) == CQ2BSP.ESurface.SURF_SKY | (_SModel.texinfo[surf.texinfo].flags & CQ2BSP.ESurface.SURF_TRANS33) == CQ2BSP.ESurface.SURF_TRANS33 | (_SModel.texinfo[surf.texinfo].flags & CQ2BSP.ESurface.SURF_TRANS66) == CQ2BSP.ESurface.SURF_TRANS66 | (_SModel.texinfo[surf.texinfo].flags & CQ2BSP.ESurface.SURF_WARP) == CQ2BSP.ESurface.SURF_WARP ) { CMain.Error(CMain.EErrorParm.ERR_WARNING, "BuildLightMap called for non-lit surface"); } smax = (surf.extents[0] >> 4) + 1; tmax = (surf.extents[1] >> 4) + 1; size = smax * tmax; if (size > ((sizeof(float) * s_blocklights.Length) >> 4)) { CMain.Error(CMain.EErrorParm.ERR_WARNING, "Bad s_blocklights size"); } // set to full bright if no light data if (surf.samples == -1) { for (int i = 0; i < size * 3; i++) { s_blocklights[i] = 255; } for (int maps = 0; maps < CQ2BSP.MAXLIGHTMAPS && surf.styles[maps] != 255; maps++) { style = CClient.cl.RefDef.lightstyles[surf.styles[maps]]; } goto store; } // count the # of maps for (nummaps = 0; nummaps < CQ2BSP.MAXLIGHTMAPS && surf.styles[nummaps] != 255; nummaps++) { ; } lightmap = surf.samples; // add all the lightmaps if (nummaps == 1) { scale = new float[3]; for (int maps = 0; maps < CQ2BSP.MAXLIGHTMAPS && surf.styles[maps] != 255; maps++) { bl = s_blocklights; bl_pos = 0; for (int i = 0; i < 3; i++) { scale[i] = modulate * CClient.cl.RefDef.lightstyles[surf.styles[maps]].rgb[i]; } if (scale[0] == 1.0f && scale[1] == 1.0f && scale[2] == 1.0f) { for (int i = 0; i < size; i++, bl_pos += 3) { bl[bl_pos + 0] = _SModel.lightdata[lightmap + (i * 3) + 0]; bl[bl_pos + 1] = _SModel.lightdata[lightmap + (i * 3) + 1]; bl[bl_pos + 2] = _SModel.lightdata[lightmap + (i * 3) + 2]; } } else { for (int i = 0; i < size; i++, bl_pos += 3) { bl[bl_pos + 0] = _SModel.lightdata[lightmap + (i * 3) + 0] * scale[0]; bl[bl_pos + 1] = _SModel.lightdata[lightmap + (i * 3) + 1] * scale[1]; bl[bl_pos + 2] = _SModel.lightdata[lightmap + (i * 3) + 2] * scale[2]; } } // skip to next lightmap lightmap += size * 3; } } else { scale = new float[3]; for (int i = 0; i < (sizeof(float) * size * 3); i++) { s_blocklights[i] = 0; } for (int maps = 0; maps < CQ2BSP.MAXLIGHTMAPS && surf.styles[maps] != 255; maps++) { bl = s_blocklights; bl_pos = 0; for (int i = 0; i < 3; i++) { scale[i] = modulate * CClient.cl.RefDef.lightstyles[surf.styles[maps]].rgb[i]; } if (scale[0] == 1.0f && scale[1] == 1.0f && scale[2] == 1.0f) { for (int i = 0; i < size; i++, bl_pos += 3) { bl[bl_pos + 0] += _SModel.lightdata[lightmap + (i * 3) + 0]; bl[bl_pos + 1] += _SModel.lightdata[lightmap + (i * 3) + 1]; bl[bl_pos + 2] += _SModel.lightdata[lightmap + (i * 3) + 2]; } } else { for (int i = 0; i < size; i++, bl_pos += 3) { bl[bl_pos + 0] += _SModel.lightdata[lightmap + (i * 3) + 0] * scale[0]; bl[bl_pos + 1] += _SModel.lightdata[lightmap + (i * 3) + 1] * scale[1]; bl[bl_pos + 2] += _SModel.lightdata[lightmap + (i * 3) + 2] * scale[2]; } } // skip to next lightmap lightmap += size * 3; } } // add all the dynamic lights //if (surf.dlightframe == CMain.r_framecount) // R_AddDynamicLights(surf); // put into texture format store: stride -= (smax << 2); bl = s_blocklights; bl_pos = 0; for (int i = 0; i < tmax; i++, dest_pos += stride) { for (int j = 0; j < smax; j++) { r = Convert.ToInt32(bl[bl_pos + 0]); g = Convert.ToInt32(bl[bl_pos + 1]); b = Convert.ToInt32(bl[bl_pos + 2]); // catch negative lights if (r < 0) { r = 0; } if (g < 0) { g = 0; } if (b < 0) { b = 0; } // determine the brightest of the three color components if (r > g) { max = r; } else { max = g; } if (b > max) { max = b; } // alpha is ONLY used for the mono lightmap case. For this reason // we set it to the brightest of the color components so that // things don't get too dim. a = max; // rescale all the color components if the intensity of the greatest // channel exceeds 1.0 if (max > 255) { float t = 255.0f / max; r = (int)(r * t); g = (int)(g * t); b = (int)(b * t); a = (int)(a * t); } dest[dest_pos + 0] = (byte)r; dest[dest_pos + 1] = (byte)g; dest[dest_pos + 2] = (byte)b; dest[dest_pos + 3] = (byte)a; bl_pos += 3; dest_pos += 4; } } }
/// <summary> /// SubdividePolygon /// ---------------- /// Breaks a polygon up along axial 64 unit boundaries /// so that turbulent and sky warps can be done reasonably. /// </summary> public void SubdividePolygon(CModel.SMSurface surf, ref List <CModel.SGLPoly> polys, int numverts, float[] verts) { float[] mins, maxs, dist; float m; float frac; int i, j, k, v; float[] Front, Back; int f, b; CModel.SGLPoly poly; Vector3 total; float total_s; float total_t; mins = new float[3]; maxs = new float[3]; dist = new float[64]; Front = new float[64 * 3]; Back = new float[64 * 3]; if (numverts > 60) { CMain.Error(CMain.EErrorParm.ERR_WARNING, "(error) numverts = " + numverts); } // Bind mins and maxs BoundPoly(numverts, verts, ref mins, ref maxs); for (i = 0; i < 3; i++) { m = (mins[i] + maxs[i]) * 0.5f; m = SUBDIVIDE_SIZE * (float)Math.Floor(m / (float)SUBDIVIDE_SIZE + 0.5f); if (maxs[i] - m < 8) { continue; } if (m - mins[i] < 8) { continue; } // cut it v = i; for (j = 0; j < numverts; j++, v += 3) { dist[j] = verts[v] - m; } // wrap cases dist[j] = dist[0]; v -= i; verts[v + 0] = verts[0 + 0]; verts[v + 1] = verts[0 + 1]; verts[v + 2] = verts[0 + 2]; f = 0; b = 0; v = 0; for (j = 0; j < numverts; j++, v += 3) { if (dist[j] >= 0) { Front[(f * 3) + 0] = verts[v + 0]; Front[(f * 3) + 1] = verts[v + 1]; Front[(f * 3) + 2] = verts[v + 2]; f++; } if (dist[j] <= 0) { Back[(b * 3) + 0] = verts[v + 0]; Back[(b * 3) + 1] = verts[v + 1]; Back[(b * 3) + 2] = verts[v + 2]; b++; } if (dist[j] == 0 || dist[j + 1] == 0) { continue; } if ((dist[j] > 0) != (dist[j + 1] > 0)) { // clip point frac = dist[j] / (dist[j] - dist[j + 1]); for (k = 0; k < 3; k++) { Front[(f * 3) + k] = Back[(b * 3) + k] = verts[v + k] + frac * (verts[3 + v + k] - verts[v + k]); } f++; b++; } } SubdividePolygon(surf, ref polys, f, Front); SubdividePolygon(surf, ref polys, b, Back); return; } // add a point in the center to help keep warp valid poly.next = 0; poly.chain = 0; poly.numverts = numverts + 2; poly.verts = new CModel.SPolyVerts[poly.numverts]; total = Vector3.Zero; total_s = 0.0f; total_t = 0.0f; v = 0; for (i = 0; i < numverts; i++, v += 3) { float s; float t; Vector3 vec0; Vector3 vec1; poly.verts[i + 1].vertex.Position.X = verts[v + 0]; poly.verts[i + 1].vertex.Position.Y = verts[v + 1]; poly.verts[i + 1].vertex.Position.Z = verts[v + 2]; vec0.X = CQ2BSP.SWorldData.texinfo[surf.texinfo].vecs[0].X; vec0.Y = CQ2BSP.SWorldData.texinfo[surf.texinfo].vecs[0].Y; vec0.Z = CQ2BSP.SWorldData.texinfo[surf.texinfo].vecs[0].Z; vec1.X = CQ2BSP.SWorldData.texinfo[surf.texinfo].vecs[1].X; vec1.Y = CQ2BSP.SWorldData.texinfo[surf.texinfo].vecs[1].Y; vec1.Z = CQ2BSP.SWorldData.texinfo[surf.texinfo].vecs[1].Z; s = Vector3.Dot(poly.verts[i + 1].vertex.Position, vec0); t = Vector3.Dot(poly.verts[i + 1].vertex.Position, vec1); total_s += s; total_t += t; total.X += verts[v + 0]; total.Y += verts[v + 1]; total.Z += verts[v + 2]; poly.verts[i + 1].vertex.TextureCoordinate.X = s; poly.verts[i + 1].vertex.TextureCoordinate.Y = t; poly.verts[i + 1].vertex.LightmapCoordinate.X = 0.0f; poly.verts[i + 1].vertex.LightmapCoordinate.Y = 0.0f; poly.verts[i + 1].vertex.Normal.X = 0.0f; poly.verts[i + 1].vertex.Normal.Y = 0.0f; poly.verts[i + 1].vertex.Normal.Z = 0.0f; } CShared.VectorScale(total, (1.0f / numverts), ref poly.verts[0].vertex.Position); poly.verts[0].vertex.TextureCoordinate.X = total_s / numverts; poly.verts[0].vertex.TextureCoordinate.Y = total_t / numverts; // copy first vertex to last poly.verts[i + 1] = poly.verts[1]; // insert centered point at first index polys.Insert(0, poly); }
public void BuildPolygonFromSurface(ref CModel.SMSurface surf) { // most parts of the BuildWorld() function in CMain could be done here // if so, the polygons needs to be subdivided first most likely // see the function calling this function }