=== added file 'data/textures/nasa1.png'
Binary files data/textures/nasa1.png	1970-01-01 00:00:00 +0000 and data/textures/nasa1.png	2012-05-15 18:35:35 +0000 differ
=== added file 'data/textures/nasa2.png'
Binary files data/textures/nasa2.png	1970-01-01 00:00:00 +0000 and data/textures/nasa2.png	2012-05-15 18:35:35 +0000 differ
=== added file 'data/textures/nasa3.png'
Binary files data/textures/nasa3.png	1970-01-01 00:00:00 +0000 and data/textures/nasa3.png	2012-05-15 18:35:35 +0000 differ
=== modified file 'src/model.cpp'
--- src/model.cpp	2011-11-16 10:07:47 +0000
+++ src/model.cpp	2012-05-18 13:23:26 +0000
@@ -28,6 +28,7 @@
 #include "options.h"
 #include "util.h"
 #include "float.h"
+#include "math.h"
 #include <fstream>
 #include <sstream>
 #include <memory>
@@ -35,7 +36,6 @@
 using std::string;
 using std::vector;
 using LibMatrix::vec3;
-using LibMatrix::vec2;
 using LibMatrix::uvec3;
 
 #define read_or_fail(file, dst, size) do { \
@@ -222,6 +222,34 @@
     }
 }
 
+void
+Model::calculate_texcoords()
+{
+    // Since the model didn't come with texcoords, and we don't actually know
+    // if it came with normals, either, we'll use positional spherical mapping
+    // to generate texcoords for the model.  See:
+    // http://www.mvps.org/directx/articles/spheremap.htm for more details.
+    vec3 centerVec = maxVec_ + minVec_;
+    centerVec *= 0.5;
+    
+    for (std::vector<Object>::iterator iter = objects_.begin();
+         iter != objects_.end();
+         iter++)
+    {
+        Object &object = *iter;
+        for (vector<Vertex>::iterator vertexIt = object.vertices.begin();
+             vertexIt != object.vertices.end();
+             vertexIt++)
+        {
+            Vertex& curVertex = *vertexIt;
+            vec3 vnorm(curVertex.v - centerVec);
+            vnorm.normalize();
+            curVertex.t.x(asinf(vnorm.x()) / M_PI + 0.5);
+            curVertex.t.y(asinf(vnorm.y()) / M_PI + 0.5);
+        }
+    }
+}
+
 /**
  * Calculates the normal vectors of the model vertices.
  */
@@ -443,6 +471,7 @@
                     object->vertices[i].t.y(f[1]);
                 }
                 }
+                gotTexcoords_ = true;
                 break;
 
             //----------- Skip unknow chunks ------------

=== modified file 'src/model.h'
--- src/model.h	2011-11-16 10:07:47 +0000
+++ src/model.h	2012-05-15 12:44:24 +0000
@@ -77,11 +77,13 @@
         AttribTypeCustom
     } AttribType;
 
-    Model() {}
+    Model() : gotTexcoords_(false) {}
     ~Model() {}
 
     bool load(const std::string& name);
 
