<?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>Skitten</title>
	<atom:link href="http://skitten.org/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://skitten.org/blog</link>
	<description>Blog for stuff that isn't food, drink or not having a car</description>
	<lastBuildDate>Mon, 14 Sep 2009 04:51:16 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Comparing 160-bit hash values with SSE</title>
		<link>http://skitten.org/blog/2009/09/13/comparing-with-sse/</link>
		<comments>http://skitten.org/blog/2009/09/13/comparing-with-sse/#comments</comments>
		<pubDate>Mon, 14 Sep 2009 04:51:16 +0000</pubDate>
		<dc:creator>skitten</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://skitten.org/blog/?p=37</guid>
		<description><![CDATA[Comparing 160-bit values using SSE - an educational adventure.]]></description>
			<content:encoded><![CDATA[<p><em>I&#8217;m writing this up as it&#8217;s one of those things I was trying to do and thought I&#8217;d just be able to crib how to do it from the Internet yet a good amount of googleing turned up nothing.  So here it it for anyone else looking for the same thing.</em></p>
<p>I was working on something at work where we put a whole load of entries into a dictionary (an STL map) using a 160-bit key (SHA-1 hash).  Using STL maps requires you to define an ordering on your keys and I&#8217;d knocked up a quick naïve Key class that compared keys by iterating through it in 32-bit chunks testing if those 32 bits were less than or greater than—you only need to continue to the next chunk if they are equal.  Here&#8217;s the class:</p>
<pre>class Key {
 public:

 unsigned int &amp; operator[] (int i) {
 return _data[i];
 }

 unsigned int operator [] (int i) const {
 return _data[i];
 }

 bool operator &lt; (const Key &amp;) const;

 private:
 static const int size = 5;

 unsigned int _data[size];
};</pre>
<p>And here&#8217;s the simple comparison operator:</p>
<pre>inline bool Key::operator &lt; (const Key &amp;k) const {
 for (int i = 0; i &lt; size; i++)
 {
 if (_data[i] &lt; k._data[i])
 return true;
 else if (_data[i] &gt; k._data[i])
 return false;
 }

 // Equal
 return false;
}</pre>
<p>There was a lot of talk about using SSE at the time so I decided that as an academic exercise I&#8217;d try to optimise the key comparison using SSE.  After all, loops with comparisons inside are bad, aren&#8217;t they?</p>
<h1>Deciphering SSE</h1>
<p>My only past experience with SIMD instruction sets had been writing a bilinear interpolation function using 3D-NOW back in 2000 &#8211; SSE was totally new to me.  First I had to work out how to use it from C++ (this is on linux).</p>
<p>With GCC on linux it seems you have a choice of intrinsics you can use.  GCC has <a href="http://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Vector-Extensions.html#Vector-Extensions" onclick="pageTracker._trackPageview('/outgoing/gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Vector-Extensions.html_Vector-Extensions?referer=');">built in vector types</a> which appear to support simple operations and be portable to different architectures; the manual didn&#8217;t explain them very well and it was unclear if I&#8217;d be able to do what I was trying to do using them.  And then there are SSE specific intrinsics I think defined by Microsoft.  These are in various include files depending on which subset of SSE you want:</p>
<ul>
<li>xmmintrin.h &#8211; SSE</li>
<li>emmintrin.h &#8211; SSE2</li>
<li>pmmintrin.h &#8211; SSE3</li>
<li>tmmintrin.h &#8211; <strong>S</strong>SSE3</li>
<li>smmintrin.h &#8211; SSE4.1</li>
<li>ammintrin.h &#8211; SSE4a</li>
<li>bmmintrin.h &#8211; SSE5</li>
</ul>
<p>See how the already confusing SSE numbering scheme is further obfuscated?</p>
<p>These define functions like <em>_mm_add_ps(a, b)</em> that will add two vectors of four 32-bit fp values.  If you actually look in the header you&#8217;ll see it maps to <em>__builtin_ia32_addps(a, b)</em> which will (I hope) compile down to the <em>ADDPS</em> instruction.  There isn&#8217;t always a direct mapping from the intrinsic name to the instruction name.  Microsoft&#8217;s MSDN seems to be the goto place for documentation on these and I found the best way to map between intrinsics and instructions was to type them into google and look for the MSDN links.</p>
<p>Armed with this knowledge, and deciding i didn&#8217;t need anything beyond SSE2, I set forth.</p>
<h1>Figuring out how to do it</h1>
<p>I downloaded the <a href="http://support.amd.com/us/Processor_TechDocs/26568.pdf" onclick="pageTracker._trackPageview('/outgoing/support.amd.com/us/Processor_TechDocs/26568.pdf?referer=');">AMD SSE docs</a> and tried to figure out how to compare two compare two long values without ending up with a loop again.  And it&#8217;s not necessarily obvious when you just have a bit alphabetical list of opcodes.  I followed a few red herring lines of thinking for a bit then realised I could implement the same algorithm but doing the comparisons in parallel, removing the evil loop/compare/branch.</p>
<p>SSE2 has packed equality and greater than instructions for 8, 16 and 32 bit integers.  I decided to avoid the floating point comparisons since my hash values could have bit patterns for illegal fp values.  PCMPGTD seemed the obvious choice since it compares four 32-bit values at once—I decided to ignore the last 32-bits of my 160-bit key until I figured this bit out.  Unfortunately it sets the result by setting each 32-bit chunk to either all 1&#8217;s or all 0&#8217;s and it wasn&#8217;t to clear where to go from there.  However the result from the packed 8-bit comparison PCMPGTB can then be fed into PMOVMSKB instruction to get a nice packed 16-bit array of comparison results.  Thus I can can the packed 8-bit greater than, equal and less than results from 128-but values thusly:</p>
<pre>inline bool Key::operator &lt; (const Key &amp;k) const {
 __m128i i1 = _mm_load_si128(reinterpret_cast&lt;const __m128i *&gt;(_data));
 __m128i i2 = _mm_load_si128(reinterpret_cast&lt;const __m128i *&gt;(k._data));

 __m128i gt128 = _mm_cmpgt_epi8(i1, i2);
 __m128i eq128 = _mm_cmpeq_epi8(i1, i2);
 unsigned int gt = _mm_movemask_epi8(gt128);
 unsigned int eq = _mm_movemask_epi8(eq128);
 unsigned int lt = ~(gt | eq) &amp; 0x0000ffff;</pre>
<p>The idea of our initial algorithm was that I start by comparing the most significant chunks of the two values and if a &gt; b for that then the rest of the bits don&#8217;t matter, likewise is a &lt; b.  Only if the chunks are equal do I have to move on to the next most significant chunk.  Since I now have two binary masks for gt and lt of the chunks I can interpret them as integers and simply say</p>
<pre>bool result = gt &lt; lt;</pre>
<p>and we&#8217;ve compared two 128-bit values with no loops or branches.  And indeed it compiles down to:</p>
<pre>movdqa    (%rsi), %xmm0
 movdqa    (%rdi), %xmm1
 movdqa    %xmm1, %xmm2
 pcmpgtb    %xmm0, %xmm2
 pcmpeqb    %xmm1, %xmm0
 pmovmskb    %xmm2, %edx
 pmovmskb    %xmm0, %eax
 orl    %edx, %eax
 notl    %eax
 andl    $65535, %eax
 cmpl    %eax, %edx
 setb    %al
 ret</pre>
<p>with gcc.</p>
<h2>Two asides</h2>
<p>When I started looking at the compiler&#8217;s assembly output I was completely confused for at least a couple of hours about how it worked at all.  I&#8217;m not too familiar with x86 assembler (I grew up on 68000) and I&#8217;d been reading all the Intel/AMD docs that have it as <em>op dst, src</em> and the gcc output looked like it was loading things then overwriting them, using uninitialised values and all sorts of lunacy.  Turns out gcc uses AT&amp;T syntax, which is <em>op src, dst</em>.</p>
<p>Also, the SSE version of the comparison is not equivalent to the original &#8211; we compare in 8-bit chunks rather than 32-bit and the order of the comparisons is messed up further by the little-endian storage of the 32-bit values in the array.  But it doesn&#8217;t matter as long as the ordering is consistent with itself.</p>
<h1>Extending to 160 bits</h1>
<p>We have 32 bits left over to compare.  Rather than trying to marshal them into our SSE scheme we treat them as the least significant chunk and only defer to them if the first 128 bits are equal, so the result becomes</p>
<pre>bool r = gt &lt; lt || _data[4] &lt; k._data[4];</pre>
<p>Our code now has a single conditional branch in it, but it has to be better than the original, no?</p>
<h1>Evaluation</h1>
<p>I wrote some tests to exercise the STL map &#8211; inserting a million random keys and then looking up both keys that we know are and are not in the map.  The results?  The SSE code is 10% slower.</p>
<p>A colleague pointed out that if the hash is any good then the comparison should almost always be decided on the first chunk and thus usually in the original algorithm only one 32-bit comparison happens anyway.  In the SSE code we are loading more data than needed most of the time and have the additional overhead of marshalling it around.</p>
<p>So the conclusion is think about your problem before plunging in thinking &#8220;SSE good, loops and branches bad&#8221;.  But it was a good SSE learning experience for me.</p>
]]></content:encoded>
			<wfw:commentRss>http://skitten.org/blog/2009/09/13/comparing-with-sse/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Siggraph 2009 google calendars</title>
		<link>http://skitten.org/blog/2009/07/06/siggraph-2009-google-calendars/</link>
		<comments>http://skitten.org/blog/2009/07/06/siggraph-2009-google-calendars/#comments</comments>
		<pubDate>Mon, 06 Jul 2009 20:33:56 +0000</pubDate>
		<dc:creator>skitten</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[siggraph]]></category>

		<guid isPermaLink="false">http://skitten.org/blog/?p=28</guid>
		<description><![CDATA[Update: The calendars are offline.  Trying to clear up my google calendar list I inadvertently deleted them, but Siggraph was over a month ago anyway.

To add to your google calendars, cut and paste the blah@group.calendar.google.com address for the calendar into the &#8220;Add a friend&#8217;s calendar&#8221; box in the &#8220;Other calendars&#8221; list on the left side [...]]]></description>
			<content:encoded><![CDATA[<div><strong>Update:</strong> The calendars are offline.  Trying to clear up my google calendar list I inadvertently deleted them, but Siggraph was over a month ago anyway.</div>
<div></div>
<div>To add to your google calendars, cut and paste the blah@group.calendar.google.com address for the calendar into the &#8220;Add a friend&#8217;s calendar&#8221; box in the &#8220;Other calendars&#8221; list on the left side of the google calendar page.</div>
<ul>
<li>Courses<br />
hvujkn0sbt7jhtirbg948htv18@group.calendar.google.com<br />
<a href="http://www.google.com/calendar/feeds/hvujkn0sbt7jhtirbg948htv18%40group.calendar.google.com/public/basic" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/feeds/hvujkn0sbt7jhtirbg948htv18_40group.calendar.google.com/public/basic?referer=');"><img class="alignnone" title="xml" src="http://www.google.com/calendar/images/xml.gif" alt="" width="36" height="14" /></a> <a href="http://www.google.com/calendar/ical/hvujkn0sbt7jhtirbg948htv18%40group.calendar.google.com/public/basic.ics" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/ical/hvujkn0sbt7jhtirbg948htv18_40group.calendar.google.com/public/basic.ics?referer=');"><img class="alignnone" title="ical" src="http://www.google.com/calendar/images/ical.gif" alt="" width="36" height="14" /></a> <a href="http://www.google.com/calendar/embed?src=hvujkn0sbt7jhtirbg948htv18%40group.calendar.google.com&amp;ctz=America/Los_Angeles" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/embed?src=hvujkn0sbt7jhtirbg948htv18_40group.calendar.google.com_amp_ctz=America/Los_Angeles&amp;referer=');"><img class="alignnone" title="html" src="http://www.google.com/calendar/images/html.gif" alt="" width="36" height="14" /></a></li>
<li>Panels<br />
fkufpr69okdkoeghfpon1gf3ts@group.calendar.google.com<br />
<a href="http://www.google.com/calendar/feeds/fkufpr69okdkoeghfpon1gf3ts%40group.calendar.google.com/public/basic" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/feeds/fkufpr69okdkoeghfpon1gf3ts_40group.calendar.google.com/public/basic?referer=');"><img class="alignnone" title="xml" src="http://www.google.com/calendar/images/xml.gif" alt="" width="36" height="14" /></a> <a href="http://www.google.com/calendar/ical/fkufpr69okdkoeghfpon1gf3ts%40group.calendar.google.com/public/basic.ics" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/ical/fkufpr69okdkoeghfpon1gf3ts_40group.calendar.google.com/public/basic.ics?referer=');"><img class="alignnone" title="ical" src="http://www.google.com/calendar/images/ical.gif" alt="" width="36" height="14" /></a> <a href="http://www.google.com/calendar/embed?src=fkufpr69okdkoeghfpon1gf3ts%40group.calendar.google.com&amp;ctz=America/Los_Angeles" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/embed?src=fkufpr69okdkoeghfpon1gf3ts_40group.calendar.google.com_amp_ctz=America/Los_Angeles&amp;referer=');"><img class="alignnone" title="html" src="http://www.google.com/calendar/images/html.gif" alt="" width="36" height="14" /></a></li>
<li>Papers (<a href="http://kesen.huang.googlepages.com/sig2009.html" onclick="pageTracker._trackPageview('/outgoing/kesen.huang.googlepages.com/sig2009.html?referer=');">Previews</a> from Ke-Sen Huang)<br />
bjem1niro4scd9f983ithrc0j8@group.calendar.google.com<br />
<a href="http://www.google.com/calendar/feeds/bjem1niro4scd9f983ithrc0j8%40group.calendar.google.com/public/basic" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/feeds/bjem1niro4scd9f983ithrc0j8_40group.calendar.google.com/public/basic?referer=');"><img class="alignnone" title="xml" src="http://www.google.com/calendar/images/xml.gif" alt="" width="36" height="14" /></a> <a href="http://www.google.com/calendar/ical/bjem1niro4scd9f983ithrc0j8%40group.calendar.google.com/public/basic.ics" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/ical/bjem1niro4scd9f983ithrc0j8_40group.calendar.google.com/public/basic.ics?referer=');"><img class="alignnone" title="ical" src="http://www.google.com/calendar/images/ical.gif" alt="" width="36" height="14" /></a> <a href="http://www.google.com/calendar/embed?src=bjem1niro4scd9f983ithrc0j8%40group.calendar.google.com&amp;ctz=America/Los_Angeles" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/embed?src=bjem1niro4scd9f983ithrc0j8_40group.calendar.google.com_amp_ctz=America/Los_Angeles&amp;referer=');"><img class="alignnone" title="html" src="http://www.google.com/calendar/images/html.gif" alt="" width="36" height="14" /></a></li>
<li>Talks<br />
l802o82pfvrpdqeq51611i8e98@group.calendar.google.com<br />
<a href="http://www.google.com/calendar/feeds/l802o82pfvrpdqeq51611i8e98%40group.calendar.google.com/public/basic" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/feeds/l802o82pfvrpdqeq51611i8e98_40group.calendar.google.com/public/basic?referer=');"><img class="alignnone" title="xml" src="http://www.google.com/calendar/images/xml.gif" alt="" width="36" height="14" /></a> <a href="http://www.google.com/calendar/ical/l802o82pfvrpdqeq51611i8e98%40group.calendar.google.com/public/basic.ics" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/ical/l802o82pfvrpdqeq51611i8e98_40group.calendar.google.com/public/basic.ics?referer=');"><img class="alignnone" title="ical" src="http://www.google.com/calendar/images/ical.gif" alt="" width="36" height="14" /></a> <a href="http://www.google.com/calendar/embed?src=l802o82pfvrpdqeq51611i8e98%40group.calendar.google.com&amp;ctz=America/Los_Angeles" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/embed?src=l802o82pfvrpdqeq51611i8e98_40group.calendar.google.com_amp_ctz=America/Los_Angeles&amp;referer=');"><img class="alignnone" title="html" src="http://www.google.com/calendar/images/html.gif" alt="" width="36" height="14" /></a></li>
<li>Special events<br />
u68gj6c9b7cqumpt4vqu4u2560@group.calendar.google.com<br />
<a href="http://www.google.com/calendar/feeds/u68gj6c9b7cqumpt4vqu4u2560%40group.calendar.google.com/public/basic" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/feeds/u68gj6c9b7cqumpt4vqu4u2560_40group.calendar.google.com/public/basic?referer=');"><img class="alignnone" title="xml" src="http://www.google.com/calendar/images/xml.gif" alt="" width="36" height="14" /></a> <a href="http://www.google.com/calendar/ical/u68gj6c9b7cqumpt4vqu4u2560%40group.calendar.google.com/public/basic.ics" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/ical/u68gj6c9b7cqumpt4vqu4u2560_40group.calendar.google.com/public/basic.ics?referer=');"><img class="alignnone" title="ical" src="http://www.google.com/calendar/images/ical.gif" alt="" width="36" height="14" /></a> <a href="http://www.google.com/calendar/embed?src=u68gj6c9b7cqumpt4vqu4u2560%40group.calendar.google.com&amp;ctz=America/Los_Angeles" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/embed?src=u68gj6c9b7cqumpt4vqu4u2560_40group.calendar.google.com_amp_ctz=America/Los_Angeles&amp;referer=');"><img class="alignnone" title="html" src="http://www.google.com/calendar/images/html.gif" alt="" width="36" height="14" /></a></li>
<li>Parties!<br />
11aadmb8iiqj93ka0guk111lag@group.calendar.google.com<br />
<a href="http://www.google.com/calendar/feeds/11aadmb8iiqj93ka0guk111lag%40group.calendar.google.com/public/basic" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/feeds/11aadmb8iiqj93ka0guk111lag_40group.calendar.google.com/public/basic?referer=');"><img class="alignnone" title="xml" src="http://www.google.com/calendar/images/xml.gif" alt="" width="36" height="14" /></a> <a href="http://www.google.com/calendar/ical/11aadmb8iiqj93ka0guk111lag%40group.calendar.google.com/public/basic.ics" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/ical/11aadmb8iiqj93ka0guk111lag_40group.calendar.google.com/public/basic.ics?referer=');"><img class="alignnone" title="ical" src="http://www.google.com/calendar/images/ical.gif" alt="" width="36" height="14" /></a> <a href="http://www.google.com/calendar/embed?src=11aadmb8iiqj93ka0guk111lag%40group.calendar.google.com&amp;ctz=America/Los_Angeles" onclick="pageTracker._trackPageview('/outgoing/www.google.com/calendar/embed?src=11aadmb8iiqj93ka0guk111lag_40group.calendar.google.com_amp_ctz=America/Los_Angeles&amp;referer=');"><img class="alignnone" title="html" src="http://www.google.com/calendar/images/html.gif" alt="" width="36" height="14" /></a></li>
</ul>
<p>If you notice anything wrong or missing &#8211; and especially if you have leads on parties, which seem rather thin on the ground so far &#8211; email me at <a href="mailto:c.j.wills@acm.org">c.j.wills@acm.org</a></p>
]]></content:encoded>
			<wfw:commentRss>http://skitten.org/blog/2009/07/06/siggraph-2009-google-calendars/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Projection mapping Arthur&#8217;s Seat</title>
		<link>http://skitten.org/blog/2009/06/21/projection-mapping-arthurs-seat/</link>
		<comments>http://skitten.org/blog/2009/06/21/projection-mapping-arthurs-seat/#comments</comments>
		<pubDate>Mon, 22 Jun 2009 05:31:26 +0000</pubDate>
		<dc:creator>skitten</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[old stuff]]></category>

		<guid isPermaLink="false">http://skitten.org/blog/?p=9</guid>
		<description><![CDATA[Here&#8217;s something fun I found while clearing out the hard drives on my old pc—my undergraduate graphics project.  Basically it involved projection mapping photographs of Arthur&#8217;s Seat (a big hill in Edinburgh) onto a model created from map contours.  Here&#8217;s a couple of videos:


Looks like shit, huh?  But it was a big deal for an [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s something fun I found while clearing out the hard drives on my old pc—my undergraduate graphics project.  Basically it involved projection mapping photographs of Arthur&#8217;s Seat (a big hill in Edinburgh) onto a model created from map contours.  Here&#8217;s a couple of videos:</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="425" height="344" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/5VkV4T_FX0E&amp;hl=en&amp;fs=1&amp;rel=0" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="425" height="344" src="http://www.youtube.com/v/5VkV4T_FX0E&amp;hl=en&amp;fs=1&amp;rel=0" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="425" height="344" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/qcHvHC02VbU&amp;hl=en&amp;fs=1&amp;rel=0" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="425" height="344" src="http://www.youtube.com/v/qcHvHC02VbU&amp;hl=en&amp;fs=1&amp;rel=0" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p>Looks like shit, huh?  But it was a big deal for an undergraduate in 1997 ;)</p>
<p>There were two parts to it; generating a mesh from contour data and then texturing it using photographs.</p>
<h1>Building the mesh</h1>
<p>The starting point for the mesh was digitised contour data from the UK&#8217;s Ordnance Survey (the national mapping agency).  As far as I know the data is literally the contour lines digitised from paper maps so they aren&#8217;t as clean as one might like; for example there are gaps where text appears on the map.  And it&#8217;s all encoded into a fun text format called NTF.</p>
<p>After writing a NTF parser (which I think I did in perl) my first attempt to get useful data was to try and create complete contours.  I found a couple of papers on the internet that weren&#8217;t very useful, then tried taking the end point of each contour segment and trying to connect it to the nearest endpoint of another contour at the same elevation.  This produced fairly crappy results (I don&#8217;t remember why, but I still ended up with a lot of gaps.)</p>
<p>Time for a different approach. This time I took each digitised contour point and just treated it as an isolated point—a point cloud.  Then then assuming that contours don&#8217;t cross (that the ground never twists over itself) I can project them all onto the ground plane (i.e. set z = 0) and create a mesh in 2d using a delaunay triangulation.  Then bring back the z coordinates and voila, a 3d mesh.</p>
<p>The mesh is nice and detailed, but with more polygons than we really want—I forget how many, tens of thousands?  More than you wanted on 1997 hardware, anyway.  I implemented Michael Garland&#8217;s <em><a href="http://mgarland.org/files/papers/scape.pdf" onclick="pageTracker._trackPageview('/outgoing/mgarland.org/files/papers/scape.pdf?referer=');">scape</a></em> algorithm to decimate the mesh to a specified number of points.</p>
<p>I could now texture the mesh with the OS 1:50,000 map of the area and spin it around in real time on an O2—whee!</p>
<p>[Hardware aside: I was mostly writing this on the University's Sun workstations with the <a href="http://www.mesa3d.org/" onclick="pageTracker._trackPageview('/outgoing/www.mesa3d.org/?referer=');">mesa</a> software OpenGL library.  I also had access to the multimedia lab, which had some SGI kit; an O2, an Indy and a Onyx with Reality Engine graphics and a whole 4Mb of texture memory!  Around this time I also bought a 3dfx <a href="http://en.wikipedia.org/wiki/3dfx#Voodoo_Rush" onclick="pageTracker._trackPageview('/outgoing/en.wikipedia.org/wiki/3dfx_Voodoo_Rush?referer=');">Voodoo Rush</a> card for my PC at home, and getting it to run on that required porting the code to windows.]</p>
<h1>Texturing</h1>
<p>I set off with a borrowed SLR and my bicycle to take photos to use for texturing.  It started off as a nice sunny day but was raining by the time I got to the back side of the hill so the lighting is horribly variable.  I had the film scanned on to PhotoCD up to a resolution of about 3k—you can see the set <a href="http://www.flickr.com/photos/ciaran-sf/sets/72157619921275553/" onclick="pageTracker._trackPageview('/outgoing/www.flickr.com/photos/ciaran-sf/sets/72157619921275553/?referer=');">here</a>.</p>
<p>Calibrating the photos was a two stage process.  First marking on the map where I took the photos from and working out those co-ordinates, and then figuring out the camera rotation and field of view (I ignored lens distortion).  For this I wrote a little GL app that displayed the photo and the mesh and allowed the rotation and fov to be varied until the horizons matched.  This was kind of tedious, so I wrote a little routine to work out the error between the two horizons and threw a numerical optimiser (from Numerical Recipes) at it.</p>
<p>Now I could project the textures onto the model.  I had a lot of overlap in coverage, and the original idea had been to use view dependent texturing (an idea borrowed from the <a href="http://www.debevec.org/Research/" onclick="pageTracker._trackPageview('/outgoing/www.debevec.org/Research/?referer=');">Facade</a> paper) where each pixel is textured from the image where the projection is closest to the surface normal.  Fancy shading like this required ditching OpenGL and going to renderman (<a href="http://en.wikipedia.org/wiki/Blue_Moon_Rendering_Tools" onclick="pageTracker._trackPageview('/outgoing/en.wikipedia.org/wiki/Blue_Moon_Rendering_Tools?referer=');">BMRT</a> actually) .</p>
<p>The view dependent texturing looked pretty bad, mainly because of the lighting differences between the photos.  I attempted to balance the photos in PaintShop Pro, but it still looked poor.  In the end the shader just ended up blending the photos.</p>
<p><em>If I find more stuff (like more images) I&#8217;ll update this post.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://skitten.org/blog/2009/06/21/projection-mapping-arthurs-seat/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>My first plugin</title>
		<link>http://skitten.org/blog/2009/06/05/my-first-plugin/</link>
		<comments>http://skitten.org/blog/2009/06/05/my-first-plugin/#comments</comments>
		<pubDate>Sat, 06 Jun 2009 04:01:11 +0000</pubDate>
		<dc:creator>skitten</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[flame]]></category>
		<category><![CDATA[old stuff]]></category>

		<guid isPermaLink="false">http://skitten.org/blog/?p=12</guid>
		<description><![CDATA[My first vfx job was with Smoke &#38; Mirrors in London—I use the word &#8216;job&#8217; in the loosest sense, as they were actually part-sponsoring my doctorate and part of the deal was that they had to employ me for three months over the three years (it was actually more complicated than that, involving a subsidiary that went [...]]]></description>
			<content:encoded><![CDATA[<p>My first vfx job was with <a href="http://smoke-mirrors.com" onclick="pageTracker._trackPageview('/outgoing/smoke-mirrors.com?referer=');">Smoke &amp; Mirrors</a> in London—I use the word &#8216;job&#8217; in the loosest sense, as they were actually part-sponsoring my doctorate and part of the deal was that they had to employ me for three months over the three years (it was actually more complicated than that, involving a subsidiary that went bust, but that&#8217;s a story for another day).</p>
<p>S&amp;M was your typical Soho post-production boutique; five <a href="http://usa.autodesk.com/adsk/servlet/item?siteID=123112&amp;id=10243429" onclick="pageTracker._trackPageview('/outgoing/usa.autodesk.com/adsk/servlet/item?siteID=123112_amp_id=10243429&amp;referer=');">flame</a> suites and a bunch of crazy people rushing around.  Me, fresh-faced post graduate who&#8217;d seen A Bug&#8217;s Life and thought he&#8217;d like to work in computer graphics, is dumped in front of the engineering flame (i.e. unlicensed), handed the thousand page manual and asked to write a rack defocus plugin so that they didn&#8217;t have to buy another copy of a <a href="http://www.genarts.com/product/sapphire/autodesk/features" onclick="pageTracker._trackPageview('/outgoing/www.genarts.com/product/sapphire/autodesk/features?referer=');">commercial one</a>.</p>
<p>The flame API is actually very simple; you effectively get a buffer of pixels in and some parameters then have to produce a buffer of pixels out.  The hard parts were finding my way around the flame interface and working out what a rack defocus is.</p>
<p>I knew enough about optics and image processing to know that there&#8217;s not enough information in the 2d image to do a defocus properly.  So I spent a good while looking at what the commercial plugin did and googling stuff until I hit the in-retrospect obvious key—convolve the image with a kernel the shape of the lens aperture you want to see, and boost the highlights so that they don&#8217;t go flat and disappear when they are blurred.</p>
<p>So to get those nice hexagonal, octagonal and circular <a href="http://en.wikipedia.org/wiki/Bokeh" onclick="pageTracker._trackPageview('/outgoing/en.wikipedia.org/wiki/Bokeh?referer=');">bokeh</a> effects that people love so much I just needed to write a routine to draw a polygon with the appropriate number of sides into the kernel and then convolve it with the image (after first boosting any pixels above a certain threshold).  For the convolution I converted the image to floating point and used the <a href="http://www.fftw.org" onclick="pageTracker._trackPageview('/outgoing/www.fftw.org?referer=');">FFTW</a> library to do a fourier convolution, and believe me on an old <a href="http://en.wikipedia.org/wiki/SGI_Octane" onclick="pageTracker._trackPageview('/outgoing/en.wikipedia.org/wiki/SGI_Octane?referer=');">SGI Octane</a> it was slow.</p>
<p>Sure it&#8217;s simple and everyone knows how to do it, but it was a big deal to me at the time.  And here&#8217;s a little animation I found on my old pc, a still photo of Hong Kong with a foreground matte and animated defocus.</p>
<p><object width="425" height="344" data="http://www.youtube.com/v/pypSxoWbI5g&amp;hl=en&amp;fs=1&amp;rel=0" type="application/x-shockwave-flash"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/pypSxoWbI5g&amp;hl=en&amp;fs=1&amp;rel=0" /><param name="allowfullscreen" value="true" /></object></p>
]]></content:encoded>
			<wfw:commentRss>http://skitten.org/blog/2009/06/05/my-first-plugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Siggraph sketch that never happened</title>
		<link>http://skitten.org/blog/2009/06/01/siggraph-sketch-that-never-happened/</link>
		<comments>http://skitten.org/blog/2009/06/01/siggraph-sketch-that-never-happened/#comments</comments>
		<pubDate>Tue, 02 Jun 2009 04:41:02 +0000</pubDate>
		<dc:creator>skitten</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[phd]]></category>

		<guid isPermaLink="false">http://www.skitten.org/blog/?p=4</guid>
		<description><![CDATA[Back in 2003, I submitted a sketch to Siggraph on my thesis work.  I had been working at Framestore for a few months, was waiting for the University to organise my viva and figured that if I got something accepted I might have a fighting chance of getting someone else to pay my airfare to [...]]]></description>
			<content:encoded><![CDATA[<p>Back in 2003, I submitted a sketch to Siggraph on my thesis work.  I had been working at <a href="http://framestore.com" onclick="pageTracker._trackPageview('/outgoing/framestore.com?referer=');">Framestore</a> for a few months, was waiting for the University to organise my viva and figured that if I got something accepted I might have a fighting chance of getting someone else to pay my airfare to San Diego.</p>
<p>Also our group never pushed people very hard to publish so all I had out there was a couple of work in progress papers, and since I wasn&#8217;t intending to pursue this work further it seemed a shame that there wasn&#8217;t anything out there describing my work in its final form (other than the dusty copies of the thesis sitting on my and the University&#8217;s bookshelves).</p>
<p>So I sent it off into the electronic submission system, got an id number, and never heard anything again—I assume it was rejected but who knows, maybe it was just lost in the system.</p>
<p>But for those who care, <a href="http://www.skitten.org/blog/wp-content/uploads/2009/06/sketches0020.pdf" onclick="pageTracker._trackPageview('/outgoing/www.skitten.org/blog/wp-content/uploads/2009/06/sketches0020.pdf?referer=');">here it is</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://skitten.org/blog/2009/06/01/siggraph-sketch-that-never-happened/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
