<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title>ltslashgt</title>
        <link>https://ltslashgt.com</link>
        <language>en</language>
        <webMaster>no@nathanostgard.com (Nathan Ostgard)</webMaster>
        <lastBuildDate>Wed, 03 Jan 2018 22:31:00 +0000</lastBuildDate>
        <pubDate>Sun, 07 Aug 2011 21:00:00 +0000</pubDate>
        <copyright>Copyright 2007-2018</copyright>
        <ttl>60</ttl>
        <description>musings about coding and design</description>
        
        <item>
            <title>Rendering models with Molehill</title>
            <link>https://ltslashgt.com/2011/08/07/rendering-models-with-molehill/</link>
            <pubDate>Sun, 07 Aug 2011 21:00:00 +0000</pubDate>
            <guid>https://ltslashgt.com/2011/08/07/rendering-models-with-molehill/</guid>
            <description><![CDATA[
<p>I’ve been wanting to follow up on my <a href="http://ltslashgt.com/2011/02/28/molehill-spinning-cube/">last Molehill post</a>
with some more advanced examples, and I’ve finally gotten around to it! This
is the first of several posts about loading and rendering various model
formats in <a href="http://labs.adobe.com/technologies/flashplatformruntimes/features/stage3d.html">Molehill</a>.</p>

<p>Model formats can be frustrating. Most are ancient or written without hardware
rendering in mind. You often have to massage the data a bit to make it usable.
It’s interesting to see how formats changed over time as hardware accelerated
rendering standardize things. I’m going to start with some older formats, and
work up to newer ones.</p>

<p>Here’s <a href="https://github.com/noonat/models-molehill">the code</a> for this post in action, featuring the lovely <a href="http://forums.cgsociety.org/showthread.php?t=113636">Bunker model</a>
by <a href="http://www.bobotheseal.com/">Bobo the Seal</a>:</p>

<div style="margin-bottom: 1em;">
  <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="640" height="480">
    <param name="movie" value="https://ltslashgt.com/images/molehill-models-obj.swf" />
    <param name="wmode" value="direct" />
    <!--[if !IE]>-->
    <object type="application/x-shockwave-flash" data="https://ltslashgt.com/images/molehill-models-obj.swf" width="640" height="480">
      <param name="wmode" value="direct" />
    <!--<![endif]-->
      <p>Flash is required to view this content.</p>
    <!--[if !IE]>-->
    </object>
    <!--<![endif]-->
  </object>
</div>

<h3 id="wavefront-obj">Wavefront OBJ</h3>

<p>The <a href="http://en.wikipedia.org/wiki/Wavefront_.obj_file">Wavefront OBJ</a> format was created for Wavefront’s Advanced Visualizer
back in the 80s. It hasn’t seen an update since the 90s, as far I can tell.
(This is how things usually go with model formats.) It’s often still used
today for static objects, because it’s a relatively simple format, and
supported by nearly all modeling programs.</p>

<p>It’s a text-based model format. It supports <a href="http://people.sc.fsu.edu/~jburkardt/txt/obj_format.txt">many esoteric things</a>, but this
loader only supports the most common ones: vertex position, normal, and UV
coordinates; faces with three or more vertices; and material groups. The OBJ
format doesn’t support animations.</p>

<p>Here’s a <a href="https://gist.github.com/1131091">Gist with an example</a> of a cube in OBJ format. It’s a line based
format. The first word of each line specifies the command, and parameters
to the command follow. Lines starting with a <code>#</code> are comments.</p>

<ul>
  <li><code>o cube</code>: Sets the object name. There is only one object per OBJ file.</li>
  <li><code>mtllib cube.mtl</code>: Material file to use for this model file. This example
loader doesn’t support MTL files, so you will have to set the material
textures manually by name.</li>
  <li><code>v -0.500000 -0.500000 0.500000</code>: Vertex position (X, Y and Z coordinates).</li>
  <li><code>vn 0.000000 0.000000 1.000000</code>: Vertex normal.</li>
  <li><code>vt 0.000000 0.000000</code>: Vertex texture coordinate (UV).</li>
  <li><code>g cube</code>: Starts a new face group. Each face group has its own set of
indexes and a material. The OBJ file may have more than one of these.</li>
  <li><code>usemtl cube</code>: Specifies the material from the <code>mtllib</code> to use for the
face group.</li>
  <li><code>s 1</code>: Sets the smoothing group for a set of faces. Smoothing groups control
how vertex normals are generated for a model. I’m ignoring this statement
in my loader, since most game models have normals exported for them.</li>
  <li><code>f 1/1/1 2/2/1 3/3/1</code>: Indexes for a face. Each vertex has a tuple of
indexes: <code>positionIndex/uvIndex/normalIndex</code>. OBJ faces are polygons, not
triangles, so they may have more than three vertices.</li>
</ul>

<h3 id="the-code">The code</h3>

<p>You’ll want to <a href="https://github.com/noonat/models-molehill">grab the example code on GitHub</a> to follow along.
It’s based on <a href="http://ltslashgt.com/2011/02/28/molehill-spinning-cube/">my previous post</a>. I’m not going to explain all of it, but will
outline the broad strokes.</p>

<p>The OBJ file and its textures get embedded into the AS3 code as a byte array
and bitmaps, like normal. You can find that in DemoOBJ.as:</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="o">[</span><span class="n">Embed</span><span class="o">(</span><span class="n">source</span><span class="o">=</span><span class="s2">&quot;../res/bunker/bunker.obj&quot;</span><span class="o">,</span> <span class="n">mimeType</span><span class="o">=</span><span class="s2">&quot;application/octet-stream&quot;</span><span class="o">)]</span>
<span class="kd">static</span> <span class="kd">protected</span> <span class="kd">const</span> <span class="n">BUNKER_OBJ</span><span class="p">:</span><span class="kt">Class</span><span class="o">;</span>
<span class="o">[</span><span class="n">Embed</span><span class="o">(</span><span class="n">source</span><span class="o">=</span><span class="s2">&quot;../res/bunker/fidget_head.png&quot;</span><span class="o">)]</span>
<span class="kd">static</span> <span class="kd">protected</span> <span class="kd">const</span> <span class="n">BUNKER_HEAD</span><span class="p">:</span><span class="kt">Class</span><span class="o">;</span>
<span class="o">[</span><span class="n">Embed</span><span class="o">(</span><span class="n">source</span><span class="o">=</span><span class="s2">&quot;../res/bunker/fidget_body.png&quot;</span><span class="o">)]</span>
<span class="kd">static</span> <span class="kd">protected</span> <span class="kd">const</span> <span class="n">BUNKER_BODY</span><span class="p">:</span><span class="kt">Class</span><span class="o">;</span></code></pre></div>

<p>It’s created, and loaded like so:</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="c1">// Load the model, and set the material textures</span>
<span class="n">_obj</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">OBJ</span><span class="o">();</span>
<span class="n">_obj</span><span class="o">.</span><span class="na">readBytes</span><span class="o">(</span><span class="k">new</span> <span class="kt">BUNKER_OBJ</span><span class="o">(),</span> <span class="n">_context</span><span class="o">);</span>
<span class="n">_obj</span><span class="o">.</span><span class="na">setMaterial</span><span class="o">(</span><span class="s1">&#39;h_head&#39;</span><span class="o">,</span> <span class="n">_headTexture</span><span class="o">);</span>
<span class="n">_obj</span><span class="o">.</span><span class="na">setMaterial</span><span class="o">(</span><span class="s1">&#39;u_torso&#39;</span><span class="o">,</span> <span class="n">_bodyTexture</span><span class="o">);</span>
<span class="n">_obj</span><span class="o">.</span><span class="na">setMaterial</span><span class="o">(</span><span class="s1">&#39;l_legs&#39;</span><span class="o">,</span> <span class="n">_bodyTexture</span><span class="o">);</span></code></pre></div>

<p>You’ll note that I set the material textures manually here, since the loader
doesn’t handle MTL files.</p>

<p>The OBJ file itself is parsed in <code>OBJ.as</code>, in the <code>readBytes()</code> method. The
byte array for the OBJ is passed in, and it has to be converted into text,
then read in line by line. Any empty lines or lines starting with a <code>#</code> should
be skipped, like so:</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="kd">var</span> <span class="n">text</span><span class="p">:</span><span class="kt">String</span> <span class="o">=</span> <span class="n">bytes</span><span class="o">.</span><span class="na">readUTFBytes</span><span class="o">(</span><span class="n">bytes</span><span class="o">.</span><span class="na">bytesAvailable</span><span class="o">);</span>
<span class="kd">var</span> <span class="n">lines</span><span class="p">:</span><span class="kt">Array</span> <span class="o">=</span> <span class="n">text</span><span class="o">.</span><span class="na">split</span><span class="o">(/[\</span><span class="n">r</span><span class="o">\</span><span class="n">n</span><span class="o">]+/);</span>
<span class="k">for</span> <span class="k">each</span> <span class="o">(</span><span class="kd">var</span> <span class="n">line</span><span class="p">:</span><span class="kt">String</span> <span class="k">in</span> <span class="n">lines</span><span class="o">)</span> <span class="o">{</span>
  <span class="c1">// Trim whitespace from the line</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="na">replace</span><span class="o">(/^\</span><span class="n">s</span><span class="o">*|\</span><span class="n">s</span><span class="o">*</span><span class="n">$</span><span class="o">/</span><span class="n">g</span><span class="o">,</span> <span class="s1">&#39;&#39;</span><span class="o">);</span>
  <span class="k">if</span> <span class="o">(</span><span class="n">line</span> <span class="o">===</span> <span class="s1">&#39;&#39;</span> <span class="o">||</span> <span class="n">line</span><span class="o">.</span><span class="na">charAt</span><span class="o">(</span><span class="mi">0</span><span class="o">)</span> <span class="o">===</span> <span class="s1">&#39;#&#39;</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">// Blank line or comment, ignore it</span>
    <span class="k">continue</span><span class="o">;</span>
  <span class="o">}</span>

  <span class="c1">// TODO: parse the line</span>
<span class="o">}</span></code></pre></div>

<p>For that todo, you need to split the line up by spaces, and then check what
kind of command it is. That can be done like so:</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="c1">// Split line into fields on whitespace</span>
<span class="kd">var</span> <span class="n">fields</span><span class="p">:</span><span class="kt">Array</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="na">split</span><span class="o">(/\</span><span class="n">s</span><span class="o">+/);</span>
<span class="k">switch</span> <span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">toLowerCase</span><span class="o">())</span> <span class="o">{</span>
  <span class="k">case</span> <span class="s1">&#39;v&#39;</span><span class="o">:</span>
    <span class="c1">// TODO: parse vertex position</span>
    <span class="k">break</span><span class="o">;</span>

  <span class="k">case</span> <span class="s1">&#39;vn&#39;</span><span class="o">:</span>
    <span class="c1">// TODO: parse vertex normal</span>
    <span class="k">break</span><span class="o">;</span>

  <span class="k">case</span> <span class="s1">&#39;vt&#39;</span><span class="o">:</span>
    <span class="c1">// TODO: parse vertex uv</span>
    <span class="k">break</span><span class="o">;</span>

  <span class="k">case</span> <span class="s1">&#39;f&#39;</span><span class="o">:</span>
    <span class="c1">// TODO: parse face</span>
    <span class="k">break</span><span class="o">;</span>

  <span class="k">case</span> <span class="s1">&#39;g&#39;</span><span class="o">:</span>
    <span class="c1">// TODO: parse group</span>
    <span class="k">break</span><span class="o">;</span>

  <span class="k">case</span> <span class="s1">&#39;o&#39;</span><span class="o">:</span>
    <span class="c1">// TODO: parse object</span>
    <span class="k">break</span><span class="o">;</span>

  <span class="k">case</span> <span class="s1">&#39;usemtl&#39;</span><span class="o">:</span>
    <span class="c1">// TODO: parse material</span>
    <span class="k">break</span><span class="o">;</span>
<span class="o">}</span></code></pre></div>

<p>The vertex position (<code>v</code> command) is just three floats. The fields all get
converted from strings into numbers, and pushed into the positions array:</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="k">case</span> <span class="s1">&#39;v&#39;</span><span class="o">:</span>
  <span class="n">positions</span><span class="o">.</span><span class="na">push</span><span class="o">(</span>
    <span class="nf">parseFloat</span><span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="mi">1</span><span class="o">]),</span>
    <span class="nf">parseFloat</span><span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="mi">2</span><span class="o">]),</span>
    <span class="nf">parseFloat</span><span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="mi">3</span><span class="o">]));</span>
  <span class="k">break</span><span class="o">;</span></code></pre></div>

