@pascal-app/viewer 1.0.5 → 1.0.7

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.
Files changed (199) hide show
  1. package/dist/components/viewer/daylight-atmosphere.d.ts +9 -0
  2. package/dist/components/viewer/daylight-atmosphere.d.ts.map +1 -0
  3. package/dist/components/viewer/daylight-atmosphere.js +315 -0
  4. package/dist/components/viewer/drone-controller.d.ts +15 -0
  5. package/dist/components/viewer/drone-controller.d.ts.map +1 -0
  6. package/dist/components/viewer/drone-controller.js +194 -0
  7. package/dist/components/viewer/glb-interactive.d.ts +4 -18
  8. package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
  9. package/dist/components/viewer/glb-interactive.js +32 -268
  10. package/dist/components/viewer/glb-scene-motion.d.ts +8 -0
  11. package/dist/components/viewer/glb-scene-motion.d.ts.map +1 -0
  12. package/dist/components/viewer/glb-scene-motion.js +57 -0
  13. package/dist/components/viewer/glb-scene.d.ts +3 -1
  14. package/dist/components/viewer/glb-scene.d.ts.map +1 -1
  15. package/dist/components/viewer/glb-scene.js +289 -49
  16. package/dist/components/viewer/glb-selection-callout.d.ts +17 -0
  17. package/dist/components/viewer/glb-selection-callout.d.ts.map +1 -0
  18. package/dist/components/viewer/glb-selection-callout.js +54 -0
  19. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
  20. package/dist/components/viewer/glb-walkthrough-controller.js +6 -3
  21. package/dist/components/viewer/index.d.ts +5 -0
  22. package/dist/components/viewer/index.d.ts.map +1 -1
  23. package/dist/components/viewer/index.js +30 -6
  24. package/dist/components/viewer/lights.d.ts.map +1 -1
  25. package/dist/components/viewer/lights.js +192 -5
  26. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  27. package/dist/components/viewer/post-processing.js +150 -94
  28. package/dist/components/viewer/scene-environment.js +4 -5
  29. package/dist/components/viewer/selection-callout.d.ts +51 -0
  30. package/dist/components/viewer/selection-callout.d.ts.map +1 -0
  31. package/dist/components/viewer/selection-callout.js +197 -0
  32. package/dist/components/viewer/use-daylight-rooms.d.ts +13 -0
  33. package/dist/components/viewer/use-daylight-rooms.d.ts.map +1 -0
  34. package/dist/components/viewer/use-daylight-rooms.js +236 -0
  35. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  36. package/dist/components/viewer/viewer-camera.js +12 -1
  37. package/dist/index.d.ts +21 -7
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +23 -7
  40. package/dist/lib/daylight/ground.d.ts +21 -0
  41. package/dist/lib/daylight/ground.d.ts.map +1 -0
  42. package/dist/lib/daylight/ground.js +53 -0
  43. package/dist/lib/daylight/location.d.ts +32 -0
  44. package/dist/lib/daylight/location.d.ts.map +1 -0
  45. package/dist/lib/daylight/location.js +74 -0
  46. package/dist/lib/daylight/look.d.ts +33 -0
  47. package/dist/lib/daylight/look.d.ts.map +1 -0
  48. package/dist/lib/daylight/look.js +13 -0
  49. package/dist/lib/daylight/material-hooks.d.ts +2 -0
  50. package/dist/lib/daylight/material-hooks.d.ts.map +1 -0
  51. package/dist/lib/daylight/material-hooks.js +150 -0
  52. package/dist/lib/daylight/room-context.d.ts +12 -0
  53. package/dist/lib/daylight/room-context.d.ts.map +1 -0
  54. package/dist/lib/daylight/room-context.js +53 -0
  55. package/dist/lib/daylight/room-scene.d.ts +54 -0
  56. package/dist/lib/daylight/room-scene.d.ts.map +1 -0
  57. package/dist/lib/daylight/room-scene.js +356 -0
  58. package/dist/lib/daylight/rooms.d.ts +262 -0
  59. package/dist/lib/daylight/rooms.d.ts.map +1 -0
  60. package/dist/lib/daylight/rooms.js +1223 -0
  61. package/dist/lib/daylight/runtime.d.ts +33 -0
  62. package/dist/lib/daylight/runtime.d.ts.map +1 -0
  63. package/dist/lib/daylight/runtime.js +42 -0
  64. package/dist/lib/daylight/sky.d.ts +26 -0
  65. package/dist/lib/daylight/sky.d.ts.map +1 -0
  66. package/dist/lib/daylight/sky.js +217 -0
  67. package/dist/lib/daylight/solar.d.ts +46 -0
  68. package/dist/lib/daylight/solar.d.ts.map +1 -0
  69. package/dist/lib/daylight/solar.js +156 -0
  70. package/dist/lib/device-tier.d.ts +29 -0
  71. package/dist/lib/device-tier.d.ts.map +1 -0
  72. package/dist/lib/device-tier.js +71 -0
  73. package/dist/lib/dispose-object3d.d.ts.map +1 -1
  74. package/dist/lib/dispose-object3d.js +4 -2
  75. package/dist/lib/drone-flight.d.ts +44 -0
  76. package/dist/lib/drone-flight.d.ts.map +1 -0
  77. package/dist/lib/drone-flight.js +121 -0
  78. package/dist/lib/editor-highlight.d.ts +16 -0
  79. package/dist/lib/editor-highlight.d.ts.map +1 -0
  80. package/dist/lib/editor-highlight.js +33 -0
  81. package/dist/lib/foundation-materials.d.ts.map +1 -1
  82. package/dist/lib/foundation-materials.js +2 -4
  83. package/dist/lib/glb-merge.d.ts +23 -0
  84. package/dist/lib/glb-merge.d.ts.map +1 -0
  85. package/dist/lib/glb-merge.js +184 -0
  86. package/dist/lib/hardware-geometry.d.ts +21 -0
  87. package/dist/lib/hardware-geometry.d.ts.map +1 -0
  88. package/dist/lib/hardware-geometry.js +283 -0
  89. package/dist/lib/layer-pass.d.ts +2 -0
  90. package/dist/lib/layer-pass.d.ts.map +1 -1
  91. package/dist/lib/layer-pass.js +20 -0
  92. package/dist/lib/materials.d.ts.map +1 -1
  93. package/dist/lib/materials.js +50 -23
  94. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  95. package/dist/lib/merged-outline-node.js +7 -1
  96. package/dist/lib/occlusion-culler.d.ts +34 -0
  97. package/dist/lib/occlusion-culler.d.ts.map +1 -0
  98. package/dist/lib/occlusion-culler.js +698 -0
  99. package/dist/lib/post-processing-resources.d.ts +4 -0
  100. package/dist/lib/post-processing-resources.d.ts.map +1 -1
  101. package/dist/lib/post-processing-resources.js +5 -0
  102. package/dist/lib/readback-rows.d.ts +14 -0
  103. package/dist/lib/readback-rows.d.ts.map +1 -0
  104. package/dist/lib/readback-rows.js +31 -0
  105. package/dist/lib/reveal-pose.d.ts.map +1 -1
  106. package/dist/lib/reveal-pose.js +3 -0
  107. package/dist/lib/scene-matrices.d.ts +12 -0
  108. package/dist/lib/scene-matrices.d.ts.map +1 -0
  109. package/dist/lib/scene-matrices.js +287 -0
  110. package/dist/lib/scene-pass-mrt.d.ts +5 -2
  111. package/dist/lib/scene-pass-mrt.d.ts.map +1 -1
  112. package/dist/lib/scene-pass-mrt.js +7 -2
  113. package/dist/lib/scene-themes.d.ts +0 -2
  114. package/dist/lib/scene-themes.d.ts.map +1 -1
  115. package/dist/lib/scene-themes.js +0 -2
  116. package/dist/lib/scene-visibility.d.ts +2 -0
  117. package/dist/lib/scene-visibility.d.ts.map +1 -1
  118. package/dist/lib/scene-visibility.js +12 -3
  119. package/dist/lib/site-surfaces.d.ts +1 -8
  120. package/dist/lib/site-surfaces.d.ts.map +1 -1
  121. package/dist/lib/site-surfaces.js +0 -19
  122. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  123. package/dist/lib/snapshot-pipeline.js +24 -40
  124. package/dist/lib/texture-reference.d.ts.map +1 -1
  125. package/dist/lib/texture-reference.js +10 -8
  126. package/dist/lib/underground-ambiance.d.ts +0 -2
  127. package/dist/lib/underground-ambiance.d.ts.map +1 -1
  128. package/dist/lib/underground-ambiance.js +0 -2
  129. package/dist/store/use-item-light-pool.d.ts +3 -6
  130. package/dist/store/use-item-light-pool.d.ts.map +1 -1
  131. package/dist/store/use-item-light-pool.js +12 -0
  132. package/dist/store/use-viewer.d.ts +72 -15
  133. package/dist/store/use-viewer.d.ts.map +1 -1
  134. package/dist/store/use-viewer.js +103 -18
  135. package/dist/systems/ceiling/ceiling-system.d.ts +5 -0
  136. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  137. package/dist/systems/ceiling/ceiling-system.js +11 -1
  138. package/dist/systems/door/door-system.d.ts.map +1 -1
  139. package/dist/systems/door/door-system.js +501 -145
  140. package/dist/systems/door/leaf-profile.d.ts +11 -0
  141. package/dist/systems/door/leaf-profile.d.ts.map +1 -0
  142. package/dist/systems/door/leaf-profile.js +140 -0
  143. package/dist/systems/interactive/callout-actions.d.ts +72 -0
  144. package/dist/systems/interactive/callout-actions.d.ts.map +1 -0
  145. package/dist/systems/interactive/callout-actions.js +212 -0
  146. package/dist/systems/interactive/interactive-system.d.ts +5 -1
  147. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  148. package/dist/systems/interactive/interactive-system.js +44 -121
  149. package/dist/systems/interactive/opening-settled.d.ts +16 -0
  150. package/dist/systems/interactive/opening-settled.d.ts.map +1 -0
  151. package/dist/systems/interactive/opening-settled.js +53 -0
  152. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  153. package/dist/systems/item-light/item-light-system.js +22 -214
  154. package/dist/systems/item-light/pooled-lights.d.ts +70 -0
  155. package/dist/systems/item-light/pooled-lights.d.ts.map +1 -0
  156. package/dist/systems/item-light/pooled-lights.js +449 -0
  157. package/dist/systems/level/level-system.d.ts.map +1 -1
  158. package/dist/systems/level/level-system.js +19 -1
  159. package/dist/systems/roof/roof-material-slots.d.ts +14 -0
  160. package/dist/systems/roof/roof-material-slots.d.ts.map +1 -0
  161. package/dist/systems/roof/roof-material-slots.js +13 -0
  162. package/dist/systems/roof/roof-materials.d.ts +25 -2
  163. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  164. package/dist/systems/roof/roof-materials.js +85 -26
  165. package/dist/systems/roof/roof-system.d.ts +34 -1
  166. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  167. package/dist/systems/roof/roof-system.js +477 -105
  168. package/dist/systems/wall/opening-casing-geometry.d.ts +14 -0
  169. package/dist/systems/wall/opening-casing-geometry.d.ts.map +1 -0
  170. package/dist/systems/wall/opening-casing-geometry.js +102 -0
  171. package/dist/systems/wall/opening-cutout-geometry.d.ts +9 -0
  172. package/dist/systems/wall/opening-cutout-geometry.d.ts.map +1 -1
  173. package/dist/systems/wall/opening-cutout-geometry.js +7 -3
  174. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  175. package/dist/systems/wall/wall-materials.js +28 -35
  176. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  177. package/dist/systems/wall/wall-system.js +34 -19
  178. package/dist/systems/wall/wall-t-junctions.d.ts +15 -0
  179. package/dist/systems/wall/wall-t-junctions.d.ts.map +1 -0
  180. package/dist/systems/wall/wall-t-junctions.js +149 -0
  181. package/dist/systems/window/window-animation-system.d.ts +3 -12
  182. package/dist/systems/window/window-animation-system.d.ts.map +1 -1
  183. package/dist/systems/window/window-animation-system.js +37 -122
  184. package/dist/systems/window/window-pose.d.ts +31 -0
  185. package/dist/systems/window/window-pose.d.ts.map +1 -0
  186. package/dist/systems/window/window-pose.js +152 -0
  187. package/dist/systems/window/window-roller-shutter.d.ts +8 -0
  188. package/dist/systems/window/window-roller-shutter.d.ts.map +1 -0
  189. package/dist/systems/window/window-roller-shutter.js +64 -0
  190. package/dist/systems/window/window-system.d.ts +2 -11
  191. package/dist/systems/window/window-system.d.ts.map +1 -1
  192. package/dist/systems/window/window-system.js +470 -303
  193. package/package.json +6 -6
  194. package/dist/systems/interactive/control-widget.d.ts +0 -10
  195. package/dist/systems/interactive/control-widget.d.ts.map +0 -1
  196. package/dist/systems/interactive/control-widget.js +0 -35
  197. package/dist/systems/interactive/procedural-controls.d.ts +0 -13
  198. package/dist/systems/interactive/procedural-controls.d.ts.map +0 -1
  199. package/dist/systems/interactive/procedural-controls.js +0 -17
