feat(web): wire Blender world probe controls
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-08-30 20:32:02 +08:00
parent b15ac3770a
commit 6692739cfe
38 changed files with 1976 additions and 20 deletions

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -585,10 +585,12 @@ function assertFutureCapability(payload: Extract<WebEngineRequest["command"], {
case "setWorldProperties": {
const world = currentSnapshot?.worlds.find((candidate) => candidate.id === payload.dataId);
const properties = payload.properties;
if (!world || !validPropertyKeys(properties, ["color", "exposure", "ambientOcclusionDistance", "ambientOcclusionEnergy", "sunThreshold", "sunAngle", "sunShadowMaximumResolution", "sunShadowJitterOverblur", "sunShadowFilterRadius", "mist"])) throw report("RENDER_PROPERTY_INVALID", "World target or properties are invalid");
if (!world || !validPropertyKeys(properties, ["color", "exposure", "ambientOcclusionDistance", "ambientOcclusionEnergy", "sunThreshold", "sunAngle", "sunShadowMaximumResolution", "sunShadowJitterOverblur", "sunShadowFilterRadius", "probeResolution", "useSunShadow", "useSunShadowJitter", "mist"])) throw report("RENDER_PROPERTY_INVALID", "World target or properties are invalid");
if (properties.color !== undefined && (!Array.isArray(properties.color) || properties.color.length !== 3 || properties.color.some((value) => !boundedNumber(value, 0, 100)))) throw report("RENDER_PROPERTY_INVALID", "World color is invalid");
if (properties.exposure !== undefined && !boundedNumber(properties.exposure, -20, 20)) throw report("RENDER_PROPERTY_INVALID", "World exposure is invalid");
for (const [field, minimum, maximum] of [["ambientOcclusionDistance", 0, 1e9], ["ambientOcclusionEnergy", 0, 1e9], ["sunThreshold", 0, 1e9], ["sunAngle", 0, Math.PI], ["sunShadowMaximumResolution", 0.0000001, 1e9], ["sunShadowJitterOverblur", 0, 1e9], ["sunShadowFilterRadius", 0, 1e9]] as const) if (properties[field] !== undefined && !boundedNumber(properties[field], minimum, maximum)) throw report("RENDER_PROPERTY_INVALID", `World ${field} is outside the bounded range`);
if (properties.probeResolution !== undefined && (!Number.isSafeInteger(properties.probeResolution) || properties.probeResolution < 7 || properties.probeResolution > 12)) throw report("RENDER_PROPERTY_INVALID", "World probe resolution is invalid");
for (const field of ["useSunShadow", "useSunShadowJitter"] as const) if (properties[field] !== undefined && typeof properties[field] !== "boolean") throw report("RENDER_PROPERTY_INVALID", `World ${field} must be boolean`);
if (properties.mist !== undefined) {
const mist = properties.mist;
if (!validPropertyKeys(mist, ["enabled", "type", "start", "depth", "intensity", "height"])) throw report("RENDER_PROPERTY_INVALID", "World mist is invalid");

View File

@@ -288,6 +288,9 @@ export interface WorldIR {
sunShadowMaximumResolution?: number;
sunShadowJitterOverblur?: number;
sunShadowFilterRadius?: number;
probeResolution?: number;
useSunShadow?: boolean;
useSunShadowJitter?: boolean;
backgroundVisible?: boolean;
lightgroup?: string | null;
mist?: {
@@ -1578,7 +1581,8 @@ export function parseSceneSnapshotIR(value: unknown): SceneSnapshotIR {
if (world.environmentImageId !== undefined && world.environmentImageId !== null) requireString(world.environmentImageId, `worlds[${index}].environmentImageId`);
if (world.environmentStrength !== undefined) requireNumber(world.environmentStrength, `worlds[${index}].environmentStrength`);
if (world.environmentRotation !== undefined) requireNumber(world.environmentRotation, `worlds[${index}].environmentRotation`);
for (const field of ["ambientOcclusionDistance", "ambientOcclusionEnergy", "sunThreshold", "sunAngle", "sunShadowMaximumResolution", "sunShadowJitterOverblur", "sunShadowFilterRadius"] as const) if (world[field] !== undefined) requireNumber(world[field], `worlds[${index}].${field}`);
for (const field of ["ambientOcclusionDistance", "ambientOcclusionEnergy", "sunThreshold", "sunAngle", "sunShadowMaximumResolution", "sunShadowJitterOverblur", "sunShadowFilterRadius", "probeResolution"] as const) if (world[field] !== undefined) requireNumber(world[field], `worlds[${index}].${field}`);
for (const field of ["useSunShadow", "useSunShadowJitter"] as const) if (world[field] !== undefined) requireBoolean(world[field], `worlds[${index}].${field}`);
if (world.backgroundVisible !== undefined && typeof world.backgroundVisible !== "boolean") throw new Error(`SceneIR.worlds[${index}].backgroundVisible must be a boolean`);
if (world.mist !== undefined) {
if (!isRecord(world.mist) || !["QUADRATIC", "LINEAR", "INVERSE_QUADRATIC"].includes(world.mist.type as string)) throw new Error(`SceneIR.worlds[${index}].mist is invalid`);

View File

@@ -142,7 +142,7 @@ export type WebEngineEditCommand =
| { type: "setVertexWeights"; objectId: string; vertexGroup: string; indices: number[]; values: number[]; normalize?: boolean; limit?: number; mirror?: boolean; mirrorAxis?: 0 | 1 | 2; mirrorTolerance?: number }
| { type: "setLightProperties"; dataId: string; properties: { type?: "POINT" | "SUN" | "SPOT" | "AREA"; areaShape?: 0 | 1 | 4 | 5; diffuseFactor?: number; specularFactor?: number; transmissionFactor?: number; volumeFactor?: number; attenuationDistance?: number; shadowFilterRadius?: number; shadowMaximumResolution?: number; shadowJitterOverblur?: number; cascadeMaxDistance?: number; cascadeExponent?: number; cascadeFade?: number; cascadeCount?: number; areaSizeZ?: number; clipStart?: number; color?: [number, number, number]; energy?: number; exposure?: number; temperature?: number; useTemperature?: boolean; castsShadow?: boolean; radius?: number; spotAngle?: number; spotBlend?: number; areaSize?: number; areaSizeY?: number; areaSpread?: number; sunAngle?: number } }
| { type: "setCameraProperties"; dataId: string; properties: { projection?: "PERSPECTIVE" | "ORTHOGRAPHIC"; lensMm?: number; sensorWidthMm?: number; sensorHeightMm?: number; sensorFit?: 0 | 1 | 2; shift?: [number, number]; near?: number; far?: number; orthoScale?: number; depthOfField?: { enabled?: boolean; focusDistance?: number; apertureFStop?: number; apertureBlades?: number; apertureRotation?: number; apertureRatio?: number } } }
| { type: "setWorldProperties"; dataId: string; properties: { color?: [number, number, number]; exposure?: number; ambientOcclusionDistance?: number; ambientOcclusionEnergy?: number; sunThreshold?: number; sunAngle?: number; sunShadowMaximumResolution?: number; sunShadowJitterOverblur?: number; sunShadowFilterRadius?: number; mist?: { enabled?: boolean; type?: "QUADRATIC" | "LINEAR" | "INVERSE_QUADRATIC"; start?: number; depth?: number; intensity?: number; height?: number } } }
| { type: "setWorldProperties"; dataId: string; properties: { color?: [number, number, number]; exposure?: number; ambientOcclusionDistance?: number; ambientOcclusionEnergy?: number; sunThreshold?: number; sunAngle?: number; sunShadowMaximumResolution?: number; sunShadowJitterOverblur?: number; sunShadowFilterRadius?: number; probeResolution?: 7 | 8 | 9 | 10 | 11 | 12; useSunShadow?: boolean; useSunShadowJitter?: boolean; mist?: { enabled?: boolean; type?: "QUADRATIC" | "LINEAR" | "INVERSE_QUADRATIC"; start?: number; depth?: number; intensity?: number; height?: number } } }
| { type: "setSceneProperties"; dataId: string; properties: { renderEngine?: string; viewTransform?: string; look?: string; exposure?: number; gamma?: number } }
| { type: "setSceneRenderSettings"; dataId: string; properties: { resolutionX?: number; resolutionY?: number; resolutionPercentage?: number; frameStart?: number; frameEnd?: number; outputPath?: string } }
| { type: "setSceneAudioSettings"; dataId: string; properties: { volume?: number; speedOfSound?: number; dopplerFactor?: number; distanceModel?: number; muted?: boolean; sync?: boolean } }