Material.cpp 12 KB

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