]> git.mxchange.org Git - simgear.git/commitdiff
New materials.xml format
authorStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Sat, 9 Aug 2014 19:34:08 +0000 (20:34 +0100)
committerStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Sat, 9 Aug 2014 19:34:08 +0000 (20:34 +0100)
simgear/scene/material/mat.cxx
simgear/scene/material/mat.hxx
simgear/scene/material/matlib.cxx
simgear/scene/material/matlib.hxx
simgear/scene/tgdb/SGOceanTile.cxx
simgear/scene/tgdb/apt_signs.cxx
simgear/scene/tgdb/obj.cxx

index 7d661dbbd8a1c9f5b83ee0017ced6b4bf2aed28a..4456e4b2088d60f78703f909e3ed16b32a8c661d 100644 (file)
@@ -96,19 +96,27 @@ void SGMaterial::_internal_state::add_texture(const std::string &t, int i)
 
 SGMaterial::SGMaterial( const SGReaderWriterOptions* options,
                         const SGPropertyNode *props,
-                        SGPropertyNode *prop_root )
+                        SGPropertyNode *prop_root,
+                        AreaList *a,
+                                               SGSharedPtr<const SGCondition> c)
 {
     init();
+    areas = a;
+    condition = c;
     read_properties( options, props, prop_root );
     buildEffectProperties(options);
 }
 
 SGMaterial::SGMaterial( const osgDB::Options* options,
                         const SGPropertyNode *props, 
-                        SGPropertyNode *prop_root)
+                        SGPropertyNode *prop_root,
+                        AreaList *a,
+                        SGSharedPtr<const SGCondition> c)
 {
     osg::ref_ptr<SGReaderWriterOptions> opt;
     opt = SGReaderWriterOptions::copyOrCreate(options);
+    areas = a;
+    condition = c;
     init();
     read_properties(opt.get(), props, prop_root);
     buildEffectProperties(opt.get());
@@ -409,12 +417,6 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
     } else {
         parameters = new SGPropertyNode();
     }
-
-    // Read conditions node
-    const SGPropertyNode *conditionNode = props->getChild("condition");
-    if (conditionNode) {
-        condition = sgReadCondition(prop_root, conditionNode);
-    }
 }
 
 
@@ -552,13 +554,43 @@ SGMaterialGlyph* SGMaterial::get_glyph (const std::string& name) const
     return it->second;
 }
 
-bool SGMaterial::valid() const
-{ 
-  if (condition) {
-    return condition->test();       
-  } else {
-    return true;
-  }
+bool SGMaterial::valid(SGVec2f loc) const
+{
+       SG_LOG( SG_TERRAIN, SG_BULK, "Checking materials for location ("
+                       << loc.x() << ","
+                       << loc.y() << ")");
+
+       // Check location first again the areas the material is valid for
+       AreaList::const_iterator i = areas->begin();
+
+       if (i == areas->end()) {
+               // No areas defined, so simply check against condition
+               if (condition) {
+                       return condition->test();
+               } else {
+                       return true;
+               }
+       }
+
+       for (; i != areas->end(); i++) {
+
+               SG_LOG( SG_TERRAIN, SG_BULK, "Checking area ("
+                               << i->x() << ","
+                               << i->y() << ") width:"
+                               << i->width() << " height:"
+                               << i->height());
+               // Areas defined, so check that the tile location falls within it
+               // before checking against condition
+               if (i->contains(loc.x(), loc.y())) {
+                       if (condition) {
+                               return condition->test();
+                       } else {
+                               return true;
+                       }
+               }
+       }
+
+       return false;
 }
 
 \f
index c0a651327e43084722869c9e35cecde17a085889..5a37b756f0024d1591cc73b27e4563b9fcc51dea 100644 (file)
@@ -42,14 +42,18 @@ namespace osg
 class StateSet;
 }
 
-
-typedef osg::ref_ptr<osg::Texture2D> Texture2DRef;
-    
 #include <simgear/structure/SGSharedPtr.hxx>
 #include <simgear/threads/SGThread.hxx> // for SGMutex