<p>The vertex normal (<code>vn</code> command) works the same way:</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="k">case</span> <span class="s1">&#39;vn&#39;</span><span class="o">:</span>
  <span class="n">normals</span><span class="o">.</span><span class="na">push</span><span class="o">(</span>
    <span class="nf">parseFloat</span><span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="mi">1</span><span class="o">]),</span>
    <span class="nf">parseFloat</span><span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="mi">2</span><span class="o">]),</span>
    <span class="nf">parseFloat</span><span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="mi">3</span><span class="o">]));</span>
  <span class="k">break</span><span class="o">;</span></code></pre></div>

<p>The vertex UV (<code>vt</code> command) is only two floats. OBJ has a flipped V axis for
texture coordinates, so it needs to get flipped back to normal:</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="k">case</span> <span class="s1">&#39;vt&#39;</span><span class="o">:</span>
  <span class="n">uvs</span><span class="o">.</span><span class="na">push</span><span class="o">(</span>
    <span class="nf">parseFloat</span><span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="mi">1</span><span class="o">]),</span>
    <span class="mf">1.0</span> <span class="o">-</span> <span class="nf">parseFloat</span><span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="mi">2</span><span class="o">]));</span>
  <span class="k">break</span><span class="o">;</span></code></pre></div>

<p>For the group (<code>g</code> command), a new <code>OBJGroup</code> object is created and added to
the list of groups. Groups have several properties (name, material, and
faces), so the object is useful to keep track of all that.</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="k">case</span> <span class="s1">&#39;g&#39;</span><span class="o">:</span>
  <span class="n">group</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">OBJGroup</span><span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="mi">1</span><span class="o">],</span> <span class="n">materialName</span><span class="o">);</span>
  <span class="n">groups</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="n">group</span><span class="o">);</span>
  <span class="k">break</span><span class="o">;</span></code></pre></div>

