three-base 2.0.172 → 2.0.180

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 (127) hide show
  1. package/FPScontrol.d.ts +15 -24
  2. package/FPScontrol.js +63 -87
  3. package/FPScontrol.ts +64 -88
  4. package/Ground1/Ground1.d.ts +4 -2
  5. package/Ground1/Ground1.js +77 -57
  6. package/Ground1/Ground1.ts +77 -57
  7. package/Mirror.js +0 -4
  8. package/Mirror.ts +0 -4
  9. package/MktMesh/MktMesh.d.ts +3 -2
  10. package/MktMesh/MktMesh.js +27 -8
  11. package/MktMesh/MktMesh.ts +24 -8
  12. package/MktMesh/boxLine2.d.ts +42 -0
  13. package/MktMesh/boxLine2.js +185 -0
  14. package/MktMesh/boxLine2.ts +222 -0
  15. package/MktMesh/imagePlane.d.ts +60 -0
  16. package/MktMesh/imagePlane.js +158 -0
  17. package/MktMesh/imagePlane.ts +203 -0
  18. package/MktMesh/imageTileLoader.d.ts +19 -0
  19. package/MktMesh/imageTileLoader.js +55 -0
  20. package/MktMesh/imageTileLoader.ts +64 -0
  21. package/MktMesh/mktMappingShader.d.ts +6 -5
  22. package/MktMesh/mktMappingShader.js +26 -19
  23. package/MktMesh/mktMappingShader.ts +28 -19
  24. package/MktMesh/mktShader.d.ts +127 -58
  25. package/MktMesh/mktShader.js +192 -117
  26. package/MktMesh/mktShader.ts +251 -127
  27. package/MktMesh/modelSlot.d.ts +123 -0
  28. package/MktMesh/modelSlot.js +339 -0
  29. package/MktMesh/modelSlot.ts +383 -0
  30. package/MktMesh/panoSlotRoster.d.ts +2 -2
  31. package/MktMesh/panoSlotRoster.js +9 -7
  32. package/MktMesh/panoSlotRoster.ts +9 -7
  33. package/MktMesh/panoTileStitcher.d.ts +20 -13
  34. package/MktMesh/panoTileStitcher.js +97 -76
  35. package/MktMesh/panoTileStitcher.ts +119 -90
  36. package/PipeMesh/PipeJoint.d.ts +22 -0
  37. package/PipeMesh/PipeJoint.js +33 -0
  38. package/PipeMesh/PipeJoint.ts +38 -0
  39. package/PipeMesh/PipeMesh.d.ts +94 -22
  40. package/PipeMesh/PipeMesh.js +192 -53
  41. package/PipeMesh/PipeMesh.ts +223 -76
  42. package/PipeMesh/PipeStraight.d.ts +19 -0
  43. package/PipeMesh/PipeStraight.js +34 -0
  44. package/PipeMesh/PipeStraight.ts +45 -0
  45. package/PipeMesh/axisSnap.d.ts +52 -0
  46. package/PipeMesh/axisSnap.js +52 -0
  47. package/PipeMesh/axisSnap.ts +93 -0
  48. package/PipeMesh/directRoute.d.ts +85 -0
  49. package/PipeMesh/directRoute.js +126 -0
  50. package/PipeMesh/directRoute.ts +190 -0
  51. package/PipeMesh/index.d.ts +6 -0
  52. package/PipeMesh/index.js +6 -0
  53. package/PipeMesh/index.ts +7 -1
  54. package/PipeMesh/pipeMaterial.d.ts +16 -0
  55. package/PipeMesh/pipeMaterial.js +23 -0
  56. package/PipeMesh/pipeMaterial.ts +26 -0
  57. package/ThreeBase.d.ts +1 -39
  58. package/ThreeBase.js +38 -147
  59. package/ThreeBase.ts +1176 -1302
  60. package/instanced/InstancedBatch.d.ts +14 -1
  61. package/instanced/InstancedBatch.js +53 -3
  62. package/instanced/InstancedBatch.ts +62 -3
  63. package/instanced/InstancedChild.d.ts +24 -0
  64. package/instanced/InstancedChild.js +40 -0
  65. package/instanced/InstancedChild.ts +41 -0
  66. package/instanced/InstancedGroup.d.ts +48 -0
  67. package/instanced/InstancedGroup.js +45 -1
  68. package/instanced/InstancedGroup.ts +85 -0
  69. package/package.json +22 -7
  70. package/pass/debugDepthState.d.ts +16 -8
  71. package/pass/debugDepthState.js +4 -0
  72. package/pass/debugDepthState.ts +27 -9
  73. package/pass/glow_pass.js +11 -94
  74. package/pass/glow_pass.ts +11 -97
  75. package/pass/ssao_pass.d.ts +9 -23
  76. package/pass/ssao_pass.js +41 -128
  77. package/pass/ssao_pass.ts +36 -166
  78. package/pass/stroke_pass.js +3 -0
  79. package/pass/stroke_pass.ts +4 -0
  80. package/shaders/debugDepthCompareShader.d.ts +1 -1
  81. package/shaders/debugDepthCompareShader.js +1 -1
  82. package/shaders/debugDepthCompareShader.ts +1 -1
  83. package/shaders/debugDepthJudgeShader.d.ts +1 -1
  84. package/shaders/debugDepthJudgeShader.js +1 -1
  85. package/shaders/debugDepthJudgeShader.ts +1 -1
  86. package/PipeMesh/PathGeometry.d.ts +0 -44
  87. package/PipeMesh/PathGeometry.js +0 -307
  88. package/PipeMesh/PathGeometry.ts +0 -343
  89. package/PipeMesh/PathPoint.d.ts +0 -17
  90. package/PipeMesh/PathPoint.js +0 -31
  91. package/PipeMesh/PathPoint.ts +0 -36
  92. package/PipeMesh/PathPointList.d.ts +0 -31
  93. package/PipeMesh/PathPointList.js +0 -209
  94. package/PipeMesh/PathPointList.ts +0 -227
  95. package/PipeMesh/PathTubeGeometry.d.ts +0 -25
  96. package/PipeMesh/PathTubeGeometry.js +0 -130
  97. package/PipeMesh/PathTubeGeometry.ts +0 -146
  98. package/WaterFlow/FloatingDuck.d.ts +0 -88
  99. package/WaterFlow/FloatingDuck.js +0 -307
  100. package/WaterFlow/FloatingDuck.ts +0 -445
  101. package/WaterFlow/GPUFluidSimulation.d.ts +0 -56
  102. package/WaterFlow/GPUFluidSimulation.js +0 -785
  103. package/WaterFlow/GPUFluidSimulation.ts +0 -827
  104. package/WaterFlow/ReservoirSystem.d.ts +0 -106
  105. package/WaterFlow/ReservoirSystem.js +0 -621
  106. package/WaterFlow/ReservoirSystem.ts +0 -754
  107. package/WaterFlow/SPHField.d.ts +0 -35
  108. package/WaterFlow/SPHField.js +0 -204
  109. package/WaterFlow/SPHField.ts +0 -228
  110. package/WaterFlow/ScreenSpaceFluidPass.d.ts +0 -69
  111. package/WaterFlow/ScreenSpaceFluidPass.js +0 -736
  112. package/WaterFlow/ScreenSpaceFluidPass.ts +0 -789
  113. package/WaterFlow/ShallowWaterSurface.d.ts +0 -45
  114. package/WaterFlow/ShallowWaterSurface.js +0 -441
  115. package/WaterFlow/ShallowWaterSurface.ts +0 -510
  116. package/WaterFlow/WaterFlow.d.ts +0 -87
  117. package/WaterFlow/WaterFlow.js +0 -171
  118. package/WaterFlow/WaterFlow.ts +0 -219
  119. package/WaterFlow/types.d.ts +0 -161
  120. package/WaterFlow/types.js +0 -1
  121. package/WaterFlow/types.ts +0 -189
  122. package/WaterFlow/waterFlowPass.d.ts +0 -3
  123. package/WaterFlow/waterFlowPass.js +0 -20
  124. package/WaterFlow/waterFlowPass.ts +0 -40
  125. package/shaders/debugExcludedShader.d.ts +0 -15
  126. package/shaders/debugExcludedShader.js +0 -48
  127. package/shaders/debugExcludedShader.ts +0 -50