+#include <simgear/math/SGLimits.hxx>
+#include <simgear/math/SGMisc.hxx>
 #include <simgear/math/SGMath.hxx>
+#include <simgear/math/SGVec2.hxx>
+#include <simgear/math/SGRect.hxx>
 #include <simgear/bvh/BVHMaterial.hxx>
 
+typedef osg::ref_ptr<osg::Texture2D> Texture2DRef;
+typedef std::vector<SGRect <float> > AreaList;
+
 namespace simgear
 {
 class Effect;
@@ -88,13 +92,19 @@ public:
    * state information for the material.  This node is usually
    * loaded from the $FG_ROOT/materials.xml file.
    */
-  SGMaterial( const osgDB::Options*, 
-              const SGPropertyNode *props, 
-              SGPropertyNode *prop_root);
+  SGMaterial(const osgDB::Options*,
+             const SGPropertyNode *props,
+             SGPropertyNode *prop_root,
+             AreaList *a,
+                        SGSharedPtr<const SGCondition> c);
+
 
   SGMaterial(const simgear::SGReaderWriterOptions*,
              const SGPropertyNode *props,
-             SGPropertyNode *prop_root);
+             SGPropertyNode *prop_root,
+             AreaList *a,
+                        SGSharedPtr<const SGCondition> c);
+
   /**
    * Destructor.
    */
@@ -306,9 +316,9 @@ public:
   
   /**
    * Evaluate whether this material is valid given the current global
-   * property state.
+   * property state and the tile location.
    */