+    bool needTexcoords() const { return !gotTexcoords_; }
+    void calculate_texcoords();
     void calculate_normals();
     void convert_to_mesh(Mesh &mesh);
     void convert_to_mesh(Mesh &mesh,
@@ -90,6 +92,8 @@
     const LibMatrix::vec3& maxVec() const { return maxVec_; }
     static const ModelMap& find_models();
 private:
+    // If the model we loaded contained texcoord data...
+    bool gotTexcoords_;
     struct Face {
         uint32_t a, b, c;
         uint16_t face_flags;

=== modified file 'src/scene-build.cpp'
--- src/scene-build.cpp	2011-12-08 11:09:09 +0000
+++ src/scene-build.cpp	2012-05-22 08:46:33 +0000
@@ -132,6 +132,12 @@
         orientationAngle_ = -90.0;
         orientationVec_ = vec3(1.0, 0.0, 0.0);
     }
+    else if (whichModel == "armadillo")
+    {
+        orientModel_ = true;
+        orientationAngle_ = 180.0; 
+        orientationVec_ = vec3(0.0, 1.0, 0.0);
+    }
 
     model.calculate_normals();
 

=== modified file 'src/scene-bump.cpp'
--- src/scene-bump.cpp	2011-12-08 11:09:09 +0000
+++ src/scene-bump.cpp	2012-05-15 18:38:47 +0000
@@ -154,7 +154,7 @@
     attrib_locations.push_back(program_["texcoord"].location());
     mesh_.set_attrib_locations(attrib_locations);
 
-    Texture::load(GLMARK_DATA_PATH"/textures/asteroid-normal-map.png", &texture_,
+    Texture::load("asteroid-normal-map", &texture_,
                   GL_NEAREST, GL_NEAREST, 0);
 }
 
@@ -206,7 +206,7 @@
     attrib_locations.push_back(program_["tangent"].location());
     mesh_.set_attrib_locations(attrib_locations);
 
-    Texture::load(GLMARK_DATA_PATH"/textures/asteroid-normal-map-tangent.png", &texture_,
+    Texture::load("asteroid-normal-map-tangent", &texture_,
                   GL_NEAREST, GL_NEAREST, 0);
 }
 
@@ -260,7 +260,7 @@
     attrib_locations.push_back(program_["tangent"].location());
     mesh_.set_attrib_locations(attrib_locations);
 
-    Texture::load(GLMARK_DATA_PATH"/textures/asteroid-height-map.png", &texture_,
+    Texture::load("asteroid-height-map", &texture_,
                   GL_NEAREST, GL_NEAREST, 0);
 }
 
@@ -270,7 +270,7 @@
     Scene::setup();
 
     const std::string &bump_render = options_["bump-render"].value;
-
+    Texture::find_textures();
     Model::find_models();
     if (bump_render == "normals")
         setup_model_normals();

=== modified file 'src/scene-desktop.cpp'
--- src/scene-desktop.cpp	2012-05-07 12:45:34 +0000
+++ src/scene-desktop.cpp	2012-05-15 18:38:47 +0000
@@ -726,12 +726,12 @@
 };
 
 int RenderWindowBlur::use_count = 0;
-RenderClearImage RenderWindowBlur::window_contents_(GLMARK_DATA_PATH"/textures/desktop-window.png");
+RenderClearImage RenderWindowBlur::window_contents_("desktop-window");
 int RenderWindowShadow::use_count = 0;
-RenderClearImage RenderWindowShadow::window_contents_(GLMARK_DATA_PATH"/textures/desktop-window.png");
-RenderClearImage RenderWindowShadow::shadow_h_(GLMARK_DATA_PATH"/textures/desktop-shadow.png");
-RenderClearImage RenderWindowShadow::shadow_v_(GLMARK_DATA_PATH"/textures/desktop-shadow.png");
-RenderClearImage RenderWindowShadow::shadow_corner_(GLMARK_DATA_PATH"/textures/desktop-shadow-corner.png");
+RenderClearImage RenderWindowShadow::window_contents_("desktop-window");
+RenderClearImage RenderWindowShadow::shadow_h_("desktop-shadow");
+RenderClearImage RenderWindowShadow::shadow_v_("desktop-shadow");
+RenderClearImage RenderWindowShadow::shadow_corner_("desktop-shadow-corner");
 
 /*******************************
  * SceneDesktop implementation *
@@ -748,7 +748,7 @@
     std::vector<RenderObject *> windows;
 
     SceneDesktopPrivate(Canvas &canvas) :
-        screen(canvas), desktop(GLMARK_DATA_PATH"/textures/effect-2d.png") {}
+        screen(canvas), desktop("effect-2d") {}
 
     ~SceneDesktopPrivate() { Util::dispose_pointer_vector(windows); }
 
@@ -810,6 +810,9 @@
     blur_radius = Util::fromString<unsigned int>(options_["blur-radius"].value);
     shadow_size = Util::fromString<unsigned int>(options_["shadow-size"].value);
 
+    // Make sure the Texture object knows where to find our images.
+    Texture::find_textures();
+
     /* Ensure we get a transparent clear color for all following operations */
     glClearColor(0.0, 0.0, 0.0, 0.0);
     glDisable(GL_DEPTH_TEST);

