Skip to content

Commit 73d83d4

Browse files
committed
Add initial glasses ui library and sample.
1 parent a17ab18 commit 73d83d4

23 files changed

Lines changed: 1792 additions & 13 deletions

rollup.config.js

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -129,19 +129,19 @@ export default [
129129
},
130130
{
131131
input: Object.fromEntries(
132-
globSync('src/addons/**/*.{js,ts}', {ignore: 'src/addons/**/cli/**'}).map(
133-
(file) => [
134-
// This removes `src/` as well as the file extension from
135-
// each file, so e.g. src/nested/foo.js becomes nested/foo
136-
path.relative(
137-
'src',
138-
file.slice(0, file.length - path.extname(file).length)
139-
),
140-
// This expands the relative paths to absolute paths, so
141-
// e.g. src/nested/foo becomes /project/src/nested/foo.js
142-
fileURLToPath(new URL(file, import.meta.url)),
143-
]
144-
)
132+
globSync('src/addons/**/*.{js,ts}', {
133+
ignore: ['src/addons/**/cli/**', 'src/addons/**/*.d.ts'],
134+
}).map((file) => [
135+
// This removes `src/` as well as the file extension from
136+
// each file, so e.g. src/nested/foo.js becomes nested/foo
137+
path.relative(
138+
'src',
139+
file.slice(0, file.length - path.extname(file).length)
140+
),
141+
// This expands the relative paths to absolute paths, so
142+
// e.g. src/nested/foo becomes /project/src/nested/foo.js
143+
fileURLToPath(new URL(file, import.meta.url)),
144+
])
145145
),
146146
external: [...externalPackages, ...xrblocksPackages],
147147
output: {
@@ -196,4 +196,36 @@ export default [
196196
}),
197197
],
198198
})),
199+
// Enable demo projects (excluding those with a custom build system) to use TypeScript
200+
// and import it in their index.html via by referencing, e.g. `./build/main.js`.
201+
...globSync('samples/**/*.ts', {
202+
ignore: ['samples/**/node_modules/**', 'samples/**/build/**'],
203+
}).map((file) => ({
204+
input: file,
205+
external: () => true,
206+
output: {
207+
file: path.join(
208+
path.dirname(file),
209+
'build',
210+
path.basename(file).replace(/\.ts$/, '.js')
211+
),
212+
format: 'esm',
213+
},
214+
plugins: [
215+
typescript({
216+
tsconfig: false,
217+
include: [file],
218+
compilerOptions: {
219+
target: 'ES2022',
220+
module: 'ESNext',
221+
moduleResolution: 'bundler',
222+
esModuleInterop: true,
223+
forceConsistentCasingInFileNames: true,
224+
strict: true,
225+
skipLibCheck: true,
226+
declaration: false,
227+
},
228+
}),
229+
],
230+
})),
199231
];

samples/glasses-ui/index.html

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<title>Glasses UI | XR Blocks</title>
5+
<meta charset="utf-8" />
6+
<meta
7+
name="viewport"
8+
content="width=device-width, initial-scale=1.0, user-scalable=no"
9+
/>
10+
<link
11+
type="text/css"
12+
rel="stylesheet"
13+
href="https://cdn.jsdelivr.net/gh/google/xrblocks@main/samples/main.css"
14+
/>
15+
<script src="https://cdn.tailwindcss.com"></script>
16+
<script>
17+
window.litDisableBundleWarning = true;
18+
</script>
19+
<style>
20+
body {
21+
position: relative;
22+
font-family: 'Roboto', sans-serif;
23+
}
24+
</style>
25+
<script type="importmap">
26+
{
27+
"imports": {
28+
"three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
29+
"three/": "https://cdn.jsdelivr.net/npm/three@0.182.0/",
30+
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
31+
"lit": "https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js",
32+
"lit/": "https://esm.run/lit@3/",
33+
"@pmndrs/uikit": "https://cdn.jsdelivr.net/npm/@pmndrs/uikit@1.0.60/dist/index.min.js",
34+
"@pmndrs/uikit-pub-sub": "https://cdn.jsdelivr.net/npm/@pmndrs/uikit-pub-sub@1.0.60/dist/index.min.js",
35+
"@pmndrs/msdfonts": "https://cdn.jsdelivr.net/npm/@pmndrs/msdfonts@1.0.60/dist/index.min.js",
36+
"@preact/signals-core": "https://cdn.jsdelivr.net/npm/@preact/signals-core@1.12.1/dist/signals-core.mjs",
37+
"yoga-layout/load": "https://cdn.jsdelivr.net/npm/yoga-layout@3.2.1/dist/src/load.js",
38+
"xrblocks": "../../build/xrblocks.js",
39+
"xrblocks/addons/": "../../build/addons/"
40+
}
41+
}
42+
</script>
43+
</head>
44+
45+
<body>
46+
<script type="module" src="build/main.js"></script>
47+
</body>
48+
</html>

