diff --git a/SConstruct b/SConstruct index 1e84b0dd..aeaf88db 100644 --- a/SConstruct +++ b/SConstruct @@ -277,6 +277,12 @@ env.Append(LIBPATH=['src/libcore']) # Rendering-specific library renderEnv = env.Clone() renderEnv.Append(CPPDEFINES = {'MTS_BUILD_MODULE' : 'MTS_MODULE_RENDER'} ) +if renderEnv.has_key('XERCESINCLUDE'): + renderEnv.Append(CPPPATH=renderEnv['XERCESINCLUDE']) +if renderEnv.has_key('XERCESLIBDIR'): + renderEnv.Append(LIBPATH=renderEnv['XERCESLIBDIR']) +if renderEnv.has_key('XERCESLIB'): + renderEnv.Append(LIBS=renderEnv['XERCESLIB']) librender = renderEnv.SharedLibrary('src/librender/mitsuba-render', [ 'src/librender/bsdf.cpp', 'src/librender/camera.cpp', 'src/librender/film.cpp', 'src/librender/integrator.cpp', @@ -295,7 +301,8 @@ librender = renderEnv.SharedLibrary('src/librender/mitsuba-render', [ 'src/librender/preview.cpp', 'src/librender/photonmap.cpp', 'src/librender/gatherproc.cpp', 'src/librender/mipmap3d.cpp', 'src/librender/volume.cpp', 'src/librender/vpl.cpp', - 'src/librender/shader.cpp' + 'src/librender/shader.cpp', 'src/librender/shandler.cpp', + 'src/librender/util.cpp' ]) if sys.platform == "darwin": @@ -353,12 +360,6 @@ env['SHLIBPREFIX']='' # Environment with Xerces + wxWidgets mainEnv = env.Clone() -if mainEnv.has_key('XERCESINCLUDE'): - mainEnv.Append(CPPPATH=mainEnv['XERCESINCLUDE']) -if mainEnv.has_key('XERCESLIBDIR'): - mainEnv.Append(LIBPATH=mainEnv['XERCESLIBDIR']) -if mainEnv.has_key('XERCESLIB'): - mainEnv.Append(LIBS=mainEnv['XERCESLIB']) if mainEnv.has_key('GLLIB'): mainEnv.Append(LIBS=mainEnv['GLLIB']) if mainEnv.has_key('GLLIBDIR'): @@ -374,12 +375,11 @@ darwinStub = [] if sys.platform == 'win32': resources += [env.RES('tools/windows/mitsuba_res.rc'), env.StaticObject('src/mitsuba/getopt.c')] -shandler = mainEnv.StaticObject('src/mitsuba/shandler.cpp') # Build the command-line+GUI interface mainEnv.Program('mtssrv', resources + ['src/mitsuba/mtssrv.cpp']) -mainEnv.Program('mitsuba', resources + ['src/mitsuba/mitsuba.cpp', shandler]) -mainEnv.Program('mtsutil', resources + ['src/mitsuba/mtsutil.cpp', shandler]) +mainEnv.Program('mitsuba', resources + ['src/mitsuba/mitsuba.cpp']) +mainEnv.Program('mtsutil', resources + ['src/mitsuba/mtsutil.cpp']) if sys.platform == 'darwin': mainEnv_osx = mainEnv.Clone(); @@ -388,7 +388,6 @@ if sys.platform == 'darwin': mainEnv_osx['CXXFLAGS'].append('-fno-strict-aliasing'); darwinStub += [mainEnv_osx.StaticObject('src/mitsuba/darwin_stub.mm')] -env.Program('src/utils/utils_test', ['src/utils/utils_test.cpp']) env.Program('src/utils/joinrgb', ['src/utils/joinrgb.cpp']) env.Program('src/utils/ttest', ['src/utils/ttest.cpp']) env.Program('src/utils/createvol', ['src/utils/createvol.cpp']) @@ -433,7 +432,7 @@ if hasQt: qtInterfaces = [qtEnv.Uic4(uic) for uic in scanFiles('src/qtgui', ['*.ui'])] qtResources = [qtEnv.Qrc(qrc) for qrc in scanFiles('src/qtgui', ['*.qrc'])] - qtgui_files = scanFiles('src/qtgui', ['*.cpp']) + qtResources + shandler + resources + qtgui_files = scanFiles('src/qtgui', ['*.cpp']) + qtResources + resources if hasCollada: qtgui_files += converter_objects @@ -599,6 +598,11 @@ plugins += env.SharedLibrary('plugins/vpl', ['src/integrators/vpl/vpl.cpp']) # pathvertex_mlt, path_mlt #]) +# Testcases +for plugin in glob.glob('src/tests/test_*.cpp'): + name = os.path.basename(plugin) + plugins += env.SharedLibrary('plugins/' + name[0:len(name)-4], plugin) + installTargets = [] # Windows build? diff --git a/doc/parallelization.tex b/doc/parallelization.tex index 3d6e3a7c..1e83ea35 100644 --- a/doc/parallelization.tex +++ b/doc/parallelization.tex @@ -36,17 +36,14 @@ MTS_NAMESPACE_BEGIN class ROT13Encoder : public Utility { public: - ROT13Encoder(UtilityServices *us) : Utility(us) { } - int run(int argc, char **argv) { cout << "Hello world!" << endl; return 0; } - MTS_DECLARE_CLASS() + MTS_DECLARE_UTILITY() }; -MTS_IMPLEMENT_CLASS(ROT13Encoder, false, Utility) MTS_EXPORT_UTILITY(ROT13Encoder, "Perform a ROT13 encryption of a string") MTS_NAMESPACE_END \end{cpp} diff --git a/include/mitsuba/core/plugin.h b/include/mitsuba/core/plugin.h index a8b68c05..480348e4 100644 --- a/include/mitsuba/core/plugin.h +++ b/include/mitsuba/core/plugin.h @@ -6,7 +6,6 @@ MTS_NAMESPACE_BEGIN class Utility; -class UtilityServices; /** * Abstract plugin class -- can represent loadable configurable objects @@ -15,7 +14,7 @@ class UtilityServices; */ class MTS_EXPORT_CORE Plugin { typedef void *(*CreateInstanceFunc)(const Properties &props); - typedef void *(*CreateUtilityFunc)(UtilityServices *us); + typedef void *(*CreateUtilityFunc)(); typedef char *(*GetDescriptionFunc)(); public: /// Load a plugin from the supplied path @@ -31,7 +30,7 @@ public: ConfigurableObject *createInstance(const Properties &props) const; /// Return an utility instance (if this is an utility plugin) - Utility *createUtility(UtilityServices *us) const; + Utility *createUtility() const; /// Return a description of this plugin std::string getDescription() const; diff --git a/include/mitsuba/core/shvector.h b/include/mitsuba/core/shvector.h index 18e4c23d..8d722e5a 100644 --- a/include/mitsuba/core/shvector.h +++ b/include/mitsuba/core/shvector.h @@ -234,7 +234,7 @@ public: Float value = f(sphericalDirection(theta, phi))*std::sin(theta) * weightExt*weightInt; - + for (int l=0; l XERCES_CPP_NAMESPACE_USE -using namespace mitsuba; +MTS_NAMESPACE_BEGIN /** * XML parser for mitsuba scene files. Uses Xerces-C and SAX @@ -72,4 +72,6 @@ private: bool m_isIncludedFile; }; +MTS_NAMESPACE_END + #endif /* __SHANDLER_H */ diff --git a/include/mitsuba/render/testcase.h b/include/mitsuba/render/testcase.h index 011c1b47..546d70ca 100644 --- a/include/mitsuba/render/testcase.h +++ b/include/mitsuba/render/testcase.h @@ -1,10 +1,45 @@ #if !defined(__TESTCASE_H) #define __TESTCASE_H -#include +#include MTS_NAMESPACE_BEGIN +/** \brief Base class of all testcases. Implementations of this + * interface can be executed using the 'mtsutil' command. The execution + * order is as follows: after initializaiton using init(), any tests + * declared using the MTS_DECLARE_TEST() macro are executed. Finally, + * the shutdown() method is called. See the files in 'mitsuba/src/tests' + * for examples. + */ +class MTS_EXPORT_RENDER TestCase : public Utility { +public: + /** + * Perform any required initializations. The default + * implementation simply returns + */ + virtual void init(); + + /** + * Execute any required shutdown code. The default + * implementation simply returns + */ + virtual void shutdown(); + + /// Return the number of executed testcases + inline int getExecuted() const { return m_executed; } + + /// Return the number of successfully executed testcases + inline int getSucceeded() const { return m_succeeded; } + + MTS_DECLARE_CLASS() +protected: + /// Virtual destructor + virtual ~TestCase() { } +protected: + int m_executed, m_succeeded; +}; + /** * The test supervisor is used when rendering a collection of testcases in the * form of scenes with analytic solutions. Reference output is compared against @@ -40,4 +75,40 @@ private: MTS_NAMESPACE_END +#define EXECUTE_GUARDED(name) \ + try { \ + Log(EInfo, "Executing test \"%s\" ..", #name); \ + m_executed++;\ + name();\ + m_succeeded++;\ + } catch (std::exception &e) {\ + Log(EInfo, "Testcase failed with error: %s", e.what());\ + } + +#define MTS_BEGIN_TESTCASE() \ + MTS_DECLARE_CLASS() \ + int run(int argc, char **argv) {\ + init(); \ + Log(EInfo, "Executing testcase \"%s\" ..", getClass()->getName().c_str()); \ + m_executed = m_succeeded = 0; + +#define MTS_DECLARE_TEST(name) \ + EXECUTE_GUARDED(name) + +#define MTS_END_TESTCASE()\ + shutdown();\ + return 0;\ + } + +#define MTS_EXPORT_TESTCASE(name, descr) \ + MTS_IMPLEMENT_CLASS(name, false, TestCase) \ + extern "C" { \ + void MTS_EXPORT *CreateUtility() { \ + return new name(); \ + } \ + const char MTS_EXPORT *GetDescription() { \ + return descr; \ + } \ + } + #endif /* __TESTCASE_H */ diff --git a/include/mitsuba/render/util.h b/include/mitsuba/render/util.h index 9cf76c4f..0e4bfa2f 100644 --- a/include/mitsuba/render/util.h +++ b/include/mitsuba/render/util.h @@ -5,24 +5,12 @@ MTS_NAMESPACE_BEGIN -/** - * Contains functionality provided to utility plugins, - * such as loading a scene from an XML file. - */ -class MTS_EXPORT_RENDER UtilityServices { -public: - virtual ref loadScene(const std::string &filename) = 0; -}; - /** \brief Abstract utility class -- can be used to implement * loadable utility plugins that perform various actions. They * can be started using the 'mtsutil' launcher. */ class MTS_EXPORT_RENDER Utility : public Object { public: - inline Utility(UtilityServices *services) - : m_utilityServices(services) { } - /** * Run the utility. The supplied argc * and argv parameters contain any @@ -38,17 +26,17 @@ protected: virtual ~Utility() { } /// Load a scene - inline ref loadScene(const std::string &fname) { - return m_utilityServices->loadScene(fname); - } -private: - UtilityServices *m_utilityServices; + ref loadScene(const std::string &fname); }; +#define MTS_DECLARE_UTILITY() \ + MTS_DECLARE_CLASS() + #define MTS_EXPORT_UTILITY(name, descr) \ + MTS_IMPLEMENT_CLASS(name, false, Utility) \ extern "C" { \ - void MTS_EXPORT *CreateUtility(UtilityServices *us) { \ - return new name(us); \ + void MTS_EXPORT *CreateUtility() { \ + return new name(); \ } \ const char MTS_EXPORT *GetDescription() { \ return descr; \ diff --git a/src/libcore/plugin.cpp b/src/libcore/plugin.cpp index 4a2b3083..290a045c 100644 --- a/src/libcore/plugin.cpp +++ b/src/libcore/plugin.cpp @@ -76,8 +76,8 @@ ConfigurableObject *Plugin::createInstance(const Properties &props) const { return (ConfigurableObject *) m_createInstance(props); } -Utility *Plugin::createUtility(UtilityServices *us) const { - return (Utility *) m_createUtility(us); +Utility *Plugin::createUtility() const { + return (Utility *) m_createUtility(); } std::string Plugin::getDescription() const { diff --git a/src/libcore/util.cpp b/src/libcore/util.cpp index ae133409..bc092816 100644 --- a/src/libcore/util.cpp +++ b/src/libcore/util.cpp @@ -177,6 +177,10 @@ bool endsWith(const std::string& str, const std::string& end) { return (pos == str.size() - end.size()) && str.length() >= end.length(); } +bool startsWith(const std::string& str, const std::string& start) { + return str.find(start) == 0; +} + void * __restrict allocAligned(size_t size) { #if defined(WIN32) return _aligned_malloc(size, L1_CACHE_LINE_SIZE); diff --git a/src/librender/scene.cpp b/src/librender/scene.cpp index d4c9cb80..d5b0869b 100644 --- a/src/librender/scene.cpp +++ b/src/librender/scene.cpp @@ -635,5 +635,4 @@ std::string Scene::toString() const { } MTS_IMPLEMENT_CLASS_S(Scene, false, ConfigurableObject) -MTS_IMPLEMENT_CLASS(Utility, true, Object) MTS_NAMESPACE_END diff --git a/src/mitsuba/shandler.cpp b/src/librender/shandler.cpp similarity index 99% rename from src/mitsuba/shandler.cpp rename to src/librender/shandler.cpp index a6c9e5ec..37e63ab3 100644 --- a/src/mitsuba/shandler.cpp +++ b/src/librender/shandler.cpp @@ -1,6 +1,8 @@ #include #include -#include "shandler.h" +#include + +MTS_NAMESPACE_BEGIN SceneHandler::SceneHandler() : m_isIncludedFile(false) { m_pluginManager = PluginManager::getInstance(); @@ -399,3 +401,4 @@ void SceneHandler::fatalError(const SAXParseException& e) { transcode(e.getMessage()).c_str()); } +MTS_NAMESPACE_END diff --git a/src/librender/testcase.cpp b/src/librender/testcase.cpp index 7718c22e..f8654af6 100644 --- a/src/librender/testcase.cpp +++ b/src/librender/testcase.cpp @@ -5,6 +5,9 @@ MTS_NAMESPACE_BEGIN +void TestCase::init() { } +void TestCase::shutdown() { } + struct Sample { Float value; Float variance; @@ -169,4 +172,5 @@ void TestSupervisor::printSummary() const { } MTS_IMPLEMENT_CLASS(TestSupervisor, false, Object) +MTS_IMPLEMENT_CLASS(TestCase, false, Utility) MTS_NAMESPACE_END diff --git a/src/librender/util.cpp b/src/librender/util.cpp new file mode 100644 index 00000000..c34c6508 --- /dev/null +++ b/src/librender/util.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +MTS_NAMESPACE_BEGIN + +ref Utility::loadScene(const std::string &filename) { + /* Prepare for parsing scene descriptions */ + FileResolver *resolver = FileResolver::getInstance(); + SAXParser* parser = new SAXParser(); + std::string schemaPath = resolver->resolveAbsolute("schema/scene.xsd"); + + /* Check against the 'scene.xsd' XML Schema */ + parser->setDoSchema(true); + parser->setValidationSchemaFullChecking(true); + parser->setValidationScheme(SAXParser::Val_Always); + parser->setExternalNoNamespaceSchemaLocation(schemaPath.c_str()); + + std::map parameters; + SceneHandler *handler = new SceneHandler(parameters); + parser->setDoNamespaces(true); + parser->setDocumentHandler(handler); + parser->setErrorHandler(handler); + + parser->parse(filename.c_str()); + ref scene = handler->getScene(); + + delete parser; + delete handler; + + return scene; +} + +MTS_IMPLEMENT_CLASS(Utility, true, Object) +MTS_NAMESPACE_END diff --git a/src/mitsuba/mitsuba.cpp b/src/mitsuba/mitsuba.cpp index e383e462..e0038da8 100644 --- a/src/mitsuba/mitsuba.cpp +++ b/src/mitsuba/mitsuba.cpp @@ -5,9 +5,9 @@ #include #include #include +#include #include #include -#include "shandler.h" #if defined(WIN32) #include "getopt.h" diff --git a/src/mitsuba/mtsutil.cpp b/src/mitsuba/mtsutil.cpp index 1e8e334c..e219c16e 100644 --- a/src/mitsuba/mtsutil.cpp +++ b/src/mitsuba/mtsutil.cpp @@ -6,10 +6,10 @@ #include #include #include +#include #include #include #include -#include "shandler.h" #if !defined(WIN32) #include @@ -24,36 +24,6 @@ using namespace mitsuba; -class UtilityServicesImpl : public UtilityServices { -public: - ref loadScene(const std::string &filename) { - /* Prepare for parsing scene descriptions */ - FileResolver *resolver = FileResolver::getInstance(); - SAXParser* parser = new SAXParser(); - std::string schemaPath = resolver->resolveAbsolute("schema/scene.xsd"); - - /* Check against the 'scene.xsd' XML Schema */ - parser->setDoSchema(true); - parser->setValidationSchemaFullChecking(true); - parser->setValidationScheme(SAXParser::Val_Always); - parser->setExternalNoNamespaceSchemaLocation(schemaPath.c_str()); - - std::map parameters; - SceneHandler *handler = new SceneHandler(parameters); - parser->setDoNamespaces(true); - parser->setDocumentHandler(handler); - parser->setErrorHandler(handler); - - parser->parse(filename.c_str()); - ref scene = handler->getScene(); - - delete parser; - delete handler; - - return scene; - } -}; - void help() { cout << "Mitsuba version " MTS_VERSION ", Copyright (c) " MTS_YEAR " Wenzel Jakob" << endl; cout << "Usage: mtsutil [mtsutil options] [arguments]" << endl; @@ -74,6 +44,7 @@ void help() { cout << " -s file Connect to additional Mitsuba servers specified in a file" << endl; cout << " with one name per line (same format as in -c)" << endl<< endl; cout << " -n name Assign a node name to this instance (Default: host name)" << endl << endl; + cout << " -t Execute all testcases" << endl << endl; cout << " -v Be more verbose" << endl << endl; FileResolver *resolver = FileResolver::getInstance(); @@ -140,6 +111,7 @@ int ubi_main(int argc, char **argv) { bool quietMode = false; ELogLevel logLevel = EInfo; FileResolver *resolver = FileResolver::getInstance(); + bool testCaseMode = false; if (argc < 2) { help(); @@ -148,7 +120,7 @@ int ubi_main(int argc, char **argv) { optind = 1; /* Parse command-line arguments */ - while ((optchar = getopt(argc, argv, "a:c:s:n:p:qhv")) != -1) { + while ((optchar = getopt(argc, argv, "a:c:s:n:p:qhvt")) != -1) { switch (optchar) { case 'a': { std::vector paths = tokenize(optarg, ";"); @@ -159,6 +131,9 @@ int ubi_main(int argc, char **argv) { case 'c': networkHosts = networkHosts + std::string(";") + std::string(optarg); break; + case 't': + testCaseMode = true; + break; case 's': { std::ifstream is(optarg); if (is.fail()) @@ -261,36 +236,96 @@ int ubi_main(int argc, char **argv) { scheduler->start(); - if (argc <= optind) { - std::cerr << "A utility name must be supplied!" << endl; - return -1; - } + if (testCaseMode) { + std::vector dirPaths = resolver->resolveAllAbsolute("plugins"); + std::set seen; + int executed = 0, succeeded = 0; - /* Build the full plugin file name */ -#if defined(WIN32) - std::string shortName = std::string("plugins/") + argv[optind] + std::string(".dll"); -#elif defined(__OSX__) - std::string shortName = std::string("plugins/") + argv[optind] + std::string(".dylib"); + for (size_t i=0; id_name); + if (!endsWith(fname, ".dylib") && !endsWith(fname, ".so")) + continue; + std::string fullName = dirPath + "/" + fname; #else - std::string shortName = std::string("plugins/") + argv[optind] + std::string(".so"); + HANDLE hFind; + WIN32_FIND_DATA findFileData; + + if ((hFind = FindFirstFile((dirPath + "\\*.dll").c_str(), &findFileData)) == INVALID_HANDLE_VALUE) + SLog(EInfo, "Could not open plugin directory"); + + do { + std::string fname = findFileData.cFileName; + std::string fullName = dirPath + "\\" + fname; #endif - std::string fullName = resolver->resolve(shortName); + std::string shortName = fname.substr(0, strrchr(fname.c_str(), '.') - fname.c_str()); + if (!startsWith(shortName, "test_") || seen.find(shortName) != seen.end()) + continue; + seen.insert(shortName); + Plugin plugin(shortName, fullName); + if (!plugin.isUtility()) + continue; - if (!FileStream::exists(fullName)) { - /* Plugin not found! */ - SLog(EError, "Utility \"%s\" not found (run \"mtsutil\" without arguments to " - "see a list of available utilities)", fullName.c_str()); + ref utility = plugin.createUtility(); + + TestCase *testCase = static_cast(utility.get()); + if (!utility->getClass()->derivesFrom(TestCase::m_theClass)) + SLog(EError, "This is not a test case!"); + + if (testCase->run(argc-optind, argv+optind) != 0) + SLog(EError, "Testcase unexpectedly returned with a nonzero value."); + + executed += testCase->getExecuted(); + succeeded += testCase->getSucceeded(); +#if !defined(WIN32) + } +#else + } while (FindNextFile(hFind, &findFileData)); + FindClose(hFind); +#endif + } + + SLog(EError, "Ran %i tests, %i succeeded, %i failed.", executed, succeeded, executed-succeeded); + } else { + if (argc <= optind) { + std::cerr << "A utility name must be supplied!" << endl; + return -1; + } + + /* Build the full plugin file name */ +#if defined(WIN32) + std::string shortName = std::string("plugins/") + argv[optind] + std::string(".dll"); +#elif defined(__OSX__) + std::string shortName = std::string("plugins/") + argv[optind] + std::string(".dylib"); +#else + std::string shortName = std::string("plugins/") + argv[optind] + std::string(".so"); +#endif + std::string fullName = resolver->resolve(shortName); + + if (!FileStream::exists(fullName)) { + /* Plugin not found! */ + SLog(EError, "Utility \"%s\" not found (run \"mtsutil\" without arguments to " + "see a list of available utilities)", fullName.c_str()); + } + SLog(EInfo, "Loading utility \"%s\" ..", argv[optind]); + Plugin *plugin = new Plugin(argv[optind], fullName); + if (!plugin->isUtility()) + SLog(EError, "This plugin does not implement the 'Utility' interface!"); + Statistics::getInstance()->logPlugin(argv[optind], plugin->getDescription()); + + ref utility = plugin->createUtility(); + + return utility->run(argc-optind, argv+optind); } - SLog(EInfo, "Loading utility \"%s\" ..", argv[optind]); - Plugin *plugin = new Plugin(argv[optind], fullName); - if (!plugin->isUtility()) - SLog(EError, "This plugin does not implement the 'Utility' interface!"); - Statistics::getInstance()->logPlugin(argv[optind], plugin->getDescription()); - - UtilityServices *utilityServices = new UtilityServicesImpl(); - Utility *utility = plugin->createUtility(utilityServices); - - return utility->run(argc-optind, argv+optind); } catch (const std::exception &e) { std::cerr << "Caught a critical exeption: " << e.what() << std::endl; } catch (...) { diff --git a/src/qtgui/programsettingsdlg.ui b/src/qtgui/programsettingsdlg.ui index b7ac37ea..2ac991f0 100644 --- a/src/qtgui/programsettingsdlg.ui +++ b/src/qtgui/programsettingsdlg.ui @@ -7,7 +7,7 @@ 0 0 377 - 480 + 500 @@ -455,7 +455,7 @@ movements while navigating in the realt-time preview. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Lucida Grande'; font-size:13pt; font-weight:400; font-style:normal;"> +</style></head><body> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This option specifies the camera behavior when navigating within the realtime preview.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Fly-through (Fix yaw)</span>: Always yaw around a fixed &quot;up&quot; axis, which is determined when a scene is loaded. This is intuitive and similar to a person walking through a scene, but assumes that the camera has already been set up correctly.</p> @@ -476,7 +476,7 @@ p, li { white-space: pre-wrap; } <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Lucida Grande'; font-size:13pt; font-weight:400; font-style:normal;"> +</style></head><body> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This option specifies the camera behavior when navigating within the realtime preview.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Fly-through (Fix yaw)</span>: Always yaw around a fixed &quot;up&quot; axis, which is determined when a scene is loaded. This is intuitive and similar to a person walking through a scene, but assumes that the camera has already been set up correctly.</p> diff --git a/src/qtgui/sceneloader.cpp b/src/qtgui/sceneloader.cpp index 12e78eca..4a993fab 100644 --- a/src/qtgui/sceneloader.cpp +++ b/src/qtgui/sceneloader.cpp @@ -1,7 +1,7 @@ #include #include "glwidget.h" #include "sceneloader.h" -#include "../mitsuba/shandler.h" +#include SceneLoader::SceneLoader(FileResolver *resolver, const std::string &filename) : Thread("load"), m_resolver(resolver), m_filename(filename) { diff --git a/src/tests/test_samplers.cpp b/src/tests/test_samplers.cpp new file mode 100644 index 00000000..65489a3d --- /dev/null +++ b/src/tests/test_samplers.cpp @@ -0,0 +1,72 @@ +#include +#include + +MTS_NAMESPACE_BEGIN + +class TestSamplers : public TestCase { +public: + MTS_BEGIN_TESTCASE() + MTS_DECLARE_TEST(test01_Halton) + MTS_DECLARE_TEST(test02_Hammersley) + MTS_DECLARE_TEST(test03_radicalInverseIncr) + MTS_END_TESTCASE() + + void test01_Halton() { + ref sampler = static_cast (PluginManager::getInstance()-> + createObject(Sampler::m_theClass, Properties("halton"))); + + /* MATLAB: p = haltonset(5); net(p,5) */ + Float comparison[] = { + 0, 0, 0, 0, 0, + 0.500000000000000, 0.333333333333333, 0.200000000000000, 0.142857142857143, 0.090909090909091, + 0.250000000000000, 0.666666666666667, 0.400000000000000, 0.285714285714286, 0.181818181818182, + 0.750000000000000, 0.111111111111111, 0.600000000000000, 0.428571428571429, 0.272727272727273, + 0.125000000000000, 0.444444444444444, 0.800000000000000, 0.571428571428571, 0.363636363636364 + }; + + int pos = 0; + sampler->generate(); + for (int i=0; i<5; ++i) { + for (int j=0; j<5; ++j) + Assert(std::abs(sampler->next1D() - comparison[pos++]) < 1e-7); + sampler->advance(); + } + } + + void test02_Hammersley() { + Properties props("hammersley"); + props.setInteger("sampleCount", 5); + + ref sampler = static_cast (PluginManager::getInstance()-> + createObject(Sampler::m_theClass, props)); + + Float comparison[] = { + 0.0, 0, 0, 0, 0, 0, + 1.0/5.0, 0.500000000000000, 0.333333333333333, 0.200000000000000, 0.142857142857143, 0.090909090909091, + 2.0/5.0, 0.250000000000000, 0.666666666666667, 0.400000000000000, 0.285714285714286, 0.181818181818182, + 3.0/5.0, 0.750000000000000, 0.111111111111111, 0.600000000000000, 0.428571428571429, 0.272727272727273, + 4.0/5.0, 0.125000000000000, 0.444444444444444, 0.800000000000000, 0.571428571428571, 0.363636363636364 + }; + + int pos = 0; + sampler->generate(); + for (int i=0; i<5; ++i) { + for (int j=0; j<6; ++j) + Assert(std::abs(sampler->next1D() - comparison[pos++]) < 1e-7); + sampler->advance(); + } + } + + void test03_radicalInverseIncr() { + Float x = 0.0f; + + for (int i=0; i<20; ++i) { + Assert(x == radicalInverse(2, i)); + x = radicalInverseIncremental(2, x); + } + } +}; + +MTS_EXPORT_TESTCASE(TestSamplers, "Testcase for Sampler implementations") + +MTS_NAMESPACE_END diff --git a/src/tests/test_sh.cpp b/src/tests/test_sh.cpp new file mode 100644 index 00000000..aed347a1 --- /dev/null +++ b/src/tests/test_sh.cpp @@ -0,0 +1,84 @@ +#include +#include + +MTS_NAMESPACE_BEGIN + +class TestSphericalHarmonics : public TestCase { +public: + MTS_BEGIN_TESTCASE() + MTS_DECLARE_TEST(test01_shRotation) + MTS_DECLARE_TEST(test02_shSampler) + MTS_END_TESTCASE() + + void test01_shRotation() { + /* Generate a random SH expansion, rotate it and + spot-check 100 times against the original */ + + ref random = new Random(); + int bands = 8; + + SHVector vec1(bands); + for (int l=0; lnextFloat(); + + Vector axis(squareToSphere(Point2(random->nextFloat(), random->nextFloat()))); + Transform trafo = Transform::rotate(axis, random->nextFloat()*360); + Transform inv = trafo.inverse(); + SHRotation rot(vec1.getBands()); + + SHVector::rotation(trafo, rot); + SHVector vec2(bands); + + rot(vec1, vec2); + + for (int i=0; i<100; ++i) { + Vector dir1(squareToSphere(Point2(random->nextFloat(), random->nextFloat()))), dir2; + trafo(dir1, dir2); + + Float value1 = vec1.eval(dir2); + Float value2 = vec2.eval(dir1); + Assert(std::abs(value1-value2) < Epsilon); + } + } + + struct ClampedCos { + Vector axis; + ClampedCos(Vector axis) : axis(axis) { } + Float operator()(const Vector &w) const { return std::max((Float) 0, dot(w, axis)); } + }; + + void test02_shSampler() { + /* Draw 100 samples from a SH expansion of a clamped cosine-shaped + distribution and verify the returned probabilities */ + int bands = 13, numSamples = 100, depth = 12; + + Vector v = normalize(Vector(1, 2, 3)); + ref random = new Random(); + SHVector clampedCos = SHVector(bands); + clampedCos.project(ClampedCos(v), numSamples); + //Float clampedCosError = clampedCos.l2Error(ClampedCos(v), numSamples); + clampedCos.normalize(); + + //cout << "Projection error = " << clampedCosError << endl; + //cout << "Precomputing mip-maps" << endl; + ref sampler = new SHSampler(bands, depth); + //cout << "Done: "<< sampler->toString() << endl; + Float accum = 0; + int nsamples = 100, nInAvg = 0; + for (int i=0; i<=nsamples; ++i) { + Point2 sample(random->nextFloat(), random->nextFloat()); + Float pdf1 = sampler->warp(clampedCos, sample); + Float pdf2 = dot(v, sphericalDirection(sample.x, sample.y))/M_PI; + Float relerr = std::abs(pdf1-pdf2)/pdf2; + if (pdf2 > 0.01) { + accum += relerr; ++nInAvg; + Assert(relerr < 0.08); + } + } + Assert(accum / nInAvg < 0.01); + } +}; + +MTS_EXPORT_TESTCASE(TestSphericalHarmonics, "Testcase for Spherical Harmonics code") +MTS_NAMESPACE_END diff --git a/src/utils/addimages.cpp b/src/utils/addimages.cpp index 65ba6f56..675d77a5 100644 --- a/src/utils/addimages.cpp +++ b/src/utils/addimages.cpp @@ -5,8 +5,6 @@ MTS_NAMESPACE_BEGIN class AddImages : public Utility { public: - AddImages(UtilityServices *us) : Utility(us) { } - int run(int argc, char **argv) { if (argc != 6) { cout << "Add the weighted pixel values of two EXR images to produce a new one" << endl; @@ -53,9 +51,8 @@ public: return 0; } - MTS_DECLARE_CLASS() + MTS_DECLARE_UTILITY() }; -MTS_IMPLEMENT_CLASS(AddImages, false, Utility) MTS_EXPORT_UTILITY(AddImages, "Generate linear combinations of EXR images") MTS_NAMESPACE_END diff --git a/src/utils/utils_test.cpp b/src/utils/utils_test.cpp deleted file mode 100644 index 993de520..00000000 --- a/src/utils/utils_test.cpp +++ /dev/null @@ -1,611 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "../medium/maxexp.h" -#include - -using namespace mitsuba; - -void testHalton() { - ref sampler = static_cast (PluginManager::getInstance()-> - createObject(Sampler::m_theClass, Properties("halton"))); - - /* MATLAB: p = haltonset(5); net(p,5) */ - Float comparison[] = { - 0, 0, 0, 0, 0, - 0.500000000000000, 0.333333333333333, 0.200000000000000, 0.142857142857143, 0.090909090909091, - 0.250000000000000, 0.666666666666667, 0.400000000000000, 0.285714285714286, 0.181818181818182, - 0.750000000000000, 0.111111111111111, 0.600000000000000, 0.428571428571429, 0.272727272727273, - 0.125000000000000, 0.444444444444444, 0.800000000000000, 0.571428571428571, 0.363636363636364 - }; - - int pos = 0; - sampler->generate(); - for (int i=0; i<5; ++i) { - for (int j=0; j<5; ++j) - SAssert(std::abs(sampler->next1D() - comparison[pos++]) < 1e-7); - sampler->advance(); - } -} - -void testHammersley() { - Properties props("hammersley"); - props.setInteger("sampleCount", 5); - - ref sampler = static_cast (PluginManager::getInstance()-> - createObject(Sampler::m_theClass, props)); - - Float comparison[] = { - 0.0, 0, 0, 0, 0, 0, - 1.0/5.0, 0.500000000000000, 0.333333333333333, 0.200000000000000, 0.142857142857143, 0.090909090909091, - 2.0/5.0, 0.250000000000000, 0.666666666666667, 0.400000000000000, 0.285714285714286, 0.181818181818182, - 3.0/5.0, 0.750000000000000, 0.111111111111111, 0.600000000000000, 0.428571428571429, 0.272727272727273, - 4.0/5.0, 0.125000000000000, 0.444444444444444, 0.800000000000000, 0.571428571428571, 0.363636363636364 - }; - - int pos = 0; - sampler->generate(); - for (int i=0; i<5; ++i) { - for (int j=0; j<6; ++j) - SAssert(std::abs(sampler->next1D() - comparison[pos++]) < 1e-7); - sampler->advance(); - } -} - -void testRadicalInverseIncr() { - Float x = 0.0f; - - for (int i=0; i<20; ++i) { - SAssert(x == radicalInverse(2, i)); - x = radicalInverseIncremental(2, x); - } -} - -void testSpotLuminaire() { - Properties props("spot"); - props.setFloat("beamWidth", 20); - props.setFloat("cutoffAngle", 25); - ref spot = static_cast (PluginManager::getInstance()-> - createObject(Luminaire::m_theClass, props)); - ref random = new Random(); - - Spectrum power; - EmissionRecord eRec; - const int nSamples = 1000000; - for (int i=0; inextFloat(), random->nextFloat()); - Point2 dirSample(random->nextFloat(), random->nextFloat()); - spot->sampleEmission(eRec, areaSample, dirSample); - power += eRec.P / (eRec.pdfArea*eRec.pdfDir); - } - power /= (Float) nSamples; - cout << "Estimated power: " << power.toString() << endl; - cout << "Actual power: " << spot->getPower().toString() << endl; -} - -void testHG1() { - Properties props("hg"); - props.setFloat("g", .8); - - ref hg = static_cast (PluginManager::getInstance()-> - createObject(PhaseFunction::m_theClass, props)); - ref random = new Random(); - - Float sum = 0; - Vector dir1(0, 0, 1); - const int nSamples = 100000; - MediumSamplingRecord mRec; - for (int i=0; inextFloat(), random->nextFloat()))); - sum += hg->f(mRec, dir2, dir2)[0]; - } - sum *= (4 * M_PI) / nSamples; - - cout << sum << endl; -} - -void testHG2() { - const int res = 8; - Float buf[res][res]; - Properties props("hg"); - props.setFloat("g", -.2); - MediumSamplingRecord mRec; - - ref hg = static_cast (PluginManager::getInstance()-> - createObject(PhaseFunction::m_theClass, props)); - ref random = new Random(); - - const Vector dir1(0, 0, 1); - memset(buf, 0, sizeof(Float)*res*res); - Vector dir2; - PhaseFunction::ESampledType sampledType; - - const int nSamples = 10000000; - for (int i=0; inextFloat(), random->nextFloat()); - hg->sample(mRec, dir1, dir2, sampledType, sample); - Float pdf = hg->f(mRec, dir1, dir2)[0]; - Float dir2Phi = (std::atan2(dir2.y, dir2.x) + (Float) M_PI) / (2 * (Float) M_PI); - Float dir2CosTheta = (dir2.z + 1) / (Float) 2; - - int pos1 = std::max(0, std::min((int) (dir2Phi * res), res-1)); - int pos2 = std::max(0, std::min((int) (dir2CosTheta* res), res-1)); - buf[pos1][pos2] += 1/pdf; - - } - cout << "A=["; - for (int y=0; y= 2); - Float cdf[4], intervalStart[3]; - cdf[0] = 0; - - /* Requires the coefficients to be sorted in descending order */ - for (int i=0; i= 0 && index < n); - - /* Sample according to f_i */ - Float t = -std::log(std::exp(-intervalStart[index] * sigma[index]) - - normFactor * (u - cdf[index])) / sigma[index]; - - /* Compute the probability of this sample */ - prob = sigma[index] * std::exp(-sigma[index] * t) / normFactor; - - /* CDF computation - Float *lowerBound = std::lower_bound(&intervalStart[0], &intervalStart[n], t); - int index = std::max(0, (int) (lowerBound - &intervalStart[0]) - 1); - Float lower = (index==0) ? -1 : -std::pow((sigma[index]/sigma[index-1]), - -sigma[index] / (sigma[index]-sigma[index-1])); - Float upper = -std::exp(-sigma[index] * t); - Float integral = cdf[index] + (upper - lower) / normFactor; - */ - - return t; -} - -void testVariance() { - ref random = new Random(); - Spectrum mean, meanSqr, variance; - Spectrum sigmaT; - sigmaT[0] = 0.7014; - sigmaT[1] = 1.2225; - sigmaT[2] = 1.9142; - int nSamples = 100000; - - for (int chan=0; chan<3; ++chan) { - cout << "Sampling with respect to channel " << chan << endl; - mean = Spectrum(0.0f); meanSqr = Spectrum(0.0f); - for (int i=0; inextFloat(); - - Float t = -std::log(desiredAttenuation)/sigmaT[chan]; - Float prob = sigmaT[chan] * std::exp(-sigmaT[chan] * t); - Spectrum value = (sigmaT * (-t)).exp() / prob; - - Spectrum delta = value - mean; - mean += delta / (Float) (i+1); - meanSqr += delta * (value - mean); - variance = meanSqr / (Float) i; - } - cout << "Expectation : " << (Spectrum(1)/sigmaT).toString() << endl; - cout << "Mean : " << mean.toString() << endl; - cout << "Sample variance : " << variance.toString() << endl << endl; - } - - cout << "Random sampling test" << endl; - mean = Spectrum(0.0f); meanSqr = Spectrum(0.0f); - for (int i=0; inextFloat(); - int chan = random->nextInteger(3); - - Float t = -std::log(desiredAttenuation)/sigmaT[chan]; - Spectrum prob = sigmaT * (-sigmaT * t).exp(); - Spectrum value = (sigmaT * (-t)).exp() / (prob[chan]); - - Spectrum delta = value - mean; - mean += delta / (Float) (i+1); - meanSqr += delta * (value - mean); - variance = meanSqr / (Float) i; - } - cout << "Expectation : " << (Spectrum(1)/sigmaT).toString() << endl; - cout << "Mean : " << mean.toString() << endl; - cout << "Sample variance : " << variance.toString() << endl << endl; - - cout << "Single sample test" << endl; - mean = Spectrum(0.0f); meanSqr = Spectrum(0.0f); - for (int i=0; inextFloat(); - int chan = random->nextInteger(3); - - Float t = -std::log(desiredAttenuation)/sigmaT[chan]; - Spectrum prob = sigmaT * (-sigmaT * t).exp(); - Float weight = prob[chan] / (prob[0] + prob[1] + prob[2]); - Spectrum value = (sigmaT * (-t)).exp() / (prob[chan] * 1/(Float) 3) * weight; - - Spectrum delta = value - mean; - mean += delta / (Float) (i+1); - meanSqr += delta * (value - mean); - variance = meanSqr / (Float) i; - } - cout << "Expectation : " << (Spectrum(1)/sigmaT).toString() << endl; - cout << "Mean : " << mean.toString() << endl; - cout << "Sample variance : " << variance.toString() << endl << endl; - - - cout << "New distribution test" << endl; - mean = Spectrum(0.0f); meanSqr = Spectrum(0.0f); - Float extinction[] = {1.9142, 1.2225, 0.7014}; - for (int i=0; inextFloat(), prob); - - Spectrum value = (sigmaT * (-t)).exp() / prob; - - Spectrum delta = value - mean; - mean += delta / (Float) (i+1); - meanSqr += delta * (value - mean); - variance = meanSqr / (Float) i; - } - - cout << "Expectation : " << (Spectrum(1)/sigmaT).toString() << endl; - cout << "Mean : " << mean.toString() << endl; - cout << "Sample variance : " << variance.toString() << endl << endl; -} - -void testPhotonMap() { - int nPhotons = 3000000; - ref map = new PhotonMap(nPhotons); - Spectrum power; - ref random = new Random(); - power.fromLinearRGB(1, 2, 3); - power *= M_PI; - - bool storePhoton(const Point &pos, const Vector &dir, const Spectrum &power); - for (int i=0; inextFloat(), random->nextFloat())); - map->storePhoton(Point(disk.x,disk.y,0), Normal(0,0,1), Vector(0, 0, -1), power, 1); - } - - map->setScale(1/(Float) nPhotons); - map->balance(); - - for (int i=1; i<10; ++i) { - Point2 disk = squareToDisk(Point2(random->nextFloat(), random->nextFloat())) * .2; - - cout << map->estimateIrradiance(Point(disk.x, disk.y, 0), Normal(0, 0, 1), .3, 10000).toString() << endl; - cout << map->estimateIrradianceFiltered(Point(disk.x, disk.y, 0), Normal(0, 0, 1), .3, 10000).toString() << endl; - } -} - -void testMaxExp() { - std::vector sigmaT; - sigmaT.push_back(0.7014); - sigmaT.push_back(1.2225); - sigmaT.push_back(1.9142); - - MaxExpDist dist(sigmaT); - - for (int i=0; i<9; ++i) { - Float U=.01f + i/10.0f, t, pdf, cdf; - t = dist.sample(U, pdf); - cdf = dist.cdf(t); - - cout << "Sampled U=" << U << " => t = " << t << ", pdf=" << pdf << ", cdf=" << cdf << endl; - } -} - -void testHeterogeneous() { - ref sampler = static_cast (PluginManager::getInstance()-> - createObject(Sampler::m_theClass, Properties("independent"))); - - Properties props("heterogeneous"); - props.setString("filename", "scenes/cornell/smoke-density.65.vol"); - props.setSpectrum("sigmaS", Spectrum(2.62)); - props.setSpectrum("sigmaA", Spectrum(.05f)); - props.setString("strategy", "standard"); - props.setFloat("sizeMultiplier", 10); - ref standardMedium = static_cast (PluginManager::getInstance()-> - createObject(Medium::m_theClass, props)); - standardMedium->configure(); - - props = Properties("heterogeneous"); - props.setString("filename", "scenes/cornell/smoke-density.65.vol"); - props.setString("strategy", "coleman"); - props.setSpectrum("sigmaS", Spectrum(2.62)); - props.setSpectrum("sigmaA", Spectrum(.05f)); - props.setFloat("sizeMultiplier", 10); - ref colemanMedium = static_cast (PluginManager::getInstance()-> - createObject(Medium::m_theClass, props)); - colemanMedium->configure(); - - Ray ray(Point(0, 1, .4), Vector(1, 0, 0)); - - MediumSamplingRecord mRec; - - std::ofstream distr("distr.m"); - int res = 300000, failures = 0; - - distr << "samplesStandard=[" << endl; - for (int i=0; isampleDistance(ray, std::numeric_limits::infinity(), mRec, sampler)) { -// distr << mRec.t << " "; - distr << mRec.sigmaS[0] * mRec.attenuation[0] / mRec.pdf << " " << endl; - } else { - ++failures; - } - } - distr << "];" << endl; - cout << failures/(Float)res << endl; - failures = 0; - cout << failures/(Float)res << endl; - distr << "samplesColeman=[" << endl; - for (int i=0; isampleDistance(ray, std::numeric_limits::infinity(), mRec, sampler)) { -// distr << mRec.t << " "; - distr << mRec.sigmaS[0] * mRec.attenuation[0] / mRec.pdf << " " << endl; - } else { - ++failures; - } - } - distr << "];" << endl; - distr << "hist(samplesStandard, 300);" << endl; - distr << "title('Standard');" << endl; - distr << "figure;" << endl; - distr << "hist(samplesColeman, 300);" << endl; - distr.close(); - cout << failures/(Float)res << endl; -} - -void testWavelet() { - ref stream = new FileStream("cat.png", FileStream::EReadOnly); - ref bitmap = new Bitmap(Bitmap::EPNG, stream); - - ref wavelet = new Wavelet2D(bitmap); - cout << "2D compression ratio: " << wavelet->compress(.015) << endl; - ref sw = wavelet->toSparseWavelet(); - ref stream3 = new FileStream("cat2.raw", FileStream::ETruncReadWrite); - sw->serialize(stream3, NULL); - stream3->close(); - ref stream4 = new FileStream("cat2.raw", FileStream::EReadOnly); - ref sw2 = new SparseWavelet2D(stream4, NULL); - ref wavelet2 = new Wavelet2D(sw2); - ref bitmap2 = new Bitmap(bitmap->getWidth(), bitmap->getHeight(), 8); - - wavelet2->decode(bitmap2); - - size_t nEntries = 512*512; - Float mean = 0, sqrError = 0; - for (size_t i=0; igetData()[i] / (255.0f * nEntries); - sqrError += std::pow(bitmap->getData()[i]/(255.0f)-bitmap2->getData()[i]/(255.0f), 2)/nEntries; - } - - cout << "Mean: " << mean << ", error:" << std::sqrt(sqrError)/mean<< endl; - - ref stream2 = new FileStream("cat2.png", FileStream::ETruncReadWrite); - bitmap2->save(Bitmap::EPNG, stream2); -} - -void testLineIntegral() { - ref stream = new FileStream("cat.png", FileStream::EReadOnly); - ref bitmap = new Bitmap(Bitmap::EPNG, stream); - - Point2 start(0.121, 0.566); - Point2 end(0.815, 0.318); - - Vector2 dir = Vector2(end-start); - - for (int i=3; i<18; ++i) { - size_t nsteps = (int) std::ldexp((Float) 1, i); - uint8_t *data = bitmap->getData(); - double accum = 0; - for (size_t i=0; i stream = new FileStream("cat.png", FileStream::EReadOnly); - ref bitmap = new Bitmap(Bitmap::EPNG, stream); - ref wavelet = new Wavelet2D(bitmap); - ref sparse = wavelet->toSparseWavelet(); - - Point2 start(0.121, 0.566); - Point2 end(0.815, 0.318); - start*=512; end *= 512; - - cout << sparse->lineIntegral(start, end) << endl; -} - -void testSHRotation() { - ref random = new Random(); - int bands = 8; - - SHVector vec1(bands); - for (int l=0; lnextFloat(); - - Vector axis(squareToSphere(Point2(random->nextFloat(), random->nextFloat()))); - Transform trafo = Transform::rotate(axis, random->nextFloat()*360); - Transform inv = trafo.inverse(); - SHRotation rot(vec1.getBands()); - - SHVector::rotation(trafo, rot); - SHVector vec2(bands); - - rot(vec1, vec2); - - for (int i=0; i<100; ++i) { - Vector dir1(squareToSphere(Point2(random->nextFloat(), random->nextFloat()))), dir2; - trafo(dir1, dir2); - - Float value1 = vec1.eval(dir2); - Float value2 = vec2.eval(dir1); - SAssert(std::abs(value1-value2) < Epsilon); - } - cout << "Passed." << endl; -} - -struct ClampedCos { - Vector axis; - ClampedCos(Vector axis) : axis(axis) { } - Float operator()(const Vector &w) const { return std::max((Float) 0, dot(w, axis)); } -}; - -void testSHSampler() { - int bands = 25, numSamples = 100, depth = 12; - - Vector v = normalize(Vector(1, 2, 3)); - ref random = new Random(); - SHVector clampedCos = SHVector(bands); - clampedCos.project(ClampedCos(v), numSamples); - Float clampedCosError = clampedCos.l2Error(ClampedCos(v), numSamples); - clampedCos.normalize(); - - cout << "Projection error = " << clampedCosError << endl; - cout << "Precomputing mip-maps" << endl; - ref sampler = new SHSampler(bands, depth); - cout << "Done: "<< sampler->toString() << endl; - Float accum = 0; - int nsamples = 100; - for (int i=0; i<=nsamples; ++i) { - Point2 sample(random->nextFloat(), random->nextFloat()); - Float pdf1 = sampler->warp(clampedCos, sample); - Float pdf2 = dot(v, sphericalDirection(sample.x, sample.y))/M_PI; - Float relerr = std::abs(pdf1-pdf2)/pdf2; - accum += relerr; - SAssert(relerr < 0.04); - } - SAssert(accum / nsamples < 1); -} - -struct GridFunctor { - Float operator()(Float value, Float length) const { - cout << "functor(value=" << value << ", length=" << length << ")" << endl; - return 0; - } -}; - -void testGrid() { - Grid grid(Vector3i(3, 3, 1), AABB(Point(10, 0, 0), Point(11,1,1))); - Ray ray(Point(10 + 1.0f/6.0f, .5, .5), Vector(1, 0, 0), 0, 2.0f/3.0f); - - grid(0, 1, 0) = 1; - grid(1, 1, 0) = 2; - grid(2, 1, 0) = 3; - - SAssert(std::abs(grid.lookup(Point(10 + 1.0/3.0f, .5, .5))-1.5) < 1e-6); - - GridFunctor functor; - grid.rasterize(ray, functor); - - cout << "Expected: functor(value=1, length=" << 1.0f/6.0f << ")" << endl; - cout << "Expected: functor(value=2, length=" << 1.0f/3.0f << ")" << endl; - cout << "Expected: functor(value=3, length=" << 1.0f/6.0f << ")" << endl; - - SAssert(grid(0,1,0) == 0); - SAssert(grid(1,1,0) == 0); - SAssert(grid(2,1,0) == 0); -} - -int main(int argc, char **argv) { - Class::staticInitialization(); - Thread::staticInitialization(); - Logger::staticInitialization(); - Spectrum::staticInitialization(); - SHVector::staticInitialization(); - try { - /* - testHalton(); - testHammersley(); - testRadicalInverseIncr(); - testSpotLuminaire(); - testHG1(); - testHG2(); - testVariance(); - testPhotonMap(); - testMaxExp(); - testHeterogeneous(); - testLineIntegral(); - testLineIntegralWavelet(); - testWaveletBasic(); - testWavelet(); - testWavelet3D(); - testGrid(); - testSHRotation(); - testSHSampler(); - */ - } catch (const std::exception &e) { - std::cerr << "Caught a critical exeption: " << e.what() << std::endl; - exit(-1); - } catch (...) { - std::cerr << "Caught a critical exeption of unknown type! " << std::endl; - exit(-1); - } - SHVector::staticShutdown(); - Spectrum::staticShutdown(); - Logger::staticShutdown(); - Thread::staticShutdown(); - Class::staticShutdown(); - return 0; -}