From 0ee249c434b21e99838ec3881c857f0e282fd683 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Mon, 18 Nov 2013 16:46:42 +0100 Subject: [PATCH] support for constructing triangle meshes from within python --- doc/python.tex | 37 +++++++++++++++++++ include/mitsuba/core/spectrum.h | 14 +++++++ include/mitsuba/render/trimesh.h | 9 +++++ src/libpython/base.h | 35 +++++++++++++++++- src/libpython/core.cpp | 57 ++++++++++++++++++++++++----- src/libpython/render.cpp | 63 +++++++++++++++++++++++++++++--- 6 files changed, 198 insertions(+), 17 deletions(-) diff --git a/doc/python.tex b/doc/python.tex index 6627e035..6a8c8fd1 100644 --- a/doc/python.tex +++ b/doc/python.tex @@ -395,3 +395,40 @@ A useful property of this approach is that scene loading and initialization must only take place once. Performance-wise, this compares favourably with running many separate rendering jobs, e.g. using the \code{mitsuba} command-line executable. + +\subsection{Creating triangle-based shapes} +It is possible to create new triangle-based shapes directly in Python, though +doing so is discouraged: because Python is an interpreted programming language, +the construction of large meshes will run very slowly. The builtin shapes +and shape loaders are to be preferred when this is an option. That said, the +following snippet shows how to create \code{TriMesh} objects from within Python: +\begin{python} +# Create a new mesh with 1 triangle, 3 vertices, +# and allocate buffers for normals and texture coordinates +mesh = TriMesh('Name of this mesh', 1, 3, True, True) + +v = mesh.getVertexPositions() +v[0] = Point3(0, 0, 0) +v[1] = Point3(1, 0, 0) +v[2] = Point3(0, 1, 0) + +n = mesh.getVertexNormals() +n[0] = Normal(0, 0, 1) +n[1] = Normal(0, 0, 1) +n[2] = Normal(0, 0, 1) + +t = mesh.getTriangles() # Indexed triangle list: tri 1 references vertices 0,1,2 +t[0] = 0 +t[1] = 1 +t[2] = 2 + +uv = mesh.getTexcoords() +uv[0] = Point2(0, 0) +uv[1] = Point2(1, 0) +uv[2] = Point2(0, 1) + +mesh.configure() + +# Add to a scene (assumes 'scene' is available) +sensor.addChild(mesh) +\end{python} diff --git a/include/mitsuba/core/spectrum.h b/include/mitsuba/core/spectrum.h index bd490a6e..4582fdde 100644 --- a/include/mitsuba/core/spectrum.h +++ b/include/mitsuba/core/spectrum.h @@ -501,6 +501,14 @@ public: return value; } + /// Component-wise logarithm + inline TSpectrum log() const { + TSpectrum value; + for (int i=0; i { public: @@ -616,6 +625,11 @@ public: inline Color3(Float r, Float g, Float b) { s[0] = r; s[1] = g; s[2] = b; } + + /// Return the luminance (assuming the color value is expressed in linear sRGB) + inline Float getLuminance() const { + return s[0] * 0.212671f + s[1] * 0.715160f + s[2] * 0.072169f; + } }; diff --git a/include/mitsuba/render/trimesh.h b/include/mitsuba/render/trimesh.h index 4f6a8606..d6b48917 100644 --- a/include/mitsuba/render/trimesh.h +++ b/include/mitsuba/render/trimesh.h @@ -33,6 +33,7 @@ MTS_NAMESPACE_BEGIN * necessarily orthogonal. * * \ingroup librender + * \ingroup libpython */ struct TangentSpace { /// Position partial with respect to the U parameter of the local chart @@ -42,6 +43,8 @@ struct TangentSpace { Vector dpdv; inline TangentSpace() { } + inline TangentSpace(const Vector &dpdu, const Vector &dpdv) + : dpdu(dpdu), dpdv(dpdv) { } inline TangentSpace(Stream *stream) : dpdu(stream), dpdv(stream) { } @@ -50,6 +53,12 @@ struct TangentSpace { dpdu.serialize(stream); dpdv.serialize(stream); } + + inline std::string toString() const { + std::ostringstream oss; + oss << "TangentSpace[dpdu=" << dpdu.toString() << ", dpdv=" << dpdv.toString() << "]"; + return oss.str(); + } }; /** \brief Abstract triangle mesh base class diff --git a/src/libpython/base.h b/src/libpython/base.h index 91f4de9a..68ebe847 100644 --- a/src/libpython/base.h +++ b/src/libpython/base.h @@ -184,7 +184,7 @@ public: using namespace mitsuba; if (i < 0 || i >= Size) { - SLog(EError, "Index %i is out of range!", i); + SLog(mitsuba::EError, "Index %i is out of range!", i); return (Scalar) 0; } return value[i]; @@ -194,7 +194,7 @@ public: using namespace mitsuba; if (i < 0 || i >= Size) - SLog(EError, "Index %i is out of range!", i); + SLog(mitsuba::EError, "Index %i is out of range!", i); else value[i] = arg; } @@ -204,6 +204,37 @@ public: } }; +template struct InternalArray { +public: + InternalArray(mitsuba::Object *obj, Value *ptr, size_t length) : obj(obj), ptr(ptr), length(length) { } + InternalArray(const InternalArray &a) : obj(a.obj), ptr(a.ptr), length(a.length) { } + + inline int len() { return (int) this->length; } + + Value get(int i) { + if (i < 0 || (size_t) i >= length) + SLog(mitsuba::EError, "Index %i is out of range!", i); + return ptr[i]; + } + + void set(int i, Value value) { + if (i < 0 || (size_t) i >= length) + SLog(mitsuba::EError, "Index %i is out of range!", i); + ptr[i] = value; + } +private: + mitsuba::ref obj; + Value *ptr; + size_t length; +}; + +#define BP_INTERNAL_ARRAY(Name) \ + BP_STRUCT(Name, bp::no_init) \ + .def("__len__", &Name::len) \ + .def("__getitem__", &Name::get) \ + .def("__setitem__", &Name::set) + + namespace mitsuba { class SerializableObject; class ConfigurableObject; diff --git a/src/libpython/core.cpp b/src/libpython/core.cpp index e56752be..cc0280ed 100644 --- a/src/libpython/core.cpp +++ b/src/libpython/core.cpp @@ -56,25 +56,25 @@ void shutdownFramework() { Class::staticShutdown(); } -class spectrum_wrapper { +template class SpectrumWrapper { public: - static Float get(const Spectrum &spec, int i) { - if (i < 0 || i >= SPECTRUM_SAMPLES) { + static Float get(const SpectrumType &spec, int i) { + if (i < 0 || i >= SpectrumType::dim) { SLog(EError, "Index %i is out of range!", i); return 0.0f; } return spec[i]; } - static void set(Spectrum &spec, int i, Float value) { - if (i < 0 || i >= SPECTRUM_SAMPLES) + static void set(SpectrumType &spec, int i, Float value) { + if (i < 0 || i >= SpectrumType::dim) SLog(EError, "Index %i is out of range!", i); else spec[i] = value; } - static int len(Spectrum &) { - return SPECTRUM_SAMPLES; + static int len(SpectrumType &) { + return SpectrumType::dim; } }; @@ -1092,6 +1092,41 @@ void export_core() { .def("getSpatialBounds", &AnimatedTransform::getSpatialBounds, BP_RETURN_VALUE) .def("eval", &AnimatedTransform::eval, BP_RETURN_VALUE); + BP_STRUCT(Color3, bp::init<>()) + .def(bp::init()) + .def(bp::init()) + .def(bp::self != bp::self) + .def(bp::self == bp::self) + .def(-bp::self) + .def(bp::self + bp::self) + .def(bp::self += bp::self) + .def(bp::self - bp::self) + .def(bp::self -= bp::self) + .def(bp::self *= Float()) + .def(bp::self * Float()) + .def(bp::self *= bp::self) + .def(bp::self * bp::self) + .def(bp::self / Float()) + .def(bp::self /= Float()) + .def(bp::self /= bp::self) + .def(bp::self / bp::self) + .def("isValid", &Color3::isValid) + .def("isNaN", &Color3::isNaN) + .def("average", &Color3::average) + .def("sqrt", &Color3::sqrt) + .def("exp", &Color3::exp) + .def("log", &Color3::log) + .def("pow", &Color3::pow) + .def("clampNegative", &Color3::clampNegative) + .def("min", &Color3::min) + .def("max", &Color3::max) + .def("isZero", &Color3::isZero) + .def("getLuminance", &Color3::getLuminance) + .def("__repr__", &Color3::toString) + .def("__len__", &SpectrumWrapper::len) + .def("__getitem__", &SpectrumWrapper::get) + .def("__setitem__", &SpectrumWrapper::set); + BP_STRUCT(Spectrum, bp::init<>()) .def("__init__", bp::make_constructor(spectrum_array_constructor)) .def(bp::init()) @@ -1115,7 +1150,9 @@ void export_core() { .def("isNaN", &Spectrum::isNaN) .def("average", &Spectrum::average) .def("sqrt", &Spectrum::sqrt) + .def("safe_sqrt", &Spectrum::safe_sqrt) .def("exp", &Spectrum::exp) + .def("log", &Spectrum::log) .def("pow", &Spectrum::pow) .def("clampNegative", &Spectrum::clampNegative) .def("min", &Spectrum::min) @@ -1134,9 +1171,9 @@ void export_core() { .def("fromContinuousSpectrum", &Spectrum::fromContinuousSpectrum) .def("serialize", &Spectrum::serialize) .def("__repr__", &Spectrum::toString) - .def("__len__", &spectrum_wrapper::len) - .def("__getitem__", &spectrum_wrapper::get) - .def("__setitem__", &spectrum_wrapper::set); + .def("__len__", &SpectrumWrapper::len) + .def("__getitem__", &SpectrumWrapper::get) + .def("__setitem__", &SpectrumWrapper::set); BP_SETSCOPE(Spectrum_struct); bp::enum_("EConversionIntent") diff --git a/src/libpython/render.cpp b/src/libpython/render.cpp index e011a795..78308eac 100644 --- a/src/libpython/render.cpp +++ b/src/libpython/render.cpp @@ -125,6 +125,39 @@ bp::list scene_getMedia(Scene *scene) { return list; } + +typedef InternalArray InternalUInt32Array; +typedef InternalArray InternalPoint3Array; +typedef InternalArray InternalNormalArray; +typedef InternalArray InternalPoint2Array; +typedef InternalArray InternalColor3Array; +typedef InternalArray InternalTangentSpaceArray; + +InternalUInt32Array trimesh_getTriangles(TriMesh *triMesh) { + BOOST_STATIC_ASSERT(sizeof(Triangle) == 3*sizeof(uint32_t)); + return InternalUInt32Array(triMesh, (uint32_t *) triMesh->getTriangles(), triMesh->getTriangleCount()*3); +} + +InternalPoint3Array trimesh_getVertexPositions(TriMesh *triMesh) { + return InternalPoint3Array(triMesh, triMesh->getVertexPositions(), triMesh->getVertexCount()); +} + +InternalNormalArray trimesh_getVertexNormals(TriMesh *triMesh) { + return InternalNormalArray(triMesh, triMesh->getVertexNormals(), triMesh->getVertexCount()); +} + +InternalPoint2Array trimesh_getVertexTexcoords(TriMesh *triMesh) { + return InternalPoint2Array(triMesh, triMesh->getVertexTexcoords(), triMesh->getVertexCount()); +} + +InternalColor3Array trimesh_getVertexColors(TriMesh *triMesh) { + return InternalColor3Array(triMesh, triMesh->getVertexColors(), triMesh->getVertexCount()); +} + +InternalTangentSpaceArray trimesh_getUVTangents(TriMesh *triMesh) { + return InternalTangentSpaceArray(triMesh, triMesh->getUVTangents(), triMesh->getVertexCount()); +} + void export_render() { bp::object renderModule( bp::handle<>(bp::borrowed(PyImport_AddModule("mitsuba.render")))); @@ -133,6 +166,13 @@ void export_render() { BP_SETSCOPE(renderModule); + BP_INTERNAL_ARRAY(InternalUInt32Array); + BP_INTERNAL_ARRAY(InternalPoint3Array); + BP_INTERNAL_ARRAY(InternalPoint2Array); + BP_INTERNAL_ARRAY(InternalColor3Array); + BP_INTERNAL_ARRAY(InternalNormalArray); + BP_INTERNAL_ARRAY(InternalTangentSpaceArray); + bp::enum_("ETransportMode") .value("ERadiance", ERadiance) .value("EImportance", EImportance) @@ -230,9 +270,7 @@ void export_render() { .staticmethod("loadScene"); BP_CLASS(RenderJob, Thread, (bp::init())) - .def(bp::init()) - .def(bp::init()) - .def(bp::init()) + .def(bp::init >()) .def("flush", &RenderJob::flush) .def("cancel", &RenderJob::cancel) .def("wait", &RenderJob::wait); @@ -275,6 +313,14 @@ void export_render() { .def("LoSub", &Intersection::LoSub) .def("__repr__", &Intersection::toString); + BP_STRUCT(TangentSpace, bp::init<>()) + .def(bp::init()) + .def(bp::init()) + .def_readwrite("dpdu", &TangentSpace::dpdu) + .def_readwrite("dpdv", &TangentSpace::dpdv) + .def("serialize", &TangentSpace::serialize) + .def("__repr__", &TangentSpace::toString); + BP_STRUCT(PositionSamplingRecord, bp::init<>()) .def(bp::init()) .def(bp::init()) @@ -340,20 +386,27 @@ void export_render() { .def("hasBSDF", &Shape::hasBSDF) .def("getBSDF", shape_getBSDF, BP_RETURN_VALUE) .def("getPrimitiveCount", &Shape::getPrimitiveCount) - .def("getEffectivePrimitiveCount", &Shape::getEffectivePrimitiveCount); + .def("getEffectivePrimitiveCount", &Shape::getEffectivePrimitiveCount) + .def("copyAttachments", &Shape::copyAttachments); void (TriMesh::*triMesh_serialize1)(Stream *stream) const = &TriMesh::serialize; void (TriMesh::*triMesh_serialize2)(Stream *stream, InstanceManager *) const = &TriMesh::serialize; - BP_CLASS(TriMesh, Shape, (bp::init())) + BP_CLASS(TriMesh, Shape, (bp::init >())) .def(bp::init()) .def(bp::init()) .def("getTriangleCount", &TriMesh::getTriangleCount) + .def("getTriangles", trimesh_getTriangles) .def("getVertexCount", &TriMesh::getVertexCount) + .def("getVertexPositions", trimesh_getVertexPositions, BP_RETURN_VALUE) .def("hasVertexNormals", &TriMesh::hasVertexNormals) + .def("getVertexNormals", trimesh_getVertexNormals, BP_RETURN_VALUE) .def("hasVertexColors", &TriMesh::hasVertexColors) + .def("getVertexColors", trimesh_getVertexColors, BP_RETURN_VALUE) .def("hasVertexTexcoords", &TriMesh::hasVertexTexcoords) + .def("getVertexTexcoords", trimesh_getVertexTexcoords, BP_RETURN_VALUE) .def("hasUVTangents", &TriMesh::hasUVTangents) + .def("getUVTangents", trimesh_getUVTangents, BP_RETURN_VALUE) .def("computeUVTangents", &TriMesh::computeUVTangents) .def("computeNormals", &TriMesh::computeNormals) .def("rebuildTopology", &TriMesh::rebuildTopology)