{"id":8554,"date":"2022-05-07T14:20:57","date_gmt":"2022-05-07T12:20:57","guid":{"rendered":"https:\/\/vielzutun.ch\/wordpress\/?p=8554"},"modified":"2022-05-08T19:16:53","modified_gmt":"2022-05-08T17:16:53","slug":"spacemouse-intuitive","status":"publish","type":"post","link":"https:\/\/vielzutun.ch\/wordpress\/?p=8554","title":{"rendered":"Intuitive SpaceMouse driver for Three.js"},"content":{"rendered":"\n<p>Since you came here, you&#8217;ve probably read my <a rel=\"noreferrer noopener\" href=\"https:\/\/vielzutun.ch\/wordpress\/?p=8416\" target=\"_blank\">blog post on accessing the SpaceMouse&#8217;s raw sensor data<\/a> from a JavaScript environment. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"902\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem-1024x902.jpg\" alt=\"\" class=\"wp-image-8406\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem-1024x902.jpg 1024w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem-300x264.jpg 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem-768x676.jpg 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem-1536x1352.jpg 1536w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem-1200x1057.jpg 1200w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem.jpg 2000w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><figcaption>Coordinate system used by SpaceMouse when reporting events<\/figcaption><\/figure>\n\n\n\n<p>Some of you may be scratching their heads why one would want to access raw sensor data in the first place, or how they might be put to use. Others may feel intimidated by the sheer unlimited freedom, which raw data from a 6DoF input device allows. I&#8217;m writing this post for you!<\/p>\n\n\n\n<p>Basically, there are two paradigms for using a SpaceMouse device in a meaningful way. After all, this is about <a href=\"https:\/\/en.wikipedia.org\/wiki\/Six_degrees_of_freedom\" target=\"_blank\" rel=\"noreferrer noopener\">6 degrees of freedom<\/a>, which incidentally matches the count of degrees of freedom in 3D-space, namely three axes of translation and three axes of rotation.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The first of the two paradigms is the <strong>&#171;Object&#187; paradigm<\/strong>. You use the puck of a SpaceMouse like you would be interacting with the object itself. Whatever shifting, twisting or tilting you do to the puck, is reflected by a corresponding translation and\/or rotation of the object.<\/li><li>The second of the two paradigms is the <strong>&#171;Camera&#187; paradigm<\/strong>. You use the puck of a SpaceMouse like you would be interacting with the camera itself. Whatever shifting, twisting or tilting you do to the puck, is reflected by a corresponding position and\/or orientation change of the camera.<\/li><\/ul>\n\n\n\n<p>In this post, I&#8217;m presenting a simple demo program for a SpaceMouse-with-Three.js application which processes <a rel=\"noreferrer noopener\" href=\"https:\/\/vielzutun.ch\/wordpress\/?p=8416\" target=\"_blank\">raw sensor data of a SpaceMouse<\/a>  into an intuitive control of five axes in 3D space. Evaluation of the sixth axis is also implemented, but disabled by default. More on this further down this post.<\/p>\n\n\n\n<p>This demo program works with the current (as of this writing) <strong>version 0.140.0<\/strong> of three.js. See the respective <em><strong>import <\/strong><\/em>statement in source code.<\/p>\n\n\n\n<p><strong>Rotation:<\/strong><br>Each axis of rotation can be independently controlled, without tainting the rotation of the remaining axes, and without sacrificing their simultaneous control. Control of <a href=\"https:\/\/simple.wikipedia.org\/wiki\/Pitch,_yaw,_and_roll#\/media\/File:Yaw_Axis_Corrected.svg\">Roll-axis<\/a> has been disabled by default, but can easily be enabled by setting one variable to \u2018true\u2019. I suggest novices at <a href=\"https:\/\/en.wikipedia.org\/wiki\/Six_degrees_of_freedom\">6DoF<\/a> devices keep the default setting until they have acquired the necessary level of <a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eye%E2%80%93hand_coordination\" target=\"_blank\">eye-hand coordination<\/a> skills. Yes, folks, it does take some practice to become proficient at handling a 6Dof input device. But it&#8217;s clearly doable and it&#8217;s worth it, imo.<\/p>\n\n\n\n<p>Attached to the camera is an (invisible) orthonormal coordinate system, aligned with the camera\u2019s line-of-sight and up-direction, which I\u2019m showing from a 3rd-person perspective for documentation purposes only:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/3rd_person_view.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"799\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/3rd_person_view-1024x799.png\" alt=\"\" class=\"wp-image-8593\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/3rd_person_view-1024x799.png 1024w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/3rd_person_view-300x234.png 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/3rd_person_view-768x600.png 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/3rd_person_view-1200x937.png 1200w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/3rd_person_view.png 1322w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/a><figcaption>3rd-person view of camera frustum and attached camera axes helper<\/figcaption><\/figure>\n\n\n\n<p>Note the red\/blue\/green camera-axesHelper:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>blue:  line-of-sight\ngreen: up-vector\nred:   side-vector<\/code><\/pre>\n\n\n\n<p>Also note, that for the sake of intuitiveness I&#8217;m intentionally mixing systems of reference when implementing rotations:<\/p>\n\n\n\n<p>I&#8217;m processing a twist of the SpaceMouse puck around its z-axis (see introductory image) as a camera rotation about the Three.js <strong>world y-axis<\/strong>, which is what you as a human do when looking left or right around your own vertical axis.<\/p>\n\n\n\n<p>Tilting of the Spacemouse puck around its x-axis is processed as a camera rotation about its <strong>local x-axis<\/strong>, which is what you as a human do when looking  down to your feet or up into the sky.<\/p>\n\n\n\n<p>Tilting of the Spacemouse puck around its y-axis is processed as a camera rotation about its <strong>local (Three.js) z-axis<\/strong>, which is equivalent to the camera&#8217;s line of sight. There is no equivalent in typical human experience for this type of roll rotation. Except maybe you&#8217;re an experienced member of an aerobatics team or a fighter jet pilot. <\/p>\n\n\n\n<p>Even the poster children of banking movements, motorcycle riders, (should) avoid banking their heads during cornering:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"891\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/Bike_cornering-1024x891.jpg\" alt=\"\" class=\"wp-image-8605\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/Bike_cornering-1024x891.jpg 1024w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/Bike_cornering-300x261.jpg 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/Bike_cornering-768x668.jpg 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/Bike_cornering.jpg 1032w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><figcaption>Even bikers avoid &#171;roll&#187; rotation of their heads during cornering<\/figcaption><\/figure>\n\n\n\n<p>Image source: <a href=\"https:\/\/motofomo.com\/best-motorcycle-riding-books\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/motofomo.com\/best-motorcycle-riding-books\/<\/a><\/p>\n\n\n\n<p>And if one looks closely, birds keep their heads level during curved flightpaths, too:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"533\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/Bird_cornering.jpg\" alt=\"\" class=\"wp-image-8610\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/Bird_cornering.jpg 800w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/Bird_cornering-300x200.jpg 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/Bird_cornering-768x512.jpg 768w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px\" \/><figcaption>\u00a9Douglas Herr, www.wildlightphoto.com<\/figcaption><\/figure>\n\n\n\n<p>So all in all, living creatures are apparently not meant to perform roll rotations. If you do it anyways, you&#8217;ll be leaving the realm of being &#171;intuitive&#187;. That&#8217;s why I&#8217;m disabling this by default.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>Translation:<\/strong><br>When rendering the 1st-person view of the SpaceMouse-controlled camera, puck-displacement forward-backward corresponds to the camera dollying along the line of sight (blue). Likewise will a puck displacement along the red line effect a \u201chorizontal\u201d pan in screen space, and a puck-displacement along the green line a \u201cvertical\u201d pan in screen space, irrespective of the current camera orientation.<\/p>\n\n\n\n<p>Gamers among you will recognise this as the &#171;<a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/First-person_(video_games)\" target=\"_blank\">1st-person<\/a>&#187; perspective. Which is, what makes this perspective so intuitive. \ud83d\ude0e<\/p>\n\n\n\n<p class=\"has-medium-font-size\"><strong>Development<\/strong><\/p>\n\n\n\n<p>Development of this driver was not as straightforward as it might seem. I intentionally split the sensor data evaluation into a rotation part and a translation part. Rotation sets the cameras <strong>orientation<\/strong>, that is: a 3D direction into which the camera is pointing. Do not confuse this with the Three.js <a rel=\"noreferrer noopener\" href=\"https:\/\/threejs.org\/docs\/index.html?q=Orbit#examples\/en\/controls\/OrbitControls.target\" target=\"_blank\">ObitControls &#171;target&#187;<\/a>, which is a 3D <strong>point<\/strong> in space. In OrbitControls, the camera&#8217;s angles of rotation are constantly re-computed to make sure, the camera is constantly looking at the target point. In my driver, the camera&#8217;s orientation (line of sight) is maintained during camera translations: the line of sight is shifted in parallel &#8211; absent any rotational input from the SpaceMouse.<\/p>\n\n\n\n<p>While my concept was straightforward, there are always opportunities to confuse sines with cosines, positive or negative signs and many more. When I tried to identify such errors when looking at the view from an unfinished and still erroneous camera control and conclude backwards as to why the view was different from my expectation, I got dizzy very quickly. Until I finally came up with the idea, to view the SpaceMouse controlled camera (including its <a rel=\"noreferrer noopener\" href=\"https:\/\/threejs.org\/docs\/index.html?q=camera#api\/en\/helpers\/CameraHelper\" target=\"_blank\">frustum<\/a> and local coordinate system) from a fixed <strong><a rel=\"noreferrer noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/Third-Person-Perspektive\" target=\"_blank\">3rd person perspective<\/a><\/strong>. Which gave me the much needed insight into the nature of remaining implementation errors.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Except for an import of Three.js, my one-file demo is completely self contained and comprises approx. 350 SLOC, including comments and minimal HTML (200 LLOC).<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>Prerequisites:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/3Dconnexion\" target=\"_blank\">SpaceMouse or SpaceNavigator<\/a><\/li><li>compatibel (i.e. <a href=\"https:\/\/caniuse.com\/webhid\">WebHID enabled<\/a>) browser.<\/li><li>Internet access, to resolve the import of three.js<\/li><\/ul>\n\n\n\n<p><\/p>\n\n\n\n<div class=\"wp-block-file\"><a id=\"wp-block-file--media-e924fd21-cc72-40ac-b67d-34418e77dd2a\" href=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/WebHID.html\">WebHID.html<\/a><a href=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/05\/WebHID.html\" class=\"wp-block-file__button\" download aria-describedby=\"wp-block-file--media-e924fd21-cc72-40ac-b67d-34418e77dd2a\">Herunterladen<\/a><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Since you came here, you&#8217;ve probably read my blog post on accessing the SpaceMouse&#8217;s raw sensor data from a JavaScript environment. Some of you may be scratching their heads why one would want to access raw sensor data in the first place, or how they might be put to use. Others may feel intimidated by &hellip; <a href=\"https:\/\/vielzutun.ch\/wordpress\/?p=8554\" class=\"more-link\"><span class=\"screen-reader-text\">&#8220;Intuitive SpaceMouse driver for Three.js&#8221; <\/span>weiterlesen<\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_stc_notifier_status":"","_stc_notifier_sent_time":"","_stc_notifier_request":false,"_stc_notifier_prevent":false,"_stc_subscriber_keywords":"","_stc_subscriber_search_areas":"","footnotes":""},"categories":[184],"tags":[187,188,185,186,178,189],"class_list":["post-8554","post","type-post","status-publish","format-standard","hentry","category-computergrafik","tag-3dconnexion","tag-javascript","tag-spacemouse","tag-spacenavigator","tag-three-js","tag-webhid"],"_links":{"self":[{"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/8554","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=8554"}],"version-history":[{"count":63,"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/8554\/revisions"}],"predecessor-version":[{"id":8631,"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/8554\/revisions\/8631"}],"wp:attachment":[{"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=8554"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=8554"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=8554"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}