Refactor foliage rendering view state and chunk visibility tracking
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
|||||||
InstancedMesh,
|
InstancedMesh,
|
||||||
Matrix4,
|
Matrix4,
|
||||||
Mesh,
|
Mesh,
|
||||||
|
Sphere,
|
||||||
Vector3,
|
Vector3,
|
||||||
type BufferGeometry,
|
type BufferGeometry,
|
||||||
type Material
|
type Material
|
||||||
@@ -20,7 +21,6 @@ import { applyRendererRenderCategoryFromMaterial } from "../rendering/render-lay
|
|||||||
import { loadBundledFoliageModelTemplate } from "./bundled-foliage-model-loader";
|
import { loadBundledFoliageModelTemplate } from "./bundled-foliage-model-loader";
|
||||||
import {
|
import {
|
||||||
createFoliageInstanceMatrix,
|
createFoliageInstanceMatrix,
|
||||||
createFoliageRenderBatchKey,
|
|
||||||
createFoliageRenderResourcePlan,
|
createFoliageRenderResourcePlan,
|
||||||
resolveFoliageRenderChunkLod,
|
resolveFoliageRenderChunkLod,
|
||||||
type FoliageRenderBatch,
|
type FoliageRenderBatch,
|
||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
import type {
|
import type {
|
||||||
FoliageLayer,
|
FoliageLayer,
|
||||||
FoliageLayerRegistry,
|
FoliageLayerRegistry,
|
||||||
|
FoliagePrototypeLodLevel,
|
||||||
FoliagePrototypeRegistry
|
FoliagePrototypeRegistry
|
||||||
} from "./foliage";
|
} from "./foliage";
|
||||||
import {
|
import {
|
||||||
@@ -170,24 +171,28 @@ function scaleFoliageLayerRegistryDensities(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRenderViewFromCamera(camera: Camera): FoliageRenderView {
|
function writeRenderViewFromCamera(
|
||||||
|
camera: Camera,
|
||||||
|
view: FoliageRenderView,
|
||||||
|
cameraPosition: Vector3,
|
||||||
|
projectionViewMatrix: Matrix4,
|
||||||
|
frustum: Frustum
|
||||||
|
): FoliageRenderView {
|
||||||
camera.updateMatrixWorld();
|
camera.updateMatrixWorld();
|
||||||
camera.matrixWorldInverse.copy(camera.matrixWorld).invert();
|
camera.matrixWorldInverse.copy(camera.matrixWorld).invert();
|
||||||
const cameraPosition = new Vector3();
|
|
||||||
camera.getWorldPosition(cameraPosition);
|
camera.getWorldPosition(cameraPosition);
|
||||||
const projectionViewMatrix = new Matrix4().multiplyMatrices(
|
projectionViewMatrix.multiplyMatrices(
|
||||||
camera.projectionMatrix,
|
camera.projectionMatrix,
|
||||||
camera.matrixWorldInverse
|
camera.matrixWorldInverse
|
||||||
);
|
);
|
||||||
|
frustum.setFromProjectionMatrix(projectionViewMatrix);
|
||||||
|
|
||||||
return {
|
view.cameraPosition.x = cameraPosition.x;
|
||||||
cameraPosition: {
|
view.cameraPosition.y = cameraPosition.y;
|
||||||
x: cameraPosition.x,
|
view.cameraPosition.z = cameraPosition.z;
|
||||||
y: cameraPosition.y,
|
view.frustum = frustum;
|
||||||
z: cameraPosition.z
|
|
||||||
},
|
return view;
|
||||||
frustum: new Frustum().setFromProjectionMatrix(projectionViewMatrix)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCameraViewSignature(camera: Camera): string {
|
function createCameraViewSignature(camera: Camera): string {
|
||||||
@@ -295,6 +300,11 @@ export class FoliageInstancedRenderer {
|
|||||||
private requestId = 0;
|
private requestId = 0;
|
||||||
private activeBatchGroup: Group | null = null;
|
private activeBatchGroup: Group | null = null;
|
||||||
private batchGroupsByKey = new Map<string, Group>();
|
private batchGroupsByKey = new Map<string, Group>();
|
||||||
|
private activeBatchKeyByChunkKey = new Map<string, string>();
|
||||||
|
private activeLodLevelByChunkKey = new Map<
|
||||||
|
string,
|
||||||
|
FoliagePrototypeLodLevel
|
||||||
|
>();
|
||||||
private renderChunks: FoliageRenderChunk[] = [];
|
private renderChunks: FoliageRenderChunk[] = [];
|
||||||
private scatter: FoliageScatterResult | null = null;
|
private scatter: FoliageScatterResult | null = null;
|
||||||
private prototypeRegistry: FoliagePrototypeRegistry = {};
|
private prototypeRegistry: FoliagePrototypeRegistry = {};
|
||||||
@@ -302,6 +312,14 @@ export class FoliageInstancedRenderer {
|
|||||||
private currentView: FoliageRenderView | null = null;
|
private currentView: FoliageRenderView | null = null;
|
||||||
private viewSignature: string | null = null;
|
private viewSignature: string | null = null;
|
||||||
private renderResourceSignature: string | null = null;
|
private renderResourceSignature: string | null = null;
|
||||||
|
private readonly renderViewCameraPosition = new Vector3();
|
||||||
|
private readonly renderViewProjectionMatrix = new Matrix4();
|
||||||
|
private readonly renderViewFrustum = new Frustum();
|
||||||
|
private readonly renderView: FoliageRenderView = {
|
||||||
|
cameraPosition: { x: 0, y: 0, z: 0 },
|
||||||
|
frustum: this.renderViewFrustum
|
||||||
|
};
|
||||||
|
private readonly chunkFrustumSphere = new Sphere();
|
||||||
private readonly sourceMeshPromisesByBundledPath = new Map<
|
private readonly sourceMeshPromisesByBundledPath = new Map<
|
||||||
string,
|
string,
|
||||||
Promise<FoliageTemplateSourceMesh[]>
|
Promise<FoliageTemplateSourceMesh[]>
|
||||||
@@ -319,6 +337,7 @@ export class FoliageInstancedRenderer {
|
|||||||
sync(input: FoliageInstancedRendererSyncInput) {
|
sync(input: FoliageInstancedRendererSyncInput) {
|
||||||
const terrains = normalizeTerrainRegistry(input.terrains);
|
const terrains = normalizeTerrainRegistry(input.terrains);
|
||||||
const quality = resolveFoliageQualitySettings(input.quality);
|
const quality = resolveFoliageQualitySettings(input.quality);
|
||||||
|
const previousMaxDistanceMultiplier = this.quality.maxDistanceMultiplier;
|
||||||
const prototypeRegistry = createFoliageScatterPrototypeRegistry({
|
const prototypeRegistry = createFoliageScatterPrototypeRegistry({
|
||||||
foliagePrototypes: input.foliagePrototypes,
|
foliagePrototypes: input.foliagePrototypes,
|
||||||
bundledFoliagePrototypes: input.bundledFoliagePrototypes
|
bundledFoliagePrototypes: input.bundledFoliagePrototypes
|
||||||
@@ -337,6 +356,10 @@ export class FoliageInstancedRenderer {
|
|||||||
this.quality = quality;
|
this.quality = quality;
|
||||||
this.prototypeRegistry = prototypeRegistry;
|
this.prototypeRegistry = prototypeRegistry;
|
||||||
|
|
||||||
|
if (quality.maxDistanceMultiplier !== previousMaxDistanceMultiplier) {
|
||||||
|
this.resetActiveChunkViewState();
|
||||||
|
}
|
||||||
|
|
||||||
if (!quality.enabled || quality.densityMultiplier <= 0) {
|
if (!quality.enabled || quality.densityMultiplier <= 0) {
|
||||||
this.scatter = null;
|
this.scatter = null;
|
||||||
this.renderResourceSignature = null;
|
this.renderResourceSignature = null;
|
||||||
@@ -364,7 +387,13 @@ export class FoliageInstancedRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateView(camera: Camera) {
|
updateView(camera: Camera) {
|
||||||
this.currentView = createRenderViewFromCamera(camera);
|
this.currentView = writeRenderViewFromCamera(
|
||||||
|
camera,
|
||||||
|
this.renderView,
|
||||||
|
this.renderViewCameraPosition,
|
||||||
|
this.renderViewProjectionMatrix,
|
||||||
|
this.renderViewFrustum
|
||||||
|
);
|
||||||
|
|
||||||
if (this.scatter === null) {
|
if (this.scatter === null) {
|
||||||
return;
|
return;
|
||||||
@@ -412,34 +441,60 @@ export class FoliageInstancedRenderer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const visibleBatchKeys = new Set<string>();
|
|
||||||
|
|
||||||
for (const chunk of this.renderChunks) {
|
for (const chunk of this.renderChunks) {
|
||||||
|
const previousBatchKey =
|
||||||
|
this.activeBatchKeyByChunkKey.get(chunk.key) ?? null;
|
||||||
|
const previousLodLevel =
|
||||||
|
this.activeLodLevelByChunkKey.get(chunk.key) ?? null;
|
||||||
const renderLod = resolveFoliageRenderChunkLod({
|
const renderLod = resolveFoliageRenderChunkLod({
|
||||||
chunk,
|
chunk,
|
||||||
view: this.currentView,
|
view: this.currentView,
|
||||||
quality: this.quality
|
quality: this.quality,
|
||||||
|
previousLodLevel,
|
||||||
|
frustumSphere: this.chunkFrustumSphere
|
||||||
});
|
});
|
||||||
|
const nextBatchKey =
|
||||||
|
renderLod === null
|
||||||
|
? null
|
||||||
|
: (chunk.batchKeysByLodLevel[renderLod.level] ?? null);
|
||||||
|
|
||||||
if (renderLod === null) {
|
if (nextBatchKey === previousBatchKey) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
visibleBatchKeys.add(
|
if (previousBatchKey !== null) {
|
||||||
createFoliageRenderBatchKey({
|
this.setBatchGroupVisibility(previousBatchKey, false);
|
||||||
chunkId: chunk.chunkId,
|
}
|
||||||
terrainId: chunk.terrainId,
|
|
||||||
layerId: chunk.layerId,
|
if (nextBatchKey === null || renderLod === null) {
|
||||||
prototypeId: chunk.prototypeId,
|
this.activeBatchKeyByChunkKey.delete(chunk.key);
|
||||||
lodLevel: renderLod.level,
|
this.activeLodLevelByChunkKey.delete(chunk.key);
|
||||||
bundledPath: renderLod.bundledPath
|
continue;
|
||||||
})
|
}
|
||||||
);
|
|
||||||
|
this.setBatchGroupVisibility(nextBatchKey, true);
|
||||||
|
this.activeBatchKeyByChunkKey.set(chunk.key, nextBatchKey);
|
||||||
|
this.activeLodLevelByChunkKey.set(chunk.key, renderLod.level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setBatchGroupVisibility(batchKey: string, visible: boolean) {
|
||||||
|
const batchGroup = this.batchGroupsByKey.get(batchKey);
|
||||||
|
|
||||||
|
if (batchGroup === undefined || batchGroup.visible === visible) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [batchKey, batchGroup] of this.batchGroupsByKey) {
|
batchGroup.visible = visible;
|
||||||
batchGroup.visible = visibleBatchKeys.has(batchKey);
|
}
|
||||||
|
|
||||||
|
private resetActiveChunkViewState() {
|
||||||
|
for (const batchKey of this.activeBatchKeyByChunkKey.values()) {
|
||||||
|
this.setBatchGroupVisibility(batchKey, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.activeBatchKeyByChunkKey.clear();
|
||||||
|
this.activeLodLevelByChunkKey.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
@@ -455,6 +510,8 @@ export class FoliageInstancedRenderer {
|
|||||||
|
|
||||||
private clearActiveBatches() {
|
private clearActiveBatches() {
|
||||||
this.batchGroupsByKey.clear();
|
this.batchGroupsByKey.clear();
|
||||||
|
this.activeBatchKeyByChunkKey.clear();
|
||||||
|
this.activeLodLevelByChunkKey.clear();
|
||||||
this.renderChunks = [];
|
this.renderChunks = [];
|
||||||
|
|
||||||
if (this.activeBatchGroup === null) {
|
if (this.activeBatchGroup === null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user