Skip to content

Commit ac87cd7

Browse files
authored
Merge pull request #1163 from 3DStreet/ar-overlay-controls
AR overlay controls
2 parents 69f73b1 + 5af212e commit ac87cd7

File tree

9 files changed

+338
-14
lines changed

9 files changed

+338
-14
lines changed

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
<!-- AR Overlay - only shown when in AR mode -->
6060
<div id="viewer-mode-ar-overlay">
6161
<button id="viewer-mode-ar-overlay-exit-button" onclick="AFRAME.scenes[0].renderer.xr.getSession().end()">Exit AR Mode</button>
62+
<div id="react-ar-controls"></div>
6263
</div>
6364

6465
<!-- <div class="right-fixed">

public/functions/webxr-variant.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ exports.serveWebXRVariant = functions.https.onRequest((req, res) => {
6666
<!-- AR Overlay - only shown when in AR mode -->
6767
<div id="viewer-mode-ar-overlay">
6868
<button id="viewer-mode-ar-overlay-exit-button" onclick="AFRAME.scenes[0].renderer.xr.getSession().end()">Exit AR Mode</button>
69+
<div id="react-ar-controls"></div>
6970
</div>
7071
7172
<!-- <div class="right-fixed">

src/editor/components/elements/Logo/Logo.component.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const Logo = () => {
2525
className={styles.btn}
2626
variant="toolbtn"
2727
>
28-
{isInspectorEnabled ? 'Enter Viewer mode' : 'Return to Editor'}
28+
{isInspectorEnabled ? 'Start Viewer' : 'Editor'}
2929
</Button>
3030
)}
3131
</div>

src/editor/components/elements/TimeControls.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,13 @@ const TimeControls = () => {
206206
return null;
207207
}
208208

209-
// Check if the viewer-mode component has preset="locomotion"
209+
// Check if the viewer-mode component has preset="locomotion" or "ar-webxr"
210210
const viewerModeEl = document.querySelector('[viewer-mode]');
211-
if (
212-
viewerModeEl &&
213-
viewerModeEl.getAttribute('viewer-mode').preset === 'locomotion'
214-
) {
215-
return null;
211+
if (viewerModeEl) {
212+
const preset = viewerModeEl.getAttribute('viewer-mode').preset;
213+
if (preset === 'locomotion' || preset === 'ar-webxr') {
214+
return null;
215+
}
216216
}
217217

218218
return (
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { useState } from 'react';
2+
import styles from './ARControls.module.scss';
3+
4+
const ARControls = () => {
5+
const [isExpanded, setIsExpanded] = useState(false);
6+
const [rotationValue, setRotationValue] = useState(0);
7+
const [positionX, setPositionX] = useState(0);
8+
const [positionZ, setPositionZ] = useState(0);
9+
const [height, setHeight] = useState(0);
10+
11+
const updateStreetContainer = (rotation, x, z, y) => {
12+
const streetContainer = document.getElementById('street-container');
13+
if (streetContainer) {
14+
streetContainer.setAttribute('rotation', `0 ${rotation} 0`);
15+
streetContainer.setAttribute('position', `${x} ${y} ${z}`);
16+
} else {
17+
console.log('street-container element not found');
18+
}
19+
};
20+
21+
const handleRotationChange = (event) => {
22+
const newRotation = parseFloat(event.target.value);
23+
setRotationValue(newRotation);
24+
updateStreetContainer(newRotation, positionX, positionZ, height);
25+
};
26+
27+
const handlePositionXChange = (event) => {
28+
const newX = parseFloat(event.target.value);
29+
setPositionX(newX);
30+
updateStreetContainer(rotationValue, newX, positionZ, height);
31+
};
32+
33+
const handlePositionZChange = (event) => {
34+
const newZ = parseFloat(event.target.value);
35+
setPositionZ(newZ);
36+
updateStreetContainer(rotationValue, positionX, newZ, height);
37+
};
38+
39+
const handleHeightChange = (event) => {
40+
const newHeight = parseFloat(event.target.value);
41+
setHeight(newHeight);
42+
updateStreetContainer(rotationValue, positionX, positionZ, newHeight);
43+
};
44+
45+
const toggleExpanded = () => {
46+
setIsExpanded(!isExpanded);
47+
};
48+
49+
if (!isExpanded) {
50+
return (
51+
<button
52+
className={styles.toggleButton}
53+
onClick={toggleExpanded}
54+
title="AR Scene Adjustments"
55+
>
56+
🎯
57+
</button>
58+
);
59+
}
60+
61+
return (
62+
<div className={styles.arControls}>
63+
<div className={styles.header}>
64+
<span className={styles.title}>AR Scene Adjustments</span>
65+
<button
66+
className={styles.closeButton}
67+
onClick={toggleExpanded}
68+
title="Collapse"
69+
>
70+
71+
</button>
72+
</div>
73+
74+
<div className={styles.controlGroup}>
75+
<label className={styles.label}>Rotation: {rotationValue}°</label>
76+
<input
77+
type="range"
78+
min="-180"
79+
max="180"
80+
step="5"
81+
value={rotationValue}
82+
onInput={handleRotationChange}
83+
className={styles.slider}
84+
/>
85+
</div>
86+
87+
<div className={styles.controlGroup}>
88+
<label className={styles.label}>Position X: {positionX}m</label>
89+
<input
90+
type="range"
91+
min="-50"
92+
max="50"
93+
step="1"
94+
value={positionX}
95+
onInput={handlePositionXChange}
96+
className={styles.slider}
97+
/>
98+
</div>
99+
100+
<div className={styles.controlGroup}>
101+
<label className={styles.label}>Position Z: {positionZ}m</label>
102+
<input
103+
type="range"
104+
min="-50"
105+
max="50"
106+
step="1"
107+
value={positionZ}
108+
onInput={handlePositionZChange}
109+
className={styles.slider}
110+
/>
111+
</div>
112+
113+
<div className={styles.controlGroup}>
114+
<label className={styles.label}>Height: {height}m</label>
115+
<input
116+
type="range"
117+
min="-5"
118+
max="5"
119+
step="0.1"
120+
value={height}
121+
onInput={handleHeightChange}
122+
className={styles.slider}
123+
/>
124+
</div>
125+
</div>
126+
);
127+
};
128+
129+
export default ARControls;
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
.arControls {
2+
background: rgba(0, 0, 0, 0.9);
3+
border-radius: 12px;
4+
padding: 16px;
5+
margin: 8px;
6+
color: white;
7+
font-family:
8+
system-ui,
9+
-apple-system,
10+
sans-serif;
11+
min-width: 280px;
12+
max-width: 320px;
13+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
14+
backdrop-filter: blur(10px);
15+
}
16+
17+
.toggleButton {
18+
position: fixed;
19+
top: 20px;
20+
right: 20px;
21+
width: 48px;
22+
height: 48px;
23+
border-radius: 24px;
24+
background: rgba(0, 0, 0, 0.8);
25+
border: 2px solid rgba(255, 255, 255, 0.2);
26+
color: white;
27+
font-size: 24px;
28+
cursor: pointer;
29+
display: flex;
30+
align-items: center;
31+
justify-content: center;
32+
backdrop-filter: blur(10px);
33+
transition: all 0.2s ease;
34+
z-index: 1000;
35+
36+
&:hover {
37+
background: rgba(0, 0, 0, 0.9);
38+
border-color: rgba(255, 255, 255, 0.4);
39+
transform: scale(1.05);
40+
}
41+
42+
&:active {
43+
transform: scale(0.95);
44+
}
45+
}
46+
47+
.header {
48+
display: flex;
49+
justify-content: space-between;
50+
align-items: center;
51+
margin-bottom: 16px;
52+
padding-bottom: 8px;
53+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
54+
}
55+
56+
.title {
57+
font-size: 16px;
58+
font-weight: 600;
59+
margin: 0;
60+
}
61+
62+
.closeButton {
63+
background: none;
64+
border: none;
65+
color: rgba(255, 255, 255, 0.7);
66+
font-size: 18px;
67+
cursor: pointer;
68+
padding: 4px;
69+
border-radius: 4px;
70+
transition: all 0.2s ease;
71+
72+
&:hover {
73+
color: white;
74+
background: rgba(255, 255, 255, 0.1);
75+
}
76+
}
77+
78+
.controlGroup {
79+
margin-bottom: 20px;
80+
81+
&:last-child {
82+
margin-bottom: 0;
83+
}
84+
}
85+
86+
.label {
87+
display: block;
88+
font-size: 13px;
89+
font-weight: 500;
90+
margin-bottom: 10px;
91+
color: rgba(255, 255, 255, 0.9);
92+
}
93+
94+
.slider {
95+
width: 100%;
96+
height: 8px;
97+
border-radius: 4px;
98+
background: rgba(255, 255, 255, 0.2);
99+
outline: none;
100+
-webkit-appearance: none;
101+
appearance: none;
102+
cursor: pointer;
103+
transition: background-color 0.2s ease;
104+
105+
&:hover {
106+
background: rgba(255, 255, 255, 0.3);
107+
}
108+
109+
&::-webkit-slider-thumb {
110+
-webkit-appearance: none;
111+
appearance: none;
112+
width: 20px;
113+
height: 20px;
114+
border-radius: 50%;
115+
background: #fff;
116+
cursor: pointer;
117+
border: 3px solid #007bff;
118+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
119+
transition: all 0.2s ease;
120+
}
121+
122+
&::-webkit-slider-thumb:hover {
123+
transform: scale(1.1);
124+
border-color: #0056b3;
125+
}
126+
127+
&::-moz-range-thumb {
128+
width: 20px;
129+
height: 20px;
130+
border-radius: 50%;
131+
background: #fff;
132+
cursor: pointer;
133+
border: 3px solid #007bff;
134+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
135+
}
136+
}
137+
138+
.actionButton {
139+
width: 100%;
140+
padding: 10px;
141+
background: #007bff;
142+
color: white;
143+
border: none;
144+
border-radius: 4px;
145+
font-size: 14px;
146+
font-weight: 500;
147+
cursor: pointer;
148+
transition: background-color 0.2s ease;
149+
150+
&:hover {
151+
background: #0056b3;
152+
}
153+
154+
&:active {
155+
background: #004085;
156+
}
157+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './ARControls.component.jsx';

src/editor/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import '../styles/tailwind.css';
22
import { createRoot } from 'react-dom/client';
33
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter';
44
import MainWrapper from './components/MainWrapper';
5+
import ARControls from './components/viewport/ARControls';
56
import { AuthProvider, GeoProvider } from './contexts';
67
import Events from './lib/Events';
78
import { AssetsLoader } from './lib/assetsLoader';
@@ -90,6 +91,13 @@ Inspector.prototype = {
9091
</AuthProvider>
9192
);
9293

94+
// Mount AR Controls to the AR overlay div
95+
const arControlsContainer = document.getElementById('react-ar-controls');
96+
if (arControlsContainer) {
97+
const arRoot = createRoot(arControlsContainer);
98+
arRoot.render(<ARControls />);
99+
}
100+
93101
this.scene = this.sceneEl.object3D;
94102
this.helpers = {};
95103
this.sceneHelpers = new THREE.Scene();

0 commit comments

Comments
 (0)