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,406 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AnimationAction,
|
|
3
|
+
AnimationMixer,
|
|
4
|
+
BufferGeometry,
|
|
5
|
+
Color,
|
|
6
|
+
DynamicDrawUsage,
|
|
7
|
+
Group,
|
|
8
|
+
InstancedBufferAttribute,
|
|
9
|
+
InstancedMesh,
|
|
10
|
+
Matrix4,
|
|
11
|
+
Object3D,
|
|
12
|
+
Skeleton,
|
|
13
|
+
Vector3,
|
|
14
|
+
} from "three";
|
|
15
|
+
import InstancedChild from "./InstancedChild";
|
|
16
|
+
import { InstancedTemplate, loadTemplate } from "./InstancedTemplate";
|
|
17
|
+
import FPScontrol from "../FPScontrol";
|
|
18
|
+
|
|
19
|
+
const _m = new Matrix4();
|
|
20
|
+
const _v = new Vector3();
|
|
21
|
+
const _n = new Vector3();
|
|
22
|
+
const _skinMat = new Matrix4();
|
|
23
|
+
const _tmpMat = new Matrix4();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 把 skeleton 当前姿态烘焙进 geometry(线性混合蒙皮,LBS)。
|
|
27
|
+
* 由于同一 batch 内所有实例共享同一套骨骼矩阵(共享姿态),每帧只需烘焙一份几何,
|
|
28
|
+
* 所有实例复用 —— 这是把"GPU skinning + instancing"降级为"每帧 CPU 烘焙一份共享几何 + instancing"的关键,
|
|
29
|
+
* 复杂度从自定义 shader 降到几何更新,天然兼容项目所有材质与后处理。
|
|
30
|
+
*/
|
|
31
|
+
function bakeSkinning(
|
|
32
|
+
srcGeom: BufferGeometry,
|
|
33
|
+
dstGeom: BufferGeometry,
|
|
34
|
+
skeleton: Skeleton,
|
|
35
|
+
bindMatrix: Matrix4,
|
|
36
|
+
bindMatrixInverse: Matrix4
|
|
37
|
+
): void {
|
|
38
|
+
// 从 srcGeom(原始 bind-pose,只读)逐顶点读取,计算结果写入 dstGeom(渲染用几何)。
|
|
39
|
+
// 绝不能读改写同一份几何 —— 否则上一帧烘焙结果被当成 bind-pose 再烤一遍,蒙皮累积错位。
|
|
40
|
+
const pos = srcGeom.attributes.position;
|
|
41
|
+
const norm = srcGeom.attributes.normal;
|
|
42
|
+
const si = srcGeom.attributes.skinIndex;
|
|
43
|
+
const sw = srcGeom.attributes.skinWeight;
|
|
44
|
+
const dpos = dstGeom.attributes.position;
|
|
45
|
+
const dnorm = dstGeom.attributes.normal;
|
|
46
|
+
if (!pos || !si || !sw || !dpos) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const boneMatrices = skeleton.boneMatrices; // Float32Array,长度 = bones * 16
|
|
50
|
+
if (!boneMatrices) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
for (let i = 0; i < pos.count; i++) {
|
|
54
|
+
// 与 three 着色器保持一致:先 bindMatrix 把顶点转到"骨骼局部坐标系",
|
|
55
|
+
// 再叠加骨骼混合矩阵,最后 bindMatrixInverse 回到模型空间。顺序反了动画姿态会错。
|
|
56
|
+
_v.fromBufferAttribute(pos, i).applyMatrix4(bindMatrix);
|
|
57
|
+
_skinMat.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
58
|
+
for (let j = 0; j < 4; j++) {
|
|
59
|
+
const w = sw.getComponent(i, j);
|
|
60
|
+
if (w === 0) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const bi = si.getComponent(i, j);
|
|
64
|
+
_tmpMat.fromArray(boneMatrices, bi * 16);
|
|
65
|
+
_tmpMat.multiplyScalar(w);
|
|
66
|
+
const se = _skinMat.elements;
|
|
67
|
+
const te = _tmpMat.elements;
|
|
68
|
+
for (let k = 0; k < 16; k++) {
|
|
69
|
+
se[k] += te[k];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
_v.applyMatrix4(_skinMat).applyMatrix4(bindMatrixInverse);
|
|
73
|
+
dpos.setXYZ(i, _v.x, _v.y, _v.z);
|
|
74
|
+
if (norm && dnorm) {
|
|
75
|
+
_n.fromBufferAttribute(norm, i).applyMatrix4(_skinMat).normalize();
|
|
76
|
+
dnorm.setXYZ(i, _n.x, _n.y, _n.z);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
dpos.needsUpdate = true;
|
|
80
|
+
if (dnorm) {
|
|
81
|
+
dnorm.needsUpdate = true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 把节点相对根的世界变换烘焙进几何(非骨骼节点动画的实例化方案)。
|
|
87
|
+
* 与骨骼 LBS 同理:把"节点层级变换"降级为每帧 CPU 几何变换,
|
|
88
|
+
* 所有实例复用一份变换后的几何,天然兼容项目所有材质与后处理。
|
|
89
|
+
*/
|
|
90
|
+
function bakeNodeTransform(
|
|
91
|
+
geometry: BufferGeometry,
|
|
92
|
+
local: BufferGeometry,
|
|
93
|
+
m: Matrix4
|
|
94
|
+
): void {
|
|
95
|
+
const pos = geometry.attributes.position;
|
|
96
|
+
const lp = local.attributes.position;
|
|
97
|
+
if (!pos || !lp) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
for (let i = 0; i < lp.count; i++) {
|
|
101
|
+
_v.fromBufferAttribute(lp, i).applyMatrix4(m);
|
|
102
|
+
pos.setXYZ(i, _v.x, _v.y, _v.z);
|
|
103
|
+
}
|
|
104
|
+
pos.needsUpdate = true;
|
|
105
|
+
const norm = geometry.attributes.normal;
|
|
106
|
+
const ln = local.attributes.normal;
|
|
107
|
+
if (norm && ln) {
|
|
108
|
+
for (let i = 0; i < ln.count; i++) {
|
|
109
|
+
_n.fromBufferAttribute(ln, i).applyMatrix4(m).normalize();
|
|
110
|
+
norm.setXYZ(i, _n.x, _n.y, _n.z);
|
|
111
|
+
}
|
|
112
|
+
norm.needsUpdate = true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 内部类:封装一个 batch 的真实 InstancedMesh(多 submesh 时每个 submesh 一个,共享同一组实例序号)。
|
|
118
|
+
* - 按 batchKey 分组(相同 geometry+material 或相同 instanceUrl+animation 为一类);
|
|
119
|
+
* - 增量同步:仅 needUpdate=true 的实例才重写 matrix/color,写完后立即置 false;
|
|
120
|
+
* - 列表变化(增删/重排)时全量 compact 重写并重建索引;
|
|
121
|
+
* - 含骨骼动画时:用共享姿态 AnimationMixer 驱动 skeleton,每帧烘焙到共享几何(LBS);
|
|
122
|
+
* - 含非骨骼节点动画时:同样用共享姿态 AnimationMixer 驱动节点变换,每帧把"节点相对根的世界变换"
|
|
123
|
+
* 烘焙进几何(节点变换动画的实例化方案)。
|
|
124
|
+
*/
|
|
125
|
+
export default class InstancedBatch {
|
|
126
|
+
key: string;
|
|
127
|
+
/** 渲染组:持有本 batch 的 InstancedMesh,由 InstancedGroup add 进 scene */
|
|
128
|
+
group: Group = new Group();
|
|
129
|
+
template?: InstancedTemplate;
|
|
130
|
+
ready: boolean = false;
|
|
131
|
+
|
|
132
|
+
private _children: InstancedChild[] = [];
|
|
133
|
+
private _meshes: InstancedMesh[] = [];
|
|
134
|
+
private _capacity: number;
|
|
135
|
+
private _step: number;
|
|
136
|
+
private _loading?: Promise<void>;
|
|
137
|
+
private _mixer?: AnimationMixer;
|
|
138
|
+
private _clipName?: string;
|
|
139
|
+
/** 当前正在播放的动画剪辑名(运行时可由 child.playClip 切换) */
|
|
140
|
+
private _currentClip?: string;
|
|
141
|
+
/** 首例实例是否声明碰撞(构造时快照,buildMeshes 用它给 InstancedMesh 打标记) */
|
|
142
|
+
private _sampleFpsCollision: boolean = false;
|
|
143
|
+
/** 当前正在播放的 AnimationAction(切换时先 stop 旧的) */
|
|
144
|
+
private _currentAction?: AnimationAction;
|
|
145
|
+
/** 非骨骼动画:需每帧烘焙节点变换的几何列表(与 _meshes 顺序对齐) */
|
|
146
|
+
private _nodeBake: { node: Object3D; local: BufferGeometry; render: BufferGeometry }[] = [];
|
|
147
|
+
|
|
148
|
+
constructor(key: string, sample: InstancedChild, capacity = 100, step = 100) {
|
|
149
|
+
this.key = key;
|
|
150
|
+
this._capacity = capacity;
|
|
151
|
+
this._step = step;
|
|
152
|
+
// 用实例当前生效动画(playClip 优先,否则 static animation)构建本 batch 的 mixer;
|
|
153
|
+
// 因 key 已含动画名,相同 glb+动画的实例都进同一个常驻 batch
|
|
154
|
+
this._clipName = sample.getEffectiveAnimation();
|
|
155
|
+
this._sampleFpsCollision = !!sample.fpsCollision;
|
|
156
|
+
this._loading = loadTemplate(sample).then((t) => {
|
|
157
|
+
const ctor = sample.constructor as typeof InstancedChild;
|
|
158
|
+
// GLB 模板就绪后,交给子类按需修正材质(如瓶子 metalness 修正)
|
|
159
|
+
if (typeof ctor.prepareTemplate === "function") {
|
|
160
|
+
ctor.prepareTemplate(t);
|
|
161
|
+
}
|
|
162
|
+
this.template = t;
|
|
163
|
+
this.buildMeshes();
|
|
164
|
+
this.ready = true;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** 模板就绪后,为每个 submesh 建一个 InstancedMesh,并启动动画(若有) */
|
|
169
|
+
private buildMeshes(): void {
|
|
170
|
+
if (!this.template) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
for (const sub of this.template.submeshes) {
|
|
174
|
+
const geom =
|
|
175
|
+
sub.skinned && this.template.sharedGeometry
|
|
176
|
+
? this.template.sharedGeometry
|
|
177
|
+
: sub.geometry;
|
|
178
|
+
const im = new InstancedMesh(geom, sub.material, this._capacity);
|
|
179
|
+
im.frustumCulled = false;
|
|
180
|
+
im.instanceMatrix.setUsage(DynamicDrawUsage);
|
|
181
|
+
// 预创建 instanceColor(默认白,setColorAt 会覆盖)
|
|
182
|
+
im.instanceColor = new InstancedBufferAttribute(
|
|
183
|
+
new Float32Array(this._capacity * 3).fill(1),
|
|
184
|
+
3
|
|
185
|
+
);
|
|
186
|
+
im.count = 0;
|
|
187
|
+
im.userData.__batchKey = this.key;
|
|
188
|
+
// 若本 batch 的实例声明了碰撞检测,则把真实 InstancedMesh 标记为碰撞体,
|
|
189
|
+
// 供 FPScontrol 的碰撞射线命中(实例化对象的几何合并在 InstancedMesh 里,只能整体标记)。
|
|
190
|
+
im.userData.fpsCollision = this._sampleFpsCollision;
|
|
191
|
+
this._meshes.push(im);
|
|
192
|
+
this.group.add(im);
|
|
193
|
+
// 非骨骼动画:记录节点烘焙信息,每帧把节点相对根的世界变换写进几何
|
|
194
|
+
if (sub.node && this.template.animRoot && !this.template.skeleton && sub.localGeometry) {
|
|
195
|
+
this._nodeBake.push({ node: sub.node, local: sub.localGeometry, render: sub.geometry });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// 模板就绪即标记碰撞体,并通知所有 FPScontrol 重新收集(模板是异步加载的,
|
|
199
|
+
// 此时控制器多半已先创建,必须主动通知它重建碰撞列表,否则实例化碰撞体不生效)。
|
|
200
|
+
if (this._sampleFpsCollision) {
|
|
201
|
+
FPScontrol.markAllCollidersDirty();
|
|
202
|
+
}
|
|
203
|
+
// 动画:共享姿态,一个 mixer 驱动整批(骨骼 LBS 与非骨骼节点动画都走这里)。
|
|
204
|
+
// 一个 batch 只对应一种动画(key = glb+动画),故 mixer 启动即播放该动画、整批同源同步。
|
|
205
|
+
// 仅当本 batch 的动画名(_clipName)存在且 GLB 自带剪辑才启动;否则渲染静态绑定姿态(bind pose)。
|
|
206
|
+
if (this._clipName && this.template.animRoot && this.template.animations.length) {
|
|
207
|
+
const clip =
|
|
208
|
+
this.template.animations.find((c) => c.name === this._clipName) ||
|
|
209
|
+
this.template.animations[0];
|
|
210
|
+
if (clip) {
|
|
211
|
+
this._mixer = new AnimationMixer(this.template.animRoot);
|
|
212
|
+
const action = this._mixer.clipAction(clip);
|
|
213
|
+
action.play();
|
|
214
|
+
this._currentAction = action;
|
|
215
|
+
this._currentClip = clip.name;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** 每帧同步:children 为按 batchKey 收集的 InstancedChild 列表 */
|
|
221
|
+
sync(children: InstancedChild[], delta: number): void {
|
|
222
|
+
if (!this.ready || !this.template) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const listChanged =
|
|
226
|
+
this._children.length !== children.length ||
|
|
227
|
+
this._children.some((c, i) => c !== children[i]);
|
|
228
|
+
if (listChanged) {
|
|
229
|
+
this._children = children;
|
|
230
|
+
this.ensureCapacity(children.length);
|
|
231
|
+
this.compactWrite();
|
|
232
|
+
// 列表变化(含首帧就绪):重新同步碰撞标记——实例可能在创建后才改 fpsCollision,
|
|
233
|
+
// 用最新 children[0] 的标记覆盖,并在开启时通知 FPScontrol 重建碰撞列表。
|
|
234
|
+
this._applyCollisionFlag();
|
|
235
|
+
} else {
|
|
236
|
+
// 增量:只写 needUpdate 的实例,写完后立即置 false(避免重复写)
|
|
237
|
+
for (let i = 0; i < children.length; i++) {
|
|
238
|
+
const c = children[i];
|
|
239
|
+
if (c.needUpdate) {
|
|
240
|
+
this.writeInstance(i, c);
|
|
241
|
+
c.needUpdate = false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// 动画(共享姿态):更新骨骼/节点 → 烘焙到几何
|
|
246
|
+
// 注意:本 batch 只对应一种动画(key 已含动画名),动作切换由「实例迁移到另一个常驻 batch」完成,
|
|
247
|
+
// 故这里不再做 batch 内的 mixer 切换,只需推进当前动画即可。
|
|
248
|
+
if (this._mixer && this.template.animRoot) {
|
|
249
|
+
this._mixer.update(delta);
|
|
250
|
+
this.template.animRoot.updateMatrixWorld(true); // 触发骨架/skeleton 计算
|
|
251
|
+
if (
|
|
252
|
+
this.template.skeleton &&
|
|
253
|
+
this.template.sourceGeometry &&
|
|
254
|
+
this.template.sharedGeometry &&
|
|
255
|
+
this.template.bindMatrix &&
|
|
256
|
+
this.template.bindMatrixInverse
|
|
257
|
+
) {
|
|
258
|
+
// 关键:mixer 已更新 bone 的世界矩阵,但骨骼矩阵(boneMatrices)需显式重算,
|
|
259
|
+
// 否则每帧都停在构造时的绑定姿态(士兵会变成静止 T-pose 甚至退化不可见)。
|
|
260
|
+
this.template.skeleton.update();
|
|
261
|
+
// 骨骼 LBS 烘焙:从 sourceGeometry(bind-pose)读、写到 sharedGeometry(渲染用),避免累积。
|
|
262
|
+
bakeSkinning(
|
|
263
|
+
this.template.sourceGeometry,
|
|
264
|
+
this.template.sharedGeometry,
|
|
265
|
+
this.template.skeleton,
|
|
266
|
+
this.template.bindMatrix,
|
|
267
|
+
this.template.bindMatrixInverse
|
|
268
|
+
);
|
|
269
|
+
} else if (this._nodeBake.length) {
|
|
270
|
+
// 非骨骼节点动画:把每个节点相对根的世界变换烘焙进几何
|
|
271
|
+
const rootInv = _tmpMat.copy(this.template.animRoot.matrixWorld).invert();
|
|
272
|
+
for (const nb of this._nodeBake) {
|
|
273
|
+
_m.copy(nb.node.matrixWorld).multiply(rootInv);
|
|
274
|
+
bakeNodeTransform(nb.render, nb.local, _m);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// 渲染数 = 当前子实例数
|
|
279
|
+
for (const im of this._meshes) {
|
|
280
|
+
im.count = children.length;
|
|
281
|
+
// ⚠️ 实例数量/矩阵变化后必须清空包围球:three 的 InstancedMesh.raycast 只在
|
|
282
|
+
// boundingSphere === null 时才重算,而它首次被计算时 count 往往还是 0
|
|
283
|
+
// (模板异步加载),算出的是「空球」radius = -1 并被永久缓存 →
|
|
284
|
+
// 后续所有射线检测(FPS 碰撞、鼠标 hover/click)在包围球剔除阶段就被全部跳过。
|
|
285
|
+
// 置 null 后由下一次 raycast 按需重算(本类 frustumCulled=false,不会每帧白算)。
|
|
286
|
+
im.boundingSphere = null;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** 容量不足时按 step 扩容(只扩不缩,避免频繁 realloc) */
|
|
291
|
+
private ensureCapacity(n: number): void {
|
|
292
|
+
if (n <= this._capacity) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
// 容量按 100 为一阶阶梯增长、只增不降:1 个对象→100 矩阵,101 个→200 矩阵,201 个→300 ...
|
|
296
|
+
// 避免每超出即整段 realloc,也避免从 200 回降到 100 造成反复分配/收缩。
|
|
297
|
+
const newCap = Math.ceil(n / 100) * 100;
|
|
298
|
+
for (const im of this._meshes) {
|
|
299
|
+
const imMat = new InstancedBufferAttribute(new Float32Array(newCap * 16), 16);
|
|
300
|
+
const imCol = new InstancedBufferAttribute(
|
|
301
|
+
new Float32Array(newCap * 3).fill(1),
|
|
302
|
+
3
|
|
303
|
+
);
|
|
304
|
+
for (let i = 0; i < im.count; i++) {
|
|
305
|
+
imMat.copyAt(i, im.instanceMatrix, i);
|
|
306
|
+
if (im.instanceColor) {
|
|
307
|
+
imCol.copyAt(i, im.instanceColor, i);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
im.instanceMatrix = imMat;
|
|
311
|
+
im.instanceColor = imCol;
|
|
312
|
+
}
|
|
313
|
+
this._capacity = newCap;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** 全量重写所有实例(列表变化时调用) */
|
|
317
|
+
private compactWrite(): void {
|
|
318
|
+
for (let i = 0; i < this._children.length; i++) {
|
|
319
|
+
this.writeInstance(i, this._children[i]);
|
|
320
|
+
this._children[i].needUpdate = false;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** 根据当前首批实例的 fpsCollision 标记,把真实 InstancedMesh 的 userData.fpsCollision 同步过去,
|
|
325
|
+
* 并在开启时通知所有 FPScontrol 重建碰撞列表。碰撞以整个 batch 为单位开关。 */
|
|
326
|
+
private _applyCollisionFlag(): void {
|
|
327
|
+
const col = this._children.length > 0 && this._children[0].fpsCollision;
|
|
328
|
+
for (const im of this._meshes) {
|
|
329
|
+
im.userData.fpsCollision = col;
|
|
330
|
+
}
|
|
331
|
+
if (col) {
|
|
332
|
+
FPScontrol.markAllCollidersDirty();
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** 把单个实例的 matrix + color 写入所有 submesh 的对应槽位 */
|
|
337
|
+
private writeInstance(i: number, child: InstancedChild): void {
|
|
338
|
+
for (let k = 0; k < this._meshes.length; k++) {
|
|
339
|
+
const im = this._meshes[k];
|
|
340
|
+
const sub = this.template!.submeshes[k];
|
|
341
|
+
// 叠加 GLB 节点层级的世界变换(如 Soldier.glb 根节点 scale=0.01),
|
|
342
|
+
// 使实例化渲染与"把整棵 GLB 场景图加进场景"一致(对齐 MeshBase 行为)。
|
|
343
|
+
_m.copy(child.matrixWorld);
|
|
344
|
+
if (sub.nodeWorldMatrix) {
|
|
345
|
+
_m.multiply(sub.nodeWorldMatrix);
|
|
346
|
+
}
|
|
347
|
+
im.setMatrixAt(i, _m);
|
|
348
|
+
im.instanceMatrix.needsUpdate = true;
|
|
349
|
+
if (im.instanceColor) {
|
|
350
|
+
im.setColorAt(i, child.color);
|
|
351
|
+
im.instanceColor.needsUpdate = true;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** 事件路由:给定 InstancedMesh 与 instanceId,返回对应 InstancedChild */
|
|
357
|
+
resolveInstance(mesh: InstancedMesh, instanceId: number): InstancedChild | null {
|
|
358
|
+
if (!mesh.userData.__batchKey || mesh.userData.__batchKey !== this.key) {
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
return this._children[instanceId] ?? null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** 该 batch 是否有任一 child 注册了指定类型监听(供 ThreeMouse 决定是否可拾取) */
|
|
365
|
+
hasListener(types: string[]): boolean {
|
|
366
|
+
for (const c of this._children) {
|
|
367
|
+
const listeners = (c as any)._listeners as
|
|
368
|
+
| Record<string, unknown[]>
|
|
369
|
+
| undefined;
|
|
370
|
+
if (listeners && types.some((t) => listeners[t] && listeners[t].length)) {
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/** 该 batch 是否有任一 child 设置了鼠标手势(cursor),供 InstancedGroup 决定是否收集 */
|
|
378
|
+
hasCursor(): boolean {
|
|
379
|
+
for (const c of this._children) {
|
|
380
|
+
if (c.userData && c.userData.cursor) {
|
|
381
|
+
return true;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** 拾取用 InstancedMesh 列表 */
|
|
388
|
+
get pickables(): InstancedMesh[] {
|
|
389
|
+
return this._meshes;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
dispose(): void {
|
|
393
|
+
for (const im of this._meshes) {
|
|
394
|
+
// 只释放 InstancedMesh 自带的实例缓冲(instanceMatrix / instanceColor),
|
|
395
|
+
// 不能 dispose geometry/material —— 它们来自 static geometry 或模板缓存,是共享资源,
|
|
396
|
+
// 一旦 dispose,后续重建同 batch 时会拿到已销毁的几何/材质导致渲染异常。
|
|
397
|
+
im.dispose();
|
|
398
|
+
this.group.remove(im);
|
|
399
|
+
}
|
|
400
|
+
this._meshes = [];
|
|
401
|
+
this._nodeBake = [];
|
|
402
|
+
if (this._mixer) {
|
|
403
|
+
this._mixer.stopAllAction();
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { BufferGeometry, Color, Group, Material, Object3D } from "three";
|
|
2
|
+
import type { InstancedTemplate } from "./InstancedTemplate";
|
|
3
|
+
/** 选择代理可叠加的后处理效果开关;默认只开描边(保持向后兼容),可被 glow / bloom / cover 任意叠加。 */
|
|
4
|
+
export interface SelectProxyOptions {
|
|
5
|
+
stroke?: boolean;
|
|
6
|
+
glow?: boolean;
|
|
7
|
+
bloom?: boolean;
|
|
8
|
+
cover?: boolean;
|
|
9
|
+
/** 描边粗细(像素级,越大越粗);不传则沿用 stroke_pass 默认 1 */
|
|
10
|
+
stroke_width?: number;
|
|
11
|
+
stroke_color?: string;
|
|
12
|
+
glow_color?: string;
|
|
13
|
+
glow_width?: number;
|
|
14
|
+
glow_opacity?: number;
|
|
15
|
+
bloom_strength?: number;
|
|
16
|
+
bloom_radius?: number;
|
|
17
|
+
cover_color?: string;
|
|
18
|
+
cover_opacity?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 所有活着的 InstancedChild 实例集合,供 InstancedGroup 每帧收集。
|
|
22
|
+
* 用全局注册表而非遍历整棵 scene,避免大场景每帧 traverse 全树的性能开销。
|
|
23
|
+
*
|
|
24
|
+
* ⚠️ 多 InstancedGroup 共存时的归属(owner):见 `instancedChildOwner` 与 `getOwner()`。
|
|
25
|
+
* 同一个进程里可能并存多个 InstancedGroup(如主编辑器的 ThreeBase 与预览弹窗各自一套),
|
|
26
|
+
* 若不隔离,双方会互相把对方的实例收进自己的 batch,导致「A 场景的立方体出现在 B 场景」。
|
|
27
|
+
*/
|
|
28
|
+
export declare const instancedChildRegistry: Set<InstancedChild>;
|
|
29
|
+
/**
|
|
30
|
+
* 实例归属(owner tag)注册表:child -> owner 字符串。
|
|
31
|
+
* InstancedGroup 构造时可声明自己的 owner,每帧只收集 `owner` 匹配(或未声明 owner)的实例。
|
|
32
|
+
* 未在表中登记的实例视为「无归属」,会被**声明了 owner 的组**忽略、
|
|
33
|
+
* 被**未声明 owner 的组**(如 ThreeBase 的默认组)收集 —— 保持对既有代码零影响。
|
|
34
|
+
*/
|
|
35
|
+
export declare const instancedChildOwner: Map<InstancedChild, string>;
|
|
36
|
+
/**
|
|
37
|
+
* 用户继承的「逻辑实例」基类。自身不参与渲染,只描述"一个实例"的位置 / 颜色 / 事件。
|
|
38
|
+
*
|
|
39
|
+
* 用法:
|
|
40
|
+
* ```ts
|
|
41
|
+
* class Cube extends InstancedChild {
|
|
42
|
+
* static geometry = new BoxGeometry(1, 1, 1);
|
|
43
|
+
* static material = new MeshStandardMaterial({ color: 0xffffff });
|
|
44
|
+
* constructor() {
|
|
45
|
+
* super();
|
|
46
|
+
* this.addEventListener("click", () => {
|
|
47
|
+
* this.setColor("#ff0000");
|
|
48
|
+
* this.position.set(0, 0, 0);
|
|
49
|
+
* });
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
* const cube = new Cube();
|
|
53
|
+
* cube.position.set(1, 2, 3);
|
|
54
|
+
* threeBase.scene.add(cube); // 加进任意层级即可
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* ThreeBase 里固定的 InstancedGroup 会自动把「同 geometry+material」(或「同 instanceUrl+animation」)
|
|
58
|
+
* 的 InstancedChild 合并进同一个 InstancedMesh 批量渲染,无需用户管理实例缓冲。
|
|
59
|
+
*/
|
|
60
|
+
export default class InstancedChild extends Object3D {
|
|
61
|
+
/** 内置几何模式:子类覆盖这两个静态字段 */
|
|
62
|
+
static geometry?: BufferGeometry;
|
|
63
|
+
/** 内置几何模式材质 */
|
|
64
|
+
static material?: Material;
|
|
65
|
+
/** GLB 模式:模型地址(与 geometry/material 互斥,优先于内置) */
|
|
66
|
+
static instanceUrl?: string;
|
|
67
|
+
/** GLB 模式:要播放的动画剪辑名;不同动画 = 不同 batch(彼此独立) */
|
|
68
|
+
static animation?: string;
|
|
69
|
+
/** GLB 模式:模板(含材质)加载完成后回调,用于按需修正材质(如瓷器瓶 metalness=1 改 0) */
|
|
70
|
+
static prepareTemplate?: (template: InstancedTemplate) => void;
|
|
71
|
+
/** 实例颜色,写入 InstancedMesh.instanceColor */
|
|
72
|
+
color: Color;
|
|
73
|
+
/**
|
|
74
|
+
* 脏标记:true 时本帧由 InstancedGroup 把本实例的 matrix/color 写回实例缓冲,
|
|
75
|
+
* 写完后立即置 false —— 避免没有改动时重复写入真正参与渲染的 InstancedMesh。
|
|
76
|
+
*/
|
|
77
|
+
needUpdate: boolean;
|
|
78
|
+
/** 模板是否就绪(GLB 异步加载完成后置 true) */
|
|
79
|
+
templateReady: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* 第一人称碰撞检测标记:为 true 时,本实例所属 batch 的 InstancedMesh 会被打上
|
|
82
|
+
* `userData.fpsCollision = true`,从而被 FPScontrol 的碰撞射线命中(实例自身的世界矩阵即碰撞体位置)。
|
|
83
|
+
* 需配合 three-base 的 FPScontrol 使用(详见 FPSDemo 示例页)。
|
|
84
|
+
*
|
|
85
|
+
* ⚠️ 语义约束:真实几何合并在同一个 InstancedMesh 里,故碰撞以「整个 batch
|
|
86
|
+
* (相同 geometry+material / 相同 instanceUrl+animation 的所有实例)」为单位开关——
|
|
87
|
+
* 同一 batch 内的所有实例要么都参与碰撞、要么都不参与。若只想让部分实例参与碰撞,
|
|
88
|
+
* 请把它们拆成不同的 InstancedChild 子类(不同 geometry 或不同 batchKey)。
|
|
89
|
+
*/
|
|
90
|
+
fpsCollision: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* 自动脏检测开关(实验用,默认开):
|
|
93
|
+
* - true:markDirtyIfWorldChanged 每帧比对 matrixWorld,变化则自动置 needUpdate(位置/旋转/缩放变化无需手动置位);
|
|
94
|
+
* - false:关闭自动检测,仅手动置 needUpdate(如 setColor)的实例才会写回实例缓冲。
|
|
95
|
+
* 关闭后,静态布置的实例渲染不变,但「运动」(旋转/移动/缩放)将不再反映到画面(因为 matrixWorld 变了却没写回)。
|
|
96
|
+
* 可在运行时于控制台切换:InstancedChild.AUTO_MARK_DIRTY = true / false。
|
|
97
|
+
*/
|
|
98
|
+
static AUTO_MARK_DIRTY: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* 请求切换到的动画剪辑名(由所属 batch 每帧应用)。
|
|
101
|
+
* 实例化是"整批共享一个 mixer",动画是 batch 级而非实例级;
|
|
102
|
+
* 默认 null 表示不主动切换,沿用 batch 构造时的 static animation。
|
|
103
|
+
*/
|
|
104
|
+
private _pendingClip;
|
|
105
|
+
private _cachedWorld;
|
|
106
|
+
constructor(fpsCollision?: boolean);
|
|
107
|
+
/** 该实例归属哪个 batch 的 key:相同 geometry+material 或相同 instanceUrl+当前生效动画 合并为一类。
|
|
108
|
+
* 因 key 含「当前生效动画」,切换动画 = 实例在「glb+旧动画」与「glb+新动画」两个常驻 batch 间迁移,而非 batch 内切 mixer。 */
|
|
109
|
+
getBatchKey(): string;
|
|
110
|
+
/** 本实例当前生效的动画名:优先 playClip 请求,否则类声明的 static animation;用于分桶 key 与 batch mixer 构建 */
|
|
111
|
+
getEffectiveAnimation(): string | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* 为一组 InstancedChild 构建"选择代理"(select proxy):
|
|
114
|
+
* 用每个实例的世界矩阵 matrixWorld + 同款 geometry/material 重建独立网格,
|
|
115
|
+
* 供 stroke_pass 单独渲染其深度 / 灰度,从而得到这组实例"专属"的描边。
|
|
116
|
+
*
|
|
117
|
+
* 为什么需要它:InstancedChild 是纯逻辑节点(无几何),真实几何在 InstancedGroup 合并出的
|
|
118
|
+
* InstancedMesh 里,且一个 InstancedMesh 通常包含很多实例。要单独描边"某一个 / 某一组"实例,
|
|
119
|
+
* 无法靠直接渲染该 InstancedChild(空)或整个 InstancedMesh(会描到所有实例)。
|
|
120
|
+
* 本方法把目标实例重新"画"成一份独立几何,就能像普通 Mesh 一样单独取深度、单独描边。
|
|
121
|
+
*
|
|
122
|
+
* 关键点:
|
|
123
|
+
* - 用 instance.matrixWorld 直接作为代理网格的 local matrix(代理本身不在 scene 内、无父变换),
|
|
124
|
+
* 因此代理在屏幕上的位置 / 深度与真实实例严格一致,描边轮廓正好贴合,且能被同一张 baseDepthTexture 正确遮挡;
|
|
125
|
+
* - 代理对象通常注册到 ThreeBase.addStrokeObject(不在 scene 内),由渲染管线在效果阶段描边,
|
|
126
|
+
* 不会被正常 base pass 重复绘制。
|
|
127
|
+
*
|
|
128
|
+
* 仅支持内置几何模式(static geometry + static material);GLB 模式的几何由 batch 解析,
|
|
129
|
+
* 需从对应 InstancedBatch 取几何,此处暂跳过(无 static geometry 的实例不进代理)。
|
|
130
|
+
*
|
|
131
|
+
* @param children 要描边的一组实例(如某个 Group 的全部 InstancedChild)
|
|
132
|
+
* @returns 代理 Group(含顺序与 children 一一对应的代理网格);若没有任何可重建的实例则返回 null
|
|
133
|
+
*/
|
|
134
|
+
static buildSelectProxy(children: InstancedChild[], options?: SelectProxyOptions): {
|
|
135
|
+
proxy: Group;
|
|
136
|
+
sources: InstancedChild[];
|
|
137
|
+
} | null;
|
|
138
|
+
/** 把代理网格的矩阵同步到各源实例最新的 matrixWorld(组旋转 / 实例移动时描边跟着动)。
|
|
139
|
+
* 代理网格与 sources 一一对应(顺序由 buildSelectProxy 保证)。 */
|
|
140
|
+
static syncSelectProxy(proxy: Group): void;
|
|
141
|
+
/** 设置实例颜色(自动置 needUpdate) */
|
|
142
|
+
setColor(c: Color | number | string): this;
|
|
143
|
+
/**
|
|
144
|
+
* 请求切换本实例的动画剪辑。batch 按「glb + 当前生效动画」分桶(每个动画一个常驻 InstancedBatch),
|
|
145
|
+
* 调用 playClip 只改变本实例的生效动画,下一帧 getBatchKey 随之变化,实例便自动从旧动画 batch
|
|
146
|
+
* 迁移到新动画 batch(两 batch 常驻、不销毁)。传 null 恢复为 static animation。
|
|
147
|
+
* 例:soldier.playClip("Walk") / soldier.playClip("Idle")
|
|
148
|
+
*/
|
|
149
|
+
playClip(name: string | null): this;
|
|
150
|
+
/** 供 InstancedBatch 每帧读取当前请求的动画剪辑 */
|
|
151
|
+
get pendingClip(): string | null;
|
|
152
|
+
/**
|
|
153
|
+
* 位置 / 缩放 / 旋转直接用 three 原生写法即可(position.set / scale.setScalar / rotation.set),
|
|
154
|
+
* 无需任何自定义 setter;任一变换变化都会由 InstancedGroup 每帧的 matrixWorld 比对自动置 needUpdate。
|
|
155
|
+
* 注:Vector3 在 three 0.167 没有 onChange 回调;Euler/Quaternion 虽有 onChange,但 three 内部已用它
|
|
156
|
+
* 同步 rotation↔quaternion,不能覆盖,故统一用 matrixWorld 比对兜底,最贴合 three 的变换检测机制。
|
|
157
|
+
*/
|
|
158
|
+
/** 设置鼠标手势(悬停时光标样式):pointer / grab / move / text / crosshair ... */
|
|
159
|
+
setCursor(c: string): this;
|
|
160
|
+
/** 由 InstancedGroup 每帧调用:世界矩阵变化则置 needUpdate(setPosition 等已置过,这里双保险) */
|
|
161
|
+
markDirtyIfWorldChanged(): void;
|
|
162
|
+
/** 从注册表移除(对象销毁时调用,避免内存泄漏) */
|
|
163
|
+
dispose(): void;
|
|
164
|
+
/**
|
|
165
|
+
* 声明本实例归属哪个 InstancedGroup(owner tag)。多组共存时用于隔离,
|
|
166
|
+
* 只有 owner 相同的 InstancedGroup 会收集本实例。
|
|
167
|
+
*
|
|
168
|
+
* 用法:`new Cube().setOwner("preview")`,配合 `new InstancedGroup("preview")`。
|
|
169
|
+
* 不调用则归属为空 —— 会被「未声明 owner 的组」(如 ThreeBase 默认组)收集,保持旧行为。
|
|
170
|
+
*/
|
|
171
|
+
setOwner(tag: string): this;
|
|
172
|
+
/** 读取本实例的归属 tag(未声明返回 undefined) */
|
|
173
|
+
getOwner(): string | undefined;
|
|
174
|
+
}
|