@@ -2,45 +2,40 @@
2
2
  * @Author: xiaosihan
3
3
  * @Date: 2022-09-20 23:06:00
4
4
  * @Last Modified by: xiaosihan
5
- * @Last Modified time: 2024-08-17 02:16:56
5
+ * @Last Modified time: 2026-10-12 00:00:00
6
6
  *
7
- * ⭐ 2026-09-20 皇上旨两改:
8
- * ① 原「科技感装饰地板」(三张格子贴图 + 紫色扩散动画 `#8D108E` + 格子线 `#263C56`)
9
- * → 深灰地板(每帧 3 次贴图采样、贴图平铺无各向异性 → 摩尔纹 + home 页卡顿元凶之一)。
10
- * ② 皇上再旨「地板就是**随机渐变**的深灰色、**不透明**」:
11
- * → 深灰区间内用 **value noise 做随机渐变**(无贴图、零采样,天然无摩尔纹);
12
- * → `transparent: false`、alpha 恒 1(不透明)。
7
+ * ⭐ 装饰地板 = 科技感格子贴图 + 紫色扩散动画(2026-10-12 皇上旨「整条退回旧科技感地板」+「装饰地板需要透明」)。
8
+ * 历史:2026-09-20 皇上曾两旨把它改成「随机渐变的深灰、不透明」,理由是每帧 3 次贴图采样 +
9
+ * 平铺无各向异性 → 摩尔纹,且是 home 页卡顿元凶之一。**当年那个摩尔纹根因现在已经在
10
+ * `threeLoader.getTexture` 里根治了**(anisotropy=16 + generateMipmaps + LinearMipmapLinear),
11
+ * 所以贴图路线的重画成本比 09-20 那版低一档,本次按皇上旨意恢复旧外观。
13
12
  *
14
- * ⚠️ 不透明带来的变化与对策:
15
- * - 原靠「径向 alpha 淡出」柔化 500×500 地板的边界;不透明后改用**径向压暗**(乘 0.72~1.0)
16
- * 弱化硬边 —— 若哪天皇上要「边界完全消失」,只能改回 alpha 淡出(即恢复 transparent)。
17
- * - 调试线(FPScontrol)两条口径分家(2026-09-25 皇上逐条定案):**10 条射线辅助线开深度测试**,
18
- * 正是靠本地板「不透明 + depthWrite:true」才挡得住埋进地里的那半截线;**8/9 两条回弹向量线
19
- * 保持穿透显示**(关深度测试 + renderOrder:9999),照旧常显在最前。
20
- * ⚠️ 若哪天把本地板改回 transparent + depthWrite:false,射线辅助线会重新穿地浮出来。
13
+ * ⭐ 2026-10-12 皇上旨「装饰地板需要透明」⇒ 恢复旧版的**外观**口径(逐字照抄 09-20 之前的写法):
14
+ * - `transparent: true` + 径向 alpha 淡出 `mapColor.a = 1 - dist(center)*2`(中心实、外缘全透)。
15
+ * - **但 `depthWrite: true` 保留**(旧版本来就是 transparent + depthWrite:true):主渲染的深度缓冲
16
+ * 照写 ⇒ FPScontrol 那 10 条射线辅助线仍被地板挡住埋进地里的那半截(09-25 定案口径不受影响)。
21
17
  *
22
- * ⚠️ 改动红线(这几条被别处依赖,**不许顺手改**):
23
- * - `userData.fpsCollision = true`:FPScontrol 把地板当碰撞体(见 FPScontrol.ts 地面处理段)。
24
- * - ⚠️ 2026-10-11 皇上旨「物体的深度都要进深度图」⇒ 原第三条红线 `userData.aoFloor = true`
25
- * (把地板从可见深度图里排除的那条例外)**整条拆除**:它的前提是地板半透明可透视,与本文件
26
- * ② 的「不透明改造」自相矛盾。地板现在按普通实心面写进深度图,被它挡住的描边/辉光/覆盖色/
27
- * 磨砂会被裁掉。见 three-base/pass/ssao_pass.ts 文件头。
28
- * - ShaderMaterial 必须含 logdepthbuf chunk;linear 输出必须 `#include <colorspace_fragment>`;
29
- * 否则末端 sRGB 编码会二次编码 → 发白。
18
+ * ⭐ 2026-10-18 皇上旨「所有的对象都要进入到深度图里面了,不需要用参数过滤掉了」⇒ 当年为「可透视
19
+ * 地板」加的 `userData.aoFloor`(把地板从后处理的可见深度图里排除)连同 ssao_pass 里那整条过滤判据
20
+ * 一并删除。现在地板两张深度图都写 ⇒ 透过地板看到的实体,其描边/辉光/覆盖色/磨砂会被地板裁掉,
21
+ * 包括外缘 alpha≈0 那圈「看得见空、看不见地板」的区域。判据出处 three-base/pass/ssao_pass.ts 文件头,
22
+ * 护栏 test/depthDebug.test.js J 组。
30
23
  */
31
- import { Color, DoubleSide, Matrix4, Mesh, PlaneGeometry, ShaderChunk, ShaderMaterial } from "three";
24
+ import { Clock, Color, DoubleSide, BufferGeometry, Camera, Group, Material, Matrix4, Mesh, PlaneGeometry, Scene, ShaderChunk, ShaderMaterial, Vector2, WebGLRenderer } from "three";
32
25
  import { MathUtils } from "three";
