/** * projectM -- Milkdrop-esque visualisation SDK * Copyright (C)2003-2004 projectM Team * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * See 'LICENSE.txt' included within this release * */ #include #include #include #ifdef WIN32 #include "win32-dirent.h" #else #include #endif /** WIN32 */ #include #include "MilkdropPreset.hpp" #include "Parser.hpp" #include "ParamUtils.hpp" #include "InitCondUtils.hpp" #include "fatal.h" #include #include #include "PresetFrameIO.hpp" MilkdropPreset::MilkdropPreset(std::istream & in, const std::string & presetName, PresetOutputs & presetOutputs): Preset(presetName), builtinParams(_presetInputs, presetOutputs), _presetOutputs(presetOutputs) { initialize(in); } MilkdropPreset::MilkdropPreset(const std::string & absoluteFilePath, const std::string & presetName, PresetOutputs & presetOutputs): Preset(presetName), builtinParams(_presetInputs, presetOutputs), _absoluteFilePath(absoluteFilePath), _presetOutputs(presetOutputs), _filename(parseFilename(absoluteFilePath)) { initialize(absoluteFilePath); } MilkdropPreset::~MilkdropPreset() { traverse >(init_cond_tree); traverse >(per_frame_init_eqn_tree); traverse >(per_pixel_eqn_tree); traverseVector >(per_frame_eqn_tree); traverse >(user_param_tree); /// Testing deletion of render items by the preset. would be nice if it worked, /// and seems to be working if you use a mutex on the preset switching. /* std::cout << "begin freeing of waves / shapes" << std::endl; for (PresetOutputs::cwave_container::iterator pos = customWaves.begin(); pos != customWaves.end(); ++pos ) { for (PresetOutputs::cwave_container::iterator pos2 = presetOutputs().customWaves.begin(); pos2 != presetOutputs().customWaves.end(); ++pos2 ) { if ((*pos2)->id == (*pos)->id) presetOutputs().customWaves.erase(pos2); } delete(*pos); } for (PresetOutputs::cshape_container::iterator pos = customShapes.begin(); pos != customShapes.end(); ++pos ) { for (PresetOutputs::cshape_container::iterator pos2 = presetOutputs().customShapes.begin(); 1aaa339f10vmlkd pos2 != presetOutputs().customShapes.end(); ++pos2 ) { if ((*pos2)->id == (*pos)->id) presetOutputs().customShapes.erase(pos2); } delete(*pos); } std::cout << "end freeing of waves / shapes" << std::endl; */ } /* Adds a per pixel equation according to its string name. This will be used only by the parser */ int MilkdropPreset::add_per_pixel_eqn(char * name, GenExpr * gen_expr) { PerPixelEqn * per_pixel_eqn = NULL; int index; Param * param = NULL; assert(gen_expr); assert(name); if (PER_PIXEL_EQN_DEBUG) printf("add_per_pixel_eqn: per pixel equation (name = \"%s\")\n", name); /* Search for the parameter so we know what matrix the per pixel equation is referencing */ param = ParamUtils::find(name, &this->builtinParams, &this->user_param_tree); if ( !param ) { if (PER_PIXEL_EQN_DEBUG) printf("add_per_pixel_eqn: failed to allocate a new parameter!\n"); return PROJECTM_FAILURE; } index = per_pixel_eqn_tree.size(); /* Create the per pixel equation given the index, parameter, and general expression */ if ((per_pixel_eqn = new PerPixelEqn(index, param, gen_expr)) == NULL) { if (PER_PIXEL_EQN_DEBUG) printf("add_per_pixel_eqn: failed to create new per pixel equation!\n"); return PROJECTM_FAILURE; } /* Insert the per pixel equation into the preset per pixel database */ std::pair::iterator, bool> inserteeOption = per_pixel_eqn_tree.insert (std::make_pair(per_pixel_eqn->index, per_pixel_eqn)); if (!inserteeOption.second) { printf("failed to add per pixel eqn!\n"); delete(per_pixel_eqn); return PROJECTM_FAILURE; } /* Done */ return PROJECTM_SUCCESS; } void MilkdropPreset::evalCustomShapeInitConditions() { for (PresetOutputs::cshape_container::iterator pos = customShapes.begin(); pos != customShapes.end(); ++pos) { assert(*pos); (*pos)->evalInitConds(); } } void MilkdropPreset::evalCustomWaveInitConditions() { for (PresetOutputs::cwave_container::iterator pos = customWaves.begin(); pos != customWaves.end(); ++pos) { assert(*pos); (*pos)->evalInitConds(); } } void MilkdropPreset::evalCustomWavePerFrameEquations() { for (PresetOutputs::cwave_container::iterator pos = customWaves.begin(); pos != customWaves.end(); ++pos) { std::map & init_cond_tree = (*pos)->init_cond_tree; for (std::map::iterator _pos = init_cond_tree.begin(); _pos != init_cond_tree.end(); ++_pos) { assert(_pos->second); _pos->second->evaluate(); } std::vector & per_frame_eqn_tree = (*pos)->per_frame_eqn_tree; for (std::vector::iterator _pos = per_frame_eqn_tree.begin(); _pos != per_frame_eqn_tree.end(); ++_pos) { (*_pos)->evaluate(); } } } void MilkdropPreset::evalCustomShapePerFrameEquations() { for (PresetOutputs::cshape_container::iterator pos = customShapes.begin(); pos != customShapes.end(); ++pos) { std::map & init_cond_tree = (*pos)->init_cond_tree; for (std::map::iterator _pos = init_cond_tree.begin(); _pos != init_cond_tree.end(); ++_pos) { assert(_pos->second); _pos->second->evaluate(); } std::vector & per_frame_eqn_tree = (*pos)->per_frame_eqn_tree; for (std::vector::iterator _pos = per_frame_eqn_tree.begin(); _pos != per_frame_eqn_tree.end(); ++_pos) { (*_pos)->evaluate(); } } } void MilkdropPreset::evalPerFrameInitEquations() { for (std::map::iterator pos = per_frame_init_eqn_tree.begin(); pos != per_frame_init_eqn_tree.end(); ++pos) { assert(pos->second); pos->second->evaluate(); } } void MilkdropPreset::evalPerFrameEquations() { for (std::map::iterator pos = init_cond_tree.begin(); pos != init_cond_tree.end(); ++pos) { assert(pos->second); pos->second->evaluate(); } for (std::vector::iterator pos = per_frame_eqn_tree.begin(); pos != per_frame_eqn_tree.end(); ++pos) { (*pos)->evaluate(); } } void MilkdropPreset::preloadInitialize() { /// @note commented this out because it should be unnecessary // Clear equation trees //init_cond_tree.clear(); //user_param_tree.clear(); //per_frame_eqn_tree.clear(); //per_pixel_eqn_tree.clear(); //per_frame_init_eqn_tree.clear(); } void MilkdropPreset::postloadInitialize() { /* It's kind of ugly to reset these values here. Should definitely be placed in the parser somewhere */ this->per_frame_eqn_count = 0; this->per_frame_init_eqn_count = 0; this->loadBuiltinParamsUnspecInitConds(); this->loadCustomWaveUnspecInitConds(); this->loadCustomShapeUnspecInitConds(); /// @bug are you handling all the q variables conditions? in particular, the un-init case? //m_presetOutputs.q1 = 0; //m_presetOutputs.q2 = 0; //m_presetOutputs.q3 = 0; //m_presetOutputs.q4 = 0; //m_presetOutputs.q5 = 0; //m_presetOutputs.q6 = 0; //m_presetOutputs.q7 = 0; //m_presetOutputs.q8 = 0; } void MilkdropPreset::Render(const BeatDetect &music, const PipelineContext &context) { _presetInputs.update(music, context); evaluateFrame(); pipeline().Render(music, context); } void MilkdropPreset::initialize(const std::string & pathname) { int retval; preloadInitialize(); if (MILKDROP_PRESET_DEBUG) std::cerr << "[Preset] loading file \"" << pathname << "\"..." << std::endl; if ((retval = loadPresetFile(pathname)) < 0) { if (MILKDROP_PRESET_DEBUG) std::cerr << "[Preset] failed to load file \"" << pathname << "\"!" << std::endl; /// @bug how should we handle this problem? a well define exception? throw retval; } postloadInitialize(); } void MilkdropPreset::initialize(std::istream & in) { int retval; preloadInitialize(); if ((retval = readIn(in)) < 0) { if (MILKDROP_PRESET_DEBUG) std::cerr << "[Preset] failed to load from stream " << std::endl; /// @bug how should we handle this problem? a well define exception? throw retval; } postloadInitialize(); } void MilkdropPreset::loadBuiltinParamsUnspecInitConds() { InitCondUtils::LoadUnspecInitCond loadUnspecInitCond(this->init_cond_tree, this->per_frame_init_eqn_tree); this->builtinParams.apply(loadUnspecInitCond); traverse(user_param_tree, loadUnspecInitCond); } void MilkdropPreset::loadCustomWaveUnspecInitConds() { for (PresetOutputs::cwave_container::iterator pos = customWaves.begin(); pos != customWaves.end(); ++pos) { assert(*pos); (*pos)->loadUnspecInitConds(); } } void MilkdropPreset::loadCustomShapeUnspecInitConds() { for (PresetOutputs::cshape_container::iterator pos = customShapes.begin(); pos != customShapes.end(); ++pos) { assert(*pos); (*pos)->loadUnspecInitConds(); } } void MilkdropPreset::evaluateFrame() { // Evaluate all equation objects according to milkdrop flow diagram evalPerFrameInitEquations(); evalPerFrameEquations(); // Important step to ensure custom shapes and waves don't stamp on the q variable values // calculated by the per frame (init) and per pixel equations. transfer_q_variables(customWaves); transfer_q_variables(customShapes); initialize_PerPixelMeshes(); evalPerPixelEqns(); evalCustomWaveInitConditions(); evalCustomWavePerFrameEquations(); evalCustomShapeInitConditions(); evalCustomShapePerFrameEquations(); // Setup pointers of the custom waves and shapes to the preset outputs instance /// @slow an extra O(N) per frame, could do this during eval _presetOutputs.customWaves = PresetOutputs::cwave_container(customWaves); _presetOutputs.customShapes = PresetOutputs::cshape_container(customShapes); } void MilkdropPreset::initialize_PerPixelMeshes() { int x,y; for (x=0;x::iterator pos = per_pixel_eqn_tree.begin(); pos != per_pixel_eqn_tree.end(); ++pos) pos->second->evaluate(mesh_x, mesh_y); } int MilkdropPreset::readIn(std::istream & fs) { line_mode_t line_mode; presetOutputs().compositeShader.programSource.clear(); presetOutputs().warpShader.programSource.clear(); /* Parse any comments */ if (Parser::parse_top_comment(fs) < 0) { if (MILKDROP_PRESET_DEBUG) std::cerr << "[Preset::readIn] no left bracket found..." << std::endl; return PROJECTM_FAILURE; } /* Parse the preset name and a left bracket */ char tmp_name[MAX_TOKEN_SIZE]; if (Parser::parse_preset_name(fs, tmp_name) < 0) { std::cerr << "[Preset::readIn] loading of preset name failed" << std::endl; return PROJECTM_ERROR; } /// @note We ignore the preset name because [preset00] is just not so useful // Loop through each line in file, trying to succesfully parse the file. // If a line does not parse correctly, keep trucking along to next line. int retval; while ((retval = Parser::parse_line(fs, this)) != EOF) { if (retval == PROJECTM_PARSE_ERROR) { line_mode = UNSET_LINE_MODE; // std::cerr << "[Preset::readIn()] parse error in file \"" << this->absoluteFilePath() << "\"" << std::endl; } } // std::cerr << "loadPresetFile: finished line parsing successfully" << std::endl; /* Now the preset has been loaded. Evaluation calls can be made at appropiate times in the frame loop */ return PROJECTM_SUCCESS; } /* loadPresetFile: private function that loads a specific preset denoted by the given pathname */ int MilkdropPreset::loadPresetFile(const std::string & pathname) { /* Open the file corresponding to pathname */ std::ifstream fs(pathname.c_str()); if (!fs || fs.eof()) { if (MILKDROP_PRESET_DEBUG) std::cerr << "loadPresetFile: loading of file \"" << pathname << "\" failed!\n"; return PROJECTM_ERROR; } return readIn(fs); } const std::string & MilkdropPreset::name() const { return name().empty() ? filename() : name(); }