public static void FindFacesAndGroups(Map map, out List <LMFace> faces, out List <LightmapGroup> lmGroups) { faces = new List <LMFace>(); lmGroups = new List <LightmapGroup>(); foreach (Solid solid in map.WorldSpawn.Find(x => x is Solid).OfType <Solid>()) { foreach (Face tface in solid.Faces) { tface.Vertices.ForEach(v => { v.LMU = -500.0f; v.LMV = -500.0f; }); tface.UpdateBoundingBox(); if (tface.Texture?.Texture == null) { continue; } if (tface.Texture.Name.ToLowerInvariant() == "tooltextures/invisible_collision") { continue; } if (tface.Texture.Name.ToLowerInvariant() == "tooltextures/remove_face") { continue; } if (tface.Texture.Name.ToLowerInvariant() == "tooltextures/block_light") { continue; } if (tface.Texture.Texture.HasTransparency()) { continue; } LMFace face = new LMFace(tface, solid); LightmapGroup group = LightmapGroup.FindCoplanar(lmGroups, face); BoxF faceBox = new BoxF(face.BoundingBox.Start - new CoordinateF(3.0f, 3.0f, 3.0f), face.BoundingBox.End + new CoordinateF(3.0f, 3.0f, 3.0f)); if (group == null) { group = new LightmapGroup(); group.BoundingBox = faceBox; group.Faces = new List <LMFace>(); group.Plane = new PlaneF(face.Plane.Normal, face.Vertices[0].Location); lmGroups.Add(group); } #if DEBUG if (face.Texture.ToLowerInvariant() == "tooltextures/debug_breakpoint") { group.DebugBreakpoint = true; } #endif group.Faces.Add(face); group.Plane = new PlaneF(group.Plane.Normal, (face.Vertices[0].Location + group.Plane.PointOnPlane) / 2); group.BoundingBox = new BoxF(new BoxF[] { group.BoundingBox, faceBox }); } } }
public static void FindFacesAndGroups(Map map, out List <LMFace> faces, out List <LightmapGroup> lmGroups) { faces = new List <LMFace>(); lmGroups = new List <LightmapGroup>(); foreach (Solid solid in map.WorldSpawn.Find(x => x is Solid).OfType <Solid>()) { foreach (Face tface in solid.Faces) { tface.Vertices.ForEach(v => { v.LMU = -500.0f; v.LMV = -500.0f; }); tface.UpdateBoundingBox(); if (tface.Texture?.Texture == null) { continue; } if (tface.Texture.IsToolTexture) { continue; } if (tface.Texture.Texture.HasTransparency()) { continue; } LMFace face = new LMFace(tface, solid); LightmapGroup group = LightmapGroup.FindCoplanar(lmGroups, face); BoxF faceBox = new BoxF(face.BoundingBox.Start - new CoordinateF(3.0f, 3.0f, 3.0f), face.BoundingBox.End + new CoordinateF(3.0f, 3.0f, 3.0f)); if (group == null) { group = new LightmapGroup(); group.BoundingBox = faceBox; group.Faces = new List <LMFace>(); group.Plane = new PlaneF(face.Plane.Normal, face.Vertices[0].Location); lmGroups.Add(group); } group.Faces.Add(face); group.Plane = new PlaneF(group.Plane.Normal, (face.Vertices[0].Location + group.Plane.PointOnPlane) / 2); group.BoundingBox = new BoxF(new BoxF[] { group.BoundingBox, faceBox }); } } }
private static void CalculateUV(List <LightmapGroup> lmGroups, Rectangle area, out int usedWidth, out int usedHeight) { usedWidth = 0; usedHeight = 0; if (lmGroups.Count <= 0) { return; } for (int i = 0; i < lmGroups.Count; i++) { LightmapGroup lmGroup = lmGroups[i]; if ((area.Width <= area.Height) != (lmGroup.Width <= lmGroup.Height)) { lmGroup.SwapUV(); } for (int j = 0; j < 2; j++) { int downscaledWidth = (int)Math.Ceiling(lmGroup.Width / LightmapConfig.DownscaleFactor); int downscaledHeight = (int)Math.Ceiling(lmGroup.Height / LightmapConfig.DownscaleFactor); if (downscaledWidth <= area.Width && downscaledHeight <= area.Height) { usedWidth += downscaledWidth; usedHeight += downscaledHeight; lmGroups.RemoveAt(i); lmGroup.writeX = area.Left; lmGroup.writeY = area.Top; int subWidth = -1; int subHeight = -1; if (downscaledWidth < area.Width) { int subUsedWidth = 0; while (subWidth != 0) { CalculateUV(lmGroups, new Rectangle(area.Left + subUsedWidth + downscaledWidth + LightmapConfig.PlaneMargin, area.Top, area.Width - subUsedWidth - downscaledWidth - LightmapConfig.PlaneMargin, downscaledHeight), out subWidth, out subHeight); subUsedWidth += subWidth + LightmapConfig.PlaneMargin; } usedWidth += subUsedWidth; subWidth = -1; subHeight = -1; } if (downscaledHeight < area.Height) { int subUsedHeight = 0; while (subHeight != 0) { CalculateUV(lmGroups, new Rectangle(area.Left, area.Top + subUsedHeight + downscaledHeight + LightmapConfig.PlaneMargin, downscaledWidth, area.Height - subUsedHeight - downscaledHeight - LightmapConfig.PlaneMargin), out subWidth, out subHeight); subUsedHeight += subHeight + LightmapConfig.PlaneMargin; } usedHeight += subUsedHeight; } if (downscaledWidth < area.Width && downscaledHeight < area.Height) { Rectangle remainder = new Rectangle(area.Left + downscaledWidth + LightmapConfig.PlaneMargin, area.Top + downscaledHeight + LightmapConfig.PlaneMargin, area.Width - downscaledWidth - LightmapConfig.PlaneMargin, area.Height - downscaledHeight - LightmapConfig.PlaneMargin); CalculateUV(lmGroups, remainder, out subWidth, out subHeight); usedWidth += subWidth; usedHeight += subHeight; } return; } lmGroup.SwapUV(); } } }
private static void RenderLightOntoFace(Document doc, float[][] bitmaps, List <Light> lights, LightmapGroup group, LMFace targetFace, IEnumerable <LMFace> blockerFaces) { Random rand = new Random(); int writeX = group.writeX; int writeY = group.writeY; int textureDims; lock (doc.TextureCollection.Lightmaps) { textureDims = doc.TextureCollection.Lightmaps[0].Width; } lights = lights.FindAll(x => { float range = x.Range; BoxF lightBox = new BoxF(x.Origin - new CoordinateF(range, range, range), x.Origin + new CoordinateF(range, range, range)); return(lightBox.IntersectsWith(targetFace.BoundingBox)); }); float?minX = null; float?maxX = null; float?minY = null; float?maxY = null; foreach (CoordinateF coord in targetFace.Vertices.Select(x => x.Location)) { float x = coord.Dot(group.uAxis); float y = coord.Dot(group.vAxis); if (minX == null || x < minX) { minX = x; } if (minY == null || y < minY) { minY = y; } if (maxX == null || x > maxX) { maxX = x; } if (maxY == null || y > maxY) { maxY = y; } } CoordinateF leewayPoint = group.Plane.PointOnPlane + (group.Plane.Normal * Math.Max(LightmapConfig.DownscaleFactor * 0.25f, 1.5f)); minX -= LightmapConfig.DownscaleFactor; minY -= LightmapConfig.DownscaleFactor; maxX += LightmapConfig.DownscaleFactor; maxY += LightmapConfig.DownscaleFactor; minX /= LightmapConfig.DownscaleFactor; minX = (float)Math.Ceiling(minX.Value); minX *= LightmapConfig.DownscaleFactor; minY /= LightmapConfig.DownscaleFactor; minY = (float)Math.Ceiling(minY.Value); minY *= LightmapConfig.DownscaleFactor; maxX /= LightmapConfig.DownscaleFactor; maxX = (float)Math.Ceiling(maxX.Value); maxX *= LightmapConfig.DownscaleFactor; maxY /= LightmapConfig.DownscaleFactor; maxY = (float)Math.Ceiling(maxY.Value); maxY *= LightmapConfig.DownscaleFactor; foreach (LMFace.Vertex vert in targetFace.Vertices) { float x = vert.Location.Dot(group.uAxis); float y = vert.Location.Dot(group.vAxis); float u = (writeX + 0.5f + (x - group.minTotalX.Value) / LightmapConfig.DownscaleFactor); float v = (writeY + 0.5f + (y - group.minTotalY.Value) / LightmapConfig.DownscaleFactor); targetFace.LmIndex = (u >= LightmapConfig.TextureDims ? 1 : 0) + (v >= LightmapConfig.TextureDims ? 2 : 0); u /= (float)textureDims; v /= (float)textureDims; vert.LMU = u; vert.LMV = v; vert.OriginalVertex.LMU = u; vert.OriginalVertex.LMV = v; } float centerX = (maxX.Value + minX.Value) / 2; float centerY = (maxY.Value + minY.Value) / 2; int iterX = (int)Math.Ceiling((maxX.Value - minX.Value) / LightmapConfig.DownscaleFactor); int iterY = (int)Math.Ceiling((maxY.Value - minY.Value) / LightmapConfig.DownscaleFactor); float[][,] r = new float[4][, ]; r[0] = new float[iterX, iterY]; r[1] = new float[iterX, iterY]; r[2] = new float[iterX, iterY]; r[3] = new float[iterX, iterY]; float[][,] g = new float[4][, ]; g[0] = new float[iterX, iterY]; g[1] = new float[iterX, iterY]; g[2] = new float[iterX, iterY]; g[3] = new float[iterX, iterY]; float[][,] b = new float[4][, ]; b[0] = new float[iterX, iterY]; b[1] = new float[iterX, iterY]; b[2] = new float[iterX, iterY]; b[3] = new float[iterX, iterY]; foreach (Light light in lights) { CoordinateF lightPos = light.Origin; float lightRange = light.Range; CoordinateF lightColor = light.Color * (1.0f / 255.0f) * light.Intensity; BoxF lightBox = new BoxF(new BoxF[] { targetFace.BoundingBox, new BoxF(light.Origin - new CoordinateF(30.0f, 30.0f, 30.0f), light.Origin + new CoordinateF(30.0f, 30.0f, 30.0f)) }); List <LMFace> applicableBlockerFaces = blockerFaces.Where(x => { if (x == targetFace) { return(false); } if (group.Faces.Contains(x)) { return(false); } //return true; if (lightBox.IntersectsWith(x.BoundingBox)) { return(true); } return(false); }).ToList(); bool[,] illuminated = new bool[iterX, iterY]; for (int y = 0; y < iterY; y++) { for (int x = 0; x < iterX; x++) { illuminated[x, y] = true; } } for (int y = 0; y < iterY; y++) { for (int x = 0; x < iterX; x++) { int tX = (int)(writeX + x + (int)(minX - group.minTotalX) / LightmapConfig.DownscaleFactor); int tY = (int)(writeY + y + (int)(minY - group.minTotalY) / LightmapConfig.DownscaleFactor); if (tX >= 0 && tY >= 0 && tX < textureDims && tY < textureDims) { int offset = (tX + tY * textureDims) * Bitmap.GetPixelFormatSize(System.Drawing.Imaging.PixelFormat.Format32bppArgb) / 8; bitmaps[0][offset + 3] = 1.0f; bitmaps[1][offset + 3] = 1.0f; bitmaps[2][offset + 3] = 1.0f; bitmaps[3][offset + 3] = 1.0f; } } } for (int y = 0; y < iterY; y++) { for (int x = 0; x < iterX; x++) { float ttX = minX.Value + (x * LightmapConfig.DownscaleFactor); float ttY = minY.Value + (y * LightmapConfig.DownscaleFactor); CoordinateF pointOnPlane = (ttX - centerX) * group.uAxis + (ttY - centerY) * group.vAxis + targetFace.BoundingBox.Center; /*Entity entity = new Entity(map.IDGenerator.GetNextObjectID()); * entity.Colour = Color.Pink; * entity.Origin = new Coordinate(pointOnPlane); * entity.UpdateBoundingBox(); * entity.SetParent(map.WorldSpawn);*/ int tX = (int)(writeX + x + (int)(minX - group.minTotalX) / LightmapConfig.DownscaleFactor); int tY = (int)(writeY + y + (int)(minY - group.minTotalY) / LightmapConfig.DownscaleFactor); CoordinateF luxelColor0 = new CoordinateF(r[0][x, y], g[0][x, y], b[0][x, y]); CoordinateF luxelColor1 = new CoordinateF(r[1][x, y], g[1][x, y], b[1][x, y]); CoordinateF luxelColor2 = new CoordinateF(r[2][x, y], g[2][x, y], b[2][x, y]); CoordinateF luxelColorNorm = new CoordinateF(r[3][x, y], g[3][x, y], b[3][x, y]); float dotToLight0 = Math.Max((lightPos - pointOnPlane).Normalise().Dot(targetFace.LightBasis0), 0.0f); float dotToLight1 = Math.Max((lightPos - pointOnPlane).Normalise().Dot(targetFace.LightBasis1), 0.0f); float dotToLight2 = Math.Max((lightPos - pointOnPlane).Normalise().Dot(targetFace.LightBasis2), 0.0f); float dotToLightNorm = Math.Max((lightPos - pointOnPlane).Normalise().Dot(targetFace.Normal), 0.0f); if (illuminated[x, y] && (pointOnPlane - lightPos).LengthSquared() < lightRange * lightRange) { #if TRUE LineF lineTester = new LineF(lightPos, pointOnPlane); for (int i = 0; i < applicableBlockerFaces.Count; i++) { LMFace otherFace = applicableBlockerFaces[i]; CoordinateF hit = otherFace.GetIntersectionPoint(lineTester); if (hit != null && ((hit - leewayPoint).Dot(group.Plane.Normal) > 0.0f || (hit - pointOnPlane).LengthSquared() > LightmapConfig.DownscaleFactor * 2f)) { applicableBlockerFaces.RemoveAt(i); applicableBlockerFaces.Insert(0, otherFace); illuminated[x, y] = false; i++; break; } } #endif } else { illuminated[x, y] = false; } if (illuminated[x, y]) { float brightness = (lightRange - (pointOnPlane - lightPos).VectorMagnitude()) / lightRange; if (light.Direction != null) { float directionDot = light.Direction.Dot((pointOnPlane - lightPos).Normalise()); if (directionDot < light.innerCos) { if (directionDot < light.outerCos) { brightness = 0.0f; } else { brightness *= (directionDot - light.outerCos.Value) / (light.innerCos.Value - light.outerCos.Value); } } } float brightness0 = dotToLight0 * brightness * brightness; float brightness1 = dotToLight1 * brightness * brightness; float brightness2 = dotToLight2 * brightness * brightness; float brightnessNorm = dotToLightNorm * brightness * brightness; brightness0 += ((float)rand.NextDouble() - 0.5f) * 0.005f; brightness1 += ((float)rand.NextDouble() - 0.5f) * 0.005f; brightness2 += ((float)rand.NextDouble() - 0.5f) * 0.005f; brightnessNorm += ((float)rand.NextDouble() - 0.5f) * 0.005f; r[0][x, y] += lightColor.Z * brightness0; if (r[0][x, y] > 1.0f) { r[0][x, y] = 1.0f; } if (r[0][x, y] < 0) { r[0][x, y] = 0; } g[0][x, y] += lightColor.Y * brightness0; if (g[0][x, y] > 1.0f) { g[0][x, y] = 1.0f; } if (g[0][x, y] < 0) { g[0][x, y] = 0; } b[0][x, y] += lightColor.X * brightness0; if (b[0][x, y] > 1.0f) { b[0][x, y] = 1.0f; } if (b[0][x, y] < 0) { b[0][x, y] = 0; } r[1][x, y] += lightColor.Z * brightness1; if (r[1][x, y] > 1.0f) { r[1][x, y] = 1.0f; } if (r[1][x, y] < 0) { r[1][x, y] = 0; } g[1][x, y] += lightColor.Y * brightness1; if (g[1][x, y] > 1.0f) { g[1][x, y] = 1.0f; } if (g[1][x, y] < 0) { g[1][x, y] = 0; } b[1][x, y] += lightColor.X * brightness1; if (b[1][x, y] > 1.0f) { b[1][x, y] = 1.0f; } if (b[1][x, y] < 0) { b[1][x, y] = 0; } r[2][x, y] += lightColor.Z * brightness2; if (r[2][x, y] > 1.0f) { r[2][x, y] = 1.0f; } if (r[2][x, y] < 0) { r[2][x, y] = 0; } g[2][x, y] += lightColor.Y * brightness2; if (g[2][x, y] > 1.0f) { g[2][x, y] = 1.0f; } if (g[2][x, y] < 0) { g[2][x, y] = 0; } b[2][x, y] += lightColor.X * brightness2; if (b[2][x, y] > 1.0f) { b[2][x, y] = 1.0f; } if (b[2][x, y] < 0) { b[2][x, y] = 0; } r[3][x, y] += lightColor.Z * brightnessNorm; if (r[3][x, y] > 1.0f) { r[3][x, y] = 1.0f; } if (r[3][x, y] < 0) { r[3][x, y] = 0; } g[3][x, y] += lightColor.Y * brightnessNorm; if (g[3][x, y] > 1.0f) { g[3][x, y] = 1.0f; } if (g[3][x, y] < 0) { g[3][x, y] = 0; } b[3][x, y] += lightColor.X * brightnessNorm; if (b[3][x, y] > 1.0f) { b[3][x, y] = 1.0f; } if (b[3][x, y] < 0) { b[3][x, y] = 0; } luxelColor0 = new CoordinateF(r[0][x, y], g[0][x, y], b[0][x, y]); luxelColor1 = new CoordinateF(r[1][x, y], g[1][x, y], b[1][x, y]); luxelColor2 = new CoordinateF(r[2][x, y], g[2][x, y], b[2][x, y]); luxelColorNorm = new CoordinateF(r[3][x, y], g[3][x, y], b[3][x, y]); if (tX >= 0 && tY >= 0 && tX < textureDims && tY < textureDims) { int offset = (tX + tY * textureDims) * Bitmap.GetPixelFormatSize(System.Drawing.Imaging.PixelFormat.Format32bppArgb) / 8; if (luxelColor0.X + luxelColor0.Y + luxelColor0.Z > bitmaps[0][offset + 2] + bitmaps[0][offset + 1] + bitmaps[0][offset + 0]) { bitmaps[0][offset + 0] = luxelColor0.X; bitmaps[0][offset + 1] = luxelColor0.Y; bitmaps[0][offset + 2] = luxelColor0.Z; } if (luxelColor1.X + luxelColor1.Y + luxelColor1.Z > bitmaps[1][offset + 2] + bitmaps[1][offset + 1] + bitmaps[1][offset + 0]) { bitmaps[1][offset + 0] = luxelColor1.X; bitmaps[1][offset + 1] = luxelColor1.Y; bitmaps[1][offset + 2] = luxelColor1.Z; } if (luxelColor2.X + luxelColor2.Y + luxelColor2.Z > bitmaps[2][offset + 2] + bitmaps[2][offset + 1] + bitmaps[2][offset + 0]) { bitmaps[2][offset + 0] = luxelColor2.X; bitmaps[2][offset + 1] = luxelColor2.Y; bitmaps[2][offset + 2] = luxelColor2.Z; } if (luxelColorNorm.X + luxelColorNorm.Y + luxelColorNorm.Z > bitmaps[3][offset + 2] + bitmaps[3][offset + 1] + bitmaps[3][offset + 0]) { bitmaps[3][offset + 0] = luxelColorNorm.X; bitmaps[3][offset + 1] = luxelColorNorm.Y; bitmaps[3][offset + 2] = luxelColorNorm.Z; } } } } } } }
private static Thread CreateLightmapRenderThread(Document doc, float[][] bitmaps, List <Light> lights, LightmapGroup group, LMFace targetFace, IEnumerable <LMFace> blockerFaces) { return(new Thread(() => { try { RenderLightOntoFace(doc, bitmaps, lights, group, targetFace, blockerFaces); } catch (ThreadAbortException) { //do nothing } catch (Exception e) { threadExceptions.Add(new LMThreadException(e)); } })); }