three-base 2.0.137 → 2.0.145
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/DirectionalLight.d.ts +2 -2
- package/DirectionalLight.js +1 -1
- package/DirectionalLight.ts +70 -0
- package/EventMap.d.ts +103 -0
- package/EventMap.js +17 -0
- package/EventMap.ts +139 -0
- package/EventMesh.ts +60 -0
- package/ExtrudeMesh.js +1 -25
- package/ExtrudeMesh.ts +553 -0
- package/FPScontrol.d.ts +448 -15
- package/FPScontrol.js +1448 -266
- package/FPScontrol.ts +1855 -0
- package/Fire/Fire.d.ts +4 -4
- package/Fire/Fire.js +8 -7
- package/Fire/Fire.ts +89 -0
- package/Fire/{FireGromtry.d.ts → FireGeometry.d.ts} +2 -2
- package/Fire/{FireGromtry.js → FireGeometry.js} +1 -1
- package/Fire/FireGeometry.ts +40 -0
- package/Fire/FireMaterial.d.ts +3 -3
- package/Fire/FireMaterial.js +1 -1
- package/Fire/FireMaterial.ts +89 -0
- package/Fire/index.d.ts +1 -0
- package/Fire/index.js +1 -0
- package/Fire/index.ts +2 -0
- package/GLTFDracoLoader.d.ts +71 -0
- package/GLTFDracoLoader.js +266 -0
- package/GLTFDracoLoader.ts +430 -0
- package/Grass/Grass.d.ts +2 -2
- package/Grass/Grass.ts +38 -0
- package/Grass/index.d.ts +1 -0
- package/Grass/index.js +1 -0
- package/Grass/index.ts +2 -0
- package/Ground1/Ground1.d.ts +2 -3
- package/Ground1/Ground1.js +64 -60
- package/Ground1/Ground1.ts +114 -0
- package/Ground1/index.ts +1 -0
- package/MeshBase.d.ts +1 -0
- package/MeshBase.js +37 -13
- package/MeshBase.ts +253 -0
- package/Mirror.d.ts +3 -3
- package/Mirror.js +7 -3
- package/Mirror.ts +203 -0
- package/MktMesh/MktMesh.d.ts +301 -0
- package/MktMesh/MktMesh.js +1064 -0
- package/MktMesh/MktMesh.ts +1096 -0
- package/MktMesh/assetUrl.d.ts +59 -0
- package/MktMesh/assetUrl.js +154 -0
- package/MktMesh/assetUrl.ts +155 -0
- package/MktMesh/mktMappingShader.d.ts +12 -0
- package/MktMesh/mktMappingShader.js +139 -0
- package/MktMesh/mktMappingShader.ts +139 -0
- package/MktMesh/mktShader.d.ts +263 -0
- package/MktMesh/mktShader.js +1248 -0
- package/MktMesh/mktShader.ts +1344 -0
- package/MktMesh/panoBitmapTexture.d.ts +25 -0
- package/MktMesh/panoBitmapTexture.js +52 -0
- package/MktMesh/panoBitmapTexture.ts +54 -0
- package/MktMesh/panoSlotBind.d.ts +29 -0
- package/MktMesh/panoSlotBind.js +106 -0
- package/MktMesh/panoSlotBind.ts +153 -0
- package/MktMesh/panoSlotRoster.d.ts +22 -0
- package/MktMesh/panoSlotRoster.js +169 -0
- package/MktMesh/panoSlotRoster.ts +168 -0
- package/MktMesh/panoTileStitcher.d.ts +119 -0
- package/MktMesh/panoTileStitcher.js +533 -0
- package/MktMesh/panoTileStitcher.ts +616 -0
- package/MktMesh/types.d.ts +75 -0
- package/MktMesh/types.js +1 -0
- package/MktMesh/types.ts +87 -0
- package/MktMesh/wallCapTessellate.d.ts +33 -0
- package/MktMesh/wallCapTessellate.js +97 -0
- package/MktMesh/wallCapTessellate.ts +68 -0
- package/PipeMesh/PathGeometry.js +9 -2
- package/PipeMesh/PathGeometry.ts +343 -0
- package/PipeMesh/PathPoint.ts +36 -0
- package/PipeMesh/PathPointList.js +1 -0
- package/PipeMesh/PathPointList.ts +227 -0
- package/PipeMesh/PathTubeGeometry.ts +146 -0
- package/PipeMesh/PipeMesh.d.ts +1 -1
- package/PipeMesh/PipeMesh.js +1 -1
- package/PipeMesh/PipeMesh.ts +76 -0
- package/PipeMesh/index.ts +1 -0
- package/PlaneModal1/PlaneModal1.d.ts +16 -13
- package/PlaneModal1/PlaneModal1.js +4 -0
- package/PlaneModal1/PlaneModal1.ts +138 -0
- package/PlaneModal1/index.ts +1 -0
- package/PlaneText.d.ts +2 -14
- package/PlaneText.js +7 -2
- package/PlaneText.ts +55 -0
- package/PlaneWithHtml.d.ts +4 -2
- package/PlaneWithHtml.js +2 -1
- package/PlaneWithHtml.ts +70 -0
- package/PointLight/PointLight.d.ts +3 -4
- package/PointLight/PointLight.js +1 -7
- package/PointLight/PointLight.ts +67 -0
- package/PointLight/index.ts +1 -0
- package/README.md +110 -79
- package/Sky/Sky.ts +22 -0
- package/Sky/index.ts +1 -0
- package/SpotLight.d.ts +2 -2
- package/SpotLight.js +1 -1
- package/SpotLight.ts +74 -0
- package/SpriteModal1/SpriteModal1.d.ts +16 -13
- package/SpriteModal1/SpriteModal1.js +8 -2
- package/SpriteModal1/SpriteModal1.ts +146 -0
- package/SpriteModal1/index.ts +1 -0
- package/SpriteText.d.ts +14 -13
- package/SpriteText.js +22 -9
- package/SpriteText.ts +205 -0
- package/SpriteText2.d.ts +9 -8
- package/SpriteText2.js +57 -32
- package/SpriteText2.ts +258 -0
- package/SpriteText3.d.ts +86 -0
- package/SpriteText3.js +519 -0
- package/SpriteText3.ts +599 -0
- package/SpriteWithHtml.d.ts +4 -2
- package/SpriteWithHtml.ts +50 -0
- package/Text3D/Text3D.js +17 -21
- package/Text3D/Text3D.ts +77 -0
- package/Text3D/fontController.d.ts +10 -9
- package/Text3D/fontController.js +17 -50
- package/Text3D/fontController.ts +209 -0
- package/Text3D/index.ts +1 -0
- package/ThreeBase.d.ts +168 -31
- package/ThreeBase.js +663 -117
- package/ThreeBase.ts +1314 -0
- package/ThreeMouse.d.ts +20 -7
- package/ThreeMouse.js +78 -32
- package/ThreeMouse.ts +472 -0
- package/Transition.d.ts +12 -7
- package/Transition.js +26 -27
- package/Transition.ts +242 -0
- package/Water/Water.js +21 -3
- package/Water/Water.ts +45 -0
- package/Water/index.d.ts +2 -0
- package/Water/index.js +2 -0
- package/Water/index.ts +2 -0
- package/WaterFlow/FloatingDuck.d.ts +88 -0
- package/WaterFlow/FloatingDuck.js +307 -0
- package/WaterFlow/FloatingDuck.ts +445 -0
- package/WaterFlow/GPUFluidSimulation.d.ts +56 -0
- package/WaterFlow/GPUFluidSimulation.js +785 -0
- package/WaterFlow/GPUFluidSimulation.ts +827 -0
- package/WaterFlow/ReservoirSystem.d.ts +106 -0
- package/WaterFlow/ReservoirSystem.js +621 -0
- package/WaterFlow/ReservoirSystem.ts +754 -0
- package/WaterFlow/SPHField.d.ts +35 -0
- package/WaterFlow/SPHField.js +204 -0
- package/WaterFlow/SPHField.ts +228 -0
- package/WaterFlow/ScreenSpaceFluidPass.d.ts +69 -0
- package/WaterFlow/ScreenSpaceFluidPass.js +736 -0
- package/WaterFlow/ScreenSpaceFluidPass.ts +789 -0
- package/WaterFlow/ShallowWaterSurface.d.ts +45 -0
- package/WaterFlow/ShallowWaterSurface.js +441 -0
- package/WaterFlow/ShallowWaterSurface.ts +510 -0
- package/WaterFlow/WaterFlow.d.ts +87 -0
- package/WaterFlow/WaterFlow.js +171 -0
- package/WaterFlow/WaterFlow.ts +219 -0
- package/WaterFlow/types.d.ts +161 -0
- package/WaterFlow/types.js +1 -0
- package/WaterFlow/types.ts +189 -0
- package/WaterFlow/waterFlowPass.d.ts +3 -0
- package/WaterFlow/waterFlowPass.js +20 -0
- package/WaterFlow/waterFlowPass.ts +40 -0
- package/collisionOBB.d.ts +42 -0
- package/collisionOBB.js +112 -0
- package/collisionOBB.ts +108 -0
- package/declare.d.ts +25 -167
- package/index.js +0 -1
- package/index.ts +1 -0
- package/instanced/InstancedBatch.d.ts +57 -0
- package/instanced/InstancedBatch.js +348 -0
- package/instanced/InstancedBatch.ts +406 -0
- package/instanced/InstancedChild.d.ts +174 -0
- package/instanced/InstancedChild.js +264 -0
- package/instanced/InstancedChild.ts +327 -0
- package/instanced/InstancedGroup.d.ts +43 -0
- package/instanced/InstancedGroup.js +126 -0
- package/instanced/InstancedGroup.ts +137 -0
- package/{InstancedMeshBase.d.ts → instanced/InstancedMeshBase.d.ts} +6 -7
- package/{InstancedMeshBase.js → instanced/InstancedMeshBase.js} +36 -47
- package/instanced/InstancedMeshBase.ts +292 -0
- package/instanced/InstancedTemplate.d.ts +46 -0
- package/instanced/InstancedTemplate.js +125 -0
- package/instanced/InstancedTemplate.ts +188 -0
- package/keyControls.d.ts +29 -0
- package/{keyContorls.js → keyControls.js} +10 -7
- package/keyControls.ts +107 -0
- package/lensflare/LensFlare.d.ts +39 -0
- package/lensflare/LensFlare.js +373 -0
- package/lensflare/LensFlare.ts +448 -0
- package/lensflare/lensflare0.png +0 -0
- package/lensflare/lensflare3.png +0 -0
- package/lensflare/lensflare_custom.png +0 -0
- package/lensflare/lensflare_hex_ghost.png +0 -0
- package/lensflare/lensflare_hex_main.png +0 -0
- package/package.json +104 -25
- package/pass/bloom_pass.d.ts +1 -1
- package/pass/bloom_pass.js +127 -40
- package/pass/bloom_pass.ts +243 -0
- package/pass/cover_pass.d.ts +1 -1
- package/pass/cover_pass.js +55 -32
- package/pass/cover_pass.ts +182 -0
- package/pass/debugDepthState.d.ts +20 -0
- package/pass/debugDepthState.js +9 -0
- package/pass/debugDepthState.ts +22 -0
- package/pass/depthTestBias.d.ts +12 -0
- package/pass/depthTestBias.js +12 -0
- package/pass/depthTestBias.ts +12 -0
- package/pass/effectUserData.d.ts +27 -0
- package/pass/effectUserData.js +1 -0
- package/pass/effectUserData.ts +46 -0
- package/pass/frosted_glass_pass.d.ts +1 -1
- package/pass/frosted_glass_pass.js +45 -64
- package/pass/frosted_glass_pass.ts +288 -0
- package/pass/fsQuad.d.ts +2 -2
- package/pass/fsQuad.ts +34 -0
- package/pass/glow_pass.d.ts +1 -1
- package/pass/glow_pass.js +288 -72
- package/pass/glow_pass.ts +654 -0
- package/pass/screen_bloom_pass.d.ts +11 -0
- package/pass/screen_bloom_pass.js +235 -0
- package/pass/screen_bloom_pass.ts +263 -0
- package/pass/ssao_pass.d.ts +78 -3
- package/pass/ssao_pass.js +642 -206
- package/pass/ssao_pass.ts +918 -0
- package/pass/stroke_pass.d.ts +1 -1
- package/pass/stroke_pass.js +187 -116
- package/pass/stroke_pass.ts +638 -0
- package/shaders/FXAAShaderMaterial.js +5 -48
- package/shaders/FXAAShaderMaterial.ts +201 -0
- package/shaders/baseDepthVisShader.d.ts +11 -0
- package/shaders/baseDepthVisShader.js +36 -0
- package/shaders/baseDepthVisShader.ts +36 -0
- package/shaders/baseTextureToRenderShader.js +46 -51
- package/shaders/baseTextureToRenderShader.ts +232 -0
- package/shaders/copyShader.js +2 -3
- package/shaders/copyShader.ts +32 -0
- package/shaders/debugDepthCompareShader.d.ts +14 -0
- package/shaders/debugDepthCompareShader.js +49 -0
- package/shaders/debugDepthCompareShader.ts +50 -0
- package/shaders/debugDepthJudgeShader.d.ts +18 -0
- package/shaders/debugDepthJudgeShader.js +52 -0
- package/shaders/debugDepthJudgeShader.ts +54 -0
- package/shaders/debugExcludedShader.d.ts +13 -0
- package/shaders/debugExcludedShader.js +46 -0
- package/shaders/debugExcludedShader.ts +48 -0
- package/spriteTextBg.d.ts +1 -0
- package/spriteTextBg.js +20 -0
- package/spriteTextBg.ts +20 -0
- package/threeLoader.d.ts +8 -7
- package/threeLoader.js +58 -67
- package/threeLoader.ts +525 -0
- package/threeUtils.d.ts +2 -1
- package/threeUtils.js +24 -25
- package/threeUtils.ts +373 -0
- package/types.d.ts +2 -0
- package/types.js +6 -0
- package/types.ts +8 -0
- package/ApngTexture.d.ts +0 -13
- package/ApngTexture.js +0 -61
- package/ApngTextureManage.d.ts +0 -18
- package/ApngTextureManage.js +0 -87
- package/KeyFrameAnimation.d.ts +0 -25
- package/KeyFrameAnimation.js +0 -78
- package/keyContorls.d.ts +0 -25
- package/lib/draco/draco_decoder.js +0 -34
- package/lib/draco/draco_decoder.wasm +0 -0
- package/lib/draco/draco_wasm_wrapper.js +0 -117
- package/pass/emissive_ao_pass.d.ts +0 -13
- package/pass/emissive_ao_pass.js +0 -370
- package/shaders.d.ts +0 -5
- package/shaders.js +0 -93
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Clock, Object3D } from "three";
|
|
2
|
+
import { instancedChildRegistry, instancedChildOwner } from "./InstancedChild";
|
|
3
|
+
import InstancedBatch from "./InstancedBatch";
|
|
4
|
+
const PICK_EVENT_TYPES = [
|
|
5
|
+
"mouseenter",
|
|
6
|
+
"mouseleave",
|
|
7
|
+
"mousemove",
|
|
8
|
+
"click",
|
|
9
|
+
"leftclick",
|
|
10
|
+
"rightclick",
|
|
11
|
+
"dbclick",
|
|
12
|
+
"mousedown",
|
|
13
|
+
"mouseup",
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* 固定在 ThreeBase.scene 里的分组器。每帧收集所有 InstancedChild,按 batchKey(glb+当前生效动画)分组,
|
|
17
|
+
* 为每个 key 创建/复用 InstancedBatch,把实例的世界矩阵 / 颜色同步进真实 InstancedMesh。
|
|
18
|
+
* batch 常驻(空桶不销毁),切换动画 = 实例在「glb+旧动画」「glb+新动画」两个常驻 batch 间迁移。
|
|
19
|
+
*
|
|
20
|
+
* 同时也是事件路由中枢:ThreeMouse 命中 InstancedMesh 后,通过本类把 (mesh, instanceId)
|
|
21
|
+
* 还原成对应的 InstancedChild,使逻辑实例成为透明事件接收端。
|
|
22
|
+
*/
|
|
23
|
+
export default class InstancedGroup extends Object3D {
|
|
24
|
+
/** batchKey -> InstancedBatch */
|
|
25
|
+
batches = new Map();
|
|
26
|
+
_clock = new Clock();
|
|
27
|
+
/**
|
|
28
|
+
* 本组的归属 tag(可选)。多 InstancedGroup 共存时用于隔离:
|
|
29
|
+
* - 声明了 owner 的组:**只**收集 `getOwner() === owner` 的实例(未声明归属的实例一律不收);
|
|
30
|
+
* - 未声明 owner 的组(如 ThreeBase 的默认组):只收**未声明归属**的实例,跳过已归属他组的实例。
|
|
31
|
+
* 这样主编辑器(默认组)与预览弹窗(owner="preview")各自只画自己的实例,互不污染。
|
|
32
|
+
*/
|
|
33
|
+
owner;
|
|
34
|
+
constructor(owner) {
|
|
35
|
+
super();
|
|
36
|
+
this.owner = owner;
|
|
37
|
+
}
|
|
38
|
+
/** 本组是否该收集该实例:按 owner 归属隔离(详见 owner 字段注释) */
|
|
39
|
+
_accepts(child) {
|
|
40
|
+
const childOwner = instancedChildOwner.get(child);
|
|
41
|
+
if (this.owner === undefined) {
|
|
42
|
+
// 无归属的组(ThreeBase 默认组):只收未声明归属的实例,跳过已归属他组的
|
|
43
|
+
return childOwner === undefined;
|
|
44
|
+
}
|
|
45
|
+
// 有归属的组:只收 owner 完全一致的实例
|
|
46
|
+
return childOwner === this.owner;
|
|
47
|
+
}
|
|
48
|
+
/** 每帧由 ThreeBase.render 在 scene.updateMatrixWorld 之后调用 */
|
|
49
|
+
update() {
|
|
50
|
+
const delta = this._clock.getDelta();
|
|
51
|
+
// 1. 按 batchKey 收集所有活着的 InstancedChild(按 owner 归属过滤,避免多组互相污染)
|
|
52
|
+
const groups = new Map();
|
|
53
|
+
for (const child of instancedChildRegistry) {
|
|
54
|
+
if (!this._accepts(child))
|
|
55
|
+
continue;
|
|
56
|
+
// 强制刷新世界矩阵(实例可能位于任意层级,不依赖 scene 遍历顺序)
|
|
57
|
+
child.updateWorldMatrix(true, false);
|
|
58
|
+
child.markDirtyIfWorldChanged();
|
|
59
|
+
const key = child.getBatchKey();
|
|
60
|
+
let arr = groups.get(key);
|
|
61
|
+
if (!arr) {
|
|
62
|
+
arr = [];
|
|
63
|
+
groups.set(key, arr);
|
|
64
|
+
}
|
|
65
|
+
arr.push(child);
|
|
66
|
+
}
|
|
67
|
+
// 2. 创建 / 更新每个有实例的 batch(按 glb+当前动画分桶,每个动画一个常驻 batch)
|
|
68
|
+
for (const [key, children] of groups) {
|
|
69
|
+
let batch = this.batches.get(key);
|
|
70
|
+
if (!batch) {
|
|
71
|
+
batch = new InstancedBatch(key, children[0]);
|
|
72
|
+
this.batches.set(key, batch);
|
|
73
|
+
this.add(batch.group);
|
|
74
|
+
}
|
|
75
|
+
batch.sync(children, delta);
|
|
76
|
+
}
|
|
77
|
+
// 3. 持久化:实例已迁移走的空 batch 仍需 sync([]) 把 count 归零,避免残留上一帧实例;
|
|
78
|
+
// 但【不销毁】,使其常驻可复用 —— 切换动作时实例在常驻 batch 间迁移,无需频繁创建/销毁 InstancedBatch。
|
|
79
|
+
for (const [key, batch] of this.batches) {
|
|
80
|
+
if (!groups.has(key)) {
|
|
81
|
+
batch.sync([], delta);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** 事件路由:命中 InstancedMesh 时返回对应 InstancedChild,否则 null */
|
|
86
|
+
resolveInstance(mesh, instanceId) {
|
|
87
|
+
if (instanceId === undefined) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const key = mesh.userData?.__batchKey;
|
|
91
|
+
if (!key) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const batch = this.batches.get(key);
|
|
95
|
+
if (!batch) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return batch.resolveInstance(mesh, instanceId);
|
|
99
|
+
}
|
|
100
|
+
/** 收集可拾取的 InstancedMesh(已就绪 batch,且注册了监听或设置了 cursor 的),供 ThreeMouse 候选 */
|
|
101
|
+
collectPickable(out) {
|
|
102
|
+
for (const batch of this.batches.values()) {
|
|
103
|
+
if (batch.ready && (batch.hasListener(PICK_EVENT_TYPES) || batch.hasCursor())) {
|
|
104
|
+
for (const im of batch.pickables) {
|
|
105
|
+
out.push(im);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 释放本组的全部 batch(实例缓冲 + 从组内摘除 mesh)。
|
|
112
|
+
*
|
|
113
|
+
* 用途:自建 InstancedGroup 的场景(如预览弹窗的独立轻量渲染器)在销毁时调用,
|
|
114
|
+
* 避免 InstancedMesh 的实例缓冲随组对象一起被丢弃却未释放。
|
|
115
|
+
*
|
|
116
|
+
* 注:batch 的 geometry/material **不会**被释放(InstancedBatch.dispose 刻意跳过)——
|
|
117
|
+
* 它们来自 `InstancedChild` 的 static geometry/material 或模板缓存,是跨实例共享资源。
|
|
118
|
+
* ⚠️ 默认的 ThreeBase 组**不要**调用本方法:它随应用生命周期常驻,batch 复用是设计意图。
|
|
119
|
+
*/
|
|
120
|
+
dispose() {
|
|
121
|
+
for (const batch of this.batches.values()) {
|
|
122
|
+
batch.dispose();
|
|
123
|
+
}
|
|
124
|
+
this.batches.clear();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Clock, Object3D } from "three";
|
|
2
|
+
import InstancedChild, { instancedChildRegistry, instancedChildOwner } from "./InstancedChild";
|
|
3
|
+
import InstancedBatch from "./InstancedBatch";
|
|
4
|
+
|
|
5
|
+
const PICK_EVENT_TYPES = [
|
|
6
|
+
"mouseenter",
|
|
7
|
+
"mouseleave",
|
|
8
|
+
"mousemove",
|
|
9
|
+
"click",
|
|
10
|
+
"leftclick",
|
|
11
|
+
"rightclick",
|
|
12
|
+
"dbclick",
|
|
13
|
+
"mousedown",
|
|
14
|
+
"mouseup",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 固定在 ThreeBase.scene 里的分组器。每帧收集所有 InstancedChild,按 batchKey(glb+当前生效动画)分组,
|
|
19
|
+
* 为每个 key 创建/复用 InstancedBatch,把实例的世界矩阵 / 颜色同步进真实 InstancedMesh。
|
|
20
|
+
* batch 常驻(空桶不销毁),切换动画 = 实例在「glb+旧动画」「glb+新动画」两个常驻 batch 间迁移。
|
|
21
|
+
*
|
|
22
|
+
* 同时也是事件路由中枢:ThreeMouse 命中 InstancedMesh 后,通过本类把 (mesh, instanceId)
|
|
23
|
+
* 还原成对应的 InstancedChild,使逻辑实例成为透明事件接收端。
|
|
24
|
+
*/
|
|
25
|
+
export default class InstancedGroup extends Object3D {
|
|
26
|
+
/** batchKey -> InstancedBatch */
|
|
27
|
+
batches = new Map<string, InstancedBatch>();
|
|
28
|
+
private _clock = new Clock();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 本组的归属 tag(可选)。多 InstancedGroup 共存时用于隔离:
|
|
32
|
+
* - 声明了 owner 的组:**只**收集 `getOwner() === owner` 的实例(未声明归属的实例一律不收);
|
|
33
|
+
* - 未声明 owner 的组(如 ThreeBase 的默认组):只收**未声明归属**的实例,跳过已归属他组的实例。
|
|
34
|
+
* 这样主编辑器(默认组)与预览弹窗(owner="preview")各自只画自己的实例,互不污染。
|
|
35
|
+
*/
|
|
36
|
+
readonly owner?: string;
|
|
37
|
+
|
|
38
|
+
constructor(owner?: string) {
|
|
39
|
+
super();
|
|
40
|
+
this.owner = owner;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** 本组是否该收集该实例:按 owner 归属隔离(详见 owner 字段注释) */
|
|
44
|
+
private _accepts(child: InstancedChild): boolean {
|
|
45
|
+
const childOwner = instancedChildOwner.get(child);
|
|
46
|
+
if (this.owner === undefined) {
|
|
47
|
+
// 无归属的组(ThreeBase 默认组):只收未声明归属的实例,跳过已归属他组的
|
|
48
|
+
return childOwner === undefined;
|
|
49
|
+
}
|
|
50
|
+
// 有归属的组:只收 owner 完全一致的实例
|
|
51
|
+
return childOwner === this.owner;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** 每帧由 ThreeBase.render 在 scene.updateMatrixWorld 之后调用 */
|
|
55
|
+
update(): void {
|
|
56
|
+
const delta = this._clock.getDelta();
|
|
57
|
+
|
|
58
|
+
// 1. 按 batchKey 收集所有活着的 InstancedChild(按 owner 归属过滤,避免多组互相污染)
|
|
59
|
+
const groups = new Map<string, InstancedChild[]>();
|
|
60
|
+
for (const child of instancedChildRegistry) {
|
|
61
|
+
if (!this._accepts(child)) continue;
|
|
62
|
+
// 强制刷新世界矩阵(实例可能位于任意层级,不依赖 scene 遍历顺序)
|
|
63
|
+
child.updateWorldMatrix(true, false);
|
|
64
|
+
child.markDirtyIfWorldChanged();
|
|
65
|
+
const key = child.getBatchKey();
|
|
66
|
+
let arr = groups.get(key);
|
|
67
|
+
if (!arr) {
|
|
68
|
+
arr = [];
|
|
69
|
+
groups.set(key, arr);
|
|
70
|
+
}
|
|
71
|
+
arr.push(child);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 2. 创建 / 更新每个有实例的 batch(按 glb+当前动画分桶,每个动画一个常驻 batch)
|
|
75
|
+
for (const [key, children] of groups) {
|
|
76
|
+
let batch = this.batches.get(key);
|
|
77
|
+
if (!batch) {
|
|
78
|
+
batch = new InstancedBatch(key, children[0]);
|
|
79
|
+
this.batches.set(key, batch);
|
|
80
|
+
this.add(batch.group);
|
|
81
|
+
}
|
|
82
|
+
batch.sync(children, delta);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 3. 持久化:实例已迁移走的空 batch 仍需 sync([]) 把 count 归零,避免残留上一帧实例;
|
|
86
|
+
// 但【不销毁】,使其常驻可复用 —— 切换动作时实例在常驻 batch 间迁移,无需频繁创建/销毁 InstancedBatch。
|
|
87
|
+
for (const [key, batch] of this.batches) {
|
|
88
|
+
if (!groups.has(key)) {
|
|
89
|
+
batch.sync([], delta);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** 事件路由:命中 InstancedMesh 时返回对应 InstancedChild,否则 null */
|
|
95
|
+
resolveInstance(mesh: Object3D, instanceId: number | undefined): InstancedChild | null {
|
|
96
|
+
if (instanceId === undefined) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const key = (mesh as any).userData?.__batchKey as string | undefined;
|
|
100
|
+
if (!key) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const batch = this.batches.get(key);
|
|
104
|
+
if (!batch) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
return batch.resolveInstance(mesh as any, instanceId);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** 收集可拾取的 InstancedMesh(已就绪 batch,且注册了监听或设置了 cursor 的),供 ThreeMouse 候选 */
|
|
111
|
+
collectPickable(out: Object3D[]): void {
|
|
112
|
+
for (const batch of this.batches.values()) {
|
|
113
|
+
if (batch.ready && (batch.hasListener(PICK_EVENT_TYPES) || batch.hasCursor())) {
|
|
114
|
+
for (const im of batch.pickables) {
|
|
115
|
+
out.push(im);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 释放本组的全部 batch(实例缓冲 + 从组内摘除 mesh)。
|
|
123
|
+
*
|
|
124
|
+
* 用途:自建 InstancedGroup 的场景(如预览弹窗的独立轻量渲染器)在销毁时调用,
|
|
125
|
+
* 避免 InstancedMesh 的实例缓冲随组对象一起被丢弃却未释放。
|
|
126
|
+
*
|
|
127
|
+
* 注:batch 的 geometry/material **不会**被释放(InstancedBatch.dispose 刻意跳过)——
|
|
128
|
+
* 它们来自 `InstancedChild` 的 static geometry/material 或模板缓存,是跨实例共享资源。
|
|
129
|
+
* ⚠️ 默认的 ThreeBase 组**不要**调用本方法:它随应用生命周期常驻,batch 复用是设计意图。
|
|
130
|
+
*/
|
|
131
|
+
dispose(): void {
|
|
132
|
+
for (const batch of this.batches.values()) {
|
|
133
|
+
batch.dispose();
|
|
134
|
+
}
|
|
135
|
+
this.batches.clear();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { Color, Euler, InstancedBufferAttribute, InstancedMesh, Matrix4, Mesh, Object3D, Quaternion, Vector3 } from "three";
|
|
2
|
-
import MeshBase from "
|
|
1
|
+
import { Color, Event, Euler, InstancedBufferAttribute, InstancedMesh, Matrix4, Mesh, Object3D, Quaternion, Vector3 } from "three";
|
|
2
|
+
import MeshBase from "../MeshBase";
|
|
3
|
+
import { DebouncedFuncLeading } from "lodash";
|
|
3
4
|
export default class InstancedMeshBase extends MeshBase {
|
|
4
5
|
constructor(url?: string);
|
|
5
|
-
isInstancedGroupMesh: boolean;
|
|
6
6
|
count: number;
|
|
7
7
|
size: Vector3;
|
|
8
8
|
meshCollect: Record<string, Mesh[]>;
|
|
9
9
|
instanceCollect: Record<string, InstancedMesh>;
|
|
10
10
|
colorMap: Map<String, Color>;
|
|
11
|
-
children: InstancedMesh[];
|
|
12
11
|
static _position: Vector3;
|
|
13
12
|
static _scale: Vector3;
|
|
14
13
|
static _rotation: Euler;
|
|
@@ -18,7 +17,7 @@ export default class InstancedMeshBase extends MeshBase {
|
|
|
18
17
|
instanceColor: InstancedBufferAttribute;
|
|
19
18
|
reset(...object: Object3D[]): this;
|
|
20
19
|
loadModel(url: string | File | Array<File>, filename?: string): Promise<void>;
|
|
21
|
-
computeBoundingSphere:
|
|
20
|
+
computeBoundingSphere: DebouncedFuncLeading<() => void>;
|
|
22
21
|
setCount(count?: number): Promise<void>;
|
|
23
22
|
setMatrixAt(index: number, matrix?: Matrix4): Promise<void>;
|
|
24
23
|
setTRS(index: number, { position, rotation, scale, }: {
|
|
@@ -34,6 +33,6 @@ export default class InstancedMeshBase extends MeshBase {
|
|
|
34
33
|
setColorAt(index: number, color: (instancedMesh: InstancedMesh) => Color | undefined): Promise<void>;
|
|
35
34
|
getColorAt(index?: number): Promise<Color>;
|
|
36
35
|
updateMatrixWorld(force?: boolean): void;
|
|
37
|
-
addEventListener(name:
|
|
38
|
-
removeEventListener(name:
|
|
36
|
+
addEventListener<T extends string>(name: T, callback: (e: Event<T, this>) => void): Promise<void>;
|
|
37
|
+
removeEventListener<T extends string>(name: T, callback: (e: Event<T, this>) => void): void;
|
|
39
38
|
}
|
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
* @Author: xiaosihan
|
|
3
3
|
* @Date: 2024-02-05 09:31:57
|
|
4
4
|
* @Last Modified by: xiaosihan
|
|
5
|
-
* @Last Modified time:
|
|
5
|
+
* @Last Modified time: 2025-06-25
|
|
6
6
|
*/
|
|
7
7
|
import { first, isString, throttle } from "lodash";
|
|
8
|
-
import { Color, Euler, InstancedBufferAttribute, InstancedMesh, Matrix4, Quaternion, Vector3
|
|
9
|
-
import MeshBase from "
|
|
10
|
-
import threeUtils from "
|
|
11
|
-
import threeLoader from "
|
|
8
|
+
import { Color, Euler, InstancedBufferAttribute, InstancedMesh, Matrix4, Quaternion, Vector3 } from "three";
|
|
9
|
+
import MeshBase from "../MeshBase";
|
|
10
|
+
import threeUtils from "../threeUtils";
|
|
11
|
+
import threeLoader from "../threeLoader";
|
|
12
|
+
import { dispatchCustomEvent } from "../EventMap";
|
|
12
13
|
// 实例模型组对象
|
|
13
14
|
export default class InstancedMeshBase extends MeshBase {
|
|
14
15
|
constructor(url) {
|
|
15
16
|
super(url);
|
|
16
17
|
}
|
|
17
|
-
isInstancedGroupMesh = true;
|
|
18
18
|
count = 0;
|
|
19
19
|
size = new Vector3(1, 1, 1);
|
|
20
20
|
meshCollect = {};
|
|
@@ -62,6 +62,7 @@ export default class InstancedMeshBase extends MeshBase {
|
|
|
62
62
|
}
|
|
63
63
|
return modelurl;
|
|
64
64
|
}
|
|
65
|
+
return "";
|
|
65
66
|
})();
|
|
66
67
|
this.url = modelurl || "";
|
|
67
68
|
if (!url) {
|
|
@@ -83,33 +84,20 @@ export default class InstancedMeshBase extends MeshBase {
|
|
|
83
84
|
} while (MeshBase.loadModeling.get(this.url));
|
|
84
85
|
if (MeshBase.models.get(this.url)) {
|
|
85
86
|
const mesh = MeshBase.models.get(this.url);
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
// if (this.hasAnimation) {
|
|
89
|
-
// const model = SkeletonUtils.clone(mesh);
|
|
90
|
-
// this.mixer = new AnimationMixer(model);
|
|
91
|
-
// this.animationActions = {};
|
|
92
|
-
// mesh.animations.map(animation => {
|
|
93
|
-
// const animationAction = this.mixer!.clipAction(animation).play();
|
|
94
|
-
// animationAction.weight = 0;
|
|
95
|
-
// this.animationActions[animation.name] = animationAction;
|
|
96
|
-
// });
|
|
97
|
-
// this.add(model);
|
|
98
|
-
// } else {
|
|
99
|
-
// this.add(mesh.clone(true));
|
|
100
|
-
// }
|
|
87
|
+
// 兜底:与 MeshBase 一致,为缺失顶点法线的几何体补算法线,避免 AO 全黑与金属材质发黑
|
|
88
|
+
this.ensureNormals(mesh);
|
|
101
89
|
this.size.copy(threeUtils.getSize(mesh));
|
|
102
90
|
const { meshCollect, instanceCollect } = this;
|
|
103
91
|
mesh.updateMatrixWorld(true);
|
|
104
|
-
// collect all mesh counts
|
|
92
|
+
// collect all mesh counts
|
|
105
93
|
mesh.traverse((obj) => {
|
|
106
94
|
const mesh = obj;
|
|
107
95
|
if (mesh.isMesh) {
|
|
108
96
|
mesh.matrixWorld.decompose(mesh.position, mesh.quaternion, mesh.scale);
|
|
109
97
|
mesh.rotation.setFromQuaternion(mesh.quaternion);
|
|
110
98
|
mesh.updateMatrix();
|
|
111
|
-
|
|
112
|
-
const uuid = mesh.geometry.uuid + "/" +
|
|
99
|
+
const materialUuid = mesh.material instanceof Array ? mesh.material[0].uuid : mesh.material.uuid;
|
|
100
|
+
const uuid = mesh.geometry.uuid + "/" + materialUuid;
|
|
113
101
|
if (meshCollect[uuid]) {
|
|
114
102
|
meshCollect[uuid].push(mesh);
|
|
115
103
|
}
|
|
@@ -136,16 +124,16 @@ export default class InstancedMeshBase extends MeshBase {
|
|
|
136
124
|
});
|
|
137
125
|
// 加载完成事件
|
|
138
126
|
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
139
|
-
// 加载完成事件
|
|
140
127
|
this.loaded = true;
|
|
141
|
-
this
|
|
128
|
+
dispatchCustomEvent(this, { type: "loaded" });
|
|
142
129
|
}
|
|
143
130
|
}
|
|
144
131
|
// 更新包围盒
|
|
145
132
|
computeBoundingSphere = throttle(() => {
|
|
146
|
-
this.children
|
|
133
|
+
for (const child of this.children) {
|
|
134
|
+
const instancedMesh = child;
|
|
147
135
|
instancedMesh.computeBoundingSphere && instancedMesh.computeBoundingSphere();
|
|
148
|
-
}
|
|
136
|
+
}
|
|
149
137
|
}, 1000);
|
|
150
138
|
//初始实例矩阵的数量
|
|
151
139
|
async setCount(count = 0) {
|
|
@@ -166,25 +154,28 @@ export default class InstancedMeshBase extends MeshBase {
|
|
|
166
154
|
this.instanceMatrix = instanceMatrix;
|
|
167
155
|
this.instanceColor = instanceColor;
|
|
168
156
|
this.count = count;
|
|
169
|
-
this.children
|
|
170
|
-
const
|
|
171
|
-
const
|
|
157
|
+
for (const child of this.children) {
|
|
158
|
+
const instanceMesh = child;
|
|
159
|
+
const im = new InstancedBufferAttribute(new Float32Array(count * 16), 16);
|
|
160
|
+
const ic = new InstancedBufferAttribute(new Float32Array(count * 3).fill(1), 3);
|
|
172
161
|
for (let i = 0; i < count; i++) {
|
|
173
|
-
|
|
174
|
-
|
|
162
|
+
im.set(InstancedMeshBase._matrix.identity().toArray(), i * 16);
|
|
163
|
+
ic.set([1, 1, 1], i * 3);
|
|
175
164
|
}
|
|
176
165
|
for (let i = 0; i < instanceMesh.count; i++) {
|
|
177
|
-
|
|
178
|
-
|
|
166
|
+
im.copyAt(i, instanceMesh.instanceMatrix, i);
|
|
167
|
+
if (instanceMesh.instanceColor) {
|
|
168
|
+
ic.copyAt(i, instanceMesh.instanceColor, i);
|
|
169
|
+
}
|
|
179
170
|
}
|
|
180
|
-
instanceMesh.instanceMatrix =
|
|
181
|
-
instanceMesh.instanceColor =
|
|
171
|
+
instanceMesh.instanceMatrix = im;
|
|
172
|
+
instanceMesh.instanceColor = ic;
|
|
182
173
|
instanceMesh.count = count;
|
|
183
|
-
}
|
|
174
|
+
}
|
|
184
175
|
}
|
|
185
176
|
// 设置矩阵
|
|
186
177
|
async setMatrixAt(index, matrix = new Matrix4().identity()) {
|
|
187
|
-
const new_matrix = matrix.clone();
|
|
178
|
+
const new_matrix = matrix.clone();
|
|
188
179
|
await this.awaitLoaded();
|
|
189
180
|
Object.keys(this.meshCollect).forEach((uuid) => {
|
|
190
181
|
const instancedMesh = this.instanceCollect[uuid];
|
|
@@ -208,7 +199,6 @@ export default class InstancedMeshBase extends MeshBase {
|
|
|
208
199
|
const instancedMesh = this.instanceCollect[uuid];
|
|
209
200
|
const collect = this.meshCollect[uuid];
|
|
210
201
|
collect.forEach((mesh, i) => {
|
|
211
|
-
//获取对应的模型的矩阵
|
|
212
202
|
instancedMesh.getMatrixAt(collect.length * index + i, InstancedMeshBase._matrix);
|
|
213
203
|
InstancedMeshBase._matrix.premultiply(mesh.matrix.clone().invert());
|
|
214
204
|
InstancedMeshBase._matrix.decompose(InstancedMeshBase._position, InstancedMeshBase._Quaternion, InstancedMeshBase._scale);
|
|
@@ -278,16 +268,15 @@ export default class InstancedMeshBase extends MeshBase {
|
|
|
278
268
|
async addEventListener(name, callback) {
|
|
279
269
|
super.addEventListener(name, callback);
|
|
280
270
|
await this.awaitLoaded();
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
});
|
|
271
|
+
for (const child of this.children) {
|
|
272
|
+
child.addEventListener(name, callback);
|
|
273
|
+
}
|
|
285
274
|
}
|
|
286
275
|
// 取消事件的监听
|
|
287
276
|
removeEventListener(name, callback) {
|
|
288
277
|
super.removeEventListener(name, callback);
|
|
289
|
-
this.children
|
|
290
|
-
|
|
291
|
-
}
|
|
278
|
+
for (const child of this.children) {
|
|
279
|
+
child.removeEventListener(name, callback);
|
|
280
|
+
}
|
|
292
281
|
}
|
|
293
282
|
}
|