Compare commits

..

3 Commits

Author SHA1 Message Date
Jarrod Doyle a5078b0f2b
Use occlusion rays for non-sun tracing
More performant, no longer requires annoying fiddling to check distances, and removes the need for water surfaces in the mesh. Also reduces the number of edge case misses
2025-01-15 20:26:23 +00:00
Jarrod Doyle 4b3016d895
Combine plane facing and initial radius checks 2025-01-14 21:16:25 +00:00
Jarrod Doyle df8cb6f1ca
Fix logging null when failing to find object model 2025-01-14 20:54:21 +00:00
2 changed files with 35 additions and 67 deletions

View File

@ -699,20 +699,11 @@ public class LightMapper
var light = _lights[lightIdx - 1];
// Check if plane normal is facing towards the light
// If it's not then we're never going to be (directly) lit by this
// light.
var centerDirection = renderPoly.Center - light.Position;
if (Vector3.Dot(plane.Normal, centerDirection) >= 0)
{
continue;
}
// If there aren't *any* points on the plane that are in range of the light
// then none of the lightmap points will be so we can discard.
// The more compact a map is the less effective this is
var planeDist = Math.Abs(MathUtils.DistanceFromPlane(plane, light.Position));
if (planeDist > light.Radius)
// If the light is behind the plane we'll never be directly lit by this light.
// Additionally, if the distance from the plane is more than the light's radius
// we know no points on the plane will be lit.
var planeDist = MathUtils.DistanceFromPlane(plane, light.Position);
if (planeDist <= MathUtils.Epsilon || planeDist > light.Radius)
{
continue;
}
@ -739,7 +730,7 @@ public class LightMapper
continue;
}
if (TraceRay(light.Position, point))
if (!TraceOcclusion(light.Position, point))
{
strength += targetWeights[idx] * light.StrengthAtPoint(point, plane, settings.AnimLightCutoff);
}
@ -826,81 +817,58 @@ public class LightMapper
{
var offset = offsets[i];
var pos = basePosition + offset;
// Embree has robustness issues when hitting poly edges which
// results in false misses. To alleviate this we pre-push everything
// slightly towards the center of the poly.
var centerOffset = polyCenter - pos;
if (centerOffset.LengthSquared() > MathUtils.Epsilon)
{
pos += Vector3.Normalize(centerOffset) * MathUtils.Epsilon;
}
// If we can't see our target point from the center of the poly
// then it's outside the world. We need to clip the point to slightly
// inside the poly and retrace to avoid three problems:
// then we need to clip the point to slightly inside the poly
// and retrace to avoid two problems:
// 1. Darkened spots from lightmap pixels whose center is outside
// the polygon but is partially contained in the polygon
// 2. Darkened spots from linear filtering of points outside the
// polygon which have missed
// 3. Darkened spots where centers are on the exact edge of a poly
// which can sometimes cause Embree to miss casts
var inPoly = TraceRay(polyCenter + planeMapper.Normal * 0.25f, pos);
if (!inPoly)
var occluded = TraceOcclusion(polyCenter + planeMapper.Normal * 0.25f, pos);
if (occluded)
{
var p2d = planeMapper.MapTo2d(pos);
p2d = MathUtils.ClipPointToPoly2d(p2d, v2ds);
pos = planeMapper.MapTo3d(p2d);
}
tracePoints[i] = pos;
}
return tracePoints;
}
private bool TraceRay(Vector3 origin, Vector3 target)
private bool TraceOcclusion(Vector3 origin, Vector3 target)
{
var hitDistanceFromTarget = float.MinValue;
var hitSurfaceType = SurfaceType.Water;
while (hitDistanceFromTarget < -MathUtils.Epsilon && hitSurfaceType == SurfaceType.Water)
var direction = target - origin;
var ray = new Ray
{
var direction = target - origin;
var hitResult = _scene.Trace(new Ray
{
Origin = origin,
Direction = Vector3.Normalize(direction),
});
hitDistanceFromTarget = hitResult.Distance - direction.Length();
hitSurfaceType = _triangleTypeMap[(int)hitResult.PrimId];
origin = hitResult.Position += direction * MathUtils.Epsilon;
}
// A large epsilon is used here to fix shadow acne on sloped surfaces :)
return Math.Abs(hitDistanceFromTarget) < 10 * MathUtils.Epsilon;
Origin = origin,
Direction = Vector3.Normalize(direction),
};
// Epsilon is used here to avoid occlusion when origin lies exactly on a poly
return _scene.IsOccluded(new ShadowRay(ray, direction.Length() - MathUtils.Epsilon));
}
// TODO: Can this be merged with the above?
// TODO: direction should already be normalised here
private bool TraceSunRay(Vector3 origin, Vector3 direction)
{
// Avoid self intersection
origin += direction * MathUtils.Epsilon;
var hitSurfaceType = SurfaceType.Water;
while (hitSurfaceType == SurfaceType.Water)
var hitResult = _scene.Trace(new Ray
{
var hitResult = _scene.Trace(new Ray
{
Origin = origin,
Direction = Vector3.Normalize(direction),
});
hitSurfaceType = _triangleTypeMap[(int)hitResult.PrimId];
origin = hitResult.Position += direction * MathUtils.Epsilon;
Origin = origin,
Direction = Vector3.Normalize(direction),
});
if (hitResult)
{
return _triangleTypeMap[(int)hitResult.PrimId] == SurfaceType.Sky;
}
return hitSurfaceType == SurfaceType.Sky;
return false;
}
private void SetAnimLightCellMaps()

View File

@ -43,9 +43,8 @@ public class MeshBuilder
var cellIdxOffset = 0;
for (var polyIdx = 0; polyIdx < numPolys; polyIdx++)
{
// There's 3 types of poly that we need to include in the mesh:
// There's 2 types of poly that we need to include in the mesh:
// - Terrain
// - Water surfaces
// - Door vision blockers
//
// Door vision blockers are the interesting one. They're not RenderPolys at all, just flagged Polys.
@ -56,7 +55,8 @@ public class MeshBuilder
}
else if (polyIdx < numRenderPolys)
{
primType = SurfaceType.Water;
// we no longer want water polys :)
continue;
}
else if ((cell.Flags & 8) != 0)
{
@ -118,7 +118,7 @@ public class MeshBuilder
var modelPath = campaignResources.GetResourcePath(ResourceType.Object, modelName);
if (modelPath == null)
{
Log.Warning("Failed to find model file: {Path}", modelPath);
Log.Warning("Failed to find model file: {Name}", modelName);
continue;
}