@eva/plugin-renderer-scene-capture 2.1.0-beta.14

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,550 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var eva_js = require('@eva/eva.js');
6
+ var inspectorDecorator = require('@eva/inspector-decorator');
7
+ var pluginRenderer = require('@eva/plugin-renderer');
8
+ var pixi_js = require('pixi.js');
9
+
10
+ /******************************************************************************
11
+ Copyright (c) Microsoft Corporation.
12
+
13
+ Permission to use, copy, modify, and/or distribute this software for any
14
+ purpose with or without fee is hereby granted.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
17
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
19
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
20
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22
+ PERFORMANCE OF THIS SOFTWARE.
23
+ ***************************************************************************** */
24
+
25
+ function __decorate(decorators, target, key, desc) {
26
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
27
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
28
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
29
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
30
+ }
31
+
32
+ function __metadata(metadataKey, metadataValue) {
33
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
34
+ }
35
+
36
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
37
+ var e = new Error(message);
38
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
39
+ };
40
+
41
+ const finitePositive$1 = (value, fallback) => {
42
+ const numberValue = Number(value);
43
+ return Number.isFinite(numberValue) && numberValue > 0 ? numberValue : fallback;
44
+ };
45
+ const clamp = (value, min, max, fallback) => {
46
+ const numberValue = Number(value);
47
+ if (!Number.isFinite(numberValue))
48
+ return fallback;
49
+ return Math.min(max, Math.max(min, numberValue));
50
+ };
51
+ /**
52
+ * Captures named live Eva renderer containers into an offscreen Pixi RenderTexture.
53
+ *
54
+ * Requires SceneCaptureSystem plus RendererSystem from @eva/plugin-renderer. Source
55
+ * names are resolved at capture time, so late-created reflection proxy entities work
56
+ * without rebinding. The component only stores serializable configuration; the Pixi
57
+ * target and presentation lifecycle are owned by SceneCaptureSystem.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * reflectionRoot.addComponent(new SceneCapture({
62
+ * sourceNames: ['building-reflection', 'actor-reflection'],
63
+ * output: 'rain-reflection',
64
+ * width: 750,
65
+ * height: 420,
66
+ * resolutionScale: 0.5,
67
+ * updateMode: 'always',
68
+ * clearAlpha: 0,
69
+ * }));
70
+ * ```
71
+ */
72
+ class SceneCapture extends eva_js.Component {
73
+ constructor() {
74
+ super(...arguments);
75
+ this.sourceNames = [];
76
+ this.output = '';
77
+ this.width = 256;
78
+ this.height = 256;
79
+ this.resolutionScale = 1;
80
+ this.updateMode = 'always';
81
+ this.clearColor = 0x000000;
82
+ this.clearAlpha = 0;
83
+ this.enabled = true;
84
+ /** Monotonic runtime invalidation sequence consumed by SceneCaptureSystem. */
85
+ this.captureRevision = 0;
86
+ }
87
+ init(params) {
88
+ if (params)
89
+ Object.assign(this, params);
90
+ this.sourceNames = Array.isArray(this.sourceNames)
91
+ ? this.sourceNames.filter(name => typeof name === 'string' && name.trim().length > 0)
92
+ : [];
93
+ this.output = typeof this.output === 'string' ? this.output.trim() : '';
94
+ this.width = finitePositive$1(this.width, 256);
95
+ this.height = finitePositive$1(this.height, 256);
96
+ this.resolutionScale = finitePositive$1(this.resolutionScale, 1);
97
+ this.updateMode = this.normalizeUpdateMode(this.updateMode);
98
+ this.clearColor = Math.max(0, Math.min(0xffffff, Number(this.clearColor) || 0)) >>> 0;
99
+ this.clearAlpha = clamp(this.clearAlpha, 0, 1, 0);
100
+ this.enabled = this.enabled !== false;
101
+ }
102
+ /** Mark this target dirty; manual targets capture on the next presentation frame. */
103
+ requestCapture() {
104
+ this.captureRevision++;
105
+ this.emit('captureRequested', { revision: this.captureRevision });
106
+ }
107
+ normalizeUpdateMode(value) {
108
+ return value === 'onDirty' || value === 'manual' ? value : 'always';
109
+ }
110
+ }
111
+ SceneCapture.componentName = 'SceneCapture';
112
+ __decorate([
113
+ inspectorDecorator.type('array'),
114
+ __metadata("design:type", Array)
115
+ ], SceneCapture.prototype, "sourceNames", void 0);
116
+ __decorate([
117
+ inspectorDecorator.type('string'),
118
+ __metadata("design:type", String)
119
+ ], SceneCapture.prototype, "output", void 0);
120
+ __decorate([
121
+ inspectorDecorator.type('number'),
122
+ __metadata("design:type", Number)
123
+ ], SceneCapture.prototype, "width", void 0);
124
+ __decorate([
125
+ inspectorDecorator.type('number'),
126
+ __metadata("design:type", Number)
127
+ ], SceneCapture.prototype, "height", void 0);
128
+ __decorate([
129
+ inspectorDecorator.type('number'),
130
+ __metadata("design:type", Number)
131
+ ], SceneCapture.prototype, "resolutionScale", void 0);
132
+ __decorate([
133
+ inspectorDecorator.type('string'),
134
+ __metadata("design:type", String)
135
+ ], SceneCapture.prototype, "updateMode", void 0);
136
+ __decorate([
137
+ inspectorDecorator.type('number'),
138
+ __metadata("design:type", Number)
139
+ ], SceneCapture.prototype, "clearColor", void 0);
140
+ __decorate([
141
+ inspectorDecorator.type('number'),
142
+ __metadata("design:type", Number)
143
+ ], SceneCapture.prototype, "clearAlpha", void 0);
144
+ __decorate([
145
+ inspectorDecorator.type('boolean'),
146
+ __metadata("design:type", Boolean)
147
+ ], SceneCapture.prototype, "enabled", void 0);
148
+ __decorate([
149
+ inspectorDecorator.type('number'),
150
+ __metadata("design:type", Number)
151
+ ], SceneCapture.prototype, "captureRevision", void 0);
152
+
153
+ /**
154
+ * Public, owner-aware lookup table for live SceneCapture outputs.
155
+ *
156
+ * A later capture may intentionally replace the same key. Disposal is guarded by
157
+ * owner identity so removing an older component cannot erase the newer texture.
158
+ */
159
+ class SceneCaptureTextureRegistry {
160
+ constructor() {
161
+ this.entries = new Map();
162
+ }
163
+ get(output) {
164
+ var _a;
165
+ return (_a = this.entries.get(this.normalize(output))) === null || _a === void 0 ? void 0 : _a.texture;
166
+ }
167
+ getEntry(output) {
168
+ return this.entries.get(this.normalize(output));
169
+ }
170
+ has(output) {
171
+ return this.entries.has(this.normalize(output));
172
+ }
173
+ set(output, owner, texture) {
174
+ const key = this.normalize(output);
175
+ if (!key || !owner || !texture)
176
+ return;
177
+ this.entries.set(key, Object.freeze({ output: key, owner, texture }));
178
+ }
179
+ delete(output, owner) {
180
+ const key = this.normalize(output);
181
+ const entry = this.entries.get(key);
182
+ if (!entry || (owner && entry.owner !== owner))
183
+ return false;
184
+ return this.entries.delete(key);
185
+ }
186
+ normalize(output) {
187
+ return typeof output === 'string' ? output.trim() : '';
188
+ }
189
+ }
190
+ /** Shared lookup table for shader/filter consumers that do not own the capture system. */
191
+ const sceneCaptureTextureRegistry = new SceneCaptureTextureRegistry();
192
+ /** Get a live Pixi Texture produced by a SceneCapture `output` key. */
193
+ function getSceneCaptureTexture(output) {
194
+ return sceneCaptureTextureRegistry.get(output);
195
+ }
196
+
197
+ let nextSystemId = 0;
198
+ const finitePositive = (value, fallback) => {
199
+ const numberValue = Number(value);
200
+ return Number.isFinite(numberValue) && numberValue > 0 ? numberValue : fallback;
201
+ };
202
+ const clampAlpha = (value) => {
203
+ const numberValue = Number(value);
204
+ if (!Number.isFinite(numberValue))
205
+ return 0;
206
+ return Math.max(0, Math.min(1, numberValue));
207
+ };
208
+ const normalizedOutput = (value) => (typeof value === 'string' ? value.trim() : '');
209
+ /**
210
+ * Renders named live Eva containers into transparent offscreen Pixi textures.
211
+ *
212
+ * The system registers a presentation participant scoped to the root RendererSystem
213
+ * application. This makes each capture finish before the application performs its
214
+ * final main-stage render. It never reparents source containers and explicitly
215
+ * rejects the main Pixi stage as a capture source.
216
+ */
217
+ let SceneCaptureSystem = class SceneCaptureSystem extends pluginRenderer.Renderer {
218
+ constructor() {
219
+ super(...arguments);
220
+ this.name = 'SceneCapture';
221
+ /** Let a newly-added target become available during a zero-simulation presentation frame. */
222
+ this.presentationAddFlushEnabled = true;
223
+ this.records = new Map();
224
+ this.outputs = new Map();
225
+ this.emptyCaptureRoot = new pixi_js.Container();
226
+ this.participantId = `scene-capture:${++nextSystemId}`;
227
+ this.destroyed = false;
228
+ }
229
+ init() {
230
+ this.renderSystem = this.game.getSystem(pluginRenderer.RendererSystem);
231
+ if (!this.renderSystem) {
232
+ throw new Error('[SceneCapture] RendererSystem must be registered before SceneCaptureSystem.');
233
+ }
234
+ this.renderSystem.rendererManager.register(this);
235
+ this.ensurePresentationParticipant();
236
+ }
237
+ update(time) {
238
+ super.update(time);
239
+ this.ensurePresentationParticipant();
240
+ }
241
+ /** Compatibility fallback for hosts that have not yet exposed presentation participants. */
242
+ lateUpdate() {
243
+ var _a;
244
+ if (this.hasLivePresentationParticipant())
245
+ return;
246
+ const application = (_a = this.renderSystem) === null || _a === void 0 ? void 0 : _a.application;
247
+ if (application)
248
+ this.captureForApplication(application);
249
+ }
250
+ componentChanged(changed) {
251
+ if (changed.componentName !== 'SceneCapture')
252
+ return;
253
+ const component = changed.component;
254
+ const gameObjectId = changed.gameObject.id;
255
+ if (changed.type === eva_js.OBSERVER_TYPE.ADD) {
256
+ this.addRecord(gameObjectId, component);
257
+ return;
258
+ }
259
+ if (changed.type === eva_js.OBSERVER_TYPE.REMOVE) {
260
+ const record = this.records.get(gameObjectId);
261
+ if (record)
262
+ this.disposeRecord(record);
263
+ return;
264
+ }
265
+ const record = this.records.get(gameObjectId);
266
+ if (!record)
267
+ return;
268
+ record.component = component;
269
+ this.resizeRecord(record);
270
+ this.updateOutput(record);
271
+ // `manual` remains explicitly controlled by requestCapture(). `onDirty`
272
+ // invalidates for every declarative configuration/source change.
273
+ if (component.updateMode !== 'manual')
274
+ record.dirty = true;
275
+ if (component.captureRevision !== record.lastCaptureRevision)
276
+ record.dirty = true;
277
+ }
278
+ /** Return this system's texture for an output key without relying on global state. */
279
+ getTexture(output) {
280
+ var _a;
281
+ return (_a = this.outputs.get(normalizedOutput(output))) === null || _a === void 0 ? void 0 : _a.texture;
282
+ }
283
+ onDestroy() {
284
+ var _a;
285
+ this.destroyed = true;
286
+ (_a = this.presentationHandle) === null || _a === void 0 ? void 0 : _a.dispose();
287
+ this.presentationHandle = undefined;
288
+ for (const record of Array.from(this.records.values()))
289
+ this.disposeRecord(record);
290
+ try {
291
+ this.emptyCaptureRoot.destroy({ children: false });
292
+ }
293
+ catch (_b) {
294
+ // Renderer-adapter mocks and already-destroyed Pixi roots can reject cleanup.
295
+ }
296
+ }
297
+ addRecord(gameObjectId, component) {
298
+ const existing = this.records.get(gameObjectId);
299
+ if (existing)
300
+ this.disposeRecord(existing);
301
+ const record = {};
302
+ const owner = record;
303
+ Object.assign(record, {
304
+ owner,
305
+ gameObjectId,
306
+ component,
307
+ texture: this.createTexture(component),
308
+ output: normalizedOutput(component.output),
309
+ width: this.widthOf(component),
310
+ height: this.heightOf(component),
311
+ resolutionScale: this.resolutionOf(component),
312
+ dirty: component.updateMode !== 'manual' || component.captureRevision > 0,
313
+ lastCaptureRevision: 0,
314
+ disposed: false,
315
+ onCaptureRequested: () => {
316
+ if (!record.disposed)
317
+ record.dirty = true;
318
+ },
319
+ });
320
+ component.on('captureRequested', record.onCaptureRequested);
321
+ this.records.set(gameObjectId, record);
322
+ this.bindOutput(record);
323
+ }
324
+ createTexture(component) {
325
+ return pixi_js.RenderTexture.create({
326
+ width: this.widthOf(component),
327
+ height: this.heightOf(component),
328
+ resolution: this.resolutionOf(component),
329
+ });
330
+ }
331
+ widthOf(component) {
332
+ return finitePositive(component.width, 256);
333
+ }
334
+ heightOf(component) {
335
+ return finitePositive(component.height, 256);
336
+ }
337
+ resolutionOf(component) {
338
+ return finitePositive(component.resolutionScale, 1);
339
+ }
340
+ resizeRecord(record) {
341
+ const width = this.widthOf(record.component);
342
+ const height = this.heightOf(record.component);
343
+ const resolutionScale = this.resolutionOf(record.component);
344
+ if (record.width === width && record.height === height && record.resolutionScale === resolutionScale)
345
+ return;
346
+ record.width = width;
347
+ record.height = height;
348
+ record.resolutionScale = resolutionScale;
349
+ const texture = record.texture;
350
+ if (typeof texture.resize === 'function') {
351
+ texture.resize(width, height, resolutionScale);
352
+ return;
353
+ }
354
+ const previous = record.texture;
355
+ record.texture = this.createTexture(record.component);
356
+ this.bindOutput(record);
357
+ try {
358
+ previous.destroy(true);
359
+ }
360
+ catch (_a) {
361
+ // The old output is already detached from registry ownership.
362
+ }
363
+ }
364
+ updateOutput(record) {
365
+ const nextOutput = normalizedOutput(record.component.output);
366
+ if (record.output === nextOutput)
367
+ return;
368
+ this.unbindOutput(record);
369
+ record.output = nextOutput;
370
+ this.bindOutput(record);
371
+ }
372
+ bindOutput(record) {
373
+ if (!record.output)
374
+ return;
375
+ this.outputs.set(record.output, record);
376
+ sceneCaptureTextureRegistry.set(record.output, record.owner, record.texture);
377
+ }
378
+ unbindOutput(record) {
379
+ if (!record.output)
380
+ return;
381
+ if (this.outputs.get(record.output) === record)
382
+ this.outputs.delete(record.output);
383
+ sceneCaptureTextureRegistry.delete(record.output, record.owner);
384
+ }
385
+ disposeRecord(record) {
386
+ if (record.disposed)
387
+ return;
388
+ record.disposed = true;
389
+ this.records.delete(record.gameObjectId);
390
+ this.unbindOutput(record);
391
+ try {
392
+ record.component.off('captureRequested', record.onCaptureRequested);
393
+ }
394
+ catch (_a) {
395
+ // Component could have been fully torn down by a synchronous game destroy.
396
+ }
397
+ try {
398
+ record.texture.destroy(true);
399
+ }
400
+ catch (_b) {
401
+ // A renderer teardown may have destroyed its GPU texture first.
402
+ }
403
+ }
404
+ ensurePresentationParticipant() {
405
+ var _a;
406
+ if (this.destroyed || this.hasLivePresentationParticipant())
407
+ return;
408
+ const renderer = this.renderSystem;
409
+ if (!(renderer === null || renderer === void 0 ? void 0 : renderer.application) || typeof renderer.registerPresentationParticipant !== 'function')
410
+ return;
411
+ const applicationId = (_a = renderer.getPresentationApplicationId) === null || _a === void 0 ? void 0 : _a.call(renderer, renderer.application);
412
+ let handle;
413
+ handle = renderer.registerPresentationParticipant({
414
+ id: this.participantId,
415
+ applicationId,
416
+ build: () => ({
417
+ writes: Array.from(this.outputs.keys(), output => `scene-capture:${output}`),
418
+ }),
419
+ submit: (context) => {
420
+ this.captureForApplication(context.application);
421
+ },
422
+ dispose: () => {
423
+ if (this.presentationHandle === handle)
424
+ this.presentationHandle = undefined;
425
+ },
426
+ });
427
+ this.presentationHandle = handle;
428
+ }
429
+ hasLivePresentationParticipant() {
430
+ return Boolean(this.presentationHandle && !this.presentationHandle.disposed);
431
+ }
432
+ /** Executes synchronously inside the renderer's presentation phase. */
433
+ captureForApplication(application) {
434
+ const renderer = application === null || application === void 0 ? void 0 : application.renderer;
435
+ if (!(renderer === null || renderer === void 0 ? void 0 : renderer.render))
436
+ return;
437
+ for (const record of this.records.values()) {
438
+ if (!this.shouldCapture(record))
439
+ continue;
440
+ if (!this.captureRecord(renderer, application, record))
441
+ continue;
442
+ record.dirty = false;
443
+ record.lastCaptureRevision = record.component.captureRevision;
444
+ }
445
+ }
446
+ shouldCapture(record) {
447
+ const component = record.component;
448
+ if (record.disposed || component.enabled === false)
449
+ return false;
450
+ if (component.updateMode === 'always')
451
+ return true;
452
+ if (component.captureRevision !== record.lastCaptureRevision)
453
+ record.dirty = true;
454
+ return record.dirty;
455
+ }
456
+ captureRecord(renderer, application, record) {
457
+ const sources = this.resolveSourceContainers(record.component, application);
458
+ const clearColor = this.toClearColor(record.component);
459
+ try {
460
+ if (sources.length === 0) {
461
+ renderer.render({
462
+ container: this.emptyCaptureRoot,
463
+ target: record.texture,
464
+ clear: true,
465
+ clearColor,
466
+ });
467
+ return true;
468
+ }
469
+ for (let index = 0; index < sources.length; index++) {
470
+ const source = sources[index];
471
+ const options = {
472
+ container: source,
473
+ target: record.texture,
474
+ clear: index === 0,
475
+ };
476
+ if (index === 0)
477
+ options.clearColor = clearColor;
478
+ // Passing the current world transform preserves root/parent placement
479
+ // without reparenting a live container out of the main stage.
480
+ if (source.worldTransform)
481
+ options.transform = source.worldTransform;
482
+ renderer.render(options);
483
+ }
484
+ return true;
485
+ }
486
+ catch (_a) {
487
+ // Keep the record dirty so a transient WebGL/WebGPU target error retries
488
+ // during a later presentation frame instead of freezing a stale reflection.
489
+ return false;
490
+ }
491
+ }
492
+ resolveSourceContainers(component, application) {
493
+ var _a;
494
+ const sources = [];
495
+ const sourceNames = Array.isArray(component.sourceNames) ? component.sourceNames : [];
496
+ const game = this.game;
497
+ for (const sourceName of sourceNames) {
498
+ if (!sourceName || typeof (game === null || game === void 0 ? void 0 : game.findAllByName) !== 'function')
499
+ continue;
500
+ const gameObjects = game.findAllByName(sourceName);
501
+ for (const gameObject of gameObjects || []) {
502
+ if (!gameObject || gameObject.destroyed)
503
+ continue;
504
+ const container = (_a = this.containerManager) === null || _a === void 0 ? void 0 : _a.getContainer(gameObject.id);
505
+ if (!container ||
506
+ container.destroyed ||
507
+ container === application.stage ||
508
+ container === this.emptyCaptureRoot) {
509
+ continue;
510
+ }
511
+ if (!sources.includes(container))
512
+ sources.push(container);
513
+ }
514
+ }
515
+ return sources;
516
+ }
517
+ toClearColor(component) {
518
+ const color = (Number(component.clearColor) || 0) >>> 0;
519
+ return [
520
+ ((color >>> 16) & 0xff) / 255,
521
+ ((color >>> 8) & 0xff) / 255,
522
+ (color & 0xff) / 255,
523
+ clampAlpha(component.clearAlpha),
524
+ ];
525
+ }
526
+ };
527
+ SceneCaptureSystem.systemName = 'SceneCapture';
528
+ SceneCaptureSystem = __decorate([
529
+ eva_js.decorators.componentObserver({
530
+ SceneCapture: [
531
+ { prop: ['sourceNames'], deep: true },
532
+ { prop: ['output'], deep: false },
533
+ { prop: ['width'], deep: false },
534
+ { prop: ['height'], deep: false },
535
+ { prop: ['resolutionScale'], deep: false },
536
+ { prop: ['updateMode'], deep: false },
537
+ { prop: ['clearColor'], deep: false },
538
+ { prop: ['clearAlpha'], deep: false },
539
+ { prop: ['enabled'], deep: false },
540
+ { prop: ['captureRevision'], deep: false },
541
+ ],
542
+ })
543
+ ], SceneCaptureSystem);
544
+ var SceneCaptureSystem$1 = SceneCaptureSystem;
545
+
546
+ exports.SceneCapture = SceneCapture;
547
+ exports.SceneCaptureSystem = SceneCaptureSystem$1;
548
+ exports.SceneCaptureTextureRegistry = SceneCaptureTextureRegistry;
549
+ exports.getSceneCaptureTexture = getSceneCaptureTexture;
550
+ exports.sceneCaptureTextureRegistry = sceneCaptureTextureRegistry;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@eva/eva.js"),t=require("@eva/inspector-decorator"),r=require("@eva/plugin-renderer"),i=require("pixi.js");function o(e,t,r,i){var o,s=arguments.length,n=s<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(n=(s<3?o(n):s>3?o(t,r,n):o(t,r))||n);return s>3&&n&&Object.defineProperty(t,r,n),n}function s(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}"function"==typeof SuppressedError&&SuppressedError;const n=(e,t)=>{const r=Number(e);return Number.isFinite(r)&&r>0?r:t};class a extends e.Component{constructor(){super(...arguments),this.sourceNames=[],this.output="",this.width=256,this.height=256,this.resolutionScale=1,this.updateMode="always",this.clearColor=0,this.clearAlpha=0,this.enabled=!0,this.captureRevision=0}init(e){e&&Object.assign(this,e),this.sourceNames=Array.isArray(this.sourceNames)?this.sourceNames.filter(e=>"string"==typeof e&&e.trim().length>0):[],this.output="string"==typeof this.output?this.output.trim():"",this.width=n(this.width,256),this.height=n(this.height,256),this.resolutionScale=n(this.resolutionScale,1),this.updateMode=this.normalizeUpdateMode(this.updateMode),this.clearColor=Math.max(0,Math.min(16777215,Number(this.clearColor)||0))>>>0,this.clearAlpha=((e,t,r,i)=>{const o=Number(e);return Number.isFinite(o)?Math.min(r,Math.max(t,o)):i})(this.clearAlpha,0,1,0),this.enabled=!1!==this.enabled}requestCapture(){this.captureRevision++,this.emit("captureRequested",{revision:this.captureRevision})}normalizeUpdateMode(e){return"onDirty"===e||"manual"===e?e:"always"}}a.componentName="SceneCapture",o([t.type("array"),s("design:type",Array)],a.prototype,"sourceNames",void 0),o([t.type("string"),s("design:type",String)],a.prototype,"output",void 0),o([t.type("number"),s("design:type",Number)],a.prototype,"width",void 0),o([t.type("number"),s("design:type",Number)],a.prototype,"height",void 0),o([t.type("number"),s("design:type",Number)],a.prototype,"resolutionScale",void 0),o([t.type("string"),s("design:type",String)],a.prototype,"updateMode",void 0),o([t.type("number"),s("design:type",Number)],a.prototype,"clearColor",void 0),o([t.type("number"),s("design:type",Number)],a.prototype,"clearAlpha",void 0),o([t.type("boolean"),s("design:type",Boolean)],a.prototype,"enabled",void 0),o([t.type("number"),s("design:type",Number)],a.prototype,"captureRevision",void 0);class u{constructor(){this.entries=new Map}get(e){var t;return null===(t=this.entries.get(this.normalize(e)))||void 0===t?void 0:t.texture}getEntry(e){return this.entries.get(this.normalize(e))}has(e){return this.entries.has(this.normalize(e))}set(e,t,r){const i=this.normalize(e);i&&t&&r&&this.entries.set(i,Object.freeze({output:i,owner:t,texture:r}))}delete(e,t){const r=this.normalize(e),i=this.entries.get(r);return!(!i||t&&i.owner!==t)&&this.entries.delete(r)}normalize(e){return"string"==typeof e?e.trim():""}}const p=new u;let d=0;const c=(e,t)=>{const r=Number(e);return Number.isFinite(r)&&r>0?r:t},h=e=>{const t=Number(e);return Number.isFinite(t)?Math.max(0,Math.min(1,t)):0},l=e=>"string"==typeof e?e.trim():"";let m=class extends r.Renderer{constructor(){super(...arguments),this.name="SceneCapture",this.presentationAddFlushEnabled=!0,this.records=new Map,this.outputs=new Map,this.emptyCaptureRoot=new i.Container,this.participantId="scene-capture:"+ ++d,this.destroyed=!1}init(){if(this.renderSystem=this.game.getSystem(r.RendererSystem),!this.renderSystem)throw new Error("[SceneCapture] RendererSystem must be registered before SceneCaptureSystem.");this.renderSystem.rendererManager.register(this),this.ensurePresentationParticipant()}update(e){super.update(e),this.ensurePresentationParticipant()}lateUpdate(){var e;if(this.hasLivePresentationParticipant())return;const t=null===(e=this.renderSystem)||void 0===e?void 0:e.application;t&&this.captureForApplication(t)}componentChanged(t){if("SceneCapture"!==t.componentName)return;const r=t.component,i=t.gameObject.id;if(t.type===e.OBSERVER_TYPE.ADD)return void this.addRecord(i,r);if(t.type===e.OBSERVER_TYPE.REMOVE){const e=this.records.get(i);return void(e&&this.disposeRecord(e))}const o=this.records.get(i);o&&(o.component=r,this.resizeRecord(o),this.updateOutput(o),"manual"!==r.updateMode&&(o.dirty=!0),r.captureRevision!==o.lastCaptureRevision&&(o.dirty=!0))}getTexture(e){var t;return null===(t=this.outputs.get(l(e)))||void 0===t?void 0:t.texture}onDestroy(){var e;this.destroyed=!0,null===(e=this.presentationHandle)||void 0===e||e.dispose(),this.presentationHandle=void 0;for(const e of Array.from(this.records.values()))this.disposeRecord(e);try{this.emptyCaptureRoot.destroy({children:!1})}catch(e){}}addRecord(e,t){const r=this.records.get(e);r&&this.disposeRecord(r);const i={},o=i;Object.assign(i,{owner:o,gameObjectId:e,component:t,texture:this.createTexture(t),output:l(t.output),width:this.widthOf(t),height:this.heightOf(t),resolutionScale:this.resolutionOf(t),dirty:"manual"!==t.updateMode||t.captureRevision>0,lastCaptureRevision:0,disposed:!1,onCaptureRequested:()=>{i.disposed||(i.dirty=!0)}}),t.on("captureRequested",i.onCaptureRequested),this.records.set(e,i),this.bindOutput(i)}createTexture(e){return i.RenderTexture.create({width:this.widthOf(e),height:this.heightOf(e),resolution:this.resolutionOf(e)})}widthOf(e){return c(e.width,256)}heightOf(e){return c(e.height,256)}resolutionOf(e){return c(e.resolutionScale,1)}resizeRecord(e){const t=this.widthOf(e.component),r=this.heightOf(e.component),i=this.resolutionOf(e.component);if(e.width===t&&e.height===r&&e.resolutionScale===i)return;e.width=t,e.height=r,e.resolutionScale=i;const o=e.texture;if("function"==typeof o.resize)return void o.resize(t,r,i);const s=e.texture;e.texture=this.createTexture(e.component),this.bindOutput(e);try{s.destroy(!0)}catch(e){}}updateOutput(e){const t=l(e.component.output);e.output!==t&&(this.unbindOutput(e),e.output=t,this.bindOutput(e))}bindOutput(e){e.output&&(this.outputs.set(e.output,e),p.set(e.output,e.owner,e.texture))}unbindOutput(e){e.output&&(this.outputs.get(e.output)===e&&this.outputs.delete(e.output),p.delete(e.output,e.owner))}disposeRecord(e){if(!e.disposed){e.disposed=!0,this.records.delete(e.gameObjectId),this.unbindOutput(e);try{e.component.off("captureRequested",e.onCaptureRequested)}catch(e){}try{e.texture.destroy(!0)}catch(e){}}}ensurePresentationParticipant(){var e;if(this.destroyed||this.hasLivePresentationParticipant())return;const t=this.renderSystem;if(!(null==t?void 0:t.application)||"function"!=typeof t.registerPresentationParticipant)return;const r=null===(e=t.getPresentationApplicationId)||void 0===e?void 0:e.call(t,t.application);let i;i=t.registerPresentationParticipant({id:this.participantId,applicationId:r,build:()=>({writes:Array.from(this.outputs.keys(),e=>`scene-capture:${e}`)}),submit:e=>{this.captureForApplication(e.application)},dispose:()=>{this.presentationHandle===i&&(this.presentationHandle=void 0)}}),this.presentationHandle=i}hasLivePresentationParticipant(){return Boolean(this.presentationHandle&&!this.presentationHandle.disposed)}captureForApplication(e){const t=null==e?void 0:e.renderer;if(null==t?void 0:t.render)for(const r of this.records.values())this.shouldCapture(r)&&this.captureRecord(t,e,r)&&(r.dirty=!1,r.lastCaptureRevision=r.component.captureRevision)}shouldCapture(e){const t=e.component;return!e.disposed&&!1!==t.enabled&&("always"===t.updateMode||(t.captureRevision!==e.lastCaptureRevision&&(e.dirty=!0),e.dirty))}captureRecord(e,t,r){const i=this.resolveSourceContainers(r.component,t),o=this.toClearColor(r.component);try{if(0===i.length)return e.render({container:this.emptyCaptureRoot,target:r.texture,clear:!0,clearColor:o}),!0;for(let t=0;t<i.length;t++){const s=i[t],n={container:s,target:r.texture,clear:0===t};0===t&&(n.clearColor=o),s.worldTransform&&(n.transform=s.worldTransform),e.render(n)}return!0}catch(e){return!1}}resolveSourceContainers(e,t){var r;const i=[],o=Array.isArray(e.sourceNames)?e.sourceNames:[],s=this.game;for(const e of o){if(!e||"function"!=typeof(null==s?void 0:s.findAllByName))continue;const o=s.findAllByName(e);for(const e of o||[]){if(!e||e.destroyed)continue;const o=null===(r=this.containerManager)||void 0===r?void 0:r.getContainer(e.id);o&&!o.destroyed&&o!==t.stage&&o!==this.emptyCaptureRoot&&(i.includes(o)||i.push(o))}}return i}toClearColor(e){const t=(Number(e.clearColor)||0)>>>0;return[(t>>>16&255)/255,(t>>>8&255)/255,(255&t)/255,h(e.clearAlpha)]}};m.systemName="SceneCapture",m=o([e.decorators.componentObserver({SceneCapture:[{prop:["sourceNames"],deep:!0},{prop:["output"],deep:!1},{prop:["width"],deep:!1},{prop:["height"],deep:!1},{prop:["resolutionScale"],deep:!1},{prop:["updateMode"],deep:!1},{prop:["clearColor"],deep:!1},{prop:["clearAlpha"],deep:!1},{prop:["enabled"],deep:!1},{prop:["captureRevision"],deep:!1}]})],m);var y=m;exports.SceneCapture=a,exports.SceneCaptureSystem=y,exports.SceneCaptureTextureRegistry=u,exports.getSceneCaptureTexture=function(e){return p.get(e)},exports.sceneCaptureTextureRegistry=p;