Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions include/mitsuba/core/xml.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <mitsuba/mitsuba.h>
#include <mitsuba/core/properties.h>
#include <string>
#include <map>

/// Max level of nested <include> directives
#define MI_XML_INCLUDE_MAX_RECURSION 15
Expand Down Expand Up @@ -94,9 +95,8 @@ extern MI_EXPORT_LIB std::vector<ref<Object>> expand_node(

/// Read a Mitsuba XML file and return a list of pairs containing the
/// name of the plugin and the corresponding populated Properties object
extern MI_EXPORT_LIB std::vector<std::pair<std::string, Properties>> xml_to_properties(
const fs::path &path,
const std::string &variant);
extern MI_EXPORT_LIB std::map<std::string, std::pair<std::string, Properties>>
xml_to_properties(const fs::path &path, const std::string &variant);

NAMESPACE_END(detail)

Expand Down
8 changes: 7 additions & 1 deletion include/mitsuba/render/mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ NAMESPACE_BEGIN(mitsuba)
template <typename Float, typename Spectrum>
class MI_EXPORT_LIB Mesh : public Shape<Float, Spectrum> {
public:
MI_IMPORT_TYPES()
MI_IMPORT_TYPES(BSDF, Texture)
MI_IMPORT_BASE(Shape, m_to_world, mark_dirty, m_emitter, m_sensor, m_bsdf,
m_interior_medium, m_exterior_medium, m_is_instance,
m_discontinuity_types, m_shape_type, m_initialized)
Expand Down Expand Up @@ -428,6 +428,9 @@ class MI_EXPORT_LIB Mesh : public Shape<Float, Spectrum> {
*/
void build_parameterization();

// Apply displacement map to vertex position (and recompute surface normals)
void apply_displacement_map();

// Ensures that the sampling table are ready.
DRJIT_INLINE void ensure_pmf_built() const {
if (unlikely(m_area_pmf.empty()))
Expand Down Expand Up @@ -591,6 +594,9 @@ class MI_EXPORT_LIB Mesh : public Shape<Float, Spectrum> {

/// Pointer to the scene that owns this mesh
Scene<Float, Spectrum>* m_scene = nullptr;

/// Optional: displacement map to be applied during initialization
ref<Texture> m_displacement_map;
};

MI_EXTERN_CLASS(Mesh)
Expand Down
16 changes: 14 additions & 2 deletions src/bsdfs/bumpmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ Bump map BSDF adapter (:monosp:`bumpmap`)
- Bump map gradient multiplier. (Default: 1.0)
- |exposed|

* - strength
- |float|
- Interpolation factor relative to original normal. (Default: 1.0)
- |exposed|

Bump mapping is a simple technique for cheaply adding surface detail to a rendering. This is done
by perturbing the shading coordinate frame based on a displacement height field provided as a
texture. This method can lend objects a highly realistic and detailed appearance (e.g. wrinkled
Expand Down Expand Up @@ -107,6 +112,11 @@ class BumpMap final : public BSDF<Float, Spectrum> {

m_scale = props.get<ScalarFloat>("scale", 1.f);

m_strength = props.get<ScalarFloat>("strength", 1.f);

if (m_strength < 0.f || m_strength > 1.f)
Throw("Strength must be between [0,1]!");

// Add all nested components
m_components.clear();
for (size_t i = 0; i < m_nested_bsdf->component_count(); ++i)
Expand All @@ -118,6 +128,7 @@ class BumpMap final : public BSDF<Float, Spectrum> {
callback->put_object("nested_bsdf", m_nested_bsdf.get(), ParamFlags::Differentiable | ParamFlags::Discontinuous);
callback->put_object("nested_texture", m_nested_texture.get(), ParamFlags::Differentiable | ParamFlags::Discontinuous);
callback->put_parameter("scale", m_scale, +ParamFlags::NonDifferentiable);
callback->put_parameter("strength", m_scale, +ParamFlags::NonDifferentiable);
}

std::pair<BSDFSample3f, Spectrum> sample(const BSDFContext &ctx,
Expand Down Expand Up @@ -205,7 +216,7 @@ class BumpMap final : public BSDF<Float, Spectrum> {

// Bump-mapped shading normal
Frame3f result;
result.n = dr::normalize(dr::cross(dp_du, dp_dv));
result.n = dr::lerp(si.sh_frame.n, dr::normalize(dr::cross(dp_du, dp_dv)), m_strength);

// Flip if not aligned with geometric normal
result.n[dr::dot(si.n, result.n) < .0f] *= -1.f;
Expand All @@ -231,13 +242,14 @@ class BumpMap final : public BSDF<Float, Spectrum> {
<< " nested_bsdf = " << string::indent(m_nested_bsdf) << std::endl
<< " nested_texture = " << string::indent(m_nested_texture) << "," << std::endl
<< " scale = " << string::indent(m_scale) << "," << std::endl
<< " strength = " << string::indent(m_strength) << "," << std::endl
<< "]";
return oss.str();
}

MI_DECLARE_CLASS()
protected:
ScalarFloat m_scale;
ScalarFloat m_scale, m_strength;
ref<Texture> m_nested_texture;
ref<Base> m_nested_bsdf;
};
Expand Down
1 change: 1 addition & 0 deletions src/core/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ set(CORE_PY_SRC
${CMAKE_CURRENT_SOURCE_DIR}/appender.cpp
${CMAKE_CURRENT_SOURCE_DIR}/argparser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/bitmap.cpp
${CMAKE_CURRENT_SOURCE_DIR}/blender.cpp
${CMAKE_CURRENT_SOURCE_DIR}/cast.cpp
${CMAKE_CURRENT_SOURCE_DIR}/filesystem.cpp
${CMAKE_CURRENT_SOURCE_DIR}/formatter.cpp
Expand Down
87 changes: 87 additions & 0 deletions src/core/python/blender.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#include <nanobind/nanobind.h> // Needs to be first, to get `ref<T>` caster
#include <mitsuba/python/python.h>
#include <mitsuba/core/mstream.h>
#include <mitsuba/core/bitmap.h>

#include <drjit/python.h>
#include <nanobind/ndarray.h>
#include <nanobind/stl/string.h>
#include <nanobind/stl/vector.h>
#include <nanobind/stl/pair.h>

/*
* This file contains bindings for routines using in the Mitsuba-Blender add-on.
*/

// Blender struct to be used when object is passed using the `.as_pointer()` method.
namespace blender {
struct ImBuf {
int x, y;
unsigned char planes;
int channels;
int flags;
char padding[24]; // Padding for other structs that are ignored here
float *data;
/// ...
};
struct RenderPass {
struct RenderPass *next, *prev;
int channels;
char name[64];
char chan_id[8];
ImBuf *ibuf; // The only thing we are interested in
int rectx, recty;
// ...
};
struct PackedFile {
int size;
int seek;
const void *data;
void* padding;
};
}

using ContigCpuNdArray = nb::ndarray<nb::device::cpu, nb::c_contig>;

MI_PY_EXPORT(blender) {
/// Routine to accelerates the writing of a numpy array image into a
/// RenderPass iBuf data buffer.
m.def("write_blender_framebuffer", [](ContigCpuNdArray data, const size_t ptr) {
blender::RenderPass *render_pass = reinterpret_cast<blender::RenderPass *>(ptr);

if (data.ndim() != 3)
throw nb::type_error("Invalid num of dimensions. Expected three!");

if (data.dtype() != nb::dtype<float>())
throw nb::type_error("Invalid array type. Expected float32!");

float* src = (float*) data.data();
float* dst = render_pass->ibuf->data;

const size_t width = data.shape(0);
const size_t height = data.shape(1);
const size_t src_channels = data.shape(2);
const size_t dst_channels = render_pass->channels;

for (size_t y = 0; y < height; ++y) {
size_t src_index = y * width * src_channels;
size_t dst_index = y * width * dst_channels;

for (size_t x = 0; x < width; ++x) {
for (size_t c = 0; c < dst_channels; c++)
dst[dst_index + c] = (c < src_channels ? src[src_index + c] : 1.f);

src_index += src_channels;
dst_index += dst_channels;
}
}
});

/// Routine to directly load a Bitmap image from a Blender packed file.
m.def("packed_file_to_bitmap", [](const size_t packed_file_ptr) {
blender::PackedFile *packed_file = reinterpret_cast<blender::PackedFile *>(packed_file_ptr);
ref<MemoryStream> stream = new MemoryStream((void *) packed_file->data, packed_file->size);
ref<Bitmap> bmp = new Bitmap(stream.get());
return bmp;
});
}
30 changes: 15 additions & 15 deletions src/core/python/properties_v.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,20 +156,20 @@ MI_PY_EXPORT(Properties) {
// FIXME: Binding this enumeration leaks. Defining an internal enum to
// an arbitrary class is fine, this seems to be specifically an issue
// with defining an internal enum in Properties
//nb::enum_<Properties::Type>(p, "Type");
//.value("Bool", Properties::Type::Bool, D(Properties, Type, Bool))
//.value("Long", Properties::Type::Long, D(Properties, Type, Long))
//.value("Float", Properties::Type::Float, D(Properties, Type, Float))
//.value("Array3f", Properties::Type::Array3f, D(Properties, Type, Array3f))
//.value("Transform3f", Properties::Type::Transform3f, D(Properties, Type, Transform3f))
//.value("Transform4f", Properties::Type::Transform4f, D(Properties, Type, Transform4f))
//// .value("AnimatedTransform", Properties::Type::AnimatedTransform, D(Properties, Type, AnimatedTransform))
//.value("TensorHandle", Properties::Type::Tensor, D(Properties, Type, Tensor))
//.value("Color", Properties::Type::Color, D(Properties, Type, Color))
//.value("String", Properties::Type::String, D(Properties, Type, String))
//.value("NamedReference", Properties::Type::NamedReference, D(Properties, Type, NamedReference))
//.value("Object", Properties::Type::Object, D(Properties, Type, Object))
//.value("Pointer", Properties::Type::Pointer, D(Properties, Type, Pointer))
//.export_values();
nb::enum_<Properties::Type>(p, "Type")
.value("Bool", Properties::Type::Bool, D(Properties, Type, Bool))
.value("Long", Properties::Type::Long, D(Properties, Type, Long))
.value("Float", Properties::Type::Float, D(Properties, Type, Float))
.value("Array3f", Properties::Type::Array3f, D(Properties, Type, Array3f))
.value("Transform3f", Properties::Type::Transform3f, D(Properties, Type, Transform3f))
.value("Transform4f", Properties::Type::Transform4f, D(Properties, Type, Transform4f))
// .value("AnimatedTransform", Properties::Type::AnimatedTransform, D(Properties, Type, AnimatedTransform))
.value("TensorHandle", Properties::Type::Tensor, D(Properties, Type, Tensor))
.value("Color", Properties::Type::Color, D(Properties, Type, Color))
.value("String", Properties::Type::String, D(Properties, Type, String))
.value("NamedReference", Properties::Type::NamedReference, D(Properties, Type, NamedReference))
.value("Object", Properties::Type::Object, D(Properties, Type, Object))
.value("Pointer", Properties::Type::Pointer, D(Properties, Type, Pointer))
.export_values();
}
}
73 changes: 47 additions & 26 deletions src/core/python/xml_v.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
#include <nanobind/stl/pair.h>
#include <nanobind/stl/vector.h>
#include <nanobind/stl/string.h>
#include <nanobind/stl/map.h>

using Caster = nb::object(*)(mitsuba::Object *);
extern Caster cast_object;

struct DictInstance {
Properties props;
const Class *class_ = nullptr;
ref<Object> object = nullptr;
std::vector<std::pair<std::string, std::string>> dependencies;
};

struct DictParseContext {
Expand Down Expand Up @@ -146,15 +147,38 @@ Parameter ``parallel``:

)doc");

m.def(
"dict_to_props",
[](const nb::dict dict) {
DictParseContext ctx;
ctx.parallel = false;
ctx.env = ThreadEnvironment();

parse_dictionary<Float, Spectrum>(ctx, "__root__", dict);

std::map<std::string, std::pair<std::string, PropertiesV<Float>>> res;
for (auto &[id, instance] : ctx.instances) {
const Class* class_ = instance.class_;
while ((class_->name() == class_->alias()) && class_->parent())
class_ = class_->parent();
res[id] = { class_->name(), PropertiesV<Float>(std::move(instance.props)) };
}

return res;
},
"dict"_a,
R"doc(Get the names and properties of the objects described in a Python dictionary)doc");

m.def(
"xml_to_props",
[](const std::string &path) {
nb::gil_scoped_release release;

auto result = std::vector<std::pair<std::string, PropertiesV<Float>>>();
for (const auto& [k,v] : xml::detail::xml_to_properties(path, GET_VARIANT()))
result.emplace_back(k, PropertiesV<Float>(v));
auto props = xml::detail::xml_to_properties(path, GET_VARIANT());

auto result = std::map<std::string, std::pair<std::string, PropertiesV<Float>>>();
for (const auto& [k, v] : props)
result[k] = { v.first, PropertiesV<Float>(v.second) };
return result;
},
"path"_a,
Expand All @@ -180,13 +204,15 @@ std::string get_type(const nb::dict &dict) {
void expand_and_set_object(Properties &props, const std::string &name, const ref<Object> &obj) {
std::vector<ref<Object>> children = obj->expand();
if (children.empty()) {
props.set_object(name, obj);
props.set_object(name, obj, false);
} else if (children.size() == 1) {
props.set_object(name, children[0]);
if (children[0].get())
props.set_object(name, children[0], false);
} else {
int ctr = 0;
for (auto c : children)
props.set_object(name + "_" + std::to_string(ctr++), c);
if (c)
props.set_object(name + "_" + std::to_string(ctr++), c, false);
}
}

Expand Down Expand Up @@ -280,13 +306,13 @@ void parse_dictionary(DictParseContext &ctx,
return;
}

const Class *class_;
if (is_scene)
class_ = Class::for_name("Scene", GET_VARIANT());
else
class_ = PluginManager::instance()->get_plugin_class(type, GET_VARIANT())->parent();

bool within_emitter = (!is_scene && class_->alias() == "emitter");
bool within_emitter = false;
if (is_scene) {
inst.class_ = Class::for_name("Scene", GET_VARIANT());
} else {
inst.class_ = PluginManager::instance()->get_plugin_class(type, GET_VARIANT());
within_emitter = inst.class_->parent()->alias() == "emitter";
}

Properties &props = inst.props;
props.set_plugin_name(type);
Expand Down Expand Up @@ -381,15 +407,15 @@ void parse_dictionary(DictParseContext &ctx,
path2 = id2;
if (ctx.instances.count(path2) != 1)
Throw("Referenced id \"%s\" not found: %s", path2, path);
inst.dependencies.push_back({key, path2});
props.set_named_reference(key, path2);
} else if (key2 != "type") {
Throw("Unexpected key in ref dictionary: %s", key2);
}
}
} else {
std::string path2 = is_root ? key : path + "." + key;
inst.dependencies.push_back({key, path2});
parse_dictionary<Float, Spectrum>(ctx, path2, dict2);
props.set_named_reference(key, path2);
}
continue;
}
Expand Down Expand Up @@ -419,12 +445,7 @@ void parse_dictionary(DictParseContext &ctx,
} catch (const nb::cast_error &) { }

// Didn't match any of the other types above
Throw("Unsupported value type for parameter \"%s.%s\": %s! One of the "
"following types is expected: "
"bool, int, float, str, mitsuba.ScalarColor3f, "
"mitsuba.ScalarArray3f, mitsuba.ScalarTransform3f, "
"mitsuba.ScalarTransform4f, mitsuba.TensorXf, mitsuba.Object",
path, key, nb::str(value.type()).c_str());
Throw("Unsupported value type: %s!\n", nb::str(value.type()).c_str());
}

// Set object id based on path in dictionary if no id is provided
Expand Down Expand Up @@ -453,7 +474,7 @@ Task *instantiate_node(DictParseContext &ctx,
return nullptr;

std::vector<Task *> deps;
for (auto &[key2, path2] : inst.dependencies) {
for (auto &[key2, path2] : inst.props.named_references()) {
if (task_map.find(path2) == task_map.end()) {
Task *task = instantiate_node<Float, Spectrum>(ctx, path2, task_map);
task_map.insert({path2, task});
Expand Down Expand Up @@ -483,15 +504,15 @@ Task *instantiate_node(DictParseContext &ctx,
else
class_ = PluginManager::instance()->get_plugin_class(type, GET_VARIANT())->parent();

for (auto &[key2, path2] : inst.dependencies) {
for (auto &[key2, path2] : props.named_references()) {
if (ctx.instances.count(path2) == 1) {
ref<Object> obj2 = ctx.instances[path2].object;
if (obj2)
expand_and_set_object(props, key2, obj2);
else
Throw("Dependence hasn't been instantiated yet: %s, %s -> %s", path, path2, key2);
Throw("Dependence hasn't been instantiated yet: %s, %s -> %s", path, (std::string) path2, key2);
} else {
Throw("Dependence path \"%s\" not found: %s", path2, path);
Throw("Dependence path \"%s\" not found: %s", (std::string) path2, path);
}
}

Expand Down
Loading