@xperimntl/vue-threejs-postprocessing 1.1.0 → 1.1.1

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.
@@ -0,0 +1,555 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var vue = require('vue');
6
+ var postprocessing = require('postprocessing');
7
+ var vueThreejs = require('@xperimntl/vue-threejs');
8
+
9
+ /** Injection key for the EffectComposer context. */
10
+ const COMPOSER_CONTEXT = Symbol('v3f-composer-context');
11
+ /** Injection key for plugin-level defaults. */
12
+ const POSTPROCESSING_DEFAULTS = Symbol('v3f-postprocessing-defaults');
13
+
14
+ const EffectComposer = vue.defineComponent({
15
+ name: 'EffectComposer',
16
+ props: {
17
+ enabled: {
18
+ type: Boolean,
19
+ default: true
20
+ },
21
+ multisampling: {
22
+ type: Number,
23
+ default: 8
24
+ },
25
+ autoClear: {
26
+ type: Boolean,
27
+ default: true
28
+ },
29
+ resolutionScale: {
30
+ type: Number,
31
+ default: 1
32
+ },
33
+ depthBuffer: {
34
+ type: Boolean,
35
+ default: true
36
+ },
37
+ stencilBuffer: {
38
+ type: Boolean,
39
+ default: false
40
+ }
41
+ },
42
+ setup(props, {
43
+ slots
44
+ }) {
45
+ const state = vueThreejs.useThree();
46
+ const composerRef = vue.shallowRef(null);
47
+
48
+ // Ordered list of registered effects
49
+ const effects = [];
50
+
51
+ // Track the current EffectPass so we can dispose/replace it
52
+ let currentEffectPass = null;
53
+ let currentRenderPass = null;
54
+
55
+ // Store the original autoClear value so we can restore on unmount
56
+ let originalAutoClear = null;
57
+ function disposeComposer() {
58
+ // Dispose composer
59
+ if (composerRef.value) {
60
+ composerRef.value.dispose();
61
+ composerRef.value = null;
62
+ }
63
+ currentEffectPass = null;
64
+ currentRenderPass = null;
65
+ }
66
+ function createComposer() {
67
+ const {
68
+ gl,
69
+ scene,
70
+ camera
71
+ } = state.value;
72
+ if (!gl || !scene || !camera) return null;
73
+
74
+ // Dispose previous composer if it exists
75
+ disposeComposer();
76
+ const composer = new postprocessing.EffectComposer(gl, {
77
+ multisampling: props.multisampling > 0 ? props.multisampling : 0,
78
+ depthBuffer: props.depthBuffer,
79
+ stencilBuffer: props.stencilBuffer
80
+ });
81
+
82
+ // Create render pass
83
+ currentRenderPass = new postprocessing.RenderPass(scene, camera);
84
+ composer.addPass(currentRenderPass);
85
+
86
+ // Create effect pass if we already have effects
87
+ if (effects.length > 0) {
88
+ const sortedEffects = [...effects].sort((a, b) => a.priority - b.priority).map(e => e.effect);
89
+ currentEffectPass = new postprocessing.EffectPass(camera, ...sortedEffects);
90
+ composer.addPass(currentEffectPass);
91
+ }
92
+ composerRef.value = composer;
93
+
94
+ // Disable default renderer autoClear
95
+ if (originalAutoClear === null) {
96
+ originalAutoClear = gl.autoClear;
97
+ }
98
+ gl.autoClear = false;
99
+ return composer;
100
+ }
101
+ function rebuildEffectPass() {
102
+ const composer = composerRef.value;
103
+ if (!composer) return;
104
+ const {
105
+ camera
106
+ } = state.value;
107
+
108
+ // Remove old effect pass
109
+ if (currentEffectPass) {
110
+ composer.removePass(currentEffectPass);
111
+ currentEffectPass.dispose();
112
+ currentEffectPass = null;
113
+ }
114
+
115
+ // Build new effect pass with current effects
116
+ if (effects.length > 0) {
117
+ const sortedEffects = [...effects].sort((a, b) => a.priority - b.priority).map(e => e.effect);
118
+ currentEffectPass = new postprocessing.EffectPass(camera, ...sortedEffects);
119
+ composer.addPass(currentEffectPass);
120
+ }
121
+ }
122
+ vue.watch(() => [state.value.gl, state.value.scene, state.value.camera], ([gl, scene, camera]) => {
123
+ if (!gl || !scene || !camera) {
124
+ disposeComposer();
125
+ return;
126
+ }
127
+ createComposer();
128
+ }, {
129
+ immediate: true
130
+ });
131
+
132
+ // Provide the context so child effect components can register
133
+ const composerContext = {
134
+ addEffect(effect, priority = 0) {
135
+ effects.push({
136
+ effect,
137
+ priority
138
+ });
139
+ rebuildEffectPass();
140
+ return () => composerContext.removeEffect(effect);
141
+ },
142
+ removeEffect(effect) {
143
+ const index = effects.findIndex(e => e.effect === effect);
144
+ if (index !== -1) {
145
+ effects.splice(index, 1);
146
+ rebuildEffectPass();
147
+ }
148
+ },
149
+ composer: composerRef
150
+ };
151
+ vue.provide(COMPOSER_CONTEXT, composerContext);
152
+
153
+ // Watch for camera changes — update render pass and effect pass
154
+ vue.watch(() => state.value.camera, camera => {
155
+ if (currentRenderPass) {
156
+ currentRenderPass.mainCamera = camera;
157
+ }
158
+ if (currentEffectPass) {
159
+ currentEffectPass.mainCamera = camera;
160
+ }
161
+ });
162
+
163
+ // Watch for size changes — update composer size
164
+ vue.watch(() => state.value.size, size => {
165
+ const composer = composerRef.value;
166
+ if (composer) {
167
+ composer.setSize(size.width, size.height, false);
168
+ }
169
+ });
170
+
171
+ // Watch for multisampling changes — recreate composer
172
+ vue.watch(() => props.multisampling, () => {
173
+ if (createComposer()) rebuildEffectPass();
174
+ });
175
+
176
+ // Use useFrame with priority 1 to take over rendering
177
+ vueThreejs.useFrame((_frameState, delta) => {
178
+ if (!props.enabled || !composerRef.value) return;
179
+ composerRef.value.render(delta);
180
+ }, 1);
181
+
182
+ // Cleanup on unmount
183
+ vue.onBeforeUnmount(() => {
184
+ const {
185
+ gl
186
+ } = state.value;
187
+
188
+ // Restore original autoClear
189
+ if (originalAutoClear !== null) {
190
+ gl.autoClear = originalAutoClear;
191
+ }
192
+ disposeComposer();
193
+ effects.length = 0;
194
+ });
195
+ return () => slots.default == null ? void 0 : slots.default();
196
+ }
197
+ });
198
+
199
+ const Bloom = vue.defineComponent({
200
+ name: 'Bloom',
201
+ props: {
202
+ intensity: {
203
+ type: Number,
204
+ default: 1
205
+ },
206
+ luminanceThreshold: {
207
+ type: Number,
208
+ default: 0.9
209
+ },
210
+ luminanceSmoothing: {
211
+ type: Number,
212
+ default: 0.025
213
+ },
214
+ mipmapBlur: {
215
+ type: Boolean,
216
+ default: true
217
+ }
218
+ },
219
+ setup(props) {
220
+ const composerCtx = vue.inject(COMPOSER_CONTEXT);
221
+ if (!composerCtx) {
222
+ throw new Error('Bloom must be a child of EffectComposer');
223
+ }
224
+ const effect = new postprocessing.BloomEffect({
225
+ intensity: props.intensity,
226
+ luminanceThreshold: props.luminanceThreshold,
227
+ luminanceSmoothing: props.luminanceSmoothing,
228
+ mipmapBlur: props.mipmapBlur
229
+ });
230
+ const removeEffect = composerCtx.addEffect(effect, 0);
231
+ vue.watch(() => props.intensity, value => {
232
+ effect.intensity = value;
233
+ });
234
+ vue.watch(() => props.luminanceThreshold, value => {
235
+ effect.luminanceMaterial.threshold = value;
236
+ });
237
+ vue.watch(() => props.luminanceSmoothing, value => {
238
+ effect.luminanceMaterial.smoothing = value;
239
+ });
240
+ vue.watch(() => props.mipmapBlur, value => {
241
+ effect.mipmapBlurPass.enabled = value;
242
+ });
243
+ vue.onBeforeUnmount(() => {
244
+ removeEffect();
245
+ effect.dispose();
246
+ });
247
+ return () => null;
248
+ }
249
+ });
250
+
251
+ const BrightnessContrast = vue.defineComponent({
252
+ name: 'BrightnessContrast',
253
+ props: {
254
+ brightness: {
255
+ type: Number,
256
+ default: 0
257
+ },
258
+ contrast: {
259
+ type: Number,
260
+ default: 0
261
+ }
262
+ },
263
+ setup(props) {
264
+ const composerCtx = vue.inject(COMPOSER_CONTEXT);
265
+ if (!composerCtx) {
266
+ throw new Error('BrightnessContrast must be a child of EffectComposer');
267
+ }
268
+ const effect = new postprocessing.BrightnessContrastEffect({
269
+ brightness: props.brightness,
270
+ contrast: props.contrast
271
+ });
272
+ const removeEffect = composerCtx.addEffect(effect, 0);
273
+ vue.watch(() => props.brightness, value => {
274
+ effect.brightness = value;
275
+ });
276
+ vue.watch(() => props.contrast, value => {
277
+ effect.contrast = value;
278
+ });
279
+ vue.onBeforeUnmount(() => {
280
+ removeEffect();
281
+ effect.dispose();
282
+ });
283
+ return () => null;
284
+ }
285
+ });
286
+
287
+ const HueSaturation = vue.defineComponent({
288
+ name: 'HueSaturation',
289
+ props: {
290
+ hue: {
291
+ type: Number,
292
+ default: 0
293
+ },
294
+ saturation: {
295
+ type: Number,
296
+ default: 0
297
+ }
298
+ },
299
+ setup(props) {
300
+ const composerCtx = vue.inject(COMPOSER_CONTEXT);
301
+ if (!composerCtx) {
302
+ throw new Error('HueSaturation must be a child of EffectComposer');
303
+ }
304
+ const effect = new postprocessing.HueSaturationEffect({
305
+ hue: props.hue,
306
+ saturation: props.saturation
307
+ });
308
+ const removeEffect = composerCtx.addEffect(effect, 0);
309
+ vue.watch(() => props.hue, value => {
310
+ effect.hue = value;
311
+ });
312
+ vue.watch(() => props.saturation, value => {
313
+ effect.saturation = value;
314
+ });
315
+ vue.onBeforeUnmount(() => {
316
+ removeEffect();
317
+ effect.dispose();
318
+ });
319
+ return () => null;
320
+ }
321
+ });
322
+
323
+ const LUT = vue.defineComponent({
324
+ name: 'LUT',
325
+ props: {
326
+ lut: {
327
+ type: Object,
328
+ required: true
329
+ },
330
+ tetrahedralInterpolation: {
331
+ type: Boolean,
332
+ default: false
333
+ }
334
+ },
335
+ setup(props) {
336
+ const composerCtx = vue.inject(COMPOSER_CONTEXT);
337
+ if (!composerCtx) {
338
+ throw new Error('LUT must be a child of EffectComposer');
339
+ }
340
+ const effect = new postprocessing.LUT3DEffect(props.lut, {
341
+ tetrahedralInterpolation: props.tetrahedralInterpolation
342
+ });
343
+ const removeEffect = composerCtx.addEffect(effect, 0);
344
+ vue.watch(() => props.lut, value => {
345
+ effect.lut = value;
346
+ });
347
+ vue.watch(() => props.tetrahedralInterpolation, value => {
348
+ effect.tetrahedralInterpolation = value;
349
+ });
350
+ vue.onBeforeUnmount(() => {
351
+ removeEffect();
352
+ effect.dispose();
353
+ });
354
+ return () => null;
355
+ }
356
+ });
357
+
358
+ const ToneMapping = vue.defineComponent({
359
+ name: 'ToneMapping',
360
+ props: {
361
+ mode: {
362
+ type: Number,
363
+ default: postprocessing.ToneMappingMode.AGX
364
+ },
365
+ resolution: {
366
+ type: Number,
367
+ default: 256
368
+ },
369
+ whitePoint: {
370
+ type: Number,
371
+ default: 4
372
+ },
373
+ middleGrey: {
374
+ type: Number,
375
+ default: 0.6
376
+ }
377
+ },
378
+ setup(props) {
379
+ const composerCtx = vue.inject(COMPOSER_CONTEXT);
380
+ if (!composerCtx) {
381
+ throw new Error('ToneMapping must be a child of EffectComposer');
382
+ }
383
+ const effect = new postprocessing.ToneMappingEffect({
384
+ mode: props.mode,
385
+ resolution: props.resolution,
386
+ whitePoint: props.whitePoint,
387
+ middleGrey: props.middleGrey
388
+ });
389
+ const removeEffect = composerCtx.addEffect(effect, 0);
390
+ vue.watch(() => props.mode, value => {
391
+ effect.mode = value;
392
+ });
393
+ vue.watch(() => props.resolution, value => {
394
+ effect.resolution.width = value;
395
+ effect.resolution.height = value;
396
+ });
397
+ vue.watch(() => props.whitePoint, value => {
398
+ effect.whitePoint = value;
399
+ });
400
+ vue.watch(() => props.middleGrey, value => {
401
+ effect.middleGrey = value;
402
+ });
403
+ vue.onBeforeUnmount(() => {
404
+ removeEffect();
405
+ effect.dispose();
406
+ });
407
+ return () => null;
408
+ }
409
+ });
410
+
411
+ const DepthOfField = vue.defineComponent({
412
+ name: 'DepthOfField',
413
+ props: {
414
+ focusDistance: {
415
+ type: Number,
416
+ default: 0
417
+ },
418
+ focalLength: {
419
+ type: Number,
420
+ default: 0.1
421
+ },
422
+ bokehScale: {
423
+ type: Number,
424
+ default: 2
425
+ }
426
+ },
427
+ setup(props) {
428
+ const composerCtx = vue.inject(COMPOSER_CONTEXT);
429
+ if (!composerCtx) {
430
+ throw new Error('DepthOfField must be a child of EffectComposer');
431
+ }
432
+ const state = vueThreejs.useThree();
433
+ const {
434
+ camera
435
+ } = state.value;
436
+ const effect = new postprocessing.DepthOfFieldEffect(camera, {
437
+ focusDistance: props.focusDistance,
438
+ focalLength: props.focalLength,
439
+ bokehScale: props.bokehScale
440
+ });
441
+ const removeEffect = composerCtx.addEffect(effect, 0);
442
+ vue.watch(() => props.focusDistance, value => {
443
+ effect.cocMaterial.focusDistance = value;
444
+ });
445
+ vue.watch(() => props.focalLength, value => {
446
+ effect.cocMaterial.focalLength = value;
447
+ });
448
+ vue.watch(() => props.bokehScale, value => {
449
+ effect.bokehScale = value;
450
+ });
451
+ vue.onBeforeUnmount(() => {
452
+ removeEffect();
453
+ effect.dispose();
454
+ });
455
+ return () => null;
456
+ }
457
+ });
458
+
459
+ const Noise = vue.defineComponent({
460
+ name: 'Noise',
461
+ props: {
462
+ premultiply: {
463
+ type: Boolean,
464
+ default: false
465
+ },
466
+ blendFunction: {
467
+ type: Number,
468
+ default: postprocessing.BlendFunction.SCREEN
469
+ }
470
+ },
471
+ setup(props) {
472
+ const composerCtx = vue.inject(COMPOSER_CONTEXT);
473
+ if (!composerCtx) {
474
+ throw new Error('Noise must be a child of EffectComposer');
475
+ }
476
+ const effect = new postprocessing.NoiseEffect({
477
+ premultiply: props.premultiply,
478
+ blendFunction: props.blendFunction
479
+ });
480
+ const removeEffect = composerCtx.addEffect(effect, 0);
481
+ vue.watch(() => props.premultiply, value => {
482
+ effect.premultiply = value;
483
+ });
484
+ vue.watch(() => props.blendFunction, value => {
485
+ effect.blendMode.blendFunction = value;
486
+ });
487
+ vue.onBeforeUnmount(() => {
488
+ removeEffect();
489
+ effect.dispose();
490
+ });
491
+ return () => null;
492
+ }
493
+ });
494
+
495
+ const Vignette = vue.defineComponent({
496
+ name: 'Vignette',
497
+ props: {
498
+ offset: {
499
+ type: Number,
500
+ default: 0.5
501
+ },
502
+ darkness: {
503
+ type: Number,
504
+ default: 0.5
505
+ }
506
+ },
507
+ setup(props) {
508
+ const composerCtx = vue.inject(COMPOSER_CONTEXT);
509
+ if (!composerCtx) {
510
+ throw new Error('Vignette must be a child of EffectComposer');
511
+ }
512
+ const effect = new postprocessing.VignetteEffect({
513
+ offset: props.offset,
514
+ darkness: props.darkness
515
+ });
516
+ const removeEffect = composerCtx.addEffect(effect, 0);
517
+ vue.watch(() => props.offset, value => {
518
+ effect.offset = value;
519
+ });
520
+ vue.watch(() => props.darkness, value => {
521
+ effect.darkness = value;
522
+ });
523
+ vue.onBeforeUnmount(() => {
524
+ removeEffect();
525
+ effect.dispose();
526
+ });
527
+ return () => null;
528
+ }
529
+ });
530
+
531
+ const postprocessingFiberPlugin = vueThreejs.defineFiberPlugin({
532
+ name: '@xperimntl/vue-threejs-postprocessing',
533
+ setup(ctx, options) {
534
+ if (options) {
535
+ ctx.provide(POSTPROCESSING_DEFAULTS, options);
536
+ }
537
+ }
538
+ });
539
+ function createPostprocessingPlugin(options) {
540
+ return vueThreejs.withPluginOptions(postprocessingFiberPlugin, options);
541
+ }
542
+
543
+ exports.Bloom = Bloom;
544
+ exports.BrightnessContrast = BrightnessContrast;
545
+ exports.COMPOSER_CONTEXT = COMPOSER_CONTEXT;
546
+ exports.DepthOfField = DepthOfField;
547
+ exports.EffectComposer = EffectComposer;
548
+ exports.HueSaturation = HueSaturation;
549
+ exports.LUT = LUT;
550
+ exports.Noise = Noise;
551
+ exports.POSTPROCESSING_DEFAULTS = POSTPROCESSING_DEFAULTS;
552
+ exports.ToneMapping = ToneMapping;
553
+ exports.Vignette = Vignette;
554
+ exports.createPostprocessingPlugin = createPostprocessingPlugin;
555
+ exports.postprocessingFiberPlugin = postprocessingFiberPlugin;
@@ -1,16 +1,7 @@
1
- "use strict";
2
- // this file might look strange and you might be wondering what it's for
3
- // it's lets you import your source files by importing this entrypoint
4
- // as you would import it if it was built with preconstruct build
5
- // this file is slightly different to some others though
6
- // it has a require hook which compiles your code with Babel
7
- // this means that you don't have to set up @babel/register or anything like that
8
- // but you can still require this module and it'll be compiled
1
+ 'use strict';
9
2
 
10
- // this bit of code imports the require hook and registers it
11
- let unregister = require("../../../node_modules/@preconstruct/hook").___internalHook(typeof __dirname === 'undefined' ? undefined : __dirname, "../../..", "..");
12
-
13
- // this re-exports the source file
14
- module.exports = require("../src/index.ts");
15
-
16
- unregister();
3
+ if (process.env.NODE_ENV === "production") {
4
+ module.exports = require("./xperimntl-vue-threejs-postprocessing.cjs.prod.js");
5
+ } else {
6
+ module.exports = require("./xperimntl-vue-threejs-postprocessing.cjs.dev.js");
7
+ }