Skip to content

Commit

Permalink
feat: add VoxelmapViewer.setAdaptativeQuality() API
Browse files Browse the repository at this point in the history
  • Loading branch information
piellardj committed Nov 21, 2024
1 parent 2130211 commit b7de64e
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ abstract class PatchFactoryBase {
voxelsRenderable.container.name = `Voxels patch ${patchId.asString}`;
voxelsRenderable.container.position.set(patchStart.x, patchStart.y, patchStart.z);
voxelsRenderable.container.updateWorldMatrix(false, true);
voxelsRenderable.boundingBox.translate(new THREE.Vector3().copy(patchStart));
}
return voxelsRenderable;
}
Expand Down
49 changes: 46 additions & 3 deletions src/lib/terrain/voxelmap/viewer/simple/voxelmap-viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { PatchFactoryCpuWorker } from '../../patch/patch-factory/merged/patch-fa
import { PatchFactoryGpuSequential } from '../../patch/patch-factory/merged/patch-factory-gpu-sequential';
import { type PatchFactoryBase } from '../../patch/patch-factory/patch-factory-base';
import { PatchId } from '../../patch/patch-id';
import { EVoxelMaterialQuality } from '../../voxelsRenderable/voxels-material';
import { type VoxelsRenderable } from '../../voxelsRenderable/voxels-renderable';
import { type CheckerboardType, type VoxelsChunkData } from '../../voxelsRenderable/voxelsRenderableFactory/voxels-renderable-factory-base';
import { VoxelmapViewerBase, type ComputedPatch, type PatchRenderable } from '../voxelmap-viewer-base';
Expand Down Expand Up @@ -65,10 +66,17 @@ type EnqueuedPatchRenderable = {
invalidated: boolean;
};

type AdaptativeQualityParameters = {
readonly distanceThreshold: number;
readonly cameraPosition: THREE.Vector3Like;
};

