diff --git a/src/OceanLOD.ts b/src/OceanLOD.ts index 7c736cb..e6e366b 100644 --- a/src/OceanLOD.ts +++ b/src/OceanLOD.ts @@ -1,7 +1,7 @@ import { Grid } from './Grid'; import { vec3 } from 'gl-matrix'; -/** Manages multiple ocean grid patches with LOD based on camera distance */ +/** Manages multiple ocean grid patches with LOD based on camera distance and view cone */ export class OceanLOD { private grids: Array<{ grid: Grid; @@ -12,14 +12,15 @@ export class OceanLOD { }> = []; private readonly LOD_LEVELS = [ - { distance: 2.0, gridSize: 128 }, // Closest - highest detail - { distance: 5.0, gridSize: 64 }, // Medium distance - { distance: 10.0, gridSize: 32 }, // Far distance - { distance: 20.0, gridSize: 16 }, // Very far - lowest detail + { distance: 2.0, gridSize: 256 }, // Closest - highest detail + { distance: 5.0, gridSize: 128 }, // Medium distance + { distance: 10.0, gridSize: 64 }, // Far distance + { distance: 20.0, gridSize: 32 }, // Very far - lowest detail ]; private readonly PATCH_SIZE = 2.0; // World size of each patch private readonly PATCHES_PER_SIDE = 7; // 7x7 = 49 patches total + private readonly VIEW_CONE_COS = Math.cos(Math.PI * 0.45); // ~81 degree half-angle (wider than typical FOV) constructor() { this.createGridPatches(); @@ -52,20 +53,54 @@ export class OceanLOD { } } - /** Update LOD based on camera position */ - updateLOD(gl: WebGL2RenderingContext, cameraPos: vec3): void { + /** Update LOD based on camera position and view direction */ + updateLOD(gl: WebGL2RenderingContext, cameraPos: vec3, cameraTarget: vec3): void { + // Calculate view direction (normalized) + const viewDir = vec3.create(); + vec3.subtract(viewDir, cameraTarget, cameraPos); + vec3.normalize(viewDir, viewDir); + for (const patch of this.grids) { - // Calculate distance from camera to patch center - const dx = patch.centerX - cameraPos[0]; - const dy = patch.centerY - cameraPos[1]; - const distance = Math.sqrt(dx * dx + dy * dy); + // Calculate vector from camera to patch center (on XY plane, Z=0 for ocean surface) + const toPatch = vec3.fromValues( + patch.centerX - cameraPos[0], + patch.centerY - cameraPos[1], + 0 - cameraPos[2] // Ocean is at Z=0 + ); + const distance = vec3.length(toPatch); - // Determine appropriate LOD level + // Normalize direction to patch + const toPatchDir = vec3.create(); + vec3.normalize(toPatchDir, toPatch); + + // Calculate dot product with view direction (how aligned is patch with where we're looking) + const dotProduct = vec3.dot(viewDir, toPatchDir); + + // Determine if patch is in front of camera and within view cone + const isInFront = dotProduct > -0.2; // Slightly behind is ok for edge cases + const isInViewCone = dotProduct > this.VIEW_CONE_COS; + + // Calculate LOD level let newLodLevel = 3; // Default to lowest detail - for (let i = 0; i < this.LOD_LEVELS.length; i++) { - if (distance < this.LOD_LEVELS[i].distance) { - newLodLevel = i; - break; + + if (!isInFront) { + // Behind camera - always lowest detail + newLodLevel = 3; + } else if (isInViewCone) { + // In view cone - use distance-based LOD + for (let i = 0; i < this.LOD_LEVELS.length; i++) { + if (distance < this.LOD_LEVELS[i].distance) { + newLodLevel = i; + break; + } + } + } else { + // In front but outside view cone - reduce detail by 1-2 levels + for (let i = 0; i < this.LOD_LEVELS.length; i++) { + if (distance < this.LOD_LEVELS[i].distance) { + newLodLevel = Math.min(i + 2, 3); // Reduce detail + break; + } } } diff --git a/src/main.ts b/src/main.ts index 2e29cf0..1343d57 100644 --- a/src/main.ts +++ b/src/main.ts @@ -190,8 +190,8 @@ function drawScene() { camera.setRotationY((curRotY += mouseXVel * Config.MOUSE_SENSITIVITY + keyboardRotationY)); var view = camera.getViewMatrix(); - // Update LOD based on camera position - oceanLOD.updateLOD(gl, camera.pos); + // Update LOD based on camera position and view direction + oceanLOD.updateLOD(gl, camera.pos, camera.target); var model = mat4.create(); mat4.identity(model);