Material.cpp 14 KB

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