@woosh/meep-engine 2.165.1 → 2.165.3

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 (25) hide show
  1. package/package.json +1 -1
  2. package/src/engine/ecs/EntityManager.d.ts.map +1 -1
  3. package/src/engine/ecs/EntityManager.js +11 -7
  4. package/src/engine/ecs/System.d.ts.map +1 -1
  5. package/src/engine/ecs/System.js +4 -0
  6. package/src/engine/ecs/fow/shader/screenSpaceFogOfWarShader.d.ts.map +1 -1
  7. package/src/engine/ecs/fow/shader/screenSpaceFogOfWarShader.js +20 -10
  8. package/src/engine/ecs/system_validate_class.d.ts.map +1 -1
  9. package/src/engine/ecs/system_validate_class.js +9 -2
  10. package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts +2 -0
  11. package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts.map +1 -1
  12. package/src/engine/graphics/ecs/trail2d/Trail2D.js +5 -2
  13. package/src/engine/graphics/ecs/trail2d/Trail2DSystem.d.ts.map +1 -1
  14. package/src/engine/graphics/ecs/trail2d/Trail2DSystem.js +3 -2
  15. package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +2 -0
  16. package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
  17. package/src/engine/graphics/ecs/trail3d/Trail3D.js +5 -2
  18. package/src/engine/graphics/ecs/trail3d/Trail3DSystem.d.ts.map +1 -1
  19. package/src/engine/graphics/ecs/trail3d/Trail3DSystem.js +3 -2
  20. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts +5 -3
  21. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts.map +1 -1
  22. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +6 -4
  23. package/src/engine/graphics/trail/x/simulator/RibbonXFixedPhysicsSimulator.d.ts +2 -1
  24. package/src/engine/graphics/trail/x/simulator/RibbonXFixedPhysicsSimulator.d.ts.map +1 -1
  25. package/src/engine/graphics/trail/x/simulator/RibbonXFixedPhysicsSimulator.js +3 -2
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "description": "Pure JavaScript game engine. Fully featured and production ready.",
7
7
  "type": "module",
8
8
  "author": "Alexander Goldring",
9
- "version": "2.165.1",
9
+ "version": "2.165.3",
10
10
  "main": "build/meep.module.js",
11
11
  "module": "build/meep.module.js",
