@laplace.live/persona-sdk 1.8.1 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/values/controller.d.ts +5 -0
- package/dist/values/controller.js +6 -4
- package/dist/values/gltf-extensions.d.ts +1 -1
- package/dist/values/gltf-extensions.js +5 -7
- package/dist/values/limits.d.ts +44 -15
- package/dist/values/limits.js +74 -24
- package/dist/values/stage-info.d.ts +4 -1
- package/dist/values/volumetric-lighting.d.ts +42 -0
- package/dist/values/volumetric-lighting.js +43 -0
- package/dist/wire/methods.d.ts +16 -1
- package/dist/wire/schemas/requests.d.ts +9 -0
- package/dist/wire/schemas/requests.js +5 -0
- package/dist/wire/types.d.ts +73 -12
- package/dist/wire/types.js +7 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export * from './values/model-info.ts';
|
|
|
19
19
|
export * from './values/scene-transition.ts';
|
|
20
20
|
export * from './values/stage-info.ts';
|
|
21
21
|
export * from './values/terms.ts';
|
|
22
|
+
export * from './values/volumetric-lighting.ts';
|
|
22
23
|
export * from './values/vrm-bindings.ts';
|
|
23
24
|
export * from './wire/envelope.ts';
|
|
24
25
|
export * from './wire/errors.ts';
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ export * from "./values/model-info.js";
|
|
|
21
21
|
export * from "./values/scene-transition.js";
|
|
22
22
|
export * from "./values/stage-info.js";
|
|
23
23
|
export * from "./values/terms.js";
|
|
24
|
+
export * from "./values/volumetric-lighting.js";
|
|
24
25
|
export * from "./values/vrm-bindings.js";
|
|
25
26
|
export * from "./wire/envelope.js";
|
|
26
27
|
export * from "./wire/errors.js";
|
|
@@ -75,6 +75,11 @@ export interface ControllerMovementConfig {
|
|
|
75
75
|
/** Turn a VRM avatar toward its movement direction. */
|
|
76
76
|
faceMovement: boolean;
|
|
77
77
|
}
|
|
78
|
+
export declare const CONTROLLER_MOVE_SPEED_MAX = 10;
|
|
79
|
+
export declare const CONTROLLER_TURN_SPEED_MAX = 720;
|
|
80
|
+
export declare const CONTROLLER_MOVEMENT_SMOOTHING_MAX = 0.5;
|
|
81
|
+
export declare const CONTROLLER_WALK_SPEED_MIN = 0.1;
|
|
82
|
+
export declare const CONTROLLER_WALK_SPEED_MAX = 10;
|
|
78
83
|
export declare const DEFAULT_CONTROLLER_MOVEMENT: Readonly<ControllerMovementConfig>;
|
|
79
84
|
/** Heal saved movement or a merged API patch using the same limits as the controls. */
|
|
80
85
|
export declare function healControllerMovement(raw: unknown): ControllerMovementConfig;
|
|
@@ -114,9 +114,11 @@ export function controllerSlot(full) {
|
|
|
114
114
|
? parts.slot
|
|
115
115
|
: null;
|
|
116
116
|
}
|
|
117
|
-
const CONTROLLER_MOVE_SPEED_MAX = 10;
|
|
118
|
-
const CONTROLLER_TURN_SPEED_MAX = 720;
|
|
119
|
-
const CONTROLLER_MOVEMENT_SMOOTHING_MAX = 0.5;
|
|
117
|
+
export const CONTROLLER_MOVE_SPEED_MAX = 10;
|
|
118
|
+
export const CONTROLLER_TURN_SPEED_MAX = 720;
|
|
119
|
+
export const CONTROLLER_MOVEMENT_SMOOTHING_MAX = 0.5;
|
|
120
|
+
export const CONTROLLER_WALK_SPEED_MIN = 0.1;
|
|
121
|
+
export const CONTROLLER_WALK_SPEED_MAX = 10;
|
|
120
122
|
export const DEFAULT_CONTROLLER_MOVEMENT = {
|
|
121
123
|
enabled: false,
|
|
122
124
|
slot: 1,
|
|
@@ -140,7 +142,7 @@ export function healControllerMovement(raw) {
|
|
|
140
142
|
smoothing: clamp(finiteOr(r.smoothing, d.smoothing), 0, CONTROLLER_MOVEMENT_SMOOTHING_MAX),
|
|
141
143
|
walkEnabled: typeof r.walkEnabled === 'boolean' ? r.walkEnabled : d.walkEnabled,
|
|
142
144
|
walkClip: nonEmptyString(r.walkClip),
|
|
143
|
-
walkSpeed: clamp(finiteOr(r.walkSpeed, d.walkSpeed),
|
|
145
|
+
walkSpeed: clamp(finiteOr(r.walkSpeed, d.walkSpeed), CONTROLLER_WALK_SPEED_MIN, CONTROLLER_WALK_SPEED_MAX),
|
|
144
146
|
faceMovement: typeof r.faceMovement === 'boolean' ? r.faceMovement : d.faceMovement,
|
|
145
147
|
};
|
|
146
148
|
}
|
|
@@ -4,7 +4,7 @@ export type GltfLoadPath = 'vrm' | 'gltf';
|
|
|
4
4
|
export declare const LAPLACE_OUTFIT = "LAPLACE_outfit";
|
|
5
5
|
/** The app's room extension, read on props and sets — `renderer/vrm/gltf-extension.ts`. */
|
|
6
6
|
export declare const LAPLACE_ENVIRONMENT = "LAPLACE_environment";
|
|
7
|
-
/** `GLTFLoader` extensions
|
|
7
|
+
/** `GLTFLoader` extensions needing a decoder the app doesn't set. */
|
|
8
8
|
export declare const DECODER_EXTENSIONS: string[];
|
|
9
9
|
/** Per loader; the app's own extensions ride with the loader that reads them. */
|
|
10
10
|
export declare const SUPPORTED_EXTENSIONS: Record<GltfLoadPath, ReadonlySet<string>>;
|
|
@@ -15,16 +15,12 @@ const VRM_EXTENSIONS = [
|
|
|
15
15
|
'VRMC_materials_mtoon',
|
|
16
16
|
'VRMC_materials_hdr_emissiveMultiplier',
|
|
17
17
|
];
|
|
18
|
-
/** `GLTFLoader` extensions
|
|
19
|
-
export const DECODER_EXTENSIONS = [
|
|
20
|
-
'KHR_draco_mesh_compression',
|
|
21
|
-
'KHR_texture_basisu',
|
|
22
|
-
'EXT_meshopt_compression',
|
|
23
|
-
'KHR_meshopt_compression',
|
|
24
|
-
];
|
|
18
|
+
/** `GLTFLoader` extensions needing a decoder the app doesn't set. */
|
|
19
|
+
export const DECODER_EXTENSIONS = ['KHR_texture_basisu'];
|
|
25
20
|
/** The rest of `GLTFLoader`'s own `EXTENSIONS` table, which either loader renders as authored. */
|
|
26
21
|
const RENDERABLE = [
|
|
27
22
|
'KHR_binary_glTF',
|
|
23
|
+
'KHR_draco_mesh_compression',
|
|
28
24
|
'KHR_lights_punctual',
|
|
29
25
|
'KHR_materials_clearcoat',
|
|
30
26
|
'KHR_materials_dispersion',
|
|
@@ -42,6 +38,8 @@ const RENDERABLE = [
|
|
|
42
38
|
'EXT_materials_bump',
|
|
43
39
|
'EXT_texture_webp',
|
|
44
40
|
'EXT_texture_avif',
|
|
41
|
+
'EXT_meshopt_compression',
|
|
42
|
+
'KHR_meshopt_compression',
|
|
45
43
|
'EXT_mesh_gpu_instancing',
|
|
46
44
|
];
|
|
47
45
|
/** Per loader; the app's own extensions ride with the loader that reads them. */
|
package/dist/values/limits.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssetRef, Attach, EnvironmentLook, ModelFormat, MToonTuning, ObjectContent, ObjectSpace, SceneEnvironment, SceneLight, SceneLightType, ScreenPlacement, VrmPlacement } from '../wire/types.ts';
|
|
1
|
+
import type { AssetRef, Attach, EnvironmentLook, ModelFormat, MToonTuning, ObjectContent, ObjectSpace, SceneEnvironment, SceneLight, SceneLightCameraFollowOptions, SceneLightType, ScreenPlacement, VrmPlacement } from '../wire/types.ts';
|
|
2
2
|
export declare function clamp(v: number, min: number, max: number): number;
|
|
3
3
|
/** Clamp to the unit interval. */
|
|
4
4
|
export declare function clamp01(v: number): number;
|
|
@@ -6,21 +6,33 @@ export declare function clamp01(v: number): number;
|
|
|
6
6
|
export declare function wrapAngle(a: number): number;
|
|
7
7
|
export declare const DEFAULT_LIVE2D_PLACEMENT: ScreenPlacement;
|
|
8
8
|
export declare const DEFAULT_VRM_PLACEMENT: VrmPlacement;
|
|
9
|
-
/** Scene colors heal to 6-digit hex. */
|
|
9
|
+
/** Scene colors heal to 6-digit hex; both cases are spelled out because JSON Schema patterns carry no flags. */
|
|
10
10
|
export declare const SCENE_COLOR_RE: RegExp;
|
|
11
11
|
/** The value when it is a 6-digit hex color, else `fallback` — validated, never clamped. */
|
|
12
12
|
export declare function hexColorOr(v: unknown, fallback: string): string;
|
|
13
13
|
/** Drop the alpha byte a picker's hex input can produce (`#rrggbbaa` → `#rrggbb`). */
|
|
14
14
|
export declare function opaqueHex(hex: string): string;
|
|
15
15
|
export declare const SCENE_LIGHT_INTENSITY_MAX = 2;
|
|
16
|
+
export declare const SCENE_SKYBOX_INTENSITY_MAX = 2;
|
|
16
17
|
/**
|
|
17
|
-
* Point lights
|
|
18
|
-
*
|
|
19
|
-
* types have no falloff to overcome — 20 would just blow the stage out.
|
|
18
|
+
* Point lights need headroom against inverse-square decay; directional and ambient
|
|
19
|
+
* lights have no falloff and would wash out the stage at the same intensity.
|
|
20
20
|
*/
|
|
21
21
|
export declare const SCENE_LIGHT_POINT_INTENSITY_MAX = 20;
|
|
22
|
-
|
|
22
|
+
export declare const SCENE_LIGHT_AREA_SIZE_MIN = 0.01;
|
|
23
|
+
export declare const SCENE_LIGHT_AREA_SIZE_MAX = 20;
|
|
24
|
+
export declare const DEFAULT_AREA_LIGHT_SIZE = 1;
|
|
25
|
+
export declare const SCENE_LIGHT_SPOT_ANGLE_MIN = 1;
|
|
26
|
+
/** A 90° half-angle would make the spot shadow camera's 180° projection singular. */
|
|
27
|
+
export declare const SCENE_LIGHT_SPOT_ANGLE_MAX = 89;
|
|
28
|
+
export declare const DEFAULT_SPOT_ANGLE_DEG = 30;
|
|
29
|
+
export declare const DEFAULT_SPOT_PENUMBRA = 0.3;
|
|
30
|
+
/** Positional lights need falloff headroom; the slider and scene healing share this ceiling. */
|
|
23
31
|
export declare function sceneLightIntensityMax(type: SceneLightType): number;
|
|
32
|
+
/** Whether a light of `type` aims with azimuth/elevation. */
|
|
33
|
+
export declare function sceneLightIsAimed(type: SceneLightType): boolean;
|
|
34
|
+
/** Whether a light of `type` can cast shadows; ambient and area lights never do. */
|
|
35
|
+
export declare function sceneLightCastsShadows(type: SceneLightType): boolean;
|
|
24
36
|
export declare const LIVE2D_SCALE_MIN = 0.1;
|
|
25
37
|
export declare const LIVE2D_SCALE_MAX = 24;
|
|
26
38
|
export declare const VRM_SCALE_MIN = 0.05;
|
|
@@ -30,6 +42,8 @@ export declare const SCENE_LIGHT_RANGE_MAX = 20;
|
|
|
30
42
|
export declare const SCENE_LIGHT_SHADOW_RADIUS_MAX = 16;
|
|
31
43
|
/** The point disk's 32 fixed taps stay dense across a far wider penumbra than 5 dithered ones. */
|
|
32
44
|
export declare const SCENE_LIGHT_POINT_SHADOW_RADIUS_MAX = 32;
|
|
45
|
+
export declare const SCENE_LIGHT_SHADOW_SOURCE_SIZE_MAX = 10;
|
|
46
|
+
export declare const DEFAULT_LIGHT_SHADOW_SOURCE_SIZE = 0.1;
|
|
33
47
|
/** Shadow softness (PCF disk radius, in shadow-map texels) ceiling for a light of `type`. */
|
|
34
48
|
export declare function sceneLightShadowRadiusMax(type: SceneLightType): number;
|
|
35
49
|
/**
|
|
@@ -50,6 +64,17 @@ export declare const ELEVATION_LIMIT: number;
|
|
|
50
64
|
/** Manual navigation distance bounds; authored cameras retain signed, unrestricted distances. */
|
|
51
65
|
export declare const DISTANCE_MIN = 0.35;
|
|
52
66
|
export declare const DISTANCE_MAX = 20;
|
|
67
|
+
/** A scene's manual navigation distance bounds, in world units. */
|
|
68
|
+
export interface CameraDistanceLimits {
|
|
69
|
+
min: number;
|
|
70
|
+
max: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* {@link DISTANCE_MIN} and {@link DISTANCE_MAX}, widened in proportion for an Environment Model placed larger
|
|
74
|
+
* or smaller than a ~10 m room. Neither bound ever narrows.
|
|
75
|
+
* @param setRadius The loaded set's own bounding radius, before its placement scale; null until one has loaded.
|
|
76
|
+
*/
|
|
77
|
+
export declare function cameraDistanceLimits(env: Pick<SceneEnvironment, 'modelAssetId' | 'modelPlace'>, setRadius: number | null): CameraDistanceLimits;
|
|
53
78
|
/** ×2 doubles Cubism's own breath amplitude — past that the head sway reads as a nod, not a breath. */
|
|
54
79
|
export declare const BREATH_DEPTH_MAX = 2;
|
|
55
80
|
export declare const MTOON_NORMAL_SCALE_MAX = 2;
|
|
@@ -65,6 +90,8 @@ export declare const MTOON_UV_ANIMATION_MAX = 4;
|
|
|
65
90
|
export declare function defaultMToonTuning(): MToonTuning;
|
|
66
91
|
export declare const DEFAULT_LIGHT_AZIMUTH_DEG = 45;
|
|
67
92
|
export declare const DEFAULT_LIGHT_ELEVATION_DEG: number;
|
|
93
|
+
/** What a following light tracks when `followCameraOptions` is omitted. */
|
|
94
|
+
export declare const DEFAULT_LIGHT_FOLLOW_CAMERA_OPTIONS: SceneLightCameraFollowOptions;
|
|
68
95
|
/** A fresh light of `type`, at the defaults that read sensibly for that type. */
|
|
69
96
|
export declare function defaultSceneLightOf(type: SceneLightType): SceneLight;
|
|
70
97
|
/**
|
|
@@ -102,16 +129,18 @@ export declare function applyEnvironmentLook(env: SceneEnvironment, look: Enviro
|
|
|
102
129
|
* simply arrive wearing it.
|
|
103
130
|
*/
|
|
104
131
|
export declare function environmentLookIsUntouched(env: SceneEnvironment, reference: SceneEnvironment): boolean;
|
|
132
|
+
/** An environment asset: the 3D set (`modelAssetId`) or the equirect map (`iblAssetId`). */
|
|
133
|
+
export type EnvironmentSlot = 'model' | 'map';
|
|
105
134
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
135
|
+
* The edit that removes one environment asset, or both for null. The model takes its baking and
|
|
136
|
+
* light overrides along; removing only the map hands lighting to a remaining model. `modelLook`
|
|
137
|
+
* stays, as in {@link environmentWithAsset}.
|
|
138
|
+
*/
|
|
139
|
+
export declare function environmentClearPatch(env: SceneEnvironment, slot: EnvironmentSlot | null): Partial<Pick<SceneEnvironment, 'iblAssetId' | 'modelAssetId' | 'bakeFromModel' | 'modelLightOverrides'>>;
|
|
140
|
+
/**
|
|
141
|
+
* Pick a model or map without removing the other; null clears both for legacy callers.
|
|
142
|
+
* A picked map becomes the lighting source. A first model generates lighting only without a map.
|
|
143
|
+
* `modelLook` stays put so the desktop can compare the outgoing look before adopting the next.
|
|
115
144
|
* @returns A new environment; the input is untouched.
|
|
116
145
|
*/
|
|
117
146
|
export declare function environmentWithAsset(env: SceneEnvironment, asset: Pick<AssetRef, 'id' | 'kind'> | null): SceneEnvironment;
|
package/dist/values/limits.js
CHANGED
|
@@ -18,8 +18,8 @@ export function wrapAngle(a) {
|
|
|
18
18
|
}
|
|
19
19
|
export const DEFAULT_LIVE2D_PLACEMENT = { x: 0, y: 0, scale: 1, rotation: 0 };
|
|
20
20
|
export const DEFAULT_VRM_PLACEMENT = { x: 0, y: 0, z: 0, rotX: 0, rotY: 0, rotZ: 0, scale: 1 };
|
|
21
|
-
/** Scene colors heal to 6-digit hex. */
|
|
22
|
-
export const SCENE_COLOR_RE = /^#[0-9a-
|
|
21
|
+
/** Scene colors heal to 6-digit hex; both cases are spelled out because JSON Schema patterns carry no flags. */
|
|
22
|
+
export const SCENE_COLOR_RE = /^#[0-9a-fA-F]{6}$/;
|
|
23
23
|
/** The value when it is a 6-digit hex color, else `fallback` — validated, never clamped. */
|
|
24
24
|
export function hexColorOr(v, fallback) {
|
|
25
25
|
return typeof v === 'string' && SCENE_COLOR_RE.test(v) ? v : fallback;
|
|
@@ -29,15 +29,31 @@ export function opaqueHex(hex) {
|
|
|
29
29
|
return /^#[0-9a-f]{8}$/i.test(hex) ? hex.slice(0, 7) : hex;
|
|
30
30
|
}
|
|
31
31
|
export const SCENE_LIGHT_INTENSITY_MAX = 2;
|
|
32
|
+
export const SCENE_SKYBOX_INTENSITY_MAX = 2;
|
|
32
33
|
/**
|
|
33
|
-
* Point lights
|
|
34
|
-
*
|
|
35
|
-
* types have no falloff to overcome — 20 would just blow the stage out.
|
|
34
|
+
* Point lights need headroom against inverse-square decay; directional and ambient
|
|
35
|
+
* lights have no falloff and would wash out the stage at the same intensity.
|
|
36
36
|
*/
|
|
37
37
|
export const SCENE_LIGHT_POINT_INTENSITY_MAX = 20;
|
|
38
|
-
|
|
38
|
+
export const SCENE_LIGHT_AREA_SIZE_MIN = 0.01;
|
|
39
|
+
export const SCENE_LIGHT_AREA_SIZE_MAX = 20;
|
|
40
|
+
export const DEFAULT_AREA_LIGHT_SIZE = 1;
|
|
41
|
+
export const SCENE_LIGHT_SPOT_ANGLE_MIN = 1;
|
|
42
|
+
/** A 90° half-angle would make the spot shadow camera's 180° projection singular. */
|
|
43
|
+
export const SCENE_LIGHT_SPOT_ANGLE_MAX = 89;
|
|
44
|
+
export const DEFAULT_SPOT_ANGLE_DEG = 30;
|
|
45
|
+
export const DEFAULT_SPOT_PENUMBRA = 0.3;
|
|
46
|
+
/** Positional lights need falloff headroom; the slider and scene healing share this ceiling. */
|
|
39
47
|
export function sceneLightIntensityMax(type) {
|
|
40
|
-
return type === '
|
|
48
|
+
return type === 'directional' || type === 'ambient' ? SCENE_LIGHT_INTENSITY_MAX : SCENE_LIGHT_POINT_INTENSITY_MAX;
|
|
49
|
+
}
|
|
50
|
+
/** Whether a light of `type` aims with azimuth/elevation. */
|
|
51
|
+
export function sceneLightIsAimed(type) {
|
|
52
|
+
return type === 'directional' || type === 'spot' || type === 'area';
|
|
53
|
+
}
|
|
54
|
+
/** Whether a light of `type` can cast shadows; ambient and area lights never do. */
|
|
55
|
+
export function sceneLightCastsShadows(type) {
|
|
56
|
+
return type !== 'ambient' && type !== 'area';
|
|
41
57
|
}
|
|
42
58
|
// Live2D bounds are the zoom clamp (zoom.ts re-exports these); VRM scales a world-space root.
|
|
43
59
|
export const LIVE2D_SCALE_MIN = 0.1;
|
|
@@ -51,6 +67,8 @@ export const SCENE_LIGHT_RANGE_MAX = 20;
|
|
|
51
67
|
export const SCENE_LIGHT_SHADOW_RADIUS_MAX = 16;
|
|
52
68
|
/** The point disk's 32 fixed taps stay dense across a far wider penumbra than 5 dithered ones. */
|
|
53
69
|
export const SCENE_LIGHT_POINT_SHADOW_RADIUS_MAX = 32;
|
|
70
|
+
export const SCENE_LIGHT_SHADOW_SOURCE_SIZE_MAX = 10;
|
|
71
|
+
export const DEFAULT_LIGHT_SHADOW_SOURCE_SIZE = 0.1;
|
|
54
72
|
/** Shadow softness (PCF disk radius, in shadow-map texels) ceiling for a light of `type`. */
|
|
55
73
|
export function sceneLightShadowRadiusMax(type) {
|
|
56
74
|
return type === 'point' ? SCENE_LIGHT_POINT_SHADOW_RADIUS_MAX : SCENE_LIGHT_SHADOW_RADIUS_MAX;
|
|
@@ -73,6 +91,17 @@ export const ELEVATION_LIMIT = (80 * Math.PI) / 180;
|
|
|
73
91
|
/** Manual navigation distance bounds; authored cameras retain signed, unrestricted distances. */
|
|
74
92
|
export const DISTANCE_MIN = 0.35;
|
|
75
93
|
export const DISTANCE_MAX = 20;
|
|
94
|
+
/** Bounding radius of the set the distance bounds are sized for: a ~10 m room {@link DISTANCE_MAX} frames whole. */
|
|
95
|
+
const DISTANCE_SET_RADIUS = 5;
|
|
96
|
+
/**
|
|
97
|
+
* {@link DISTANCE_MIN} and {@link DISTANCE_MAX}, widened in proportion for an Environment Model placed larger
|
|
98
|
+
* or smaller than a ~10 m room. Neither bound ever narrows.
|
|
99
|
+
* @param setRadius The loaded set's own bounding radius, before its placement scale; null until one has loaded.
|
|
100
|
+
*/
|
|
101
|
+
export function cameraDistanceLimits(env, setRadius) {
|
|
102
|
+
const size = env.modelAssetId === null || setRadius === null ? 1 : (setRadius * env.modelPlace.scale) / DISTANCE_SET_RADIUS;
|
|
103
|
+
return { min: DISTANCE_MIN * Math.min(1, size), max: DISTANCE_MAX * Math.max(1, size) };
|
|
104
|
+
}
|
|
76
105
|
/** ×2 doubles Cubism's own breath amplitude — past that the head sway reads as a nod, not a breath. */
|
|
77
106
|
export const BREATH_DEPTH_MAX = 2;
|
|
78
107
|
export const MTOON_NORMAL_SCALE_MAX = 2;
|
|
@@ -107,25 +136,40 @@ export function defaultMToonTuning() {
|
|
|
107
136
|
// Angles at which lightDirection reproduces (1,1,1).normalize() — the app's original hardcoded light.
|
|
108
137
|
export const DEFAULT_LIGHT_AZIMUTH_DEG = 45;
|
|
109
138
|
export const DEFAULT_LIGHT_ELEVATION_DEG = (Math.asin(1 / Math.sqrt(3)) * 180) / Math.PI;
|
|
139
|
+
/** What a following light tracks when `followCameraOptions` is omitted. */
|
|
140
|
+
export const DEFAULT_LIGHT_FOLLOW_CAMERA_OPTIONS = {
|
|
141
|
+
position: true,
|
|
142
|
+
rotation: true,
|
|
143
|
+
distance: true,
|
|
144
|
+
};
|
|
110
145
|
/** A fresh light of `type`, at the defaults that read sensibly for that type. */
|
|
111
146
|
export function defaultSceneLightOf(type) {
|
|
112
147
|
return {
|
|
113
148
|
id: crypto.randomUUID(),
|
|
114
149
|
type,
|
|
115
150
|
enabled: true,
|
|
151
|
+
followCamera: false,
|
|
152
|
+
followCameraOptions: { ...DEFAULT_LIGHT_FOLLOW_CAMERA_OPTIONS },
|
|
116
153
|
color: '#ffffff',
|
|
117
154
|
// Ambient is a flat albedo floor with no shading — 1.0 alone washes the model out.
|
|
118
155
|
intensity: type === 'ambient' ? 0.4 : 1,
|
|
119
|
-
azimuth: DEFAULT_LIGHT_AZIMUTH_DEG,
|
|
120
|
-
elevation: DEFAULT_LIGHT_ELEVATION_DEG,
|
|
156
|
+
azimuth: type === 'area' || type === 'spot' ? 0 : DEFAULT_LIGHT_AZIMUTH_DEG,
|
|
157
|
+
elevation: type === 'area' || type === 'spot' ? 0 : DEFAULT_LIGHT_ELEVATION_DEG,
|
|
121
158
|
x: 0,
|
|
122
159
|
y: 1.4,
|
|
123
160
|
z: 1,
|
|
124
161
|
range: 10,
|
|
162
|
+
angle: DEFAULT_SPOT_ANGLE_DEG,
|
|
163
|
+
penumbra: DEFAULT_SPOT_PENUMBRA,
|
|
164
|
+
width: DEFAULT_AREA_LIGHT_SIZE,
|
|
165
|
+
height: DEFAULT_AREA_LIGHT_SIZE,
|
|
166
|
+
roll: 0,
|
|
125
167
|
// A point light shadow is six cube faces, so it stays opt-in where a
|
|
126
168
|
// directional light's single depth pass can be on by default.
|
|
127
169
|
shadowQuality: type === 'directional' ? 'high' : 'off',
|
|
128
170
|
shadowRadius: 6,
|
|
171
|
+
shadowFilter: 'pcf',
|
|
172
|
+
shadowSourceSize: DEFAULT_LIGHT_SHADOW_SOURCE_SIZE,
|
|
129
173
|
};
|
|
130
174
|
}
|
|
131
175
|
/**
|
|
@@ -186,26 +230,32 @@ export function environmentLookIsUntouched(env, reference) {
|
|
|
186
230
|
return looked(env) === looked(reference);
|
|
187
231
|
}
|
|
188
232
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
233
|
+
* The edit that removes one environment asset, or both for null. The model takes its baking and
|
|
234
|
+
* light overrides along; removing only the map hands lighting to a remaining model. `modelLook`
|
|
235
|
+
* stays, as in {@link environmentWithAsset}.
|
|
236
|
+
*/
|
|
237
|
+
export function environmentClearPatch(env, slot) {
|
|
238
|
+
if (slot === 'map')
|
|
239
|
+
return { iblAssetId: null, bakeFromModel: env.modelAssetId !== null };
|
|
240
|
+
const model = { modelAssetId: null, bakeFromModel: false, modelLightOverrides: {} };
|
|
241
|
+
return slot === 'model' ? model : { ...model, iblAssetId: null };
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Pick a model or map without removing the other; null clears both for legacy callers.
|
|
245
|
+
* A picked map becomes the lighting source. A first model generates lighting only without a map.
|
|
246
|
+
* `modelLook` stays put so the desktop can compare the outgoing look before adopting the next.
|
|
198
247
|
* @returns A new environment; the input is untouched.
|
|
199
248
|
*/
|
|
200
249
|
export function environmentWithAsset(env, asset) {
|
|
201
|
-
|
|
202
|
-
|
|
250
|
+
if (asset === null)
|
|
251
|
+
return { ...env, ...environmentClearPatch(env, null) };
|
|
252
|
+
if (asset.kind !== 'prop')
|
|
253
|
+
return { ...env, iblAssetId: asset.id, bakeFromModel: false };
|
|
203
254
|
return {
|
|
204
255
|
...env,
|
|
205
|
-
|
|
206
|
-
modelAssetId,
|
|
207
|
-
|
|
208
|
-
modelLightOverrides: modelAssetId === env.modelAssetId ? env.modelLightOverrides : {},
|
|
256
|
+
modelAssetId: asset.id,
|
|
257
|
+
bakeFromModel: env.modelAssetId === null ? env.iblAssetId === null : env.bakeFromModel,
|
|
258
|
+
modelLightOverrides: asset.id === env.modelAssetId ? env.modelLightOverrides : {},
|
|
209
259
|
};
|
|
210
260
|
}
|
|
211
261
|
export const PLACE_2D_SCALE_MIN = 0.01;
|
|
@@ -22,6 +22,9 @@ export interface SceneAssetInfo {
|
|
|
22
22
|
/** Metadata for the active scene, tagged so a response from a previous scene can be discarded. */
|
|
23
23
|
export interface SceneInspection {
|
|
24
24
|
sceneId: string;
|
|
25
|
-
environment: Omit<SceneAssetInfo, 'mediaSize'
|
|
25
|
+
environment: Omit<SceneAssetInfo, 'mediaSize'> & {
|
|
26
|
+
/** The set's own bounding radius before placement scale, for `cameraDistanceLimits`; omitted by older hosts. */
|
|
27
|
+
radius?: number | null;
|
|
28
|
+
};
|
|
26
29
|
objects: Record<string, SceneAssetInfo>;
|
|
27
30
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type SceneEnvironment, type SceneVolumetricLighting } from '../wire/types.ts';
|
|
2
|
+
/** Slider and healing metadata for the haze's numeric settings. */
|
|
3
|
+
export declare const SCENE_VOLUMETRIC_LIGHTING_SPECS: {
|
|
4
|
+
readonly density: {
|
|
5
|
+
readonly default: 1;
|
|
6
|
+
readonly min: 0;
|
|
7
|
+
readonly max: 10;
|
|
8
|
+
readonly step: 0.05;
|
|
9
|
+
readonly digits: 2;
|
|
10
|
+
};
|
|
11
|
+
readonly intensity: {
|
|
12
|
+
readonly default: 1;
|
|
13
|
+
readonly min: 0;
|
|
14
|
+
readonly max: 10;
|
|
15
|
+
readonly step: 0.05;
|
|
16
|
+
readonly digits: 2;
|
|
17
|
+
};
|
|
18
|
+
readonly range: {
|
|
19
|
+
readonly default: 20;
|
|
20
|
+
readonly min: 1;
|
|
21
|
+
readonly max: 100;
|
|
22
|
+
readonly step: 1;
|
|
23
|
+
readonly digits: 0;
|
|
24
|
+
};
|
|
25
|
+
readonly speed: {
|
|
26
|
+
readonly default: 0.2;
|
|
27
|
+
readonly min: 0;
|
|
28
|
+
readonly max: 5;
|
|
29
|
+
readonly step: 0.05;
|
|
30
|
+
readonly digits: 2;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
/** Existing scenes retain clear air until volumetric lighting is enabled. */
|
|
34
|
+
export declare function defaultSceneVolumetricLighting(): SceneVolumetricLighting;
|
|
35
|
+
/** Restore saved haze settings and clamp them to the shared editor bounds. */
|
|
36
|
+
export declare function healSceneVolumetricLighting(raw: unknown): SceneVolumetricLighting;
|
|
37
|
+
/** An environment edit whose haze settings arrive as leaves, so queued edits never replace one another. */
|
|
38
|
+
export type EnvironmentPatch = Omit<Partial<SceneEnvironment>, 'volumetricLighting'> & {
|
|
39
|
+
volumetricLighting?: Partial<SceneVolumetricLighting>;
|
|
40
|
+
};
|
|
41
|
+
/** Merge `patch` into `target` in place; haze leaves land in the settings object `target` already holds. */
|
|
42
|
+
export declare function mergeEnvironmentPatch(target: EnvironmentPatch, { volumetricLighting, ...fields }: EnvironmentPatch): void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { EFFECTS_QUALITY_LEVELS } from "../wire/types.js";
|
|
2
|
+
import { finiteOr, isOneOf, isRecord } from "./guards.js";
|
|
3
|
+
import { clamp } from "./limits.js";
|
|
4
|
+
/** Slider and healing metadata for the haze's numeric settings. */
|
|
5
|
+
export const SCENE_VOLUMETRIC_LIGHTING_SPECS = {
|
|
6
|
+
density: { default: 1, min: 0, max: 10, step: 0.05, digits: 2 },
|
|
7
|
+
intensity: { default: 1, min: 0, max: 10, step: 0.05, digits: 2 },
|
|
8
|
+
range: { default: 20, min: 1, max: 100, step: 1, digits: 0 },
|
|
9
|
+
speed: { default: 0.2, min: 0, max: 5, step: 0.05, digits: 2 },
|
|
10
|
+
};
|
|
11
|
+
/** Existing scenes retain clear air until volumetric lighting is enabled. */
|
|
12
|
+
export function defaultSceneVolumetricLighting() {
|
|
13
|
+
const specs = SCENE_VOLUMETRIC_LIGHTING_SPECS;
|
|
14
|
+
return {
|
|
15
|
+
enabled: false,
|
|
16
|
+
density: specs.density.default,
|
|
17
|
+
intensity: specs.intensity.default,
|
|
18
|
+
range: specs.range.default,
|
|
19
|
+
quality: 'medium',
|
|
20
|
+
speed: specs.speed.default,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Restore saved haze settings and clamp them to the shared editor bounds. */
|
|
24
|
+
export function healSceneVolumetricLighting(raw) {
|
|
25
|
+
const d = defaultSceneVolumetricLighting();
|
|
26
|
+
if (!isRecord(raw))
|
|
27
|
+
return d;
|
|
28
|
+
const specs = SCENE_VOLUMETRIC_LIGHTING_SPECS;
|
|
29
|
+
return {
|
|
30
|
+
enabled: typeof raw.enabled === 'boolean' ? raw.enabled : d.enabled,
|
|
31
|
+
density: clamp(finiteOr(raw.density, d.density), specs.density.min, specs.density.max),
|
|
32
|
+
intensity: clamp(finiteOr(raw.intensity, d.intensity), specs.intensity.min, specs.intensity.max),
|
|
33
|
+
range: clamp(finiteOr(raw.range, d.range), specs.range.min, specs.range.max),
|
|
34
|
+
quality: isOneOf(raw.quality, EFFECTS_QUALITY_LEVELS) ? raw.quality : d.quality,
|
|
35
|
+
speed: clamp(finiteOr(raw.speed, d.speed), specs.speed.min, specs.speed.max),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Merge `patch` into `target` in place; haze leaves land in the settings object `target` already holds. */
|
|
39
|
+
export function mergeEnvironmentPatch(target, { volumetricLighting, ...fields }) {
|
|
40
|
+
Object.assign(target, fields);
|
|
41
|
+
if (volumetricLighting)
|
|
42
|
+
target.volumetricLighting = Object.assign(target.volumetricLighting ?? {}, volumetricLighting);
|
|
43
|
+
}
|
package/dist/wire/methods.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { ControllerMovementConfig, ControllerState } from '../values/contro
|
|
|
3
3
|
import type { LipSyncCalibrationCommand, LipSyncConfig, LipSyncMode, LipSyncState } from '../values/lipsync.ts';
|
|
4
4
|
import type { SceneInspection } from '../values/stage-info.ts';
|
|
5
5
|
import type { EventName } from './events.ts';
|
|
6
|
-
import type { AnchorOption, AppCapability, AssetKind, AssetRef, Attach, AttachHeadAngle, AutomationInfo, BindingInput, Expression, ExpressionPersistence, HandTrackingMode, HotkeyConfig, HotkeyState, InjectEntry, InjectTarget, InstanceRuntime, JsonValue, LayerEffectKey, LayerEffects, LayerEffectsPatch, MediaPipeConfig, ModelInfo, ModelRef, MotionGroup, MToonTuning, ObjectContent, ObjectLightOverride, ObjectSpace, Place2D, Place3D, PlayingMotion, PoseSourceId, PoseStatus, Scene, SceneItem, ScenePatch, SceneState, ScreenPlacement, Settings, SettingsPatch, TrackingSourceConfig, TrackingSourceId, TrackingSourceKind, TrackingStatus, VrmPlacement } from './types.ts';
|
|
6
|
+
import type { AnchorOption, AppCapability, AssetKind, AssetRef, Attach, AttachHeadAngle, AutomationInfo, BindingInput, Expression, ExpressionPersistence, HandTrackingMode, HotkeyConfig, HotkeyState, InjectEntry, InjectTarget, InstanceRuntime, JsonValue, LayerEffectKey, LayerEffects, LayerEffectsPatch, MediaPipeConfig, ModelInfo, ModelRef, MotionGroup, MToonTuning, ObjectContent, ObjectLightOverride, ObjectSpace, Place2D, Place3D, PlayingMotion, PoseSourceId, PoseStatus, Scene, SceneItem, SceneLight, SceneLightCameraFollowOptions, ScenePatch, SceneState, ScreenPlacement, Settings, SettingsPatch, TrackingSourceConfig, TrackingSourceId, TrackingSourceKind, TrackingStatus, VrmPlacement } from './types.ts';
|
|
7
7
|
/** Marker for methods that take no parameters; the client lets you omit the argument. */
|
|
8
8
|
export type EmptyRequest = Record<never, never>;
|
|
9
9
|
/** Marker for methods whose success response carries no data. */
|
|
@@ -40,6 +40,17 @@ export type ScenePatchRequest = ScenePatch;
|
|
|
40
40
|
export interface ScenePatchResponse {
|
|
41
41
|
scene: Scene;
|
|
42
42
|
}
|
|
43
|
+
/** Rebase one active-scene light against the displayed camera without moving or re-aiming it. */
|
|
44
|
+
export interface SceneSetLightFollowCameraRequest {
|
|
45
|
+
lightId: string;
|
|
46
|
+
followCamera: boolean;
|
|
47
|
+
/** Merges independent follow choices while preserving the light's displayed pose. */
|
|
48
|
+
options?: Partial<SceneLightCameraFollowOptions>;
|
|
49
|
+
}
|
|
50
|
+
export interface SceneSetLightFollowCameraResponse {
|
|
51
|
+
/** The rebased light, or null if it no longer exists in the active scene. */
|
|
52
|
+
light: SceneLight | null;
|
|
53
|
+
}
|
|
43
54
|
export type InstanceListRequest = EmptyRequest;
|
|
44
55
|
export interface InstanceListResponse {
|
|
45
56
|
instances: InstanceRuntime[];
|
|
@@ -667,6 +678,10 @@ export interface MethodMap {
|
|
|
667
678
|
request: ScenePatchRequest;
|
|
668
679
|
response: ScenePatchResponse;
|
|
669
680
|
};
|
|
681
|
+
'scene.setLightFollowCamera': {
|
|
682
|
+
request: SceneSetLightFollowCameraRequest;
|
|
683
|
+
response: SceneSetLightFollowCameraResponse;
|
|
684
|
+
};
|
|
670
685
|
'instance.list': {
|
|
671
686
|
request: InstanceListRequest;
|
|
672
687
|
response: InstanceListResponse;
|
|
@@ -113,6 +113,15 @@ export declare const requestSchemas: {
|
|
|
113
113
|
'scene.delete': z.ZodObject<{
|
|
114
114
|
sceneId: z.ZodString;
|
|
115
115
|
}, z.core.$strip>;
|
|
116
|
+
'scene.setLightFollowCamera': z.ZodObject<{
|
|
117
|
+
lightId: z.ZodString;
|
|
118
|
+
followCamera: z.ZodBoolean;
|
|
119
|
+
options: z.ZodOptional<z.ZodObject<{
|
|
120
|
+
position: z.ZodOptional<z.ZodBoolean>;
|
|
121
|
+
rotation: z.ZodOptional<z.ZodBoolean>;
|
|
122
|
+
distance: z.ZodOptional<z.ZodBoolean>;
|
|
123
|
+
}, z.core.$strip>>;
|
|
124
|
+
}, z.core.$strip>;
|
|
116
125
|
'instance.add': z.ZodObject<{
|
|
117
126
|
modelId: z.ZodString;
|
|
118
127
|
}, z.core.$strip>;
|
|
@@ -78,6 +78,11 @@ export const requestSchemas = {
|
|
|
78
78
|
'scene.duplicate': z.object({ sceneId: nonEmpty }),
|
|
79
79
|
'scene.rename': z.object({ sceneId: nonEmpty, name: nonEmpty }),
|
|
80
80
|
'scene.delete': z.object({ sceneId: nonEmpty }),
|
|
81
|
+
'scene.setLightFollowCamera': z.object({
|
|
82
|
+
lightId: nonEmpty,
|
|
83
|
+
followCamera: z.boolean(),
|
|
84
|
+
options: z.object({ position: z.boolean(), rotation: z.boolean(), distance: z.boolean() }).partial().optional(),
|
|
85
|
+
}),
|
|
81
86
|
'instance.add': z.object({ modelId: nonEmpty }),
|
|
82
87
|
'instance.setModel': z.object({ instanceId: nonEmpty, modelId: nonEmpty }),
|
|
83
88
|
'object.add': z.object({
|
package/dist/wire/types.d.ts
CHANGED
|
@@ -330,7 +330,8 @@ export interface SceneCamera {
|
|
|
330
330
|
* the clip: applying a pose leaves whatever camera motion the scene has running.
|
|
331
331
|
*/
|
|
332
332
|
export type CameraPose = Pick<SceneCamera, 'orbit' | 'fov' | 'roll' | 'projection'>;
|
|
333
|
-
export
|
|
333
|
+
export declare const SCENE_LIGHT_TYPES: readonly ["directional", "point", "ambient", "area", "spot"];
|
|
334
|
+
export type SceneLightType = (typeof SCENE_LIGHT_TYPES)[number];
|
|
334
335
|
/**
|
|
335
336
|
* Per-light shadow tier. `off` is the old `castShadow: false`; the rest raise
|
|
336
337
|
* shadow-map resolution. A point light pays 6 cube faces for the same tier a
|
|
@@ -338,17 +339,46 @@ export type SceneLightType = 'directional' | 'point' | 'ambient';
|
|
|
338
339
|
*/
|
|
339
340
|
export declare const SHADOW_QUALITY_LEVELS: readonly ["off", "low", "medium", "high", "ultra"];
|
|
340
341
|
export type ShadowQuality = (typeof SHADOW_QUALITY_LEVELS)[number];
|
|
342
|
+
export declare const SHADOW_FILTERS: readonly ["pcf", "pcss"];
|
|
343
|
+
export type ShadowFilter = (typeof SHADOW_FILTERS)[number];
|
|
344
|
+
/** Camera components followed by a light; omitted options default to all true. */
|
|
345
|
+
export interface SceneLightCameraFollowOptions {
|
|
346
|
+
/** Follow the camera's target/pan translation. */
|
|
347
|
+
position: boolean;
|
|
348
|
+
/** Follow the camera's world rotation, including roll. */
|
|
349
|
+
rotation: boolean;
|
|
350
|
+
/** Follow the signed orbit distance from target to camera. */
|
|
351
|
+
distance: boolean;
|
|
352
|
+
}
|
|
353
|
+
/** Frozen camera components used when the matching follow option is disabled. */
|
|
354
|
+
export interface SceneLightCameraFollowReference extends Pick<OrbitTransform, 'targetX' | 'targetY' | 'targetZ' | 'distance'> {
|
|
355
|
+
/** Unit quaternion in world space. */
|
|
356
|
+
rotation: {
|
|
357
|
+
x: number;
|
|
358
|
+
y: number;
|
|
359
|
+
z: number;
|
|
360
|
+
w: number;
|
|
361
|
+
};
|
|
362
|
+
}
|
|
341
363
|
/**
|
|
342
364
|
* One scene light. Angles are degrees. Directional lights aim with
|
|
343
365
|
* azimuth/elevation and sit at x/y/z (which moves their handle and shadow
|
|
344
366
|
* coverage, not their parallel shading), point lights use x/y/z + range,
|
|
345
|
-
*
|
|
367
|
+
* spot adds azimuth/elevation, angle and penumbra to point's fields;
|
|
368
|
+
* area uses x/y/z, azimuth/elevation/roll and width/height; ambient uses none.
|
|
369
|
+
* All fields stay so a type switch keeps them. Area lights affect PBR and MToon materials.
|
|
346
370
|
*/
|
|
347
371
|
export interface SceneLight {
|
|
348
372
|
id: string;
|
|
349
373
|
type: SceneLightType;
|
|
350
374
|
/** Mutes illumination and shadows. Defaults to true; omitted by hosts without light toggles. */
|
|
351
375
|
enabled?: boolean;
|
|
376
|
+
/** Camera-relative placement and angles; defaults to false. Gate editing on `camera-follow-lights`; ambient ignores it. */
|
|
377
|
+
followCamera?: boolean;
|
|
378
|
+
/** Independent camera components; omitted means all true. */
|
|
379
|
+
followCameraOptions?: SceneLightCameraFollowOptions;
|
|
380
|
+
/** Missing references use target zero, identity rotation, and distance zero for disabled components. */
|
|
381
|
+
followCameraReference?: SceneLightCameraFollowReference;
|
|
352
382
|
color: string;
|
|
353
383
|
intensity: number;
|
|
354
384
|
azimuth: number;
|
|
@@ -357,10 +387,23 @@ export interface SceneLight {
|
|
|
357
387
|
y: number;
|
|
358
388
|
z: number;
|
|
359
389
|
range: number;
|
|
360
|
-
/**
|
|
390
|
+
/** Spot cone half-angle in degrees, 1–89; omitted values default to 30. */
|
|
391
|
+
angle?: number;
|
|
392
|
+
/** Spot cone edge softness, 0–1; omitted values default to 0.3. */
|
|
393
|
+
penumbra?: number;
|
|
394
|
+
/** Rectangular area light dimensions in metres; omitted values default to 1. */
|
|
395
|
+
width?: number;
|
|
396
|
+
height?: number;
|
|
397
|
+
/** Area light rotation in its rectangle's plane, in degrees; defaults to 0. */
|
|
398
|
+
roll?: number;
|
|
399
|
+
/** Ambient and area lights never cast shadows. */
|
|
361
400
|
shadowQuality: ShadowQuality;
|
|
362
|
-
/**
|
|
401
|
+
/** PCF disk radius in shadow-map texels; retained while PCSS is selected. */
|
|
363
402
|
shadowRadius: number;
|
|
403
|
+
/** Per-light filter; defaults to PCF. Gate editing on the `shadow-filters` app capability. */
|
|
404
|
+
shadowFilter?: ShadowFilter;
|
|
405
|
+
/** PCSS source diameter: degrees for directional lights, metres for point/spot; 0–10, default 0.1. */
|
|
406
|
+
shadowSourceSize?: number;
|
|
364
407
|
}
|
|
365
408
|
/** Distance haze. Only geometry fogs — empty space keeps the window's transparency. */
|
|
366
409
|
export interface SceneFog {
|
|
@@ -369,6 +412,16 @@ export interface SceneFog {
|
|
|
369
412
|
/** Exponential-squared falloff; the useful band is well under 1 at avatar scale. */
|
|
370
413
|
density: number;
|
|
371
414
|
}
|
|
415
|
+
/** Illuminated haze in the 3D stage; rendered only by perspective cameras. */
|
|
416
|
+
export interface SceneVolumetricLighting {
|
|
417
|
+
enabled: boolean;
|
|
418
|
+
density: number;
|
|
419
|
+
intensity: number;
|
|
420
|
+
/** Distance from the camera in scene units. */
|
|
421
|
+
range: number;
|
|
422
|
+
quality: EffectsQuality;
|
|
423
|
+
speed: number;
|
|
424
|
+
}
|
|
372
425
|
/** Display transform applied after the scene renders. `none` keeps colors exactly as authored. */
|
|
373
426
|
export type SceneToneMapping = 'none' | 'neutral' | 'aces' | 'agx';
|
|
374
427
|
/** Highlight glow in normal, streak, or star mode; color selection applies only to layer effects. */
|
|
@@ -812,11 +865,8 @@ export interface EnvironmentLook {
|
|
|
812
865
|
color: SceneColorGrade;
|
|
813
866
|
}
|
|
814
867
|
/**
|
|
815
|
-
* The world the 3D stage sits in:
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
* The two asset ids are a single slot the editors write through
|
|
819
|
-
* {@link environmentWithAsset}: at most one is ever set.
|
|
868
|
+
* The world the 3D stage sits in: independent environment map and 3D set,
|
|
869
|
+
* scene-global fog, and post-processing.
|
|
820
870
|
*/
|
|
821
871
|
export interface SceneEnvironment {
|
|
822
872
|
/** Equirectangular `.hdr` or image driving the environment lighting, or null. */
|
|
@@ -828,14 +878,21 @@ export interface SceneEnvironment {
|
|
|
828
878
|
* has no IBL term of its own and the app adds one.
|
|
829
879
|
*/
|
|
830
880
|
iblAvatarAmount: number;
|
|
881
|
+
/** Show the selected map, falling back to the model's sky, independently of the lighting source. */
|
|
831
882
|
showSkybox: boolean;
|
|
883
|
+
/** Visible skybox blur, 0..1; defaults to 0. Omitted by older hosts. */
|
|
884
|
+
skyboxBlur?: number;
|
|
885
|
+
/** Visible skybox brightness, 0..2; defaults to 1. Omitted by older hosts. */
|
|
886
|
+
skyboxIntensity?: number;
|
|
887
|
+
/** Panorama and lighting yaw in radians; defaults to 0 and may wrap beyond one turn. Omitted by older hosts. */
|
|
888
|
+
iblRotation?: number;
|
|
832
889
|
/** A `prop` asset (glb/gltf) rendered as the set the scene sits in, or null. */
|
|
833
890
|
modelAssetId: string | null;
|
|
834
891
|
/** Where that set sits, in the same units as an object's `place3d`. */
|
|
835
892
|
modelPlace: Place3D;
|
|
836
893
|
/**
|
|
837
|
-
*
|
|
838
|
-
*
|
|
894
|
+
* Generate lighting from the set, using the selected map or the set's sky as input.
|
|
895
|
+
* False uses the map directly; the skybox remains independent of this choice.
|
|
839
896
|
*/
|
|
840
897
|
bakeFromModel: boolean;
|
|
841
898
|
/** Whether lights baked into the set render at all. */
|
|
@@ -849,6 +906,10 @@ export interface SceneEnvironment {
|
|
|
849
906
|
/** The set's suggested look, captured when the model was picked; null when it carries none. */
|
|
850
907
|
modelLook: EnvironmentLook | null;
|
|
851
908
|
fog: SceneFog;
|
|
909
|
+
/** Omitted by hosts without volumetric lighting support. */
|
|
910
|
+
volumetricLighting?: SceneVolumetricLighting;
|
|
911
|
+
/** Optimize unshadowed point lights on supported renderers; omitted by older hosts. */
|
|
912
|
+
clusteredLighting?: boolean;
|
|
852
913
|
effects: SceneEffects;
|
|
853
914
|
/**
|
|
854
915
|
* Built-in effects added as layers, in registry order — one keeps its row and tuning while
|
|
@@ -908,7 +969,7 @@ export interface ScenePatch {
|
|
|
908
969
|
* App-level features a client gates on (never version-sniff): `hello` and
|
|
909
970
|
* `app.info` report them — the per-app mirror of {@link InstanceRuntime.capabilities}.
|
|
910
971
|
*/
|
|
911
|
-
export declare const APP_CAPABILITIES: readonly ["storage", "speech", "automations", "controllers", "model-editing", "asset-inspection", "layer-effects", "scene-transitions"];
|
|
972
|
+
export declare const APP_CAPABILITIES: readonly ["storage", "speech", "automations", "controllers", "model-editing", "asset-inspection", "layer-effects", "scene-transitions", "area-lights", "spot-lights", "camera-follow-lights", "shadow-filters", "environment-map-model"];
|
|
912
973
|
export type AppCapability = (typeof APP_CAPABILITIES)[number];
|
|
913
974
|
export declare function isAppCapability(v: unknown): v is AppCapability;
|
|
914
975
|
/** What a loaded model instance can do; absent capabilities answer `unsupported-for-format`. */
|
package/dist/wire/types.js
CHANGED
|
@@ -18,12 +18,14 @@ export function isAssetKind(v) {
|
|
|
18
18
|
}
|
|
19
19
|
// ---- Objects -------------------------------------------------------------------
|
|
20
20
|
export const OBJECT_SPACES = ['2d', '3d'];
|
|
21
|
+
export const SCENE_LIGHT_TYPES = ['directional', 'point', 'ambient', 'area', 'spot'];
|
|
21
22
|
/**
|
|
22
23
|
* Per-light shadow tier. `off` is the old `castShadow: false`; the rest raise
|
|
23
24
|
* shadow-map resolution. A point light pays 6 cube faces for the same tier a
|
|
24
25
|
* directional light covers with one map, so it gets the smaller of the pair.
|
|
25
26
|
*/
|
|
26
27
|
export const SHADOW_QUALITY_LEVELS = ['off', 'low', 'medium', 'high', 'ultra'];
|
|
28
|
+
export const SHADOW_FILTERS = ['pcf', 'pcss'];
|
|
27
29
|
// ---- Runtime state -------------------------------------------------------------
|
|
28
30
|
/**
|
|
29
31
|
* App-level features a client gates on (never version-sniff): `hello` and
|
|
@@ -38,6 +40,11 @@ export const APP_CAPABILITIES = [
|
|
|
38
40
|
'asset-inspection',
|
|
39
41
|
'layer-effects',
|
|
40
42
|
'scene-transitions',
|
|
43
|
+
'area-lights',
|
|
44
|
+
'spot-lights',
|
|
45
|
+
'camera-follow-lights',
|
|
46
|
+
'shadow-filters',
|
|
47
|
+
'environment-map-model',
|
|
41
48
|
];
|
|
42
49
|
export function isAppCapability(v) {
|
|
43
50
|
return isOneOf(v, APP_CAPABILITIES);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@laplace.live/persona-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "TypeScript SDK and wire schema for the LAPLACE Persona plugin API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"node": ">=24"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"zod": "^4.6.
|
|
37
|
+
"zod": "^4.6.4"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "rimraf dist && tsc -p tsconfig.build.json",
|