-     bool valid() const;
+     bool valid(SGVec2f loc) const;
 
   /**
    * Return pointer to glyph class, or 0 if it doesn't exist.
@@ -474,6 +484,9 @@ private:
   // Condition, indicating when this material is active
   SGSharedPtr<const SGCondition> condition;
   
+  // List of geographical rectangles for this material
+  AreaList* areas;
+
   // Parameters from the materials file
   const SGPropertyNode* parameters;
 
index a7b017fab184c3213628d57b2d3894571c113121..64f75ab205950ea80f5cb384af6209ed1b9c1e6e 100644 (file)
@@ -37,6 +37,7 @@
 #include <simgear/debug/logstream.hxx>
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/misc/sgstream.hxx>
+#include <simgear/props/props.hxx>
 #include <simgear/props/props_io.hxx>
 #include <simgear/props/condition.hxx>
 #include <simgear/scene/tgdb/userdata.hxx>
@@ -68,11 +69,11 @@ SGMaterialLib::SGMaterialLib ( void ) :
 bool SGMaterialLib::load( const string &fg_root, const string& mpath,
         SGPropertyNode *prop_root )
 {
-    SGPropertyNode materials;
+    SGPropertyNode materialblocks;
 
     SG_LOG( SG_INPUT, SG_INFO, "Reading materials from " << mpath );
     try {
-        readProperties( mpath, &materials );
+        readProperties( mpath, &materialblocks );
     } catch (const sg_exception &ex) {
         SG_LOG( SG_INPUT, SG_ALERT, "Error reading materials: "
                 << ex.getMessage() );
@@ -82,44 +83,88 @@ bool SGMaterialLib::load( const string &fg_root, const string& mpath,
         = new osgDB::Options;
     options->setObjectCacheHint(osgDB::Options::CACHE_ALL);
     options->setDatabasePath(fg_root);
-    int nMaterials = materials.nChildren();
-    for (int i = 0; i < nMaterials; i++) {
-        const SGPropertyNode *node = materials.getChild(i);
-        if (!strcmp(node->getName(), "material")) {
-            SGSharedPtr<SGMaterial> m = new SGMaterial(options.get(), node, prop_root);
-
-            std::vector<SGPropertyNode_ptr>names = node->getChildren("name");
-            for ( unsigned int j = 0; j < names.size(); j++ ) {
-                string name = names[j]->getStringValue();
-                // cerr << "Material " << name << endl;
-                matlib[name].push_back(m);
-                m->add_name(name);
-                SG_LOG( SG_TERRAIN, SG_DEBUG, "  Loading material "
-                        << names[j]->getStringValue() );
-            }
-        } else {
-            SG_LOG(SG_INPUT, SG_WARN,
-                   "Skipping bad material entry " << node->getName());
-        }
+
+    simgear::PropertyList blocks = materialblocks.getChildren("region");
+    simgear::PropertyList::const_iterator block_iter = blocks.begin();
+
+    for (; block_iter != blocks.end(); block_iter++) {
+       SGPropertyNode_ptr node = block_iter->get();
+
+               // Read name node purely for logging purposes
+               const SGPropertyNode *nameNode = node->getChild("name");
+               if (nameNode) {
+                       SG_LOG( SG_TERRAIN, SG_INFO, "Loading region "
+                                       << nameNode->getStringValue());
+               }
+
+               // Read list of areas
+               AreaList* arealist = new AreaList;
+
+               const simgear::PropertyList areas = node->getChildren("area");
+               simgear::PropertyList::const_iterator area_iter = areas.begin();
+               for (; area_iter != areas.end(); area_iter++) {
+                       float x1 = area_iter->get()->getFloatValue("lon1", -180.0f);
+                       float x2 = area_iter->get()->getFloatValue("lon2", 180.0);
+                       float y1 = area_iter->get()->getFloatValue("lat1", -90.0f);
+                       float y2 = area_iter->get()->getFloatValue("lat2", 90.0f);
+                       SGRect<float> rect = SGRect<float>(
+                                       fminf(x1, x2),
+                                       fminf(y1, y2),
+                                       fabs(x2 - x1),
+                                       fabs(y2 - y1));
+                       arealist->push_back(rect);
+                       SG_LOG( SG_TERRAIN, SG_INFO, " Area ("
+                                       << rect.x() << ","
+                                       << rect.y() << ") width:"
+                                       << rect.width() << " height:"
+                                       << rect.height());
+               }
+
+               // Read conditions node
+               const SGPropertyNode *conditionNode = node->getChild("condition");
+               SGSharedPtr<const SGCondition> condition;
+               if (conditionNode) {
+                       condition = sgReadCondition(prop_root, conditionNode);
+               }
+
+               // Now build all the materials for this set of areas and conditions
+
+               const simgear::PropertyList materials = node->getChildren("material");
+               simgear::PropertyList::const_iterator materials_iter = materials.begin();
+               for (; materials_iter != materials.end(); materials_iter++) {
+                       const SGPropertyNode *node = materials_iter->get();
+                       SGSharedPtr<SGMaterial> m =
+                                       new SGMaterial(options.get(), node, prop_root, arealist, condition);
+
+                       std::vector<SGPropertyNode_ptr>names = node->getChildren("name");
+                       for ( unsigned int j = 0; j < names.size(); j++ ) {
+                               string name = names[j]->getStringValue();
+                               // cerr << "Material " << name << endl;
+                               matlib[name].push_back(m);
+                               m->add_name(name);
+                               SG_LOG( SG_TERRAIN, SG_DEBUG, "  Loading material "
+                                               << names[j]->getStringValue() );
+                       }
+               }
     }
 
     return true;
 }
 
-// find a material record by material name
-SGMaterial *SGMaterialLib::find( const string& material ) const
+// find a material record by material name and tile center
+SGMaterial *SGMaterialLib::find( const string& material, const SGVec2f center ) const
 {
     SGMaterial *result = NULL;
     const_material_map_iterator it = matlib.find( material );
     if ( it != end() ) {            
         // We now have a list of materials that match this
-        // name. Find the first one that either doesn't have
-        // a condition, or has a condition that evaluates
-        // to true.
-        material_list::const_iterator iter = it->second.begin();
-        while (iter != it->second.end()) {            
+        // name. Find the first one that matches.
+       // We start at the end of the list, as the materials
+       // list is ordered with the smallest regions at the end.
+        material_list::const_reverse_iterator iter = it->second.rbegin();
+        while (iter != it->second.rend()) {
             result = *iter;
-            if (result->valid()) {
+            if (result->valid(center)) {
                 return result;
             }
             iter++;
@@ -129,31 +174,31 @@ SGMaterial *SGMaterialLib::find( const string& material ) const
     return NULL;
 }
 
-void SGMaterialLib::refreshActiveMaterials()
+// find a material record by material name and tile center
+SGMaterial *SGMaterialLib::find( const string& material, const SGGeod& center ) const
 {
-    active_material_cache newCache;
-    material_map_iterator it = matlib.begin();
-    for (; it != matlib.end(); ++it) {
-        newCache[it->first] = find(it->first);
+       SGVec2f c = SGVec2f(center.getLongitudeDeg(), center.getLatitudeDeg());
+       return find(material, c);
+}
+
+SGMaterialCache *SGMaterialLib::generateMatCache(SGVec2f center)
+{
+       SGMaterialCache* newCache = new SGMaterialCache();
+    material_map::const_reverse_iterator it = matlib.rbegin();
+    for (; it != matlib.rend(); ++it) {
+        newCache->insert(it->first, find(it->first, center));
     }
     
-    // use this approach to minimise the time we're holding the lock
-    // lock on the mutex (and hence, would block findCached calls)
-    SGGuard<SGMutex> g(d->mutex);
-    active_cache = newCache;
+    return newCache;
 }
 
-SGMaterial *SGMaterialLib::findCached( const string& material ) const
+SGMaterialCache *SGMaterialLib::generateMatCache(SGGeod center)
 {
-    SGGuard<SGMutex> g(d->mutex);
-    
-    active_material_cache::const_iterator it = active_cache.find(material);
-    if (it == active_cache.end())
-        return NULL;
-    
-    return it->second;
+       SGVec2f c = SGVec2f(center.getLongitudeDeg(), center.getLatitudeDeg());
+       return SGMaterialLib::generateMatCache(c);
 }
 
+
 // Destructor
 SGMaterialLib::~SGMaterialLib ( void ) {
     SG_LOG( SG_GENERAL, SG_INFO, "SGMaterialLib::~SGMaterialLib() size=" << matlib.size());
@@ -176,3 +221,28 @@ const SGMaterial *SGMaterialLib::findMaterial(const osg::Geode* geode)
         return 0;
     return userData->getMaterial();
 }
+
+// Constructor
+SGMaterialCache::SGMaterialCache ( void )
+{
+}
+
+// Insertion into the material cache
+void SGMaterialCache::insert(const std::string& name, SGSharedPtr<SGMaterial> material) {
+       cache[name] = material;
+}
+
+// Search of the material cache
+SGMaterial *SGMaterialCache::find(const string& material) const
+{
+    SGMaterialCache::material_cache::const_iterator it = cache.find(material);
+    if (it == cache.end())
+        return NULL;
+
+    return it->second;
+}
+
+// Destructor
+SGMaterialCache::~SGMaterialCache ( void ) {
+    SG_LOG( SG_GENERAL, SG_INFO, "SGMaterialCache::~SGMaterialCache() size=" << cache.size());
+}
index 8a298b5fe4788ca18b4f8a3d8b7323da4ca3751a..7ead9d75ceb42a3ed46454834429d8cbf8502738 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <simgear/structure/SGReferenced.hxx>
 #include <simgear/structure/SGSharedPtr.hxx>
+#include <simgear/math/SGMath.hxx>
 
 #include <memory>
 #include <string>              // Standard C++ string library
@@ -41,6 +42,27 @@ class SGPropertyNode;
 namespace simgear { class Effect; }
 namespace osg { class Geode; }
 
+// Material cache class
+class SGMaterialCache : public osg::Referenced
+{
+private:
+    typedef std::map < std::string, SGSharedPtr<SGMaterial> > material_cache;
+    material_cache cache;
+
+public:
+    // Constructor
+    SGMaterialCache ( void );
+
+    // Insertion
+    void insert( const std::string& name, SGSharedPtr<SGMaterial> material );
+
+    // Lookup
+    SGMaterial *find( const std::string& material ) const;
+
+    // Destructor
+    ~SGMaterialCache ( void );
+};
+
 // Material management class
 class SGMaterialLib : public SGReferenced
 {
@@ -59,9 +81,6 @@ private:
 
     material_map matlib;
     
-    typedef std::map < std::string, SGSharedPtr<SGMaterial> > active_material_cache;
-    active_material_cache active_cache;
-    
 public:
 
     // Constructor
@@ -71,24 +90,24 @@ public:
     bool load( const std::string &fg_root, const std::string& mpath,
             SGPropertyNode *prop_root );
     // find a material record by material name
-    SGMaterial *find( const std::string& material ) const;
+    SGMaterial *find( const std::string& material, SGVec2f center ) const;
+    SGMaterial *find( const std::string& material, const SGGeod& center ) const;
 
     /**
-     * Material lookup involves evaluation of SGConditions to determine which
-     * possible material (by season, region, etc) is valid. This involves
-     * vproperty tree queries, so repeated calls to find() can cause
+     * Material lookup involves evaluation of position and SGConditions to
+     * determine which possible material (by season, region, etc) is valid.
+     * This involves property tree queries, so repeated calls to find() can cause
      * race conditions when called from the osgDB pager thread. (especially
      * during startup)
      *
      * To fix this, and also avoid repeated re-evaluation of the material
-     * conditions, we provide a version which uses a cached, threadsafe table
-     * of the currently valid materials. The main thread calls the refresh
-     * method below to evaluate the valid materials, and findCached can be
-     * safely called from other threads with no access to unprotected state.
+     * conditions, we provide factory method to generate a material library
+     * cache of the valid materials based on the current state and a given position.
      */
