From cf83c16e3e12e4aad7bf32fddd75accd4e06d1a9 Mon Sep 17 00:00:00 2001 From: w1z7ard Date: Wed, 18 Feb 2009 09:31:41 +0000 Subject: [PATCH] first buildable implementation of a render item object optimal matching algorithm. git-svn-id: https://projectm.svn.sourceforge.net/svnroot/projectm/personal/carm/represet@1207 6778bc44-b910-0410-a7a0-be141de4315d --- src/libprojectM/Common.hpp | 20 ++- src/libprojectM/HungarianMethod.hpp | 3 +- src/libprojectM/PresetFactory.hpp | 4 +- src/libprojectM/PresetLoader.cpp | 2 +- src/libprojectM/PresetMerge.cpp | 9 +- src/libprojectM/PresetMerge.hpp | 4 +- src/libprojectM/Renderer/CMakeLists.txt | 4 +- .../Renderer/RenderItemDistanceMetric.cpp | 10 ++ .../Renderer/RenderItemDistanceMetric.hpp | 150 ++++++++++++++++++ .../Renderer/RenderItemMatcher.hpp | 65 ++++++++ src/libprojectM/Renderer/Renderable.hpp | 4 + src/libprojectM/Renderer/Renderer.hpp | 2 +- src/libprojectM/projectM.cpp | 10 +- src/libprojectM/projectM.hpp | 5 +- 14 files changed, 269 insertions(+), 23 deletions(-) create mode 100644 src/libprojectM/Renderer/RenderItemDistanceMetric.cpp create mode 100644 src/libprojectM/Renderer/RenderItemDistanceMetric.hpp create mode 100644 src/libprojectM/Renderer/RenderItemMatcher.hpp diff --git a/src/libprojectM/Common.hpp b/src/libprojectM/Common.hpp index 4f8d55195..8a38e98be 100755 --- a/src/libprojectM/Common.hpp +++ b/src/libprojectM/Common.hpp @@ -24,12 +24,12 @@ * $Log$ */ -#ifndef COMMON_H -#define COMMON_H - -#include +#ifndef COMMON_HPP +#define COMMON_HPP +#include +#include #include -#ifdef _MSC_VER +#ifdef _MSC_sVER #define strcasecmp(s, t) _strcmpi(s, t) #endif @@ -204,12 +204,10 @@ else } -inline void DWRITE( char *fmt, ... ) { - return; - va_list args; - va_start( args, fmt ); - va_end( args ); - } + +inline double meanSquaredError(const double & x, const double & y) { + return (x-y)*(x-y); +} #endif diff --git a/src/libprojectM/HungarianMethod.hpp b/src/libprojectM/HungarianMethod.hpp index b0c3de043..4b6b93c58 100644 --- a/src/libprojectM/HungarianMethod.hpp +++ b/src/libprojectM/HungarianMethod.hpp @@ -4,7 +4,7 @@ #include #include #include - +#include /// A function object which calculates the maximum-weighted bipartite matching between /// two sets via the hungarian method. @@ -189,3 +189,4 @@ inline int inverseMatching(int j) const { }; +#endif diff --git a/src/libprojectM/PresetFactory.hpp b/src/libprojectM/PresetFactory.hpp index 684d0b036..e9c8a4cd0 100644 --- a/src/libprojectM/PresetFactory.hpp +++ b/src/libprojectM/PresetFactory.hpp @@ -1,7 +1,7 @@ // // C++ Interface: PresetFactory // -// Description: +// Description: // // // Author: Carmelo Piccione , (C) 2008 @@ -36,7 +36,7 @@ public: /// Returns a space separated list of supported extensions virtual std::string supportedExtensions() const = 0; - + }; #endif diff --git a/src/libprojectM/PresetLoader.cpp b/src/libprojectM/PresetLoader.cpp index f5791c694..18e3e3cdf 100644 --- a/src/libprojectM/PresetLoader.cpp +++ b/src/libprojectM/PresetLoader.cpp @@ -139,7 +139,7 @@ std::auto_ptr PresetLoader::loadPreset ( unsigned int index ) const return _presetFactoryManager.factory(extension).allocate ( _entries[index], _presetNames[index] ); - + } diff --git a/src/libprojectM/PresetMerge.cpp b/src/libprojectM/PresetMerge.cpp index 9743760bc..fba7b0df1 100644 --- a/src/libprojectM/PresetMerge.cpp +++ b/src/libprojectM/PresetMerge.cpp @@ -1,8 +1,8 @@ #include "PresetMerge.hpp" +#include "RenderItemMatcher.hpp" - -void PipelineMerger::MergePipelines(const Pipeline & a, const Pipeline & b, Pipeline & out, float ratio) +void PipelineMerger::MergePipelines(const Pipeline & a, const Pipeline & b, Pipeline & out, RenderItemMatcher & matcher, float ratio) { double invratio = 1.0 - ratio; @@ -12,6 +12,11 @@ void PipelineMerger::MergePipelines(const Pipeline & a, const Pipeline & b, Pipe out.screenDecay =lerp( b.screenDecay, a.screenDecay, ratio); out.drawables.clear(); + double error = matcher(a.drawables, b.drawables); + for (int i = 0; i < a.drawables.size();i++) + for (int j = 0; j < b.drawables.size();j++) + std::cerr << "[" << i << "][" << j << "]" << matcher.weight(i,j); + for (std::vector::const_iterator pos = a.drawables.begin(); pos != a.drawables.end(); ++pos) { diff --git a/src/libprojectM/PresetMerge.hpp b/src/libprojectM/PresetMerge.hpp index a72975e79..dde38b538 100644 --- a/src/libprojectM/PresetMerge.hpp +++ b/src/libprojectM/PresetMerge.hpp @@ -2,6 +2,7 @@ #define PRESET_MERGE_HPP #include "Preset.hpp" #include "Pipeline.hpp" +#include "RenderItemMatcher.hpp" class PipelineMerger { @@ -11,7 +12,8 @@ class PipelineMerger } public: - static void MergePipelines(const Pipeline &a, const Pipeline &b, Pipeline &out, float ratio); + static void MergePipelines(const Pipeline &a, const Pipeline &b, Pipeline &out, RenderItemMatcher & matcher, float ratio); }; + #endif diff --git a/src/libprojectM/Renderer/CMakeLists.txt b/src/libprojectM/Renderer/CMakeLists.txt index 40adc427e..269880e22 100644 --- a/src/libprojectM/Renderer/CMakeLists.txt +++ b/src/libprojectM/Renderer/CMakeLists.txt @@ -3,7 +3,9 @@ cmake_minimum_required(VERSION 2.4.0) SET(SOIL_SOURCES SOIL/image_DXT.c SOIL/image_helper.c SOIL/SOIL.c SOIL/stb_image_aug.c) -SET(Renderer_SOURCES FBO.cpp MilkdropWaveform.cpp PerPixelMesh.cpp Pipeline.cpp Renderer.cpp ShaderEngine.cpp UserTexture.cpp Waveform.cpp Filters.cpp PerlinNoise.cpp PipelineContext.cpp Renderable.cpp BeatDetect.cpp Shader.cpp TextureManager.cpp VideoEcho.cpp ${SOIL_SOURCES}) +SET(Renderer_SOURCES FBO.cpp MilkdropWaveform.cpp PerPixelMesh.cpp Pipeline.cpp Renderer.cpp ShaderEngine.cpp UserTexture.cpp Waveform.cpp +Filters.cpp PerlinNoise.cpp PipelineContext.cpp Renderable.cpp BeatDetect.cpp Shader.cpp TextureManager.cpp VideoEcho.cpp +RenderItemDistanceMetric.cpp ${SOIL_SOURCES}) SET (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -fPIC) SET (CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -fPIC) diff --git a/src/libprojectM/Renderer/RenderItemDistanceMetric.cpp b/src/libprojectM/Renderer/RenderItemDistanceMetric.cpp new file mode 100644 index 000000000..cdbaa1645 --- /dev/null +++ b/src/libprojectM/Renderer/RenderItemDistanceMetric.cpp @@ -0,0 +1,10 @@ +/* + * RenderItemDistanceMetric.cpp + * + * Created on: Feb 18, 2009 + * Author: struktured + */ + +#include "RenderItemDistanceMetric.hpp" +const double RenderItemDistanceMetric::NOT_COMPARABLE_VALUE + (std::numeric_limits::max()); diff --git a/src/libprojectM/Renderer/RenderItemDistanceMetric.hpp b/src/libprojectM/Renderer/RenderItemDistanceMetric.hpp new file mode 100644 index 000000000..e846c0189 --- /dev/null +++ b/src/libprojectM/Renderer/RenderItemDistanceMetric.hpp @@ -0,0 +1,150 @@ +/* + * RenderItemDistanceMetric.h + * + * Created on: Feb 16, 2009 + * Author: struktured + */ + +#ifndef RenderItemDISTANCEMETRIC_H_ +#define RenderItemDISTANCEMETRIC_H_ + +#include "Common.hpp" +#include "Renderable.hpp" +#include +#include +#include + +struct TypeIdPair { + TypeIdPair(const std::type_info & info1, const std::type_info & info2): id1(info1.name()), id2(info2.name()) {} + TypeIdPair(const std::string & id1, const std::string & id2): id1(id1), id2(id2) {} + std::string id1; + std::string id2; + inline bool operator<(const TypeIdPair & rhs) const { + return this->id1 < rhs.id1 || (this->id1 == rhs.id1 && this->id2 < rhs.id2); + } + + inline bool operator>(const TypeIdPair & rhs) const { + return !operator<(rhs) && !operator==(rhs); + } + + inline bool operator==(const TypeIdPair & rhs) const { + return this->id1 == rhs.id1 && this->id2 == rhs.id2; + } +}; + +/// Compares two render items and returns zero if they are virtually equivalent and large values +/// when they are dissimilar. If two render items cannot be compared, NOT_COMPARABLE_VALUE is returned. +class RenderItemDistanceMetric : public std::binary_function { +public: + const static double NOT_COMPARABLE_VALUE; + virtual double operator()(const RenderItem * r1, const RenderItem * r2) const = 0; + virtual TypeIdPair typeIdPair() const = 0; +}; + +// A base class to construct render item distance metrics. Just specify your two concrete +// render item types as template parameters and override the computeDistance() function. +template +class RenderItemDistance : public RenderItemDistanceMetric { + +protected: +// Override to create your own distance metric for your specified custom types. +virtual double computeDistance(const R1 * r1, const R2 * r2) const = 0; + +public: + +inline virtual double operator()(const RenderItem * r1, const RenderItem * r2) const { + if (supported(r1, r2)) + return computeDistance(dynamic_cast(r1), dynamic_cast(r2)); + else if (supported(r2,r1)) + return computeDistance(dynamic_cast(r2), dynamic_cast(r1)); + else + return NOT_COMPARABLE_VALUE; +} + +// Returns true if and only if r1 and r2 are of type R1 and R2 respectively. +inline bool supported(const RenderItem * r1, const RenderItem * r2) const { + return typeid(r1) == typeid(const R1 *) && typeid(r2) == typeid(const R2 *); +} + +inline TypeIdPair typeIdPair() const { + return TypeIdPair(typeid(const R1*).name(), typeid(const R2*).name()); +} + +}; + + +class RTIRenderItemDistance : public RenderItemDistance { +public: + + RTIRenderItemDistance() {} + virtual ~RTIRenderItemDistance() {} + +protected: + virtual inline double computeDistance(const RenderItem * lhs, const RenderItem * rhs) const { + if (typeid(lhs) == typeid(rhs)) + return 0.0; + else + return NOT_COMPARABLE_VALUE; + } + + +}; + + + +class ShapeXYDistance : public RenderItemDistance { + +public: + + ShapeXYDistance() {} + virtual ~ShapeXYDistance() {} + +protected: + + virtual inline double computeDistance(const Shape * lhs, const Shape * rhs) const { + return (meanSquaredError(lhs->x, rhs->x) + meanSquaredError(lhs->y, rhs->y)) / 2; + } + +}; + + +class MasterRenderItemDistance : public RenderItemDistance { + +typedef std::map DistanceMetricMap; +public: + + MasterRenderItemDistance() {} + virtual ~MasterRenderItemDistance() {} + + inline void addMetric(RenderItemDistanceMetric * fun) { + _distanceMetricMap[fun->typeIdPair()] = fun; + } + +protected: + virtual inline double computeDistance(const RenderItem * lhs, const RenderItem * rhs) const { + + RenderItemDistanceMetric * metric; + + TypeIdPair pair(typeid(lhs), typeid(rhs)); + if (_distanceMetricMap.count(pair)) { + metric = _distanceMetricMap[pair]; + } else if (_distanceMetricMap.count(pair = TypeIdPair(typeid(lhs), typeid(rhs)))) { + metric = _distanceMetricMap[pair]; + } else { + metric = 0; + } + + // If specialized metric exists, use it to get higher granularity + // of correctness + if (metric) + return (*metric)(lhs, rhs); + else // Failing that, use rtti and return 0 if they match, max value otherwise + return _rttiDistance(lhs,rhs); + } + +private: + mutable RTIRenderItemDistance _rttiDistance; + mutable DistanceMetricMap _distanceMetricMap; +}; + +#endif /* RenderItemDISTANCEMETRIC_H_ */ diff --git a/src/libprojectM/Renderer/RenderItemMatcher.hpp b/src/libprojectM/Renderer/RenderItemMatcher.hpp new file mode 100644 index 000000000..211e84845 --- /dev/null +++ b/src/libprojectM/Renderer/RenderItemMatcher.hpp @@ -0,0 +1,65 @@ +/* + * RenderItemMatcher.hpp + * + * Created on: Feb 16, 2009 + * Author: struktured + */ + +#ifndef RenderItemMatcher_HPP +#define RenderItemMatcher_HPP + +#include "RenderItemDistanceMetric.hpp" +#include +#include +#include +#include "HungarianMethod.hpp" + + +class RenderItemMatcher : public std::binary_function { +public: + static const std::size_t MAXIMUM_SET_SIZE = 1000; + + /// Computes an optimal matching between two renderable item sets. + inline virtual double operator()(const RenderItemList & lhs, const RenderItemList & rhs) const { + + // Ensure the first argument is greater than next to aid the helper function's logic. + if (lhs.size() >= rhs.size()) + return computeMatching(lhs, rhs); + else + return computeMatching(rhs, lhs); + } + + /// @idea could instead just pass back a vector of render item pointer pairs. ask sperl + inline int matching(int i) const { return _hungarianMethod.matching(i); } + inline int inverseMatching(int j) const { return _hungarianMethod.inverseMatching(j); } + inline double weight(int i, int j) const { return _weights[i][j]; } + + RenderItemMatcher() {} + virtual ~RenderItemMatcher() {} + + MasterRenderItemDistance & distanceFunction() { return _distanceFunction; } + +private: + mutable HungarianMethod _hungarianMethod; + mutable double _weights[MAXIMUM_SET_SIZE][MAXIMUM_SET_SIZE]; + + /// @idea interface this entirely allow overriding of its type. + mutable MasterRenderItemDistance _distanceFunction; + + inline double computeMatching(const RenderItemList & lhs, const RenderItemList & rhs) const { + for (int i = 0; i < lhs.size();i++) { + int j; + for (j = 0; j < rhs.size();j++) + _weights[i][j] = _distanceFunction(lhs[i], rhs[j]); + for (; j < lhs.size();j++) + _weights[i][j] = RenderItemDistanceMetric::NOT_COMPARABLE_VALUE; + } + + const double error = _hungarianMethod(_weights, lhs.size()); + std::cout << "[computeMatching] total error is " << error << std::endl; + return error; + } + +}; + +#endif diff --git a/src/libprojectM/Renderer/Renderable.hpp b/src/libprojectM/Renderer/Renderable.hpp index 2ff1e2dfb..0f7d05e2f 100644 --- a/src/libprojectM/Renderer/Renderable.hpp +++ b/src/libprojectM/Renderer/Renderable.hpp @@ -1,9 +1,11 @@ #ifndef Renderable_HPP #define Renderable_HPP +#include #include "TextureManager.hpp" class BeatDetect; + class RenderContext { public: @@ -25,6 +27,8 @@ public: RenderItem(); }; +typedef std::vector RenderItemList; + class DarkenCenter : public RenderItem { public: diff --git a/src/libprojectM/Renderer/Renderer.hpp b/src/libprojectM/Renderer/Renderer.hpp index 69990f7f4..5513fa6b8 100644 --- a/src/libprojectM/Renderer/Renderer.hpp +++ b/src/libprojectM/Renderer/Renderer.hpp @@ -66,7 +66,7 @@ public: Renderer( int width, int height, int gx, int gy, int texsize, BeatDetect *beatDetect, std::string presetURL, std::string title_fontURL, std::string menu_fontURL); ~Renderer(); - + void RenderFrame(const Pipeline &pipeline, const PipelineContext &pipelineContext); void ResetTextures(); void reset(int w, int h); diff --git a/src/libprojectM/projectM.cpp b/src/libprojectM/projectM.cpp index 532700678..0090b8c56 100755 --- a/src/libprojectM/projectM.cpp +++ b/src/libprojectM/projectM.cpp @@ -18,8 +18,8 @@ * See 'LICENSE.txt' included within this release * */ -#include "wipemalloc.h" +#include "RenderItemMatcher.hpp" #include "fatal.h" #include "Common.hpp" @@ -53,6 +53,7 @@ #include "ConfigFile.h" #include "TextureManager.hpp" #include "TimeKeeper.hpp" + #ifdef USE_THREADS #include "pthread.h" #endif @@ -330,7 +331,9 @@ Pipeline pipeline; pipeline.setStaticPerPixel(settings().meshX, settings().meshY); -PipelineMerger::MergePipelines( m_activePreset->pipeline(),m_activePreset2->pipeline(), pipeline,timeKeeper->SmoothRatio()); + +assert(_matcher); +PipelineMerger::MergePipelines( m_activePreset->pipeline(),m_activePreset2->pipeline(), pipeline, *_matcher, timeKeeper->SmoothRatio()); /// @bug not sure if this is correct renderer->RenderFrame(pipeline, pipelineContext()); @@ -535,6 +538,9 @@ int projectM::initPresetTools(int gx, int gy) << m_presetLoader->directoryName() << "\"" << std::endl; } + _matcher = new RenderItemMatcher(); + _matcher->distanceFunction().addMetric(new ShapeXYDistance()); + //std::cerr << "[projectM] Idle preset allocated." << std::endl; projectM_resetengine(); diff --git a/src/libprojectM/projectM.hpp b/src/libprojectM/projectM.hpp index 8e72a1396..31f5da28e 100755 --- a/src/libprojectM/projectM.hpp +++ b/src/libprojectM/projectM.hpp @@ -77,6 +77,7 @@ class PresetChooser; class PresetLoader; class TimeKeeper; class Pipeline; +class RenderItemMatcher; #include #ifdef WIN32 @@ -104,7 +105,7 @@ typedef enum { /// A functor class that allows users of this library to specify random preset behavior class RandomizerFunctor { - public: + public: RandomizerFunctor(PresetChooser & chooser) ; virtual ~RandomizerFunctor(); virtual double operator() (int index); @@ -299,6 +300,8 @@ private: int m_flags; + RenderItemMatcher * _matcher; + pthread_mutex_t mutex; pthread_cond_t condition; pthread_t thread;