Feature: Implement wind simulation for foliage rendering across renderer, runtime, and viewport hosts
This commit is contained in:
@@ -473,6 +473,8 @@ export class FoliageInstancedRenderer {
|
|||||||
private scatter: FoliageScatterResult | null = null;
|
private scatter: FoliageScatterResult | null = null;
|
||||||
private prototypeRegistry: FoliagePrototypeRegistry = {};
|
private prototypeRegistry: FoliagePrototypeRegistry = {};
|
||||||
private quality: FoliageQualitySettings = resolveFoliageQualitySettings(null);
|
private quality: FoliageQualitySettings = resolveFoliageQualitySettings(null);
|
||||||
|
private windTime = 0;
|
||||||
|
private windUniforms: FoliageWindUniformSet[] = [];
|
||||||
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;
|
||||||
@@ -498,6 +500,18 @@ export class FoliageInstancedRenderer {
|
|||||||
this.group.userData.nonPickable = true;
|
this.group.userData.nonPickable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateWind(deltaSeconds: number) {
|
||||||
|
if (!this.shouldApplyWindShader()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Number.isFinite(deltaSeconds) && deltaSeconds > 0) {
|
||||||
|
this.windTime += deltaSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.writeWindUniforms();
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
@@ -536,6 +550,7 @@ export class FoliageInstancedRenderer {
|
|||||||
renderResourceSignature === this.renderResourceSignature &&
|
renderResourceSignature === this.renderResourceSignature &&
|
||||||
this.scatter !== null
|
this.scatter !== null
|
||||||
) {
|
) {
|
||||||
|
this.writeWindUniforms();
|
||||||
this.applyCurrentViewToRenderResources();
|
this.applyCurrentViewToRenderResources();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -668,6 +683,7 @@ export class FoliageInstancedRenderer {
|
|||||||
this.currentView = null;
|
this.currentView = null;
|
||||||
this.viewSignature = null;
|
this.viewSignature = null;
|
||||||
this.renderResourceSignature = null;
|
this.renderResourceSignature = null;
|
||||||
|
this.windUniforms = [];
|
||||||
this.sourceMeshPromisesByBundledPath.clear();
|
this.sourceMeshPromisesByBundledPath.clear();
|
||||||
this.clearActiveBatches();
|
this.clearActiveBatches();
|
||||||
}
|
}
|
||||||
@@ -677,6 +693,7 @@ export class FoliageInstancedRenderer {
|
|||||||
this.activeBatchKeyByChunkKey.clear();
|
this.activeBatchKeyByChunkKey.clear();
|
||||||
this.activeLodLevelByChunkKey.clear();
|
this.activeLodLevelByChunkKey.clear();
|
||||||
this.renderChunks = [];
|
this.renderChunks = [];
|
||||||
|
this.windUniforms = [];
|
||||||
|
|
||||||
if (this.activeBatchGroup === null) {
|
if (this.activeBatchGroup === null) {
|
||||||
return;
|
return;
|
||||||
@@ -696,6 +713,41 @@ export class FoliageInstancedRenderer {
|
|||||||
console.warn(message);
|
console.warn(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private shouldApplyWindShader(): boolean {
|
||||||
|
return (
|
||||||
|
this.quality.enabled &&
|
||||||
|
this.quality.densityMultiplier > 0 &&
|
||||||
|
this.quality.windEnabled
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerWindUniforms = (uniforms: FoliageWindUniformSet) => {
|
||||||
|
writeFoliageWindUniformValues(uniforms, this.quality, this.windTime);
|
||||||
|
this.windUniforms.push(uniforms);
|
||||||
|
};
|
||||||
|
|
||||||
|
private writeWindUniforms() {
|
||||||
|
if (!this.shouldApplyWindShader()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const uniforms of this.windUniforms) {
|
||||||
|
writeFoliageWindUniformValues(uniforms, this.quality, this.windTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getWindMaterialOptions(): FoliageWindMaterialOptions | undefined {
|
||||||
|
if (!this.shouldApplyWindShader()) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getSettings: () => this.quality,
|
||||||
|
getTime: () => this.windTime,
|
||||||
|
registerUniforms: this.registerWindUniforms
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private loadTemplateSourceMeshes(
|
private loadTemplateSourceMeshes(
|
||||||
bundledPath: string
|
bundledPath: string
|
||||||
): Promise<FoliageTemplateSourceMesh[]> {
|
): Promise<FoliageTemplateSourceMesh[]> {
|
||||||
@@ -761,8 +813,12 @@ export class FoliageInstancedRenderer {
|
|||||||
batchGroup.userData.foliageLayerId = batch.layerId;
|
batchGroup.userData.foliageLayerId = batch.layerId;
|
||||||
batchGroup.userData.foliageTerrainId = batch.terrainId;
|
batchGroup.userData.foliageTerrainId = batch.terrainId;
|
||||||
|
|
||||||
|
const windOptions = this.getWindMaterialOptions();
|
||||||
|
|
||||||
for (const sourceMesh of sourceMeshes) {
|
for (const sourceMesh of sourceMeshes) {
|
||||||
batchGroup.add(createInstancedMeshForSource(batch, sourceMesh));
|
batchGroup.add(
|
||||||
|
createInstancedMeshForSource(batch, sourceMesh, windOptions)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyRendererRenderCategoryFromMaterial(batchGroup);
|
applyRendererRenderCategoryFromMaterial(batchGroup);
|
||||||
|
|||||||
@@ -5704,6 +5704,7 @@ export class RuntimeHost {
|
|||||||
|
|
||||||
this.updateTerrainLodVisibility();
|
this.updateTerrainLodVisibility();
|
||||||
this.foliageRenderer.updateView(this.camera);
|
this.foliageRenderer.updateView(this.camera);
|
||||||
|
this.foliageRenderer.updateWind(simulationDt);
|
||||||
this.updateUnderwaterSceneFog();
|
this.updateUnderwaterSceneFog();
|
||||||
this.syncCelestialShadowState();
|
this.syncCelestialShadowState();
|
||||||
|
|
||||||
|
|||||||
@@ -14188,6 +14188,7 @@ export class ViewportHost {
|
|||||||
this.updateTransformGizmoPose();
|
this.updateTransformGizmoPose();
|
||||||
this.updateTerrainLodVisibility();
|
this.updateTerrainLodVisibility();
|
||||||
this.foliageRenderer.updateView(this.getActiveCamera());
|
this.foliageRenderer.updateView(this.getActiveCamera());
|
||||||
|
this.foliageRenderer.updateWind(dt);
|
||||||
this.volumeTime += dt;
|
this.volumeTime += dt;
|
||||||
|
|
||||||
for (const uniform of this.volumeAnimatedUniforms) {
|
for (const uniform of this.volumeAnimatedUniforms) {
|
||||||
|
|||||||
@@ -341,7 +341,11 @@ describe("foliage render batch helpers", () => {
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
densityMultiplier: 1,
|
densityMultiplier: 1,
|
||||||
maxDistanceMultiplier: 1,
|
maxDistanceMultiplier: 1,
|
||||||
shadows: "near"
|
shadows: "near",
|
||||||
|
windEnabled: true,
|
||||||
|
windStrength: 1,
|
||||||
|
windSpeed: 1,
|
||||||
|
windDirectionDegrees: 35
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -373,7 +377,11 @@ describe("foliage render batch helpers", () => {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
densityMultiplier: 1,
|
densityMultiplier: 1,
|
||||||
maxDistanceMultiplier: 1,
|
maxDistanceMultiplier: 1,
|
||||||
shadows: "near"
|
shadows: "near",
|
||||||
|
windEnabled: true,
|
||||||
|
windStrength: 1,
|
||||||
|
windSpeed: 1,
|
||||||
|
windDirectionDegrees: 35
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user