@navaramap/three 0.0.4 → 0.0.5
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/assets/fontWorker-OwvKiQ8o.js +2 -0
- package/dist/assets/fontWorker-OwvKiQ8o.js.map +1 -0
- package/dist/assets/{index-ywHSGImj.js → index-TKhRxurv.js} +2 -2
- package/dist/assets/index-TKhRxurv.js.map +1 -0
- package/dist/assets/{navara_wasm_font_worker_bg-Du9nb2Pn.wasm → navara_wasm_font_worker_bg-Kk06ArRB.wasm} +0 -0
- package/dist/assets/{navara_wasm_worker_bg-Xadu67fw.wasm → navara_wasm_worker_bg-BZjLKf4i.wasm} +0 -0
- package/dist/index.d.ts +349 -282
- package/dist/index.js +5394 -5470
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +287 -235
- package/dist/index.umd.cjs.map +1 -1
- package/package.json +12 -12
- package/src/atmosphere.test.ts +227 -28
- package/src/atmosphere.ts +219 -55
- package/src/constants/assets.ts +63 -13
- package/src/declutter/types.ts +1 -1
- package/src/evaluations/FeatureEvaluator.test.ts +152 -0
- package/src/evaluations/FeatureEvaluator.ts +73 -2
- package/src/event/features/billboard.ts +1 -0
- package/src/event/features/point.ts +1 -0
- package/src/index.ts +7 -9
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/index.ts +10 -7
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/mutates.test.ts +74 -81
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/mutates.ts +18 -85
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/shader.test.ts +77 -0
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/shader.ts +9 -1
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/state.test.ts +15 -19
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/state.ts +0 -13
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/types.ts +46 -48
- package/src/mesh/constants.ts +22 -0
- package/src/mesh/index.ts +1 -1
- package/src/mesh/model.ts +6 -0
- package/src/mesh/polygon.ts +6 -0
- package/src/mesh/polyline.ts +6 -0
- package/src/mesh/sdfText/batchedSdfText.ts +1223 -0
- package/src/mesh/sdfText/glyphBuffers.ts +222 -0
- package/src/mesh/sdfText/glyphSlots.test.ts +151 -0
- package/src/mesh/sdfText/glyphSlots.ts +98 -0
- package/src/mesh/sdfText/index.ts +10 -0
- package/src/mesh/sdfText/labelData.test.ts +178 -0
- package/src/mesh/sdfText/labelData.ts +187 -0
- package/src/mesh/{sdfText.test.ts → sdfText/layout.test.ts} +1 -1
- package/src/mesh/sdfText/layout.ts +306 -0
- package/src/mesh/sprite/instancedSprite.ts +32 -0
- package/dist/assets/fontWorker-wz61edjR.js +0 -2
- package/dist/assets/fontWorker-wz61edjR.js.map +0 -1
- package/dist/assets/index-ywHSGImj.js.map +0 -1
- package/src/mesh/batchedSdfText.ts +0 -708
- package/src/mesh/sdfText.ts +0 -1189
- /package/dist/{draco_decoder.wasm → assets/draco_decoder-C32yEggz.wasm} +0 -0
- /package/dist/{draco_decoder2.wasm → assets/draco_decoder-Z1_iN-Ht.wasm} +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@navaramap/three",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.5",
|
|
5
5
|
"main": "dist/index.umd.cjs",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"astronomy-engine": "2.1.19",
|
|
31
31
|
"stats.js": "0.17.0",
|
|
32
32
|
"three-stdlib": "2.36.1",
|
|
33
|
-
"@navaramap/core": "0.0.
|
|
34
|
-
"@navaramap/engine": "0.0.
|
|
35
|
-
"@navaramap/engine-api": "0.0.
|
|
36
|
-
"@navaramap/
|
|
37
|
-
"@navaramap/three-
|
|
38
|
-
"@navaramap/
|
|
39
|
-
"@navaramap/worker": "0.0.
|
|
33
|
+
"@navaramap/core": "0.0.5",
|
|
34
|
+
"@navaramap/engine": "0.0.5",
|
|
35
|
+
"@navaramap/engine-api": "0.0.5",
|
|
36
|
+
"@navaramap/font": "0.0.5",
|
|
37
|
+
"@navaramap/three-api": "0.0.5",
|
|
38
|
+
"@navaramap/three-csm": "0.0.5",
|
|
39
|
+
"@navaramap/worker": "0.0.5"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@playwright/test": "1.61.1",
|
|
@@ -71,10 +71,10 @@
|
|
|
71
71
|
"tweakpane": "4.0.5",
|
|
72
72
|
"vite-plugin-glsl": "1.6.0",
|
|
73
73
|
"vite-plugin-static-copy": "4.1.1",
|
|
74
|
-
"@navaramap/three-default-descs": "0.0.
|
|
75
|
-
"@navaramap/three-plugins": "0.0.
|
|
76
|
-
"@navaramap/three-
|
|
77
|
-
"@navaramap/three-
|
|
74
|
+
"@navaramap/three-default-descs": "0.0.5",
|
|
75
|
+
"@navaramap/three-plugins": "0.0.5",
|
|
76
|
+
"@navaramap/three-react": "0.0.5",
|
|
77
|
+
"@navaramap/three-default-plugin": "0.0.5"
|
|
78
78
|
},
|
|
79
79
|
"scripts": {
|
|
80
80
|
"start": "vite",
|
package/src/atmosphere.test.ts
CHANGED
|
@@ -1,76 +1,275 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type WebGLRenderer } from "three";
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
|
|
4
|
-
import { Atmosphere } from "./atmosphere";
|
|
4
|
+
import { Atmosphere, type AtmosphereOptions } from "./atmosphere";
|
|
5
5
|
|
|
6
6
|
const { loadAsyncMock } = vi.hoisted(() => ({
|
|
7
7
|
loadAsyncMock: vi.fn(),
|
|
8
8
|
}));
|
|
9
9
|
|
|
10
10
|
vi.mock("@takram/three-atmosphere", () => ({
|
|
11
|
-
PrecomputedTexturesLoader: class {
|
|
12
|
-
setType() {
|
|
13
|
-
return this;
|
|
14
|
-
}
|
|
15
|
-
loadAsync = loadAsyncMock;
|
|
16
|
-
},
|
|
17
11
|
getSunDirectionECEF: vi.fn(),
|
|
18
12
|
getMoonDirectionECEF: vi.fn(),
|
|
19
13
|
getECIToECEFRotationMatrix: vi.fn(),
|
|
14
|
+
TRANSMITTANCE_TEXTURE_WIDTH: 256,
|
|
15
|
+
TRANSMITTANCE_TEXTURE_HEIGHT: 64,
|
|
16
|
+
IRRADIANCE_TEXTURE_WIDTH: 64,
|
|
17
|
+
IRRADIANCE_TEXTURE_HEIGHT: 16,
|
|
18
|
+
SCATTERING_TEXTURE_WIDTH: 256,
|
|
19
|
+
SCATTERING_TEXTURE_HEIGHT: 128,
|
|
20
|
+
SCATTERING_TEXTURE_DEPTH: 32,
|
|
20
21
|
}));
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
vi.mock("@takram/three-geospatial", () => {
|
|
24
|
+
class MockLoader {
|
|
25
|
+
loadAsync = loadAsyncMock;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
EXRTextureLoader: MockLoader,
|
|
29
|
+
EXR3DTextureLoader: MockLoader,
|
|
30
|
+
isFloatLinearSupported: () => false,
|
|
31
|
+
reinterpretType: () => {},
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const fakeTexture = () => ({ image: {}, dispose: vi.fn() });
|
|
36
|
+
|
|
37
|
+
/** Resolves every load immediately with a fake texture, recording the URL. */
|
|
38
|
+
const autoResolveLoads = () => {
|
|
39
|
+
loadAsyncMock.mockImplementation(() => Promise.resolve(fakeTexture()));
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const requestedFiles = () =>
|
|
43
|
+
loadAsyncMock.mock.calls.map((call) => {
|
|
44
|
+
const url = call[0] as string;
|
|
45
|
+
return url.slice(url.lastIndexOf("/") + 1).replace(/\?.*$/, "");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const createAtmosphere = (options?: Partial<AtmosphereOptions>) =>
|
|
49
|
+
new Atmosphere({} as unknown as WebGLRenderer, {
|
|
50
|
+
date: new Date(0),
|
|
51
|
+
...options,
|
|
52
|
+
});
|
|
24
53
|
|
|
25
|
-
const
|
|
54
|
+
const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
26
55
|
|
|
27
56
|
describe("Atmosphere.initTextures", () => {
|
|
28
57
|
beforeEach(() => {
|
|
29
58
|
loadAsyncMock.mockReset();
|
|
30
59
|
});
|
|
31
60
|
|
|
32
|
-
it("
|
|
33
|
-
|
|
61
|
+
it("loads all four textures", async () => {
|
|
62
|
+
autoResolveLoads();
|
|
63
|
+
const atmosphere = createAtmosphere();
|
|
64
|
+
|
|
65
|
+
await atmosphere.initTextures();
|
|
66
|
+
|
|
67
|
+
expect(requestedFiles().sort()).toEqual([
|
|
68
|
+
"higher_order_scattering.exr",
|
|
69
|
+
"irradiance.exr",
|
|
70
|
+
"scattering.exr",
|
|
71
|
+
"transmittance.exr",
|
|
72
|
+
]);
|
|
73
|
+
expect(atmosphere.textures.transmittanceTexture).toBeDefined();
|
|
74
|
+
expect(atmosphere.textures.scatteringTexture).toBeDefined();
|
|
75
|
+
expect(atmosphere.textures.irradianceTexture).toBeDefined();
|
|
76
|
+
expect(atmosphere.textures.higherOrderScatteringTexture).toBeDefined();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("shares in-flight loads across concurrent calls", async () => {
|
|
80
|
+
autoResolveLoads();
|
|
34
81
|
const atmosphere = createAtmosphere();
|
|
35
82
|
|
|
36
83
|
await Promise.all([atmosphere.initTextures(), atmosphere.initTextures()]);
|
|
37
84
|
|
|
38
|
-
expect(loadAsyncMock).toHaveBeenCalledTimes(
|
|
39
|
-
expect(atmosphere.textures).toBeDefined();
|
|
85
|
+
expect(loadAsyncMock).toHaveBeenCalledTimes(4);
|
|
40
86
|
});
|
|
41
87
|
|
|
42
|
-
it("
|
|
43
|
-
|
|
88
|
+
it("does not reload once textures are loaded", async () => {
|
|
89
|
+
autoResolveLoads();
|
|
44
90
|
const atmosphere = createAtmosphere();
|
|
45
91
|
|
|
46
92
|
await atmosphere.initTextures();
|
|
47
93
|
await atmosphere.initTextures();
|
|
48
94
|
|
|
49
|
-
expect(loadAsyncMock).toHaveBeenCalledTimes(
|
|
95
|
+
expect(loadAsyncMock).toHaveBeenCalledTimes(4);
|
|
50
96
|
});
|
|
51
97
|
|
|
52
|
-
it("emits textureLoaded
|
|
53
|
-
|
|
98
|
+
it("emits textureLoaded once per texture", async () => {
|
|
99
|
+
autoResolveLoads();
|
|
54
100
|
const atmosphere = createAtmosphere();
|
|
55
101
|
const onLoaded = vi.fn();
|
|
56
102
|
atmosphere.on("textureLoaded", onLoaded);
|
|
57
103
|
|
|
58
|
-
await
|
|
104
|
+
await atmosphere.initTextures();
|
|
59
105
|
|
|
60
|
-
expect(onLoaded).toHaveBeenCalledTimes(
|
|
106
|
+
expect(onLoaded).toHaveBeenCalledTimes(4);
|
|
61
107
|
});
|
|
62
108
|
|
|
63
|
-
it("
|
|
64
|
-
|
|
65
|
-
loadAsyncMock.
|
|
109
|
+
it("retries only the texture that failed", async () => {
|
|
110
|
+
let failTransmittance = true;
|
|
111
|
+
loadAsyncMock.mockImplementation((url: string) => {
|
|
112
|
+
if (failTransmittance && url.includes("transmittance.exr")) {
|
|
113
|
+
return Promise.reject(new Error("network error"));
|
|
114
|
+
}
|
|
115
|
+
return Promise.resolve(fakeTexture());
|
|
116
|
+
});
|
|
66
117
|
const atmosphere = createAtmosphere();
|
|
67
118
|
|
|
68
119
|
await expect(atmosphere.initTextures()).rejects.toThrow("network error");
|
|
69
|
-
expect(atmosphere.textures).toBeUndefined();
|
|
120
|
+
expect(atmosphere.textures.transmittanceTexture).toBeUndefined();
|
|
121
|
+
expect(atmosphere.textures.scatteringTexture).toBeDefined();
|
|
70
122
|
|
|
123
|
+
failTransmittance = false;
|
|
71
124
|
await atmosphere.initTextures();
|
|
72
125
|
|
|
73
|
-
expect(
|
|
74
|
-
expect(
|
|
126
|
+
expect(atmosphere.textures.transmittanceTexture).toBeDefined();
|
|
127
|
+
expect(
|
|
128
|
+
requestedFiles().filter((f) => f === "transmittance.exr"),
|
|
129
|
+
).toHaveLength(2);
|
|
130
|
+
expect(requestedFiles().filter((f) => f === "scattering.exr")).toHaveLength(
|
|
131
|
+
1,
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("loads from atmosphereAssetsUrl when provided, normalizing slashes", async () => {
|
|
136
|
+
autoResolveLoads();
|
|
137
|
+
const atmosphere = createAtmosphere({
|
|
138
|
+
atmosphereAssetsUrl: "https://example.com/assets/",
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await atmosphere.initTextures();
|
|
142
|
+
|
|
143
|
+
for (const [url] of loadAsyncMock.mock.calls as [string][]) {
|
|
144
|
+
expect(url).toMatch(/^https:\/\/example\.com\/assets\/[a-z_]+\.exr$/);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe("Atmosphere.onTexturesReady", () => {
|
|
150
|
+
beforeEach(() => {
|
|
151
|
+
loadAsyncMock.mockReset();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("does not load anything until a consumer registers", () => {
|
|
155
|
+
autoResolveLoads();
|
|
156
|
+
createAtmosphere();
|
|
157
|
+
|
|
158
|
+
expect(loadAsyncMock).not.toHaveBeenCalled();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("loads only the needed textures", async () => {
|
|
162
|
+
autoResolveLoads();
|
|
163
|
+
const atmosphere = createAtmosphere();
|
|
164
|
+
const callback = vi.fn();
|
|
165
|
+
|
|
166
|
+
atmosphere.onTexturesReady(callback, { transmittance: true });
|
|
167
|
+
await flush();
|
|
168
|
+
|
|
169
|
+
expect(requestedFiles()).toEqual(["transmittance.exr"]);
|
|
170
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
171
|
+
expect(callback.mock.calls[0][0].transmittanceTexture).toBeDefined();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("shares a fetch between consumers needing the same texture", async () => {
|
|
175
|
+
autoResolveLoads();
|
|
176
|
+
const atmosphere = createAtmosphere();
|
|
177
|
+
const first = vi.fn();
|
|
178
|
+
const second = vi.fn();
|
|
179
|
+
|
|
180
|
+
atmosphere.onTexturesReady(first, { transmittance: true });
|
|
181
|
+
atmosphere.onTexturesReady(second, { transmittance: true });
|
|
182
|
+
await flush();
|
|
183
|
+
|
|
184
|
+
expect(loadAsyncMock).toHaveBeenCalledTimes(1);
|
|
185
|
+
expect(first).toHaveBeenCalledTimes(1);
|
|
186
|
+
expect(second).toHaveBeenCalledTimes(1);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("fetches only the missing textures for wider needs", async () => {
|
|
190
|
+
autoResolveLoads();
|
|
191
|
+
const atmosphere = createAtmosphere();
|
|
192
|
+
|
|
193
|
+
atmosphere.onTexturesReady(vi.fn(), { transmittance: true });
|
|
194
|
+
await flush();
|
|
195
|
+
const callback = vi.fn();
|
|
196
|
+
atmosphere.onTexturesReady(callback, {
|
|
197
|
+
transmittance: true,
|
|
198
|
+
scattering: true,
|
|
199
|
+
});
|
|
200
|
+
await flush();
|
|
201
|
+
|
|
202
|
+
expect(requestedFiles()).toEqual(["transmittance.exr", "scattering.exr"]);
|
|
203
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("loads all textures and fires once when needs are omitted", async () => {
|
|
207
|
+
autoResolveLoads();
|
|
208
|
+
const atmosphere = createAtmosphere();
|
|
209
|
+
const callback = vi.fn();
|
|
210
|
+
|
|
211
|
+
atmosphere.onTexturesReady(callback);
|
|
212
|
+
await flush();
|
|
213
|
+
|
|
214
|
+
expect(loadAsyncMock).toHaveBeenCalledTimes(4);
|
|
215
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("waits until every needed texture is ready", async () => {
|
|
219
|
+
const pending = new Map<string, (t: unknown) => void>();
|
|
220
|
+
loadAsyncMock.mockImplementation(
|
|
221
|
+
(url: string) =>
|
|
222
|
+
new Promise((resolve) => {
|
|
223
|
+
pending.set(
|
|
224
|
+
url.slice(url.lastIndexOf("/") + 1).replace(/\?.*$/, ""),
|
|
225
|
+
resolve,
|
|
226
|
+
);
|
|
227
|
+
}),
|
|
228
|
+
);
|
|
229
|
+
const atmosphere = createAtmosphere();
|
|
230
|
+
const callback = vi.fn();
|
|
231
|
+
|
|
232
|
+
atmosphere.onTexturesReady(callback, {
|
|
233
|
+
transmittance: true,
|
|
234
|
+
scattering: true,
|
|
235
|
+
});
|
|
236
|
+
pending.get("transmittance.exr")?.(fakeTexture());
|
|
237
|
+
await flush();
|
|
238
|
+
expect(callback).not.toHaveBeenCalled();
|
|
239
|
+
|
|
240
|
+
pending.get("scattering.exr")?.(fakeTexture());
|
|
241
|
+
await flush();
|
|
242
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("invokes the callback immediately once loaded", async () => {
|
|
246
|
+
autoResolveLoads();
|
|
247
|
+
const atmosphere = createAtmosphere();
|
|
248
|
+
await atmosphere.initTextures();
|
|
249
|
+
const callback = vi.fn();
|
|
250
|
+
|
|
251
|
+
atmosphere.onTexturesReady(callback, { irradiance: true });
|
|
252
|
+
|
|
253
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
254
|
+
expect(loadAsyncMock).toHaveBeenCalledTimes(4);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("logs instead of rejecting when a lazy load fails", async () => {
|
|
258
|
+
const error = new Error("network error");
|
|
259
|
+
loadAsyncMock.mockRejectedValue(error);
|
|
260
|
+
const consoleError = vi
|
|
261
|
+
.spyOn(console, "error")
|
|
262
|
+
.mockImplementation(() => {});
|
|
263
|
+
const atmosphere = createAtmosphere();
|
|
264
|
+
|
|
265
|
+
atmosphere.onTexturesReady(vi.fn(), { transmittance: true });
|
|
266
|
+
|
|
267
|
+
await vi.waitFor(() => {
|
|
268
|
+
expect(consoleError).toHaveBeenCalledWith(
|
|
269
|
+
"Failed to load atmosphere textures:",
|
|
270
|
+
error,
|
|
271
|
+
);
|
|
272
|
+
});
|
|
273
|
+
consoleError.mockRestore();
|
|
75
274
|
});
|
|
76
275
|
});
|