testcase support -- see 'test_samplers.cpp' for an example

metadata
Wenzel Jakob 2010-09-02 17:57:04 +02:00
parent e0b52ef022
commit 1c60e7368d
22 changed files with 409 additions and 722 deletions

View File

@ -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?

View File

@ -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}

View File

@ -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;

View File

@ -234,7 +234,7 @@ public:
Float value = f(sphericalDirection(theta, phi))*std::sin(theta)
* weightExt*weightInt;
for (int l=0; l<m_bands; ++l) {
for (int m=1; m<=l; ++m) {
Float L = legendre(l, m, cosTheta) * normalization(l, m);

View File

@ -36,6 +36,9 @@ extern MTS_EXPORT_CORE std::string toUpperCase(const std::string &string);
/// Trim spaces (' ', '\n', '\r', '\t') from the ends of a string
extern MTS_EXPORT_CORE std::string trim(const std::string& str);
/// Determines whether a string starts with the string given as second parameter
extern MTS_EXPORT_CORE bool startsWith(const std::string& str, const std::string& start);
/// Determines whether a string ends with the string given as second parameter
extern MTS_EXPORT_CORE bool endsWith(const std::string& str, const std::string& end);

View File

@ -11,7 +11,7 @@
#include <map>
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 */

View File

@ -1,10 +1,45 @@
#if !defined(__TESTCASE_H)
#define __TESTCASE_H
#include <mitsuba/mitsuba.h>
#include <mitsuba/render/util.h>
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 */

View File

@ -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<Scene> 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 <tt>argc</tt>
* and <tt>argv</tt> parameters contain any
@ -38,17 +26,17 @@ protected:
virtual ~Utility() { }
/// Load a scene
inline ref<Scene> loadScene(const std::string &fname) {
return m_utilityServices->loadScene(fname);
}
private:
UtilityServices *m_utilityServices;
ref<Scene> 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; \

View File

@ -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 {

View File

@ -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);

View File

@ -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

View File

@ -1,6 +1,8 @@
#include <mitsuba/core/platform.h>
#include <xercesc/parsers/SAXParser.hpp>
#include "shandler.h"
#include <mitsuba/render/shandler.h>
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

View File

@ -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

36
src/librender/util.cpp Normal file
View File

@ -0,0 +1,36 @@
#include <mitsuba/core/platform.h>
#include <xercesc/parsers/SAXParser.hpp>
#include <mitsuba/render/util.h>
#include <mitsuba/render/shandler.h>
MTS_NAMESPACE_BEGIN
ref<Scene> 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<std::string, std::string> parameters;
SceneHandler *handler = new SceneHandler(parameters);
parser->setDoNamespaces(true);
parser->setDocumentHandler(handler);
parser->setErrorHandler(handler);
parser->parse(filename.c_str());
ref<Scene> scene = handler->getScene();
delete parser;
delete handler;
return scene;
}
MTS_IMPLEMENT_CLASS(Utility, true, Object)
MTS_NAMESPACE_END

View File

@ -5,9 +5,9 @@
#include <mitsuba/core/sshstream.h>
#include <mitsuba/core/shvector.h>
#include <mitsuba/render/renderjob.h>
#include <mitsuba/render/shandler.h>
#include <fstream>
#include <stdexcept>
#include "shandler.h"
#if defined(WIN32)
#include "getopt.h"

View File

@ -6,10 +6,10 @@
#include <mitsuba/core/shvector.h>
#include <mitsuba/render/util.h>
#include <mitsuba/render/renderjob.h>
#include <mitsuba/render/shandler.h>
#include <fstream>
#include <stdexcept>
#include <sys/types.h>
#include "shandler.h"
#if !defined(WIN32)
#include <dlfcn.h>
@ -24,36 +24,6 @@
using namespace mitsuba;
class UtilityServicesImpl : public UtilityServices {
public:
ref<Scene> 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<std::string, std::string> parameters;
SceneHandler *handler = new SceneHandler(parameters);
parser->setDoNamespaces(true);
parser->setDocumentHandler(handler);
parser->setErrorHandler(handler);
parser->parse(filename.c_str());
ref<Scene> 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] <utility name> [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<std::string> 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<std::string> dirPaths = resolver->resolveAllAbsolute("plugins");
std::set<std::string> 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; i<dirPaths.size(); ++i) {
std::string dirPath = dirPaths[i];
#if !defined(WIN32)
DIR *directory;
struct dirent *dirinfo;
if ((directory = opendir(dirPath.c_str())) == NULL)
SLog(EInfo, "Could not open plugin directory");
while ((dirinfo = readdir(directory)) != NULL) {
std::string fname(dirinfo->d_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> utility = plugin.createUtility();
TestCase *testCase = static_cast<TestCase *>(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> 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 (...) {

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>377</width>
<height>480</height>
<height>500</height>
</rect>
</property>
<property name="sizePolicy">
@ -455,7 +455,7 @@ movements while navigating in the realt-time preview.</string>
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Lucida Grande'; font-size:13pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;/style&gt;&lt;/head&gt;&lt;body&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;This option specifies the camera behavior when navigating within the realtime preview.&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Fly-through (Fix yaw)&lt;/span&gt;: Always yaw around a fixed &amp;quot;up&amp;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.&lt;/p&gt;
@ -476,7 +476,7 @@ p, li { white-space: pre-wrap; }
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Lucida Grande'; font-size:13pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;/style&gt;&lt;/head&gt;&lt;body&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;This option specifies the camera behavior when navigating within the realtime preview.&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Fly-through (Fix yaw)&lt;/span&gt;: Always yaw around a fixed &amp;quot;up&amp;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.&lt;/p&gt;

View File

@ -1,7 +1,7 @@
#include <xercesc/parsers/SAXParser.hpp>
#include "glwidget.h"
#include "sceneloader.h"
#include "../mitsuba/shandler.h"
#include <mitsuba/render/shandler.h>
SceneLoader::SceneLoader(FileResolver *resolver, const std::string &filename)
: Thread("load"), m_resolver(resolver), m_filename(filename) {

View File

@ -0,0 +1,72 @@
#include <mitsuba/render/testcase.h>
#include <mitsuba/core/plugin.h>
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> sampler = static_cast<Sampler *> (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> sampler = static_cast<Sampler *> (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

84
src/tests/test_sh.cpp Normal file
View File

@ -0,0 +1,84 @@
#include <mitsuba/render/testcase.h>
#include <mitsuba/core/shvector.h>
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> random = new Random();
int bands = 8;
SHVector vec1(bands);
for (int l=0; l<bands; ++l)
for (int m=-l; m<=l; ++m)
vec1(l, m) = random->nextFloat();
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> 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<SHSampler> 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

View File

@ -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

View File

@ -1,611 +0,0 @@
#include <mitsuba/mitsuba.h>
#include <mitsuba/core/plugin.h>
#include <mitsuba/render/scene.h>
#include <mitsuba/render/photonmap.h>
#include <mitsuba/core/wavelet.h>
#include <mitsuba/core/shvector.h>
#include <mitsuba/core/grid.h>
#include "../medium/maxexp.h"
#include <fstream>
using namespace mitsuba;
void testHalton() {
ref<Sampler> sampler = static_cast<Sampler *> (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> sampler = static_cast<Sampler *> (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<Luminaire> spot = static_cast<Luminaire *> (PluginManager::getInstance()->
createObject(Luminaire::m_theClass, props));
ref<Random> random = new Random();
Spectrum power;
EmissionRecord eRec;
const int nSamples = 1000000;
for (int i=0; i<nSamples; ++i) {
Point2 areaSample(random->nextFloat(), 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<PhaseFunction> hg = static_cast<PhaseFunction *> (PluginManager::getInstance()->
createObject(PhaseFunction::m_theClass, props));
ref<Random> random = new Random();
Float sum = 0;
Vector dir1(0, 0, 1);
const int nSamples = 100000;
MediumSamplingRecord mRec;
for (int i=0; i<nSamples; ++i) {
Vector dir2(squareToSphere(Point2(random->nextFloat(), 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<PhaseFunction> hg = static_cast<PhaseFunction *> (PluginManager::getInstance()->
createObject(PhaseFunction::m_theClass, props));
ref<Random> 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; i<nSamples; ++i) {
Point2 sample(random->nextFloat(), 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<res; ++y) {
for (int x=0; x<res; ++x)
cout << buf[y][x] / nSamples << " ";
cout << ";" << endl;
}
cout << "];" << endl;
}
void testMATLAB() {
int count = 256;
int dim1 = 17, dim2 = 19;
cout << "x=[";
for (int i=1; i<=count; ++i) {
cout << radicalInverse(dim1, i);
if (i+1<=count)
cout << " ";
}
cout << "];" << endl;
cout << "y=[";
for (int i=1; i<=count; ++i) {
cout << radicalInverse(dim2, i);
if (i+1<=count)
cout << " ";
}
cout << "];" << endl;
}
Float sample(Float *sigma, int n, Float u, Float &prob) {
SAssert(n >= 2);
Float cdf[4], intervalStart[3];
cdf[0] = 0;
/* Requires the coefficients to be sorted in descending order */
for (int i=0; i<n; ++i) {
/* Integrate max(f_1(t), .., f_n(t)) on [0, \infty]*/
Float lower = (i==0) ? -1 : -std::pow((sigma[i]/sigma[i-1]),
-sigma[i] / (sigma[i]-sigma[i-1]));
Float upper = (i==n-1) ? 0 : -std::pow((sigma[i+1]/sigma[i]),
-sigma[i] / (sigma[i+1]-sigma[i]));
cdf[i+1] = cdf[i] + (upper - lower);
/* Store the interval covered by each f_i */
intervalStart[i] = (i == 0) ? 0
: std::log(sigma[i]/sigma[i-1]) / (sigma[i]-sigma[i-1]);
}
/* Turn into a discrete CDF and keep the normalization factor */
Float normFactor = cdf[n];
for (int i=0; i<=n; ++i)
cdf[i] /= normFactor;
/* Find the f_i for this sample */
Float *lowerBound = std::lower_bound(&cdf[0], &cdf[n+1], u);
int index = std::max(0, (int) (lowerBound - &cdf[0]) - 1);
SAssert(index >= 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> 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; i<nSamples; ++i) {
Float desiredAttenuation = 1-random->nextFloat();
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; i<nSamples; ++i) {
Float desiredAttenuation = 1-random->nextFloat();
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; i<nSamples; ++i) {
Float desiredAttenuation = 1-random->nextFloat();
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; i<nSamples; ++i) {
Float prob;
Float t = sample(extinction, 3, random->nextFloat(), 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<PhotonMap> map = new PhotonMap(nPhotons);
Spectrum power;
ref<Random> 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; i<nPhotons; ++i) {
Point2 disk = squareToDisk(Point2(random->nextFloat(), 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<Float> 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> sampler = static_cast<Sampler *> (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<Medium> standardMedium = static_cast<Medium *> (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<Medium> colemanMedium = static_cast<Medium *> (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; i<res; ++i) {
if (standardMedium->sampleDistance(ray, std::numeric_limits<Float>::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; i<res; ++i) {
if (colemanMedium->sampleDistance(ray, std::numeric_limits<Float>::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<FileStream> stream = new FileStream("cat.png", FileStream::EReadOnly);
ref<Bitmap> bitmap = new Bitmap(Bitmap::EPNG, stream);
ref<Wavelet2D> wavelet = new Wavelet2D(bitmap);
cout << "2D compression ratio: " << wavelet->compress(.015) << endl;
ref<SparseWavelet2D> sw = wavelet->toSparseWavelet();
ref<FileStream> stream3 = new FileStream("cat2.raw", FileStream::ETruncReadWrite);
sw->serialize(stream3, NULL);
stream3->close();
ref<FileStream> stream4 = new FileStream("cat2.raw", FileStream::EReadOnly);
ref<SparseWavelet2D> sw2 = new SparseWavelet2D(stream4, NULL);
ref<Wavelet2D> wavelet2 = new Wavelet2D(sw2);
ref<Bitmap> 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; i<nEntries; ++i) {
mean += bitmap->getData()[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<FileStream> stream2 = new FileStream("cat2.png", FileStream::ETruncReadWrite);
bitmap2->save(Bitmap::EPNG, stream2);
}
void testLineIntegral() {
ref<FileStream> stream = new FileStream("cat.png", FileStream::EReadOnly);
ref<Bitmap> 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<nsteps; ++i) {
Vector2 p = (start + dir * (i / (Float) (nsteps-1)));
p *= 512;
int x = (int) p.x, y = (int) p.y;
accum += data[x+512*y] / 255.0f;
}
accum /= nsteps;
accum *= dir.length();
printf("%i steps=%f\n", (int) nsteps, accum);
}
}
void testLineIntegralWavelet() {
ref<FileStream> stream = new FileStream("cat.png", FileStream::EReadOnly);
ref<Bitmap> bitmap = new Bitmap(Bitmap::EPNG, stream);
ref<Wavelet2D> wavelet = new Wavelet2D(bitmap);
ref<SparseWavelet2D> 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> random = new Random();
int bands = 8;
SHVector vec1(bands);
for (int l=0; l<bands; ++l)
for (int m=-l; m<=l; ++m)
vec1(l, m) = random->nextFloat();
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> 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<SHSampler> 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<Float> 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;
}