Skip to content

Commit ce57e92

Browse files
committed
Merge branch 'main' of https://github.com/pmndrs/xr
2 parents 2f3c73f + 84ea7a1 commit ce57e92

File tree

7 files changed

+46
-15
lines changed

7 files changed

+46
-15
lines changed

docs/handles/screen-handle-components.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,4 @@ Allows you to configure the pan speed and a filter function to disable panning o
5252
## Map Handles
5353
*alias for `MapControls`*
5454

55-
Map handles have the same properties and functionality as the orbit handles but move the camera's transform origin only in the X and Y planes, which is perfect for building applications with a flat map.
55+
Map handles have the same properties and functionality as the orbit handles but move the camera's transform origin only in the X and Z plane, which is perfect for building applications with a flat map.

docs/tutorials/layers.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
---
22
title: Layers
3-
description: How to use display images, videos, and custom renders at high quality on quad, cylinder, and equirectangular shapes?
3+
description: How to use display images, videos, and custom renders at high quality on quad, cylinder, and equirect shapes?
44
nav: 15
55
---
66

7-
Layers allow to render videos, images, and complete scenes with higher performance and higher quality while preserving battery life and latency for quad, cylinder, and equirectangular shapes using the WebXR Layer API. Layers are perfect for use cases that display flat, high-quality content, such as videos, images, and user interfaces. The following example illustrates how to create a layer that renders a video.
7+
Layers allow to render videos, images, and complete scenes with higher performance and higher quality while preserving battery life and latency for quad, cylinder, and equirect shapes using the WebXR Layer API. Layers are perfect for use cases that display flat, high-quality content, such as videos, images, and user interfaces. The following example illustrates how to create a layer that renders a video.
88

99
First, we create a layer at `0, 1.5, -0.5` with a scale of `0.5` that displays a video assigned to `src` and starts that video when clicked.
1010

packages/handle/src/screen/map.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export function defaultMapHandlesScreenCameraApply(
2020
store: StoreApi<ScreenCameraState>,
2121
) {
2222
if (update.pitch != null) {
23-
update.pitch = clamp(update.pitch, 0, Math.PI / 2)
23+
update.pitch = clamp(update.pitch, -Math.PI / 2, 0)
2424
}
2525
store.setState(update)
2626
}
@@ -60,6 +60,8 @@ export class MapHandles {
6060
this.getCamera,
6161
filterForOnePointerLeftClick,
6262
defaultMapHandlesScreenCameraApply,
63+
1,
64+
'xz',
6365
)
6466
this.zoom = new ZoomScreenHandleStore(store, this.getCamera, undefined, defaultMapHandlesScreenCameraApply)
6567
}

packages/react/xr/src/layer.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,16 @@ export type XRLayerProperties = XRLayerOptions &
6767
}
6868

