CustomMaterial.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. //
  2. // CustomMaterial.cpp
  3. // building
  4. //
  5. // Created by yanqian on 2023/5/8.
  6. //
  7. #include "CustomMaterial.hpp"
  8. NS_CC_BEGIN
  9. // Helpers declaration
  10. static const char* getOptionalString(Properties* properties, const char* key, const char* defaultValue);
  11. static bool isValidUniform(const char* name);
  12. CustomMaterial::CustomMaterial()
  13. {
  14. _time = 0;
  15. _paramFloat.clear();
  16. _paramVec2.clear();
  17. _paramVec3.clear();
  18. _paramVec4.clear();
  19. _paramMat4.clear();
  20. _paramString.clear();
  21. }
  22. CustomMaterial::~CustomMaterial()
  23. {
  24. _paramFloat.clear();
  25. _paramVec2.clear();
  26. _paramVec3.clear();
  27. _paramVec4.clear();
  28. _paramMat4.clear();
  29. _paramString.clear();
  30. }
  31. CustomMaterial* CustomMaterial::createWithFilename(const std::string& filepath)
  32. {
  33. std::string validfilename = FileUtils::getInstance()->fullPathForFilename(filepath);
  34. if (validfilename.size() > 0) {
  35. CustomMaterial* mat = new (std::nothrow) CustomMaterial();
  36. if (mat) {
  37. mat->setRelativePath(filepath); // 设置材质文件的相对路径
  38. if (mat->initWithFile(validfilename)) {
  39. mat->autorelease();
  40. return mat;
  41. }
  42. }
  43. }
  44. return nullptr;
  45. }
  46. bool CustomMaterial::initWithFile(const std::string& validfilename)
  47. {
  48. Properties* properties = Properties::createNonRefCounted(validfilename);
  49. // 解析材质属性
  50. parseProperties((strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace());
  51. CC_SAFE_DELETE(properties);
  52. return true;
  53. }
  54. bool CustomMaterial::parseProperties(Properties* materialProperties)
  55. {
  56. setName(materialProperties->getId());
  57. Properties* space = materialProperties->getNextNamespace();
  58. while (space) { // 遍历属性对象中的命名空间
  59. const char* name = space->getNamespace();
  60. if (strcmp(name, "technique") == 0) {
  61. parseTechnique(space);
  62. } else if (strcmp(name, "renderState") == 0) {
  63. parseRenderState(this, space);
  64. }
  65. space = materialProperties->getNextNamespace();
  66. }
  67. return true;
  68. }
  69. bool CustomMaterial::parseTechnique(Properties* techniqueProperties)
  70. {
  71. Technique* technique = Technique::create(this);
  72. _techniques.pushBack(technique);
  73. if (!_currentTechnique) {
  74. _currentTechnique = technique;
  75. }
  76. technique->setName(techniqueProperties->getId()); // 设置材质名称
  77. Properties* space = techniqueProperties->getNextNamespace();
  78. while (space) { // 遍历technique属性对象中的命名空间
  79. const char* name = space->getNamespace();
  80. if (strcmp(name, "pass") == 0) {
  81. parsePass(technique, space);
  82. } else if (strcmp(name, "renderState") == 0) {
  83. parseRenderState(this, space);
  84. }
  85. space = techniqueProperties->getNextNamespace();
  86. }
  87. return true;
  88. }
  89. bool CustomMaterial::parsePass(Technique* technique, Properties* passProperties)
  90. {
  91. Pass* pass = Pass::create(technique);
  92. if (pass == nullptr) {
  93. return false;
  94. }
  95. technique->addPass(pass);
  96. Properties* space = passProperties->getNextNamespace();
  97. while (space) { // 遍历pass属性对象中的命名空间
  98. const char* name = space->getNamespace();
  99. if (strcmp(name, "shader") == 0) {
  100. parseShader(pass, space);
  101. } else if (strcmp(name, "renderState") == 0) {
  102. parseRenderState(pass, space);
  103. } else if (strcmp(name, "editorUniforms") == 0) {
  104. _parseEditorUniform(space);
  105. } else if (strcmp(name, "uniforms") == 0) {
  106. _parseUniformParams(pass->getGLProgramState(), space);
  107. } else {
  108. CCASSERT(false, "Invalid namespace");
  109. return false;
  110. }
  111. space = passProperties->getNextNamespace();
  112. }
  113. return true;
  114. }
  115. bool CustomMaterial::parseShader(Pass* pass, Properties* shaderProperties)
  116. {
  117. // vertexShader
  118. const char* vertShader = getOptionalString(shaderProperties, "vertexShader", nullptr);
  119. // fragmentShader
  120. const char* fragShader = getOptionalString(shaderProperties, "fragmentShader", nullptr);
  121. // compileTimeDefines
  122. const char* compileTimeDefines = getOptionalString(shaderProperties, "defines", "");
  123. if (vertShader && fragShader)
  124. {
  125. auto glProgramState = GLProgramState::getOrCreateWithShaders(_relativePath + vertShader, _relativePath + fragShader, compileTimeDefines);
  126. pass->setGLProgramState(glProgramState);
  127. // Parse uniforms only if the GLProgramState was created
  128. auto property = shaderProperties->getNextProperty();
  129. while (property)
  130. {
  131. if (isValidUniform(property))
  132. {
  133. parseUniform(glProgramState, shaderProperties, property);
  134. }
  135. property = shaderProperties->getNextProperty();
  136. }
  137. auto space = shaderProperties->getNextNamespace();
  138. while (space)
  139. {
  140. const char* name = space->getNamespace();
  141. if (strcmp(name, "sampler") == 0)
  142. {
  143. parseSampler(glProgramState, space);
  144. }
  145. space = shaderProperties->getNextNamespace();
  146. }
  147. }
  148. return true;
  149. }
  150. bool CustomMaterial::parseRenderState(RenderState* renderState, Properties* properties)
  151. {
  152. auto state = renderState->getStateBlock();
  153. auto property = properties->getNextProperty();
  154. while (property)
  155. {
  156. // Parse uniforms only if the GLProgramState was created
  157. // Render state only can have "strings" or numbers as values. No new namespaces
  158. state->setState(property, properties->getString(property));
  159. std::string value = properties->getString(property);
  160. if (std::string(property).compare("blend") == 0) {
  161. if (value.compare("true") == 0) {
  162. _useBlendFunc = true;
  163. }
  164. } else if (std::string(property).compare("blendSrc") == 0) {
  165. _blendFunc.src = _parseBlend(value);
  166. } else if (std::string(property).compare("blendDst") == 0) {
  167. _blendFunc.dst = _parseBlend(value);
  168. }
  169. property = properties->getNextProperty();
  170. }
  171. return true;
  172. }
  173. int CustomMaterial::_parseBlend(const std::string& value)
  174. {
  175. std::string upper(value);
  176. std::transform(upper.begin(), upper.end(), upper.begin(), (int(*)(int))toupper);
  177. if (upper == "ZERO")
  178. return GL_ZERO;
  179. else if (upper == "ONE")
  180. return GL_ONE;
  181. else if (upper == "SRC_COLOR")
  182. return GL_SRC_COLOR;
  183. else if (upper == "ONE_MINUS_SRC_COLOR")
  184. return GL_ONE_MINUS_SRC_COLOR;
  185. else if (upper == "DST_COLOR")
  186. return GL_DST_COLOR;
  187. else if (upper == "ONE_MINUS_DST_COLOR")
  188. return GL_ONE_MINUS_DST_COLOR;
  189. else if (upper == "SRC_ALPHA")
  190. return GL_SRC_ALPHA;
  191. else if (upper == "ONE_MINUS_SRC_ALPHA")
  192. return GL_ONE_MINUS_SRC_ALPHA;
  193. else if (upper == "DST_ALPHA")
  194. return GL_DST_ALPHA;
  195. else if (upper == "ONE_MINUS_DST_ALPHA")
  196. return GL_ONE_MINUS_DST_ALPHA;
  197. else if (upper == "SRC_ALPHA_SATURATE")
  198. return GL_SRC_ALPHA_SATURATE;
  199. else
  200. {
  201. CCLOG("Unsupported blend value (%s). (Will default to BLEND_ONE if errors are treated as warnings)", value.c_str());
  202. return RenderState::BLEND_ONE;
  203. }
  204. }
  205. bool CustomMaterial::parseSampler(GLProgramState* glProgramState, Properties* samplerProperties)
  206. {
  207. CCASSERT(samplerProperties->getId(), "Sampler must have an id. The id is the uniform name");
  208. if (glProgramState == nullptr || samplerProperties == nullptr) {
  209. return false;
  210. }
  211. // required
  212. bool canBeEdit = samplerProperties->getBool("editorTexture");
  213. const char* filename = samplerProperties->getString("path");
  214. filename = (filename == nullptr) ? "" : filename;
  215. std::string filePath = (canBeEdit == false) ? _relativePath + filename : filename;
  216. Texture2D* texture = nullptr;
  217. Vec2 texRectOffset, texRectSize;
  218. // 先从图集里找
  219. SpriteFrame *spriteFrame = SpriteFrameCache::getInstance()->getSpriteFrameByName(filename);
  220. if (spriteFrame != nullptr) {
  221. texture = spriteFrame->getTexture();
  222. // 计算纹理的偏移和大小
  223. Rect rect = spriteFrame->getRectInPixels();
  224. Size atlasSize = spriteFrame->getTexture()->getContentSizeInPixels();
  225. texRectOffset = Vec2(rect.origin.x / atlasSize.width, rect.origin.y / atlasSize.height);
  226. texRectSize = Vec2(rect.size.width / atlasSize.width, rect.size.height / atlasSize.height);
  227. } else {
  228. texture = Director::getInstance()->getTextureCache()->addImage(filePath);
  229. texRectOffset = Vec2(0, 0);
  230. texRectSize = Vec2(1, 1);
  231. }
  232. Texture2D::TexParams texParams;
  233. // mipmap
  234. bool usemipmap = false;
  235. const char* mipmap = getOptionalString(samplerProperties, "mipmap", "false");
  236. if (mipmap && strcasecmp(mipmap, "true")==0) {
  237. usemipmap = true;
  238. }
  239. // valid options: REPEAT, CLAMP
  240. const char* wrapS = getOptionalString(samplerProperties, "wrapS", "CLAMP_TO_EDGE");
  241. if (strcasecmp(wrapS, "REPEAT")==0)
  242. texParams.wrapS = GL_REPEAT;
  243. else if(strcasecmp(wrapS, "CLAMP_TO_EDGE")==0)
  244. texParams.wrapS = GL_CLAMP_TO_EDGE;
  245. else
  246. CCLOG("Invalid wrapS: %s", wrapS);
  247. // valid options: REPEAT, CLAMP
  248. const char* wrapT = getOptionalString(samplerProperties, "wrapT", "CLAMP_TO_EDGE");
  249. if (strcasecmp(wrapT, "REPEAT")==0)
  250. texParams.wrapT = GL_REPEAT;
  251. else if(strcasecmp(wrapT, "CLAMP_TO_EDGE")==0)
  252. texParams.wrapT = GL_CLAMP_TO_EDGE;
  253. else
  254. CCLOG("Invalid wrapT: %s", wrapT);
  255. // valid options: NEAREST, LINEAR, NEAREST_MIPMAP_NEAREST, LINEAR_MIPMAP_NEAREST, NEAREST_MIPMAP_LINEAR, LINEAR_MIPMAP_LINEAR
  256. const char* minFilter = getOptionalString(samplerProperties, "minFilter", usemipmap ? "LINEAR_MIPMAP_NEAREST" : "LINEAR");
  257. if (strcasecmp(minFilter, "NEAREST")==0)
  258. texParams.minFilter = GL_NEAREST;
  259. else if(strcasecmp(minFilter, "LINEAR")==0)
  260. texParams.minFilter = GL_LINEAR;
  261. else if(strcasecmp(minFilter, "NEAREST_MIPMAP_NEAREST")==0)
  262. texParams.minFilter = GL_NEAREST_MIPMAP_NEAREST;
  263. else if(strcasecmp(minFilter, "LINEAR_MIPMAP_NEAREST")==0)
  264. texParams.minFilter = GL_LINEAR_MIPMAP_NEAREST;
  265. else if(strcasecmp(minFilter, "NEAREST_MIPMAP_LINEAR")==0)
  266. texParams.minFilter = GL_NEAREST_MIPMAP_LINEAR;
  267. else if(strcasecmp(minFilter, "LINEAR_MIPMAP_LINEAR")==0)
  268. texParams.minFilter = GL_LINEAR_MIPMAP_LINEAR;
  269. else
  270. CCLOG("Invalid minFilter: %s", minFilter);
  271. // valid options: NEAREST, LINEAR
  272. const char* magFilter = getOptionalString(samplerProperties, "magFilter", "LINEAR");
  273. if (strcasecmp(magFilter, "NEAREST")==0)
  274. texParams.magFilter = GL_NEAREST;
  275. else if(strcasecmp(magFilter, "LINEAR")==0)
  276. texParams.magFilter = GL_LINEAR;
  277. else
  278. CCLOG("Invalid magFilter: %s", magFilter);
  279. if (texture) {
  280. if (usemipmap == true) {
  281. texture->generateMipmap();
  282. }
  283. texture->setTexParameters(texParams);
  284. glProgramState->setUniformTexture(samplerProperties->getId(), texture);
  285. glProgramState->setUniformVec2("u_texRectOffset", texRectOffset);
  286. glProgramState->setUniformVec2("u_texRectSize", texRectSize);
  287. }
  288. if (canBeEdit) {
  289. _paramTexture.push_back(UniformParamTexture(samplerProperties->getId(), texParams));
  290. }
  291. return true;
  292. }
  293. bool CustomMaterial::_parseEditorUniform(Properties* paramsProperties)
  294. {
  295. const char* property = paramsProperties->getNextProperty();
  296. while (property) {
  297. _editorUniforms[property] = paramsProperties->getBool(property);
  298. property = paramsProperties->getNextProperty();
  299. }
  300. return true;
  301. }
  302. bool CustomMaterial::_parseUniformParams(GLProgramState* programState, Properties* paramsProperties)
  303. {
  304. if (programState == nullptr || paramsProperties == nullptr) {
  305. return false;
  306. }
  307. const char* property = paramsProperties->getNextProperty();
  308. while (property) { // 遍历所有属性变量
  309. Properties::Type type = paramsProperties->getType(property);
  310. switch (type) {
  311. case Properties::Type::NUMBER: {
  312. float f = paramsProperties->getFloat(property);
  313. programState->setUniformFloat(property, f);
  314. if (_editorUniforms[property] == true) {
  315. _paramFloat.push_back(UniformParamFloat(property, f));
  316. }
  317. break;
  318. }
  319. case Properties::Type::VECTOR2: {
  320. Vec2 v2;
  321. paramsProperties->getVec2(property, &v2);
  322. programState->setUniformVec2(property, v2);
  323. if (_editorUniforms[property] == true) {
  324. _paramVec2.push_back(UniformParamVec2(property, v2));
  325. }
  326. break;
  327. }
  328. case Properties::Type::VECTOR3: {
  329. Vec3 v3;
  330. paramsProperties->getVec3(property, &v3);
  331. programState->setUniformVec3(property, v3);
  332. if (_editorUniforms[property] == true) {
  333. _paramVec3.push_back(UniformParamVec3(property, v3));
  334. }
  335. break;
  336. }
  337. case Properties::Type::VECTOR4: {
  338. Vec4 v4;
  339. paramsProperties->getVec4(property, &v4);
  340. programState->setUniformVec4(property, v4);
  341. if (_editorUniforms[property] == true) {
  342. _paramVec4.push_back(UniformParamVec4(property, v4));
  343. }
  344. break;
  345. }
  346. case Properties::Type::MATRIX: {
  347. Mat4 m4;
  348. paramsProperties->getMat4(property, &m4);
  349. programState->setUniformMat4(property, m4);
  350. if (_editorUniforms[property] == true) {
  351. _paramMat4.push_back(UniformParamMat4(property, m4));
  352. }
  353. break;
  354. }
  355. case Properties::Type::STRING: {
  356. programState->setParameterAutoBinding(property, paramsProperties->getString());
  357. if (_editorUniforms[property] == true) {
  358. _paramString.push_back(UniformParamString(property, paramsProperties->getString()));
  359. }
  360. break;
  361. }
  362. default:
  363. CCASSERT(false, "未知uniform变量类型");
  364. break;
  365. }
  366. property = paramsProperties->getNextProperty();
  367. }
  368. return true;
  369. }
  370. void CustomMaterial::updateUniformTime(float delta)
  371. {
  372. // 累加time
  373. _time += delta;
  374. if (_time >= 10000) {
  375. _time = 0;
  376. }
  377. // 对每个pass中包含u_time的uniform进行参数更新
  378. for (auto technique : getTechniques()) {
  379. for (auto pass : technique->getPasses()) {
  380. auto glProgramState = pass->getGLProgramState();
  381. if (glProgramState != nullptr && glProgramState->getGLProgram()->getUniformLocation("u_time") != -1) {
  382. glProgramState->setUniformFloat("u_time", _time);
  383. }
  384. }
  385. }
  386. }
  387. const std::vector<UniformParamFloat> CustomMaterial::getParamsFloat()
  388. {
  389. return _paramFloat;
  390. }
  391. const std::vector<UniformParamVec2> CustomMaterial::getParamsVec2()
  392. {
  393. return _paramVec2;
  394. }
  395. const std::vector<UniformParamVec3> CustomMaterial::getParamsVec3()
  396. {
  397. return _paramVec3;
  398. }
  399. const std::vector<UniformParamVec4> CustomMaterial::getParamsVec4()
  400. {
  401. return _paramVec4;
  402. }
  403. const std::vector<UniformParamMat4> CustomMaterial::getParamsMat4()
  404. {
  405. return _paramMat4;
  406. }
  407. const std::vector<UniformParamString> CustomMaterial::getParamsString()
  408. {
  409. return _paramString;
  410. }
  411. void CustomMaterial::setParamFloat(std::string name, float value)
  412. {
  413. for (auto &param : _paramFloat) {
  414. if (param.name == name) {
  415. param.value = value;
  416. }
  417. }
  418. GLProgramState* programState = _getGLProgramState();
  419. if (programState) {
  420. programState->setUniformFloat(name, value);
  421. }
  422. }
  423. void CustomMaterial::setParamsVec2(std::string name, Vec2 value)
  424. {
  425. for (auto &param : _paramVec2) {
  426. if (param.name == name) {
  427. param.value = value;
  428. }
  429. }
  430. GLProgramState* programState = _getGLProgramState();
  431. if (programState) {
  432. programState->setUniformVec2(name, value);
  433. }
  434. }
  435. void CustomMaterial::setParamsVec3(std::string name, Vec3 value)
  436. {
  437. for (auto &param : _paramVec3) {
  438. if (param.name == name) {
  439. param.value = value;
  440. }
  441. }
  442. GLProgramState* programState = _getGLProgramState();
  443. if (programState) {
  444. programState->setUniformVec3(name, value);
  445. }
  446. }
  447. void CustomMaterial::setParamsVec4(std::string name, Vec4 value)
  448. {
  449. for (auto &param : _paramVec4) {
  450. if (param.name == name) {
  451. param.value = value;
  452. }
  453. }
  454. GLProgramState* programState = _getGLProgramState();
  455. if (programState) {
  456. programState->setUniformVec4(name, value);
  457. }
  458. }
  459. void CustomMaterial::setParamsMat4(std::string name, Mat4 value)
  460. {
  461. for (auto &param : _paramMat4) {
  462. if (param.name == name) {
  463. param.value = value;
  464. }
  465. }
  466. GLProgramState* programState = _getGLProgramState();
  467. if (programState) {
  468. programState->setUniformMat4(name, value);
  469. }
  470. }
  471. void CustomMaterial::setParamsString(std::string name, std::string value)
  472. {
  473. for (auto &param : _paramString) {
  474. if (param.name == name) {
  475. param.value = value;
  476. }
  477. }
  478. GLProgramState* programState = _getGLProgramState();
  479. if (programState) {
  480. programState->setParameterAutoBinding(name, value);
  481. }
  482. }
  483. void CustomMaterial::setParamsTexture(SpriteFrame* spriteFrame)
  484. {
  485. GLProgramState* programState = _getGLProgramState();
  486. if (programState == nullptr || spriteFrame == nullptr) {
  487. return;
  488. }
  489. Texture2D* texture = spriteFrame->getTexture();
  490. Vec2 texRectOffset, texRectSize;
  491. // 计算纹理的偏移和大小
  492. Rect rect = spriteFrame->getRectInPixels();
  493. Size atlasSize = spriteFrame->getTexture()->getContentSizeInPixels();
  494. texRectOffset = Vec2(rect.origin.x / atlasSize.width, rect.origin.y / atlasSize.height);
  495. texRectSize = Vec2(rect.size.width / atlasSize.width, rect.size.height / atlasSize.height);
  496. for (auto &param : _paramTexture) {
  497. texture->setTexParameters(param.texParams);
  498. programState->setUniformTexture(param.name, texture);
  499. programState->setUniformVec2("u_texRectOffset", texRectOffset);
  500. programState->setUniformVec2("u_texRectSize", texRectSize);
  501. }
  502. }
  503. GLProgramState* CustomMaterial::_getGLProgramState()
  504. {
  505. if (getTechniques().at(0) && getTechniques().at(0)->getPasses().at(0)) {
  506. return getTechniques().at(0)->getPasses().at(0)->getGLProgramState();
  507. }
  508. return nullptr;
  509. }
  510. void CustomMaterial::setRelativePath(std::string relativePath)
  511. {
  512. auto found = relativePath.find_last_of("/\\");
  513. if (found != std::string::npos) {
  514. _relativePath = relativePath.substr(0, found + 1);
  515. } else {
  516. _relativePath = "";
  517. }
  518. }
  519. bool CustomMaterial::useBlendFunc()
  520. {
  521. return _useBlendFunc;
  522. }
  523. BlendFunc CustomMaterial::getBlendFunc()
  524. {
  525. return _blendFunc;
  526. }
  527. std::string CustomMaterial::getRelativePath()
  528. {
  529. return _relativePath;
  530. }
  531. void CustomMaterial::setMaterialPath(std::string materialPath)
  532. {
  533. _materialPath = materialPath;
  534. }
  535. std::string CustomMaterial::getMaterialPath()
  536. {
  537. return _materialPath;
  538. }
  539. static bool isValidUniform(const char* name)
  540. {
  541. return !(strcmp(name, "defines")==0 ||
  542. strcmp(name, "vertexShader")==0 ||
  543. strcmp(name, "fragmentShader")==0);
  544. }
  545. static const char* getOptionalString(Properties* properties, const char* key, const char* defaultValue)
  546. {
  547. const char* ret = properties->getString(key);
  548. if (!ret)
  549. ret = defaultValue;
  550. return ret;
  551. }
  552. NS_CC_END