Compiler.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. #include <irrlicht/irrlicht.h>
  2. #include <iostream>
  3. #include <fstream>
  4. #include <string>
  5. #include <utility>
  6. // filesystem
  7. #include <WallpaperEngine/FileSystem/FileSystem.h>
  8. // shader compiler
  9. #include <WallpaperEngine/Render/Shaders/Compiler.h>
  10. #include <WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector3.h>
  11. #include <WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantInteger.h>
  12. #include <WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantFloat.h>
  13. #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariable.h"
  14. #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableFloat.h"
  15. #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableInteger.h"
  16. #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector2.h"
  17. #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector3.h"
  18. #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector4.h"
  19. using namespace WallpaperEngine::Core;
  20. using namespace WallpaperEngine::Assets;
  21. namespace WallpaperEngine::Render::Shaders
  22. {
  23. Compiler::Compiler (
  24. CContainer* container,
  25. std::string filename,
  26. Type type,
  27. std::map<std::string, int> combos,
  28. const std::map<std::string, CShaderConstant*>& constants,
  29. bool recursive) :
  30. m_combos (combos),
  31. m_recursive (recursive),
  32. m_type (type),
  33. m_file (std::move(filename)),
  34. m_error (""),
  35. m_errorInfo (""),
  36. m_constants (constants),
  37. m_container (container)
  38. {
  39. if (type == Type_Vertex)
  40. this->m_content = this->m_container->readVertexShader (this->m_file);
  41. else if (type == Type_Pixel)
  42. this->m_content = this->m_container->readFragmentShader (this->m_file);
  43. else if (type == Type_Include)
  44. this->m_content = this->m_container->readIncludeShader (this->m_file);
  45. }
  46. bool Compiler::peekString(std::string str, std::string::const_iterator& it)
  47. {
  48. std::string::const_iterator check = str.begin();
  49. std::string::const_iterator cur = it;
  50. while (cur != this->m_content.end () && check != str.end ())
  51. {
  52. if (*cur != *check) return false;
  53. cur ++; check ++;
  54. }
  55. if (cur == this->m_content.end ())
  56. {
  57. return false;
  58. }
  59. if (check != str.end ())
  60. {
  61. return false;
  62. }
  63. it = cur;
  64. return true;
  65. }
  66. bool Compiler::expectSemicolon (std::string::const_iterator& it)
  67. {
  68. if (*it != ';')
  69. {
  70. this->m_error = true;
  71. this->m_errorInfo = "Expected semicolon but got " + *it;
  72. return false;
  73. }
  74. it ++;
  75. return true;
  76. }
  77. void Compiler::ignoreSpaces(std::string::const_iterator &it)
  78. {
  79. while (it != this->m_content.end() && (*it == ' ' || *it == '\t')) it ++;
  80. }
  81. void Compiler::ignoreUpToNextLineFeed (std::string::const_iterator& it)
  82. {
  83. while (it != this->m_content.end() && *it != '\n') it ++;
  84. }
  85. void Compiler::ignoreUpToBlockCommentEnd (std::string::const_iterator& it)
  86. {
  87. while (it != this->m_content.end() && this->peekString ("*/", it) == false) it ++;
  88. }
  89. std::string Compiler::extractType (std::string::const_iterator& it)
  90. {
  91. auto cur = sTypes.begin ();
  92. auto end = sTypes.end ();
  93. while (cur != end)
  94. {
  95. if (this->peekString (*cur, it) == true)
  96. {
  97. return *cur;
  98. }
  99. cur ++;
  100. }
  101. this->m_error = true;
  102. this->m_errorInfo = "Expected type";
  103. return "";
  104. }
  105. std::string Compiler::extractName (std::string::const_iterator& it)
  106. {
  107. std::string::const_iterator cur = it;
  108. std::string::const_iterator begin = cur;
  109. // first character has to be a valid alphabetic characer
  110. if (this->isChar (cur) == false && *cur != '_')
  111. {
  112. this->m_error = true;
  113. this->m_errorInfo = "Expected name doesn't start with a valid character";
  114. return "";
  115. }
  116. cur ++;
  117. while (cur != this->m_content.end () && (this->isChar (cur) == true || *cur == '_' || this->isNumeric (cur) == true)) cur ++;
  118. it = cur;
  119. return std::string (begin, cur);
  120. }
  121. std::string Compiler::extractArray(std::string::const_iterator &it, bool mustExists)
  122. {
  123. std::string::const_iterator cur = it;
  124. std::string::const_iterator begin = cur;
  125. if (*cur != '[')
  126. {
  127. if (mustExists == false)
  128. return "";
  129. this->m_error = true;
  130. this->m_errorInfo = "Expected an array but found nothing";
  131. return "";
  132. }
  133. cur ++;
  134. while (cur != this->m_content.end () && *cur != ']') cur ++;
  135. it = ++cur;
  136. return std::string (begin, cur);
  137. }
  138. bool Compiler::isChar (std::string::const_iterator& it)
  139. {
  140. return ((*it) >= 'A' && (*it) <= 'Z') || ((*it) >= 'a' && (*it) <= 'z');
  141. }
  142. bool Compiler::isNumeric (std::string::const_iterator& it)
  143. {
  144. return (*it) >= '0' && (*it) <= '9';
  145. }
  146. std::string Compiler::extractQuotedValue(std::string::const_iterator& it)
  147. {
  148. std::string::const_iterator cur = it;
  149. if (*cur != '"')
  150. {
  151. m_error = true;
  152. m_errorInfo = "Expected opening \" but got " + (*cur);
  153. return "";
  154. }
  155. cur ++;
  156. while (cur != this->m_content.end () && *cur != '\n' && *cur != '"') cur ++;
  157. if (cur == this->m_content.end ())
  158. {
  159. m_error = true;
  160. m_errorInfo = "Expected closing \" not found";
  161. it = cur;
  162. return "";
  163. }
  164. std::string filename = std::string (++it, cur);
  165. it = ++cur;
  166. return filename;
  167. }
  168. std::string Compiler::lookupShaderFile (std::string filename)
  169. {
  170. // now compile the new shader
  171. // do not include the default header (as it's already included in the parent)
  172. Compiler loader (this->m_container, std::move (filename), Type_Include, this->m_combos, this->m_constants, true);
  173. loader.precompile ();
  174. return loader.getCompiled ();
  175. }
  176. std::string& Compiler::getCompiled ()
  177. {
  178. return this->m_compiledContent;
  179. }
  180. void Compiler::precompile()
  181. {
  182. #define BREAK_IF_ERROR if (this->m_error == true) { throw std::runtime_error ("ERROR PRE-COMPILING SHADER" + this->m_errorInfo); }
  183. // parse the shader and find #includes and such things and translate them to the correct name
  184. // also remove any #version definition to prevent errors
  185. std::string::const_iterator it = this->m_content.begin ();
  186. // reset error indicator
  187. this->m_error = false;
  188. this->m_errorInfo = "";
  189. // search preprocessor macros and parse them
  190. while (it != this->m_content.end () && this->m_error == false)
  191. {
  192. if (*it == ' ' || *it == '\t' || *it == '\n' || *it == '\r' || *it == '\0' || *it == '{' || *it == '}' || *it == '[' || *it == ']' || *it == '.')
  193. {
  194. this->m_compiledContent += *it;
  195. it ++;
  196. }
  197. else if (*it == '#')
  198. {
  199. if (this->peekString ("#include", it) == true)
  200. {
  201. std::string filename = "";
  202. // ignore whitespaces
  203. this->ignoreSpaces (it); BREAK_IF_ERROR
  204. // extract value between quotes
  205. filename = this->extractQuotedValue (it); BREAK_IF_ERROR
  206. // try to find the file first
  207. this->m_compiledContent += "// begin of included from file " + filename + "\r\n";
  208. this->m_compiledContent += this->lookupShaderFile (filename);
  209. this->m_compiledContent += "\r\n// end of included from file " + filename + "\r\n";
  210. }
  211. else
  212. {
  213. this->m_compiledContent += '#';
  214. it ++;
  215. }
  216. }
  217. else if (*it == 'u')
  218. {
  219. // uniforms might have extra information for their values
  220. if (this->peekString ("uniform", it) == true)
  221. {
  222. this->ignoreSpaces (it);
  223. std::string type = this->extractType (it); BREAK_IF_ERROR
  224. this->ignoreSpaces (it);
  225. std::string name = this->extractName (it); BREAK_IF_ERROR
  226. this->ignoreSpaces (it);
  227. std::string array = this->extractArray (it, false); BREAK_IF_ERROR
  228. this->ignoreSpaces (it);
  229. this->expectSemicolon (it); BREAK_IF_ERROR
  230. this->ignoreSpaces (it);
  231. // check if there is any actual extra information and parse it
  232. if (this->peekString ("//", it) == true)
  233. {
  234. this->ignoreSpaces (it);
  235. std::string::const_iterator begin = it;
  236. this->ignoreUpToNextLineFeed (it);
  237. std::string configuration; configuration.append (begin, it);
  238. // parse the parameter information
  239. this->parseParameterConfiguration (type, name, configuration); BREAK_IF_ERROR
  240. this->m_compiledContent += "uniform " + type + " " + name + array + "; // " + configuration;
  241. }
  242. else
  243. {
  244. this->m_compiledContent += "uniform " + type + " " + name + array + ";";
  245. }
  246. }
  247. }
  248. /*else if (*it == 'a')
  249. {
  250. if (this->peekString ("attribute", it) == true)
  251. {
  252. this->ignoreSpaces (it);
  253. }
  254. }*/
  255. // else if (*it == 'a')
  256. // {
  257. // // find attribute definitions
  258. // if (this->peekString ("attribute", it) == true)
  259. // {
  260. // this->ignoreSpaces (it);
  261. // std::string type = this->extractType (it); BREAK_IF_ERROR
  262. // this->ignoreSpaces (it);
  263. // std::string name = this->extractName (it); BREAK_IF_ERROR
  264. // this->ignoreSpaces (it);
  265. // std::string array = this->extractArray (it, false); BREAK_IF_ERROR
  266. // this->ignoreSpaces (it);
  267. // this->expectSemicolon (it); BREAK_IF_ERROR
  268. //
  269. // this->m_compiledContent += "// attribute " + type + " " + name + array;
  270. // this->m_compiledContent += "; /* replaced by " + this->lookupReplaceSymbol (name) + " */";
  271. // }
  272. // else
  273. // {
  274. // // check for types first
  275. // std::string type = this->extractType (it);
  276. //
  277. // // types not found, try names
  278. // if (this->m_error == false)
  279. // {
  280. // this->m_compiledContent += type;
  281. // }
  282. // else
  283. // {
  284. // this->m_error = false;
  285. // std::string name = this->extractName (it);
  286. //
  287. // if (this->m_error == false)
  288. // {
  289. // // check if the name is a translated one or not
  290. // this->m_compiledContent += this->lookupReplaceSymbol (name);
  291. // }
  292. // else
  293. // {
  294. // this->m_error = false;
  295. // this->m_compiledContent += *it;
  296. // it ++;
  297. // }
  298. // }
  299. // }
  300. // }
  301. else if (*it == '/')
  302. {
  303. if (this->peekString ("//", it) == true)
  304. {
  305. std::string::const_iterator begin = it - 2;
  306. // is there a COMBO mark to take care of?
  307. this->ignoreSpaces (it);
  308. if (this->peekString ("[COMBO]", it) == true)
  309. {
  310. // parse combo json data to define the proper variables
  311. this->ignoreSpaces (it);
  312. begin = it;
  313. this->ignoreUpToNextLineFeed (it);
  314. std::string configuration; configuration.append (begin, it);
  315. this->m_compiledContent += "// [COMBO] " + configuration;
  316. this->parseComboConfiguration (configuration); BREAK_IF_ERROR;
  317. }
  318. else if (this->peekString ("[COMBO_OFF]", it) == true)
  319. {
  320. // parse combo json data to define the proper variables
  321. this->ignoreSpaces (it);
  322. begin = it;
  323. this->ignoreUpToNextLineFeed (it);
  324. std::string configuration; configuration.append (begin, it);
  325. this->m_compiledContent += "// [COMBO_OFF] " + configuration;
  326. this->parseComboConfiguration (configuration); BREAK_IF_ERROR;
  327. }
  328. else
  329. {
  330. this->ignoreUpToNextLineFeed (it);
  331. this->m_compiledContent.append (begin, it);
  332. }
  333. }
  334. else if (this->peekString ("/*", it) == true)
  335. {
  336. std::string::const_iterator begin = it - 2;
  337. this->ignoreUpToBlockCommentEnd (it);
  338. this->m_compiledContent.append (begin, it);
  339. }
  340. else
  341. {
  342. this->m_compiledContent += *it;
  343. it ++;
  344. }
  345. }
  346. else
  347. {
  348. // check for types first
  349. std::string type = this->extractType (it);
  350. // types not found, try names
  351. if (this->m_error == false)
  352. {
  353. this->m_compiledContent += type;
  354. }
  355. else
  356. {
  357. this->m_error = false;
  358. std::string name = this->extractName (it);
  359. if (this->m_error == false)
  360. {
  361. // check if the name is a translated one or not
  362. this->m_compiledContent += name;
  363. }
  364. else
  365. {
  366. this->m_error = false;
  367. this->m_compiledContent += *it++;
  368. }
  369. }
  370. }
  371. }
  372. std::string finalCode;
  373. if (this->m_recursive == false)
  374. {
  375. // add the opengl compatibility at the top
  376. finalCode = "#version 120\n"
  377. "#define highp\n"
  378. "#define mediump\n"
  379. "#define lowp\n"
  380. "#define mul(x, y) (y * x)\n"
  381. "#define frac fract\n"
  382. "#define CAST2(x) (vec2(x))\n"
  383. "#define CAST3(x) (vec3(x))\n"
  384. "#define CAST4(x) (vec4(x))\n"
  385. "#define CAST3X3(x) (mat3(x))\n"
  386. "#define saturate(x) (clamp(x, 0.0, 1.0))\n"
  387. "#define texSample2D texture2D\n"
  388. "#define texSample2DLod texture2DLod\n"
  389. "#define texture2DLod texture2D\n"
  390. "#define atan2 atan\n"
  391. "#define ddx dFdx\n"
  392. "#define ddy(x) dFdy(-(x))\n"
  393. "#define GLSL 1\n\n";
  394. // add combo values
  395. auto cur = this->m_combos.begin ();
  396. auto end = this->m_combos.end ();
  397. for (; cur != end; cur ++)
  398. {
  399. finalCode += "#define " + (*cur).first + " " + std::to_string ((*cur).second) + "\n";
  400. }
  401. }
  402. finalCode += this->m_compiledContent;
  403. if (this->m_recursive == false)
  404. {
  405. if (this->m_type == Type_Vertex)
  406. std::cout << "======================== COMPILED VERTEX SHADER " << this->m_file.c_str () << " ========================" << std::endl;
  407. else
  408. std::cout << "======================== COMPILED FRAGMENT SHADER " << this->m_file.c_str () << " ========================" << std::endl;
  409. std::cout << finalCode << std::endl;
  410. }
  411. // store the final final code here
  412. this->m_compiledContent = finalCode;
  413. #undef BREAK_IF_ERROR
  414. }
  415. void Compiler::parseComboConfiguration (const std::string& content)
  416. {
  417. json data = json::parse (content);
  418. auto combo = jsonFindRequired (data, "combo", "cannot parse combo information");
  419. auto defvalue = jsonFindRequired (data, "default", "cannot parse combo information");
  420. // add line feed just in case
  421. this->m_compiledContent += "\n";
  422. // check the combos
  423. std::map<std::string, int>::const_iterator entry = this->m_combos.find ((*combo).get <std::string> ());
  424. // if the combo was not found in the predefined values this means that the default value in the JSON data can be used
  425. // so only define the ones that are not already defined
  426. if (entry == this->m_combos.end ())
  427. {
  428. // if no combo is defined just load the default settings
  429. if ((*defvalue).is_number_float ())
  430. {
  431. throw std::runtime_error ("float combos not supported");
  432. }
  433. else if ((*defvalue).is_number_integer ())
  434. {
  435. this->m_combos.insert (std::make_pair <std::string, int> (*combo, (*defvalue).get <irr::s32> ()));
  436. }
  437. else if ((*defvalue).is_string ())
  438. {
  439. throw std::runtime_error ("string combos not supported");
  440. }
  441. else
  442. {
  443. throw std::runtime_error ("cannot parse combo information, unknown type");
  444. }
  445. }
  446. }
  447. void Compiler::parseParameterConfiguration (const std::string& type, const std::string& name, const std::string& content)
  448. {
  449. json data = json::parse (content);
  450. auto material = data.find ("material");
  451. auto defvalue = data.find ("default");
  452. auto range = data.find ("range");
  453. // this is not a real parameter
  454. if (material == data.end ())
  455. return;
  456. auto constant = this->m_constants.find (*material);
  457. if (constant == this->m_constants.end () && defvalue == data.end ())
  458. {
  459. if (type != "sampler2D")
  460. throw std::runtime_error ("cannot parse parameter data");
  461. }
  462. Variables::CShaderVariable* parameter = nullptr;
  463. // TODO: SUPPORT VALUES FOR ALL THESE TYPES
  464. if (type == "vec4")
  465. {
  466. parameter = new Variables::CShaderVariableVector4 (
  467. WallpaperEngine::Core::aToVector3 (*defvalue)
  468. );
  469. }
  470. else if (type == "vec3")
  471. {
  472. parameter = new Variables::CShaderVariableVector3 (
  473. constant == this->m_constants.end ()
  474. ? WallpaperEngine::Core::aToVector3 (*defvalue)
  475. : *(*constant).second->as <CShaderConstantVector3> ()->getValue ()
  476. );
  477. }
  478. else if (type == "vec2")
  479. {
  480. parameter = new Variables::CShaderVariableVector2 (
  481. WallpaperEngine::Core::aToVector2 (*defvalue)
  482. );
  483. }
  484. else if (type == "float")
  485. {
  486. irr::f32 value = 0;
  487. if (constant == this->m_constants.end ())
  488. value = (*defvalue).get <irr::f32> ();
  489. else if ((*constant).second->is <CShaderConstantFloat> () == true)
  490. value = *(*constant).second->as <CShaderConstantFloat> ()->getValue ();
  491. else if ((*constant).second->is <CShaderConstantInteger> () == true)
  492. value = *(*constant).second->as <CShaderConstantInteger> ()->getValue ();
  493. parameter = new Variables::CShaderVariableFloat (value);
  494. }
  495. else if (type == "int")
  496. {
  497. irr::s32 value = 0;
  498. if (constant == this->m_constants.end ())
  499. value = (*defvalue).get <irr::s32> ();
  500. else if ((*constant).second->is <CShaderConstantFloat> () == true)
  501. value = *(*constant).second->as <CShaderConstantFloat> ()->getValue ();
  502. else if ((*constant).second->is <CShaderConstantInteger> () == true)
  503. value = *(*constant).second->as <CShaderConstantInteger> ()->getValue ();
  504. parameter = new Variables::CShaderVariableInteger (value);
  505. }
  506. else if (type == "sampler2D")
  507. {
  508. // samplers can have special requirements, check what sampler we're working with and create definitions
  509. // if needed
  510. auto combo = data.find ("combo");
  511. if (combo != data.end ())
  512. {
  513. // TODO: CHECK WHAT TEXTURE THIS REFERS TO
  514. // add the new combo to the list
  515. this->m_combos.insert (std::make_pair<std::string, int> (*combo, 1));
  516. }
  517. // samplers are not saved, we can ignore them for now
  518. return;
  519. }
  520. else
  521. {
  522. this->m_error = true;
  523. this->m_errorInfo = "Unknown parameter type: " + type + " for " + name;
  524. return;
  525. }
  526. parameter->setIdentifierName (*material);
  527. parameter->setName (name);
  528. this->m_parameters.push_back (parameter);
  529. }
  530. Variables::CShaderVariable* Compiler::findParameter (const std::string& identifier)
  531. {
  532. auto cur = this->m_parameters.begin ();
  533. auto end = this->m_parameters.end ();
  534. for (; cur != end; cur ++)
  535. {
  536. if ((*cur)->getIdentifierName () == identifier)
  537. {
  538. return (*cur);
  539. }
  540. }
  541. return nullptr;
  542. }
  543. const std::vector <Variables::CShaderVariable*>& Compiler::getParameters () const
  544. {
  545. return this->m_parameters;
  546. }
  547. const std::map <std::string, int>& Compiler::getCombos () const
  548. {
  549. return this->m_combos;
  550. }
  551. std::vector<std::string> Compiler::sTypes =
  552. {
  553. "vec4", "vec3", "vec2", "float", "sampler2D", "mat4"
  554. };
  555. }