@expofp/renderer 2.3.0 → 2.3.1

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/dist/index.d.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  import { default as default_2 } from 'stats-gl';
2
- import { Vector2 } from 'three';
3
2
  import { Vector2Like } from 'three';
4
3
  import { Vector2Tuple } from 'three';
5
- import { Vector3 } from 'three';
6
4
  import { Vector3Like } from 'three';
7
5
  import { Vector3Tuple } from 'three';
8
6
  import { WebGLRenderer } from 'three';
@@ -132,20 +130,6 @@ export declare interface ControlsAPI {
132
130
  getCameraState: () => CameraState;
133
131
  }
134
132
 
135
- /**
136
- * Converts multiple vector2 representations to a {@link Vector2} instance.
137
- * @param vector2 Vector2 representation as a tuple/POJO
138
- * @returns Vector2 instance
139
- */
140
- export declare function createVector2(vector2: IVector2): Vector2;
141
-
142
- /**
143
- * Converts multiple vector3 representations to a {@link Vector3} instance.
144
- * @param vector3 Vector3 representation as a tuple/POJO
145
- * @returns Vector3 instance
146
- */
147
- export declare function createVector3(vector3: IVector3 | IVector2): Vector3;
148
-
149
133
  /**
150
134
  * Mapping of engine-wide events to their payloads
151
135
  */
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  var _a;
5
- import { DataTexture, FloatType, UnsignedIntType, IntType, RGBAFormat, RGBAIntegerFormat, RGFormat, RGIntegerFormat, RedFormat, RedIntegerFormat, BatchedMesh as BatchedMesh$1, BufferAttribute, StreamDrawUsage, Color, Matrix4, Vector3, Frustum, Sphere, Box3, Vector4, AlwaysDepth, DoubleSide, MeshBasicMaterial, Texture, Group, PlaneGeometry, SRGBColorSpace, Vector2, Quaternion, BufferGeometry, Mesh, LessEqualDepth, LinearSRGBColorSpace, Plane, Raycaster, Spherical, PerspectiveCamera, Camera, Scene, MathUtils, Clock, WebGLRenderer } from "three";
5
+ import { DataTexture, FloatType, UnsignedIntType, IntType, RGBAFormat, RGBAIntegerFormat, RGFormat, RGIntegerFormat, RedFormat, RedIntegerFormat, BatchedMesh as BatchedMesh$1, BufferAttribute, StreamDrawUsage, Color, Matrix4, Vector3, Frustum, Sphere, Box3, DynamicDrawUsage, Vector4, AlwaysDepth, DoubleSide, MeshBasicMaterial, Texture, Group, PlaneGeometry, SRGBColorSpace, Vector2, Quaternion, BufferGeometry, Mesh, LessEqualDepth, LinearSRGBColorSpace, Plane, Raycaster, Spherical, PerspectiveCamera, Camera, Scene, MathUtils, Clock, WebGLRenderer } from "three";
6
6
  import { traverseAncestorsGenerator } from "three/examples/jsm/utils/SceneUtils.js";
7
7
  import createLog from "debug";
8
8
  import { BatchedText as BatchedText$1, Text as Text$1 } from "troika-three-text";
