@ev-ry/fx 0.1.0-rc.1 → 0.1.0-rc.2

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/src/text-scene.js CHANGED
@@ -1,5 +1,5 @@
1
- import { MOTION_SAMPLES, RadialMotion } from './motion.js';
2
- import {attachRasterTexture,applyRasterTextureShader} from './raster-texture-material.js';
1
+ import { MOTION_SAMPLES, RadialMotion } from './motion.js';
2
+ import {attachRasterTexture,applyRasterTextureShader} from './raster-texture-material.js';
3
3
 
4
4
  const vertexShader = `
5
5
  attribute vec3 color;
@@ -25,9 +25,10 @@ const vertexShader = `
25
25
  gl_Position = projectionMatrix * viewPosition;
26
26
  }
27
27
  `;
28
- const fragmentShader = `
29
- uniform float brightness;
30
- uniform vec3 tint;
28
+ const fragmentShader = `
29
+ uniform float brightness;
30
+ uniform float presentationOpacity;
31
+ uniform vec3 tint;
31
32
  varying vec3 vColor;
32
33
  varying vec3 vViewPosition;
33
34
  void main() {
@@ -36,12 +37,13 @@ const fragmentShader = `
36
37
  float diffuse = mix(0.16, 1.0, pow(facing, 0.72));
37
38
  float sheen = 0.16 * pow(facing, 10.0);
38
39
  float coverage = clamp(max(max(vColor.r, vColor.g), vColor.b) * brightness, 0.0, 1.0);
