123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- /****************************************************************************
- Copyright (c) 2015-2016 Chukong Technologies Inc.
- Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
- http://www.cocos2d-x.org
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- Ideas taken from:
- - GamePlay3D: http://gameplay3d.org/
- - OGRE3D: http://www.ogre3d.org/
- - Qt3D: http://qt-project.org/
- ****************************************************************************/
- #include "renderer/CCMaterial.h"
- #include "renderer/CCTechnique.h"
- #include "renderer/CCPass.h"
- #include "renderer/CCTextureCache.h"
- #include "renderer/CCTexture2D.h"
- #include "renderer/CCGLProgram.h"
- #include "renderer/CCGLProgramState.h"
- #include "base/CCProperties.h"
- #include "base/CCDirector.h"
- #include "platform/CCFileUtils.h"
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
- #define strcasecmp _stricmp
- #endif
- NS_CC_BEGIN
- // Helpers declaration
- static const char* getOptionalString(Properties* properties, const char* key, const char* defaultValue);
- static bool isValidUniform(const char* name);
- Material* Material::createWithFilename(const std::string& filepath)
- {
- auto validfilename = FileUtils::getInstance()->fullPathForFilename(filepath);
- if (validfilename.size() > 0) {
- auto mat = new (std::nothrow) Material();
- if (mat && mat->initWithFile(validfilename))
- {
- mat->autorelease();
- return mat;
- }
- }
- return nullptr;
- }
- Material* Material::createWithProperties(Properties* materialProperties)
- {
- auto mat = new (std::nothrow) Material();
- if (mat && mat->initWithProperties(materialProperties))
- {
- mat->autorelease();
- return mat;
- }
- return nullptr;
- }
- Material* Material::createWithGLStateProgram(GLProgramState* programState)
- {
- CCASSERT(programState, "Invalid GL Program State");
- auto mat = new (std::nothrow) Material();
- if (mat && mat->initWithGLProgramState(programState))
- {
- mat->autorelease();
- return mat;
- }
- return nullptr;
- }
- bool Material::initWithGLProgramState(cocos2d::GLProgramState *state)
- {
- auto technique = Technique::createWithGLProgramState(this, state);
- if (technique) {
- _techniques.pushBack(technique);
- // weak pointer
- _currentTechnique = technique;
- return true;
- }
- return false;
- }
- bool Material::initWithFile(const std::string& validfilename)
- {
- // Warning: properties is not a "Ref" object, must be manually deleted
- Properties* properties = Properties::createNonRefCounted(validfilename);
- // get the first material
- parseProperties((strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace());
- CC_SAFE_DELETE(properties);
- return true;
- }
- bool Material::initWithProperties(Properties* materialProperties)
- {
- return parseProperties(materialProperties);
- }
- void Material::setTarget(cocos2d::Node *target)
- {
- _target = target;
- }
- bool Material::parseProperties(Properties* materialProperties)
- {
- setName(materialProperties->getId());
- auto space = materialProperties->getNextNamespace();
- while (space)
- {
- const char* name = space->getNamespace();
- if (strcmp(name, "technique") == 0)
- {
- parseTechnique(space);
- }
- else if (strcmp(name, "renderState") == 0)
- {
- parseRenderState(this, space);
- }
- space = materialProperties->getNextNamespace();
- }
- return true;
- }
- bool Material::parseTechnique(Properties* techniqueProperties)
- {
- auto technique = Technique::create(this);
- _techniques.pushBack(technique);
- // first one is the default one
- if (!_currentTechnique)
- _currentTechnique = technique;
- // name
- technique->setName(techniqueProperties->getId());
- // passes
- auto space = techniqueProperties->getNextNamespace();
- while (space)
- {
- const char* name = space->getNamespace();
- if (strcmp(name, "pass") == 0)
- {
- parsePass(technique, space);
- }
- else if (strcmp(name, "renderState") == 0)
- {
- parseRenderState(this, space);
- }
- space = techniqueProperties->getNextNamespace();
- }
- return true;
- }
- bool Material::parsePass(Technique* technique, Properties* passProperties)
- {
- auto pass = Pass::create(technique);
- technique->addPass(pass);
- // Pass can have 3 different namespaces:
- // - one or more "sampler"
- // - one "renderState"
- // - one "shader"
- auto space = passProperties->getNextNamespace();
- while (space)
- {
- const char* name = space->getNamespace();
- if (strcmp(name, "shader") == 0)
- parseShader(pass, space);
- else if (strcmp(name, "renderState") == 0)
- parseRenderState(pass, space);
- else {
- CCASSERT(false, "Invalid namespace");
- return false;
- }
- space = passProperties->getNextNamespace();
- }
- return true;
- }
- // cocos2d-x doesn't support Samplers yet. But will be added soon
- bool Material::parseSampler(GLProgramState* glProgramState, Properties* samplerProperties)
- {
- CCASSERT(samplerProperties->getId(), "Sampler must have an id. The id is the uniform name");
-
- // required
- auto filename = samplerProperties->getString("path");
- auto texture = Director::getInstance()->getTextureCache()->addImage(filename);
- if (!texture) {
- CCLOG("Invalid filepath");
- return false;
- }
- // optionals
- {
- Texture2D::TexParams texParams;
- // mipmap
- bool usemipmap = false;
- const char* mipmap = getOptionalString(samplerProperties, "mipmap", "false");
- if (mipmap && strcasecmp(mipmap, "true")==0) {
- texture->generateMipmap();
- usemipmap = true;
- }
- // valid options: REPEAT, CLAMP
- const char* wrapS = getOptionalString(samplerProperties, "wrapS", "CLAMP_TO_EDGE");
- if (strcasecmp(wrapS, "REPEAT")==0)
- texParams.wrapS = GL_REPEAT;
- else if(strcasecmp(wrapS, "CLAMP_TO_EDGE")==0)
- texParams.wrapS = GL_CLAMP_TO_EDGE;
- else
- CCLOG("Invalid wrapS: %s", wrapS);
- // valid options: REPEAT, CLAMP
- const char* wrapT = getOptionalString(samplerProperties, "wrapT", "CLAMP_TO_EDGE");
- if (strcasecmp(wrapT, "REPEAT")==0)
- texParams.wrapT = GL_REPEAT;
- else if(strcasecmp(wrapT, "CLAMP_TO_EDGE")==0)
- texParams.wrapT = GL_CLAMP_TO_EDGE;
- else
- CCLOG("Invalid wrapT: %s", wrapT);
- // valid options: NEAREST, LINEAR, NEAREST_MIPMAP_NEAREST, LINEAR_MIPMAP_NEAREST, NEAREST_MIPMAP_LINEAR, LINEAR_MIPMAP_LINEAR
- const char* minFilter = getOptionalString(samplerProperties, "minFilter", usemipmap ? "LINEAR_MIPMAP_NEAREST" : "LINEAR");
- if (strcasecmp(minFilter, "NEAREST")==0)
- texParams.minFilter = GL_NEAREST;
- else if(strcasecmp(minFilter, "LINEAR")==0)
- texParams.minFilter = GL_LINEAR;
- else if(strcasecmp(minFilter, "NEAREST_MIPMAP_NEAREST")==0)
- texParams.minFilter = GL_NEAREST_MIPMAP_NEAREST;
- else if(strcasecmp(minFilter, "LINEAR_MIPMAP_NEAREST")==0)
- texParams.minFilter = GL_LINEAR_MIPMAP_NEAREST;
- else if(strcasecmp(minFilter, "NEAREST_MIPMAP_LINEAR")==0)
- texParams.minFilter = GL_NEAREST_MIPMAP_LINEAR;
- else if(strcasecmp(minFilter, "LINEAR_MIPMAP_LINEAR")==0)
- texParams.minFilter = GL_LINEAR_MIPMAP_LINEAR;
- else
- CCLOG("Invalid minFilter: %s", minFilter);
- // valid options: NEAREST, LINEAR
- const char* magFilter = getOptionalString(samplerProperties, "magFilter", "LINEAR");
- if (strcasecmp(magFilter, "NEAREST")==0)
- texParams.magFilter = GL_NEAREST;
- else if(strcasecmp(magFilter, "LINEAR")==0)
- texParams.magFilter = GL_LINEAR;
- else
- CCLOG("Invalid magFilter: %s", magFilter);
- texture->setTexParameters(texParams);
- }
- glProgramState->setUniformTexture(samplerProperties->getId(), texture);
- return true;
- }
- bool Material::parseShader(Pass* pass, Properties* shaderProperties)
- {
- // vertexShader
- const char* vertShader = getOptionalString(shaderProperties, "vertexShader", nullptr);
- // fragmentShader
- const char* fragShader = getOptionalString(shaderProperties, "fragmentShader", nullptr);
- // compileTimeDefines
- const char* compileTimeDefines = getOptionalString(shaderProperties, "defines", "");
- if (vertShader && fragShader)
- {
- auto glProgramState = GLProgramState::getOrCreateWithShaders(vertShader, fragShader, compileTimeDefines);
- pass->setGLProgramState(glProgramState);
- // Parse uniforms only if the GLProgramState was created
- auto property = shaderProperties->getNextProperty();
- while (property)
- {
- if (isValidUniform(property))
- {
- parseUniform(glProgramState, shaderProperties, property);
- }
- property = shaderProperties->getNextProperty();
- }
- auto space = shaderProperties->getNextNamespace();
- while (space)
- {
- const char* name = space->getNamespace();
- if (strcmp(name, "sampler") == 0)
- {
- parseSampler(glProgramState, space);
- }
- space = shaderProperties->getNextNamespace();
- }
- }
- return true;
- }
- bool Material::parseUniform(GLProgramState* programState, Properties* properties, const char* uniformName)
- {
- bool ret = true;
- auto type = properties->getType(uniformName);
- switch (type) {
- case Properties::Type::NUMBER:
- {
- auto f = properties->getFloat(uniformName);
- programState->setUniformFloat(uniformName, f);
- break;
- }
- case Properties::Type::VECTOR2:
- {
- Vec2 v2;
- properties->getVec2(uniformName, &v2);
- programState->setUniformVec2(uniformName, v2);
- break;
- }
- case Properties::Type::VECTOR3:
- {
- Vec3 v3;
- properties->getVec3(uniformName, &v3);
- programState->setUniformVec3(uniformName, v3);
- break;
- }
- case Properties::Type::VECTOR4:
- {
- Vec4 v4;
- properties->getVec4(uniformName, &v4);
- programState->setUniformVec4(uniformName, v4);
- break;
- }
- case Properties::Type::MATRIX:
- {
- Mat4 m4;
- properties->getMat4(uniformName, &m4);
- programState->setUniformMat4(uniformName, m4);
- break;
- }
- case Properties::Type::STRING:
- default:
- {
- // Assume this is a parameter auto-binding.
- programState->setParameterAutoBinding(uniformName, properties->getString());
- break;
- }
- }
- return ret;
- }
- bool Material::parseRenderState(RenderState* renderState, Properties* properties)
- {
- auto state = renderState->getStateBlock();
- auto property = properties->getNextProperty();
- while (property)
- {
- // Parse uniforms only if the GLProgramState was created
- // Render state only can have "strings" or numbers as values. No new namespaces
- state->setState(property, properties->getString(property));
- property = properties->getNextProperty();
- }
- return true;
- }
- void Material::setName(const std::string&name)
- {
- _name = name;
- }
- std::string Material::getName() const
- {
- return _name;
- }
- Material::Material()
- : _name("")
- , _currentTechnique(nullptr)
- , _target(nullptr)
- {
- }
- Material::~Material()
- {
- }
- Material* Material::clone() const
- {
- auto material = new (std::nothrow) Material();
- if (material)
- {
- RenderState::cloneInto(material);
- for (const auto& technique: _techniques)
- {
- auto t = technique->clone();
- t->_parent = material;
- material->_techniques.pushBack(t);
- }
- // current technique
- auto name = _currentTechnique->getName();
- material->_currentTechnique = material->getTechniqueByName(name);
- material->autorelease();
- }
- return material;
- }
- Technique* Material::getTechnique() const
- {
- return _currentTechnique;
- }
- const Vector<Technique*>& Material::getTechniques() const
- {
- return _techniques;
- }
- Technique* Material::getTechniqueByName(const std::string& name)
- {
- for(const auto& technique : _techniques) {
- if (technique->getName().compare(name)==0)
- return technique;
- }
- return nullptr;
- }
- Technique* Material::getTechniqueByIndex(ssize_t index)
- {
- CC_ASSERT(index>=0 && index<_techniques.size() && "Invalid size");
- return _techniques.at(index);
- }
- void Material::addTechnique(Technique* technique)
- {
- _techniques.pushBack(technique);
- }
- void Material::setTechnique(const std::string& techniqueName)
- {
- auto technique = getTechniqueByName(techniqueName);
- if (technique)
- _currentTechnique = technique;
- }
- ssize_t Material::getTechniqueCount() const
- {
- return _techniques.size();
- }
- // Helpers implementation
- static bool isValidUniform(const char* name)
- {
- return !(strcmp(name, "defines")==0 ||
- strcmp(name, "vertexShader")==0 ||
- strcmp(name, "fragmentShader")==0);
- }
- static const char* getOptionalString(Properties* properties, const char* key, const char* defaultValue)
- {
- const char* ret = properties->getString(key);
- if (!ret)
- ret = defaultValue;
- return ret;
- }
- NS_CC_END
|