{"id":132,"date":"2015-09-07T23:43:22","date_gmt":"2015-09-07T23:43:22","guid":{"rendered":"http:\/\/metareal.net\/blog\/?p=132"},"modified":"2015-09-07T23:43:22","modified_gmt":"2015-09-07T23:43:22","slug":"which-way-is-up","status":"publish","type":"post","link":"http:\/\/metareal.net\/blog\/?p=132","title":{"rendered":"Which Way Is Up?"},"content":{"rendered":"<p>Metareal lets you maneuver unhindered in three dimensional space. There is no gravity, no walking. (And no head-bobbing.) It&#8217;s perhaps like a drone moving through a space station.<\/p>\n<p>But I wanted to keep the movement simple and easy. The familiar four-key-and-mouse maneuvering works pretty well, after all.<\/p>\n<h2>Problem 1: Nearest Ground Plane<\/h2>\n<p>The solution was to treat the mouse relative movements as side-to-side, and up-and-down, <i>relative to the dominant ground plane<\/i>. It turns out that figuring out which ground plane is best is pretty easy! The camera position and angle can be expressed as a conventional 4&#215;4 column major matrix. From this, we look at the second column, which is where the camera&#8217;s Y-Up vector gets mapped to, and see which element has the greatest magnitude. <i>It is that simple!<\/i><\/p>\n<p>For example,<\/p>\n<pre>\n   \/                                  \\\n   |   0.826   0.070  -0.559   48.700 |\n   |   0.563  -0.103   0.820  -22.325 |\n   |   0.000  <b>-0.992<\/b>  -0.125   -0.921 |\n   |   0.000   0.000   0.000    1.000 |\n   \\                                  \/\n<\/pre>\n<p>We can see for this matrix the bolded value -0.992 shows negative-Z as the dominant Up-vector.  So, mouse left and right motions apply, inverted, to the camera&#8217;s global Z-rotation. (Arithmetically, we translate the matrix to origin, rotate around global Z, and translate the matrix back to the camera position.)<\/p>\n<h2>Problem 2: Don&#8217;t go diagonal!<\/h2>\n<p><img decoding=\"async\" src=\"http:\/\/metareal.net\/blog\/wp-content\/uploads\/2015\/09\/oopsDiagonal.jpg\" alt=\"OopsDiagonal\" title=\"oopsDiagonal.jpg\" border=\"0\" width=\"300\"\/><\/p>\n<p>A problem with this approach is you can end up stuck in a &#8220;roll&#8221;, where, on the new ground plane, you&#8217;re not standing upright. Or, rather, <i>you<\/i> are standing upright, and if you spin left and right the floor stays beneath you, but your <i>head<\/i> is tilted. To fix this, we need to discover what is our local-Z-rotation relative to the current ground plane. To correct it, we want to rotate the camera along its line of sight, not changing what it is looking at.<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/metareal.net\/blog\/wp-content\/uploads\/2015\/09\/rollQuestion.jpg\" alt=\"RollQuestion\" title=\"rollQuestion.jpg\" border=\"0\" height=\"270\" \/> <img decoding=\"async\" src=\"http:\/\/metareal.net\/blog\/wp-content\/uploads\/2015\/09\/rollQuestion3.jpg\" alt=\"RollQuestion3\" title=\"rollQuestion3.jpg\" border=\"0\" height=\"270\" \/><\/p>\n<p>A little vector arithmetic does the trick; we cast the camera&#8217;s projected X-axis to the ground plane along the projected Y-axis (up vector), and take the dot product between the X-axis and its line along the ground plane. That&#8217;s how much we need to roll. Easy! But how to apply the correction?<\/p>\n<h2>Problem 3: Correct The Up-Axis Naturally<\/h2>\n<p>I tried three approaches to correcting the up-axis, so that you&#8217;re usually looking at square walls and doors and things.<\/p>\n<ul>\n<li>Manual correction: use some more keyboard keys to roll left and roll right. Terrible! I didn&#8217;t like it, anyway.<\/li>\n<li>Automatic continuous correction: apply some radians-per-second maximum to correcting the roll. It&#8217;s ok, but adds a strange smoothness to your image movement sometimes&#8230;<\/li>\n<li>Proportional correction: apply some correction but limited by how much you&#8217;re moving the camera yourself. This way the camera never moves except when you&#8217;re moving it.<\/li>\n<\/ul>\n<p>I&#8217;m not sure which is best, but I documented all three in a short video.<\/p>\n<p><iframe loading=\"lazy\" width=\"420\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/hW-CJDdskcA?rel=0\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n<p>Also, here&#8217;s some code.<\/p>\n<pre>\n    float getUpVectorAdjustment()\n    {\n        \/\/\/ for now, just print the angle we suspect...\n        MeVec3 cameraXVector = this->cameraMatrix.column(0);\n        MeVec3 cameraYVector = this->cameraMatrix.column(1);\n        \n        float nope;\n        int upAxis = axisOf(cameraYVector, &nope);\n        \n        float t = cameraXVector[upAxis] \/ cameraYVector[upAxis];\n        MeVec3 planePoint = cameraXVector - t * cameraYVector;\n        float thetaDot = cameraXVector.normalize().dot(planePoint.normalize());\n        thetaDot = pinRangeF(thetaDot, -1, +1); \/\/ pin from tiny arithmetic drift...\n        float theta = acosf(thetaDot);\n        if(t > 0)\n            theta = -theta;\n        \n        return theta;\n    }\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Metareal lets you maneuver unhindered in three dimensional space. There is no gravity, no walking. (And no head-bobbing.) It&#8217;s perhaps like a drone moving through a space station. But I wanted to keep the movement simple and easy. The familiar four-key-and-mouse maneuvering works pretty well, after all. Problem 1: Nearest Ground Plane The solution was [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-132","post","type-post","status-publish","format-standard","hentry","category-3"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/132","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=132"}],"version-history":[{"count":1,"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/132\/revisions"}],"predecessor-version":[{"id":133,"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/132\/revisions\/133"}],"wp:attachment":[{"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=132"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=132"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/metareal.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=132"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}