1+ import * as THREE from 'https://unpkg.com/[email protected] /build/three.module.js' ; 2+
3+ import { OrbitControls } from 'https://unpkg.com/[email protected] /examples/jsm/controls/OrbitControls.js' ; 4+ import { GLTFLoader } from 'https://unpkg.com/[email protected] /examples/jsm/loaders/GLTFLoader.js' 5+ import { VRButton } from 'https://unpkg.com/[email protected] /examples/jsm/webxr/VRButton.js' 6+
7+ class TesseractViewer {
8+
9+ constructor ( )
10+ {
11+ this . _scene = null ;
12+ this . _clock = null ;
13+ this . _camera = null ;
14+ this . _renderer = null ;
15+ this . _light = null ;
16+ this . _scene_etag = null ;
17+ this . _trajectory_etag = null ;
18+ this . _disable_update_trajectory = false ;
19+ this . _animation_mixer = null ;
20+ this . _animation = null ;
21+ this . _animation_action = null ;
22+ this . _root_z_up = null ;
23+ this . _root_env = null ;
24+
25+ }
26+
27+ async createScene ( ) {
28+ this . _scene = new THREE . Scene ( ) ;
29+ this . _clock = new THREE . Clock ( ) ;
30+
31+ const camera = new THREE . PerspectiveCamera ( 45 , window . innerWidth / window . innerHeight , 0.1 , 1000 ) ;
32+ camera . position . x = 3 ;
33+ camera . position . y = 3 ;
34+ camera . position . z = - 1.5 ;
35+ this . _camera = camera ;
36+
37+ const renderer = new THREE . WebGLRenderer ( { antialias : true } ) ;
38+ renderer . setPixelRatio ( window . devicePixelRatio ) ;
39+ renderer . setSize ( window . innerWidth , window . innerHeight ) ;
40+ renderer . outputEncoding = THREE . sRGBEncoding ;
41+ renderer . xr . enabled = true ;
42+
43+ renderer . setClearColor ( "#000000" ) ;
44+
45+ this . _renderer = renderer ;
46+
47+
48+ window . addEventListener ( 'resize' , onWindowResize , false ) ;
49+
50+ function onWindowResize ( ) {
51+
52+ camera . aspect = window . innerWidth / window . innerHeight ;
53+ camera . updateProjectionMatrix ( ) ;
54+
55+ renderer . setSize ( window . innerWidth , window . innerHeight ) ;
56+ }
57+
58+ const light = new THREE . HemisphereLight ( 0xffffbb , 0x202018 , 1 ) ;
59+ this . _scene . add ( light ) ;
60+ this . _light = light ;
61+
62+ document . body . appendChild ( renderer . domElement ) ;
63+
64+ const controls = new OrbitControls ( camera , renderer . domElement ) ;
65+
66+ document . body . appendChild ( VRButton . createButton ( renderer ) ) ;
67+
68+ renderer . setAnimationLoop ( this . render . bind ( this ) ) ;
69+
70+ const gridHelper = new THREE . GridHelper ( 10 , 10 ) ;
71+ this . _scene . add ( gridHelper ) ;
72+
73+ const root_z_up = new THREE . Object3D ( ) ;
74+ root_z_up . rotateX ( - Math . PI / 2.0 ) ;
75+ this . _scene . add ( root_z_up ) ;
76+
77+ const root_env = new THREE . Object3D ( ) ;
78+ root_z_up . add ( root_env ) ;
79+
80+ this . _root_z_up = root_z_up ;
81+ this . _root_env = root_env ;
82+
83+ this . _animation_mixer = new THREE . AnimationMixer ( this . _root_env ) ;
84+
85+ await this . updateScene ( ) ;
86+
87+ let _this = this ;
88+ const queryString = window . location . search ;
89+ const urlParams = new URLSearchParams ( queryString ) ;
90+ let do_update = true ;
91+ if ( urlParams . has ( "noupdate" ) ) {
92+ if ( urlParams . get ( "noupdate" ) === "true" ) {
93+ do_update = false ;
94+ }
95+ }
96+ if ( do_update ) {
97+ setTimeout ( ( ) => _this . updateTrajectory ( ) , 2000 ) ;
98+ }
99+
100+ }
101+
102+ render ( ) {
103+ // Render the scene
104+ this . _renderer . render ( this . _scene , this . _camera ) ;
105+
106+ var delta = this . _clock . getDelta ( ) ;
107+ if ( this . _animation_mixer ) this . _animation_mixer . update ( delta ) ;
108+ } ;
109+
110+ async updateScene ( ) {
111+ let fetch_res ;
112+ try {
113+ fetch_res = await fetch ( "tesseract_scene.gltf" , { method : "HEAD" } ) ;
114+ }
115+ catch ( _a ) {
116+ let _this = this ;
117+ setTimeout ( ( ) => _this . updateScene ( ) , 1000 ) ;
118+ return ;
119+ }
120+ let etag = fetch_res . headers . get ( 'etag' ) ;
121+ if ( etag !== null ) {
122+ if ( this . _scene_etag !== null ) {
123+ if ( this . _scene_etag != etag ) {
124+ this . _scene_etag = null ;
125+ let _this = this ;
126+ setTimeout ( ( ) => _this . updateScene ( ) , 0 ) ;
127+ return ;
128+ }
129+ else {
130+ let _this = this ;
131+ setTimeout ( ( ) => _this . updateScene ( ) , 1000 ) ;
132+ return ;
133+ }
134+ }
135+ }
136+ const loader = new GLTFLoader ( ) ;
137+
138+ let gltf = await new Promise ( ( resolve , reject ) => {
139+ loader . load ( 'tesseract_scene.gltf' , data => resolve ( data ) , null , reject ) ;
140+ } ) ;
141+
142+ if ( this . _root_env )
143+ {
144+ for ( var i = this . _root_env . children . length - 1 ; i >= 0 ; i -- ) {
145+ let obj = this . _root_env . children [ i ] ;
146+ this . _root_env . remove ( obj ) ;
147+ }
148+ }
149+
150+ this . _root_env . add ( gltf . scene ) ;
151+
152+ if ( gltf . animations . length > 0 )
153+ {
154+
155+ this . _animation_mixer . stopAllAction ( ) ;
156+ this . _animation_mixer . uncacheRoot ( this . _root_env ) ;
157+
158+ let animation_action = this . _animation_mixer . clipAction ( gltf . animations [ 0 ] ) ;
159+ animation_action . play ( ) ;
160+
161+ this . _animation = gltf . animations [ 0 ] ;
162+ this . _animation_action = animation_action ;
163+ }
164+
165+ if ( etag !== null ) {
166+ this . _scene_etag = etag ;
167+ let _this = this ;
168+ setTimeout ( ( ) => _this . updateScene ( ) , 1000 ) ;
169+ }
170+ }
171+
172+ async updateTrajectory ( ) {
173+
174+ if ( this . _disable_update_trajectory ) {
175+ return ;
176+ }
177+ let fetch_res ;
178+ let _this = this ;
179+ try {
180+ fetch_res = await fetch ( "tesseract_trajectory.json" , { method : "HEAD" } ) ;
181+ }
182+ catch ( _a ) {
183+ setTimeout ( ( ) => _this . updateTrajectory ( ) , 1000 ) ;
184+ return ;
185+ }
186+ if ( ! fetch_res . ok ) {
187+ setTimeout ( ( ) => _this . updateTrajectory ( ) , 1000 ) ;
188+ return ;
189+ }
190+ let etag = fetch_res . headers . get ( 'etag' ) ;
191+ if ( etag == null || this . _trajectory_etag == etag ) {
192+ console . log ( "No updated trajectory" ) ;
193+ setTimeout ( ( ) => _this . updateTrajectory ( ) , 1000 ) ;
194+ return ;
195+ }
196+ try {
197+ let trajectory_response = await fetch ( "./tesseract_trajectory.json" ) ;
198+ let trajectory_json = await trajectory_response . json ( ) ;
199+ console . log ( trajectory_json )
200+ this . setTrajectory ( trajectory_json . joint_names , trajectory_json . trajectory ) ;
201+ }
202+ catch ( e ) {
203+ console . log ( "Trajectory not available" ) ;
204+ console . log ( e ) ;
205+ }
206+ if ( etag !== null ) {
207+ this . _trajectory_etag = etag ;
208+ setTimeout ( ( ) => _this . updateTrajectory ( ) , 1000 ) ;
209+ }
210+
211+ }
212+ disableUpdateTrajectory ( ) {
213+ this . _disable_update_trajectory = true ;
214+ }
215+ enableUpdateTrajectory ( ) {
216+ this . _disable_update_trajectory = false ;
217+ }
218+ setJointPositions ( joint_names , joint_positions ) {
219+ let trajectory = [ [ ...joint_positions , 0 ] , [ ...joint_positions , 100000 ] ] ;
220+ this . setTrajectory ( joint_names , trajectory ) ;
221+ }
222+
223+ setTrajectory ( joint_names , trajectory ) {
224+
225+ this . _animation_mixer . stopAllAction ( ) ;
226+ this . _animation_mixer . uncacheRoot ( this . _root_env ) ;
227+
228+ let anim = this . trajectoryToAnimation ( joint_names , trajectory ) ;
229+ let animation_action = this . _animation_mixer . clipAction ( anim ) ;
230+ animation_action . play ( ) ;
231+
232+ this . _animation = anim ;
233+ this . _animation_action = animation_action ;
234+ }
235+
236+ trajectoryToAnimation ( joint_names , trajectory ) {
237+ let joints = this . findJoints ( joint_names ) ;
238+ let tracks = [ ]
239+ joint_names . forEach ( ( joint_name , joint_index ) => {
240+ let joint = joints [ joint_name ] ;
241+ switch ( joint . type ) {
242+ case 1 :
243+ {
244+ let values = [ ] ;
245+ let times = [ ]
246+ trajectory . forEach ( ee => {
247+ let axis_vec = new THREE . Vector3 ( ) . fromArray ( joint . axes ) ;
248+ let quat = new THREE . Quaternion ( ) . setFromAxisAngle ( axis_vec , ee [ joint_index ] ) ;
249+ let quat_array = quat . toArray ( ) ;
250+ values . push ( ...quat_array ) ;
251+ times . push ( ee [ ee . length - 1 ] )
252+ } ) ;
253+ let track = new THREE . QuaternionKeyframeTrack ( joint . joint . name + ".quaternion" , times , values ) ;
254+ tracks . push ( track ) ;
255+ }
256+ break ;
257+ case 2 :
258+ {
259+ let values = [ ] ;
260+ let times = [ ]
261+ trajectory . forEach ( ee => {
262+ let axis_vec = new THREE . Vector3 ( ) . fromArray ( joint . axes ) ;
263+ let vec = axis_vec . multiplyScalar ( ee [ joint_index ] ) ;
264+ let vec_array = vec . toArray ( ) ;
265+ values . push ( ...vec_array ) ;
266+ times . push ( ee [ ee . length - 1 ] )
267+ } ) ;
268+ let track = new THREE . VectorKeyframeTrack ( joint . joint . name + ".position" , times , values ) ;
269+ tracks . push ( track ) ;
270+ }
271+ break ;
272+ default :
273+ throw new Error ( "Unknown joint type" ) ;
274+ }
275+ } ) ;
276+
277+ let animation_clip = new THREE . AnimationClip ( "tesseract_trajectory" , - 1 , tracks ) ;
278+
279+ return animation_clip ;
280+ }
281+
282+ findJoints ( joint_names )
283+ {
284+ let ret = { }
285+ this . _root_env . traverse ( tf => {
286+ if ( tf . userData && tf . userData [ "tesseract_joint" ] )
287+ {
288+ let t_j = tf . userData [ "tesseract_joint" ] ;
289+
290+ if ( joint_names && joint_names . indexOf ( t_j [ "name" ] ) == - 1 ) {
291+ return ;
292+ }
293+ let t = { } ;
294+ t . joint_name = t_j [ "name" ] ;
295+ t . node_name = tf . name ;
296+ t . joint = tf ;
297+ t . axes = t_j . axis ;
298+ t . type = t_j . type ;
299+ ret [ t . joint_name ] = t ;
300+ }
301+ } ) ;
302+ return ret ;
303+ }
304+ }
305+
306+ window . addEventListener ( "DOMContentLoaded" , async function ( ) {
307+ let viewer = new TesseractViewer ( ) ;
308+ window . tesseract_viewer = viewer ;
309+ await viewer . createScene ( ) ;
310+ window . addEventListener ( "message" , function ( event ) {
311+ let data = event . data ;
312+ if ( data . command === "joint_positions" ) {
313+ viewer . disableUpdateTrajectory ( ) ;
314+ viewer . setJointPositions ( data . joint_names , data . joint_positions ) ;
315+ }
316+ if ( data . command === "joint_trajectory" ) {
317+ viewer . disableUpdateTrajectory ( ) ;
318+ viewer . setTrajectory ( data . joint_names , data . joint_trajectory ) ;
319+ }
320+ } ) ;
321+ viewer . render ( ) ;
322+ } )
0 commit comments