@navaramap/three 0.0.8 → 0.1.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/LICENSE +1 -1
- package/README.md +20 -8
- package/dist/assets/fontWorker-C5cHhkWQ.js +2 -0
- package/dist/assets/fontWorker-C5cHhkWQ.js.map +1 -0
- package/dist/assets/{index-DaejBdUz.js → index-VfUncKuS.js} +2 -2
- package/dist/assets/index-VfUncKuS.js.map +1 -0
- package/dist/assets/{navara_wasm_font_worker_bg-CSmoDs_Q.wasm → navara_wasm_font_worker_bg-CKqHBoCR.wasm} +0 -0
- package/dist/assets/{navara_wasm_worker_bg-C1dUd_Nk.wasm → navara_wasm_worker_bg-CT26EH41.wasm} +0 -0
- package/dist/index.d.ts +298 -54
- package/dist/index.js +1569 -1396
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +90 -69
- package/dist/index.umd.cjs.map +1 -1
- package/package.json +12 -12
- package/src/core/BaseDesc.ts +25 -1
- package/src/core/MeshDesc.test.ts +6 -8
- package/src/core/MeshDesc.ts +4 -6
- package/src/evaluations/FeatureEvaluator.ts +17 -10
- package/src/event/feature.ts +15 -2
- package/src/event/features/billboard.test.ts +62 -0
- package/src/event/features/billboard.ts +4 -2
- package/src/index.ts +266 -98
- package/src/material/enhancer/model/modelBaseEnhancer/shader.ts +2 -2
- package/src/material/enhancer/polygon/polygonBaseEnhancer/shader.ts +2 -2
- package/src/material/macro/tileShader.ts +12 -2
- package/src/mesh/polyline.ts +32 -4
- package/src/mesh/sdfText/batchedSdfText.test.ts +65 -0
- package/src/mesh/sdfText/batchedSdfText.ts +121 -43
- package/src/mesh/sprite/instancedSprite.ts +173 -65
- package/src/pick/pickHelper.test.ts +389 -4
- package/src/pick/pickHelper.ts +212 -37
- package/src/plugins/AttributionPlugin/index.ts +1 -1
- package/src/renderOrder.ts +10 -0
- package/src/terrain/sampleTerrainMostDetailed.test.ts +6 -6
- package/src/terrain/sampleTerrainMostDetailed.ts +7 -5
- package/src/type/index.ts +22 -1
- package/dist/assets/fontWorker-CkLGtrXf.js +0 -2
- package/dist/assets/fontWorker-CkLGtrXf.js.map +0 -1
- package/dist/assets/index-DaejBdUz.js.map +0 -1
|
@@ -66,7 +66,15 @@ export class InstancedSpriteMesh
|
|
|
66
66
|
implements PickableMesh, DeclutterParticipant
|
|
67
67
|
{
|
|
68
68
|
private _geometryType: SpriteGeometryType = GEOMETRY_TYPES.Point;
|
|
69
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Feature (batch) index → this feature's instance ids. A feature owns
|
|
71
|
+
* multiple instances for MultiPoint geometry and for points derived from
|
|
72
|
+
* line/polygon vertices via `geometryTypes`, so per-feature styling must
|
|
73
|
+
* fan out to all of them. `null` means instances and features are 1:1.
|
|
74
|
+
*/
|
|
75
|
+
private _batchIndexToInstances: Map<number, number[]> | null = null;
|
|
76
|
+
/** Instance count of the current geometry; bounds the identity fallback. */
|
|
77
|
+
private _instanceCount = 0;
|
|
70
78
|
private _initialColor: Color = new Color(0xffffff);
|
|
71
79
|
private _initialHeight = 0.0;
|
|
72
80
|
private _initialSize = -1.0; // Negative value indicates "use uScale" in shader
|
|
@@ -161,7 +169,7 @@ export class InstancedSpriteMesh
|
|
|
161
169
|
const state = enhancer.states();
|
|
162
170
|
const cx = Math.min(Math.max(state.center[0], -0.5), 0.5);
|
|
163
171
|
const cy = Math.min(Math.max(state.center[1], -0.5), 0.5);
|
|
164
|
-
// Mirror of instancedSprite.vert.glsl:
|
|
172
|
+
// Mirror of instancedSprite.vert.glsl:115-118 — aspect is per-instance
|
|
165
173
|
// (from the atlas rect), not a material-level uniform; there is no
|
|
166
174
|
// material-wide "aspect" state to read.
|
|
167
175
|
const uvRect = state.billboard
|
|
@@ -181,9 +189,14 @@ export class InstancedSpriteMesh
|
|
|
181
189
|
|
|
182
190
|
const override = overrides ? overrides[i] : Number.NaN;
|
|
183
191
|
const rectH = uvRect ? uvRect.getW(i) : 0;
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
//
|
|
192
|
+
const rectW = uvRect ? uvRect.getZ(i) : 0;
|
|
193
|
+
// Mirror of the shader's empty-rect cull: an instance with no image
|
|
194
|
+
// packed yet is not drawn, so it must not reserve declutter space and
|
|
195
|
+
// hide the labels around it.
|
|
196
|
+
if (uvRect && (rectW <= 0.0 || rectH <= 0.0)) continue;
|
|
197
|
+
const aspect = uvRect ? rectW / rectH : 1.0;
|
|
198
|
+
|
|
199
|
+
// Mirror of instancedSprite.vert.glsl:122-125 — the quad spans
|
|
187
200
|
// (position.xy - center) * vec2(aspect, 1) * size around the anchor.
|
|
188
201
|
out.push({
|
|
189
202
|
anchorX: anchors[i * 3],
|
|
@@ -481,17 +494,15 @@ export class InstancedSpriteMesh
|
|
|
481
494
|
colorBuffer[i * 3 + 0] = this._initialColor.r;
|
|
482
495
|
colorBuffer[i * 3 + 1] = this._initialColor.g;
|
|
483
496
|
colorBuffer[i * 3 + 2] = this._initialColor.b;
|
|
484
|
-
|
|
485
|
-
// Map batch ID to instance id
|
|
486
|
-
// assuming batch IDs are 32bit floats
|
|
487
|
-
const batchId = positionsInfo.batchIDs[i];
|
|
488
|
-
this._batchIdToInstance.set(batchId, i);
|
|
489
497
|
}
|
|
490
498
|
|
|
499
|
+
this._instanceCount = instanceCount;
|
|
500
|
+
this._rebuildBatchIndexMap(m);
|
|
501
|
+
|
|
491
502
|
if (m instanceof NavaraBillboardMesh) {
|
|
492
503
|
// instanceUvRect: vec4(x, y, w, h) — this instance's atlas sub-rect in
|
|
493
|
-
// pixels. Zeroed until an image is packed;
|
|
494
|
-
//
|
|
504
|
+
// pixels. Zeroed until an image is packed; the vertex shader culls empty
|
|
505
|
+
// rects, so instances stay invisible rather than garbled.
|
|
495
506
|
instancedGeometry.setAttribute(
|
|
496
507
|
"instanceUvRect",
|
|
497
508
|
new InstancedBufferAttribute(new Float32Array(instanceCount * 4), 4),
|
|
@@ -755,6 +766,20 @@ export class InstancedSpriteMesh
|
|
|
755
766
|
rectAttr.setXYZW(i, rect.x, rect.y, rect.w, rect.h);
|
|
756
767
|
}
|
|
757
768
|
rectAttr.needsUpdate = true;
|
|
769
|
+
this._onAtlasRectsChanged();
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* An instance's atlas rect changed. The rect drives the quad's aspect and
|
|
774
|
+
* whether the instance draws at all, so the declutter bounds computed before
|
|
775
|
+
* the image landed are stale — and because packs resolve long after the
|
|
776
|
+
* frame that requested them, the view has usually gone idle by now. Without
|
|
777
|
+
* the render request the page keeps showing the pre-image state until the
|
|
778
|
+
* next camera move.
|
|
779
|
+
*/
|
|
780
|
+
private _onAtlasRectsChanged(): void {
|
|
781
|
+
this.ctx.declutter?.markDirty();
|
|
782
|
+
this.ctx.renderFlag.forceUpdate = true;
|
|
758
783
|
}
|
|
759
784
|
|
|
760
785
|
onBeforePicking(): void {
|
|
@@ -781,32 +806,85 @@ export class InstancedSpriteMesh
|
|
|
781
806
|
return this._enhancedMaterial;
|
|
782
807
|
}
|
|
783
808
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
809
|
+
/**
|
|
810
|
+
* Group instances by their feature's batch index from the geometry's
|
|
811
|
+
* per-instance `batch_index` buffer. The u32 view is consumed synchronously,
|
|
812
|
+
* so other wasm calls may detach views, so it must not be stored. The handle
|
|
813
|
+
* stays owned by the ECS geometry, whose destroy path frees it
|
|
814
|
+
* (`remove_from_buf`). An identity mapping (every feature owns exactly one
|
|
815
|
+
* instance, the common case) skips the map entirely.
|
|
816
|
+
*/
|
|
817
|
+
private _rebuildBatchIndexMap(
|
|
818
|
+
m: NavaraPointMesh | NavaraBillboardMesh,
|
|
819
|
+
): void {
|
|
820
|
+
const batchIndexData = m.geometry.batch_index?.data;
|
|
821
|
+
const batchIndices =
|
|
822
|
+
batchIndexData !== undefined ? this.ctx.buf.u32(batchIndexData) : null;
|
|
823
|
+
this._batchIndexToInstances = null;
|
|
824
|
+
if (!batchIndices) return;
|
|
825
|
+
|
|
826
|
+
let identity = true;
|
|
827
|
+
for (let i = 0; i < batchIndices.length; i++) {
|
|
828
|
+
if (batchIndices[i] !== i) {
|
|
829
|
+
identity = false;
|
|
830
|
+
break;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
if (identity) return;
|
|
834
|
+
|
|
835
|
+
const map = new Map<number, number[]>();
|
|
836
|
+
for (let i = 0; i < batchIndices.length; i++) {
|
|
837
|
+
const batchIndex = batchIndices[i];
|
|
838
|
+
let instances = map.get(batchIndex);
|
|
839
|
+
if (!instances) {
|
|
840
|
+
instances = [];
|
|
841
|
+
map.set(batchIndex, instances);
|
|
842
|
+
}
|
|
843
|
+
instances.push(i);
|
|
844
|
+
}
|
|
845
|
+
this._batchIndexToInstances = map;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/** All instances owned by the feature at `batchIndex`; empty when unknown. */
|
|
849
|
+
private instancesOfBatchIndex(batchIndex: number): number[] {
|
|
850
|
+
if (this._batchIndexToInstances) {
|
|
851
|
+
return this._batchIndexToInstances.get(batchIndex) ?? [];
|
|
852
|
+
}
|
|
853
|
+
return batchIndex >= 0 && batchIndex < this._instanceCount
|
|
854
|
+
? [batchIndex]
|
|
855
|
+
: [];
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
setFeatureColorByBatchIndex(batchIndex: number, color: Color) {
|
|
859
|
+
const instances = this.instancesOfBatchIndex(batchIndex);
|
|
860
|
+
if (instances.length === 0) return;
|
|
787
861
|
|
|
788
862
|
const colorAttr = this.geometry.getAttribute(
|
|
789
863
|
"instanceColor",
|
|
790
864
|
) as InstancedBufferAttribute;
|
|
791
|
-
|
|
865
|
+
for (const instanceId of instances) {
|
|
866
|
+
colorAttr.setXYZ(instanceId, color.r, color.g, color.b);
|
|
867
|
+
}
|
|
792
868
|
colorAttr.needsUpdate = true;
|
|
793
869
|
}
|
|
794
870
|
|
|
795
|
-
|
|
796
|
-
const
|
|
797
|
-
if (
|
|
871
|
+
setFeatureShowByBatchIndex(batchIndex: number, rawVisible: boolean) {
|
|
872
|
+
const instances = this.instancesOfBatchIndex(batchIndex);
|
|
873
|
+
if (instances.length === 0) return;
|
|
798
874
|
|
|
799
875
|
const paramsAttr = this.geometry.getAttribute(
|
|
800
876
|
"instanceParams",
|
|
801
877
|
) as InstancedBufferAttribute;
|
|
802
|
-
|
|
878
|
+
for (const instanceId of instances) {
|
|
879
|
+
paramsAttr.setZ(instanceId, rawVisible ? 1.0 : 0.0);
|
|
880
|
+
}
|
|
803
881
|
paramsAttr.needsUpdate = true;
|
|
804
882
|
this.ctx.declutter?.markDirty();
|
|
805
883
|
}
|
|
806
884
|
|
|
807
|
-
|
|
808
|
-
const
|
|
809
|
-
if (
|
|
885
|
+
setFeatureOpacityByBatchIndex(batchIndex: number, opacity: number) {
|
|
886
|
+
const instances = this.instancesOfBatchIndex(batchIndex);
|
|
887
|
+
if (instances.length === 0) return;
|
|
810
888
|
|
|
811
889
|
const paramsAttr = this.geometry.getAttribute(
|
|
812
890
|
"instanceParams",
|
|
@@ -814,20 +892,24 @@ export class InstancedSpriteMesh
|
|
|
814
892
|
const clampedOpacity = Number.isFinite(opacity)
|
|
815
893
|
? Math.max(0.0, Math.min(1.0, opacity))
|
|
816
894
|
: 1.0;
|
|
817
|
-
|
|
895
|
+
for (const instanceId of instances) {
|
|
896
|
+
paramsAttr.setW(instanceId, clampedOpacity);
|
|
897
|
+
}
|
|
818
898
|
paramsAttr.needsUpdate = true;
|
|
819
899
|
}
|
|
820
900
|
|
|
821
|
-
|
|
822
|
-
const
|
|
823
|
-
if (
|
|
901
|
+
setFeatureHeightByBatchIndex(batchIndex: number, height: number) {
|
|
902
|
+
const instances = this.instancesOfBatchIndex(batchIndex);
|
|
903
|
+
if (instances.length === 0) return;
|
|
824
904
|
|
|
825
905
|
const paramsAttr = this.geometry.getAttribute(
|
|
826
906
|
"instanceParams",
|
|
827
907
|
) as InstancedBufferAttribute;
|
|
828
908
|
|
|
829
909
|
const sanitizedHeight = Number.isFinite(height) ? height : 0.0;
|
|
830
|
-
|
|
910
|
+
for (const instanceId of instances) {
|
|
911
|
+
paramsAttr.setX(instanceId, sanitizedHeight);
|
|
912
|
+
}
|
|
831
913
|
paramsAttr.needsUpdate = true;
|
|
832
914
|
this.ctx.declutter?.markDirty();
|
|
833
915
|
}
|
|
@@ -854,9 +936,12 @@ export class InstancedSpriteMesh
|
|
|
854
936
|
* layer-level `declutterPriority` for this instance. Driven by the feature
|
|
855
937
|
* evaluator.
|
|
856
938
|
*/
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
939
|
+
setFeatureDeclutterPriorityByBatchIndex(
|
|
940
|
+
batchIndex: number,
|
|
941
|
+
priority: number,
|
|
942
|
+
) {
|
|
943
|
+
const instances = this.instancesOfBatchIndex(batchIndex);
|
|
944
|
+
if (instances.length === 0) return;
|
|
860
945
|
|
|
861
946
|
if (!this._declutterPriorityOverrides) {
|
|
862
947
|
const count = this._anchors ? this._anchors.length / 3 : 0;
|
|
@@ -865,15 +950,21 @@ export class InstancedSpriteMesh
|
|
|
865
950
|
Number.NaN,
|
|
866
951
|
);
|
|
867
952
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
953
|
+
let changed = false;
|
|
954
|
+
for (const instanceId of instances) {
|
|
955
|
+
if (instanceId >= this._declutterPriorityOverrides.length) continue;
|
|
956
|
+
if (this._declutterPriorityOverrides[instanceId] === priority) continue;
|
|
957
|
+
this._declutterPriorityOverrides[instanceId] = priority;
|
|
958
|
+
changed = true;
|
|
959
|
+
}
|
|
960
|
+
if (changed) {
|
|
961
|
+
this.ctx.declutter?.markDirty();
|
|
962
|
+
}
|
|
872
963
|
}
|
|
873
964
|
|
|
874
|
-
|
|
875
|
-
const
|
|
876
|
-
if (
|
|
965
|
+
setFeatureSizeByBatchIndex(batchIndex: number, size: number) {
|
|
966
|
+
const instances = this.instancesOfBatchIndex(batchIndex);
|
|
967
|
+
if (instances.length === 0) return;
|
|
877
968
|
|
|
878
969
|
const paramsAttr = this.geometry.getAttribute(
|
|
879
970
|
"instanceParams",
|
|
@@ -884,7 +975,9 @@ export class InstancedSpriteMesh
|
|
|
884
975
|
? -1.0
|
|
885
976
|
: size
|
|
886
977
|
: -1.0;
|
|
887
|
-
|
|
978
|
+
for (const instanceId of instances) {
|
|
979
|
+
paramsAttr.setY(instanceId, sanitizedSize);
|
|
980
|
+
}
|
|
888
981
|
paramsAttr.needsUpdate = true;
|
|
889
982
|
this.ctx.declutter?.markDirty();
|
|
890
983
|
}
|
|
@@ -897,45 +990,60 @@ export class InstancedSpriteMesh
|
|
|
897
990
|
* reverts the feature to the material's default image. No-op for
|
|
898
991
|
* non-billboard (point) meshes.
|
|
899
992
|
*/
|
|
900
|
-
async
|
|
901
|
-
|
|
993
|
+
async setFeatureImageByBatchIndex(
|
|
994
|
+
batchIndex: number,
|
|
902
995
|
url: string | null | undefined,
|
|
903
996
|
): Promise<void> {
|
|
904
|
-
const
|
|
905
|
-
if (
|
|
997
|
+
const instances = this.instancesOfBatchIndex(batchIndex);
|
|
998
|
+
if (instances.length === 0) return;
|
|
906
999
|
|
|
907
1000
|
const rectAttr = this.geometry.getAttribute("instanceUvRect") as
|
|
908
1001
|
InstancedBufferAttribute | undefined;
|
|
909
1002
|
if (!rectAttr) return;
|
|
910
1003
|
|
|
911
1004
|
if (url == null) {
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1005
|
+
let cleared = false;
|
|
1006
|
+
for (const instanceId of instances) {
|
|
1007
|
+
this._requestedImageUrls.delete(instanceId);
|
|
1008
|
+
if (!this._imageOverrides.delete(instanceId)) continue;
|
|
1009
|
+
// Zero rect (invisible) until the default image finishes loading,
|
|
1010
|
+
// same as instances that never had an override.
|
|
1011
|
+
const rect = this._defaultRect;
|
|
1012
|
+
rectAttr.setXYZW(
|
|
1013
|
+
instanceId,
|
|
1014
|
+
rect?.x ?? 0,
|
|
1015
|
+
rect?.y ?? 0,
|
|
1016
|
+
rect?.w ?? 0,
|
|
1017
|
+
rect?.h ?? 0,
|
|
1018
|
+
);
|
|
1019
|
+
cleared = true;
|
|
1020
|
+
}
|
|
1021
|
+
if (cleared) {
|
|
1022
|
+
rectAttr.needsUpdate = true;
|
|
1023
|
+
this._onAtlasRectsChanged();
|
|
1024
|
+
}
|
|
925
1025
|
return;
|
|
926
1026
|
}
|
|
927
1027
|
|
|
928
|
-
|
|
1028
|
+
for (const instanceId of instances) {
|
|
1029
|
+
this._requestedImageUrls.set(instanceId, url);
|
|
1030
|
+
}
|
|
929
1031
|
const rect = await this._ensureAtlas().pack(url);
|
|
930
1032
|
this._reportAtlasBytes();
|
|
931
1033
|
if (!rect) return;
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1034
|
+
let applied = false;
|
|
1035
|
+
for (const instanceId of instances) {
|
|
1036
|
+
// A newer override or a clear superseded this load while in flight.
|
|
1037
|
+
if (this._requestedImageUrls.get(instanceId) !== url) continue;
|
|
1038
|
+
this._imageOverrides.add(instanceId);
|
|
1039
|
+
rectAttr.setXYZW(instanceId, rect.x, rect.y, rect.w, rect.h);
|
|
1040
|
+
applied = true;
|
|
1041
|
+
}
|
|
1042
|
+
if (applied) {
|
|
1043
|
+
this._syncAtlasUniforms();
|
|
1044
|
+
rectAttr.needsUpdate = true;
|
|
1045
|
+
this._onAtlasRectsChanged();
|
|
1046
|
+
}
|
|
939
1047
|
}
|
|
940
1048
|
|
|
941
1049
|
dispose(): void {
|
|
@@ -958,7 +1066,7 @@ export class InstancedSpriteMesh
|
|
|
958
1066
|
(this.material as ShaderMaterial).dispose();
|
|
959
1067
|
|
|
960
1068
|
// Clear internal collections to release references
|
|
961
|
-
this.
|
|
1069
|
+
this._batchIndexToInstances = null;
|
|
962
1070
|
this._anchors = null;
|
|
963
1071
|
this._declutterTargets = null;
|
|
964
1072
|
this._declutterPriorityOverrides = null;
|