diff --git a/src/lib/map/DoodadManager.ts b/src/lib/map/DoodadManager.ts index e13eafe..684b3d5 100644 --- a/src/lib/map/DoodadManager.ts +++ b/src/lib/map/DoodadManager.ts @@ -5,6 +5,7 @@ import { AssetHost } from '../asset.js'; import { MapAreaSpec, MapDoodadDefSpec } from './loader/types.js'; import MapLight from './light/MapLight.js'; import Model from '../model/Model.js'; +import { getFade } from '../world.js'; type DoodadManagerOptions = { host: AssetHost; @@ -34,7 +35,7 @@ class DoodadManager { }); } - cull(cullingFrustum: THREE.Frustum) { + cull(cullingFrustum: THREE.Frustum, cameraPosition: THREE.Vector3) { for (const [areaId, areaGroup] of this.#loadedAreas.entries()) { const areaBounds = this.#areaBounds.get(areaId); const areaVisible = cullingFrustum.intersectsSphere(areaBounds); @@ -42,11 +43,15 @@ class DoodadManager { areaGroup.visible = areaVisible; for (const doodad of areaGroup.children as Model[]) { + const distance = cameraPosition.distanceTo(doodad.position); + const fade = getFade(distance, doodad.sizeCategory); + const doodadVisible = - areaVisible && cullingFrustum.intersectsSphere(doodad.boundingSphereWorld); + areaVisible && fade > 0.0 && cullingFrustum.intersectsSphere(doodad.boundingSphereWorld); if (doodadVisible) { doodad.show(); + doodad.alpha = fade; } else { doodad.hide(); } diff --git a/src/lib/map/MapManager.ts b/src/lib/map/MapManager.ts index 9fe9cfe..d93ed7f 100644 --- a/src/lib/map/MapManager.ts +++ b/src/lib/map/MapManager.ts @@ -194,7 +194,7 @@ class MapManager extends EventTarget { // Cull entire groups to save on frustum intersection cost this.#cullGroups(); - this.#doodadManager.cull(this.#cullingFrustum); + this.#doodadManager.cull(this.#cullingFrustum, camera.position); this.#doodadManager.update(deltaTime); } diff --git a/src/lib/model/Model.ts b/src/lib/model/Model.ts index 54236c5..7bd4906 100644 --- a/src/lib/model/Model.ts +++ b/src/lib/model/Model.ts @@ -2,19 +2,23 @@ import * as THREE from 'three'; import ModelMaterial from './ModelMaterial.js'; import ModelAnimator from './ModelAnimator.js'; import ModelAnimation from './ModelAnimation.js'; +import { getSizeCategory } from '../world.js'; class Model extends THREE.Object3D { animation: ModelAnimation; diffuseColor: THREE.Color; emissiveColor: THREE.Color; - alpha: 1.0; + alpha = 1.0; #mesh: THREE.Mesh; #skinned: boolean; #boundingSphereWorld = new THREE.Sphere(); + #size = 0.0; + #sizeCategory = 0; + constructor( geometry: THREE.BufferGeometry, materials: THREE.Material[], @@ -62,6 +66,14 @@ class Model extends THREE.Object3D { return this.#boundingSphereWorld; } + get size() { + return this.#size; + } + + get sizeCategory() { + return this.#sizeCategory; + } + get skinned() { return this.#skinned; } @@ -91,9 +103,23 @@ class Model extends THREE.Object3D { updateMatrixWorld(force?: boolean) { super.updateMatrixWorld(force); + this.#updateBounds(); + this.#updateSize(); + } + + #updateBounds() { this.#boundingSphereWorld.copy(this.boundingSphere).applyMatrix4(this.matrixWorld); } + #updateSize() { + const sizeX = (this.boundingBox.max.x - this.boundingBox.min.x) * this.scale.x; + const sizeY = (this.boundingBox.max.y - this.boundingBox.min.y) * this.scale.y; + const sizeZ = (this.boundingBox.max.z - this.boundingBox.min.z) * this.scale.z; + + this.#size = Math.max(sizeX, sizeY, sizeZ); + this.#sizeCategory = getSizeCategory(this.#size); + } + #onBeforeRender( renderer: THREE.WebGLRenderer, scene: THREE.Scene, diff --git a/src/lib/world.ts b/src/lib/world.ts index d5574c2..df0721a 100644 --- a/src/lib/world.ts +++ b/src/lib/world.ts @@ -21,6 +21,31 @@ const getSizeCategory = (size: number) => { return f; }; +const getFade = (distance: number, sizeCategory: number) => { + const min = WORLD_FADE_DIST_MIN[sizeCategory]; + const max = WORLD_FADE_DIST_MAX[sizeCategory]; + + if (distance < min) { + return 1.0; + } + + if (distance > max) { + return 0.0; + } + + const fade = 1.0 - (distance - min) / (max - min); + + if (fade <= 0.0099999998) { + return 0.0; + } + + if (fade > 0.99000001) { + return 1.0; + } + + return fade; +}; + const scaleFadeDist = (scale: number) => { for (let i = 0; i < WORLD_FADE_SIZE_DEFAULT.length; i++) { const first = i === 0; @@ -37,6 +62,7 @@ const scaleFadeDist = (scale: number) => { scaleFadeDist(1.5); export { + getFade, getSizeCategory, scaleFadeDist, WORLD_FADE_SIZE,