Sfoglia il codice sorgente

Changed Properties so that files can be loaded relative to the property file.

File paths in property files can now be relative to the working directory or the property file.
In the mesh sample, this means the texture path in the material file can be "res/duck-diffuse.png" or "duck-diffuse.png".
Darryl Gough 12 anni fa
parent
commit
1c34eebc9b

+ 1 - 0
gameplay/src/AudioBuffer.cpp

@@ -327,6 +327,7 @@ bool AudioBuffer::loadWav(Stream* stream, ALuint buffer)
             }
         }
     }
+    return false;
 }
 
 bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer)

+ 4 - 4
gameplay/src/AudioSource.cpp

@@ -77,18 +77,18 @@ AudioSource* AudioSource::create(Properties* properties)
         return NULL;
     }
 
-    const char* path = properties->getString("path");
-    if (path == NULL)
+    std::string path;
+    if (!properties->getPath("path", &path))
     {
         GP_ERROR("Audio file failed to load; the file path was not specified.");
         return NULL;
     }
 
     // Create the audio source.
-    AudioSource* audio = AudioSource::create(path);
+    AudioSource* audio = AudioSource::create(path.c_str());
     if (audio == NULL)
     {
-        GP_ERROR("Audio file '%s' failed to load properly.", path);
+        GP_ERROR("Audio file '%s' failed to load properly.", path.c_str());
         return NULL;
     }
 

+ 32 - 2
gameplay/src/Bundle.cpp

@@ -286,6 +286,24 @@ const char* Bundle::getIdFromOffset(unsigned int offset) const
     return NULL;
 }
 
+const std::string& Bundle::getMaterialPath()
+{
+    if (_materialPath.empty())
+    {
+        int pos = _path.find_last_of('.');
+        if (pos > 2)
+        {
+            _materialPath = _path.substr(0, pos);
+            _materialPath.append(".material");
+            if (!FileSystem::fileExists(_materialPath.c_str()))
+            {
+                _materialPath.clear();
+            }
+        }
+    }
+    return _materialPath;
+}
+
 Bundle::Reference* Bundle::seekTo(const char* id, unsigned int type)
 {
     Reference* ref = find(id);
@@ -968,8 +986,20 @@ Model* Bundle::readModel(const char* nodeId)
             }
             if (materialCount > 0)
             {
-                // TODO: Material loading not supported yet.
-                GP_WARN("Material loading is not yet supported.");
+                for (unsigned int i = 0; i < materialCount; ++i)
+                {
+                    std::string materialName = readString(_stream);
+                    std::string materialPath = getMaterialPath();
+                    materialPath.append("#");
+                    materialPath.append(materialName);
+                    Material* material = Material::create(materialPath.c_str());
+                    if (material)
+                    {
+                        int partIndex = model->getMesh()->getPartCount() > 0 ? i : -1;
+                        model->setMaterial(material, partIndex);
+                        SAFE_RELEASE(material);
+                    }
+                }
             }
             return model;
         }

+ 8 - 0
gameplay/src/Bundle.h

@@ -189,6 +189,13 @@ private:
      */
     const char* getIdFromOffset(unsigned int offset) const;
 
