done with the python integration (for now)

metadata
Wenzel Jakob 2011-08-21 18:54:13 -04:00
parent 38b1337788
commit 94fd78d0a5
27 changed files with 560 additions and 160 deletions

View File

@ -190,6 +190,7 @@ might start the with parameters such as the following
$\texttt{\$}$ mitsuba -xj 2 -c machine1;machine2;... animation/frame_*.xml
\end{shell}
\subsection{Direct connection server}
\label{sec:mtssrv}
A Mitsuba compute node can be created using the \code{mtssrv} executable. By default,
it will listen on port 7554:
\begin{shell}

View File

@ -8,7 +8,7 @@ software construction tool. There are some differences between the different ope
more details, please refer to one of the next sections depending on which one you use.
\subsection{Common steps}
To get started, you will need to download a recent version of Mitsuba. Make sure that you have the Mercurial (\url{http://mercurial.selenic.com/}) versioning system installed\footnote{On Windows, you might also want the convenient TortoiseHG shell extension (\url{http://tortoisehg.bitbucket.org/}) to run the subsequent steps directly from the Explorer.} and enter the following at the command prompt:
To get started, you will need to download a recent version of Mitsuba. Make sure that you have the Mercurial (\url{http://mercurial.selenic.com/}) versioning system installed\footnote{On Windows, you might want to use the convenient TortoiseHG shell extension (\url{http://tortoisehg.bitbucket.org/}) to run the subsequent steps directly from the Explorer.} and enter the following at the command prompt:
\begin{shell}
$\texttt{\$}$ hg clone https://www.mitsuba-renderer.org/hg/mitsuba
\end{shell}
@ -199,7 +199,7 @@ $\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: run
You'll first need to install a number of dependencies:
\begin{shell}
$\text{\$}$ sudo pacman -S gcc xerces-c glew openexr boost libpng libjpeg qt scons mercurial python
\end{shell}

View File

@ -81,6 +81,8 @@
% Listings settings
\lstset{
basicstyle = \small\ttfamily\raggedright,
commentstyle=\color{lstcomment}\itshape,
stringstyle=\color{lstattrib},
mathescape = true,
frame = lrtb,
backgroundcolor = \color{lstshade},
@ -111,6 +113,7 @@
},
}
% Set up textpos
\TPGrid{68}{108}
@ -125,7 +128,7 @@
{}
\lstnewenvironment{cpp}[1][]{\lstset{language=c++, #1}}
{}
\lstnewenvironment{python}[1][]{\lstset{language=python, #1}}
\lstnewenvironment{python}[1][]{\lstset{language=Python, #1}}
{}
\lstnewenvironment{xml}[1][]{\lstset{language=xml, #1}}
{}
@ -156,8 +159,8 @@
\IfFileExists{plugins_generated.tex}{\include{plugins_generated}}{}
%\include{import}
\include{development}
%\include{integrator}
%\include{parallelization}
\include{integrator}
\include{parallelization}
\include{python}
\include{acknowledgements}

View File

@ -2,7 +2,7 @@
\label{sec:python}
A recent feature of Mitsuba is a simple Python interface to the renderer API.
While the interface is still limited at this point, it can already be
used for simple automation purposes. To access the API, start your Python
used for many useful purposes. To access the API, start your Python
interpreter and enter
\begin{python}
import mitsuba
@ -10,7 +10,7 @@ import mitsuba
For this to work on MacOS X, you will first have to run the ``\emph{Apple
Menu}$\to$\emph{Command-line access}'' menu item from within Mitsuba.
On Windows and non-packaged Linux builds, you may have to update the extension
search path before issuing the \code{import} command:
search path before issuing the \code{import} command, e.g.:
\begin{python}
import sys
@ -20,26 +20,242 @@ sys.path.append('dist/python')
import mitsuba
\end{python}
For an overview of the currently exposed API subset, refer
For an overview of the currently exposed API subset, please refer
to the following page: \url{http://www.mitsuba-renderer.org/api/group__libpython.html}.
\subsection{Fundamentals}
All
\subsection{Basics}
Generally, the Python API tries to mimic the C++ API as closely as possible.
Where applicable, the Python classes and methods replicate overloaded operators,
overridable virtual function calls, and default arguments. Under rare circumstances,
some features are inherently non-portable due to fundamental differences between the
two programming languages. In this case, the API documentation will contain further
information.
Where applicable, the Python wrapper supports operator overloading,
default arguments, and
Mitsuba's linear algebra-related classes are usable with essentially the
same syntax as their C++ versions --- for example, the following snippet
creates and rotates a unit vector.
\begin{python}
import mitsuba
from mitsuba.core import *
# Create a normalized direction vector
myVector = normalize(Vector(1.0, 2.0, 3.0))
print(myVector * 2)
# 90 deg. rotation around the Y axis
trafo = Transform.rotate(Vector(0, 1, 0), 90)
Log(EInfo, "" +)
# Apply the rotation and display the result
print(trafo * myVector)
\end{python}
\subsection{Taking control of the logging system}
\subsection{Recipes}
The following section contains a series of ``recipes'' on how to do
certain things with the help of the Python bindings.
\subsubsection{Loading a scene}
The following script demonstrates how to use the
\code{FileResolver} and \code{SceneHandler} classes to
load a Mitsuba scene from an XML file:
\begin{python}
import mitsuba
from mitsuba.core import *
from mitsuba.render import SceneHandler
# Get a reference to the thread's file resolver
fileResolver = Thread.getThread().getFileResolver()
# Add the search path needed to load plugins
fileResolver.addPath('<path to mitsuba directory>')
# Add the search path needed to load scene resources
fileResolver.addPath('<path to scene directory>')
# Optional: supply parameters that can be accessed
# by the scene (e.g. as $\text{\color{lstcomment}\itshape\texttt{\$}}$myParameter)
paramMap = StringMap()
paramMap['myParameter'] = 'value'
# Load the scene from an XML file
scene = SceneHandler.loadScene(fileResolver, paramMap)
# Display a textual summary of the scene's contents
print(scene)
\end{python}
\subsubsection{Rendering a loaded scene}
Once a scene has been loaded, it can be rendered as follows:
\begin{python}
from mitsuba.core import *
from mitsuba.render import RenderQueue, RenderJob
import multiprocessing
scheduler = Scheduler.getInstance()
# Start up the scheduling system with one worker per local core
for i in range(0, multiprocessing.cpu_count()):
scheduler.registerWorker(LocalWorker('wrk%i' % i))
scheduler.start()
# Create a queue for tracking render jobs
queue = RenderQueue()
scene.setDestinationFile('renderedResult')
# Create a render job and insert it into the queue
job = RenderJob('myRenderJob', scene, queue)
job.start()
# Wait for all jobs to finish and release resources
queue.waitLeft(0)
queue.join()
# Print some statistics about the rendering process
print(Statistics.getInstance().getStats())
\end{python}
\subsubsection{Rendering over the network}
To render over the network, you must first set up one or
more machines that run the \code{mtssrv} server (see \secref{mtssrv}).
A network node can then be registered with the scheduler as follows:
\begin{python}
# Connect to a socket on a named host or IP address
# 7554 is the default port of 'mtssrv'
stream = SocketStream('128.84.103.222', 7554)
# Create a remote worker instance that communicates over the stream
remoteWorker = RemoteWorker('netWorker', stream)
scheduler = Scheduler.getInstance()
# Register the remote worker (and any other potential workers)
scheduler.registerWorker(remoteWorker)
scheduler.start()
\end{python}
\subsubsection{Constructing custom scenes from Python}
Dynamically constructing Mitsuba scenes entails loading a series of external
plugins, instantiating them with custom parameters, and finally assembling
them into an object graph.
For instance, the following snippet shows how to create a basic
perspective camera with a film that writes PNG images:
\begin{python}
from mitsuba.core import *
pmgr = PluginManager.getInstance()
# Encodes parameters on how to instantiate the 'perspective' plugin
cameraProps = Properties('perspective')
cameraProps['toWorld'] = Transform.lookAt(
Point(0, 0, -10), # Camera origin
Point(0, 0, 0), # Camera target
Vector(0, 1, 0) # 'up' vector
)
cameraProps['fov'] = 45.0
# Encodes parameters on how to instantiate the 'pngfilm' plugin
filmProps = Properties('pngfilm')
filmProps['width'] = 1920
filmProps['height'] = 1080
# Load and instantiate the plugins
camera = pmgr.createObject(cameraProps)
film = pmgr.createObject(filmProps)
# First configure the film and then add it to the camera
film.configure()
camera.addChild('film', film)
# Now, the camera can be configured
camera.configure()
\end{python}
The above code fragment uses the plugin manager to construct a
\code{Camera} instance from an external plugin named
\texttt{perspective.so/dll/dylib} and adds a child object
named \texttt{film}, which is a \texttt{Film} instance loaded from the
plugin \texttt{pngfilm.so/dll/dylib}.
Each time after instantiating a plugin, all child objects are added, and
finally the plugin's \code{configure()} method must be called.
Creating scenes in this manner ends up being rather laborious.
Since Python comes with a powerful dynamically-typed dictionary
primitive, Mitsuba additionally provides a more ``pythonic''
alternative that makes use of this facility:
\begin{python}
from mitsuba.core import *
pmgr = PluginManager.getInstance()
camera = pmgr.create({
'type' : 'perspective',
'toWorld' : Transform.lookAt(
Point(0, 0, -10),
Point(0, 0, 0),
Vector(0, 1, 0)
),
'film' : {
'type' : 'pngfilm',
'width' : 1920,
'height' : 1080
}
})
\end{python}
This code does exactly the same as the previous snippet.
By the time \code{PluginManager.create} returns, the object
hierarchy has already been assembled, and the
\code{configure()} method of every object
has been called.
Finally, here is an full example that creates a basic scene
which can be rendered. It describes a sphere lit by a point
light, rendered using the direct illumination integrator.
\begin{python}
from mitsuba.core import *
from mitsuba.render import Scene
scene = Scene()
# Create a camera, film & sample generator
scene.addChild(pmgr.create({
'type' : 'perspective',
'toWorld' : Transform.lookAt(
Point(0, 0, -10),
Point(0, 0, 0),
Vector(0, 1, 0)
),
'film' : {
'type' : 'pngfilm',
'width' : 1920,
'height' : 1080
},
'sampler' : {
'type' : 'ldsampler',
'sampleCount' : 2
}
}))
# Set the integrator
scene.addChild(pmgr.create({
'type' : 'direct'
}))
# Add a light source
scene.addChild(pmgr.create({
'type' : 'point',
'position' : Point(5, 0, -10),
'intensity' : Spectrum(100)
}))
# Add a shape
scene.addChild(pmgr.create({
'type' : 'sphere',
'center' : Point(0, 0, 0),
'radius' : 1.0,
'bsdf' : {
'type' : 'diffuse',
'reflectance' : Spectrum(0.4)
}
}))
\end{python}
\subsubsection{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
@ -57,7 +273,7 @@ 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)" % \
return '%s (log level: %s, thread: %s, class %s, file %s, line %i)' % \
(message, str(logLevel), sourceThread.getName(), sourceClass,
filename, line)
@ -66,7 +282,7 @@ class MyAppender(Appender):
print(message)
def logProgress(self, progress, name, formatted, eta):
print("Progress message: " + formatted)
print('Progress message: ' + formatted)
# Get the logger associated with the current thread
logger = Thread.getThread().getLogger()
@ -75,5 +291,5 @@ logger.clearAppenders()
logger.addAppender(MyAppender())
logger.setLogLevel(EDebug)
Log(EInfo, "Test message")
Log(EInfo, 'Test message')
\end{python}

View File

@ -23,13 +23,16 @@
MTS_NAMESPACE_BEGIN
/** \brief Abstract interface for objects that reference shared network resources.
/** \brief Abstract interface for objects that reference shared network
* resources.
*
* When a networked object is serialized as part of a parallel process executed on
* multiple machines, the object is first given the opportunity to bind named resources
* to the process (by a call to <tt>\ref bindUsedResources()</tt>). These will then be
* distributed to all participating compute servers. Once unserialized on the remote side,
* <tt>\ref wakeup()</tt> is called to let the object re-associate with the shared resources.
* When a networked object is serialized as part of a parallel process
* executed on multiple machines, the object is first given the
* opportunity to bind named resources to the process (by a call to
* \ref bindUsedResources()). These will then be distributed to all
* participating compute servers. Once unserialized on the remote side,
* \ref wakeup() is called to let the object re-associate with the
* shared resources.
*
* \ingroup libcore
*/

View File

@ -30,8 +30,8 @@ MTS_NAMESPACE_BEGIN
* \brief Abstract plugin class -- represents loadable configurable objects
* and utilities.
*
* Please see the <tt>\ref ConfigurableObject</tt> and
* <tt>\ref Utility</tt> classes for details
* Please see the \ref ConfigurableObject and \ref Utility classes for
* details.
*
* \ingroup libcore
*/
@ -86,6 +86,44 @@ private:
* \brief The plugin manager is responsible for resolving and
* loading external plugins.
*
* Ordinarily, this class will be used by making repeated calls to
* the \ref createObject() methods. The generated instances are then
* assembled into a final object graph, such as a scene. One such
* examples is the \ref SceneHandler class, which parses an XML
* scene file by esentially translating the XML elements into calls
* to \ref createObject().
*
* Since this kind of construction method can be tiresome when
* dynamically building scenes from Python, this class has an
* additional Python-only method \c create(), which works as follows:
*
* \code
* from mitsuba.core import *
*
* pmgr = PluginManager.getInstance()
* camera = pmgr.create({
* "type" : "perspective",
* "toWorld" : Transform.lookAt(
* Point(0, 0, -10),
* Point(0, 0, 0),
* Vector(0, 1, 0)
* ),
* "film" : {
* "type" : "pngfilm",
* "width" : 1920,
* "height" : 1080
* }
* })
* \endcode
*
* The above snippet constructs a \ref Camera instance from a
* plugin named \c perspective.so/dll/dylib and adds a child object
* named \c film, which is a \ref Film instance loaded from the
* plugin \c pngfilm.so/dll/dylib. By the time the function
* returns, the object hierarchy has already been assembled, and the
* \ref ConfigurableObject::configure() methods of every object
* has been called.
*
* \ingroup libcore
* \ingroup libpython
*/

View File

@ -340,10 +340,10 @@ public:
*/
static void rotation(const Transform &t, SHRotation &rot);
/** \brief Precomputes normalization coefficients for the first few bands */
/// Precomputes normalization coefficients for the first few bands
static void staticInitialization();
/// Free the memory taken by staticInitialization()
/// Free the memory taken up by staticInitialization()
static void staticShutdown();
protected:
/// Helper function for rotation() -- computes a diagonal block based on the previous level

View File

@ -41,6 +41,7 @@ MTS_NAMESPACE_BEGIN
* Note: SSH streams are set to use network byte order by default.
*
* \ingroup libcore
* \ingroup libpython
*/
class MTS_EXPORT_CORE SSHStream : public Stream {
public:
@ -49,10 +50,11 @@ public:
// =============================================================
/**
* Create a new SSH stream. The timeout parameter specifies specifies
* the maximum amount of time that can be spent before failing to
* create the initial connection. This feature is unsupported
* (and ignored) on Windows.
* \brief Create a new SSH stream.
*
* The timeout parameter specifies specifies the maximum amount of
* time that can be spent before failing to create the initial
* connection. This feature is unsupported (and ignored) on Windows.
*
* \param userName Username to use for the authentication
* \param hostName Destination host name

View File

@ -36,6 +36,7 @@ MTS_NAMESPACE_BEGIN
* order (= big endian).
*
* \ingroup libcore
* \ingroup libpython
*/
class MTS_EXPORT_CORE SocketStream : public Stream {
public:
@ -43,7 +44,10 @@ public:
//! @{ \name Constructors
// =============================================================
/// Create a stream from an existing socket
/**
* \brief Create a stream from an existing socket
* \remark This function is not exposed in the Python bindings
*/
#if defined(WIN32)
SocketStream(SOCKET socket);
#else

View File

@ -257,7 +257,11 @@ private:
/** \brief Collects various rendering statistics and presents them
* in a human-readable form.
*
* \remark Only the \ref getInstance(), \ref getStats(), and
* \ref printStats() functions are implemented in the Python bindings.
*
* \ingroup libcore
* \ingroup libpython
*/
class MTS_EXPORT_CORE Statistics : public Object {
public:

View File

@ -26,30 +26,54 @@
MTS_NAMESPACE_BEGIN
/**
* Render job - coordinates the process of rendering a single
* image. Implemented as a thread so that multiple jobs can
* \brief Coordinates the process of rendering a single image.
*
* Implemented as a thread so that multiple jobs can
* be executed concurrently.
*
* \ingroup librender
* \ingroup libpython
*/
class MTS_EXPORT_RENDER RenderJob : public Thread {
public:
/**
* Create a new render job for the given scene. When the
* scene, sampler or camera are already registered with the scheduler,
* the last parameters can optionally be specified (that way
* they do not have to be re-sent to network rendering servers).
* \brief Create a new render job for the given scene.
*
* When the Resource ID parameters (\c sceneResID, \c cameraResID, ..) are
* set to \c -1, the implementation will automatically register the
* associated objects (scene, camera, sampler) with the scheduler and
* forward copies to all involved network rendering workers. When some
* of these resources have already been registered with
* the scheduler, their IDs can be provided to avoid this extra
* communication cost.
*
* \param threadName
* Thread name identifier for this render job
* \param scene
* Scene to be rendered
* \param queue
* Pointer to a queue, to which this job should be added
* \param sceneResID
* Resource ID of \c scene (or \c -1)
* \param cameraResID
* Resource ID of \c scene->getCamera() (or \c -1)
* \param samplerResID
* Resource ID of the sample generator (or \c -1)
* \param threadIsCritical
* When set to \c true, the entire program will terminate
* if this thread fails unexpectedly.
* \param testSupervisor
* When this image is being rendered as part of a test suite,
* this parameter points to the associated \ref TestSupervisor
* instance.
*/
RenderJob(const std::string &threadName,
Scene *scene, RenderQueue *queue,
TestSupervisor *testSupervisor,
int sceneResID = -1,
int cameraResID = -1,
int samplerResID = -1,
bool threadIsCritical = true,
bool visualFeedback = false);
/// Should visual feedback be provided (true when rendering using the GUI)
inline bool hasVisualFeedback() const { return m_visualFeedback; }
TestSupervisor *testSupervisor = NULL);
/// Write out the current (partially rendered) image
inline void flush() { m_scene->flush(); }
@ -74,7 +98,6 @@ private:
bool m_ownsSceneResource;
bool m_ownsCameraResource;
bool m_ownsSamplerResource;
bool m_visualFeedback;
bool m_cancelled;
};

View File

@ -53,9 +53,12 @@ protected:
};
/**
* Render queue - used to keep track of a number of scenes
* that are simultaneously being rendered. Also distributes
* events regarding these scenes to registered listeners.
* \brief Render queue - used to keep track of a number of scenes
* that are simultaneously being rendered.
*
* This class is also responsible for distributing events about
* in-progress renderings to registered listeners.
*
* \ingroup librender
*/
class MTS_EXPORT_RENDER RenderQueue : public Object {

View File

@ -39,10 +39,12 @@ MTS_NAMESPACE_BEGIN
/**
* \brief Principal scene data structure
*
* Holds information on surfaces, luminaires and participating media and
* coordinates rendering jobs. This class also provides useful query routines
* that are mostly used by the \ref Integrator implementations.
* This class holds information on surfaces, luminaires and participating media
* and coordinates rendering jobs. It also provides useful query routines that
* are mostly used by the \ref Integrator implementations.
*
* \ingroup librender
* \ingroup libpython
*/
class MTS_EXPORT_RENDER Scene : public NetworkedObject {
public:
@ -65,6 +67,9 @@ public:
//! @{ \name Initialization and rendering
// =============================================================
/// Construct a new, empty scene (with the default properties)
Scene();
/// Construct a new, empty scene
Scene(const Properties &props);
@ -85,7 +90,7 @@ public:
* before rendering the scene. This might do a variety of things,
* such as constructing photon maps or executing distributed overture
* passes. Progress is tracked by sending status messages to a provided
* render queue. The parameter <tt>job</tt> is required to discern
* render queue. The parameter \c job is required to discern
* multiple render jobs occurring in parallel. The last three parameters
* are resource IDs of the associated scene, camera and sample generator,
* which have been made available to all local and remote workers.
@ -97,7 +102,7 @@ public:
/**
* Render the scene as seen by the scene's main camera. Progress is tracked
* by sending status messages to a provided render queue. The parameter
* <tt>job</tt> is required to discern multiple render jobs occurring in
* \c job is required to discern multiple render jobs occurring in
* parallel. The last three parameters are resource IDs of the associated
* scene, camera and sample generator, which have been made available to
* all local and remote workers. Returns true upon successful completion.
@ -428,12 +433,14 @@ public:
inline Float getTestThreshold() const { return m_testThresh; }
/**
* Set the scene's camera. Note that the camera is not included
* when this Scene instance is serialized -- the camera field
* will be <tt>NULL</tt> after unserialization. This is intentional
* so that the camera can be changed without having to re-transmit
* the whole scene. Hence, the camera needs to be submitted separately
* and re-attached on the remote side using <tt>setCamera</tt>.
* \brief Set the scene's camera.
*
* Note that the camera is not included when this Scene instance
* is serialized -- the camera field will be \c NULL after
* unserialization. This is intentional so that the camera can
* be changed without having to re-transmit the whole scene.
* Hence, the camera needs to be submitted separately
* and re-attached on the remote side using \ref setCamera().
**/
inline void setCamera(Camera *camera) { m_camera = camera; }
/// Return the scene's camera
@ -442,12 +449,14 @@ public:
inline const Camera *getCamera() const { return m_camera.get(); }
/**
* Set the scene's integrator. Note that the integrator is not included
* when this Scene instance is serialized -- the integrator field
* will be <tt>NULL</tt> after unserialization. This is intentional
* so that the integrator can be changed without having to re-transmit
* the whole scene. Hence, the integrator needs to be submitted separately
* and re-attached on the remote side using <tt>setIntegrator</tt>.
* \brief Set the scene's integrator.
*
* Note that the integrator is not included when this Scene instance
* is serialized -- the integrator field will be \c NULL after
* unserialization. This is intentional so that the integrator can
* be changed without having to re-transmit the whole scene. Hence,
* the integrator needs to be submitted separately and re-attached
* on the remote side using \ref setIntegrator().
**/
inline void setIntegrator(Integrator *integrator) { m_integrator = integrator; }
/// Return the scene's integrator
@ -456,22 +465,26 @@ public:
inline const Integrator *getIntegrator() const { return m_integrator.get(); }
/**
* Set the scene's sampler. Note that the sampler is not included
* when this Scene instance is serialized -- the sampler field
* will be <tt>NULL</tt> after unserialization. This is intentional
* so that the sampler can be changed without having to re-transmit
* the whole scene. Hence, the sampler needs to be submitted separately
* and re-attached on the remote side using <tt>setSampler</tt>.
* \brief Set the scene's sampler.
*
* Note that the sampler is not included when this Scene instance
* is serialized -- the sampler field will be \c NULL after
* unserialization. This is intentional so that the sampler can
* be changed without having to re-transmit the whole scene.
* Hence, the sampler needs to be submitted separately
* and re-attached on the remote side using \ref setSampler().
**/
inline void setSampler(Sampler *sampler) { m_sampler = sampler; }
/**
* Return the scene's sampler. Note that when rendering using multiple
* different threads, each thread will be passed a shallow copy of the
* scene, which has a different sampler instance. This helps to avoid
* locking/contention issues and ensures that different threads render
* with different random number sequences. The sampler instance provided
* here is a clone of the original sampler specified in the camera.
* \brief Return the scene's sampler.
*
* Note that when rendering using multiple different threads, each
* thread will be passed a shallow copy of the scene, which has a
* different sampler instance. This helps to avoid locking/contention
* issues and ensures that different threads render with different
* random number sequences. The sampler instance provided here is a
* clone of the original sampler specified in the camera.
*/
inline Sampler *getSampler() { return m_sampler; }
/// Return the scene's sampler

View File

@ -50,8 +50,12 @@ private:
/**
* \brief XML parser for Mitsuba scene files. To be used with the
* SAX interface Xerces-C++.
* SAX interface of Xerces-C++.
*
* \remark In the Python bindings, only the static function
* \ref loadScene() is exposed.
* \ingroup librender
* \ingroup libpython
*/
class MTS_EXPORT_RENDER SceneHandler : public HandlerBase {
public:
@ -62,6 +66,16 @@ public:
NamedObjectMap *objects = NULL, bool isIncludedFile = false);
virtual ~SceneHandler();
/// Convenience method -- load a scene from a given filename
static ref<Scene> loadScene(const fs::path &filename,
const ParameterMap &params= ParameterMap());
/// Initialize Xerces-C++ (needs to be called once at program startup)
static void staticInitialization();
/// Free the memory taken up by staticInitialization()
static void staticShutdown();
// -----------------------------------------------------------------------
// Implementation of the SAX DocumentHandler interface
// -----------------------------------------------------------------------

View File

@ -204,7 +204,7 @@ SerializableObject *Scheduler::getResource(int id, int coreIndex) {
m_mutex->unlock();
Log(EError, "getResource(): tried to look up manifold resource %i without specifying a core index!", id);
}
result = rec->resources[coreIndex];
result = rec->resources.at(coreIndex);
} else {
result = rec->resources[0];
}

View File

@ -13,4 +13,4 @@ if pythonEnv.has_key('PYTHONLIB'):
pythonEnv.Prepend(LIBS=pythonEnv['PYTHONLIB'])
if hasPython:
libpython = pythonEnv.SharedLibrary('mitsuba', ['core.cpp']);
libpython = pythonEnv.SharedLibrary('mitsuba', ['core.cpp', 'render.cpp']);

View File

@ -16,8 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(__MTSPY_H)
#define __MTSPY_H
#if !defined(__PYTHON_BASE_H)
#define __PYTHON_BASE_H
#include <mitsuba/mitsuba.h>
@ -151,5 +151,8 @@ typedef std::vector<std::string> StringVector;
typedef std::vector<mitsuba::SerializableObject *> SerializableObjectVector;
typedef std::map<std::string, std::string> StringMap;
#endif /* __MTSPY_H */
extern void export_core();
extern void export_render();
#endif /* __PYTHON_BASE_H */

View File

@ -1,4 +1,4 @@
#include "core.h"
#include "base.h"
#include <mitsuba/core/plugin.h>
#include <mitsuba/core/shvector.h>
#include <mitsuba/core/fstream.h>
@ -13,6 +13,10 @@
#include <mitsuba/core/aabb.h>
#include <mitsuba/core/frame.h>
#include <mitsuba/core/sched_remote.h>
#include <mitsuba/core/netobject.h>
#include <mitsuba/core/sstream.h>
#include <mitsuba/core/sshstream.h>
#include <mitsuba/render/scenehandler.h>
using namespace mitsuba;
@ -26,10 +30,12 @@ void initializeFramework() {
Spectrum::staticInitialization();
Scheduler::staticInitialization();
SHVector::staticInitialization();
SceneHandler::staticInitialization();
}
void shutdownFramework() {
/* Shutdown the core framework */
SceneHandler::staticShutdown();
SHVector::staticShutdown();
Scheduler::staticShutdown();
Spectrum::staticShutdown();
@ -282,6 +288,40 @@ Point transform_mul_point(Transform *transform, const Point &point) { return tra
Ray transform_mul_ray(Transform *transform, const Ray &ray) { return transform->operator()(ray); }
Transform transform_mul_transform(Transform *transform, const Transform &other) { return *transform * other; }
ConfigurableObject *pluginmgr_create(PluginManager *manager, bp::dict dict) {
Properties properties;
bp::list list = dict.items();
std::map<std::string, ConfigurableObject *> children;
for (int i=0; i<bp::len(list); ++i) {
bp::tuple tuple = bp::extract<bp::tuple>(list[i]);
std::string name = bp::extract<std::string>(tuple[0]);
bp::extract<bp::dict> extractDict(tuple[1]);
bp::extract<std::string> extractString(tuple[1]);
bp::extract<ConfigurableObject *> extractConfigurableObject(tuple[1]);
if (name == "type") {
if (!extractString.check())
SLog(EError, "'type' property must map to a string!");
else
properties.setPluginName(extractString());
} else if (extractDict.check()) {
children[name] = pluginmgr_create(manager, extractDict());
} else if (extractConfigurableObject.check()) {
children[name] = extractConfigurableObject();
} else {
properties_wrapper::set(properties, name, tuple[1]);
}
}
ConfigurableObject *object = manager->createObject(properties);
for (std::map<std::string, ConfigurableObject *>::iterator it = children.begin();
it != children.end(); ++it)
object->addChild(it->first, it->second);
object->configure();
return object;
}
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fromLinearRGB_overloads, fromLinearRGB, 3, 4)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fromXYZ_overloads, fromXYZ, 3, 4)
@ -296,6 +336,12 @@ void export_core() {
BP_SETSCOPE(coreModule);
/* Basic STL containers */
bp::class_<StringVector>("StringVector")
.def(bp::vector_indexing_suite<StringVector>());
bp::class_<StringMap>("StringMap")
.def(bp::map_indexing_suite<StringMap>());
bp::enum_<ELogLevel>("ELogLevel")
.value("ETrace", ETrace)
.value("EDebug", EDebug)
@ -381,6 +427,19 @@ void export_core() {
.def("close", &FileStream::close)
.def("remove", &FileStream::remove);
BP_CLASS(SocketStream, Stream, (bp::init<std::string, int>()))
.def("getPeer", &SocketStream::getPeer, BP_RETURN_CONSTREF)
.def("getReceivedBytes", &SocketStream::getReceivedBytes)
.def("getSentBytes", &SocketStream::getSentBytes);
BP_CLASS(SSHStream, Stream, (bp::init<std::string, std::string, const StringVector &>()))
.def(bp::init<std::string, std::string, const StringVector &, int>())
.def(bp::init<std::string, std::string, const StringVector &, int, int>())
.def("getUserName", &SSHStream::getUserName, BP_RETURN_CONSTREF)
.def("getHostName", &SSHStream::getHostName, BP_RETURN_CONSTREF)
.def("getReceivedBytes", &SSHStream::getReceivedBytes)
.def("getSentBytes", &SSHStream::getSentBytes);
BP_SETSCOPE(FileStream_class);
bp::enum_<FileStream::EFileMode>("EFileMode")
.value("EReadOnly", FileStream::EReadOnly)
@ -406,6 +465,9 @@ void export_core() {
.def("addChild", cobject_add_child_2)
.def("configure", &ConfigurableObject::configure);
BP_CLASS(NetworkedObject, ConfigurableObject, bp::no_init)
.def("bindUsedResources", &NetworkedObject::bindUsedResources);
Thread *(Thread::*thread_get_parent)() = &Thread::getParent;
BP_CLASS(Thread, Object, bp::no_init)
.def("getID", &Thread::getID)
@ -538,11 +600,18 @@ void export_core() {
BP_CLASS(PluginManager, Object, bp::no_init)
.def("ensurePluginLoaded", &PluginManager::ensurePluginLoaded)
.def("getLoadedPlugins", &PluginManager::getLoadedPlugins)
.def("create", pluginmgr_create, BP_RETURN_VALUE)
.def("createObject", pluginmgr_createobject_1, BP_RETURN_VALUE)
.def("createObject", pluginmgr_createobject_2, BP_RETURN_VALUE)
.def("getInstance", &PluginManager::getInstance, BP_RETURN_VALUE)
.staticmethod("getInstance");
BP_CLASS(Statistics, Object, bp::no_init)
.def("getStats", &Statistics::getStats, BP_RETURN_VALUE)
.def("printStats", &Statistics::printStats)
.def("getInstance", &Statistics::getInstance, BP_RETURN_VALUE)
.staticmethod("getInstance");
BP_CLASS(WorkUnit, Object, bp::no_init)
.def("set", &WorkUnit::set)
.def("load", &WorkUnit::load)
@ -999,16 +1068,11 @@ BOOST_PYTHON_MODULE(mitsuba) {
bp::object package = bp::scope();
package.attr("__path__") = "mitsuba";
/* Basic STL containers */
bp::class_<StringVector>("StringVector")
.def(bp::vector_indexing_suite<StringVector>());
bp::class_<StringMap>("StringMap")
.def(bp::map_indexing_suite<StringMap>());
/* Automatically take care of the framework
initialization / shutdown */
initializeFramework();
atexit(shutdownFramework);
export_core();
export_render();
}

View File

@ -22,10 +22,10 @@
MTS_NAMESPACE_BEGIN
RenderJob::RenderJob(const std::string &threadName,
Scene *scene, RenderQueue *queue, TestSupervisor *testSupervisor,
int sceneResID, int cameraResID, int samplerResID, bool threadIsCritical,
bool visualFeedback) : Thread(threadName), m_scene(scene), m_queue(queue),
m_testSupervisor(testSupervisor), m_visualFeedback(visualFeedback) {
Scene *scene, RenderQueue *queue, int sceneResID, int cameraResID,
int samplerResID, bool threadIsCritical, TestSupervisor *supervisor)
: Thread(threadName), m_scene(scene), m_queue(queue),
m_testSupervisor(supervisor) {
/* Optional: bring the process down when this thread crashes */
setCritical(threadIsCritical);

View File

@ -20,10 +20,20 @@
#include <mitsuba/render/renderjob.h>
#include <mitsuba/core/plugin.h>
#define DEFAULT_BLOCKSIZE 32
MTS_NAMESPACE_BEGIN
Scene::Scene()
: NetworkedObject(Properties()), m_blockSize(DEFAULT_BLOCKSIZE) {
m_kdtree = new ShapeKDTree();
m_testType = ENone;
m_testThresh = 0.0f;
m_importanceSampleLuminaires = true;
}
Scene::Scene(const Properties &props)
: NetworkedObject(props), m_blockSize(32) {
: NetworkedObject(props), m_blockSize(DEFAULT_BLOCKSIZE) {
m_kdtree = new ShapeKDTree();
/* When test case mode is active (Mitsuba is started with the -t parameter),
this specifies the type of test performed. Mitsuba will expect a reference
@ -44,7 +54,7 @@ Scene::Scene(const Properties &props)
Log(EError, "Unknown test mode \"%s\" specified (must be \"t-test\" or \"relerr\")",
testType.c_str());
/* Error threshold for use with <tt>testType</tt> */
m_testThresh = props.getFloat("testThresh", 0.01);
m_testThresh = props.getFloat("testThresh", 0.01f);
/* By default, luminaire sampling chooses a luminaire with a probability
dependent on the emitted power. Setting this parameter to false switches
to uniform sampling. */

View File

@ -556,4 +556,48 @@ void SceneHandler::fatalError(const SAXParseException& e) {
transcode(e.getMessage()).c_str());
}
// -----------------------------------------------------------------------
ref<Scene> SceneHandler::loadScene(const fs::path &filename, const ParameterMap &params) {
/* Prepare for parsing scene descriptions */
FileResolver *resolver = Thread::getThread()->getFileResolver();
SAXParser* parser = new SAXParser();
fs::path schemaPath = resolver->resolveAbsolute("data/schema/scene.xsd");
SLog(EDebug, "Loading scene \"%s\" ..", filename.file_string().c_str());
/* Check against the 'scene.xsd' XML Schema */
parser->setDoSchema(true);
parser->setValidationSchemaFullChecking(true);
parser->setValidationScheme(SAXParser::Val_Always);
parser->setExternalNoNamespaceSchemaLocation(schemaPath.file_string().c_str());
parser->setCalculateSrcOfs(true);
SceneHandler *handler = new SceneHandler(parser, params);
parser->setDoNamespaces(true);
parser->setDocumentHandler(handler);
parser->setErrorHandler(handler);
parser->parse(filename.file_string().c_str());
ref<Scene> scene = handler->getScene();
delete parser;
delete handler;
return scene;
}
void SceneHandler::staticInitialization() {
/* Initialize Xerces-C */
try {
XMLPlatformUtils::Initialize();
} catch(const XMLException &toCatch) {
SLog(EError, "Error during Xerces initialization: %s",
XMLString::transcode(toCatch.getMessage()));
}
}
void SceneHandler::staticShutdown() {
XMLPlatformUtils::Terminate();
}
MTS_NAMESPACE_END

View File

@ -26,31 +26,7 @@ MTS_NAMESPACE_BEGIN
ref<Scene> Utility::loadScene(const std::string &filename,
const ParameterMap &params) {
/* Prepare for parsing scene descriptions */
FileResolver *resolver = Thread::getThread()->getFileResolver();
SAXParser* parser = new SAXParser();
fs::path schemaPath = resolver->resolveAbsolute("data/schema/scene.xsd");
Log(EDebug, "Loading scene \"%s\" ..", filename.c_str());
/* Check against the 'scene.xsd' XML Schema */
parser->setDoSchema(true);
parser->setValidationSchemaFullChecking(true);
parser->setValidationScheme(SAXParser::Val_Always);
parser->setExternalNoNamespaceSchemaLocation(schemaPath.file_string().c_str());
parser->setCalculateSrcOfs(true);
SceneHandler *handler = new SceneHandler(parser, params);
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;
return SceneHandler::loadScene(filename, params);
}
MTS_IMPLEMENT_CLASS(Utility, true, Object)

View File

@ -27,6 +27,12 @@ class PointLuminaire : public Luminaire {
public:
PointLuminaire(const Properties &props) : Luminaire(props) {
m_intensity = props.getSpectrum("intensity", Spectrum(1));
if (props.hasProperty("position")) {
if (props.hasProperty("toWorld"))
Log(EError, "Please specify either 'toWorld' or 'position'");
m_luminaireToWorld = Transform::translate(Vector(props.getPoint("position")));
m_worldToLuminaire = m_luminaireToWorld.inverse();
}
m_position = m_luminaireToWorld(Point(0,0,0));
m_type = EDeltaPosition | EDiffuseDirection;
}

View File

@ -362,8 +362,7 @@ int mts_main(int argc, char **argv) {
continue;
ref<RenderJob> thr = new RenderJob(formatString("ren%i", jobIdx++),
scene, renderQueue, testSupervisor, -1, -1, -1, true,
flushTimer > 0);
scene, renderQueue, -1, -1, -1, true, testSupervisor);
thr->start();
renderQueue->waitLeft(numParallelScenes-1);
@ -403,6 +402,7 @@ int main(int argc, char **argv) {
Spectrum::staticInitialization();
Scheduler::staticInitialization();
SHVector::staticInitialization();
SceneHandler::staticInitialization();
#ifdef WIN32
/* Initialize WINSOCK2 */
@ -418,20 +418,10 @@ int main(int argc, char **argv) {
setlocale(LC_NUMERIC, "C");
#endif
/* Initialize Xerces-C */
try {
XMLPlatformUtils::Initialize();
} catch(const XMLException &toCatch) {
SLog(EError, "Error during Xerces initialization: %s",
XMLString::transcode(toCatch.getMessage()));
return -1;
}
int retval = mts_main(argc, argv);
XMLPlatformUtils::Terminate();
/* Shutdown the core framework */
SceneHandler::staticShutdown();
SHVector::staticShutdown();
Scheduler::staticShutdown();
Spectrum::staticShutdown();

View File

@ -373,6 +373,7 @@ int mts_main(int argc, char **argv) {
Spectrum::staticInitialization();
Scheduler::staticInitialization();
SHVector::staticInitialization();
SceneHandler::staticInitialization();
#ifdef WIN32
/* Initialize WINSOCK2 */
@ -388,20 +389,10 @@ int mts_main(int argc, char **argv) {
setlocale(LC_NUMERIC, "C");
#endif
/* Initialize Xerces-C */
try {
XMLPlatformUtils::Initialize();
} catch(const XMLException &toCatch) {
SLog(EError, "Error during Xerces initialization: %s",
XMLString::transcode(toCatch.getMessage()));
return -1;
}
int retval = mtsutil(argc, argv);
XMLPlatformUtils::Terminate();
/* Shutdown the core framework */
SceneHandler::staticShutdown();
SHVector::staticShutdown();
Scheduler::staticShutdown();
Spectrum::staticShutdown();

View File

@ -16,7 +16,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <xercesc/parsers/SAXParser.hpp>
#include <QtGui/QtGui>
#include <mitsuba/core/shvector.h>
#include <mitsuba/core/sched.h>
@ -24,6 +23,7 @@
#include <mitsuba/core/fresolver.h>
#include <mitsuba/core/appender.h>
#include <mitsuba/core/statistics.h>
#include <mitsuba/render/scenehandler.h>
#if defined(__OSX__)
#include <ApplicationServices/ApplicationServices.h>
#endif
@ -88,15 +88,6 @@ void collect_zombies(int s) {
int main(int argc, char *argv[]) {
int retval;
/* Initialize Xerces-C */
try {
XMLPlatformUtils::Initialize();
} catch(const XMLException &toCatch) {
fprintf(stderr, "Error during Xerces initialization: %s",
XMLString::transcode(toCatch.getMessage()));
return -1;
}
/* Initialize the core framework */
Class::staticInitialization();
PluginManager::staticInitialization();
@ -107,6 +98,7 @@ int main(int argc, char *argv[]) {
Spectrum::staticInitialization();
Scheduler::staticInitialization();
SHVector::staticInitialization();
SceneHandler::staticInitialization();
#if defined(__LINUX__)
XInitThreads();
@ -191,7 +183,6 @@ int main(int argc, char *argv[]) {
}
Statistics::getInstance()->printStats();
XMLPlatformUtils::Terminate();
#ifdef WIN32
/* Shut down WINSOCK2 */
@ -199,6 +190,7 @@ int main(int argc, char *argv[]) {
#endif
/* Shutdown the core framework */
SceneHandler::staticShutdown();
SHVector::staticShutdown();
Scheduler::staticShutdown();
Spectrum::staticShutdown();

View File

@ -1242,8 +1242,8 @@ void MainWindow::on_actionRender_triggered() {
Scene *scene = context->scene;
scene->setBlockSize(m_blockSize);
context->renderJob = new RenderJob("rend", scene, m_renderQueue, NULL,
context->sceneResID, -1, -1, false, true);
context->renderJob = new RenderJob("rend", scene, m_renderQueue,
context->sceneResID, -1, -1, false);
context->cancelMode = ERender;
if (context->mode != ERender)
ui->glView->downloadFramebuffer();