class VoxelmapViewer extends VoxelmapViewerBase {
public readonly computationOptions: ComputationOptions;
public readonly maxPatchesComputedInParallel: number;

private adaptativeQuality: AdaptativeQualityParameters | null = null;

private readonly promiseThrottler: PromisesQueue;
private readonly patchFactory: PatchFactoryBase;

Expand Down Expand Up @@ -221,9 +229,13 @@ class VoxelmapViewer extends VoxelmapViewerBase {
invalidated: enqueuedPatch.invalidated,
};

if (voxelsRenderable && storedPatch.isVisible) {
this.container.add(voxelsRenderable.container);
this.notifyChange();
if (voxelsRenderable) {
this.updateVoxelsRenderableQuality(voxelsRenderable);

if (storedPatch.isVisible) {
this.container.add(voxelsRenderable.container);
this.notifyChange();
}
}

resolve('success');
Expand Down Expand Up @@ -321,6 +333,37 @@ class VoxelmapViewer extends VoxelmapViewerBase {
return new THREE.Box3(voxelFrom, voxelTo);
}

public setAdaptativeQuality(parameters: AdaptativeQualityParameters): void {
this.adaptativeQuality = {
distanceThreshold: parameters.distanceThreshold,
cameraPosition: {
x: parameters.cameraPosition.x,
y: parameters.cameraPosition.y,
z: parameters.cameraPosition.z,
},
};

for (const storedPatch of Object.values(this.patchesStore)) {
if (storedPatch.status === 'ready' && storedPatch.renderable) {
this.updateVoxelsRenderableQuality(storedPatch.renderable);
}
}
}

private updateVoxelsRenderableQuality(voxelsRenderable: VoxelsRenderable): void {
let quality = EVoxelMaterialQuality.HIGH;

if (this.adaptativeQuality) {
const cameraPosition = new THREE.Vector3().copy(this.adaptativeQuality.cameraPosition);
const distance = voxelsRenderable.boundingBox.distanceToPoint(cameraPosition);
if (distance > this.adaptativeQuality.distanceThreshold) {
quality = EVoxelMaterialQuality.LOW;
}
}

voxelsRenderable.quality = quality;
}

protected override get allLoadedPatches(): ComputedPatch[] {
const result: ComputedPatch[] = [];
for (const patch of Object.values(this.patchesStore)) {
Expand Down
9 changes: 7 additions & 2 deletions src/lib/terrain/voxelmap/voxelsRenderable/voxels-material.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ type VoxelsMaterial = THREE.Material & {
};
};

enum EVoxelMaterialQuality {
LOW = 0,
HIGH = 1,
}

type VoxelsMaterials = {
readonly material: VoxelsMaterial;
readonly materials: Record<EVoxelMaterialQuality, VoxelsMaterial>;
readonly shadowMaterial: THREE.Material;
};

export { EVoxelsDisplayMode, type VoxelsMaterial, type VoxelsMaterialUniforms, type VoxelsMaterials };
export { EVoxelMaterialQuality, EVoxelsDisplayMode, type VoxelsMaterial, type VoxelsMaterialUniforms, type VoxelsMaterials };
38 changes: 35 additions & 3 deletions src/lib/terrain/voxelmap/voxelsRenderable/voxels-renderable.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as THREE from '../../../libs/three-usage';

import { EVoxelsDisplayMode, type VoxelsMaterials } from './voxels-material';
import { EVoxelMaterialQuality, EVoxelsDisplayMode, type VoxelsMaterials } from './voxels-material';

type PatchMesh = {
readonly mesh: THREE.Mesh;
Expand Down Expand Up @@ -46,6 +46,21 @@ class VoxelsRenderable {
public readonly trianglesCount: number;
public readonly gpuMemoryBytes: number;

public readonly boundingBox: THREE.Box3;

private currentQuality: EVoxelMaterialQuality = EVoxelMaterialQuality.LOW;
public get quality(): EVoxelMaterialQuality {
return this.currentQuality;
}

public set quality(value: EVoxelMaterialQuality) {
if (this.currentQuality !== value) {
this.currentQuality = value;
this.enforceMaterials();
this.updateUniforms();
}
}

public constructor(patchMeshes: PatchMesh[]) {
this.gpuResources = { patchMeshes };

Expand All @@ -68,13 +83,19 @@ class VoxelsRenderable {
this.gpuMemoryBytes = gpuMemoryBytes;
}

this.boundingBox = new THREE.Box3();
for (const patchMesh of patchMeshes) {
this.boundingBox.union(patchMesh.mesh.geometry.boundingBox!);
}

this.enforceMaterials();
this.updateUniforms();
}

public updateUniforms(): void {
if (this.gpuResources) {
for (const patchMesh of this.gpuResources.patchMeshes) {
const material = patchMesh.materials.material;
const material = patchMesh.materials.materials[this.currentQuality];
const uniforms = material.userData.uniforms;

uniforms.uAoStrength.value = +this.parameters.ao.enabled * this.parameters.ao.strength;
Expand All @@ -101,13 +122,24 @@ class VoxelsRenderable {
for (const patchMesh of this.gpuResources.patchMeshes) {
patchMesh.mesh.removeFromParent();
patchMesh.mesh.geometry.dispose();
patchMesh.materials.material.dispose();

for (const material of Object.values(patchMesh.materials.materials)) {
material.dispose();
}
patchMesh.materials.shadowMaterial.dispose();
}

this.gpuResources = null;
}
}

private enforceMaterials(): void {
if (this.gpuResources) {
for (const patchMesh of this.gpuResources.patchMeshes) {
patchMesh.mesh.material = patchMesh.materials.materials[this.currentQuality];
}
}
}
}

export { EVoxelsDisplayMode, VoxelsRenderable };
Original file line number Diff line number Diff line change
Expand Up @@ -329,14 +329,23 @@ void main() {
}

private buildVoxelsMaterials(): VoxelsMaterials {
const material = this.buildThreeJsVoxelsMaterial({
enableAo: true,
enableNoise: true,
enableRoundedCorners: true,
enableGrid: false,
});
const shadowMaterial = this.buildShadowMaterial();
return { material, shadowMaterial };
return {
materials: {
0: this.buildThreeJsVoxelsMaterial({
enableAo: false,
enableNoise: false,
enableRoundedCorners: false,
enableGrid: false,
}),
1: this.buildThreeJsVoxelsMaterial({
enableAo: true,
enableNoise: true,
enableRoundedCorners: true,
enableGrid: false,
}),
},
shadowMaterial: this.buildShadowMaterial(),
};
}

public constructor(params: Parameters) {
Expand All @@ -361,7 +370,9 @@ void main() {

public override dispose(): void {
super.dispose();
this.materialsTemplates.material.dispose();
for (const material of Object.values(this.materialsTemplates.materials)) {
material.dispose();
}
this.materialsTemplates.shadowMaterial.dispose();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,20 +129,14 @@ abstract class VoxelsRenderableFactoryBase {
geometry.boundingBox = boundingBox.clone();
geometry.boundingSphere = boundingSphere.clone();

const material = geometryAndMaterial.materials.material;
const shadowMaterial = geometryAndMaterial.materials.shadowMaterial;
const mesh = new THREE.Mesh(geometryAndMaterial.geometry, material);
const mesh = new THREE.Mesh(geometryAndMaterial.geometry);
mesh.name = geometryAndMaterial.id;
mesh.customDepthMaterial = shadowMaterial;
mesh.customDepthMaterial = geometryAndMaterial.materials.shadowMaterial;
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.frustumCulled = true;

const materials = {
material,
shadowMaterial,
};
return { mesh, materials, trianglesCount, gpuMemoryBytes };
return { mesh, materials: geometryAndMaterial.materials, trianglesCount, gpuMemoryBytes };
})
);
return voxelsRenderable;
Expand Down
6 changes: 6 additions & 0 deletions src/test/test-terrain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ class TestTerrain extends TestTerrainBase {
voxelsChunkOrdering: 'zyx',
});
this.voxelmapViewer.parameters.faces.checkerboardContrast = 0.01;
setInterval(() => {
this.voxelmapViewer.setAdaptativeQuality({
distanceThreshold: 100,
cameraPosition: this.camera.getWorldPosition(new THREE.Vector3()),
});
}, 150);

const heightmapViewer = new HeightmapViewer(map, {
basePatchSize: chunkSize.xz,
Expand Down

0 comments on commit b7de64e

Please sign in to comment.