+    /**
+     * Gets the path to the bundle's default material file, if it exists.
+     * 
+     * @return The bundle's default material path. Returns an empty string if the default material does not exist.
+     */
+    const std::string& getMaterialPath();
+
     /**
      * Seeks the file pointer to the object with the given ID and type
      * and returns the relevant Reference.
@@ -427,6 +434,7 @@ private:
     bool skipNode();
 
     std::string _path;
+    std::string _materialPath;
     unsigned int _referenceCount;
     Reference* _references;
     Stream* _stream;

+ 3 - 3
gameplay/src/ImageControl.cpp

@@ -47,10 +47,10 @@ void ImageControl::initialize(Theme::Style* style, Properties* properties)
 
     Control::initialize(style, properties);
 
-    const char* path = properties->getString("path");
-    if (path)
+    std::string path;
+    if (properties->getPath("path", &path))
     {
-        setImage(path);
+        setImage(path.c_str());
     }
 
     if (properties->exists("srcRegion"))

+ 3 - 3
gameplay/src/Material.cpp

@@ -412,8 +412,8 @@ void Material::loadRenderState(RenderState* renderState, Properties* properties)
             }
 
             // Get the texture path.
-            const char* path = ns->getString("path");
-            if (path == NULL || strlen(path) == 0)
+            std::string path;
+            if (!ns->getPath("path", &path))
             {
                 GP_ERROR("Texture sampler '%s' is missing required image file path.", name);
                 continue;
@@ -428,7 +428,7 @@ void Material::loadRenderState(RenderState* renderState, Properties* properties)
 
             // Set the sampler parameter.
             GP_ASSERT(renderState->getParameter(name));
-            Texture::Sampler* sampler = renderState->getParameter(name)->setValue(path, mipmap);
+            Texture::Sampler* sampler = renderState->getParameter(name)->setValue(path.c_str(), mipmap);
             if (sampler)
             {
                 sampler->setWrapMode(wrapS, wrapT);

+ 3 - 3
gameplay/src/ParticleEmitter.cpp

@@ -111,8 +111,8 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
 
     // Load sprite properties.
     // Path to image file is required.
-    const char* texturePath = sprite->getString("path");
-    if (!texturePath || strlen(texturePath) == 0)
+    std::string texturePath;
+    if (!sprite->getPath("path", &texturePath))
     {
         GP_ERROR("Failed to load particle emitter: required image file path ('path') is missing.");
         return NULL;
@@ -184,7 +184,7 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
     bool orbitAcceleration = properties->getBool("orbitAcceleration");
 
     // Apply all properties to a newly created ParticleEmitter.
-    ParticleEmitter* emitter = ParticleEmitter::create(texturePath, textureBlending, particleCountMax);
+    ParticleEmitter* emitter = ParticleEmitter::create(texturePath.c_str(), textureBlending, particleCountMax);
     if (!emitter)
     {
         GP_ERROR("Failed to create particle emitter.");

+ 74 - 6
gameplay/src/Properties.cpp

@@ -26,12 +26,14 @@ void calculateNamespacePath(const std::string& urlString, std::string& fileStrin
 Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath);
 
 Properties::Properties()
+    : _dirPath(NULL), _parent(NULL)
 {
 }
 
 Properties::Properties(const Properties& copy)
-    : _namespace(copy._namespace), _id(copy._id), _parentID(copy._parentID), _properties(copy._properties)
+    : _namespace(copy._namespace), _id(copy._id), _parentID(copy._parentID), _properties(copy._properties), _dirPath(NULL), _parent(copy._parent)
 {
+    setDirectoryPath(copy._dirPath);
     _namespaces = std::vector<Properties*>();
     std::vector<Properties*>::const_iterator it;
     for (it = copy._namespaces.begin(); it < copy._namespaces.end(); ++it)
@@ -44,12 +46,14 @@ Properties::Properties(const Properties& copy)
 
 
 Properties::Properties(Stream* stream)
+    : _dirPath(NULL), _parent(NULL)
 {
     readProperties(stream);
     rewind();
 }
 
-Properties::Properties(Stream* stream, const char* name, const char* id, const char* parentID) : _namespace(name)
+Properties::Properties(Stream* stream, const char* name, const char* id, const char* parentID, Properties* parent)
+    : _namespace(name), _dirPath(NULL), _parent(parent)
 {
     if (id)
     {
@@ -104,6 +108,7 @@ Properties* Properties::create(const char* url)
         p = p->clone();
         SAFE_DELETE(properties);
     }
+    p->setDirectoryPath(FileSystem::dirname(fileString.c_str()));
     return p;
 }
 
@@ -248,7 +253,7 @@ void Properties::readProperties(Stream* stream)
                     }
 
                     // New namespace without an ID.
-                    Properties* space = new Properties(stream, name, NULL, parentID);
+                    Properties* space = new Properties(stream, name, NULL, parentID, this);
                     _namespaces.push_back(space);
 
                     // If the namespace ends on this line, seek to right after the '}' character.
@@ -290,7 +295,7 @@ void Properties::readProperties(Stream* stream)
                         }
 
                         // Create new namespace.
-                        Properties* space = new Properties(stream, name, value, parentID);
+                        Properties* space = new Properties(stream, name, value, parentID, this);
                         _namespaces.push_back(space);
 
                         // If the namespace ends on this line, seek to right after the '}' character.
@@ -311,7 +316,7 @@ void Properties::readProperties(Stream* stream)
                         if (c == '{')
                         {
                             // Create new namespace.
-                            Properties* space = new Properties(stream, name, value, parentID);
+                            Properties* space = new Properties(stream, name, value, parentID, this);
                             _namespaces.push_back(space);
                         }
                         else
@@ -339,6 +344,7 @@ void Properties::readProperties(Stream* stream)
 
 Properties::~Properties()
 {
+    SAFE_DELETE(_dirPath);
     for (size_t i = 0, count = _namespaces.size(); i < count; ++i)
     {
         SAFE_DELETE(_namespaces[i]);
@@ -961,6 +967,41 @@ bool Properties::getColor(const char* name, Vector4* out) const
     return false;
 }
 
+bool Properties::getPath(const char* name, std::string* path) const
+{
+    GP_ASSERT(name && path);
+    const char* valueString = getString(name);
+    if (valueString)
+    {
+        if (FileSystem::fileExists(valueString))
+        {
+            path->assign(valueString);
+            return true;
+        }
+        else
+        {
+            const Properties* prop = this;
+            while (prop != NULL)
+            {
+                // Search for the file path relative to the bundle file
+                const std::string* dirPath = prop->_dirPath;
+                if (dirPath != NULL && !dirPath->empty())
+                {
+                    std::string relativePath = *dirPath;
+                    relativePath.append(valueString);
+                    if (FileSystem::fileExists(relativePath.c_str()))
+                    {
+                        path->assign(relativePath);
+                        return true;
+                    }
+                }
+                prop = prop->_parent;
+            }
+        }
+    }
+    return false;
+}
+
 Properties* Properties::clone()
 {
     Properties* p = new Properties();
@@ -970,17 +1011,44 @@ Properties* Properties::clone()
     p->_parentID = _parentID;
     p->_properties = _properties;
     p->_propertiesItr = p->_properties.end();
+    p->setDirectoryPath(_dirPath);
 
     for (size_t i = 0, count = _namespaces.size(); i < count; i++)
     {
         GP_ASSERT(_namespaces[i]);
-        p->_namespaces.push_back(_namespaces[i]->clone());
+        Properties* child = _namespaces[i]->clone();
+        p->_namespaces.push_back(child);
+        child->_parent = p;
     }
     p->_namespacesItr = p->_namespaces.end();
 
     return p;
 }
 
+void Properties::setDirectoryPath(const std::string* path)
+{
+    if (path)
+    {
+        setDirectoryPath(*path);
+    }
+    else
+    {
+        SAFE_DELETE(_dirPath);
+    }
+}
+
+void Properties::setDirectoryPath(const std::string& path)
+{
+    if (_dirPath == NULL)
+    {
+        _dirPath = new std::string(path);
+    }
+    else
+    {
+        _dirPath->assign(path);
+    }
+}
+
 void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath)
 {
     // If the url references a specific namespace within the file,

+ 27 - 4
gameplay/src/Properties.h

@@ -8,7 +8,7 @@
 
 namespace gameplay
 {
-
+class Properties;
 /**
  * Defines a utility for loading text files in the GamePlay "properties" files
  * and reading primitive types and GamePlay math classes out of them.
@@ -377,20 +377,38 @@ public:
      */
     bool getColor(const char* name, Vector4* out) const;
 
