python binding improvements

metadata
Wenzel Jakob 2011-08-20 03:36:40 -04:00
parent 0a766c8c22
commit e58a0fa338
10 changed files with 241 additions and 76 deletions

View File

@ -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 <some-temporary-directory>
$\text{\$}$ cp <path-to-mitsuba>/data/linux/arch/collada-dom/PKGBUILD .
$\text{\$}$ makepkg PKGBUILD
<..compiling..>
$\text{\$}$ sudo pacman -U <generated package file>
\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.

View File

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

View File

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

View File

@ -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
* </ul>
*
* \sa ref, Object
* \ingroup libcore
* \ingroup librender
*/
class MTS_EXPORT_CORE Class {
public:

View File

@ -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<fs::path> resolveAll(const fs::path &path) const;

View File

@ -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 <typename T> class MemoryPool {

View File

@ -122,7 +122,11 @@ public:
/// 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 <typename Iterator> void shuffle(Iterator it1, Iterator it2) {
for (Iterator it = it2 - 1; it > it1; --it)

View File

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

View File

@ -86,8 +86,12 @@ std::string FileResolver::toString() const {
std::ostringstream oss;
oss << "FileResolver[" << endl
<< " paths = {" << endl;
for (size_t i=0; i<m_paths.size(); ++i)
oss << " \"" << m_paths[i].file_string() << "\"," << endl;
for (size_t i=0; i<m_paths.size(); ++i) {
oss << " \"" << m_paths[i].file_string() << "\"";
if (i+1 < m_paths.size())
oss << ",";
oss << endl;
}
oss << " }" << endl
<< "]";
return oss.str();

View File

@ -8,6 +8,8 @@
#include <mitsuba/core/transform.h>
#include <mitsuba/core/properties.h>
#include <mitsuba/core/appender.h>
#include <mitsuba/core/bitmap.h>
#include <mitsuba/core/random.h>
using namespace mitsuba;
@ -140,6 +142,18 @@ static Float Matrix4x4_getItem(Matrix4x4 *matrix, bp::tuple tuple) {
return matrix->operator()(i, j);
}
Class *object_getClass(Object *object) {
return const_cast<Class *>(object->getClass());
}
Class *class_forName(const char *name) {
return const_cast<Class *>(Class::forName(name));
}
Class *class_getSuperClass(Class *theClass) {
return const_cast<Class *>(theClass->getSuperClass());
}
static ref<SerializableObject> 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<std::string>(m_self, "format", logLevel,
bp::ptr(const_cast<Class *>(theClass)),
bp::ptr(const_cast<Thread *>(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<fs::path, path_to_python_str>();
bp::implicitly_convertible<std::string, fs::path>();
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, boost::noncopyable>("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<Class*>();
bp::class_<Object, ref<Object>, 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<Object*>();
@ -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<ELogLevel>())
.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<int, int, int>()))
.def(bp::init<Bitmap::EFileFormat, Stream *>())
.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_<Bitmap::EFileFormat>("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<uint64_t>())
.def(bp::init<Random *>())
.def(bp::init<Stream *, InstanceManager *>())
.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<Float>())
@ -610,6 +716,23 @@ void export_core() {
.def(bp::self /= Float())
.def("__str__", &Matrix4x4::toString);
bp::class_<Ray>("Ray", bp::init<>())
.def(bp::init<Ray &>())
.def(bp::init<Ray &, Float, Float>())
.def(bp::init<Point, Vector, Float>())
.def(bp::init<Point, Vector, Float, Float, Float>())
.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;
}