Compare commits

..

No commits in common. "a5078b0f2be1110a88c28e57f57b91f4a0f7197b" and "86829421023cd79d1cb76715fecf73410de98412" have entirely different histories.

2 changed files with 67 additions and 35 deletions

View File

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

View File

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