@@ -540,24 +540,58 @@ class BatchedMesh extends BatchedMesh$1 {
540
540
  /** Whether to use WebGL_multi_draw extension or less performant fallback */
541
541
  __publicField(BatchedMesh, "useMultiDraw", true);
542
542
  const floatsPerMember = 32;
543
- const tempColor = new Color();
544
543
  const defaultStrokeColor = 8421504;
544
+ const defaultOrient = "+x+y";
545
+ const glyphBoundsAttrName = "aTroikaGlyphBounds";
546
+ const glyphIndexAttrName = "aTroikaGlyphIndex";
547
+ const memberIndexAttrName = "aTroikaTextBatchMemberIndex";
548
+ const syncStartEvent = { type: "syncstart" };
549
+ const syncCompleteEvent = { type: "synccomplete" };
550
+ const SYNCABLE_PROPS = [
551
+ "font",
552
+ "fontSize",
553
+ "fontStyle",
554
+ "fontWeight",
555
+ "lang",
556
+ "letterSpacing",
557
+ "lineHeight",
558
+ "maxWidth",
559
+ "overflowWrap",
560
+ "text",
561
+ "direction",
562
+ "textAlign",
563
+ "textIndent",
564
+ "whiteSpace",
565
+ "anchorX",
566
+ "anchorY",
567
+ "colorRanges",
568
+ "sdfGlyphSize"
569
+ ];
570
+ const tempColor = new Color();
545
571
  const tempMat4 = new Matrix4();
546
572
  const tempVec3a = new Vector3();
547
573
  const tempVec3b = new Vector3();
548
574
  const origin = new Vector3();
549
- const defaultOrient = "+x+y";
575
+ const frustum = new Frustum();
576
+ const sphere = new Sphere();
577
+ const box = new Box3();
550
578
  class BatchedText extends BatchedText$1 {
551
579
  // eslint-disable-next-line jsdoc/require-jsdoc
552
580
  constructor() {
553
581
  super();
582
+ __publicField(this, "_needsRepack", false);
554
583
  __publicField(this, "textArray", []);
555
584
  __publicField(this, "textureNeedsUpdate", false);
556
585
  __publicField(this, "boundsNeedsUpdate", false);
586
+ __publicField(this, "batchNeedsSync", true);
557
587
  this.addEventListener("synccomplete", () => {
558
588
  this.boundsNeedsUpdate = true;
559
589
  });
560
590
  }
591
+ /** Mark the batch as needing a sync pass. */
592
+ requestSync() {
593
+ this.batchNeedsSync = true;
594
+ }
561
595
  /** Number of texts in the batch */
562
596
  get size() {
563
597
  return this._members.size;
@@ -592,12 +626,16 @@ class BatchedText extends BatchedText$1 {
592
626
  }
593
627
  addText(text, instanceId) {
594
628
  super.addText(text);
629
+ text._batchParent = this;
595
630
  if (instanceId !== void 0) this.textArray[instanceId] = text;
631
+ this.batchNeedsSync = true;
596
632
  this.boundsNeedsUpdate = true;
597
633
  }
598
634
  removeText(text) {
599
635
  super.removeText(text);
636
+ if (text._batchParent === this) text._batchParent = void 0;
600
637
  this.textArray.splice(this.textArray.indexOf(text), 1);
638
+ this.batchNeedsSync = true;
601
639
  this.boundsNeedsUpdate = true;
602
640
  }
603
641
  dispose() {
@@ -611,19 +649,88 @@ class BatchedText extends BatchedText$1 {
611
649
  for (const text of this.textArray) {
612
650
  if (!text.visible) continue;
613
651
  if (text.matrixAutoUpdate) text.updateMatrix();
614
- _box$1.copy(text.geometry.boundingBox).applyMatrix4(text.matrix);
615
- bbox.union(_box$1);
652
+ box.copy(text.geometry.boundingBox).applyMatrix4(text.matrix);
653
+ bbox.union(box);
616
654
  }
617
655
  bbox.getBoundingSphere(this.geometry.boundingSphere);
618
656
  this.boundsNeedsUpdate = false;
619
657
  }
620
658
  }
621
659
  onBeforeRender(renderer, scene, camera, geometry, material) {
622
- this.sync();
660
+ if (this.batchNeedsSync) this.sync();
623
661
  if ("isTroikaTextMaterial" in material && material.isTroikaTextMaterial) {
624
662
  this.prepareForRender(material, scene, camera);
625
663
  }
626
664
  }
665
+ /**
666
+ * Sync members and repack the batched geometry if needed.
667
+ * @param callback Optional callback invoked after sync completes.
668
+ */
669
+ sync(callback) {
670
+ if (!this.batchNeedsSync) return;
671
+ this.batchNeedsSync = false;
672
+ let needsRepack = this._needsRepack;
673
+ this._needsRepack = false;
674
+ const pendingSyncs = [];
675
+ this._members.forEach((packingInfo, text) => {
676
+ const needsMemberSync = text._needsSync;
677
+ if (packingInfo.dirty || needsMemberSync) {
678
+ packingInfo.dirty = false;
679
+ needsRepack = true;
680
+ if (needsMemberSync) pendingSyncs.push(new Promise((resolve) => text.sync(resolve)));
681
+ }
682
+ });
683
+ if (!needsRepack) return;
684
+ this.dispatchEvent(syncStartEvent);
685
+ const repack = () => {
686
+ this.repackBatchedGeometry();
687
+ this.dispatchEvent(syncCompleteEvent);
688
+ if (callback) callback();
689
+ };
690
+ if (pendingSyncs.length) {
691
+ void Promise.all(pendingSyncs).then(repack);
692
+ } else {
693
+ repack();
694
+ }
695
+ }
696
+ repackBatchedGeometry() {
697
+ var _a2, _b, _c;
698
+ const geometry = this.geometry;
699
+ const batchedAttributes = geometry.attributes;
700
+ let memberIndexes = ((_a2 = batchedAttributes[memberIndexAttrName]) == null ? void 0 : _a2.array) ?? new Uint16Array(0);
701
+ let batchedGlyphIndexes = ((_b = batchedAttributes[glyphIndexAttrName]) == null ? void 0 : _b.array) ?? new Float32Array(0);
702
+ let batchedGlyphBounds = ((_c = batchedAttributes[glyphBoundsAttrName]) == null ? void 0 : _c.array) ?? new Float32Array(0);
703
+ let totalGlyphCount = 0;
704
+ this._members.forEach((_, { textRenderInfo }) => {
705
+ if (textRenderInfo) {
706
+ totalGlyphCount += textRenderInfo.glyphAtlasIndices.length;
707
+ this._textRenderInfo = textRenderInfo;
708
+ }
709
+ });
710
+ if (totalGlyphCount !== memberIndexes.length) {
711
+ memberIndexes = cloneAndResize(memberIndexes, totalGlyphCount);
712
+ batchedGlyphIndexes = cloneAndResize(batchedGlyphIndexes, totalGlyphCount);
713
+ batchedGlyphBounds = cloneAndResize(batchedGlyphBounds, totalGlyphCount * 4);
714
+ }
715
+ let memberIndex = 0;
716
+ let glyphIndex = 0;
717
+ this._members.forEach((packingInfo, { textRenderInfo }) => {
718
+ if (textRenderInfo) {
719
+ const glyphCount = textRenderInfo.glyphAtlasIndices.length;
720
+ memberIndexes.fill(memberIndex, glyphIndex, glyphIndex + glyphCount);
721
+ batchedGlyphIndexes.set(textRenderInfo.glyphAtlasIndices, glyphIndex);
722
+ batchedGlyphBounds.set(textRenderInfo.glyphBounds, glyphIndex * 4);
723
+ glyphIndex += glyphCount;
724
+ packingInfo.index = memberIndex++;
725
+ }
726
+ });
727
+ geometry.updateAttributeData(memberIndexAttrName, memberIndexes, 1);
728
+ geometry.getAttribute(memberIndexAttrName).setUsage(DynamicDrawUsage);
729
+ geometry.updateAttributeData(glyphIndexAttrName, batchedGlyphIndexes, 1);
730
+ geometry.updateAttributeData(glyphBoundsAttrName, batchedGlyphBounds, 4);
731
+ this.boundsNeedsUpdate = true;
732
+ this.updateBounds();
733
+ }
627
734
  prepareForRender(material, scene, camera) {
628
735
  var _a2;
629
736
  const isOutline = material.isTextOutlineMaterial;
@@ -643,14 +750,14 @@ class BatchedText extends BatchedText$1 {
643
750
  }
644
751
  const texData = texture.image.data;
645
752
  this.textureNeedsUpdate = false;
646
- _matrix$1.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(scene.matrixWorld);
647
- _frustum$1.setFromProjectionMatrix(_matrix$1, camera.coordinateSystem);
753
+ tempMat4.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(scene.matrixWorld);
754
+ frustum.setFromProjectionMatrix(tempMat4, camera.coordinateSystem);
648
755
  for (const text of this.textArray) {
649
756
  const index = ((_a2 = this._members.get(text)) == null ? void 0 : _a2.index) ?? -1;
650
757
  const textRenderInfo = text.textRenderInfo;
651
758
  if (index < 0 || !textRenderInfo) continue;
652
759
  const startIndex = index * floatsPerMember;
653
- const isHidden = !text.visible || !_frustum$1.intersectsSphere(_sphere$1.copy(text.geometry.boundingSphere).applyMatrix4(text.matrix));
760
+ const isHidden = !text.visible || !frustum.intersectsSphere(sphere.copy(text.geometry.boundingSphere).applyMatrix4(text.matrix));
654
761
  if (isHidden) {
655
762
  for (let i = 0; i < 16; i++) {
656
763
  this.setTexData(startIndex + i, 0, texData);
@@ -709,6 +816,11 @@ class BatchedText extends BatchedText$1 {
709
816
  }
710
817
  }
711
818
  class Text extends Text$1 {
819
+ constructor() {
820
+ super(...arguments);
821
+ /** Parent batch for sync invalidation. */
822
+ __publicField(this, "_batchParent");
823
+ }
712
824
  _prepareForRender(material) {
713
825
  const isOutline = material.isTextOutlineMaterial;
714
826
  const uniforms = material.uniforms;
@@ -766,6 +878,7 @@ class Text extends Text$1 {
766
878
  blockBounds[3] + pad
767
879
  );
768
880
  }
881
+ if (!this._batchParent) this.geometry.applyClipRect(uniforms.uTroikaClipRect.value);
769
882
  }
770
883
  uniforms.uTroikaSDFDebug.value = !!this.debugSDF;
771
884
  material.polygonOffset = !!this.depthOffset;
@@ -797,10 +910,29 @@ class Text extends Text$1 {
797
910
  }
798
911
  }
799
912
  }
800
- const _matrix$1 = new Matrix4();
801
- const _frustum$1 = new Frustum();
802
- const _sphere$1 = new Sphere();
803
- const _box$1 = new Box3();
913
+ SYNCABLE_PROPS.forEach((prop) => {
914
+ const privateKey = `_private_${prop}`;
915
+ Object.defineProperty(Text.prototype, prop, {
916
+ get() {
917
+ return this[privateKey];
918
+ },
919
+ set(value) {
920
+ var _a2;
921
+ const target = this;
922
+ if (value !== target[privateKey]) {
923
+ target[privateKey] = value;
924
+ target._needsSync = true;
925
+ (_a2 = target._batchParent) == null ? void 0 : _a2.requestSync();
926
+ }
927
+ }
928
+ });
929
+ });
930
+ function cloneAndResize(source, newLength) {
931
+ const constructor = source.constructor;
932
+ const copy = new constructor(newLength);
933
+ copy.set(source.subarray(0, newLength));
934
+ return copy;
935
+ }
804
936
  function setDimming(root, dim) {
805
937
  root.userData["uDim"] = dim === void 0 ? void 0 : +dim;
806
938
  }
@@ -6761,8 +6893,6 @@ export {
6761
6893
  Polygon,
6762
6894
  Rect,
6763
6895
  Renderer,
6764
- createVector2,
6765
- createVector3,
6766
6896
  isImageDef,
6767
6897
  isImageLayer,
6768
6898
  isLayerDef,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expofp/renderer",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"