CCMaterial.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. /****************************************************************************
  2. Copyright (c) 2015-2016 Chukong Technologies Inc.
  3. Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
  4. http://www.cocos2d-x.org
  5. Permission is hereby granted, free of charge, to any person obtaining a copy
  6. of this software and associated documentation files (the "Software"), to deal
  7. in the Software without restriction, including without limitation the rights
  8. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. copies of the Software, and to permit persons to whom the Software is
  10. furnished to do so, subject to the following conditions:
  11. The above copyright notice and this permission notice shall be included in
  12. all copies or substantial portions of the Software.
  13. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. THE SOFTWARE.
  20. Ideas taken from:
  21. - GamePlay3D: http://gameplay3d.org/
  22. - OGRE3D: http://www.ogre3d.org/
  23. - Qt3D: http://qt-project.org/
  24. ****************************************************************************/
  25. #include "renderer/CCMaterial.h"
  26. #include "renderer/CCTechnique.h"
  27. #include "renderer/CCPass.h"
  28. #include "renderer/CCTextureCache.h"
  29. #include "renderer/CCTexture2D.h"
  30. #include "renderer/CCGLProgram.h"
  31. #include "renderer/CCGLProgramState.h"
  32. #include "base/CCProperties.h"
  33. #include "base/CCDirector.h"
  34. #include "platform/CCFileUtils.h"
  35. #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
  36. #define strcasecmp _stricmp
  37. #endif
  38. NS_CC_BEGIN
  39. // Helpers declaration
  40. static const char* getOptionalString(Properties* properties, const char* key, const char* defaultValue);
  41. static bool isValidUniform(const char* name);
  42. Material* Material::createWithFilename(const std::string& filepath)
  43. {
  44. auto validfilename = FileUtils::getInstance()->fullPathForFilename(filepath);
  45. if (validfilename.size() > 0) {
  46. auto mat = new (std::nothrow) Material();
  47. if (mat && mat->initWithFile(validfilename))
  48. {
  49. mat->autorelease();
  50. return mat;
  51. }
  52. }
  53. return nullptr;
  54. }
  55. Material* Material::createWithProperties(Properties* materialProperties)
  56. {
  57. auto mat = new (std::nothrow) Material();
  58. if (mat && mat->initWithProperties(materialProperties))
  59. {
  60. mat->autorelease();
  61. return mat;
  62. }
  63. return nullptr;
  64. }
  65. Material* Material::createWithGLStateProgram(GLProgramState* programState)
  66. {
  67. CCASSERT(programState, "Invalid GL Program State");
  68. auto mat = new (std::nothrow) Material();
  69. if (mat && mat->initWithGLProgramState(programState))
  70. {
  71. mat->autorelease();
  72. return mat;
  73. }
  74. return nullptr;
  75. }
  76. bool Material::initWithGLProgramState(cocos2d::GLProgramState *state)
  77. {
  78. auto technique = Technique::createWithGLProgramState(this, state);
  79. if (technique) {
  80. _techniques.pushBack(technique);
  81. // weak pointer
  82. _currentTechnique = technique;
  83. return true;
  84. }
  85. return false;
  86. }
  87. bool Material::initWithFile(const std::string& validfilename)
  88. {
  89. // Warning: properties is not a "Ref" object, must be manually deleted
  90. Properties* properties = Properties::createNonRefCounted(validfilename);
  91. // get the first material
  92. parseProperties((strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace());
  93. CC_SAFE_DELETE(properties);
  94. return true;
  95. }
  96. bool Material::initWithProperties(Properties* materialProperties)
  97. {
  98. return parseProperties(materialProperties);
  99. }
  100. void Material::setTarget(cocos2d::Node *target)
  101. {
  102. _target = target;
  103. }
  104. bool Material::parseProperties(Properties* materialProperties)
  105. {
  106. setName(materialProperties->getId());
  107. auto space = materialProperties->getNextNamespace();
  108. while (space)
  109. {
  110. const char* name = space->getNamespace();
  111. if (strcmp(name, "technique") == 0)
  112. {
  113. parseTechnique(space);
  114. }
  115. else if (strcmp(name, "renderState") == 0)
  116. {
  117. parseRenderState(this, space);
  118. }
  119. space = materialProperties->getNextNamespace();
  120. }
  121. return true;
  122. }
  123. bool Material::parseTechnique(Properties* techniqueProperties)
  124. {
  125. auto technique = Technique::create(this);
  126. _techniques.pushBack(technique);
  127. // first one is the default one
  128. if (!_currentTechnique)
  129. _currentTechnique = technique;
  130. // name
  131. technique->setName(techniqueProperties->getId());
  132. // passes
  133. auto space = techniqueProperties->getNextNamespace();
  134. while (space)
  135. {
  136. const char* name = space->getNamespace();
  137. if (strcmp(name, "pass") == 0)
  138. {
  139. parsePass(technique, space);
  140. }
  141. else if (strcmp(name, "renderState") == 0)
  142. {
  143. parseRenderState(this, space);
  144. }
  145. space = techniqueProperties->getNextNamespace();
  146. }
  147. return true;
  148. }
  149. bool Material::parsePass(Technique* technique, Properties* passProperties)
  150. {
  151. auto pass = Pass::create(technique);
  152. technique->addPass(pass);
  153. // Pass can have 3 different namespaces:
  154. // - one or more "sampler"
  155. // - one "renderState"
  156. // - one "shader"
  157. auto space = passProperties->getNextNamespace();
  158. while (space)
  159. {
  160. const char* name = space->getNamespace();
  161. if (strcmp(name, "shader") == 0)
  162. parseShader(pass, space);
  163. else if (strcmp(name, "renderState") == 0)
  164. parseRenderState(pass, space);
  165. else {
  166. CCASSERT(false, "Invalid namespace");
  167. return false;
  168. }
  169. space = passProperties->getNextNamespace();
  170. }
  171. return true;
  172. }
  173. // cocos2d-x doesn't support Samplers yet. But will be added soon
  174. bool Material::parseSampler(GLProgramState* glProgramState, Properties* samplerProperties)
  175. {
  176. CCASSERT(samplerProperties->getId(), "Sampler must have an id. The id is the uniform name");
  177. // required
  178. auto filename = samplerProperties->getString("path");
  179. auto texture = Director::getInstance()->getTextureCache()->addImage(filename);
  180. if (!texture) {
  181. CCLOG("Invalid filepath");
  182. return false;
  183. }
  184. // optionals
  185. {
  186. Texture2D::TexParams texParams;
  187. // mipmap
  188. bool usemipmap = false;
  189. const char* mipmap = getOptionalString(samplerProperties, "mipmap", "false");
  190. if (mipmap && strcasecmp(mipmap, "true")==0) {
  191. texture->generateMipmap();
  192. usemipmap = true;
  193. }
  194. // valid options: REPEAT, CLAMP
  195. const char* wrapS = getOptionalString(samplerProperties, "wrapS", "CLAMP_TO_EDGE");
  196. if (strcasecmp(wrapS, "REPEAT")==0)
  197. texParams.wrapS = GL_REPEAT;
  198. else if(strcasecmp(wrapS, "CLAMP_TO_EDGE")==0)
  199. texParams.wrapS = GL_CLAMP_TO_EDGE;
  200. else
  201. CCLOG("Invalid wrapS: %s", wrapS);
  202. // valid options: REPEAT, CLAMP
  203. const char* wrapT = getOptionalString(samplerProperties, "wrapT", "CLAMP_TO_EDGE");
  204. if (strcasecmp(wrapT, "REPEAT")==0)
  205. texParams.wrapT = GL_REPEAT;
  206. else if(strcasecmp(wrapT, "CLAMP_TO_EDGE")==0)
  207. texParams.wrapT = GL_CLAMP_TO_EDGE;
  208. else
  209. CCLOG("Invalid wrapT: %s", wrapT);
  210. // valid options: NEAREST, LINEAR, NEAREST_MIPMAP_NEAREST, LINEAR_MIPMAP_NEAREST, NEAREST_MIPMAP_LINEAR, LINEAR_MIPMAP_LINEAR
  211. const char* minFilter = getOptionalString(samplerProperties, "minFilter", usemipmap ? "LINEAR_MIPMAP_NEAREST" : "LINEAR");
  212. if (strcasecmp(minFilter, "NEAREST")==0)
  213. texParams.minFilter = GL_NEAREST;
  214. else if(strcasecmp(minFilter, "LINEAR")==0)
  215. texParams.minFilter = GL_LINEAR;
  216. else if(strcasecmp(minFilter, "NEAREST_MIPMAP_NEAREST")==0)
  217. texParams.minFilter = GL_NEAREST_MIPMAP_NEAREST;
  218. else if(strcasecmp(minFilter, "LINEAR_MIPMAP_NEAREST")==0)
  219. texParams.minFilter = GL_LINEAR_MIPMAP_NEAREST;
  220. else if(strcasecmp(minFilter, "NEAREST_MIPMAP_LINEAR")==0)
  221. texParams.minFilter = GL_NEAREST_MIPMAP_LINEAR;
  222. else if(strcasecmp(minFilter, "LINEAR_MIPMAP_LINEAR")==0)
  223. texParams.minFilter = GL_LINEAR_MIPMAP_LINEAR;
  224. else
  225. CCLOG("Invalid minFilter: %s", minFilter);
  226. // valid options: NEAREST, LINEAR
  227. const char* magFilter = getOptionalString(samplerProperties, "magFilter", "LINEAR");
  228. if (strcasecmp(magFilter, "NEAREST")==0)
  229. texParams.magFilter = GL_NEAREST;
  230. else if(strcasecmp(magFilter, "LINEAR")==0)
  231. texParams.magFilter = GL_LINEAR;
  232. else
  233. CCLOG("Invalid magFilter: %s", magFilter);
  234. texture->setTexParameters(texParams);
  235. }
  236. glProgramState->setUniformTexture(samplerProperties->getId(), texture);
  237. return true;
  238. }
  239. bool Material::parseShader(Pass* pass, Properties* shaderProperties)
  240. {
  241. // vertexShader
  242. const char* vertShader = getOptionalString(shaderProperties, "vertexShader", nullptr);
  243. // fragmentShader
  244. const char* fragShader = getOptionalString(shaderProperties, "fragmentShader", nullptr);
  245. // compileTimeDefines
  246. const char* compileTimeDefines = getOptionalString(shaderProperties, "defines", "");
  247. if (vertShader && fragShader)
  248. {
  249. auto glProgramState = GLProgramState::getOrCreateWithShaders(vertShader, fragShader, compileTimeDefines);
  250. pass->setGLProgramState(glProgramState);
  251. // Parse uniforms only if the GLProgramState was created
  252. auto property = shaderProperties->getNextProperty();
  253. while (property)
  254. {
  255. if (isValidUniform(property))
  256. {
  257. parseUniform(glProgramState, shaderProperties, property);
  258. }
  259. property = shaderProperties->getNextProperty();
  260. }
  261. auto space = shaderProperties->getNextNamespace();
  262. while (space)
  263. {
  264. const char* name = space->getNamespace();
  265. if (strcmp(name, "sampler") == 0)
  266. {
  267. parseSampler(glProgramState, space);
  268. }
  269. space = shaderProperties->getNextNamespace();
  270. }
  271. }
  272. return true;
  273. }
  274. bool Material::parseUniform(GLProgramState* programState, Properties* properties, const char* uniformName)
  275. {
  276. bool ret = true;
  277. auto type = properties->getType(uniformName);
  278. switch (type) {
  279. case Properties::Type::NUMBER:
  280. {
  281. auto f = properties->getFloat(uniformName);
  282. programState->setUniformFloat(uniformName, f);
  283. break;
  284. }
  285. case Properties::Type::VECTOR2:
  286. {
  287. Vec2 v2;
  288. properties->getVec2(uniformName, &v2);
  289. programState->setUniformVec2(uniformName, v2);
  290. break;
  291. }
  292. case Properties::Type::VECTOR3:
  293. {
  294. Vec3 v3;
  295. properties->getVec3(uniformName, &v3);
  296. programState->setUniformVec3(uniformName, v3);
  297. break;
  298. }
  299. case Properties::Type::VECTOR4:
  300. {
  301. Vec4 v4;
  302. properties->getVec4(uniformName, &v4);
  303. programState->setUniformVec4(uniformName, v4);
  304. break;
  305. }
  306. case Properties::Type::MATRIX:
  307. {
  308. Mat4 m4;
  309. properties->getMat4(uniformName, &m4);
  310. programState->setUniformMat4(uniformName, m4);
  311. break;
  312. }
  313. case Properties::Type::STRING:
  314. default:
  315. {
  316. // Assume this is a parameter auto-binding.
  317. programState->setParameterAutoBinding(uniformName, properties->getString());
  318. break;
  319. }
  320. }
  321. return ret;
  322. }
  323. bool Material::parseRenderState(RenderState* renderState, Properties* properties)
  324. {
  325. auto state = renderState->getStateBlock();
  326. auto property = properties->getNextProperty();
  327. while (property)
  328. {
  329. // Parse uniforms only if the GLProgramState was created
  330. // Render state only can have "strings" or numbers as values. No new namespaces
  331. state->setState(property, properties->getString(property));
  332. property = properties->getNextProperty();
  333. }
  334. return true;
  335. }
  336. void Material::setName(const std::string&name)
  337. {
  338. _name = name;
  339. }
  340. std::string Material::getName() const
  341. {
  342. return _name;
  343. }
  344. Material::Material()
  345. : _name("")
  346. , _currentTechnique(nullptr)
  347. , _target(nullptr)
  348. {
  349. }
  350. Material::~Material()
  351. {
  352. }
  353. Material* Material::clone() const
  354. {
  355. auto material = new (std::nothrow) Material();
  356. if (material)
  357. {
  358. RenderState::cloneInto(material);
  359. for (const auto& technique: _techniques)
  360. {
  361. auto t = technique->clone();
  362. t->_parent = material;
  363. material->_techniques.pushBack(t);
  364. }
  365. // current technique
  366. auto name = _currentTechnique->getName();
  367. material->_currentTechnique = material->getTechniqueByName(name);
  368. material->autorelease();
  369. }
  370. return material;
  371. }
  372. Technique* Material::getTechnique() const
  373. {
  374. return _currentTechnique;
  375. }
  376. const Vector<Technique*>& Material::getTechniques() const
  377. {
  378. return _techniques;
  379. }
  380. Technique* Material::getTechniqueByName(const std::string& name)
  381. {
  382. for(const auto& technique : _techniques) {
  383. if (technique->getName().compare(name)==0)
  384. return technique;
  385. }
  386. return nullptr;
  387. }
  388. Technique* Material::getTechniqueByIndex(ssize_t index)
  389. {
  390. CC_ASSERT(index>=0 && index<_techniques.size() && "Invalid size");
  391. return _techniques.at(index);
  392. }
  393. void Material::addTechnique(Technique* technique)
  394. {
  395. _techniques.pushBack(technique);
  396. }
  397. void Material::setTechnique(const std::string& techniqueName)
  398. {
  399. auto technique = getTechniqueByName(techniqueName);
  400. if (technique)
  401. _currentTechnique = technique;
  402. }
  403. ssize_t Material::getTechniqueCount() const
  404. {
  405. return _techniques.size();
  406. }
  407. // Helpers implementation
  408. static bool isValidUniform(const char* name)
  409. {
  410. return !(strcmp(name, "defines")==0 ||
  411. strcmp(name, "vertexShader")==0 ||
  412. strcmp(name, "fragmentShader")==0);
  413. }
  414. static const char* getOptionalString(Properties* properties, const char* key, const char* defaultValue)
  415. {
  416. const char* ret = properties->getString(key);
  417. if (!ret)
  418. ret = defaultValue;
  419. return ret;
  420. }
  421. NS_CC_END