+    /**
+     * Gets the file path for the given property if the file exists.
+     * 
+     * This method will first search for the file relative to the working directory.
+     * If the file is not found then it will search relative to the directory the bundle file is in.
+     * 
+     * @param name The name of the property.
+     * @param path The string to copy the path to if the file exists.
+     * 
+     * @return True if the property exists and the file exists, false otherwise.
+     */
+    bool getPath(const char* name, std::string* path) const;
 
 private:
     
     /**
-     * Constructors.
+     * Constructor.
      */
     Properties();
+
+    /**
+     * Constructs the Properties class from a file.
+     *
+     * @param stream The stream used for reading the properties from file.
+     */
     Properties(Stream* stream);
     Properties(const Properties& copy);
 
     /**
-     * Constructor. Read from the beginning of namespace specified
+     * Constructor. Read from the beginning of namespace specified.
      */
-    Properties(Stream* stream, const char* name, const char* id = NULL, const char* parentID = NULL);
+    Properties(Stream* stream, const char* name, const char* id, const char* parentID, Properties* parent);
 
     void readProperties(Stream* stream);
 
@@ -407,6 +425,9 @@ private:
     // Clones the Properties object.
     Properties* clone();
 
+    void setDirectoryPath(const std::string* path);
+    void setDirectoryPath(const std::string& path);
+
     std::string _namespace;
     std::string _id;
     std::string _parentID;
