Compare commits

..

4 Commits

1 changed files with 54 additions and 11 deletions

View File

@ -8,12 +8,20 @@ namespace KeepersCompound.Lightmapper;
public class LightMapper public class LightMapper
{ {
private enum SurfaceType
{
Solid,
Sky,
Water,
}
private class Settings private class Settings
{ {
public Vector3 AmbientLight; public Vector3 AmbientLight;
public bool Hdr; public bool Hdr;
public SoftnessMode MultiSampling; public SoftnessMode MultiSampling;
public float MultiSamplingCenterWeight; public float MultiSamplingCenterWeight;
public bool LightmappedWater;
} }
private ResourcePathManager.CampaignResources _campaign; private ResourcePathManager.CampaignResources _campaign;
@ -22,6 +30,7 @@ public class LightMapper
private ObjectHierarchy _hierarchy; private ObjectHierarchy _hierarchy;
private Raytracer _scene; private Raytracer _scene;
private List<Light> _lights; private List<Light> _lights;
private List<SurfaceType> _triangleTypeMap;
public LightMapper( public LightMapper(
string installPath, string installPath,
@ -33,6 +42,7 @@ public class LightMapper
_misPath = _campaign.GetResourcePath(ResourceType.Mission, missionName); _misPath = _campaign.GetResourcePath(ResourceType.Mission, missionName);
_mission = Timing.TimeStage("Parse DB", () => new DbFile(_misPath)); _mission = Timing.TimeStage("Parse DB", () => new DbFile(_misPath));
_hierarchy = Timing.TimeStage("Build Hierarchy", BuildHierarchy); _hierarchy = Timing.TimeStage("Build Hierarchy", BuildHierarchy);
_triangleTypeMap = [];
_scene = Timing.TimeStage("Build Scene", BuildRaytracingScene); _scene = Timing.TimeStage("Build Scene", BuildRaytracingScene);
_lights = []; _lights = [];
} }
@ -47,14 +57,16 @@ public class LightMapper
return; return;
} }
// TODO: lmParams LightmappedWater doesn't mean the game will actually *use* the lightmapped water hmm
var settings = new Settings var settings = new Settings
{ {
Hdr = worldRep.DataHeader.LightmapFormat == 2, Hdr = worldRep.DataHeader.LightmapFormat == 2,
AmbientLight = rendParams.ambientLight * 255, AmbientLight = rendParams.ambientLight * 255,
MultiSampling = lmParams.ShadowSoftness, MultiSampling = lmParams.ShadowSoftness,
MultiSamplingCenterWeight = lmParams.CenterWeight, MultiSamplingCenterWeight = lmParams.CenterWeight,
LightmappedWater = lmParams.LightmappedWater,
}; };
Timing.TimeStage("Gather Lights", BuildLightList); Timing.TimeStage("Gather Lights", BuildLightList);
Timing.TimeStage("Set Light Indices", SetCellLightIndices); Timing.TimeStage("Set Light Indices", SetCellLightIndices);
Timing.TimeStage("Trace Scene", () => TraceScene(settings)); Timing.TimeStage("Trace Scene", () => TraceScene(settings));
@ -119,9 +131,9 @@ public class LightMapper
continue; continue;
} }
var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys); var solidPolys = numPolys - numPortalPolys;
var cellIdxOffset = 0; var cellIdxOffset = 0;
for (var polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++) for (var polyIdx = 0; polyIdx < numRenderPolys; polyIdx++)
{ {
var poly = cell.Polys[polyIdx]; var poly = cell.Polys[polyIdx];
var meshIndexOffset = vertices.Count; var meshIndexOffset = vertices.Count;
@ -132,12 +144,25 @@ public class LightMapper
vertices.Add(vertex); vertices.Add(vertex);
} }
// We need to know what type of surface this poly is so we can map Embree primitive IDs to surface
// types
var renderPoly = cell.RenderPolys[polyIdx];
var primType = SurfaceType.Solid;
if (renderPoly.TextureId == 249)
{
primType = SurfaceType.Sky;
} else if (polyIdx >= solidPolys)
{
primType = SurfaceType.Water;
}
// Cell polygons are n-sided, but fortunately they're convex so we can just do a fan triangulation // Cell polygons are n-sided, but fortunately they're convex so we can just do a fan triangulation
for (var j = 1; j < numPolyVertices - 1; j++) for (var j = 1; j < numPolyVertices - 1; j++)
{ {
indices.Add(meshIndexOffset); indices.Add(meshIndexOffset);
indices.Add(meshIndexOffset + j); indices.Add(meshIndexOffset + j);
indices.Add(meshIndexOffset + j + 1); indices.Add(meshIndexOffset + j + 1);
_triangleTypeMap.Add(primType);
} }
cellIdxOffset += cell.Polys[polyIdx].VertexCount; cellIdxOffset += cell.Polys[polyIdx].VertexCount;
@ -368,9 +393,9 @@ public class LightMapper
return; return;
} }
var maxPolyIdx = Math.Min(numRenderPolys, numPolys - numPortalPolys); var solidPolys = numPolys - numPortalPolys;
var cellIdxOffset = 0; var cellIdxOffset = 0;
for (var polyIdx = 0; polyIdx < maxPolyIdx; polyIdx++) for (var polyIdx = 0; polyIdx < numRenderPolys; polyIdx++)
{ {
var poly = cell.Polys[polyIdx]; var poly = cell.Polys[polyIdx];
var plane = cell.Planes[poly.PlaneId]; var plane = cell.Planes[poly.PlaneId];
@ -379,6 +404,14 @@ public class LightMapper
var lightmap = cell.Lightmaps[polyIdx]; var lightmap = cell.Lightmaps[polyIdx];
info.AnimLightBitmask = 0; info.AnimLightBitmask = 0;
// We have to reset the lightmaps for water, but we don't want to do anything else
var waterPoly = polyIdx >= solidPolys;
if (!settings.LightmappedWater && waterPoly)
{
lightmap.Reset(Vector3.One * 255f, settings.Hdr);
continue;
}
lightmap.Reset(settings.AmbientLight, settings.Hdr); lightmap.Reset(settings.AmbientLight, settings.Hdr);
// Get world position of lightmap (0, 0) (+0.5 so we cast from the center of a pixel) // Get world position of lightmap (0, 0) (+0.5 so we cast from the center of a pixel)
@ -598,13 +631,23 @@ public class LightMapper
private bool TraceRay(Vector3 origin, Vector3 target) private bool TraceRay(Vector3 origin, Vector3 target)
{ {
var direction = target - origin; var hitDistanceFromTarget = float.MinValue;
var hitResult = _scene.Trace(new Ray var hitSurfaceType = SurfaceType.Water;
while (hitDistanceFromTarget < -MathUtils.Epsilon && hitSurfaceType == SurfaceType.Water)
{ {
Origin = origin, var direction = target - origin;
Direction = Vector3.Normalize(direction), var hitResult = _scene.Trace(new Ray
}); {
return hitResult && Math.Abs(hitResult.Distance - direction.Length()) < MathUtils.Epsilon; Origin = origin,
Direction = Vector3.Normalize(direction),
});
hitDistanceFromTarget = hitResult.Distance - direction.Length();
hitSurfaceType = _triangleTypeMap[(int)hitResult.PrimId];
origin = hitResult.Position += direction * MathUtils.Epsilon;
}
return Math.Abs(hitDistanceFromTarget) < MathUtils.Epsilon;
} }
private void SetAnimLightCellMaps() private void SetAnimLightCellMaps()