=== modified file 'src/model.cpp'
--- src/model.cpp	2011-09-16 11:49:39 +0000
+++ src/model.cpp	2011-10-07 18:19:11 +0000
@@ -27,7 +27,7 @@
 #include "options.h"
 #include "util.h"
 #include "float.h"
-
+#include <dirent.h>
 #include <fstream>
 #include <sstream>
 #include <memory>
@@ -600,3 +600,119 @@
         object.vertices.size(), object.faces.size());
     return true;
 }
+
+namespace ModelPrivate
+{
+
+void
+list_files(const string& dirName, vector<string>& fileVec)
+{
+    DIR* dir = opendir(dirName.c_str());
+    if (!dir)
+    {
+        Log::error("Failed to open models directory '%s'\n", dirName.c_str());
+        return;
+    }
+
+    struct dirent* entry = readdir(dir);
+    while (entry)
+    {
+        string pathname(dirName + "/");
+        pathname += string(entry->d_name);
+        // Skip '.' and '..'
+        if (entry->d_name[0] != '.')
+        {
+            fileVec.push_back(pathname);
+        }
+        entry = readdir(dir);
+    }
+    closedir(dir);
+}
+
+ModelMap modelMap;
+
+}
+
+const ModelMap&
+Model::find_models()
+{
+    if (!ModelPrivate::modelMap.empty())
+    {
+        return ModelPrivate::modelMap;
+    }
+    vector<string> pathVec;
+    string dataDir(GLMARK_DATA_PATH"/models");
+    ModelPrivate::list_files(dataDir, pathVec);
+#ifdef GLMARK_EXTRAS_PATH
+    string extrasDir(GLMARK_EXTRAS_PATH"/models");
+    ModelPrivate::list_files(extrasDir, pathVec);
+#endif
+
+    // Now that we have a list of all of the model files available to us,
+    // let's go through and pull out the names and what format they're in
+    // so the scene can decide which ones to use.
+    for(vector<string>::const_iterator pathIt = pathVec.begin();
+        pathIt != pathVec.end();
+        pathIt++)
+    {
+        const string& curPath = *pathIt;
+        string::size_type namePos(0);
+        string::size_type slashPos = curPath.rfind("/");
+        if (slashPos != string::npos)
+        {
+            // Advance to the first character after the last slash
+            namePos = slashPos + 1;
+        }
+
+        ModelFormat format(MODEL_INVALID);
+        string::size_type extPos = curPath.rfind(".3ds");
+        if (extPos == string::npos)
+        {
+            // It's not a 3ds model
+            extPos = curPath.rfind(".obj");
+            if (extPos == string::npos)
+            {
+                // It's not an obj model either, so skip it.
+                continue;
+            }
+            format = MODEL_OBJ;
+        }
+        else
+        {
+            // It's a 3ds model
+            format = MODEL_3DS;
+        }
+
+        string name(curPath, namePos, extPos - namePos);
+        ModelDescriptor* desc = new ModelDescriptor(name, format, curPath);
+        ModelPrivate::modelMap.insert(std::make_pair(name, desc));
+    }
+
+    return ModelPrivate::modelMap;
+}
+
+bool
+Model::load(const string& modelName)
+{
+    bool retVal(false);
+    ModelMap::const_iterator modelIt = ModelPrivate::modelMap.find(modelName);
+    if (modelIt == ModelPrivate::modelMap.end())
+    {
+        return retVal;
+    }
+
+    ModelDescriptor* desc = modelIt->second;
+    switch (desc->format())
+    {
+        case MODEL_INVALID:
+            break;
+        case MODEL_3DS:
+            retVal = load_3ds(desc->pathname());
+            break;
+        case MODEL_OBJ:
+            retVal = load_obj(desc->pathname());
+            break;
+    }
+
+    return retVal;
+}

