Material.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. #include "anki/resource/Material.h"
  2. #include "anki/resource/MaterialVariable.h"
  3. #include "anki/misc/PropertyTree.h"
  4. #include "anki/resource/MaterialShaderProgramCreator.h"
  5. #include "anki/core/App.h"
  6. #include "anki/core/Globals.h"
  7. #include "anki/resource/ShaderProgram.h"
  8. #include <boost/foreach.hpp>
  9. #include <boost/property_tree/ptree.hpp>
  10. #include <boost/property_tree/xml_parser.hpp>
  11. #include <boost/assign/list_of.hpp>
  12. #include <boost/functional/hash.hpp>
  13. #include <boost/tokenizer.hpp>
  14. #include <boost/lexical_cast.hpp>
  15. #include <algorithm>
  16. namespace anki {
  17. //==============================================================================
  18. // Statics =
  19. //==============================================================================
  20. // Dont make idiotic mistakes
  21. #define TXT_AND_ENUM(x) \
  22. (#x, x)
  23. ConstCharPtrHashMap<GLenum>::Type Material::txtToBlengGlEnum =
  24. boost::assign::map_list_of
  25. TXT_AND_ENUM(GL_ZERO)
  26. TXT_AND_ENUM(GL_ONE)
  27. TXT_AND_ENUM(GL_DST_COLOR)
  28. TXT_AND_ENUM(GL_ONE_MINUS_DST_COLOR)
  29. TXT_AND_ENUM(GL_SRC_ALPHA)
  30. TXT_AND_ENUM(GL_ONE_MINUS_SRC_ALPHA)
  31. TXT_AND_ENUM(GL_DST_ALPHA)
  32. TXT_AND_ENUM(GL_ONE_MINUS_DST_ALPHA)
  33. TXT_AND_ENUM(GL_SRC_ALPHA_SATURATE)
  34. TXT_AND_ENUM(GL_SRC_COLOR)
  35. TXT_AND_ENUM(GL_ONE_MINUS_SRC_COLOR);
  36. //==============================================================================
  37. Material::Material()
  38. {}
  39. //==============================================================================
  40. Material::~Material()
  41. {}
  42. //==============================================================================
  43. void Material::load(const char* filename)
  44. {
  45. fname = filename;
  46. try
  47. {
  48. using namespace boost::property_tree;
  49. ptree pt;
  50. read_xml(filename, pt);
  51. parseMaterialTag(pt.get_child("material"));
  52. }
  53. catch(std::exception& e)
  54. {
  55. throw ANKI_EXCEPTION("File \"" + filename + "\" failed: " + e.what());
  56. }
  57. }
  58. //==============================================================================
  59. void Material::parseMaterialTag(const boost::property_tree::ptree& pt)
  60. {
  61. using namespace boost::property_tree;
  62. //
  63. // renderingStage
  64. //
  65. renderingStage = pt.get<int>("renderingStage");
  66. //
  67. // passes
  68. //
  69. boost::optional<std::string> pass =
  70. pt.get_optional<std::string>("passes");
  71. if(pass)
  72. {
  73. passes = splitString(pass.get().c_str());
  74. }
  75. else
  76. {
  77. passes.push_back("DUMMY");
  78. }
  79. //
  80. // levelsOfDetail
  81. //
  82. boost::optional<int> lod = pt.get_optional<int>("levelsOfDetail");
  83. if(lod)
  84. {
  85. levelsOfDetail = lod.get();
  86. }
  87. else
  88. {
  89. levelsOfDetail = 1;
  90. }
  91. //
  92. // shadow
  93. //
  94. boost::optional<int> sw = pt.get_optional<int>("shadow");
  95. if(sw)
  96. {
  97. shadow = sw.get();
  98. }
  99. //
  100. // blendFunctions
  101. //
  102. boost::optional<const ptree&> blendFuncsTree =
  103. pt.get_child_optional("blendFunctions");
  104. if(blendFuncsTree)
  105. {
  106. // sFactor
  107. {
  108. const std::string& tmp =
  109. blendFuncsTree.get().get<std::string>("sFactor");
  110. ConstCharPtrHashMap<GLenum>::Type::const_iterator it =
  111. txtToBlengGlEnum.find(tmp.c_str());
  112. if(it == txtToBlengGlEnum.end())
  113. {
  114. throw ANKI_EXCEPTION("Incorrect blend enum: " + tmp);
  115. }
  116. blendingSfactor = it->second;
  117. }
  118. // dFactor
  119. {
  120. const std::string& tmp =
  121. blendFuncsTree.get().get<std::string>("dFactor");
  122. ConstCharPtrHashMap<GLenum>::Type::const_iterator it =
  123. txtToBlengGlEnum.find(tmp.c_str());
  124. if(it == txtToBlengGlEnum.end())
  125. {
  126. throw ANKI_EXCEPTION("Incorrect blend enum: " + tmp);
  127. }
  128. blendingDfactor = it->second;
  129. }
  130. }
  131. //
  132. // depthTesting
  133. //
  134. boost::optional<int> dp = pt.get_optional<int>("depthTesting");
  135. if(dp)
  136. {
  137. depthTesting = dp.get();
  138. }
  139. //
  140. // wireframe
  141. //
  142. boost::optional<int> wf = pt.get_optional<int>("wireframe");
  143. if(wf)
  144. {
  145. wireframe = wf.get();
  146. }
  147. //
  148. // shaderProgram
  149. //
  150. MaterialShaderProgramCreator mspc(pt.get_child("shaderProgram"));
  151. for(uint level = 0; level < levelsOfDetail; ++level)
  152. {
  153. for(uint pid = 0; pid < passes.size(); ++pid)
  154. {
  155. std::stringstream src;
  156. src << "#define LEVEL_" << level << std::endl;
  157. src << "#define PASS_" << passes[pid] << std::endl;
  158. src << mspc.getShaderProgramSource() << std::endl;
  159. std::string filename =
  160. createShaderProgSourceToCache(src.str().c_str());
  161. ShaderProgramResourcePointer* pptr =
  162. new ShaderProgramResourcePointer;
  163. pptr->load(filename.c_str());
  164. ShaderProgram* sprog = pptr->get();
  165. sProgs.push_back(pptr);
  166. eSProgs[PassLevelKey(pid, level)] = sprog;
  167. }
  168. }
  169. populateVariables(pt.get_child("shaderProgram.inputs"));
  170. }
  171. //==============================================================================
  172. std::string Material::createShaderProgSourceToCache(const std::string& source)
  173. {
  174. // Create the hash
  175. boost::hash<std::string> stringHash;
  176. std::size_t h = stringHash(source);
  177. std::string prefix = boost::lexical_cast<std::string>(h);
  178. // Create path
  179. boost::filesystem::path newfPathName =
  180. AppSingleton::get().getCachePath() / (prefix + ".glsl");
  181. // If file not exists write it
  182. if(!boost::filesystem::exists(newfPathName))
  183. {
  184. // If not create it
  185. std::ofstream f(newfPathName.string().c_str());
  186. if(!f.is_open())
  187. {
  188. throw ANKI_EXCEPTION("Cannot open file for writing: " +
  189. newfPathName.string());
  190. }
  191. f.write(source.c_str(), source.length());
  192. f.close();
  193. }
  194. return newfPathName.string();
  195. }
  196. //==============================================================================
  197. void Material::populateVariables(const boost::property_tree::ptree& pt)
  198. {
  199. using namespace boost::property_tree;
  200. //
  201. // Get all names of all shader prog vars. Dont duplicate
  202. //
  203. std::map<std::string, GLenum> allVarNames;
  204. BOOST_FOREACH(const ShaderProgramResourcePointer& sProg, sProgs)
  205. {
  206. BOOST_FOREACH(const ShaderProgramVariable& v, sProg->getVariables())
  207. {
  208. allVarNames[v.getName()] = v.getGlDataType();
  209. }
  210. }
  211. //
  212. // Iterate shader program variables
  213. //
  214. std::map<std::string, GLenum>::const_iterator it = allVarNames.begin();
  215. for(; it != allVarNames.end(); it++)
  216. {
  217. const char* svName = it->first.c_str();
  218. GLenum dataType = it->second;
  219. // Find the ptree that contains the value
  220. const ptree* valuePt = NULL;
  221. BOOST_FOREACH(const ptree::value_type& v, pt)
  222. {
  223. if(v.first != "input")
  224. {
  225. throw ANKI_EXCEPTION("Expecting <input> and not: " +
  226. v.first);
  227. }
  228. if(v.second.get<std::string>("name") == svName)
  229. {
  230. valuePt = &v.second.get_child("value");
  231. break;
  232. }
  233. }
  234. MaterialVariable* v = NULL;
  235. // Buildin?
  236. if(!valuePt)
  237. {
  238. v = new MaterialVariable(svName, eSProgs);
  239. }
  240. // User defined
  241. else
  242. {
  243. const std::string& value = valuePt->get<std::string>("value");
  244. // Get the value
  245. switch(dataType)
  246. {
  247. // sampler2D
  248. case GL_SAMPLER_2D:
  249. v = new MaterialVariable(svName, eSProgs,
  250. value);
  251. break;
  252. // float
  253. case GL_FLOAT:
  254. v = new MaterialVariable(svName, eSProgs,
  255. boost::lexical_cast<float>(value));
  256. break;
  257. // vec2
  258. case GL_FLOAT_VEC2:
  259. v = new MaterialVariable(svName, eSProgs,
  260. setMathType<Vec2, 2>(value.c_str()));
  261. break;
  262. // vec3
  263. case GL_FLOAT_VEC3:
  264. v = new MaterialVariable(svName, eSProgs,
  265. setMathType<Vec3, 3>(value.c_str()));
  266. break;
  267. // vec4
  268. case GL_FLOAT_VEC4:
  269. v = new MaterialVariable(svName, eSProgs,
  270. setMathType<Vec4, 4>(value.c_str()));
  271. break;
  272. // default is error
  273. default:
  274. ANKI_ASSERT(0);
  275. }
  276. }
  277. vars.push_back(v);
  278. nameToVar[v->getName().c_str()] = v;
  279. } // end for all sprog vars
  280. }
  281. //==============================================================================
  282. StringList Material::splitString(const char* str)
  283. {
  284. StringList out;
  285. std::string s = str;
  286. boost::tokenizer<> tok(s);
  287. for(boost::tokenizer<>::iterator it = tok.begin(); it != tok.end(); ++it)
  288. {
  289. out.push_back(*it);
  290. }
  291. return out;
  292. }
  293. //==============================================================================
  294. template<typename Type, size_t n>
  295. Type Material::setMathType(const char* str)
  296. {
  297. Type out;
  298. StringList sl = splitString(str);
  299. ANKI_ASSERT(sl.size() == n);
  300. for(uint i = 0; i < n; ++i)
  301. {
  302. out[i] = boost::lexical_cast<float>(sl[i]);
  303. }
  304. return out;
  305. }
  306. //==============================================================================
  307. const MaterialVariable& Material::findVariableByName(const char* name) const
  308. {
  309. NameToVariableHashMap::const_iterator it = nameToVar.find(name);
  310. if(it == nameToVar.end())
  311. {
  312. throw ANKI_EXCEPTION("Cannot get material variable "
  313. "with name \"" + name + '\"');
  314. }
  315. return *(it->second);
  316. }
  317. } // end namespace