Sunday, November 8, 2015

Handlebars Essentials 3 - Template Precompilation

Assuming the project has templates in a dedicated folder, this step will optimize the way pages are rendered with Handlebars.

Why precompilation? Because Handlebars is designed to convert (compile) templates from their source form (with double-curly-braces) to a Javascript. And this step is expensive on a thin client. So, there is a quick and relatively easy extra step during development to compile all templates into a single .js file. That file is to be referenced by a <script> tag from the host page.


1. Make sure that Node.js is installed and up-to-date.

2. Use the following to install Handlebars module for Node.js.
$ sudo npm install -g handlebars


3. Place all templates into a subfolder, say “templates”. Then, from the root of the project do this:
$ handlebars templates/* -f templates/templates.js

This creates a single .js file with the code for all precompiled templates. To minimize the output and strip out spaces, use this instead:
$ handlebars templates/* -m -f templates/templates.js


4. Add a reference to the newly created templates.js.
<script src="templates/templates.js"></script>
<script src="js/initModel.js"></script>


5. App.js - replace the function to load templates with the following code. Note that partial templates still need to be loaded from the source as before, it appears this is the only way to register partials in Handlebars at present. And this is the only change required.
  function loadTemplatesAndRender() {
    indexTemplate = Handlebars.templates["index.hbs"];
    itemsTemplate = Handlebars.templates["items.hbs"];
    itemTemplate = Handlebars.templates["item.hbs"];

    // Note, the item template is a partial,
    // therefore also needs to be loaded as a source.
    var t3 = $.get("templates/item.hbs", function (data) {
      Handlebars.registerPartial("item", data);
    }, "html");
    // Wait for templates to finish loading.
    $.when(t3).done(function() {
      // Also wait for $(document).ready().
      $(function() {
        //alert("ALL DONE");
        renderPage();
        renderItems();
      })
    })
  }


UPDATE: There is a simpler way to "register" partials. Use the following somewhere after templates are loaded:
Handlebars.partials = Handlebars.templates;

For this to work the partial's reference needs to point to a template by file name, like so:
{{#each items}}
  {{> item.hbs language=@root.language}}
{{else}}
  {{language.noItemsFound}}
{{/each}}


Handlebars Essentials 2

This continues exploring Handlebars library.
Here you will see how to split markup into parts, with the goal to move templates and partials into their own units, and thus allow granular code management.
It will demonstrate how to build an MVC style framework of managing page elements, with pages being simple containers for subviews (hierarchies of views), and client-side logic in controllers being responsible for controlling a single view by feeding data from a model data source.

There is one deficiency here that will be tackled in the next post. It is about optimizing templates and speeding up page load by pre-compilation.


1. Index.html - this is a bare-bones host page for templated elements.
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bunnies</title>
    <link rel="stylesheet" href="css/styles.css">
    <script src="lib/jquery/jquery-2.1.4.min.js"></script>
    <script src="lib/handlebars/handlebars-v4.0.4.js"></script>

    <script src="js/initModel.js"></script>
</head>

<body>
    <div id="main">
    </div>
    <script src="js/app.js"></script>
</body>
</html>


2. InitModel.js - loads and initializes a model object. It is a simple skeletal code to load from a local storage, initialize, and define basic operations on items.
(function() {

  var model = window.model = {};
  model.items = [];
  model.lang = 0;
  model.languages = [
    {
      langId: "lang0",
      siteTitle: "App Prototype (with Handlebars)",
      page1Name: "Home",
      page2Name: "Favorites",
      page3Name: "Contact us",
      lang1Name: "Eng",
      lang2Name: "C++",
      noItemsFound: "No items found",
      one: "Call one",
      two: "Call two",
      good: "Good for you",
      bad: "Bad for you"
    },
    {
      langId: "lang1",
      siteTitle: "template<T> void App (std::shared_ptr<Handlebars>);",
      page1Name: "home();",
      page2Name: "favorites();",
      page3Name: "info();",
      lang1Name: "std::shared_ptr<Eng>();",
      lang2Name: "this",
      noItemsFound: "NULL",
      one: "one();",
      two: "two();",
      good: "result = true;",
      bad: "result = false;"
    },
  ];

  // MARK: - System support functions.

  function parseUrl() {
    var queryParams = window.location.search.slice(1).split('&');
    var paramsObj = {};
    queryParams.forEach(function(val) {
      if (val) {
        var pair = val.split("=");
        paramsObj[pair[0]] = pair[1];
      }
    });
    return paramsObj;
  };

  // MARK: - Init functions.

  window.model.initLanguage = function() {
    var wasUnarchived = this.unarchiveLanguage();
    console.log(this.lang);

    var lang = parseUrl().lang;
    if (lang == undefined) {
      lang = this.lang;
    }
    console.log(lang);
    if (this.lang == lang) {
      return;
    }

    this.lang = lang;
    this.archiveLanguage();
  }

  function Item(id, image, name, isGood, moreInfo) {
    this.id = id;
    this.image = image;
    this.name = name;
    this.isGood = isGood;
    this.moreInfo = moreInfo;
    return this;
  }

  window.model.initItems = function() {
    if (this.unarchiveItems()) {
      return;
    }
    this.items = [
      new Item("bunny0", "bubby_bunny.jpg", "Bubby Bunny", false),
      new Item("bunny1", "cloudy_bunny.jpg", "Cloudy Bunny", false),
      new Item("bunny2", "meditating_cat.jpg", "Meditating Cat", true)
    ];
    this.archiveItems();
  }

  // MARK: - Item operations.

  window.model.chooseItem = function(itemId) {
    var item = this.findItem(itemId);
    if (item) {
      item.moreInfo = true;
      this.archiveItems();
    }
  }

  window.model.clearItem = function(itemId) {
    var item = this.findItem(itemId);
    if (item) {
      item.moreInfo = false;
      this.archiveItems();
    }
  }

  window.model.findItem = function(itemId) {
    var item;
    this.items.forEach(function(val) {
      if (val.id === itemId) {
        item = val;
      }
    });
    return item;
  }

  // MARK: - Archiver.

  window.model.archive = function() {
    this.archiveLanguage();
    this.archiveItems();
  }

  window.model.unarchive = function() {
    var result = this.unarchiveLanguage()
              && this.unarchiveItems();
    return result;
  }

  window.model.archiveLanguage = function() {
    window.localStorage.setItem("model.language", JSON.stringify(this.lang));
  }

  window.model.archiveItems = function() {
    window.localStorage.setItem("model.items", JSON.stringify(this.items));
  }

  window.model.unarchiveLanguage = function() {
    var data = window.localStorage.getItem("model.language");
    if (!data) {
      return false;
    }
    //console.log(data);
    this.lang = JSON.parse(data);
    return true;
  }

  window.model.unarchiveItems = function() {
    var data = window.localStorage.getItem("model.items");
    if (!data) {
      return false;
    }
    var json = JSON.parse(data);

    json.forEach(function(val) {
      model.items.push(new Item(val.id,
        val.image, val.name, val.isGood, val.moreInfo));
    });
    return true;
  }

  model.initLanguage();
  model.initItems();

})();


3. App.js - loads templates and binds controls. Note that it parallelizes loading for the three templates, and each loaded template is then compiled in the same async context because there is no need to involve a window yet. Then the loader awaits on all three to finish, and finally, after the document is ready, renders templates into markup by supplying a model. In this scenario the single app.js handles all template logic, and if needed the more involved code can be moved to separate units, that can be named to match their templates.
(function() {

  var indexTemplate = null;
  var itemsTemplate = null;
  var itemTemplate = null;

  loadTemplatesAndRender();

  function loadTemplatesAndRender() {
    var t1 = $.get("templates/index.hbs", function (data) {
      var compiled = Handlebars.compile(data);
      indexTemplate = compiled;
    }, "html");
    var t2 = $.get("templates/items.hbs", function (data) {
      var compiled = Handlebars.compile(data);
      itemsTemplate = compiled;
    }, "html");
    // Note, the item template is a partial,
    // compiling just in case it’s used as a standalone template.
    var t3 = $.get("templates/item.hbs", function (data) {
      Handlebars.registerPartial("item", data);
      var compiled = Handlebars.compile(data);
      //console.log(compiled);
      itemTemplate = compiled;
    }, "html");
    // Wait for templates to finish loading.
    $.when(t1, t2, t3).done(function() {
      // Also wait for $(document).ready().
      $(function() {
        //alert("ALL DONE");
        renderPage();
        renderItems();
      })
    })
  }

  function renderPage() {
    var compiled = indexTemplate;
    // language is set by the initModel.
    var rendered = compiled(model.languages[model.lang]);
    $("#main").html(rendered);
  }

  function renderItems() {
    var compiled = itemsTemplate;
    // items and a language are set by the initModel.
    var rendered = compiled({
      items: model.items,
      language: model.languages[model.lang]
    });
    $("#theItems").html(rendered);
    attachButtons();
  }

  function attachButtons() {
    $(".button-one").click(function() {
      var id = $(this).closest(".item-card").data("item-id");
      model.chooseItem(id);
      renderItems();
    });

    $(".button-two").click(function() {
      var id = $(this).closest(".item-card").data("item-id");
      model.clearItem(id);
      renderItems();
    });
  }

})();


4. Index.hbs - the top-level template. The binding tags will be replaced by texts from the model.languages[lang] dictionary. And “theItems” element will host renderings of the deeper levels of templates.
<div class="header">
  <div class="title">
      <h1>{{siteTitle}}</h1>
  </div>
  <div>
    <nav class="navigation">
      <a href="?option=page1">{{page1Name}}</a>
      <a href="?option=page2">{{page2Name}}</a>
      <a href="?option=page3">{{page3Name}}</a>
    </nav>
  </div>
</div>
<div id="languageSwitch">
  <nav class="navigation">
    <a href="?lang=0">{{lang1Name}}</a>
    <a href="?lang=1">{{lang2Name}}</a>
  </nav>
</div>
<div class="content">
  <div id="theItems">
  </div>
  <footer class="footer">
  </footer>
</div>


5. Items.hbs - a template-iterator to render all items from the model.items array. It is using a partial to render individual items. In this case the partial is hard-coded and accepts a language object from the root template. For a conditional template selection a helper function can be used (as described in prior post).
{{#each items}}
  {{> item language=@root.language}}
{{else}}
  {{language.noItemsFound}}
{{/each}}


6. Item.hbs - a final template to render an individual item’s card. It has a simple markup to present each item’s name, picture, additional info, and two buttons. The placeholders refer to properties of an item. Handlebars will assign the source item as a context to this template.
<div class="item-card" data-item-id="{{id}}">
  <div class="card-title" style="background: url('images/{{image}}') center 15% no-repeat #46B6AC;">
      <h2>{{name}}</h2>
  </div>
  <div class="card-border">
    <button class="button-one">
      {{language.one}}
    </button>
    <button class="button-two">
      {{language.two}}
    </button>
  </div>
  {{#if moreInfo}}
    {{#if isGood}}
      <span class="result-good">{{language.good}}</span>
    {{else}}
      <span class="result-bad">{{language.bad}}</span>
    {{/if}}
  {{/if}}
</div>



Monday, November 2, 2015

Handlebars Essentials

Handlebars is a good little helper library for client-side Javascript development. It allows creating templates MVC-style in an HTML page and binding to data sources.
Here are my Handlebars code bits as a reference.

1. A page with a target element.
<!DOCTYPE html>
<html>
<head>
    <script src="lib/jquery/jquery-2.1.4.min.js"></script>
    <script src="lib/handlebars/handlebars-v4.0.4.js"></script>
    <script src="lib/material-design-lite/material.min.js"></script>
    <script src=“js/MV.js"></script>
    <script src="js/app.js"></script>
</head>

<body>
    <div id="main" class=“mail-class">
    </div>
</body>
</html>


2. A page template. This is the main container of all visible elements.
<script type="text/x-handlebars-template" id="index-template">
  <div class="header">
    <div class="title">
        <h1>{{siteTitle}}</h1>
    </div>
    <div>
      <nav class="navigation">
        <a href="?option=page1">{{page1Name}}</a>
        <a href="?option=page2">{{page2Name}}</a>
        <a href="?option=page3">{{page3Name}}</a>
      </nav>
    </div>
  </div>
  <div class="content">
    <div id="theItems">
    </div>
    <footer class="footer">
    </footer>
  </div>
</script>

3. Partial templates (partials). These simplify code structure by allowing to refactor templates as components into separate source files.
<script type="text/x-handlebars-template" id="item-template">
  <div class="item-card” data-item-id="{{id}}">
    <div class="card-title" style="background: url('images/{{image}}') center 15% no-repeat #46B6AC;">
        <h2>{{name}}</h2>
    </div>
    <div class="card-border">
      <button class="button-one">
        {{language.one}}
      </button>
      <button class="button-two">
        {{language.two}}
      </button>
    </div>
    {{#if moreInfo}}
      {{#if isCorrect}}
        <span class="result-good">{{language.correct}}</span>
      {{else}}
        <span class="result-bad">{{language.incorrect}}</span>
      {{/if}}
    {{/if}}
  </div>
</script>

4. A template-iterator. This implements iteration through items and also references a partial template for rendering an individual item.
<script type="text/x-handlebars-template" id="items-template">
  {{#each items}}
    {{> item language=@root.language}}
  {{else}}
    {{language.noItemsFoundMessage}}
  {{/each}}
</script>

5. A script (app.js) to register partials and to render a page. Also binds page elements to event handlers.
(function() {

  $(function () {
    registerPartials();
    renderPage();
    renderItems();
  });

  function registerPartials() {
    Handlebars.registerPartial("item", $("#item-template").html());
  }

  function renderPage() {
    var template = $("#index-template").html();
    var compiled = Handlebars.compile(template);
    var rendered = compiled(window.language);
    $("#main").html(rendered);
    $("#languageSwitch").click(function() {
      MV.switchLanguage();
    });
  }

  function renderItems() {
    var template = $("#items-template").html();
    var compiled = Handlebars.compile(template);
    var rendered = compiled({ items: MV.items, language: window.language });
    $("#theItems").html(rendered);
    attachItemsButtons();
  }

  function attachItemsButtons() {
    $(".button-one").click(function() {
      var id = $(this).closest(".item-card").data("item-id");
      MV.itemActionOne(id);
      renderItems();
    });

    $(".button-two").click(function() {
      var id = $(this).closest(".item-card").data("item-id");
      MV.itemActionTwo(id);
      renderItems();
    });
  }

})();

6. Helpers. These are global functions that can be invoked out of any context. “this” will refer to a current context.
For example, in helpers.js:
Handlebars.registerHelper("getLanguageFilter", function(langId) {
  var queryParam = "";
  if (langId) {
    queryParam = "&language=" + Handlebars.escapeExpression(langId);
  }
  return new Handlebars.SafeString(queryParam);
});

Handlebars.registerHelper("generatePages", function(items) {
  var pages = [];
  var pageCount = Math.ceil(items.length / 10);
  for (var i = 1; i <= pageCount; i++) {
    var link = “?page=" + i;
    pages.push({
      number: i,
      link: link
    });
  }
  return pages;
});

Then, in a page or in a template:
<!-- Navigation -->
<script type="text/x-handlebars-template" id=“navigation-template">
  <div>
      <nav class="mdl-navigation">
          <a href="?filter=ones{{getLanguageFilter langId}}">{{itemsFilterOne}}</a>
          <a href="?filter=twos{{getLanguageFilter langId}}">{{itemsFilterTwo}}</a>
      </nav>
  </div>
</script>
...
<script type="text/x-handlebars-template" id="page-template">
  <ul>
    {{#each (generatePages items)}}
      <li><a href="{{link}}">{{number}}</a></li>
    {{/each}}
  </ul>
</script>

Thursday, August 20, 2015

C++11 smart_ptr and weak_ptr best practices

C++ waited almost a decade to finally adopt one of the two most ubiquitous memory management models

Tuesday, April 28, 2015

Rendering multiple objects with instanced arrays

1. One of the methods to render instanced arrays calls for specific setup of WVP matrices:

int pos = glGetAttribLocation(shader_instancedarrays.program, "transformmatrix");
int pos1 = pos + 0;
int pos2 = pos + 1;
int pos3 = pos + 2;
int pos4 = pos + 3;
glEnableVertexAttribArray(pos1);
glEnableVertexAttribArray(pos2);
glEnableVertexAttribArray(pos3);
glEnableVertexAttribArray(pos4);
glBindBuffer(GL_ARRAY_BUFFER, VBO_containing_matrices);
glVertexAttribPointer(pos1, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4 * 4, (void*)(0));
glVertexAttribPointer(pos2, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4 * 4, (void*)(sizeof(float) * 4));
glVertexAttribPointer(pos3, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4 * 4, (void*)(sizeof(float) * 8));
glVertexAttribPointer(pos4, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4 * 4, (void*)(sizeof(float) * 12));
glVertexAttribDivisor(pos1, 1);
glVertexAttribDivisor(pos2, 1);
glVertexAttribDivisor(pos3, 1);
glVertexAttribDivisor(pos4, 1);


and then draw the elements:

glDrawElementsInstanced(primitivetype, indices, GL_UNSIGNED_INT, 0, instancecount);

shader:

attribute mat4 transformmatrix;

void main()
{
    mat4 mvp = gl_ModelViewProjectionMatrix * transformmatrix;

    gl_Position = mvp * gl_Vertex;
    gl_TexCoord[0] = gl_MultiTexCoord0;
}


2. Instance-specific attributes (WVP matrices) go into a separate vertex buffer. Vertex attributes will be read and applied for each vertex, but WVP matrix in this example will stay unchanged until all vertices have been accessed. Then the program will read a new WVP matrix, and repeat rendering all vertices.

3. Use a built-in shader variable gl_InstanceID. Use this index to access instance-specific data in uniform variable arrays (http://ogldev.atspace.co.uk/www/tutorial33/tutorial33.html).

bool Mesh::InitFromScene(const aiScene* pScene, const string& Filename)
{
...
// Generate and populate the buffers with vertex attributes and the indices

glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[POS_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(Positions[0]) * Positions.size(), &Positions[0],
GL_STATIC_DRAW);
glEnableVertexAttribArray(POSITION_LOCATION);
glVertexAttribPointer(POSITION_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, 0); 

glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[TEXCOORD_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(TexCoords[0]) * TexCoords.size(), &TexCoords[0],
GL_STATIC_DRAW);
glEnableVertexAttribArray(TEX_COORD_LOCATION);
glVertexAttribPointer(TEX_COORD_LOCATION, 2, GL_FLOAT, GL_FALSE, 0, 0);

glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[NORMAL_VB]);
glBufferData(GL_ARRAY_BUFFER, sizeof(Normals[0]) * Normals.size(), &Normals[0],
GL_STATIC_DRAW);
glEnableVertexAttribArray(NORMAL_LOCATION);
glVertexAttribPointer(NORMAL_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, 0);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Buffers[INDEX_BUFFER]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices[0]) * Indices.size(), &Indices[0],
GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[WVP_MAT_VB]);

for (unsigned int i = 0; i < 4 ; i++) {
    glEnableVertexAttribArray(WVP_LOCATION + i);
    glVertexAttribPointer(WVP_LOCATION + i, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix4f),
    (const GLvoid*)(sizeof(GLfloat) * i * 4));
    glVertexAttribDivisor(WVP_LOCATION + i, 1);
}

glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[WORLD_MAT_VB]);

for (unsigned int i = 0; i < 4 ; i++) {
    glEnableVertexAttribArray(WORLD_LOCATION + i);
    glVertexAttribPointer(WORLD_LOCATION + i, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix4f),
    (const GLvoid*)(sizeof(GLfloat) * i * 4));
    glVertexAttribDivisor(WORLD_LOCATION + i, 1);
}

return GLCheckError();
}

Note the use of offset in the glVertexAttribPointer in the last two buffer configurations. These are needed to make the mat4x4 (16 floats) to spread across 4 attribute locations. Normally one location is for a single attribute value and it needs to be 4 floats in size.
glVertexAttribDivisor instructs program to advance this specific location by instance, not by vertex. So it will read the next value after all vertices have been rendered, and will start a new instance from the beginning of the same vertex buffers.

In this case the actual data for WVP and world matrices is update each frame to make instances change their location.

More details:
The key element in making this work is to use glVertexAttribDivisor(). When using instanced rendering you basically tell openGL something like: draw N elements using e.g. GL_TRIANGLES and draw K vertices per instance. So if you have 1000 particles and you want to draw a square you can tell it to repeat a draw with 4 elements (4 elements will make a square when using GL_TRIANGLE_STRIP) and do to that 1000 times.

You use glVertexAtrribDivisor() to step through your VBO data, which will contain the vector of you particles and you tell openGL to only change the vertex attributes every X-instance. So glVertexAttribDivisor(0, 1) means that it will step through the data once per particle. The 0 here is referring to the vertex attribute location and the 1 is the number of times it should be changed per instance.


The function glVertexAttribDivisor() is what makes this an instance data rather than vertex data. It takes two parameters - the first one is the vertex array attribute and the second tells OpenGL the rate by which the attribute advances during instanced rendering. It basically means the number of times the entire set of vertices is rendered before the attribute is updated from the buffer. By default, the divisor is zero. This causes regular vertex attributes to be updated from vertex to vertex. If the divisor is 10 it means that the first 10 instances will use the first piece of data from the buffer, the next 10 instances will use the second, etc. We want to have a dedicated WVP matrix for each instance so we use a divisor of 1.

We repeat these steps for all four vertex array attributes of the matrix. We then do the same with the world matrix. Note that unlike the other vertex attributes such as the position and the normal we don't upload any data into the buffers. The reason is that the WVP and world matrices are dynamic and will be updated every frame. So we just set things up for later and leave the buffers uninitialized for now.

void Mesh::Render(unsigned int NumInstances, const Matrix4f* WVPMats, const Matrix4f* WorldMats)
    glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[WVP_MAT_VB]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Matrix4f) * NumInstances, WVPMats, GL_DYNAMIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[WORLD_MAT_VB]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Matrix4f) * NumInstances, WorldMats, GL_DYNAMIC_DRAW);

    glBindVertexArray(m_VAO);

    for (unsigned int i = 0 ; i < m_Entries.size() ; i++) {
        const unsigned int MaterialIndex = m_Entries[i].MaterialIndex;

        assert(MaterialIndex < m_Textures.size());

        if (m_Textures[MaterialIndex]) {
            m_Textures[MaterialIndex]->Bind(GL_TEXTURE0);
        }

        glDrawElementsInstancedBaseVertex(GL_TRIANGLES, 
                                         m_Entries[i].NumIndices, 
                                         GL_UNSIGNED_INT, 
                                         (void*)(sizeof(unsigned int) * m_Entries[i].BaseIndex), 
                                         NumInstances,
                                         m_Entries[i].BaseVertex);
    }

    // Make sure the VAO is not changed from the outside 
    glBindVertexArray(0);
}


Vertex shader.
Instead of getting the WVP and world matrics as uniform variables they are now coming in as regular vertex attributes. The VS doesn't care that their values will only be updated once per instance and not per vertex. As discussed above, the WVP matrix takes up locations 3-6 and the world matrix takes up locations 7-10.
The last line of the VS is where we see the second way of doing instanced rendering (the first being passing instance data as vertex attributes). 'gl_InstanceID' is a built-in variable which is available only in the VS. Since we plan to use it in the FS we have to access it here and pass it along in a regular output variable. The type of gl_InstanceID is an integer so we use an output variable of the same type. Since integers cannot be interpolated by the rasterizer we have to mark the output variable as 'flat' (forgetting to do that will trigger a compiler error).

#version 330

layout (location = 0) in vec3 Position; 
layout (location = 1) in vec2 TexCoord; 
layout (location = 2) in vec3 Normal; 
layout (location = 3) in mat4 WVP; 
layout (location = 7) in mat4 World; 

out vec2 TexCoord0; 
out vec3 Normal0; 
out vec3 WorldPos0; 
flat out int InstanceID; 

void main() 
    gl_Position = WVP * vec4(Position, 1.0); 
    TexCoord0 = TexCoord; 
    Normal0 = World * vec4(Normal, 0.0)).xyz; 
    WorldPos0 = World * vec4(Position, 1.0)).xyz; 
    InstanceID = gl_InstanceID
};


Fragment shader.

flat in int InstanceID;
...
uniform vec4 gColor[4];

...

void main() 
    vec3 Normal = normalize(Normal0); 
    vec4 TotalLight = CalcDirectionalLight(Normal); 

    for (int i = 0 ; i < gNumPointLights ; i++) { 
        TotalLight += CalcPointLight(gPointLights[i], Normal); 
    } 

    for (int i = 0 ; i < gNumSpotLights ; i++) { 
        TotalLight += CalcSpotLight(gSpotLights[i], Normal); 
    } 

    FragColor = texture(gColorMap, TexCoord0.xy) * TotalLight * gColor[InstanceID % 4];
};


Main render loop needs to transpose matrices (column-vector matrices).

Pipeline p;
p.SetCamera(m_pGameCamera->GetPos(), m_pGameCamera->GetTarget(), m_pGameCamera->GetUp());
p.SetPerspectiveProj(m_persProjInfo); 
p.Rotate(0.0f, 90.0f, 0.0f);
p.Scale(0.005f, 0.005f, 0.005f); 

Matrix4f WVPMatrics[NUM_INSTANCES];
Matrix4f WorldMatrices[NUM_INSTANCES];

for (unsigned int i = 0 ; i < NUM_INSTANCES ; i++) {
    Vector3f Pos(m_positions[i]);
    Pos.y += sinf(m_scale) * m_velocity[i];
    p.WorldPos(Pos); 
    WVPMatrics[i] = p.GetWVPTrans().Transpose();
    WorldMatrices[i] = p.GetWorldTrans().Transpose();
}

m_pMesh->Render(NUM_INSTANCES, WVPMatrics, WorldMatrices);


Buffer re-specification
This solution is to reallocate the buffer object before you start modifying it. This is termed buffer "orphaning". There are two ways to do it.

The first way is to call glBufferData with a NULL pointer, and the exact same size and usage hints it had before. This allows the implementation to simply reallocate storage for that buffer object under-the-hood. Since allocating storage is (likely) faster than the implicit synchronization, you gain significant performance advantages over synchronization. And since you passed NULL, if there wasn't a need for synchronization to begin with, this can be reduced to a no-op.

Instanced arrays
Normally, vertex attribute arrays are indexed based on the index buffer, or when doing array rendering, once per vertex from the start point to the end. However, when doing instanced rendering, it is often useful to have an alternative means of getting per-instance data than accessing it directly in the shader via a Uniform Buffer Object, a Buffer Texture, or some other means.
It is possible to have one or more attribute arrays indexed, not by the index buffer or direct array access, but by the instance count. This is done via this function:
void glVertexAttribDivisor(GLuint index​, GLuint divisor​);
The index is the attribute index to set. If divisor is zero, then the attribute acts like normal, being indexed by the array or index buffer. If divisor is non-zero, then the current instance is divided by this divisor, and the result of that is used to access the attribute array.
The "current instance" mentioned above starts at the base instance for instanced rendering, increasing by 1 for each instance in the draw call. Note that this is not how the gl_InstanceID is computed for Vertex Shaders; that is not affected by the base instance. If no base instance is specified, then the current instance starts with 0.
This is generally considered the most efficient way of getting per-instance data to the vertex shader. However, it is also the most resource-constrained method in some respects. OpenGL implementations usually offer a fairly restricted number of vertex attributes (16 or so), and you will need some of these for the actual per-vertex data. So that leaves less room for your per-instance data. While the number of instances can be arbitrarily large (unlike UBO arrays), the amount of per-instance data is much smaller.

However, that should be plenty for a quaternion orientation and a position, for a simple transformation. That would even leave one float (the position only needs to be 3D) to provide a fragment shader an index to access an Array Texture.

Matrix attributes
Attributes in GLSL can be of matrix types. However, our attribute binding functions only bind up to a dimensionality of 4. OpenGL solves this problem by converting matrix GLSL attributes into multiple attribute indices.
If you directly assign an attribute index to a matrix type, it implicitly takes up more than one attribute index. The number of attributes a matrix takes up depends on the number of columns of the matrix: a mat2 matrix will take 2, a mat2x4 matrix will take 2, while a mat4x2 will take 4. The size of each attribute is the number of rows of the matrix.
Each bound attribute in the VAO therefore fills in a single column, starting with the left-most and progressing right. Thus, if you have a 3x3 matrix, and you assign it to attribute index 3, it will naturally take attribute indices 3, 4, and 5. Each of these indices will be 3 elements in size. Attribute 3 is the first column, 4 is the second, and 5 is the last.
OpenGL will allocate locations for matrix attributes contiguously as above. So if you defined a 3x3 matrix, it will return one value, but the next two values are also valid, active attributes.

Double-precision matrices (where available) will take up twice as much space. So a dmat3x3 will take up 6 attribute indices, two for each column.

Matrix inputs take up one attribute index for every column. Array attributes take up one index per element, even if the array is a float and could have use up to 4 indices.
Double-precision input variables of double or dvec types always take up one attribute. Even if they are dvec4 .
These combine with each other. A mat2x4[2] array is broken up into four vec4 values, each of which is assigned an index. Thus, it takes up 4 indices; the first two indices specify the two columns of array index 0, and the next two indices specify the two columns of array index 1.
When an input requires multiple indices, it will always be assigned sequential indices starting from the given index. Consider:

layout(location = 3) in mat4 a_matrix;

a_matrix will be assigned attribute indices 3, 4, 5, and 6. This works regardless of what methods you use to assign vertex attribute indices to input variables.
Linking will fail if any index ranges collide. Thus, this will fail to link:

layout(location = 0) in mat4 a_matrix;
layout(location = 3) in vec4 a_vec;





Tuesday, April 21, 2015

Real-time smoke and fire simulation.

Input parameters:
- area
- gravity
- combustion (?)
- density (of surrounding medium)
- fluid buoyancy (to calc speed in opposite direction to gravity)
- turbulence (or vorticity)

Particle properties:
- temperature
- lifespan
- seed

Uniform temperature at vertex is interpolated and used by a fragment shader. So, temperature can be an attribute, updated each frame.

Sunday, March 29, 2015

Optimal use of std::ifstream to read into std::string

This is an exemplary bit of code to help read a file by std::ifstream and avoid needless buffer reallocations.
1. DataPath, returns a "Data" subdirectory inside application bundle. In Xcode to package resource files along with a bundle, add the resource directory to a project as a directory reference. Don't name it as "Resources", internal packaging system reserves that name (for whatever reason) and will throw an annoying "iOS Simulator failed to install the application" error.
So, I've chosen to scope resources inside a "Data" directory.
2. ReadFile, will read entire file content and return in a std::string. Will work for cross-platform file access, just paste in a .cpp file.


<FileSystem.hpp>

#include <string>


namespace FileSystem
{
    
    std::string DataPath();
    
    std::string ReadFile(const char* filePath);
    
}



<FileSystem.mm>

#import <Foundation/Foundation.h>
#include <iostream>
#include <fstream>
#import "FileSystem.hpp"


namespace FileSystem
{

    using namespace std;
    
    string DataPath()
    {
        string result;
        NSBundle* bundle = [NSBundle mainBundle];
        
        if (bundle == nil) {
            #ifdef DEBUG
                NSLog(@"Bundle is nil... which should never happen.");
            #endif
        } else {
            NSString* path = [bundle bundlePath];
            // Also, to get Documents directory:
            // path = [NSHomeDirectory() stringByAppendingString: @"/Documents/"];
            path = [NSString stringWithFormat: @"%@%s", path, "/Data/"];
            result = string([path UTF8String]);
        }
        
        return result;
    }
    
    string ReadFile(const char* filePath)
    {
        string result;
        
        ifstream ifs(filePath, ios::in | ios::binary | ios::ate);
        if (!ifs) {
            throw invalid_argument(string("Error opening '") + filePath + "'.");
        }
        
        auto fileSize = ifs.tellg();
        result.resize((string::size_type)fileSize);
        
        ifs.seekg(0, ios::beg);
        auto buf = &result[0];
        ifs.read(buf, (streamsize)fileSize);
        return result;
    }

}