auto-git:
[change] src/app/App.tsx [change] src/assets/starter-environment-assets.ts [change] src/document/migrate-scene-document.ts [change] src/document/scene-document-validation.ts [change] src/document/scene-document.ts [change] src/document/world-settings.ts [change] src/rendering/world-background-renderer.ts [change] src/rendering/world-shader-sky.ts [change] src/runtime-three/runtime-host.ts [change] src/runtime-three/runtime-project-time.ts [change] src/shared-ui/world-background-style.ts [change] src/viewport-three/ViewportCanvas.tsx [change] src/viewport-three/viewport-host.ts [change] tests/domain/runtime-project-time.test.ts [change] tests/domain/scene-document-validation.test.ts [change] tests/domain/world-settings.test.ts [change] tests/serialization/project-document-json.test.ts [change] tests/serialization/scene-document-json.test.ts [change] tests/unit/world-shader-sky.test.ts
This commit is contained in:
@@ -352,7 +352,10 @@ export class RuntimeHost {
|
||||
string,
|
||||
Mesh<BufferGeometry, Material[]>
|
||||
>();
|
||||
private readonly terrainMeshes = new Map<string, Mesh<BufferGeometry, Material>>();
|
||||
private readonly terrainMeshes = new Map<
|
||||
string,
|
||||
Mesh<BufferGeometry, Material>
|
||||
>();
|
||||
private volumeTime = 0;
|
||||
private readonly volumeAnimatedUniforms: Array<{ value: number }> = [];
|
||||
private readonly runtimeWaterContactUniforms: RuntimeWaterContactUniformBinding[] =
|
||||
@@ -361,7 +364,10 @@ export class RuntimeHost {
|
||||
string,
|
||||
LocalLightRenderObjects
|
||||
>();
|
||||
private readonly lightVolumeObjects = new Map<string, LightVolumeRenderObjects>();
|
||||
private readonly lightVolumeObjects = new Map<
|
||||
string,
|
||||
LightVolumeRenderObjects
|
||||
>();
|
||||
private readonly modelRenderObjects = new Map<string, Group>();
|
||||
private readonly materialTextureCache = new Map<
|
||||
string,
|
||||
@@ -1348,8 +1354,16 @@ export class RuntimeHost {
|
||||
}
|
||||
|
||||
if (!shadowsEnabled || this.currentCelestialShadowCaster === null) {
|
||||
configureAdvancedRenderingShadowLight(this.sunLight, advancedRendering, false);
|
||||
configureAdvancedRenderingShadowLight(this.moonLight, advancedRendering, false);
|
||||
configureAdvancedRenderingShadowLight(
|
||||
this.sunLight,
|
||||
advancedRendering,
|
||||
false
|
||||
);
|
||||
configureAdvancedRenderingShadowLight(
|
||||
this.moonLight,
|
||||
advancedRendering,
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1374,8 +1388,16 @@ export class RuntimeHost {
|
||||
});
|
||||
|
||||
if (fit === null) {
|
||||
configureAdvancedRenderingShadowLight(this.sunLight, advancedRendering, false);
|
||||
configureAdvancedRenderingShadowLight(this.moonLight, advancedRendering, false);
|
||||
configureAdvancedRenderingShadowLight(
|
||||
this.sunLight,
|
||||
advancedRendering,
|
||||
false
|
||||
);
|
||||
configureAdvancedRenderingShadowLight(
|
||||
this.moonLight,
|
||||
advancedRendering,
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1661,7 +1683,9 @@ export class RuntimeHost {
|
||||
target.entityKind === "pointLight"
|
||||
? this.runtimeScene.localLights.pointLights
|
||||
: this.runtimeScene.localLights.spotLights;
|
||||
const light = lights.find((candidate) => candidate.entityId === target.entityId);
|
||||
const light = lights.find(
|
||||
(candidate) => candidate.entityId === target.entityId
|
||||
);
|
||||
|
||||
if (light !== undefined) {
|
||||
mutate(light);
|
||||
@@ -1702,7 +1726,10 @@ export class RuntimeHost {
|
||||
renderObjects.light.intensity = intensity;
|
||||
}
|
||||
|
||||
private applyLightColorControl(target: LightControlTargetRef, colorHex: string) {
|
||||
private applyLightColorControl(
|
||||
target: LightControlTargetRef,
|
||||
colorHex: string
|
||||
) {
|
||||
this.mutateRuntimeLightState(target, (light) => {
|
||||
light.colorHex = colorHex;
|
||||
});
|
||||
@@ -2167,7 +2194,10 @@ export class RuntimeHost {
|
||||
kind: "terrain",
|
||||
enabled: true
|
||||
}).geometry;
|
||||
const mesh = new Mesh(geometry, this.createRuntimeTerrainMaterial(terrain));
|
||||
const mesh = new Mesh(
|
||||
geometry,
|
||||
this.createRuntimeTerrainMaterial(terrain)
|
||||
);
|
||||
|
||||
mesh.position.set(
|
||||
terrain.position.x,
|
||||
@@ -2186,8 +2216,9 @@ export class RuntimeHost {
|
||||
|
||||
private createRuntimeTerrainMaterial(terrain: RuntimeTerrain): Material {
|
||||
const layerTextures = terrain.layers.map((layer) =>
|
||||
getTerrainLayerTexture(layer.material, (material) =>
|
||||
this.getOrCreateTextureSet(material).baseColor
|
||||
getTerrainLayerTexture(
|
||||
layer.material,
|
||||
(material) => this.getOrCreateTextureSet(material).baseColor
|
||||
)
|
||||
) as [Texture, Texture, Texture, Texture];
|
||||
|
||||
@@ -2504,10 +2535,9 @@ export class RuntimeHost {
|
||||
waveStrength: brush.volume.water.waveStrength,
|
||||
surfaceDisplacementEnabled:
|
||||
brush.volume.water.surfaceDisplacementEnabled,
|
||||
opacity:
|
||||
isTopFace
|
||||
? Math.min(1, baseOpacity + 0.18)
|
||||
: baseOpacity * 0.5,
|
||||
opacity: isTopFace
|
||||
? Math.min(1, baseOpacity + 0.18)
|
||||
: baseOpacity * 0.5,
|
||||
quality: volumeRenderPaths.water === "quality",
|
||||
wireframe: false,
|
||||
isTopFace,
|
||||
@@ -3433,7 +3463,11 @@ export class RuntimeHost {
|
||||
|
||||
if (renderGroup !== undefined) {
|
||||
renderGroup.visible = npc.visible && npc.active;
|
||||
renderGroup.position.set(npc.position.x, npc.position.y, npc.position.z);
|
||||
renderGroup.position.set(
|
||||
npc.position.x,
|
||||
npc.position.y,
|
||||
npc.position.z
|
||||
);
|
||||
renderGroup.rotation.set(0, (npc.yawDegrees * Math.PI) / 180, 0);
|
||||
}
|
||||
|
||||
@@ -3552,8 +3586,9 @@ export class RuntimeHost {
|
||||
|
||||
if (this.runtimeScene !== null) {
|
||||
const brush =
|
||||
this.runtimeScene.brushes.find((candidate) => candidate.id === brushId) ??
|
||||
null;
|
||||
this.runtimeScene.brushes.find(
|
||||
(candidate) => candidate.id === brushId
|
||||
) ?? null;
|
||||
|
||||
if (brush !== null) {
|
||||
brush.visible = visible ?? !brush.visible;
|
||||
@@ -3567,8 +3602,7 @@ export class RuntimeHost {
|
||||
target: SequenceVisibilityTarget,
|
||||
mode: SequenceVisibilityMode
|
||||
) {
|
||||
const explicitVisible =
|
||||
mode === "toggle" ? undefined : mode === "show";
|
||||
const explicitVisible = mode === "toggle" ? undefined : mode === "show";
|
||||
|
||||
if (target.kind === "brush") {
|
||||
this.applyToggleBrushVisibilityAction(target.brushId, explicitVisible);
|
||||
@@ -3703,7 +3737,9 @@ export class RuntimeHost {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dialogue = npc.dialogues.find((candidate) => candidate.id === dialogueId);
|
||||
const dialogue = npc.dialogues.find(
|
||||
(candidate) => candidate.id === dialogueId
|
||||
);
|
||||
|
||||
if (dialogue === undefined) {
|
||||
return null;
|
||||
@@ -3776,10 +3812,7 @@ export class RuntimeHost {
|
||||
}
|
||||
|
||||
const resolvedDialogueId =
|
||||
dialogueId ??
|
||||
npc.defaultDialogueId ??
|
||||
npc.dialogues[0]?.id ??
|
||||
null;
|
||||
dialogueId ?? npc.defaultDialogueId ?? npc.dialogues[0]?.id ?? null;
|
||||
|
||||
if (resolvedDialogueId === null) {
|
||||
console.warn(`dialogue: npc ${npcEntityId} has no dialogue to speak`);
|
||||
|
||||
@@ -139,10 +139,7 @@ function rotateAroundAxis(vector: Vec3, axis: Vec3, radians: number): Vec3 {
|
||||
|
||||
return normalizeVec3(
|
||||
addVec3(
|
||||
addVec3(
|
||||
scaleVec3(vector, cosine),
|
||||
scaleVec3(cross(axis, vector), sine)
|
||||
),
|
||||
addVec3(scaleVec3(vector, cosine), scaleVec3(cross(axis, vector), sine)),
|
||||
scaleVec3(axis, dot(axis, vector) * (1 - cosine))
|
||||
)
|
||||
);
|
||||
@@ -158,7 +155,9 @@ function parseHexColor(colorHex: string): { r: number; g: number; b: number } {
|
||||
|
||||
function formatHexColor(color: { r: number; g: number; b: number }): string {
|
||||
const toHex = (value: number) =>
|
||||
Math.round(clamp(value, 0, 255)).toString(16).padStart(2, "0");
|
||||
Math.round(clamp(value, 0, 255))
|
||||
.toString(16)
|
||||
.padStart(2, "0");
|
||||
|
||||
return `#${toHex(color.r)}${toHex(color.g)}${toHex(color.b)}`;
|
||||
}
|
||||
@@ -170,8 +169,7 @@ function blendHexColorsByWeights(
|
||||
nightHex: string,
|
||||
weights: RuntimeDayNightPhaseWeights
|
||||
): string {
|
||||
const totalWeight =
|
||||
weights.day + weights.dawn + weights.dusk + weights.night;
|
||||
const totalWeight = weights.day + weights.dawn + weights.dusk + weights.night;
|
||||
|
||||
if (totalWeight <= 1e-6) {
|
||||
return dayHex;
|
||||
@@ -211,19 +209,19 @@ function blendScalarByWeights(
|
||||
nightValue: number,
|
||||
weights: RuntimeDayNightPhaseWeights
|
||||
): number {
|
||||
const totalWeight =
|
||||
weights.day + weights.dawn + weights.dusk + weights.night;
|
||||
const totalWeight = weights.day + weights.dawn + weights.dusk + weights.night;
|
||||
|
||||
if (totalWeight <= 1e-6) {
|
||||
return dayValue;
|
||||
}
|
||||
|
||||
return (
|
||||
dayValue * weights.day +
|
||||
dawnValue * weights.dawn +
|
||||
duskValue * weights.dusk +
|
||||
nightValue * weights.night
|
||||
) / totalWeight;
|
||||
(dayValue * weights.day +
|
||||
dawnValue * weights.dawn +
|
||||
duskValue * weights.dusk +
|
||||
nightValue * weights.night) /
|
||||
totalWeight
|
||||
);
|
||||
}
|
||||
|
||||
function resolveNoonSunDirection(direction: Vec3): Vec3 {
|
||||
@@ -279,7 +277,9 @@ function hasTimeBoundaryBeenCrossed(
|
||||
currentTimeOfDayHours: number,
|
||||
boundaryTimeOfDayHours: number
|
||||
): boolean {
|
||||
const normalizedPreviousTime = normalizeTimeOfDayHours(previousTimeOfDayHours);
|
||||
const normalizedPreviousTime = normalizeTimeOfDayHours(
|
||||
previousTimeOfDayHours
|
||||
);
|
||||
|
||||
if (areTimesEquivalent(normalizedPreviousTime, boundaryTimeOfDayHours)) {
|
||||
return false;
|
||||
@@ -563,7 +563,9 @@ function hasConfiguredImageBackground(
|
||||
return background.mode === "image" && background.assetId.trim().length > 0;
|
||||
}
|
||||
|
||||
function resolveBackgroundTopColor(background: WorldBackgroundSettings): string {
|
||||
function resolveBackgroundTopColor(
|
||||
background: WorldBackgroundSettings
|
||||
): string {
|
||||
if (background.mode === "solid") {
|
||||
return background.colorHex;
|
||||
}
|
||||
@@ -575,7 +577,9 @@ function resolveBackgroundTopColor(background: WorldBackgroundSettings): string
|
||||
return "#000000";
|
||||
}
|
||||
|
||||
function resolveBackgroundBottomColor(background: WorldBackgroundSettings): string {
|
||||
function resolveBackgroundBottomColor(
|
||||
background: WorldBackgroundSettings
|
||||
): string {
|
||||
if (background.mode === "solid") {
|
||||
return background.colorHex;
|
||||
}
|
||||
@@ -900,10 +904,15 @@ export function resolveRuntimeDayNightWorldState(
|
||||
world: WorldSettings,
|
||||
settings: ProjectTimeSettings,
|
||||
clock: RuntimeClockState | null,
|
||||
resolvedTime: RuntimeResolvedTimeState | null =
|
||||
clock === null ? null : resolveRuntimeTimeState(settings, clock)
|
||||
resolvedTime: RuntimeResolvedTimeState | null = clock === null
|
||||
? null
|
||||
: resolveRuntimeTimeState(settings, clock)
|
||||
): RuntimeDayNightWorldState {
|
||||
if (clock === null || resolvedTime === null || !world.projectTimeLightingEnabled) {
|
||||
if (
|
||||
clock === null ||
|
||||
resolvedTime === null ||
|
||||
!world.projectTimeLightingEnabled
|
||||
) {
|
||||
return {
|
||||
ambientLight: {
|
||||
colorHex: world.ambientLight.colorHex,
|
||||
|
||||
Reference in New Issue
Block a user