Material.cpp 13 KB

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