@lijuhong1981/three.instancedsprite 1.0.1 → 2.0.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.
@@ -43,6 +43,7 @@ class InstancedSpriteNodeMaterial extends NodeMaterial {
43
43
  const aScaleAndRotationAndSizeAttenuation = attribute('aScaleAndRotationAndSizeAttenuation', 'vec3');
44
44
  const aColorAndOpacity = attribute('aColorAndOpacity', 'vec4');
45
45
  const aPickColorAndEnabled = attribute('aPickColorAndEnabled', 'vec4');
46
+ const aUvRect = attribute('aUvRect', 'vec4');
46
47
 
47
48
  // ============================================================
48
49
  // Varying 变量 — 从顶点着色器传递到片元着色器
@@ -67,7 +68,7 @@ class InstancedSpriteNodeMaterial extends NodeMaterial {
67
68
  // --- 2. 输出 varying ---
68
69
  vShow.assign(show);
69
70
  vColorAndOpacity.assign(aColorAndOpacity);
70
- vUv.assign(uv());
71
+ vUv.assign(aUvRect.xy.add(uv().mul(aUvRect.zw)));
71
72
  vPickColorAndEnabled.assign(aPickColorAndEnabled);
72
73
 
73
74
  // --- 3. 计算模型视图位置 ---
@@ -84,8 +85,8 @@ class InstancedSpriteNodeMaterial extends NodeMaterial {
84
85
 
85
86
  If(isVisible, () => {
86
87
  // --- 4. sizeAttenuation:大小跟随相机深度 ---
87
- // 检查是否为透视投影: 透视投影矩阵中 m[2][3] (elements[11]) == -1
88
- const scaleFactor = sizeAttenuation.lessThan(0.5).and(cameraProjectionMatrix.element(11).equal(-1))
88
+ // 检查是否为透视投影:透视投影矩阵 m[2][3] (elements[11]) == -1
89
+ const scaleFactor = sizeAttenuation.lessThan(0.5).and(cameraProjectionMatrix.element(2).w.equal(-1))
89
90
  .select(scale.mul(depth), scale);
90
91
 
91
92
  // --- 5. 坐标对齐 ---
@@ -119,7 +120,7 @@ class InstancedSpriteNodeMaterial extends NodeMaterial {
119
120
  });
120
121
 
121
122
  return clipPosition;
122
- }).once();
123
+ }).once()();
123
124
 
124
125
  // ============================================================
125
126
  // 片元着色器 (fragmentNode)
@@ -145,7 +146,7 @@ class InstancedSpriteNodeMaterial extends NodeMaterial {
145
146
  });
146
147
 
147
148
  return diffuseColor;
148
- }).once();
149
+ }).once()();
149
150
  }
