/xbmc/visualizations/XBMCProjectM/libprojectM/Preset.cpp

http://github.com/xbmc/xbmc · C++ · 544 lines · 324 code · 155 blank · 65 comment · 76 complexity · 5c6540c7edc02337597093254552177a MD5 · raw file

  1. /**
  2. * projectM -- Milkdrop-esque visualisation SDK
  3. * Copyright (C)2003-2004 projectM Team
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Lesser General Public
  7. * License as published by the Free Software Foundation; either
  8. * version 2.1 of the License, or (at your option) any later version.
  9. *
  10. * This library is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with this library; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. * See 'LICENSE.txt' included within this release
  19. *
  20. */
  21. #include <stdio.h>
  22. #include <string.h>
  23. #include <stdlib.h>
  24. #include <fcntl.h>
  25. #ifdef WIN32
  26. #include "win32-dirent.h"
  27. #else
  28. #include <dirent.h>
  29. #endif /** WIN32 */
  30. #include <time.h>
  31. #include "Preset.hpp"
  32. #include "Parser.hpp"
  33. #include "ParamUtils.hpp"
  34. #include "InitCondUtils.hpp"
  35. #include "fatal.h"
  36. #include <iostream>
  37. #include <sstream>
  38. Preset::Preset(std::istream & in, const std::string & presetName, PresetInputs & presetInputs, PresetOutputs & presetOutputs):
  39. builtinParams(presetInputs, presetOutputs),
  40. m_presetName(presetName),
  41. m_presetOutputs(presetOutputs),
  42. m_presetInputs(presetInputs)
  43. {
  44. m_presetOutputs.customWaves.clear();
  45. m_presetOutputs.customShapes.clear();
  46. initialize(in);
  47. }
  48. Preset::Preset(const std::string & absoluteFilePath, const std::string & presetName, PresetInputs & presetInputs, PresetOutputs & presetOutputs):
  49. builtinParams(presetInputs, presetOutputs),
  50. m_absoluteFilePath(absoluteFilePath),
  51. m_presetName(presetName),
  52. m_presetOutputs(presetOutputs),
  53. m_presetInputs(presetInputs)
  54. {
  55. m_presetOutputs.customWaves.clear();
  56. m_presetOutputs.customShapes.clear();
  57. initialize(absoluteFilePath);
  58. }
  59. Preset::~Preset()
  60. {
  61. Algorithms::traverse<Algorithms::TraverseFunctors::DeleteFunctor<InitCond> >(init_cond_tree);
  62. Algorithms::traverse<Algorithms::TraverseFunctors::DeleteFunctor<InitCond> >(per_frame_init_eqn_tree);
  63. Algorithms::traverse<Algorithms::TraverseFunctors::DeleteFunctor<PerPixelEqn> >(per_pixel_eqn_tree);
  64. Algorithms::traverseVector<Algorithms::TraverseFunctors::DeleteFunctor<PerFrameEqn> >(per_frame_eqn_tree);
  65. Algorithms::traverse<Algorithms::TraverseFunctors::DeleteFunctor<Param> >(user_param_tree);
  66. for (PresetOutputs::cwave_container::iterator pos = customWaves.begin(); pos != customWaves.end(); ++pos)
  67. {
  68. delete(*pos);
  69. }
  70. for (PresetOutputs::cshape_container::iterator pos = customShapes.begin(); pos != customShapes.end(); ++pos)
  71. {
  72. delete(*pos);
  73. }
  74. }
  75. /* Adds a per pixel equation according to its string name. This
  76. will be used only by the parser */
  77. int Preset::add_per_pixel_eqn(char * name, GenExpr * gen_expr)
  78. {
  79. PerPixelEqn * per_pixel_eqn = NULL;
  80. int index;
  81. Param * param = NULL;
  82. assert(gen_expr);
  83. assert(name);
  84. if (PER_PIXEL_EQN_DEBUG) printf("add_per_pixel_eqn: per pixel equation (name = \"%s\")\n", name);
  85. /* Search for the parameter so we know what matrix the per pixel equation is referencing */
  86. param = ParamUtils::find(name, &this->builtinParams, &this->user_param_tree);
  87. if ( !param )
  88. {
  89. if (PER_PIXEL_EQN_DEBUG) printf("add_per_pixel_eqn: failed to allocate a new parameter!\n");
  90. return PROJECTM_FAILURE;
  91. }
  92. index = per_pixel_eqn_tree.size();
  93. /* Create the per pixel equation given the index, parameter, and general expression */
  94. if ((per_pixel_eqn = new PerPixelEqn(index, param, gen_expr)) == NULL)
  95. {
  96. if (PER_PIXEL_EQN_DEBUG) printf("add_per_pixel_eqn: failed to create new per pixel equation!\n");
  97. return PROJECTM_FAILURE;
  98. }
  99. /* Insert the per pixel equation into the preset per pixel database */
  100. std::pair<std::map<int, PerPixelEqn*>::iterator, bool> inserteeOption = per_pixel_eqn_tree.insert
  101. (std::make_pair(per_pixel_eqn->index, per_pixel_eqn));
  102. if (!inserteeOption.second)
  103. {
  104. printf("failed to add per pixel eqn!\n");
  105. delete(per_pixel_eqn);
  106. return PROJECTM_FAILURE;
  107. }
  108. /* Done */
  109. return PROJECTM_SUCCESS;
  110. }
  111. void Preset::evalCustomShapeInitConditions()
  112. {
  113. for (PresetOutputs::cshape_container::iterator pos = customShapes.begin(); pos != customShapes.end(); ++pos) {
  114. assert(*pos);
  115. (*pos)->evalInitConds();
  116. }
  117. }
  118. void Preset::evalCustomWaveInitConditions()
  119. {
  120. for (PresetOutputs::cwave_container::iterator pos = customWaves.begin(); pos != customWaves.end(); ++pos) {
  121. assert(*pos);
  122. (*pos)->evalInitConds();
  123. }
  124. }
  125. void Preset::evalCustomWavePerFrameEquations()
  126. {
  127. for (PresetOutputs::cwave_container::iterator pos = customWaves.begin(); pos != customWaves.end(); ++pos)
  128. {
  129. std::map<std::string, InitCond*> & init_cond_tree = (*pos)->init_cond_tree;
  130. for (std::map<std::string, InitCond*>::iterator _pos = init_cond_tree.begin(); _pos != init_cond_tree.end(); ++_pos)
  131. {
  132. assert(_pos->second);
  133. _pos->second->evaluate();
  134. }
  135. std::vector<PerFrameEqn*> & per_frame_eqn_tree = (*pos)->per_frame_eqn_tree;
  136. for (std::vector<PerFrameEqn*>::iterator _pos = per_frame_eqn_tree.begin(); _pos != per_frame_eqn_tree.end(); ++_pos)
  137. {
  138. (*_pos)->evaluate();
  139. }
  140. }
  141. }
  142. void Preset::evalCustomShapePerFrameEquations()
  143. {
  144. for (PresetOutputs::cshape_container::iterator pos = customShapes.begin(); pos != customShapes.end(); ++pos)
  145. {
  146. std::map<std::string, InitCond*> & init_cond_tree = (*pos)->init_cond_tree;
  147. for (std::map<std::string, InitCond*>::iterator _pos = init_cond_tree.begin(); _pos != init_cond_tree.end(); ++_pos)
  148. {
  149. assert(_pos->second);
  150. _pos->second->evaluate();
  151. }
  152. std::vector<PerFrameEqn*> & per_frame_eqn_tree = (*pos)->per_frame_eqn_tree;
  153. for (std::vector<PerFrameEqn*>::iterator _pos = per_frame_eqn_tree.begin(); _pos != per_frame_eqn_tree.end(); ++_pos)
  154. {
  155. (*_pos)->evaluate();
  156. }
  157. }
  158. }
  159. void Preset::evalPerFrameInitEquations()
  160. {
  161. for (std::map<std::string, InitCond*>::iterator pos = per_frame_init_eqn_tree.begin(); pos != per_frame_init_eqn_tree.end(); ++pos)
  162. {
  163. assert(pos->second);
  164. pos->second->evaluate();
  165. }
  166. }
  167. void Preset::evalPerFrameEquations()
  168. {
  169. for (std::map<std::string, InitCond*>::iterator pos = init_cond_tree.begin(); pos != init_cond_tree.end(); ++pos)
  170. {
  171. assert(pos->second);
  172. pos->second->evaluate();
  173. }
  174. for (std::vector<PerFrameEqn*>::iterator pos = per_frame_eqn_tree.begin(); pos != per_frame_eqn_tree.end(); ++pos)
  175. {
  176. (*pos)->evaluate();
  177. }
  178. }
  179. void Preset::preloadInitialize() {
  180. /// @note commented this out because it should be unnecessary
  181. // Clear equation trees
  182. //init_cond_tree.clear();
  183. //user_param_tree.clear();
  184. //per_frame_eqn_tree.clear();
  185. //per_pixel_eqn_tree.clear();
  186. //per_frame_init_eqn_tree.clear();
  187. }
  188. void Preset::postloadInitialize() {
  189. /* It's kind of ugly to reset these values here. Should definitely be placed in the parser somewhere */
  190. this->per_frame_eqn_count = 0;
  191. this->per_frame_init_eqn_count = 0;
  192. this->loadBuiltinParamsUnspecInitConds();
  193. this->loadCustomWaveUnspecInitConds();
  194. this->loadCustomShapeUnspecInitConds();
  195. /// @bug are you handling all the q variables conditions? in particular, the un-init case?
  196. //m_presetOutputs.q1 = 0;
  197. //m_presetOutputs.q2 = 0;
  198. //m_presetOutputs.q3 = 0;
  199. //m_presetOutputs.q4 = 0;
  200. //m_presetOutputs.q5 = 0;
  201. //m_presetOutputs.q6 = 0;
  202. //m_presetOutputs.q7 = 0;
  203. //m_presetOutputs.q8 = 0;
  204. }
  205. void Preset::initialize(const std::string & pathname)
  206. {
  207. int retval;
  208. preloadInitialize();
  209. if (PRESET_DEBUG)
  210. std::cerr << "[Preset] loading file \"" << pathname << "\"..." << std::endl;
  211. if ((retval = loadPresetFile(pathname)) < 0)
  212. {
  213. if (PRESET_DEBUG)
  214. std::cerr << "[Preset] failed to load file \"" <<
  215. pathname << "\"!" << std::endl;
  216. /// @bug how should we handle this problem? a well define exception?
  217. throw retval;
  218. }
  219. postloadInitialize();
  220. }
  221. void Preset::initialize(std::istream & in)
  222. {
  223. int retval;
  224. preloadInitialize();
  225. if ((retval = readIn(in)) < 0)
  226. {
  227. if (PRESET_DEBUG)
  228. std::cerr << "[Preset] failed to load from stream " << std::endl;
  229. /// @bug how should we handle this problem? a well define exception?
  230. throw retval;
  231. }
  232. postloadInitialize();
  233. }
  234. void Preset::loadBuiltinParamsUnspecInitConds() {
  235. InitCondUtils::LoadUnspecInitCond loadUnspecInitCond(this->init_cond_tree, this->per_frame_init_eqn_tree);
  236. this->builtinParams.traverse(loadUnspecInitCond);
  237. Algorithms::traverse(user_param_tree, loadUnspecInitCond);
  238. }
  239. void Preset::loadCustomWaveUnspecInitConds()
  240. {
  241. for (PresetOutputs::cwave_container::iterator pos = customWaves.begin(); pos != customWaves.end(); ++pos)
  242. {
  243. assert(*pos);
  244. (*pos)->loadUnspecInitConds();
  245. }
  246. }
  247. void Preset::loadCustomShapeUnspecInitConds()
  248. {
  249. for (PresetOutputs::cshape_container::iterator pos = customShapes.begin(); pos != customShapes.end(); ++pos)
  250. {
  251. assert(*pos);
  252. (*pos)->loadUnspecInitConds();
  253. }
  254. }
  255. void Preset::evaluateFrame()
  256. {
  257. // Evaluate all equation objects according to milkdrop flow diagram
  258. evalPerFrameInitEquations();
  259. evalPerFrameEquations();
  260. // Important step to ensure custom shapes and waves don't stamp on the q variable values
  261. // calculated by the per frame (init) and per pixel equations.
  262. transfer_q_variables(customWaves);
  263. transfer_q_variables(customShapes);
  264. initialize_PerPixelMeshes();
  265. evalPerPixelEqns();
  266. evalCustomWaveInitConditions();
  267. evalCustomWavePerFrameEquations();
  268. evalCustomShapeInitConditions();
  269. evalCustomShapePerFrameEquations();
  270. // Setup pointers of the custom waves and shapes to the preset outputs instance
  271. /// @slow an extra O(N) per frame, could do this during eval
  272. m_presetOutputs.customWaves = PresetOutputs::cwave_container(customWaves);
  273. m_presetOutputs.customShapes = PresetOutputs::cshape_container(customShapes);
  274. }
  275. void Preset::initialize_PerPixelMeshes()
  276. {
  277. int x,y;
  278. for (x=0;x<m_presetInputs.gx;x++){
  279. for(y=0;y<m_presetInputs.gy;y++){
  280. m_presetOutputs.cx_mesh[x][y]=m_presetOutputs.cx;
  281. }}
  282. for (x=0;x<m_presetInputs.gx;x++){
  283. for(y=0;y<m_presetInputs.gy;y++){
  284. m_presetOutputs.cy_mesh[x][y]=m_presetOutputs.cy;
  285. }}
  286. for (x=0;x<m_presetInputs.gx;x++){
  287. for(y=0;y<m_presetInputs.gy;y++){
  288. m_presetOutputs.sx_mesh[x][y]=m_presetOutputs.sx;
  289. }}
  290. for (x=0;x<m_presetInputs.gx;x++){
  291. for(y=0;y<m_presetInputs.gy;y++){
  292. m_presetOutputs.sy_mesh[x][y]=m_presetOutputs.sy;
  293. }}
  294. for (x=0;x<m_presetInputs.gx;x++){
  295. for(y=0;y<m_presetInputs.gy;y++){
  296. m_presetOutputs.dx_mesh[x][y]=m_presetOutputs.dx;
  297. }}
  298. for (x=0;x<m_presetInputs.gx;x++){
  299. for(y=0;y<m_presetInputs.gy;y++){
  300. m_presetOutputs.dy_mesh[x][y]=m_presetOutputs.dy;
  301. }}
  302. for (x=0;x<m_presetInputs.gx;x++){
  303. for(y=0;y<m_presetInputs.gy;y++){
  304. m_presetOutputs.zoom_mesh[x][y]=m_presetOutputs.zoom;
  305. }}
  306. for (x=0;x<m_presetInputs.gx;x++){
  307. for(y=0;y<m_presetInputs.gy;y++){
  308. m_presetOutputs.zoomexp_mesh[x][y]=m_presetOutputs.zoomexp;
  309. }}
  310. for (x=0;x<m_presetInputs.gx;x++){
  311. for(y=0;y<m_presetInputs.gy;y++){
  312. m_presetOutputs.rot_mesh[x][y]=m_presetOutputs.rot;
  313. }}
  314. for (x=0;x<m_presetInputs.gx;x++){
  315. for(y=0;y<m_presetInputs.gy;y++){
  316. m_presetOutputs.warp_mesh[x][y]=m_presetOutputs.warp;
  317. }}
  318. }
  319. // Evaluates all per-pixel equations
  320. void Preset::evalPerPixelEqns()
  321. {
  322. /* Evaluate all per pixel equations in the tree datastructure */
  323. for (int mesh_x = 0; mesh_x < m_presetInputs.gx; mesh_x++)
  324. for (int mesh_y = 0; mesh_y < m_presetInputs.gy; mesh_y++)
  325. for (std::map<int, PerPixelEqn*>::iterator pos = per_pixel_eqn_tree.begin();
  326. pos != per_pixel_eqn_tree.end(); ++pos)
  327. pos->second->evaluate(mesh_x, mesh_y);
  328. }
  329. int Preset::readIn(std::istream & fs) {
  330. line_mode_t line_mode;
  331. /* Parse any comments */
  332. if (Parser::parse_top_comment(fs) < 0)
  333. {
  334. if (PRESET_DEBUG)
  335. std::cerr << "[Preset::readIn] no left bracket found..." << std::endl;
  336. return PROJECTM_FAILURE;
  337. }
  338. /* Parse the preset name and a left bracket */
  339. char tmp_name[MAX_TOKEN_SIZE];
  340. if (Parser::parse_preset_name(fs, tmp_name) < 0)
  341. {
  342. std::cerr << "[Preset::readIn] loading of preset name failed" << std::endl;
  343. return PROJECTM_ERROR;
  344. }
  345. /// @note We ignore the preset name because [preset00] is just not so useful
  346. // Loop through each line in file, trying to succesfully parse the file.
  347. // If a line does not parse correctly, keep trucking along to next line.
  348. int retval;
  349. while ((retval = Parser::parse_line(fs, this)) != EOF)
  350. {
  351. if (retval == PROJECTM_PARSE_ERROR)
  352. {
  353. line_mode = UNSET_LINE_MODE;
  354. // std::cerr << "[Preset::readIn()] parse error in file \"" << this->absoluteFilePath() << "\"" << std::endl;
  355. }
  356. }
  357. // std::cerr << "loadPresetFile: finished line parsing successfully" << std::endl;
  358. /* Now the preset has been loaded.
  359. Evaluation calls can be made at appropiate
  360. times in the frame loop */
  361. return PROJECTM_SUCCESS;
  362. }
  363. /* loadPresetFile: private function that loads a specific preset denoted
  364. by the given pathname */
  365. int Preset::loadPresetFile(const std::string & pathname)
  366. {
  367. /* Open the file corresponding to pathname */
  368. FILE* f = fopen(pathname.c_str(), "r");
  369. if (!f) {
  370. if (PRESET_DEBUG)
  371. std::cerr << "loadPresetFile: loading of file \"" << pathname << "\" failed!\n";
  372. return PROJECTM_ERROR;
  373. }
  374. fseek(f, 0, SEEK_END);
  375. long fsize = ftell(f);
  376. rewind(f);
  377. std::vector<char> buffer(fsize);
  378. int err = fread(&buffer[0], 1, fsize, f);
  379. if (!err)
  380. {
  381. printf("read failed\n");
  382. fclose(f);
  383. return PROJECTM_ERROR;
  384. }
  385. fclose(f);
  386. std::stringstream stream;
  387. stream.rdbuf()->pubsetbuf(&buffer[0],buffer.size());
  388. return readIn(stream);
  389. }