6969
/**
70-
* Component for rendering high quality quad, cylinder, or equirectangular layers inside supported sessions. Also includes a fallback for non-supported sessions.
70+
* Component for rendering high quality quad, cylinder, or equirect layers inside supported sessions. Also includes a fallback for non-supported sessions.
7171
*
7272
* @param props
7373
* #### `src` - Property for displaying images and videos onto the layer. For rendering dynamic content to the layer, leave the `src` empty and put the dynamic (3D) content into the children, so that the layer acts as a render target.
74-
* #### `shape` - Property to configure the shape of the layer ("quad", "cylinder", "equirectangular").
74+
* #### `shape` - Property to configure the shape of the layer ("quad", "cylinder", "equirect").
7575
* #### `layout` - Property to configure the layout of the display content for stereo content ("default", "mono", "stereo-left-right", "stereo-top-bottom").
7676
* #### `centralAngle` - Property to configure the central angle in case the layer shape is a "cylinder".
77-
* #### `centralHorizontalAngle` - Property to configure the central horizontal angle in case the layer shape is "equirectangular".
78-
* #### `upperVerticalAngle` - Property to configure the upper vertical angle in case the layer shape is "equirectangular".
79-
* #### `lowerVerticalAngle` - Property to configure the lower verical angle in case the layer shape is "equirectangular".
77+
* #### `centralHorizontalAngle` - Property to configure the central horizontal angle in case the layer shape is "equirect".
78+
* #### `upperVerticalAngle` - Property to configure the upper vertical angle in case the layer shape is "equirect".
79+
* #### `lowerVerticalAngle` - Property to configure the lower vertical angle in case the layer shape is "equirect".
8080
* #### `chromaticAberrationCorrection` - Property to configure whether chromatic abberration should be corrected by the layer.
8181
* #### `quality` - Property to configure for what type of content the layer should be optimized ("default", "text-optimized", "graphics-optimized").
8282
*/
@@ -239,7 +239,11 @@ export const XRLayerImplementation = forwardRef<
239239
if (layer == null) {
240240
return
241241
}
242-
const layerEntry = (layerEntryRef.current = { layer, renderOrder: renderOrderRef.current })
242+
const layerEntry = (layerEntryRef.current = {
243+
layer,
244+
renderOrder: renderOrderRef.current,
245+
object3D: internalRef.current!,
246+
})
243247
store.addLayerEntry(layerEntry)
244248
if (resolvedSrc instanceof HTMLVideoElement || resolvedSrc instanceof WebGLRenderTarget) {
245249
return () => {

packages/xr/src/layer.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ import { getSpaceFromAncestors } from './space.js'
2020
import { XRState, XRStore } from './store.js'
2121
import { toDOMPointInit } from './utils.js'
2222

23-
export type XRLayerEntry = { renderOrder: number; readonly layer: XRCylinderLayer | XRQuadLayer | XREquirectLayer }
23+
export type XRLayerEntry = {
24+
renderOrder: number
25+
readonly layer: XRCylinderLayer | XRQuadLayer | XREquirectLayer
26+
readonly object3D: Object3D
27+
}
2428

2529
export type XRLayerOptions = Pick<
2630
Partial<XRCylinderLayerInit & XRQuadLayerInit & XREquirectLayerInit>,

packages/xr/src/store.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { XRDevice } from 'iwer'
2-
import { Camera, Object3D, WebXRManager } from 'three'
2+
import { Camera, Object3D, WebXRManager, Vector3 } from 'three'
33
import { StoreApi, createStore } from 'zustand/vanilla'
44
import { XRControllerLayoutLoaderOptions, updateXRControllerState } from './controller/index.js'
55
import { XRHandLoaderOptions } from './hand/index.js'
@@ -427,6 +427,10 @@ declare global {
427427
}
428428
}
429429

430+
//helpers for layer sorting
431+
const cameraWorldPosition = new Vector3()
432+
const tempLayerWorldPosition = new Vector3()
433+
430434
export function createXRStore<T extends XRElementImplementations>(options?: XRStoreOptions<T>): XRStore<T> {
431435
//dom overlay root element creation
432436
const domOverlayRoot =
@@ -678,8 +682,25 @@ export function createXRStore<T extends XRElementImplementations>(options?: XRSt
678682
if (currentLayers == null) {
679683
return
680684
}
681-
//TODO: sort by distance to camera
682-
;(layerEntries as Array<(typeof layerEntries)[number]>).sort((l1, l2) => l1.renderOrder - l2.renderOrder)
685+
//layer sorting
686+
const xrCamera = xrManager.getCamera()
687+
xrCamera.getWorldPosition(cameraWorldPosition)
688+
;(layerEntries as Array<XRLayerEntry>).sort((entryA, entryB) => {
689+
const renderOrderDifference = entryA.renderOrder - entryB.renderOrder
690+
691+
//if renderOrder is the same, sort by distance to camera
692+
if (renderOrderDifference !== 0) {
693+
return renderOrderDifference
694+
}
695+
696+
entryA.object3D.getWorldPosition(tempLayerWorldPosition)
697+
const distA_sq = tempLayerWorldPosition.distanceToSquared(cameraWorldPosition)
698+
699+
entryB.object3D.getWorldPosition(tempLayerWorldPosition)
700+
const distB_sq = tempLayerWorldPosition.distanceToSquared(cameraWorldPosition)
701+
702+
return distB_sq - distA_sq
703+
})
683704
let changed = false
684705
const layers = layerEntries.map<XRLayer>(({ layer }, i) => {
685706
if (layer != currentLayers[i]) {

packages/xr/src/vanilla/layer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export class XRLayer extends Mesh<BufferGeometry, MeshBasicMaterial> {
6969
this.cleanup = () => {}
7070
return
7171
}
72-
const layerEntry = (this.layerEntry = { layer, renderOrder: this.layerRenderOrder })
72+
const layerEntry = (this.layerEntry = { layer, renderOrder: this.layerRenderOrder, object3D: this })
7373
store.addLayerEntry(this.layerEntry)
7474
if (options.src instanceof HTMLVideoElement || options.src instanceof WebGLRenderTarget) {
7575
this.cleanup = () => this.store.removeLayerEntry(layerEntry)

0 commit comments

Comments
 (0)