Skip to content

Commit b7853ee

Browse files
committed
sync pose with presenter
1 parent 443cc8a commit b7853ee

File tree

15 files changed

+186
-66
lines changed

15 files changed

+186
-66
lines changed

packages/client/src/external/aframe.ts

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import Store from '@dojo/framework/stores/Store';
22
import { State } from '../interfaces';
33
import { Media } from 'present-core/api/websocket/screen';
44
import { nextSlide, previousSlide } from 'present-core/api/websocket/revealjs';
5+
import { enterVrProcess, exitVrProcess } from '../processes/vr.process';
6+
import { startSharingPose, stopSharingPose } from './aframe/pose';
7+
import { snackbar } from './aframe/snackbar';
58

69
export function initialize(store: Store<State>) {
710
const { get, path } = store;
@@ -14,8 +17,32 @@ export function initialize(store: Store<State>) {
1417
setScreenMedia(get(path('space', 'screen', 'source')));
1518
});
1619

20+
store.onChange(path('isPresenter'), () => {
21+
const isPresenter = get(path('isPresenter'));
22+
const isInVr = get(path('isInVr'));
23+
24+
if (isPresenter && isInVr) {
25+
startSharingPose();
26+
}
27+
else {
28+
stopSharingPose();
29+
}
30+
});
31+
32+
store.onChange(path('isInVr'), () => {
33+
const isPresenter = get(path('isPresenter'));
34+
const isInVr = get(path('isInVr'));
35+
36+
if (isPresenter && isInVr) {
37+
startSharingPose();
38+
}
39+
else {
40+
stopSharingPose();
41+
}
42+
});
43+
1744
// Attach event handlers to Aframe
18-
initializeHandlers();
45+
initializeHandlers(store);
1946

2047
// Wait for Aframe to load before continuing
2148
const scene = document.querySelector('a-scene');
@@ -27,28 +54,24 @@ export function initialize(store: Store<State>) {
2754
// TODO load the tracked initial values in to the store
2855
}
2956

