Material.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. #include "anki/resource/Material.h"
  2. #include "anki/resource/MaterialShaderProgramCreator.h"
  3. #include "anki/core/App.h"
  4. #include "anki/core/Logger.h"
  5. #include "anki/resource/ShaderProgramResource.h"
  6. #include "anki/resource/TextureResource.h"
  7. #include "anki/util/File.h"
  8. #include "anki/misc/Xml.h"
  9. #include "anki/renderer/MainRenderer.h"
  10. #include <functional>
  11. #include <algorithm>
  12. #include <map>
  13. namespace anki {
  14. //==============================================================================
  15. // Misc =
  16. //==============================================================================
  17. //==============================================================================
  18. static Array<const char*, PASS_COUNT> passNames = {{
  19. "COLOR", "DEPTH"
  20. }};
  21. //==============================================================================
  22. struct SetMaterialVariableValuesVisitor
  23. {
  24. const StringList& list;
  25. SetMaterialVariableValuesVisitor(const StringList& list_)
  26. : list(list_)
  27. {}
  28. template<typename TMaterialVariableTemplate>
  29. void visit(TMaterialVariableTemplate& mv)
  30. {
  31. typedef typename TMaterialVariableTemplate::Type Type;
  32. U32 floatsNeeded = mv.getAShaderProgramUniformVariable().getSize()
  33. * (sizeof(Type) / sizeof(F32));
  34. if(list.size() != floatsNeeded)
  35. {
  36. throw ANKI_EXCEPTION("Incorrect number of values");
  37. }
  38. Vector<F32> floatvec;
  39. floatvec.resize(floatsNeeded);
  40. for(U i = 0; i < floatsNeeded; ++i)
  41. {
  42. floatvec[i] = std::stof(list[i]);
  43. }
  44. mv.set((Type*)&floatvec[0],
  45. mv.getAShaderProgramUniformVariable().getSize());
  46. }
  47. };
  48. //==============================================================================
  49. /// Given a string that defines blending return the GLenum
  50. static GLenum blendToEnum(const char* str)
  51. {
  52. // Dont make idiotic mistakes
  53. #define TXT_AND_ENUM(x) \
  54. if(strcmp(str, #x) == 0) { \
  55. return x; \
  56. }
  57. TXT_AND_ENUM(GL_ZERO)
  58. TXT_AND_ENUM(GL_ONE)
  59. TXT_AND_ENUM(GL_DST_COLOR)
  60. TXT_AND_ENUM(GL_ONE_MINUS_DST_COLOR)
  61. TXT_AND_ENUM(GL_SRC_ALPHA)
  62. TXT_AND_ENUM(GL_ONE_MINUS_SRC_ALPHA)
  63. TXT_AND_ENUM(GL_DST_ALPHA)
  64. TXT_AND_ENUM(GL_ONE_MINUS_DST_ALPHA)
  65. TXT_AND_ENUM(GL_SRC_ALPHA_SATURATE)
  66. TXT_AND_ENUM(GL_SRC_COLOR)
  67. TXT_AND_ENUM(GL_ONE_MINUS_SRC_COLOR);
  68. ANKI_ASSERT(0);
  69. throw ANKI_EXCEPTION("Incorrect blend enum: " + str);
  70. #undef TXT_AND_ENUM
  71. }
  72. //==============================================================================
  73. // MaterialVariable =
  74. //==============================================================================
  75. //==============================================================================
  76. MaterialVariable::~MaterialVariable()
  77. {}
  78. //==============================================================================
  79. GLenum MaterialVariable::getGlDataType() const
  80. {
  81. return oneSProgVar->getGlDataType();
  82. }
  83. //==============================================================================
  84. const std::string& MaterialVariable::getName() const
  85. {
  86. return oneSProgVar->getName();
  87. }
  88. //==============================================================================
  89. void MaterialVariable::init(const char* shaderProgVarName,
  90. PassLevelToShaderProgramHashMap& progs)
  91. {
  92. oneSProgVar = NULL;
  93. // For all programs
  94. PassLevelToShaderProgramHashMap::iterator it = progs.begin();
  95. for(; it != progs.end(); ++it)
  96. {
  97. const ShaderProgram& sProg = *(it->second);
  98. const PassLevelKey& key = it->first;
  99. // Variable exists put it the map
  100. const ShaderProgramUniformVariable* uni =
  101. sProg.tryFindUniformVariable(shaderProgVarName);
  102. if(uni)
  103. {
  104. sProgVars[key] = uni;
  105. // Set oneSProgVar
  106. if(!oneSProgVar)
  107. {
  108. oneSProgVar = uni;
  109. }
  110. // Sanity check: All the sprog vars need to have same GL data type
  111. if(oneSProgVar->getGlDataType() != uni->getGlDataType()
  112. || oneSProgVar->getType() != uni->getType())
  113. {
  114. throw ANKI_EXCEPTION("Incompatible shader "
  115. "program variables: "
  116. + shaderProgVarName);
  117. }
  118. }
  119. }
  120. // Extra sanity checks
  121. if(!oneSProgVar)
  122. {
  123. throw ANKI_EXCEPTION("Variable not found in "
  124. "any of the shader programs: "
  125. + shaderProgVarName);
  126. }
  127. }
  128. //==============================================================================
  129. U32 MaterialVariable::getArraySize() const
  130. {
  131. return oneSProgVar->getSize();
  132. }
  133. //==============================================================================
  134. // Material =
  135. //==============================================================================
  136. //==============================================================================
  137. Material::Material()
  138. {}
  139. //==============================================================================
  140. Material::~Material()
  141. {}
  142. //==============================================================================
  143. void Material::load(const char* filename)
  144. {
  145. fname = filename;
  146. try
  147. {
  148. XmlDocument doc;
  149. doc.loadFile(filename);
  150. parseMaterialTag(doc.getChildElement("material"));
  151. }
  152. catch(std::exception& e)
  153. {
  154. throw ANKI_EXCEPTION("Failed to load file: " + filename) << e;
  155. }
  156. }
  157. //==============================================================================
  158. void Material::parseMaterialTag(const XmlElement& materialEl)
  159. {
  160. // passes
  161. //
  162. XmlElement passEl = materialEl.getChildElementOptional("passes");
  163. if(passEl)
  164. {
  165. passes = StringList::splitString(passEl.getText(), ' ');
  166. }
  167. else
  168. {
  169. ANKI_LOGW("<passes> is not defined. Expect errors later");
  170. passes.push_back("DUMMY");
  171. }
  172. // levelsOfDetail
  173. //
  174. XmlElement lodEl = materialEl.getChildElementOptional("levelsOfDetail");
  175. if(lodEl)
  176. {
  177. int tmp = lodEl.getInt();
  178. levelsOfDetail = (tmp < 1) ? 1 : tmp;
  179. }
  180. else
  181. {
  182. levelsOfDetail = 1;
  183. }
  184. // shadow
  185. //
  186. XmlElement shadowEl = materialEl.getChildElementOptional("shadow");
  187. if(shadowEl)
  188. {
  189. shadow = shadowEl.getInt();
  190. }
  191. // blendFunctions
  192. //
  193. XmlElement blendFunctionsEl =
  194. materialEl.getChildElementOptional("blendFunctions");
  195. Bool disableDepthPass = false;
  196. if(blendFunctionsEl)
  197. {
  198. // sFactor
  199. blendingSfactor = blendToEnum(
  200. blendFunctionsEl.getChildElement("sFactor").getText());
  201. // dFactor
  202. blendingDfactor = blendToEnum(
  203. blendFunctionsEl.getChildElement("dFactor").getText());
  204. disableDepthPass = true;
  205. }
  206. // depthTesting
  207. //
  208. XmlElement depthTestingEl =
  209. materialEl.getChildElementOptional("depthTesting");
  210. if(depthTestingEl)
  211. {
  212. depthTesting = depthTestingEl.getInt();
  213. }
  214. // wireframe
  215. //
  216. XmlElement wireframeEl = materialEl.getChildElementOptional("wireframe");
  217. if(wireframeEl)
  218. {
  219. wireframe = wireframeEl.getInt();
  220. }
  221. // shaderProgram
  222. //
  223. XmlElement shaderProgramEl = materialEl.getChildElement("shaderProgram");
  224. MaterialShaderProgramCreator mspc(
  225. shaderProgramEl, ANKI_RENDERER_USE_MATERIAL_UBOS);
  226. for(U32 level = 0; level < levelsOfDetail; ++level)
  227. {
  228. for(U32 pid = 0; pid < PASS_COUNT; ++pid)
  229. {
  230. if(disableDepthPass && pid == DEPTH_PASS)
  231. {
  232. continue;
  233. }
  234. std::stringstream src;
  235. src << "#define LOD " << level << "\n"
  236. << "#define PASS_" << passNames[pid] << "\n"
  237. << MainRendererSingleton::get().getShaderPostProcessorString()
  238. << "\n"
  239. << mspc.getShaderProgramSource() << std::endl;
  240. std::string filename =
  241. createShaderProgSourceToCache(src.str().c_str());
  242. ShaderProgramResourcePointer* pptr =
  243. new ShaderProgramResourcePointer;
  244. pptr->load(filename.c_str());
  245. ShaderProgram* sprog = pptr->get();
  246. progs.push_back(pptr);
  247. eSProgs[PassLevelKey(pid, level)] = sprog;
  248. }
  249. }
  250. populateVariables(mspc);
  251. // Create hash
  252. //
  253. std::hash<std::string> stringHash;
  254. hash = stringHash(mspc.getShaderProgramSource());
  255. }
  256. //==============================================================================
  257. std::string Material::createShaderProgSourceToCache(const std::string& source)
  258. {
  259. // Create the hash
  260. std::hash<std::string> stringHash;
  261. std::size_t h = stringHash(source);
  262. std::string prefix = std::to_string(h);
  263. // Create path
  264. std::string newfPathName =
  265. AppSingleton::get().getCachePath() + "/mtl_" + prefix + ".glsl";
  266. // If file not exists write it
  267. if(!File::fileExists(newfPathName.c_str()))
  268. {
  269. // If not create it
  270. File f(newfPathName.c_str(), File::OF_WRITE);
  271. f.writeText("%s\n", source.c_str());
  272. }
  273. return newfPathName;
  274. }
  275. //==============================================================================
  276. void Material::populateVariables(const MaterialShaderProgramCreator& mspc)
  277. {
  278. const char* blockName = "commonBlock";
  279. // Get default block
  280. commonUniformBlock = (*progs[0])->tryFindUniformBlock(blockName);
  281. // Get all names of all the uniforms. Dont duplicate
  282. //
  283. std::map<std::string, GLenum> allVarNames;
  284. for(const ShaderProgramResourcePointer* sProg : progs)
  285. {
  286. for(const ShaderProgramUniformVariable& v :
  287. (*sProg)->getUniformVariables())
  288. {
  289. #if ANKI_RENDERER_USE_MATERIAL_UBOS
  290. const ShaderProgramUniformBlock* bl = v.getUniformBlock();
  291. if(bl == nullptr)
  292. {
  293. ANKI_ASSERT(v.getGlDataType() == GL_SAMPLER_2D);
  294. }
  295. else
  296. {
  297. ANKI_ASSERT(bl->getName() == blockName);
  298. }
  299. #endif
  300. allVarNames[v.getName()] = v.getGlDataType();
  301. }
  302. }
  303. // Now combine
  304. //
  305. const PtrVector<MaterialShaderProgramCreator::Input>& invars =
  306. mspc.getInputVariables();
  307. for(std::map<std::string, GLenum>::value_type& it : allVarNames)
  308. {
  309. const std::string& name = it.first;
  310. GLenum dataType = it.second;
  311. // Find var from XML
  312. const MaterialShaderProgramCreator::Input* inpvar = nullptr;
  313. for(auto x : invars)
  314. {
  315. if(name == x->name)
  316. {
  317. inpvar = x;
  318. break;
  319. }
  320. }
  321. if(inpvar == nullptr)
  322. {
  323. throw ANKI_EXCEPTION("Uniform not found in the inputs: " + name);
  324. }
  325. MaterialVariable* v = nullptr;
  326. const char* n = name.c_str(); // Var name
  327. switch(dataType)
  328. {
  329. // sampler2D
  330. case GL_SAMPLER_2D:
  331. v = new MaterialVariableTemplate<TextureResourcePointer>(
  332. n, eSProgs);
  333. break;
  334. // F32
  335. case GL_FLOAT:
  336. v = new MaterialVariableTemplate<F32>(n, eSProgs);
  337. break;
  338. // vec2
  339. case GL_FLOAT_VEC2:
  340. v = new MaterialVariableTemplate<Vec2>(n, eSProgs);
  341. break;
  342. // vec3
  343. case GL_FLOAT_VEC3:
  344. v = new MaterialVariableTemplate<Vec3>(n, eSProgs);
  345. break;
  346. // vec4
  347. case GL_FLOAT_VEC4:
  348. v = new MaterialVariableTemplate<Vec4>(n, eSProgs);
  349. break;
  350. // mat3
  351. case GL_FLOAT_MAT3:
  352. v = new MaterialVariableTemplate<Mat3>(n, eSProgs);
  353. break;
  354. // mat4
  355. case GL_FLOAT_MAT4:
  356. v = new MaterialVariableTemplate<Mat4>(n, eSProgs);
  357. break;
  358. // default is error
  359. default:
  360. ANKI_ASSERT(0);
  361. }
  362. // Set value
  363. if(inpvar->value.size() != 0)
  364. {
  365. const StringList& value = inpvar->value;
  366. ANKI_ASSERT(inpvar->arraySize <= 1 && "Arrays not supported");
  367. // Get the value
  368. switch(dataType)
  369. {
  370. // sampler2D
  371. case GL_SAMPLER_2D:
  372. {
  373. TextureResourcePointer tp(value[0].c_str());
  374. v->setValues(&tp, 1);
  375. }
  376. break;
  377. // Math types
  378. case GL_FLOAT:
  379. case GL_FLOAT_VEC2:
  380. case GL_FLOAT_VEC3:
  381. case GL_FLOAT_VEC4:
  382. {
  383. SetMaterialVariableValuesVisitor vis(value);
  384. v->acceptVisitor(vis);
  385. }
  386. break;
  387. // default is error
  388. default:
  389. ANKI_ASSERT(0);
  390. }
  391. }
  392. vars.push_back(v);
  393. nameToVar[v->getName().c_str()] = v;
  394. }
  395. }
  396. } // end namespace anki