<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Casey Muratori &#8211; The Witness</title>
	<atom:link href="https://the-witness.net/news/author/casey/feed/" rel="self" type="application/rss+xml" />
	<link>https://the-witness.net/news</link>
	<description>Explore an abandoned island.</description>
	<lastBuildDate>Mon, 21 Jan 2013 05:12:03 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.9</generator>
	<item>
		<title>How do you say &#8220;plane&#8221; in The Witness?</title>
		<link>https://the-witness.net/news/2013/01/how-do-you-say-plane-in-the-witness/</link>
					<comments>https://the-witness.net/news/2013/01/how-do-you-say-plane-in-the-witness/#comments</comments>
		
		<dc:creator><![CDATA[Casey Muratori]]></dc:creator>
		<pubDate>Mon, 21 Jan 2013 02:54:07 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<guid isPermaLink="false">https://the-witness.net/news/?p=2052</guid>

					<description><![CDATA[Just before Christmas break, I fixed a bug in the transform manipulator and checked it in. Having a transform manipulator is a nice addition to any editor, and we were all happy to have it working. But shortly thereafter, I had to leave for a cross-country family Christmas trip. This …<p class="continue-reading-button"> <a class="continue-reading-link" href="https://the-witness.net/news/2013/01/how-do-you-say-plane-in-the-witness/">Continue reading<i class="crycon-right-dir"></i></a></p>]]></description>
										<content:encoded><![CDATA[<p>Just before Christmas break, I <a href="https://the-witness.net/news/2012/12/a-cliffside-cliffhanger/">fixed a bug in the transform manipulator</a> and checked it in. Having a transform manipulator is a nice addition to any editor, and we were all happy to have it working. But shortly thereafter, I had to leave for a cross-country family Christmas trip. This meant no more real <i>Witness</i> work for about ten days, because my laptop only has Linux on it, and right now the Linux version of <i>The Witness</i> is still under construction.</p>
<p>This hiatus was the primary motivation behind writing my previous entry as a cliffhanger: although I had fixed the bug, I didn’t have time to adequately document the bug via debugger inspection, so it would have been hard to write anything meaningful showing how the bug manifested itself in practice. I wanted to wait until I had a chance to go through every last little bit of it, so I could be as specific as possible. In that sense, it was meant to be a bit of a cliffhanger for me as well.</p>
<p>But as it turned out, it was much more of a cliffhanger than I’d originally anticipated.</p>
<p><span id="more-2052"></span></p>
<h2>Good News, Bad News</h2>
<p>The good news: congratulations to those of you who posted “floating point precision” as a suggestion for the cause of the bug. You aren’t any dumber than I am.</p>
<p>The bad news: we’re all dumber than we should be.</p>
<p>For those of you who don’t already know all about floating point precision problems, I’ll explain the problem we all thought we were solving. The original code, verbatim, looked like this:</p>
<pre>bool ray_vs_plane(Vector3 *xpoint, Vector3 const &amp;p0, Vector3 const &amp;dir,
                  Plane3 const &amp;plane) {
    Vector3 n = plane.get_normal();

    float denom = dot_product(n, dir);
    float epsilon = 0.0001f;
    if (fabs(denom) &lt; epsilon) return false;

    float t = - ((plane.d + dot_product(n, p0)) / denom);
    if (t &lt; 0) return false;

    *xpoint = p0 + dir * t;
    return true;
}</pre>
<p>To fix the bug, I wrote a modified version to use for the transform manipulator:</p>
<pre>bool ray_vs_plane(Vector3 *xpoint, Vector3 const &amp;p0, Vector3 const &amp;dir,
                  Vector3 const &amp;plane_point, Vector3 const &amp;plane_n)
{
    Vector3 local_p0 = p0 - plane_point;

    float denom = dot_product(plane_n, dir);
    float epsilon = 0.0001f;
    if (fabs(denom) &lt; epsilon) return false;

    float t = - (dot_product(plane_n, local_p0) / denom);

    *xpoint = p0 + dir * t;
    return true;
}</pre>
<p>I’ve done exactly the same operation, but instead of using the d value of the plane equation to solve for t, I just move the ray itself to be centered around a point on the plane, so that the d value is implicitly zero. My reasoning was very simple: if I subtract two things that I know are close together (the camera location and the picked point on the manipulator), I will have less cancellation error than if I subtract two things that may be wildly different in scale (the d value and the dot product of the camera location and the plane normal).</p>
<p>This is pretty weak sauce as far as numerical precision improvements go. I am far from an expert on the subject (<a href="http://www.amazon.com/Forman-S.-Acton/e/B001IYTXGY/ref=sr_ntt_srch_lnk_2?qid=1358735012&amp;sr=8-2">read some Forman Acton</a>, for example, if you want to see how the pros roll). But, when I did this, it fixed the bug perfectly, so I assumed, at least for the moment, that it must be a reasonable fix.</p>
<p>But, honestly, I didn’t really have a good feeling about it.</p>
<h2>Christmas Stew</h2>
<p>Sometimes, when there’s a lot to do, you’ll live with a bug fix because it works, rather than because it makes sense. But invariably, at least for me, I end up going back to look more closely at something if I don’t feel like I fully understood it. I am sure this is mostly just an obsessive personality trait, but regardless, I rarely find the time to be wasted, because I almost always learn something from the process.</p>
<p>In this specific case, I didn’t have time to look into the problem in depth before I left. I planned to do so for the eventual writeup, but of course, I couldn’t do much without being able to run the code, and while I was on Christmas break, that wasn’t an option. Unfortunately, this meant that I had the entire Christmas break to stew about the bug that I wasn’t sure I’d really fixed properly.</p>
<p>From the beginning, the thing that didn’t sit right with me about the fix was that when Jon originally reported the problem to me, he didn’t mention anything about it happening more on the outskirts of the island than on the interior. Jon’s a very experienced programmer, so if something like that were happening, he would probably notice and include that in the report, because he’d know it would help me find the bug faster.</p>
<p>Instead, it seemed pretty clear that the bug was related more to the camera angle than the position in the world. While obviously the camera angle affects the input values, and could have a number of effects on the numerical precision of the program, it’s not intuitively the kind of thing that I would expect. If these sorts of algorithms were that sensitive in this way, I would have expected to encounter similar problems with camera-relative plane intersections many times before — but I never had.</p>
<p>The more I thought about that, the more it gnawed at me that I couldn’t go see exactly what was happening and prove to myself why the fix worked. I did have the game on my laptop, because I’d done some initial Linux porting work, so I realized that maybe, if I was lucky, I could reconstruct the problem without actually being able to load and run the editor.</p>
<p>That is when I became very thankful that I had been writing these blog entries, because it meant I had stopped to take this screenshot before I left, specifically so I could do the writeup over break:</p>
<p><a href="https://the-witness.net/news/2012/12/a-cliffside-cliffhanger/witness3_yworks/" rel="attachment wp-att-1995"><img class="aligncenter size-large wp-image-1995" alt="witness3_yworks" src="https://the-witness.net/news/wp-content/uploads/2012/12/witness3_yworks-512x288.jpg" width="512" height="288" /></a></p>
<p>Do you see what I see? That lovely entity number up in the corner is pure gold for step one of reconstructing the problem. Because I could see the entity number, I was able to go into the repository and get the data file that says where that entity is placed in the world. That let me know the numbers of the center point of the manipulator: -304.114, 193.4923, 4.7582.</p>
<p>If you’re an experienced 3D programmer, then you probably are thinking the same thing that I was thinking when I saw those: how the hell could I be having numerical precision problems with such low numbers? <i>The Witness’s</i> scale is one unit per meter, which means that, at the scale of the dragging we’re talking about, there should be absolutely no problems with solving a single plane equation, regardless of how sloppily you did it.</p>
<p>But if that was the case, then how did I fix the bug? This lead me to step two of reproducing the problem: writing a test program. I wrote a little C program that would take numbers around that point and, for a large set of programmatically generated planes and rays, perform the intersection using both the old ray intersection routine and my “improved” one.</p>
<p>Perhaps unsurprisingly, these results reinforced my suspicions that there wasn’t enough of a difference between the two routines to account for the original bug. In all the tests, my “improved” routine was no more than one decimal digit more precise; so if the original routine produced intersection points that were actually, say, 0.0001 units away from the plane, the new routine might produce ones that were 0.00001 away.</p>
<p>Clearly, the fix just plain shouldn’t have worked. But it did.</p>
<p>Why?</p>
<h2>The Wrong Part of the Post</h2>
<p>I would love to be able to say that I guessed the real reason before stepping through the code, but I absolutely didn’t.</p>
<p>When I returned from Christmas break, I eagerly booted up the <i>Witness</i> and stepped through the code very carefully, keeping accurate track of all the numbers as I went, waiting to find that super-elusive numerical thing that I had overlooked. To my complete surprise, it turned out to have <i>absolutely nothing to do with precision</i>.</p>
<p>In fact, somehow, although I didn’t know it at the time, I actually did write about the root cause of the problem in my original post. It just wasn’t in the part that I thought. Indeed, rather than <i>Always Look Behind You</i> foretelling the eventual solution, it was <i>Foreign Languages</i> that contained the hint: due to my naivete with <i>The Witness</i> codebase, I misunderstood what it was doing with planes. It turns out that this one line from the original routine was the entire culprit:</p>
<pre>    Vector3 n = plane.get_normal();</pre>
<p>You will notice that it only occurs in the buggy version of the ray intersection code. In my version, I couldn’t use the plane structure, because I didn’t want an “a, b, c, d” representation of the plane. I wanted “a, b, c, p”, if you will, where p is an actual 3D point on the plane. So I never called get_normal().</p>
<p>Why does this matter? Well, upon stepping through all the code, I realized that get_normal(), being true to its name, takes the unusual step of <i>normalizing</i> the a, b, c values of the plane before returning them to you as a vector. This means that the a, b, c values of the plane that you pass in when you create the plane aren’t the ones you get out.</p>
<p>That wouldn’t be a problem if it weren’t for the fact that, when you create one of these planes, it <i>does not</i> normalize the values you pass in:</p>
<pre>    Vector3 plane_n = cross_product(camera_back, translation_axis);
    plane_n = cross_product(plane_n, translation_axis);
    grabbing_plane = plane_from_point_and_normal(hit_pos, plane_n);</pre>
<p>This is my fault on its face, because hey, it <i>does say normal</i>, and normals are traditionally supposed to be normalized. But in keeping with my reference to foreign languages, this is something I really just didn’t think about, because I am used to the way I’ve always done things with planes: nothing is normalized automatically, everything is just left unnormalized, because back in the day, normalization was very expensive and you never did it in a math routine unless it was absolutely necessary.</p>
<p>So plane_from_point_and_normal was actually doing what I thought it would: it just stored plane_n as the a, b, c values, and computed d with the inner product. But get_normal() was not doing what I thought it would. Rather than just returning me the vector {a,b,c}, it was normalizing them before giving them to me, which meant <i>all</i> my calculations with them would no longer agree with the d value computed in plane_from_point_and_normal.</p>
<p>And therein, clearly, lies the entirety of the bug.</p>
<p>Just to underscore how much of a native language issue this really is, when I showed Jon the preview of this article, he said, “If you are thinking of the plane as a geometric object, of course the normal is unit length, what else would it be?? But if you are thinking of it as some specific equation all the time, then it is natural to care about the a, b, c, you put in.” So I can only assume, were he using my math library, he would’ve been tripped up by the fact that I <i>don’t</i> normalize normals automatically, exactly the inverse of the problem I encountered with his.</p>
<p>I honestly don’t believe that this sort of thing is a case of us being presumptuous or sloppy; it’s just the price you have to pay for being able to program complex things at a reasonable speed. If we constantly have to think about what it means to work with a vector, or a plane, or a quaternion, we’re going to be a lot slower. It’s much more efficient to have a consistent perspective, and always rely on it.</p>
<p>And of course, the cost is bugs like this — but that’s a very small cost relative to the benefits.</p>
<h2>Partial Credit</h2>
<p>So, where does that leave you?</p>
<p>Clearly, automatic partial credit goes to everyone who wrote in suggesting that numerical precision was the problem. You came to the same conclusion I did with the information I knew at the time, which was all you had to go on. You certainly can’t be blamed for not finding the real bug since you would have needed the source code to the <i>Witness</i> math library to even see it. And it is true that the numerical precision of the routine could have been improved, as I believe I was able to verify separately with my test program.</p>
<p>But by the same token, nobody gets automatic full credit. There were a lot of reasons why, even with only the information in the original article, you could have said, “I don’t think it’s numerical precision, Muratori.” Maybe I am ascribing overly heroic attributes to the man, but I feel like <a href="http://en.wikipedia.org/wiki/Forman_S._Acton">Forman Acton</a> would have known, from looking at the plane intersection snippet, that there wasn’t any way to lose enough precision in there to account for the bug as I described it, at least not in any way that would have been substantially improved by the suggested fixes people wrote in (or the fix I actually did, for that matter).</p>
<p>So in order to actually get full credit, you’re going to have to earn it, I think, but on the honor system. Ask yourself, if you had encountered this bug, and you had “fixed” it as I had, would you have gone back and figured out what was really happening before moving on to something else?</p>
<p>If you can honestly answer yes, then you get full credit, for this is about as good as you can hope to do as a working programmer, I think. You’re no Forman Acton, of course. . . but then again, who among us is?</p>
]]></content:encoded>
					
					<wfw:commentRss>https://the-witness.net/news/2013/01/how-do-you-say-plane-in-the-witness/feed/</wfw:commentRss>
			<slash:comments>28</slash:comments>
		
		
			</item>
		<item>
		<title>A Cliffside Cliffhanger</title>
		<link>https://the-witness.net/news/2012/12/a-cliffside-cliffhanger/</link>
					<comments>https://the-witness.net/news/2012/12/a-cliffside-cliffhanger/#comments</comments>
		
		<dc:creator><![CDATA[Casey Muratori]]></dc:creator>
		<pubDate>Tue, 25 Dec 2012 01:24:36 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<guid isPermaLink="false">https://the-witness.net/news/?p=1989</guid>

					<description><![CDATA[This is a little mystery about a rock that refused to move. The rock lives off the coast of The Witness, just beyond the desert cliffs: I do not know who made this rock or how long it has been there, but when I first happened upon it, I found …<p class="continue-reading-button"> <a class="continue-reading-link" href="https://the-witness.net/news/2012/12/a-cliffside-cliffhanger/">Continue reading<i class="crycon-right-dir"></i></a></p>]]></description>
										<content:encoded><![CDATA[<p>This is a little mystery about a rock that refused to move. The rock lives off the coast of <i>The Witness</i>, just beyond the desert cliffs:</p>
<p><a href="https://the-witness.net/news/2012/12/a-cliffside-cliffhanger/witness3_rock/" rel="attachment wp-att-1993"><img class="aligncenter size-large wp-image-1993" alt="witness3_rock" src="https://the-witness.net/news/wp-content/uploads/2012/12/witness3_rock-512x288.jpg" width="512" height="288" /></a></p>
<p>I do not know who made this rock or how long it has been there, but when I first happened upon it, I found that it resides in a nicely isolated part of the island where there’s nothing else in view, so the frame rate is always high even in full debug mode. Plus, it’s big, unobstructed, and easily pickable in the editor without fear of selecting any other objects. These properties make it perfect for testing editing features in progress, so the rock and I have become fast friends.</p>
<p>Normally, I do not want large rocks to move around. If I’m in the game engine, and a giant rock starts moving, I know I’m in for some debugging work. But if I’m in the editor, and I tell a rock to move, I expect it to move. That’s the whole point of having an editor. So when this cliffside rock, which otherwise had been so pleasant, decided that it would only move in certain directions, I knew I had a bit of a mystery on my hands, one that eventually forced my brain to remember some very important things it had forgotten in the ten years since I last did any serious 3D graphics programming.</p>
<p><span id="more-1989"></span></p>
<h2>Foreign Languages</h2>
<p>Working in someone else’s codebase is much like moving to a foreign country that happens to use the same alphabet as you do, but not the same language: technically, you can read, but you don’t really have any idea what most things mean. At first, every little thing you do is a painstaking linguistic procedure, and you have to constantly check and recheck things to make sure you actually know what’s going on. Over time, as you gain more experience, you start to intuit the way things work, and you’re able to operate at closer to your full speed.</p>
<p>I’m used to the process of working in unfamiliar codebases since I’ve worked with so many of them over the years. I tend to approach each new one the same way: first, I write something very isolated, and keep it walled off. This lets me start making some contributions right away, while I gradually learn how to interface with the rest of the code. In the case of <i>The Witness</i>, this was <a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/">Walk Monster</a>. Next, I try to do some performance optimization or bug fixing, so I learn to work locally, but directly, with existing code. That was <a href="https://the-witness.net/news/2012/12/finding-and-fixing-a-five-second-stall/">fixing the five-second stall</a>. Finally, I start picking real features to add to other people’s code, preferably in the systems that I will most likely need to learn well.</p>
<p>Since I’m working on the collision system, and at this point I am certain there will need to be some interactive tools for it, I picked the <i>Witness</i> editor as the best place to start adding real features. After playing with it for a while, I selected two features I thought would be good additions from both the standpoint of learning the code, and for be generally useful for people working on the game. The first feature was compatible camera controls, and the second was a translation manipulator.</p>
<p>By “compatible camera controls”, I mean camera controls that work like common 3D art packages. My understanding was that the <i>Witness</i> team did their modeling in Maya, so I put a toggle in the editor that, when enabled, allowed you to use ALT-drag camera movements that replicate the way Maya moves the camera. After checking this in, I learned from the artists that some of them prefer 3D Studio MAX, so I added another button that emulates its camera controls (once you do one, it’s very little incremental cost to add others, as they’re really all just other sets of bindings for the same movements). All of this went rather smoothly.</p>
<p>Unfortunately the second feature, the translation manipulator, did not go so smoothly at all.</p>
<h2>The Translation Manipulator</h2>
<p>By “translation manipulator”, I mean the standard three-axis overlay that modern 3D packages have where you can click and drag on any axis and move your selection along that axis. Depending on the package, there are also little squares on each plane that allow planar movement. I chose to include these, but to allow them to be toggled separately since not all packages have them and I didn’t want them to be in the way if an artist wasn’t accustomed to using them.</p>
<p>Compared to the camera controls, it was moderately more work to understand all the intricacies of the editor code involved with translation. But I worked my way through it, and eventually I had a translation manipulator working reasonably well in my test world, which was a completely empty world with just a few boxy entities that I could move around to see if everything was working.</p>
<p>When I was confident everything was solid — undo was working, occlusion was working, etc. — I tried it out on the full <i>Witness</i> island for a bit. Nothing seemed immediately wrong, so I checked in the feature on a toggle switch that defaulted to “off”, and asked <a href="https://number-none.com/blow/">Jon</a> to check it out when he got a chance. I figured it was better not to check it in enabled by default and have the artists all start using it, only to find out it had some critical bug that would corrupt things in some irreparable way, losing lots of valuable work in the process. Since Jon does a lot of work in the editor, and he wrote a lot of the editor code originally, I figured he would know the things to try that might break so we could fix them before any real damage was done.</p>
<p>When I heard back from Jon, he said it did seem to be working correctly, but sometimes “moving along the Z axis wasn’t working”.</p>
<h2>The Occasionally Movable Rock</h2>
<p>I was not at all surprised to hear that Z-axis translation was unreliable. This was something I was expecting to have happen. Surely something was broken in the way my code interacted with the existing planar movement constraints, which were buttons the editor had always provided and which were the only way of constraining interactive movement before I added the translation manipulator. But I checked the code thoroughly, and I just didn’t see any way that I could be mishandling the Z axis in the intermittent way Jon had reported.</p>
<p>I tried playing around with movement more in the editor, to see if I could reproduce the problem myself. Soon, I started to notice that when I played with the solitary rock in the ocean, it wasn’t really “sticking” to the cursor as well as it should be; the code I had written was supposed to feel as if you had “grabbed” the manipulator, and it should follow your cursor rather exactly. It wasn’t doing that.</p>
<p>Then I noticed something even stranger. Depending on how I rotated the camera, I was able to find angles at which Y-axis dragging moved the rock extremely slowly even for large cursor movements. At one particular camera angle, the rock refused to move along the Y axis at all! For example, at this angle, I could move the rock along the Y (green) axis, albeit slowly:</p>
<p><a href="https://the-witness.net/news/2012/12/a-cliffside-cliffhanger/witness3_yworks/" rel="attachment wp-att-1995"><img class="aligncenter size-large wp-image-1995" alt="witness3_yworks" src="https://the-witness.net/news/wp-content/uploads/2012/12/witness3_yworks-512x288.jpg" width="512" height="288" /></a></p>
<p>and at this angle, I could not move the rock along the Y axis at all:</p>
<p><a href="https://the-witness.net/news/2012/12/a-cliffside-cliffhanger/witness3_ybroken/" rel="attachment wp-att-1994"><img class="aligncenter size-large wp-image-1994" alt="witness3_ybroken" src="https://the-witness.net/news/wp-content/uploads/2012/12/witness3_ybroken-512x288.jpg" width="512" height="288" /></a></p>
<p>even though the other two axes worked fine, and from other camera angles the Y axis worked fine, too. Since the manipulator supported entity-relative axial movement as well as world-relative axial movement, I tried switching to entity-relative axes to test if there was something wrong with the Y axis specifically. I rotated the rock such that the Z (blue) axis was pointing in the direction that the Y axis pointed previously:</p>
<p><a href="https://the-witness.net/news/2012/12/a-cliffside-cliffhanger/witness3_zbroken/" rel="attachment wp-att-1996"><img class="aligncenter size-large wp-image-1996" alt="witness3_zbroken" src="https://the-witness.net/news/wp-content/uploads/2012/12/witness3_zbroken-512x288.jpg" width="512" height="288" /></a></p>
<p>To my surprise, it now refused to move along Z! So it really had nothing to do with which semantic axis you were moving, it seemed to be the direction in the world and the angle of the camera alone that mattered for reproducing the bug.</p>
<p>Looking for more ideas about what the bug could be, I tried loading the empty test world again and playing around with the manipulator there. Again, to my surprise, it was completely impossible to reproduce the bug. No matter what camera angle I used, or where the axes were pointing, the always dragged perfectly, and seemingly at the correct speed to boot.</p>
<p>What on earth was going on? Clearly, it was time to think more deeply about the code and the behavior it was exhibiting.</p>
<h2>Ray Versus Plane</h2>
<p>In most 3D editing operations, you must confront the problem of turning a 2D cursor position into something useful in three dimensions. Since a 2D point on the screen mathematically represents a ray in three dimensions (everything in the world that is “under” that point from the perspective of the user), this typically involves taking the ray and intersecting it with something. For example, to implement picking of objects in 3D, you typically intersect this 3D ray with the objects in the world, and you see which intersection is closest. That will be the closest object under the cursor when the user looks at the screen, and therefore the one that they most often expect to pick when they press the mouse button.</p>
<p>My translation manipulator also works by intersecting the ray implied by the cursor. Knowing which axis the user clicks on is analogous to the picking problem, and I essentially implemented it that way: I pretend that there are three long, skinny boxes that align with the axis arrows, and I test to see if the cursor ray intersects them.</p>
<p>Picking the axis is straightforward, but things get more complicated once dragging begins. Although I know which axis the user started dragging, in order to actually move things properly, I have to have a way of computing exactly how far they have dragged the object along the selected axis. Intuitively you might think well, why not just extend the box used to pick the axis to be infinitely long along the dragging axis, and continue intersecting with it as the user moves the cursor? That’s not a bad idea, and it would work, but it isn’t the best solution.</p>
<p>The reason is because it doesn’t feel very good to use. Although it will feel fine if the user drags right along the axis in question, a lot of times users don’t do that. They tend to drag erratically, and pay more attention to the object’s position than how closely they follow the line implied by the axis. This quickly moves the cursor off the imaginary box entirely, so the ray implied by the cursor won’t intersect it. This causes the manipulator to stop working until the user moves the cursor back onto the axis, resulting in a sticky and unpleasant user interaction.</p>
<p>What we’d rather have happen in this case is for the dragging to happen as if the user had moved the mouse to the point on the axis closest to where they actually dragged, even when they’ve wandered quite far from the axis itself. To see how to solve this problem geometrically, imagine a user dragging the mouse along the X axis of a 2D plane. If the user starts dragging erratically, and moves along Y as well, their cursor will end up out in the middle of the plane somewhere. Figuring out where they would have been on the X axis is quite simple, though: ignore the Y coordinate, and just use the X coordinate. Nothing could be simpler.</p>
<p>But how do we do this operation for an arbitrary 3D axis and a ray extending from the cursor? It turns out you can do it exactly the same way. All you have to do is pick a 3D plane that happens to contain the dragging axis, then intersect the cursor ray with that plane. That gives you a nice 2D coordinate on a 2D plane, where the dragging axis is analogous to the X axis in the simple example. You then “throw out the Y”, and are left with a drag that always feels nice in 3D, sticks the object tightly to the cursor, and does not require the user to drag close to the axis.</p>
<p>You don’t need any fancy math to do this. It’s entirely constructible with simple operations available in any math library: dot product, cross product, and ray-intersects-plane, all three of which were already available in <i>The Witness</i> codebase.</p>
<p>So my translation manipulator was built entirely out of the most basic 3D operations in any math library. Yet somehow it was exhibiting very odd behavior atypical for a simple system. How could this be? What was I missing?</p>
<h2>Debug Rendering</h2>
<p>My first thought was that I must be picking the dragging plane poorly. Constructing planes in 3D with only partial information is always a little dicey; you need three non-collinear points to define a plane, but often you want to construct one with only one or two points. The translation manipulator is a perfect example: it needs a plane to operate, but it only really has a line to work with, which is equivalent to only two points. There are, in fact, an infinite number of planes that pass through the dragging axis of the translation manipulator. It’s much like a waterwheel: the dragging axis is the center axle, and each blade of the wheel is another valid choice for a plane.</p>
<p>So how do you pick one? The best method I’ve found for translation manipulators is to use the location of the user. Since we want the user to be able to drag around on this plane easily, it makes sense to say that, of all the planes containing the dragging axis, the best one is the one that is most visible to them. This avoids the potentially bad cases you can hit if you pick something independent of the user’s location, such as the resulting plane being edge-on or nearly edge-on so that the user cannot actually drag along it effectively.</p>
<p>Since even simple 3D math like this can be difficult to verify by inspection alone, my first inclination was to add some debugging rendering to verify that I was picking the dragging plane properly. Since the camera angle clearly mattered, and the plane was one of the only things dependent on that angle, it seemed like a likely culprit. So I tried drawing the normal to the plane in yellow at the base of the manipulator:</p>
<p><a href="https://the-witness.net/news/2012/12/a-cliffside-cliffhanger/witness3_dragplane/" rel="attachment wp-att-1991"><img class="aligncenter size-large wp-image-1991" alt="witness3_dragplane" src="https://the-witness.net/news/wp-content/uploads/2012/12/witness3_dragplane-512x288.jpg" width="512" height="288" /></a></p>
<p>Even in the cases where the dragging failed, the plane normal looked right. Maybe the positioning of the plane was wrong? I tried computing the location of the plane directly, by moving along the normal by the plane constant <i>d</i> and drawing the normal there. This, of course, put the normal nowhere near the manipulator, so I had to look around for it (the plane equation gives you the point closest to the origin, which has no real relationship to where the manipulator might be, other than that they both lie on the same infinite plane). But once I found it, even though it was far away, looking back toward the manipulator it still did seem like it was the right plane.</p>
<p>Next I moved onto the intersections. I tried drawing the intersection point that I had computed when picking the axis in the first place, and also the point that I used to compute the plane constant in the first place. Both looked correct.</p>
<p>But then I tried drawing the actual intersection computed at each drag update, and that didn’t look correct. In fact, it didn’t really “look” at all. Although I was specifically telling the renderer to draw a white sphere where I’d found the intersection of the cursor line with the plane, no white sphere appeared. I checked the code again, and it looked correct, so I tried doing a few more test drags, this time from angles that had seemed to be working before. Now, the sphere showed up, although it was bigger than I thought it should be, which was odd.</p>
<p>I tried rotating the camera. I wrote the code so as to leave the sphere rendering wherever the last dragging intersection occurred, so if I stopped dragging, it just stayed where it was. This allowed me to rotate the camera and see precisely where the sphere was located, and as I did, I realized that the reason the sphere appeared too large was because it wasn’t even close to being on the plane I thought I was using. It was much closer to the camera. Weirder still was that, at the buggier camera angles, the sphere was jumping around erratically, and wasn’t really sticking to the cursor, which it always should do no matter what plane was being used. I was, after all, intersecting the cursor line with a plane. How could the intersection point possibly lie anywhere but directly under the cursor, regardless of the parameters of the plane?</p>
<p>It was at that moment that my math brain finally woke up. It had been sleeping for about a decade, which is how long it’d been since I’d done any real mathematical programming. In an instant, I immediately knew both what the problem was and how to fix it.</p>
<h2>Always Look Behind You</h2>
<p>The problem was embarrassingly obvious when you consider the clues I’d been generously given and yet completely ignored. Remember when I said I drew the plane normal at the location indicated by the plane constant, and I had to look “far away” at the manipulator to see if it looked like it was in line with the plane? Well if the generally erratic behavior of the manipulator hadn’t been telling enough, that little episode should have been the dead giveaway.</p>
<p>At this point, every serious 3D programmer who hasn’t been doing other work for ten years surely knows what the bug was. But for the sake of people who are less experienced, I decided to end this article with a bit of a cliffhanger.</p>
<p>I am going to tell you that you can find the bug by looking at this simplified version of the ray_vs_plane function in <i>The Witness</i> that I was calling (the real version does some epsilon checking in case the ray and plane are parallel, but otherwise it is identical):</p>
<pre>Vector3 ray_vs_plane(Vector3 ray_origin, Vector3 ray_direction,
                     Vector3 plane_normal, float plane_d)
{
    float denominator = dot_product(plane_normal, ray_direction);
    float numerator = dot_product(plane_normal, ray_origin);
    float t = -((plane_d + numerator) / denominator);
    return ray_origin + t*ray_direction;
}</pre>
<p>And I will also show you this screenshot of what it looks like if you turn around 180 degrees from the rock and look back at the island itself:</p>
<p><a href="https://the-witness.net/news/2012/12/a-cliffside-cliffhanger/witness3_island/" rel="attachment wp-att-1992"><img class="aligncenter size-large wp-image-1992" alt="witness3_island" src="https://the-witness.net/news/wp-content/uploads/2012/12/witness3_island-512x288.jpg" width="512" height="288" /></a></p>
<p>But, until next time, I’m not going to tell you what the bug actually is. Can you find it, and figure out how to fix it, before then?</p>
]]></content:encoded>
					
					<wfw:commentRss>https://the-witness.net/news/2012/12/a-cliffside-cliffhanger/feed/</wfw:commentRss>
			<slash:comments>83</slash:comments>
		
		
			</item>
		<item>
		<title>Finding and Fixing a Five Second Stall</title>
		<link>https://the-witness.net/news/2012/12/finding-and-fixing-a-five-second-stall/</link>
					<comments>https://the-witness.net/news/2012/12/finding-and-fixing-a-five-second-stall/#comments</comments>
		
		<dc:creator><![CDATA[Casey Muratori]]></dc:creator>
		<pubDate>Tue, 18 Dec 2012 20:23:22 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<guid isPermaLink="false">https://the-witness.net/news/?p=1976</guid>

					<description><![CDATA[The great thing about programming on Windows is that it is the only commercially viable platform where you can ship software to users without getting approval from a giant bureaucracy (well, perhaps I should say it used to be). The not-so-great thing about programming on Windows is that, well, the …<p class="continue-reading-button"> <a class="continue-reading-link" href="https://the-witness.net/news/2012/12/finding-and-fixing-a-five-second-stall/">Continue reading<i class="crycon-right-dir"></i></a></p>]]></description>
										<content:encoded><![CDATA[<p><a href="https://the-witness.net/news/2012/12/finding-and-fixing-a-five-second-stall/witness_2_windowskeys/" rel="attachment wp-att-1980"><img src="https://the-witness.net/news/wp-content/uploads/2012/12/witness_2_windowskeys-512x290.jpg" alt="witness_2_windowskeys" width="512" height="290" class="aligncenter size-large wp-image-1980" /></a><br />
The great thing about programming on Windows is that it is the only commercially viable platform where you can ship software to users without getting approval from a giant bureaucracy (well, perhaps I should say <a href="http://mollyrocket.com/casey/stream_0004.html">it used to be</a>). The not-so-great thing about programming on Windows is that, well, the Windows API is a horrific nightmare.</p>
<p>Granted, it is not uniformly horrific. It ranges from only slightly scarey (xinput, comdlg32) to full psycho (DirectShow, Event Tracing, TabletPC, etc.). But no matter which part of the spectrum you encounter, programming on Windows tends to be about doing a lot of unnecessary research, experimentation, and debugging due to a confluence of poorly designed APIs.</p>
<p>Since time spent debugging Windows issues isn’t often chronicled, I thought I’d go ahead and describe my experience this Saturday of tracking down a Windows bug in <i>The Witness</i>.</p>
<p><span id="more-1976"></span></p>
<h2>The Symptom</h2>
<p>Apparently, not everyone on <i>The Witness</i> team was seeing the symptom. But trust me, it was there.</p>
<p>On both <a href="https://number-none.com/blow">Jon</a> and my machines, there was a period during the initial loading of the game where you couldn’t do things like ALT-TAB back to the debugger. If you set a breakpoint then ran the game, when the breakpoint eventually hit and the debugger came to the front, it would wait about five seconds before it allowed you to actually interact with it. Sometimes, in rare cases, it would go into some sort of psycho mode whereby mouse movements stuttered, and the computer would literally beep at you as the mouse moved, for several seconds. Then everything would return to normal.</p>
<p>While not technically a showstopper, this is the kind of bug that’s worth fixing if you can, because it costs you real development time every day. Five seconds wasted every breakpoint is bad news, and people often underestimate the effects of frustration that can build up over time due to flaky and unpleasant development environments. So I decided to try to track it down.</p>
<p>Because Windows is a closed-source platform, there’s no great way to track down problems of this nature. However, having programmed on Windows for over twenty years now, I have a nose for it, so I felt like I knew the best place to start: since there was definitely a UI blackout somewhere during startup, there had to be something occurring early in the execution of the program that was causing at least that particular symptom. So before doing anything else, I started stepping through the entire startup sequence of the program to see if anything out of the ordinary struck me.</p>
<p>I very quickly came upon this code:</p>
<pre>// @@ The hook appears to install properly, but is never invoked :(
keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc,
                                 GetModuleHandle(NULL), 0);</pre>
<p>Now, I don’t want to make it sound like the “find the source of the problem” phase of Windows debugging is straightforward. It usually takes a very long time, sometimes multiple days. But as luck would have it, this was literally the first piece of code I suspected, and it turned out to be the culprit.</p>
<p>I commented out the SetWindowsHookEx, ran the program, and poof! All the symptoms were gone.</p>
<h2>The Problem</h2>
<p>From experience, I’ve learned that it’s always best to fully understand a problem before you fix it. If you just patch over its symptoms but never figure out what the problem really was, it will often come back to haunt you.</p>
<p>In this context, that meant two things: first, I should figure out specifically why the keyboard hook was causing the UI stalls; second, I should determine why the code had been trying to call SetWindowsHookEx in the first place, even though apparently it never quite worked (according to the “is never invoked” comment).</p>
<p>The first part turns out to be obvious in hindsight, but it took me a little while to put all the pieces together and be sure I had a solid explanation for what we were seeing. As with any puzzle, looking at the finished picture is easy, but figuring out what that picture might be when all you have are little pieces coming in one at a time is much more difficult. Here’s the best picture I could come up with, but of course there’s no way to completely verify it external to Microsoft:</p>
<p>Normal Windows hooks are pieces of code which execute in the address space of the eventual message recipient, not the installer of the hook. It is for this reason that, normally, if you want to install a global hook, you must put the hook code in a DLL, because that DLL must be mapped into the address space of every running executable on the machine that can receive Windows messages.</p>
<p>But low-level hooks are completely different. A low-level hook like WH_KEYBOARD_LL is a piece of code that resides in only the process that registers the hook. Windows remembers which thread registered the hook, and when any other executable is about to have a hooked message posted to its message queue, Windows actually waits until it can switch contexts back to the hook thread, run the hook in that context, and then finally deliver the message.</p>
<p>This leads directly to the symptom that we were experiencing. When the game is loading, its main thread isn’t yielding to Windows frequently like it is during normal play. Since the main thread is the one that registered the hook, Windows must wait for that thread to finish all the load-time work it was doing and call a function which yields to Windows in order for Windows to actually call the hook procedure. Since no keyboard processing can occur until the hook procedure has run, the keyboard becomes unresponsive as messages back up waiting to be processed through the hook. This completely explains the UI problems during startup, and fits with the oddity that the mouse never seemed to be affected, just the keyboard.</p>
<p>When the game hit a breakpoint, the keyboard hook couldn’t execute at all because the debugger had halted the thread required to execute it. This would have meant that keyboard input would be completely halted, and the debugger would never be able to receive keystrokes, if it weren’t for the fact that Windows actually uses a timeout on hook calls. If the timeout value is exceeded waiting for the hook thread to become available, the hooking executable is assumed to have crashed, and it will silently remove that hook and continue with normal operation.</p>
<p>That timeout value? Five seconds — right in line with our observed pause (technically, it’s whatever is set in the registry under HKEY_LOCAL_MACHINE\Control Panel\Desktop\LowLevelHooksTimeout, but the default is five seconds).</p>
<p>So, no mystery as to the pathology. But what about the second part? What was the code actually trying to do?</p>
<h2>The Windows Logo Keys</h2>
<p>Although SetWindowsHookEx might seem like an odd call to make in a game executable, it’s actually quite common. It’s there to prevent the Windows logo keys on modern keyboards from ruining full-screen games. Because the keys are placed in a location that’s easy to hit by mistake, many gamers would accidentally hit them, causing their games to be instantly deactivated by Windows in favor of bringing up the start menu. So widely acknowledged was this problem that, when these keys were first introduced, Microsoft itself published a recommended work-around. From <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ee416808%28v=vs.85%29.aspx">Disabling Shortcut Keys in Games</a>:</p>
<blockquote><p>This article describes how to temporarily disable keyboard shortcuts in Microsoft Windows to prevent disruption of game play for full screen games... Use a low-level keyboard hook to filter out the Windows key from being processed.</p></blockquote>
<p>The article goes on to show sample code that is basically the same code that was being used in <i>The Witness</i>. It’s a simple SetWindowsHookEx call with WH_KEYBOARD_LL whose hook routine does nothing but block VK_LWIN and VK_RWIN keys from being processed. The only difference between the code in <i>The Witness</i> and the code in the sample is that <i>The Witness</i> would conscientiously remove the hook when it no longer had focus, and then reinstall the hook when focus was regained. The sample code, by contrast, just set a global variable.</p>
<p>So if this was what the code was doing, why the frowny face? If you look back at the comment, it states very clearly that the hook was never being invoked, even though no error message came back from Windows. And, although I normally remap my Windows keys via the registry, I went ahead and mapped them back to see if <i>The Witness</i> was properly blocking their effects.</p>
<p>Much as the frowny face foretold, it wasn’t.</p>
<h2>Obeying the Static Discipline</h2>
<p>I don’t know anything about digital circuit design, but I once watched a lecture by <a href="http://www.darpa.mil/Our_Work/DSO/Personnel/Dr_Gill_Pratt.aspx">Gill Pratt</a> where he explained something called “the static discipline”. In essence, it is the requirement that any component in a circuit must accept voltages with a certain range for “1” and “0”, and then must output its own results in a certain range. These ranges are defined so as to require that components always produce equal or better conditioned voltages than their inputs, thus ensuring that the digital signals being propagated through the circuit don’t degrade into noise due to lots of tiny losses.</p>
<p>For some reason, I took this concept to heart in a programming sense and have found it is a good rule to code by. My version of the static discipline, adapted for software, is that whenever you are making a modification to a piece of code, you should always leave it in a state of stability equal to or better than how you found it. And preferably the latter.</p>
<p>All too often, people go in to fix a problem or add a feature to a piece of code, and they just hammer on it until it does that one new thing. The resulting code is then usually more fragile, less well designed, more unnecessarily complex, etc. To try to prevent myself from having this effect, I try to observe the static discipline.</p>
<p>And so it was with this Windows key situation. I could see what the code was supposed to be doing, and although I could reasonably fix my bug by just commenting out the hook (because it didn’t work), I felt it was the more disciplined thing to do to figure out why the hook wasn’t working, and to implement something that did what it was trying to do. Since there was no obvious bug, I had to start experimenting.</p>
<p>After adding some debug outputs and running Spy to keep an eye on the window messages, the first thing I tried was removing the part of the code that unregistered the hook when his window lost focus. Since I didn’t know exactly how his windowing code worked at all levels, I figured it wasn’t entirely out of the question that it could be getting disabled prematurely.</p>
<p>Although that turned out not to be the case, this experiment did pay off unexpectedly (as many do): although the keyboard hook still never got called when <i>The Witness</i> window had focus, the hook did start getting called when it lost focus. Yes, oddly enough, the hook was working just fine for everyone else’s window, but on the one window where it actually needed to work, it wasn’t working.</p>
<p>This lead to the obvious question, what was <i>The Witness</i> doing differently?</p>
<h2>Raw Input</h2>
<p>This was a case where having lots of Windows programming experience probably saved a lot of time. Instead of having to experiment blindly, I knew right off the bat that Windows Raw Input probably had something to do with the situation. I’ve debugged lots of Windows front end code in the past, and any time Raw Input, DirectInput, XInput, or anything else with the word “input” in it is involved, you know that things are going to be going a little haywire.</p>
<p>I commented out the device registration call for Raw Input, and lo and behold, the hook started working for <i>The Witness</i> window, no problem. So, although I was completely unable to find any documentation on MSDN that discussed what was going on under the hood, it must be the case that Raw Input processing happens before low-level keyboard hook processing in such as way as to prevent the keyboard hook processing from ever seeing keyboard messages on a Raw Input-registered window. Normally, this wouldn’t be a problem, but in our case, the Microsoft-recommended fix for the Windows logo key problem relies on keyboard hooks. <i>The Witness</i> needs Raw Input to get good low-level relative mouse movements, but it also needs to disable the Windows logo keys; sadly, this is the kind of situation that is all too routine for anyone who programs Windows professionally.</p>
<p>I couldn’t think of any plausible ways that I could convince Raw Input to start using the Windows keyboard hook, since I don’t have access to any of the Windows source code and I have no idea how it works internally. So I focused my efforts on Raw Input.</p>
<p>I experimented with just about everything I could think of. I tried not calling DefWindowProc on WM_INPUT calls, to see if it was the default window processing that was using the Windows logo key events to switch to the start menu. No such luck. I tried using RIDEV_NOLEGACY during Raw Input initialization to see if preventing keyboard messages from being generated would solve the problem. It didn’t. After many failed attempts, and finding no relevant information on the web, I decided to call it a night and sleep on it.</p>
<p>When I woke the next morning, for some reason I remembered the most important thing anyone programming Windows must remember: never, ever trust the documentation. It’s always either inaccurate or incomplete. So when I sat down at my keyboard, the first thing I did was try using the RIDEV_NOHOTKEYS flag when initializing Raw Input.</p>
<p>You see, the previous day I hadn’t bothered to try this flag, because it explicitly says in the documentation that it does not block system-level hotkeys, only application-level hotkeys (like the kind you create yourself with RegisterHotKey). But why was I believing that? That was just something somebody wrote down when they needed to create a documentation page. They probably never even saw the actual code for Raw Input in the first place.</p>
<p>Surprise surprise, RIDEV_NOHOTKEYS worked. After all the fussing, all that had to be done to make <i>The Witness</i> impervious to Windows Logo keys was one little flag during Raw Input initialization. No keyboard hook necessary.</p>
<h2>Zero-Sum Game</h2>
<p>It’s worth taking a step back for a moment and asking the question, why did all of this happen? Why did I have to spend several hours working on code that has nothing to do with my project, is not valuable in any intrinsic way, and whose specifics are relevant only in a fleeting temporal sense (there will be no need to know anything about RIDEV_NOHOTKEYS when the next user input model for Windows is invariably introduced)?</p>
<p>The answer is the same on Windows as it is on most every other platform that exists today: the platform providers just don’t spend real resources designing and improving their APIs. If there had simply been an API call, WindowIgnoresWindowsLogoKeys(HWND), then everyone would always just call that, and the platform developers and Microsoft could field it and do the right thing, no matter how many times they revised their platform and its infrastructure.</p>
<p>Better yet, way back when they first started catering to games with Windows 95, they could have introduce a call like BeginFullScreenGame(), and that could have done literally everything for you: initialize the window, set up the input stream, prepare the sound device, etc. Then they wouldn’t have had to ever worry about the Windows logo keys at all, because they would have been automatically disabled in any application that initialized itself with that call.</p>
<p>But instead of any of these things, Microsoft chose to ship a new feature (the Windows logo hotkey) without ever thinking about the consequences. Then they patched over its problems by telling developers to install a keyboard hook (SetWindowsHookEx), something that has nothing to do with the actual problem but which was the most expedient way for them to get the problem solved without doing any real work on their platform. Then they broke that method, which they’d previously advocated, when they introduce Raw Input, so (presumably) they introduced a new way to do the same thing (RIDEV_NOHOTKEYS), although they failed to document it. Then they didn’t even bother to publish an obvious tech article explaining this change, or even revise their old documents saying that SetWindowsHookEx was the preferred solution.</p>
<p>As broken as all that sounds, it is absolutely par for the course. On every project, in every discipline, developers just like me spend hundreds of hours doing nothing but diagnosing and repairing problems with the platforms on which we ship our software. It may sound like it’s just “part of the job”, but the pernicious thing about it is that development resources are always finite. External to the platform, development is always a zero-sum game. The hours I spent tracking down this bug were hours that won’t get spent making parts of The Witness better, and there’s nothing me or the team can do about that. If we want to ship a polished project, it’s a sacrifice we have to make.</p>
<div>
<p>And it’s a sacrifice that’s not just made by us, but by thousands of teams on thousands of projects every year. The cost of obtuse, easy to misuse APIs is multiplied across all these projects, turning the hundreds of hours we will spend on Witness platform work (across multiple platforms) into hundreds of thousands of hours when you consider that this same work has to be done by every other developer, too. It’s really thousands of zero-sum games being played, all across the industry, and the end result is universally worse software because the underlying platforms take up too much effort.</p>
<p>But there is a way for this not to be a zero-sum game, and that’s if there’s a shift in thinking internal to the platform providers. If Microsoft, for example, started realizing that software on Windows gets better if their APIs are clear, concise, and difficult to misuse, they might start changing their development processes to make API quality a priority. They might try to hire and cultivate good API designers, and have real, talented developers who do nothing but work on the APIs to the internal systems.</p>
<p>If that happened, the relatively small amount of hours spent on this endeavor internal to Microsoft (or Sony, or Apple, or Nintendo, or Google) would have a massively leveraged effect. If nobody has to think about things like the Windows keys again, that’s four hours right there that every software project gets back. That is literally thousands of hours, just that. Don’t need to worry about task switching or window minimization? That’s a day, at least — which makes it <i>thousands of days</i> saved. Straightforward user input? Easy access to camera inputs? Each little thing adds up until every developer is getting weeks of their schedule back to devote to working on their actual software, not to mention all the bugs that wouldn’t slip through the cracks because the developers may not have been experienced enough with the particular platform to know to look for them.</p>
<p>So how do we get there? Honestly, I have no idea. In the meantime, I guess we just keep writing articles like this one that detail the right solutions as we find them, and hope that saves everyone some of the vast amount of time that we all lose to platform-related problems each year. Beyond that, it’s really up to the platform providers to start taking these issues more seriously.</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://the-witness.net/news/2012/12/finding-and-fixing-a-five-second-stall/feed/</wfw:commentRss>
			<slash:comments>46</slash:comments>
		
		
			</item>
		<item>
		<title>Mapping the Island&#8217;s Walkable Surfaces</title>
		<link>https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/</link>
					<comments>https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/#comments</comments>
		
		<dc:creator><![CDATA[Casey Muratori]]></dc:creator>
		<pubDate>Wed, 12 Dec 2012 13:46:24 +0000</pubDate>
				<category><![CDATA[Engine Tech]]></category>
		<guid isPermaLink="false">https://the-witness.net/news/?p=1939</guid>

					<description><![CDATA[Despite not even being finished, The Witness is already one of my all-time favorite games. I’ve been playing it since Jon started developing it several years ago, and I could not be more excited about its upcoming release. Unlike Jon’s previous game Braid, the asset and programming scale for The …<p class="continue-reading-button"> <a class="continue-reading-link" href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/">Continue reading<i class="crycon-right-dir"></i></a></p>]]></description>
										<content:encoded><![CDATA[<p><a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_comparison/" rel="attachment wp-att-1943"><img class="aligncenter size-large wp-image-1943" src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_comparison-512x288.jpg" alt="" width="512" height="288" /></a></p>
<p>Despite not even being finished, <em><a href="https://the-witness.net">The Witness</a></em> is already one of my all-time favorite games. I’ve been playing it since <a href="https://the-witness.net/news/author/jon/">Jon</a> started developing it several years ago, and I could not be more excited about its upcoming release.</p>
<p>Unlike Jon’s previous game <em><a href="https://braid-game.com">Braid</a></em>, the asset and programming scale for <em>The Witness</em> is much further toward the “triple-A” side of the scale than the “indie game” side. As anyone who works on these types of projects knows, the work required to successfully pull off a game goes up dramatically when you go that direction. <em>The Witness</em> already has several more people working on it than <em>Braid</em> did at its peak, but as with any project of this size, there are still many parts of the project that need more attention than the principals can afford to give them.</p>
<p>For this reason, it was always my intention to try to find some time to help out on <em>The Witness</em> when it came time to ship it. So this past Thanksgiving, Jon and I sat down and he went through a list of things in the codebase that he thought would benefit from the attention of an extra programmer. After considering the relative importance of the items, we decided that the player movement code was the place where improvements could have the biggest impact on the player’s experience.</p>
<h2>Walkmonster in Wall</h2>
<p>In the context of <em>The Witness</em>, the goal of player movement code is to be as unobtrusive as possible. The player is supposed to fully immerse themselves in an alternate reality, and every last detail is important to this experience. The last thing you ever want to happen is to have the player notice the fact that they’re sitting at a computer asking it to move a virtual viewpoint around.</p>
<p>For that reason, the player movement code has to be rock solid. If you can get stuck on edges, trapped in walls, fall through the floor, walk through a cliff, walk down a hill but not back up it, etc., it immediately breaks the immersion and reminds you that you are playing an artificial experience mitigated by an unreliable system. It can also lead to disastrous consequences for the player in some circumstances if there’s no way for them to recover from the problem with restarting or reloading a (possibly very old) save game. If you play games often, you’ve probably encountered one or more of these problems in shipping titles, and you know what I’m talking about.</p>
<p>Last week I began to work on this problem. I decided the first thing to do was to write a few integrated tools for working with the player movement code so we could analyze it and see how it was currently behaving. Immediately, when I opened up the project, I found myself with a familiar conundrum: what to name my first source file. This is always the most important part of any project (<a href="http://www.gbv.com/inter4.html">as Bob Pollard once said of band names and album names</a>). If you have the right name for your source file, it’s smooth sailing from there. Pick the wrong name, and you might as well call the project off.</p>
<p>But what to call a system for bulletproofing player movement code? I’d never been asked to write code for that before. When I thought about it, I realized that I’d only personally seen evidence of similar code once before: when playing an early beta of Quake. There were some bugs with the monster placement, and in the console window, you could see error messages announcing the fact that the monsters, instead of spawning on the ground, had spawned partially intersecting with the level geometry. Each debug message began with the phrase “walkmonster in wall at. . .”</p>
<p>Bingo. It’s hard to ask for a better source file name than “walk_monster.cpp”. I was pretty sure, from that point forward, the code was going to come along nicely.</p>
<h2>Drive Toward Point</h2>
<p>When you’re trying to test a system, the most important thing to do is <em>actually test the system</em>. Even though that sounds like a straightforward rule, people writing tests often fail to observe it.</p>
<p>In this specific case, there are very easy ways to <em>think</em> that you were testing the player movement code without actually testing it. One example would be to do some kind of analysis on the collision volumes and walkable surfaces in the game, looking for small areas, gaps, etc. Once you’d eliminated all of these, you’d then proclaim the world safe to traverse and move on.</p>
<p>But this is testing the data, not the actual code. It’s still easily possible to have bugs in the movement code that result in bad behavior even with sanitized data.</p>
<p>To avoid this kind of trap, I wanted the testing system to remain as close as possible to what a human does to actually control player movement in the game. I started out by writing two routines to serve as the building blocks of this kind of testing.</p>
<p>The first is the closest to real human usage. It’s an update call that hooks into <em>The Witness</em>’s input processing and feeds it synthetic mouse and keyboard events. It can do some basic things that humans might do, such as looking around, walking toward a point, looking toward a point, and so on. It does these through nothing but emulation of human interaction with the mouse and keyboard, so I know that everything in <em>The Witness</em>’s input path will be running as it actually would during testing. I’ll talk more about this system and how it’s used in later installments.</p>
<p>The second piece of code is one step removed from that level. It’s a function called <em>DriveTowardPoint</em> that takes two points in the world and, calling the existing player collision system, attempts to move cleanly from one to the other. When it returns, it provides information about the attempt such as obstacles encountered and whether or not the destination was successfully reached.</p>
<p>This function is not quite as reliable as the synthetic input method of testing, because it eliminates a portion of the actual player movement system from testing. For example, any erroneous state associated with where the player is that the collision system might have built up won’t affect tests using this function. Nevertheless, I felt it valuable to have this level of testing available because it can much more rapidly test large areas, since it does not require the entire game loop to run, and thus can be employed much more frequently and over the entire world instead of just isolated test runs.</p>
<p>It’s also worth noting that there are no physics inputs to this function; there are no velocities specified for the starting point, for example. This is because <em>The Witness</em> is not an action game, so there are few stateful physical properties for the player. Players can’t jump, they can’t wall-run, they can’t enter bullet-time. Supporting these types of behaviors is possible with systems of the kind I’ll be discussing, but they add a layer of complexity that we don’t have to worry about on this project.</p>
<p>Anyhow, with DriveTowardPoint in place, it was possible for me to start on my first goal for the system: determining everywhere the player can go on the island of <em>The Witness</em>.</p>
<h2>Rapidly Exploring Random Trees</h2>
<p>Where can the player go? It seems like a simple question, but you’d be surprised how many games ship without the development team knowing the real answer. If possible, I want <em>The Witness</em> to be one of the few games where the team knows prior to shipping exactly where the player can and can’t go, no surprises.</p>
<p>This makes the problem statement, but perhaps not the problem, very simple: given a DriveTowardPoint function that faithfully determines whether a player could move in a straight line between two points, produce a coverage map showing everywhere the player could end up.</p>
<p>For reasons that I can’t necessarily explain, I had in my head, even before writing a line of code, that the best way to do this would be to use a <a href="http://msl.cs.uiuc.edu/rrt">Rapidly Exploring Random Tree</a>. For those of you unfamiliar with this algorithm, it’s a very simple process whereby you record all the points you’ve visited along with a reference to the point from which you walked there. To add points to the tree, you pick a random target point anywhere in the world, select the closest point in your tree thus far, and try to get from that tree point to the target point. Wherever you end up, that’s a new sample point.</p>
<p>Normally, this is more of a pathfinding algorithm, so interleaved with random points, you repeatedly select some goal point as a target. This biases the exploration of the space toward the target, which is what you want when your only aim is to reach the goal. But in this case, I wanted to produce a complete map of everywhere the player could go, so I was exclusively using random samples.</p>
<p>After implementing this (it is thankfully a very simple algorithm to write, so it doesn’t take long), I could see that it did do a reasonable job of exploring the space (the white etchings show paths that have been explored, and the red vertical lines show places where an obstacle was hit):</p>
<p><a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_rert/" rel="attachment wp-att-1950"><img class="aligncenter size-large wp-image-1950" src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_rert-512x288.jpg" alt="" width="512" height="288" /></a></p>
<p>However, once I actually looked at how it behaved, it was clear to me that it wasn’t really the algorithm I wanted. For example, even after many iterations, it often barely explores rooms like this one, despite densely covering the area just outside, simply because it doesn’t pick enough random targets inside it:</p>
<p><a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_rert_fail/" rel="attachment wp-att-1951"><img class="aligncenter size-large wp-image-1951" src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_rert_fail-512x288.jpg" alt="" width="512" height="288" /></a></p>
<p><span id="more-1939"></span></p>
<p>If I’d thought about it before I started, I would have realized that the benefit of something like a Rapidly Exploring Random Tree is that it explores high-dimensional spaces efficiently. In fact that’s the primary reason you normally use it. But in <em>The Witness</em>, we don’t have a high-dimensional space. We have a two-dimensional space (distributed on an intricate manifold, yes, but a two-dimensional space nonetheless).</p>
<p>In this low-dimensional space, the benefits of a Rapidly Exploring Random Tree are muted, and the drawback is critically bad for my stated purpose: it’s designed to most efficiently find ways to connect pairs of points in a space, not to efficiently find all reachable points in that space. If you care about the latter, then Rapidly Exploring Random Trees will actually take an excruciatingly long time to do so.</p>
<p>So, I quickly realized that what I should be looking for is an algorithm that efficiently covers low-dimensional spaces in their entirety.</p>
<h2>3D Flood Filling</h2>
<p>Once I actually thought about the choice of algorithm, it seemed obvious that what I actually wanted was something like the old 2D flood fills we used to use to fill regions of a bitmap. Given a starting point, I just wanted to fill up the entire space, exhaustively probing every possible way you could go. Unfortunately, this is a lot less straightforward in the world of <em>The Witness</em> than it is in a 2D bitmap, for a number of reasons.</p>
<p>First, there’s no clear concept of finite connectivity around a point. Everything is continuous. It’s not like pixels where you can easily enumerate 4 possible places to go from every given location, and check each one in turn.</p>
<p>Second, there’s no fixed size for a locus in space like there is for a pixel in a bitmap. Walkable surfaces and obstacles can be anywhere, they have no minimum or maximum feature size, and no alignment to any external grid.</p>
<p>Third, although walking around in <em>The Witness</em> locally acts as if it were on a plane, the space itself actually is a deeply interconnected and varying manifold, with walkable regions directly above other regions (stacked perhaps several times over), and special connectivity that changes based on world states (such as doors that open, elevators that go up and down, etc.)</p>
<p>Given these complexities, it is very easy to think you have devised a way to do a flood fill, only to find after you’ve implemented it that it gets bogged down oversampling areas, misses important paths, produces erroneous connectivity information at places where the manifold is complex, or is simply too cumbersome to use because it must be restarted to cope with changes in the world state.</p>
<p>No good solution to all of this immediately came to mind, so I tried some simple experimentation. Using the Rapidly Exploring Random Tree code I had written, I changed the target point selection from random selection to a very controlled selection. Each time a new point was added to the tree, I would say that points a unit step along the cardinal directions from that point where to be considered future targets, just like a basic 2D flood fill.</p>
<p>But of course, this would produce a useless sampling loop if I wasn’t careful. A point would branch out to a neighborhood of 8 points around it, but those 8 points would all then want to try the original point again, and this would go back and forth forever. So in addition to the controlled target selection, I also needed the simple constraint that any target which wasn’t some minimum useful distance away from an existing target point would not be considered. To my surprise, these two rather simple rules did produce a somewhat usable flood fill:</p>
<p><a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_sff/" rel="attachment wp-att-1953"><img class="aligncenter size-large wp-image-1953" src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_sff-512x288.jpg" alt="" width="512" height="288" /></a></p>
<p>Not bad for just trying the obvious thing. But it suffers from what I’ve taken to calling “boundary echo”. You can see this effect in the following screenshot, taken while the map exploration was still in progress:</p>
<p><a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_sff_echo/" rel="attachment wp-att-1954"><img src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_sff_echo-512x288.jpg" alt="" width="512" height="288" class="aligncenter size-large wp-image-1954" /></a></p>
<p>In regions with no obstacles, this algorithm proceeds nicely, sampling at a relatively even distance. But once it hits a boundary, the intersections there produce points that are “off grid”, in that they are not aligned along the sampling pattern with which the algorithm filled the adjacent open region. The reason “on grid” points don’t produce overly dense tessellation is because any new point that tries to step back onto one of the previous points finds the previous point there and declines to reconsider it. But when new points are produced on the boundary, they are completely unaligned, so there is nothing to stop them from stepping back into the already explored space. This creates a wave of offset sampling which will continue until it runs into a fortuitous line of points somewhere else that happen to be close enough to its advancing front for it to consider them coincident.</p>
<p>Although this may not seem like a huge problem, it is actually critical. The entire goal of an algorithm like this is to concentrate the samples in the areas where they are most likely to yield productive results. The more time you spend sampling and resampling wide open regions, the less time you spend mapping the actual edges of that region, which is the information you actually wanted. Because this is a continuous space and only an infinite number of samples can truly reveal its exact shape, your ratio of meaningful to meaningless samples is literally the measure of how effective your algorithm is at producing the walkable area.</p>
<p>Now, there is an easy fix for this specific problem: expand the distance at which two points are considered “close enough”. But by doing this to reduce sampling density in places you <em>don’t</em> care about, you lose sampling density in places you <em>do</em> care about, like around boundaries where you are trying to carefully probe for holes.</p>
<h2>Localized Directional Sampling</h2>
<p>Perhaps because I had started with a Rapidly Exploring Random Tree, my brain had been pigeonholed into thinking about proximity to the exclusion of everything else. The previous algorithms had all been about using proximity to do things, like figure out whether a new point should be considered, or which point to start from when trying to get to a new target point.</p>
<p>But after thinking about the problem for a while, I came to realize is that everything falls more neatly into place if you think in terms of <em>directionality</em> as well as proximity. While obvious in hindsight, if you’ve worked on these sorts of problems before, you know it’s easy to get trapped in one way of thinking for a while and not see the bigger picture, even if it turns out to be a simple one. That is precisely what had happened to me.</p>
<p>Once I shifted perspective, the correct sampling behavior was obvious. Each time I wanted to expand the exploration from a point, I would do a proximity query for a local neighborhood of existing nearby points. Instead of using the distance to those points to guide the exploration, however, I would categorize them by their direction (currently, I just use the eight cardinal directions, but I’d like to experiment with different kernels here).</p>
<p>In any direction where I don’t “see” a point, I walk a predetermined distance and add a point wherever I end up (whether I hit something or not). In any direction where I do see a point, I walk there and make sure I can get to it. If I can, I just add a visible edge, so it’s easy for the user to see that we’re connected. If I can’t, then I do add a new point at the collision, revealing the obstacle boundary.</p>
<p>This sampling method works beautifully. It allows you to very accurately control the sampling with nicely tunable parameters, keeping all the points you want while preventing unnecessary tesselation, which leads to a very fast filling of the space:</p>
<p><a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_lds/" rel="attachment wp-att-1944"><img class="aligncenter size-large wp-image-1944" src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_lds-512x288.jpg" alt="" width="512" height="288" /></a></p>
<p>Because it searches along directions rather than just using proximity, it is immune to boundary echo, and restricts oversampling to the boundaries where you actually want it:</p>
<p><a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_lds_noecho/" rel="attachment wp-att-1945"><img class="aligncenter size-large wp-image-1945" src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_lds_noecho-512x288.jpg" alt="" width="512" height="288" /></a></p>
<p>Furthermore, it’s completely impervious to state changes or complex manifold problems. It deals strictly with points and those points can be anywhere, and you can add new ones at any time. If you’ve already made a map of an area with a door closed, and you open the door, all you have to do is plant a single new exploration point on the other side of the door and say “continue expanding this map”, and it’ll connect itself back up and explore the entire area beyond the door properly.</p>
<p>You can also change any of the core parameters at any time, and everything still works. Want to sample an area more densely? Just set the default spacing lower. You can do this in the middle of a map build, and it will start sampling more densely, with no need to jettison the existing results (which may have taken some time to produce).</p>
<h2>Rudimentary Edge Probing</h2>
<p>Although the algorithm by default will already sample boundaries more thoroughly, because intersections produce additional points beyond the sampling pattern, it didn’t necessarily probe them as thoroughly as I wanted, because it doesn’t do anything special when it encounters obstacles. It occurred to me that since I know which points were produced from collisions and which were not, whenever two collision points found themselves connected by an edge, I could choose to invoke extra sampling to try to find more boundary points nearby.</p>
<p>I haven’t yet worked on this extensively, but I put in a rudimentary method to test the theory, and it shows promise. For any two collision points connected by an edge, I step to the midpoint of the edge and probe outwards perpendicular to the edge. If I don’t collide with the boundary within a very short distance, I assume that the boundary is more complex, and add a new target there so the search will continue in that area.</p>
<p>Even this simple scheme produces nicely dense sampling along the boundary, without any oversampling of open regions nearby. Here’s a region with several boundaries, but without edge probing:</p>
<p><a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_lds_noprobing/" rel="attachment wp-att-1946"><img class="aligncenter size-large wp-image-1946" src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_lds_noprobing-512x288.jpg" alt="" width="512" height="288" /></a></p>
<p>Now here’s the same region with edge probing:</p>
<p><a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_lds_probing/" rel="attachment wp-att-1947"><img src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_lds_probing-512x288.jpg" alt="" width="512" height="288" class="aligncenter size-large wp-image-1947" /></a></p>
<p>As pleased as I am with this, I’d be surprised if there weren’t significantly better algorithms for sampling the boundary, and I look forward to trying a few more methods in the future.</p>
<h2>Early Victories</h2>
<p>Even with little development time, and the resulting rather simplistic code, Walk Monster already produces very usable output that can find real problems in the game. Here are just a few examples of problems I’ve noticed in passing while developing the algorithm:</p>
<p><a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_slopebug/" rel="attachment wp-att-1955"><img class="aligncenter size-large wp-image-1955" src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_slopebug-512x288.jpg" alt="" width="512" height="288" /></a></p>
<p>The slopes on the side of this extended platform should not have been walkable, but they were. This was because the player movement code has a pathologically bad way in which it deals with sloping geometry. I now know that’s in there, and I’ll be sure to fix it when the time comes to bulletproof it.</p>
<p><a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_rockbug/" rel="attachment wp-att-1952"><img class="aligncenter size-large wp-image-1952" src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_rockbug-512x288.jpg" alt="" width="512" height="288" /></a></p>
<p><em>The Witness</em> is meant to be a contemplative game, but wondering why there seemed to be a rock where there was no rock was not meant to be one of its koans. As you might guess, this was because someone left a collision volume in the game when the geometry it represented had already been removed. This is an easy thing to have happen, and it’s nice to have a tool that trivially reveals these mistakes quickly so people don’t have to worry about it.</p>
<p><a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_pathbuga/" rel="attachment wp-att-1948"><img class="aligncenter size-large wp-image-1948" src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_pathbuga-512x288.jpg" alt="" width="512" height="288" /></a><br />
<a href="https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/walkmonster_1_pathbugb/" rel="attachment wp-att-1949"><img class="aligncenter size-large wp-image-1949" src="https://the-witness.net/news/wp-content/uploads/2012/12/walkmonster_1_pathbugb-512x288.jpg" alt="" width="512" height="288" /></a></p>
<p>These were supposed to be impenetrable rocks, but Walk Monster revealed them to be anything but. What’s worse, Walk Monster revealed that the path was somehow only traversible in one direction (from left to right in this top-down screenshot), which is never supposed to happen. I haven’t actually looked at this problem yet, besides verifying that I could actually do it via player motion (I could). It’ll be interesting to see what’s going on!</p>
<h2>Open Questions</h2>
<p>It’s encouraging to already have good results upon which we can build. Like I said, if you pick the right name for your source files, it all flows smoothly from there! But all of this work was done in the span of a few days, so it is obviously far from comprehensive and at lot of things are still completely ad hoc. If I have time to develop these systems further, there are a number of questions worth answering.</p>
<p>First, what kinds of post-processing on the data should I do to make it easiest to visualize? Drawing the raw network of points and edges can be very difficult for people to visually untangle, and some sort of better representation might make it possible for people to quickly understand more complex walkable areas at a glance.</p>
<p>Second, how can I improve the sampling patterns around boundaries to ensure that I find the most number of holes? Are there good methods for characterizing the ways in which shapes fall into a lattice, and are there good tessellation schemes that maximize the chances of intersecting and passing through these shapes?</p>
<p>Third, are regular sampling patterns or randomized sampling patterns better for filling spaces? I could easily modify the target point selection criteria to produce randomized patterns, but it’s not clear if this is worthwhile or, if it is, what kind of randomized patterns would be best.</p>
<p>Fourth, what other information would we like to get from our walkable area maps, now that we can build them? For example, it would be a straightforward extension of the existing system to do things like pathfinding or distance mapping, where a user could pick a point and ask for the shortest path between it and some other point, or to see a heat map of the distance between that point and all other points in the map. Are queries like this useful? What other queries might be?</p>
<p>For now, the walkable area visualizations we’re already getting from Walk Monster are more than enough to show us that the player movement code is pretty broken. I had planned to move on to creating a system for overnight testing via the user input method, but it seems clear that we already have enough problems to fix that there’s no need for that yet. So my next order of business, before improving these tools any further, will be to see what I can do to make the player movement code more robust. While I’m at it, I’d also like to see if I can get it an order of magnitude faster or two, because currently, the thing that slows down Walk Monster rather severely is the slow speed of the collision system.</p>
<p>With luck, I’ll report back soon in another installment on how that work is going.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://the-witness.net/news/2012/12/mapping-the-islands-walkable-surfaces/feed/</wfw:commentRss>
			<slash:comments>50</slash:comments>
		
		
			</item>
	</channel>
</rss>