33
26
  const { degToRad } = MathUtils;
27
+ import floorMap1PNG from "./floorMap1.png";
28
+ import floorMap2PNG from "./floorMap2.png";
29
+ import floorMap3PNG from "./floorMap3.png";
30
+ import threeLoader from "../threeLoader";
34
31
 
35
- /** 装饰地板(随机渐变的深灰、不透明) */
32
+ /** 装饰地板(科技感格子贴图 + 紫色扩散动画,半透明可透视外观) */
36
33
  export default class Ground1 extends Mesh {
37
34
  tempMatrix: Matrix4 = new Matrix4();
38
35
  constructor() {
39
36
  super();
40
37
  // 标记为自己参与 FPS 碰撞检测
41
38
  this.userData.fpsCollision = true;
42
- // ⚠️ 这里原来还有 `this.userData.aoFloor = true;`(把本地板从可见深度图里排除的特例标记),
43
- // 2026-10-11 皇上旨「物体的深度都要进深度图」⇒ 连同 ssao_pass 的特例分支一起整条删除。
44
39
  }
45
40
  renderOrder: number = -1;
46
41
  geometry: PlaneGeometry = (() => {
@@ -52,14 +47,22 @@ export default class Ground1 extends Mesh {
52
47
  setSize(width: number, height: number): void {
53
48
  this.scale.set(width, 1, height);
54
49
  }
50
+ clock: Clock = new Clock();
51
+ onBeforeRender: (renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group) => void = (renderer, scene, camera, geometry, material, group) => {
52
+ const elapsedTime: number = this.clock.getElapsedTime();
53
+ this.material.uniforms.speed.value = (elapsedTime * 0.1) % 0.6;
54
+ this.material.uniforms.speed2.value = ((elapsedTime * 0.1) % 0.6) - 0.1;
55
+ };
55
56
  material: ShaderMaterial = new ShaderMaterial({
56
57
  uniforms: {
57
- /** 随机渐变的**暗端**深灰(three 的 Color 存 linear,末端 colorspace_fragment 编码回 sRGB) */
58
- colorDark: { value: new Color("#2A2E33") },
59
- /** 随机渐变的**亮端**深灰 */
60
- colorLight: { value: new Color("#3D434B") },
61
- /** 低频噪声格数(越大斑块越碎;9 → 500×500 地板上每块约 55m 的大块渐变) */
62
- noiseScale: { value: 9 },
58
+ size: { value: new Vector2(10, 10) }, // 格子平铺格数
59
+ speed: { value: 0 }, // 扩散值0-1
60
+ speed2: { value: 0 }, // 扩散值0-1
61
+ speedColor: { value: new Color("#8D108E") }, // 格子扩散颜色
62
+ lineColor: { value: new Color("#263C56") }, // 格子线的颜色
63
+ map1: { value: threeLoader.getTexture(floorMap1PNG) }, // 贴图纹理
64
+ map2: { value: threeLoader.getTexture(floorMap2PNG) }, // 贴图纹理
65
+ map3: { value: threeLoader.getTexture(floorMap3PNG) }, // 贴图纹理
63
66
  },
64
67
  vertexShader: `
65
68
  ${ShaderChunk.common}
@@ -73,42 +76,59 @@ export default class Ground1 extends Mesh {
73
76
  `,
74
77
  fragmentShader: `
75
78
  ${ShaderChunk.logdepthbuf_pars_fragment}
76
- uniform vec3 colorDark;
77
- uniform vec3 colorLight;
78
- uniform float noiseScale;
79
+ uniform sampler2D map1;
80
+ uniform sampler2D map2;
81
+ uniform sampler2D map3;
82
+ uniform vec2 size;
83
+ uniform vec3 lineColor;
84
+ uniform float speed;
85
+ uniform float speed2;
86
+ uniform vec3 speedColor;
79
87
  varying vec2 vUv;
88
+ void main() {
80
89
 
81
- // 伪随机 hash → value noise:无贴图、零采样,天然没有平铺摩尔纹
82
- float hash21(vec2 p) {
83
- return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
84
- }
85
- float valueNoise(vec2 p) {
86
- vec2 i = floor(p);
87
- vec2 f = fract(p);
88
- vec2 u = f * f * (3.0 - 2.0 * f); // smoothstep 插值 → 柔和渐变,不是硬格子
89
- return mix(mix(hash21(i), hash21(i + vec2(1.0, 0.0)), u.x),
90
- mix(hash21(i + vec2(0.0, 1.0)), hash21(i + vec2(1.0, 1.0)), u.x), u.y);
91
- }
90
+ // 中心点
91
+ vec2 center = vec2(0.5,0.5);
92
92
 
93
- void main() {
94
- // 两个八度叠加:低频大块渐变 + 中频细节 → 深灰区间内的**随机渐变**
95
- float n = valueNoise(vUv * noiseScale) * 0.65
96
- + valueNoise(vUv * noiseScale * 3.1) * 0.35;
97
- vec3 col = mix(colorDark, colorLight, clamp(n, 0.0, 1.0));
93
+ vec4 map1Color = texture2D(map1, fract( vUv * size) );
94
+
95
+ // 格子中心渐变颜色
96
+ if(distance( vUv , center ) < speed){
97
+ map1Color = mix(map1Color, vec4(speedColor, 1.0), clamp((1.0 - (distance(distance( vUv , center ), speed) * 8.0) ) * map1Color.a, 0.0, 1.0 ) );
98
+ }
99
+
100
+ vec4 map2Color = texture2D(map2, fract( vUv * size) );
101
+
102
+ vec4 map3Color = texture2D(map3, fract( vUv * size) );
103
+
104
+ // 正方形装饰中心渐变颜色
105
+ if(distance( vUv , center ) < speed2){
106
+ map3Color = mix(map3Color, map3Color * 3.0, clamp((1.0 - (distance(distance( vUv , center ), speed) * 4.0) ) * map3Color.a, 0.0, 1.0 ) );
107
+ }
108
+
109
+ vec4 mapColor = mix(map1Color, map2Color, map2Color.a);
110
+
111
+ mapColor = mix(mapColor, map3Color, map3Color.a);
112
+
113
+ //添加背景色
114
+ mapColor = mix(mapColor, vec4(lineColor.rgb, 1.0 ), 1.0 - mapColor.a);
115
+
116
+ // 设置透明度(中心实、外缘全透的径向淡出 —— 500×500 大地板的硬边靠它消失)
117
+ mapColor.a = 1.0 - (distance( vUv , center ) * 2.0);
98
118
 
99
- // ⚠️ 不透明地板无法用 alpha 淡出边界 → 改用径向压暗弱化 500×500 的硬边
100
- float edge = 1.0 - smoothstep(0.34, 0.5, distance(vUv, vec2(0.5, 0.5)));
101
- col *= mix(0.72, 1.0, edge);
119
+ gl_FragColor = mapColor;
102
120
 
103
- gl_FragColor = vec4(col, 1.0); // 不透明
104
121
  ${ShaderChunk.logdepthbuf_fragment}
105
122
  #include <colorspace_fragment>
106
123
  }
107
124
  `,
108
125
  depthTest: true,
109
126
  depthWrite: true,
110
- // ⭐ 皇上 2026-09-20 旨:地板**不透明**
111
- transparent: false,
127
+ // ⭐ 皇上 2026-10-12 旨「装饰地板需要透明」:恢复旧版 transparent:true(半透可透视)。
128
+ // ⚠️ depthWrite 保持 true —— 主渲染的深度缓冲照写,FPScontrol 的 10 条射线辅助线才挡得住
129
+ // 埋进地里的那半截(09-25 定案)。后处理特效的遮挡判定走 baseDepthTexture,2026-10-18
130
+ // 皇上旨「所有的对象都要进入到深度图里面了」⇒ 地板也写进那张图,会裁掉身后特效。
131
+ transparent: true,
112
132
  side: DoubleSide,
113
133
  });
114
134
  }
package/Mirror.js CHANGED
@@ -9,10 +9,6 @@ import { Color, DoubleSide, HalfFloatType, Matrix4, Mesh, PerspectiveCamera, Pla
9
9
  export default class Mirror extends Mesh {
10
10
  constructor(geometry = Mirror.geometry) {
11
11
  super(geometry);
12
- // 镜子虽标了 transparent:true(为渲染顺序),但 fragment shader 实际输出 alpha=1.0,
13
- // 是实心反射面、并非可透视表面 → 必须参与 AO 与描边/辉光的深度遮挡。
14
- // SSAO 的 AO 数据准备会排除所有 transparent 物体,故此处显式声明保留。
15
- this.userData.aoOpaque = true;
16
12
  }
17
13
  // 正方形网格
18
14
  static geometry = (() => {
package/Mirror.ts CHANGED
@@ -10,10 +10,6 @@ import { Color, DoubleSide, HalfFloatType, Matrix4, Mesh, PerspectiveCamera, Pla
10
10
  export default class Mirror extends Mesh {
11
11
  constructor(geometry: BufferGeometry = Mirror.geometry) {
12
12
  super(geometry);
13
- // 镜子虽标了 transparent:true(为渲染顺序),但 fragment shader 实际输出 alpha=1.0,
14
- // 是实心反射面、并非可透视表面 → 必须参与 AO 与描边/辉光的深度遮挡。
15
- // SSAO 的 AO 数据准备会排除所有 transparent 物体,故此处显式声明保留。
16
- this.userData.aoOpaque = true;
17
13
  }
18
14
 
19
15
  // 正方形网格
@@ -61,8 +61,9 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
61
61
  * ⇒ 与编辑态 `mktMeshEditRenderer._slotPanos` **同一份算法、同一个常数**(预览看到的那几张 = 编辑器里的那几张,均为 8 个全景槽)。
62
62
  * 存**对象引用**不存下标(删点会让下标整体前移,引用不会认错人)。
63
63
  * 消费方 = `feedFaceMapping`(墙 8 格);⭐ 10-03 旨② 后预览立方体与墙**共用同一份材质**,
64
- * 立方体几何无 `aPanoIndex` ⇒ `vPanoIndex ≡ 0` ⇒ 它显示的就是 0 号槽那张(墙与立方体天然同源,
65
- * 原来那条「把中心那几张抄给立方体单全景材质」的 `currentTiles` 旁路已整条删除)。
64
+ * ⭐ 10-12 旨后立方体**逐颗自带序号**(`InstancedChild.panoIndex` → 逐实例 `aPanoIndex`),
65
+ * 不再是从前那句「几何没这条 attribute ⇒ `vPanoIndex ≡ 0` ⇒ 全体看 0 号槽那张」;
66
+ * 原来那条「把中心那几张抄给立方体单全景材质」的 `currentTiles` 旁路已整条删除。
66
67
  */
67
68
  private _slotPanos;
68
69
  /**
@@ -107,8 +107,9 @@ export default class MktMesh extends Mesh {
107
107
  * ⇒ 与编辑态 `mktMeshEditRenderer._slotPanos` **同一份算法、同一个常数**(预览看到的那几张 = 编辑器里的那几张,均为 8 个全景槽)。
108
108
  * 存**对象引用**不存下标(删点会让下标整体前移,引用不会认错人)。
109
109
  * 消费方 = `feedFaceMapping`(墙 8 格);⭐ 10-03 旨② 后预览立方体与墙**共用同一份材质**,
110
- * 立方体几何无 `aPanoIndex` ⇒ `vPanoIndex ≡ 0` ⇒ 它显示的就是 0 号槽那张(墙与立方体天然同源,
111
- * 原来那条「把中心那几张抄给立方体单全景材质」的 `currentTiles` 旁路已整条删除)。
110
+ * ⭐ 10-12 旨后立方体**逐颗自带序号**(`InstancedChild.panoIndex` → 逐实例 `aPanoIndex`),
111
+ * 不再是从前那句「几何没这条 attribute ⇒ `vPanoIndex ≡ 0` ⇒ 全体看 0 号槽那张」;
112
+ * 原来那条「把中心那几张抄给立方体单全景材质」的 `currentTiles` 旁路已整条删除。
112
113
  */
113
114
  _slotPanos = [];
114
115
  /**
@@ -801,6 +802,10 @@ export default class MktMesh extends Mesh {
801
802
  * ⭐ `rt:` 虚拟键(32 瓦片拼合槽)走拼合器 peek;⛔ 不在这里 pump(每帧驱动点唯一,见 `updateTexture`,
802
803
  * 否则一帧画好几格 = 把「每帧只载入一张新纹理」那道闸从后门拆了)。 */
803
804
  _peekTileAt(key, original) {
805
+ // ⭐ 2026-10-25:空地址 = 「这个点还没设图片」,不是一条能加载的 URL ⇒ 直接判没有。
806
+ // 放过去会让 ensureTileTexture 拿空串请求当前文档地址、失败后每帧重试。
807
+ if (!key)
808
+ return null;
804
809
  if (isRtStitchKey(key)) {
805
810
  const p = rtStitchKeyParts(key);
806
811
  if (!p)
@@ -838,6 +843,9 @@ export default class MktMesh extends Mesh {
838
843
  * ⚠️ 皇上旨 2026-09-28「加载瓦片的时候, 瓦片纹理不需要添加 oss 的参数」⇒ 真瓦片(`original=true`,现行 32 片/全景)走原图,
839
844
  * 整图档才按 ossSize 档缩(`resolveOssUrl`)。 */
840
845
  ensureTileTexture(key, original = false) {
846
+ // ⭐ 2026-10-25:空地址直接判「没有」,不起载(与编辑态 ensureFloorTexture 同一道闸)。
847
+ if (!key)
848
+ return null;
841
849
  const cached = this._texCache.get(key);
842
850
  if (cached)
843
851
  return cached;
@@ -887,8 +895,9 @@ export default class MktMesh extends Mesh {
887
895
  if (this._glRenderer)
888
896
  this._glRenderer.initTexture(tex);
889
897
  }
890
- catch (e) {
891
- console.warn("[MktMesh] 瓦片预上传失败,交给常规渲染路径自行上传:", e);
898
+ catch {
899
+ // 皇上旨 2026-10-12「日志都去掉」⇒ 这里不再出声;降级路径本身不变(下面照记「已处理」,
900
+ // 最坏退回 three 在绑定那一帧自己上传的老路径)。
892
901
  }
893
902
  // ⚠️ 成败都记「已处理」。不记 ⇒ 这张永远进不了 `_texUploaded` ⇒ 那一格永远换不上新图,
894
903
  // 比「卡一下」严重得多;记了最坏也只是退回 three 在绑定那一帧自己上传的老路径。
@@ -932,6 +941,18 @@ export default class MktMesh extends Mesh {
932
941
  continue;
933
942
  }
934
943
  idxU[s] = this.textures.indexOf(info);
944
+ // ⭐ 2026-10-25 皇上旨「把默认图去掉,图片没有加载的时候就显示纯灰色」:
945
+ // 这个点**没设图片**(url 空且没有切片)⇒ 按空槽喂料,权重 0 ⇒ 片元回落 uFaceMissingColor 中性灰。
946
+ // ⛔ 别把空地址放下去:空 key 会走到 `_peekTileAt` → `ensureTileTexture("")` 发一次空请求
947
+ // (解析成当前文档地址),失败后下一帧再试 = 每帧一个废请求。序号照留(归属还在,只是没图)。
948
+ const hasSource = !!info.url || !!(info.tiles && info.tiles.length > 0);
949
+ if (!hasSource) {
950
+ for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++)
951
+ panos[base + k] = this._placeholder;
952
+ mats[s].identity();
953
+ ws[s] = 0;
954
+ continue;
955
+ }
935
956
  // 各槽喂什么地址 = 「用户选的形态 + 该点有没有切过片」共同决定(whole 或无 tiles ⇒ 各槽同为一张整图)
936
957
  const tiles = mktPanoTexUrls(info.url, info.tiles, this._panoTexMode);
937
958
  // ⚠️ 「是不是真瓦片」只算一次:喂地址(走不走 OSS 尺寸档)与写 uPanoTiled 共用同一个值
@@ -1042,12 +1063,10 @@ export default class MktMesh extends Mesh {
1042
1063
  // setWallPoints 已按位置重算过映射(旧为空 → 全 -1),此处用存档里的逐面指派覆盖。
1043
1064
  // 几何拓扑由同一份 wallPoints 重建,面索引与导出时一致 ⇒ 逐面写入即可对齐。
1044
1065
  // ⭐ 皇上旨 2026-10-03「补」:长度不符**不再整份丢弃**——按较小长度逐面灌回(存档多出的面丢掉,
1045
- // 墙变长多出来的那些面保持 setWallPoints 按位置继承到的值),并把两个数 warn 出来(不静默吞)。
1066
+ // 墙变长多出来的那些面保持 setWallPoints 按位置继承到的值)。
1067
+ // 2026-10-12 旨「日志都去掉」⇒ 这一条不再 warn,差值只按较小者静默还原。
1046
1068
  const imported = (data.panoFaceMapping && data.panoFaceMapping.length ? data.panoFaceMapping : []);
1047
1069
  const faceCount = this.panoFaceMapping.length;
1048
- if (imported.length && imported.length !== faceCount) {
1049
- console.warn(`[MktMesh] 存档序号映射面数 ${imported.length} ≠ 当前墙体面数 ${faceCount},按较小者逐面还原(差值来自墙点/墙高变化)`);
1050
- }
1051
1070
  const restore = Math.min(imported.length, faceCount);
1052
1071
  if (restore > 0) {
1053
1072
  for (let f = 0; f < restore; f++)
@@ -130,8 +130,9 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
130
130
  * ⇒ 与编辑态 `mktMeshEditRenderer._slotPanos` **同一份算法、同一个常数**(预览看到的那几张 = 编辑器里的那几张,均为 8 个全景槽)。
131
131
  * 存**对象引用**不存下标(删点会让下标整体前移,引用不会认错人)。
132
132
  * 消费方 = `feedFaceMapping`(墙 8 格);⭐ 10-03 旨② 后预览立方体与墙**共用同一份材质**,
133
- * 立方体几何无 `aPanoIndex` ⇒ `vPanoIndex ≡ 0` ⇒ 它显示的就是 0 号槽那张(墙与立方体天然同源,
134
- * 原来那条「把中心那几张抄给立方体单全景材质」的 `currentTiles` 旁路已整条删除)。
133
+ * ⭐ 10-12 旨后立方体**逐颗自带序号**(`InstancedChild.panoIndex` → 逐实例 `aPanoIndex`),
134
+ * 不再是从前那句「几何没这条 attribute ⇒ `vPanoIndex ≡ 0` ⇒ 全体看 0 号槽那张」;
135
+ * 原来那条「把中心那几张抄给立方体单全景材质」的 `currentTiles` 旁路已整条删除。
135
136
  */
136
137
  private _slotPanos: (MktTextureInfo | undefined)[] = [];
137
138
  /**
@@ -833,6 +834,9 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
833
834
  * ⭐ `rt:` 虚拟键(32 瓦片拼合槽)走拼合器 peek;⛔ 不在这里 pump(每帧驱动点唯一,见 `updateTexture`,
834
835
  * 否则一帧画好几格 = 把「每帧只载入一张新纹理」那道闸从后门拆了)。 */
835
836
  private _peekTileAt(key: string, original: boolean): Texture | null {
837
+ // ⭐ 2026-10-25:空地址 = 「这个点还没设图片」,不是一条能加载的 URL ⇒ 直接判没有。
838
+ // 放过去会让 ensureTileTexture 拿空串请求当前文档地址、失败后每帧重试。
839
+ if (!key) return null;
836
840
  if (isRtStitchKey(key)) {
837
841
  const p = rtStitchKeyParts(key);
838
842
  if (!p) return null;
@@ -869,6 +873,8 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
869
873
  * ⚠️ 皇上旨 2026-09-28「加载瓦片的时候, 瓦片纹理不需要添加 oss 的参数」⇒ 真瓦片(`original=true`,现行 32 片/全景)走原图,
870
874
  * 整图档才按 ossSize 档缩(`resolveOssUrl`)。 */
871
875
  private ensureTileTexture(key: string, original = false): Texture | null {
876
+ // ⭐ 2026-10-25:空地址直接判「没有」,不起载(与编辑态 ensureFloorTexture 同一道闸)。
877
+ if (!key) return null;
872
878
  const cached = this._texCache.get(key);
873
879
  if (cached) return cached;
874
880
  if (cached === null) return null; // 加载中
@@ -915,8 +921,9 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
915
921
  // `_glRenderer` 还没被 `onBeforeRender` 捕获到(构造后尚未渲染过一帧)⇒ 跳过预上传,
916
922
  // 交给 three 在绑定那一帧自己上传 —— 那是改造前的老路径,不会报错,只是那一张没享受「错峰」。
917
923
  if (this._glRenderer) this._glRenderer.initTexture(tex);
918
- } catch (e) {
919
- console.warn("[MktMesh] 瓦片预上传失败,交给常规渲染路径自行上传:", e);
924
+ } catch {
925
+ // 皇上旨 2026-10-12「日志都去掉」⇒ 这里不再出声;降级路径本身不变(下面照记「已处理」,
926
+ // 最坏退回 three 在绑定那一帧自己上传的老路径)。
920
927
  }
921
928
  // ⚠️ 成败都记「已处理」。不记 ⇒ 这张永远进不了 `_texUploaded` ⇒ 那一格永远换不上新图,
922
929
  // 比「卡一下」严重得多;记了最坏也只是退回 three 在绑定那一帧自己上传的老路径。
@@ -959,6 +966,17 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
959
966
  continue;
960
967
  }
961
968
  idxU[s] = this.textures.indexOf(info);
969
+ // ⭐ 2026-10-25 皇上旨「把默认图去掉,图片没有加载的时候就显示纯灰色」:
970
+ // 这个点**没设图片**(url 空且没有切片)⇒ 按空槽喂料,权重 0 ⇒ 片元回落 uFaceMissingColor 中性灰。
971
+ // ⛔ 别把空地址放下去:空 key 会走到 `_peekTileAt` → `ensureTileTexture("")` 发一次空请求
972
+ // (解析成当前文档地址),失败后下一帧再试 = 每帧一个废请求。序号照留(归属还在,只是没图)。
973
+ const hasSource = !!info.url || !!(info.tiles && info.tiles.length > 0);
974
+ if (!hasSource) {
975
+ for (let k = 0; k < RT_STITCH_SLOT_COUNT; k++) panos[base + k] = this._placeholder;
976
+ mats[s].identity();
977
+ ws[s] = 0;
978
+ continue;
979
+ }
962
980
  // 各槽喂什么地址 = 「用户选的形态 + 该点有没有切过片」共同决定(whole 或无 tiles ⇒ 各槽同为一张整图)
963
981
  const tiles = mktPanoTexUrls(info.url, info.tiles, this._panoTexMode);
964
982
  // ⚠️ 「是不是真瓦片」只算一次:喂地址(走不走 OSS 尺寸档)与写 uPanoTiled 共用同一个值
@@ -1075,12 +1093,10 @@ export default class MktMesh extends Mesh<BufferGeometry, ShaderMaterial> {
1075
1093
  // setWallPoints 已按位置重算过映射(旧为空 → 全 -1),此处用存档里的逐面指派覆盖。
1076
1094
  // 几何拓扑由同一份 wallPoints 重建,面索引与导出时一致 ⇒ 逐面写入即可对齐。
1077
1095
  // ⭐ 皇上旨 2026-10-03「补」:长度不符**不再整份丢弃**——按较小长度逐面灌回(存档多出的面丢掉,
1078
- // 墙变长多出来的那些面保持 setWallPoints 按位置继承到的值),并把两个数 warn 出来(不静默吞)。
1096
+ // 墙变长多出来的那些面保持 setWallPoints 按位置继承到的值)。
1097
+ // 2026-10-12 旨「日志都去掉」⇒ 这一条不再 warn,差值只按较小者静默还原。
1079
1098
  const imported = (data.panoFaceMapping && data.panoFaceMapping.length ? data.panoFaceMapping : []) as number[];
1080
1099
  const faceCount = this.panoFaceMapping.length;
1081
- if (imported.length && imported.length !== faceCount) {
1082
- console.warn(`[MktMesh] 存档序号映射面数 ${imported.length} ≠ 当前墙体面数 ${faceCount},按较小者逐面还原(差值来自墙点/墙高变化)`);
1083
- }
1084
1100
  const restore = Math.min(imported.length, faceCount);
1085
1101
  if (restore > 0) {
1086
1102
  for (let f = 0; f < restore; f++) this.panoFaceMapping[f] = imported[f];
@@ -0,0 +1,42 @@
1
+ import { BoxGeometry, BufferGeometry, Camera, Group, Material, Mesh, MeshBasicMaterial, NormalBufferAttributes, Raycaster, Scene, Vector3, WebGLRenderer } from "three";
2
+ import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
3
+ import { LineSegments2 } from "three/examples/jsm/lines/LineSegments2.js";
4
+ export default class BoxLine2 extends Mesh {
5
+ constructor();
6
+ /** 控制台识别用(本仓约定:对象要能被认出来是谁的哪一份) */
7
+ type: string;
8
+ frustumCulled: boolean;
9
+ size: Vector3;
10
+ T1: Vector3;
11
+ T2: Vector3;
12
+ T3: Vector3;
13
+ T4: Vector3;
14
+ B1: Vector3;
15
+ B2: Vector3;
16
+ B3: Vector3;
17
+ B4: Vector3;
18
+ frontLineMaterial: LineMaterial;
19
+ backLineMaterial: LineMaterial;
20
+ createLine(name: string): LineSegments2;
21
+ T1_line: LineSegments2;
22
+ T2_line: LineSegments2;
23
+ T3_line: LineSegments2;
24
+ T4_line: LineSegments2;
25
+ M1_line: LineSegments2;
26
+ M2_line: LineSegments2;
27
+ M3_line: LineSegments2;
28
+ M4_line: LineSegments2;
29
+ B1_line: LineSegments2;
30
+ B2_line: LineSegments2;
31
+ B3_line: LineSegments2;
32
+ B4_line: LineSegments2;
33
+ /** 射线靶子(比框小一圈):`alphaTest: 1.1` ⇒ 一个像素都不画,只给下面的遮挡判据打 */
34
+ boxMesh: Mesh<BoxGeometry, MeshBasicMaterial, import("three").Object3DEventMap>;
35
+ /** 代理盒单边内缩量:米级盒子照参考实现减 0.1;薄一片/占位方块那种小尺寸钳到自身的一半,⛔ 不出负数 */
36
+ private _insetSize;
37
+ setSize(v: Vector3): void;
38
+ raycaster: Raycaster;
39
+ onBeforeRender: (renderer: WebGLRenderer, _scene: Scene, camera: Camera, _geometry: BufferGeometry<NormalBufferAttributes>, _material: Material, _group: Group) => void;
40
+ /** 释放:12 份线段几何 + 代理盒几何 + 两份 LineMaterial + 代理盒材质(模型槽删除/换框时调用) */
41
+ dispose(): void;
42
+ }
@@ -0,0 +1,185 @@
1
+ /*
2
+ * @Description: 选中设备的包围盒高亮框 —— 皇上旨 2026-10-22「选中设备的时候,设备还需要有一个高亮的框,
3
+ * 你参考旁边 archives2026 项目」⇒ 整搬那边的 `srcWeb/pages/LargeScreen/component3d/BoxLine2.ts`
4
+ * (12 条棱 fat-line:朝向相机那几棱**绿色实线**,被盒子自己挡住的那几棱**红色虚线**)。
5
+ *
6
+ * 为什么不直接用现成的 `EdgesGeometry + LineBasicMaterial`(也就是本仓旧版那圈细线):
7
+ * - 旧那圈是 `GL_LINES`,`linewidth` 在 WebGL/ANGLE 下是**死旋钮** ⇒ 永远 1 像素,深色模型上几乎看不见
8
+ * (仓内同族早已踩过:墙线/立方体边框 10-05、10-07 两旨都换成了 `LineSegments2 + LineMaterial`)。
9
+ * - fat-line 的粗细是**屏幕像素**(`worldUnits:false`)⇒ 远近一样粗,这正是「高亮框」要的效果。
10
+ *
11
+ * 遮挡那一刀怎么来的(照搬,⛔ 不是臣自己发明的判据):八顶点各自从相机朝该角发一条射线,打这份**内嵌代理盒**
12
+ * —— 打得着 = 这条视线被盒子自己截住了 = 那个角在背面 ⇒ 凡是含背角的棱改用红色虚线材质。
13
+ * 代理盒本身 `alphaTest: 1.1` ⇒ 一个像素都不出、也不写深度,只当射线靶子用。
14
+ *
15
+ * ⚠️ 相对参考实现改了五处,每处都只为了贴合本仓:
16
+ * ① 代理盒内缩量钳到「不小于原尺寸的 50%」(`_insetSize`):那边设备都是米级大物件,`v - 0.1` 恒正;
17
+ * 本仓的模型槽可能是薄一片(画作/展牌)或 0.3 m 的占位方块,`v - 0.1` 会出负数拿到一份翻面的几何。
18
+ * ② 整棵子树标 `userData.pickIgnored = true`:本仓编辑器拾取模型走 `intersectObjects(this.models, true)`
19
+ * (递归打进槽本体),一个实心代理盒挂在槽下面会把「框内的空地」也点成选中这只槽 —— 旧版那圈细线是靠
20
+ * `raycast = noop` 躲开的,这里换成标记,由 `_pickModel` 跳过(⛔ 类内部的遮挡射线不受影响,它打的是
21
+ * 自己那份 `boxMesh`,与本仓的拾取射线是两把尺)。
22
+ * ③ 加 `dispose()`:本仓模型槽删除/换模型要释放那 12 份线段几何与两份 LineMaterial,否则删一只漏一份显存。
23
+ * ④ 没搬那边那四颗 `setDepthTest` / `setLinewidth` / 两个 getter —— 在 archives2026 全仓 **0 处调用**
24
+ * (而且那两个私有字段的初值 `false`/`1` 与两份材质真正用的 `depthTest:true`/`linewidth:2` 本来就不一致,
25
+ * 只有从没被调过才没露馅)。真正生效的就是下面材质构造里那两个数,⛔ 不留没人拧的旋钮。
26
+ * ⑤ 12 条棱各自 `raycast = noop`:那边从不把线框交给射线,本仓的拾取射线会**递归**进槽子树打到它们,
27
+ * 而 `LineSegments2.raycast` 在 `worldUnits:false` 那一路要先读 `raycaster.camera`(编辑器那把没设)
28
+ * ⇒ 先 `console.error` 再撞 `camera.projectionMatrix` 抛异常。详见 `createLine` 里那句注释。
29
+ */
30
+ import { BoxGeometry, DoubleSide, Mesh, MeshBasicMaterial, Raycaster, Vector2, Vector3, } from "three";
31
+ import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
32
+ import { LineSegments2 } from "three/examples/jsm/lines/LineSegments2.js";
33
+ import { LineSegmentsGeometry } from "three/examples/jsm/lines/LineSegmentsGeometry.js";
34
+ /** 朝向相机那几棱:绿色实线 */
35
+ const FRONT_COLOR = 0x00ff00;
36
+ /** 被盒子挡住的那几棱:红色虚线(dash/gap 各 5,与参考实现同档) */
37
+ const BACK_COLOR = 0xff0000;
38
+ /** 棱线粗细(屏幕像素,`worldUnits:false` ⇒ 远近一样粗) */
39
+ const LINE_WIDTH_PX = 2;
40
+ export default class BoxLine2 extends Mesh {
41
+ constructor() {
42
+ super();
43
+ // 拾取/射线都不该把高亮框算进来(见文件头 ⚠️②);内部遮挡判据另打 `this.boxMesh`,不走这条
44
+ this.traverse((o) => { o.userData.pickIgnored = true; });
45
+ }
46
+ /** 控制台识别用(本仓约定:对象要能被认出来是谁的哪一份) */
47
+ type = "boxLine2";
48
+ frustumCulled = false;
49
+ size = new Vector3(1, 1, 1);
50
+ T1 = new Vector3();
51
+ T2 = new Vector3();
52
+ T3 = new Vector3();
53
+ T4 = new Vector3();
54
+ B1 = new Vector3();
55
+ B2 = new Vector3();
56
+ B3 = new Vector3();
57
+ B4 = new Vector3();
58
+ frontLineMaterial = new LineMaterial({
59
+ color: FRONT_COLOR,
60
+ worldUnits: false,
61
+ linewidth: LINE_WIDTH_PX,
62
+ depthTest: true,
63
+ resolution: new Vector2(1, 1),
64
+ });
65
+ backLineMaterial = new LineMaterial({
66
+ color: BACK_COLOR,
67
+ worldUnits: false,
68
+ dashed: true,
69
+ dashSize: 5,
70
+ gapSize: 5,
71
+ linewidth: LINE_WIDTH_PX,
72
+ depthTest: true,
73
+ resolution: new Vector2(1, 1),
74
+ });
75
+ createLine(name) {
76
+ const segmentsGeometry = new LineSegmentsGeometry();
77
+ segmentsGeometry.setPositions([-0.5, 0, 0, 0.5, 0, 0]);
78
+ const line = new LineSegments2(segmentsGeometry, this.frontLineMaterial);
79
+ line.name = name;
80
+ line.renderOrder = 100;
81
+ // ⚠️ 本仓必改(参考那边没有这一行):`LineSegments2.raycast` 在 `worldUnits:false` 那一路要读
82
+ // `raycaster.camera`(屏幕空间投影),而编辑器拾取模型用的是 `intersectObjects(models, true)` 递归打到这里、
83
+ // 那把射线没设 camera ⇒ three 自己 `console.error` 之后紧接着碰 `camera.projectionMatrix` 直接抛 ⇒ 选中点不动。
84
+ // 线段本身不是「可点的东西」,置空最省事;那颗代理盒的 `raycast` 必须留着(遮挡判据打它)。
85
+ line.raycast = () => { };
86
+ line.computeLineDistances();
87
+ this.add(line);
88
+ return line;
89
+ }
90
+ T1_line = this.createLine("T1,T2");
91
+ T2_line = this.createLine("T2,T3");
92
+ T3_line = this.createLine("T3,T4");
93
+ T4_line = this.createLine("T4,T1");
94
+ M1_line = this.createLine("T1,B1");
95
+ M2_line = this.createLine("T2,B2");
96
+ M3_line = this.createLine("T3,B3");
97
+ M4_line = this.createLine("T4,B4");
98
+ B1_line = this.createLine("B1,B2");
99
+ B2_line = this.createLine("B2,B3");
100
+ B3_line = this.createLine("B3,B4");
101
+ B4_line = this.createLine("B4,B1");
102
+ /** 射线靶子(比框小一圈):`alphaTest: 1.1` ⇒ 一个像素都不画,只给下面的遮挡判据打 */
103
+ boxMesh = (() => {
104
+ const boxGeometry = new BoxGeometry(1, 1, 1);
105
+ const boxMesh = new Mesh(boxGeometry, new MeshBasicMaterial({
106
+ color: "#ff0000", side: DoubleSide, wireframe: true, alphaTest: 1.1,
107
+ }));
108
+ boxMesh.name = "遮挡判据代理盒";
109
+ this.add(boxMesh);
110
+ return boxMesh;
111
+ })();
112
+ /** 代理盒单边内缩量:米级盒子照参考实现减 0.1;薄一片/占位方块那种小尺寸钳到自身的一半,⛔ 不出负数 */
113
+ _insetSize(v) {
114
+ return Math.max(v - 0.1, v * 0.5);
115
+ }
116
+ setSize(v) {
117
+ this.size.set(v.x, v.y, v.z);
118
+ this.boxMesh.geometry.dispose();
119
+ this.boxMesh.geometry = new BoxGeometry(this._insetSize(v.x), this._insetSize(v.y), this._insetSize(v.z));
120
+ this.T1.set(v.x / -2, v.y / 2, v.z / -2);
121
+ this.T2.set(v.x / -2, v.y / 2, v.z / 2);
122
+ this.T3.set(v.x / 2, v.y / 2, v.z / 2);
123
+ this.T4.set(v.x / 2, v.y / 2, v.z / -2);
124
+ this.B1.set(v.x / -2, v.y / -2, v.z / -2);
125
+ this.B2.set(v.x / -2, v.y / -2, v.z / 2);
126
+ this.B3.set(v.x / 2, v.y / -2, v.z / 2);
127
+ this.B4.set(v.x / 2, v.y / -2, v.z / -2);
128
+ const lines = [
129
+ { line: this.T1_line, p1: this.T1, p2: this.T2 },
130
+ { line: this.T2_line, p1: this.T2, p2: this.T3 },
131
+ { line: this.T3_line, p1: this.T3, p2: this.T4 },
132
+ { line: this.T4_line, p1: this.T4, p2: this.T1 },
133
+ { line: this.M1_line, p1: this.T1, p2: this.B1 },
134
+ { line: this.M2_line, p1: this.T2, p2: this.B2 },
135
+ { line: this.M3_line, p1: this.T3, p2: this.B3 },
136
+ { line: this.M4_line, p1: this.T4, p2: this.B4 },
137
+ { line: this.B1_line, p1: this.B1, p2: this.B2 },
138
+ { line: this.B2_line, p1: this.B2, p2: this.B3 },
139
+ { line: this.B3_line, p1: this.B3, p2: this.B4 },
140
+ { line: this.B4_line, p1: this.B4, p2: this.B1 },
141
+ ];
142
+ lines.forEach(({ line, p1, p2 }) => {
143
+ line.geometry.setPositions([p1.x, p1.y, p1.z, p2.x, p2.y, p2.z]);
144
+ line.computeLineDistances();
145
+ });
146
+ }
147
+ raycaster = new Raycaster();
148
+ onBeforeRender = (renderer, _scene, camera, _geometry, _material, _group) => {
149
+ renderer.getSize(this.frontLineMaterial.resolution);
150
+ renderer.getSize(this.backLineMaterial.resolution);
151
+ const farVectorName = [];
152
+ const farVectorNames = ["T1", "T2", "T3", "T4", "B1", "B2", "B3", "B4"];
153
+ this.raycaster.ray.origin.copy(camera.position);
154
+ [this.T1, this.T2, this.T3, this.T4, this.B1, this.B2, this.B3, this.B4].forEach((v, i) => {
155
+ const direction = v.clone().applyMatrix4(this.matrixWorld).sub(camera.position);
156
+ this.raycaster.ray.direction.copy(direction).normalize();
157
+ this.raycaster.far = direction.length();
158
+ const intersect = this.raycaster.intersectObject(this.boxMesh);
159
+ if (intersect.length > 0) {
160
+ farVectorName.push(farVectorNames[i]);
161
+ }
162
+ });
163
+ [
164
+ this.T1_line, this.T2_line, this.T3_line, this.T4_line,
165
+ this.M1_line, this.M2_line, this.M3_line, this.M4_line,
166
+ this.B1_line, this.B2_line, this.B3_line, this.B4_line,
167
+ ].forEach((line) => {
168
+ const has = farVectorName.some((name) => line.name.includes(name));
169
+ line.material = has ? this.backLineMaterial : this.frontLineMaterial;
170
+ });
171
+ };
172
+ /** 释放:12 份线段几何 + 代理盒几何 + 两份 LineMaterial + 代理盒材质(模型槽删除/换框时调用) */
173
+ dispose() {
174
+ for (const line of [
175
+ this.T1_line, this.T2_line, this.T3_line, this.T4_line,
176
+ this.M1_line, this.M2_line, this.M3_line, this.M4_line,
177
+ this.B1_line, this.B2_line, this.B3_line, this.B4_line,
178
+ ])
179
+ line.geometry.dispose();
180
+ this.boxMesh.geometry.dispose();
181
+ this.boxMesh.material.dispose();
182
+ this.frontLineMaterial.dispose();
183
+ this.backLineMaterial.dispose();
184
+ }
185
+ }