angular-three-soba 2.8.1 → 2.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.
@@ -1,13 +1,16 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, computed, effect, Component, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, viewChild, untracked, inject, DestroyRef } from '@angular/core';
3
- import { omit, is, pick, NgtArgs, injectBeforeRender, injectStore, applyProps, getLocalState } from 'angular-three';
2
+ import { input, computed, effect, Component, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, inject, Injector, afterNextRender, Directive, viewChild, contentChild, TemplateRef, signal, untracked, DestroyRef } from '@angular/core';
3
+ import { omit, is, pick, NgtArgs, injectBeforeRender, injectStore, getLocalState, extend, applyProps } from 'angular-three';
4
4
  import CustomShaderMaterial from 'three-custom-shader-material/vanilla';
5
- import { MeshDistortMaterial, BlurPass, MeshReflectorMaterial, MeshDiscardMaterial, MeshTransmissionMaterial, MeshWobbleMaterial } from 'angular-three-soba/vanilla-exports';
5
+ import { MeshDistortMaterial, MeshPortalMaterial, meshPortalMaterialApplySDF, BlurPass, MeshReflectorMaterial, MeshDiscardMaterial, MeshTransmissionMaterial, MeshWobbleMaterial } from 'angular-three-soba/vanilla-exports';
6
6
  import { mergeInputs } from 'ngxtension/inject-inputs';
7
- import { Plane, Vector3, Matrix4, Vector4, PerspectiveCamera, LinearFilter, HalfFloatType, WebGLRenderTarget, DepthTexture, DepthFormat, UnsignedShortType, FrontSide, NoToneMapping, BackSide } from 'three';
7
+ import { NgTemplateOutlet } from '@angular/common';
8
+ import { injectFBO, getVersion, injectIntersect } from 'angular-three-soba/misc';
9
+ import { NgtsRenderTexture, NgtsRenderTextureContent } from 'angular-three-soba/staging';
10
+ import { ShaderMaterial, Mesh, Plane, Vector3, Matrix4, Vector4, PerspectiveCamera, LinearFilter, HalfFloatType, WebGLRenderTarget, DepthTexture, DepthFormat, UnsignedShortType, FrontSide, NoToneMapping, BackSide } from 'three';
11
+ import { FullScreenQuad } from 'three-stdlib';
8
12
  import { MeshRefractionMaterial, PointMaterial } from 'angular-three-soba/shaders';
9
13
  import { MeshBVHUniformStruct, MeshBVH, SAH } from 'three-mesh-bvh';
10
- import { injectFBO } from 'angular-three-soba/misc';
11
14
 
12
15
  class NgtsCustomShaderMaterial {
13
16
  constructor() {
@@ -72,13 +75,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.11", ngImpo
72
75
  }]
73
76
  }], ctorParameters: () => [] });
74
77
 
75
- const defaultOptions$4 = {
78
+ const defaultOptions$5 = {
76
79
  speed: 1,
77
80
  };
78
81
  class NgtsMeshDistortMaterial {
79
82
  constructor() {
80
83
  this.attach = input('material');
81
- this.options = input(defaultOptions$4, { transform: mergeInputs(defaultOptions$4) });
84
+ this.options = input(defaultOptions$5, { transform: mergeInputs(defaultOptions$5) });
82
85
  this.parameters = omit(this.options, ['speed']);
83
86
  this.material = new MeshDistortMaterial();
84
87
  this.speed = pick(this.options, 'speed');
@@ -109,6 +112,290 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.11", ngImpo
109
112
  }]
110
113
  }], ctorParameters: () => [] });
111
114
 
