From ce59687e9f0e00f231e9001a6499caf03e9e401c Mon Sep 17 00:00:00 2001 From: fallenoak Date: Tue, 30 Jan 2024 21:59:25 -0600 Subject: [PATCH] fix(model): account for sequence bounds when culling models --- src/lib/model/Model.ts | 5 +++ src/lib/model/ModelManager.ts | 10 ++--- src/lib/model/loader/ModelLoaderWorker.ts | 54 +++++++++++++++++------ src/lib/model/loader/types.ts | 11 ++++- src/lib/model/loader/util.ts | 26 ++++++++++- 5 files changed, 84 insertions(+), 22 deletions(-) diff --git a/src/lib/model/Model.ts b/src/lib/model/Model.ts index e8ba4ee..ff22995 100644 --- a/src/lib/model/Model.ts +++ b/src/lib/model/Model.ts @@ -27,6 +27,7 @@ class Model extends THREE.Object3D { this.#mesh = new THREE.Mesh(geometry, materials); } + this.#mesh.frustumCulled = false; this.#mesh.onBeforeRender = this.#onBeforeRender.bind(this); this.add(this.#mesh); @@ -44,6 +45,10 @@ class Model extends THREE.Object3D { this.alpha = 1.0; } + get boundingBox() { + return this.#mesh.geometry.boundingBox; + } + get boundingSphere() { return this.#mesh.geometry.boundingSphere; } diff --git a/src/lib/model/ModelManager.ts b/src/lib/model/ModelManager.ts index 09a6947..372f176 100644 --- a/src/lib/model/ModelManager.ts +++ b/src/lib/model/ModelManager.ts @@ -141,14 +141,14 @@ class ModelManager { // Bounds - geometry.boundingBox = new THREE.Box3().setFromArray(spec.geometry.bounds.extent); + geometry.boundingBox = new THREE.Box3().setFromArray(spec.bounds.extent); const boundsCenter = new THREE.Vector3( - spec.geometry.bounds.center[0], - spec.geometry.bounds.center[1], - spec.geometry.bounds.center[2], + spec.bounds.center[0], + spec.bounds.center[1], + spec.bounds.center[2], ); - geometry.boundingSphere = new THREE.Sphere(boundsCenter, spec.geometry.bounds.radius); + geometry.boundingSphere = new THREE.Sphere(boundsCenter, spec.bounds.radius); return geometry; } diff --git a/src/lib/model/loader/ModelLoaderWorker.ts b/src/lib/model/loader/ModelLoaderWorker.ts index 3b06fad..cf668f2 100644 --- a/src/lib/model/loader/ModelLoaderWorker.ts +++ b/src/lib/model/loader/ModelLoaderWorker.ts @@ -1,6 +1,6 @@ import { M2Batch, M2Model, M2SkinProfile } from '@wowserhq/format'; -import { ModelSpec } from './types.js'; -import { getBoundsCenter } from './util.js'; +import { ModelBounds, ModelSpec, SequenceSpec } from './types.js'; +import { expandExtent, getBoundsCenter, getBoundsRadius } from './util.js'; import SceneWorker from '../../worker/SceneWorker.js'; import { AssetHost, loadAsset } from '../../asset.js'; @@ -30,12 +30,21 @@ class ModelLoaderWorker extends SceneWorker { const geometry = this.#createGeometrySpec(model, skinProfile); const materials = this.#createMaterialSpecs(skinProfile); const { bones, skinned } = this.#createBoneSpecs(model); - const sequences = this.#createSequenceSpecs(model); + const { sequences, sequenceBounds } = this.#createSequenceSpecs(model); const loops = model.loops; const textureWeights = model.textureWeights; const textureTransforms = model.textureTransforms; const materialColors = model.colors; + // Expand geometry bounds by sequence bounds to produce model bounds + const extent = geometry.bounds.extent.slice(0); + expandExtent(extent, sequenceBounds.extent); + const bounds: ModelBounds = { + extent, + center: getBoundsCenter(extent), + radius: getBoundsRadius(extent), + }; + const spec: ModelSpec = { name: model.name, geometry, @@ -44,6 +53,7 @@ class ModelLoaderWorker extends SceneWorker { skinned, loops, sequences, + bounds, textureWeights, textureTransforms, materialColors, @@ -118,17 +128,33 @@ class ModelLoaderWorker extends SceneWorker { } #createSequenceSpecs(model: M2Model) { - return model.sequences.map((sequence) => ({ - id: sequence.id, - variationIndex: sequence.variationIndex, - duration: sequence.duration, - moveSpeed: sequence.moveSpeed, - flags: sequence.flags, - frequency: sequence.frequency, - blendTime: sequence.blendTime, - variationNext: sequence.variationNext, - aliasNext: sequence.aliasNext, - })); + const extent = new Float32Array(6); + const sequenceSpecs: SequenceSpec[] = []; + + for (const sequence of model.sequences) { + expandExtent(extent, sequence.bounds.extent); + + sequenceSpecs.push({ + id: sequence.id, + variationIndex: sequence.variationIndex, + duration: sequence.duration, + moveSpeed: sequence.moveSpeed, + flags: sequence.flags, + frequency: sequence.frequency, + blendTime: sequence.blendTime, + variationNext: sequence.variationNext, + aliasNext: sequence.aliasNext, + }); + } + + // Produce bounds that encompass all sequences + const sequenceBounds: ModelBounds = { + extent, + center: getBoundsCenter(extent), + radius: getBoundsRadius(extent), + }; + + return { sequences: sequenceSpecs, sequenceBounds }; } #createBoneSpecs(model: M2Model) { diff --git a/src/lib/model/loader/types.ts b/src/lib/model/loader/types.ts index 653463b..a538fee 100644 --- a/src/lib/model/loader/types.ts +++ b/src/lib/model/loader/types.ts @@ -9,6 +9,12 @@ import { M2Track, } from '@wowserhq/format'; +type ModelBounds = { + extent: Float32Array; + center: Float32Array; + radius: number; +}; + type TextureSpec = { flags: number; component: M2_TEXTURE_COMPONENT; @@ -33,7 +39,7 @@ type GroupSpec = { }; type GeometrySpec = { - bounds: { extent: Float32Array; center: Float32Array; radius: number }; + bounds: ModelBounds; vertexBuffer: ArrayBuffer; indexBuffer: ArrayBuffer; groups: GroupSpec[]; @@ -67,10 +73,11 @@ type ModelSpec = { bones: BoneSpec[]; skinned: boolean; sequences: SequenceSpec[]; + bounds: ModelBounds; loops: Uint32Array; textureWeights: M2TextureWeight[]; textureTransforms: M2TextureTransform[]; materialColors: M2Color[]; }; -export { ModelSpec, BoneSpec, MaterialSpec, TextureSpec, SequenceSpec }; +export { ModelBounds, ModelSpec, BoneSpec, MaterialSpec, TextureSpec, SequenceSpec }; diff --git a/src/lib/model/loader/util.ts b/src/lib/model/loader/util.ts index 26eb99e..af69978 100644 --- a/src/lib/model/loader/util.ts +++ b/src/lib/model/loader/util.ts @@ -1,3 +1,15 @@ +const _scratch = new Float32Array(3); + +const expandExtent = (extent: Float32Array, expand: Float32Array) => { + extent[0] = Math.min(extent[0], expand[0]); + extent[1] = Math.min(extent[1], expand[1]); + extent[2] = Math.min(extent[2], expand[2]); + + extent[3] = Math.max(extent[3], expand[3]); + extent[4] = Math.max(extent[4], expand[4]); + extent[5] = Math.max(extent[5], expand[5]); +}; + const getBoundsCenter = (extent: Float32Array) => { const center = new Float32Array(3); @@ -8,4 +20,16 @@ const getBoundsCenter = (extent: Float32Array) => { return center; }; -export { getBoundsCenter }; +const getBoundsRadius = (extent: Float32Array) => { + _scratch[0] = extent[3] - extent[0]; + _scratch[1] = extent[4] - extent[1]; + _scratch[2] = extent[5] - extent[2]; + + const size = Math.sqrt( + _scratch[0] * _scratch[0] + _scratch[1] * _scratch[1] + _scratch[2] * _scratch[2], + ); + + return size * 0.5; +}; + +export { expandExtent, getBoundsCenter, getBoundsRadius };