30-
function initializeHandlers() {
57+
function initializeHandlers(store: Store<State>) {
3158
// const leftHand = document.getElementById('leftControl');
3259
const rightHand = document.getElementById('rightControl');
60+
const scene = document.querySelector('a-scene');
3361

3462
rightHand?.addEventListener('abuttondown', () => {
3563
nextSlide({});
3664
});
3765
rightHand?.addEventListener('bbuttondown', () => {
3866
previousSlide({});
3967
});
40-
// TODO track pose when connected && is presenter
41-
}
42-
43-
export function snackbar(message: string, timeout: number = 3000) {
44-
const textNode = document.querySelector('a-text');
45-
textNode?.setAttribute('value', message);
46-
setTimeout(() => {
47-
const currentValue = textNode?.getAttribute('value');
48-
if (currentValue === message) {
49-
textNode?.removeAttribute('value');
50-
}
51-
}, timeout);
68+
scene?.addEventListener('enter-vr', () => {
69+
enterVrProcess(store)({});
70+
snackbar('Welcome');
71+
});
72+
scene?.addEventListener('exit-vr', () => {
73+
exitVrProcess(store)({});
74+
});
5275
}
5376

5477
function setSkyColor(color: string) {
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { Entity } from 'aframe';
2+
import { sharePose, CharacterPose, Pose } from 'present-core/api/websocket/presenter';
3+
import { Object3D } from 'three';
4+
5+
let isSharingPose: boolean = false;
6+
7+
function getPose(obj: Object3D) {
8+
return {
9+
pos: {
10+
x: obj.position.x,
11+
y: obj.position.y,
12+
z: obj.position.z,
13+
},
14+
rot: {
15+
x: obj.rotation.x,
16+
y: obj.rotation.y,
17+
z: obj.rotation.z,
18+
}
19+
}
20+
}
21+
22+
export function startSharingPose() {
23+
if (isSharingPose) {
24+
return;
25+
}
26+
27+
isSharingPose = true;
28+
29+
const leftHand = document.getElementById('leftControl') as Entity;
30+
const rightHand = document.getElementById('rightControl') as Entity;
31+
32+
function updatePose() {
33+
if (!isSharingPose) {
34+
return;
35+
}
36+
37+
const data = {
38+
left: getPose(leftHand.object3D),
39+
right: getPose(rightHand.object3D)
40+
}
41+
42+
sharePose(data);
43+
setTimeout(updatePose, 250);
44+
}
45+
46+
updatePose();
47+
}
48+
49+
export function stopSharingPose() {
50+
isSharingPose = false;
51+
}
52+
53+
function setPose(obj3d: Object3D, { pos, rot }: Pose) {
54+
obj3d.position.set(pos.x, pos.y, pos.z);
55+
obj3d.rotation.set(rot.x, rot.y, rot.z);
56+
}
57+
58+
export function changePose(pose: CharacterPose) {
59+
if (pose.left) {
60+
const left = document.getElementById('leftPresenter') as Entity;
61+
setPose(left.object3D, pose.left);
62+
}
63+
if (pose.right) {
64+
const right = document.getElementById('rightPresenter') as Entity;
65+
setPose(right.object3D, pose.right);
66+
}
67+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export function snackbar(message: string, timeout: number = 3000) {
2+
const textNode = document.querySelector('a-text');
3+
textNode?.setAttribute('value', message);
4+
setTimeout(() => {
5+
const currentValue = textNode?.getAttribute('value');
6+
if (currentValue === message) {
7+
textNode?.removeAttribute('value');
8+
}
9+
}, timeout);
10+
}
11+
12+
export function displayText(message?: string) {
13+
const textNode = document.querySelector('a-text');
14+
textNode?.setAttribute('value', message);
15+
}

packages/client/src/external/omnisockets.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import Store from '@dojo/framework/stores/Store';
22
import { handleStatus } from 'present-core/api/websocket/info';
33
import { handleShowMedia } from 'present-core/api/websocket/screen';
4+
import { handlePose } from 'present-core/api/websocket/presenter';
45

56
import { State } from '../interfaces';
67
import { setSourceProcess } from '../processes/screen.process';
78
import { handleAuthenticateError, handleAuthenticated, handleNotAuthenticated } from 'present-core/api/websocket/authenticate';
89
import { setUnauthenticatedProcess, setAuthenticatedProcess } from '../processes/authenticate.process';
9-
import { snackbar } from './aframe';
10+
import { snackbar } from './aframe/snackbar';
11+
import { changePose } from './aframe/pose';
1012

1113
let store: Store<State>
1214

@@ -34,4 +36,8 @@ export function initialize(s: Store<State>) {
3436
setUnauthenticatedProcess(store)({});
3537
snackbar('Not Authenticated');
3638
});
39+
40+
handlePose((pose) => {
41+
changePose(pose);
42+
});
3743
}

packages/client/src/index.html

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
<a-assets>
1414
<video id="vrscreen" playsinline></video>
1515
</a-assets>
16-
<a-sky key="sky" color="#191a47"></a-sky>
17-
<a-entity key="camera" camera look-controls position="0 1.6 0">
18-
<a-text key="text" id="txt" value="" position="0 0 -1" scale="0.4 0.4 0.4" align="center" color="#FFF"></a-text>
16+
<a-sky color="#191a47"></a-sky>
17+
<a-entity camera look-controls position="0 1.6 0">
18+
<a-text id="txt" value="" position="0 0 -1" scale="0.4 0.4 0.4" align="center" color="#FFF"></a-text>
1919
</a-entity>
2020
<a-plane position="0 2 -1.10276" geometry="width: 1.8" color="#000000"></a-plane>
2121
<a-entity id="leftControl" laser-controls="hand: left"></a-entity>
2222
<a-entity id="rightControl" laser-controls="hand: right"></a-entity>
23+
<a-sphere id="leftPresenter" scale="0.2 0.2 0.2" color="red"></a-sphere>
24+
<a-sphere id="rightPresenter" scale="0.2 0.2 0.2" color="green"></a-sphere>
2325
</a-scene>
2426
</body>
2527
</html>

packages/client/src/interfaces.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { Media } from 'present-core/api/websocket/screen';
2+
import { CharacterPose } from 'present-core/api/websocket/presenter';
23

34
export interface State {
45
auth: Authentication;
56
/** If the user is the presenter */
67
isPresenter: boolean;
78
/** If the user is connected to websockets */
89
isConnected: boolean;
10+
/** If the user is in VR */
11+
isInVr: boolean;
912
/** If the menu should be displayed */
1013
openMenu: boolean;
1114

@@ -31,6 +34,7 @@ export interface Presentation {
3134
export interface Space {
3235
sky: Sky;
3336
screen?: Screen;
37+
presenter?: CharacterPose;
3438
}
3539

3640
export interface Sky {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { createCommandFactory, createProcess } from '@dojo/framework/stores/process';
2+
import { replace } from '@dojo/framework/stores/state/operations';
3+
4+
import { State } from '../interfaces';
5+
6+
const commandFactory = createCommandFactory<State>();
7+
8+
const setEnterVrCommand = commandFactory(({ path }) => {
9+
return [
10+
replace(path('isInVr'), true)
11+
];
12+
});
13+
14+
const setExitVrCommand = commandFactory(({ path }) => {
15+
return [
16+
replace(path('isInVr'), false)
17+
];
18+
});
19+
20+
const setPresentingCommand = commandFactory(({ path, payload: { value } }) => {
21+
return [
22+
replace(path('isPresenter'), value)
23+
];
24+
});
25+
26+
export const enterVrProcess = createProcess('enter-vr', [ setEnterVrCommand ]);
27+
export const exitVrProcess = createProcess('exit-vr', [ setExitVrCommand ]);
28+
export const startPresentingProcess = createProcess('start-presenting', [ setPresentingCommand ]);

packages/client/src/widgets/menu/index.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { create, tsx } from '@dojo/framework/core/vdom';
2-
import { store } from '../../middleware/store';
32
import SlidePane from '@dojo/widgets/slide-pane';
4-
import Hamburger from '../hamburger';
5-
import { openMenuProcess, closeMenuProcess } from '../../processes/menu.process';
3+
4+
import { store } from '../../middleware/store';
5+
import { connectProcess, disconnectProcess } from '../../processes/connection.process';
6+
import { closeMenuProcess, openMenuProcess } from '../../processes/menu.process';
7+
import { startPresentingProcess } from '../../processes/vr.process';
8+
import Authentication from '../authentication/Authentication';
69
import { Card } from '../card/Card';
7-
import Status from '../status/Status';
810
import Control from '../control/Control';
9-
import Authentication from '../authentication/Authentication';
10-
import { connectProcess, disconnectProcess } from '../../processes/connection.process';
11-
11+
import Hamburger from '../hamburger';
12+
import Status from '../status/Status';
1213
import * as css from './menu.m.css';
1314

1415
const factory = create({ store });
@@ -22,6 +23,7 @@ export default factory(function Menu({ middleware: { store: { get, path, executo
2223
const closeMenu = executor(closeMenuProcess);
2324
const connect = executor(connectProcess);
2425
const disconnect = executor(disconnectProcess);
26+
const presenting = executor(startPresentingProcess);
2527

2628
return <virtual>
2729
{ !isMenuOpen && <Hamburger onClick={() => { openMenu({}) }}></Hamburger> }
@@ -50,7 +52,8 @@ export default factory(function Menu({ middleware: { store: { get, path, executo
5052
</div>
5153
</Card>
5254
{ isAuthenticated && <Card title="Presenter">
53-
<Control show={!isPresenter} title="Start Presenting" onClick={() => { }}/>
55+
<Control show={!isPresenter} title="Start Presenting" onClick={() => { presenting({ value: true }) }}/>
56+
<Control show={!!isPresenter} title="Stop Presenting" onClick={() => { presenting({ value: false }) }}/>
5457
</Card> }
5558
{ !isAuthenticated && <Card title="Authentication">
5659
<Authentication />

packages/core/src/api/websocket/api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const enum Action {
66
GetStatus = 'getStatus',
77
HideLaser = 'hideLaser',
88
NextSlide = 'nextSlide',
9+
Pose = 'pose',
910
PreviousSlide = 'previousSlide',
1011
RoleConnected = 'roleConnected',
1112
RoleLeft = 'roleLeft',
Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,13 @@
1-
/**
2-
* Stops showing pose data
3-
*/
4-
export function stopPresenting(socket: WebSocket) {
5-
// TODO implement
6-
}
1+
import { createRequest, Action, createHandler } from "./api";
72

8-
/**
9-
* Indicates that the presenter should be removed from the scene
10-
*/
11-
export function handleStopPresenting(socket: WebSocket) {
12-
// TODO implement
3+
export interface Pose {
4+
pos: { x: number, y: number, z: number };
5+
rot: { x: number, y: number, z: number };
136
}
147

15-
/**
16-
* Describes the pose of the presenter
17-
*/
18-
export function updatePose(socket: WebSocket) {
19-
// TODO implement
8+
export interface CharacterPose {
9+
[ key: string ]: Pose;
2010
}
2111

22-
/**
23-
* Update the presenter in the scene
24-
*/
25-
export function handleUpdatePose(socket: WebSocket) {
26-
// TODO implement
27-
}
12+
export const sharePose = createRequest<CharacterPose>(Action.Pose);
13+
export const handlePose = createHandler<CharacterPose>(Action.Pose);

0 commit comments

Comments
 (0)