samples/glasses-ui/main.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import 'xrblocks/addons/simulator/SimulatorAddons.js';
2+
3+
import {reversePainterSortStable} from '@pmndrs/uikit';
4+
import * as THREE from 'three';
5+
import * as xb from 'xrblocks';
6+
import {SystemUI} from 'xrblocks/addons/glasses/ui/SystemUI.js';
7+
import {CardStack} from 'xrblocks/addons/glasses/ui/CardStack.js';
8+
import {CardManager} from 'xrblocks/addons/glasses/ui/CardManager.js';
9+
import {GlassesRenderer} from 'xrblocks/addons/glasses/ui/GlassesRenderer.js';
10+
11+
class GlassesUISample extends xb.Script {
12+
private runningInXr = false;
13+
private systemUiGroup = new THREE.Group();
14+
private systemUi!: SystemUI;
15+
16+
private glassesRenderer?: GlassesRenderer;
17+
18+
// Card Display.
19+
private cardManager = new CardManager();
20+
private cardNumber = 0;
21+
22+
override async init() {
23+
this.systemUi = new SystemUI(/*sizeX=*/ 1, /*sizeY=*/ 1);
24+
this.glassesRenderer = new GlassesRenderer(this.systemUi);
25+
this.systemUiGroup.add(this.glassesRenderer);
26+
this.systemUiGroup.position.set(0, 0, -1);
27+
xb.core.camera.add(this.systemUiGroup);
28+
xb.core.renderSceneOverride = this.renderSceneOverride;
29+
30+
this.systemUi.canvas.add(
31+
new CardStack({
32+
scrollPosition: this.cardManager.scrollPosition,
33+
cards: this.cardManager.cards,
34+
})
35+
);
36+
37+
this.add(this.cardManager);
38+
39+
this.createNewCard();
40+
}
41+
42+
override onSelectStart() {
43+
this.createNewCard();
44+
}
45+
46+
private createNewCard() {
47+
this.cardNumber++;
48+
const {cardBodySignal, cardTitleSignal} = this.cardManager.createNewCard();
49+
cardTitleSignal.value = `Card ${this.cardNumber}`;
50+
cardBodySignal.value = `This is card ${this.cardNumber}.`;
51+
}
52+
53+
private onRightXrCamera(rightCamera: THREE.Camera) {
54+
this.runningInXr = true;
55+
xb.add(rightCamera);
56+
this.systemUiGroup.position.set(0, 0, -2);
57+
}
58+
59+
override update() {
60+
this.systemUi.update(xb.getDeltaTime());
61+
const rightCamera = xb.getXrCameraRight();
62+
if (rightCamera && !this.runningInXr) {
63+
this.onRightXrCamera(rightCamera);
64+
}
65+
}
66+
67+
private renderSceneOverride = (
68+
renderer: THREE.WebGLRenderer,
69+
scene: THREE.Scene,
70+
camera: THREE.Camera
71+
) => {
72+
this.glassesRenderer!.render(renderer);
73+
renderer.render(scene, camera);
74+
};
75+
}
76+
77+
document.addEventListener('DOMContentLoaded', async () => {
78+
const options = new xb.Options();
79+
options.camera.near = 0.001;
80+
options.reticles.enabled = false;
81+
options.simulator.instructions.enabled = false;
82+
options.simulator.handPosePanel.enabled = false;
83+
options.simulator.modeIndicator.enabled = false;
84+
options.simulator.renderToRenderTexture = false;
85+
xb.add(new GlassesUISample());
86+
await xb.init(options);
87+
// Setup for @pmndrs/uikit.
88+
const renderer = xb.core.renderer;
89+
renderer.localClippingEnabled = true;
90+
renderer.setTransparentSort(reversePainterSortStable);
91+
});

