@rzmps/rzmps 1.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.
Files changed (133) hide show
  1. package/README.md +1048 -0
  2. package/build/EmissionShape.d.ts +33 -0
  3. package/build/EmissionShape.js +129 -0
  4. package/build/Emitter.d.ts +58 -0
  5. package/build/Emitter.js +166 -0
  6. package/build/GpuModule.d.ts +28 -0
  7. package/build/GpuModule.js +28 -0
  8. package/build/Module.d.ts +19 -0
  9. package/build/Module.js +31 -0
  10. package/build/Particle.d.ts +60 -0
  11. package/build/Particle.js +52 -0
  12. package/build/ParticleForceField.d.ts +39 -0
  13. package/build/ParticleForceField.js +101 -0
  14. package/build/ParticleForceFieldHelper.d.ts +18 -0
  15. package/build/ParticleForceFieldHelper.js +103 -0
  16. package/build/ParticleSystem.d.ts +122 -0
  17. package/build/ParticleSystem.js +609 -0
  18. package/build/Renderer.d.ts +15 -0
  19. package/build/Renderer.js +11 -0
  20. package/build/assets/images/default.png +0 -0
  21. package/build/collision/ThreeCollisionBackend.d.ts +47 -0
  22. package/build/collision/ThreeCollisionBackend.js +340 -0
  23. package/build/enums/Axis.d.ts +6 -0
  24. package/build/enums/Axis.js +7 -0
  25. package/build/enums/DynamicValueType.d.ts +6 -0
  26. package/build/enums/DynamicValueType.js +7 -0
  27. package/build/enums/EmissionSource copy.d.ts +7 -0
  28. package/build/enums/EmissionSource copy.js +7 -0
  29. package/build/enums/EmissionSource.d.ts +7 -0
  30. package/build/enums/EmissionSource.js +7 -0
  31. package/build/enums/EndBehavior.d.ts +6 -0
  32. package/build/enums/EndBehavior.js +7 -0
  33. package/build/enums/SimulationSpace.d.ts +5 -0
  34. package/build/enums/SimulationSpace.js +6 -0
  35. package/build/enums/SpriteMaterialType.d.ts +5 -0
  36. package/build/enums/SpriteMaterialType.js +6 -0
  37. package/build/enums/TagSelectionMethod.d.ts +6 -0
  38. package/build/enums/TagSelectionMethod.js +7 -0
  39. package/build/enums/TrailMode copy.d.ts +5 -0
  40. package/build/enums/TrailMode copy.js +7 -0
  41. package/build/enums/TrailMode.d.ts +5 -0
  42. package/build/enums/TrailMode.js +7 -0
  43. package/build/enums/TrailTextureMode.d.ts +7 -0
  44. package/build/enums/TrailTextureMode.js +9 -0
  45. package/build/gpu/GpuDynamicValue.d.ts +9 -0
  46. package/build/gpu/GpuDynamicValue.js +113 -0
  47. package/build/gpu/GpuMath.d.ts +3 -0
  48. package/build/gpu/GpuMath.js +12 -0
  49. package/build/gpu/GpuParticleContext.d.ts +63 -0
  50. package/build/gpu/GpuParticleContext.js +291 -0
  51. package/build/helpers/acceptMultiple.d.ts +2 -0
  52. package/build/helpers/acceptMultiple.js +6 -0
  53. package/build/helpers/evaluateDynamic.d.ts +2 -0
  54. package/build/helpers/evaluateDynamic.js +18 -0
  55. package/build/helpers/evaluateDynamicColor.d.ts +3 -0
  56. package/build/helpers/evaluateDynamicColor.js +5 -0
  57. package/build/helpers/evaluateDynamicNumber.d.ts +2 -0
  58. package/build/helpers/evaluateDynamicNumber.js +4 -0
  59. package/build/helpers/evaluateDynamicVector3.d.ts +3 -0
  60. package/build/helpers/evaluateDynamicVector3.js +5 -0
  61. package/build/helpers/isPointInMesh.d.ts +2 -0
  62. package/build/helpers/isPointInMesh.js +8 -0
  63. package/build/helpers/particleRatio.d.ts +2 -0
  64. package/build/helpers/particleRatio.js +13 -0
  65. package/build/helpers/tagsIntersect.d.ts +2 -0
  66. package/build/helpers/tagsIntersect.js +4 -0
  67. package/build/index.d.ts +37 -0
  68. package/build/index.js +34 -0
  69. package/build/interfaces/ICollisionBackend.d.ts +21 -0
  70. package/build/interfaces/ICollisionBackend.js +1 -0
  71. package/build/interfaces/IParticleForceField.d.ts +5 -0
  72. package/build/interfaces/IParticleForceField.js +1 -0
  73. package/build/interfaces/InitialParticleValues.d.ts +18 -0
  74. package/build/interfaces/InitialParticleValues.js +1 -0
  75. package/build/materials/BasicSprite.d.ts +21 -0
  76. package/build/materials/BasicSprite.js +46 -0
  77. package/build/materials/UnlitSprite.d.ts +13 -0
  78. package/build/materials/UnlitSprite.js +31 -0
  79. package/build/materials/WebGPUBasicSprite.d.ts +8 -0
  80. package/build/materials/WebGPUBasicSprite.js +65 -0
  81. package/build/materials/WebGPUUnlitSprite.d.ts +8 -0
  82. package/build/materials/WebGPUUnlitSprite.js +49 -0
  83. package/build/modules/Audio.d.ts +60 -0
  84. package/build/modules/Audio.js +181 -0
  85. package/build/modules/Collision.d.ts +40 -0
  86. package/build/modules/Collision.js +162 -0
  87. package/build/modules/ColorBySpeed.d.ts +18 -0
  88. package/build/modules/ColorBySpeed.js +26 -0
  89. package/build/modules/ColorOverLifetime.d.ts +12 -0
  90. package/build/modules/ColorOverLifetime.js +17 -0
  91. package/build/modules/ExternalForces.d.ts +21 -0
  92. package/build/modules/ExternalForces.js +61 -0
  93. package/build/modules/ForceOverLifetime.d.ts +11 -0
  94. package/build/modules/ForceOverLifetime.js +12 -0
  95. package/build/modules/LimitVelocityOverLifetime.d.ts +16 -0
  96. package/build/modules/LimitVelocityOverLifetime.js +34 -0
  97. package/build/modules/MassOverLifetime.d.ts +11 -0
  98. package/build/modules/MassOverLifetime.js +15 -0
  99. package/build/modules/NoiseModule.d.ts +23 -0
  100. package/build/modules/NoiseModule.js +45 -0
  101. package/build/modules/RotationBySpeed.d.ts +14 -0
  102. package/build/modules/RotationBySpeed.js +19 -0
  103. package/build/modules/RotationOverLifetime.d.ts +11 -0
  104. package/build/modules/RotationOverLifetime.js +12 -0
  105. package/build/modules/ScaleBySpeed.d.ts +14 -0
  106. package/build/modules/ScaleBySpeed.js +19 -0
  107. package/build/modules/ScaleOverLifetime.d.ts +11 -0
  108. package/build/modules/ScaleOverLifetime.js +11 -0
  109. package/build/modules/TransformByNoise.d.ts +17 -0
  110. package/build/modules/TransformByNoise.js +32 -0
  111. package/build/modules/VelocityOverLifetime.d.ts +15 -0
  112. package/build/modules/VelocityOverLifetime.js +32 -0
  113. package/build/renderers/LightRenderer.d.ts +54 -0
  114. package/build/renderers/LightRenderer.js +141 -0
  115. package/build/renderers/MeshRenderer.d.ts +27 -0
  116. package/build/renderers/MeshRenderer.js +70 -0
  117. package/build/renderers/SpriteRenderer.d.ts +95 -0
  118. package/build/renderers/SpriteRenderer.js +456 -0
  119. package/build/renderers/TrailRenderer.d.ts +71 -0
  120. package/build/renderers/TrailRenderer.js +400 -0
  121. package/build/shaders/BasicSprite.frag.js +1 -0
  122. package/build/shaders/BasicSprite.vert.js +1 -0
  123. package/build/shaders/UnlitSprite.frag.js +1 -0
  124. package/build/shaders/UnlitSprite.vert.js +1 -0
  125. package/build/types/DynamicValue.d.ts +2 -0
  126. package/build/types/DynamicValue.js +1 -0
  127. package/build/types/EnumStringLiteral.d.ts +1 -0
  128. package/build/types/EnumStringLiteral.js +1 -0
  129. package/build/types/Multiple.d.ts +2 -0
  130. package/build/types/Multiple.js +1 -0
  131. package/build/types/Tag.d.ts +1 -0
  132. package/build/types/Tag.js +1 -0
  133. package/package.json +39 -0