@@ -414,6 +435,8 @@ private:
     std::map<std::string, std::string>::const_iterator _propertiesItr;
     std::vector<Properties*> _namespaces;
     std::vector<Properties*>::const_iterator _namespacesItr;
+    std::string* _dirPath;
+    Properties* _parent;
 };
 
 }

+ 4 - 2
gameplay/src/SceneLoader.cpp

@@ -44,9 +44,11 @@ Scene* SceneLoader::loadInternal(const char* url)
     }
 
     // Get the path to the main GPB.
-    const char* path = sceneProperties->getString("path");
-    if (path)
+    std::string path;
+    if (sceneProperties->getPath("path", &path))
+    {
         _gpbPath = path;
+    }
 
     // Build the node URL/property and animation reference tables and load the referenced files/store the inline properties objects.
     buildReferenceTables(sceneProperties);

+ 25 - 17
gameplay/src/Terrain.cpp

@@ -96,8 +96,8 @@ Terrain* Terrain::create(const char* path, Properties* properties)
         if (pHeightmap)
         {
             // Read heightmap path
-            const char* heightmap = pHeightmap->getString("path");
-            if (strlen(heightmap) == 0)
+            std::string heightmap;
+            if (!pHeightmap->getPath("path", &heightmap))
             {
                 GP_WARN("No 'path' property supplied in heightmap section of terrain definition: %s", path);
                 if (!externalProperties)
@@ -105,11 +105,11 @@ Terrain* Terrain::create(const char* path, Properties* properties)
                 return NULL;
             }
 
-            std::string ext = FileSystem::getExtension(heightmap);
+            std::string ext = FileSystem::getExtension(heightmap.c_str());
             if (ext == ".PNG")
             {
                 // Read normalized height values from heightmap image
-                heightfield = HeightField::createFromImage(heightmap, 0, 1);
+                heightfield = HeightField::createFromImage(heightmap.c_str(), 0, 1);
             }
             else if (ext == ".RAW" || ext == ".R16")
             {
@@ -124,12 +124,12 @@ Terrain* Terrain::create(const char* path, Properties* properties)
                 }
 
                 // Read normalized height values from RAW file
-                heightfield = HeightField::createFromRAW(heightmap, (unsigned int)imageSize.x, (unsigned int)imageSize.y, 0, 1);
+                heightfield = HeightField::createFromRAW(heightmap.c_str(), (unsigned int)imageSize.x, (unsigned int)imageSize.y, 0, 1);
             }
             else
             {
                 // Unsupported heightmap format
-                GP_WARN("Unsupported heightmap format ('%s') in terrain definition: %s", heightmap, path);
+                GP_WARN("Unsupported heightmap format ('%s') in terrain definition: %s", heightmap.c_str(), path);
                 if (!externalProperties)
                     SAFE_DELETE(p);
                 return NULL;
@@ -138,8 +138,8 @@ Terrain* Terrain::create(const char* path, Properties* properties)
         else
         {
             // Try to read 'heightmap' as a simple string property
-            const char* heightmap = pTerrain->getString("heightmap");
-            if (heightmap == NULL || strlen(heightmap) == 0)
+            std::string heightmap;
+            if (!pTerrain->getPath("heightmap", &heightmap))
             {
                 GP_WARN("No 'heightmap' property supplied in terrain definition: %s", path);
                 if (!externalProperties)
@@ -147,11 +147,11 @@ Terrain* Terrain::create(const char* path, Properties* properties)
                 return NULL;
             }
 
-            std::string ext = FileSystem::getExtension(heightmap);
+            std::string ext = FileSystem::getExtension(heightmap.c_str());
             if (ext == ".PNG")
             {
                 // Read normalized height values from heightmap image
-                heightfield = HeightField::createFromImage(heightmap, 0, 1);
+                heightfield = HeightField::createFromImage(heightmap.c_str(), 0, 1);
             }
             else if (ext == ".RAW" || ext == ".R16")
             {
@@ -162,7 +162,7 @@ Terrain* Terrain::create(const char* path, Properties* properties)
             }
             else
             {
-                GP_WARN("Unsupported 'heightmap' format ('%s') in terrain definition: %s.", heightmap, path);
+                GP_WARN("Unsupported 'heightmap' format ('%s') in terrain definition: %s.", heightmap.c_str(), path);
                 if (!externalProperties)
                     SAFE_DELETE(p);
                 return NULL;
@@ -301,8 +301,10 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale, unsigne
                 else
                     ++index;
 
-                const char* textureMap = NULL;
-                const char* blendMap = NULL;
+                std::string textureMap;
+                const char* textureMapPtr = NULL;
+                std::string blendMap;
+                const char* blendMapPtr = NULL;
                 Vector2 textureRepeat;
                 int blendChannel = 0;
                 int row = -1, column = -1;
@@ -312,7 +314,10 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale, unsigne
                 Properties* t = lp->getNamespace("texture", true);
                 if (t)
                 {
-                    textureMap = t->getString("path");
+                    if (t->getPath("path", &textureMap))
+                    {
+                        textureMapPtr = textureMap.c_str();
+                    }
                     if (!t->getVector2("repeat", &textureRepeat))
                         textureRepeat.set(1,1);
                 }
@@ -320,7 +325,10 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale, unsigne
                 Properties* b = lp->getNamespace("blend", true);
                 if (b)
                 {
-                    blendMap = b->getString("path");
+                    if (b->getPath("path", &blendMap))
+                    {
+                        blendMapPtr = blendMap.c_str();
+                    }
                     const char* channel = b->getString("channel");
                     if (channel && strlen(channel) > 0)
                     {
@@ -342,9 +350,9 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale, unsigne
                 if (lp->exists("column"))
                     column = lp->getInt("column");
 
-                if (!terrain->setLayer(index, textureMap, textureRepeat, blendMap, blendChannel, row, column))
+                if (!terrain->setLayer(index, textureMapPtr, textureRepeat, blendMapPtr, blendChannel, row, column))
                 {
-                    GP_WARN("Failed to load terrain layer: %s", textureMap);
+                    GP_WARN("Failed to load terrain layer: %s", textureMap.c_str());
                 }
             }
         }