From 0a8f7190e121114809be102dbcba72ab778227c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Piellard?= Date: Mon, 4 Nov 2024 21:58:17 +0100 Subject: [PATCH] refactor: allow simplied input object for empty chunks --- .../merged/patch-factory-gpu-optimized.ts | 21 +++++++++++------- .../patch/patch-factory/patch-factory-base.ts | 22 +++++++------------ .../voxelmap/viewer/simple/voxelmap-viewer.ts | 5 ++++- .../voxels-renderable-factory-cpu-worker.ts | 8 +++---- .../cpu/voxels-renderable-factory-cpu.ts | 15 +++++-------- .../merged/gpu/voxels-computer-gpu.ts | 6 ++--- .../gpu/voxels-renderable-factory-gpu.ts | 10 +++------ .../voxels-renderable-factory-base.ts | 20 ++++++++++++----- src/test/map/trees/tree.ts | 3 +++ 9 files changed, 58 insertions(+), 52 deletions(-) diff --git a/src/lib/terrain/voxelmap/patch/patch-factory/merged/patch-factory-gpu-optimized.ts b/src/lib/terrain/voxelmap/patch/patch-factory/merged/patch-factory-gpu-optimized.ts index e9f7a487..e2755d24 100644 --- a/src/lib/terrain/voxelmap/patch/patch-factory/merged/patch-factory-gpu-optimized.ts +++ b/src/lib/terrain/voxelmap/patch/patch-factory/merged/patch-factory-gpu-optimized.ts @@ -1,17 +1,18 @@ -import * as THREE from '../../../../../libs/three-usage'; import { AsyncTask } from '../../../../../helpers/async/async-task'; -import { type VoxelsChunkOrdering, type IVoxelMap, type IVoxelMaterial, type VoxelsChunkSize } from '../../../i-voxelmap'; +import * as THREE from '../../../../../libs/three-usage'; +import { type IVoxelMap, type IVoxelMaterial, type VoxelsChunkOrdering, type VoxelsChunkSize } from '../../../i-voxelmap'; import { type VoxelsRenderable } from '../../../voxelsRenderable/voxels-renderable'; import { VoxelsRenderableFactoryGpu } from '../../../voxelsRenderable/voxelsRenderableFactory/merged/gpu/voxels-renderable-factory-gpu'; import { + type VoxelsChunkData, type CheckerboardType, type GeometryAndMaterial, } from '../../../voxelsRenderable/voxelsRenderableFactory/voxels-renderable-factory-base'; -import { PatchFactoryBase, type LocalMapData } from '../patch-factory-base'; +import { PatchFactoryBase } from '../patch-factory-base'; type PatchGenerationJob = { readonly patchId: number; - cpuTask: AsyncTask; + cpuTask: AsyncTask; gpuTask?: Promise; readonly resolve: (value: GeometryAndMaterial[]) => void; }; @@ -60,11 +61,11 @@ class PatchFactoryGpuOptimized extends PatchFactoryBase { this.pendingJobs.push({ patchId, - cpuTask: new AsyncTask(async () => { + cpuTask: new AsyncTask(() => { // logger.diagnostic(`CPU ${patchId} start`); - const result = await PatchFactoryBase.buildLocalMapData(patchStart, patchEnd, map); + // const result = await PatchFactoryBase.buildLocalMapData(patchStart, patchEnd, map); // logger.diagnostic(`CPU ${patchId} end`); - return result; + return PatchFactoryBase.buildLocalMapData(patchStart, patchEnd, map); }), resolve: onGeometryAndMaterialsListComputed, }); @@ -86,7 +87,11 @@ class PatchFactoryGpuOptimized extends PatchFactoryBase { if (!currentJob.gpuTask) { const localMapData = currentJob.cpuTask.getResultSync(); - currentJob.gpuTask = this.voxelsRenderableFactory.buildGeometryAndMaterials(localMapData); + if (localMapData.isEmpty) { + currentJob.gpuTask = Promise.resolve([]); + } else { + currentJob.gpuTask = this.voxelsRenderableFactory.buildGeometryAndMaterials(localMapData); + } currentJob.gpuTask.then(result => { this.pendingJobs.shift(); diff --git a/src/lib/terrain/voxelmap/patch/patch-factory/patch-factory-base.ts b/src/lib/terrain/voxelmap/patch/patch-factory/patch-factory-base.ts index 73941f4f..f30edb46 100644 --- a/src/lib/terrain/voxelmap/patch/patch-factory/patch-factory-base.ts +++ b/src/lib/terrain/voxelmap/patch/patch-factory/patch-factory-base.ts @@ -1,9 +1,10 @@ -import * as THREE from '../../../../libs/three-usage'; import { processAsap } from '../../../../helpers/async/async-sync'; import { vec3ToString } from '../../../../helpers/string'; -import { type VoxelsChunkOrdering, type IVoxelMap } from '../../i-voxelmap'; +import * as THREE from '../../../../libs/three-usage'; +import { type IVoxelMap } from '../../i-voxelmap'; import { type VoxelsRenderable } from '../../voxelsRenderable/voxels-renderable'; import { + type VoxelsChunkDataNotEmpty, type VoxelsChunkData, type VoxelsRenderableFactoryBase, } from '../../voxelsRenderable/voxelsRenderableFactory/voxels-renderable-factory-base'; @@ -16,13 +17,6 @@ type VertexData = { readonly roundnessY: boolean; }; -type LocalMapData = { - readonly size: THREE.Vector3; - readonly data: Uint16Array; - readonly dataOrdering: VoxelsChunkOrdering; - readonly isEmpty: boolean; -}; - abstract class PatchFactoryBase { public readonly maxPatchSize: THREE.Vector3; @@ -55,7 +49,7 @@ abstract class PatchFactoryBase { patchId: PatchId, patchStart: THREE.Vector3, patchEnd: THREE.Vector3, - voxelsChunkData: VoxelsChunkData + voxelsChunkData: VoxelsChunkDataNotEmpty ): Promise { patchStart = patchStart.clone(); patchEnd = patchEnd.clone(); @@ -76,8 +70,8 @@ abstract class PatchFactoryBase { return this.finalizePatch(voxelsRenderable, patchId, patchStart); } - public async buildVoxelsRenderable(voxelsChunkData: VoxelsChunkData): Promise { - return await this.voxelsRenderableFactory.buildVoxelsRenderable(voxelsChunkData); + public buildVoxelsRenderable(voxelsChunkData: VoxelsChunkData): null | Promise { + return this.voxelsRenderableFactory.buildVoxelsRenderable(voxelsChunkData); } public dispose(): void { @@ -90,7 +84,7 @@ abstract class PatchFactoryBase { map: IVoxelMap ): Promise; - protected static async buildLocalMapData(patchStart: THREE.Vector3, patchEnd: THREE.Vector3, map: IVoxelMap): Promise { + protected static async buildLocalMapData(patchStart: THREE.Vector3, patchEnd: THREE.Vector3, map: IVoxelMap): Promise { const cacheStart = patchStart.clone().subScalar(1); const cacheEnd = patchEnd.clone().addScalar(1); const cacheSize = new THREE.Vector3().subVectors(cacheEnd, cacheStart); @@ -124,4 +118,4 @@ abstract class PatchFactoryBase { } } -export { PatchFactoryBase, type LocalMapData, type VertexData }; +export { PatchFactoryBase, type VertexData }; diff --git a/src/lib/terrain/voxelmap/viewer/simple/voxelmap-viewer.ts b/src/lib/terrain/voxelmap/viewer/simple/voxelmap-viewer.ts index b2678f76..9f98efcc 100644 --- a/src/lib/terrain/voxelmap/viewer/simple/voxelmap-viewer.ts +++ b/src/lib/terrain/voxelmap/viewer/simple/voxelmap-viewer.ts @@ -2,7 +2,7 @@ import { AsyncTask } from '../../../../helpers/async/async-task'; import { PromisesQueue } from '../../../../helpers/async/promises-queue'; import { vec3ToString } from '../../../../helpers/string'; import * as THREE from '../../../../libs/three-usage'; -import { type VoxelsChunkOrdering, type IVoxelMaterial, type VoxelsChunkSize } from '../../i-voxelmap'; +import { type IVoxelMaterial, type VoxelsChunkOrdering, type VoxelsChunkSize } from '../../i-voxelmap'; import { PatchFactoryCpu } from '../../patch/patch-factory/merged/patch-factory-cpu'; import { PatchFactoryCpuWorker } from '../../patch/patch-factory/merged/patch-factory-cpu-worker'; import { PatchFactoryGpuSequential } from '../../patch/patch-factory/merged/patch-factory-gpu-sequential'; @@ -162,6 +162,9 @@ class VoxelmapViewer extends VoxelmapViewerBase { id: patchId, status: 'in-queue', computationTask: new AsyncTask(async () => { + if (voxelsChunkData.isEmpty) { + return null; + } const patchStart = new THREE.Vector3().multiplyVectors(patchId, this.patchSize); const patchEnd = new THREE.Vector3().addVectors(patchStart, this.patchSize); return await this.patchFactory.buildPatchFromVoxelsChunk(patchId, patchStart, patchEnd, voxelsChunkData); diff --git a/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/cpu/voxels-renderable-factory-cpu-worker.ts b/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/cpu/voxels-renderable-factory-cpu-worker.ts index 41ea5999..b479c51e 100644 --- a/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/cpu/voxels-renderable-factory-cpu-worker.ts +++ b/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/cpu/voxels-renderable-factory-cpu-worker.ts @@ -1,7 +1,7 @@ import { type WorkerDefinition } from '../../../../../../helpers/async/dedicatedWorkers/dedicated-worker'; import { DedicatedWorkersPool } from '../../../../../../helpers/async/dedicatedWorkers/dedicated-workers-pool'; -import { type VoxelsChunkOrdering, type IVoxelMaterial, type VoxelsChunkSize } from '../../../../i-voxelmap'; -import { type CheckerboardType, type VoxelsChunkData } from '../../voxels-renderable-factory-base'; +import { type IVoxelMaterial, type VoxelsChunkOrdering, type VoxelsChunkSize } from '../../../../i-voxelmap'; +import { type CheckerboardType, type VoxelsChunkDataNotEmpty } from '../../voxels-renderable-factory-base'; import { VoxelsRenderableFactoryCpu } from './voxels-renderable-factory-cpu'; @@ -28,12 +28,12 @@ class VoxelsRenderableFactoryCpuWorker extends VoxelsRenderableFactoryCpu { this.workersPoolSize = params.workersPoolSize; } - protected override buildBuffer(voxelsChunkData: VoxelsChunkData): Promise { + protected override buildBuffer(voxelsChunkData: VoxelsChunkDataNotEmpty): Promise { if (!this.workersPool) { const workerDefinition: WorkerDefinition = { commonCode: `const factory = ${this.serialize()};`, tasks: { - buildBuffer: (voxelsChunkData: VoxelsChunkData) => { + buildBuffer: (voxelsChunkData: VoxelsChunkDataNotEmpty) => { // eslint-disable-next-line no-eval const factory2 = eval('factory') as VoxelsRenderableFactoryCpu['serializableFactory']; const buffer = factory2.buildBuffer(voxelsChunkData); diff --git a/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/cpu/voxels-renderable-factory-cpu.ts b/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/cpu/voxels-renderable-factory-cpu.ts index 496213c3..64713045 100644 --- a/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/cpu/voxels-renderable-factory-cpu.ts +++ b/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/cpu/voxels-renderable-factory-cpu.ts @@ -1,11 +1,12 @@ import type * as THREE from '../../../../../../libs/three-usage'; -import { voxelmapDataPacking, type VoxelsChunkOrdering, type IVoxelMaterial, type VoxelsChunkSize } from '../../../../i-voxelmap'; +import { voxelmapDataPacking, type IVoxelMaterial, type VoxelsChunkOrdering, type VoxelsChunkSize } from '../../../../i-voxelmap'; import * as Cube from '../../cube'; import { type CheckerboardType, type GeometryAndMaterial, type VertexData, type VoxelsChunkData, + type VoxelsChunkDataNotEmpty, } from '../../voxels-renderable-factory-base'; import { type CheckerboardCellId } from '../vertex-data2-encoder'; import { VoxelsRenderableFactory } from '../voxels-renderable-factory'; @@ -53,7 +54,7 @@ class VoxelsRenderableFactoryCpu extends VoxelsRenderableFactory { greedyMeshing: true, voxelsChunkOrdering: 'zyx' as VoxelsChunkOrdering, - buildBuffer(voxelsChunkData: VoxelsChunkData): Uint32Array { + buildBuffer(voxelsChunkData: VoxelsChunkDataNotEmpty): Uint32Array { if (voxelsChunkData.isEmpty) { return new Uint32Array(); } @@ -187,7 +188,7 @@ class VoxelsRenderableFactoryCpu extends VoxelsRenderableFactory { return new Uint32Array(bufferData.buffer.subarray(0, uint32PerVertex * bufferData.verticesCount)); }, - buildLocalMapCache(voxelsChunkData: VoxelsChunkData): VoxelsChunkCache { + buildLocalMapCache(voxelsChunkData: VoxelsChunkDataNotEmpty): VoxelsChunkCache { type Component = 'x' | 'y' | 'z'; const buildIndexFactorComponent = (component: Component): number => { const sanitizeXYZ = (s: string | undefined): Component => { @@ -322,11 +323,7 @@ class VoxelsRenderableFactoryCpu extends VoxelsRenderableFactory { this.serializableFactory.voxelsChunkOrdering = params.voxelsChunkOrdering; } - public async buildGeometryAndMaterials(voxelsChunkData: VoxelsChunkData): Promise { - if (voxelsChunkData.isEmpty) { - return []; - } - + public async buildGeometryAndMaterials(voxelsChunkData: VoxelsChunkDataNotEmpty): Promise { if (voxelsChunkData.dataOrdering !== this.serializableFactory.voxelsChunkOrdering) { throw new Error( `Invalid data ordering: expected "${this.serializableFactory.voxelsChunkOrdering}" but received "${voxelsChunkData.dataOrdering}".` @@ -337,7 +334,7 @@ class VoxelsRenderableFactoryCpu extends VoxelsRenderableFactory { return this.assembleGeometryAndMaterials(buffer); } - protected async buildBuffer(voxelsChunkData: VoxelsChunkData): Promise { + protected async buildBuffer(voxelsChunkData: VoxelsChunkDataNotEmpty): Promise { return this.serializableFactory.buildBuffer(voxelsChunkData); } diff --git a/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/gpu/voxels-computer-gpu.ts b/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/gpu/voxels-computer-gpu.ts index 241cc2af..d4fde47a 100644 --- a/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/gpu/voxels-computer-gpu.ts +++ b/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/gpu/voxels-computer-gpu.ts @@ -1,13 +1,13 @@ /// -import type * as THREE from '../../../../../../libs/three-usage'; import { PromisesQueue } from '../../../../../../helpers/async/promises-queue'; import { logger } from '../../../../../../helpers/logger'; import { vec3ToString } from '../../../../../../helpers/string'; import { getGpuDevice } from '../../../../../../helpers/webgpu/webgpu-device'; +import type * as THREE from '../../../../../../libs/three-usage'; import { voxelmapDataPacking, type VoxelsChunkOrdering } from '../../../../i-voxelmap'; import * as Cube from '../../cube'; -import { type CheckerboardType, type VoxelsChunkData } from '../../voxels-renderable-factory-base'; +import { type VoxelsChunkDataNotEmpty, type CheckerboardType } from '../../voxels-renderable-factory-base'; import { type VertexData1Encoder } from '../vertex-data1-encoder'; import { type VertexData2Encoder } from '../vertex-data2-encoder'; @@ -240,7 +240,7 @@ class VoxelsComputerGpu { }); } - public async computeBuffer(voxelsChunkData: VoxelsChunkData): Promise { + public async computeBuffer(voxelsChunkData: VoxelsChunkDataNotEmpty): Promise { if (voxelsChunkData.dataOrdering !== this.voxelsChunkOrdering) { throw new Error( `Invalid data ordering: expected "${this.voxelsChunkOrdering}" but received "${voxelsChunkData.dataOrdering}".` diff --git a/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/gpu/voxels-renderable-factory-gpu.ts b/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/gpu/voxels-renderable-factory-gpu.ts index 583b0b4c..87b2ac31 100644 --- a/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/gpu/voxels-renderable-factory-gpu.ts +++ b/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/merged/gpu/voxels-renderable-factory-gpu.ts @@ -1,5 +1,5 @@ -import { type VoxelsChunkOrdering, type IVoxelMaterial, type VoxelsChunkSize } from '../../../../i-voxelmap'; -import { type CheckerboardType, type GeometryAndMaterial, type VoxelsChunkData } from '../../voxels-renderable-factory-base'; +import { type IVoxelMaterial, type VoxelsChunkOrdering, type VoxelsChunkSize } from '../../../../i-voxelmap'; +import { type VoxelsChunkDataNotEmpty, type CheckerboardType, type GeometryAndMaterial } from '../../voxels-renderable-factory-base'; import { VoxelsRenderableFactory } from '../voxels-renderable-factory'; import { VoxelsComputerGpu } from './voxels-computer-gpu'; @@ -35,11 +35,7 @@ class VoxelsRenderableFactoryGpu extends VoxelsRenderableFactory { this.voxelsComputerGpuPromise?.then(computer => computer.dispose()); } - public async buildGeometryAndMaterials(voxelsChunkData: VoxelsChunkData): Promise { - if (voxelsChunkData.isEmpty) { - return []; - } - + public async buildGeometryAndMaterials(voxelsChunkData: VoxelsChunkDataNotEmpty): Promise { const voxelsComputerGpu = await this.getVoxelsComputerGpu(); const buffer = await voxelsComputerGpu.computeBuffer(voxelsChunkData); return this.assembleGeometryAndMaterials(buffer); diff --git a/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/voxels-renderable-factory-base.ts b/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/voxels-renderable-factory-base.ts index dd5f2ed1..5262c3ef 100644 --- a/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/voxels-renderable-factory-base.ts +++ b/src/lib/terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/voxels-renderable-factory-base.ts @@ -21,12 +21,17 @@ type VertexData = { readonly roundnessY: boolean; }; -type VoxelsChunkData = { +type VoxelsChunkDataEmpty = { + readonly size: THREE.Vector3; + readonly isEmpty: true; +}; +type VoxelsChunkDataNotEmpty = { readonly size: THREE.Vector3; readonly data: Uint16Array; readonly dataOrdering: VoxelsChunkOrdering; - readonly isEmpty: boolean; + readonly isEmpty: false; }; +type VoxelsChunkData = VoxelsChunkDataEmpty | VoxelsChunkDataNotEmpty; type CheckerboardType = 'x' | 'y' | 'z' | 'xy' | 'xz' | 'yz' | 'xyz'; @@ -83,7 +88,7 @@ abstract class VoxelsRenderableFactoryBase { this.uniformsTemplate.uTexture.value = this.texture; } - public async buildVoxelsRenderable(voxelsChunkData: VoxelsChunkData): Promise { + public buildVoxelsRenderable(voxelsChunkData: VoxelsChunkData): null | Promise { const innerChunkSize = voxelsChunkData.size.clone().subScalar(2); if ( innerChunkSize.x > this.maxVoxelsChunkSize.x || @@ -97,8 +102,9 @@ abstract class VoxelsRenderableFactoryBase { return null; } - const geometryAndMaterialsList = await this.buildGeometryAndMaterials(voxelsChunkData); - return this.assembleVoxelsRenderable(innerChunkSize, geometryAndMaterialsList); + return this.buildGeometryAndMaterials(voxelsChunkData).then(geometryAndMaterialsList => { + return this.assembleVoxelsRenderable(innerChunkSize, geometryAndMaterialsList); + }); } public dispose(): void { @@ -142,7 +148,7 @@ abstract class VoxelsRenderableFactoryBase { return voxelsRenderable; } - public abstract buildGeometryAndMaterials(voxelsChunkData: VoxelsChunkData): Promise; + public abstract buildGeometryAndMaterials(voxelsChunkData: VoxelsChunkDataNotEmpty): Promise; private static buildMaterialsTexture( voxelMaterials: ReadonlyArray, @@ -191,4 +197,6 @@ export { type Parameters, type VertexData, type VoxelsChunkData, + type VoxelsChunkDataEmpty, + type VoxelsChunkDataNotEmpty, }; diff --git a/src/test/map/trees/tree.ts b/src/test/map/trees/tree.ts index d139cd79..a6bc76f5 100644 --- a/src/test/map/trees/tree.ts +++ b/src/test/map/trees/tree.ts @@ -80,6 +80,9 @@ class Tree { public getVoxel(position: THREE.Vector3Like): number | null { const index = this.buildIndex(position); + if (this.voxels.isEmpty) { + return null; + } const voxel = this.voxels.data[index]; if (typeof voxel === 'undefined') { throw new Error();