💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

580 lines
16 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. /*
  2. * Copyright (C) 2017 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper Source Code.
  5. *
  6. * Antkeeper Source Code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper Source Code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "material-loader.hpp"
  20. #include <algorithm>
  21. #include <fstream>
  22. #include <iostream>
  23. #include <sstream>
  24. #include <vector>
  25. MaterialLoader::MaterialLoader()
  26. {
  27. textureLoader.setGamma(1.0f);
  28. textureLoader.setMipmapChain(false);
  29. textureLoader.setMaxAnisotropy(1.0f);
  30. }
  31. MaterialLoader::~MaterialLoader()
  32. {
  33. unload();
  34. }
  35. void MaterialLoader::unload()
  36. {
  37. for (auto it = materialCache.begin(); it != materialCache.end(); ++it)
  38. {
  39. delete it->second;
  40. }
  41. materialCache.clear();
  42. for (auto it = texture2DCache.begin(); it != texture2DCache.end(); ++it)
  43. {
  44. delete it->second;
  45. }
  46. texture2DCache.clear();
  47. for (auto it = textureCubeCache.begin(); it != textureCubeCache.end(); ++it)
  48. {
  49. delete it->second;
  50. }
  51. textureCubeCache.clear();
  52. }
  53. Material* MaterialLoader::load(const std::string& filename)
  54. {
  55. // Check if material exists in cache
  56. auto it = materialCache.find(filename);
  57. if (it != materialCache.end())
  58. {
  59. return it->second;
  60. }
  61. // Allocate new material
  62. Material* material = new Material();
  63. // Open file
  64. std::ifstream file(filename.c_str(), std::ifstream::in);
  65. if (!file.is_open())
  66. {
  67. std::cerr << std::string("MaterialLoader::load(): Failed to open material file \"") << filename << std::string("\"") << std::endl;
  68. delete material;
  69. return nullptr;
  70. }
  71. std::string line;
  72. std::size_t lineNumber = 0;
  73. const std::string whitespace = " \t\r\n";
  74. // Parse lines
  75. while (file.good() && std::getline(file, line))
  76. {
  77. // Increment current line number
  78. ++lineNumber;
  79. // Skip empty lines
  80. if (line.empty())
  81. {
  82. continue;
  83. }
  84. // Find position of first character in the command
  85. std::size_t commandPosition = line.find_first_not_of(whitespace, 0);
  86. if (commandPosition == std::string::npos)
  87. {
  88. // Skip whitespace-only lines
  89. continue;
  90. }
  91. // Determine command type
  92. std::string command = line.substr(commandPosition, line.find_first_of(" \t=", commandPosition) - commandPosition);
  93. // Parse command
  94. if (command == "shader" || command == "flags")
  95. {
  96. // Find position of equals sign
  97. std::size_t equalsSignPosition = line.find_first_of("=", commandPosition);
  98. if (equalsSignPosition == std::string::npos)
  99. {
  100. // Skip lines with no equals sign
  101. std::cerr << std::string("MaterialLoader::load(): Invalid line ") << lineNumber << std::string(" in \"") << filename << std::string("\"") << std::endl;
  102. continue;
  103. }
  104. // Find position of first character in the value string
  105. std::size_t valueStartPosition = line.find_first_not_of(whitespace, equalsSignPosition + 1);
  106. if (valueStartPosition == std::string::npos)
  107. {
  108. // Skip lines with no value
  109. std::cerr << std::string("MaterialLoader::load(): Invalid line ") << lineNumber << std::string(" in \"") << filename << std::string("\"") << std::endl;
  110. continue;
  111. }
  112. // Find position the end of the value string
  113. std::size_t valueEndPosition = line.find_first_of(" \t;\r\n", valueStartPosition);
  114. // Determine value string
  115. std::string valueString;
  116. if (valueEndPosition == std::string::npos)
  117. {
  118. valueString = line.substr(valueStartPosition);
  119. }
  120. else
  121. {
  122. valueString = line.substr(valueStartPosition, valueEndPosition - valueStartPosition);
  123. }
  124. // Execute command
  125. if (command == "shader")
  126. {
  127. // Load shader
  128. Shader* shader = loadShader(valueString);
  129. if (!shader)
  130. {
  131. std::cerr << std::string("MaterialLoader::load(): Failed to load shader \"") << valueString << std::string("\" on line ") << lineNumber << std::string(" in \"") << filename << std::string("\"") << std::endl;
  132. }
  133. else
  134. {
  135. material->setShader(shader);
  136. }
  137. }
  138. else
  139. {
  140. // Parse flags
  141. std::uint64_t flags;
  142. std::stringstream stream;
  143. stream << valueString;
  144. stream >> flags;
  145. material->setFlags(flags);
  146. }
  147. }
  148. else if (command == "var")
  149. {
  150. // Find position of first character in variable name
  151. std::size_t variableNamePosition = line.find_first_not_of(whitespace, commandPosition + command.length());
  152. if (variableNamePosition == std::string::npos)
  153. {
  154. // Skip lines with no variable name
  155. std::cerr << std::string("MaterialLoader::load(): Invalid variable on line ") << lineNumber << std::string(" in \"") << filename << std::string("\"") << std::endl;
  156. continue;
  157. }
  158. // Find position of equals sign
  159. std::size_t equalsSignPosition = line.find_first_of("=", variableNamePosition);
  160. if (equalsSignPosition == std::string::npos)
  161. {
  162. // Skip lines with no equals sign
  163. std::cerr << std::string("MaterialLoader::load(): Invalid variable on line ") << lineNumber << std::string(" in \"") << filename << std::string("\"") << std::endl;
  164. continue;
  165. }
  166. // Find position of first character in variable type
  167. std::size_t variableTypePosition = line.find_first_not_of(whitespace, equalsSignPosition + 1);
  168. if (variableTypePosition == std::string::npos)
  169. {
  170. // Skip lines with no variable type definition
  171. std::cerr << std::string("MaterialLoader::load(): Invalid variable on line ") << lineNumber << std::string(" in \"") << filename << std::string("\"") << std::endl;
  172. continue;
  173. }
  174. // Count parentheses
  175. std::size_t leftParenthesisCount = std::count(line.begin() + variableNamePosition, line.end(), '(');
  176. std::size_t rightParenthesisCount = std::count(line.begin() + variableNamePosition, line.end(), ')');
  177. if (leftParenthesisCount != rightParenthesisCount || leftParenthesisCount == 0)
  178. {
  179. // Skip lines with invalid number of parentheses
  180. std::cerr << std::string("MaterialLoader::load(): Invalid variable on line ") << lineNumber << std::string(" in \"") << filename << std::string("\"") << std::endl;
  181. continue;
  182. }
  183. std::string variableName = line.substr(variableNamePosition, line.find_first_of(" \t=", variableNamePosition) - variableNamePosition);
  184. std::string variableType = line.substr(variableTypePosition, line.find_first_of(" \t[(", variableTypePosition) - variableTypePosition);
  185. std::size_t elementCount = leftParenthesisCount;
  186. std::size_t currentPosition = variableTypePosition;
  187. std::vector<std::vector<std::string>> elements;
  188. bool invalid = false;
  189. for (std::size_t i = 0; i < elementCount; ++i)
  190. {
  191. std::size_t leftParenthesisPosition = line.find_first_of("(", currentPosition);
  192. std::size_t rightParenthesisPosition = line.find_first_of(")", leftParenthesisPosition + 1);
  193. if (leftParenthesisPosition == std::string::npos || rightParenthesisPosition == std::string::npos)
  194. {
  195. invalid = true;
  196. break;
  197. }
  198. currentPosition = leftParenthesisPosition + 1;
  199. std::size_t argumentCount = std::count(line.begin() + leftParenthesisPosition + 1, line.begin() + rightParenthesisPosition, ',') + 1;
  200. std::vector<std::string> arguments;
  201. for (std::size_t j = 0; j < argumentCount; ++j)
  202. {
  203. std::size_t argumentStart = line.find_first_not_of(whitespace, currentPosition);
  204. std::size_t argumentEnd = line.find_first_of(" \t,)", argumentStart + 1);
  205. if (argumentStart == std::string::npos || argumentEnd == std::string::npos)
  206. {
  207. // Unable to parse argument
  208. invalid = true;
  209. break;
  210. }
  211. std::string argument = line.substr(argumentStart, argumentEnd - argumentStart);
  212. arguments.push_back(argument);
  213. currentPosition = argumentEnd + 1;
  214. }
  215. if (invalid)
  216. {
  217. // Unable to parse element
  218. break;
  219. }
  220. elements.push_back(arguments);
  221. currentPosition = rightParenthesisPosition + 1;
  222. }
  223. if (invalid)
  224. {
  225. // Unable to parse line
  226. continue;
  227. }
  228. if (variableType == "int")
  229. {
  230. ShaderInt* variable = material->addVariable<int>(variableName, elements.size());
  231. loadShaderInt(variable, elements);
  232. }
  233. else if (variableType == "float")
  234. {
  235. ShaderFloat* variable = material->addVariable<float>(variableName, elements.size());
  236. loadShaderFloat(variable, elements);
  237. }
  238. else if (variableType == "vec2")
  239. {
  240. ShaderVector2* variable = material->addVariable<Vector2>(variableName, elements.size());
  241. loadShaderVector2(variable, elements);
  242. }
  243. else if (variableType == "vec3")
  244. {
  245. ShaderVector3* variable = material->addVariable<Vector3>(variableName, elements.size());
  246. loadShaderVector3(variable, elements);
  247. }
  248. else if (variableType == "vec4")
  249. {
  250. ShaderVector4* variable = material->addVariable<Vector4>(variableName, elements.size());
  251. loadShaderVector4(variable, elements);
  252. }
  253. else if (variableType == "mat3")
  254. {
  255. ShaderMatrix3* variable = material->addVariable<Matrix3>(variableName, elements.size());
  256. loadShaderMatrix3(variable, elements);
  257. }
  258. else if (variableType == "mat4")
  259. {
  260. ShaderMatrix4* variable = material->addVariable<Matrix4>(variableName, elements.size());
  261. loadShaderMatrix4(variable, elements);
  262. }
  263. else if (variableType == "texture")
  264. {
  265. ShaderTexture2D* variable = material->addVariable<const Texture2D*>(variableName, elements.size());
  266. loadShaderTexture2D(variable, elements);
  267. }
  268. else if (variableType == "textureCube")
  269. {
  270. ShaderTextureCube* variable = material->addVariable<const TextureCube*>(variableName, elements.size());
  271. loadShaderTextureCube(variable, elements);
  272. }
  273. }
  274. else
  275. {
  276. if (command[0] == '#')
  277. {
  278. // Skip comments
  279. continue;
  280. }
  281. // Invalid command
  282. std::cerr << std::string("MaterialLoader::load(): Invalid command \"") << command << std::string("\" on line ") << lineNumber << std::string(" in \"") << filename << std::string("\"") << std::endl;
  283. }
  284. }
  285. // Close file
  286. file.close();
  287. // Add material to cache
  288. materialCache[filename] = material;
  289. return material;
  290. }
  291. Shader* MaterialLoader::loadShader(const std::string& filename)
  292. {
  293. auto it = shaderCache.find(filename);
  294. if (it != shaderCache.end())
  295. {
  296. return it->second;
  297. }
  298. std::string fullFilename = std::string("data/shaders/") + filename;
  299. std::cout << std::string("Loading shader \"") << fullFilename << std::string("\"\n");
  300. // Load shader
  301. Shader* shader = new Shader();
  302. if (!shader->loadSource(fullFilename))
  303. {
  304. delete shader;
  305. return nullptr;
  306. }
  307. // Add shader to cache
  308. shaderCache[filename] = shader;
  309. return shader;
  310. }
  311. Texture2D* MaterialLoader::loadTexture2D(const std::string& filename)
  312. {
  313. // Check if texture exists in cache
  314. auto it = texture2DCache.find(filename);
  315. if (it != texture2DCache.end())
  316. {
  317. return it->second;
  318. }
  319. std::string fullFilename = std::string("data/textures/") + filename;
  320. // Load texture
  321. Texture2D* texture = textureLoader.load2D(fullFilename);
  322. if (!texture)
  323. {
  324. std::cerr << std::string("MaterialLoader::loadTexture2D(): Failed to load texture file \"") << fullFilename << std::string("\"") << std::endl;
  325. return nullptr;
  326. }
  327. // Add texture to cache
  328. texture2DCache[filename] = texture;
  329. return texture;
  330. }
  331. TextureCube* MaterialLoader::loadTextureCube(const std::string& filename)
  332. {
  333. // Check if texture exists in cache
  334. auto it = textureCubeCache.find(filename);
  335. if (it != textureCubeCache.end())
  336. {
  337. return it->second;
  338. }
  339. std::string fullFilename = std::string("data/textures/") + filename;
  340. // Load texture
  341. TextureCube* texture = textureLoader.loadCube(fullFilename);
  342. if (!texture)
  343. {
  344. std::cerr << std::string("MaterialLoader::loadTextureCube(): Failed to load texture file \"") << fullFilename << std::string("\"") << std::endl;
  345. return nullptr;
  346. }
  347. // Add texture to cache
  348. textureCubeCache[filename] = texture;
  349. return texture;
  350. }
  351. bool MaterialLoader::loadShaderInt(ShaderInt* variable, const std::vector<std::vector<std::string>>& elements)
  352. {
  353. for (int i = 0; i < elements.size(); ++i)
  354. {
  355. int value;
  356. std::stringstream stream;
  357. stream << elements[i][0];
  358. stream >> value;
  359. variable->setValue(i, value);
  360. }
  361. return true;
  362. }
  363. bool MaterialLoader::loadShaderFloat(ShaderFloat* variable, const std::vector<std::vector<std::string>>& elements)
  364. {
  365. for (int i = 0; i < elements.size(); ++i)
  366. {
  367. float value;
  368. std::stringstream stream;
  369. stream << elements[i][0];
  370. stream >> value;
  371. variable->setValue(i, value);
  372. }
  373. return true;
  374. }
  375. bool MaterialLoader::loadShaderVector2(ShaderVector2* variable, const std::vector<std::vector<std::string>>& elements)
  376. {
  377. for (int i = 0; i < elements.size(); ++i)
  378. {
  379. Vector2 value;
  380. for (int j = 0; j < 2; ++j)
  381. {
  382. std::stringstream stream;
  383. stream << elements[i][j];
  384. stream >> value[j];
  385. }
  386. variable->setValue(i, value);
  387. }
  388. return true;
  389. }
  390. bool MaterialLoader::loadShaderVector3(ShaderVector3* variable, const std::vector<std::vector<std::string>>& elements)
  391. {
  392. for (int i = 0; i < elements.size(); ++i)
  393. {
  394. Vector3 value;
  395. for (int j = 0; j < 3; ++j)
  396. {
  397. std::stringstream stream;
  398. stream << elements[i][j];
  399. stream >> value[j];
  400. }
  401. variable->setValue(i, value);
  402. }
  403. return true;
  404. }
  405. bool MaterialLoader::loadShaderVector4(ShaderVector4* variable, const std::vector<std::vector<std::string>>& elements)
  406. {
  407. for (int i = 0; i < elements.size(); ++i)
  408. {
  409. Vector4 value;
  410. for (int j = 0; j < 4; ++j)
  411. {
  412. std::stringstream stream;
  413. stream << elements[i][j];
  414. stream >> value[j];
  415. }
  416. variable->setValue(i, value);
  417. }
  418. return true;
  419. }
  420. bool MaterialLoader::loadShaderMatrix3(ShaderMatrix3* variable, const std::vector<std::vector<std::string>>& elements)
  421. {
  422. for (int i = 0; i < elements.size(); ++i)
  423. {
  424. Matrix3 value;
  425. for (int j = 0; j < 3; ++j)
  426. {
  427. for (int k = 0; k < 3; ++k)
  428. {
  429. std::stringstream stream;
  430. stream << elements[i][k * 3 + j];
  431. stream >> value[j][k];
  432. }
  433. }
  434. variable->setValue(i, value);
  435. }
  436. return true;
  437. }
  438. bool MaterialLoader::loadShaderMatrix4(ShaderMatrix4* variable, const std::vector<std::vector<std::string>>& elements)
  439. {
  440. for (int i = 0; i < elements.size(); ++i)
  441. {
  442. Matrix4 value;
  443. for (int j = 0; j < 4; ++j)
  444. {
  445. for (int k = 0; k < 4; ++k)
  446. {
  447. std::stringstream stream;
  448. stream << elements[i][k * 4 + j];
  449. stream >> value[j][k];
  450. }
  451. }
  452. variable->setValue(i, value);
  453. }
  454. return true;
  455. }
  456. bool MaterialLoader::loadShaderTexture2D(ShaderTexture2D* variable, const std::vector<std::vector<std::string>>& elements)
  457. {
  458. for (int i = 0; i < elements.size(); ++i)
  459. {
  460. std::string filename;
  461. std::stringstream stream;
  462. stream << elements[i][0];
  463. stream >> filename;
  464. Texture2D* value = loadTexture2D(filename);
  465. if (!value)
  466. {
  467. std::cerr << std::string("MaterialLoader::loadShaderTexture2D(): Failed to load 2D texture \"") << filename << std::string("\"") << std::endl;
  468. return false;
  469. }
  470. variable->setValue(i, value);
  471. }
  472. return true;
  473. }
  474. bool MaterialLoader::loadShaderTextureCube(ShaderTextureCube* variable, const std::vector<std::vector<std::string>>& elements)
  475. {
  476. for (int i = 0; i < elements.size(); ++i)
  477. {
  478. std::string filename;
  479. std::stringstream stream;
  480. stream << elements[i][0];
  481. stream >> filename;
  482. TextureCube* value = loadTextureCube(filename);
  483. if (!value)
  484. {
  485. std::cerr << std::string("MaterialLoader::loadShaderTextureCube(): Failed to load cube texture \"") << filename << std::string("\"") << std::endl;
  486. return false;
  487. }
  488. variable->setValue(i, value);
  489. }
  490. return true;
  491. }