12
12
  "exports": {
@@ -1 +1 @@
1
- {"version":3,"file":"EntityManager.d.ts","sourceRoot":"","sources":["../../../../src/engine/ecs/EntityManager.js"],"names":[],"mappings":"iCAgBU,MAAM;;;;;;;;;AAiBhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH;IAEI;;;;OAIG;IACH,kBAFU,iCAAQ,CAEL;IAEb;;;;;;OAMG;IACH,uCAA2B;IAE3B;;;;;OAKG;IACH,iCAA4B;IAE5B;;OAEG;IACH;;;QAGI;;WAEG;qBADO,uCAAc;;MAI1B;IAEF;;;OAGG;IACH,OAFU,kBAAkB,CAEO;IAEnC;;;;;;;;;OASG;IACH,qBAFU,MAAM,CAEqB;IAErC;;;;;;;;OAQG;IACH,wCAFU,MAAM,CAE4B;IAE5C;;;;;;;;OAQG;IACH,eAFU,MAAM,CAEE;IAElB;;;;;OAKG;IACH,+BAA2B;IAE3B;;;;OAIG;IACH,SAFU,sBAAsB,CAEjB;IAEf;;;;;OAKG;IACH,uCAAsC;IAEtC;;;OAGG;IACH,6BAmFC;IAED;;;OAGG;IACH,uBAFa,OAAO,CAuBnB;IAED;;;;;OAKG;IACH,uBAJW,sBAAsB,QA4ChC;IAGD;;;OAGG;IACH,sBA2BC;IAED;;;;OAIG;IACH,qCAFa,OAAO,CAInB;IAED;;;;OAIG;IACH,6CAgBC;IAED;;;;;OAKG;IACH,wCAHW,MAAM,GACJ,IAAI,aAAS,CAiBzB;IAGD;;;OAGG;IACH,6BAFW,MAAM,QA8EhB;IAED;;;;;;;;;OASG;IACH,qBAFa,MAAM,CAKlB;IAED;;;;;OAKG;IACH,iEA2DC;IAED;;;;OAIG;IACH,uDAFa,QAAQ,OAAO,CAAC,CAyC5B;IAED;;;;;OAKG;IACH,mBAmCC;IAED;;;;;OAKG;IACH,oBAqFC;IAED;;;;;OAKG;IACH,kEAyEC;IAED;;;;OAIG;IACH,mCAFa,wCAAgB,CA0B5B;IAED;;;;OAIG;IACH,8DAFa,wCAAgB,CA0B5B;IAED;;;;;OAKG;IACH,mEAoFC;IAGL;;OAEG;IACH,yBAjtBe,sBAAsB,UAitBA;IACrC;;OAEG;IACH,0BAAqC;CATpC;uBA59BmC,aAAa;mBAR9B,oCAAoC;uCAMhB,6BAA6B"}
1
+ {"version":3,"file":"EntityManager.d.ts","sourceRoot":"","sources":["../../../../src/engine/ecs/EntityManager.js"],"names":[],"mappings":"iCAgBU,MAAM;;;;;;;;;AAiBhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH;IAEI;;;;OAIG;IACH,kBAFU,iCAAQ,CAEL;IAEb;;;;;;OAMG;IACH,uCAA2B;IAE3B;;;;;OAKG;IACH,iCAA4B;IAE5B;;OAEG;IACH;;;QAGI;;WAEG;qBADO,uCAAc;;MAI1B;IAEF;;;OAGG;IACH,OAFU,kBAAkB,CAEO;IAEnC;;;;;;;;;OASG;IACH,qBAFU,MAAM,CAEqB;IAErC;;;;;;;;OAQG;IACH,wCAFU,MAAM,CAE4B;IAE5C;;;;;;;;OAQG;IACH,eAFU,MAAM,CAEE;IAElB;;;;;OAKG;IACH,+BAA2B;IAE3B;;;;OAIG;IACH,SAFU,sBAAsB,CAEjB;IAEf;;;;;OAKG;IACH,uCAAsC;IAEtC;;;OAGG;IACH,6BAmFC;IAED;;;OAGG;IACH,uBAFa,OAAO,CAuBnB;IAED;;;;;OAKG;IACH,uBAJW,sBAAsB,QA4ChC;IAGD;;;OAGG;IACH,sBA2BC;IAED;;;;OAIG;IACH,qCAFa,OAAO,CAInB;IAED;;;;OAIG;IACH,6CAgBC;IAED;;;;;OAKG;IACH,wCAHW,MAAM,GACJ,IAAI,aAAS,CAiBzB;IAGD;;;OAGG;IACH,6BAFW,MAAM,QA8EhB;IAED;;;;;;;;;OASG;IACH,qBAFa,MAAM,CAKlB;IAED;;;;;OAKG;IACH,iEA6DC;IAED;;;;OAIG;IACH,uDAFa,QAAQ,OAAO,CAAC,CA2C5B;IAED;;;;;OAKG;IACH,mBAmCC;IAED;;;;;OAKG;IACH,oBAqFC;IAED;;;;;OAKG;IACH,kEAyEC;IAED;;;;OAIG;IACH,mCAFa,wCAAgB,CA0B5B;IAED;;;;OAIG;IACH,8DAFa,wCAAgB,CA0B5B;IAED;;;;;OAKG;IACH,mEAoFC;IAGL;;OAEG;IACH,yBArtBe,sBAAsB,UAqtBA;IACrC;;OAEG;IACH,0BAAqC;CATpC;uBAh+BmC,aAAa;mBAR9B,oCAAoC;uCAMhB,6BAA6B"}
@@ -557,9 +557,11 @@ export class EntityManager {
557
557
  systems[systemIndex] = system;
558
558
 
559
559
 
560
- //build observer
561
- const entityObserver = new EntityObserver(system.dependencies, system.link, system.unlink, system);
562
- this.systemObservers.set(system, entityObserver);
560
+ //build observer; a dependency-less system observes no entity tuple and never links
561
+ if (system.dependencies.length > 0) {
562
+ const entityObserver = new EntityObserver(system.dependencies, system.link, system.unlink, system);
563
+ this.systemObservers.set(system, entityObserver);
564
+ }
563
565
 
564
566
  let result_promise;
565
567
  if (this.state === EntityManagerState.Running) {
@@ -596,11 +598,11 @@ export class EntityManager {
596
598
  return false;
597
599
  }
598
600
 
599
- //unlink system observer
601
+ //unlink system observer, dependency-less systems don't have one
600
602
  const systemObserver = this.systemObservers.get(system);
601
603
  this.systemObservers.delete(system);
602
604
 
603
- assert.notEqual(systemObserver, undefined, "System observer is undefined, it was possibly removed illegally or was not created");
605
+ assert.ok(systemObserver !== undefined || system.dependencies.length === 0, "System observer is undefined, it was possibly removed illegally or was not created");
604
606
 
605
607
  //shutdown system
606
608
  this.systems.splice(systemIndex, 1);
@@ -609,8 +611,10 @@ export class EntityManager {
609
611
  this.__execution_order_needs_update = true;
610
612
 
611
613
  if (this.dataset !== null) {
612
- // remove observed and unlink entities
613
- this.dataset.removeObserver(systemObserver, true);
614
+ if (systemObserver !== undefined) {
615
+ // remove observed and unlink entities
616
+ this.dataset.removeObserver(systemObserver, true);
617
+ }
614
618
  system.handleDatasetDetached(this.dataset);
615
619
  }
616
620
 
@@ -1 +1 @@
1
- {"version":3,"file":"System.d.ts","sourceRoot":"","sources":["../../../../src/engine/ecs/System.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH;IAEI;;;;;OAKG;IACH,uCAAqB;IAErB;;;;OAIG;IACH,oBAA+C;IAE/C;;;;;;;OAOG;IACH,6BAAkB;IAElB;;;;;;;;;;OAUG;IACH,0BAFU,6BAA6B,CAElB;IAGrB;;OAEG;IACH,mCAcC;IAED;;;;;OAKG;IACH,oCAFY,MAAM,CA8BjB;IAED;;;;;;OAMG;IACH,uCAHa,QAAQ,IAAI,CAAC,CAKzB;IAED;;;;;;;OAOG;IACH,wCAHa,QAAQ,IAAI,CAAC,CAKzB;IAGD;;;;OAIG;IACH,wCAEC;IAED;;;;OAIG;IACH,0CAEC;IAED;;;;;;;;;;OAUG;IACH,6DAEC;IAED;;;;;;;;;;OAUG;IACH,6DAEC;IAIL;;;OAGG;IACH,mBAFU,OAAO,CAEQ;IAEzB;;;;OAIG;IACH,2CAFsC,MAAM,CAEhB;IAG5B;;;;;;OAMG;IACH,sCAFsC,MAAM,CAErB;CAvBtB;0BA2BS,MAAM"}
1
+ {"version":3,"file":"System.d.ts","sourceRoot":"","sources":["../../../../src/engine/ecs/System.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH;IAEI;;;;;OAKG;IACH,uCAAqB;IAErB;;;;OAIG;IACH,oBAA+C;IAE/C;;;;;;;;;OASG;IACH,6BAAkB;IAElB;;;;;;;;;;OAUG;IACH,0BAFU,6BAA6B,CAElB;IAGrB;;OAEG;IACH,mCAcC;IAED;;;;;OAKG;IACH,oCAFY,MAAM,CA8BjB;IAED;;;;;;OAMG;IACH,uCAHa,QAAQ,IAAI,CAAC,CAKzB;IAED;;;;;;;OAOG;IACH,wCAHa,QAAQ,IAAI,CAAC,CAKzB;IAGD;;;;;OAKG;IACH,wCAEC;IAED;;;;;OAKG;IACH,0CAEC;IAED;;;;;;;;;;OAUG;IACH,6DAEC;IAED;;;;;;;;;;OAUG;IACH,6DAEC;IAIL;;;OAGG;IACH,mBAFU,OAAO,CAEQ;IAEzB;;;;OAIG;IACH,2CAFsC,MAAM,CAEhB;IAG5B;;;;;;OAMG;IACH,sCAFsC,MAAM,CAErB;CAvBtB;0BA2BS,MAAM"}
@@ -53,6 +53,8 @@ export class System {
53
53
 
54
54
  /**
55
55
  * Component classes which have to be present before the system links an entity.
56
+ * May be left empty: such a system observes no entity tuple, {@link link}/{@link unlink} are never invoked,
57
+ * and it is driven purely by {@link startup}/{@link shutdown} and the {@link update}/{@link fixedUpdate} methods.
56
58
  * NOTE: do not modify this while the system is running.
57
59
  * @see link
58
60
  * @see unlink
@@ -157,6 +159,7 @@ export class System {
157
159
  /**
158
160
  * Called automatically when an entity gets a complete component tuple {@link this.dependencies}.
159
161
  * Inputs are the same as dependencies, only instances instead of classes, plus there is an extra argument at the end, which is the entity ID.
162
+ * Never invoked when {@link this.dependencies} is empty.
160
163
  * see {@link EntityObserver} for more implementation detail
161
164
  */
162
165
  link(component, entity) {
@@ -166,6 +169,7 @@ export class System {
166
169
  /**
167
170
  * Called automatically when an entity breaks component tuple {@link this.dependencies}.
168
171
  * Inputs are the same as dependencies, only instances instead of classes, plus there is an extra argument at the end, which is the entity ID.
172
+ * Never invoked when {@link this.dependencies} is empty.
169
173
  * see {@link EntityObserver} for more implementation detail
170
174
  */
171
175
  unlink(component, entity) {
@@ -1 +1 @@
1
- {"version":3,"file":"screenSpaceFogOfWarShader.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/ecs/fow/shader/screenSpaceFogOfWarShader.js"],"names":[],"mappings":"AAGA;;;GAGG;AACH,kDAFW,cAAc,CAyJxB;+BA9JyE,OAAO"}
1
+ {"version":3,"file":"screenSpaceFogOfWarShader.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/ecs/fow/shader/screenSpaceFogOfWarShader.js"],"names":[],"mappings":"AAGA;;;GAGG;AACH,kDAFW,cAAc,CAmKxB;+BAxKyE,OAAO"}
@@ -28,16 +28,20 @@ export function buildScreenSpaceFogOfWarShader() {
28
28
  const float c_samplesX = 5.0;
29
29
  const float c_samplesY = 5.0;
30
30
 
31
- const float c_halfSamplesX = c_samplesX / 2.0;
32
- const float c_halfSamplesY = c_samplesY / 2.0;
31
+ // True kernel centre is (N-1)/2, so a 5-tap kernel is centred on index 2.
32
+ const float c_halfSamplesX = (c_samplesX - 1.0) / 2.0;
33
+ const float c_halfSamplesY = (c_samplesY - 1.0) / 2.0;
33
34
 
34
- const vec2 sigma = vec2(23.0, 23.0);
35
+ // Std-dev in *sample-index* units (kernel is only c_samples wide), NOT
36
+ // fog-resolution units. ~1.2 gives a real bell over the 5 taps.
37
+ const vec2 sigma = vec2(1.2, 1.2);
35
38
 
36
39
  const vec2 sigma2_sq = sigma*sigma*2.0;
37
40
 
38
- const float radius = 0.9;
39
-
40
- const vec2 samplingBias = vec2(c_halfSamplesX,c_halfSamplesY)/ vec2( radius );
41
+ // Spacing between adjacent taps, in fog texels. Controls blur width and
42
+ // therefore how strongly the coarse fog grid is smoothed. 1.0 = one texel
43
+ // per tap, so a 5-tap kernel reaches +/-2 texels either side of centre.
44
+ const float texelSpacing = 1.0;
41
45
 
42
46
  vec3 computeWorldPosition(){
43
47
  // Convert screen coordinates to normalized device coordinates (NDC)
@@ -72,7 +76,8 @@ export function buildScreenSpaceFogOfWarShader() {
72
76
 
73
77
  float getFog(const in vec2 uv){
74
78
 
75
- vec2 texelSize = samplingBias * uResolution;
79
+ // size of one fog texel, in UV space, scaled by the desired tap spacing
80
+ vec2 texelSize = texelSpacing / uResolution;
76
81
 
77
82
  float total = 0.0;
78
83
 
@@ -81,12 +86,12 @@ export function buildScreenSpaceFogOfWarShader() {
81
86
  for (float iy = 0.0; iy < c_samplesY; ++iy){
82
87
 
83
88
  float fy = Gaussian(sigma2_sq.y, iy - c_halfSamplesY);
84
- float offsety = (iy-c_halfSamplesY) / texelSize.y;
89
+ float offsety = (iy - c_halfSamplesY) * texelSize.y;
85
90
 
86
91
  for (float ix = 0.0; ix < c_samplesX; ++ix){
87
92
 
88
93
  float fx = Gaussian(sigma2_sq.x, ix - c_halfSamplesX);
89
- float offsetx = (ix-c_halfSamplesX) / texelSize.x;
94
+ float offsetx = (ix - c_halfSamplesX) * texelSize.x;
90
95
 
91
96
  float weight = fx * fy;
92
97
 
@@ -110,8 +115,13 @@ export function buildScreenSpaceFogOfWarShader() {
110
115
  gl_FragColor = uColor;
111
116
  gl_FragColor.a *= fogValue;
112
117
 
113
- // dither the alpha ramp to break up 8-bit quantization banding
118
+ // The fog is written to an 8-bit intermediate layer target; both channels of
119
+ // that target carry a quantized ramp: alpha holds fogValue, rgb holds
120
+ // uColor.rgb * alpha (multiplied by ROP blending). Dither each at +/-0.5 LSB
121
+ // of its own quantizer to break up banding. The rgb noise is attenuated by
122
+ // alpha in the blend, exactly matching how the rgb band amplitude scales.
114
123
  gl_FragColor.a += gradientNoise(gl_FragCoord.xy) * (1.0 / 255.0) - (0.5 / 255.0);
124
+ gl_FragColor.rgb += vec3( gradientNoise(gl_FragCoord.xy + vec2(7.0, 13.0)) * (1.0 / 255.0) - (0.5 / 255.0) );
115
125
  }
116
126
  `;
117
127
  }
@@ -1 +1 @@
1
- {"version":3,"file":"system_validate_class.d.ts","sourceRoot":"","sources":["../../../../src/engine/ecs/system_validate_class.js"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,qFA0FC;uBAjGsB,aAAa"}
1
+ {"version":3,"file":"system_validate_class.d.ts","sourceRoot":"","sources":["../../../../src/engine/ecs/system_validate_class.js"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,qFAiGC;uBAxGsB,aAAa"}
@@ -60,8 +60,15 @@ export function system_validate_class(system) {
60
60
 
61
61
  const numDependencies = dependencies.length;
62
62
 
63
- if (numDependencies < 1) {
64
- throw new Error(`A system must declare at least one dependency`);
63
+ if (numDependencies === 0) {
64
+ // no dependencies means no entity tuple is observed, so 'link'/'unlink' can never be invoked
65
+ if (system.link !== System.prototype.link) {
66
+ throw new Error(`'link' is overridden, but no dependencies are declared, so it would never be invoked`);
67
+ }
68
+
69
+ if (system.unlink !== System.prototype.unlink) {
70
+ throw new Error(`'unlink' is overridden, but no dependencies are declared, so it would never be invoked`);
71
+ }
65
72
  }
66
73
 
67
74
  if (numDependencies > 1) {
@@ -43,6 +43,8 @@ declare class Trail2D {
43
43
  */
44
44
  distanceSinceLastUpdate: number;
45
45
  /**
46
+ * RGB tints the ribbon; alpha is the trail's base opacity, which the
47
+ * age-driven fade scales down to zero.
46
48
  * @readonly
47
49
  * @type {Color}
48
50
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Trail2D.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail2d/Trail2D.js"],"names":[],"mappings":";AAwBA;IAgMI,oCAMC;IApMD;;;OAGG;IACH,QAFU,MAAM,CAES;IAEzB;;;OAGG;IACH,OAFU,MAAM,CAEM;IAEtB;;;OAGG;IACH,WAFU,cAAc,GAAC,MAAM,CAEC;IAEhC;;;;OAIG;IACH,eAFU,MAAM,CAEuB;IAEvC;;;OAGG;IACH,MAFU,MAAM,CAEP;IAET;;;OAGG;IACH,eAFU,MAAM,CAEE;IAElB;;;OAGG;IACH,qBAFU,MAAM,CAEQ;IAExB;;;OAGG;IACH,yBAFU,MAAM,CAEY;IAE5B;;;OAGG;IACH,gBAFU,KAAK,CAEe;IAE9B;;;;OAIG;IACH,iBAFU,OAAO,CAEM;IAEvB;;;OAGG;IACH,QAFU,OAAO,GAAC,IAAI,CAER;IAEd;;;OAGG;IACH,UAFU,mBAAmB,CAEQ;IAErC;;;OAGG;IACH,cAFU,SAAS,CAEG;IAEtB;;;OAGG;IACH,cAAsB;IAEtB,gBAQC;IAMD,4BAEC;IAND,yBAEC;IAMD;;;;OAIG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,YAAY,SACnB,OAAO,QAQjB;IAED;;;;OAIG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,OAAO,CAInB;IAED;;;OAGG;IACH,qBAFW,MAAM,QAehB;IADG,kHAAgB;IAGpB;;OAEG;IACH,cAsBC;IAUD;;;;;;;;;;;;;aAkBC;IAGD;;;;;;;;;;;;;;;;;MAUC;IAED;;;OAGG;IACH,cAFW,OAAO,WAWjB;IAED;;;;;;;OAOG;IACH,cANW,MAAM,KACN,MAAM,KACN,MAAM,YACN,MAAM,GACJ,OAAO,CAkEnB;CAEJ;;;;;;+BAnV8B,+BAA+B;sBALxC,iCAAiC;oBACnC,kCAAkC;wBAM9B,0BAA0B;oCACd,sCAAsC;0BAThD,yCAAyC;6BAWtC,mBAAmB;kCADd,wBAAwB"}
1
+ {"version":3,"file":"Trail2D.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail2d/Trail2D.js"],"names":[],"mappings":";AAwBA;IAkMI,oCAMC;IAtMD;;;OAGG;IACH,QAFU,MAAM,CAES;IAEzB;;;OAGG;IACH,OAFU,MAAM,CAEM;IAEtB;;;OAGG;IACH,WAFU,cAAc,GAAC,MAAM,CAEC;IAEhC;;;;OAIG;IACH,eAFU,MAAM,CAEuB;IAEvC;;;OAGG;IACH,MAFU,MAAM,CAEP;IAET;;;OAGG;IACH,eAFU,MAAM,CAEE;IAElB;;;OAGG;IACH,qBAFU,MAAM,CAEQ;IAExB;;;OAGG;IACH,yBAFU,MAAM,CAEY;IAE5B;;;;;OAKG;IACH,gBAFU,KAAK,CAEe;IAE9B;;;;OAIG;IACH,iBAFU,OAAO,CAEM;IAEvB;;;OAGG;IACH,QAFU,OAAO,GAAC,IAAI,CAER;IAEd;;;OAGG;IACH,UAFU,mBAAmB,CAEQ;IAErC;;;OAGG;IACH,cAFU,SAAS,CAEG;IAEtB;;;OAGG;IACH,cAAsB;IAEtB,gBAQC;IAMD,4BAEC;IAND,yBAEC;IAMD;;;;OAIG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,YAAY,SACnB,OAAO,QAQjB;IAED;;;;OAIG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,OAAO,CAInB;IAED;;;OAGG;IACH,qBAFW,MAAM,QAehB;IADG,kHAAgB;IAGpB;;OAEG;IACH,cAsBC;IAUD;;;;;;;;;;;;;aAkBC;IAGD;;;;;;;;;;;;;;;;;MAUC;IAED;;;OAGG;IACH,cAFW,OAAO,WAWjB;IAED;;;;;;;OAOG;IACH,cANW,MAAM,KACN,MAAM,KACN,MAAM,YACN,MAAM,GACJ,OAAO,CAmEnB;CAEJ;;;;;;+BAtV8B,+BAA+B;sBALxC,iCAAiC;oBACnC,kCAAkC;wBAM9B,0BAA0B;oCACd,sCAAsC;0BAThD,yCAAyC;6BAWtC,mBAAmB;kCADd,wBAAwB"}
@@ -74,6 +74,8 @@ class Trail2D {
74
74
  distanceSinceLastUpdate = 0;
75
75
 
76
76
  /**
77
+ * RGB tints the ribbon; alpha is the trail's base opacity, which the
78
+ * age-driven fade scales down to zero.
77
79
  * @readonly
78
80
  * @type {Color}
79
81
  */
@@ -336,9 +338,10 @@ class Trail2D {
336
338
  this.setFlag(Trail2DFlags.BoundsNeedUpdate);
337
339
  }
338
340
 
339
- // set head to fresh value
341
+ // set head to fresh value; alpha follows the same fade law the simulator
342
+ // applies every tick, so the point doesn't pop when it stops being the head
340
343
  ribbon.setPointAttribute_Scalar(head_index, RIBBON_ATTRIBUTE_ADDRESS_AGE, head_age);
341
- ribbon.setPointAlpha(head_index, clamp01(head_age / maxAge));
344
+ ribbon.setPointAlpha(head_index, (1 - clamp01(head_age / maxAge)) * this.color.w);
342
345
  ribbon.setPointThickness(head_index, this.width);
343
346
 
344
347
  return head_position_changed;
@@ -1 +1 @@
1
- {"version":3,"file":"Trail2DSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail2d/Trail2DSystem.js"],"names":[],"mappings":";AA0BA;IAkCI;;;OAGG;IACH,4BA4BC;IAjED,oDAAoC;IAEpC,+DAEE;IAEF;;;OAGG;IACH,WAFU,4BAA4B,CAES;IAE/C;;;;OAIG;IACH,wBAAiC;IAGjC;;;;OAIG;IACH,oBAAgB;IAEhB;;;OAGG;IACH,YAAgB;IAcZ;;;;OAIG;IACH,iBAAsB;IAEtB;;;OAGG;IACH,UAFU,cAAc,CAEO;IAE/B;;;OAGG;IACH,aAFU,cAAY,IAAI,CAEH;IAK3B,2CAmBC;IAED,4CAIC;IAED;;;;;OAKG;IACH,sBAqDC;IAED;;;;;OAKG;IACH,YAHW,OAAO,aADP,SAAS,YAET,MAAM,QAahB;IAED;;;;;OAKG;IACH,kBAJW,OAAO,aACP,SAAS,UACT,MAAM,QAShB;IAED;;;;OAIG;IACH,yBAHW,OAAO,aACP,SAAS,QAoCnB;IAED,6BAWC;CACJ;uBArQsB,wBAAwB;0BACrB,qCAAqC;oBAY3C,cAAc;4CAdU,uDAAuD;6CAYtD,yDAAyD;+BARvE,yBAAyB"}
1
+ {"version":3,"file":"Trail2DSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail2d/Trail2DSystem.js"],"names":[],"mappings":";AA0BA;IAkCI;;;OAGG;IACH,4BA4BC;IAjED,oDAAoC;IAEpC,+DAEE;IAEF;;;OAGG;IACH,WAFU,4BAA4B,CAES;IAE/C;;;;OAIG;IACH,wBAAiC;IAGjC;;;;OAIG;IACH,oBAAgB;IAEhB;;;OAGG;IACH,YAAgB;IAcZ;;;;OAIG;IACH,iBAAsB;IAEtB;;;OAGG;IACH,UAFU,cAAc,CAEO;IAE/B;;;OAGG;IACH,aAFU,cAAY,IAAI,CAEH;IAK3B,2CAmBC;IAED,4CAIC;IAED;;;;;OAKG;IACH,sBAsDC;IAED;;;;;OAKG;IACH,YAHW,OAAO,aADP,SAAS,YAET,MAAM,QAahB;IAED;;;;;OAKG;IACH,kBAJW,OAAO,aACP,SAAS,UACT,MAAM,QAShB;IAED;;;;OAIG;IACH,yBAHW,OAAO,aACP,SAAS,QAoCnB;IAED,6BAWC;CACJ;uBAtQsB,wBAAwB;0BACrB,qCAAqC;oBAY3C,cAAc;4CAdU,uDAAuD;6CAYtD,yDAAyD;+BARvE,yBAAyB"}
@@ -167,7 +167,8 @@ class Trail2DSystem extends System {
167
167
  ribbon.setPointPosition(i, position_x, position_y, position_z);
168
168
  ribbon.setPointThickness(i, trail.width);
169
169
  ribbon.setPointAttribute_Scalar(i, RIBBON_ATTRIBUTE_ADDRESS_UV_OFFSET, f);
170
- ribbon.setPointAlpha(i, color.w);
170
+ // seeded age = maxAge means "already expired", so alpha follows the fade law: 0
171
+ ribbon.setPointAlpha(i, 0);
171
172
  ribbon.setPointAttribute_Scalar(i, RIBBON_ATTRIBUTE_ADDRESS_AGE, trail.maxAge);
172
173
 
173
174
  }
@@ -238,7 +239,7 @@ class Trail2DSystem extends System {
238
239
 
239
240
  const ageOffset = max2(0, trail.maxAge - trail.time);
240
241
 
241
- this.simulator.update(ribbon, trail.maxAge, timeDelta);
242
+ this.simulator.update(ribbon, trail.maxAge, timeDelta, trail.color.w);
242
243
 
243
244
  if (trail.getFlag(Trail2DFlags.Spawning)) {
244
245
  trail.updateHead(newPosition.x, newPosition.y, newPosition.z, ageOffset);
@@ -54,6 +54,8 @@ declare class Trail3D {
54
54
  */
55
55
  distanceSinceLastUpdate: number;
56
56
  /**
57
+ * RGB tints the tube; alpha is the trail's base opacity, which the
58
+ * age-driven fade scales down to zero.
57
59
  * @readonly
58
60
  * @type {Color}
59
61
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Trail3D.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail3d/Trail3D.js"],"names":[],"mappings":";AAgCA;;;;;GAKG;AACH;IA4MI,oCAMC;IAhND;;;OAGG;IACH,QAFU,MAAM,CAES;IAEzB;;;OAGG;IACH,OAFU,MAAM,CAEM;IAEtB;;;;OAIG;IACH,gBAFU,MAAM,CAEyB;IAEzC;;;OAGG;IACH,WAFU,cAAc,GAAC,MAAM,CAEC;IAEhC;;;;OAIG;IACH,eAFU,MAAM,CAEuB;IAEvC;;;OAGG;IACH,MAFU,MAAM,CAEP;IAET;;OAEG;IACH,eAFU,MAAM,CAEE;IAElB;;;OAGG;IACH,qBAFU,MAAM,CAEQ;IAExB;;;OAGG;IACH,yBAFU,MAAM,CAEY;IAE5B;;;OAGG;IACH,gBAFU,KAAK,CAEe;IAE9B;;;;OAIG;IACH,iBAFU,OAAO,CAEM;IAEvB;;;OAGG;IACH,MAFU,KAAK,GAAC,IAAI,CAER;IAEZ;;;OAGG;IACH,MAFU,OAAK,IAAI,CAEP;IAEZ;;;OAGG;IACH,UAFU,iBAAiB,CAEQ;IAEnC;;;OAGG;IACH,cAFU,SAAS,CAEG;IAEtB;;;OAGG;IACH,cAAsB;IAEtB,gBAQC;IAMD,4BAEC;IAND,yBAEC;IAUD,6BAEC;IAND,0BAEC;IAMD;;;OAGG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;OAGG;IACH,gBAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;OAGG;IACH,gBAHW,MAAM,GAAC,YAAY,SACnB,OAAO,QAQjB;IAED;;;OAGG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,OAAO,CAInB;IAED;;OAEG;IACH,qBAFW,MAAM,QAehB;IAED;;OAEG;IACH,cAmBC;IAUD;;;;;;;;;;;;;;;aAsBC;IAED;;;;;;;;;;;;;;;;;;;MAYC;IAED;;OAEG;IACH,cAFW,OAAO,WAajB;IAkDD;;;;;;OAMG;IACH,cANW,MAAM,KACN,MAAM,KACN,MAAM,YACN,MAAM,GACJ,OAAO,CAgEnB;;CAEJ;;;;;+BA9Z8B,+BAA+B;sBAPxC,iCAAiC;oBACnC,kCAAkC;sBAShC,2BAA2B;kCACf,uCAAuC;0BAZ/C,yCAAyC;6BAatC,mBAAmB"}
1
+ {"version":3,"file":"Trail3D.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail3d/Trail3D.js"],"names":[],"mappings":";AAgCA;;;;;GAKG;AACH;IA8MI,oCAMC;IAlND;;;OAGG;IACH,QAFU,MAAM,CAES;IAEzB;;;OAGG;IACH,OAFU,MAAM,CAEM;IAEtB;;;;OAIG;IACH,gBAFU,MAAM,CAEyB;IAEzC;;;OAGG;IACH,WAFU,cAAc,GAAC,MAAM,CAEC;IAEhC;;;;OAIG;IACH,eAFU,MAAM,CAEuB;IAEvC;;;OAGG;IACH,MAFU,MAAM,CAEP;IAET;;OAEG;IACH,eAFU,MAAM,CAEE;IAElB;;;OAGG;IACH,qBAFU,MAAM,CAEQ;IAExB;;;OAGG;IACH,yBAFU,MAAM,CAEY;IAE5B;;;;;OAKG;IACH,gBAFU,KAAK,CAEe;IAE9B;;;;OAIG;IACH,iBAFU,OAAO,CAEM;IAEvB;;;OAGG;IACH,MAFU,KAAK,GAAC,IAAI,CAER;IAEZ;;;OAGG;IACH,MAFU,OAAK,IAAI,CAEP;IAEZ;;;OAGG;IACH,UAFU,iBAAiB,CAEQ;IAEnC;;;OAGG;IACH,cAFU,SAAS,CAEG;IAEtB;;;OAGG;IACH,cAAsB;IAEtB,gBAQC;IAMD,4BAEC;IAND,yBAEC;IAUD,6BAEC;IAND,0BAEC;IAMD;;;OAGG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;OAGG;IACH,gBAHW,MAAM,GAAC,YAAY,GACjB,IAAI,CAIhB;IAED;;;OAGG;IACH,gBAHW,MAAM,GAAC,YAAY,SACnB,OAAO,QAQjB;IAED;;;OAGG;IACH,cAHW,MAAM,GAAC,YAAY,GACjB,OAAO,CAInB;IAED;;OAEG;IACH,qBAFW,MAAM,QAehB;IAED;;OAEG;IACH,cAmBC;IAUD;;;;;;;;;;;;;;;aAsBC;IAED;;;;;;;;;;;;;;;;;;;MAYC;IAED;;OAEG;IACH,cAFW,OAAO,WAajB;IAkDD;;;;;;OAMG;IACH,cANW,MAAM,KACN,MAAM,KACN,MAAM,YACN,MAAM,GACJ,OAAO,CAiEnB;;CAEJ;;;;;+BAja8B,+BAA+B;sBAPxC,iCAAiC;oBACnC,kCAAkC;sBAShC,2BAA2B;kCACf,uCAAuC;0BAZ/C,yCAAyC;6BAatC,mBAAmB"}
@@ -94,6 +94,8 @@ class Trail3D {
94
94
  distanceSinceLastUpdate = 0;
95
95
 
96
96
  /**
97
+ * RGB tints the tube; alpha is the trail's base opacity, which the
98
+ * age-driven fade scales down to zero.
97
99
  * @readonly
98
100
  * @type {Color}
99
101
  */
@@ -413,9 +415,10 @@ class Trail3D {
413
415
  this.setFlag(Trail3DFlags.BoundsNeedUpdate);
414
416
  }
415
417
 
416
- // set head to fresh value
418
+ // set head to fresh value; alpha follows the same fade law the simulator
419
+ // applies every tick, so the knot doesn't pop when it stops being the head
417
420
  tube.setKnotAttribute_Scalar(head_index, TUBE_ATTRIBUTE_ADDRESS_AGE, head_age);
418
- tube.setKnotAlpha(head_index, clamp01(head_age / maxAge));
421
+ tube.setKnotAlpha(head_index, (1 - clamp01(head_age / maxAge)) * this.color.w);
419
422
  tube.setKnotThickness(head_index, this.width);
420
423
 
421
424
  return head_position_changed;
@@ -1 +1 @@
1
- {"version":3,"file":"Trail3DSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail3d/Trail3DSystem.js"],"names":[],"mappings":";AAyBA;IAiCI;;;OAGG;IACH,4BAkBC;IAtDD,oDAAoC;IAEpC,+DAEE;IAEF;;OAEG;IACH,WAFU,0BAA0B,CAES;IAiB7C;;;OAGG;IACH,YAAgB;IAeZ;;OAEG;IACH,UAFU,cAAc,CAEO;IAE/B;;OAEG;IACH,aAFU,cAAY,IAAI,CAEH;IAG3B,2CAmBC;IAED,4CAIC;IA0DD;;;;OAIG;IACH,YAJW,OAAO,aACP,SAAS,YACT,MAAM,QAYhB;IAED;;;;OAIG;IACH,kBAJW,OAAO,aACP,SAAS,UACT,MAAM,QAQhB;IAED;;;OAGG;IACH,yBAHW,OAAO,aACP,SAAS,QAkCnB;IAED,6BAWC;;CACJ;uBA7OsB,wBAAwB;0BACrB,qCAAqC;oBAW3C,cAAc;4CAbU,uDAAuD;2CAUxD,0DAA0D;+BANtE,yBAAyB"}
1
+ {"version":3,"file":"Trail3DSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/trail3d/Trail3DSystem.js"],"names":[],"mappings":";AAyBA;IAiCI;;;OAGG;IACH,4BAkBC;IAtDD,oDAAoC;IAEpC,+DAEE;IAEF;;OAEG;IACH,WAFU,0BAA0B,CAES;IAiB7C;;;OAGG;IACH,YAAgB;IAeZ;;OAEG;IACH,UAFU,cAAc,CAEO;IAE/B;;OAEG;IACH,aAFU,cAAY,IAAI,CAEH;IAG3B,2CAmBC;IAED,4CAIC;IA2DD;;;;OAIG;IACH,YAJW,OAAO,aACP,SAAS,YACT,MAAM,QAYhB;IAED;;;;OAIG;IACH,kBAJW,OAAO,aACP,SAAS,UACT,MAAM,QAQhB;IAED;;;OAGG;IACH,yBAHW,OAAO,aACP,SAAS,QAkCnB;IAED,6BAWC;;CACJ;uBA9OsB,wBAAwB;0BACrB,qCAAqC;oBAW3C,cAAc;4CAbU,uDAAuD;2CAUxD,0DAA0D;+BANtE,yBAAyB"}
@@ -151,7 +151,8 @@ class Trail3DSystem extends System {
151
151
  tube.setKnotPosition(i, position_x, position_y, position_z);
152
152
  tube.setKnotThickness(i, trail.width);
153
153
  tube.setKnotAttribute_Scalar(i, TUBE_ATTRIBUTE_ADDRESS_UV_V, f);
154
- tube.setKnotAlpha(i, color.w);
154
+ // seeded age = maxAge means "already expired", so alpha follows the fade law: 0
155
+ tube.setKnotAlpha(i, 0);
155
156
  tube.setKnotAttribute_Scalar(i, TUBE_ATTRIBUTE_ADDRESS_AGE, trail.maxAge);
156
157
  }
157
158
 
@@ -215,7 +216,7 @@ class Trail3DSystem extends System {
215
216
 
216
217
  const ageOffset = max2(0, trail.maxAge - trail.time);
217
218
 
218
- this.simulator.update(tube, trail.maxAge, timeDelta);
219
+ this.simulator.update(tube, trail.maxAge, timeDelta, trail.color.w);
219
220
 
220
221
  if (trail.getFlag(Trail3DFlags.Spawning)) {
221
222
  trail.updateHead(newPosition.x, newPosition.y, newPosition.z, ageOffset);
@@ -1,14 +1,16 @@
1
1
  /**
2
2
  * Fixed-function simulation for {@link TubeX} tubes — the 3D analogue of
3
- * {@link RibbonXFixedPhysicsSimulator}. Ages every knot, fades its alpha to zero
4
- * as it approaches `max_age`, and flows the along-tube UV coordinate with age.
3
+ * {@link RibbonXFixedPhysicsSimulator}. Ages every knot, fades its alpha from
4
+ * `alpha` to zero as it approaches `max_age`, and flows the along-tube UV
5
+ * coordinate with age.
5
6
  */
6
7
  export class TubeXFixedPhysicsSimulator {
7
8
  /**
8
9
  * @param {TubeX} tube
9
10
  * @param {number} max_age
10
11
  * @param {number} timeDelta
12
+ * @param {number} [alpha] base alpha of an age-0 knot; the age fade scales it
11
13
  */
12
- update(tube: TubeX, max_age: number, timeDelta: number): void;
14
+ update(tube: TubeX, max_age: number, timeDelta: number, alpha?: number): void;
13
15
  }
14
16
  //# sourceMappingURL=TubeXFixedPhysicsSimulator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TubeXFixedPhysicsSimulator.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js"],"names":[],"mappings":"AAGA;;;;GAIG;AACH;IAEI;;;;OAIG;IACH,6BAHW,MAAM,aACN,MAAM,QAiBhB;CACJ"}
1
+ {"version":3,"file":"TubeXFixedPhysicsSimulator.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH;IAEI;;;;;OAKG;IACH,6BAJW,MAAM,aACN,MAAM,UACN,MAAM,QAiBhB;CACJ"}
@@ -3,8 +3,9 @@ import { TUBE_ATTRIBUTE_ADDRESS_AGE, TUBE_ATTRIBUTE_ADDRESS_UV_V } from "../tube
3
3
 
4
4
  /**
5
5
  * Fixed-function simulation for {@link TubeX} tubes — the 3D analogue of
6
- * {@link RibbonXFixedPhysicsSimulator}. Ages every knot, fades its alpha to zero
7
- * as it approaches `max_age`, and flows the along-tube UV coordinate with age.
6
+ * {@link RibbonXFixedPhysicsSimulator}. Ages every knot, fades its alpha from
7
+ * `alpha` to zero as it approaches `max_age`, and flows the along-tube UV
8
+ * coordinate with age.
8
9
  */
9
10
  export class TubeXFixedPhysicsSimulator {
10
11
 
@@ -12,8 +13,9 @@ export class TubeXFixedPhysicsSimulator {
12
13
  * @param {TubeX} tube
13
14
  * @param {number} max_age
14
15
  * @param {number} timeDelta
16
+ * @param {number} [alpha] base alpha of an age-0 knot; the age fade scales it
15
17
  */
16
- update(tube, max_age, timeDelta) {
18
+ update(tube, max_age, timeDelta, alpha = 1) {
17
19
 
18
20
  const n = tube.getCount();
19
21
 
@@ -23,7 +25,7 @@ export class TubeXFixedPhysicsSimulator {
23
25
  const relative_age = clamp01(age / max_age);
24
26
 
25
27
  // fade out with age
26
- tube.setKnotAlpha(i, 1.0 - relative_age);
28
+ tube.setKnotAlpha(i, (1.0 - relative_age) * alpha);
27
29
 
28
30
  // flow the along-tube UV with age
29
31
  tube.setKnotAttribute_Scalar(i, TUBE_ATTRIBUTE_ADDRESS_UV_V, relative_age);
@@ -7,7 +7,8 @@ export class RibbonXFixedPhysicsSimulator {
7
7
  * @param {RibbonX} ribbon
8
8
  * @param {number} max_age
9
9
  * @param {number} timeDelta
10
+ * @param {number} [alpha] base alpha of an age-0 point; the age fade scales it
10
11
  */
11
- update(ribbon: RibbonX, max_age: number, timeDelta: number): void;
12
+ update(ribbon: RibbonX, max_age: number, timeDelta: number, alpha?: number): void;
12
13
  }
13
14
  //# sourceMappingURL=RibbonXFixedPhysicsSimulator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RibbonXFixedPhysicsSimulator.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/trail/x/simulator/RibbonXFixedPhysicsSimulator.js"],"names":[],"mappings":"AAIA;;GAEG;AACH;IAEI;;;;;OAKG;IACH,iCAHW,MAAM,aACN,MAAM,QAkBhB;CACJ"}
1
+ {"version":3,"file":"RibbonXFixedPhysicsSimulator.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/trail/x/simulator/RibbonXFixedPhysicsSimulator.js"],"names":[],"mappings":"AAIA;;GAEG;AACH;IAEI;;;;;;OAMG;IACH,iCAJW,MAAM,aACN,MAAM,UACN,MAAM,QAkBhB;CACJ"}
@@ -12,8 +12,9 @@ export class RibbonXFixedPhysicsSimulator {
12
12
  * @param {RibbonX} ribbon
13
13
  * @param {number} max_age
14
14
  * @param {number} timeDelta
15
+ * @param {number} [alpha] base alpha of an age-0 point; the age fade scales it
15
16
  */
16
- update(ribbon, max_age, timeDelta) {
17
+ update(ribbon, max_age, timeDelta, alpha = 1) {
17
18
 
18
19
  const n = ribbon.getCount();
19
20
  for (let i = 0; i < n; i++) {
@@ -23,7 +24,7 @@ export class RibbonXFixedPhysicsSimulator {
23
24
  const relative_age = clamp01(age / max_age);
24
25
 
25
26
  // apply alpha over-time
26
- ribbon.setPointAlpha(i, 1.0 - relative_age);
27
+ ribbon.setPointAlpha(i, (1.0 - relative_age) * alpha);
27
28
 
28
29
  ribbon.setPointAttribute_Scalar(i, RIBBON_ATTRIBUTE_ADDRESS_UV_OFFSET, relative_age);
29
30
  }