=== modified file 'src/scene-effect-2d.cpp'
--- src/scene-effect-2d.cpp	2012-01-26 17:36:24 +0000
+++ src/scene-effect-2d.cpp	2012-05-15 18:38:47 +0000
@@ -281,7 +281,7 @@
 bool
 SceneEffect2D::load()
 {
-    Texture::load(GLMARK_DATA_PATH"/textures/effect-2d.png", &texture_,
+    Texture::load("effect-2d", &texture_,
                   GL_NEAREST, GL_NEAREST, 0);
     running_ = false;
 
@@ -299,6 +299,8 @@
 {
     Scene::setup();
 
+    Texture::find_textures();
+
     static const std::string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/effect-2d.vert");
 
     std::vector<float> kernel;

=== modified file 'src/scene-pulsar.cpp'
--- src/scene-pulsar.cpp	2012-05-07 12:45:34 +0000
+++ src/scene-pulsar.cpp	2012-05-15 18:38:47 +0000
@@ -115,7 +115,8 @@
 
     if (options_["texture"].value == "true") {
         frg_shader_filename = GLMARK_DATA_PATH"/shaders/light-basic-tex.frag";
-        Texture::load(GLMARK_DATA_PATH"/textures/crate-base.png", &texture_,
+        Texture::find_textures();
+        Texture::load("crate-base", &texture_,
                       GL_NEAREST, GL_NEAREST, 0);
 
     } else {

=== modified file 'src/scene-shading.cpp'
--- src/scene-shading.cpp	2011-12-08 11:09:09 +0000
+++ src/scene-shading.cpp	2012-05-22 08:46:33 +0000
@@ -212,6 +212,12 @@
         orientationAngle_ = -90.0;
         orientationVec_ = vec3(1.0, 0.0, 0.0);
     }
+    else if (whichModel == "armadillo")
+    {
+        orientModel_ = true;
+        orientationAngle_ = 180.0; 
+        orientationVec_ = vec3(0.0, 1.0, 0.0);
+    }
 
     model.calculate_normals();
 

=== modified file 'src/scene-texture.cpp'
--- src/scene-texture.cpp	2011-12-08 11:09:09 +0000
+++ src/scene-texture.cpp	2012-05-22 08:46:33 +0000
@@ -34,11 +34,50 @@
 #include "util.h"
 #include <cmath>
 
+using LibMatrix::vec3;
+using std::string;
+
 SceneTexture::SceneTexture(Canvas &pCanvas) :
     Scene(pCanvas, "texture")
 {
+    const ModelMap& modelMap = Model::find_models();
+    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 += "]";
+    options_["model"] = Scene::Option("model", "cube",
+                                      optionDesc);
     options_["texture-filter"] = Scene::Option("texture-filter", "nearest",
                                                "[nearest, linear, linear-shader, mipmap]");
+    optionDesc = "Which texture to use [";
+    const TextureMap& textureMap = Texture::find_textures();
+    for (TextureMap::const_iterator textureIt = textureMap.begin();
+         textureIt != textureMap.end();
+         textureIt++)
+    {
+        static bool doSeparator(false);
+        if (doSeparator)
+        {
+            optionDesc += ", ";
+        }
+        const std::string& curName = textureIt->first;
+        optionDesc += curName;
+        doSeparator = true;
+    }
+    optionDesc += "]";
+    options_["texture"] = Scene::Option("texture", "crate-base",
+                                        optionDesc);
 }
 
 SceneTexture::~SceneTexture()
@@ -48,16 +87,6 @@
 bool
 SceneTexture::load()
 {
-    Model::find_models();
-    Model model;
-
-    if(!model.load("cube"))
-        return false;
-
-    model.calculate_normals();
-    model.convert_to_mesh(mesh_);
-    mesh_.build_vbo();
-
     rotationSpeed_ = LibMatrix::vec3(36.0f, 36.0f, 36.0f);
 
     running_ = false;
@@ -104,7 +133,8 @@
         mag_filter = GL_LINEAR;
     }
 
-    Texture::load(GLMARK_DATA_PATH"/textures/crate-base.png", &texture_,
+    const string& whichTexture(options_["texture"].value);
+    Texture::load(whichTexture, &texture_,
                   min_filter, mag_filter, 0);
 
     // Load shaders
@@ -129,14 +159,69 @@
         return;
     }
 
+    Model model;
+    const string& whichModel(options_["model"].value);
+    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")
+    {
+        orientModel_ = true;
+        orientationAngle_ = -90.0;
+        orientationVec_ = vec3(1.0, 0.0, 0.0);
+    }
+    else if (whichModel == "armadillo")
+    {
+        orientModel_ = true;
+        orientationAngle_ = 180.0; 
+        orientationVec_ = vec3(0.0, 1.0, 0.0);
+    }
+
+    if (model.needTexcoords())
+        model.calculate_texcoords();
+    model.calculate_normals();
+    model.convert_to_mesh(mesh_);
+    mesh_.build_vbo();
+
+    // Calculate a projection matrix that is a good fit for the model
+    vec3 maxVec = model.maxVec();
+    vec3 minVec = model.minVec();
+    vec3 diffVec = maxVec - minVec;
+    centerVec_ = maxVec + minVec;
+    centerVec_ /= 2.0;
+    float diameter = diffVec.length();
+    radius_ = diameter / 2;
+    float fovy = 2.0 * atanf(radius_ / (2.0 + radius_));
+    fovy /= M_PI;
+    fovy *= 180.0;
+    float aspect(static_cast<float>(canvas_.width())/static_cast<float>(canvas_.height()));
+    perspective_.setIdentity();
+    perspective_ *= LibMatrix::Mat4::perspective(fovy, aspect, 2.0, 2.0 + diameter);
+
+    program_.start();
+
     std::vector<GLint> attrib_locations;
     attrib_locations.push_back(program_["position"].location());
     attrib_locations.push_back(program_["normal"].location());
     attrib_locations.push_back(program_["texcoord"].location());
     mesh_.set_attrib_locations(attrib_locations);
 
-    program_.start();
-
     currentFrame_ = 0;
     rotation_ = LibMatrix::vec3();
     running_ = true;
@@ -170,12 +255,15 @@
 {
     // Load the ModelViewProjectionMatrix uniform in the shader
     LibMatrix::Stack4 model_view;
-    LibMatrix::mat4 model_view_proj(canvas_.projection());
-
-    model_view.translate(0.0f, 0.0f, -5.0f);
+    model_view.translate(-centerVec_.x(), -centerVec_.y(), -(centerVec_.z() + 2.5 + radius_));
     model_view.rotate(rotation_.x(), 1.0f, 0.0f, 0.0f);
     model_view.rotate(rotation_.y(), 0.0f, 1.0f, 0.0f);
     model_view.rotate(rotation_.z(), 0.0f, 0.0f, 1.0f);
+    if (orientModel_)
+    {
+        model_view.rotate(orientationAngle_, orientationVec_.x(), orientationVec_.y(), orientationVec_.z());
+    }
+    LibMatrix::mat4 model_view_proj(perspective_);
     model_view_proj *= model_view.getCurrent();
 
     program_["ModelViewProjectionMatrix"] = model_view_proj;

=== modified file 'src/scene.h'
--- src/scene.h	2012-04-27 18:00:45 +0000
+++ src/scene.h	2012-05-15 12:46:43 +0000
@@ -281,6 +281,12 @@
     Program program_;
     Mesh mesh_;
     GLuint texture_;
+    float radius_;
+    bool orientModel_;
+    float orientationAngle_;
+    LibMatrix::vec3 orientationVec_;
+    LibMatrix::mat4 perspective_;
+    LibMatrix::vec3 centerVec_;
     LibMatrix::vec3 rotation_;
     LibMatrix::vec3 rotationSpeed_;
 };

=== modified file 'src/text-renderer.cpp'
--- src/text-renderer.cpp	2012-01-13 18:36:53 +0000
+++ src/text-renderer.cpp	2012-05-15 18:38:47 +0000
@@ -67,7 +67,8 @@
     glUseProgram(prev_program);
 
     /* Load the glyph texture atlas */
-    Texture::load(GLMARK_DATA_PATH"/textures/glyph-atlas.png", &texture_,
+    Texture::find_textures();
+    Texture::load("glyph-atlas", &texture_,
                   GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR,0);
 }
 

=== modified file 'src/texture.cpp'
--- src/texture.cpp	2011-11-08 21:41:23 +0000
+++ src/texture.cpp	2012-05-15 18:38:47 +0000
@@ -28,6 +28,7 @@
 #include <cstdarg>
 #include <png.h>
 #include <memory>
+#include <vector>
 
 class PNGState
 {
@@ -177,9 +178,24 @@
     }
 }
 
+namespace TexturePrivate
+{
+TextureMap textureMap;
+}
+
 bool
-Texture::load(const std::string &filename, GLuint *pTexture, ...)
+Texture::load(const std::string &textureName, GLuint *pTexture, ...)
 {
+    // Make sure the named texture is in the map.
+    TextureMap::const_iterator textureIt = TexturePrivate::textureMap.find(textureName);
+    if (textureIt == TexturePrivate::textureMap.end())
+    {
+        return false;
+    }
+
+    // Pull the pathname out of the descriptor and use it for the PNG load.
+    TextureDescriptor* desc = textureIt->second;
+    const std::string& filename = desc->pathname();
     ImageData image;
 
     if (!image.load_png(filename))
@@ -199,3 +215,46 @@
 
     return true;
 }
+
+const TextureMap&
+Texture::find_textures()
+{
+    using std::vector;
+    using std::string;
+    if (!TexturePrivate::textureMap.empty())
+    {
+        return TexturePrivate::textureMap;
+    }
+    vector<string> pathVec;
+    string dataDir(GLMARK_DATA_PATH"/textures");
+    Util::list_files(dataDir, pathVec);
+    // 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;
+        }
+
+        string::size_type extPos = curPath.rfind(".png");
+        if (extPos == string::npos)
+        {
+            // We can't trivially determine it's a PNG file so skip it...
+            continue;
+        }
+
+        string name(curPath, namePos, extPos - namePos);
+        TextureDescriptor* desc = new TextureDescriptor(name, curPath);
+        TexturePrivate::textureMap.insert(std::make_pair(name, desc));
+    }
+
+    return TexturePrivate::textureMap;
+}

=== modified file 'src/texture.h'
--- src/texture.h	2011-11-08 21:41:23 +0000
+++ src/texture.h	2012-05-15 18:38:47 +0000
@@ -27,11 +27,50 @@
 #include "gl-headers.h"
 
 #include <string>
+#include <map>
+
+/**
+ * A descriptor for a texture file.
+ */
+class TextureDescriptor
+{
+    std::string name_;
+    std::string pathname_;
+    TextureDescriptor();
+public:
+    TextureDescriptor(const std::string& name, const std::string& pathname) :
+        name_(name),
+        pathname_(pathname) {}
+    ~TextureDescriptor() {}
+    const std::string& pathname() const { return pathname_; }
+};
+
+typedef std::map<std::string, TextureDescriptor*> TextureMap;
 
 class Texture
 {
 public:
-    static bool load(const std::string &filename, GLuint *pTexture, ...);
+    /**
+     * Load a texture by name.
+     *
+     * You must initialize the available texture collection using
+     * Texture::find_textures() before using this method.
+     *
+     * @name:        the texture name
+     *
+     * @return:      true if the operation succeeded, false otherwise
+     */
+    static bool load(const std::string &name, GLuint *pTexture, ...);
+    /**
+     * Locate all available textures.
+     *
+     * This method scans the built-in data paths and builds a database of usable
+     * textures available to scenes.  Map is available on a read-only basis to
+     * scenes that might find it useful for listing textures, etc.
+     *
+     * @return:     a map containing information about the located textures
+     */
+    static const TextureMap& find_textures();
 };
 
 #endif