-    SGMaterial *findCached( const std::string& material ) const;
-    void refreshActiveMaterials();
-    
+
+    SGMaterialCache *generateMatCache( SGVec2f center);
+    SGMaterialCache *generateMatCache( SGGeod center);
+
     material_map_iterator begin() { return matlib.begin(); }
     const_material_map_iterator begin() const { return matlib.begin(); }
 
@@ -99,6 +118,7 @@ public:
 
     // Destructor
     ~SGMaterialLib ( void );
+
 };
 
 typedef SGSharedPtr<SGMaterialLib> SGMaterialLibPtr;
index c550280c8965a19e974e288282395d88de2b9ff2..f4636159a7cbbc071fa81922c29356fb5d329ea1 100644 (file)
@@ -279,7 +279,10 @@ osg::Node* SGOceanTile(const SGBucket& b, SGMaterialLib *matlib, int latPoints,
     double tex_width = 1000.0;
   
     // find Ocean material in the properties list
-    SGMaterial *mat = matlib->findCached( "Ocean" );
+    SGMaterialCache* matcache = matlib->generateMatCache(b.get_center());
+    SGMaterial* mat = matcache->find( "Ocean" );
+    delete matcache;
+
     if ( mat != NULL ) {
         // set the texture width and height values for this
         // material
index 7d07132bff07d9ad5f6eb4f34b93ea939a32cdd1..e04ddc67af2a33bc6282bc7cf655019b8388fa41 100644 (file)
@@ -260,7 +260,7 @@ AirportSignBuilder::AirportSignBuilder(SGMaterialLib* mats, const SGGeod& center
     
     assert(mats);
     d->materials = mats;
-    d->signCaseGeometry = d->getGeometry(d->materials->find("signcase")->get_effect());
+    d->signCaseGeometry = d->getGeometry(d->materials->find("signcase", center)->get_effect());
 }
 
 osg::Node* AirportSignBuilder::getSignsGroup()
@@ -430,7 +430,7 @@ void AirportSignBuilder::addSign(const SGGeod& pos, double heading, const std::s
         }
 
         if (! newmat.empty()) {
-            material = d->materials->find(newmat);
+            material = d->materials->find(newmat, pos);
             newmat.clear();
         }
 
@@ -504,7 +504,7 @@ void AirportSignBuilder::addSign(const SGGeod& pos, double heading, const std::s
   // Part II: typeset
     double boxwidth = std::max(total_width1, total_width2) * 0.5;
     double hpos = -boxwidth;
-    SGMaterial *mat = d->materials->find("signcase");
+    SGMaterial *mat = d->materials->find("signcase", pos);
   
     double coverSize = fabs(total_width1 - total_width2) * 0.5;
     element_info* s1 = new element_info(mat, mat->get_glyph("cover1"), sign_height, coverSize);
index 1b55325d9b2ea698b79c9554227b2a1ebd454970..1239d090e9a3bf66a15f3d91f9cd7ab6ebffbc9d 100644 (file)
@@ -141,7 +141,7 @@ public:
   }
 
   bool
-  insertPtGeometry(const SGBinObject& obj, SGMaterialLib* matlib)
+  insertPtGeometry(const SGBinObject& obj, SGMaterialCache* matcache)
   {
     if (obj.get_pts_v().size() != obj.get_pts_n().size()) {
       SG_LOG(SG_TERRAIN, SG_ALERT,
@@ -151,9 +151,7 @@ public:
 
     for (unsigned grp = 0; grp < obj.get_pts_v().size(); ++grp) {
       std::string materialName = obj.get_pt_materials()[grp];
-      SGMaterial* material = 0;
-      if (matlib)
-          material = matlib->findCached(materialName);
+      SGMaterial* material = matcache->find(materialName);
       SGVec4f color = getMaterialLightColor(material);
 
       if (3 <= materialName.size() && materialName.substr(0, 3) != "RWY") {
@@ -381,11 +379,11 @@ public:
     }
   }
 
-  SGVec2f getTexCoordScale(const std::string& name, SGMaterialLib* matlib)
+  SGVec2f getTexCoordScale(const std::string& name, SGMaterialCache* matcache)
   {
-    if (!matlib)
+    if (!matcache)
       return SGVec2f(1, 1);
-    SGMaterial* material = matlib->findCached(name);
+    SGMaterial* material = matcache->find(name);
     if (!material)
       return SGVec2f(1, 1);
 
@@ -393,7 +391,7 @@ public:
   }
 
   bool
-  insertSurfaceGeometry(const SGBinObject& obj, SGMaterialLib* matlib)
+  insertSurfaceGeometry(const SGBinObject& obj, SGMaterialCache* matcache)
   {
     if (obj.get_tris_n().size() < obj.get_tris_v().size() ||
         obj.get_tris_tcs().size() < obj.get_tris_v().size()) {
@@ -404,7 +402,7 @@ public:
 
     for (unsigned grp = 0; grp < obj.get_tris_v().size(); ++grp) {
       std::string materialName = obj.get_tri_materials()[grp];
-      SGVec2f tc0Scale = getTexCoordScale(materialName, matlib);
+      SGVec2f tc0Scale = getTexCoordScale(materialName, matcache);
       SGVec2f tc1Scale(1.0, 1.0);
       addTriangleGeometry(materialTriangleMap[materialName],
                           obj, grp, tc0Scale, tc1Scale );
@@ -418,7 +416,7 @@ public:
     }
     for (unsigned grp = 0; grp < obj.get_strips_v().size(); ++grp) {
       std::string materialName = obj.get_strip_materials()[grp];
-      SGVec2f tc0Scale = getTexCoordScale(materialName, matlib);
+      SGVec2f tc0Scale = getTexCoordScale(materialName, matcache);
       SGVec2f tc1Scale(1.0, 1.0);
       addStripGeometry(materialTriangleMap[materialName],
                           obj, grp, tc0Scale, tc1Scale);
@@ -432,7 +430,7 @@ public:
     }
     for (unsigned grp = 0; grp < obj.get_fans_v().size(); ++grp) {
       std::string materialName = obj.get_fan_materials()[grp];
-      SGVec2f tc0Scale = getTexCoordScale(materialName, matlib);
+      SGVec2f tc0Scale = getTexCoordScale(materialName, matcache);
       SGVec2f tc1Scale(1.0, 1.0);
       addFanGeometry(materialTriangleMap[materialName],
                        obj, grp, tc0Scale, tc1Scale );
@@ -440,7 +438,7 @@ public:
     return true;
   }
 
-  osg::Node* getSurfaceGeometry(SGMaterialLib* matlib, bool useVBOs) const
+  osg::Node* getSurfaceGeometry(SGMaterialCache* matcache, bool useVBOs) const
   {
     if (materialTriangleMap.empty())
       return 0;
@@ -456,8 +454,8 @@ public:
     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
       osg::Geometry* geometry = i->second.buildGeometry(useVBOs);
       SGMaterial *mat = NULL;
-      if (matlib) {
-        mat = matlib->findCached(i->first);
+      if (matcache) {
+        mat = matcache->find(i->first);
       }
       eg = new EffectGeode;
       eg->setName("EffectGeode");
@@ -478,7 +476,7 @@ public:
     }
   }
 
-  void computeRandomSurfaceLights(SGMaterialLib* matlib)
+  void computeRandomSurfaceLights(SGMaterialCache* matcache)
   {
     SGMaterialTriangleMap::iterator i;
 
@@ -487,7 +485,7 @@ public:
     mt_init(&seed, unsigned(123));
 
     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
-      SGMaterial *mat = matlib->findCached(i->first);
+      SGMaterial *mat = matcache->find(i->first);
       if (!mat)
         continue;
 
@@ -530,7 +528,7 @@ public:
   }
 
   void computeRandomObjectsAndBuildings(
-    SGMaterialLib* matlib,
+    SGMaterialCache* matcache,
     float building_density,
     bool use_random_objects,
     bool use_random_buildings,
@@ -543,7 +541,7 @@ public:
     mt_init(&seed, unsigned(123));
 
     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
-      SGMaterial *mat = matlib->findCached(i->first);
+      SGMaterial *mat = matcache->find(i->first);
       SGTexturedTriangleBin triangleBin = i->second;
 
       if (!mat)
@@ -850,7 +848,7 @@ public:
     }
   }
 
-  void computeRandomForest(SGMaterialLib* matlib, float vegetation_density)
+  void computeRandomForest(SGMaterialCache* matcache, float vegetation_density)
   {
     SGMaterialTriangleMap::iterator i;
 
@@ -860,7 +858,7 @@ public:
     mt_init(&seed, unsigned(586));
 
     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
-      SGMaterial *mat = matlib->findCached(i->first);
+      SGMaterial *mat = matcache->find(i->first);
       if (!mat)
         continue;
 
@@ -910,11 +908,11 @@ public:
     }
   }
 
-  bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
+  bool insertBinObj(const SGBinObject& obj, SGMaterialCache* matcache)
   {
-    if (!insertPtGeometry(obj, matlib))
+    if (!insertPtGeometry(obj, matcache))
       return false;
-    if (!insertSurfaceGeometry(obj, matlib))
+    if (!insertSurfaceGeometry(obj, matcache))
       return false;
     return true;
   }
@@ -978,6 +976,7 @@ public:
         return NULL;
 
       SGMaterialLibPtr matlib;
+      SGMaterialCache* matcache = 0;
       bool useVBOs = false;
       bool simplifyNear    = false;
       double ratio       = SG_SIMPLIFIER_RATIO;
@@ -1001,6 +1000,9 @@ public:
       SGGeod geodPos = SGGeod::fromCart(center);
       SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
 
+      // Generate a materials cache
+      if (matlib) matcache = matlib->generateMatCache(geodPos);
+
       // rotate the tiles so that the bounding boxes get nearly axis aligned.
       // this will help the collision tree's bounding boxes a bit ...
       std::vector<SGVec3d> nodes = tile.get_wgs84_nodes();
@@ -1016,10 +1018,10 @@ public:
 
       osg::ref_ptr<SGTileGeometryBin> tileGeometryBin = new SGTileGeometryBin;
 
-      if (!tileGeometryBin->insertBinObj(tile, matlib))
+      if (!tileGeometryBin->insertBinObj(tile, matcache))
         return NULL;
 
-      osg::Node* node = tileGeometryBin->getSurfaceGeometry(matlib, useVBOs);
+      osg::Node* node = tileGeometryBin->getSurfaceGeometry(matcache, useVBOs);
       if (node && simplifyNear) {
         osgUtil::Simplifier simplifier(ratio, maxError, maxLength);
         node->accept(simplifier);
@@ -1031,14 +1033,8 @@ public:
     // Generate all the lighting objects for the tile.
     osg::LOD* generateLightingTileObjects()
     {
-      SGMaterialLibPtr matlib;
-
-      if (_options)
-        matlib = _options->getMaterialLib();
-
-      // FIXME: ugly, has a side effect
-      if (matlib)
-        _tileGeometryBin->computeRandomSurfaceLights(matlib);
+      if (_matcache)
+        _tileGeometryBin->computeRandomSurfaceLights(_matcache);
 
       GroundLightManager* lightManager = GroundLightManager::instance();
       osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
@@ -1080,14 +1076,14 @@ public:
         vasiGeode->setEffect(vasiEffect);
         SGVec4f red(1, 0, 0, 1);
         SGMaterial* mat = 0;
-        if (matlib)
-          mat = matlib->findCached("RWY_RED_LIGHTS");
+        if (_matcache)
+          mat = _matcache->find("RWY_RED_LIGHTS");
         if (mat)
           red = mat->get_light_color();
         SGVec4f white(1, 1, 1, 1);
         mat = 0;
-        if (matlib)
-          mat = matlib->findCached("RWY_WHITE_LIGHTS");
+        if (_matcache)
+          mat = _matcache->find("RWY_WHITE_LIGHTS");
         if (mat)
           white = mat->get_light_color();
         SGDirectionalLightListBin::const_iterator i;
@@ -1211,7 +1207,7 @@ public:
 
 
       if (matlib && (use_random_objects || use_random_buildings)) {
-        _tileGeometryBin->computeRandomObjectsAndBuildings(matlib,
+        _tileGeometryBin->computeRandomObjectsAndBuildings(_matcache,
                                                          building_density,
                                                          use_random_objects,
                                                          use_random_buildings,
@@ -1274,7 +1270,7 @@ public:
 
       if (use_random_vegetation && matlib) {
         // Now add some random forest.
-        _tileGeometryBin->computeRandomForest(matlib, vegetation_density);
+        _tileGeometryBin->computeRandomForest(_matcache, vegetation_density);
 
         if (! _tileGeometryBin->randomForest.empty()) {
           forestNode = createForest(_tileGeometryBin->randomForest, osg::Matrix::identity(),
@@ -1301,6 +1297,7 @@ public:
 
     /// The original options to use for this bunch of models
     osg::ref_ptr<SGReaderWriterOptions> _options;
+    osg::ref_ptr<SGMaterialCache> _matcache;
     osg::ref_ptr<SGTileGeometryBin> _tileGeometryBin;
     string _path;
     bool _loadterrain;
@@ -1314,6 +1311,7 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
       return NULL;
 
     SGMaterialLibPtr matlib;
+    osg::ref_ptr<SGMaterialCache> matcache;
     bool useVBOs = false;
     bool simplifyDistant = false;
     bool simplifyNear    = false;
@@ -1340,6 +1338,8 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
     SGVec3d center = tile.get_gbs_center();
     SGGeod geodPos = SGGeod::fromCart(center);
     SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
+    if (matlib)
+       matcache = matlib->generateMatCache(geodPos);
 
     // rotate the tiles so that the bounding boxes get nearly axis aligned.
     // this will help the collision tree's bounding boxes a bit ...
@@ -1356,11 +1356,10 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
 
     osg::ref_ptr<SGTileGeometryBin> tileGeometryBin = new SGTileGeometryBin;
 
-    if (!tileGeometryBin->insertBinObj(tile, matlib))
+    if (!tileGeometryBin->insertBinObj(tile, matcache))
       return NULL;
 
-
-    osg::Node* node = tileGeometryBin->getSurfaceGeometry(matlib, useVBOs);
+    osg::Node* node = tileGeometryBin->getSurfaceGeometry(matcache, useVBOs);
     if (node && simplifyDistant) {
       osgUtil::Simplifier simplifier(ratio, maxError, maxLength);
       node->accept(simplifier);
@@ -1399,6 +1398,7 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
     randomObjectCallback->_tileGeometryBin = tileGeometryBin;
     randomObjectCallback->_path = std::string(path);
     randomObjectCallback->_loadterrain = ! (simplifyNear == simplifyDistant);
+    randomObjectCallback->_matcache = matcache;
 
     osg::ref_ptr<osgDB::Options> callbackOptions = new osgDB::Options;
     callbackOptions->setReadFileCallback(randomObjectCallback.get());