@@ -4,11 +4,17 @@ import {OrbitControls} from "three/examples/jsm/controls/OrbitControls"
4
4
import Hyperbeam from "@hyperbeam/web"
5
5
6
6
const scene = new THREE . Scene ( )
7
+ const pointer = new THREE . Vector2 ( )
8
+ const raycaster = new THREE . Raycaster ( )
7
9
const camera = new THREE . PerspectiveCamera ( 70 , window . innerWidth / window . innerHeight , .0001 , 100 )
8
10
camera . target = new THREE . Vector3 ( 0 , 0 , 0 )
9
11
camera . position . set ( 0 , 0 , 1 )
10
12
camera . lookAt ( camera . target )
11
13
14
+ const listener = new THREE . AudioListener ( )
15
+ const sound = new THREE . PositionalAudio ( listener )
16
+ camera . add ( listener )
17
+
12
18
const renderer = new THREE . WebGLRenderer ( { antialias : true , preserveDrawingBuffer : ! true } )
13
19
renderer . setPixelRatio ( window . devicePixelRatio )
14
20
renderer . setClearColor ( 0x000000 , 1 )
@@ -17,37 +23,120 @@ threejscontainer.appendChild(renderer.domElement)
17
23
18
24
const controls = new OrbitControls ( camera , renderer . domElement )
19
25
26
+ // The default aspect ratio of the virtual computer is 16:9
27
+ const width = 1
28
+ const height = width * 9 / 16
20
29
const texture = new THREE . Texture ( )
21
- const geometry = new THREE . PlaneBufferGeometry ( 1 , 9 / 16 )
22
- // geometry.rotateZ(Math.PI*1)
23
- const plane = new THREE . Mesh (
24
- geometry ,
25
- new THREE . MeshBasicMaterial ( { map : texture } )
26
- )
30
+ const geometry = new THREE . PlaneBufferGeometry ( width , height )
31
+ const material = new THREE . MeshBasicMaterial ( { map : texture } )
32
+ // Need to offset for Three.js left-handed coordinate system
33
+ // https://stackoverflow.com/questions/1263072/changing-a-matrix-from-right-handed-to-left-handed-coordinate-system
34
+ geometry . rotateZ ( Math . PI )
35
+ geometry . rotateY ( Math . PI )
36
+ material . side = THREE . DoubleSide
37
+
38
+ const plane = new THREE . Mesh ( geometry , material )
27
39
scene . add ( plane )
40
+ plane . add ( sound )
41
+
42
+ const embedURL = "https://1aa2bnwfuuv7hod22dmbiqxql.hyperbeam.com/62lhP-IYR_ya0Fxb0474rg?token=NTbSjoEtqSfJJ0iZhzZKWzOwaznvT4MygJA-2Yr1tMk"
43
+ const hb = await Hyperbeam ( hbcontainer , embedURL , {
44
+ frameCb : ( frame ) => {
45
+ plane . material . map . image = frame
46
+ plane . material . map . needsUpdate = true
47
+ } ,
48
+ audioTrackCb : tryAudio
49
+ } )
28
50
29
51
window . addEventListener ( "resize" , onWindowResized )
52
+ window . addEventListener ( "wheel" , onWheel )
53
+ window . addEventListener ( "contextmenu" , onContextMenu )
54
+ window . addEventListener ( "pointermove" , onPointerMove )
55
+ window . addEventListener ( "pointerdown" , onPointerDown )
56
+ window . addEventListener ( "pointerup" , onPointerUp )
57
+
30
58
onWindowResized ( )
31
59
animate ( )
32
60
61
+ function tryAudio ( track ) {
62
+ sound . setMediaStreamSource ( new MediaStream ( [ track ] ) )
63
+ sound . setRefDistance ( 0.5 )
64
+ // The audio context might be waiting on a user gesture
65
+ // In that case, we'll call sound.play() in the pointerdown handler
66
+ if ( listener . context . state === "running" ) {
67
+ sound . play ( )
68
+ }
69
+ }
70
+
71
+
33
72
function onWindowResized ( ) {
34
73
const w = window . innerWidth
35
74
const h = window . innerHeight
36
- renderer . setSize ( w , h )
75
+ renderer . setSize ( w , h )
37
76
camera . aspect = w / h
38
77
camera . updateProjectionMatrix ( )
39
78
}
40
79
80
+ function getPlaneIntersects ( ) {
81
+ raycaster . setFromCamera ( pointer , camera )
82
+ return raycaster . intersectObject ( plane , false )
83
+ }
84
+
85
+ function onWheel ( e ) {
86
+ if ( getPlaneIntersects ( ) . length > 0 ) {
87
+ hb . sendEvent ( {
88
+ type : "wheel" ,
89
+ deltaY : e . deltaY
90
+ } )
91
+ }
92
+ }
93
+
94
+ function onContextMenu ( e ) {
95
+ if ( getPlaneIntersects ( ) . length > 0 ) {
96
+ e . preventDefault ( )
97
+ }
98
+ }
99
+
100
+ function handlePointer ( e , type ) {
101
+ pointer . x = ( e . clientX / window . innerWidth ) * 2 - 1
102
+ pointer . y = - ( e . clientY / window . innerHeight ) * 2 + 1
103
+ const intersects = getPlaneIntersects ( )
104
+ if ( intersects . length > 0 ) {
105
+ // We disable the OrbitControls when the user's pointer is on the virtual computer
106
+ controls . enabled = false
107
+ const vector = new THREE . Vector3 ( ) . copy ( intersects [ 0 ] . point )
108
+ plane . worldToLocal ( vector )
109
+ hb . sendEvent ( {
110
+ type,
111
+ x : vector . x / width + 0.5 ,
112
+ y : - vector . y / height + 0.5 ,
113
+ button : e . button
114
+ } )
115
+ } else {
116
+ controls . enabled = true
117
+ }
118
+ }
119
+
120
+ function onPointerMove ( e ) {
121
+ handlePointer ( e , "mousemove" )
122
+ }
123
+
124
+ function onPointerDown ( e ) {
125
+ handlePointer ( e , "mousedown" )
126
+ }
127
+
128
+ async function onPointerUp ( e ) {
129
+ handlePointer ( e , "mouseup" )
130
+ // If the audio context was suspended because there were no user gestures,
131
+ // resume the audio context now since the user interacted with the page
132
+ if ( listener . context . state === "suspended" ) {
133
+ await listener . context . resume ( )
134
+ await sound . play ( )
135
+ }
136
+ }
137
+
41
138
function animate ( ) {
42
139
window . requestAnimationFrame ( animate )
43
140
controls . update ( )
44
141
renderer . render ( scene , camera )
45
142
}
46
-
47
- const embedURL = "https://1aa2bnwfuuv7hod22dmbiqxql.hyperbeam.com/fY0wxooSQ_yQxBQmD3jYHg?token=XOvtBJel4_RvA66MGF8qiyxxMH1LL3e8kdvB48C9pU8"
48
- const hb = await Hyperbeam ( hbcontainer , embedURL , {
49
- frameCb : ( frame ) => {
50
- plane . material . map . image = frame
51
- plane . material . map . needsUpdate = true
52
- }
53
- } )
0 commit comments