=== modified file 'src/model.h'
--- src/model.h	2011-09-16 11:49:39 +0000
+++ src/model.h	2011-10-07 18:39:57 +0000
@@ -30,9 +30,35 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <vector>
-
-
-// A model as loaded from a 3ds file
+#include <map>
+
+enum ModelFormat
+{
+    MODEL_INVALID,
+    MODEL_3DS,
+    MODEL_OBJ
+};
+
+class ModelDescriptor
+{
+    std::string name_;
+    std::string pathname_;
+    ModelFormat format_;
+    ModelDescriptor();
+public:
+    ModelDescriptor(const std::string& name, ModelFormat format, 
+                    const std::string& pathname) :
+        name_(name),
+        pathname_(pathname),
+        format_(format) {}
+    ~ModelDescriptor() {}
+    const std::string& pathname() const { return pathname_; }
+    ModelFormat format() const { return format_; }
+};
+
+typedef std::map<std::string, ModelDescriptor*> ModelMap;
+
+// A model as loaded from a 3D object data file
 class Model
 {
 public:
@@ -47,14 +73,21 @@
     Model() {}
     ~Model() {}
 
-    bool load_3ds(const std::string &filename);
-    bool load_obj(const std::string &filename);
+    // Load a named model from the ModelMap.
+    bool load(const std::string& name);
+
     void calculate_normals();
     void convert_to_mesh(Mesh &mesh);
     void convert_to_mesh(Mesh &mesh, 
                          const std::vector<std::pair<AttribType, int> > &attribs);
     const LibMatrix::vec3& minVec() const { return minVec_; }
     const LibMatrix::vec3& maxVec() const { return maxVec_; }
+    // Scan the built-in data paths and build a database of usable models
+    // available to scenes.  Map is available on a read-only basis to scenes
+    // that might find it useful for listing models, etc.
+    //
+    // NOTE: This must be called before load().
+    static const ModelMap& find_models();
 private:
     struct Face {
         uint32_t a, b, c;
@@ -76,6 +109,8 @@
 
     void append_object_to_mesh(const Object &object, Mesh &mesh,
                                int p_pos, int n_pos, int t_pos);
+    bool load_3ds(const std::string &filename);
+    bool load_obj(const std::string &filename);
 
     // For vertices of the bounding box for this model.
     void compute_bounding_box(const Object& object);

=== modified file 'src/scene-build.cpp'
--- src/scene-build.cpp	2011-10-05 16:42:33 +0000
+++ src/scene-build.cpp	2011-10-11 09:46:59 +0000
@@ -29,14 +29,31 @@
 #include <cmath>
 
 SceneBuild::SceneBuild(Canvas &pCanvas) :
-    Scene(pCanvas, "build")
+    Scene(pCanvas, "build"),
+    mOrientModel(false)
 {
+    const ModelMap& modelMap = Model::find_models();
+    std::string optionDesc("Which model to use [");
+    for (ModelMap::const_iterator modelIt = modelMap.begin();
+         modelIt != modelMap.end();
+         modelIt++)
+    {
+        static bool doSeparator(false);
+        if (doSeparator)
+        {
+            optionDesc += ", ";
+        }
+        const std::string& curName = modelIt->first;
+        optionDesc += curName;
+        doSeparator = true;
+    }
+    optionDesc += "]";
     mOptions["use-vbo"] = Scene::Option("use-vbo", "true",
                                         "Whether to use VBOs for rendering [true,false]");
     mOptions["interleave"] = Scene::Option("interleave", "false",
                                            "Whether to interleave vertex attribute data [true,false]");
     mOptions["model"] = Scene::Option("model", "horse",
-                                      "Which model to use [horse, bunny]");
+                                      optionDesc);
 }
 
 SceneBuild::~SceneBuild()
@@ -83,24 +100,34 @@
     }
 
     Model model;
-    bool modelLoaded(false);
     const std::string& whichModel(mOptions["model"].value);
-
-    if (whichModel == "bunny")
-    {
-        // Bunny rotates around the Y axis
-        modelLoaded = model.load_obj(GLMARK_DATA_PATH"/models/bunny.obj");
-    }
-    else
-    {
-        // Default is "horse", so we don't need to look further
-        // Horse rotates around the Y axis
-        modelLoaded = model.load_3ds(GLMARK_DATA_PATH"/models/horse.3ds");
-    }
+    bool modelLoaded = model.load(whichModel);
 
     if(!modelLoaded)
         return;
 
+    // Now that we're successfully loaded, there are a few quirks about
+    // some of the known models that we need to account for.  The draw
+    // logic for the scene wants to rotate the model around the Y axis.
+    // Most of our models are described this way.  Some need adjustment
+    // (an additional rotation that gets the model into the correct
+    // orientation).
+    //
+    // Here's a summary:
+    //
+    // Angel rotates around the Y axis
+    // Armadillo rotates around the Y axis
+    // Buddha rotates around the X axis
+    // Bunny rotates around the Y axis
+    // Dragon rotates around the X axis
+    // Horse rotates around the Y axis
+    if (whichModel == "buddha" || whichModel == "dragon")
+    {
+        mOrientModel = true;
+        mOrientationAngle = -90.0;
+        mOrientationVec = vec3(1.0, 0.0, 0.0);
+    }
+
     model.calculate_normals();
 
     /* Tell the converter that we only care about position and normal attributes */
@@ -187,6 +214,10 @@
     LibMatrix::mat4 model_view_proj(mPerspective);
     model_view.translate(-mCenterVec.x(), -mCenterVec.y(), -(mCenterVec.z() + 2.0 + mRadius));
     model_view.rotate(mRotation, 0.0f, 1.0f, 0.0f);
+    if (mOrientModel)
+    {
+        model_view.rotate(mOrientationAngle, mOrientationVec.x(), mOrientationVec.y(), mOrientationVec.z());
+    }
     model_view_proj *= model_view.getCurrent();
 
     mProgram["ModelViewProjectionMatrix"] = model_view_proj;

