+ string omname = masks[i]->getStringValue();
+
+ if (! omname.empty()) {
+ SGPath ompath("Textures.high");
+ ompath.append(omname);
+ string fullMaskPath = SGModelLib::findDataFile(ompath.str(), options);
+
+ if (fullMaskPath.empty()) {
+ ompath = SGPath("Textures");
+ ompath.append(omname);
+ fullMaskPath = SGModelLib::findDataFile(ompath.str(), options);
+ }
+
+ if (fullMaskPath.empty()) {
+ SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""
+ << omname << "\" in Textures or Textures.high folders.");
+ }
+ else
+ {
+ osg::Image* image = osgDB::readImageFile(fullMaskPath, options);
+ if (image && image->valid())
+ {
+ osg::Texture2D* object_mask = new osg::Texture2D;
+
+ bool dds_mask = (ompath.lower_extension() == "dds");
+
+ if (dds[i] != dds_mask) {
+ // Texture format does not match mask format. This is relevant for
+ // the object mask, as DDS textures have an origin at the bottom
+ // left rather than top left, therefore we flip the object mask
+ // vertically.
+ image->flipVertical();
+ }
+
+ object_mask->setImage(image);
+
+ // We force the filtering to be nearest, as the red channel (rotation)
+ // in particular, doesn't make sense to be interpolated between pixels.
+ object_mask->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST);
+ object_mask->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST);
+
+ object_mask->setDataVariance(osg::Object::STATIC);
+ object_mask->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
+ object_mask->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
+ _masks.push_back(object_mask);
+ }
+ }
+ }
+ }
+
+ xsize = props->getDoubleValue("xsize", 0.0);
+ ysize = props->getDoubleValue("ysize", 0.0);
+ wrapu = props->getBoolValue("wrapu", true);
+ wrapv = props->getBoolValue("wrapv", true);
+ mipmap = props->getBoolValue("mipmap", true);
+ light_coverage = props->getDoubleValue("light-coverage", 0.0);
+
+ // Building properties
+ building_coverage = props->getDoubleValue("building-coverage", 0.0);
+ building_spacing = props->getDoubleValue("building-spacing-m", 5.0);
+
+ string bt = props->getStringValue("building-texture", "Textures/buildings.png");
+ building_texture = SGModelLib::findDataFile(bt, options);
+
+ if (building_texture.empty()) {
+ SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \"" << bt);
+ }
+
+ bt = props->getStringValue("building-lightmap", "Textures/buildings-lightmap.png");
+ building_lightmap = SGModelLib::findDataFile(bt, options);
+
+ if (building_lightmap.empty()) {
+ SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \"" << bt);
+ }
+
+ building_small_ratio = props->getDoubleValue("building-small-ratio", 0.8);
+ building_medium_ratio = props->getDoubleValue("building-medium-ratio", 0.15);
+ building_large_ratio = props->getDoubleValue("building-large-ratio", 0.05);
+
+ building_small_pitch = props->getDoubleValue("building-small-pitch", 0.8);
+ building_medium_pitch = props->getDoubleValue("building-medium-pitch", 0.2);
+ building_large_pitch = props->getDoubleValue("building-large-pitch", 0.1);
+
+ building_small_min_floors = props->getIntValue("building-small-min-floors", 1);
+ building_small_max_floors = props->getIntValue("building-small-max-floors", 3);
+ building_medium_min_floors = props->getIntValue("building-medium-min-floors", 3);
+ building_medium_max_floors = props->getIntValue("building-medium-max-floors", 8);
+ building_large_min_floors = props->getIntValue("building-large-min-floors", 5);
+ building_large_max_floors = props->getIntValue("building-large-max-floors", 20);
+
+ building_small_min_width = props->getFloatValue("building-small-min-width-m", 15.0);
+ building_small_max_width = props->getFloatValue("building-small-max-width-m", 60.0);
+ building_small_min_depth = props->getFloatValue("building-small-min-depth-m", 10.0);
+ building_small_max_depth = props->getFloatValue("building-small-max-depth-m", 20.0);
+
+ building_medium_min_width = props->getFloatValue("building-medium-min-width-m", 25.0);
+ building_medium_max_width = props->getFloatValue("building-medium-max-width-m", 50.0);
+ building_medium_min_depth = props->getFloatValue("building-medium-min-depth-m", 20.0);
+ building_medium_max_depth = props->getFloatValue("building-medium-max-depth-m", 50.0);
+
+ building_large_min_width = props->getFloatValue("building-large-min-width-m", 50.0);
+ building_large_max_width = props->getFloatValue("building-large-max-width-m", 75.0);
+ building_large_min_depth = props->getFloatValue("building-large-min-depth-m", 50.0);
+ building_large_max_depth = props->getFloatValue("building-large-max-depth-m", 75.0);
+
+ // Random vegetation properties
+ wood_coverage = props->getDoubleValue("wood-coverage", 0.0);
+ tree_height = props->getDoubleValue("tree-height-m", 0.0);
+ tree_width = props->getDoubleValue("tree-width-m", 0.0);
+ tree_range = props->getDoubleValue("tree-range-m", 0.0);
+ tree_varieties = props->getIntValue("tree-varieties", 1);
+
+ const SGPropertyNode* treeTexNode = props->getChild("tree-texture");
+
+ if (treeTexNode) {
+ string treeTexPath = props->getStringValue("tree-texture");
+
+ if (! treeTexPath.empty()) {
+ SGPath treePath("Textures.high");
+ treePath.append(treeTexPath);
+ tree_texture = SGModelLib::findDataFile(treePath.str(), options);
+
+ if (tree_texture.empty()) {
+ treePath = SGPath("Textures");
+ treePath.append(treeTexPath);
+ tree_texture = SGModelLib::findDataFile(treePath.str(), options);
+ }
+ }
+ }
+
+ // surface values for use with ground reactions
+ solid = props->getBoolValue("solid", true);
+ friction_factor = props->getDoubleValue("friction-factor", 1.0);
+ rolling_friction = props->getDoubleValue("rolling-friction", 0.02);
+ bumpiness = props->getDoubleValue("bumpiness", 0.0);
+ load_resistance = props->getDoubleValue("load-resistance", 1e30);
+
+ // Taken from default values as used in ac3d
+ ambient[0] = props->getDoubleValue("ambient/r", 0.2);
+ ambient[1] = props->getDoubleValue("ambient/g", 0.2);
+ ambient[2] = props->getDoubleValue("ambient/b", 0.2);
+ ambient[3] = props->getDoubleValue("ambient/a", 1.0);
+
+ diffuse[0] = props->getDoubleValue("diffuse/r", 0.8);
+ diffuse[1] = props->getDoubleValue("diffuse/g", 0.8);
+ diffuse[2] = props->getDoubleValue("diffuse/b", 0.8);
+ diffuse[3] = props->getDoubleValue("diffuse/a", 1.0);
+
+ specular[0] = props->getDoubleValue("specular/r", 0.0);
+ specular[1] = props->getDoubleValue("specular/g", 0.0);
+ specular[2] = props->getDoubleValue("specular/b", 0.0);
+ specular[3] = props->getDoubleValue("specular/a", 1.0);
+
+ emission[0] = props->getDoubleValue("emissive/r", 0.0);
+ emission[1] = props->getDoubleValue("emissive/g", 0.0);
+ emission[2] = props->getDoubleValue("emissive/b", 0.0);
+ emission[3] = props->getDoubleValue("emissive/a", 1.0);
+
+ shininess = props->getDoubleValue("shininess", 1.0);
+
+ if (props->hasChild("effect"))
+ effect = props->getStringValue("effect");
+
+ std::vector<SGPropertyNode_ptr> object_group_nodes =
+ ((SGPropertyNode *)props)->getChildren("object-group");
+ for (unsigned int i = 0; i < object_group_nodes.size(); i++)
+ object_groups.push_back(new SGMatModelGroup(object_group_nodes[i]));
+
+ // read glyph table for taxi-/runway-signs
+ std::vector<SGPropertyNode_ptr> glyph_nodes = props->getChildren("glyph");
+ for (unsigned int i = 0; i < glyph_nodes.size(); i++) {
+ const char *name = glyph_nodes[i]->getStringValue("name");
+ if (name)
+ glyphs[name] = new SGMaterialGlyph(glyph_nodes[i]);