src/addons/glasses/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# glasses ui
2+
3+
This is an experimental implementation of Glasses UI inspired by Jetpack Compose Glimmer for use in XR Blocks.
4+
It depends on @pmndrs/uikit and uiblocks.
5+
6+
## Features
7+
8+
- System UI with functional clock and weather widgets.
9+
- Card component.
10+
- Card stack component.
11+
- Google Sans Flex font.
12+
13+
## Usage
14+
15+
Please see samples/glasses-ui/ for a working example. Note that this is a work in progress and the API may change significantly without notice.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import {
2+
BaseOutProperties,
3+
Container,
4+
InProperties,
5+
RenderContext,
6+
Text,
7+
WithSignal,
8+
} from '@pmndrs/uikit';
9+
import {computed} from '@preact/signals-core';
10+
11+
import {HighlightMaterial} from './HighlightMaterial';
12+
import {MaterialSymbolsIcon} from './MaterialSymbolsIcon';
13+
14+
export type ActionButtonOutProperties = {
15+
text: string;
16+
icon?: string;
17+
iconStyle?: string;
18+
iconWeight?: number;
19+
} & BaseOutProperties;
20+
21+
export class ActionButton<
22+
OutProperties extends ActionButtonOutProperties = ActionButtonOutProperties,
23+
> extends Container<OutProperties> {
24+
name = 'Action Button';
25+
constructor(
26+
inputProperties?: InProperties<OutProperties>,
27+
initialClasses?: Array<InProperties<BaseOutProperties> | string>,
28+
config?: {
29+
renderContext?: RenderContext;
30+
defaultOverrides?: InProperties<OutProperties>;
31+
defaults?: WithSignal<OutProperties>;
32+
}
33+
) {
34+
super(inputProperties, initialClasses, {
35+
defaultOverrides: {
36+
width: 'auto',
37+
marginX: 'auto',
38+
height: 56,
39+
minWidth: 56,
40+
borderWidth: 4,
41+
borderRadius: 100,
42+
borderColor: 0x858885,
43+
paddingBottom: 8,
44+
paddingTop: 8,
45+
paddingLeft: 16,
46+
paddingRight: 16,
47+
justifyContent: 'center',
48+
alignItems: 'center',
49+
gapColumn: 8,
50+
positionType: 'relative',
51+
panelMaterialClass: HighlightMaterial,
52+
...config?.defaultOverrides,
53+
} as InProperties<OutProperties>,
54+
...config,
55+
});
56+
57+
const icon = new MaterialSymbolsIcon({
58+
icon: this.properties.signal.icon,
59+
iconStyle: this.properties.signal.iconStyle,
60+
iconWeight: this.properties.signal.iconWeight,
61+
width: 40,
62+
color: 0xa8c7fa,
63+
display: computed(() =>
64+
this.properties.signal.icon ? 'initial' : 'none'
65+
),
66+
});
67+
this.add(icon);
68+
69+
const text = new Text({
70+
text: this.properties.signal.text,
71+
fontSize: 24,
72+
color: 'white',
73+
fontWeight: 600,
74+
letterSpacing: 1.26,
75+
});
76+
this.add(text);
77+
}
78+
}

0 commit comments

Comments
 (0)