diff --git a/docs/guide/adapters/equirectangular-video.md b/docs/guide/adapters/equirectangular-video.md index b8c40983..e2f61438 100644 --- a/docs/guide/adapters/equirectangular-video.md +++ b/docs/guide/adapters/equirectangular-video.md @@ -89,3 +89,7 @@ const viewer = new Viewer({ }, }); ``` + +::: tip `panoData` support +It is possible to declare the [`panoData`](../config.md#panodata) option if the video does not cover a full sphere. +::: diff --git a/docs/guide/adapters/equirectangular.md b/docs/guide/adapters/equirectangular.md index 2360c5dd..1dc03cff 100644 --- a/docs/guide/adapters/equirectangular.md +++ b/docs/guide/adapters/equirectangular.md @@ -100,7 +100,7 @@ The XMP payload is as follow: 0 0 - 0 + 60 @@ -146,6 +146,26 @@ const viewer = new PhotoSphereViewer.Viewer({ }); ``` +#### Default parameters + +If the image does not have a 2:1 ratio and no XMP data are found and no `panoData` is provided, a best effort is done to display the image without distortion. The exact algorithm is as follow: + +```js +const fullWidth = Math.max(img.width, img.height * 2); +const fullHeight = Math.round(fullWidth / 2); +const croppedX = Math.round((fullWidth - img.width) / 2); +const croppedY = Math.round((fullHeight - img.height) / 2); + +panoData = { + fullWidth: fullWidth, + fullHeight: fullHeight, + croppedWidth: img.width, + croppedHeight: img.height, + croppedX: croppedX, + croppedY: croppedY, +}; +``` + ### Playground Use this demo to find the best values for your image. diff --git a/docs/guide/config.md b/docs/guide/config.md index de3f21eb..5c6ba9aa 100644 --- a/docs/guide/config.md +++ b/docs/guide/config.md @@ -243,10 +243,6 @@ panoData: (image, xmpData) => ({ }); ``` -::: warning -Only the default [equirectangular](./adapters/equirectangular.md) adapter supports `panoData`. -::: - #### `moveSpeed` - type: `double` diff --git a/packages/cubemap-tiles-adapter/src/CubemapTilesAdapter.ts b/packages/cubemap-tiles-adapter/src/CubemapTilesAdapter.ts index 80365ed0..93dd6cdc 100644 --- a/packages/cubemap-tiles-adapter/src/CubemapTilesAdapter.ts +++ b/packages/cubemap-tiles-adapter/src/CubemapTilesAdapter.ts @@ -4,12 +4,16 @@ import { CubemapAdapter, CubemapData, CubemapFaces } from '@photo-sphere-viewer/ import { BoxGeometry, BufferAttribute, Group, Mesh, MeshBasicMaterial, Texture, Vector3 } from 'three'; import { Queue, Task } from '../../shared/Queue'; import { buildDebugTexture, buildErrorMaterial, createWireFrame } from '../../shared/tiles-utils'; -import { CubemapMultiTilesPanorama, CubemapTilesAdapterConfig, CubemapTilesPanorama } from './model'; +import { CubemapMultiTilesPanorama, CubemapTilesAdapterConfig, CubemapTilesPanoData, CubemapTilesPanorama } from './model'; import { CubemapTileConfig, checkPanoramaConfig, getTileConfig, getTileConfigByIndex, isTopOrBottom } from './utils'; type CubemapMesh = Mesh; type CubemapTilesMesh = Mesh; -type CubemapTilesTextureData = TextureData; +type CubemapTilesTextureData = TextureData< + Texture[], + CubemapTilesPanorama | CubemapMultiTilesPanorama, + CubemapTilesPanoData +>; type CubemapTile = { face: number; col: number; @@ -58,7 +62,7 @@ const vertexPosition = new Vector3(); */ export class CubemapTilesAdapter extends AbstractAdapter< CubemapTilesPanorama | CubemapMultiTilesPanorama, - CubemapData, + CubemapTilesPanoData, Texture[], Group > { @@ -156,11 +160,11 @@ export class CubemapTilesAdapter extends AbstractAdapter< return !!panorama.baseUrl; } - override textureCoordsToSphericalCoords(point: PanoramaPosition, data: CubemapData): Position { + override textureCoordsToSphericalCoords(point: PanoramaPosition, data: CubemapTilesPanoData): Position { return this.adapter.textureCoordsToSphericalCoords(point, data); } - override sphericalCoordsToTextureCoords(position: Position, data: CubemapData): PanoramaPosition { + override sphericalCoordsToTextureCoords(position: Position, data: CubemapTilesPanoData): PanoramaPosition { return this.adapter.sphericalCoordsToTextureCoords(position, data); } @@ -182,14 +186,20 @@ export class CubemapTilesAdapter extends AbstractAdapter< return { panorama, - panoData, + panoData: { + ...panoData, + baseData: textureData.panoData, + }, cacheKey: textureData.cacheKey, texture: textureData.texture, }; } else { return { panorama, - panoData, + panoData: { + ...panoData, + baseData: null, + }, cacheKey: panorama.tileUrl('front', 0, 0, 0), texture: null, }; @@ -241,7 +251,7 @@ export class CubemapTilesAdapter extends AbstractAdapter< this.adapter.setTexture(baseMesh, { panorama: textureData.panorama.baseUrl, texture: textureData.texture, - panoData: textureData.panoData, + panoData: textureData.panoData.baseData, }); } else { baseMesh.visible = false; @@ -281,8 +291,8 @@ export class CubemapTilesAdapter extends AbstractAdapter< return; } - const panorama: CubemapTilesPanorama | CubemapMultiTilesPanorama = this.viewer.state.textureData.panorama; - const panoData: CubemapData = this.viewer.state.textureData.panoData; + const panorama = this.viewer.state.textureData.panorama as CubemapTilesPanorama | CubemapMultiTilesPanorama; + const panoData = this.viewer.state.textureData.panoData as CubemapTilesPanoData; const zoomLevel = this.viewer.getZoomLevel(); const tileConfig = getTileConfig(panorama, zoomLevel, { CUBE_SEGMENTS }); @@ -396,7 +406,7 @@ export class CubemapTilesAdapter extends AbstractAdapter< * Applies a new texture to the faces */ private __swapMaterial(tile: CubemapTile, material: MeshBasicMaterial, isError: boolean) { - const panoData = this.viewer.state.textureData.panoData as CubemapData; + const panoData = this.viewer.state.textureData.panoData as CubemapTilesPanoData; const uvs = this.state.geom.getAttribute(ATTR_UV) as BufferAttribute; for (let c = 0; c < tile.config.facesByTile; c++) { diff --git a/packages/cubemap-tiles-adapter/src/model.ts b/packages/cubemap-tiles-adapter/src/model.ts index bec2c76f..fc16fa07 100644 --- a/packages/cubemap-tiles-adapter/src/model.ts +++ b/packages/cubemap-tiles-adapter/src/model.ts @@ -1,4 +1,4 @@ -import type { Cubemap, CubemapAdapterConfig, CubemapPanorama } from '@photo-sphere-viewer/cubemap-adapter'; +import type { Cubemap, CubemapAdapterConfig, CubemapData, CubemapPanorama } from '@photo-sphere-viewer/cubemap-adapter'; /** * Configuration of a tiled cubemap @@ -88,3 +88,7 @@ export type CubemapTilesAdapterConfig = CubemapAdapterConfig & { */ debug?: boolean; }; + +export type CubemapTilesPanoData = CubemapData & { + baseData: CubemapData; +}; diff --git a/packages/equirectangular-tiles-adapter/src/EquirectangularTilesAdapter.ts b/packages/equirectangular-tiles-adapter/src/EquirectangularTilesAdapter.ts index 48f3c302..466a1ed6 100644 --- a/packages/equirectangular-tiles-adapter/src/EquirectangularTilesAdapter.ts +++ b/packages/equirectangular-tiles-adapter/src/EquirectangularTilesAdapter.ts @@ -6,6 +6,7 @@ import { buildDebugTexture, buildErrorMaterial, createWireFrame } from '../../sh import { EquirectangularMultiTilesPanorama, EquirectangularTilesAdapterConfig, + EquirectangularTilesPanoData, EquirectangularTilesPanorama, } from './model'; import { EquirectangularTileConfig, checkPanoramaConfig, getTileConfig, getTileConfigByIndex } from './utils'; @@ -47,7 +48,7 @@ type EquirectangularTilesMesh = Mesh; type EquirectangularTilesTextureData = TextureData< Texture, EquirectangularTilesPanorama | EquirectangularMultiTilesPanorama, - PanoData + EquirectangularTilesPanoData >; type EquirectangularTile = { row: number; @@ -90,7 +91,7 @@ const vertexPosition = new Vector3(); */ export class EquirectangularTilesAdapter extends AbstractAdapter< EquirectangularTilesPanorama | EquirectangularMultiTilesPanorama, - PanoData, + EquirectangularTilesPanoData, Texture, Group > { @@ -198,11 +199,11 @@ export class EquirectangularTilesAdapter extends AbstractAdapter< return !!panorama.baseUrl; } - override textureCoordsToSphericalCoords(point: PanoramaPosition, data: PanoData): Position { + override textureCoordsToSphericalCoords(point: PanoramaPosition, data: EquirectangularTilesPanoData): Position { return this.adapter.textureCoordsToSphericalCoords(point, data); } - override sphericalCoordsToTextureCoords(position: Position, data: PanoData): PanoramaPosition { + override sphericalCoordsToTextureCoords(position: Position, data: EquirectangularTilesPanoData): PanoramaPosition { return this.adapter.sphericalCoordsToTextureCoords(position, data); } @@ -212,41 +213,48 @@ export class EquirectangularTilesAdapter extends AbstractAdapter< ): Promise { checkPanoramaConfig(panorama, this); + const firstTile = getTileConfig(panorama, 0, this); + const panoData: PanoData = { + isEquirectangular: true, + fullWidth: firstTile.width, + fullHeight: firstTile.width / 2, + croppedWidth: firstTile.width, + croppedHeight: firstTile.width / 2, + croppedX: 0, + croppedY: 0, + poseHeading: 0, + posePitch: 0, + poseRoll: 0, + }; + if (panorama.baseUrl) { const textureData = await this.adapter.loadTexture(panorama.baseUrl, loader, panorama.basePanoData, true); return { panorama, - panoData: textureData.panoData, + panoData: { + ...panoData, + baseData: textureData.panoData, + }, cacheKey: textureData.cacheKey, texture: textureData.texture, }; } else { - const firstTile = getTileConfig(panorama, 0, this); - const panoData: PanoData = { - isEquirectangular: true, - fullWidth: firstTile.width, - fullHeight: firstTile.width / 2, - croppedWidth: firstTile.width, - croppedHeight: firstTile.width / 2, - croppedX: 0, - croppedY: 0, - poseHeading: 0, - posePitch: 0, - poseRoll: 0, - }; - + return { panorama, - panoData, + panoData: { + ...panoData, + baseData: null, + }, cacheKey: panorama.tileUrl(0, 0, 0), texture: null, }; } } - createMesh(panoData: PanoData): Group { - const baseMesh = this.adapter.createMesh(panoData); + createMesh(panoData: EquirectangularTilesPanoData): Group { + const baseMesh = this.adapter.createMesh(panoData.baseData ?? panoData); const geometry = new SphereGeometry( CONSTANTS.SPHERE_RADIUS, @@ -303,7 +311,7 @@ export class EquirectangularTilesAdapter extends AbstractAdapter< this.adapter.setTexture(baseMesh, { panorama: textureData.panorama.baseUrl, texture: textureData.texture, - panoData: textureData.panoData, + panoData: textureData.panoData.baseData, }); } else { baseMesh.visible = false; @@ -343,7 +351,7 @@ export class EquirectangularTilesAdapter extends AbstractAdapter< return; } - const panorama: EquirectangularTilesPanorama | EquirectangularMultiTilesPanorama = this.viewer.config.panorama; + const panorama = this.viewer.config.panorama as EquirectangularTilesPanorama | EquirectangularMultiTilesPanorama; const zoomLevel = this.viewer.getZoomLevel(); const tileConfig = getTileConfig(panorama, zoomLevel, this); diff --git a/packages/equirectangular-tiles-adapter/src/model.ts b/packages/equirectangular-tiles-adapter/src/model.ts index 4d9861bc..d1d5ee3e 100644 --- a/packages/equirectangular-tiles-adapter/src/model.ts +++ b/packages/equirectangular-tiles-adapter/src/model.ts @@ -94,3 +94,7 @@ export type EquirectangularTilesAdapterConfig = Omit