150
151
  /**
151
152
  * 图片纹理
@@ -161,3 +162,9 @@ class InstancedSpriteNodeMaterial extends NodeMaterial {
161
162
 
162
163
  export default InstancedSpriteNodeMaterial;
163
164
  export { InstancedSpriteNodeMaterial };
165
+ /**
166
+ * InstancedSpriteNodeMaterial 的别名。
167
+ * @class
168
+ * @name BillboardNodeMaterial
169
+ */
170
+ export const BillboardNodeMaterial = InstancedSpriteNodeMaterial;
@@ -0,0 +1,125 @@
1
+ import { CanvasTexture } from "three";
2
+
3
+ /**
4
+ * 纹理图集,将多张图片按行(shelf)打包进一张 Canvas 纹理,用于合并 draw call
5
+ */
6
+ class TextureAtlas {
7
+ /**
8
+ * @param {object} [options]
9
+ * @param {number} [options.initialSize=1024] 初始边长(正方形,单位像素)
10
+ * @param {number} [options.maxSize=8192] 最大边长,自动扩容到该值后不再增大
11
+ * @param {number} [options.padding=2] 子图间距,防止线性过滤时边缘渗色
12
+ */
13
+ constructor(options = {}) {
14
+ this.initialSize = options.initialSize || 1024;
15
+ this.maxSize = options.maxSize || 8192;
16
+ this.padding = options.padding !== undefined ? options.padding : 2;
17
+
18
+ this.size = this.initialSize;
19
+ this.canvas = document.createElement('canvas');
20
+ this.canvas.width = this.size;
21
+ this.canvas.height = this.size;
22
+ this.ctx = this.canvas.getContext('2d');
23
+
24
+ this.texture = new CanvasTexture(this.canvas);
25
+ this.texture.needsUpdate = true;
26
+
27
+ this._cursorX = this.padding;
28
+ this._cursorY = this.padding;
29
+ this._rowHeight = 0;
30
+ this._rects = new Map(); // source -> {x, y, w, h} 像素矩形
31
+ }
32
+ /**
33
+ * 将图片加入图集,返回其像素矩形;已加入过则返回缓存;图集整体已满则返回null
34
+ * @param {HTMLImageElement|HTMLCanvasElement} source
35
+ * @returns {{x:number,y:number,w:number,h:number}|null}
36
+ */
37
+ add(source) {
38
+ const cached = this._rects.get(source);
39
+ if (cached) return cached;
40
+
41
+ const w = source.width;
42
+ const h = source.height;
43
+ const stepW = w + this.padding * 2;
44
+ const stepH = h + this.padding * 2;
45
+
46
+ // 单张图片超过当前图集尺寸时,先扩容到能容纳
47
+ while (stepW > this.size || stepH > this.size) {
48
+ if (!this.grow()) {
49
+ throw new Error('TextureAtlas: 图片尺寸超过图集最大尺寸');
50
+ }
51
+ }
52
+
53
+ // 当前行放不下则换行
54
+ if (this._cursorX + stepW > this.size) {
55
+ this._cursorX = this.padding;
56
+ this._cursorY += this._rowHeight;
57
+ this._rowHeight = 0;
58
+ }
59
+ // 图集整体已满
60
+ if (this._cursorY + stepH > this.size) {
61
+ return null;
62
+ }
63
+
64
+ const x = this._cursorX + this.padding;
65
+ const y = this._cursorY + this.padding;
66
+ const rect = { x, y, w, h };
67
+ this._rects.set(source, rect);
68
+
69
+ this.ctx.drawImage(source, x, y, w, h);
70
+
71
+ this._cursorX += stepW;
72
+ this._rowHeight = Math.max(this._rowHeight, stepH);
73
+
74
+ this.texture.needsUpdate = true;
75
+ return rect;
76
+ }
77
+ /**
78
+ * 获取图片已分配的像素矩形
79
+ * @param {HTMLImageElement|HTMLCanvasElement} source
80
+ * @returns {{x:number,y:number,w:number,h:number}|undefined}
81
+ */
82
+ getRect(source) {
83
+ return this._rects.get(source);
84
+ }
85
+ /**
86
+ * 扩容图集(边长翻倍),成功返回true;已达最大尺寸返回false
87
+ * @returns {boolean}
88
+ */
89
+ grow() {
90
+ const newSize = this.size * 2;
91
+ if (newSize > this.maxSize) return false;
92
+
93
+ const newCanvas = document.createElement('canvas');
94
+ newCanvas.width = newSize;
95
+ newCanvas.height = newSize;
96
+ const newCtx = newCanvas.getContext('2d');
97
+ newCtx.drawImage(this.canvas, 0, 0);
98
+
99
+ this.canvas = newCanvas;
100
+ this.ctx = newCtx;
101
+ this.size = newSize;
102
+
103
+ // 尺寸变化必须重建纹理:WebGL2 用 texStorage 分配固定尺寸,替换 image 后 texSubImage2D 无法扩容
104
+ this.texture.dispose();
105
+ this.texture = new CanvasTexture(this.canvas);
106
+ this.texture.needsUpdate = true;
107
+ return true;
108
+ }
109
+ /**
110
+ * 将像素矩形转为归一化 UV(uOffset, vOffset, uScale, vScale)
111
+ * @param {{x:number,y:number,w:number,h:number}} rect
112
+ * @returns {Array<number>}
113
+ */
114
+ getUvRect(rect) {
115
+ return [
116
+ rect.x / this.size,
117
+ 1 - (rect.y + rect.h) / this.size, // flipY=true 时纹理上下翻转,v 坐标需反转
118
+ rect.w / this.size,
119
+ rect.h / this.size,
120
+ ];
121
+ }
122
+ }
123
+
124
+ export default TextureAtlas;
125
+ export { TextureAtlas };
@@ -1,7 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(node --check src/InstancedSpriteNodeMaterial.js)"
5
- ]
6
- }
7
- }