@lijuhong1981/three.instancedsprite 1.0.0 → 1.1.0
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/.gitattributes +2 -0
- package/API.md +61 -3
- package/README.md +125 -55
- package/index.js +5 -5
- package/package.json +1 -1
- package/src/InstancedSprite.js +25 -2
- package/src/InstancedSpriteCollection.js +16 -0
- package/src/InstancedSpriteMaterial.js +146 -133
- package/src/InstancedSpriteMesh.js +425 -406
- package/src/InstancedSpriteNodeMaterial.js +21 -6
- package/.claude/settings.local.json +0 -7
|
@@ -1,406 +1,425 @@
|
|
|
1
|
-
import Check from "@lijuhong1981/jscheck/src/Check.js";
|
|
2
|
-
import { BufferAttribute, InstancedBufferAttribute, InstancedBufferGeometry, Mesh, Raycaster, Texture } from "three";
|
|
3
|
-
import InstancedSprite from "./InstancedSprite.js";
|
|
4
|
-
import InstancedSpriteMaterial from "./InstancedSpriteMaterial.js";
|
|
5
|
-
import InstancedSpriteNodeMaterial from "./InstancedSpriteNodeMaterial.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @import InstancedSpriteCollection from "./InstancedSpriteCollection.js";
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* 顶点数据,定义了一个单位正方形的四个顶点位置和UV坐标,以及组成两个三角形的索引
|
|
13
|
-
* @ignore
|
|
14
|
-
*/
|
|
15
|
-
const positions = new Float32Array([
|
|
16
|
-
-0.5, -0.5, 0,
|
|
17
|
-
0.5, -0.5, 0,
|
|
18
|
-
0.5, 0.5, 0,
|
|
19
|
-
-0.5, 0.5, 0,
|
|
20
|
-
]);
|
|
21
|
-
const uvs = new Float32Array([
|
|
22
|
-
0, 0,
|
|
23
|
-
1, 0,
|
|
24
|
-
1, 1,
|
|
25
|
-
0, 1,
|
|
26
|
-
]);
|
|
27
|
-
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
|
|
28
|
-
const geometry = new InstancedBufferGeometry();
|
|
29
|
-
geometry.setAttribute("position", new BufferAttribute(positions, 3));
|
|
30
|
-
geometry.setAttribute("uv", new BufferAttribute(uvs, 2));
|
|
31
|
-
geometry.setIndex(new BufferAttribute(indices, 1));
|
|
32
|
-
// geometry.computeVertexNormals();
|
|
33
|
-
|
|
34
|
-
// --- 定义InstancedSpriteMaterial使用的attribute名称常量 ---
|
|
35
|
-
const aPositionAndShow = 'aPositionAndShow';
|
|
36
|
-
const aCenterAndSize = 'aCenterAndSize';
|
|
37
|
-
const aScaleAndRotationAndSizeAttenuation = 'aScaleAndRotationAndSizeAttenuation';
|
|
38
|
-
const aColorAndOpacity = 'aColorAndOpacity';
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
*
|
|
42
|
-
* @
|
|
43
|
-
* @
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
*
|
|
55
|
-
* @
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
attributesData.aPositionAndShow[idx +
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
sprite.
|
|
97
|
-
attributesData.aCenterAndSize[idx +
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
sprite.
|
|
111
|
-
attributesData.aScaleAndRotationAndSizeAttenuation[idx
|
|
112
|
-
}
|
|
113
|
-
if (
|
|
114
|
-
sprite.
|
|
115
|
-
attributesData.aScaleAndRotationAndSizeAttenuation[idx +
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
attributesData.aColorAndOpacity[idx +
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
*
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
attributesData
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
attributesData.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
attributesData.
|
|
178
|
-
attributesData.
|
|
179
|
-
sprite.
|
|
180
|
-
attributesData.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
*
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
this._maxCapacity
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
this
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
*
|
|
342
|
-
* @
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
//
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
1
|
+
import Check from "@lijuhong1981/jscheck/src/Check.js";
|
|
2
|
+
import { BufferAttribute, InstancedBufferAttribute, InstancedBufferGeometry, Mesh, Raycaster, Texture } from "three";
|
|
3
|
+
import InstancedSprite from "./InstancedSprite.js";
|
|
4
|
+
import InstancedSpriteMaterial from "./InstancedSpriteMaterial.js";
|
|
5
|
+
import InstancedSpriteNodeMaterial from "./InstancedSpriteNodeMaterial.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @import InstancedSpriteCollection from "./InstancedSpriteCollection.js";
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 顶点数据,定义了一个单位正方形的四个顶点位置和UV坐标,以及组成两个三角形的索引
|
|
13
|
+
* @ignore
|
|
14
|
+
*/
|
|
15
|
+
const positions = new Float32Array([
|
|
16
|
+
-0.5, -0.5, 0,
|
|
17
|
+
0.5, -0.5, 0,
|
|
18
|
+
0.5, 0.5, 0,
|
|
19
|
+
-0.5, 0.5, 0,
|
|
20
|
+
]);
|
|
21
|
+
const uvs = new Float32Array([
|
|
22
|
+
0, 0,
|
|
23
|
+
1, 0,
|
|
24
|
+
1, 1,
|
|
25
|
+
0, 1,
|
|
26
|
+
]);
|
|
27
|
+
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
|
|
28
|
+
const geometry = new InstancedBufferGeometry();
|
|
29
|
+
geometry.setAttribute("position", new BufferAttribute(positions, 3));
|
|
30
|
+
geometry.setAttribute("uv", new BufferAttribute(uvs, 2));
|
|
31
|
+
geometry.setIndex(new BufferAttribute(indices, 1));
|
|
32
|
+
// geometry.computeVertexNormals();
|
|
33
|
+
|
|
34
|
+
// --- 定义InstancedSpriteMaterial使用的attribute名称常量 ---
|
|
35
|
+
const aPositionAndShow = 'aPositionAndShow';
|
|
36
|
+
const aCenterAndSize = 'aCenterAndSize';
|
|
37
|
+
const aScaleAndRotationAndSizeAttenuation = 'aScaleAndRotationAndSizeAttenuation';
|
|
38
|
+
const aColorAndOpacity = 'aColorAndOpacity';
|
|
39
|
+
const aPickColorAndEnabled = 'aPickColorAndEnabled';
|
|
40
|
+
/**
|
|
41
|
+
* InstancedSpriteMaterial使用的attribute名称常量与itemSize大小,InstancedSpriteMesh会根据名称和itemSize创建InstancedBufferAttribute
|
|
42
|
+
* @type {Object<string, number>}
|
|
43
|
+
* @readonly
|
|
44
|
+
* @ignore
|
|
45
|
+
*/
|
|
46
|
+
const AttributesItemSize = Object.freeze({
|
|
47
|
+
aPositionAndShow: 4,
|
|
48
|
+
aCenterAndSize: 4,
|
|
49
|
+
aScaleAndRotationAndSizeAttenuation: 3,
|
|
50
|
+
aColorAndOpacity: 4,
|
|
51
|
+
aPickColorAndEnabled: 4,
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* 检查并更新一个InstancedSprite实例的数据到对应的InstancedBufferAttribute位置
|
|
55
|
+
* @param {InstancedSpriteMesh} mesh
|
|
56
|
+
* @param {InstancedSprite} sprite
|
|
57
|
+
* @ignore
|
|
58
|
+
*/
|
|
59
|
+
function checkAndUpdateInstancedSprite(mesh, sprite) {
|
|
60
|
+
const attributesData = mesh._attributesData;
|
|
61
|
+
const index = sprite._instanceIndex;
|
|
62
|
+
|
|
63
|
+
const positionChanged = (sprite._position.equals(sprite.position) === false);
|
|
64
|
+
const showChanged = (sprite._show !== sprite.show);
|
|
65
|
+
const centerChanged = (sprite._center.equals(sprite.center) === false);
|
|
66
|
+
const imageSizeChanged = (sprite._imageSize.equals(sprite.imageSize) === false);
|
|
67
|
+
const scaleChanged = (sprite._scale !== sprite.scale);
|
|
68
|
+
const rotationChanged = (sprite._rotation !== sprite.rotation);
|
|
69
|
+
const sizeAttenuationChanged = (sprite._sizeAttenuation !== sprite.sizeAttenuation);
|
|
70
|
+
const colorChanged = (sprite._color.equals(sprite.color) === false);
|
|
71
|
+
const opacityChanged = (sprite._opacity !== sprite.opacity);
|
|
72
|
+
const pickColorChanged = (sprite._pickColor.equals(sprite.pickColor) === false);
|
|
73
|
+
const enablePickColorChanged = (sprite._enablePickColor !== sprite.enablePickColor);
|
|
74
|
+
|
|
75
|
+
let idx = 0;
|
|
76
|
+
|
|
77
|
+
if (positionChanged || showChanged) {
|
|
78
|
+
idx = index * AttributesItemSize.aPositionAndShow;
|
|
79
|
+
if (positionChanged) {
|
|
80
|
+
sprite._position.copy(sprite.position);
|
|
81
|
+
attributesData.aPositionAndShow[idx] = sprite._position.x;
|
|
82
|
+
attributesData.aPositionAndShow[idx + 1] = sprite._position.y;
|
|
83
|
+
attributesData.aPositionAndShow[idx + 2] = sprite._position.z;
|
|
84
|
+
}
|
|
85
|
+
if (showChanged) {
|
|
86
|
+
sprite._show = sprite.show;
|
|
87
|
+
attributesData.aPositionAndShow[idx + 3] = sprite._show ? 1 : 0;
|
|
88
|
+
}
|
|
89
|
+
mesh.geometry.attributes.aPositionAndShow.needsUpdate = true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (centerChanged || imageSizeChanged) {
|
|
93
|
+
idx = index * AttributesItemSize.aCenterAndSize;
|
|
94
|
+
if (centerChanged) {
|
|
95
|
+
sprite._center.copy(sprite.center);
|
|
96
|
+
attributesData.aCenterAndSize[idx] = sprite._center.x;
|
|
97
|
+
attributesData.aCenterAndSize[idx + 1] = sprite._center.y;
|
|
98
|
+
}
|
|
99
|
+
if (imageSizeChanged) {
|
|
100
|
+
sprite._imageSize.copy(sprite.imageSize);
|
|
101
|
+
attributesData.aCenterAndSize[idx + 2] = sprite._imageSize.x;
|
|
102
|
+
attributesData.aCenterAndSize[idx + 3] = sprite._imageSize.y;
|
|
103
|
+
}
|
|
104
|
+
mesh.geometry.attributes.aCenterAndSize.needsUpdate = true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (scaleChanged || rotationChanged || sizeAttenuationChanged) {
|
|
108
|
+
idx = index * AttributesItemSize.aScaleAndRotationAndSizeAttenuation;
|
|
109
|
+
if (scaleChanged) {
|
|
110
|
+
sprite._scale = sprite.scale;
|
|
111
|
+
attributesData.aScaleAndRotationAndSizeAttenuation[idx] = sprite._scale;
|
|
112
|
+
}
|
|
113
|
+
if (rotationChanged) {
|
|
114
|
+
sprite._rotation = sprite.rotation;
|
|
115
|
+
attributesData.aScaleAndRotationAndSizeAttenuation[idx + 1] = sprite._rotation;
|
|
116
|
+
}
|
|
117
|
+
if (sizeAttenuationChanged) {
|
|
118
|
+
sprite._sizeAttenuation = sprite.sizeAttenuation;
|
|
119
|
+
attributesData.aScaleAndRotationAndSizeAttenuation[idx + 2] = sprite._sizeAttenuation ? 1 : 0;
|
|
120
|
+
}
|
|
121
|
+
mesh.geometry.attributes.aScaleAndRotationAndSizeAttenuation.needsUpdate = true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (colorChanged || opacityChanged) {
|
|
125
|
+
idx = index * AttributesItemSize.aColorAndOpacity;
|
|
126
|
+
if (colorChanged) {
|
|
127
|
+
sprite._color.copy(sprite.color);
|
|
128
|
+
attributesData.aColorAndOpacity[idx] = sprite._color.r;
|
|
129
|
+
attributesData.aColorAndOpacity[idx + 1] = sprite._color.g;
|
|
130
|
+
attributesData.aColorAndOpacity[idx + 2] = sprite._color.b;
|
|
131
|
+
}
|
|
132
|
+
if (opacityChanged) {
|
|
133
|
+
sprite._opacity = sprite.opacity;
|
|
134
|
+
attributesData.aColorAndOpacity[idx + 3] = sprite._opacity;
|
|
135
|
+
}
|
|
136
|
+
mesh.geometry.attributes.aColorAndOpacity.needsUpdate = true;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (pickColorChanged || enablePickColorChanged) {
|
|
140
|
+
idx = index * AttributesItemSize.aPickColorAndEnabled;
|
|
141
|
+
if (pickColorChanged) {
|
|
142
|
+
sprite._pickColor.copy(sprite.pickColor);
|
|
143
|
+
attributesData.aPickColorAndEnabled[idx] = sprite._pickColor.r;
|
|
144
|
+
attributesData.aPickColorAndEnabled[idx + 1] = sprite._pickColor.g;
|
|
145
|
+
attributesData.aPickColorAndEnabled[idx + 2] = sprite._pickColor.b;
|
|
146
|
+
}
|
|
147
|
+
if (enablePickColorChanged) {
|
|
148
|
+
sprite._enablePickColor = sprite.enablePickColor;
|
|
149
|
+
attributesData.aPickColorAndEnabled[idx + 3] = sprite._enablePickColor ? 1 : 0;
|
|
150
|
+
}
|
|
151
|
+
mesh.geometry.attributes.aPickColorAndEnabled.needsUpdate = true;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* 添加一个InstancedSprite实例的数据到InstancedBufferAttribute中对应的位置
|
|
156
|
+
* @param {InstancedSpriteMesh} mesh
|
|
157
|
+
* @param {InstancedSprite} sprite
|
|
158
|
+
* @param {number} index
|
|
159
|
+
* @ignore
|
|
160
|
+
*/
|
|
161
|
+
function addInstancedSprite(mesh, sprite, index) {
|
|
162
|
+
const attributesData = mesh._attributesData;
|
|
163
|
+
sprite._instanceIndex = index;
|
|
164
|
+
let idx = 0;
|
|
165
|
+
|
|
166
|
+
idx = index * AttributesItemSize.aPositionAndShow;
|
|
167
|
+
sprite._position.copy(sprite.position);
|
|
168
|
+
attributesData.aPositionAndShow[idx] = sprite._position.x;
|
|
169
|
+
attributesData.aPositionAndShow[idx + 1] = sprite._position.y;
|
|
170
|
+
attributesData.aPositionAndShow[idx + 2] = sprite._position.z;
|
|
171
|
+
sprite._show = sprite.show;
|
|
172
|
+
attributesData.aPositionAndShow[idx + 3] = sprite._show ? 1 : 0;
|
|
173
|
+
mesh.geometry.attributes.aPositionAndShow.needsUpdate = true;
|
|
174
|
+
|
|
175
|
+
idx = index * AttributesItemSize.aCenterAndSize;
|
|
176
|
+
sprite._center.copy(sprite.center);
|
|
177
|
+
attributesData.aCenterAndSize[idx] = sprite._center.x;
|
|
178
|
+
attributesData.aCenterAndSize[idx + 1] = sprite._center.y;
|
|
179
|
+
sprite._imageSize.copy(sprite.imageSize);
|
|
180
|
+
attributesData.aCenterAndSize[idx + 2] = sprite._imageSize.x;
|
|
181
|
+
attributesData.aCenterAndSize[idx + 3] = sprite._imageSize.y;
|
|
182
|
+
mesh.geometry.attributes.aCenterAndSize.needsUpdate = true;
|
|
183
|
+
|
|
184
|
+
idx = index * AttributesItemSize.aScaleAndRotationAndSizeAttenuation;
|
|
185
|
+
sprite._scale = sprite.scale;
|
|
186
|
+
attributesData.aScaleAndRotationAndSizeAttenuation[idx] = sprite._scale;
|
|
187
|
+
sprite._rotation = sprite.rotation;
|
|
188
|
+
attributesData.aScaleAndRotationAndSizeAttenuation[idx + 1] = sprite._rotation;
|
|
189
|
+
sprite._sizeAttenuation = sprite.sizeAttenuation;
|
|
190
|
+
attributesData.aScaleAndRotationAndSizeAttenuation[idx + 2] = sprite._sizeAttenuation ? 1 : 0;
|
|
191
|
+
mesh.geometry.attributes.aScaleAndRotationAndSizeAttenuation.needsUpdate = true;
|
|
192
|
+
|
|
193
|
+
idx = index * AttributesItemSize.aColorAndOpacity;
|
|
194
|
+
sprite._color.copy(sprite.color);
|
|
195
|
+
attributesData.aColorAndOpacity[idx] = sprite._color.r;
|
|
196
|
+
attributesData.aColorAndOpacity[idx + 1] = sprite._color.g;
|
|
197
|
+
attributesData.aColorAndOpacity[idx + 2] = sprite._color.b;
|
|
198
|
+
sprite._opacity = sprite.opacity;
|
|
199
|
+
attributesData.aColorAndOpacity[idx + 3] = sprite._opacity;
|
|
200
|
+
mesh.geometry.attributes.aColorAndOpacity.needsUpdate = true;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* InstancedSpriteMesh类,基于InstancedBufferGeometry实现的高性能InstancedSprite渲染组件
|
|
205
|
+
*
|
|
206
|
+
* @extends {Mesh}
|
|
207
|
+
*/
|
|
208
|
+
class InstancedSpriteMesh extends Mesh {
|
|
209
|
+
/**
|
|
210
|
+
* @param {InstancedSpriteCollection} collection - 所属的InstancedSpriteCollection实例,必填
|
|
211
|
+
* @param {Texture} texture - 材质图像纹理,必填
|
|
212
|
+
* @constructor
|
|
213
|
+
*/
|
|
214
|
+
constructor(collection, texture) {
|
|
215
|
+
Check.defined('collection', collection);
|
|
216
|
+
Check.defined('texture', texture);
|
|
217
|
+
super(geometry.clone(), collection.useNodeMaterial ? new InstancedSpriteNodeMaterial() : new InstancedSpriteMaterial());
|
|
218
|
+
this._collection = collection; //所属的InstancedSpriteCollection实例
|
|
219
|
+
this.material.texture = texture;
|
|
220
|
+
/**
|
|
221
|
+
* 对象类型标识
|
|
222
|
+
* @type {string}
|
|
223
|
+
* @readonly
|
|
224
|
+
*/
|
|
225
|
+
this.type = "InstancedSpriteMesh";
|
|
226
|
+
/**
|
|
227
|
+
* @type {Array<InstancedSprite>}
|
|
228
|
+
* @ignore
|
|
229
|
+
*/
|
|
230
|
+
this._instancedSprites = [];
|
|
231
|
+
/**
|
|
232
|
+
* @type {Array<InstancedSprite>}
|
|
233
|
+
* @ignore
|
|
234
|
+
*/
|
|
235
|
+
this._addInstancedSprites = [];
|
|
236
|
+
/**
|
|
237
|
+
* @type {Array<InstancedSprite>}
|
|
238
|
+
* @ignore
|
|
239
|
+
*/
|
|
240
|
+
this._removeInstancedSprites = [];
|
|
241
|
+
/**
|
|
242
|
+
* InstancedBufferAttribute数据存储对象,key为属性名称,value为对应的Float32Array数组
|
|
243
|
+
* @type {Object<string, Float32Array>}
|
|
244
|
+
* @ignore
|
|
245
|
+
*/
|
|
246
|
+
this._attributesData = {};
|
|
247
|
+
/**
|
|
248
|
+
* 当前能容纳的最大InstancedSprite数量,初始为0,InstancedSpriteMesh会根据需要动态扩容
|
|
249
|
+
* @type {number}
|
|
250
|
+
* @ignore
|
|
251
|
+
*/
|
|
252
|
+
this._maxCapacity = 0;
|
|
253
|
+
this._enaureCapacity(64);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* InstancedSpriteMesh对象标识
|
|
257
|
+
* @type {boolean}
|
|
258
|
+
* @readonly
|
|
259
|
+
*/
|
|
260
|
+
get isInstancedSpriteMesh() { return true; }
|
|
261
|
+
/**
|
|
262
|
+
* 深度测试开关,默认为true,开启后会进行深度测试以正确处理遮挡关系,但可能会有性能影响;如果关闭则所有InstancedSprite都会被渲染在最前面,适合需要始终显示的UI元素等场景
|
|
263
|
+
* @type {boolean}
|
|
264
|
+
* @default true
|
|
265
|
+
*/
|
|
266
|
+
get depthTest() { return this.material.depthTest; }
|
|
267
|
+
set depthTest(value) {
|
|
268
|
+
Check.typeOf.boolean('depthTest', value);
|
|
269
|
+
this.material.depthTest = value;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* InstancedSprite对象数组
|
|
273
|
+
* @type {Array<InstancedSprite>}
|
|
274
|
+
* @readonly
|
|
275
|
+
*/
|
|
276
|
+
get instancedSprites() { return this._instancedSprites; }
|
|
277
|
+
/**
|
|
278
|
+
* 确保InstancedBufferAttribute的大小能够容纳指定数量的InstancedSprite实例,如果当前最大容量不足,则进行扩容
|
|
279
|
+
* @param {number} required - 需要容纳的InstancedSprite数量
|
|
280
|
+
* @private
|
|
281
|
+
*/
|
|
282
|
+
_enaureCapacity(required) {
|
|
283
|
+
if (required < this._maxCapacity) return;
|
|
284
|
+
|
|
285
|
+
const newCapacity = Math.max(required, this._maxCapacity + 64);
|
|
286
|
+
|
|
287
|
+
for (const [name, itemSize] of Object.entries(AttributesItemSize)) {
|
|
288
|
+
const newArray = new Float32Array(newCapacity * itemSize);
|
|
289
|
+
const oldArray = this._attributesData[name];
|
|
290
|
+
if (oldArray) {
|
|
291
|
+
const copyLen = Math.min(oldArray.length, newArray.length);
|
|
292
|
+
newArray.set(oldArray.subarray(0, copyLen));
|
|
293
|
+
}
|
|
294
|
+
this._attributesData[name] = newArray;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 创建 InstancedBufferAttribute 对象
|
|
298
|
+
for (const [name, itemSize] of Object.entries(AttributesItemSize)) {
|
|
299
|
+
const attr = new InstancedBufferAttribute(this._attributesData[name], itemSize);
|
|
300
|
+
this.geometry.setAttribute(name, attr);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
this.geometry.instanceCount = required;
|
|
304
|
+
this._maxCapacity = newCapacity;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* 添加一个InstancedSprite
|
|
308
|
+
* @param {InstancedSprite} sprite - 要添加的InstancedSprite实例
|
|
309
|
+
* @returns {InstancedSpriteMesh}
|
|
310
|
+
*/
|
|
311
|
+
add(sprite) {
|
|
312
|
+
sprite._mesh = this;
|
|
313
|
+
this._addInstancedSprites.push(sprite);
|
|
314
|
+
return this;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* 移除一个InstancedSprite
|
|
318
|
+
* @param {InstancedSprite} sprite - 要移除的InstancedSprite实例
|
|
319
|
+
* @returns {InstancedSpriteMesh}
|
|
320
|
+
*/
|
|
321
|
+
remove(sprite) {
|
|
322
|
+
if (this._instancedSprites.includes(sprite) && !this._removeInstancedSprites.includes(sprite))
|
|
323
|
+
this._removeInstancedSprites.push(sprite);
|
|
324
|
+
else
|
|
325
|
+
console.warn('InstancedSprite not found in InstancedSpriteMesh or already marked for removal');
|
|
326
|
+
return this;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* 移除所有InstancedSprite
|
|
330
|
+
* @returns {InstancedSpriteMesh}
|
|
331
|
+
*/
|
|
332
|
+
clear() {
|
|
333
|
+
this._addInstancedSprites.length = 0;
|
|
334
|
+
this._removeInstancedSprites.length = 0;
|
|
335
|
+
this._instancedSprites.length = 0;
|
|
336
|
+
this.geometry.instanceCount = 0;
|
|
337
|
+
return this;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Computes intersection points between a casted ray and this sprite.
|
|
341
|
+
*
|
|
342
|
+
* @param {Raycaster} raycaster - The raycaster.
|
|
343
|
+
* @param {Array<Object>} intersects - The target array that holds the intersection points.
|
|
344
|
+
*/
|
|
345
|
+
raycast(raycaster, intersects) {
|
|
346
|
+
|
|
347
|
+
if (raycaster.camera === null) {
|
|
348
|
+
|
|
349
|
+
console.error('InstancedSpriteMesh: "Raycaster.camera" needs to be set in order to raycast against sprites.');
|
|
350
|
+
return;
|
|
351
|
+
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const sprites = this._instancedSprites;
|
|
355
|
+
for (const sprite of sprites) {
|
|
356
|
+
sprite.raycast(raycaster, intersects, raycaster.camera.matrixWorldInverse);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* 每帧更新
|
|
361
|
+
* @private
|
|
362
|
+
*/
|
|
363
|
+
update() {
|
|
364
|
+
if (!this.visible) return;
|
|
365
|
+
|
|
366
|
+
const removeInstancedSprites = this._removeInstancedSprites;
|
|
367
|
+
const addInstancedSprites = this._addInstancedSprites;
|
|
368
|
+
const instancedSprites = this._instancedSprites;
|
|
369
|
+
|
|
370
|
+
// 计算最终需要的容量
|
|
371
|
+
const requiredCapacity = instancedSprites.length + addInstancedSprites.length - removeInstancedSprites.length;
|
|
372
|
+
this._enaureCapacity(requiredCapacity);
|
|
373
|
+
|
|
374
|
+
if (removeInstancedSprites.length > 0) {
|
|
375
|
+
// 如果有需要移除的InstancedSprite,先从instancedSprites数组中移除
|
|
376
|
+
for (const sprite of removeInstancedSprites) {
|
|
377
|
+
const index = instancedSprites.indexOf(sprite);
|
|
378
|
+
if (index !== -1) {
|
|
379
|
+
instancedSprites.splice(index, 1); //从instancedSprites数组中移除
|
|
380
|
+
sprite._mesh = undefined;
|
|
381
|
+
sprite._instanceIndex = -1;
|
|
382
|
+
}
|
|
383
|
+
// else
|
|
384
|
+
// console.warn('InstancedSprite not found in InstancedSpriteMesh during removal');
|
|
385
|
+
}
|
|
386
|
+
removeInstancedSprites.length = 0;
|
|
387
|
+
|
|
388
|
+
// 如果有需要添加的InstancedSprite,则加入instancedSprites数组中
|
|
389
|
+
if (addInstancedSprites.length > 0) {
|
|
390
|
+
instancedSprites.push(...addInstancedSprites);
|
|
391
|
+
addInstancedSprites.length = 0;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// 重新写入所有InstancedSprite的属性数据到InstancedBufferAttribute中
|
|
395
|
+
const length = instancedSprites.length;
|
|
396
|
+
for (let i = 0; i < length; i++) {
|
|
397
|
+
const sprite = instancedSprites[i];
|
|
398
|
+
addInstancedSprite(this, sprite, i);
|
|
399
|
+
}
|
|
400
|
+
} else {
|
|
401
|
+
// 检查并更新属性值有变化的InstancedSprite
|
|
402
|
+
for (const sprite of instancedSprites) {
|
|
403
|
+
checkAndUpdateInstancedSprite(this, sprite);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// 添加InstancedSprite
|
|
407
|
+
while (addInstancedSprites.length > 0) {
|
|
408
|
+
const sprite = addInstancedSprites.shift();
|
|
409
|
+
addInstancedSprite(this, sprite, instancedSprites.length);
|
|
410
|
+
instancedSprites.push(sprite);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
this.geometry.instanceCount = instancedSprites.length; //更新instanceCount
|
|
415
|
+
}
|
|
416
|
+
dispose() {
|
|
417
|
+
this.clear();
|
|
418
|
+
this.geometry.dispose();
|
|
419
|
+
this.material.dispose();
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
export default InstancedSpriteMesh;
|
|
424
|
+
export { InstancedSpriteMesh };
|
|
425
|
+
|