From e58a0fa338778aeac4894259cb991972902b462c Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 20 Aug 2011 03:36:40 -0400 Subject: [PATCH] python binding improvements --- doc/compiling.tex | 81 +++++++------------ doc/python.tex | 37 +++++++++ include/mitsuba/core/bitmap.h | 22 ++++-- include/mitsuba/core/class.h | 3 +- include/mitsuba/core/fresolver.h | 6 +- include/mitsuba/core/mempool.h | 10 ++- include/mitsuba/core/random.h | 13 +++- include/mitsuba/core/ray.h | 8 +- src/libcore/fresolver.cpp | 8 +- src/libpython/core.cpp | 129 ++++++++++++++++++++++++++++++- 10 files changed, 241 insertions(+), 76 deletions(-) diff --git a/doc/compiling.tex b/doc/compiling.tex index a0cb10d1..80753d03 100644 --- a/doc/compiling.tex +++ b/doc/compiling.tex @@ -95,7 +95,7 @@ If all goes well, SCons should finish successfully within a few minutes: \begin{shell} scons: $\texttt{done}$ building targets. \end{shell} -To be able to run the renderer from the command line, you will also have to import it into your path: +To be able to run the renderer from the command line, you will first have to import it into your path: \begin{shell} $\text{\$}$ . setpath.sh \end{shell} @@ -173,7 +173,7 @@ If all goes well, SCons should finish successfully within a few minutes: \begin{shell} scons: $\texttt{done}$ building targets. \end{shell} -To be able to run the renderer from the command line, you will also have to import it into your path: +To be able to run the renderer from the command line, you will first have to import it into your path: \begin{shell} $\text{\$}$ . setpath.sh \end{shell} @@ -199,71 +199,42 @@ $\text{\$}$ rpmbuild -bb mitsuba-$\code{\MitsubaVersion}$/data/linux/fedora/mits \end{shell} After this command finishes, its output can be found in the directory \code{rpmbuild/RPMS}. \subsection{Building on Arch Linux} -You'll first need to install a number of dependencies. - -First, run +You'll first need to install a number of dependencies: run \begin{shell} $\text{\$}$ sudo pacman -S gcc xerces-c glew openexr boost libpng libjpeg qt scons mercurial python \end{shell} - - -There are two ways to install Mitsuba on Archlinux, the Arch way, and the other way. - -The Arch Way is to use the Aur software repository. -Accessing software in the Aur repository is easiest when using a script called \texttt{packer}. - -First download packer then use \texttt{makepkg} to build and install it. -The \texttt{-is} flags will prompt you for your sudo password and then install the package after it has finished building as well as install any needed dependencies. +For COLLADA support, you will also have to install the \code{collada-dom} +library. For this, you can either install the binary package available on +the Mitsuba website, or you can compile it yourself using the \code{PKGBUILD} +supplied with Mitsuba, i.e. \begin{shell} -$\text{\$}$ mkdir packer && cd packer -$\text{\$}$ wget http://aur.archlinux.org/packages/packer/packer/PKGBUILD -$\text{\$}$ makepkg -is +$\text{\$}$ cd +$\text{\$}$ cp /data/linux/arch/collada-dom/PKGBUILD . +$\text{\$}$ makepkg PKGBUILD +<..compiling..> +$\text{\$}$ sudo pacman -U \end{shell} - -Next, use packer to automatically download, build, and install Mitsuba as well as any needed dependencies. -The optional \code{--noedit} flag is used if you do not wish to edit the files after they are downloaded. -The optional \code{--noconfirm} flag is used if you do not wish to confirm each step of the installation. +Once all dependencies are taken care of, simply run \begin{shell} -$\text{\$}$ sudo packer -S --noedit --noconfirm mitsuba-hg glewmx collada-dom +$\text{\$}$ scons \end{shell} - -Periodically you may wish to update Mitsuba to the latest version. -To do this simply reinstall it and packer will pull and build the latest version. +inside the Mitsuba directory. In the case that you have multiple processors, you might want to parallelize the build by appending \code{-j }\emph{core count} to the command. +If all goes well, SCons should finish successfully within a few minutes: \begin{shell} -$\text{\$}$ sudo packer -S --noedit --noconfirm mitsuba-hg +scons: $\texttt{done}$ building targets. \end{shell} - -Alternatively you can skip using packer and manually download the files and install them one at a time yourself. -First install glewmx +To be able to run the renderer from the command line, you will first have to import it into your path: \begin{shell} -$\text{\$}$ mkdir glewmx && cd glewmx -$\text{\$}$ wget http://aur.archlinux.org/packages/glewmx/glewmx/PKGBUILD -$\text{\$}$ makepkg -is +$\text{\$}$ . setpath.sh \end{shell} - -And then collada-dom -\begin{shell} -$\text{\$}$ mkdir collada-dom && cd collada-dom -$\text{\$}$ wget http://aur.archlinux.org/packages/collada-dom/collada-dom/PKGBUILD -$\text{\$}$ makepkg -is -\end{shell} - -And finally Mitsuba -\begin{shell} -$\text{\$}$ mkdir mitsuba-hg && cd mitsuba-hg -$\text{\$}$ wget http://aur.archlinux.org/packages/mitsuba-hg/mitsuba-hg/PKGBUILD -$\text{\$}$ makepkg -is -\end{shell} - -To uninstall do this -\begin{shell} -$\text{\$}$ sudo pacman -R mitsuba-hg glewmx collada-dom -\end{shell} -After installing you will be able to run the renderer from the command line. +(note the period at the beginning -- this assumes that you are using \code{bash}). Having set up everything, you can now move on to \secref{basics}. - -If for some reason you are unable access the Aur files, they are also hosted at -(\url{https://www.mitsuba-renderer.org/releases/contrib/archlinux/}). +\subsubsection{Creating Arch Linux packages} +Mitsuba ships with a \code{PKGBUILD} file, which automatically builds +a package from the most recent repository version: +\begin{shell} +$\text{\$}$ makepkg data/linux/arch/mitsuba/PKGBUILD +\end{shell} \subsection{Building on Windows} On the Windows platform, Mitsuba already includes most of the dependencies in precompiled form. diff --git a/doc/python.tex b/doc/python.tex index 1f4c39a0..33a89914 100644 --- a/doc/python.tex +++ b/doc/python.tex @@ -39,4 +39,41 @@ print(myVector * 2) Log(EInfo, "" +) \end{python} +\subsection{Taking control of the logging system} +Many operations in Mitsuba will print one or more log messages +during their execution. By default, they will be printed to the console, +which may be undesirable. Similar to the C++ side, it is possible to define +custom \code{Formatter} and \code{Appender} classes to interpret and direct +the flow of these messages. +Roughly, a \code{Formatter} turns detailed +information about a logging event into a human-readable string, and a +\code{Appender} routes it to some destination (e.g. by appending it to +a file or a log viewer in a graphical user interface). Here is an example +of how to activate such extensions: +\begin{python} +import mitsuba +from mitsuba.core import * + +class MyFormatter(Formatter): + def format(self, logLevel, sourceClass, sourceThread, message, filename, line): + return "%s (log level: %s, thread: %s, class %s, file %s, line %i)" % \ + (message, str(logLevel), sourceThread.getName(), sourceClass, + filename, line) + +class MyAppender(Appender): + def append(self, logLevel, message): + print(message) + + def logProgress(self, progress, name, formatted, eta): + print("Progress message: " + formatted) + +# Get the logger associated with the current thread +logger = Thread.getThread().getLogger() +logger.setFormatter(MyFormatter()) +logger.clearAppenders() +logger.addAppender(MyAppender()) +logger.setLogLevel(EDebug) + +Log(EInfo, "Test message") +\end{python} diff --git a/include/mitsuba/core/bitmap.h b/include/mitsuba/core/bitmap.h index 7c4ffa7d..01bf0b80 100644 --- a/include/mitsuba/core/bitmap.h +++ b/include/mitsuba/core/bitmap.h @@ -67,7 +67,7 @@ public: void save(EFileFormat format, Stream *stream, int compression = 5) const; /// Return the image's title identifier - inline const std::string &getTile() const { return m_title; } + inline const std::string &getTitle() const { return m_title; } /// Return the image's author identifier inline const std::string &getAuthor() const { return m_author; } @@ -90,16 +90,28 @@ public: /// Set the image's gamma identifier (-1: sRGB) inline void setGamma(Float gamma) { m_gamma = gamma; } - /// Access the underlying raster + /** + * \brief Access the underlying raster + * \remark This function is not exposed in the Python bindings + */ inline unsigned char *getData() { return m_data; } - /// Access the underlying bit raster + /** + * \brief Access the underlying raster + * \remark This function is not exposed in the Python bindings + */ inline const unsigned char *getData() const { return m_data; } - /// Access the underlying raster (only meant for 128bpp images) + /** + * \brief Access the underlying raster (128bpp images) + * \remark This function is not exposed in the Python bindings + */ inline float *getFloatData() { return (float *) m_data; } - /// Access the underlying raster (only meant for 128bpp images) + /** + * \brief Access the underlying raster (128bpp images) + * \remark This function is not exposed in the Python bindings + */ inline const float *getFloatData() const { return (const float *) m_data; } /// Return the bitmap width diff --git a/include/mitsuba/core/class.h b/include/mitsuba/core/class.h index 4551a4e5..7e7ea0df 100644 --- a/include/mitsuba/core/class.h +++ b/include/mitsuba/core/class.h @@ -24,7 +24,6 @@ MTS_NAMESPACE_BEGIN /** * \headerfile mitsuba/core/class.h mitsuba/mitsuba.h * \brief Stores meta-information about \ref Object instances. - * \ingroup libcore * * This class provides a thin layer of RTTI (run-time type information), * which is useful for doing things like: @@ -37,6 +36,8 @@ MTS_NAMESPACE_BEGIN * * * \sa ref, Object + * \ingroup libcore + * \ingroup librender */ class MTS_EXPORT_CORE Class { public: diff --git a/include/mitsuba/core/fresolver.h b/include/mitsuba/core/fresolver.h index 45c4c68b..f3bedc4a 100644 --- a/include/mitsuba/core/fresolver.h +++ b/include/mitsuba/core/fresolver.h @@ -62,6 +62,8 @@ public: * * In comparison to \ref resolve(), this funtion returns all * matches instead of only the first one. + * + * \remark This function is not exposed in the Python bindings */ std::vector resolveAll(const fs::path &path) const; @@ -76,10 +78,10 @@ public: /// Add a search path to the resolver void addPath(const fs::path &path); - + /// Clear all stored search paths void clear(); - + /// Return a human-readable string representation std::string toString() const; diff --git a/include/mitsuba/core/mempool.h b/include/mitsuba/core/mempool.h index bbfe8367..7e57148a 100644 --- a/include/mitsuba/core/mempool.h +++ b/include/mitsuba/core/mempool.h @@ -24,10 +24,12 @@ MTS_NAMESPACE_BEGIN /** - * Basic memory pool -- allows repeated allocation & deallocation - * of objects of the same type, while attempting to keep them - * contiguous in memory and having only minimal interaction with the - * underlying allocator. + * \brief Basic memory pool for efficient allocation and deallocation + * of objects of the same type. + * + * This class attempts to keep most instances contiguous in memory, while + * having only minimal interaction with the underlying allocator. + * * \ingroup libcore */ template class MemoryPool { diff --git a/include/mitsuba/core/random.h b/include/mitsuba/core/random.h index 6975609c..622e2883 100644 --- a/include/mitsuba/core/random.h +++ b/include/mitsuba/core/random.h @@ -118,11 +118,15 @@ public: /// Seed the random generator with a single 64bit value void seed(uint64_t value = 5489ULL); - + /// Seed the random generator from another random generator void seed(Random *random); - - /// Seed the random generator from an array + + /** + * \brief Seed the random generator from an array + * \remark This function is currently not exposed + * by the Python bindings + */ void seed(uint64_t *values, uint64_t length); /// Return an integer on the [0, 2^63-1]-interval @@ -142,6 +146,9 @@ public: * given STL container. * * See Knuth, TAoCP Vol. 2 (3rd 3d), Section 3.4.2. + * + * \remark This function is currently not exposed + * by the Python bindings */ template void shuffle(Iterator it1, Iterator it2) { for (Iterator it = it2 - 1; it > it1; --it) diff --git a/include/mitsuba/core/ray.h b/include/mitsuba/core/ray.h index 81bcf1e8..065b61a6 100644 --- a/include/mitsuba/core/ray.h +++ b/include/mitsuba/core/ray.h @@ -108,7 +108,13 @@ struct Ray { #endif } - /// Return 3d coordinates of a point on the ray + /** + * \brief Return 3D coordinates of a point along the ray + * + * \remark In the Python bindings, this operator is + * exposed as a function named \c eval -- i.e. + * position lookups should be written as \c ray.eval(t) + */ inline Point operator() (Float t) const { return o + t * d; } /// Return a string representation of this ray diff --git a/src/libcore/fresolver.cpp b/src/libcore/fresolver.cpp index eba1ea05..5f74b255 100644 --- a/src/libcore/fresolver.cpp +++ b/src/libcore/fresolver.cpp @@ -86,8 +86,12 @@ std::string FileResolver::toString() const { std::ostringstream oss; oss << "FileResolver[" << endl << " paths = {" << endl; - for (size_t i=0; i #include #include +#include +#include using namespace mitsuba; @@ -139,7 +141,19 @@ static Float Matrix4x4_getItem(Matrix4x4 *matrix, bp::tuple tuple) { return matrix->operator()(i, j); } - + +Class *object_getClass(Object *object) { + return const_cast(object->getClass()); +} + +Class *class_forName(const char *name) { + return const_cast(Class::forName(name)); +} + +Class *class_getSuperClass(Class *theClass) { + return const_cast(theClass->getSuperClass()); +} + static ref instance_manager_getinstance(InstanceManager *manager, Stream *stream) { return manager->getInstance(stream); } @@ -167,6 +181,25 @@ void mts_log(ELogLevel level, const std::string &msg) { msg.c_str()); } +class FormatterWrapper : public Formatter { +public: + FormatterWrapper(PyObject *self) : m_self(self) { Py_INCREF(m_self); } + + std::string format(ELogLevel logLevel, const Class *theClass, + const Thread *thread, const std::string &text, + const char *file, int line) { + return bp::call_method(m_self, "format", logLevel, + bp::ptr(const_cast(theClass)), + bp::ptr(const_cast(thread)), text, file, line); + } + + virtual ~FormatterWrapper() { + Py_DECREF(m_self); + } +private: + PyObject *m_self; +}; + class AppenderWrapper : public Appender { public: AppenderWrapper(PyObject *self) : m_self(self) { Py_INCREF(m_self); } @@ -199,12 +232,16 @@ static Spectrum *spectrum_array_constructor(bp::list list) { return new Spectrum(spec); } +static Point ray_eval(const Ray &ray, Float t) { + return ray(t); +} + BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fromLinearRGB_overloads, fromLinearRGB, 3, 4) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fromXYZ_overloads, fromXYZ, 3, 4) void export_core() { - boost::python::to_python_converter< - fs::path, path_to_python_str>(); + bp::to_python_converter(); + bp::implicitly_convertible(); bp::object coreModule( bp::handle<>(bp::borrowed(PyImport_AddModule("mitsuba.core")))); @@ -223,8 +260,22 @@ void export_core() { bp::def("Log", &mts_log); + bp::class_("Class", bp::no_init) + .def("getName", &Class::getName, BP_RETURN_CONSTREF) + .def("isAbstract", &Class::isAbstract) + .def("isInstantiable", &Class::isInstantiable) + .def("isSerializable", &Class::isSerializable) + .def("derivesFrom", &Class::derivesFrom) + .def("getSuperClass", &class_getSuperClass, BP_RETURN_INTREF) + .def("forName", &class_forName, BP_RETURN_INTREF) + .def("unserialize", &Class::unserialize, BP_RETURN_VALUE) + .def("instantiate", &Class::instantiate, BP_RETURN_VALUE) + .staticmethod("forName"); + bp::register_ptr_to_python(); + bp::class_, boost::noncopyable>("Object", bp::no_init) .def("getRefCount", &Object::getRefCount) + .def("getClass", &object_getClass, BP_RETURN_INTREF) .def("__str__", &Object::toString); bp::register_ptr_to_python(); @@ -338,6 +389,9 @@ void export_core() { .def("append", &Appender::append) .def("logProgress", &appender_logProgress); + BP_WRAPPED_CLASS(Formatter, FormatterWrapper, Object, bp::init<>()) + .def("format", &Formatter::format); + Appender *(Logger::*logger_get_appender)(size_t) = &Logger::getAppender; BP_CLASS(Logger, Object, bp::init()) .def("logProgress", logger_logProgress) @@ -350,6 +404,8 @@ void export_core() { .def("clearAppenders", &Logger::clearAppenders) .def("getAppenderCount", &Logger::getAppenderCount) .def("getAppender", logger_get_appender, BP_RETURN_VALUE) + .def("getFormatter", &Logger::getFormatter, BP_RETURN_VALUE) + .def("setFormatter", &Logger::setFormatter) .def("getWarningCount", &Logger::getWarningCount); BP_CLASS(InstanceManager, Object, bp::init<>()) @@ -369,6 +425,56 @@ void export_core() { .def("clear", &InterpolatedSpectrum::clear) .def("zeroExtend", &InterpolatedSpectrum::zeroExtend); + BP_CLASS(Bitmap, Object, (bp::init())) + .def(bp::init()) + .def("clone", &Bitmap::clone, BP_RETURN_VALUE) + .def("clear", &Bitmap::clear) + .def("save", &Bitmap::save) + .def("setTitle", &Bitmap::setTitle) + .def("getTitle", &Bitmap::getTitle, BP_RETURN_INTREF) + .def("setAuthor", &Bitmap::setAuthor) + .def("getAuthor", &Bitmap::getAuthor, BP_RETURN_INTREF) + .def("setComment", &Bitmap::setComment) + .def("getComment", &Bitmap::getComment, BP_RETURN_INTREF) + .def("setGamma", &Bitmap::setGamma) + .def("getGamma", &Bitmap::getGamma) + .def("getWidth", &Bitmap::getWidth) + .def("getHeight", &Bitmap::getHeight) + .def("getBitsPerPixel", &Bitmap::getBitsPerPixel) + .def("getSize", &Bitmap::getSize); + + BP_SETSCOPE(Bitmap_class); + bp::enum_("EFileFormat") + .value("EPNG", Bitmap::EPNG) + .value("EEXR", Bitmap::EEXR) + .value("ETGA", Bitmap::ETGA) + .value("EBMP", Bitmap::EBMP) + .value("EJPEG", Bitmap::EJPEG) + .export_values(); + BP_SETSCOPE(coreModule); + + BP_CLASS(FileResolver, Object, bp::init<>()) + .def("resolve", &FileResolver::resolve, BP_RETURN_VALUE) + .def("resolveAbsolute", &FileResolver::resolveAbsolute, BP_RETURN_VALUE) + .def("clone", &FileResolver::clone, BP_RETURN_VALUE) + .def("addPath", &FileResolver::addPath) + .def("clear", &FileResolver::clear); + + void (Random::*random_seed_random)(Random *) = &Random::seed; + void (Random::*random_seed_uint64_t)(uint64_t) = &Random::seed; + BP_CLASS(Random, Object, bp::init<>()) + .def(bp::init()) + .def(bp::init()) + .def(bp::init()) + .def("set", &Random::set) + .def("seed", random_seed_random) + .def("seed", random_seed_uint64_t) + .def("nextULong", &Random::nextULong) + .def("nextUInt", &Random::nextUInt) + .def("nextSize", &Random::nextSize) + .def("nextFloat", &Random::nextFloat) + .def("serialize", &Random::serialize); + BP_STRUCT(Spectrum, bp::init<>()) .def("__init__", bp::make_constructor(spectrum_array_constructor)) .def(bp::init()) @@ -610,6 +716,23 @@ void export_core() { .def(bp::self /= Float()) .def("__str__", &Matrix4x4::toString); + bp::class_("Ray", bp::init<>()) + .def(bp::init()) + .def(bp::init()) + .def(bp::init()) + .def(bp::init()) + .def_readwrite("o", &Ray::o) + .def_readwrite("d", &Ray::d) + .def_readwrite("dRcp", &Ray::dRcp) + .def_readwrite("mint", &Ray::mint) + .def_readwrite("maxt", &Ray::maxt) + .def_readwrite("time", &Ray::time) + .def("setOrigin", &Ray::setOrigin) + .def("setDirection", &Ray::setDirection) + .def("setTime", &Ray::setTime) + .def("eval", &ray_eval, BP_RETURN_VALUE) + .def("__str__", &Ray::toString); + bp::detail::current_scope = oldScope; }