@@ -0,0 +1,283 @@
1
+ import { BoxGeometry, CapsuleGeometry, CatmullRomCurve3, CylinderGeometry, LatheGeometry, SphereGeometry, TubeGeometry, Vector2, Vector3, } from 'three';
2
+ import { RoundedBoxGeometry } from 'three/addons/geometries/RoundedBoxGeometry.js';
3
+ import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
4
+ const cache = new Map();
5
+ const mm = (value) => Math.round(value * 1000);
6
+ function cached(name, dimensions, build) {
7
+ const sizes = dimensions.map(mm);
8
+ const key = `${name}:${sizes.join(':')}`;
9
+ let geometry = cache.get(key);
10
+ if (!geometry) {
11
+ geometry = build(...sizes.map((size) => size / 1000));
12
+ geometry.userData.shared = true;
13
+ cache.set(key, geometry);
14
+ }
15
+ return geometry;
16
+ }
17
+ function scaleUVs(geometry, u, v) {
18
+ const uv = geometry.getAttribute('uv');
19
+ for (let i = 0; i < uv.count; i++)
20
+ uv.setXY(i, uv.getX(i) * u, uv.getY(i) * v);
21
+ }
22
+ function lathe(profile, segments) {
23
+ const geometry = new LatheGeometry(profile.map(([r, y]) => new Vector2(r, y)), segments);
24
+ geometry.normalizeNormals();
25
+ const uv = geometry.getAttribute('uv');
26
+ let distance = 0;
27
+ for (let j = 0; j < profile.length; j++) {
28
+ const [r, y] = profile[j];
29
+ if (j > 0) {
30
+ const [previousR, previousY] = profile[j - 1];
31
+ distance += Math.hypot(r - previousR, y - previousY);
32
+ }
33
+ for (let i = 0; i <= segments; i++) {
34
+ uv.setXY(i * profile.length + j, (i / segments) * 2 * Math.PI * r, distance);
35
+ }
36
+ }
37
+ return geometry;
38
+ }
39
+ function cylinder(radius, length, segments) {
40
+ const geometry = new CylinderGeometry(radius, radius, length, segments);
41
+ const uv = geometry.getAttribute('uv');
42
+ const position = geometry.getAttribute('position');
43
+ const sideVertices = (segments + 1) * 2;
44
+ for (let i = 0; i < uv.count; i++) {
45
+ if (i < sideVertices) {
46
+ uv.setXY(i, uv.getX(i) * 2 * Math.PI * radius, uv.getY(i) * length);
47
+ }
48
+ else {
49
+ uv.setXY(i, position.getX(i), position.getZ(i));
50
+ }
51
+ }
52
+ return geometry;
53
+ }
54
+ function merge(parts) {
55
+ const indexed = parts.every((part) => part.index !== null);
56
+ const normalized = parts.map((part) => (!indexed && part.index ? part.toNonIndexed() : part));
57
+ const geometry = mergeGeometries(normalized);
58
+ for (const part of normalized)
59
+ if (!parts.includes(part))
60
+ part.dispose();
61
+ for (const part of parts)
62
+ part.dispose();
63
+ return geometry;
64
+ }
65
+ function hemisphere(radius, radialSegments, heightSegments) {
66
+ const geometry = new SphereGeometry(radius, radialSegments, heightSegments, 0, 2 * Math.PI, 0, Math.PI / 2);
67
+ scaleUVs(geometry, 2 * Math.PI * radius, (Math.PI * radius) / 2);
68
+ return geometry;
69
+ }
70
+ /** Origin is the spindle on the mounting face; the grip points along +X, out along +Z. */
71
+ function buildLever(curved, plate = false) {
72
+ return cached(plate ? 'lever-plate' : curved ? 'lever-curved' : 'lever-straight', [], () => {
73
+ const faceFillet = 0.0005;
74
+ const faceRadius = 0.0245 - faceFillet * (Math.SQRT2 - 1);
75
+ const profile = [
76
+ [0, 0],
77
+ [0.026, 0],
78
+ [0.026, 0.0075],
79
+ ];
80
+ for (const angle of [Math.PI / 4, Math.PI / 2]) {
81
+ profile.push([
82
+ faceRadius + faceFillet * Math.cos(angle),
83
+ 0.0085 + faceFillet * Math.sin(angle),
84
+ ]);
85
+ }
86
+ profile.push([0, 0.009]);
87
+ const rose = plate
88
+ ? roundedBox(0.04, 0.22, 0.006, 0.003).clone().translate(0, 0, 0.003)
89
+ : lathe(profile, 32).rotateX(Math.PI / 2);
90
+ const neck = cylinder(0.01, 0.037, 16)
91
+ .rotateX(Math.PI / 2)
92
+ .translate(0, 0, 0.009 + 0.037 / 2);
93
+ const neckProfile = [];
94
+ for (let i = 0; i <= 4; i++) {
95
+ const angle = (i * Math.PI) / 8;
96
+ neckProfile.push([0.009 + 0.001 * Math.cos(angle), 0.046 + 0.001 * Math.sin(angle)]);
97
+ }
98
+ neckProfile.push([0, 0.047]);
99
+ const fillet = lathe(neckProfile, 16);
100
+ const filletNormals = fillet.getAttribute('normal');
101
+ // Match the cylinder tangent exactly rather than averaging the first fillet chord.
102
+ for (let i = 0; i <= 16; i++) {
103
+ const phi = (i * 2 * Math.PI) / 16;
104
+ for (let j = 0; j < neckProfile.length; j++) {
105
+ const angle = (Math.min(j, 4) * Math.PI) / 8;
106
+ filletNormals.setXYZ(i * neckProfile.length + j, Math.cos(angle) * Math.sin(phi), Math.sin(angle), Math.cos(angle) * Math.cos(phi));
107
+ }
108
+ }
109
+ fillet.rotateX(Math.PI / 2);
110
+ const curve = new CatmullRomCurve3((curved
111
+ ? [
112
+ [0, 0],
113
+ [0.01, 0],
114
+ [0.02, 0],
115
+ [0.035, 0],
116
+ [0.065, -0.004],
117
+ [0.095, -0.011],
118
+ [0.105, -0.012],
119
+ [0.1155, -0.012],
120
+ ]
121
+ : [
122
+ [0, 0],
123
+ [0.1155, 0],
124
+ ]).map(([x, y]) => new Vector3(x, y, 0.047)));
125
+ const grip = new TubeGeometry(curve, curved ? 24 : 2, 0.0095, 16, false);
126
+ scaleUVs(grip, curve.getLength(), 2 * Math.PI * 0.0095);
127
+ const cap = hemisphere(0.0095, 12, 6)
128
+ .rotateZ(-Math.PI / 2)
129
+ .translate(0.1155, curved ? -0.012 : 0, 0.047);
130
+ // Close the heel where the horizontal grip meets the vertical neck.
131
+ const heel = hemisphere(0.0095, 12, 6)
132
+ .rotateZ(Math.PI / 2)
133
+ .translate(0, 0, 0.047);
134
+ if (plate)
135
+ for (const part of [neck, fillet, grip, cap, heel])
136
+ part.translate(0, 0, -0.003);
137
+ return merge([rose, neck, fillet, grip, cap, heel]);
138
+ });
139
+ }
140
+ export function leverOnRose() {
141
+ return buildLever(false);
142
+ }
143
+ export function leverOnRoseCurved() {
144
+ return buildLever(true);
145
+ }
146
+ export function leverOnPlate() {
147
+ return buildLever(false, true);
148
+ }
149
+ /** Origin at the mounting face; a 56 mm ball on a 20 mm neck and 56 mm rose. */
150
+ export function knobOnRose() {
151
+ return cached('knob', [], () => {
152
+ const rose = cylinder(0.028, 0.006, 32)
153
+ .rotateX(Math.PI / 2)
154
+ .translate(0, 0, 0.003);
155
+ const neck = cylinder(0.01, 0.025, 16)
156
+ .rotateX(Math.PI / 2)
157
+ .translate(0, 0, 0.0185);
158
+ const ball = new SphereGeometry(0.028, 32, 16);
159
+ scaleUVs(ball, 2 * Math.PI * 0.028, Math.PI * 0.028);
160
+ ball.translate(0, 0, 0.052);
161
+ return merge([rose, neck, ball]);
162
+ });
163
+ }
164
+ /** Origin at the face; a 40 × 120 mm open cup with a 3 mm rim, recessed 12 mm. */
165
+ export function flushPull() {
166
+ return cached('flush-pull', [], () => {
167
+ const parts = [];
168
+ const box = (w, h, d, x, y, z) => {
169
+ const geometry = new BoxGeometry(w, h, d);
170
+ boxUVs(geometry, w, h, d);
171
+ parts.push(geometry.translate(x, y, z));
172
+ };
173
+ box(0.04, 0.12, 0.002, 0, 0, -0.011);
174
+ for (const side of [-1, 1]) {
175
+ box(0.002, 0.12, 0.01, side * 0.019, 0, -0.005);
176
+ box(0.036, 0.002, 0.01, 0, side * 0.059, -0.005);
177
+ box(0.003, 0.126, 0.001, side * 0.0215, 0, 0.0005);
178
+ box(0.04, 0.003, 0.001, 0, side * 0.0615, 0.0005);
179
+ }
180
+ return merge(parts);
181
+ });
182
+ }
183
+ function hingePart(leaf, diameter, height) {
184
+ return cached(leaf ? 'hinge-leaf' : 'hinge-jamb', [diameter, height], (d, h) => {
185
+ const radialScale = d / 0.014;
186
+ const heightScale = h / 0.103;
187
+ const radius = d / 2;
188
+ const plateW = 0.0025 * radialScale;
189
+ const plateH = 0.089 * heightScale;
190
+ const plateD = 0.028 * radialScale;
191
+ const plate = new BoxGeometry(plateW, plateH, plateD);
192
+ boxUVs(plate, plateW, plateH, plateD);
193
+ plate.translate(leaf ? plateW / 2 : -plateW / 2, 0, -plateD / 2);
194
+ const parts = [plate];
195
+ for (let i = leaf ? 0 : 1; i < 5; i += 2) {
196
+ parts.push(cylinder(radius, 0.017 * heightScale, 16).translate(0, (-0.036 + i * 0.018) * heightScale, 0));
197
+ }
198
+ if (leaf) {
199
+ for (const side of [-1, 1]) {
200
+ const cap = hemisphere(radius, 12, d <= 0.01 ? 2 : 3);
201
+ const capScale = heightScale / radialScale;
202
+ cap.scale(1, capScale, 1);
203
+ const uv = cap.getAttribute('uv');
204
+ for (let i = 0; i < uv.count; i++)
205
+ uv.setY(i, uv.getY(i) * capScale);
206
+ if (side < 0)
207
+ cap.rotateX(Math.PI);
208
+ parts.push(cap.translate(0, side * 0.0445 * heightScale, 0));
209
+ }
210
+ }
211
+ return merge(parts);
212
+ });
213
+ }
214
+ /** Odd knuckles and plate on the leaf edge (+X), extending into the rebate (-Z). */
215
+ export function hingeLeafPart(diameter = 0.014, height = 0.103) {
216
+ return hingePart(true, diameter, height);
217
+ }
218
+ /** Even knuckles and plate on the jamb face (-X), extending into the rebate (-Z). */
219
+ export function hingeJambPart(diameter = 0.014, height = 0.103) {
220
+ return hingePart(false, diameter, height);
221
+ }
222
+ /** Origin on the mounting face; bar along Y, projecting along +Z. */
223
+ export function pullBar(doorScale = false) {
224
+ return cached(doorScale ? 'door-pull' : 'pull', [], () => {
225
+ if (doorScale) {
226
+ const bar = cylinder(0.0125, 0.4, 32).translate(0, 0, 0.05);
227
+ return merge([
228
+ bar,
229
+ ...[-0.15, 0.15].map((y) => cylinder(0.008, 0.05, 16)
230
+ .rotateX(Math.PI / 2)
231
+ .translate(0, y, 0.025)),
232
+ ]);
233
+ }
234
+ const bar = new CapsuleGeometry(0.009, 0.102, 6, 12);
235
+ scaleUVs(bar, 2 * Math.PI * 0.009, 0.102 + Math.PI * 0.009);
236
+ bar.translate(0, 0, 0.022);
237
+ const parts = [bar];
238
+ for (const y of [-0.02, 0.02]) {
239
+ parts.push(cylinder(0.005, 0.02, 16)
240
+ .rotateX(Math.PI / 2)
241
+ .translate(0, y, 0.01));
242
+ }
243
+ return merge(parts);
244
+ });
245
+ }
246
+ function boxUVs(geometry, width, height, depth) {
247
+ const uv = geometry.getAttribute('uv');
248
+ const faceSizes = [
249
+ [depth, height],
250
+ [depth, height],
251
+ [width, depth],
252
+ [width, depth],
253
+ [width, height],
254
+ [width, height],
255
+ ];
256
+ const verticesPerFace = uv.count / 6;
257
+ for (let i = 0; i < uv.count; i++) {
258
+ const [u, v] = faceSizes[Math.floor(i / verticesPerFace)];
259
+ uv.setXY(i, uv.getX(i) * u, uv.getY(i) * v);
260
+ }
261
+ }
262
+ export function roundedBox(w, h, d, r = Math.min(0.004, w / 4, h / 4, d / 4)) {
263
+ return cached('box', [w, h, d, r], (width, height, depth, radius) => {
264
+ const geometry = new RoundedBoxGeometry(width, height, depth, 1, radius);
265
+ boxUVs(geometry, width, height, depth);
266
+ return geometry;
267
+ });
268
+ }
269
+ /** A capped round bar along Y. */
270
+ export function tube(radius, length) {
271
+ return cached('tube', [2 * radius, length], (diameter, l) => cylinder(diameter / 2, l, 12));
272
+ }
273
+ export function sphere(radius) {
274
+ return cached('sphere', [2 * radius], (diameter) => {
275
+ const geometry = new SphereGeometry(diameter / 2, 12, 8);
276
+ scaleUVs(geometry, Math.PI * diameter, (Math.PI * diameter) / 2);
277
+ return geometry;
278
+ });
279
+ }
280
+ /** A wheel with its axle along Z. */
281
+ export function roller(radius, width) {
282
+ return cached('roller', [2 * radius, width], (diameter, w) => cylinder(diameter / 2, w, 20).rotateX(Math.PI / 2));
283
+ }
@@ -1,5 +1,7 @@
1
1
  import { type Camera, Object3D, type Scene } from 'three';