<p>The material name (<code>usemtl</code> command) just gets saved and assigned to the
current group (if there is one). Any future groups get assigned the current
material by default, unless they have their own <code>usemtl</code> command.</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="k">case</span> <span class="s1">&#39;usemtl&#39;</span><span class="o">:</span>
  <span class="n">materialName</span> <span class="o">=</span> <span class="n">fields</span><span class="o">[</span><span class="mi">1</span><span class="o">];</span>
  <span class="k">if</span> <span class="o">(</span><span class="n">group</span> <span class="o">!==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">group</span><span class="o">.</span><span class="na">materialName</span> <span class="o">=</span> <span class="n">materialName</span><span class="o">;</span>
  <span class="o">}</span>
  <span class="k">break</span><span class="o">;</span></code></pre></div>

<p>The group face (<code>f</code> command) is a list of index tuples, as described earlier.
A new vector is created to store the index tuples for the face, and the face
is added to the current group. It will be processed later.</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="k">case</span> <span class="s1">&#39;f&#39;</span><span class="o">:</span>
  <span class="n">face</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">Vector.&lt;String&gt;</span><span class="o">();</span>
  <span class="k">for</span> <span class="k">each</span> <span class="o">(</span><span class="kd">var</span> <span class="n">tuple</span><span class="p">:</span><span class="kt">String</span> <span class="k">in</span> <span class="n">fields</span><span class="o">.</span><span class="na">slice</span><span class="o">(</span><span class="mi">1</span><span class="o">))</span> <span class="o">{</span>
    <span class="n">face</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="n">tuple</span><span class="o">);</span>
  <span class="o">}</span>
  <span class="k">if</span> <span class="o">(</span><span class="n">group</span> <span class="o">===</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">group</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">OBJGroup</span><span class="o">(</span><span class="kc">null</span><span class="o">,</span> <span class="n">materialName</span><span class="o">);</span>
    <span class="n">groups</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="n">group</span><span class="o">);</span>
  <span class="o">}</span>
  <span class="n">group</span><span class="o">.</span><span class="na">_faces</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="n">face</span><span class="o">);</span>
  <span class="k">break</span><span class="o">;</span></code></pre></div>

