@kkarum/framework 2.3.28 → 2.3.29
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.
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
const { ccclass, property, executeInEditMode, disallowMultiple, menu } =
|
|
2
|
+
cc._decorator;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* WEColorAssembler - 顶点色渐变
|
|
6
|
+
* ============================================================
|
|
7
|
+
* 2.4.x 适配版:保留 3.8 版本的渐变计算方式,仅替换引擎渲染数据接入点。
|
|
8
|
+
*
|
|
9
|
+
* 行为:
|
|
10
|
+
* - `colors` 数组中的颜色作为 N 个色标,沿 angle 方向等距铺开,按顶点位置投影插值
|
|
11
|
+
* · 1 个色:所有顶点同色
|
|
12
|
+
* · 2 个色:两端渐变
|
|
13
|
+
* · N 个色:N stops 渐变(CSS linear-gradient 多色那种)
|
|
14
|
+
* - `colors` 为空 → 全部顶点取白色
|
|
15
|
+
* - 最终 alpha 乘以本节点到根的累乘 opacity
|
|
16
|
+
*/
|
|
17
|
+
@ccclass("FWColorAssembler")
|
|
18
|
+
@menu("render/FWColorAssembler(顶点色渐变)")
|
|
19
|
+
@disallowMultiple
|
|
20
|
+
@executeInEditMode
|
|
21
|
+
export class FWColorAssembler extends cc.Component {
|
|
22
|
+
@property
|
|
23
|
+
private _colors: cc.Color[] = [];
|
|
24
|
+
|
|
25
|
+
@property({
|
|
26
|
+
type: [cc.Color],
|
|
27
|
+
tooltip:
|
|
28
|
+
"颜色色标(按 angle 方向均匀铺开):\n" +
|
|
29
|
+
"- 1 个 → 全部同色\n" +
|
|
30
|
+
"- 2 个 → 两端渐变\n" +
|
|
31
|
+
"- N 个 → N stops 渐变\n" +
|
|
32
|
+
"- 0 个 → 全部白色",
|
|
33
|
+
})
|
|
34
|
+
public get colors(): cc.Color[] {
|
|
35
|
+
return this._colors;
|
|
36
|
+
}
|
|
37
|
+
public set colors(v: cc.Color[]) {
|
|
38
|
+
this._colors = v || [];
|
|
39
|
+
this._markDirty();
|
|
40
|
+
this._updateColors();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
private _angle: number = 0;
|
|
45
|
+
|
|
46
|
+
@property({
|
|
47
|
+
type: cc.Float,
|
|
48
|
+
min: 0,
|
|
49
|
+
max: 360,
|
|
50
|
+
step: 1,
|
|
51
|
+
slide: true,
|
|
52
|
+
tooltip:
|
|
53
|
+
"渐变角度(度),对任意色数生效:\n" +
|
|
54
|
+
" 0° = 由上向下(默认)\n" +
|
|
55
|
+
" 90° = 由左向右\n" +
|
|
56
|
+
" 180° = 由下向上\n" +
|
|
57
|
+
" 270° = 由右向左",
|
|
58
|
+
})
|
|
59
|
+
public get angle(): number {
|
|
60
|
+
return this._angle;
|
|
61
|
+
}
|
|
62
|
+
public set angle(v: number) {
|
|
63
|
+
this._angle = v;
|
|
64
|
+
this._markDirty();
|
|
65
|
+
this._updateColors();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private _tmp: cc.Color = new cc.Color();
|
|
69
|
+
private _renderer: cc.RenderComponent = null;
|
|
70
|
+
private _projBuf: Float32Array = null;
|
|
71
|
+
|
|
72
|
+
private _hookedAssembler: WEAssembler = null;
|
|
73
|
+
private _origUpdateRenderData: Function = null;
|
|
74
|
+
private _origFillBuffers: Function = null;
|
|
75
|
+
|
|
76
|
+
private _convertBuffer: ArrayBuffer = new ArrayBuffer(4);
|
|
77
|
+
private _convertFloat: Float32Array = new Float32Array(this._convertBuffer);
|
|
78
|
+
private _convertUint: Uint32Array = new Uint32Array(this._convertBuffer);
|
|
79
|
+
|
|
80
|
+
protected onLoad(): void {
|
|
81
|
+
this._refreshRenderer();
|
|
82
|
+
this._hook();
|
|
83
|
+
this._updateColors();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
protected onEnable(): void {
|
|
87
|
+
this._refreshRenderer();
|
|
88
|
+
this._hook();
|
|
89
|
+
this._markDirty();
|
|
90
|
+
this._updateColors();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
protected onDisable(): void {
|
|
94
|
+
this._unhook();
|
|
95
|
+
this._restoreDefaultColor();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
protected onDestroy(): void {
|
|
99
|
+
this._unhook();
|
|
100
|
+
this._renderer = null;
|
|
101
|
+
this._projBuf = null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
protected update(): void {
|
|
105
|
+
this._refreshRenderer();
|
|
106
|
+
this._hook();
|
|
107
|
+
this._updateColors();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
protected onFocusInEditor(): void {
|
|
111
|
+
this._updateColors();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
protected onLostFocusInEditor(): void {
|
|
115
|
+
this._updateColors();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
public setGradient(angleDeg: number, ...stops: cc.Color[]): void {
|
|
119
|
+
this._angle = angleDeg;
|
|
120
|
+
this._colors = stops || [];
|
|
121
|
+
this._markDirty();
|
|
122
|
+
this._updateColors();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public refresh(): void {
|
|
126
|
+
this._markDirty();
|
|
127
|
+
this._updateColors();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public getEffectiveOpacity(): number {
|
|
131
|
+
let alpha = 255;
|
|
132
|
+
let node: cc.Node = this.node;
|
|
133
|
+
while (node) {
|
|
134
|
+
alpha = (alpha * node.opacity) / 255;
|
|
135
|
+
node = node.parent;
|
|
136
|
+
}
|
|
137
|
+
return Math.round(alpha);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private _refreshRenderer(): void {
|
|
141
|
+
const renderer = this.getComponent(cc.RenderComponent);
|
|
142
|
+
if (renderer === this._renderer) return;
|
|
143
|
+
|
|
144
|
+
this._unhook();
|
|
145
|
+
this._renderer = renderer;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private _hook(): void {
|
|
149
|
+
const renderer = this._renderer;
|
|
150
|
+
const assembler = renderer && ((renderer as any)._assembler as WEAssembler);
|
|
151
|
+
if (!assembler || assembler === this._hookedAssembler) return;
|
|
152
|
+
|
|
153
|
+
this._unhook();
|
|
154
|
+
|
|
155
|
+
this._origUpdateRenderData = assembler.updateRenderData;
|
|
156
|
+
this._origFillBuffers = assembler.fillBuffers;
|
|
157
|
+
|
|
158
|
+
const self = this;
|
|
159
|
+
assembler.updateRenderData = function (comp?: cc.RenderComponent) {
|
|
160
|
+
if (self._origUpdateRenderData) {
|
|
161
|
+
self._origUpdateRenderData.call(this, comp || renderer);
|
|
162
|
+
}
|
|
163
|
+
if (self.enabled) self._updateColors();
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
assembler.fillBuffers = function (
|
|
167
|
+
comp: cc.RenderComponent,
|
|
168
|
+
rendererNode: cc.Node,
|
|
169
|
+
) {
|
|
170
|
+
if (self.enabled) self._updateColors();
|
|
171
|
+
if (self._origFillBuffers) {
|
|
172
|
+
return self._origFillBuffers.call(this, comp, rendererNode);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
this._hookedAssembler = assembler;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private _unhook(): void {
|
|
180
|
+
const assembler = this._hookedAssembler;
|
|
181
|
+
if (!assembler) return;
|
|
182
|
+
|
|
183
|
+
if (this._origUpdateRenderData) {
|
|
184
|
+
assembler.updateRenderData = this._origUpdateRenderData;
|
|
185
|
+
}
|
|
186
|
+
if (this._origFillBuffers) {
|
|
187
|
+
assembler.fillBuffers = this._origFillBuffers;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this._hookedAssembler = null;
|
|
191
|
+
this._origUpdateRenderData = null;
|
|
192
|
+
this._origFillBuffers = null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private _markDirty(): void {
|
|
196
|
+
const renderer = this._renderer || this.getComponent(cc.RenderComponent);
|
|
197
|
+
if (renderer && renderer.setVertsDirty) {
|
|
198
|
+
renderer.setVertsDirty();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private _restoreDefaultColor(): void {
|
|
203
|
+
const renderer = this._renderer || this.getComponent(cc.RenderComponent);
|
|
204
|
+
if (!renderer) return;
|
|
205
|
+
|
|
206
|
+
if (renderer._updateColor) {
|
|
207
|
+
renderer._updateColor();
|
|
208
|
+
}
|
|
209
|
+
if (renderer.setVertsDirty) {
|
|
210
|
+
renderer.setVertsDirty();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private _updateColors(): void {
|
|
215
|
+
const renderer = this._renderer || this.getComponent(cc.RenderComponent);
|
|
216
|
+
if (!renderer) return;
|
|
217
|
+
|
|
218
|
+
const assembler = (renderer as any)._assembler as WEAssembler;
|
|
219
|
+
const renderData = this._getRenderData(assembler, renderer);
|
|
220
|
+
const vData = renderData && renderData.vDatas && renderData.vDatas[0];
|
|
221
|
+
const uintVData =
|
|
222
|
+
renderData && renderData.uintVDatas && renderData.uintVDatas[0];
|
|
223
|
+
if (!renderData || (!vData && !uintVData)) return;
|
|
224
|
+
|
|
225
|
+
const floatStride = 5;
|
|
226
|
+
const vertexCount =
|
|
227
|
+
renderData.vertexCount || (vData || uintVData).length / floatStride;
|
|
228
|
+
if (vertexCount <= 0) return;
|
|
229
|
+
|
|
230
|
+
const nodeColor = this.node.color || cc.Color.WHITE;
|
|
231
|
+
const alphaScale = this.getEffectiveOpacity() / 255;
|
|
232
|
+
const len = this._colors.length;
|
|
233
|
+
|
|
234
|
+
let projMin = 0;
|
|
235
|
+
let projRange = 1;
|
|
236
|
+
let projBuf: Float32Array = null;
|
|
237
|
+
|
|
238
|
+
if (len >= 2) {
|
|
239
|
+
const rad = (this._angle * Math.PI) / 180;
|
|
240
|
+
const dx = Math.sin(rad);
|
|
241
|
+
const dy = -Math.cos(rad);
|
|
242
|
+
|
|
243
|
+
if (!this._projBuf || this._projBuf.length < vertexCount) {
|
|
244
|
+
this._projBuf = new Float32Array(vertexCount);
|
|
245
|
+
}
|
|
246
|
+
projBuf = this._projBuf;
|
|
247
|
+
|
|
248
|
+
let pMin = Infinity;
|
|
249
|
+
let pMax = -Infinity;
|
|
250
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
251
|
+
const base = i * floatStride;
|
|
252
|
+
const x = this._readFloat(vData, uintVData, base);
|
|
253
|
+
const y = this._readFloat(vData, uintVData, base + 1);
|
|
254
|
+
const p = x * dx + y * dy;
|
|
255
|
+
projBuf[i] = p;
|
|
256
|
+
if (p < pMin) pMin = p;
|
|
257
|
+
if (p > pMax) pMax = p;
|
|
258
|
+
}
|
|
259
|
+
projMin = pMin;
|
|
260
|
+
projRange = Math.max(pMax - pMin, 1e-4);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const out = this._tmp;
|
|
264
|
+
const lastStop = len - 1;
|
|
265
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
266
|
+
if (len === 0) {
|
|
267
|
+
cc.Color.set(out, 255, 255, 255, 255);
|
|
268
|
+
} else if (len === 1) {
|
|
269
|
+
const c = this._colors[0] || nodeColor;
|
|
270
|
+
cc.Color.set(out, c.r, c.g, c.b, c.a);
|
|
271
|
+
} else {
|
|
272
|
+
const t = (projBuf[i] - projMin) / projRange;
|
|
273
|
+
const f = t * lastStop;
|
|
274
|
+
const i0 = Math.min(Math.floor(f), lastStop - 1);
|
|
275
|
+
const alpha = f - i0;
|
|
276
|
+
const c0 = this._colors[i0] || nodeColor;
|
|
277
|
+
const c1 = this._colors[i0 + 1] || nodeColor;
|
|
278
|
+
cc.Color.set(
|
|
279
|
+
out,
|
|
280
|
+
Math.round(c0.r + (c1.r - c0.r) * alpha),
|
|
281
|
+
Math.round(c0.g + (c1.g - c0.g) * alpha),
|
|
282
|
+
Math.round(c0.b + (c1.b - c0.b) * alpha),
|
|
283
|
+
Math.round(c0.a + (c1.a - c0.a) * alpha),
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
cc.Color.set(out, out.r, out.g, out.b, Math.round(out.a * alphaScale));
|
|
288
|
+
this._writeColor(vData, uintVData, i * floatStride + 4, out);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
renderData.vertDirty = true;
|
|
292
|
+
renderer._vertsDirty = true;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private _getRenderData(
|
|
296
|
+
assembler: WEAssembler,
|
|
297
|
+
renderer: cc.RenderComponent,
|
|
298
|
+
): WEAssemblerRenderData {
|
|
299
|
+
if (assembler && assembler._renderData) return assembler._renderData;
|
|
300
|
+
return (renderer as any)._renderData as WEAssemblerRenderData;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private _readFloat(
|
|
304
|
+
vData: Float32Array,
|
|
305
|
+
uintVData: Uint32Array,
|
|
306
|
+
index: number,
|
|
307
|
+
): number {
|
|
308
|
+
if (vData) return vData[index];
|
|
309
|
+
return this._uintToFloat(uintVData[index]);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private _writeColor(
|
|
313
|
+
vData: Float32Array,
|
|
314
|
+
uintVData: Uint32Array,
|
|
315
|
+
index: number,
|
|
316
|
+
color: cc.Color,
|
|
317
|
+
): void {
|
|
318
|
+
const value = (color as any)._val;
|
|
319
|
+
if (uintVData) uintVData[index] = value;
|
|
320
|
+
if (vData) vData[index] = this._uintToFloat(value);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private _uintToFloat(value: number): number {
|
|
324
|
+
this._convertUint[0] = value;
|
|
325
|
+
return this._convertFloat[0];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
interface WEAssembler {
|
|
330
|
+
_renderData?: WEAssemblerRenderData;
|
|
331
|
+
updateRenderData?: Function;
|
|
332
|
+
fillBuffers?: Function;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
interface WEAssemblerRenderData {
|
|
336
|
+
vDatas?: Float32Array[];
|
|
337
|
+
uintVDatas?: Uint32Array[];
|
|
338
|
+
iDatas?: Uint16Array[];
|
|
339
|
+
vertexCount?: number;
|
|
340
|
+
vertDirty?: boolean;
|
|
341
|
+
}
|