@fps-games/vfx 0.3.6 → 0.3.7
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/CHANGELOG.md +8 -0
- package/dist/effects/building-upgrade/index.d.ts +2 -5
- package/dist/effects/building-upgrade/index.js +77 -51
- package/dist/effects/debris-smoke/index.d.ts +8 -0
- package/dist/effects/debris-smoke/index.js +37 -2
- package/dist/effects/energy-shield/index.js +96 -34
- package/dist/effects/weapon-trail/index.d.ts +1 -0
- package/dist/effects/weapon-trail/index.js +62 -9
- package/dist/index.js +1 -0
- package/dist/types.d.ts +3 -1
- package/package.json +1 -1
- package/packages/effects/building-upgrade/vfx-params.json +1 -5
- package/packages/effects/energy-shield/vfx-params.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { VfxColorValue, VfxEffectPackage, VfxParamValues } from '../../types';
|
|
2
|
+
type BuildingUpgradeFootprintShape = 'rectangle' | 'circle';
|
|
2
3
|
interface BuildingUpgradeParams extends VfxParamValues {
|
|
3
4
|
lifetime: number;
|
|
4
|
-
scale: number;
|
|
5
|
-
initialHeight: number;
|
|
6
5
|
color: VfxColorValue;
|
|
7
6
|
opacity: number;
|
|
8
|
-
|
|
9
|
-
scaleY: number;
|
|
10
|
-
scaleZ: number;
|
|
7
|
+
footprintShape: BuildingUpgradeFootprintShape;
|
|
11
8
|
centerCount: number;
|
|
12
9
|
outlineCount: number;
|
|
13
10
|
strandWidth: number;
|
|
@@ -15,18 +15,15 @@ const manifest = {
|
|
|
15
15
|
};
|
|
16
16
|
const BASE_FOOTPRINT_X = 3.2;
|
|
17
17
|
const BASE_FOOTPRINT_Z = 2.6;
|
|
18
|
+
const BASE_CIRCLE_RADIUS = 1.4;
|
|
18
19
|
const BASE_CENTER_HEIGHT = 2.2;
|
|
19
20
|
const BASE_OUTLINE_HEIGHT = 3.1;
|
|
20
21
|
const BASE_GLOW_THICKNESS = 0.38;
|
|
21
22
|
const DEFAULT_PARAMS = {
|
|
22
23
|
lifetime: 1.8,
|
|
23
|
-
scale: 1,
|
|
24
|
-
initialHeight: 0.02,
|
|
25
24
|
color: { r: 1, g: 0.84, b: 0.12 },
|
|
26
25
|
opacity: 1,
|
|
27
|
-
|
|
28
|
-
scaleY: 1,
|
|
29
|
-
scaleZ: 1,
|
|
26
|
+
footprintShape: "rectangle",
|
|
30
27
|
centerCount: 46,
|
|
31
28
|
outlineCount: 150,
|
|
32
29
|
strandWidth: 0.035,
|
|
@@ -43,13 +40,12 @@ const buildingUpgradeEffectPackage = {
|
|
|
43
40
|
debugAnchor: "playerRight",
|
|
44
41
|
debugParams: [
|
|
45
42
|
{ key: "lifetime", label: "生命周期", kind: "lifetime", min: 0.2, max: 6, step: 0.01 },
|
|
46
|
-
{ key: "scale", label: "整体缩放", kind: "scale", min: 0.1, max: 6, step: 0.01 },
|
|
47
|
-
{ key: "initialHeight", label: "初始高度", kind: "number", min: -1, max: 3, step: 0.01 },
|
|
48
43
|
{ key: "color", label: "金光颜色", kind: "color" },
|
|
49
44
|
{ key: "opacity", label: "透明度", kind: "opacity", min: 0, max: 1, step: 0.01 },
|
|
50
|
-
{ key: "
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
{ key: "footprintShape", label: "发射形状", kind: "enum", options: [
|
|
46
|
+
{ value: "rectangle", label: "矩形" },
|
|
47
|
+
{ value: "circle", label: "圆形" }
|
|
48
|
+
] },
|
|
53
49
|
{ key: "centerCount", label: "中心金丝数量", kind: "number", min: 0, max: 240, step: 1 },
|
|
54
50
|
{ key: "outlineCount", label: "轮廓金丝数量", kind: "number", min: 0, max: 420, step: 1 },
|
|
55
51
|
{ key: "strandWidth", label: "金丝粗细", kind: "number", min: 5e-3, max: 0.2, step: 1e-3 },
|
|
@@ -58,14 +54,15 @@ const buildingUpgradeEffectPackage = {
|
|
|
58
54
|
{ key: "outlineThickness", label: "轮廓厚度", kind: "number", min: 0, max: 1, step: 0.01 },
|
|
59
55
|
{ key: "glowOpacity", label: "光芒强度", kind: "opacity", min: 0, max: 1, step: 0.01 }
|
|
60
56
|
],
|
|
61
|
-
create({ scene, root, position, params }) {
|
|
62
|
-
root.position.copyFrom(position)
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
const
|
|
57
|
+
create({ scene, root, position, params, scale }) {
|
|
58
|
+
root.position.copyFrom(position);
|
|
59
|
+
const visualScale = normalizeBuildingUpgradeScale(scale);
|
|
60
|
+
const centerStrands = createStrandSystem(scene, root, params, visualScale, "center");
|
|
61
|
+
const outlineStrands = createStrandSystem(scene, root, params, visualScale, "outline");
|
|
62
|
+
const glow = createOutlineGlow(scene, root, params, visualScale);
|
|
66
63
|
const disposeTimer = window.setTimeout(
|
|
67
64
|
() => disposeAll(),
|
|
68
|
-
Math.max(0.05, params.lifetime + getMaxStrandHeight(
|
|
65
|
+
Math.max(0.05, params.lifetime + getMaxStrandHeight(visualScale) / Math.max(0.1, params.riseSpeed) * 0.18 + 0.45) * 1e3
|
|
69
66
|
);
|
|
70
67
|
let disposed = false;
|
|
71
68
|
centerStrands.start();
|
|
@@ -88,12 +85,12 @@ const buildingUpgradeEffectPackage = {
|
|
|
88
85
|
}
|
|
89
86
|
}
|
|
90
87
|
};
|
|
91
|
-
function createStrandSystem(scene, root, params, mode) {
|
|
88
|
+
function createStrandSystem(scene, root, params, visualScale, mode) {
|
|
92
89
|
const count = Math.max(0, Math.round(mode === "center" ? params.centerCount : params.outlineCount));
|
|
93
90
|
const system = new ParticleSystem(`building-upgrade.${mode}-strands`, Math.max(1, count), scene);
|
|
94
91
|
const color = params.color;
|
|
95
|
-
const height = mode === "center" ? getCenterHeight(
|
|
96
|
-
const width = params.strandWidth *
|
|
92
|
+
const height = mode === "center" ? getCenterHeight(visualScale) : getOutlineHeight(visualScale);
|
|
93
|
+
const width = params.strandWidth * visualScale.horizontal * (mode === "center" ? 1.35 : 0.92);
|
|
97
94
|
configureVfxParticleSystem(system);
|
|
98
95
|
system.particleTexture = getGoldStrandTexture(scene);
|
|
99
96
|
system.emitter = root;
|
|
@@ -110,9 +107,9 @@ function createStrandSystem(scene, root, params, mode) {
|
|
|
110
107
|
system.maxScaleX = Math.max(system.minScaleX, width * 1.8);
|
|
111
108
|
system.minScaleY = 1;
|
|
112
109
|
system.maxScaleY = mode === "center" ? 1.45 : 1.75;
|
|
113
|
-
system.minEmitPower = Math.max(0.01, params.riseSpeed *
|
|
114
|
-
system.maxEmitPower = Math.max(system.minEmitPower, params.riseSpeed *
|
|
115
|
-
system.gravity = new Vector3(0, 0.38
|
|
110
|
+
system.minEmitPower = Math.max(0.01, params.riseSpeed * (mode === "center" ? 0.5 : 0.7));
|
|
111
|
+
system.maxEmitPower = Math.max(system.minEmitPower, params.riseSpeed * (mode === "center" ? 1.15 : 1.65));
|
|
112
|
+
system.gravity = new Vector3(0, 0.38, 0);
|
|
116
113
|
system.blendMode = ParticleSystem.BLENDMODE_ADD;
|
|
117
114
|
system.color1 = toColor4(color, params.opacity * (mode === "center" ? 0.72 : 0.86));
|
|
118
115
|
system.color2 = toColor4({ r: 1, g: 0.98, b: 0.52 }, params.opacity);
|
|
@@ -136,7 +133,7 @@ function createStrandSystem(scene, root, params, mode) {
|
|
|
136
133
|
system.startDirectionFunction = (worldMatrix, directionToUpdate, particle, isLocal) => {
|
|
137
134
|
const index = particle.id + directionIndex;
|
|
138
135
|
directionIndex += 1;
|
|
139
|
-
const sway =
|
|
136
|
+
const sway = mode === "center" ? 0.22 : 0.12;
|
|
140
137
|
const local = new Vector3(
|
|
141
138
|
(sequence01(index, 4) - 0.5) * sway,
|
|
142
139
|
0.8 + sequence01(index, 5) * 0.65,
|
|
@@ -147,11 +144,11 @@ function createStrandSystem(scene, root, params, mode) {
|
|
|
147
144
|
};
|
|
148
145
|
return system;
|
|
149
146
|
}
|
|
150
|
-
function createOutlineGlow(scene, root, params) {
|
|
147
|
+
function createOutlineGlow(scene, root, params, visualScale) {
|
|
151
148
|
const count = Math.max(1, Math.round(params.outlineCount * 0.82));
|
|
152
149
|
const system = new ParticleSystem("building-upgrade.outline-glow-rays", count, scene);
|
|
153
|
-
const height = getOutlineHeight(
|
|
154
|
-
const glowWidth = Math.max(0.04, BASE_GLOW_THICKNESS *
|
|
150
|
+
const height = getOutlineHeight(visualScale) * 1.06;
|
|
151
|
+
const glowWidth = Math.max(0.04, BASE_GLOW_THICKNESS * visualScale.horizontal);
|
|
155
152
|
const alpha = params.glowOpacity * params.opacity;
|
|
156
153
|
configureVfxParticleSystem(system);
|
|
157
154
|
system.particleTexture = getOutlineGlowTexture(scene);
|
|
@@ -169,9 +166,9 @@ function createOutlineGlow(scene, root, params) {
|
|
|
169
166
|
system.maxScaleX = glowWidth * 1.18;
|
|
170
167
|
system.minScaleY = 1.05;
|
|
171
168
|
system.maxScaleY = 1.75;
|
|
172
|
-
system.minEmitPower = Math.max(0.01, params.riseSpeed *
|
|
173
|
-
system.maxEmitPower = Math.max(system.minEmitPower, params.riseSpeed *
|
|
174
|
-
system.gravity = new Vector3(0, 0.3
|
|
169
|
+
system.minEmitPower = Math.max(0.01, params.riseSpeed * 0.58);
|
|
170
|
+
system.maxEmitPower = Math.max(system.minEmitPower, params.riseSpeed * 1.38);
|
|
171
|
+
system.gravity = new Vector3(0, 0.3, 0);
|
|
175
172
|
system.blendMode = ParticleSystem.BLENDMODE_ADD;
|
|
176
173
|
system.color1 = toColor4(params.color, alpha * 0.62);
|
|
177
174
|
system.color2 = toColor4({ r: 1, g: 0.98, b: 0.58 }, alpha * 0.86);
|
|
@@ -195,7 +192,7 @@ function createOutlineGlow(scene, root, params) {
|
|
|
195
192
|
system.startDirectionFunction = (worldMatrix, directionToUpdate, particle, isLocal) => {
|
|
196
193
|
const index = particle.id + directionIndex + 31;
|
|
197
194
|
directionIndex += 1;
|
|
198
|
-
const sway = 0.08
|
|
195
|
+
const sway = 0.08;
|
|
199
196
|
const local = new Vector3(
|
|
200
197
|
(sequence01(index, 4) - 0.5) * sway,
|
|
201
198
|
0.82 + sequence01(index, 5) * 0.58,
|
|
@@ -207,49 +204,78 @@ function createOutlineGlow(scene, root, params) {
|
|
|
207
204
|
return system;
|
|
208
205
|
}
|
|
209
206
|
function sampleCenterPosition(index, params) {
|
|
207
|
+
const shape = getFootprintShape(params);
|
|
208
|
+
if (shape === "circle") return sampleCircleCenterPosition(index, params);
|
|
210
209
|
const angle = sequence01(index, 0) * Math.PI * 2;
|
|
211
210
|
const radius = Math.sqrt(0.16 + sequence01(index, 1) * 0.84);
|
|
212
|
-
const xRadius = getFootprintX(
|
|
213
|
-
const zRadius = getFootprintZ(
|
|
211
|
+
const xRadius = getFootprintX() * 0.5 * params.centerSpread;
|
|
212
|
+
const zRadius = getFootprintZ() * 0.5 * params.centerSpread;
|
|
214
213
|
const jitterX = (sequence01(index, 7) - 0.5) * xRadius * 0.18;
|
|
215
214
|
const jitterZ = (sequence01(index, 8) - 0.5) * zRadius * 0.18;
|
|
216
215
|
const x = Math.cos(angle) * radius * xRadius + jitterX;
|
|
217
216
|
const z = Math.sin(angle) * radius * zRadius + jitterZ;
|
|
218
|
-
const y = sequence01(index, 2) * 0.12
|
|
217
|
+
const y = sequence01(index, 2) * 0.12;
|
|
219
218
|
return new Vector3(x, y, z);
|
|
220
219
|
}
|
|
221
220
|
function sampleOutlinePosition(index, params) {
|
|
222
|
-
const
|
|
223
|
-
|
|
221
|
+
const shape = getFootprintShape(params);
|
|
222
|
+
if (shape === "circle") return sampleCircleOutlinePosition(index, params);
|
|
223
|
+
return sampleRectOutlinePosition(index, getFootprintX(), getFootprintZ(), params.outlineThickness);
|
|
224
|
+
}
|
|
225
|
+
function sampleCircleCenterPosition(index, params) {
|
|
226
|
+
const angle = sequence01(index, 0) * Math.PI * 2;
|
|
227
|
+
const radius = Math.sqrt(sequence01(index, 1)) * BASE_CIRCLE_RADIUS * params.centerSpread;
|
|
228
|
+
const jitterRadius = (sequence01(index, 7) - 0.5) * BASE_CIRCLE_RADIUS * 0.08;
|
|
229
|
+
const r = Math.max(0, radius + jitterRadius);
|
|
230
|
+
const y = sequence01(index, 2) * 0.12;
|
|
231
|
+
return new Vector3(Math.cos(angle) * r, y, Math.sin(angle) * r);
|
|
232
|
+
}
|
|
233
|
+
function sampleCircleOutlinePosition(index, params) {
|
|
234
|
+
const angle = sequence01(index, 0) * Math.PI * 2;
|
|
235
|
+
const jitter = (sequence01(index, 1) - 0.5) * params.outlineThickness;
|
|
236
|
+
const radius = Math.max(1e-3, BASE_CIRCLE_RADIUS + jitter);
|
|
237
|
+
const y = sequence01(index, 2) * 0.16;
|
|
238
|
+
return new Vector3(Math.cos(angle) * radius, y, Math.sin(angle) * radius);
|
|
239
|
+
}
|
|
240
|
+
function sampleRectOutlinePosition(index, width, depth, thickness) {
|
|
224
241
|
const perimeter = Math.max(1e-3, width * 2 + depth * 2);
|
|
225
242
|
const distance = sequence01(index, 0) * perimeter;
|
|
226
|
-
const jitter = (sequence01(index, 1) - 0.5) *
|
|
227
|
-
const y = sequence01(index, 2) * 0.16
|
|
243
|
+
const jitter = (sequence01(index, 1) - 0.5) * thickness;
|
|
244
|
+
const y = sequence01(index, 2) * 0.16;
|
|
228
245
|
if (distance < width) return new Vector3(-width * 0.5 + distance, y, -depth * 0.5 + jitter);
|
|
229
246
|
if (distance < width + depth) return new Vector3(width * 0.5 + jitter, y, -depth * 0.5 + distance - width);
|
|
230
247
|
if (distance < width * 2 + depth) return new Vector3(width * 0.5 - (distance - width - depth), y, depth * 0.5 + jitter);
|
|
231
248
|
return new Vector3(-width * 0.5 + jitter, y, depth * 0.5 - (distance - width * 2 - depth));
|
|
232
249
|
}
|
|
233
|
-
function
|
|
234
|
-
|
|
250
|
+
function getFootprintShape(params) {
|
|
251
|
+
if (params.footprintShape === "circle") return params.footprintShape;
|
|
252
|
+
return "rectangle";
|
|
235
253
|
}
|
|
236
|
-
function
|
|
237
|
-
return
|
|
254
|
+
function getFootprintX() {
|
|
255
|
+
return BASE_FOOTPRINT_X;
|
|
238
256
|
}
|
|
239
|
-
function
|
|
240
|
-
return
|
|
257
|
+
function getFootprintZ() {
|
|
258
|
+
return BASE_FOOTPRINT_Z;
|
|
241
259
|
}
|
|
242
|
-
function
|
|
243
|
-
return
|
|
260
|
+
function getCenterHeight(visualScale) {
|
|
261
|
+
return BASE_CENTER_HEIGHT * visualScale.y;
|
|
244
262
|
}
|
|
245
|
-
function
|
|
246
|
-
return
|
|
263
|
+
function getOutlineHeight(visualScale) {
|
|
264
|
+
return BASE_OUTLINE_HEIGHT * visualScale.y;
|
|
247
265
|
}
|
|
248
|
-
function
|
|
249
|
-
return
|
|
266
|
+
function getMaxStrandHeight(visualScale) {
|
|
267
|
+
return Math.max(getCenterHeight(visualScale), getOutlineHeight(visualScale));
|
|
250
268
|
}
|
|
251
|
-
function
|
|
252
|
-
|
|
269
|
+
function normalizeBuildingUpgradeScale(scale) {
|
|
270
|
+
const x = Math.max(0.05, Math.abs(scale.x));
|
|
271
|
+
const y = Math.max(0.05, Math.abs(scale.y));
|
|
272
|
+
const z = Math.max(0.05, Math.abs(scale.z));
|
|
273
|
+
return {
|
|
274
|
+
x,
|
|
275
|
+
y,
|
|
276
|
+
z,
|
|
277
|
+
horizontal: Math.max(0.05, (x + z) * 0.5)
|
|
278
|
+
};
|
|
253
279
|
}
|
|
254
280
|
function getGoldStrandTexture(scene) {
|
|
255
281
|
const key = "__vfx_building_upgrade_gold_strand_texture";
|
|
@@ -3,6 +3,14 @@ interface DebrisSmokeParams extends VfxParamValues {
|
|
|
3
3
|
debrisCount: number;
|
|
4
4
|
debrisSize: number;
|
|
5
5
|
debrisRadius: number;
|
|
6
|
+
debrisSpeed: number;
|
|
7
|
+
debrisHorizontalSpeed: number;
|
|
8
|
+
debrisUpSpeed: number;
|
|
9
|
+
debrisDirectionX: number;
|
|
10
|
+
debrisDirectionY: number;
|
|
11
|
+
debrisDirectionZ: number;
|
|
12
|
+
debrisGravity: number;
|
|
13
|
+
debrisLifetime: number;
|
|
6
14
|
smokeCount: number;
|
|
7
15
|
smokeSize: number;
|
|
8
16
|
}
|
|
@@ -11,6 +11,14 @@ const DEFAULT_PARAMS = {
|
|
|
11
11
|
debrisCount: 14,
|
|
12
12
|
debrisSize: 0.35,
|
|
13
13
|
debrisRadius: 1.8,
|
|
14
|
+
debrisSpeed: 1,
|
|
15
|
+
debrisHorizontalSpeed: 1,
|
|
16
|
+
debrisUpSpeed: 1,
|
|
17
|
+
debrisDirectionX: 0,
|
|
18
|
+
debrisDirectionY: 0,
|
|
19
|
+
debrisDirectionZ: 0,
|
|
20
|
+
debrisGravity: 7,
|
|
21
|
+
debrisLifetime: 1.2,
|
|
14
22
|
smokeCount: 10,
|
|
15
23
|
smokeSize: 1
|
|
16
24
|
};
|
|
@@ -51,6 +59,14 @@ const debrisSmokeEffectPackage = {
|
|
|
51
59
|
{ key: "debrisCount", label: "碎石·数量", kind: "number", min: 0, max: 30, step: 1 },
|
|
52
60
|
{ key: "debrisSize", label: "碎石·大小", kind: "number", min: 0.1, max: 1, step: 0.01 },
|
|
53
61
|
{ key: "debrisRadius", label: "碎石·范围", kind: "scale", min: 0.5, max: 5, step: 0.1 },
|
|
62
|
+
{ key: "debrisSpeed", label: "碎石·整体速度", kind: "number", min: 0, max: 5, step: 0.05 },
|
|
63
|
+
{ key: "debrisHorizontalSpeed", label: "碎石·横向速度", kind: "number", min: 0, max: 5, step: 0.05 },
|
|
64
|
+
{ key: "debrisUpSpeed", label: "碎石·上抛速度", kind: "number", min: 0, max: 5, step: 0.05 },
|
|
65
|
+
{ key: "debrisDirectionX", label: "碎石·方向 X", kind: "number", min: -1, max: 1, step: 0.01 },
|
|
66
|
+
{ key: "debrisDirectionY", label: "碎石·方向 Y", kind: "number", min: -1, max: 1, step: 0.01 },
|
|
67
|
+
{ key: "debrisDirectionZ", label: "碎石·方向 Z", kind: "number", min: -1, max: 1, step: 0.01 },
|
|
68
|
+
{ key: "debrisGravity", label: "碎石·下落重力", kind: "number", min: 0, max: 20, step: 0.1 },
|
|
69
|
+
{ key: "debrisLifetime", label: "碎石·可见时长", kind: "lifetime", min: 0.2, max: 4, step: 0.05 },
|
|
54
70
|
// 段2 · 烟雾
|
|
55
71
|
{ key: "smokeCount", label: "烟雾·数量", kind: "number", min: 0, max: 40, step: 1 },
|
|
56
72
|
{ key: "smokeSize", label: "烟雾·尺寸", kind: "scale", min: 0.2, max: 3, step: 0.05 }
|
|
@@ -85,12 +101,12 @@ const debrisSmokeEffectPackage = {
|
|
|
85
101
|
d.isPickable = false;
|
|
86
102
|
d.normalizeToUnitCube();
|
|
87
103
|
d.scaling.scaleInPlace(r * params.debrisSize * (0.5 + Math.random() * 0.8));
|
|
88
|
-
pool.spawn(d, position,
|
|
104
|
+
pool.spawn(d, position, makeDebrisVelocity(params), params.debrisGravity, params.debrisLifetime);
|
|
89
105
|
}
|
|
90
106
|
}).catch((e) => {
|
|
91
107
|
console.warn("[VFX] debris-smoke load failed", e);
|
|
92
108
|
});
|
|
93
|
-
const disposeTimer = window.setTimeout(() => disposeAll(), 2200);
|
|
109
|
+
const disposeTimer = window.setTimeout(() => disposeAll(), Math.max(2200, params.debrisLifetime * 1e3 + 1e3));
|
|
94
110
|
function disposeAll() {
|
|
95
111
|
if (disposed) return;
|
|
96
112
|
disposed = true;
|
|
@@ -104,6 +120,25 @@ const debrisSmokeEffectPackage = {
|
|
|
104
120
|
} };
|
|
105
121
|
}
|
|
106
122
|
};
|
|
123
|
+
function makeDebrisVelocity(params) {
|
|
124
|
+
const velocity = new Vector3(
|
|
125
|
+
(Math.random() - 0.5) * 13 * params.debrisHorizontalSpeed,
|
|
126
|
+
(Math.random() * 6.75 + 2.25) * params.debrisUpSpeed,
|
|
127
|
+
(Math.random() - 0.5) * 13 * params.debrisHorizontalSpeed
|
|
128
|
+
);
|
|
129
|
+
const direction = new Vector3(params.debrisDirectionX, params.debrisDirectionY, params.debrisDirectionZ);
|
|
130
|
+
const directionLength = direction.length();
|
|
131
|
+
if (directionLength > 1e-4) {
|
|
132
|
+
direction.scaleInPlace(1 / directionLength);
|
|
133
|
+
const directionSpeed = 6.5 * Math.min(directionLength, 1);
|
|
134
|
+
velocity.addInPlace(new Vector3(
|
|
135
|
+
direction.x * directionSpeed * params.debrisHorizontalSpeed,
|
|
136
|
+
direction.y * directionSpeed * params.debrisUpSpeed,
|
|
137
|
+
direction.z * directionSpeed * params.debrisHorizontalSpeed
|
|
138
|
+
));
|
|
139
|
+
}
|
|
140
|
+
return velocity.scaleInPlace(params.debrisSpeed);
|
|
141
|
+
}
|
|
107
142
|
export {
|
|
108
143
|
debrisSmokeEffectPackage
|
|
109
144
|
};
|
|
@@ -7,6 +7,9 @@ import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";
|
|
|
7
7
|
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
|
|
8
8
|
import { V as VFX_RENDERING_GROUP_ID } from "../../chunks/rendering-CV3nhygX.js";
|
|
9
9
|
const SHADER_NAME = "vfxEnergyShield";
|
|
10
|
+
const HALO_ALPHA_SCALE_MAX = 0.3;
|
|
11
|
+
const HALO_FEATHER_ALPHA_RATIO = 0.72;
|
|
12
|
+
const HALO_FEATHER_SCALE_OFFSET = 0.018;
|
|
10
13
|
const DEFAULT_PARAMS = {
|
|
11
14
|
radius: 3.18,
|
|
12
15
|
verticalScale: 1.02,
|
|
@@ -25,7 +28,7 @@ const DEFAULT_PARAMS = {
|
|
|
25
28
|
flowIntensity: 1.15,
|
|
26
29
|
marbleContrast: 0.67,
|
|
27
30
|
bloomIntensity: 0.45,
|
|
28
|
-
haloScale: 1.
|
|
31
|
+
haloScale: 1.018,
|
|
29
32
|
showHalo: false,
|
|
30
33
|
segments: 64
|
|
31
34
|
};
|
|
@@ -146,7 +149,8 @@ void main(void) {
|
|
|
146
149
|
vec3 normalW = normalize(vNormalW);
|
|
147
150
|
vec3 viewDir = normalize(cameraPosition - vWorldPos);
|
|
148
151
|
float ndv = saturate(dot(normalW, viewDir));
|
|
149
|
-
float
|
|
152
|
+
float viewRim = saturate(1.0 - ndv);
|
|
153
|
+
float fresnel = pow(viewRim, max(0.05, uFresnelPower)) * uFresnelStrength;
|
|
150
154
|
|
|
151
155
|
float t = uTime * uFlowSpeed;
|
|
152
156
|
vec3 spherePos = normalize(vObjPos + vec3(0.0001)) * max(0.05, uNoiseScale);
|
|
@@ -175,12 +179,25 @@ void main(void) {
|
|
|
175
179
|
alpha += fresnel * uOpacity * 0.58;
|
|
176
180
|
alpha += noiseEdge * uNoiseEdgeIntensity * 0.05;
|
|
177
181
|
|
|
178
|
-
if (uHaloMode >
|
|
179
|
-
float haloRim = pow(
|
|
180
|
-
float
|
|
181
|
-
float
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
if (uHaloMode > 1.5) {
|
|
183
|
+
float haloRim = pow(viewRim, max(0.28, uFresnelPower * 0.44));
|
|
184
|
+
float featherIn = smoothstep(0.1, 0.46, haloRim);
|
|
185
|
+
float featherOut = 1.0 - smoothstep(0.72, 1.0, haloRim);
|
|
186
|
+
float featherRim = featherIn * featherOut;
|
|
187
|
+
float outerFade = 1.0 - smoothstep(0.88, 1.0, haloRim);
|
|
188
|
+
color = uColor * (0.2 + featherRim * 0.24 + cloud * 0.04);
|
|
189
|
+
alpha = featherRim * 0.58 * outerFade;
|
|
190
|
+
} else if (uHaloMode > 0.5) {
|
|
191
|
+
float haloRim = pow(viewRim, max(0.3, uFresnelPower * 0.48));
|
|
192
|
+
float bodyIn = smoothstep(0.34, 0.68, haloRim);
|
|
193
|
+
float bodyOut = 1.0 - smoothstep(0.72, 1.0, haloRim);
|
|
194
|
+
float bodyRim = bodyIn * bodyOut;
|
|
195
|
+
float featherIn = smoothstep(0.12, 0.44, haloRim);
|
|
196
|
+
float featherOut = 1.0 - smoothstep(0.48, 1.0, haloRim);
|
|
197
|
+
float featherRim = featherIn * featherOut;
|
|
198
|
+
float outerFade = 1.0 - smoothstep(0.86, 1.0, haloRim);
|
|
199
|
+
color = uColor * (0.24 + bodyRim * 0.34 + featherRim * 0.12 + cloud * 0.05);
|
|
200
|
+
alpha = (bodyRim * 0.5 + featherRim * 0.14 + noiseEdge * 0.01) * outerFade;
|
|
184
201
|
}
|
|
185
202
|
|
|
186
203
|
alpha *= uHaloAlphaScale;
|
|
@@ -254,47 +271,78 @@ function createEnergyShield(scene, options = {}) {
|
|
|
254
271
|
shield.material = material;
|
|
255
272
|
let halo = null;
|
|
256
273
|
let haloMaterial = null;
|
|
274
|
+
let haloFeather = null;
|
|
275
|
+
let haloFeatherMaterial = null;
|
|
257
276
|
const disposeHalo = () => {
|
|
258
277
|
halo?.dispose(false, false);
|
|
259
278
|
haloMaterial?.dispose();
|
|
279
|
+
haloFeather?.dispose(false, false);
|
|
280
|
+
haloFeatherMaterial?.dispose();
|
|
260
281
|
halo = null;
|
|
261
282
|
haloMaterial = null;
|
|
283
|
+
haloFeather = null;
|
|
284
|
+
haloFeatherMaterial = null;
|
|
262
285
|
};
|
|
263
286
|
const ensureHalo = () => {
|
|
264
287
|
if (!params.showHalo || params.bloomIntensity <= 1e-3) {
|
|
265
288
|
disposeHalo();
|
|
266
289
|
return;
|
|
267
290
|
}
|
|
268
|
-
if (halo
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
291
|
+
if (!halo || !haloMaterial) {
|
|
292
|
+
halo = MeshBuilder.CreateSphere(
|
|
293
|
+
"vfx_energyShield_halo",
|
|
294
|
+
{
|
|
295
|
+
diameter: 2,
|
|
296
|
+
segments: Math.max(16, Math.round(params.segments)),
|
|
297
|
+
slice: 0.5,
|
|
298
|
+
updatable: false
|
|
299
|
+
},
|
|
300
|
+
scene
|
|
301
|
+
);
|
|
302
|
+
halo.parent = shield;
|
|
303
|
+
halo.isPickable = false;
|
|
304
|
+
halo.alwaysSelectAsActiveMesh = true;
|
|
305
|
+
halo.renderingGroupId = shield.renderingGroupId;
|
|
306
|
+
halo.alphaIndex = shield.alphaIndex + 1;
|
|
307
|
+
halo.scaling.setAll(Math.max(1, params.haloScale));
|
|
308
|
+
haloMaterial = createShieldMaterial(scene, {
|
|
309
|
+
name: "vfx_energyShield_halo_material",
|
|
310
|
+
alphaMode: Constants.ALPHA_ADD
|
|
311
|
+
});
|
|
312
|
+
halo.material = haloMaterial;
|
|
313
|
+
}
|
|
314
|
+
if (!haloFeather || !haloFeatherMaterial) {
|
|
315
|
+
haloFeather = MeshBuilder.CreateSphere(
|
|
316
|
+
"vfx_energyShield_halo_feather",
|
|
317
|
+
{
|
|
318
|
+
diameter: 2,
|
|
319
|
+
segments: Math.max(16, Math.round(params.segments)),
|
|
320
|
+
slice: 0.5,
|
|
321
|
+
updatable: false
|
|
322
|
+
},
|
|
323
|
+
scene
|
|
324
|
+
);
|
|
325
|
+
haloFeather.parent = shield;
|
|
326
|
+
haloFeather.isPickable = false;
|
|
327
|
+
haloFeather.alwaysSelectAsActiveMesh = true;
|
|
328
|
+
haloFeather.renderingGroupId = shield.renderingGroupId;
|
|
329
|
+
haloFeather.alphaIndex = shield.alphaIndex + 2;
|
|
330
|
+
haloFeather.scaling.setAll(getHaloFeatherScale(params));
|
|
331
|
+
haloFeatherMaterial = createShieldMaterial(scene, {
|
|
332
|
+
name: "vfx_energyShield_halo_feather_material",
|
|
333
|
+
alphaMode: Constants.ALPHA_ADD
|
|
334
|
+
});
|
|
335
|
+
haloFeather.material = haloFeatherMaterial;
|
|
336
|
+
}
|
|
290
337
|
};
|
|
291
338
|
ensureHalo();
|
|
292
|
-
applyParams(material, shield, halo, haloMaterial, params);
|
|
339
|
+
applyParams(material, shield, halo, haloMaterial, haloFeather, haloFeatherMaterial, params);
|
|
293
340
|
let elapsed = 0;
|
|
294
341
|
const observer = scene.onBeforeRenderObservable.add(() => {
|
|
295
342
|
elapsed += Math.min(scene.getEngine().getDeltaTime() / 1e3, 0.05);
|
|
296
343
|
material.setFloat("uTime", elapsed);
|
|
297
344
|
haloMaterial?.setFloat("uTime", elapsed);
|
|
345
|
+
haloFeatherMaterial?.setFloat("uTime", elapsed);
|
|
298
346
|
});
|
|
299
347
|
let disposed = false;
|
|
300
348
|
const dispose = () => {
|
|
@@ -313,7 +361,7 @@ function createEnergyShield(scene, options = {}) {
|
|
|
313
361
|
update(partial) {
|
|
314
362
|
params = mergeParams(partial, params);
|
|
315
363
|
ensureHalo();
|
|
316
|
-
applyParams(material, shield, halo, haloMaterial, params);
|
|
364
|
+
applyParams(material, shield, halo, haloMaterial, haloFeather, haloFeatherMaterial, params);
|
|
317
365
|
},
|
|
318
366
|
dispose
|
|
319
367
|
};
|
|
@@ -356,19 +404,30 @@ function createShieldMaterial(scene, options = {}) {
|
|
|
356
404
|
material.fogEnabled = false;
|
|
357
405
|
return material;
|
|
358
406
|
}
|
|
359
|
-
function applyParams(material, shield, halo, haloMaterial, params) {
|
|
407
|
+
function applyParams(material, shield, halo, haloMaterial, haloFeather, haloFeatherMaterial, params) {
|
|
360
408
|
shield.scaling.set(params.radius, params.radius * params.verticalScale, params.radius);
|
|
361
409
|
applyMaterialParams(material, params, params.opacity, 1, 0);
|
|
410
|
+
const haloAlphaScale = Math.min(HALO_ALPHA_SCALE_MAX, params.opacity * Math.min(1, params.bloomIntensity));
|
|
362
411
|
if (halo && haloMaterial) {
|
|
363
412
|
halo.scaling.setAll(Math.max(1, params.haloScale));
|
|
364
413
|
applyMaterialParams(
|
|
365
414
|
haloMaterial,
|
|
366
415
|
params,
|
|
367
416
|
1,
|
|
368
|
-
|
|
417
|
+
haloAlphaScale,
|
|
369
418
|
1
|
|
370
419
|
);
|
|
371
420
|
}
|
|
421
|
+
if (haloFeather && haloFeatherMaterial) {
|
|
422
|
+
haloFeather.scaling.setAll(getHaloFeatherScale(params));
|
|
423
|
+
applyMaterialParams(
|
|
424
|
+
haloFeatherMaterial,
|
|
425
|
+
params,
|
|
426
|
+
1,
|
|
427
|
+
haloAlphaScale * HALO_FEATHER_ALPHA_RATIO,
|
|
428
|
+
2
|
|
429
|
+
);
|
|
430
|
+
}
|
|
372
431
|
}
|
|
373
432
|
function applyMaterialParams(material, params, opacity, haloAlphaScale, haloMode) {
|
|
374
433
|
material.setColor3("uColor", toColor3(params.color));
|
|
@@ -398,6 +457,9 @@ function mergeParams(patch, base = DEFAULT_PARAMS) {
|
|
|
398
457
|
function toColor3(color) {
|
|
399
458
|
return new Color3(color.r, color.g, color.b);
|
|
400
459
|
}
|
|
460
|
+
function getHaloFeatherScale(params) {
|
|
461
|
+
return Math.max(1, params.haloScale + HALO_FEATHER_SCALE_OFFSET);
|
|
462
|
+
}
|
|
401
463
|
function registerShaders() {
|
|
402
464
|
Effect.ShadersStore[`${SHADER_NAME}VertexShader`] = VERTEX_SHADER;
|
|
403
465
|
Effect.ShadersStore[`${SHADER_NAME}FragmentShader`] = FRAGMENT_SHADER;
|
|
@@ -12,6 +12,7 @@ const trailTextureUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAP4AAABNC
|
|
|
12
12
|
const DEFAULT_PARAMS = {
|
|
13
13
|
...LIFETIME_DEFAULTS,
|
|
14
14
|
playbackMode: "follow",
|
|
15
|
+
historySpace: "world",
|
|
15
16
|
orientation: "horizontal",
|
|
16
17
|
showGlow: true,
|
|
17
18
|
showOuter: true,
|
|
@@ -101,6 +102,7 @@ const weaponTrailEffectPackage = {
|
|
|
101
102
|
nameZh: "武器拖尾",
|
|
102
103
|
placement: "socket",
|
|
103
104
|
geometrySpace: "world",
|
|
105
|
+
optionalInputs: ["reference"],
|
|
104
106
|
debugAnchor: "player",
|
|
105
107
|
defaultParams: DEFAULT_PARAMS,
|
|
106
108
|
debugParams: [
|
|
@@ -109,6 +111,10 @@ const weaponTrailEffectPackage = {
|
|
|
109
111
|
{ value: "follow", label: "跟随挂点" },
|
|
110
112
|
{ value: "orbit", label: "环绕预览" }
|
|
111
113
|
] },
|
|
114
|
+
{ key: "historySpace", label: "历史空间", kind: "enum", options: [
|
|
115
|
+
{ value: "world", label: "世界坐标" },
|
|
116
|
+
{ value: "reference", label: "参考点相对" }
|
|
117
|
+
] },
|
|
112
118
|
{ key: "orientation", label: "拖尾朝向", kind: "enum", options: [
|
|
113
119
|
{ value: "horizontal", label: "水平面" },
|
|
114
120
|
{ value: "vertical", label: "竖直" }
|
|
@@ -134,7 +140,7 @@ const weaponTrailEffectPackage = {
|
|
|
134
140
|
{ key: "flowSpeed", label: "能量流动速度", kind: "number", min: 0, max: 4, step: 0.05 },
|
|
135
141
|
{ key: "tint", label: "颜色", kind: "color" }
|
|
136
142
|
],
|
|
137
|
-
create({ scene, root, position, params, renderingGroupId }) {
|
|
143
|
+
create({ scene, root, position, params, renderingGroupId, inputs }) {
|
|
138
144
|
root.position.copyFrom(position);
|
|
139
145
|
const rgid = renderingGroupId ?? 0;
|
|
140
146
|
registerShaders();
|
|
@@ -199,6 +205,10 @@ const weaponTrailEffectPackage = {
|
|
|
199
205
|
let outerInit = false, coreInit = false, glowInit = false;
|
|
200
206
|
const cur = sample();
|
|
201
207
|
const mountPos = new Vector3();
|
|
208
|
+
const referencePos = new Vector3();
|
|
209
|
+
const sampleWorldPos = new Vector3();
|
|
210
|
+
let usingReferenceHistory = false;
|
|
211
|
+
let warnedMissingReference = false;
|
|
202
212
|
const lastWidthAxis = new Vector3(0, 0, 1);
|
|
203
213
|
const tmpDir = new Vector3();
|
|
204
214
|
const tailDir = new Vector3(1, 0, 0);
|
|
@@ -208,6 +218,32 @@ const weaponTrailEffectPackage = {
|
|
|
208
218
|
if (params.playbackMode === "follow" || params.playbackMode === "orbit") return params.playbackMode;
|
|
209
219
|
return params.orbitRadius > 0 ? "orbit" : "follow";
|
|
210
220
|
}
|
|
221
|
+
function resolveHistorySpace() {
|
|
222
|
+
return params.historySpace === "reference" ? "reference" : "world";
|
|
223
|
+
}
|
|
224
|
+
function updateReferencePosition() {
|
|
225
|
+
if (resolveHistorySpace() !== "reference") return false;
|
|
226
|
+
const provider = inputs?.reference;
|
|
227
|
+
if (!provider) {
|
|
228
|
+
if (!warnedMissingReference) {
|
|
229
|
+
console.warn('[weapon-trail] historySpace="reference" 需要 inputs.reference; 已回退到世界坐标历史。');
|
|
230
|
+
warnedMissingReference = true;
|
|
231
|
+
}
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
const value = provider();
|
|
235
|
+
if (!value) return false;
|
|
236
|
+
referencePos.copyFrom(value);
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
function clearHistory() {
|
|
240
|
+
rawCount = 0;
|
|
241
|
+
for (let i = 0; i < targetCount; i++) {
|
|
242
|
+
points[i].position.set(0, 0, 0);
|
|
243
|
+
points[i].widthAxis.copyFrom(lastWidthAxis);
|
|
244
|
+
pointPathT[i] = 0;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
211
247
|
function sampleMinDistance() {
|
|
212
248
|
return Math.max(3e-3, Math.min(0.04, params.worldLength / Math.max(16, targetCount * 3)));
|
|
213
249
|
}
|
|
@@ -262,15 +298,24 @@ const weaponTrailEffectPackage = {
|
|
|
262
298
|
rawCount += 1;
|
|
263
299
|
trimRawHistory();
|
|
264
300
|
}
|
|
265
|
-
function sampleEmitter(out) {
|
|
301
|
+
function sampleEmitter(out, useReference) {
|
|
266
302
|
const playbackMode = resolvePlaybackMode();
|
|
267
303
|
const orbitRadius = playbackMode === "orbit" ? params.orbitRadius : 0;
|
|
268
304
|
const c = Math.cos(angle), s = Math.sin(angle);
|
|
269
|
-
|
|
305
|
+
sampleWorldPos.set(
|
|
270
306
|
mountPos.x + c * orbitRadius,
|
|
271
307
|
mountPos.y + params.height,
|
|
272
308
|
mountPos.z + s * orbitRadius
|
|
273
309
|
);
|
|
310
|
+
if (useReference) {
|
|
311
|
+
out.position.set(
|
|
312
|
+
sampleWorldPos.x - referencePos.x,
|
|
313
|
+
sampleWorldPos.y - referencePos.y,
|
|
314
|
+
sampleWorldPos.z - referencePos.z
|
|
315
|
+
);
|
|
316
|
+
} else {
|
|
317
|
+
out.position.copyFrom(sampleWorldPos);
|
|
318
|
+
}
|
|
274
319
|
if (params.orientation === "vertical") {
|
|
275
320
|
out.widthAxis.set(0, 1, 0);
|
|
276
321
|
return;
|
|
@@ -330,7 +375,7 @@ const weaponTrailEffectPackage = {
|
|
|
330
375
|
if (!found) extrapolateTailSample(targetDist, distBefore, t);
|
|
331
376
|
}
|
|
332
377
|
}
|
|
333
|
-
function buildRibbon(mesh, halfWidth, wasInit) {
|
|
378
|
+
function buildRibbon(mesh, halfWidth, wasInit, useReference) {
|
|
334
379
|
positions.length = 0;
|
|
335
380
|
uvs.length = 0;
|
|
336
381
|
colors.length = 0;
|
|
@@ -343,7 +388,10 @@ const weaponTrailEffectPackage = {
|
|
|
343
388
|
const tailAlpha = Math.max(0, 1 - Math.pow(pathT, params.tailFadeExponent) * params.tailFadeStrength);
|
|
344
389
|
const alpha = tailAlpha;
|
|
345
390
|
const ox = s.widthAxis.x * halfWidth, oy = s.widthAxis.y * halfWidth, oz = s.widthAxis.z * halfWidth;
|
|
346
|
-
|
|
391
|
+
const px = useReference ? s.position.x + referencePos.x : s.position.x;
|
|
392
|
+
const py = useReference ? s.position.y + referencePos.y : s.position.y;
|
|
393
|
+
const pz = useReference ? s.position.z + referencePos.z : s.position.z;
|
|
394
|
+
positions.push(px + ox, py + oy, pz + oz, px - ox, py - oy, pz - oz);
|
|
347
395
|
uvs.push(u, 0, u, 1);
|
|
348
396
|
colors.push(1, 1, 1, alpha, 1, 1, 1, alpha);
|
|
349
397
|
if (i < targetCount - 1) {
|
|
@@ -373,13 +421,18 @@ const weaponTrailEffectPackage = {
|
|
|
373
421
|
if (resolvePlaybackMode() === "orbit") angle += params.orbitSpeed * dt;
|
|
374
422
|
root.computeWorldMatrix(true);
|
|
375
423
|
mountPos.copyFrom(root.getAbsolutePosition());
|
|
376
|
-
|
|
424
|
+
const useReference = updateReferencePosition();
|
|
425
|
+
if (useReference !== usingReferenceHistory) {
|
|
426
|
+
usingReferenceHistory = useReference;
|
|
427
|
+
clearHistory();
|
|
428
|
+
}
|
|
429
|
+
sampleEmitter(cur, useReference);
|
|
377
430
|
if (rawCount === 0 || Vector3.Distance(cur.position, raw[0].position) > sampleMinDistance()) insertRawSample(cur);
|
|
378
431
|
resample();
|
|
379
432
|
const halfW = params.width * 0.5;
|
|
380
|
-
if (glow) glowInit = buildRibbon(glow, halfW * 2.2, glowInit);
|
|
381
|
-
if (outer) outerInit = buildRibbon(outer, halfW, outerInit);
|
|
382
|
-
if (core) coreInit = buildRibbon(core, halfW * params.coreWidth, coreInit);
|
|
433
|
+
if (glow) glowInit = buildRibbon(glow, halfW * 2.2, glowInit, useReference);
|
|
434
|
+
if (outer) outerInit = buildRibbon(outer, halfW, outerInit, useReference);
|
|
435
|
+
if (core) coreInit = buildRibbon(core, halfW * params.coreWidth, coreInit, useReference);
|
|
383
436
|
outerMat?.setFloat("uTime", time);
|
|
384
437
|
coreMat?.setFloat("uTime", time);
|
|
385
438
|
glowMat?.setFloat("uTime", time);
|
package/dist/index.js
CHANGED
|
@@ -192,6 +192,7 @@ function normalizeInputs(inputs) {
|
|
|
192
192
|
if (inputs.normal != null) out.normal = asFn(inputs.normal);
|
|
193
193
|
if (inputs.endpoints) out.endpoints = inputs.endpoints.map((e) => asFn(e)).filter(Boolean);
|
|
194
194
|
if (inputs.target) out.target = inputs.target;
|
|
195
|
+
if (inputs.reference != null) out.reference = asFn(inputs.reference);
|
|
195
196
|
if (inputs.scalars) {
|
|
196
197
|
out.scalars = {};
|
|
197
198
|
for (const [k, v] of Object.entries(inputs.scalars)) {
|
package/dist/types.d.ts
CHANGED
|
@@ -4,13 +4,14 @@ import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
|
|
|
4
4
|
import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
|
|
5
5
|
export type VfxDebugAnchor = 'playerRight' | 'player';
|
|
6
6
|
export type VfxInput<T> = T | (() => T);
|
|
7
|
-
export type VfxInputKey = 'aim' | 'normal' | 'endpoints' | 'target';
|
|
7
|
+
export type VfxInputKey = 'aim' | 'normal' | 'endpoints' | 'target' | 'reference';
|
|
8
8
|
/** 调用方提供的 inputs(放在 VfxSpawnTransform.inputs)。 */
|
|
9
9
|
export interface VfxEffectInputs {
|
|
10
10
|
aim?: VfxInput<Vector3>;
|
|
11
11
|
normal?: VfxInput<Vector3>;
|
|
12
12
|
endpoints?: Array<VfxInput<Vector3>>;
|
|
13
13
|
target?: AbstractMesh;
|
|
14
|
+
reference?: VfxInput<Vector3>;
|
|
14
15
|
/** 运行时标量(throttle/intensity 等),值可静态或 provider;特效每帧读取做活改(G5)。 */
|
|
15
16
|
scalars?: Record<string, VfxInput<number>>;
|
|
16
17
|
}
|
|
@@ -20,6 +21,7 @@ export interface VfxResolvedInputs {
|
|
|
20
21
|
normal?: () => Vector3;
|
|
21
22
|
endpoints?: Array<() => Vector3>;
|
|
22
23
|
target?: AbstractMesh;
|
|
24
|
+
reference?: () => Vector3;
|
|
23
25
|
scalars?: Record<string, () => number>;
|
|
24
26
|
}
|
|
25
27
|
export type VfxParamValue = number | string | boolean | VfxColorValue;
|
package/package.json
CHANGED
|
@@ -3,17 +3,13 @@
|
|
|
3
3
|
"__generation.offsetY": 0,
|
|
4
4
|
"__generation.offsetZ": 0,
|
|
5
5
|
"lifetime": 1,
|
|
6
|
-
"scale": 2,
|
|
7
|
-
"initialHeight": 0.05,
|
|
8
6
|
"color": {
|
|
9
7
|
"r": 1,
|
|
10
8
|
"g": 0.8392156862745098,
|
|
11
9
|
"b": 0.12156862745098039
|
|
12
10
|
},
|
|
13
11
|
"opacity": 1,
|
|
14
|
-
"
|
|
15
|
-
"scaleY": 0.2,
|
|
16
|
-
"scaleZ": 2,
|
|
12
|
+
"footprintShape": "rectangle",
|
|
17
13
|
"centerCount": 100,
|
|
18
14
|
"outlineCount": 300,
|
|
19
15
|
"strandWidth": 0.1,
|