<h3 id="fixing-up-the-data">Fixing up the data</h3>

<p>That’s all of the commands we need to handle. This loop will repeat for all
of the lines in the file. Once it’s done, we’ll have several separate streams
of vertex data (<code>positions</code>, <code>normals</code>, and <code>uvs</code>). We’ll also have a list of
<code>groups</code>, each with its own list of <code>faces</code>, which have indices into these
separate streams.</p>

<p>This is a problem. OBJ specifies a separate index for position, normal, and
UV, but modern hardware rendering doesn’t support that. We can only have one
index stream. To fix this, we need to merge all three vertex streams into a
single stream. The face indices also need to be updated to point to the right
offsets within this new stream.</p>

<p>To do this, each group gets a new index stream. Then, for each unique index
tuple in the faces, we write a new vertex into the merged stream. If we’ve
already encountered that index tuple in another face, we use the existing
merged index.</p>

<p>The other problem we have is that OBJ allows polygonal faces: that is, faces
don’t have to be triangles. This is a problem: Context3D only supports drawing
triangles. To fix this, we’ll turn any non-triangles into a <a href="http://en.wikipedia.org/wiki/Triangle_fan">triangle fan</a>.</p>

<p>The loop for all of this looks like this:</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="k">for</span> <span class="k">each</span> <span class="o">(</span><span class="n">group</span> <span class="k">in</span> <span class="n">groups</span><span class="o">)</span> <span class="o">{</span>
  <span class="n">group</span><span class="o">.</span><span class="na">_indices</span><span class="o">.</span><span class="na">length</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
  <span class="k">for</span> <span class="k">each</span> <span class="o">(</span><span class="n">face</span> <span class="k">in</span> <span class="n">group</span><span class="o">.</span><span class="na">_faces</span><span class="o">)</span> <span class="o">{</span>
    <span class="kd">var</span> <span class="n">il</span><span class="p">:</span><span class="kt">int</span> <span class="o">=</span> <span class="n">face</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span>
    <span class="k">for</span> <span class="o">(</span><span class="kd">var</span> <span class="n">i</span><span class="p">:</span><span class="kt">int</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">il</span><span class="o">;</span> <span class="o">++</span><span class="n">i</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">group</span><span class="o">.</span><span class="na">_indices</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="n">mergeTuple</span><span class="o">(</span><span class="n">face</span><span class="o">[</span><span class="n">i</span><span class="o">],</span> <span class="n">positions</span><span class="o">,</span> <span class="n">normals</span><span class="o">,</span> <span class="n">uvs</span><span class="o">));</span>
      <span class="n">group</span><span class="o">.</span><span class="na">_indices</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="n">mergeTuple</span><span class="o">(</span><span class="n">face</span><span class="o">[</span><span class="mi">0</span><span class="o">],</span> <span class="n">positions</span><span class="o">,</span> <span class="n">normals</span><span class="o">,</span> <span class="n">uvs</span><span class="o">));</span>
      <span class="n">group</span><span class="o">.</span><span class="na">_indices</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="n">mergeTuple</span><span class="o">(</span><span class="n">face</span><span class="o">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="o">],</span> <span class="n">positions</span><span class="o">,</span> <span class="n">normals</span><span class="o">,</span> <span class="n">uvs</span><span class="o">));</span>
    <span class="o">}</span>
  <span class="o">}</span>
  <span class="n">group</span><span class="o">.</span><span class="na">indexBuffer</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">createIndexBuffer</span><span class="o">(</span><span class="n">group</span><span class="o">.</span><span class="na">_indices</span><span class="o">.</span><span class="na">length</span><span class="o">);</span>
  <span class="n">group</span><span class="o">.</span><span class="na">indexBuffer</span><span class="o">.</span><span class="na">uploadFromVector</span><span class="o">(</span><span class="n">group</span><span class="o">.</span><span class="na">_indices</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">group</span><span class="o">.</span><span class="na">_indices</span><span class="o">.</span><span class="na">length</span><span class="o">);</span>
  <span class="n">group</span><span class="o">.</span><span class="na">_faces</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span></code></pre></div>

<p>This loop calls <code>mergeTuple</code> for each index tuple in the face. That function
looks like this:</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="kd">protected</span> <span class="kd">function </span><span class="nf">mergeTuple</span><span class="o">(</span>
  <span class="n">tuple</span><span class="o">:</span><span class="kt">String</span><span class="o">,</span> <span class="n">positions</span><span class="o">:</span><span class="kt">Vector.&lt;Number&gt;</span><span class="o">,</span> <span class="n">normals</span><span class="o">:</span><span class="kt">Vector.&lt;Number&gt;</span><span class="o">,</span>
  <span class="n">uvs</span><span class="o">:</span><span class="kt">Vector.&lt;Number&gt;</span><span class="o">):</span><span class="kt">uint</span>
<span class="o">{</span>
  <span class="k">if</span> <span class="o">(</span><span class="n">_tupleIndices</span><span class="o">[</span><span class="n">tuple</span><span class="o">]</span> <span class="o">!==</span> <span class="kc">undefined</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">// Already merged, return the merged index</span>
    <span class="k">return</span> <span class="n">_tupleIndices</span><span class="o">[</span><span class="n">tuple</span><span class="o">];</span>
  <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
    <span class="kd">var</span> <span class="n">faceIndices</span><span class="p">:</span><span class="kt">Array</span> <span class="o">=</span> <span class="n">tuple</span><span class="o">.</span><span class="na">split</span><span class="o">(</span><span class="s1">&#39;/&#39;</span><span class="o">);</span>

    <span class="c1">// Position index</span>
    <span class="kd">var</span> <span class="n">index</span><span class="p">:</span><span class="kt">uint</span> <span class="o">=</span> <span class="nf">parseInt</span><span class="o">(</span><span class="n">faceIndices</span><span class="o">[</span><span class="mi">0</span><span class="o">],</span> <span class="mi">10</span><span class="o">)</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span>
    <span class="n">_vertices</span><span class="o">.</span><span class="na">push</span><span class="o">(</span>
      <span class="n">positions</span><span class="o">[</span><span class="n">index</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">0</span><span class="o">],</span>
      <span class="n">positions</span><span class="o">[</span><span class="n">index</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">1</span><span class="o">],</span>
      <span class="n">positions</span><span class="o">[</span><span class="n">index</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">2</span><span class="o">]);</span>

    <span class="c1">// Normal index</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">faceIndices</span><span class="o">.</span><span class="na">length</span> <span class="o">&gt;</span> <span class="mi">2</span> <span class="o">&amp;&amp;</span> <span class="n">faceIndices</span><span class="o">[</span><span class="mi">2</span><span class="o">].</span><span class="n">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">index</span> <span class="o">=</span> <span class="nf">parseInt</span><span class="o">(</span><span class="n">faceIndices</span><span class="o">[</span><span class="mi">2</span><span class="o">],</span> <span class="mi">10</span><span class="o">)</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span>
      <span class="n">_vertices</span><span class="o">.</span><span class="na">push</span><span class="o">(</span>
        <span class="n">normals</span><span class="o">[</span><span class="n">index</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">0</span><span class="o">],</span>
        <span class="n">normals</span><span class="o">[</span><span class="n">index</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">1</span><span class="o">],</span>
        <span class="n">normals</span><span class="o">[</span><span class="n">index</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">2</span><span class="o">]);</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
      <span class="c1">// Face doesn&#39;t have a normal</span>
      <span class="n">_vertices</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">// UV index</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">faceIndices</span><span class="o">.</span><span class="na">length</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="n">faceIndices</span><span class="o">[</span><span class="mi">1</span><span class="o">].</span><span class="n">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">index</span> <span class="o">=</span> <span class="nf">parseInt</span><span class="o">(</span><span class="n">faceIndices</span><span class="o">[</span><span class="mi">1</span><span class="o">],</span> <span class="mi">10</span><span class="o">)</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span>
      <span class="n">_vertices</span><span class="o">.</span><span class="na">push</span><span class="o">(</span>
        <span class="n">uvs</span><span class="o">[</span><span class="n">index</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">0</span><span class="o">],</span>
        <span class="n">uvs</span><span class="o">[</span><span class="n">index</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">1</span><span class="o">]);</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
      <span class="c1">// Face doesn&#39;t have a UV</span>
      <span class="n">_vertices</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">// Cache the merged tuple index in case it&#39;s used again</span>
    <span class="k">return</span> <span class="n">_tupleIndices</span><span class="o">[</span><span class="n">tuple</span><span class="o">]</span> <span class="o">=</span> <span class="n">_tupleIndex</span><span class="o">++;</span>
  <span class="o">}</span>
<span class="o">}</span></code></pre></div>

<p>This function is the bulk of the work for the OBJ loader. If the tuple
already exists in our tuple indices cache, we return the existing merged
index. Otherwise, we copy the vertex data that the face points to into the
merged array, and then return the new index.</p>

<p>OBJ doesn’t require that normal and UV are specified, so we just shove some
zeroes in there to keep things consistent when that happens. Modern vertex
buffers are zero-indexed, but OBJ’s are one-indexed, so we also need to
subtract one from all of the indices.</p>

<p>Last but not least, we need to create a vertex buffer for the new stream:</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="n">vertexBuffer</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">createVertexBuffer</span><span class="o">(</span><span class="n">_vertices</span><span class="o">.</span><span class="na">length</span> <span class="o">/</span> <span class="mi">8</span><span class="o">,</span> <span class="mi">8</span><span class="o">);</span>
<span class="n">vertexBuffer</span><span class="o">.</span><span class="na">uploadFromVector</span><span class="o">(</span><span class="n">_vertices</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">_vertices</span><span class="o">.</span><span class="na">length</span> <span class="o">/</span> <span class="mi">8</span><span class="o">);</span></code></pre></div>

<h3 id="rendering-the-model">Rendering the model</h3>

<p>That handles loading the model. Rendering it is pretty straight-forward. The
<code>update()</code> function in <code>DemoOBJ.as</code> does some setup, then renders the OBJ
like so:</p>

<div class="highlight"><pre><code class="language-as3" data-lang="as3"><span class="c1">// Draw the model</span>
<span class="n">_context</span><span class="o">.</span><span class="na">setVertexBufferAt</span><span class="o">(</span>
  <span class="mi">0</span><span class="o">,</span> <span class="n">_obj</span><span class="o">.</span><span class="na">vertexBuffer</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">Context3DVertexBufferFormat</span><span class="o">.</span><span class="na">FLOAT_3</span><span class="o">);</span>
<span class="n">_context</span><span class="o">.</span><span class="na">setVertexBufferAt</span><span class="o">(</span>
  <span class="mi">1</span><span class="o">,</span> <span class="n">_obj</span><span class="o">.</span><span class="na">vertexBuffer</span><span class="o">,</span> <span class="mi">3</span><span class="o">,</span> <span class="n">Context3DVertexBufferFormat</span><span class="o">.</span><span class="na">FLOAT_3</span><span class="o">);</span>
<span class="n">_context</span><span class="o">.</span><span class="na">setVertexBufferAt</span><span class="o">(</span>
  <span class="mi">2</span><span class="o">,</span> <span class="n">_obj</span><span class="o">.</span><span class="na">vertexBuffer</span><span class="o">,</span> <span class="mi">6</span><span class="o">,</span> <span class="n">Context3DVertexBufferFormat</span><span class="o">.</span><span class="na">FLOAT_2</span><span class="o">);</span>
<span class="k">for</span> <span class="k">each</span> <span class="o">(</span><span class="kd">var</span> <span class="n">group</span><span class="p">:</span><span class="kt">OBJGroup</span> <span class="k">in</span> <span class="n">_obj</span><span class="o">.</span><span class="na">groups</span><span class="o">)</span> <span class="o">{</span>
  <span class="n">_context</span><span class="o">.</span><span class="na">setTextureAt</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">_obj</span><span class="o">.</span><span class="na">getMaterial</span><span class="o">(</span><span class="n">group</span><span class="o">.</span><span class="na">materialName</span><span class="o">));</span>
  <span class="n">_context</span><span class="o">.</span><span class="na">drawTriangles</span><span class="o">(</span><span class="n">group</span><span class="o">.</span><span class="na">indexBuffer</span><span class="o">);</span>
<span class="o">}</span></code></pre></div>

<p>This sets up the vertex buffer streams for the position, normal, and UVs in
the OBJ buffer. Then it loops over each group, setting the texture for the
group’s material, and drawing the triangles associated with that group.</p>

<p>I hope that helps show how to load and render models in Molehill. I can’t
cover all the code in this post, so feel free to <a href="http://ltslashgt.com/2011/08/07/rendering-models-with-molehill/#comments">comment</a> or email me if you
have any questions. If you want to find more OBJ files to mess around with,
I recommend looking at the <a href="http://www.polycount.com/forum/showthread.php?t=70908">SDK master thread</a> on Polycount.</p>

<p>In my next post, I’ll look at <a href="http://tfc.duke.free.fr/coding/mdl-specs-en.html">Quake MDL</a> files. This is another arcane
format, but it’s binary and has animation, so there is more to learn!</p>



<span class="comments">
  <a href="https://ltslashgt.com/2011/08/07/rendering-models-with-molehill/#disqus_thread" data-disqus-identifier="/2011/08/07/rendering-models-with-molehill/">Comments</a>
</span>

]]></description>
            
            <category domain="https://ltslashgt.com/tags/#actionscript">actionscript</category>
            
        </item>
        
        <item>
            <title>Texty</title>
            <link>https://ltslashgt.com/2011/08/02/texty/</link>
            <pubDate>Tue, 02 Aug 2011 13:00:00 +0000</pubDate>
            <guid>https://ltslashgt.com/2011/08/02/texty/</guid>
            <description><![CDATA[
<p><a href="https://github.com/LearnBoost/texty">Texty</a> is a JavaScript library from <a href="https://www.learnboost.com/">LearnBoost</a> that provides slim,
textbox-like editing for the canvas. It provides text input, selection,
and caret positioning. It seems like it would be a great starting point if
you’re thinking about building a canvas text editor or IDE.</p>



]]></description>
            
            <category domain="https://ltslashgt.com/tags/#javascript">javascript</category>
            
            <category domain="https://ltslashgt.com/tags/#links">links</category>
            
        </item>
        
        <item>
            <title>Reds</title>
            <link>https://ltslashgt.com/2011/08/01/reds/</link>
            <pubDate>Mon, 01 Aug 2011 10:35:00 +0000</pubDate>
            <guid>https://ltslashgt.com/2011/08/01/reds/</guid>
            <description><![CDATA[
<p><a href="https://github.com/visionmedia/reds">Reds</a> is a light-weight Redis backed search for node.js. It allows you to
associate strings with arbitrary IDs, and then search against the strings. It
does some <a href="http://tartarus.org/~martin/PorterStemmer/">stemming</a> and <a href="http://en.wikipedia.org/wiki/Metaphone">metaphone</a> processing on the words for you, as well.</p>



]]></description>
            
            <category domain="https://ltslashgt.com/tags/#javascript">javascript</category>
            
            <category domain="https://ltslashgt.com/tags/#links">links</category>
            
        </item>
        
        <item>
            <title>PhantomJS</title>
            <link>https://ltslashgt.com/2011/03/30/phantomjs/</link>
            <pubDate>Wed, 30 Mar 2011 15:00:00 +0000</pubDate>
            <guid>https://ltslashgt.com/2011/03/30/phantomjs/</guid>
            <description><![CDATA[
<p><a href="http://www.phantomjs.org/">PhantomJS</a> is a command line tool that gives you a headless browser stack.
It uses <a href="http://www.webkit.org/">WebKit</a> under the hood, and allows you to do everything from
JavaScript testing to <a href="http://code.google.com/p/phantomjs/wiki/QuickStart#Rendering">page rendering</a>, all driven via JavaScript, like so:</p>

<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// Log Twitter status</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">phantom</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">phantom</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="s1">&#39;tweets&#39;</span><span class="p">;</span>
  <span class="nx">phantom</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">&#39;http://mobile.twitter.com/noonat&#39;</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">list</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="s1">&#39;span.status&#39;</span><span class="p">);</span>
  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">list</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="o">++</span><span class="nx">i</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">((</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="s1">&#39;: &#39;</span> <span class="o">+</span> <span class="nx">list</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">innerHTML</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/&lt;.*?&gt;/g</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">));</span>
  <span class="p">}</span>
  <span class="nx">phantom</span><span class="p">.</span><span class="nx">exit</span><span class="p">();</span>
<span class="p">}</span></code></pre></div>



]]></description>
            
            <category domain="https://ltslashgt.com/tags/#javascript">javascript</category>
            
            <category domain="https://ltslashgt.com/tags/#links">links</category>
            
        </item>
        
        <item>
            <title>NodePHP</title>
            <link>https://ltslashgt.com/2011/03/29/nodephp/</link>
            <pubDate>Tue, 29 Mar 2011 13:15:00 +0000</pubDate>
            <guid>https://ltslashgt.com/2011/03/29/nodephp/</guid>
            <description><![CDATA[
<p><a href="https://github.com/davidcoallier/node-php">NodePHP</a> is an experimental project from <a href="https://github.com/davidcoallier">David Coallier</a> that turns NodeJS
into a FastCGI server for PHP. This allows you to use NodeJS as your HTTP
server, proxying requests to <a href="http://php-fpm.org/">PHP-FPM</a> in the same way that nginx does.</p>

<p>Between this and <a href="/2011/02/26/photon/">Photon</a>, it’s nice to see people trying to innovate in the
PHP stack. We were stuck with a legacy PHP stack at <a href="http://en.wikipedia.org/wiki/InstantAction">InstantAction</a>, and I
was considering writing something like this to give us more flexibility.
<a href="https://github.com/billywhizz/node-fastcgi-parser">node-fastcgi-parser</a> has appeared since then, and I imagine that makes the
job a bit more feasible.</p>



]]></description>
            
            <category domain="https://ltslashgt.com/tags/#javascript">javascript</category>
            
            <category domain="https://ltslashgt.com/tags/#php">php</category>
            
            <category domain="https://ltslashgt.com/tags/#links">links</category>
            
        </item>
        
    </channel>
</rss>