2
2
  import { type NodeFrame, PassNode } from 'three/webgpu';
3
+ export declare function getLayoutRevision(): number;
4
+ export declare function isLayoutObserved(scene: Object3D): boolean;
3
5
  export declare class LayerPassIndex {
4
6
  readonly source: Scene;
5
7
  private readonly members;
@@ -1 +1 @@
1
- {"version":3,"file":"layer-pass.d.ts","sourceRoot":"","sources":["../../src/lib/layer-pass.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAiB,QAAQ,EAAE,KAAK,KAAK,EAAW,MAAM,OAAO,CAAA;AACjF,OAAO,EAAE,KAAK,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAoBvD,qBAAa,cAAc;IAKvB,QAAQ,CAAC,MAAM,EAAE,KAAK;IAJxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAE3D,YACW,MAAM,EAAE,KAAK,EACtB,MAAM,EAAE,MAAM,EAAE,EASjB;IAED,QAAQ,CAAC,OAAO,EAAE,QAAQ,QAQzB;IAED,OAAO,CAAC,MAAM,CA2Cb;IAED,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE;;;MAkCvC;IAED,OAAO,SAGN;CACF;AAiBD,qBAAa,aAAc,SAAQ,QAAQ;IAMvC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAEtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,UAAU,CAAO;IAEzB,YACmB,KAAK,EAAE,cAAc,EACtC,MAAM,EAAE,MAAM,EACG,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAYpC;IAEQ,OAAO,SAGf;IAEQ,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CA+DjD;CACF"}
1
+ {"version":3,"file":"layer-pass.d.ts","sourceRoot":"","sources":["../../src/lib/layer-pass.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAiB,QAAQ,EAAE,KAAK,KAAK,EAAW,MAAM,OAAO,CAAA;AACjF,OAAO,EAAE,KAAK,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAYvD,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAEzD;AAkBD,qBAAa,cAAc;IAKvB,QAAQ,CAAC,MAAM,EAAE,KAAK;IAJxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAE3D,YACW,MAAM,EAAE,KAAK,EACtB,MAAM,EAAE,MAAM,EAAE,EAUjB;IAED,QAAQ,CAAC,OAAO,EAAE,QAAQ,QAQzB;IAED,OAAO,CAAC,MAAM,CA8Cb;IAED,OAAO,CAAC,MAAM;IAOd,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE;;;MAkCvC;IAED,OAAO,SAIN;CACF;AAiBD,qBAAa,aAAc,SAAQ,QAAQ;IAMvC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAEtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,UAAU,CAAO;IAEzB,YACmB,KAAK,EAAE,cAAc,EACtC,MAAM,EAAE,MAAM,EACG,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAYpC;IAEQ,OAAO,SAGf;IAEQ,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CA+DjD;CACF"}
@@ -5,6 +5,19 @@
5
5
  import { Object3D, Vector2 } from 'three';
6
6
  import { PassNode } from 'three/webgpu';
7
7
  const maskObserver = Symbol('LayerPassIndex.maskObserver');
8
+ /**
9
+ * Bumped on every observed mask change and child insertion or removal, so a per-frame walk can
10
+ * tell when what is drawable may have changed (scene-matrices.ts). Only meaningful for scenes
11
+ * with a live index ({@link isLayoutObserved}).
12
+ */
13
+ let layoutRevision = 0;
14
+ const observedScenes = new WeakSet();
15
+ export function getLayoutRevision() {
16
+ return layoutRevision;
17
+ }
18
+ export function isLayoutObserved(scene) {
19
+ return observedScenes.has(scene);
20
+ }
8
21
  function maskOwner(object) {
9
22
  const get = Object.getOwnPropertyDescriptor(object.layers, 'mask')?.get;
10
23
  return get?.[maskObserver];
@@ -22,6 +35,7 @@ export class LayerPassIndex {
22
35
  this.members.set(1 << layer, new Set());
23
36
  try {
24
37
  this.attach(source);
38
+ observedScenes.add(source);
25
39
  }
26
40
  catch (error) {
27
41
  this.dispose();
@@ -45,7 +59,11 @@ export class LayerPassIndex {
45
59
  throw new Error('LayerPassIndex requires one index per scene and unshared Layers');
46
60
  }
47
61
  let mask = layers.mask;
62
+ // Passes switch the camera's layers every frame; that changes nothing in the scene.
63
+ const isCamera = object.isCamera === true;
48
64
  const sync = () => {
65
+ if (!isCamera)
66
+ layoutRevision++;
49
67
  for (const [bit, members] of this.members) {
50
68
  if ((mask & bit) !== 0 && object !== this.source)
51
69
  members.add(object);
@@ -88,6 +106,7 @@ export class LayerPassIndex {
88
106
  this.attach(child);
89
107
  };
90
108
  detach(object) {
109
+ layoutRevision++;
91
110
  for (const child of object.children)
92
111
  this.detach(child);
93
112
  this.cleanups.get(object)?.();
@@ -136,6 +155,7 @@ export class LayerPassIndex {
136
155
  return { drawable, shadowLight };
137
156
  }
138
157
  dispose() {
158
+ observedScenes.delete(this.source);
139
159
  for (const cleanup of this.cleanups.values())
140
160
  cleanup();
141
161
  this.cleanups.clear();
@@ -1 +1 @@
1
- {"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { getMaterialPresetByRef, materialColorPaint, parseMaterialColor, parseMaterialRef, resolveMaterial, } from '@pascal-app/core';
2
2
  import { addAfterEffect, invalidate } from '@react-three/fiber';
3
3
  import * as THREE from 'three';
4
- import { float, mix, positionViewDirection, transformedNormalView } from 'three/tsl';
4
+ import { float, mix, normalView, positionViewDirection } from 'three/tsl';
5
5
  import { MeshLambertNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
6
6
  import { resolveCdnUrl } from './asset-url';
7
7
  import { isKtx2Url, ktx2Loader, whenKtx2Ready } from './ktx2-loader';
@@ -162,35 +162,41 @@ export function resolveTextureRepeat(repeat, scale) {
162
162
  }
163
163
  return [fallback, fallback];
164
164
  }
165
+ function textureOffset(texture) {
166
+ return texture.offset ?? [0, 0];
167
+ }
168
+ function getPlacementKey(texture) {
169
+ const [repeatX, repeatY] = resolveTextureRepeat(texture.repeat, texture.scale);
170
+ const [offsetX, offsetY] = textureOffset(texture);
171
+ return `${repeatX}x${repeatY}+${offsetX},${offsetY}`;
172
+ }
165
173
  export function getTextureKey(material) {
166
174
  const texture = material?.texture;
167
175
  if (!texture)
168
176
  return 'none';
169
- const [repeatX, repeatY] = resolveTextureRepeat(texture.repeat, texture.scale);
170
- return `${texture.url}-${repeatX}x${repeatY}`;
177
+ const normal = texture.normalMap ? `-n:${texture.normalMap}*${texture.normalScale ?? 1}` : '';
178
+ const bump = texture.bumpMap ? `-b:${texture.bumpMap}*${texture.bumpScale ?? 1}` : '';
179
+ return `${texture.url}-${getPlacementKey(texture)}${normal}${bump}`;
171
180
  }
172
- function getTexture(material) {
173
- const textureConfig = material?.texture;
174
- if (!textureConfig?.url)
175
- return undefined;
176
- const cacheKey = getTextureKey(material);
181
+ /** One map of a native material's texture; every map shares the albedo's repeat and offset. */
182
+ function getTexture(textureConfig, url, slot) {
183
+ const cacheKey = `${url}-${getPlacementKey(textureConfig)}-${slot}`;
177
184
  const cached = textureCache.get(cacheKey);
178
185
  if (cached)
179
186
  return cached;
180
- const resolvedUrl = /^(?:asset|blob|data):/.test(textureConfig.url)
181
- ? textureConfig.url
182
- : (resolveCdnUrl(textureConfig.url) ?? textureConfig.url);
187
+ const resolvedUrl = /^(?:asset|blob|data):/.test(url) ? url : (resolveCdnUrl(url) ?? url);
183
188
  const texture = loadTexture(resolvedUrl, cacheKey);
184
189
  texture.wrapS = THREE.RepeatWrapping;
185
190
  texture.wrapT = THREE.RepeatWrapping;
186
191
  const [repeatX, repeatY] = resolveTextureRepeat(textureConfig.repeat, textureConfig.scale);
187
192
  texture.repeat.set(repeatX, repeatY);
193
+ texture.offset.set(...textureOffset(textureConfig));
188
194
  texture.updateMatrix();
189
- texture.colorSpace = THREE.SRGBColorSpace;
195
+ texture.colorSpace = slot === 'map' ? THREE.SRGBColorSpace : THREE.NoColorSpace;
190
196
  stampPascalTextureRef(texture, {
191
197
  kind: 'project-asset',
192
198
  src: resolvedUrl,
193
- slot: 'map',
199
+ slot,
194
200
  });
195
201
  textureCache.set(cacheKey, texture);
196
202
  return texture;
@@ -272,9 +278,14 @@ async function loadPresetTexture(path, props, slot) {
272
278
  // materials can be created while a capture canvas's renderer is still
273
279
  // initializing, and failing here would cache the material permanently
274
280
  // texture-less (white).
275
- const load = isKtx2Url(resolvedPath)
276
- ? whenKtx2Ready().then(() => ktx2Loader.loadAsync(resolvedPath))
277
- : textureLoader.loadAsync(resolvedPath);
281
+ const loadFrom = (url) => isKtx2Url(url)
282
+ ? whenKtx2Ready().then(() => ktx2Loader.loadAsync(url))
283
+ : textureLoader.loadAsync(url);
284
+ // A catalog map ships in the app's public folder before the asset CDN has it
285
+ // (a branch, a deploy ahead of the CDN): a CDN miss retries the app's own copy.
286
+ const load = resolvedPath !== path && path.startsWith('/')
287
+ ? loadFrom(resolvedPath).catch(() => loadFrom(path))
288
+ : loadFrom(resolvedPath);
278
289
  const promise = load
279
290
  .then((texture) => {
280
291
  applyTextureProperties(texture, props, slot);
@@ -383,7 +394,9 @@ export function materialCastsShadow(material) {
383
394
  * This is what makes glass read as a surface instead of a flat blue tint.
384
395
  */
385
396
  function applyGlassFresnel(material) {
386
- const facing = transformedNormalView.dot(positionViewDirection).clamp(0, 1);
397
+ // The daylight look swaps in its tuned glass for flagged materials (lib/daylight/material-hooks).
398
+ material.userData.isGlass = true;
399
+ const facing = normalView.dot(positionViewDirection).clamp(0, 1);
387
400
  const fresnel = facing.oneMinus().pow(3);
388
401
  material.opacityNode = mix(float(material.opacity), float(0.92), fresnel);
389
402
  material.envMapIntensity = 1.4;
@@ -449,22 +462,36 @@ export function createMaterial(material, shading = 'rendered') {
449
462
  if (materialCache.has(cacheKey)) {
450
463
  return materialCache.get(cacheKey);
451
464
  }
452
- const map = getTexture(material);
465
+ const texture = material?.texture?.url ? material.texture : undefined;
453
466
  const materialParams = {
454
467
  color: props.color,
455
468
  opacity: props.opacity,
456
469
  transparent: props.transparent,
457
470
  side: sideMap[props.side],
458
471
  };
459
- if (map)
460
- materialParams.map = map;
461
- const threeMaterial = shading === 'solid'
462
- ? new MeshLambertNodeMaterial(materialParams)
463
- : new MeshStandardNodeMaterial({
472
+ if (texture)
473
+ materialParams.map = getTexture(texture, texture.url, 'map');
474
+ let threeMaterial;
475
+ if (shading === 'solid') {
476
+ threeMaterial = new MeshLambertNodeMaterial(materialParams);
477
+ }
478
+ else {
479
+ const standard = new MeshStandardNodeMaterial({
464
480
  ...materialParams,
465
481
  roughness: props.roughness,
466
482
  metalness: props.metalness,
467
483
  });
484
+ if (texture?.normalMap) {
485
+ standard.normalMap = getTexture(texture, texture.normalMap, 'normalMap');
486
+ const normalScale = texture.normalScale ?? 1;
487
+ standard.normalScale.set(normalScale, normalScale);
488
+ }
489
+ if (texture?.bumpMap) {
490
+ standard.bumpMap = getTexture(texture, texture.bumpMap, 'bumpMap');
491
+ standard.bumpScale = texture.bumpScale ?? 1;
492
+ }
493
+ threeMaterial = standard;
494
+ }
468
495
  // Explicit source transparency keeps its opacity; the glass preset owns this styling.
469
496
  if (glassPreset)
470
497
  maybeApplyGlassFresnel(threeMaterial);
@@ -1 +1 @@
1
- {"version":3,"file":"merged-outline-node.d.ts","sourceRoot":"","sources":["../../src/lib/merged-outline-node.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"merged-outline-node.d.ts","sourceRoot":"","sources":["../../src/lib/merged-outline-node.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AAEH,OAAO,EAIL,QAAQ,EAKT,MAAM,OAAO,CAAA;AA2Bd,OAAO,EAML,QAAQ,EACT,MAAM,cAAc,CAAA;AAmErB,qBAAa,iBAAkB,SAAQ,QAAQ;IAC7C,MAAM,KAAK,IAAI,WAEd;IAED,KAAK,EAAE,GAAG,CAAA;IACV,MAAM,EAAE,GAAG,CAAA;IACX,cAAc,EAAE,QAAQ,EAAE,CAAA;IAC1B,gBAAgB,EAAE,QAAQ,EAAE,CAAA;IAC5B,wBAAwB,EAAE,GAAG,CAAA;IAC7B,0BAA0B,EAAE,GAAG,CAAA;IAC/B,mBAAmB,EAAE,GAAG,CAAA;IACxB,qBAAqB,EAAE,GAAG,CAAA;IAC1B,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IAExB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAK;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAK;IAEtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAK;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAK;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAK;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAK;IACpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAK;IACrC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAK;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAK;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAK;IAEhC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAoB;IACzD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAc;IAC/C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAoB;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAc;IAC9C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAc;IAC/C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAoB;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAc;IAC9C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAE7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqC;IAC/D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqC;IAC/D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA6C;IACjF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA+B;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA+B;IAC7D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAiB;IACnD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAiB;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAO1C,OAAO,CAAC,qBAAqB,CAAQ;IACrC,OAAO,CAAC,qBAAqB,CAAQ;IAErC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;IAEnC,YACE,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,GAAG,EACX,MAAM,GAAE;QACN,cAAc,CAAC,EAAE,QAAQ,EAAE,CAAA;QAC3B,gBAAgB,CAAC,EAAE,QAAQ,EAAE,CAAA;QAC7B,oBAAoB,CAAC,EAAE,GAAG,CAAA;QAC1B,sBAAsB,CAAC,EAAE,GAAG,CAAA;QAC5B,eAAe,CAAC,EAAE,GAAG,CAAA;QACrB,iBAAiB,CAAC,EAAE,GAAG,CAAA;QACvB,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,sEAAsE;QACtE,cAAc,CAAC,EAAE,GAAG,CAAA;KAChB,EAgGP;IAED,IAAI,kBAAkB,QAErB;IACD,IAAI,iBAAiB,QAEpB;IACD,IAAI,oBAAoB,QAEvB;IACD,IAAI,mBAAmB,QAEtB;IAED,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAIpC;IAED,YAAY,CAAC,KAAK,EAAE,GAAG,QAmGtB;IAED,OAAO,CAAC,WAAW;IAyFnB,OAAO,CAAC,YAAY;IAkDpB,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,gBAAgB;IA8CxB,KAAK,CAAC,OAAO,EAAE,GAAG,OA+KjB;IAED,OAAO,SAkCN;IAED,OAAO,CAAC,WAAW;CAQpB;AAED,eAAO,MAAM,aAAa,UACjB,GAAG,UACF,GAAG,WACF,qBAAqB,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC,sBACZ,CAAA"}
@@ -15,6 +15,8 @@ import { DepthTexture, FloatType, Mesh, Object3D, RenderTarget, Scene, Sprite, V
15
15
  import { builtin, color, exp, Fn, float, floatBitsToUint, int, Loop, min, mul, nodeObject, orthographicDepthToViewZ, passTexture, perspectiveDepthToViewZ, positionView, reference, screenUV, texture, textureSize, uniform, uv, vec2, vec3, vec4, } from 'three/tsl';
16
16
  import { NodeMaterial, NodeUpdateType, QuadMesh, RendererUtils, SpriteNodeMaterial, TempNode, } from 'three/webgpu';
17
17
  import { hasDrawableGeometry } from './drawable-geometry';
18
+ import { BATCHED_LAYER, SCENE_LAYER } from './layers';
19
+ import { isSetAsideForRender } from './scene-matrices';
18
20
  const _quadMesh = new QuadMesh();
19
21
  const _size = new Vector2();
20
22
  const _BLUR_X = new Vector2(1.0, 0.0);
@@ -431,7 +433,8 @@ export class MergedOutlineNode extends TempNode {
431
433
  let visible = true;
432
434
  let attached = false;
433
435
  for (let ancestor = source; ancestor; ancestor = ancestor.parent) {
434
- if (!ancestor.visible)
436
+ // A subtree set aside for this render (nothing in it draws itself) is still shown.
437
+ if (!ancestor.visible && !isSetAsideForRender(ancestor))
435
438
  visible = false;
436
439
  if (ancestor === this.scene)
437
440
  attached = true;
@@ -440,6 +443,9 @@ export class MergedOutlineNode extends TempNode {
440
443
  proxy.material = source.material;
441
444
  proxy.matrixWorld.copy(source.matrixWorld);
442
445
  proxy.layers.mask = source.layers.mask;
446
+ // A batched source is drawn by its batch: outline it as if it drew itself.
447
+ if (proxy.layers.isEnabled(BATCHED_LAYER))
448
+ proxy.layers.enable(SCENE_LAYER);
443
449
  proxy.visible = visible && attached;
444
450
  proxy.renderOrder = source.renderOrder;
445
451
  proxy.morphTargetInfluences = source.morphTargetInfluences;
@@ -0,0 +1,34 @@
1
+ import * as THREE from 'three';
2
+ export type OcclusionStats = {
3
+ occluders: number;
4
+ triangles: number;
5
+ tested: number;
6
+ culled: number;
7
+ ms: number;
8
+ };
9
+ export type OcclusionCuller = {
10
+ /** Computes which drawn meshes the camera can't see. Returns the culled meshes. */
11
+ cull(camera: THREE.Camera): ReadonlySet<THREE.Mesh>;
12
+ /** Whether a mesh moved, appeared or vanished since the last call. */
13
+ changed(): boolean;
14
+ readonly stats: OcclusionStats;
15
+ dispose(): void;
16
+ };
17
+ export declare function createOcclusionCuller(root: THREE.Object3D): OcclusionCuller;
18
+ /** The part of three's drawing pipeline (`renderer.drawingPipeline`) the culler hooks into. */
19
+ export type CullingPipeline = {
20
+ isCulled(object: THREE.Object3D, camera: THREE.Camera): boolean;
21
+ };
22
+ /**
23
+ * Culls `culler`'s meshes from the renders of `scene` through the camera `getCamera` returns:
24
+ * recomputed when that camera moves or the scene changes, and answered through the drawing
25
+ * pipeline's own culling test while such a render collects its draws. Nothing about the meshes
26
+ * changes, so other cameras (the sun's shadow, captures) see them all.
27
+ */
28
+ export declare function attachOcclusionCuller({ scene, pipeline, culler, getCamera, }: {
29
+ scene: THREE.Scene;
30
+ pipeline: CullingPipeline;
31
+ culler: OcclusionCuller;
32
+ getCamera: () => THREE.Camera;
33
+ }): () => void;
34
+ //# sourceMappingURL=occlusion-culler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"occlusion-culler.d.ts","sourceRoot":"","sources":["../../src/lib/occlusion-culler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAiD9B,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,MAAM,CAAA;CACX,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,mFAAmF;IACnF,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACnD,sEAAsE;IACtE,OAAO,IAAI,OAAO,CAAA;IAClB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;IAC9B,OAAO,IAAI,IAAI,CAAA;CAChB,CAAA;AAUD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,GAAG,eAAe,CA6gB3E;AAED,+FAA+F;AAC/F,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,CAAA;CAAE,CAAA;AAEjG;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE;IACD,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,QAAQ,EAAE,eAAe,CAAA;IACzB,MAAM,EAAE,eAAe,CAAA;IACvB,SAAS,EAAE,MAAM,KAAK,CAAC,MAAM,CAAA;CAC9B,GAAG,MAAM,IAAI,CAoDb"}