39
- // Coverage belongs in alpha only; multiplying RGB by it as well darkens thin strokes twice.
40
- gl_FragColor = vec4(tint * min(1.0, diffuse + sheen), coverage);
41
- // CSS tint and sampled image RGB are linear in Three's working space.
42
- // Encode RGB once for the render target; coverage alpha stays unchanged.
43
- #include <colorspace_fragment>
44
- }
40
+ // Coverage belongs in alpha only; multiplying RGB by it as well darkens thin strokes twice.
41
+ gl_FragColor = vec4(tint * min(1.0, diffuse + sheen), coverage);
42
+ // CSS tint and sampled image RGB are linear in Three's working space.
43
+ // Encode RGB once for the render target; coverage alpha stays unchanged.
44
+ #include <colorspace_fragment>
45
+ gl_FragColor.a *= presentationOpacity;
46
+ }
45
47
  `;
46
48
 
47
49
  export class TextScene {
@@ -55,7 +57,7 @@ export class TextScene {
55
57
  this.glyphs = [];
56
58
  this.version = 0;
57
59
  this.effectStarted = null;
58
- this.uniforms = { angles: { value: this.motion.angles }, radius: { value: 1 }, effectTime: { value: 2 }, brightness: { value: 1.25 }, tint: { value: new THREE.Color(1,1,1) } };
60
+ this.uniforms = { presentationOpacity:{value:1}, angles: { value: this.motion.angles }, radius: { value: 1 }, effectTime: { value: 2 }, brightness: { value: 1.25 }, tint: { value: new THREE.Color(1,1,1) } };
59
61
  }
60
62
 
61
63
  setText(text) {
@@ -182,7 +184,7 @@ export class TextScene {
182
184
  }
183
185
  }
184
186
 
185
- // Shared path: one draw per distinct glyph, with one XY offset per occurrence.
187
+ // Shared path: one draw per distinct glyph, with one XY offset per occurrence.
186
188
  export class SharedTextScene extends TextScene {
187
189
  setText(text) {
188
190
  const THREE = this.THREE, scale = this.engine.scale;
@@ -205,7 +207,7 @@ export class SharedTextScene extends TextScene {
205
207
  }
206
208
  const previous = this.sharedGroups || new Map(), next = new Map();
207
209
  this.bounds = {minX:Infinity,maxX:-Infinity,minY:Infinity,maxY:-Infinity};
208
- this.bufferBytes = 0;this.textureBytes=0;
210
+ this.bufferBytes = 0;this.textureBytes=0;
209
211
  for (const [glyph, offsets] of groups) {
210
212
  let resource = previous.get(glyph);
211
213
  if (resource && resource.scale !== scale) { this.releaseShared(resource); resource=null; }
@@ -219,35 +221,35 @@ export class SharedTextScene extends TextScene {
219
221
  const shader = vertexShader.replace('attribute float delay;', 'attribute float delay; attribute vec2 glyphOffset;')
220
222
  .replace('position + vec3(0.0, baseline, 0.0)', 'position + vec3(glyphOffset, 0.0)')
221
223
  .replace('(effectTime - delay)', '(effectTime - fract(delay + dot(glyphOffset, vec2(0.173, 0.317))))');
222
- const material = new THREE.ShaderMaterial({uniforms:{...this.uniforms,baseline:{value:0}},
223
- vertexShader:shader,fragmentShader,side:THREE.DoubleSide,transparent:true,depthWrite:false,extensions:{derivatives:true}});
224
- let releaseTexture=null;
225
- if(glyph.rasterSurface){
226
- geometry.setAttribute('rasterUV',new THREE.BufferAttribute(glyph.meshData.uvs,2));
227
- applyRasterTextureShader(material,{mask:glyph.rasterSurface.mask!==false});
228
- releaseTexture=attachRasterTexture(THREE,material,glyph.rasterSurface);
229
- }
224
+ const material = new THREE.ShaderMaterial({uniforms:{...this.uniforms,baseline:{value:0}},
225
+ vertexShader:shader,fragmentShader,side:THREE.DoubleSide,transparent:true,depthWrite:false,extensions:{derivatives:true}});
226
+ let releaseTexture=null;
227
+ if(glyph.rasterSurface){
228
+ geometry.setAttribute('rasterUV',new THREE.BufferAttribute(glyph.meshData.uvs,2));
229
+ applyRasterTextureShader(material,{mask:glyph.rasterSurface.mask!==false});
230
+ releaseTexture=attachRasterTexture(THREE,material,glyph.rasterSurface);
231
+ }
230
232
  const mesh = new THREE.Mesh(geometry,material);mesh.frustumCulled=false;this.scene.add(mesh);
231
- resource={mesh,scale,releaseTexture};
233
+ resource={mesh,scale,releaseTexture};
232
234
  }
233
235
  const geometry=resource.mesh.geometry;
234
236
  let attribute=geometry.getAttribute('glyphOffset');
235
237
  // Keep the same GPU buffer while capacity suffices; edits upload only offsets.
236
- if (!attribute || attribute.count < offsets.length/2 ||
237
- (attribute.array.length > 32 && offsets.length < attribute.array.length / 4)) {
238
+ if (!attribute || attribute.count < offsets.length/2 ||
239
+ (attribute.array.length > 32 && offsets.length < attribute.array.length / 4)) {
238
240
  // Replacing a BufferAttribute does not release its old GPU buffer in Three.
239
241
  if (attribute) geometry.dispose();
240
- const capacity = 2 ** Math.ceil(Math.log2(Math.max(8, offsets.length)));
241
- attribute=new THREE.InstancedBufferAttribute(new Float32Array(capacity),2);
242
- geometry.setAttribute('glyphOffset',attribute);
243
- }
244
- let changed = geometry.instanceCount !== offsets.length / 2 || attribute.version === 0;
245
- for (let i=0; i<offsets.length && !changed; i++) changed = attribute.array[i] !== Math.fround(offsets[i]);
246
- if (changed) {
247
- attribute.array.set(offsets);
248
- attribute.updateRange.offset=0; attribute.updateRange.count=offsets.length;
249
- attribute.needsUpdate=true;
250
- }
242
+ const capacity = 2 ** Math.ceil(Math.log2(Math.max(8, offsets.length)));
243
+ attribute=new THREE.InstancedBufferAttribute(new Float32Array(capacity),2);
244
+ geometry.setAttribute('glyphOffset',attribute);
245
+ }
246
+ let changed = geometry.instanceCount !== offsets.length / 2 || attribute.version === 0;
247
+ for (let i=0; i<offsets.length && !changed; i++) changed = attribute.array[i] !== Math.fround(offsets[i]);
248
+ if (changed) {
249
+ attribute.array.set(offsets);
250
+ attribute.updateRange.offset=0; attribute.updateRange.count=offsets.length;
251
+ attribute.needsUpdate=true;
252
+ }
251
253
  geometry.instanceCount=offsets.length/2;
252
254
  const box=geometry.boundingBox;
253
255
  for(let i=0;i<offsets.length;i+=2){
@@ -256,8 +258,8 @@ export class SharedTextScene extends TextScene {
256
258
  this.bounds.minY=Math.min(this.bounds.minY,box.min.y+offsets[i+1]);
257
259
  this.bounds.maxY=Math.max(this.bounds.maxY,box.max.y+offsets[i+1]);
258
260
  }
259
- for(const attr of Object.values(geometry.attributes))this.bufferBytes+=attr.array.byteLength;
260
- if(glyph.rasterSurface)this.textureBytes+=glyph.rasterSurface.rgba.byteLength;
261
+ for(const attr of Object.values(geometry.attributes))this.bufferBytes+=attr.array.byteLength;
262
+ if(glyph.rasterSurface)this.textureBytes+=glyph.rasterSurface.rgba.byteLength;
261
263
  next.set(glyph,resource);
262
264
  }
263
265
  for(const [glyph,resource] of previous)if(!next.has(glyph))this.releaseShared(resource);
@@ -267,6 +269,6 @@ export class SharedTextScene extends TextScene {
267
269
  this.motion.radius=Math.max(.001,Math.hypot(Math.max(Math.abs(b.minX),Math.abs(b.maxX)),Math.max(Math.abs(b.minY),Math.abs(b.maxY))));
268
270
  this.uniforms.radius.value=this.motion.radius;this.effectStarted=null;this.uniforms.effectTime.value=2;this.version++;
269
271
  }
270
- releaseShared(resource){this.scene.remove(resource.mesh);resource.mesh.geometry.dispose();resource.mesh.material.dispose();resource.releaseTexture?.();}
272
+ releaseShared(resource){this.scene.remove(resource.mesh);resource.mesh.geometry.dispose();resource.mesh.material.dispose();resource.releaseTexture?.();}
271
273
  dispose(){for(const resource of this.sharedGroups?.values() || [])this.releaseShared(resource);this.sharedGroups?.clear();super.dispose();}
272
274
  }