feat(web): wire Blender light contribution factors
This commit is contained in:
@@ -937,7 +937,7 @@ function Properties({ snapshot, selectedFaceIndices, selectedVertexIndices, grea
|
||||
{activeTexture ? <div className="property-section" data-testid="texture-properties-workbench"><h3>Texture</h3><output>{activeTexture.name} · Type {activeTexture.type}</output><output>Noise {activeTexture.noiseScale.toFixed(3)} · Depth {activeTexture.noiseDepth}</output><div className="property-actions"><button type="button" onClick={() => onCommand(ioMediaCommands.setTextureProperties(activeTexture.id, { noiseScale: Math.min(1000000, activeTexture.noiseScale + 0.05) }))}>Increase Noise</button><button type="button" onClick={() => onCommand(ioMediaCommands.setTextureProperties(activeTexture.id, { noiseDepth: Math.min(30, activeTexture.noiseDepth + 1) }))}>Increase Depth</button></div></div> : null}
|
||||
{(snapshot?.libraries?.length ?? 0) > 0 ? <div className="property-section" data-testid="library-append-workbench"><h3>Library Append</h3>{snapshot?.libraries?.slice(0, 16).map((library) => <output key={library.id} data-resource-id={library.id}>{library.name} · {library.status} · {library.sourcePath}</output>)}<label>Source .blend <input aria-label="Library source blend" type="file" accept=".blend,application/octet-stream" onChange={(event) => { setLibrarySourceFile(event.target.files?.[0] ?? null); setLibraryAppendError(""); }} /></label><label>Binding JSON <input aria-label="Library append binding" type="file" accept="application/json,.json" onChange={(event) => { setLibraryRequestFile(event.target.files?.[0] ?? null); setLibraryAppendError(""); }} /></label><button type="button" disabled={!librarySourceFile || !libraryRequestFile} onClick={() => void appendLibrary()}>Append to Main</button>{libraryAppendError ? <output role="alert">{libraryAppendError}</output> : null}</div> : null}
|
||||
{activeCamera ? <div className="property-section" data-testid="camera-workbench"><h3>Camera</h3><output>{activeCamera.projection} · {activeCamera.lensMm.toFixed(1)}mm</output><button type="button" onClick={() => onCommand(animationRenderCommands.setCameraProperties(activeCamera.id, { projection: activeCamera.projection === "ORTHOGRAPHIC" ? "PERSPECTIVE" : "ORTHOGRAPHIC" }))}>切换投影</button><button type="button" onClick={() => onCommand(animationRenderCommands.setCameraProperties(activeCamera.id, { lensMm: Math.min(10000, activeCamera.lensMm + 5) }))}>增加焦距</button></div> : null}
|
||||
{activeLight ? <div className="property-section" data-testid="light-workbench"><h3>Light</h3><output>{activeLight.energy.toFixed(1)} energy</output><label>类型 <select aria-label="灯光类型" value={({ 0: "POINT", 1: "SUN", 2: "SPOT", 4: "AREA" } as Record<number, string>)[activeLight.lightType] ?? "POINT"} onChange={(event) => onCommand(animationRenderCommands.setLightProperties(activeLight.id, { type: event.target.value as "POINT" | "SUN" | "SPOT" | "AREA" }))}><option value="POINT">Point</option><option value="SUN">Sun</option><option value="SPOT">Spot</option><option value="AREA">Area</option></select></label><label>Area Shape <select aria-label="灯光区域形状" value={activeLight.areaShape} onChange={(event) => onCommand(animationRenderCommands.setLightProperties(activeLight.id, { areaShape: Number(event.target.value) as 0 | 1 | 4 | 5 }))}><option value="0">Square</option><option value="1">Rectangle</option><option value="4">Disk</option><option value="5">Ellipse</option></select></label><button type="button" onClick={() => onCommand(animationRenderCommands.setLightProperties(activeLight.id, { energy: Math.min(1e9, activeLight.energy + 10) }))}>增加能量</button><button type="button" onClick={() => onCommand(animationRenderCommands.setLightProperties(activeLight.id, { castsShadow: !(activeLight.castsShadow ?? true) }))}>切换阴影</button></div> : null}
|
||||
{activeLight ? <div className="property-section" data-testid="light-workbench"><h3>Light</h3><output>{activeLight.energy.toFixed(1)} energy</output><label>类型 <select aria-label="灯光类型" value={({ 0: "POINT", 1: "SUN", 2: "SPOT", 4: "AREA" } as Record<number, string>)[activeLight.lightType] ?? "POINT"} onChange={(event) => onCommand(animationRenderCommands.setLightProperties(activeLight.id, { type: event.target.value as "POINT" | "SUN" | "SPOT" | "AREA" }))}><option value="POINT">Point</option><option value="SUN">Sun</option><option value="SPOT">Spot</option><option value="AREA">Area</option></select></label><label>Area Shape <select aria-label="灯光区域形状" value={activeLight.areaShape} onChange={(event) => onCommand(animationRenderCommands.setLightProperties(activeLight.id, { areaShape: Number(event.target.value) as 0 | 1 | 4 | 5 }))}><option value="0">Square</option><option value="1">Rectangle</option><option value="4">Disk</option><option value="5">Ellipse</option></select></label><output>贡献因子 D {(activeLight.diffuseFactor ?? 1).toFixed(2)} · S {(activeLight.specularFactor ?? 1).toFixed(2)} · T {(activeLight.transmissionFactor ?? 1).toFixed(2)} · V {(activeLight.volumeFactor ?? 1).toFixed(2)}</output><div className="property-actions"><button type="button" onClick={() => onCommand(animationRenderCommands.setLightProperties(activeLight.id, { diffuseFactor: Math.min(1, (activeLight.diffuseFactor ?? 1) + 0.1) }))}>提高漫反射</button><button type="button" onClick={() => onCommand(animationRenderCommands.setLightProperties(activeLight.id, { specularFactor: Math.min(1, (activeLight.specularFactor ?? 1) + 0.1) }))}>提高镜面</button><button type="button" onClick={() => onCommand(animationRenderCommands.setLightProperties(activeLight.id, { transmissionFactor: Math.min(1, (activeLight.transmissionFactor ?? 1) + 0.1) }))}>提高透射</button><button type="button" onClick={() => onCommand(animationRenderCommands.setLightProperties(activeLight.id, { volumeFactor: Math.min(1, (activeLight.volumeFactor ?? 1) + 0.1) }))}>提高体积</button></div><button type="button" onClick={() => onCommand(animationRenderCommands.setLightProperties(activeLight.id, { energy: Math.min(1e9, activeLight.energy + 10) }))}>增加能量</button><button type="button" onClick={() => onCommand(animationRenderCommands.setLightProperties(activeLight.id, { castsShadow: !(activeLight.castsShadow ?? true) }))}>切换阴影</button></div> : null}
|
||||
{activeWorld ? <div className="property-section" data-testid="world-workbench"><h3>World</h3><output>Exposure {activeWorld.exposure.toFixed(2)} · Lightgroup {activeWorld.lightgroup ?? "None"}</output><div className="property-actions"><button type="button" onClick={() => onCommand(animationRenderCommands.setWorldProperties(activeWorld.id, { exposure: Math.max(-20, Math.min(20, activeWorld.exposure + 0.5)) }))}>增加曝光</button><button type="button" onClick={() => onCommand(animationRenderCommands.setWorldLightgroup(activeWorld.id, activeWorld.lightgroup ? null : "Lightgroup_1"))}>{activeWorld.lightgroup ? "清除分组" : "绑定分组"}</button></div></div> : null}
|
||||
{activeScene ? <div className="property-section" data-testid="scene-properties-workbench"><h3>Scene Render</h3><output>{activeScene.name} · {activeScene.renderEngine ?? "UNKNOWN"}</output><output>{activeScene.colorManagement ? `${activeScene.colorManagement.viewTransform} · ${activeScene.colorManagement.look} · Exposure ${activeScene.colorManagement.exposure.toFixed(2)} · Gamma ${activeScene.colorManagement.gamma.toFixed(2)}` : "Color management unavailable"}</output><div className="property-actions"><button type="button" onClick={() => onCommand(animationRenderCommands.setSceneProperties(activeScene.id, { exposure: Math.max(-1000, Math.min(1000, (activeScene.colorManagement?.exposure ?? 0) + 0.5)) }))}>增加曝光</button><button type="button" onClick={() => onCommand(animationRenderCommands.setSceneProperties(activeScene.id, { gamma: Math.max(0, Math.min(1000, (activeScene.colorManagement?.gamma ?? 1) + 0.1)) }))}>增加 Gamma</button><button type="button" onClick={() => onCommand(animationRenderCommands.setSceneProperties(activeScene.id, { viewTransform: activeScene.colorManagement?.viewTransform === "AgX" ? "Standard" : "AgX" }))}>切换 View Transform</button></div></div> : null}
|
||||
{activeScene?.colorManagement ? <div className="property-section" data-testid="scene-color-management-workbench"><h3>Color Management</h3><output>{activeScene.colorManagement.displayDevice} · {activeScene.colorManagement.temperature?.toFixed(0) ?? "-"} K · Tint {activeScene.colorManagement.tint?.toFixed(2) ?? "-"}</output><div className="property-actions"><button type="button" onClick={() => onCommand(animationRenderCommands.setSceneColorManagement(activeScene.id, { temperature: Math.min(20000, (activeScene.colorManagement!.temperature ?? 6500) + 250) }))}>升高色温</button><button type="button" onClick={() => onCommand(animationRenderCommands.setSceneColorManagement(activeScene.id, { tint: Math.max(-100, Math.min(100, (activeScene.colorManagement!.tint ?? 0) + 1)) }))}>调整 Tint</button></div></div> : null}
|
||||
|
||||
2
web/app/src/vendor/blender/web_engine.js
vendored
2
web/app/src/vendor/blender/web_engine.js
vendored
File diff suppressed because one or more lines are too long
BIN
web/app/src/vendor/blender/web_engine.wasm
vendored
BIN
web/app/src/vendor/blender/web_engine.wasm
vendored
Binary file not shown.
@@ -541,9 +541,10 @@ function assertFutureCapability(payload: Extract<WebEngineRequest["command"], {
|
||||
case "setLightProperties": {
|
||||
const light = currentSnapshot?.lights.find((candidate) => candidate.id === payload.dataId);
|
||||
const properties = payload.properties;
|
||||
if (!light || !validPropertyKeys(properties, ["type", "areaShape", "color", "energy", "exposure", "temperature", "useTemperature", "castsShadow", "radius", "spotAngle", "spotBlend", "areaSize", "areaSizeY", "areaSpread", "sunAngle"])) throw report("RENDER_PROPERTY_INVALID", "Light target or properties are invalid");
|
||||
if (!light || !validPropertyKeys(properties, ["type", "areaShape", "diffuseFactor", "specularFactor", "transmissionFactor", "volumeFactor", "color", "energy", "exposure", "temperature", "useTemperature", "castsShadow", "radius", "spotAngle", "spotBlend", "areaSize", "areaSizeY", "areaSpread", "sunAngle"])) throw report("RENDER_PROPERTY_INVALID", "Light target or properties are invalid");
|
||||
if (properties.type !== undefined && !["POINT", "SUN", "SPOT", "AREA"].includes(properties.type)) throw report("RENDER_PROPERTY_INVALID", "Light type is invalid");
|
||||
if (properties.areaShape !== undefined && ![0, 1, 4, 5].includes(properties.areaShape)) throw report("RENDER_PROPERTY_INVALID", "Light area shape is invalid");
|
||||
for (const field of ["diffuseFactor", "specularFactor", "transmissionFactor", "volumeFactor"] as const) if (properties[field] !== undefined && !boundedNumber(properties[field], 0, 1)) throw report("RENDER_PROPERTY_INVALID", `Light ${field} is outside the bounded range`);
|
||||
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", "Light color is invalid");
|
||||
for (const [field, minimum, maximum] of [["energy", 0, 1e9], ["exposure", -20, 20], ["temperature", 800, 20000], ["radius", 0, 1e9], ["spotAngle", 0.0001, Math.PI], ["spotBlend", 0, 1], ["areaSize", 0.0001, 1e9], ["areaSizeY", 0.0001, 1e9], ["areaSpread", 0.0001, Math.PI], ["sunAngle", 0, Math.PI]] as const) {
|
||||
const value = properties[field];
|
||||
|
||||
Reference in New Issue
Block a user