@@ -0,0 +1,400 @@
1
+ import * as THREE from 'three';
2
+ import seedrandom from 'seedrandom';
3
+ import Renderer from '../Renderer';
4
+ import evaluateDynamicNumber from '../helpers/evaluateDynamicNumber';
5
+ import evaluateDynamicColor from '../helpers/evaluateDynamicColor';
6
+ import { TrailMode } from '../enums/TrailMode';
7
+ import { TrailTextureMode } from '../enums/TrailTextureMode';
8
+ export const TRAIL_RENDERER_USER_DATA_KEY = "__rzmps_trailRenderer";
9
+ class TrailRenderer extends Renderer {
10
+ constructor(options = {}) {
11
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
12
+ super(options);
13
+ this.mode = TrailMode.Particle;
14
+ this.ratio = 1;
15
+ this.lifetime = 1;
16
+ this.minimumVertexDistance = 0.1;
17
+ this.dieWithParticles = true;
18
+ this.ribbonCount = 1;
19
+ this.textureMode = TrailTextureMode.Stretch;
20
+ this.width = 1;
21
+ this.sizeAffectsWidth = false;
22
+ this.sizeAffectsLifetime = false;
23
+ this.inheritParticleColor = true;
24
+ this.colorOverLifetime = new THREE.Color(0xffffff);
25
+ this.widthOverTrail = 1;
26
+ this.colorOverTrail = new THREE.Color(0xffffff);
27
+ this.castShadow = false;
28
+ this.receiveShadow = false;
29
+ this.particles = [];
30
+ this.trails = new Map();
31
+ this.elapsedTime = 0;
32
+ this.mode = (_a = options.mode) !== null && _a !== void 0 ? _a : this.mode;
33
+ this.ratio = (_b = options.ratio) !== null && _b !== void 0 ? _b : this.ratio;
34
+ this.lifetime = (_c = options.lifetime) !== null && _c !== void 0 ? _c : this.lifetime;
35
+ this.minimumVertexDistance = (_d = options.minimumVertexDistance) !== null && _d !== void 0 ? _d : this.minimumVertexDistance;
36
+ this.dieWithParticles = (_e = options.dieWithParticles) !== null && _e !== void 0 ? _e : this.dieWithParticles;
37
+ this.ribbonCount = (_f = options.ribbonCount) !== null && _f !== void 0 ? _f : this.ribbonCount;
38
+ this.textureMode = (_g = options.textureMode) !== null && _g !== void 0 ? _g : this.textureMode;
39
+ this.width = (_h = options.width) !== null && _h !== void 0 ? _h : this.width;
40
+ this.sizeAffectsWidth = (_j = options.sizeAffectsWidth) !== null && _j !== void 0 ? _j : this.sizeAffectsWidth;
41
+ this.sizeAffectsLifetime = (_k = options.sizeAffectsLifetime) !== null && _k !== void 0 ? _k : this.sizeAffectsLifetime;
42
+ this.widthOverTrail = (_l = options.widthOverTrail) !== null && _l !== void 0 ? _l : this.widthOverTrail;
43
+ this.inheritParticleColor = (_m = options.inheritParticleColor) !== null && _m !== void 0 ? _m : this.inheritParticleColor;
44
+ this.colorOverLifetime = (_o = options.colorOverLifetime) !== null && _o !== void 0 ? _o : this.colorOverLifetime;
45
+ this.colorOverTrail = (_p = options.colorOverTrail) !== null && _p !== void 0 ? _p : this.colorOverTrail;
46
+ this.castShadow = (_q = options.castShadow) !== null && _q !== void 0 ? _q : this.castShadow;
47
+ this.receiveShadow = (_r = options.receiveShadow) !== null && _r !== void 0 ? _r : this.receiveShadow;
48
+ this.geometry = new THREE.BufferGeometry();
49
+ this.setEmptyGeometry();
50
+ this.material = (_s = options.material) !== null && _s !== void 0 ? _s : new THREE.MeshStandardMaterial(Object.assign({ vertexColors: true, side: THREE.DoubleSide, transparent: true, depthWrite: false }, options.materialOptions));
51
+ this.preprocessMaterial(this.material);
52
+ this.mesh = new THREE.Mesh(this.geometry, this.material);
53
+ // Add user data to the mesh so we can recognize it elsewhere
54
+ this.mesh.userData[TRAIL_RENDERER_USER_DATA_KEY] = true;
55
+ this.mesh.frustumCulled = false;
56
+ }
57
+ setup(system) {
58
+ system.addRendererObject(this.mesh);
59
+ this.mesh.onBeforeRender = (renderer, _scene, camera) => {
60
+ if (!renderer.isWebGPURenderer)
61
+ return;
62
+ this.rebuildGeometry(camera);
63
+ };
64
+ }
65
+ _update(particles, system, deltaTime) {
66
+ this.particles = particles;
67
+ this.elapsedTime += Math.max(0, deltaTime);
68
+ this.mesh.castShadow = this.castShadow;
69
+ this.mesh.receiveShadow = this.receiveShadow;
70
+ if (this.mode === TrailMode.Particle) {
71
+ this.updateParticleTrails(particles);
72
+ }
73
+ if (system.sceneCamera) {
74
+ this.rebuildGeometry(system.sceneCamera);
75
+ }
76
+ }
77
+ destroy() {
78
+ this.geometry.dispose();
79
+ this.trails.clear();
80
+ this.mesh.removeFromParent();
81
+ }
82
+ updateParticleTrails(particles) {
83
+ const now = this.elapsedTime;
84
+ const aliveParticles = new Set();
85
+ // Adding new trail points
86
+ particles.forEach((particle) => {
87
+ aliveParticles.add(particle.id);
88
+ if (!this.particleHasTrail(particle.id)) {
89
+ return;
90
+ }
91
+ let trail = this.trails.get(particle.id);
92
+ if (!trail) {
93
+ trail = {
94
+ particleId: particle.id,
95
+ points: [],
96
+ alive: true,
97
+ headPosition: particle.position.clone(),
98
+ particleTime: particle.time,
99
+ particleColor: particle.color.clone(),
100
+ particleAlpha: particle.alpha,
101
+ particleSize: this.getParticleSize(particle),
102
+ };
103
+ this.trails.set(particle.id, trail);
104
+ this.addTrailPoint(trail, particle, now);
105
+ }
106
+ trail.alive = true;
107
+ trail.headPosition.copy(particle.position);
108
+ trail.particleTime = particle.time;
109
+ trail.particleColor.copy(particle.color);
110
+ trail.particleAlpha = particle.alpha;
111
+ trail.particleSize = this.getParticleSize(particle);
112
+ const lastPoint = trail.points[trail.points.length - 1];
113
+ if (!lastPoint
114
+ || lastPoint.position.distanceTo(particle.position) >= this.minimumVertexDistance) {
115
+ this.addTrailPoint(trail, particle, now);
116
+ }
117
+ });
118
+ // Disposing of dead trails
119
+ for (const [id, trail] of this.trails) {
120
+ if (!aliveParticles.has(id)) {
121
+ trail.alive = false;
122
+ if (this.dieWithParticles) {
123
+ this.trails.delete(id);
124
+ continue;
125
+ }
126
+ }
127
+ while (trail.points.length > 0 && trail.points[0].expiresAt <= now) {
128
+ trail.points.shift();
129
+ }
130
+ if (!trail.alive && trail.points.length === 0) {
131
+ this.trails.delete(id);
132
+ }
133
+ }
134
+ }
135
+ addTrailPoint(trail, particle, now) {
136
+ let vertexLifetime = particle.lifetime * evaluateDynamicNumber(this.lifetime, particle.time, particle.id);
137
+ if (this.sizeAffectsLifetime) {
138
+ vertexLifetime *= this.getParticleSize(particle);
139
+ }
140
+ trail.points.push({
141
+ position: particle.position.clone(),
142
+ createdAt: now,
143
+ expiresAt: now + Math.max(0, vertexLifetime),
144
+ });
145
+ }
146
+ getPaths() {
147
+ if (this.mode === TrailMode.Ribbon) {
148
+ return this.getRibbonPaths();
149
+ }
150
+ return this.getParticlePaths();
151
+ }
152
+ getParticlePaths() {
153
+ const paths = [];
154
+ for (const trail of this.trails.values()) {
155
+ if (trail.points.length === 0) {
156
+ continue;
157
+ }
158
+ const path = trail.points.map((point) => ({
159
+ position: point.position,
160
+ particleTime: trail.particleTime,
161
+ particleColor: trail.particleColor,
162
+ particleAlpha: trail.particleAlpha,
163
+ particleSize: trail.particleSize,
164
+ particleId: trail.particleId,
165
+ }));
166
+ if (trail.alive) {
167
+ const lastPosition = path[path.length - 1].position;
168
+ if (lastPosition.distanceToSquared(trail.headPosition) > 0) {
169
+ path.push({
170
+ position: trail.headPosition,
171
+ particleTime: trail.particleTime,
172
+ particleColor: trail.particleColor,
173
+ particleAlpha: trail.particleAlpha,
174
+ particleSize: trail.particleSize,
175
+ particleId: trail.particleId,
176
+ });
177
+ }
178
+ }
179
+ if (path.length >= 2) {
180
+ paths.push(path);
181
+ }
182
+ }
183
+ return paths;
184
+ }
185
+ getRibbonPaths() {
186
+ const ribbonCount = Math.max(1, Math.floor(this.ribbonCount));
187
+ // Ordering based on age
188
+ const particles = this.particles
189
+ .filter((particle) => this.particleHasTrail(particle.id))
190
+ .sort((a, b) => a.startTime - b.startTime);
191
+ const ribbons = Array.from({ length: ribbonCount }, () => []);
192
+ particles.forEach((particle, index) => {
193
+ const ribbonIndex = index % ribbonCount;
194
+ ribbons[ribbonIndex].push({
195
+ position: particle.position,
196
+ particleTime: particle.time,
197
+ particleColor: particle.color,
198
+ particleAlpha: particle.alpha,
199
+ particleSize: this.getParticleSize(particle),
200
+ particleId: particle.id,
201
+ });
202
+ });
203
+ return ribbons.filter((ribbon) => ribbon.length >= 2);
204
+ }
205
+ rebuildGeometry(camera) {
206
+ const paths = this.getPaths();
207
+ const positions = [];
208
+ const normals = [];
209
+ const uvs = [];
210
+ const colors = [];
211
+ const indices = [];
212
+ const cameraPosition = camera.getWorldPosition(new THREE.Vector3());
213
+ this.mesh.worldToLocal(cameraPosition);
214
+ for (const path of paths) {
215
+ this.appendPathGeometry(path, cameraPosition, positions, normals, uvs, colors, indices);
216
+ }
217
+ if (positions.length === 0) {
218
+ this.setEmptyGeometry();
219
+ return;
220
+ }
221
+ this.geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
222
+ this.geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
223
+ this.geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
224
+ this.geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 4));
225
+ this.geometry.setIndex(indices);
226
+ this.geometry.setDrawRange(0, Infinity);
227
+ if (positions.length > 0) {
228
+ this.geometry.computeBoundingSphere();
229
+ }
230
+ else {
231
+ this.geometry.boundingSphere = null;
232
+ }
233
+ }
234
+ setEmptyGeometry() {
235
+ this.geometry.setAttribute('position', new THREE.Float32BufferAttribute(new Float32Array(12), 3));
236
+ this.geometry.setAttribute('normal', new THREE.Float32BufferAttribute(new Float32Array([
237
+ 0, 0, 1,
238
+ 0, 0, 1,
239
+ 0, 0, 1,
240
+ 0, 0, 1,
241
+ ]), 3));
242
+ this.geometry.setAttribute('uv', new THREE.Float32BufferAttribute(new Float32Array(8), 2));
243
+ this.geometry.setAttribute('color', new THREE.Float32BufferAttribute(new Float32Array([
244
+ 1, 1, 1, 0,
245
+ 1, 1, 1, 0,
246
+ 1, 1, 1, 0,
247
+ 1, 1, 1, 0,
248
+ ]), 4));
249
+ this.geometry.setIndex([0, 1, 2, 1, 3, 2]);
250
+ this.geometry.setDrawRange(0, 0);
251
+ this.geometry.boundingSphere = new THREE.Sphere(new THREE.Vector3(), 0);
252
+ }
253
+ appendPathGeometry(path, cameraPosition, positions, normals, uvs, colors, indices) {
254
+ if (path.length < 2) {
255
+ return;
256
+ }
257
+ const baseVertex = positions.length / 3;
258
+ const distances = new Array(path.length);
259
+ distances[0] = 0;
260
+ for (let i = 1; i < path.length; i++) {
261
+ distances[i] = distances[i - 1] + path[i].position.distanceTo(path[i - 1].position);
262
+ }
263
+ const totalDistance = distances[distances.length - 1];
264
+ let previousSide;
265
+ for (let i = 0; i < path.length; i++) {
266
+ const point = path[i];
267
+ const trailT = totalDistance > 0 ? distances[i] / totalDistance : 0;
268
+ const tangent = this.getTangent(path, i);
269
+ const viewDirection = cameraPosition.clone().sub(point.position);
270
+ if (viewDirection.lengthSq() > 0) {
271
+ viewDirection.normalize();
272
+ }
273
+ else {
274
+ viewDirection.set(0, 0, 1);
275
+ }
276
+ const side = new THREE.Vector3().crossVectors(tangent, viewDirection);
277
+ if (side.lengthSq() < 0.000001) {
278
+ if (previousSide) {
279
+ side.copy(previousSide);
280
+ }
281
+ else {
282
+ side.copy(this.getPerpendicular(tangent));
283
+ }
284
+ }
285
+ side.normalize();
286
+ if (previousSide && side.dot(previousSide) < 0) {
287
+ side.negate();
288
+ }
289
+ previousSide = side.clone();
290
+ const normal = new THREE.Vector3()
291
+ .crossVectors(side, tangent)
292
+ .normalize();
293
+ let width = evaluateDynamicNumber(this.width, point.particleTime, point.particleId);
294
+ width *= evaluateDynamicNumber(this.widthOverTrail, trailT, point.particleId);
295
+ if (this.sizeAffectsWidth) {
296
+ width *= point.particleSize;
297
+ }
298
+ const halfWidth = width * 0.5;
299
+ const left = point.position
300
+ .clone()
301
+ .addScaledVector(side, -halfWidth);
302
+ const right = point.position
303
+ .clone()
304
+ .addScaledVector(side, halfWidth);
305
+ positions.push(left.x, left.y, left.z, right.x, right.y, right.z);
306
+ normals.push(normal.x, normal.y, normal.z, normal.x, normal.y, normal.z);
307
+ const u = this.getTextureU(i, path.length, distances[i], totalDistance);
308
+ uvs.push(u, 0, u, 1);
309
+ const lifetimeColor = evaluateDynamicColor(this.colorOverLifetime, point.particleTime, point.particleId).clone();
310
+ const trailColor = evaluateDynamicColor(this.colorOverTrail, trailT, point.particleId).clone();
311
+ const finalColor = lifetimeColor.multiply(trailColor);
312
+ if (this.inheritParticleColor) {
313
+ finalColor.multiply(point.particleColor);
314
+ }
315
+ const alpha = this.inheritParticleColor ? point.particleAlpha : 1;
316
+ colors.push(finalColor.r, finalColor.g, finalColor.b, alpha, finalColor.r, finalColor.g, finalColor.b, alpha);
317
+ }
318
+ // Build tris
319
+ for (let i = 0; i < path.length - 1; i++) {
320
+ const a = baseVertex + i * 2;
321
+ const b = a + 1;
322
+ const c = a + 2;
323
+ const d = a + 3;
324
+ indices.push(a, b, c, b, d, c);
325
+ }
326
+ }
327
+ getTangent(path, index) {
328
+ const tangent = new THREE.Vector3();
329
+ if (index === 0) {
330
+ tangent.subVectors(path[1].position, path[0].position);
331
+ }
332
+ else if (index === path.length - 1) {
333
+ tangent.subVectors(path[index].position, path[index - 1].position);
334
+ }
335
+ else {
336
+ tangent.subVectors(path[index + 1].position, path[index - 1].position);
337
+ }
338
+ /*
339
+ * Duplicate trail positions can otherwise give us an
340
+ * unusable zero-length tangent.
341
+ */
342
+ if (tangent.lengthSq() < 0.000001) {
343
+ if (index > 0) {
344
+ tangent.subVectors(path[index].position, path[index - 1].position);
345
+ }
346
+ if (tangent.lengthSq() < 0.000001 && index < path.length - 1) {
347
+ tangent.subVectors(path[index + 1].position, path[index].position);
348
+ }
349
+ }
350
+ if (tangent.lengthSq() < 0.000001) {
351
+ tangent.set(1, 0, 0);
352
+ }
353
+ return tangent.normalize();
354
+ }
355
+ getPerpendicular(tangent) {
356
+ const reference = Math.abs(tangent.z) < 0.9
357
+ ? new THREE.Vector3(0, 0, 1)
358
+ : new THREE.Vector3(0, 1, 0);
359
+ return new THREE.Vector3()
360
+ .crossVectors(tangent, reference)
361
+ .normalize();
362
+ }
363
+ getTextureU(index, pointCount, distance, totalDistance) {
364
+ switch (this.textureMode) {
365
+ case TrailTextureMode.Tile:
366
+ return distance;
367
+ case TrailTextureMode.RepeatPerSegment:
368
+ return index;
369
+ case TrailTextureMode.DistributePerSegment:
370
+ return pointCount > 1 ? index / (pointCount - 1) : 0;
371
+ case TrailTextureMode.Stretch:
372
+ default:
373
+ return totalDistance > 0 ? distance / totalDistance : 0;
374
+ }
375
+ }
376
+ getParticleSize(particle) {
377
+ return Math.max(Math.abs(particle.scale.x), Math.abs(particle.scale.y), Math.abs(particle.scale.z));
378
+ }
379
+ particleHasTrail(id) {
380
+ const ratio = THREE.MathUtils.clamp(this.ratio, 0, 1);
381
+ if (ratio <= 0) {
382
+ return false;
383
+ }
384
+ if (ratio >= 1) {
385
+ return true;
386
+ }
387
+ const random = seedrandom(id).quick();
388
+ return random < ratio;
389
+ }
390
+ preprocessMaterial(material) {
391
+ if (Array.isArray(material)) {
392
+ material.forEach((item) => this.preprocessMaterial(item));
393
+ return;
394
+ }
395
+ material.vertexColors = true;
396
+ material.transparent = true;
397
+ material.needsUpdate = true;
398
+ }
399
+ }
400
+ export default TrailRenderer;
@@ -0,0 +1 @@
1
+ export default "uniform sampler2D pointTexture;\n\nuniform sampler2D alphaMap;\nuniform bool hasAlphaMap;\n\nuniform sampler2D normalMap;\nuniform bool hasNormalMap;\nuniform float normalStrength;\n\nuniform float normalLighting;\nuniform bool sphericalNormals;\n\nuniform float roughness;\nuniform sampler2D roughnessMap;\nuniform bool hasRoughnessMap;\n\nuniform vec2 gridSize;\nuniform int n_frames;\n\nuniform sampler2D envMap;\nuniform float envIntensity;\nuniform bool hasEnvMap;\n\nuniform bool softParticles;\nuniform float softParticleDistance;\nuniform sampler2D sceneDepthTexture;\nuniform vec2 depthResolution;\nuniform float depthCameraNear;\nuniform float depthCameraFar;\n\nvarying vec4 vColor;\nvarying float aspectRatio;\nvarying float angle;\n\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;\n\nflat in int fragFrame;\n\n// Provides:\n// IncidentLight\n// ReflectedLight\n// saturate()\n// pow2(), pow4(), etc.\n#include <common>\n\n// Provides:\n// ambientLightColor\n// directionalLights[]\n// pointLights[]\n// spotLights[]\n// hemisphereLights[]\n// getDirectionalLightInfo()\n// getPointLightInfo()\n// getSpotLightInfo()\n#include <lights_pars_begin>\n\n// Provides PMREM environment decoding\n#include <cube_uv_reflection_fragment>\n\n// perspectiveDepthToViewZ\n#include <packing>\n\n\nvec2 rotate_vector(vec2 value, float rotation)\n{\n return vec2(\n cos(rotation) * value.x + sin(rotation) * value.y,\n cos(rotation) * value.y - sin(rotation) * value.x\n );\n}\n\n\nvec2 sprite_coord(vec2 coord, int frame)\n{\n float f_frame = mod(float(frame), float(n_frames));\n\n return vec2(\n (coord.x / gridSize.x)\n + (\n mod(f_frame, gridSize.x)\n * (1.0 / gridSize.x)\n ),\n\n 1.0 - (\n (coord.y / gridSize.y)\n + (\n floor(f_frame / gridSize.x)\n * (1.0 / gridSize.y)\n )\n )\n );\n}\n\nvec2 directionToEquirectUv(vec3 dir)\n{\n dir = normalize(dir);\n\n return vec2(\n atan(dir.z, dir.x) / (2.0 * PI) + 0.5,\n asin(clamp(dir.y, -1.0, 1.0)) / PI + 0.5\n );\n}\n\n\nvoid accumulateLight(\n IncidentLight light,\n vec3 normal,\n vec3 viewDirection,\n float shininess,\n float specularStrength,\n float normalLighting,\n inout vec3 diffuseLight,\n inout vec3 specularLight\n)\n{\n if (!light.visible)\n return;\n\n float normalDiffuse =\n max(dot(normal, light.direction), 0.0);\n\n float diffuseFactor =\n mix(1.0, normalDiffuse, normalLighting);\n\n diffuseLight +=\n light.color * diffuseFactor;\n\n if (normalLighting > 0.0 && normalDiffuse > 0.0)\n {\n vec3 halfDirection =\n normalize(light.direction + viewDirection);\n\n float specular =\n pow(\n max(dot(normal, halfDirection), 0.0),\n shininess\n );\n\n specularLight +=\n light.color\n * specular\n * specularStrength\n * normalLighting;\n }\n}\n\n\nvoid main()\n{\n //\n // SPRITE UV\n //\n\n vec2 scaleVector;\n\n if (aspectRatio < 1.0 / aspectRatio)\n {\n scaleVector =\n vec2(1.0, 1.0 / aspectRatio);\n }\n else\n {\n scaleVector =\n vec2(aspectRatio, 1.0);\n }\n\n // Transform in LOCAL TILE coordinates first.\n vec2 fromCenter =\n gl_PointCoord - vec2(0.5);\n\n // Undo the particle rotation in the square point-sprite space FIRST.\n vec2 rotatedFromCenter =\n rotate_vector(fromCenter, angle);\n\n // Then account for the non-square sprite dimensions.\n vec2 scaledFromCenter =\n rotatedFromCenter * scaleVector;\n\n vec2 rotatedLocalCoord =\n vec2(0.5) + scaledFromCenter;\n\n // Rotation can push us outside this sprite's tile.\n // Discard rather than sampling an adjacent tile.\n if (\n rotatedLocalCoord.x < 0.0\n || rotatedLocalCoord.x > 1.0\n || rotatedLocalCoord.y < 0.0\n || rotatedLocalCoord.y > 1.0\n )\n {\n discard;\n }\n\n vec2 spriteCoord =\n sprite_coord(\n rotatedLocalCoord,\n fragFrame\n );\n\n\n //\n // BASE COLOR / ALPHA\n //\n\n vec4 textureColor =\n texture2D(pointTexture, spriteCoord);\n\n vec4 baseColor =\n vColor * textureColor;\n\n if (hasAlphaMap)\n {\n baseColor.a *=\n texture2D(alphaMap, spriteCoord).r;\n }\n\n if (baseColor.a <= 0.0)\n discard;\n\n\n //\n // NORMAL\n //\n\n vec3 normal =\n normalize(vNormal);\n\n if (sphericalNormals)\n {\n vec2 p =\n rotatedLocalCoord * 2.0 - 1.0;\n\n float r2 = dot(p, p);\n\n if (r2 <= 1.0)\n {\n normal = normalize(vec3(\n p.x,\n -p.y,\n sqrt(1.0 - r2)\n ));\n }\n }\n\n if (hasNormalMap)\n {\n vec3 mapNormal =\n texture2D(\n normalMap,\n spriteCoord\n ).xyz * 2.0 - 1.0;\n\n // Normal map rotates with sprite.\n mapNormal.xy =\n rotate_vector(\n mapNormal.xy,\n angle\n );\n\n mapNormal.xy *= normalStrength;\n\n normal =\n normalize(mapNormal);\n }\n\n\n //\n // ROUGHNESS -> BLINN/PHONG PARAMETERS\n //\n\n float roughnessValue =\n roughness;\n\n if (hasRoughnessMap)\n {\n roughnessValue *=\n texture2D(\n roughnessMap,\n spriteCoord\n ).r;\n }\n\n roughnessValue =\n clamp(roughnessValue, 0.0, 1.0);\n\n // We're not implementing full PBR here.\n // Convert roughness to something sensible\n // for a Blinn-Phong highlight.\n float shininess =\n mix(128.0, 2.0, roughnessValue);\n\n float specularStrength =\n 1.0 - roughnessValue;\n\n\n //\n // LIGHTING\n //\n\n vec3 viewDirection =\n normalize(vViewPosition);\n\n // Sample environment lighting\n vec3 environmentSpecular = vec3(0.0);\n vec3 environmentDiffuse = vec3(0.0);\n\n #ifdef ENVMAP_TYPE_CUBE_UV\n if (hasEnvMap && envIntensity > 0.0)\n {\n vec3 worldNormal =\n normalize(\n inverseTransformDirection(\n normal,\n viewMatrix\n )\n );\n\n vec3 worldViewDirection =\n normalize(\n inverseTransformDirection(\n viewDirection,\n viewMatrix\n )\n );\n\n vec3 reflectionDirection =\n reflect(\n -worldViewDirection,\n worldNormal\n );\n\n reflectionDirection =\n normalize(\n mix(\n reflectionDirection,\n worldNormal,\n pow(roughnessValue, 4.0)\n )\n );\n\n environmentSpecular =\n textureCubeUV(\n envMap,\n reflectionDirection,\n roughnessValue\n ).rgb\n * envIntensity;\n\n float NdotV =\n max(dot(normal, viewDirection), 0.0);\n\n float fresnel =\n pow(1.0 - NdotV, 5.0);\n\n float specularFactor =\n mix(0.04, 1.0, fresnel);\n\n environmentSpecular *= specularFactor;\n\n environmentDiffuse =\n textureCubeUV(\n envMap,\n worldNormal,\n 1.0\n ).rgb\n * envIntensity;\n }\n #endif\n\n // Three gives ambient light as a combined color.\n vec3 diffuseLight =\n ambientLightColor\n + environmentDiffuse;\n\n vec3 specularLight =\n environmentSpecular;\n\n IncidentLight directLight;\n\n\n //\n // DIRECTIONAL LIGHTS\n //\n\n #if NUM_DIR_LIGHTS > 0\n\n DirectionalLight directionalLight;\n\n #pragma unroll_loop_start\n\n for (\n int i = 0;\n i < NUM_DIR_LIGHTS;\n i++\n )\n {\n directionalLight =\n directionalLights[i];\n\n getDirectionalLightInfo(\n directionalLight,\n directLight\n );\n\n accumulateLight(\n directLight,\n normal,\n viewDirection,\n shininess,\n specularStrength,\n normalLighting,\n diffuseLight,\n specularLight\n );\n }\n\n #pragma unroll_loop_end\n\n #endif\n\n\n //\n // POINT LIGHTS\n //\n\n #if NUM_POINT_LIGHTS > 0\n\n PointLight pointLight;\n\n #pragma unroll_loop_start\n\n for (\n int i = 0;\n i < NUM_POINT_LIGHTS;\n i++\n )\n {\n pointLight =\n pointLights[i];\n\n getPointLightInfo(\n pointLight,\n -vViewPosition,\n directLight\n );\n\n accumulateLight(\n directLight,\n normal,\n viewDirection,\n shininess,\n specularStrength,\n normalLighting,\n diffuseLight,\n specularLight\n );\n }\n\n #pragma unroll_loop_end\n\n #endif\n\n\n //\n // SPOT LIGHTS\n //\n\n #if NUM_SPOT_LIGHTS > 0\n\n SpotLight spotLight;\n\n #pragma unroll_loop_start\n\n for (\n int i = 0;\n i < NUM_SPOT_LIGHTS;\n i++\n )\n {\n spotLight =\n spotLights[i];\n\n getSpotLightInfo(\n spotLight,\n -vViewPosition,\n directLight\n );\n\n accumulateLight(\n directLight,\n normal,\n viewDirection,\n shininess,\n specularStrength,\n normalLighting,\n diffuseLight,\n specularLight\n );\n }\n\n #pragma unroll_loop_end\n\n #endif\n\n\n //\n // HEMISPHERE LIGHTS\n //\n\n #if NUM_HEMI_LIGHTS > 0\n\n #pragma unroll_loop_start\n\n for (\n int i = 0;\n i < NUM_HEMI_LIGHTS;\n i++\n )\n {\n diffuseLight +=\n getHemisphereLightIrradiance(\n hemisphereLights[i],\n normal\n );\n }\n\n #pragma unroll_loop_end\n\n #endif\n\n //\n // SOFT PARTICLES\n //\n\n float finalAlpha = baseColor.a;\n\n if (softParticles)\n {\n float particleViewZ =\n perspectiveDepthToViewZ(\n gl_FragCoord.z,\n depthCameraNear,\n depthCameraFar\n );\n\n vec2 screenUv =\n gl_FragCoord.xy / depthResolution;\n\n float sceneDepth =\n texture2D(\n sceneDepthTexture,\n screenUv\n ).x;\n\n float sceneViewZ =\n perspectiveDepthToViewZ(\n sceneDepth,\n depthCameraNear,\n depthCameraFar\n );\n\n float depthDifference =\n abs(particleViewZ - sceneViewZ);\n\n float softFade =\n smoothstep(\n 0.0,\n softParticleDistance,\n depthDifference\n );\n\n finalAlpha *= softFade;\n }\n\n\n //\n // FINAL COLOR\n //\n\n vec3 litColor =\n baseColor.rgb * diffuseLight\n + specularLight;\n\n gl_FragColor =\n vec4(\n litColor,\n finalAlpha\n );\n}\n";
@@ -0,0 +1 @@
1
+ export default "attribute vec3 scale;\nattribute vec3 rotation;\nattribute float frame;\n\nvarying vec4 vColor;\nvarying float aspectRatio;\nvarying float angle;\n\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;\n\nflat out int fragFrame;\n\nvoid main()\n{\n vColor = color;\n angle = rotation.x;\n\n vec4 mvPosition =\n modelViewMatrix\n * vec4(position, 1.0);\n\n aspectRatio =\n float(scale.y)\n / float(scale.x);\n\n gl_PointSize =\n max(scale.x, scale.y)\n * (300.0 / -mvPosition.z);\n\n gl_Position =\n projectionMatrix\n * mvPosition;\n\n fragFrame =\n int(frame);\n\n // Match Three's convention:\n // vector from fragment toward camera.\n vViewPosition =\n -mvPosition.xyz;\n\n // Point sprites always face the camera,\n // so their base normal in view space points forward.\n vNormal =\n vec3(0.0, 0.0, 1.0);\n}\n";
@@ -0,0 +1 @@
1
+ export default "uniform sampler2D pointTexture;\nuniform sampler2D alphaMap;\nuniform bool hasAlphaMap;\nuniform vec2 gridSize;\nuniform int n_frames;\n\nuniform bool softParticles;\nuniform float softParticleDistance;\nuniform sampler2D sceneDepthTexture;\nuniform vec2 depthResolution;\nuniform float depthCameraNear;\nuniform float depthCameraFar;\n\nvarying vec4 vColor;\nvarying float aspectRatio;\nvarying float angle;\n\nflat in int fragFrame;\n\n// perspectiveDepthToViewZ\n#include <packing>\n\nvec2 sprite_coord(vec2 coord, int frame) {\n float f_frame = mod(float(frame), float(n_frames));\n\n return vec2(\n (coord.x / gridSize.x) + (mod(f_frame, gridSize.x) * (1.0 / gridSize.x)),\n 1.0 - ((coord.y / gridSize.y) + (floor(f_frame / gridSize.x) * (1.0 / gridSize.y)))\n );\n}\n\nvec2 rotate_vector(vec2 value, float rotation)\n{\n return vec2(\n cos(rotation) * value.x\n + sin(rotation) * value.y,\n\n cos(rotation) * value.y\n - sin(rotation) * value.x\n );\n}\n\n\nvoid main() {\n\n gl_FragColor = vColor;\n\n vec2 scaleVector;\n if (aspectRatio < 1.0/aspectRatio) {\n scaleVector = vec2(1.0, 1.0/aspectRatio);\n } else {\n scaleVector = vec2(aspectRatio, 1.0);\n }\n\n vec2 fromCenter =\n gl_PointCoord - vec2(0.5);\n\n vec2 rotatedFromCenter =\n rotate_vector(fromCenter, angle);\n\n vec2 scaledFromCenter =\n rotatedFromCenter * scaleVector;\n\n vec2 rotatedCoord =\n vec2(0.5) + scaledFromCenter;\n\n if (\n rotatedCoord.x < 0.0\n || rotatedCoord.x > 1.0\n || rotatedCoord.y < 0.0\n || rotatedCoord.y > 1.0\n )\n {\n discard;\n }\n\n vec2 spriteCoord = sprite_coord(rotatedCoord, fragFrame);\n\n vec4 baseColor = gl_FragColor * texture2D(pointTexture, spriteCoord);\n\n //\n // SOFT PARTICLES\n //\n\n float finalAlpha = baseColor.a;\n if (softParticles)\n {\n float particleViewZ =\n perspectiveDepthToViewZ(\n gl_FragCoord.z,\n depthCameraNear,\n depthCameraFar\n );\n\n vec2 screenUv =\n gl_FragCoord.xy / depthResolution;\n\n float sceneDepth =\n texture2D(\n sceneDepthTexture,\n screenUv\n ).x;\n\n float sceneViewZ =\n perspectiveDepthToViewZ(\n sceneDepth,\n depthCameraNear,\n depthCameraFar\n );\n\n float depthDifference =\n abs(particleViewZ - sceneViewZ);\n\n float softFade =\n smoothstep(\n 0.0,\n softParticleDistance,\n depthDifference\n );\n\n finalAlpha *= softFade;\n }\n\n if (hasAlphaMap) {\n finalAlpha *= texture2D(alphaMap, spriteCoord).r;\n }\n\n gl_FragColor = vec4(\n baseColor.rgb,\n finalAlpha\n );\n}\n";
@@ -0,0 +1 @@
1
+ export default "attribute vec4 scale;\nattribute vec3 rotation;\nattribute float frame;\n\nvarying vec4 vColor;\nvarying float aspectRatio;\nvarying float angle;\nflat out int fragFrame;\n\nvoid main() {\n\n vColor = color;\n\n angle = rotation.x;\n\n vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\n aspectRatio = float(scale.y) / float(scale.x);\n\n gl_PointSize = max(scale.x, scale.y) * ( 300.0 / -mvPosition.z );\n\n gl_Position = projectionMatrix * mvPosition;\n\n fragFrame = int(frame);\n\n}\n";
@@ -0,0 +1,2 @@
1
+ export type DynamicValue<T> = T | ((t: number) => DynamicValue<T>) | [DynamicValue<T>, DynamicValue<T>] | Set<DynamicValue<T>>;
2
+ export type DynamicUntimedValue<T> = Exclude<DynamicValue<T>, ((t: number) => DynamicValue<T>)>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export type EnumStringLiteral<T> = ;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export type Multiple<T> = T | T[];
2
+ export type StrictMultiple<T> = T | [T, ...[T]];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export type Tag = string;
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@rzmps/rzmps",
3
+ "version": "1.0.0",
4
+ "description": "🧅 Modular and extendable particle system for Three.js",
5
+ "main": "build/index.js",
6
+ "types": "build/index.d.ts",
7
+ "files": [
8
+ "build/**/*"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc && npm run build:shaders && npm run copy:assets && npm run fix:shader-imports",
12
+ "build:shaders": "node scripts/build-shaders.mjs",
13
+ "copy:assets": "mkdir -p build && rm -rf build/assets && cp -r src/assets build/assets",
14
+ "fix:shader-imports": "node scripts/fix-shader-imports.mjs",
15
+ "dev": "concurrently -k \"tsc --watch\" \"watch 'npm run build:shaders && npm run fix:shader-imports' src/shaders\" \"watch 'npm run copy:assets' src/assets\"",
16
+ "prepack": "npm run build"
17
+ },
18
+ "author": "Robert May",
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "fast-simplex-noise": "^4.0.0",
22
+ "nanoid": "^3.1.23",
23
+ "seedrandom": "^3.0.5"
24
+ },
25
+ "peerDependencies": {
26
+ "three": "^0.185.1"
27
+ },
28
+ "devDependencies": {
29
+ "@types/seedrandom": "^3.0.8",
30
+ "@types/three": "^0.185.4",
31
+ "@typescript-eslint/eslint-plugin": "^4.23.0",
32
+ "concurrently": "^6.1.0",
33
+ "eslint": "^7.26.0",
34
+ "eslint-config-airbnb": "^18.2.1",
35
+ "three": "^0.185.1",
36
+ "typescript": "5.9",
37
+ "watch": "^0.13.0"
38
+ }
39
+ }