@3driseai/3drise-viewer 0.1.21 → 0.1.23
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/components/CreatedObject.d.ts.map +1 -1
- package/dist/components/CreatedObject.js +6 -1
- package/dist/components/LightObject.d.ts.map +1 -1
- package/dist/components/LightObject.js +24 -9
- package/dist/components/ModelPrimitive.d.ts.map +1 -1
- package/dist/components/ModelPrimitive.js +10 -1
- package/dist/components/ObjectNode.js +1 -1
- package/dist/components/ParticlesGenerator.d.ts +3 -1
- package/dist/components/ParticlesGenerator.d.ts.map +1 -1
- package/dist/components/ParticlesGenerator.js +2 -2
- package/dist/components/ProjectLoader.js +1 -1
- package/dist/components/SceneBuilder.d.ts.map +1 -1
- package/dist/components/SceneBuilder.js +5 -1
- package/dist/components/fluorescentTube/FluorescentTube.d.ts +14 -0
- package/dist/components/fluorescentTube/FluorescentTube.d.ts.map +1 -0
- package/dist/components/fluorescentTube/FluorescentTube.js +262 -0
- package/dist/components/fluorescentTube/index.d.ts +5 -0
- package/dist/components/fluorescentTube/index.d.ts.map +1 -0
- package/dist/components/fluorescentTube/index.js +3 -0
- package/dist/components/fluorescentTube/tubeMaterials.d.ts +34 -0
- package/dist/components/fluorescentTube/tubeMaterials.d.ts.map +1 -0
- package/dist/components/fluorescentTube/tubeMaterials.js +185 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +4 -0
- package/dist/components/ledArray/LedArray.d.ts +14 -0
- package/dist/components/ledArray/LedArray.d.ts.map +1 -0
- package/dist/components/ledArray/LedArray.js +243 -0
- package/dist/components/ledArray/index.d.ts +7 -0
- package/dist/components/ledArray/index.d.ts.map +1 -0
- package/dist/components/ledArray/index.js +4 -0
- package/dist/components/ledArray/ledLayout.d.ts +22 -0
- package/dist/components/ledArray/ledLayout.d.ts.map +1 -0
- package/dist/components/ledArray/ledLayout.js +59 -0
- package/dist/components/ledArray/ledMaterials.d.ts +14 -0
- package/dist/components/ledArray/ledMaterials.d.ts.map +1 -0
- package/dist/components/ledArray/ledMaterials.js +151 -0
- package/dist/components/lightBulb/LightBulb.d.ts +16 -0
- package/dist/components/lightBulb/LightBulb.d.ts.map +1 -0
- package/dist/components/lightBulb/LightBulb.js +141 -0
- package/dist/components/lightBulb/bulbMaterials.d.ts +27 -0
- package/dist/components/lightBulb/bulbMaterials.d.ts.map +1 -0
- package/dist/components/lightBulb/bulbMaterials.js +133 -0
- package/dist/components/lightBulb/bulbShapes.d.ts +15 -0
- package/dist/components/lightBulb/bulbShapes.d.ts.map +1 -0
- package/dist/components/lightBulb/bulbShapes.js +180 -0
- package/dist/components/lightBulb/index.d.ts +5 -0
- package/dist/components/lightBulb/index.d.ts.map +1 -0
- package/dist/components/lightBulb/index.js +3 -0
- package/dist/components/lightBulb/kelvin.d.ts +7 -0
- package/dist/components/lightBulb/kelvin.d.ts.map +1 -0
- package/dist/components/lightBulb/kelvin.js +21 -0
- package/dist/components/lightBulb/types.d.ts +2 -0
- package/dist/components/lightBulb/types.d.ts.map +1 -0
- package/dist/components/lightBulb/types.js +1 -0
- package/dist/components/particles/BioluminescentWakeParticles.d.ts +3 -1
- package/dist/components/particles/BioluminescentWakeParticles.d.ts.map +1 -1
- package/dist/components/particles/BioluminescentWakeParticles.js +7 -3
- package/dist/components/particles/BrownianDustParticles.d.ts +3 -1
- package/dist/components/particles/BrownianDustParticles.d.ts.map +1 -1
- package/dist/components/particles/BrownianDustParticles.js +7 -3
- package/dist/components/particles/ChladniSandParticles.d.ts +3 -1
- package/dist/components/particles/ChladniSandParticles.d.ts.map +1 -1
- package/dist/components/particles/ChladniSandParticles.js +7 -3
- package/dist/components/particles/ElectronOrbitalsParticles.d.ts +3 -1
- package/dist/components/particles/ElectronOrbitalsParticles.d.ts.map +1 -1
- package/dist/components/particles/ElectronOrbitalsParticles.js +7 -3
- package/dist/components/particles/FaradayPowderParticles.d.ts +3 -1
- package/dist/components/particles/FaradayPowderParticles.d.ts.map +1 -1
- package/dist/components/particles/FaradayPowderParticles.js +7 -3
- package/dist/components/particles/GrinderSparksParticles.d.ts +3 -1
- package/dist/components/particles/GrinderSparksParticles.d.ts.map +1 -1
- package/dist/components/particles/GrinderSparksParticles.js +7 -3
- package/dist/components/particles/NeonRainParticles.d.ts +3 -1
- package/dist/components/particles/NeonRainParticles.d.ts.map +1 -1
- package/dist/components/particles/NeonRainParticles.js +7 -3
- package/dist/components/particles/TidalStreamParticles.d.ts +3 -1
- package/dist/components/particles/TidalStreamParticles.d.ts.map +1 -1
- package/dist/components/particles/TidalStreamParticles.js +7 -3
- package/dist/components/spotBeam/SpotBeam.d.ts +14 -0
- package/dist/components/spotBeam/SpotBeam.d.ts.map +1 -0
- package/dist/components/spotBeam/SpotBeam.js +244 -0
- package/dist/components/spotBeam/beamPatterns.d.ts +11 -0
- package/dist/components/spotBeam/beamPatterns.d.ts.map +1 -0
- package/dist/components/spotBeam/beamPatterns.js +106 -0
- package/dist/components/spotBeam/index.d.ts +7 -0
- package/dist/components/spotBeam/index.d.ts.map +1 -0
- package/dist/components/spotBeam/index.js +4 -0
- package/dist/components/spotBeam/spotBeamMaterials.d.ts +16 -0
- package/dist/components/spotBeam/spotBeamMaterials.d.ts.map +1 -0
- package/dist/components/spotBeam/spotBeamMaterials.js +307 -0
- package/dist/hooks/index.d.ts +4 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +3 -1
- package/dist/hooks/useConfigLive.d.ts +2 -2
- package/dist/hooks/useConfigLive.d.ts.map +1 -1
- package/dist/hooks/useConfigLive.js +1 -1
- package/dist/hooks/useLightConfig.d.ts +25 -0
- package/dist/hooks/useLightConfig.d.ts.map +1 -0
- package/dist/hooks/useLightConfig.js +125 -0
- package/dist/hooks/useLightLive.d.ts +12 -0
- package/dist/hooks/useLightLive.d.ts.map +1 -0
- package/dist/hooks/useLightLive.js +53 -0
- package/dist/hooks/useShadowApplication.d.ts +8 -0
- package/dist/hooks/useShadowApplication.d.ts.map +1 -1
- package/dist/hooks/useShadowApplication.js +33 -9
- package/package.json +2 -2
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// spotBeamMaterials — the emitter, its halo, and the haze in the beam
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// The viewer has no tone mapping and no bloom, so everything bright here is
|
|
6
|
+
// drawn by hand: an emissive lens face, a camera-facing halo, and the beam.
|
|
7
|
+
//
|
|
8
|
+
// The beam is a stack of nested cone shells rendered as one InstancedMesh. A
|
|
9
|
+
// single shell can only ever sample the rim of the projected pattern; nesting
|
|
10
|
+
// them (aFrac = 0 at the axis, 1 at the cone edge) samples the pattern across
|
|
11
|
+
// its whole width, which is what makes a slot read as a slot in the air as well
|
|
12
|
+
// as on the floor. Density is weighted towards the axis, the way a real cone
|
|
13
|
+
// has more haze to look through down the middle.
|
|
14
|
+
//
|
|
15
|
+
import { AdditiveBlending, BackSide, Color, DoubleSide, MeshBasicMaterial, ShaderMaterial, Vector3 } from 'three';
|
|
16
|
+
export const BEAM_MODES = ['none', 'soft', 'layered', 'volumetric'];
|
|
17
|
+
const OUTPUT = /* glsl */ `
|
|
18
|
+
#include <tonemapping_fragment>
|
|
19
|
+
#include <colorspace_fragment>
|
|
20
|
+
`;
|
|
21
|
+
function haloMaterial() {
|
|
22
|
+
return new ShaderMaterial({
|
|
23
|
+
uniforms: {
|
|
24
|
+
uColor: { value: new Color(1, 0.9, 0.75) },
|
|
25
|
+
uStrength: { value: 0 },
|
|
26
|
+
uRadius: { value: 0.55 },
|
|
27
|
+
},
|
|
28
|
+
vertexShader: /* glsl */ `
|
|
29
|
+
uniform float uRadius;
|
|
30
|
+
varying vec2 vUv;
|
|
31
|
+
void main() {
|
|
32
|
+
vec4 mv = modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0);
|
|
33
|
+
float s = length(modelMatrix[0].xyz) * uRadius;
|
|
34
|
+
mv.xy += position.xy * s;
|
|
35
|
+
vUv = position.xy;
|
|
36
|
+
gl_Position = projectionMatrix * mv;
|
|
37
|
+
}`,
|
|
38
|
+
fragmentShader: /* glsl */ `
|
|
39
|
+
uniform vec3 uColor;
|
|
40
|
+
uniform float uStrength;
|
|
41
|
+
varying vec2 vUv;
|
|
42
|
+
void main() {
|
|
43
|
+
float d = length(vUv);
|
|
44
|
+
float a = (exp(-d * 9.0) + exp(-d * d * 5.0) * 0.3) * (1.0 - smoothstep(0.65, 1.0, d));
|
|
45
|
+
gl_FragColor = vec4(uColor * uStrength * a, 1.0);
|
|
46
|
+
${OUTPUT}
|
|
47
|
+
}`,
|
|
48
|
+
transparent: true,
|
|
49
|
+
depthWrite: false,
|
|
50
|
+
blending: AdditiveBlending,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function beamMaterial() {
|
|
54
|
+
return new ShaderMaterial({
|
|
55
|
+
uniforms: {
|
|
56
|
+
uColor: { value: new Color(1, 0.9, 0.75) },
|
|
57
|
+
uStrength: { value: 0 },
|
|
58
|
+
uPenumbra: { value: 0.3 },
|
|
59
|
+
uPattern: { value: null },
|
|
60
|
+
uNoise: { value: 0.35 },
|
|
61
|
+
uTime: { value: 0 },
|
|
62
|
+
uLayers: { value: 6 },
|
|
63
|
+
},
|
|
64
|
+
vertexShader: /* glsl */ `
|
|
65
|
+
attribute float aFrac; // 0 at the axis, 1 at the cone edge
|
|
66
|
+
varying float vFrac;
|
|
67
|
+
varying float vT; // 0 at the lens, 1 at the far end
|
|
68
|
+
varying vec2 vPattern; // where this point sits in the mask
|
|
69
|
+
varying vec3 vLocal;
|
|
70
|
+
void main() {
|
|
71
|
+
vec3 p = position;
|
|
72
|
+
vFrac = aFrac;
|
|
73
|
+
vT = clamp(p.y, 0.0, 1.0);
|
|
74
|
+
// Unit cone: at height y the rim sits at radius y, so dividing by
|
|
75
|
+
// y gives the projector's normalised coordinates — the same ones
|
|
76
|
+
// three uses to project the light's map.
|
|
77
|
+
vec2 proj = p.y > 1e-4 ? vec2(p.x, p.z) / p.y : vec2(0.0);
|
|
78
|
+
vPattern = proj * aFrac * 0.5 + 0.5;
|
|
79
|
+
vec3 scaled = vec3(p.x * aFrac, p.y, p.z * aFrac);
|
|
80
|
+
vLocal = scaled;
|
|
81
|
+
vec4 mv = modelViewMatrix * instanceMatrix * vec4(scaled, 1.0);
|
|
82
|
+
gl_Position = projectionMatrix * mv;
|
|
83
|
+
}`,
|
|
84
|
+
fragmentShader: /* glsl */ `
|
|
85
|
+
uniform vec3 uColor;
|
|
86
|
+
uniform float uStrength;
|
|
87
|
+
uniform float uPenumbra;
|
|
88
|
+
uniform sampler2D uPattern;
|
|
89
|
+
uniform float uNoise;
|
|
90
|
+
uniform float uTime;
|
|
91
|
+
uniform float uLayers;
|
|
92
|
+
varying float vFrac;
|
|
93
|
+
varying float vT;
|
|
94
|
+
varying vec2 vPattern;
|
|
95
|
+
varying vec3 vLocal;
|
|
96
|
+
|
|
97
|
+
// cheap value noise, for haze that drifts instead of sitting still
|
|
98
|
+
float hash(vec3 p) { return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453); }
|
|
99
|
+
float vnoise(vec3 p) {
|
|
100
|
+
vec3 i = floor(p), f = fract(p);
|
|
101
|
+
f = f * f * (3.0 - 2.0 * f);
|
|
102
|
+
float n000 = hash(i), n100 = hash(i + vec3(1,0,0));
|
|
103
|
+
float n010 = hash(i + vec3(0,1,0)), n110 = hash(i + vec3(1,1,0));
|
|
104
|
+
float n001 = hash(i + vec3(0,0,1)), n101 = hash(i + vec3(1,0,1));
|
|
105
|
+
float n011 = hash(i + vec3(0,1,1)), n111 = hash(i + vec3(1,1,1));
|
|
106
|
+
return mix(mix(mix(n000,n100,f.x), mix(n010,n110,f.x), f.y),
|
|
107
|
+
mix(mix(n001,n101,f.x), mix(n011,n111,f.x), f.y), f.z);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
void main() {
|
|
111
|
+
float mask = texture2D(uPattern, vPattern).r;
|
|
112
|
+
if (mask < 0.004) discard;
|
|
113
|
+
|
|
114
|
+
// Density: thickest down the axis, thinning towards the rim, and
|
|
115
|
+
// fading with distance from the lens.
|
|
116
|
+
float core = pow(1.0 - vFrac * vFrac, 0.75);
|
|
117
|
+
float edge = mix(1.0, smoothstep(1.0, 0.55, vFrac), uPenumbra);
|
|
118
|
+
float fade = pow(1.0 - vT, 1.8);
|
|
119
|
+
float throat = smoothstep(0.0, 0.05, vT);
|
|
120
|
+
|
|
121
|
+
float haze = 1.0;
|
|
122
|
+
if (uNoise > 0.001) {
|
|
123
|
+
float n = vnoise(vLocal * 3.5 + vec3(0.0, -uTime * 0.35, uTime * 0.12));
|
|
124
|
+
haze = mix(1.0, 0.45 + n, uNoise);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
float a = mask * core * edge * fade * throat * haze / uLayers;
|
|
128
|
+
gl_FragColor = vec4(uColor * uStrength * a, 1.0);
|
|
129
|
+
${OUTPUT}
|
|
130
|
+
}`,
|
|
131
|
+
transparent: true,
|
|
132
|
+
depthWrite: false,
|
|
133
|
+
blending: AdditiveBlending,
|
|
134
|
+
side: DoubleSide,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* One shell, brightest where the view grazes it. No pattern in the air, but it
|
|
139
|
+
* is a single draw and reads perfectly well in a hazy room.
|
|
140
|
+
*/
|
|
141
|
+
function softMaterial() {
|
|
142
|
+
return new ShaderMaterial({
|
|
143
|
+
uniforms: {
|
|
144
|
+
uColor: { value: new Color(1, 0.9, 0.75) },
|
|
145
|
+
uStrength: { value: 0 },
|
|
146
|
+
uPenumbra: { value: 0.3 },
|
|
147
|
+
},
|
|
148
|
+
vertexShader: /* glsl */ `
|
|
149
|
+
varying vec3 vNormal;
|
|
150
|
+
varying vec3 vView;
|
|
151
|
+
varying float vT;
|
|
152
|
+
void main() {
|
|
153
|
+
vec4 mv = modelViewMatrix * vec4(position, 1.0);
|
|
154
|
+
vNormal = normalize(normalMatrix * normal);
|
|
155
|
+
vView = normalize(-mv.xyz);
|
|
156
|
+
vT = clamp(position.y, 0.0, 1.0);
|
|
157
|
+
gl_Position = projectionMatrix * mv;
|
|
158
|
+
}`,
|
|
159
|
+
fragmentShader: /* glsl */ `
|
|
160
|
+
uniform vec3 uColor;
|
|
161
|
+
uniform float uStrength;
|
|
162
|
+
uniform float uPenumbra;
|
|
163
|
+
varying vec3 vNormal;
|
|
164
|
+
varying vec3 vView;
|
|
165
|
+
varying float vT;
|
|
166
|
+
void main() {
|
|
167
|
+
float graze = 1.0 - abs(dot(normalize(vNormal), normalize(vView)));
|
|
168
|
+
float body = pow(graze, 3.0 - uPenumbra);
|
|
169
|
+
float fade = pow(1.0 - vT, 2.0);
|
|
170
|
+
float throat = smoothstep(0.0, 0.05, vT);
|
|
171
|
+
gl_FragColor = vec4(uColor * uStrength * body * fade * throat, 1.0);
|
|
172
|
+
${OUTPUT}
|
|
173
|
+
}`,
|
|
174
|
+
transparent: true,
|
|
175
|
+
depthWrite: false,
|
|
176
|
+
blending: AdditiveBlending,
|
|
177
|
+
side: DoubleSide,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The real thing: march the cone's interior from where the view ray enters it to
|
|
182
|
+
* where it leaves, sampling the projected mask at every step. Because each step
|
|
183
|
+
* knows its own position inside the cone, a slot is a slot in the air, blinds
|
|
184
|
+
* are blinds, and the haze thins with distance the way it should.
|
|
185
|
+
*
|
|
186
|
+
* Rendered on the cone's back faces so the volume is covered whether the camera
|
|
187
|
+
* is outside the beam or standing in it. Geometry is the unit cone (radius == y),
|
|
188
|
+
* so the ray maths works in that space and the mesh scale does the rest.
|
|
189
|
+
*/
|
|
190
|
+
function volumetricMaterial() {
|
|
191
|
+
return new ShaderMaterial({
|
|
192
|
+
uniforms: {
|
|
193
|
+
uColor: { value: new Color(1, 0.9, 0.75) },
|
|
194
|
+
uStrength: { value: 0 },
|
|
195
|
+
uPenumbra: { value: 0.3 },
|
|
196
|
+
uPattern: { value: null },
|
|
197
|
+
uNoise: { value: 0.35 },
|
|
198
|
+
uTime: { value: 0 },
|
|
199
|
+
uCamera: { value: new Vector3() },
|
|
200
|
+
uSteps: { value: 28 },
|
|
201
|
+
},
|
|
202
|
+
vertexShader: /* glsl */ `
|
|
203
|
+
varying vec3 vLocal;
|
|
204
|
+
void main() {
|
|
205
|
+
vLocal = position;
|
|
206
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
207
|
+
}`,
|
|
208
|
+
fragmentShader: /* glsl */ `
|
|
209
|
+
uniform vec3 uColor;
|
|
210
|
+
uniform float uStrength;
|
|
211
|
+
uniform float uPenumbra;
|
|
212
|
+
uniform sampler2D uPattern;
|
|
213
|
+
uniform float uNoise;
|
|
214
|
+
uniform float uTime;
|
|
215
|
+
uniform vec3 uCamera; // camera position in this cone's local space
|
|
216
|
+
uniform float uSteps;
|
|
217
|
+
varying vec3 vLocal;
|
|
218
|
+
|
|
219
|
+
float hash(vec3 p) { return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453); }
|
|
220
|
+
float vnoise(vec3 p) {
|
|
221
|
+
vec3 i = floor(p), f = fract(p);
|
|
222
|
+
f = f * f * (3.0 - 2.0 * f);
|
|
223
|
+
float n000 = hash(i), n100 = hash(i + vec3(1,0,0));
|
|
224
|
+
float n010 = hash(i + vec3(0,1,0)), n110 = hash(i + vec3(1,1,0));
|
|
225
|
+
float n001 = hash(i + vec3(0,0,1)), n101 = hash(i + vec3(1,0,1));
|
|
226
|
+
float n011 = hash(i + vec3(0,1,1)), n111 = hash(i + vec3(1,1,1));
|
|
227
|
+
return mix(mix(mix(n000,n100,f.x), mix(n010,n110,f.x), f.y),
|
|
228
|
+
mix(mix(n001,n101,f.x), mix(n011,n111,f.x), f.y), f.z);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
void main() {
|
|
232
|
+
vec3 ro = uCamera;
|
|
233
|
+
vec3 rd = normalize(vLocal - ro);
|
|
234
|
+
|
|
235
|
+
// Infinite cone x^2 + z^2 = y^2, opening along +y.
|
|
236
|
+
float a = rd.x * rd.x + rd.z * rd.z - rd.y * rd.y;
|
|
237
|
+
float b = 2.0 * (ro.x * rd.x + ro.z * rd.z - ro.y * rd.y);
|
|
238
|
+
float c = ro.x * ro.x + ro.z * ro.z - ro.y * ro.y;
|
|
239
|
+
|
|
240
|
+
float t0, t1;
|
|
241
|
+
if (abs(a) < 1e-5) {
|
|
242
|
+
if (abs(b) < 1e-6) discard;
|
|
243
|
+
t0 = -c / b; t1 = t0; // ray parallel to the surface
|
|
244
|
+
} else {
|
|
245
|
+
float disc = b * b - 4.0 * a * c;
|
|
246
|
+
if (disc < 0.0) discard;
|
|
247
|
+
float sq = sqrt(disc);
|
|
248
|
+
t0 = (-b - sq) / (2.0 * a);
|
|
249
|
+
t1 = (-b + sq) / (2.0 * a);
|
|
250
|
+
if (t0 > t1) { float tmp = t0; t0 = t1; t1 = tmp; }
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Keep the half with y in [0, 1] — the cone, not its mirror.
|
|
254
|
+
const int MAX_STEPS = 48;
|
|
255
|
+
float tEnter = max(t0, 0.0);
|
|
256
|
+
float tExit = max(t1, 0.0);
|
|
257
|
+
if (tExit <= tEnter) { tEnter = 0.0; tExit = max(t1, 0.0); }
|
|
258
|
+
if (tExit <= 0.0) discard;
|
|
259
|
+
|
|
260
|
+
float acc = 0.0;
|
|
261
|
+
float steps = clamp(uSteps, 8.0, float(MAX_STEPS));
|
|
262
|
+
float dt = (tExit - tEnter) / steps;
|
|
263
|
+
// Dither the start so slices do not band.
|
|
264
|
+
float jitter = hash(vec3(gl_FragCoord.xy, uTime)) * dt;
|
|
265
|
+
|
|
266
|
+
for (int i = 0; i < MAX_STEPS; i++) {
|
|
267
|
+
if (float(i) >= steps) break;
|
|
268
|
+
vec3 p = ro + rd * (tEnter + jitter + float(i) * dt);
|
|
269
|
+
if (p.y <= 0.04 || p.y > 1.0) continue; // skip the singular near field
|
|
270
|
+
float r = length(p.xz) / p.y;
|
|
271
|
+
if (r > 1.0) continue;
|
|
272
|
+
|
|
273
|
+
vec2 uv = (p.xz / p.y) * 0.5 + 0.5;
|
|
274
|
+
float mask = texture2D(uPattern, uv).r;
|
|
275
|
+
if (mask < 0.004) continue;
|
|
276
|
+
|
|
277
|
+
float edge = mix(1.0, smoothstep(1.0, 0.45, r), uPenumbra);
|
|
278
|
+
float fall = 1.0 / (0.35 + p.y * p.y * 2.2); // falls off from the lens, softened so the near field does not swamp the pattern
|
|
279
|
+
float haze = 1.0;
|
|
280
|
+
if (uNoise > 0.001) {
|
|
281
|
+
float n = vnoise(p * 4.0 + vec3(0.0, -uTime * 0.3, uTime * 0.1));
|
|
282
|
+
haze = mix(1.0, 0.45 + n, uNoise);
|
|
283
|
+
}
|
|
284
|
+
acc += mask * edge * fall * haze * dt;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
gl_FragColor = vec4(uColor * uStrength * acc, 1.0);
|
|
288
|
+
${OUTPUT}
|
|
289
|
+
}`,
|
|
290
|
+
transparent: true,
|
|
291
|
+
depthWrite: false,
|
|
292
|
+
blending: AdditiveBlending,
|
|
293
|
+
side: BackSide,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
export function createSpotBeamMaterials() {
|
|
297
|
+
return {
|
|
298
|
+
lens: new MeshBasicMaterial({ color: '#0a0a0a', toneMapped: false }),
|
|
299
|
+
halo: haloMaterial(),
|
|
300
|
+
soft: softMaterial(),
|
|
301
|
+
layered: beamMaterial(),
|
|
302
|
+
volumetric: volumetricMaterial(),
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
export function disposeSpotBeamMaterials(m) {
|
|
306
|
+
Object.values(m).forEach((mat) => mat.dispose());
|
|
307
|
+
}
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -44,6 +44,9 @@ export { useLightHandlers } from './useLightHandlers';
|
|
|
44
44
|
export type { UseLightMouseMoveOptions } from './useLightMouseMove';
|
|
45
45
|
export { useLightMouseMove } from './useLightMouseMove';
|
|
46
46
|
export { useLightShadow } from './useLightShadow';
|
|
47
|
+
export { LIGHT_STRUCTURAL, applyLightLive, useLightLive } from './useLightLive';
|
|
48
|
+
export type { UseLightConfigResult } from './useLightConfig';
|
|
49
|
+
export { applyLightConfig, useLightConfig } from './useLightConfig';
|
|
47
50
|
export { useMaterialApplication } from './useMaterialApplication';
|
|
48
51
|
export type { UseMaterialOptionsReturn } from './useMaterialOptions';
|
|
49
52
|
export { useMaterialOptions } from './useMaterialOptions';
|
|
@@ -70,7 +73,7 @@ export type { PathAnimationOptions } from './usePathAnimation';
|
|
|
70
73
|
export { usePathAnimation } from './usePathAnimation';
|
|
71
74
|
export { useRegisterScene } from './useRegisterScene';
|
|
72
75
|
export { useSequenceAnimation } from './useSequenceAnimation';
|
|
73
|
-
export { useShadowApplication } from './useShadowApplication';
|
|
76
|
+
export { applyShadowFlags, useShadowApplication } from './useShadowApplication';
|
|
74
77
|
export { useShapeGeometry } from './useShapeGeometry';
|
|
75
78
|
export type { SkyAnimValues } from './useSkyAnimation';
|
|
76
79
|
export { useSkyAnimation } from './useSkyAnimation';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
|
package/dist/hooks/index.js
CHANGED
|
@@ -30,6 +30,8 @@ export { useLightAnimation } from './useLightAnimation.js';
|
|
|
30
30
|
export { useLightHandlers } from './useLightHandlers.js';
|
|
31
31
|
export { useLightMouseMove } from './useLightMouseMove.js';
|
|
32
32
|
export { useLightShadow } from './useLightShadow.js';
|
|
33
|
+
export { LIGHT_STRUCTURAL, applyLightLive, useLightLive } from './useLightLive.js';
|
|
34
|
+
export { applyLightConfig, useLightConfig } from './useLightConfig.js';
|
|
33
35
|
export { useMaterialApplication } from './useMaterialApplication.js';
|
|
34
36
|
export { useMaterialOptions } from './useMaterialOptions.js';
|
|
35
37
|
export { useMaterialUniforms } from './useMaterialUniforms.js';
|
|
@@ -51,7 +53,7 @@ export { useParticlesTextures } from './useParticlesTextures.js';
|
|
|
51
53
|
export { usePathAnimation } from './usePathAnimation.js';
|
|
52
54
|
export { useRegisterScene } from './useRegisterScene.js';
|
|
53
55
|
export { useSequenceAnimation } from './useSequenceAnimation.js';
|
|
54
|
-
export { useShadowApplication } from './useShadowApplication.js';
|
|
56
|
+
export { applyShadowFlags, useShadowApplication } from './useShadowApplication.js';
|
|
55
57
|
export { useShapeGeometry } from './useShapeGeometry.js';
|
|
56
58
|
export { useSkyAnimation } from './useSkyAnimation.js';
|
|
57
59
|
export { useSkySystem } from './useSkySystem.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type MutableRefObject, type RefObject } from 'react';
|
|
2
2
|
import type { Object3D } from 'three';
|
|
3
3
|
/**
|
|
4
|
-
* Live config editing for the generative families (particles
|
|
4
|
+
* Live config editing for the generative families (particles, effects) and lights,
|
|
5
5
|
* the counterpart to `useOceanLive` / `useTerrainLive`.
|
|
6
6
|
*
|
|
7
7
|
* A slider drag fires on every pointer-move. Routing that through Redux hands the
|
|
@@ -29,7 +29,7 @@ import type { Object3D } from 'three';
|
|
|
29
29
|
export declare const CONFIG_LIVE_KEY = "__configLive";
|
|
30
30
|
export type ConfigLiveValue = number | string | boolean;
|
|
31
31
|
/** Generative families that support live config editing. */
|
|
32
|
-
export type ConfigLiveFamily = 'particles' | 'effects';
|
|
32
|
+
export type ConfigLiveFamily = 'particles' | 'effects' | 'lights';
|
|
33
33
|
export interface ConfigLiveHandle {
|
|
34
34
|
id: string;
|
|
35
35
|
family: ConfigLiveFamily;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useConfigLive.d.ts","sourceRoot":"","sources":["../../src/hooks/useConfigLive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,gBAAgB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAChG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,oFAAoF;AACpF,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAExD,4DAA4D;AAC5D,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"useConfigLive.d.ts","sourceRoot":"","sources":["../../src/hooks/useConfigLive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,gBAAgB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAChG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,oFAAoF;AACpF,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAExD,4DAA4D;AAC5D,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAElE,MAAM,WAAW,gBAAgB;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,gBAAgB,CAAC;IACzB,+DAA+D;IAC/D,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;CAClD;AAgBD,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG;IAAE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAAA;CAAE,CAYpG;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAC/B,OAAO,EAAE,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,EACnC,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,KAAK,IAAI,GAC5D,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CA8BnD;AAED,eAAe,aAAa,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useLayoutEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { SceneStore } from '@3driseai/3drise-core';
|
|
3
3
|
/**
|
|
4
|
-
* Live config editing for the generative families (particles
|
|
4
|
+
* Live config editing for the generative families (particles, effects) and lights,
|
|
5
5
|
* the counterpart to `useOceanLive` / `useTerrainLive`.
|
|
6
6
|
*
|
|
7
7
|
* A slider drag fires on every pointer-move. Routing that through Redux hands the
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MutableRefObject, RefObject } from 'react';
|
|
2
|
+
import type { Light, Object3D } from 'three';
|
|
3
|
+
import type { AnimationOptions, MouseMoveDomain } from '@3driseai/3drise-core';
|
|
4
|
+
/**
|
|
5
|
+
* Push a light config onto a THREE light. Only properties the light actually
|
|
6
|
+
* has are written, so one function covers every light type.
|
|
7
|
+
*/
|
|
8
|
+
export declare function applyLightConfig(light: Light, cfg: Record<string, any>): void;
|
|
9
|
+
export interface UseLightConfigResult {
|
|
10
|
+
/** Committed config + live drags + animation overrides, refreshed every frame. */
|
|
11
|
+
merged: MutableRefObject<Record<string, any>>;
|
|
12
|
+
/** What the slider drags have written since the last commit. */
|
|
13
|
+
live: MutableRefObject<Record<string, string | number | boolean>>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Registers the live-edit handle, evaluates the light's keyframe animations and
|
|
17
|
+
* its pointer bindings, and applies the merged config to the light.
|
|
18
|
+
*
|
|
19
|
+
* Call it before the component's own useFrame: R3F runs frame callbacks in
|
|
20
|
+
* subscription order, so a light with visuals of its own reads a `merged` that is
|
|
21
|
+
* already up to date for this frame.
|
|
22
|
+
*/
|
|
23
|
+
export declare function useLightConfig(rootRef: RefObject<Object3D | null>, lightRef: RefObject<Light | null>, id: string | undefined, config: Record<string, any>, animations: AnimationOptions[] | undefined, mouseMove?: MouseMoveDomain): UseLightConfigResult;
|
|
24
|
+
export default useLightConfig;
|
|
25
|
+
//# sourceMappingURL=useLightConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLightConfig.d.ts","sourceRoot":"","sources":["../../src/hooks/useLightConfig.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEzD,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAS/E;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CA6B7E;AAED,MAAM,WAAW,oBAAoB;IACjC,kFAAkF;IAClF,MAAM,EAAE,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAC9C,gEAAgE;IAChE,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CACrE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC1B,OAAO,EAAE,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,EACnC,QAAQ,EAAE,SAAS,CAAC,KAAK,GAAG,IAAI,CAAC,EACjC,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,UAAU,EAAE,gBAAgB,EAAE,GAAG,SAAS,EAC1C,SAAS,CAAC,EAAE,eAAe,GAC5B,oBAAoB,CA2DtB;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// useLightConfig — one config → the THREE light, every frame
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// The light family's version of the generative config pattern (see
|
|
6
|
+
// useConfigLive / configAnimations): one committed config in Redux, two
|
|
7
|
+
// per-frame override layers, one merge point.
|
|
8
|
+
//
|
|
9
|
+
// committed config → live drag overrides → keyframes → pointer (mouse move)
|
|
10
|
+
//
|
|
11
|
+
// `applyLightConfig` is the single door into the light: a knob it does not read
|
|
12
|
+
// cannot be animated, which is exactly what core's LIGHTS_ANIMATABLE lists. The
|
|
13
|
+
// merged config is also handed back through `merged`, so a light with visuals
|
|
14
|
+
// of its own (the bulb's filament, glow and halo) reads the same values the
|
|
15
|
+
// light got, instead of the committed config alone.
|
|
16
|
+
//
|
|
17
|
+
// This replaces useLightAnimation / useLightMouseMove (handleLightAnimations,
|
|
18
|
+
// handleLightMouseMove) for createdObject lights: those only knew the six
|
|
19
|
+
// properties of a THREE light, so a bulb's temperature, flicker or halo could
|
|
20
|
+
// never be driven. LightsManager's ambient + sun still use that path — they are
|
|
21
|
+
// scene lights, not config objects.
|
|
22
|
+
//
|
|
23
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
24
|
+
import { Color } from 'three';
|
|
25
|
+
import { DEFAULT_LIGHTS, getGenerativeAnimatable } from '@3driseai/3drise-core';
|
|
26
|
+
import { useFrame } from '@react-three/fiber';
|
|
27
|
+
import { applyConfigKeyframeAnimations, applyConfigMouseMove } from '../utils/configAnimations.js';
|
|
28
|
+
import { useLightLive } from './useLightLive.js';
|
|
29
|
+
import { kelvinToColor } from '../components/lightBulb/kelvin.js';
|
|
30
|
+
const tmpColor = new Color();
|
|
31
|
+
/**
|
|
32
|
+
* Push a light config onto a THREE light. Only properties the light actually
|
|
33
|
+
* has are written, so one function covers every light type.
|
|
34
|
+
*/
|
|
35
|
+
export function applyLightConfig(light, cfg) {
|
|
36
|
+
if (typeof cfg.intensity === 'number')
|
|
37
|
+
light.intensity = cfg.intensity;
|
|
38
|
+
// A type that has a colour-mode knob takes its colour from Kelvin in
|
|
39
|
+
// 'temperature' mode; everything else takes the colour knob. Read the knob
|
|
40
|
+
// rather than listing type names: `base` lays DEFAULT_LIGHTS under the config,
|
|
41
|
+
// so a type that has the knob always carries it and one that does not never
|
|
42
|
+
// will — and a new light type needs no edit here.
|
|
43
|
+
const byTemperature = typeof cfg.colorMode === 'string' && cfg.colorMode === 'temperature';
|
|
44
|
+
if (byTemperature) {
|
|
45
|
+
if (typeof cfg.temperature === 'number')
|
|
46
|
+
light.color.copy(kelvinToColor(cfg.temperature, tmpColor));
|
|
47
|
+
}
|
|
48
|
+
else if (typeof cfg.color === 'string' || cfg.color?.isColor) {
|
|
49
|
+
// A pointer binding on a colour interpolates to a THREE.Color, a keyframe
|
|
50
|
+
// or the committed config to a hex string; Color.set() takes either.
|
|
51
|
+
light.color.set(cfg.color);
|
|
52
|
+
}
|
|
53
|
+
const target = light;
|
|
54
|
+
if (typeof cfg.distance === 'number' && 'distance' in light)
|
|
55
|
+
target.distance = cfg.distance;
|
|
56
|
+
if (typeof cfg.decay === 'number' && 'decay' in light)
|
|
57
|
+
target.decay = cfg.decay;
|
|
58
|
+
if (typeof cfg.angle === 'number' && 'angle' in light)
|
|
59
|
+
target.angle = cfg.angle;
|
|
60
|
+
if (typeof cfg.penumbra === 'number' && 'penumbra' in light)
|
|
61
|
+
target.penumbra = cfg.penumbra;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Registers the live-edit handle, evaluates the light's keyframe animations and
|
|
65
|
+
* its pointer bindings, and applies the merged config to the light.
|
|
66
|
+
*
|
|
67
|
+
* Call it before the component's own useFrame: R3F runs frame callbacks in
|
|
68
|
+
* subscription order, so a light with visuals of its own reads a `merged` that is
|
|
69
|
+
* already up to date for this frame.
|
|
70
|
+
*/
|
|
71
|
+
export function useLightConfig(rootRef, lightRef, id, config, animations, mouseMove) {
|
|
72
|
+
// Fill the type's defaults under the committed config: a knob the saved config
|
|
73
|
+
// never stored (an older light has no `temperature`) has no value to drive
|
|
74
|
+
// from, and a keyframe or pointer binding on it would silently do nothing.
|
|
75
|
+
const base = useMemo(() => {
|
|
76
|
+
const merged = { ...(DEFAULT_LIGHTS[config?.type] ?? {}) };
|
|
77
|
+
// Skip undefined: a config that carries the key explicitly unset (a
|
|
78
|
+
// controller writing a cleared field) would otherwise erase the default
|
|
79
|
+
// and leave the knob with nothing to drive from.
|
|
80
|
+
for (const key of Object.keys(config ?? {})) {
|
|
81
|
+
if (config[key] !== undefined)
|
|
82
|
+
merged[key] = config[key];
|
|
83
|
+
}
|
|
84
|
+
return merged;
|
|
85
|
+
}, [config]);
|
|
86
|
+
const live = useLightLive(rootRef, lightRef, id, base);
|
|
87
|
+
const merged = useRef(base);
|
|
88
|
+
const animOverride = useRef({});
|
|
89
|
+
const animFrom = useRef({});
|
|
90
|
+
const animStart = useRef(performance.now());
|
|
91
|
+
// Editing the keyframes restarts them, and re-samples every `from`-less start value.
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
animStart.current = performance.now();
|
|
94
|
+
animFrom.current = {};
|
|
95
|
+
for (const k of Object.keys(animOverride.current))
|
|
96
|
+
delete animOverride.current[k];
|
|
97
|
+
}, [animations]);
|
|
98
|
+
// A committed config supersedes the previous frame's merge until the next one.
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
merged.current = base;
|
|
101
|
+
}, [base]);
|
|
102
|
+
const allowed = useMemo(() => new Set(getGenerativeAnimatable('lights', base).map((p) => p.value)), [base]);
|
|
103
|
+
useFrame((state) => {
|
|
104
|
+
const hasAnimations = !!animations?.length;
|
|
105
|
+
const hasPointer = !!mouseMove?.enabled && !!mouseMove.properties?.length;
|
|
106
|
+
const hasLive = Object.keys(live.current).length > 0;
|
|
107
|
+
if (!hasAnimations && !hasPointer && !hasLive) {
|
|
108
|
+
// Nothing overriding: the committed config is already on the light
|
|
109
|
+
// (R3F props), and useLightMouseMove owns the frame from here.
|
|
110
|
+
merged.current = base;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Keyframes first, pointer on top: a property bound to both follows the cursor.
|
|
114
|
+
applyConfigKeyframeAnimations(animations, animFrom.current, base, animOverride.current, performance.now() - animStart.current, allowed);
|
|
115
|
+
if (hasPointer)
|
|
116
|
+
applyConfigMouseMove(mouseMove.properties, base, animOverride.current, state.pointer, allowed);
|
|
117
|
+
const next = { ...base, ...live.current, ...animOverride.current };
|
|
118
|
+
merged.current = next;
|
|
119
|
+
const light = lightRef.current;
|
|
120
|
+
if (light)
|
|
121
|
+
applyLightConfig(light, next);
|
|
122
|
+
});
|
|
123
|
+
return { merged, live };
|
|
124
|
+
}
|
|
125
|
+
export default useLightConfig;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { MutableRefObject, RefObject } from 'react';
|
|
2
|
+
import type { Light, Object3D } from 'three';
|
|
3
|
+
import type { ConfigLiveValue } from './useConfigLive';
|
|
4
|
+
export declare const LIGHT_STRUCTURAL: readonly string[];
|
|
5
|
+
export declare function applyLightLive(light: Light, overrides: Record<string, ConfigLiveValue>, config: Record<string, any>): void;
|
|
6
|
+
/**
|
|
7
|
+
* Registers the live handle on `rootRef` (found by `id`) and applies drags to
|
|
8
|
+
* `lightRef`. Returns the ref of everything dragged since the last commit.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useLightLive(rootRef: RefObject<Object3D | null>, lightRef: RefObject<Light | null>, id: string | undefined, config: Record<string, any>): MutableRefObject<Record<string, ConfigLiveValue>>;
|
|
11
|
+
export default useLightLive;
|
|
12
|
+
//# sourceMappingURL=useLightLive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLightLive.d.ts","sourceRoot":"","sources":["../../src/hooks/useLightLive.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGvD,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAU7C,CAAC;AAKF,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAyB1H;AAED;;;GAGG;AACH,wBAAgB,YAAY,CACxB,OAAO,EAAE,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,EACnC,QAAQ,EAAE,SAAS,CAAC,KAAK,GAAG,IAAI,CAAC,EACjC,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAMnD;AAED,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useConfigLiveHandle } from './useConfigLive.js';
|
|
2
|
+
import { kelvinToColor } from '../components/lightBulb/kelvin.js';
|
|
3
|
+
export const LIGHT_STRUCTURAL = [
|
|
4
|
+
'shape', 'glass', 'glassTint', 'colorMode', 'on', 'targetPosition',
|
|
5
|
+
// spotBeam: the gobo is redrawn and the beam/emitter geometry is rebuilt.
|
|
6
|
+
'pattern', 'patternSoftness', 'patternRotation', 'beamMode', 'emitter',
|
|
7
|
+
// ledArray: the die layout is rebuilt. fluorescentTube: the tube geometry is.
|
|
8
|
+
'layout', 'count', 'rows', 'spacing', 'dieSize',
|
|
9
|
+
'tubes', 'tubeSpacing', 'length', 'diameter', 'startup',
|
|
10
|
+
'shadow.enabled', 'shadow.mapSize', 'shadow.cameraNear', 'shadow.cameraFar',
|
|
11
|
+
'shadow.cameraLeft', 'shadow.cameraRight', 'shadow.cameraTop', 'shadow.cameraBottom',
|
|
12
|
+
'shadow.cameraFov', 'shadow.focus',
|
|
13
|
+
];
|
|
14
|
+
const DIRECT = ['intensity', 'decay', 'distance', 'angle', 'penumbra'];
|
|
15
|
+
const SHADOW = ['bias', 'normalBias', 'radius', 'intensity'];
|
|
16
|
+
export function applyLightLive(light, overrides, config) {
|
|
17
|
+
// A type that has a colour-mode knob drives the light from either `color` or
|
|
18
|
+
// `temperature`; the plain lights only ever have `color`. Read the knob, not a
|
|
19
|
+
// list of type names — `config` here is the defaults-merged base.
|
|
20
|
+
const colorMode = typeof config?.colorMode === 'string' ? config.colorMode : 'color';
|
|
21
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
22
|
+
if (DIRECT.includes(key)) {
|
|
23
|
+
if (typeof value === 'number' && key in light)
|
|
24
|
+
light[key] = value;
|
|
25
|
+
}
|
|
26
|
+
else if (key === 'color') {
|
|
27
|
+
if (colorMode === 'color' && typeof value === 'string')
|
|
28
|
+
light.color.set(value);
|
|
29
|
+
}
|
|
30
|
+
else if (key === 'temperature') {
|
|
31
|
+
if (colorMode === 'temperature' && typeof value === 'number')
|
|
32
|
+
kelvinToColor(value, light.color);
|
|
33
|
+
}
|
|
34
|
+
else if (key.startsWith('shadow.')) {
|
|
35
|
+
const k = key.slice(7);
|
|
36
|
+
const shadow = light.shadow;
|
|
37
|
+
if (SHADOW.includes(k) && typeof value === 'number' && shadow)
|
|
38
|
+
shadow[k] = value;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Registers the live handle on `rootRef` (found by `id`) and applies drags to
|
|
44
|
+
* `lightRef`. Returns the ref of everything dragged since the last commit.
|
|
45
|
+
*/
|
|
46
|
+
export function useLightLive(rootRef, lightRef, id, config) {
|
|
47
|
+
return useConfigLiveHandle(rootRef, id, 'lights', config, LIGHT_STRUCTURAL, (overrides) => {
|
|
48
|
+
const light = lightRef.current;
|
|
49
|
+
if (light)
|
|
50
|
+
applyLightLive(light, overrides, config);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export default useLightLive;
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import { Object3D } from "three";
|
|
1
2
|
import type { Object3DSource } from "../utils/resolveObject3D";
|
|
3
|
+
/**
|
|
4
|
+
* Set castShadow / receiveShadow on every mesh under `obj`. Callers resolve an
|
|
5
|
+
* unset flag with `resolveShadowFlags(type, meshSettings)` from core, which
|
|
6
|
+
* falls back to the object type's default. Shared with ModelPrimitive, which
|
|
7
|
+
* calls it for models that attach after this hook's effect has already run.
|
|
8
|
+
*/
|
|
9
|
+
export declare const applyShadowFlags: (obj: Object3D, castShadow: boolean | undefined, receiveShadow: boolean | undefined) => void;
|
|
2
10
|
export declare const useShadowApplication: (source: Object3DSource, castShadow: boolean | undefined, receiveShadow: boolean | undefined, triggerKey?: unknown) => void;
|
|
3
11
|
//# sourceMappingURL=useShadowApplication.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useShadowApplication.d.ts","sourceRoot":"","sources":["../../src/hooks/useShadowApplication.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useShadowApplication.d.ts","sourceRoot":"","sources":["../../src/hooks/useShadowApplication.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,QAAQ,EAAE,YAAY,OAAO,GAAG,SAAS,EAAE,eAAe,OAAO,GAAG,SAAS,KAAG,IAwBrH,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,QAAQ,cAAc,EAAE,YAAY,OAAO,GAAG,SAAS,EAAE,eAAe,OAAO,GAAG,SAAS,EAAE,aAAa,OAAO,KAAG,IASxJ,CAAC"}
|