Material.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. #include "Base.h"
  2. #include "Material.h"
  3. #include "FileSystem.h"
  4. #include "Effect.h"
  5. #include "Technique.h"
  6. #include "Pass.h"
  7. #include "Properties.h"
  8. #include "Node.h"
  9. namespace gameplay
  10. {
  11. Material::Material() :
  12. _currentTechnique(NULL)
  13. {
  14. }
  15. Material::~Material()
  16. {
  17. // Destroy all the techniques.
  18. for (size_t i = 0, count = _techniques.size(); i < count; ++i)
  19. {
  20. Technique* technique = _techniques[i];
  21. SAFE_RELEASE(technique);
  22. }
  23. }
  24. Material* Material::create(const char* url)
  25. {
  26. return create(url, (PassCallback)NULL, NULL);
  27. }
  28. Material* Material::create(const char* url, PassCallback callback, void* cookie)
  29. {
  30. // Load the material properties from file.
  31. Properties* properties = Properties::create(url);
  32. if (properties == NULL)
  33. {
  34. GP_WARN("Failed to create material from file: %s", url);
  35. return NULL;
  36. }
  37. Material* material = create((strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace(), callback, cookie);
  38. SAFE_DELETE(properties);
  39. return material;
  40. }
  41. Material* Material::create(Properties* materialProperties)
  42. {
  43. return create(materialProperties, (PassCallback)NULL, NULL);
  44. }
  45. Material* Material::create(Properties* materialProperties, PassCallback callback, void* cookie)
  46. {
  47. // Check if the Properties is valid and has a valid namespace.
  48. if (!materialProperties || !(strcmp(materialProperties->getNamespace(), "material") == 0))
  49. {
  50. GP_ERROR("Properties object must be non-null and have namespace equal to 'material'.");
  51. return NULL;
  52. }
  53. // Create new material from the file passed in.
  54. Material* material = new Material();
  55. // Load uniform value parameters for this material.
  56. loadRenderState(material, materialProperties);
  57. // Go through all the material properties and create techniques under this material.
  58. Properties* techniqueProperties = NULL;
  59. while ((techniqueProperties = materialProperties->getNextNamespace()))
  60. {
  61. if (strcmp(techniqueProperties->getNamespace(), "technique") == 0)
  62. {
  63. if (!loadTechnique(material, techniqueProperties, callback, cookie))
  64. {
  65. GP_ERROR("Failed to load technique for material.");
  66. SAFE_RELEASE(material);
  67. return NULL;
  68. }
  69. }
  70. }
  71. // Set the current technique to the first found technique.
  72. if (material->getTechniqueCount() > 0)
  73. {
  74. Technique* t = material->getTechniqueByIndex(0);
  75. if (t)
  76. {
  77. material->_currentTechnique = t;
  78. }
  79. }
  80. return material;
  81. }
  82. Material* Material::create(Effect* effect)
  83. {
  84. GP_ASSERT(effect);
  85. // Create a new material with a single technique and pass for the given effect.
  86. Material* material = new Material();
  87. Technique* technique = new Technique(NULL, material);
  88. material->_techniques.push_back(technique);
  89. Pass* pass = new Pass(NULL, technique);
  90. pass->_effect = effect;
  91. technique->_passes.push_back(pass);
  92. effect->addRef();
  93. material->_currentTechnique = technique;
  94. return material;
  95. }
  96. Material* Material::create(const char* vshPath, const char* fshPath, const char* defines)
  97. {
  98. GP_ASSERT(vshPath);
  99. GP_ASSERT(fshPath);
  100. // Create a new material with a single technique and pass for the given effect
  101. Material* material = new Material();
  102. Technique* technique = new Technique(NULL, material);
  103. material->_techniques.push_back(technique);
  104. Pass* pass = new Pass(NULL, technique);
  105. if (!pass->initialize(vshPath, fshPath, defines))
  106. {
  107. GP_WARN("Failed to create pass for material: vertexShader = %s, fragmentShader = %s, defines = %s", vshPath, fshPath, defines ? defines : "");
  108. SAFE_RELEASE(pass);
  109. SAFE_RELEASE(material);
  110. return NULL;
  111. }
  112. technique->_passes.push_back(pass);
  113. material->_currentTechnique = technique;
  114. return material;
  115. }
  116. unsigned int Material::getTechniqueCount() const
  117. {
  118. return (unsigned int)_techniques.size();
  119. }
  120. Technique* Material::getTechniqueByIndex(unsigned int index) const
  121. {
  122. GP_ASSERT(index < _techniques.size());
  123. return _techniques[index];
  124. }
  125. Technique* Material::getTechnique(const char* id) const
  126. {
  127. GP_ASSERT(id);
  128. for (size_t i = 0, count = _techniques.size(); i < count; ++i)
  129. {
  130. Technique* t = _techniques[i];
  131. GP_ASSERT(t);
  132. if (strcmp(t->getId(), id) == 0)
  133. {
  134. return t;
  135. }
  136. }
  137. return NULL;
  138. }
  139. Technique* Material::getTechnique() const
  140. {
  141. return _currentTechnique;
  142. }
  143. void Material::setTechnique(const char* id)
  144. {
  145. Technique* t = getTechnique(id);
  146. if (t)
  147. {
  148. _currentTechnique = t;
  149. }
  150. }
  151. void Material::setNodeBinding(Node* node)
  152. {
  153. RenderState::setNodeBinding(node);
  154. for (size_t i = 0, count = _techniques.size(); i < count; ++i)
  155. {
  156. _techniques[i]->setNodeBinding(node);
  157. }
  158. }
  159. Material* Material::clone(NodeCloneContext &context) const
  160. {
  161. Material* material = new Material();
  162. RenderState::cloneInto(material, context);
  163. for (std::vector<Technique*>::const_iterator it = _techniques.begin(); it != _techniques.end(); ++it)
  164. {
  165. const Technique* technique = *it;
  166. GP_ASSERT(technique);
  167. Technique* techniqueClone = technique->clone(material, context);
  168. material->_techniques.push_back(techniqueClone);
  169. if (_currentTechnique == technique)
  170. {
  171. material->_currentTechnique = techniqueClone;
  172. }
  173. }
  174. return material;
  175. }
  176. bool Material::loadTechnique(Material* material, Properties* techniqueProperties, PassCallback callback, void* cookie)
  177. {
  178. GP_ASSERT(material);
  179. GP_ASSERT(techniqueProperties);
  180. // Create a new technique.
  181. Technique* technique = new Technique(techniqueProperties->getId(), material);
  182. // Load uniform value parameters for this technique.
  183. loadRenderState(technique, techniqueProperties);
  184. // Go through all the properties and create passes under this technique.
  185. techniqueProperties->rewind();
  186. Properties* passProperties = NULL;
  187. while ((passProperties = techniqueProperties->getNextNamespace()))
  188. {
  189. if (strcmp(passProperties->getNamespace(), "pass") == 0)
  190. {
  191. // Create and load passes.
  192. if (!loadPass(technique, passProperties, callback, cookie))
  193. {
  194. GP_ERROR("Failed to create pass for technique.");
  195. SAFE_RELEASE(technique);
  196. return false;
  197. }
  198. }
  199. }
  200. // Add the new technique to the material.
  201. material->_techniques.push_back(technique);
  202. return true;
  203. }
  204. bool Material::loadPass(Technique* technique, Properties* passProperties, PassCallback callback, void* cookie)
  205. {
  206. GP_ASSERT(passProperties);
  207. GP_ASSERT(technique);
  208. // Fetch shader info required to create the effect of this technique.
  209. const char* vertexShaderPath = passProperties->getString("vertexShader");
  210. GP_ASSERT(vertexShaderPath);
  211. const char* fragmentShaderPath = passProperties->getString("fragmentShader");
  212. GP_ASSERT(fragmentShaderPath);
  213. const char* passDefines = passProperties->getString("defines");
  214. // Create the pass
  215. Pass* pass = new Pass(passProperties->getId(), technique);
  216. // Load render state.
  217. loadRenderState(pass, passProperties);
  218. // If a pass callback was specified, call it and add the result to our list of defines
  219. std::string allDefines = passDefines ? passDefines : "";
  220. if (callback)
  221. {
  222. std::string customDefines = callback(pass, cookie);
  223. if (customDefines.length() > 0)
  224. {
  225. if (allDefines.length() > 0)
  226. allDefines += ';';
  227. allDefines += customDefines;
  228. }
  229. }
  230. // Initialize/compile the effect with the full set of defines
  231. if (!pass->initialize(vertexShaderPath, fragmentShaderPath, allDefines.c_str()))
  232. {
  233. GP_WARN("Failed to create pass for technique.");
  234. SAFE_RELEASE(pass);
  235. return false;
  236. }
  237. // Add the new pass to the technique.
  238. technique->_passes.push_back(pass);
  239. return true;
  240. }
  241. static bool isMaterialKeyword(const char* str)
  242. {
  243. GP_ASSERT(str);
  244. #define MATERIAL_KEYWORD_COUNT 3
  245. static const char* reservedKeywords[MATERIAL_KEYWORD_COUNT] =
  246. {
  247. "vertexShader",
  248. "fragmentShader",
  249. "defines"
  250. };
  251. for (unsigned int i = 0; i < MATERIAL_KEYWORD_COUNT; ++i)
  252. {
  253. if (strcmp(reservedKeywords[i], str) == 0)
  254. {
  255. return true;
  256. }
  257. }
  258. return false;
  259. }
  260. static Texture::Filter parseTextureFilterMode(const char* str, Texture::Filter defaultValue)
  261. {
  262. if (str == NULL || strlen(str) == 0)
  263. {
  264. GP_ERROR("Texture filter mode string must be non-null and non-empty.");
  265. return defaultValue;
  266. }
  267. else if (strcmp(str, "NEAREST") == 0)
  268. {
  269. return Texture::NEAREST;
  270. }
  271. else if (strcmp(str, "LINEAR") == 0)
  272. {
  273. return Texture::LINEAR;
  274. }
  275. else if (strcmp(str, "NEAREST_MIPMAP_NEAREST") == 0)
  276. {
  277. return Texture::NEAREST_MIPMAP_NEAREST;
  278. }
  279. else if (strcmp(str, "LINEAR_MIPMAP_NEAREST") == 0)
  280. {
  281. return Texture::LINEAR_MIPMAP_NEAREST;
  282. }
  283. else if (strcmp(str, "NEAREST_MIPMAP_LINEAR") == 0)
  284. {
  285. return Texture::NEAREST_MIPMAP_LINEAR;
  286. }
  287. else if (strcmp(str, "LINEAR_MIPMAP_LINEAR") == 0)
  288. {
  289. return Texture::LINEAR_MIPMAP_LINEAR;
  290. }
  291. else
  292. {
  293. GP_ERROR("Unsupported texture filter mode string ('%s').", str);
  294. return defaultValue;
  295. }
  296. }
  297. static Texture::Wrap parseTextureWrapMode(const char* str, Texture::Wrap defaultValue)
  298. {
  299. if (str == NULL || strlen(str) == 0)
  300. {
  301. GP_ERROR("Texture wrap mode string must be non-null and non-empty.");
  302. return defaultValue;
  303. }
  304. else if (strcmp(str, "REPEAT") == 0)
  305. {
  306. return Texture::REPEAT;
  307. }
  308. else if (strcmp(str, "CLAMP") == 0)
  309. {
  310. return Texture::CLAMP;
  311. }
  312. else
  313. {
  314. GP_ERROR("Unsupported texture wrap mode string ('%s').", str);
  315. return defaultValue;
  316. }
  317. }
  318. void Material::loadRenderState(RenderState* renderState, Properties* properties)
  319. {
  320. GP_ASSERT(renderState);
  321. GP_ASSERT(properties);
  322. // Rewind the properties to start reading from the start.
  323. properties->rewind();
  324. const char* name;
  325. while ((name = properties->getNextProperty()))
  326. {
  327. if (isMaterialKeyword(name))
  328. continue; // keyword - skip
  329. switch (properties->getType())
  330. {
  331. case Properties::NUMBER:
  332. GP_ASSERT(renderState->getParameter(name));
  333. renderState->getParameter(name)->setValue(properties->getFloat());
  334. break;
  335. case Properties::VECTOR2:
  336. {
  337. Vector2 vector2;
  338. if (properties->getVector2(NULL, &vector2))
  339. {
  340. GP_ASSERT(renderState->getParameter(name));
  341. renderState->getParameter(name)->setValue(vector2);
  342. }
  343. }
  344. break;
  345. case Properties::VECTOR3:
  346. {
  347. Vector3 vector3;
  348. if (properties->getVector3(NULL, &vector3))
  349. {
  350. GP_ASSERT(renderState->getParameter(name));
  351. renderState->getParameter(name)->setValue(vector3);
  352. }
  353. }
  354. break;
  355. case Properties::VECTOR4:
  356. {
  357. Vector4 vector4;
  358. if (properties->getVector4(NULL, &vector4))
  359. {
  360. GP_ASSERT(renderState->getParameter(name));
  361. renderState->getParameter(name)->setValue(vector4);
  362. }
  363. }
  364. break;
  365. case Properties::MATRIX:
  366. {
  367. Matrix matrix;
  368. if (properties->getMatrix(NULL, &matrix))
  369. {
  370. GP_ASSERT(renderState->getParameter(name));
  371. renderState->getParameter(name)->setValue(matrix);
  372. }
  373. }
  374. break;
  375. default:
  376. {
  377. // Assume this is a parameter auto-binding.
  378. renderState->setParameterAutoBinding(name, properties->getString());
  379. }
  380. break;
  381. }
  382. }
  383. // Iterate through all child namespaces searching for samplers and render state blocks.
  384. Properties* ns;
  385. while ((ns = properties->getNextNamespace()))
  386. {
  387. if (strcmp(ns->getNamespace(), "sampler") == 0)
  388. {
  389. // Read the texture uniform name.
  390. name = ns->getId();
  391. if (strlen(name) == 0)
  392. {
  393. GP_ERROR("Texture sampler is missing required uniform name.");
  394. continue;
  395. }
  396. // Get the texture path.
  397. std::string path;
  398. if (!ns->getPath("path", &path))
  399. {
  400. GP_ERROR("Texture sampler '%s' is missing required image file path.", name);
  401. continue;
  402. }
  403. // Read texture state (booleans default to 'false' if not present).
  404. bool mipmap = ns->getBool("mipmap");
  405. Texture::Wrap wrapS = parseTextureWrapMode(ns->getString("wrapS"), Texture::REPEAT);
  406. Texture::Wrap wrapT = parseTextureWrapMode(ns->getString("wrapT"), Texture::REPEAT);
  407. Texture::Filter minFilter = parseTextureFilterMode(ns->getString("minFilter"), mipmap ? Texture::NEAREST_MIPMAP_LINEAR : Texture::LINEAR);
  408. Texture::Filter magFilter = parseTextureFilterMode(ns->getString("magFilter"), Texture::LINEAR);
  409. // Set the sampler parameter.
  410. GP_ASSERT(renderState->getParameter(name));
  411. Texture::Sampler* sampler = renderState->getParameter(name)->setValue(path.c_str(), mipmap);
  412. if (sampler)
  413. {
  414. sampler->setWrapMode(wrapS, wrapT);
  415. sampler->setFilterMode(minFilter, magFilter);
  416. }
  417. }
  418. else if (strcmp(ns->getNamespace(), "renderState") == 0)
  419. {
  420. while ((name = ns->getNextProperty()))
  421. {
  422. GP_ASSERT(renderState->getStateBlock());
  423. renderState->getStateBlock()->setState(name, ns->getString());
  424. }
  425. }
  426. }
  427. }
  428. }