@2112-lab/central-plant 0.3.26 → 0.3.27
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/bundle/index.js +894 -117
- package/dist/cjs/src/core/centralPlant.js +1 -1
- package/dist/cjs/src/core/centralPlantInternals.js +17 -0
- package/dist/cjs/src/core/sceneViewer.js +55 -2
- package/dist/cjs/src/managers/behaviors/IoAnimationManager.js +24 -1
- package/dist/cjs/src/managers/behaviors/IoOutlineManager.js +258 -0
- package/dist/cjs/src/managers/controls/transformControlsManager.js +319 -43
- package/dist/cjs/src/managers/scene/animationManager.js +9 -2
- package/dist/cjs/src/managers/scene/componentTooltipManager.js +190 -27
- package/dist/cjs/src/managers/scene/modelManager.js +15 -1
- package/dist/cjs/src/utils/boundingBoxUtils.js +38 -40
- package/dist/esm/src/core/centralPlant.js +1 -1
- package/dist/esm/src/core/centralPlantInternals.js +17 -0
- package/dist/esm/src/core/sceneViewer.js +55 -2
- package/dist/esm/src/managers/behaviors/IoAnimationManager.js +24 -1
- package/dist/esm/src/managers/behaviors/IoOutlineManager.js +234 -0
- package/dist/esm/src/managers/controls/transformControlsManager.js +319 -43
- package/dist/esm/src/managers/scene/animationManager.js +9 -2
- package/dist/esm/src/managers/scene/componentTooltipManager.js +191 -28
- package/dist/esm/src/managers/scene/modelManager.js +16 -2
- package/dist/esm/src/utils/boundingBoxUtils.js +39 -42
- package/package.json +1 -1
|
@@ -35,7 +35,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
35
35
|
* Initialize the CentralPlant manager
|
|
36
36
|
*
|
|
37
37
|
* @constructor
|
|
38
|
-
* @version 0.3.
|
|
38
|
+
* @version 0.3.27
|
|
39
39
|
* @updated 2025-10-22
|
|
40
40
|
*
|
|
41
41
|
* @description Creates a new CentralPlant instance and initializes internal managers and utilities.
|
|
@@ -28,8 +28,10 @@ var sceneTooltipsManager = require('../managers/scene/sceneTooltipsManager.js');
|
|
|
28
28
|
var componentTooltipManager = require('../managers/scene/componentTooltipManager.js');
|
|
29
29
|
var viewport2DManager = require('../managers/scene/viewport2DManager.js');
|
|
30
30
|
var IoAnimationManager = require('../managers/behaviors/IoAnimationManager.js');
|
|
31
|
+
var IoOutlineManager = require('../managers/behaviors/IoOutlineManager.js');
|
|
31
32
|
var nameUtils = require('../utils/nameUtils.js');
|
|
32
33
|
var ioDeviceUtils = require('../utils/ioDeviceUtils.js');
|
|
34
|
+
var boundingBoxUtils = require('../utils/boundingBoxUtils.js');
|
|
33
35
|
var modelPreloader = require('../rendering/modelPreloader.js');
|
|
34
36
|
|
|
35
37
|
function _interopNamespace(e) {
|
|
@@ -174,6 +176,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
|
|
|
174
176
|
this.centralPlant.managers.componentDragManager = new componentDragManager.ComponentDragManager(this.centralPlant.sceneViewer);
|
|
175
177
|
this.centralPlant.managers.viewport2DManager = new viewport2DManager.Viewport2DManager(this.centralPlant.sceneViewer);
|
|
176
178
|
this.centralPlant.managers.ioAnimationManager = new IoAnimationManager.IoAnimationManager(this.centralPlant.sceneViewer);
|
|
179
|
+
this.centralPlant.managers.ioOutlineManager = new IoOutlineManager.IoOutlineManager(this.centralPlant.sceneViewer);
|
|
177
180
|
|
|
178
181
|
// All managers are now stored in the managers collection and will be attached via attachToComponent()
|
|
179
182
|
}
|
|
@@ -1190,6 +1193,20 @@ var CentralPlantInternals = /*#__PURE__*/function () {
|
|
|
1190
1193
|
componentManager.registerComponent(componentModel);
|
|
1191
1194
|
}
|
|
1192
1195
|
|
|
1196
|
+
// Pre-warm the filtered bounding-box cache for smart components so the
|
|
1197
|
+
// first selection is instant. Deferred to idle time so it does not
|
|
1198
|
+
// block the current frame.
|
|
1199
|
+
if (componentData.isSmart) {
|
|
1200
|
+
var warmFn = function warmFn() {
|
|
1201
|
+
return boundingBoxUtils.computeFilteredBoundingBoxCached(componentModel, ['io-device', 'connector']);
|
|
1202
|
+
};
|
|
1203
|
+
if (typeof requestIdleCallback !== 'undefined') {
|
|
1204
|
+
requestIdleCallback(warmFn);
|
|
1205
|
+
} else {
|
|
1206
|
+
setTimeout(warmFn, 0);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1193
1210
|
// EMIT COMPONENT ADDED EVENT
|
|
1194
1211
|
// This allows UI components (like SceneHierarchy) to update reactively
|
|
1195
1212
|
if (this.centralPlant.sceneViewer.emit) {
|
|
@@ -102,7 +102,7 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
102
102
|
this.centralPlant.attachToComponent();
|
|
103
103
|
|
|
104
104
|
// Sync our managers tracking object after attachment
|
|
105
|
-
managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'pathFlowManager', 'behaviorManager', 'ioAnimationManager', 'sceneOperationsManager', 'animationManager', 'cameraControlsManager', 'componentDragManager', 'tooltipsManager', 'componentTooltipManager']; // Populate our managers tracking object
|
|
105
|
+
managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'pathFlowManager', 'behaviorManager', 'ioAnimationManager', 'ioOutlineManager', 'sceneOperationsManager', 'animationManager', 'cameraControlsManager', 'componentDragManager', 'tooltipsManager', 'componentTooltipManager']; // Populate our managers tracking object
|
|
106
106
|
managerKeys.forEach(function (key) {
|
|
107
107
|
if (_this2[key]) {
|
|
108
108
|
_this2.managers[key] = _this2[key];
|
|
@@ -290,7 +290,21 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
// Update camera aspect ratio
|
|
293
|
-
this.camera.
|
|
293
|
+
if (this.camera.isPerspectiveCamera) {
|
|
294
|
+
this.camera.aspect = width / height;
|
|
295
|
+
} else if (this.camera.isOrthographicCamera) {
|
|
296
|
+
var _this$camera$userData;
|
|
297
|
+
var aspect = width / height;
|
|
298
|
+
var extents = (_this$camera$userData = this.camera.userData) === null || _this$camera$userData === void 0 ? void 0 : _this$camera$userData._orthoHalfExtents;
|
|
299
|
+
if (extents) {
|
|
300
|
+
var frustumHalfH = Math.max(extents.halfH, extents.halfW / aspect);
|
|
301
|
+
var frustumHalfW = frustumHalfH * aspect;
|
|
302
|
+
this.camera.left = -frustumHalfW;
|
|
303
|
+
this.camera.right = frustumHalfW;
|
|
304
|
+
this.camera.top = frustumHalfH;
|
|
305
|
+
this.camera.bottom = -frustumHalfH;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
294
308
|
this.camera.updateProjectionMatrix();
|
|
295
309
|
|
|
296
310
|
// Update renderer size (updateStyle=true to sync canvas CSS)
|
|
@@ -417,6 +431,45 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
417
431
|
if (_this4.componentTooltipManager) {
|
|
418
432
|
_this4.componentTooltipManager.toggleIODeviceBinaryState(ioDeviceObject);
|
|
419
433
|
}
|
|
434
|
+
},
|
|
435
|
+
onIODeviceDrag: function onIODeviceDrag(ioDeviceObject, signedDelta, isStart) {
|
|
436
|
+
if (isStart) {
|
|
437
|
+
var _ioDeviceObject$userD, _this4$managers$ioAni, _this4$managers, _this4$managers2;
|
|
438
|
+
// Resolve parentUuid by walking up to the host component.
|
|
439
|
+
// Use userData.originalUuid (the custom componentId) because that
|
|
440
|
+
// is what IoAnimationManager uses as the map key — NOT obj.uuid.
|
|
441
|
+
var parentUuid = null;
|
|
442
|
+
var obj = ioDeviceObject.parent;
|
|
443
|
+
while (obj) {
|
|
444
|
+
var _obj$userData;
|
|
445
|
+
if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) === 'component') {
|
|
446
|
+
parentUuid = obj.userData.originalUuid || obj.uuid;
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
obj = obj.parent;
|
|
450
|
+
}
|
|
451
|
+
var attachmentId = (_ioDeviceObject$userD = ioDeviceObject.userData) === null || _ioDeviceObject$userD === void 0 ? void 0 : _ioDeviceObject$userD.attachmentId;
|
|
452
|
+
// When animated meshes are available, outline ONLY them so their
|
|
453
|
+
// silhouette is isolated and the outline ring is visible around
|
|
454
|
+
// them specifically (not swallowed by the larger device body).
|
|
455
|
+
// Fall back to the whole device group when none are registered.
|
|
456
|
+
var animatedMeshes = attachmentId && parentUuid ? (_this4$managers$ioAni = (_this4$managers = _this4.managers) === null || _this4$managers === void 0 || (_this4$managers = _this4$managers.ioAnimationManager) === null || _this4$managers === void 0 ? void 0 : _this4$managers.getAnimatedMeshes(parentUuid, attachmentId)) !== null && _this4$managers$ioAni !== void 0 ? _this4$managers$ioAni : [] : [];
|
|
457
|
+
var targets = animatedMeshes.length > 0 ? animatedMeshes : [ioDeviceObject];
|
|
458
|
+
(_this4$managers2 = _this4.managers) === null || _this4$managers2 === void 0 || (_this4$managers2 = _this4$managers2.ioOutlineManager) === null || _this4$managers2 === void 0 || _this4$managers2.setTargets(targets);
|
|
459
|
+
}
|
|
460
|
+
if (!_this4.componentTooltipManager) return;
|
|
461
|
+
if (isStart) {
|
|
462
|
+
_this4.componentTooltipManager.startIODeviceDrag(ioDeviceObject);
|
|
463
|
+
} else {
|
|
464
|
+
_this4.componentTooltipManager.updateIODeviceDrag(signedDelta);
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
onIODeviceDragEnd: function onIODeviceDragEnd(ioDeviceObject) {
|
|
468
|
+
var _this4$managers3;
|
|
469
|
+
(_this4$managers3 = _this4.managers) === null || _this4$managers3 === void 0 || (_this4$managers3 = _this4$managers3.ioOutlineManager) === null || _this4$managers3 === void 0 || _this4$managers3.setTargets([]);
|
|
470
|
+
if (_this4.componentTooltipManager) {
|
|
471
|
+
_this4.componentTooltipManager.endIODeviceDrag();
|
|
472
|
+
}
|
|
420
473
|
}
|
|
421
474
|
});
|
|
422
475
|
|
|
@@ -243,6 +243,26 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
243
243
|
return dps;
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
+
/**
|
|
247
|
+
* Return the Three.js mesh objects that are animated for a given attachment.
|
|
248
|
+
* Used by IoOutlineManager to include animated meshes in the outline.
|
|
249
|
+
*
|
|
250
|
+
* @param {string} parentUuid
|
|
251
|
+
* @param {string} attachmentId
|
|
252
|
+
* @returns {THREE.Object3D[]}
|
|
253
|
+
*/
|
|
254
|
+
}, {
|
|
255
|
+
key: "getAnimatedMeshes",
|
|
256
|
+
value: function getAnimatedMeshes(parentUuid, attachmentId) {
|
|
257
|
+
var key = this._key(parentUuid, attachmentId);
|
|
258
|
+
var entries = this._entries.get(key);
|
|
259
|
+
if (!(entries !== null && entries !== void 0 && entries.length)) return [];
|
|
260
|
+
// Deduplicate — multiple animations can target the same mesh
|
|
261
|
+
return _rollupPluginBabelHelpers.toConsumableArray(new Set(entries.map(function (e) {
|
|
262
|
+
return e.mesh;
|
|
263
|
+
})));
|
|
264
|
+
}
|
|
265
|
+
|
|
246
266
|
/**
|
|
247
267
|
* Remove all animation entries associated with a given host component.
|
|
248
268
|
* Call when a component is removed from the scene.
|
|
@@ -474,7 +494,10 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
|
474
494
|
value: function _applyTranslation(mesh, origPos, transform) {
|
|
475
495
|
var _transform$x, _transform$y, _transform$z;
|
|
476
496
|
if (!transform) return;
|
|
477
|
-
|
|
497
|
+
// X and Y are negated to match the sign convention used in the AnimateDevicesDialog
|
|
498
|
+
// preview (_syncViewerTransform negates x and y before calling setMeshPreviewOffset).
|
|
499
|
+
// Z is added directly (no negation) — matching the dialog's z handling.
|
|
500
|
+
mesh.position.set(origPos.x - ((_transform$x = transform.x) !== null && _transform$x !== void 0 ? _transform$x : 0), origPos.y - ((_transform$y = transform.y) !== null && _transform$y !== void 0 ? _transform$y : 0), origPos.z + ((_transform$z = transform.z) !== null && _transform$z !== void 0 ? _transform$z : 0));
|
|
478
501
|
}
|
|
479
502
|
|
|
480
503
|
/**
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHelpers.js');
|
|
6
|
+
var THREE = require('three');
|
|
7
|
+
var baseDisposable = require('../../core/baseDisposable.js');
|
|
8
|
+
|
|
9
|
+
function _interopNamespace(e) {
|
|
10
|
+
if (e && e.__esModule) return e;
|
|
11
|
+
var n = Object.create(null);
|
|
12
|
+
if (e) {
|
|
13
|
+
Object.keys(e).forEach(function (k) {
|
|
14
|
+
if (k !== 'default') {
|
|
15
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return e[k]; }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
n["default"] = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Three.js layer reserved exclusively for the outline mask pass.
|
|
31
|
+
* Layer 20 is high enough to be well clear of typical app layer usage.
|
|
32
|
+
*/
|
|
33
|
+
var OUTLINE_LAYER = 20;
|
|
34
|
+
|
|
35
|
+
// ── Shaders ──────────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
var VERT_SHADER = /* glsl */"\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position.xy, 0.0, 1.0);\n}\n";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Screen-space outline shader.
|
|
41
|
+
*
|
|
42
|
+
* Samples in a circular kernel to find the nearest filled (silhouette) pixel.
|
|
43
|
+
* Applies a smooth alpha falloff at the outer edge using smoothstep so the
|
|
44
|
+
* outline fades cleanly rather than cutting off hard.
|
|
45
|
+
*
|
|
46
|
+
* RADIUS — maximum distance in pixels from the silhouette edge to draw.
|
|
47
|
+
* SAMPLES — kernel half-extent; must be an integer ≥ ceil(RADIUS).
|
|
48
|
+
*/
|
|
49
|
+
var FRAG_SHADER = /* glsl */"\nuniform sampler2D tMask;\nuniform vec2 uInvSize;\nvarying vec2 vUv;\n\nconst float RADIUS = 3.0; // total outline width in pixels\nconst int SAMPLES = 4; // kernel half-extent (\u2265 ceil(RADIUS))\n\nvoid main() {\n float center = texture2D(tMask, vUv).r;\n\n // Pixels inside the silhouette: the main render already drew them.\n if (center > 0.5) discard;\n\n // Find the closest filled neighbour within the circular kernel.\n float minDist = 99.0;\n for (int x = -SAMPLES; x <= SAMPLES; x++) {\n for (int y = -SAMPLES; y <= SAMPLES; y++) {\n float dist = length(vec2(float(x), float(y)));\n if (dist > RADIUS + 1.0) continue; // skip corners outside circle\n vec2 offset = vec2(float(x), float(y)) * uInvSize;\n if (texture2D(tMask, vUv + offset).r > 0.5) {\n minDist = min(minDist, dist);\n }\n }\n }\n\n if (minDist > RADIUS + 0.5) discard;\n\n // Smooth alpha: full opacity near the edge, fades to 0 at RADIUS pixels out.\n float alpha = 1.0 - smoothstep(RADIUS - 1.0, RADIUS + 0.5, minDist);\n gl_FragColor = vec4(1.0, 1.0, 1.0, alpha);\n}\n";
|
|
50
|
+
var IoOutlineManager = /*#__PURE__*/function (_BaseDisposable) {
|
|
51
|
+
function IoOutlineManager(sceneViewer) {
|
|
52
|
+
var _this;
|
|
53
|
+
_rollupPluginBabelHelpers.classCallCheck(this, IoOutlineManager);
|
|
54
|
+
_this = _rollupPluginBabelHelpers.callSuper(this, IoOutlineManager);
|
|
55
|
+
_this.sceneViewer = sceneViewer;
|
|
56
|
+
_this._maskTarget = null;
|
|
57
|
+
_this._maskMat = null;
|
|
58
|
+
_this._overlayScene = null;
|
|
59
|
+
_this._overlayCamera = null;
|
|
60
|
+
_this._overlayMat = null;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Meshes that have been moved onto OUTLINE_LAYER, stored with their
|
|
64
|
+
* original layers.mask so they can be restored on clearance.
|
|
65
|
+
* @type {{ mesh: THREE.Mesh, originalMask: number }[]}
|
|
66
|
+
*/
|
|
67
|
+
_this._layeredMeshes = [];
|
|
68
|
+
|
|
69
|
+
/** @type {boolean} Whether an outline is currently active. */
|
|
70
|
+
_this.isActive = false;
|
|
71
|
+
return _this;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
75
|
+
// PUBLIC API
|
|
76
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Set the objects to outline. Pass an empty array (or call with no args)
|
|
80
|
+
* to remove the outline.
|
|
81
|
+
* @param {THREE.Object3D[]} objects
|
|
82
|
+
*/
|
|
83
|
+
_rollupPluginBabelHelpers.inherits(IoOutlineManager, _BaseDisposable);
|
|
84
|
+
return _rollupPluginBabelHelpers.createClass(IoOutlineManager, [{
|
|
85
|
+
key: "setTargets",
|
|
86
|
+
value: function setTargets() {
|
|
87
|
+
var _this2 = this;
|
|
88
|
+
var objects = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
|
89
|
+
this._clearLayeredMeshes();
|
|
90
|
+
if (!objects.length) {
|
|
91
|
+
this.isActive = false;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
objects.forEach(function (obj) {
|
|
95
|
+
obj.traverse(function (child) {
|
|
96
|
+
if (!child.isMesh) return;
|
|
97
|
+
var originalMask = child.layers.mask;
|
|
98
|
+
child.layers.enable(OUTLINE_LAYER);
|
|
99
|
+
_this2._layeredMeshes.push({
|
|
100
|
+
mesh: child,
|
|
101
|
+
originalMask: originalMask
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
this.isActive = this._layeredMeshes.length > 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Render one frame: main scene → mask pass → outline composite.
|
|
110
|
+
* Must be called instead of renderer.render() when isActive is true.
|
|
111
|
+
*/
|
|
112
|
+
}, {
|
|
113
|
+
key: "render",
|
|
114
|
+
value: function render() {
|
|
115
|
+
var sv = this.sceneViewer;
|
|
116
|
+
var renderer = sv.renderer,
|
|
117
|
+
scene = sv.scene,
|
|
118
|
+
camera = sv.camera;
|
|
119
|
+
if (!renderer || !scene || !camera) return;
|
|
120
|
+
|
|
121
|
+
// ── Step 1: Normal scene render — sky, transparency, all unaffected ──
|
|
122
|
+
renderer.render(scene, camera);
|
|
123
|
+
if (!this.isActive) return;
|
|
124
|
+
this._ensureResources();
|
|
125
|
+
if (!this._maskTarget) return;
|
|
126
|
+
|
|
127
|
+
// Stash renderer / scene / camera state we are about to mutate
|
|
128
|
+
var prevBg = scene.background;
|
|
129
|
+
var prevOverride = scene.overrideMaterial;
|
|
130
|
+
var prevLayerMask = camera.layers.mask;
|
|
131
|
+
var prevAutoClear = renderer.autoClear;
|
|
132
|
+
var prevClearClr = renderer.getClearColor(new THREE__namespace.Color());
|
|
133
|
+
var prevClearA = renderer.getClearAlpha();
|
|
134
|
+
|
|
135
|
+
// ── Step 2: Render the device silhouette into the private mask target ──
|
|
136
|
+
scene.background = null; // transparent clear
|
|
137
|
+
scene.overrideMaterial = this._maskMat; // flat white for all geometry
|
|
138
|
+
camera.layers.set(OUTLINE_LAYER); // only see the device meshes
|
|
139
|
+
renderer.setClearColor(0x000000, 0);
|
|
140
|
+
renderer.autoClear = true;
|
|
141
|
+
renderer.setRenderTarget(this._maskTarget);
|
|
142
|
+
renderer.render(scene, camera);
|
|
143
|
+
renderer.setRenderTarget(null);
|
|
144
|
+
|
|
145
|
+
// Restore mutated state
|
|
146
|
+
scene.background = prevBg;
|
|
147
|
+
scene.overrideMaterial = prevOverride;
|
|
148
|
+
camera.layers.mask = prevLayerMask;
|
|
149
|
+
renderer.setClearColor(prevClearClr, prevClearA);
|
|
150
|
+
|
|
151
|
+
// ── Step 3: Composite the outline on top without clearing the framebuffer
|
|
152
|
+
renderer.autoClear = false;
|
|
153
|
+
renderer.render(this._overlayScene, this._overlayCamera);
|
|
154
|
+
renderer.autoClear = prevAutoClear;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Call when the canvas is resized to keep the mask target and shader in sync.
|
|
159
|
+
* @param {number} width
|
|
160
|
+
* @param {number} height
|
|
161
|
+
*/
|
|
162
|
+
}, {
|
|
163
|
+
key: "setSize",
|
|
164
|
+
value: function setSize(width, height) {
|
|
165
|
+
if (this._maskTarget) this._maskTarget.setSize(width, height);
|
|
166
|
+
if (this._overlayMat) {
|
|
167
|
+
this._overlayMat.uniforms.uInvSize.value.set(1 / width, 1 / height);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}, {
|
|
171
|
+
key: "dispose",
|
|
172
|
+
value: function dispose() {
|
|
173
|
+
this._clearLayeredMeshes();
|
|
174
|
+
if (this._maskTarget) {
|
|
175
|
+
this._maskTarget.dispose();
|
|
176
|
+
this._maskTarget = null;
|
|
177
|
+
}
|
|
178
|
+
if (this._maskMat) {
|
|
179
|
+
this._maskMat.dispose();
|
|
180
|
+
this._maskMat = null;
|
|
181
|
+
}
|
|
182
|
+
if (this._overlayMat) {
|
|
183
|
+
this._overlayMat.dispose();
|
|
184
|
+
this._overlayMat = null;
|
|
185
|
+
}
|
|
186
|
+
_rollupPluginBabelHelpers.superPropGet(IoOutlineManager, "dispose", this, 3)([]);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
190
|
+
// PRIVATE
|
|
191
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
/** Lazy-initialise GPU resources on first use. */
|
|
194
|
+
}, {
|
|
195
|
+
key: "_ensureResources",
|
|
196
|
+
value: function _ensureResources() {
|
|
197
|
+
if (this._maskTarget) return;
|
|
198
|
+
var renderer = this.sceneViewer.renderer;
|
|
199
|
+
if (!renderer) return;
|
|
200
|
+
var size = renderer.getSize(new THREE__namespace.Vector2());
|
|
201
|
+
var w = size.x;
|
|
202
|
+
var h = size.y;
|
|
203
|
+
|
|
204
|
+
// Private render target — receives the flat white device silhouette
|
|
205
|
+
this._maskTarget = new THREE__namespace.WebGLRenderTarget(w, h);
|
|
206
|
+
|
|
207
|
+
// Flat white material used during the mask pass
|
|
208
|
+
this._maskMat = new THREE__namespace.MeshBasicMaterial({
|
|
209
|
+
color: 0xffffff
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Screen-space overlay: reads the mask and draws only the outline pixels
|
|
213
|
+
this._overlayMat = new THREE__namespace.ShaderMaterial({
|
|
214
|
+
uniforms: {
|
|
215
|
+
tMask: {
|
|
216
|
+
value: this._maskTarget.texture
|
|
217
|
+
},
|
|
218
|
+
uInvSize: {
|
|
219
|
+
value: new THREE__namespace.Vector2(1 / w, 1 / h)
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
vertexShader: VERT_SHADER,
|
|
223
|
+
fragmentShader: FRAG_SHADER,
|
|
224
|
+
transparent: true,
|
|
225
|
+
depthTest: false,
|
|
226
|
+
depthWrite: false
|
|
227
|
+
});
|
|
228
|
+
var quad = new THREE__namespace.Mesh(new THREE__namespace.PlaneGeometry(2, 2), this._overlayMat);
|
|
229
|
+
quad.frustumCulled = false;
|
|
230
|
+
this._overlayScene = new THREE__namespace.Scene();
|
|
231
|
+
this._overlayScene.add(quad);
|
|
232
|
+
this._overlayCamera = new THREE__namespace.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Remove OUTLINE_LAYER from all tracked meshes and clear the list. */
|
|
236
|
+
}, {
|
|
237
|
+
key: "_clearLayeredMeshes",
|
|
238
|
+
value: function _clearLayeredMeshes() {
|
|
239
|
+
var _iterator = _rollupPluginBabelHelpers.createForOfIteratorHelper(this._layeredMeshes),
|
|
240
|
+
_step;
|
|
241
|
+
try {
|
|
242
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
243
|
+
var _step$value = _step.value,
|
|
244
|
+
mesh = _step$value.mesh,
|
|
245
|
+
originalMask = _step$value.originalMask;
|
|
246
|
+
mesh.layers.mask = originalMask;
|
|
247
|
+
}
|
|
248
|
+
} catch (err) {
|
|
249
|
+
_iterator.e(err);
|
|
250
|
+
} finally {
|
|
251
|
+
_iterator.f();
|
|
252
|
+
}
|
|
253
|
+
this._layeredMeshes = [];
|
|
254
|
+
}
|
|
255
|
+
}]);
|
|
256
|
+
}(baseDisposable.BaseDisposable);
|
|
257
|
+
|
|
258
|
+
exports.IoOutlineManager = IoOutlineManager;
|