115
+ /**
116
+ * This directive is used inside of the render texture, hence has access to the render texture store (a portal store)
117
+ */
118
+ class ManagePortalScene {
119
+ constructor() {
120
+ this.events = input();
121
+ this.rootScene = input.required();
122
+ this.material = input.required();
123
+ this.priority = input.required();
124
+ this.worldUnits = input.required();
125
+ const injector = inject(Injector);
126
+ const renderTextureStore = injectStore();
127
+ const portalScene = renderTextureStore.select('scene');
128
+ const portalSetEvents = renderTextureStore.select('setEvents');
129
+ const buffer1 = injectFBO();
130
+ const buffer2 = injectFBO();
131
+ const fullScreenQuad = computed(() => {
132
+ // This fullscreen-quad is used to blend the two textures
133
+ const blend = { value: 0 };
134
+ const quad = new FullScreenQuad(new ShaderMaterial({
135
+ uniforms: {
136
+ a: { value: buffer1().texture },
137
+ b: { value: buffer2().texture },
138
+ blend,
139
+ },
140
+ vertexShader: /*glsl*/ `
141
+ varying vec2 vUv;
142
+ void main() {
143
+ vUv = uv;
144
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
145
+ }`,
146
+ fragmentShader: /*glsl*/ `
147
+ uniform sampler2D a;
148
+ uniform sampler2D b;
149
+ uniform float blend;
150
+ varying vec2 vUv;
151
+ #include <packing>
152
+ void main() {
153
+ vec4 ta = texture2D(a, vUv);
154
+ vec4 tb = texture2D(b, vUv);
155
+ gl_FragColor = mix(tb, ta, blend);
156
+ #include <tonemapping_fragment>
157
+ #include <${getVersion() >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}>
158
+ }`,
159
+ }));
160
+ return [quad, blend];
161
+ });
162
+ effect(() => {
163
+ const [events, setEvents] = [this.events(), portalSetEvents()];
164
+ if (!events)
165
+ return;
166
+ setEvents({ enabled: events });
167
+ });
168
+ afterNextRender(() => {
169
+ portalScene().matrixAutoUpdate = false;
170
+ // we start the before render in afterNextRender because we need the priority input to be resolved
171
+ injectBeforeRender(({ gl, camera }) => {
172
+ const material = this.material();
173
+ const localState = getLocalState(material);
174
+ if (!localState)
175
+ return;
176
+ const parent = localState.parent();
177
+ if (!parent)
178
+ return;
179
+ const materialBlend = 'blend' in material && typeof material.blend === 'number' ? material.blend : 0;
180
+ const [worldUnits, priority, rootScene, scene, [quad, blend]] = [
181
+ this.worldUnits(),
182
+ this.priority(),
183
+ this.rootScene(),
184
+ portalScene(),
185
+ fullScreenQuad(),
186
+ ];
187
+ // Move portal contents along with the parent if worldUnits is true
188
+ if (!worldUnits) {
189
+ // If the portal renders exclusively the original scene needs to be updated
190
+ if (priority && materialBlend === 1)
191
+ parent.updateWorldMatrix(true, false);
192
+ scene.matrixWorld.copy(parent.matrixWorld);
193
+ }
194
+ else {
195
+ scene.matrixWorld.identity();
196
+ }
197
+ // This bit is only necessary if the portal is blended, now it has a render-priority
198
+ // and will take over the render loop
199
+ if (priority) {
200
+ if (materialBlend > 0 && materialBlend < 1) {
201
+ // If blend is ongoing (> 0 and < 1) then we need to render both the root scene
202
+ // and the portal scene, both will then be mixed in the quad from above
203
+ blend.value = materialBlend;
204
+ gl.setRenderTarget(buffer1());
205
+ gl.render(scene, camera);
206
+ gl.setRenderTarget(buffer2());
207
+ gl.render(rootScene, camera);
208
+ gl.setRenderTarget(null);
209
+ quad.render(gl);
210
+ }
211
+ else if (materialBlend === 1) {
212
+ // However if blend is 1 we only need to render the portal scene
213
+ gl.render(scene, camera);
214
+ }
215
+ }
216
+ }, { injector, priority: this.priority() });
217
+ });
218
+ }
219
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.11", ngImport: i0, type: ManagePortalScene, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
220
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.11", type: ManagePortalScene, isStandalone: true, selector: "ngts-manage-portal-scene", inputs: { events: { classPropertyName: "events", publicName: "events", isSignal: true, isRequired: false, transformFunction: null }, rootScene: { classPropertyName: "rootScene", publicName: "rootScene", isSignal: true, isRequired: true, transformFunction: null }, material: { classPropertyName: "material", publicName: "material", isSignal: true, isRequired: true, transformFunction: null }, priority: { classPropertyName: "priority", publicName: "priority", isSignal: true, isRequired: true, transformFunction: null }, worldUnits: { classPropertyName: "worldUnits", publicName: "worldUnits", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 }); }
221
+ }
222
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.11", ngImport: i0, type: ManagePortalScene, decorators: [{
223
+ type: Directive,
224
+ args: [{ selector: 'ngts-manage-portal-scene', standalone: true }]
225
+ }], ctorParameters: () => [] });
226
+ const defaultOptions$4 = {
227
+ blend: 0.5,
228
+ blur: 0,
229
+ resolution: 512,
230
+ worldUnits: false,
231
+ eventPriority: 0,
232
+ renderPriority: 0,
233
+ events: false,
234
+ };
235
+ class NgtsMeshPortalMaterial {
236
+ constructor() {
237
+ this.attach = input('material');
238
+ this.options = input(defaultOptions$4, { transform: mergeInputs(defaultOptions$4) });
239
+ this.parameters = omit(this.options, ['blur', 'resolution', 'worldUnits', 'eventPriority', 'renderPriority', 'events']);
240
+ this.blur = pick(this.options, 'blur');
241
+ this.eventPriority = pick(this.options, 'eventPriority');
242
+ this.renderPriority = pick(this.options, 'renderPriority');
243
+ this.events = pick(this.options, 'events');
244
+ this.worldUnits = pick(this.options, 'worldUnits');
245
+ this.materialRef = viewChild.required('material');
246
+ this.content = contentChild.required(TemplateRef);
247
+ this.store = injectStore();
248
+ this.size = this.store.select('size');
249
+ this.viewport = this.store.select('viewport');
250
+ this.gl = this.store.select('gl');
251
+ this.setEvents = this.store.select('setEvents');
252
+ this.rootScene = this.store.select('scene');
253
+ this.materialResolution = computed(() => [
254
+ this.size().width * this.viewport().dpr,
255
+ this.size().height * this.viewport().dpr,
256
+ ]);
257
+ this.resolution = pick(this.options, 'resolution');
258
+ this.parent = signal(null);
259
+ this.visible = injectIntersect(this.parent, { source: signal(true) });
260
+ this.renderTextureFrames = computed(() => (this.visible() ? Infinity : 0));
261
+ this.renderTextureCompute = computed(() => {
262
+ const [parent, material] = [this.parent(), this.materialRef().nativeElement];
263
+ const computeFn = (event, state) => {
264
+ if (!parent)
265
+ return false;
266
+ state.snapshot.pointer.set((event.offsetX / state.snapshot.size.width) * 2 - 1, -(event.offsetY / state.snapshot.size.height) * 2 + 1);
267
+ state.snapshot.raycaster.setFromCamera(state.snapshot.pointer, state.snapshot.camera);
268
+ if ('blend' in material && material.blend === 0) {
269
+ // We run a quick check against the parent, if it isn't hit there's no need to raycast at all
270
+ const [intersection] = state.snapshot.raycaster.intersectObject(parent);
271
+ if (!intersection) {
272
+ // Cancel out the raycast camera if the parent mesh isn't hit
273
+ Object.assign(state.snapshot.raycaster, { camera: undefined });
274
+ return false;
275
+ }
276
+ }
277
+ return;
278
+ };
279
+ return computeFn;
280
+ });
281
+ this.priority = signal(0);
282
+ extend({ MeshPortalMaterial });
283
+ afterNextRender(() => {
284
+ const material = this.materialRef().nativeElement;
285
+ const localState = getLocalState(material);
286
+ if (!localState)
287
+ return;
288
+ const materialParent = localState.parent();
289
+ if (!materialParent || !(materialParent instanceof Mesh))
290
+ return;
291
+ // Since the ref above is not tied to a mesh directly (we're inside a material),
292
+ // it has to be tied to the parent mesh here
293
+ this.parent.set(materialParent);
294
+ });
295
+ effect(() => {
296
+ const events = this.events();
297
+ if (!events)
298
+ return;
299
+ const setEvents = this.setEvents();
300
+ setEvents({ enabled: !events });
301
+ });
302
+ // React.useEffect(() => {
303
+ // if (events !== undefined) setEvents({ enabled: !events })
304
+ // }, [events])
305
+ effect(() => {
306
+ const [material, parent] = [this.materialRef().nativeElement, this.parent()];
307
+ if (!parent)
308
+ return;
309
+ const [resolution, blur, gl] = [this.resolution(), this.blur(), this.gl()];
310
+ // apply the SDF mask once
311
+ if (blur && material.sdf == null) {
312
+ meshPortalMaterialApplySDF(parent, resolution, gl);
313
+ }
314
+ });
315
+ injectBeforeRender(() => {
316
+ const material = this.materialRef().nativeElement;
317
+ const priority = 'blend' in material && typeof material.blend === 'number' && material.blend > 0
318
+ ? Math.max(1, this.renderPriority())
319
+ : 0;
320
+ // If blend is > 0 then the portal is being entered, the render-priority must change
321
+ if (this.priority() !== priority) {
322
+ this.priority.set(priority);
323
+ }
324
+ });
325
+ }
326
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.11", ngImport: i0, type: NgtsMeshPortalMaterial, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
327
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "18.2.11", type: NgtsMeshPortalMaterial, isStandalone: true, selector: "ngts-mesh-portal-material", inputs: { attach: { classPropertyName: "attach", publicName: "attach", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "content", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "materialRef", first: true, predicate: ["material"], descendants: true, isSignal: true }], ngImport: i0, template: `
328
+ <ngt-mesh-portal-material
329
+ #material
330
+ [attach]="attach()"
331
+ [blur]="blur()"
332
+ [blend]="0"
333
+ [resolution]="materialResolution()"
334
+ [parameters]="parameters()"
335
+ >
336
+ <ngts-render-texture
337
+ [options]="{
338
+ frames: renderTextureFrames(),
339
+ eventPriority: eventPriority(),
340
+ renderPriority: renderPriority(),
341
+ compute: renderTextureCompute(),
342
+ }"
343
+ >
344
+ <ng-template renderTextureContent let-injector="injector">
345
+ <ng-container *ngTemplateOutlet="content(); injector: injector" />
346
+ <ngts-manage-portal-scene
347
+ [events]="events()"
348
+ [rootScene]="rootScene()"
349
+ [priority]="priority()"
350
+ [material]="material"
351
+ [worldUnits]="worldUnits()"
352
+ />
353
+ </ng-template>
354
+ </ngts-render-texture>
355
+ </ngt-mesh-portal-material>
356
+ `, isInline: true, dependencies: [{ kind: "component", type: NgtsRenderTexture, selector: "ngts-render-texture", inputs: ["attach", "options"] }, { kind: "directive", type: NgtsRenderTextureContent, selector: "ng-template[renderTextureContent]" }, { kind: "directive", type: ManagePortalScene, selector: "ngts-manage-portal-scene", inputs: ["events", "rootScene", "material", "priority", "worldUnits"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
357
+ }
358
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.11", ngImport: i0, type: NgtsMeshPortalMaterial, decorators: [{
359
+ type: Component,
360
+ args: [{
361
+ selector: 'ngts-mesh-portal-material',
362
+ standalone: true,
363
+ template: `
364
+ <ngt-mesh-portal-material
365
+ #material
366
+ [attach]="attach()"
367
+ [blur]="blur()"
368
+ [blend]="0"
369
+ [resolution]="materialResolution()"
370
+ [parameters]="parameters()"
371
+ >
372
+ <ngts-render-texture
373
+ [options]="{
374
+ frames: renderTextureFrames(),
375
+ eventPriority: eventPriority(),
376
+ renderPriority: renderPriority(),
377
+ compute: renderTextureCompute(),
378
+ }"
379
+ >
380
+ <ng-template renderTextureContent let-injector="injector">
381
+ <ng-container *ngTemplateOutlet="content(); injector: injector" />
382
+ <ngts-manage-portal-scene
383
+ [events]="events()"
384
+ [rootScene]="rootScene()"
385
+ [priority]="priority()"
386
+ [material]="material"
387
+ [worldUnits]="worldUnits()"
388
+ />
389
+ </ng-template>
390
+ </ngts-render-texture>
391
+ </ngt-mesh-portal-material>
392
+ `,
393
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
394
+ changeDetection: ChangeDetectionStrategy.OnPush,
395
+ imports: [NgtsRenderTexture, NgtsRenderTextureContent, ManagePortalScene, NgTemplateOutlet],
396
+ }]
397
+ }], ctorParameters: () => [] });
398
+
112
399
  const defaultOptions$3 = {
113
400
  mixBlur: 0,
114
401
  mixStrength: 1,
@@ -762,5 +1049,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.11", ngImpo
762
1049
  * Generated bundle index. Do not edit.
763
1050
  */
764
1051
 
765
- export { NgtsCustomShaderMaterial, NgtsMeshDistortMaterial, NgtsMeshReflectorMaterial, NgtsMeshRefractionMaterial, NgtsMeshTransmissionMaterial, NgtsMeshWobbleMaterial, NgtsPointMaterial };
1052
+ export { ManagePortalScene, NgtsCustomShaderMaterial, NgtsMeshDistortMaterial, NgtsMeshPortalMaterial, NgtsMeshReflectorMaterial, NgtsMeshRefractionMaterial, NgtsMeshTransmissionMaterial, NgtsMeshWobbleMaterial, NgtsPointMaterial };
766
1053
  //# sourceMappingURL=angular-three-soba-materials.mjs.map