| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- #include "Base.h"
- #include "Material.h"
- #include "FileSystem.h"
- #include "Effect.h"
- #include "Technique.h"
- #include "Pass.h"
- #include "Properties.h"
- #include "Node.h"
- namespace gameplay
- {
- Material::Material() :
- _currentTechnique(NULL)
- {
- }
- Material::~Material()
- {
- // Destroy all the techniques.
- for (size_t i = 0, count = _techniques.size(); i < count; ++i)
- {
- Technique* technique = _techniques[i];
- SAFE_RELEASE(technique);
- }
- }
- Material* Material::create(const char* url)
- {
- // Load the material properties from file.
- Properties* properties = Properties::create(url);
- if (properties == NULL)
- {
- GP_ERROR("Failed to create material from file.");
- return NULL;
- }
- Material* material = create((strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace());
- SAFE_DELETE(properties);
- return material;
- }
- Material* Material::create(Properties* materialProperties)
- {
- // Check if the Properties is valid and has a valid namespace.
- if (!materialProperties || !(strcmp(materialProperties->getNamespace(), "material") == 0))
- {
- GP_ERROR("Properties object must be non-null and have namespace equal to 'material'.");
- return NULL;
- }
- // Create new material from the file passed in.
- Material* material = new Material();
- // Go through all the material properties and create techniques under this material.
- Properties* techniqueProperties = NULL;
- while ((techniqueProperties = materialProperties->getNextNamespace()))
- {
- if (strcmp(techniqueProperties->getNamespace(), "technique") == 0)
- {
- if (!loadTechnique(material, techniqueProperties))
- {
- GP_ERROR("Failed to load technique for material.");
- SAFE_RELEASE(material);
- return NULL;
- }
- }
- }
- // Load uniform value parameters for this material.
- loadRenderState(material, materialProperties);
- // Set the current technique to the first found technique.
- if (material->getTechniqueCount() > 0)
- {
- Technique* t = material->getTechniqueByIndex(0);
- if (t)
- {
- material->_currentTechnique = t;
- }
- }
- return material;
- }
- Material* Material::create(Effect* effect)
- {
- GP_ASSERT(effect);
- // Create a new material with a single technique and pass for the given effect.
- Material* material = new Material();
- Technique* technique = new Technique(NULL, material);
- material->_techniques.push_back(technique);
- Pass* pass = new Pass(NULL, technique, effect);
- technique->_passes.push_back(pass);
- effect->addRef();
- material->_currentTechnique = technique;
- return material;
- }
- Material* Material::create(const char* vshPath, const char* fshPath, const char* defines)
- {
- // Create a new material with a single technique and pass for the given effect
- Material* material = new Material();
- Technique* technique = new Technique(NULL, material);
- material->_techniques.push_back(technique);
- Pass* pass = Pass::create(NULL, technique, vshPath, fshPath, defines);
- if (!pass)
- {
- GP_ERROR("Failed to create pass for material.");
- SAFE_RELEASE(material);
- return NULL;
- }
- technique->_passes.push_back(pass);
- material->_currentTechnique = technique;
- return material;
- }
- unsigned int Material::getTechniqueCount() const
- {
- return (unsigned int)_techniques.size();
- }
- Technique* Material::getTechniqueByIndex(unsigned int index) const
- {
- GP_ASSERT(index < _techniques.size());
- return _techniques[index];
- }
- Technique* Material::getTechnique(const char* id) const
- {
- GP_ASSERT(id);
- for (size_t i = 0, count = _techniques.size(); i < count; ++i)
- {
- Technique* t = _techniques[i];
- GP_ASSERT(t);
- if (strcmp(t->getId(), id) == 0)
- {
- return t;
- }
- }
- return NULL;
- }
- Technique* Material::getTechnique() const
- {
- return _currentTechnique;
- }
- void Material::setTechnique(const char* id)
- {
- Technique* t = getTechnique(id);
- if (t)
- {
- _currentTechnique = t;
- }
- }
- Material* Material::clone(NodeCloneContext &context) const
- {
- Material* material = new Material();
- RenderState::cloneInto(material, context);
- for (std::vector<Technique*>::const_iterator it = _techniques.begin(); it != _techniques.end(); ++it)
- {
- const Technique* technique = *it;
- GP_ASSERT(technique);
- Technique* techniqueClone = technique->clone(material, context);
- material->_techniques.push_back(techniqueClone);
- if (_currentTechnique == technique)
- {
- material->_currentTechnique = techniqueClone;
- }
- }
- return material;
- }
- bool Material::loadTechnique(Material* material, Properties* techniqueProperties)
- {
- GP_ASSERT(material);
- GP_ASSERT(techniqueProperties);
- // Create a new technique.
- Technique* technique = new Technique(techniqueProperties->getId(), material);
- // Go through all the properties and create passes under this technique.
- techniqueProperties->rewind();
- Properties* passProperties = NULL;
- while ((passProperties = techniqueProperties->getNextNamespace()))
- {
- if (strcmp(passProperties->getNamespace(), "pass") == 0)
- {
- // Create and load passes.
- if (!loadPass(technique, passProperties))
- {
- GP_ERROR("Failed to create pass for technique.");
- SAFE_RELEASE(technique);
- return false;
- }
- }
- }
- // Load uniform value parameters for this technique.
- loadRenderState(technique, techniqueProperties);
- // Add the new technique to the material.
- material->_techniques.push_back(technique);
- return true;
- }
- bool Material::loadPass(Technique* technique, Properties* passProperties)
- {
- GP_ASSERT(passProperties);
- GP_ASSERT(technique);
- // Fetch shader info required to create the effect of this technique.
- const char* vertexShaderPath = passProperties->getString("vertexShader");
- GP_ASSERT(vertexShaderPath);
- const char* fragmentShaderPath = passProperties->getString("fragmentShader");
- GP_ASSERT(fragmentShaderPath);
- const char* defines = passProperties->getString("defines");
- // Create the pass.
- Pass* pass = Pass::create(passProperties->getId(), technique, vertexShaderPath, fragmentShaderPath, defines);
- if (!pass)
- {
- GP_ERROR("Failed to create pass for technique.");
- return false;
- }
- // Load render state.
- loadRenderState(pass, passProperties);
- // Add the new pass to the technique.
- technique->_passes.push_back(pass);
- return true;
- }
- static bool isMaterialKeyword(const char* str)
- {
- GP_ASSERT(str);
- #define MATERIAL_KEYWORD_COUNT 3
- static const char* reservedKeywords[MATERIAL_KEYWORD_COUNT] =
- {
- "vertexShader",
- "fragmentShader",
- "defines"
- };
- for (unsigned int i = 0; i < MATERIAL_KEYWORD_COUNT; ++i)
- {
- if (strcmp(reservedKeywords[i], str) == 0)
- {
- return true;
- }
- }
- return false;
- }
- static Texture::Filter parseTextureFilterMode(const char* str, Texture::Filter defaultValue)
- {
- if (str == NULL || strlen(str) == 0)
- {
- GP_ERROR("Texture filter mode string must be non-null and non-empty.");
- return defaultValue;
- }
- else if (strcmp(str, "NEAREST") == 0)
- {
- return Texture::NEAREST;
- }
- else if (strcmp(str, "LINEAR") == 0)
- {
- return Texture::LINEAR;
- }
- else if (strcmp(str, "NEAREST_MIPMAP_NEAREST") == 0)
- {
- return Texture::NEAREST_MIPMAP_NEAREST;
- }
- else if (strcmp(str, "LINEAR_MIPMAP_NEAREST") == 0)
- {
- return Texture::LINEAR_MIPMAP_NEAREST;
- }
- else if (strcmp(str, "NEAREST_MIPMAP_LINEAR") == 0)
- {
- return Texture::NEAREST_MIPMAP_LINEAR;
- }
- else if (strcmp(str, "LINEAR_MIPMAP_LINEAR") == 0)
- {
- return Texture::LINEAR_MIPMAP_LINEAR;
- }
- else
- {
- GP_ERROR("Unsupported texture filter mode string ('%s').", str);
- return defaultValue;
- }
- }
- static Texture::Wrap parseTextureWrapMode(const char* str, Texture::Wrap defaultValue)
- {
- if (str == NULL || strlen(str) == 0)
- {
- GP_ERROR("Texture wrap mode string must be non-null and non-empty.");
- return defaultValue;
- }
- else if (strcmp(str, "REPEAT") == 0)
- {
- return Texture::REPEAT;
- }
- else if (strcmp(str, "CLAMP") == 0)
- {
- return Texture::CLAMP;
- }
- else
- {
- GP_ERROR("Unsupported texture wrap mode string ('%s').", str);
- return defaultValue;
- }
- }
- void Material::loadRenderState(RenderState* renderState, Properties* properties)
- {
- GP_ASSERT(renderState);
- GP_ASSERT(properties);
- // Rewind the properties to start reading from the start.
- properties->rewind();
- const char* name;
- while ((name = properties->getNextProperty()))
- {
- if (isMaterialKeyword(name))
- continue; // keyword - skip
- switch (properties->getType())
- {
- case Properties::NUMBER:
- GP_ASSERT(renderState->getParameter(name));
- renderState->getParameter(name)->setValue(properties->getFloat());
- break;
- case Properties::VECTOR2:
- {
- Vector2 vector2;
- if (properties->getVector2(NULL, &vector2))
- {
- GP_ASSERT(renderState->getParameter(name));
- renderState->getParameter(name)->setValue(vector2);
- }
- }
- break;
- case Properties::VECTOR3:
- {
- Vector3 vector3;
- if (properties->getVector3(NULL, &vector3))
- {
- GP_ASSERT(renderState->getParameter(name));
- renderState->getParameter(name)->setValue(vector3);
- }
- }
- break;
- case Properties::VECTOR4:
- {
- Vector4 vector4;
- if (properties->getVector4(NULL, &vector4))
- {
- GP_ASSERT(renderState->getParameter(name));
- renderState->getParameter(name)->setValue(vector4);
- }
- }
- break;
- case Properties::MATRIX:
- {
- Matrix matrix;
- if (properties->getMatrix(NULL, &matrix))
- {
- GP_ASSERT(renderState->getParameter(name));
- renderState->getParameter(name)->setValue(matrix);
- }
- }
- break;
- default:
- {
- // Assume this is a parameter auto-binding.
- renderState->setParameterAutoBinding(name, properties->getString());
- }
- break;
- }
- }
- // Iterate through all child namespaces searching for samplers and render state blocks.
- Properties* ns;
- while ((ns = properties->getNextNamespace()))
- {
- if (strcmp(ns->getNamespace(), "sampler") == 0)
- {
- // Read the texture uniform name.
- name = ns->getId();
- if (strlen(name) == 0)
- {
- GP_ERROR("Texture sampler is missing required uniform name.");
- continue;
- }
- // Get the texture path.
- const char* path = ns->getString("path");
- if (path == NULL || strlen(path) == 0)
- {
- GP_ERROR("Texture sampler '%s' is missing required image file path.", name);
- continue;
- }
- // Read texture state (booleans default to 'false' if not present).
- bool mipmap = ns->getBool("mipmap");
- Texture::Wrap wrapS = parseTextureWrapMode(ns->getString("wrapS"), Texture::REPEAT);
- Texture::Wrap wrapT = parseTextureWrapMode(ns->getString("wrapT"), Texture::REPEAT);
- Texture::Filter minFilter = parseTextureFilterMode(ns->getString("minFilter"), mipmap ? Texture::NEAREST_MIPMAP_LINEAR : Texture::LINEAR);
- Texture::Filter magFilter = parseTextureFilterMode(ns->getString("magFilter"), Texture::LINEAR);
- // Set the sampler parameter.
- GP_ASSERT(renderState->getParameter(name));
- Texture::Sampler* sampler = renderState->getParameter(name)->setValue(path, mipmap);
- if (sampler)
- {
- sampler->setWrapMode(wrapS, wrapT);
- sampler->setFilterMode(minFilter, magFilter);
- }
- }
- else if (strcmp(ns->getNamespace(), "renderState") == 0)
- {
- while ((name = ns->getNextProperty()))
- {
- GP_ASSERT(renderState->getStateBlock());
- renderState->getStateBlock()->setState(name, ns->getString());
- }
- }
- }
- }
- }
|