@ericchen1990/pano-view 2.0.0 → 2.1.0
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/README.md +90 -28
- package/dist/index.cjs +84 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +31 -7
- package/dist/index.d.ts +31 -7
- package/dist/index.mjs +123 -60
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,10 +7,13 @@ Try the components in the [live playground](https://pano-view-playground.vercel.
|
|
|
7
7
|
## krpano-compatible tile output
|
|
8
8
|
|
|
9
9
|
Use this package when you want a React-native viewer around equirectangular
|
|
10
|
-
images or an existing krpano cube-tile pyramid.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
images or an existing krpano cube-tile pyramid. Copy the krpano `<cube>` `url`
|
|
11
|
+
and `multires` attributes into `Tile`'s `urlTemplate` and `multires`, and copy
|
|
12
|
+
`<preview url>` into `previewUrl`; krpano's on-disk naming is not the same as
|
|
13
|
+
this package's defaults, so omitting them loads the wrong tiles or preview.
|
|
14
|
+
Rendering, controls, hotspots, and scene transitions stay in your React
|
|
15
|
+
application. This package is compatible with that tile output format but is
|
|
16
|
+
not affiliated with krpano.
|
|
14
17
|
|
|
15
18
|
## Install
|
|
16
19
|
|
|
@@ -120,7 +123,7 @@ export function ControlledExample() {
|
|
|
120
123
|
style={{ height: 560 }}
|
|
121
124
|
>
|
|
122
125
|
<AutoRotate enabled speed={18} acceleration={18} startDelay={1_000} />
|
|
123
|
-
<Sphere src="/panoramas/room.webp" />
|
|
126
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
124
127
|
</PanoViewer>
|
|
125
128
|
</>
|
|
126
129
|
);
|
|
@@ -151,6 +154,12 @@ The canvas cursor defaults to `grab`, becomes `grabbing` while dragging with the
|
|
|
151
154
|
|
|
152
155
|
`Sphere` expects a 2:1 equirectangular image. Use `yawOffset` when the source's forward direction needs horizontal adjustment.
|
|
153
156
|
|
|
157
|
+
`previewUrl` is required and shows a low-resolution 2:1 image while `src` loads.
|
|
158
|
+
Relative paths resolve against the directory of `src`; root-absolute and
|
|
159
|
+
`http(s)` URLs are used as-is. For krpano sphere scenes, copy
|
|
160
|
+
[`<preview url>`](https://krpano.com/docu/xml/#preview) into `previewUrl`. That
|
|
161
|
+
preview must be equirectangular, not a cube strip.
|
|
162
|
+
|
|
154
163
|
```tsx
|
|
155
164
|
"use client";
|
|
156
165
|
|
|
@@ -159,7 +168,10 @@ import { PanoViewer, Sphere } from "@ericchen1990/pano-view";
|
|
|
159
168
|
export function SphereExample() {
|
|
160
169
|
return (
|
|
161
170
|
<PanoViewer style={{ width: "100%", height: 560 }}>
|
|
162
|
-
<Sphere
|
|
171
|
+
<Sphere
|
|
172
|
+
src="/panoramas/room.webp"
|
|
173
|
+
previewUrl="preview.webp"
|
|
174
|
+
/>
|
|
163
175
|
</PanoViewer>
|
|
164
176
|
);
|
|
165
177
|
}
|
|
@@ -240,13 +252,13 @@ that are not same-origin should set `crossOrigin` (usually `"anonymous"`).
|
|
|
240
252
|
|
|
241
253
|
## Tile
|
|
242
254
|
|
|
243
|
-
`Tile` renders six inward-facing cube faces and loads only tiles around the current view.
|
|
255
|
+
`Tile` renders six inward-facing cube faces and loads only tiles around the current view. When `urlTemplate` is omitted, paths relative to `baseUrl` use this package default:
|
|
244
256
|
|
|
245
257
|
```text
|
|
246
|
-
tiles/
|
|
258
|
+
tiles/%s/l%l/%v/l%l_%s_%h_%v.webp
|
|
247
259
|
```
|
|
248
260
|
|
|
249
|
-
Faces are `f`, `r`, `b`, `l`, `u`, and `d`; rows and columns are 1-based.
|
|
261
|
+
which expands to `tiles/{face}/l{level}/{row}/l{level}_{face}_{col}_{row}.webp`. Faces are `f`, `r`, `b`, `l`, `u`, and `d`; rows and columns are 1-based.
|
|
250
262
|
|
|
251
263
|
```tsx
|
|
252
264
|
import { PanoViewer, Tile } from "@ericchen1990/pano-view";
|
|
@@ -257,33 +269,81 @@ export function TileExample() {
|
|
|
257
269
|
<Tile
|
|
258
270
|
baseUrl="https://cdn.example.com/panoramas/room"
|
|
259
271
|
multires="512,500,1000,2000"
|
|
272
|
+
previewUrl="previews/cube-vertical.webp"
|
|
260
273
|
/>
|
|
261
274
|
</PanoViewer>
|
|
262
275
|
);
|
|
263
276
|
}
|
|
264
277
|
```
|
|
265
278
|
|
|
266
|
-
|
|
279
|
+
krpano cube-tile output does not follow that default. Read `url`, `multires`,
|
|
280
|
+
and the preview path from the krpano XML
|
|
281
|
+
([`<cube>`](https://krpano.com/docu/xml/#image.cube),
|
|
282
|
+
[`<preview>`](https://krpano.com/docu/xml/#preview)) and pass them through. If
|
|
283
|
+
those values are missing or do not match the files on disk, tiles resolve to
|
|
284
|
+
the wrong face, level, or row/column and the panorama looks scrambled.
|
|
285
|
+
|
|
286
|
+
| krpano | `Tile` / `TileScene` prop |
|
|
287
|
+
|---|---|
|
|
288
|
+
| `<cube url>` | `urlTemplate` |
|
|
289
|
+
| `<cube multires>` | `multires` |
|
|
290
|
+
| `<preview url>` | `previewUrl` |
|
|
291
|
+
| `<preview striporder>` | `previewFaceOrder` |
|
|
292
|
+
|
|
293
|
+
`Sphere` uses the same `<preview url>` → `previewUrl` mapping; that file must be a 2:1 equirectangular image, not a cube strip.
|
|
294
|
+
|
|
295
|
+
krpano's documented short syntax:
|
|
296
|
+
|
|
297
|
+
```xml
|
|
298
|
+
<preview url="preview.jpg" />
|
|
299
|
+
<image>
|
|
300
|
+
<cube url="pano_%s_%l_%v_%h.jpg"
|
|
301
|
+
multires="512,1024,2048,4096"
|
|
302
|
+
/>
|
|
303
|
+
</image>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
maps to:
|
|
307
|
+
|
|
308
|
+
```tsx
|
|
309
|
+
<Tile
|
|
310
|
+
baseUrl="/panoramas/room"
|
|
311
|
+
urlTemplate="pano_%s_%l_%v_%h.jpg"
|
|
312
|
+
multires="512,1024,2048,4096"
|
|
313
|
+
previewUrl="preview.jpg"
|
|
314
|
+
/>
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
A MAKE PANO (MULTIRES) folder layout is often
|
|
318
|
+
`tiles/%s/l%l/%v/l%l_%s_%v_%h.jpg` with `preview.jpg` at the scene root. That
|
|
319
|
+
filename is `%v_%h` (row then column). This package's default tile template is
|
|
320
|
+
`%h_%v` (column then row). Copy the XML values instead of assuming the two
|
|
321
|
+
conventions match.
|
|
322
|
+
|
|
323
|
+
The first `multires` value is the tile size. Remaining values are ascending cube-face sizes for `l1`, `l2`, and later levels. `previewUrl` is required. Relative paths resolve against `baseUrl`; root-absolute and `http(s)` URLs are used as-is. A typical krpano MAKE PANO (MULTIRES) scene looks like this:
|
|
267
324
|
|
|
268
325
|
```tsx
|
|
269
326
|
<Tile
|
|
270
327
|
baseUrl="/panoramas/room"
|
|
328
|
+
urlTemplate="tiles/%s/l%l/%v/l%l_%s_%v_%h.jpg"
|
|
271
329
|
multires="512,1000,2000"
|
|
272
|
-
previewUrl="
|
|
330
|
+
previewUrl="preview.jpg"
|
|
273
331
|
previewFaceOrder={["l", "f", "r", "b", "u", "d"]}
|
|
274
332
|
/>
|
|
275
333
|
```
|
|
276
334
|
|
|
277
|
-
`previewFaceOrder` must list the six face codes in their top-to-bottom atlas order: `f`, `r`, `b`, `l`, `u`, and `d`.
|
|
335
|
+
`previewFaceOrder` must list the six face codes in their top-to-bottom atlas order: `f`, `r`, `b`, `l`, `u`, and `d`. The preview image is a vertical 1×6 cubestrip. krpano `thumb.jpg` is a small thumbnail and is not this atlas.
|
|
278
336
|
|
|
279
337
|
During rapid rotation or zoom, loaded tiles remain visible while newly visible tiles use their parent level or the preview as a local fallback.
|
|
280
338
|
|
|
281
|
-
|
|
339
|
+
For layouts that are not krpano XML, override the default with a placeholder
|
|
340
|
+
template or a resolver. Both return paths relative to `baseUrl`:
|
|
282
341
|
|
|
283
342
|
```tsx
|
|
284
343
|
<Tile
|
|
285
344
|
baseUrl="/panoramas/room"
|
|
286
345
|
multires={{ tileSize: 512, levels: [500, 1000, 2000] }}
|
|
346
|
+
previewUrl="previews/cube-vertical.webp"
|
|
287
347
|
urlTemplate="assets/%s/%l/%v_%h.webp"
|
|
288
348
|
resolveTileUrl={({ face, level, row, col }) =>
|
|
289
349
|
`api/tile/${face}/${level}/${row}/${col}`
|
|
@@ -304,7 +364,7 @@ a single non-stereo cube panorama.
|
|
|
304
364
|
## Scenes
|
|
305
365
|
|
|
306
366
|
`Scenes` switches controlled sphere and cube-tile scenes with GPU-only
|
|
307
|
-
snapshot blending. The target scene loads its sphere
|
|
367
|
+
snapshot blending. The target scene loads its sphere or tile preview first;
|
|
308
368
|
once ready, the current framebuffer becomes a temporary GPU texture, its source
|
|
309
369
|
textures are released, and the target scene blends in. This avoids holding two
|
|
310
370
|
high-resolution tile scenes in WebGL memory at once.
|
|
@@ -317,12 +377,14 @@ import {
|
|
|
317
377
|
} from "@ericchen1990/pano-view";
|
|
318
378
|
|
|
319
379
|
const scenes: Scene[] = [
|
|
320
|
-
{ id: "lobby", type: "sphere", src: "/panoramas/lobby.webp" },
|
|
380
|
+
{ id: "lobby", type: "sphere", src: "/panoramas/lobby.webp", previewUrl: "preview.webp" },
|
|
321
381
|
{
|
|
322
382
|
id: "terrace",
|
|
323
383
|
type: "tile",
|
|
324
384
|
baseUrl: "/panoramas/terrace",
|
|
325
385
|
multires: "512,1000,2000",
|
|
386
|
+
urlTemplate: "tiles/%s/l%l/%v/l%l_%s_%v_%h.webp",
|
|
387
|
+
previewUrl: "previews/cube-vertical.webp",
|
|
326
388
|
},
|
|
327
389
|
];
|
|
328
390
|
|
|
@@ -368,7 +430,7 @@ to leave the source unfiltered.
|
|
|
368
430
|
```tsx
|
|
369
431
|
<PanoViewer style={{ height: 560 }}>
|
|
370
432
|
<PanoFilter preset="pencil" intensity={0.85} />
|
|
371
|
-
<Sphere src="/panoramas/room.webp" />
|
|
433
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
372
434
|
<ImageHotspot
|
|
373
435
|
id="door"
|
|
374
436
|
position={{ yaw: 24, pitch: -6 }}
|
|
@@ -402,7 +464,7 @@ Auto-rotation is off by default. Render `AutoRotate` inside `PanoViewer` to enab
|
|
|
402
464
|
```tsx
|
|
403
465
|
<PanoViewer style={{ height: 560 }}>
|
|
404
466
|
<AutoRotate enabled speed={12} acceleration={6} startDelay={2_000} />
|
|
405
|
-
<Sphere src="/panoramas/room.webp" />
|
|
467
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
406
468
|
</PanoViewer>
|
|
407
469
|
```
|
|
408
470
|
|
|
@@ -451,7 +513,7 @@ export function GyroExample() {
|
|
|
451
513
|
touchMode="full"
|
|
452
514
|
onDenied={() => setEnabled(false)}
|
|
453
515
|
/>
|
|
454
|
-
<Sphere src="/panoramas/room.webp" />
|
|
516
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
455
517
|
</PanoViewer>
|
|
456
518
|
</>
|
|
457
519
|
);
|
|
@@ -508,7 +570,7 @@ export function WebVRExample() {
|
|
|
508
570
|
mobileVr
|
|
509
571
|
onEnterVR={(mode) => console.log("Entered", mode)}
|
|
510
572
|
/>
|
|
511
|
-
<Sphere src="/panoramas/room.webp" />
|
|
573
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
512
574
|
</PanoViewer>
|
|
513
575
|
);
|
|
514
576
|
}
|
|
@@ -551,7 +613,7 @@ interactive through the default WebXR controller rays.
|
|
|
551
613
|
}}
|
|
552
614
|
style={{ height: 560 }}
|
|
553
615
|
>
|
|
554
|
-
<Sphere src="/panoramas/room.webp" />
|
|
616
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
555
617
|
</PanoViewer>
|
|
556
618
|
```
|
|
557
619
|
|
|
@@ -649,7 +711,7 @@ export function PlacementExample() {
|
|
|
649
711
|
onPanoramaPointerMove={({ position }) => console.log("move", position)}
|
|
650
712
|
style={{ height: 560 }}
|
|
651
713
|
>
|
|
652
|
-
<Sphere src="/panoramas/room.webp" />
|
|
714
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
653
715
|
</PanoViewer>
|
|
654
716
|
);
|
|
655
717
|
}
|
|
@@ -690,7 +752,7 @@ import {
|
|
|
690
752
|
onAutoRotateOneRound={() => console.log("full turn")}
|
|
691
753
|
/>
|
|
692
754
|
<AutoRotate enabled />
|
|
693
|
-
<Sphere src="/panoramas/room.webp" />
|
|
755
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
694
756
|
</PanoViewer>;
|
|
695
757
|
```
|
|
696
758
|
|
|
@@ -723,7 +785,7 @@ separator between them.
|
|
|
723
785
|
|
|
724
786
|
```tsx
|
|
725
787
|
<PanoViewer style={{ height: 560 }}>
|
|
726
|
-
<Sphere src="/panoramas/room.webp" />
|
|
788
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
727
789
|
</PanoViewer>
|
|
728
790
|
```
|
|
729
791
|
|
|
@@ -731,7 +793,7 @@ Disable the default menu (restore the browser menu, or mount your own):
|
|
|
731
793
|
|
|
732
794
|
```tsx
|
|
733
795
|
<PanoViewer contextMenu={false}>
|
|
734
|
-
<Sphere src="/panoramas/room.webp" />
|
|
796
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
735
797
|
</PanoViewer>
|
|
736
798
|
```
|
|
737
799
|
|
|
@@ -739,7 +801,7 @@ Tune appearance while keeping the default items:
|
|
|
739
801
|
|
|
740
802
|
```tsx
|
|
741
803
|
<PanoViewer contextMenu={{ appearance: { opacity: 0.92, borderRadius: 8 } }}>
|
|
742
|
-
<Sphere src="/panoramas/room.webp" />
|
|
804
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
743
805
|
</PanoViewer>
|
|
744
806
|
```
|
|
745
807
|
|
|
@@ -766,7 +828,7 @@ only add your own:
|
|
|
766
828
|
}}
|
|
767
829
|
style={{ height: 560 }}
|
|
768
830
|
>
|
|
769
|
-
<Sphere src="/panoramas/room.webp" />
|
|
831
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
770
832
|
</PanoViewer>
|
|
771
833
|
```
|
|
772
834
|
|
|
@@ -800,7 +862,7 @@ reimplementing fullscreen enter/exit state:
|
|
|
800
862
|
}}
|
|
801
863
|
style={{ height: 560 }}
|
|
802
864
|
>
|
|
803
|
-
<Sphere src="/panoramas/room.webp" />
|
|
865
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
804
866
|
</PanoViewer>
|
|
805
867
|
```
|
|
806
868
|
|
|
@@ -931,7 +993,7 @@ are angular degrees, so they remain independent of the canvas resolution.
|
|
|
931
993
|
import { ImageHotspot, PanoViewer, Sphere } from "@ericchen1990/pano-view";
|
|
932
994
|
|
|
933
995
|
<PanoViewer style={{ height: 560 }}>
|
|
934
|
-
<Sphere src="/panoramas/room.webp" />
|
|
996
|
+
<Sphere src="/panoramas/room.webp" previewUrl="preview.webp" />
|
|
935
997
|
<ImageHotspot
|
|
936
998
|
id="gallery"
|
|
937
999
|
ariaLabel="Open gallery"
|
package/dist/index.cjs
CHANGED
|
@@ -6364,6 +6364,27 @@ var import_fiber15 = require("@react-three/fiber");
|
|
|
6364
6364
|
var import_react31 = require("react");
|
|
6365
6365
|
var import_three9 = require("three");
|
|
6366
6366
|
|
|
6367
|
+
// src/asset-url.ts
|
|
6368
|
+
function isAbsoluteAssetUrl(url) {
|
|
6369
|
+
return url.startsWith("/") || url.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(url);
|
|
6370
|
+
}
|
|
6371
|
+
function resolveRelativeAssetUrl(baseUrl, relativePath) {
|
|
6372
|
+
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
6373
|
+
const normalizedPath = relativePath.replace(/^\/+/, "");
|
|
6374
|
+
return normalizedBaseUrl ? `${normalizedBaseUrl}/${normalizedPath}` : normalizedPath;
|
|
6375
|
+
}
|
|
6376
|
+
function resolveAssetUrl(baseUrl, path) {
|
|
6377
|
+
return isAbsoluteAssetUrl(path) ? path : resolveRelativeAssetUrl(baseUrl, path);
|
|
6378
|
+
}
|
|
6379
|
+
function resolveUrlAgainstFile(fileUrl, path) {
|
|
6380
|
+
if (isAbsoluteAssetUrl(path)) {
|
|
6381
|
+
return path;
|
|
6382
|
+
}
|
|
6383
|
+
const slash = fileUrl.lastIndexOf("/");
|
|
6384
|
+
const directory = slash === -1 ? "" : fileUrl.slice(0, slash);
|
|
6385
|
+
return resolveRelativeAssetUrl(directory, path);
|
|
6386
|
+
}
|
|
6387
|
+
|
|
6367
6388
|
// src/pano-filter/pano-basic-material.tsx
|
|
6368
6389
|
var import_react30 = require("react");
|
|
6369
6390
|
|
|
@@ -6580,25 +6601,22 @@ function PanoBasicMaterial({
|
|
|
6580
6601
|
|
|
6581
6602
|
// src/sphere.tsx
|
|
6582
6603
|
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
6583
|
-
function
|
|
6584
|
-
src,
|
|
6585
|
-
yawOffset = 0,
|
|
6586
|
-
onLoad,
|
|
6587
|
-
onError,
|
|
6588
|
-
visible = true
|
|
6589
|
-
}) {
|
|
6590
|
-
const gl = (0, import_fiber15.useThree)((state) => state.gl);
|
|
6604
|
+
function useEquirectTexture(url, anisotropy, generateMipmaps, onLoad, onError) {
|
|
6591
6605
|
const [texture, setTexture] = (0, import_react31.useState)(null);
|
|
6592
6606
|
const onLoadRef = (0, import_react31.useRef)(onLoad);
|
|
6593
6607
|
const onErrorRef = (0, import_react31.useRef)(onError);
|
|
6594
6608
|
onLoadRef.current = onLoad;
|
|
6595
6609
|
onErrorRef.current = onError;
|
|
6596
6610
|
(0, import_react31.useEffect)(() => {
|
|
6611
|
+
if (!url) {
|
|
6612
|
+
setTexture(null);
|
|
6613
|
+
return;
|
|
6614
|
+
}
|
|
6597
6615
|
let active = true;
|
|
6598
6616
|
const loader = new import_three9.TextureLoader();
|
|
6599
6617
|
setTexture(null);
|
|
6600
6618
|
loader.load(
|
|
6601
|
-
|
|
6619
|
+
url,
|
|
6602
6620
|
(loadedTexture) => {
|
|
6603
6621
|
if (!active) {
|
|
6604
6622
|
loadedTexture.dispose();
|
|
@@ -6608,10 +6626,10 @@ function Sphere({
|
|
|
6608
6626
|
loadedTexture.wrapS = import_three9.RepeatWrapping;
|
|
6609
6627
|
loadedTexture.repeat.x = -1;
|
|
6610
6628
|
loadedTexture.offset.x = 1;
|
|
6611
|
-
loadedTexture.anisotropy =
|
|
6629
|
+
loadedTexture.anisotropy = anisotropy;
|
|
6612
6630
|
loadedTexture.magFilter = import_three9.LinearFilter;
|
|
6613
|
-
loadedTexture.minFilter = import_three9.LinearMipmapLinearFilter;
|
|
6614
|
-
loadedTexture.generateMipmaps =
|
|
6631
|
+
loadedTexture.minFilter = generateMipmaps ? import_three9.LinearMipmapLinearFilter : import_three9.LinearFilter;
|
|
6632
|
+
loadedTexture.generateMipmaps = generateMipmaps;
|
|
6615
6633
|
loadedTexture.needsUpdate = true;
|
|
6616
6634
|
setTexture(loadedTexture);
|
|
6617
6635
|
onLoadRef.current?.(loadedTexture);
|
|
@@ -6619,6 +6637,7 @@ function Sphere({
|
|
|
6619
6637
|
void 0,
|
|
6620
6638
|
(error) => {
|
|
6621
6639
|
if (active) {
|
|
6640
|
+
setTexture(null);
|
|
6622
6641
|
onErrorRef.current?.(error);
|
|
6623
6642
|
}
|
|
6624
6643
|
}
|
|
@@ -6626,14 +6645,58 @@ function Sphere({
|
|
|
6626
6645
|
return () => {
|
|
6627
6646
|
active = false;
|
|
6628
6647
|
};
|
|
6629
|
-
}, [
|
|
6648
|
+
}, [anisotropy, generateMipmaps, url]);
|
|
6630
6649
|
(0, import_react31.useEffect)(
|
|
6631
6650
|
() => () => {
|
|
6632
6651
|
texture?.dispose();
|
|
6633
6652
|
},
|
|
6634
6653
|
[texture]
|
|
6635
6654
|
);
|
|
6636
|
-
|
|
6655
|
+
return texture;
|
|
6656
|
+
}
|
|
6657
|
+
function Sphere({
|
|
6658
|
+
src,
|
|
6659
|
+
previewUrl,
|
|
6660
|
+
yawOffset = 0,
|
|
6661
|
+
onReady,
|
|
6662
|
+
onLoad,
|
|
6663
|
+
onError,
|
|
6664
|
+
onPreviewError,
|
|
6665
|
+
visible = true
|
|
6666
|
+
}) {
|
|
6667
|
+
const gl = (0, import_fiber15.useThree)((state) => state.gl);
|
|
6668
|
+
const anisotropy = gl.capabilities.getMaxAnisotropy();
|
|
6669
|
+
const resolvedPreviewUrl = (0, import_react31.useMemo)(
|
|
6670
|
+
() => resolveUrlAgainstFile(src, previewUrl),
|
|
6671
|
+
[previewUrl, src]
|
|
6672
|
+
);
|
|
6673
|
+
const texture = useEquirectTexture(
|
|
6674
|
+
src,
|
|
6675
|
+
anisotropy,
|
|
6676
|
+
true,
|
|
6677
|
+
onLoad,
|
|
6678
|
+
onError
|
|
6679
|
+
);
|
|
6680
|
+
const previewTexture = useEquirectTexture(
|
|
6681
|
+
texture ? null : resolvedPreviewUrl,
|
|
6682
|
+
anisotropy,
|
|
6683
|
+
false,
|
|
6684
|
+
void 0,
|
|
6685
|
+
onPreviewError
|
|
6686
|
+
);
|
|
6687
|
+
const displayTexture = texture ?? previewTexture;
|
|
6688
|
+
const readyRef = (0, import_react31.useRef)(false);
|
|
6689
|
+
(0, import_react31.useEffect)(() => {
|
|
6690
|
+
readyRef.current = false;
|
|
6691
|
+
}, [resolvedPreviewUrl, src]);
|
|
6692
|
+
(0, import_react31.useEffect)(() => {
|
|
6693
|
+
if (!displayTexture || readyRef.current) {
|
|
6694
|
+
return;
|
|
6695
|
+
}
|
|
6696
|
+
readyRef.current = true;
|
|
6697
|
+
onReady?.();
|
|
6698
|
+
}, [displayTexture, onReady]);
|
|
6699
|
+
if (!displayTexture) {
|
|
6637
6700
|
return null;
|
|
6638
6701
|
}
|
|
6639
6702
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
@@ -6646,7 +6709,7 @@ function Sphere({
|
|
|
6646
6709
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
6647
6710
|
PanoBasicMaterial,
|
|
6648
6711
|
{
|
|
6649
|
-
map:
|
|
6712
|
+
map: displayTexture,
|
|
6650
6713
|
side: import_three9.BackSide,
|
|
6651
6714
|
toneMapped: false
|
|
6652
6715
|
}
|
|
@@ -8436,13 +8499,12 @@ function parseMultires(multires) {
|
|
|
8436
8499
|
);
|
|
8437
8500
|
return { tileSize, levels };
|
|
8438
8501
|
}
|
|
8502
|
+
var DEFAULT_TILE_URL_TEMPLATE = "tiles/%s/l%l/%v/l%l_%s_%h_%v.webp";
|
|
8439
8503
|
function buildDefaultTileUrlTemplate() {
|
|
8440
|
-
return
|
|
8504
|
+
return DEFAULT_TILE_URL_TEMPLATE;
|
|
8441
8505
|
}
|
|
8442
8506
|
function resolveRelativeTileUrl(baseUrl, relativePath) {
|
|
8443
|
-
|
|
8444
|
-
const normalizedPath = relativePath.replace(/^\/+/, "");
|
|
8445
|
-
return normalizedBaseUrl ? `${normalizedBaseUrl}/${normalizedPath}` : normalizedPath;
|
|
8507
|
+
return resolveRelativeAssetUrl(baseUrl, relativePath);
|
|
8446
8508
|
}
|
|
8447
8509
|
function resolveTemplateUrl(template, address) {
|
|
8448
8510
|
return template.replace(/%s/g, address.face).replace(/%l/g, String(address.level)).replace(/%(0*)([hxucvywr])/g, (_match, zeroes, placeholder) => {
|
|
@@ -8808,7 +8870,7 @@ function Tile({
|
|
|
8808
8870
|
[levels, tileSize]
|
|
8809
8871
|
);
|
|
8810
8872
|
const anisotropy = gl.capabilities.getMaxAnisotropy();
|
|
8811
|
-
const effectivePreviewUrl =
|
|
8873
|
+
const effectivePreviewUrl = resolveAssetUrl(baseUrl, previewUrl);
|
|
8812
8874
|
const previewTexture = usePreviewTexture(
|
|
8813
8875
|
effectivePreviewUrl,
|
|
8814
8876
|
anisotropy,
|
|
@@ -9006,9 +9068,10 @@ function SceneSource({
|
|
|
9006
9068
|
Sphere,
|
|
9007
9069
|
{
|
|
9008
9070
|
src: scene.src,
|
|
9071
|
+
previewUrl: scene.previewUrl,
|
|
9009
9072
|
yawOffset: scene.yawOffset,
|
|
9010
9073
|
visible,
|
|
9011
|
-
|
|
9074
|
+
onReady,
|
|
9012
9075
|
onError
|
|
9013
9076
|
}
|
|
9014
9077
|
);
|