1
+ <!DOCTYPE html>
2
+ < html lang ="en ">
3
+
4
+ < head >
5
+ < meta charset ="UTF-8 ">
6
+ < meta name ="viewport "
7
+ content ="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0 ">
8
+ < title > Hilo3d XR Demo</ title >
9
+ < link rel ="stylesheet " type ="text/css " href ="./example.css ">
10
+ </ head >
11
+
12
+ < body >
13
+ < div id ="container "> </ div >
14
+ < script src ="../build/Hilo3d.js "> </ script >
15
+ < script src ="./js/stats.js "> </ script >
16
+ < script src ="./js/OrbitControls.js "> </ script >
17
+ < script src ="./js/init.js "> </ script >
18
+ <!-- <script src="./js/vconsole.min.js"></script> -->
19
+ < script >
20
+ renderer . useVao = true ;
21
+ // renderer.clearColor = new Hilo3d.Color(0, 0, 0, 0);
22
+
23
+ function rand ( min , max ) {
24
+ return Math . random ( ) * ( max - min ) + min ;
25
+ }
26
+
27
+ function randArr ( arr ) {
28
+ return arr [ Math . floor ( Math . random ( ) * arr . length ) ] ;
29
+ }
30
+
31
+ const container = new Hilo3d . Node ( ) ;
32
+ stage . addChild ( container ) ;
33
+ var loader = new Hilo3d . BasicLoader ( ) ;
34
+ loader . load ( {
35
+ src : '//gw.alicdn.com/tfs/TB1T1wEizTpK1RjSZKPXXa3UpXa-512-512.png' ,
36
+ crossOrigin : 'anonymous' ,
37
+ useInstanced : true
38
+ } ) . then ( function ( image ) {
39
+ return new Hilo3d . Texture ( { image : image } ) ;
40
+ } , function ( err ) {
41
+ return new Hilo3d . Color ( 1 , 0 , 0 ) ;
42
+ } ) . then ( function ( diffuse ) {
43
+ var textureMaterial = new Hilo3d . BasicMaterial ( {
44
+ diffuse : diffuse ,
45
+ side : Hilo3d . constants . FRONT_AND_BACK
46
+ } ) ;
47
+ var colorMaterial = new Hilo3d . BasicMaterial ( {
48
+ diffuse : new Hilo3d . Color ( 0.3 , 0.6 , 0.9 ) ,
49
+ side : Hilo3d . constants . FRONT_AND_BACK
50
+ } ) ;
51
+ var planeGeometry = new Hilo3d . PlaneGeometry ( ) ;
52
+ var sphereGeometry = new Hilo3d . SphereGeometry ( {
53
+ radius : 0.3
54
+ } ) ;
55
+ var boxGeometry = new Hilo3d . BoxGeometry ( {
56
+ width : 0.3 ,
57
+ height : 0.3 ,
58
+ depth : 0.3
59
+ } ) ;
60
+ boxGeometry . setAllRectUV ( [ [ 0 , 1 ] , [ 1 , 1 ] , [ 1 , 0 ] , [ 0 , 0 ] ] ) ;
61
+
62
+ var geometryes = [ planeGeometry , sphereGeometry , boxGeometry ] ;
63
+ var materials = [ colorMaterial , textureMaterial ] ;
64
+
65
+ for ( var i = 0 ; i < 100 ; i ++ ) {
66
+ let r = 1 ;
67
+ var rect = new Hilo3d . Mesh ( {
68
+ frustumTest : true ,
69
+ geometry : randArr ( geometryes ) ,
70
+ material : randArr ( materials ) ,
71
+ x : rand ( - r * 2 , r * 2 ) ,
72
+ y : rand ( - r * 2 , r * 2 ) ,
73
+ z : rand ( - r * 2 , r * 2 )
74
+ } ) ;
75
+ rect . rotationX = Math . random ( ) * 360 ;
76
+ rect . rotationY = Math . random ( ) * 360 ;
77
+ rect . rotationZ = Math . random ( ) * 360 ;
78
+ rect . setScale ( rand ( 0.2 , 0.3 ) ) ;
79
+ rect . onUpdate = function ( ) {
80
+ if ( this !== xrSelectedInfo . mesh ) {
81
+ this . rotationX += 0.5 ;
82
+ this . rotationY += 0.5 ;
83
+ this . rotationZ += 0.5 ;
84
+ }
85
+ } ;
86
+ container . addChild ( rect ) ;
87
+ }
88
+ } ) ;
89
+
90
+
91
+ async function initXR ( ) {
92
+ const supported = await navigator . xr . isSessionSupported ( 'immersive-ar' ) ;
93
+ if ( supported ) {
94
+ var enterXrBtn = document . createElement ( "button" ) ;
95
+ enterXrBtn . innerHTML = "Enter AR" ;
96
+ enterXrBtn . style . cssText = "position: absolute; bottom: 10px; left: 50%; z-index: 9999;" ;
97
+ enterXrBtn . addEventListener ( "click" , beginXRSession ) ;
98
+ document . body . appendChild ( enterXrBtn ) ;
99
+ } else {
100
+ console . log ( "Session not supported: " + reason ) ;
101
+ }
102
+ }
103
+
104
+ let xrSession = null ;
105
+ let xrReferenceSpace = null ;
106
+ const xrSelectedInfo = {
107
+ started : false ,
108
+ mesh : null ,
109
+ meshPos : null ,
110
+ point : null ,
111
+ distance : 0 ,
112
+ } ;
113
+ async function beginXRSession ( ) {
114
+ xrSession = await navigator . xr . requestSession ( 'immersive-ar' ) ;
115
+ xrSession . addEventListener ( 'end' , ( ) => {
116
+ xrSession = null ;
117
+ camera . updateProjectionMatrix ( ) ;
118
+ camera . position . set ( 0 , 0 , 3 ) ;
119
+ renderer . clearColor . set ( 0.3 , 0.35 , 0.35 , 1 ) ;
120
+ gl . bindFramebuffer ( gl . FRAMEBUFFER , null ) ;
121
+ gl . viewport ( 0 , 0 , renderer . width , renderer . height ) ;
122
+ onXRFrame ( 0 ) ;
123
+ } ) ;
124
+ xrSession . addEventListener ( 'selectstart' , ( evt ) => {
125
+ updateHandLine ( evt . frame ) ;
126
+ const hitInfo = hitTest ( ) ?. [ 0 ] ;
127
+ if ( ! hitInfo ) {
128
+ return ;
129
+ }
130
+ xrSelectedInfo . started = true ;
131
+ xrSelectedInfo . mesh = hitInfo . mesh ;
132
+ xrSelectedInfo . meshPos = hitInfo . mesh . worldMatrix . getTranslation ( ) ;
133
+ xrSelectedInfo . point = hitInfo . point ;
134
+ xrSelectedInfo . distance = hitInfo . distance ;
135
+ console . log ( 'hit' , hitInfo . mesh . id , hitInfo . point . toArray ( ) . join ( ',' ) , xrSelectedInfo ) ;
136
+ } ) ;
137
+ xrSession . addEventListener ( 'selectend' , ( ) => {
138
+ xrSelectedInfo . started = false ;
139
+ xrSelectedInfo . mesh = null ;
140
+ console . log ( 'selectend' ) ;
141
+ } ) ;
142
+
143
+ xrSession . addEventListener ( 'inputsourceschange' , ( evt ) => {
144
+ // drawHandLine(evt.frame);
145
+ } ) ;
146
+
147
+ xrReferenceSpace = await xrSession . requestReferenceSpace ( 'local' ) ;
148
+ await renderer . gl . makeXRCompatible ( ) ;
149
+
150
+ const xrWebGLBaseLayer = new XRWebGLLayer ( xrSession , renderer . gl ) ;
151
+
152
+ xrSession . updateRenderState ( {
153
+ baseLayer : xrWebGLBaseLayer ,
154
+ } ) ;
155
+ xrSession . requestAnimationFrame ( onXRFrame ) ;
156
+ }
157
+
158
+ const line = new Hilo3d . Mesh ( {
159
+ geometry : new Hilo3d . Geometry ( {
160
+ mode : Hilo3d . constants . LINES ,
161
+ vertices : new Hilo3d . GeometryData ( new Float32Array ( [
162
+ 0 , 0 , 0 ,
163
+ 0 , 0 , - 10
164
+ ] ) , 3 ) ,
165
+ colors : new Hilo3d . GeometryData ( new Float32Array ( [
166
+ 1 , 0 , 0 , 1 ,
167
+ 1 , 0 , 0 , 1
168
+ ] ) , 4 ) ,
169
+ } ) ,
170
+ material : new Hilo3d . BasicMaterial ( {
171
+ lightType : 'NONE' ,
172
+ side : Hilo3d . constants . FRONT_AND_BACK
173
+ } )
174
+ } ) ;
175
+ const lineContainer = new Hilo3d . Node ( ) ;
176
+ lineContainer . addChild ( line ) ;
177
+ stage . addChild ( lineContainer ) ;
178
+
179
+ function updateHandLine ( frame ) {
180
+ const inputSource = frame . session . inputSources [ 0 ] ;
181
+ let targetRayPose = frame . getPose ( inputSource . targetRaySpace , xrReferenceSpace ) ;
182
+ if ( ! targetRayPose ) {
183
+ return ;
184
+ }
185
+ // console.log('inputSource', targetRayPose.transform.matrix.join(','));
186
+ lineContainer . matrix . fromArray ( targetRayPose . transform . matrix ) ;
187
+ lineContainer . matrix . mul ( stage . matrix . clone ( ) . invert ( ) , lineContainer . matrix ) ;
188
+
189
+ if ( xrSelectedInfo . started ) {
190
+ const newPoint = new Hilo3d . Vector3 ( 0 , 0 , - xrSelectedInfo . distance ) ;
191
+ newPoint . transformMat4 ( lineContainer . worldMatrix ) ;
192
+ newPoint . sub ( xrSelectedInfo . point ) ;
193
+ const newWorldPos = xrSelectedInfo . meshPos . clone ( ) . add ( newPoint ) ;
194
+ newWorldPos . transformMat4 ( xrSelectedInfo . mesh . parent . worldMatrix . clone ( ) . invert ( ) ) ;
195
+ xrSelectedInfo . mesh . position . copy ( newWorldPos ) ;
196
+ line . setScale ( 1 , 1 , xrSelectedInfo . distance / 10 ) ;
197
+ } else {
198
+ hitTest ( ) ;
199
+ }
200
+ }
201
+
202
+ // alert(1);
203
+ const ray = new Hilo3d . Ray ( ) ;
204
+ function hitTest ( ) {
205
+ ray . origin . set ( 0 , 0 , 0 ) ;
206
+ ray . direction . set ( 0 , 0 , - 1 ) ;
207
+ lineContainer . updateMatrixWorld ( ) ;
208
+ ray . transformMat4 ( lineContainer . worldMatrix ) ;
209
+ const hitTestStartTime = Date . now ( ) ;
210
+ var hitResult = container . raycast ( ray ) ;
211
+ hitResult ?. forEach ( hitInfo => {
212
+ hitInfo . distance = ray . distance ( hitInfo . point ) ;
213
+ } ) ;
214
+ hitResult ?. sort ( ( a , b ) => a . distance - b . distance ) ;
215
+ // console.log('hitTest', Date.now() - hitTestStartTime);
216
+ if ( ! xrSelectedInfo . started ) {
217
+ if ( hitResult && hitResult . length ) {
218
+ line . setScale ( 1 , 1 , hitResult [ 0 ] . distance / 10 ) ;
219
+ } else {
220
+ line . setScale ( 1 , 1 , 1 ) ;
221
+ }
222
+ }
223
+ return hitResult ;
224
+ }
225
+
226
+ let lastTS = 0 ;
227
+ function onXRFrame ( ts , xrFrame ) {
228
+ const gl = renderer . gl ;
229
+ if ( xrSession && xrFrame ) {
230
+ renderer . clearColor . set ( 0 , 0 , 0 , 0 ) ;
231
+ updateHandLine ( xrFrame ) ;
232
+ let glLayer = xrSession . renderState . baseLayer ;
233
+ gl . bindFramebuffer ( gl . FRAMEBUFFER , glLayer . framebuffer ) ;
234
+ let pose = xrFrame . getViewerPose ( xrReferenceSpace ) ;
235
+ if ( pose ) {
236
+ for ( let i = 0 ; i < 2 ; i ++ ) {
237
+ const view = pose . views [ i ] ;
238
+ if ( ! view ) {
239
+ continue ;
240
+ }
241
+ let viewport = glLayer . getViewport ( view ) ;
242
+ // console.log('viewport', [viewport.x, viewport.y, viewport.width, viewport.height].join(','));
243
+ gl . viewport ( viewport . x , viewport . y , viewport . width , viewport . height ) ;
244
+
245
+ camera . matrix . fromArray ( view . transform . matrix ) ;
246
+ camera . projectionMatrix . fromArray ( view . projectionMatrix ) ;
247
+ camera . updateMatrixWorld ( ) ;
248
+ camera . updateViewProjectionMatrix ( ) ;
249
+
250
+ if ( i === 0 ) {
251
+ stage . tick ( ts - lastTS ) ;
252
+ } else {
253
+ renderer . renderScene ( ) ;
254
+ }
255
+ }
256
+ }
257
+ } else if ( gl ) {
258
+ stage . tick ( ts - lastTS ) ;
259
+ }
260
+
261
+ lastTS = ts ;
262
+
263
+ if ( xrSession && xrFrame ) {
264
+ xrSession . requestAnimationFrame ( onXRFrame ) ;
265
+ } else {
266
+ requestAnimationFrame ( onXRFrame ) ;
267
+ }
268
+ }
269
+
270
+ ticker . removeTick ( stage ) ;
271
+ renderer . initContext ( ) ;
272
+ requestAnimationFrame ( onXRFrame ) ;
273
+
274
+ initXR ( ) ;
275
+ </ script >
276
+ </ body >
277
+
278
+ </ html >
0 commit comments