auto-git:
[change] src/rendering/precomputed-shader-sky-environment-cache.ts [change] src/rendering/quantized-environment-blend-cache.ts [change] src/rendering/world-background-renderer.ts [change] src/rendering/world-shader-sky.ts [change] src/runtime-three/runtime-host.ts
This commit is contained in:
@@ -106,14 +106,14 @@ class RendererShaderSkyEnvironmentTextureBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PrecomputedShaderSkyEnvironmentCache
|
export class PrecomputedShaderSkyEnvironmentCache implements WorldShaderSkyEnvironmentTextureResolver {
|
||||||
implements WorldShaderSkyEnvironmentTextureResolver
|
|
||||||
{
|
|
||||||
private currentKey: string | null = null;
|
private currentKey: string | null = null;
|
||||||
private readonly phaseEntries = createEmptyPhaseEntries();
|
private readonly phaseEntries = createEmptyPhaseEntries();
|
||||||
private disposed = false;
|
private disposed = false;
|
||||||
|
|
||||||
constructor(private readonly options: PrecomputedShaderSkyEnvironmentCacheOptions) {}
|
constructor(
|
||||||
|
private readonly options: PrecomputedShaderSkyEnvironmentCacheOptions
|
||||||
|
) {}
|
||||||
|
|
||||||
syncPhaseTextures(states: WorldShaderSkyEnvironmentPhaseStates) {
|
syncPhaseTextures(states: WorldShaderSkyEnvironmentPhaseStates) {
|
||||||
if (this.disposed) {
|
if (this.disposed) {
|
||||||
@@ -132,9 +132,7 @@ export class PrecomputedShaderSkyEnvironmentCache
|
|||||||
for (const phase of SHADER_SKY_PHASES) {
|
for (const phase of SHADER_SKY_PHASES) {
|
||||||
const state = states[phase];
|
const state = states[phase];
|
||||||
nextEntries[phase] =
|
nextEntries[phase] =
|
||||||
state === null
|
state === null ? null : this.options.buildEnvironmentTexture(state);
|
||||||
? null
|
|
||||||
: this.options.buildEnvironmentTexture(state);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
disposePhaseEntries(nextEntries);
|
disposePhaseEntries(nextEntries);
|
||||||
@@ -166,7 +164,8 @@ export class PrecomputedShaderSkyEnvironmentCache
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseTexture = this.phaseEntries[phaseBlend.basePhase]?.texture ?? null;
|
const baseTexture =
|
||||||
|
this.phaseEntries[phaseBlend.basePhase]?.texture ?? null;
|
||||||
|
|
||||||
if (baseTexture === null) {
|
if (baseTexture === null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -188,8 +187,7 @@ export class PrecomputedShaderSkyEnvironmentCache
|
|||||||
baseTexture,
|
baseTexture,
|
||||||
overlayTexture,
|
overlayTexture,
|
||||||
phaseBlend.blendAmount
|
phaseBlend.blendAmount
|
||||||
) ??
|
) ?? (phaseBlend.blendAmount >= 0.5 ? overlayTexture : baseTexture)
|
||||||
(phaseBlend.blendAmount >= 0.5 ? overlayTexture : baseTexture)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,20 +109,16 @@ function resolveTextureDimensions(texture: Texture): {
|
|||||||
height?: number;
|
height?: number;
|
||||||
}
|
}
|
||||||
| undefined;
|
| undefined;
|
||||||
const sourceData = texture.source.data as
|
const sourceData = texture.source.data as {
|
||||||
| {
|
width?: number;
|
||||||
width?: number;
|
height?: number;
|
||||||
height?: number;
|
} | null;
|
||||||
}
|
|
||||||
| null;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
width: Math.max(
|
width: Math.max(
|
||||||
1,
|
1,
|
||||||
Math.floor(
|
Math.floor(
|
||||||
image?.width ??
|
image?.width ?? sourceData?.width ?? DEFAULT_BLEND_RENDER_TARGET_WIDTH
|
||||||
sourceData?.width ??
|
|
||||||
DEFAULT_BLEND_RENDER_TARGET_WIDTH
|
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
height: Math.max(
|
height: Math.max(
|
||||||
@@ -186,9 +182,7 @@ export function createQuantizedEnvironmentBlendCacheKey(
|
|||||||
return `${getOrderedPairCacheKey(baseTexture, overlayTexture)}@${bucketIndex}`;
|
return `${getOrderedPairCacheKey(baseTexture, overlayTexture)}@${bucketIndex}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QuantizedEnvironmentBlendCache
|
export class QuantizedEnvironmentBlendCache implements WorldEnvironmentBlendTextureResolver {
|
||||||
implements WorldEnvironmentBlendTextureResolver
|
|
||||||
{
|
|
||||||
private readonly bucketCount: number;
|
private readonly bucketCount: number;
|
||||||
private readonly buildBlendTexture: QuantizedEnvironmentBlendCacheOptions["buildBlendTexture"];
|
private readonly buildBlendTexture: QuantizedEnvironmentBlendCacheOptions["buildBlendTexture"];
|
||||||
private readonly disposeBuildResources: (() => void) | undefined;
|
private readonly disposeBuildResources: (() => void) | undefined;
|
||||||
@@ -208,9 +202,7 @@ export class QuantizedEnvironmentBlendCache
|
|||||||
constructor(options: QuantizedEnvironmentBlendCacheOptions) {
|
constructor(options: QuantizedEnvironmentBlendCacheOptions) {
|
||||||
this.bucketCount = Math.max(
|
this.bucketCount = Math.max(
|
||||||
1,
|
1,
|
||||||
Math.floor(
|
Math.floor(options.bucketCount ?? DEFAULT_QUANTIZED_BLEND_BUCKET_COUNT)
|
||||||
options.bucketCount ?? DEFAULT_QUANTIZED_BLEND_BUCKET_COUNT
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
this.buildBlendTexture = options.buildBlendTexture;
|
this.buildBlendTexture = options.buildBlendTexture;
|
||||||
this.disposeBuildResources = options.disposeBuildResources;
|
this.disposeBuildResources = options.disposeBuildResources;
|
||||||
@@ -359,7 +351,11 @@ export class QuantizedEnvironmentBlendCache
|
|||||||
}
|
}
|
||||||
|
|
||||||
private schedulePendingBuilds() {
|
private schedulePendingBuilds() {
|
||||||
if (this.disposed || this.buildScheduled || this.pendingBuilds.length === 0) {
|
if (
|
||||||
|
this.disposed ||
|
||||||
|
this.buildScheduled ||
|
||||||
|
this.pendingBuilds.length === 0
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -756,8 +756,7 @@ function applyShaderSkyStateToMaterial(
|
|||||||
material.uniforms.uCloudSoftness.value = state.clouds.softness;
|
material.uniforms.uCloudSoftness.value = state.clouds.softness;
|
||||||
material.uniforms.uCloudScale.value = state.clouds.scale;
|
material.uniforms.uCloudScale.value = state.clouds.scale;
|
||||||
material.uniforms.uCloudHeight.value = state.clouds.height;
|
material.uniforms.uCloudHeight.value = state.clouds.height;
|
||||||
material.uniforms.uCloudHeightVariation.value =
|
material.uniforms.uCloudHeightVariation.value = state.clouds.heightVariation;
|
||||||
state.clouds.heightVariation;
|
|
||||||
material.uniforms.uCloudTint.value.set(state.clouds.tintHex);
|
material.uniforms.uCloudTint.value.set(state.clouds.tintHex);
|
||||||
material.uniforms.uCloudOpacity.value = state.clouds.opacity;
|
material.uniforms.uCloudOpacity.value = state.clouds.opacity;
|
||||||
material.uniforms.uCloudOpacityRandomness.value =
|
material.uniforms.uCloudOpacityRandomness.value =
|
||||||
@@ -904,7 +903,9 @@ export class WorldBackgroundRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyShaderSkyStateToMaterial(this.shaderSkyMaterial, shaderSkyState);
|
applyShaderSkyStateToMaterial(this.shaderSkyMaterial, shaderSkyState);
|
||||||
this.syncEnvironmentCaptureState(showShaderBackground ? shaderSkyState : null);
|
this.syncEnvironmentCaptureState(
|
||||||
|
showShaderBackground ? shaderSkyState : null
|
||||||
|
);
|
||||||
this.shaderMesh.visible = showShaderBackground;
|
this.shaderMesh.visible = showShaderBackground;
|
||||||
this.gradientMesh.visible = !showShaderBackground && !showImageBackground;
|
this.gradientMesh.visible = !showShaderBackground && !showImageBackground;
|
||||||
this.imageMesh.visible = showImageBackground;
|
this.imageMesh.visible = showImageBackground;
|
||||||
|
|||||||
@@ -98,7 +98,10 @@ function resolveTimeWindowMidpoint(
|
|||||||
startTimeOfDayHours: number,
|
startTimeOfDayHours: number,
|
||||||
endTimeOfDayHours: number
|
endTimeOfDayHours: number
|
||||||
): number {
|
): number {
|
||||||
const wrappedEndTime = wrapTimeForward(endTimeOfDayHours, startTimeOfDayHours);
|
const wrappedEndTime = wrapTimeForward(
|
||||||
|
endTimeOfDayHours,
|
||||||
|
startTimeOfDayHours
|
||||||
|
);
|
||||||
|
|
||||||
return normalizeTimeOfDayHours(
|
return normalizeTimeOfDayHours(
|
||||||
startTimeOfDayHours + (wrappedEndTime - startTimeOfDayHours) / 2
|
startTimeOfDayHours + (wrappedEndTime - startTimeOfDayHours) / 2
|
||||||
@@ -423,10 +426,8 @@ export function resolveWorldShaderSkyEnvironmentPhaseStates(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const dawnHalfDuration =
|
const dawnHalfDuration = Math.max(timeSettings.dawnDurationHours, 0.001) / 2;
|
||||||
Math.max(timeSettings.dawnDurationHours, 0.001) / 2;
|
const duskHalfDuration = Math.max(timeSettings.duskDurationHours, 0.001) / 2;
|
||||||
const duskHalfDuration =
|
|
||||||
Math.max(timeSettings.duskDurationHours, 0.001) / 2;
|
|
||||||
const dawnStart = timeSettings.sunriseTimeOfDayHours - dawnHalfDuration;
|
const dawnStart = timeSettings.sunriseTimeOfDayHours - dawnHalfDuration;
|
||||||
const dawnEnd = timeSettings.sunriseTimeOfDayHours + dawnHalfDuration;
|
const dawnEnd = timeSettings.sunriseTimeOfDayHours + dawnHalfDuration;
|
||||||
const duskStart = timeSettings.sunsetTimeOfDayHours - duskHalfDuration;
|
const duskStart = timeSettings.sunsetTimeOfDayHours - duskHalfDuration;
|
||||||
@@ -549,9 +550,9 @@ export function createWorldShaderSkyEnvironmentPhaseCacheKey(
|
|||||||
export function resolveWorldShaderSkyEnvironmentPhaseBlend(
|
export function resolveWorldShaderSkyEnvironmentPhaseBlend(
|
||||||
state: Pick<WorldShaderSkyRenderState, "time">
|
state: Pick<WorldShaderSkyRenderState, "time">
|
||||||
): WorldShaderSkyEnvironmentPhaseBlend | null {
|
): WorldShaderSkyEnvironmentPhaseBlend | null {
|
||||||
const activePhases = (
|
const activePhases = (["day", "dawn", "dusk", "night"] as const).filter(
|
||||||
["day", "dawn", "dusk", "night"] as const
|
(phase) => state.time.phaseWeights[phase] > 1e-4
|
||||||
).filter((phase) => state.time.phaseWeights[phase] > 1e-4);
|
);
|
||||||
|
|
||||||
if (activePhases.length === 0) {
|
if (activePhases.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -180,7 +180,10 @@ import {
|
|||||||
buildRuntimeNpcCollider,
|
buildRuntimeNpcCollider,
|
||||||
createRuntimeNpcFromDefinition
|
createRuntimeNpcFromDefinition
|
||||||
} from "./runtime-scene-build";
|
} from "./runtime-scene-build";
|
||||||
import { resolvePlayerStartLookInput, resolvePlayerStartPauseInput } from "./player-input-bindings";
|
import {
|
||||||
|
resolvePlayerStartLookInput,
|
||||||
|
resolvePlayerStartPauseInput
|
||||||
|
} from "./player-input-bindings";
|
||||||
|
|
||||||
interface CachedMaterialTexture {
|
interface CachedMaterialTexture {
|
||||||
signature: string;
|
signature: string;
|
||||||
@@ -584,7 +587,8 @@ export class RuntimeHost {
|
|||||||
desiredCameraPosition,
|
desiredCameraPosition,
|
||||||
radius
|
radius
|
||||||
) ?? { ...desiredCameraPosition },
|
) ?? { ...desiredCameraPosition },
|
||||||
isCameraDrivenExternally: () => this.resolveActiveRuntimeCameraRig() !== null,
|
isCameraDrivenExternally: () =>
|
||||||
|
this.resolveActiveRuntimeCameraRig() !== null,
|
||||||
getCameraYawRadians: () => {
|
getCameraYawRadians: () => {
|
||||||
this.camera.getWorldDirection(this.cameraForward);
|
this.camera.getWorldDirection(this.cameraForward);
|
||||||
return Math.atan2(this.cameraForward.x, this.cameraForward.z);
|
return Math.atan2(this.cameraForward.x, this.cameraForward.z);
|
||||||
@@ -1215,7 +1219,8 @@ export class RuntimeHost {
|
|||||||
const target = rig.target;
|
const target = rig.target;
|
||||||
const activeNpc =
|
const activeNpc =
|
||||||
this.runtimeScene.npcDefinitions.find(
|
this.runtimeScene.npcDefinitions.find(
|
||||||
(candidate) => candidate.actorId === target.actorId && candidate.active
|
(candidate) =>
|
||||||
|
candidate.actorId === target.actorId && candidate.active
|
||||||
) ??
|
) ??
|
||||||
this.runtimeScene.npcDefinitions.find(
|
this.runtimeScene.npcDefinitions.find(
|
||||||
(candidate) => candidate.actorId === target.actorId
|
(candidate) => candidate.actorId === target.actorId
|
||||||
@@ -1259,12 +1264,15 @@ export class RuntimeHost {
|
|||||||
if (this.activeCameraRigOverrideEntityId !== null) {
|
if (this.activeCameraRigOverrideEntityId !== null) {
|
||||||
return (
|
return (
|
||||||
cameraRigs.find(
|
cameraRigs.find(
|
||||||
(candidate) => candidate.entityId === this.activeCameraRigOverrideEntityId
|
(candidate) =>
|
||||||
|
candidate.entityId === this.activeCameraRigOverrideEntityId
|
||||||
) ?? null
|
) ?? null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const eligibleCameraRigs = cameraRigs.filter((candidate) => candidate.defaultActive);
|
const eligibleCameraRigs = cameraRigs.filter(
|
||||||
|
(candidate) => candidate.defaultActive
|
||||||
|
);
|
||||||
|
|
||||||
if (eligibleCameraRigs.length === 0) {
|
if (eligibleCameraRigs.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
@@ -1277,10 +1285,7 @@ export class RuntimeHost {
|
|||||||
)[0]!;
|
)[0]!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateRuntimeCameraRigLookState(
|
private updateRuntimeCameraRigLookState(rig: RuntimeCameraRig, dt: number) {
|
||||||
rig: RuntimeCameraRig,
|
|
||||||
dt: number
|
|
||||||
) {
|
|
||||||
if (this.runtimeScene === null) {
|
if (this.runtimeScene === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -4514,8 +4519,7 @@ export class RuntimeHost {
|
|||||||
deltaY * CAMERA_RIG_POINTER_LOOK_SENSITIVITY,
|
deltaY * CAMERA_RIG_POINTER_LOOK_SENSITIVITY,
|
||||||
(-this.activeRuntimeCameraRig.lookAround.pitchLimitDegrees * Math.PI) /
|
(-this.activeRuntimeCameraRig.lookAround.pitchLimitDegrees * Math.PI) /
|
||||||
180,
|
180,
|
||||||
(this.activeRuntimeCameraRig.lookAround.pitchLimitDegrees * Math.PI) /
|
(this.activeRuntimeCameraRig.lookAround.pitchLimitDegrees * Math.PI) / 180
|
||||||
180
|
|
||||||
);
|
);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
|
|||||||
@@ -735,14 +735,12 @@ export class ViewportHost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.shaderSkyEnvironmentBlendCache = createRendererQuantizedPmremBlendCache(
|
this.shaderSkyEnvironmentBlendCache =
|
||||||
this.renderer,
|
createRendererQuantizedPmremBlendCache(this.renderer, {
|
||||||
{
|
|
||||||
onTextureReady: () => {
|
onTextureReady: () => {
|
||||||
this.applyWorld();
|
this.applyWorld();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
this.shaderSkyEnvironmentCache =
|
this.shaderSkyEnvironmentCache =
|
||||||
createRendererPrecomputedShaderSkyEnvironmentCache(
|
createRendererPrecomputedShaderSkyEnvironmentCache(
|
||||||
this.renderer,
|
this.renderer,
|
||||||
|
|||||||
@@ -36,16 +36,24 @@ describe("resolveWorldEnvironmentState", () => {
|
|||||||
environmentIntensity: 0.4
|
environmentIntensity: 0.4
|
||||||
};
|
};
|
||||||
|
|
||||||
const earlyTwilight = resolveWorldEnvironmentState(world.background, dayTexture, {
|
const earlyTwilight = resolveWorldEnvironmentState(
|
||||||
texture: nightTexture,
|
world.background,
|
||||||
opacity: 0.25,
|
dayTexture,
|
||||||
environmentIntensity: 0.8
|
{
|
||||||
});
|
texture: nightTexture,
|
||||||
const lateTwilight = resolveWorldEnvironmentState(world.background, dayTexture, {
|
opacity: 0.25,
|
||||||
texture: nightTexture,
|
environmentIntensity: 0.8
|
||||||
opacity: 0.75,
|
}
|
||||||
environmentIntensity: 0.8
|
);
|
||||||
});
|
const lateTwilight = resolveWorldEnvironmentState(
|
||||||
|
world.background,
|
||||||
|
dayTexture,
|
||||||
|
{
|
||||||
|
texture: nightTexture,
|
||||||
|
opacity: 0.75,
|
||||||
|
environmentIntensity: 0.8
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
expect(earlyTwilight.texture).toBe(dayTexture);
|
expect(earlyTwilight.texture).toBe(dayTexture);
|
||||||
expect(earlyTwilight.intensity).toBeCloseTo(0.5);
|
expect(earlyTwilight.intensity).toBeCloseTo(0.5);
|
||||||
|
|||||||
Reference in New Issue
Block a user