Material.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. #include "anki/resource/Material.h"
  2. #include "anki/misc/PropertyTree.h"
  3. #include "anki/resource/MaterialShaderProgramCreator.h"
  4. #include "anki/core/App.h"
  5. #include "anki/core/Globals.h"
  6. #include "anki/resource/ShaderProgram.h"
  7. #include "anki/resource/Texture.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. // MaterialVariable =
  19. //==============================================================================
  20. //==============================================================================
  21. MaterialVariable::~MaterialVariable()
  22. {}
  23. //==============================================================================
  24. GLenum MaterialVariable::getGlDataType() const
  25. {
  26. return oneSProgVar->getGlDataType();
  27. }
  28. //==============================================================================
  29. const std::string& MaterialVariable::getName() const
  30. {
  31. return oneSProgVar->getName();
  32. }
  33. //==============================================================================
  34. void MaterialVariable::init(const char* shaderProgVarName,
  35. const PassLevelToShaderProgramHashMap& sProgs)
  36. {
  37. oneSProgVar = NULL;
  38. // For all programs
  39. PassLevelToShaderProgramHashMap::const_iterator it = sProgs.begin();
  40. for(; it != sProgs.end(); ++it)
  41. {
  42. const ShaderProgram& sProg = *(it->second);
  43. const PassLevelKey& key = it->first;
  44. // Variable exists
  45. if(sProg.uniformVariableExists(shaderProgVarName))
  46. {
  47. const ShaderProgramUniformVariable& sProgVar =
  48. sProg.findUniformVariableByName(shaderProgVarName);
  49. sProgVars[key] = &sProgVar;
  50. // Set oneSProgVar
  51. if(!oneSProgVar)
  52. {
  53. oneSProgVar = &sProgVar;
  54. }
  55. // Sanity check: All the sprog vars need to have same GL data type
  56. if(oneSProgVar->getGlDataType() != sProgVar.getGlDataType() ||
  57. oneSProgVar->getType() != sProgVar.getType())
  58. {
  59. throw ANKI_EXCEPTION("Incompatible shader "
  60. "program variables: " +
  61. shaderProgVarName);
  62. }
  63. }
  64. }
  65. // Extra sanity checks
  66. if(!oneSProgVar)
  67. {
  68. throw ANKI_EXCEPTION("Variable not found in "
  69. "any of the shader programs: " +
  70. shaderProgVarName);
  71. }
  72. }
  73. //==============================================================================
  74. // Material =
  75. //==============================================================================
  76. //==============================================================================
  77. // Dont make idiotic mistakes
  78. #define TXT_AND_ENUM(x) \
  79. (#x, x)
  80. ConstCharPtrHashMap<GLenum>::Type Material::txtToBlengGlEnum =
  81. boost::assign::map_list_of
  82. TXT_AND_ENUM(GL_ZERO)
  83. TXT_AND_ENUM(GL_ONE)
  84. TXT_AND_ENUM(GL_DST_COLOR)
  85. TXT_AND_ENUM(GL_ONE_MINUS_DST_COLOR)
  86. TXT_AND_ENUM(GL_SRC_ALPHA)
  87. TXT_AND_ENUM(GL_ONE_MINUS_SRC_ALPHA)
  88. TXT_AND_ENUM(GL_DST_ALPHA)
  89. TXT_AND_ENUM(GL_ONE_MINUS_DST_ALPHA)
  90. TXT_AND_ENUM(GL_SRC_ALPHA_SATURATE)
  91. TXT_AND_ENUM(GL_SRC_COLOR)
  92. TXT_AND_ENUM(GL_ONE_MINUS_SRC_COLOR);
  93. //==============================================================================
  94. Material::Material()
  95. {}
  96. //==============================================================================
  97. Material::~Material()
  98. {}
  99. //==============================================================================
  100. void Material::load(const char* filename)
  101. {
  102. fname = filename;
  103. try
  104. {
  105. using namespace boost::property_tree;
  106. ptree pt;
  107. read_xml(filename, pt);
  108. parseMaterialTag(pt.get_child("material"));
  109. }
  110. catch(std::exception& e)
  111. {
  112. throw ANKI_EXCEPTION("File \"" + filename + "\" failed") << e;
  113. }
  114. }
  115. //==============================================================================
  116. void Material::parseMaterialTag(const boost::property_tree::ptree& pt)
  117. {
  118. using namespace boost::property_tree;
  119. //
  120. // renderingStage
  121. //
  122. renderingStage = pt.get<int>("renderingStage");
  123. //
  124. // passes
  125. //
  126. boost::optional<std::string> pass =
  127. pt.get_optional<std::string>("passes");
  128. if(pass)
  129. {
  130. passes = StringList::splitString(pass.get(), " ");
  131. }
  132. else
  133. {
  134. passes.push_back("DUMMY");
  135. }
  136. //
  137. // levelsOfDetail
  138. //
  139. boost::optional<int> lod = pt.get_optional<int>("levelsOfDetail");
  140. if(lod)
  141. {
  142. levelsOfDetail = lod.get();
  143. }
  144. else
  145. {
  146. levelsOfDetail = 1;
  147. }
  148. //
  149. // shadow
  150. //
  151. boost::optional<int> sw = pt.get_optional<int>("shadow");
  152. if(sw)
  153. {
  154. shadow = sw.get();
  155. }
  156. //
  157. // blendFunctions
  158. //
  159. boost::optional<const ptree&> blendFuncsTree =
  160. pt.get_child_optional("blendFunctions");
  161. if(blendFuncsTree)
  162. {
  163. // sFactor
  164. {
  165. const std::string& tmp =
  166. blendFuncsTree.get().get<std::string>("sFactor");
  167. ConstCharPtrHashMap<GLenum>::Type::const_iterator it =
  168. txtToBlengGlEnum.find(tmp.c_str());
  169. if(it == txtToBlengGlEnum.end())
  170. {
  171. throw ANKI_EXCEPTION("Incorrect blend enum: " + tmp);
  172. }
  173. blendingSfactor = it->second;
  174. }
  175. // dFactor
  176. {
  177. const std::string& tmp =
  178. blendFuncsTree.get().get<std::string>("dFactor");
  179. ConstCharPtrHashMap<GLenum>::Type::const_iterator it =
  180. txtToBlengGlEnum.find(tmp.c_str());
  181. if(it == txtToBlengGlEnum.end())
  182. {
  183. throw ANKI_EXCEPTION("Incorrect blend enum: " + tmp);
  184. }
  185. blendingDfactor = it->second;
  186. }
  187. }
  188. //
  189. // depthTesting
  190. //
  191. boost::optional<int> dp = pt.get_optional<int>("depthTesting");
  192. if(dp)
  193. {
  194. depthTesting = dp.get();
  195. }
  196. //
  197. // wireframe
  198. //
  199. boost::optional<int> wf = pt.get_optional<int>("wireframe");
  200. if(wf)
  201. {
  202. wireframe = wf.get();
  203. }
  204. //
  205. // shaderProgram
  206. //
  207. MaterialShaderProgramCreator mspc(pt.get_child("shaderProgram"));
  208. for(uint level = 0; level < levelsOfDetail; ++level)
  209. {
  210. for(uint pid = 0; pid < passes.size(); ++pid)
  211. {
  212. std::stringstream src;
  213. src << "#define LOD_" << level << std::endl;
  214. src << "#define PASS_" << passes[pid] << std::endl;
  215. src << mspc.getShaderProgramSource() << std::endl;
  216. std::string filename =
  217. createShaderProgSourceToCache(src.str().c_str());
  218. ShaderProgramResourcePointer* pptr =
  219. new ShaderProgramResourcePointer;
  220. pptr->load(filename.c_str());
  221. ShaderProgram* sprog = pptr->get();
  222. sProgs.push_back(pptr);
  223. eSProgs[PassLevelKey(pid, level)] = sprog;
  224. }
  225. }
  226. populateVariables(pt.get_child("shaderProgram"));
  227. }
  228. //==============================================================================
  229. std::string Material::createShaderProgSourceToCache(const std::string& source)
  230. {
  231. // Create the hash
  232. boost::hash<std::string> stringHash;
  233. std::size_t h = stringHash(source);
  234. std::string prefix = boost::lexical_cast<std::string>(h);
  235. // Create path
  236. boost::filesystem::path newfPathName =
  237. AppSingleton::get().getCachePath() / (prefix + ".glsl");
  238. // If file not exists write it
  239. if(!boost::filesystem::exists(newfPathName))
  240. {
  241. // If not create it
  242. std::ofstream f(newfPathName.string().c_str());
  243. if(!f.is_open())
  244. {
  245. throw ANKI_EXCEPTION("Cannot open file for writing: " +
  246. newfPathName.string());
  247. }
  248. f.write(source.c_str(), source.length());
  249. f.close();
  250. }
  251. return newfPathName.string();
  252. }
  253. //==============================================================================
  254. void Material::populateVariables(const boost::property_tree::ptree& pt)
  255. {
  256. using namespace boost::property_tree;
  257. //
  258. // Get all names of all the uniforms. Dont duplicate
  259. //
  260. std::map<std::string, GLenum> allVarNames;
  261. BOOST_FOREACH(const ShaderProgramResourcePointer& sProg, sProgs)
  262. {
  263. BOOST_FOREACH(const ShaderProgramUniformVariable* v,
  264. sProg->getUniformVariables())
  265. {
  266. allVarNames[v->getName()] = v->getGlDataType();
  267. ANKI_INFO("--" << v->getName());
  268. }
  269. }
  270. //
  271. // Iterate all the <input> and get the value
  272. //
  273. std::map<std::string, std::string> nameToValue;
  274. BOOST_FOREACH(const ptree::value_type& v, pt)
  275. {
  276. if(v.first != "shader")
  277. {
  278. throw ANKI_EXCEPTION("Expected \"shader\" tag and not: " +
  279. v.first);
  280. }
  281. boost::optional<const ptree&> insPt =
  282. v.second.get_child_optional("inputs");
  283. if(!insPt)
  284. {
  285. continue;
  286. }
  287. BOOST_FOREACH(const ptree::value_type& vv, insPt.get())
  288. {
  289. if(vv.first != "input")
  290. {
  291. throw ANKI_EXCEPTION("Expected \"input\" tag and not: " +
  292. vv.first);
  293. }
  294. std::string name = vv.second.get<std::string>("name");
  295. std::string value = vv.second.get<std::string>("value");
  296. nameToValue[name] = value;
  297. ANKI_INFO("++ " << name);
  298. // A simple warning
  299. std::map<std::string, GLenum>::const_iterator iit =
  300. allVarNames.find(name);
  301. if(iit == allVarNames.end())
  302. {
  303. ANKI_WARNING("Input variable \"" <<
  304. name << "\" not used in material \"" << fname << "\"");
  305. }
  306. }
  307. }
  308. //
  309. // Now combine
  310. //
  311. std::map<std::string, GLenum>::const_iterator it = allVarNames.begin();
  312. for(; it != allVarNames.end(); it++)
  313. {
  314. std::string name = it->first;
  315. GLenum dataType = it->second;
  316. std::map<std::string, std::string>::const_iterator it1 =
  317. nameToValue.find(name);
  318. MaterialVariable* v = NULL;
  319. // Not found
  320. if(it1 == nameToValue.end())
  321. {
  322. ANKI_INFO("No value for " << name);
  323. // Get the value
  324. switch(dataType)
  325. {
  326. // sampler2D
  327. case GL_SAMPLER_2D:
  328. v = new MaterialVariable(name.c_str(), eSProgs,
  329. TextureResourcePointer(), false);
  330. break;
  331. // float
  332. case GL_FLOAT:
  333. v = new MaterialVariable(name.c_str(), eSProgs,
  334. float(), false);
  335. break;
  336. // vec2
  337. case GL_FLOAT_VEC2:
  338. v = new MaterialVariable(name.c_str(), eSProgs,
  339. Vec2(), false);
  340. break;
  341. // vec3
  342. case GL_FLOAT_VEC3:
  343. v = new MaterialVariable(name.c_str(), eSProgs,
  344. Vec3(), false);
  345. break;
  346. // vec4
  347. case GL_FLOAT_VEC4:
  348. v = new MaterialVariable(name.c_str(), eSProgs,
  349. Vec4(), false);
  350. break;
  351. // default is error
  352. default:
  353. ANKI_ASSERT(0);
  354. }
  355. }
  356. else
  357. {
  358. std::string value = it1->second;
  359. ANKI_INFO("With value " << name << " " << value);
  360. // Get the value
  361. switch(dataType)
  362. {
  363. // sampler2D
  364. case GL_SAMPLER_2D:
  365. v = new MaterialVariable(name.c_str(), eSProgs,
  366. TextureResourcePointer(value.c_str()), true);
  367. break;
  368. // float
  369. case GL_FLOAT:
  370. v = new MaterialVariable(name.c_str(), eSProgs,
  371. boost::lexical_cast<float>(value), true);
  372. break;
  373. // vec2
  374. case GL_FLOAT_VEC2:
  375. v = new MaterialVariable(name.c_str(), eSProgs,
  376. setMathType<Vec2, 2>(value.c_str()), true);
  377. break;
  378. // vec3
  379. case GL_FLOAT_VEC3:
  380. v = new MaterialVariable(name.c_str(), eSProgs,
  381. setMathType<Vec3, 3>(value.c_str()), true);
  382. break;
  383. // vec4
  384. case GL_FLOAT_VEC4:
  385. v = new MaterialVariable(name.c_str(), eSProgs,
  386. setMathType<Vec4, 4>(value.c_str()), true);
  387. break;
  388. // default is error
  389. default:
  390. ANKI_ASSERT(0);
  391. }
  392. }
  393. }
  394. }
  395. //==============================================================================
  396. template<typename Type, size_t n>
  397. Type Material::setMathType(const char* str)
  398. {
  399. Type out;
  400. StringList sl = StringList::splitString(str, " ");
  401. ANKI_ASSERT(sl.size() == n);
  402. for(uint i = 0; i < n; ++i)
  403. {
  404. out[i] = boost::lexical_cast<float>(sl[i]);
  405. }
  406. return out;
  407. }
  408. //==============================================================================
  409. const MaterialVariable& Material::findVariableByName(const char* name) const
  410. {
  411. NameToVariableHashMap::const_iterator it = nameToVar.find(name);
  412. if(it == nameToVar.end())
  413. {
  414. throw ANKI_EXCEPTION("Cannot get material variable "
  415. "with name \"" + name + '\"');
  416. }
  417. return *(it->second);
  418. }
  419. } // end namespace