=== modified file 'src/scene-bump.cpp'
--- src/scene-bump.cpp	2011-08-31 21:22:27 +0000
+++ src/scene-bump.cpp	2011-10-07 18:35:47 +0000
@@ -56,8 +56,8 @@
 {
     static const std::string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/bump-poly.vert");
     static const std::string frg_shader_filename(GLMARK_DATA_PATH"/shaders/bump-poly.frag");
-    static const std::string low_poly_filename(GLMARK_DATA_PATH"/models/asteroid-low.3ds");
-    static const std::string high_poly_filename(GLMARK_DATA_PATH"/models/asteroid-high.3ds");
+    static const std::string low_poly_filename("asteroid-low");
+    static const std::string high_poly_filename("asteroid-high");
     static const LibMatrix::vec4 lightPosition(20.0f, 20.0f, 10.0f, 1.0f);
     Model model;
 
@@ -70,7 +70,7 @@
     std::string poly_filename = type == "high-poly" ?
                                 high_poly_filename : low_poly_filename;
 
-    if(!model.load_3ds(poly_filename))
+    if(!model.load(poly_filename))
         return;
 
     model.calculate_normals();
@@ -110,7 +110,7 @@
     static const LibMatrix::vec4 lightPosition(20.0f, 20.0f, 10.0f, 1.0f);
     Model model;
 
-    if(!model.load_3ds(GLMARK_DATA_PATH"/models/asteroid-low.3ds"))
+    if(!model.load("asteroid-low"))
         return;
 
     /* Calculate the half vector */
@@ -158,6 +158,7 @@
 
     const std::string &bump_render = mOptions["bump-render"].value;
 
+    Model::find_models();
     if (bump_render == "normals")
         setup_model_normals();
     else if (bump_render == "off" || bump_render == "high-poly")

=== modified file 'src/scene-shading.cpp'
--- src/scene-shading.cpp	2011-09-21 14:29:29 +0000
+++ src/scene-shading.cpp	2011-10-07 18:35:47 +0000
@@ -67,9 +67,10 @@
 
 int SceneShading::load()
 {
+    Model::find_models();
     Model model;
 
-    if(!model.load_3ds(GLMARK_DATA_PATH"/models/cat.3ds"))
+    if(!model.load("cat"))
         return 0;
 
     model.calculate_normals();

=== modified file 'src/scene-texture.cpp'
--- src/scene-texture.cpp	2011-09-14 16:44:46 +0000
+++ src/scene-texture.cpp	2011-10-07 18:35:47 +0000
@@ -44,9 +44,10 @@
 
 int SceneTexture::load()
 {
+    Model::find_models();
     Model model;
 
-    if(!model.load_3ds(GLMARK_DATA_PATH"/models/cube.3ds"))
+    if(!model.load("cube"))
         return 0;
 
     model.calculate_normals();

=== modified file 'src/scene.h'
--- src/scene.h	2011-10-08 12:30:54 +0000
+++ src/scene.h	2011-10-11 09:46:59 +0000
@@ -159,6 +159,9 @@
     LibMatrix::vec3 mCenterVec;
     float mRadius;
     Mesh mMesh;
+    bool mOrientModel;
+    float mOrientationAngle;
+    LibMatrix::vec3 mOrientationVec;
     float mRotation;
     float mRotationSpeed;
     bool mUseVbo;

=== modified file 'wscript'
--- wscript	2011-09-21 15:01:11 +0000
+++ wscript	2011-10-07 18:17:20 +0000
@@ -25,6 +25,8 @@
                    default = True, help='disable compiler optimizations')
     opt.add_option('--data-path', action='store', dest = 'data_path',
                    help='the path to install the data to')
+    opt.add_option('--extras-path', action='store', dest = 'extras_path',
+                   help='path to additional data (models, shaders, textures)')
 
 def configure(ctx):
     if not Options.options.gl and not Options.options.glesv2:
@@ -75,6 +77,12 @@
     if Options.options.data_path is None:
         Options.options.data_path = os.path.join(ctx.env.PREFIX, 'share/glmark2')
 
+    ctx.env.HAVE_EXTRAS = False
+    if Options.options.extras_path is not None:
+        ctx.env.HAVE_EXTRAS = True
+        ctx.env.append_unique('GLMARK_EXTRAS_PATH', Options.options.extras_path)
+        ctx.env.append_unique('DEFINES', 'GLMARK_EXTRAS_PATH="%s"' % Options.options.extras_path)
+
     ctx.env.append_unique('GLMARK_DATA_PATH', Options.options.data_path)
     ctx.env.append_unique('DEFINES', 'GLMARK_DATA_PATH="%s"' % Options.options.data_path)
     ctx.env.append_unique('DEFINES', 'GLMARK_VERSION="%s"' % VERSION)
@@ -85,6 +93,10 @@
 
     ctx.msg("Prefix", ctx.env.PREFIX, color = 'PINK')
     ctx.msg("Data path", Options.options.data_path, color = 'PINK')
+    ctx.msg("Including extras", "Yes" if ctx.env.HAVE_EXTRAS else "No",
+            color = 'PINK');
+    if ctx.env.HAVE_EXTRAS:
+        ctx.msg("Extras path", Options.options.extras_path, color = 'PINK')
     ctx.msg("Building GL2 version", "Yes" if ctx.env.USE_GL else "No",
             color = 'PINK')
     ctx.msg("Building GLESv2 version", "Yes" if ctx.env.USE_GLESv2 else "No",

