support for constructing triangle meshes from within python

metadata
Wenzel Jakob 2013-11-18 16:46:42 +01:00
parent b68a38ed9a
commit 0ee249c434
6 changed files with 198 additions and 17 deletions

View File

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

View File

@ -501,6 +501,14 @@ public:
return value;
}
/// Component-wise logarithm
inline TSpectrum log() const {
TSpectrum value;
for (int i=0; i<N; i++)
value.s[i] = math::fastlog(s[i]);
return value;
}
/// Component-wise exponentation
inline TSpectrum exp() const {
TSpectrum value;
@ -591,6 +599,7 @@ protected:
/** \brief RGB color data type
*
* \ingroup libcore
* \ingroup libpython
*/
struct MTS_EXPORT_CORE Color3 : public TSpectrum<Float, 3> {
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;
}
};

View File

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

View File

@ -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 <typename Value> 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<mitsuba::Object> 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;

View File

@ -56,25 +56,25 @@ void shutdownFramework() {
Class::staticShutdown();
}
class spectrum_wrapper {
template <typename SpectrumType> 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<Float>())
.def(bp::init<Float, Float, Float>())
.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<Color3>::len)
.def("__getitem__", &SpectrumWrapper<Color3>::get)
.def("__setitem__", &SpectrumWrapper<Color3>::set);
BP_STRUCT(Spectrum, bp::init<>())
.def("__init__", bp::make_constructor(spectrum_array_constructor))
.def(bp::init<Float>())
@ -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<Spectrum>::len)
.def("__getitem__", &SpectrumWrapper<Spectrum>::get)
.def("__setitem__", &SpectrumWrapper<Spectrum>::set);
BP_SETSCOPE(Spectrum_struct);
bp::enum_<Spectrum::EConversionIntent>("EConversionIntent")

View File

@ -125,6 +125,39 @@ bp::list scene_getMedia(Scene *scene) {
return list;
}
typedef InternalArray<uint32_t> InternalUInt32Array;
typedef InternalArray<Point3> InternalPoint3Array;
typedef InternalArray<Normal> InternalNormalArray;
typedef InternalArray<Point2> InternalPoint2Array;
typedef InternalArray<Color3> InternalColor3Array;
typedef InternalArray<TangentSpace> 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>("ETransportMode")
.value("ERadiance", ERadiance)
.value("EImportance", EImportance)
@ -230,9 +270,7 @@ void export_render() {
.staticmethod("loadScene");
BP_CLASS(RenderJob, Thread, (bp::init<const std::string &, Scene *, RenderQueue *>()))
.def(bp::init<const std::string &, Scene *, RenderQueue *, int>())
.def(bp::init<const std::string &, Scene *, RenderQueue *, int, int>())
.def(bp::init<const std::string &, Scene *, RenderQueue *, int, int, int>())
.def(bp::init<const std::string &, Scene *, RenderQueue *, int, bp::optional<int, int> >())
.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<Vector, Vector>())
.def(bp::init<Stream *>())
.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<Float>())
.def(bp::init<Intersection, EMeasure>())
@ -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<std::string, size_t, size_t, bool, bool, bool, bool, bool>()))
BP_CLASS(TriMesh, Shape, (bp::init<std::string, size_t, size_t, bp::optional<bool, bool, bool, bool, bool> >()))
.def(bp::init<Stream *, InstanceManager *>())
.def(bp::init<Stream *, int>())
.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)