| 248 | } |
| 249 | |
| 250 | OrthoFit FitOrtho(const Mat4& lightView, const Vec3* pts, int count) |
| 251 | { |
| 252 | OrthoFit fit; |
| 253 | fit.proj = Identity(); |
| 254 | if (count <= 0 || pts == nullptr) |
| 255 | { |
| 256 | fit.minB = {}; |
| 257 | fit.maxB = {}; |
| 258 | return fit; |
| 259 | } |
| 260 | |
| 261 | Vec3 mn = TransformPoint(lightView, pts[0]); |
| 262 | Vec3 mx = mn; |
| 263 | for (int i = 1; i < count; i++) |
| 264 | { |
| 265 | Vec3 p = TransformPoint(lightView, pts[i]); |
| 266 | mn = Min(mn, p); |
| 267 | mx = Max(mx, p); |
| 268 | } |
| 269 | |
| 270 | // Guard degenerate (zero-extent) axes so the ortho never divides by zero. |
| 271 | const float eps = 1e-4f; |
| 272 | if (mx.x - mn.x < eps) |
| 273 | { |
| 274 | mx.x += eps; |
| 275 | mn.x -= eps; |
| 276 | } |
| 277 | if (mx.y - mn.y < eps) |
| 278 | { |
| 279 | mx.y += eps; |
| 280 | mn.y -= eps; |
| 281 | } |
| 282 | if (mx.z - mn.z < eps) |
| 283 | { |
| 284 | mx.z += eps; |
| 285 | mn.z -= eps; |
| 286 | } |
| 287 | |
| 288 | fit.minB = mn; |
| 289 | fit.maxB = mx; |
| 290 | // Light space looks down -Z, so points in front have negative z; the near |
| 291 | // plane (closest to the light) is at the largest z = -maxB.z. |
| 292 | float n = -mx.z; |
| 293 | float f = -mn.z; |
| 294 | fit.proj = Ortho(mn.x, mx.x, mn.y, mx.y, n, f); |
| 295 | return fit; |
| 296 | } |
| 297 | |
| 298 | OrthoFit SnapToTexelGrid(const OrthoFit& fit, int resolution) |
| 299 | { |