forked from bartvdbraak/blender
Cycles : add a Volume Geometry Node
This splits the volume related data (properties for rendering and attributes) of the Mesh node into a new `Volume` node type. This `Volume` node derives from the `Mesh` class since we generate a mesh for the bounds of the volume, as such we can safely work on `Volumes` as if they were `Meshes`, e.g. for BVH creation. However such code should still check for the geometry type of the object to be `MESH` or `VOLUME` which may be bug prone if this is forgotten. This is part of T79131. Reviewed By: brecht Maniphest Tasks: T79131 Differential Revision: https://developer.blender.org/D8538
This commit is contained in:
parent
3e56dd8fd9
commit
aa1e4baa22
@ -19,6 +19,7 @@
|
||||
#include "render/hair.h"
|
||||
#include "render/mesh.h"
|
||||
#include "render/object.h"
|
||||
#include "render/volume.h"
|
||||
|
||||
#include "blender/blender_sync.h"
|
||||
#include "blender/blender_util.h"
|
||||
@ -27,6 +28,19 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static Geometry::Type determine_geom_type(BL::Object &b_ob, bool use_particle_hair)
|
||||
{
|
||||
if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) {
|
||||
return Geometry::HAIR;
|
||||
}
|
||||
|
||||
if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) {
|
||||
return Geometry::VOLUME;
|
||||
}
|
||||
|
||||
return Geometry::MESH;
|
||||
}
|
||||
|
||||
Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
|
||||
BL::Object &b_ob,
|
||||
BL::Object &b_ob_instance,
|
||||
@ -40,9 +54,7 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
|
||||
BL::Material material_override = view_layer.material_override;
|
||||
Shader *default_shader = (b_ob.type() == BL::Object::type_VOLUME) ? scene->default_volume :
|
||||
scene->default_surface;
|
||||
Geometry::Type geom_type = (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) ?
|
||||
Geometry::HAIR :
|
||||
Geometry::MESH;
|
||||
Geometry::Type geom_type = determine_geom_type(b_ob, use_particle_hair);
|
||||
|
||||
/* Find shader indices. */
|
||||
vector<Shader *> used_shaders;
|
||||
@ -73,6 +85,9 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
|
||||
if (geom_type == Geometry::HAIR) {
|
||||
geom = new Hair();
|
||||
}
|
||||
else if (geom_type == Geometry::VOLUME) {
|
||||
geom = new Volume();
|
||||
}
|
||||
else {
|
||||
geom = new Mesh();
|
||||
}
|
||||
@ -121,13 +136,13 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
|
||||
|
||||
geom->name = ustring(b_ob_data.name().c_str());
|
||||
|
||||
if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) {
|
||||
if (geom_type == Geometry::HAIR) {
|
||||
Hair *hair = static_cast<Hair *>(geom);
|
||||
sync_hair(b_depsgraph, b_ob, hair, used_shaders);
|
||||
}
|
||||
else if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
sync_volume(b_ob, mesh, used_shaders);
|
||||
else if (geom_type == Geometry::VOLUME) {
|
||||
Volume *volume = static_cast<Volume *>(geom);
|
||||
sync_volume(b_ob, volume, used_shaders);
|
||||
}
|
||||
else {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
|
@ -148,7 +148,7 @@ class BlenderSync {
|
||||
bool *use_portal);
|
||||
|
||||
/* Volume */
|
||||
void sync_volume(BL::Object &b_ob, Mesh *mesh, const vector<Shader *> &used_shaders);
|
||||
void sync_volume(BL::Object &b_ob, Volume *volume, const vector<Shader *> &used_shaders);
|
||||
|
||||
/* Mesh */
|
||||
void sync_mesh(BL::Depsgraph b_depsgraph,
|
||||
|
@ -17,8 +17,8 @@
|
||||
#include "render/colorspace.h"
|
||||
#include "render/image.h"
|
||||
#include "render/image_vdb.h"
|
||||
#include "render/mesh.h"
|
||||
#include "render/object.h"
|
||||
#include "render/volume.h"
|
||||
|
||||
#include "blender/blender_sync.h"
|
||||
#include "blender/blender_util.h"
|
||||
@ -181,7 +181,7 @@ class BlenderSmokeLoader : public ImageLoader {
|
||||
AttributeStandard attribute;
|
||||
};
|
||||
|
||||
static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Mesh *mesh, float frame)
|
||||
static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Volume *volume, float frame)
|
||||
{
|
||||
BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob);
|
||||
if (!b_domain) {
|
||||
@ -198,13 +198,13 @@ static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Mesh *mesh, float
|
||||
|
||||
for (int i = 0; attributes[i] != ATTR_STD_NONE; i++) {
|
||||
AttributeStandard std = attributes[i];
|
||||
if (!mesh->need_attribute(scene, std)) {
|
||||
if (!volume->need_attribute(scene, std)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mesh->volume_clipping = b_domain.clipping();
|
||||
volume->clipping = b_domain.clipping();
|
||||
|
||||
Attribute *attr = mesh->attributes.add(std);
|
||||
Attribute *attr = volume->attributes.add(std);
|
||||
|
||||
ImageLoader *loader = new BlenderSmokeLoader(b_ob, std);
|
||||
ImageParams params;
|
||||
@ -228,7 +228,7 @@ class BlenderVolumeLoader : public VDBImageLoader {
|
||||
if (b_volume_grid.name() == grid_name) {
|
||||
const bool unload = !b_volume_grid.is_loaded();
|
||||
|
||||
Volume *volume = (Volume *)b_volume.ptr.data;
|
||||
::Volume *volume = (::Volume *)b_volume.ptr.data;
|
||||
VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data;
|
||||
grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
|
||||
|
||||
@ -252,16 +252,19 @@ class BlenderVolumeLoader : public VDBImageLoader {
|
||||
BL::Volume b_volume;
|
||||
};
|
||||
|
||||
static void sync_volume_object(BL::BlendData &b_data, BL::Object &b_ob, Scene *scene, Mesh *mesh)
|
||||
static void sync_volume_object(BL::BlendData &b_data,
|
||||
BL::Object &b_ob,
|
||||
Scene *scene,
|
||||
Volume *volume)
|
||||
{
|
||||
BL::Volume b_volume(b_ob.data());
|
||||
b_volume.grids.load(b_data.ptr.data);
|
||||
|
||||
BL::VolumeRender b_render(b_volume.render());
|
||||
|
||||
mesh->volume_clipping = b_render.clipping();
|
||||
mesh->volume_step_size = b_render.step_size();
|
||||
mesh->volume_object_space = (b_render.space() == BL::VolumeRender::space_OBJECT);
|
||||
volume->clipping = b_render.clipping();
|
||||
volume->step_size = b_render.step_size();
|
||||
volume->object_space = (b_render.space() == BL::VolumeRender::space_OBJECT);
|
||||
|
||||
/* Find grid with matching name. */
|
||||
BL::Volume::grids_iterator b_grid_iter;
|
||||
@ -289,11 +292,11 @@ static void sync_volume_object(BL::BlendData &b_data, BL::Object &b_ob, Scene *s
|
||||
std = ATTR_STD_VOLUME_VELOCITY;
|
||||
}
|
||||
|
||||
if ((std != ATTR_STD_NONE && mesh->need_attribute(scene, std)) ||
|
||||
mesh->need_attribute(scene, name)) {
|
||||
if ((std != ATTR_STD_NONE && volume->need_attribute(scene, std)) ||
|
||||
volume->need_attribute(scene, name)) {
|
||||
Attribute *attr = (std != ATTR_STD_NONE) ?
|
||||
mesh->attributes.add(std) :
|
||||
mesh->attributes.add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL);
|
||||
volume->attributes.add(std) :
|
||||
volume->attributes.add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL);
|
||||
|
||||
ImageLoader *loader = new BlenderVolumeLoader(b_data, b_volume, name.string());
|
||||
ImageParams params;
|
||||
@ -317,28 +320,30 @@ static vector<int> get_voxel_image_slots(Mesh *mesh)
|
||||
return slots;
|
||||
}
|
||||
|
||||
void BlenderSync::sync_volume(BL::Object &b_ob, Mesh *mesh, const vector<Shader *> &used_shaders)
|
||||
void BlenderSync::sync_volume(BL::Object &b_ob,
|
||||
Volume *volume,
|
||||
const vector<Shader *> &used_shaders)
|
||||
{
|
||||
vector<int> old_voxel_slots = get_voxel_image_slots(mesh);
|
||||
vector<int> old_voxel_slots = get_voxel_image_slots(volume);
|
||||
|
||||
mesh->clear();
|
||||
mesh->used_shaders = used_shaders;
|
||||
volume->clear();
|
||||
volume->used_shaders = used_shaders;
|
||||
|
||||
if (view_layer.use_volumes) {
|
||||
if (b_ob.type() == BL::Object::type_VOLUME) {
|
||||
/* Volume object. Create only attributes, bounding mesh will then
|
||||
* be automatically generated later. */
|
||||
sync_volume_object(b_data, b_ob, scene, mesh);
|
||||
sync_volume_object(b_data, b_ob, scene, volume);
|
||||
}
|
||||
else {
|
||||
/* Smoke domain. */
|
||||
sync_smoke_volume(scene, b_ob, mesh, b_scene.frame_current());
|
||||
sync_smoke_volume(scene, b_ob, volume, b_scene.frame_current());
|
||||
}
|
||||
}
|
||||
|
||||
/* Tag update. */
|
||||
bool rebuild = (old_voxel_slots != get_voxel_image_slots(mesh));
|
||||
mesh->tag_update(scene, rebuild);
|
||||
bool rebuild = (old_voxel_slots != get_voxel_image_slots(volume));
|
||||
volume->tag_update(scene, rebuild);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@ -270,7 +270,7 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair
|
||||
|
||||
void BVHBuild::add_reference_geometry(BoundBox &root, BoundBox ¢er, Geometry *geom, int i)
|
||||
{
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
add_reference_triangles(root, center, mesh, i);
|
||||
}
|
||||
@ -299,7 +299,7 @@ static size_t count_curve_segments(Hair *hair)
|
||||
|
||||
static size_t count_primitives(Geometry *geom)
|
||||
{
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
return mesh->num_triangles();
|
||||
}
|
||||
|
@ -306,7 +306,7 @@ thread_mutex BVHEmbree::rtc_shared_mutex;
|
||||
|
||||
static size_t count_primitives(Geometry *geom)
|
||||
{
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
return mesh->num_triangles();
|
||||
}
|
||||
@ -531,7 +531,7 @@ void BVHEmbree::add_object(Object *ob, int i)
|
||||
{
|
||||
Geometry *geom = ob->geometry;
|
||||
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (mesh->num_triangles() > 0) {
|
||||
add_triangles(ob, mesh, i);
|
||||
@ -979,7 +979,7 @@ void BVHEmbree::refit_nodes()
|
||||
if (!params.top_level || (ob->is_traceable() && !ob->geometry->is_instanced())) {
|
||||
Geometry *geom = ob->geometry;
|
||||
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (mesh->num_triangles() > 0) {
|
||||
update_tri_vertex_buffer(rtcGetGeometry(scene, geom_id), mesh);
|
||||
|
@ -95,7 +95,7 @@ void BVHOptiX::pack_blas()
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (geom->type == Geometry::MESH) {
|
||||
else if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *const mesh = static_cast<Mesh *const>(geom);
|
||||
if (mesh->num_triangles() > 0) {
|
||||
const size_t num_triangles = mesh->num_triangles();
|
||||
|
@ -458,7 +458,7 @@ void BVHSpatialSplit::split_object_reference(
|
||||
{
|
||||
Geometry *geom = object->geometry;
|
||||
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
for (int tri_idx = 0; tri_idx < mesh->num_triangles(); ++tri_idx) {
|
||||
split_triangle_primitive(mesh, &object->tfm, tri_idx, dim, pos, left_bounds, right_bounds);
|
||||
|
@ -1389,7 +1389,7 @@ class OptiXDevice : public CUDADevice {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (geom->type == Geometry::MESH) {
|
||||
else if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
// Build BLAS for triangle primitives
|
||||
Mesh *const mesh = static_cast<Mesh *const>(ob->geometry);
|
||||
if (mesh->num_triangles() == 0) {
|
||||
|
@ -34,7 +34,6 @@ set(SRC
|
||||
mesh.cpp
|
||||
mesh_displace.cpp
|
||||
mesh_subdivision.cpp
|
||||
mesh_volume.cpp
|
||||
nodes.cpp
|
||||
object.cpp
|
||||
osl.cpp
|
||||
@ -48,6 +47,7 @@ set(SRC
|
||||
svm.cpp
|
||||
tables.cpp
|
||||
tile.cpp
|
||||
volume.cpp
|
||||
)
|
||||
|
||||
set(SRC_HEADERS
|
||||
@ -86,6 +86,7 @@ set(SRC_HEADERS
|
||||
svm.h
|
||||
tables.h
|
||||
tile.h
|
||||
volume.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
@ -167,7 +167,7 @@ size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const
|
||||
size = 1;
|
||||
break;
|
||||
case ATTR_ELEMENT_VERTEX:
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
size = mesh->verts.size() + mesh->num_ngons;
|
||||
if (prim == ATTR_PRIM_SUBD) {
|
||||
@ -185,7 +185,7 @@ size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const
|
||||
}
|
||||
break;
|
||||
case ATTR_ELEMENT_FACE:
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (prim == ATTR_PRIM_GEOMETRY) {
|
||||
size = mesh->num_triangles();
|
||||
@ -485,6 +485,25 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
|
||||
case ATTR_STD_GENERATED_TRANSFORM:
|
||||
attr = add(name, TypeDesc::TypeMatrix, ATTR_ELEMENT_MESH);
|
||||
break;
|
||||
case ATTR_STD_POINTINESS:
|
||||
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX);
|
||||
break;
|
||||
case ATTR_STD_RANDOM_PER_ISLAND:
|
||||
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_FACE);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (geometry->type == Geometry::VOLUME) {
|
||||
switch (std) {
|
||||
case ATTR_STD_VERTEX_NORMAL:
|
||||
attr = add(name, TypeDesc::TypeNormal, ATTR_ELEMENT_VERTEX);
|
||||
break;
|
||||
case ATTR_STD_FACE_NORMAL:
|
||||
attr = add(name, TypeDesc::TypeNormal, ATTR_ELEMENT_FACE);
|
||||
break;
|
||||
case ATTR_STD_VOLUME_DENSITY:
|
||||
case ATTR_STD_VOLUME_FLAME:
|
||||
case ATTR_STD_VOLUME_HEAT:
|
||||
@ -497,12 +516,6 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
|
||||
case ATTR_STD_VOLUME_VELOCITY:
|
||||
attr = add(name, TypeDesc::TypeVector, ATTR_ELEMENT_VOXEL);
|
||||
break;
|
||||
case ATTR_STD_POINTINESS:
|
||||
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX);
|
||||
break;
|
||||
case ATTR_STD_RANDOM_PER_ISLAND:
|
||||
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_FACE);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "render/scene.h"
|
||||
#include "render/shader.h"
|
||||
#include "render/stats.h"
|
||||
#include "render/volume.h"
|
||||
|
||||
#include "subd/subd_patch_table.h"
|
||||
#include "subd/subd_split.h"
|
||||
@ -796,7 +797,7 @@ void GeometryManager::mesh_calc_offset(Scene *scene)
|
||||
size_t optix_prim_size = 0;
|
||||
|
||||
foreach (Geometry *geom, scene->geometry) {
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
|
||||
mesh->vert_offset = vert_size;
|
||||
@ -854,7 +855,7 @@ void GeometryManager::device_update_mesh(
|
||||
size_t patch_size = 0;
|
||||
|
||||
foreach (Geometry *geom, scene->geometry) {
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
|
||||
vert_size += mesh->verts.size();
|
||||
@ -888,7 +889,7 @@ void GeometryManager::device_update_mesh(
|
||||
* really use same semantic of arrays.
|
||||
*/
|
||||
foreach (Geometry *geom, scene->geometry) {
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
for (size_t i = 0; i < mesh->num_triangles(); ++i) {
|
||||
tri_prim_index[i + mesh->prim_offset] = 3 * (i + mesh->prim_offset);
|
||||
@ -916,7 +917,7 @@ void GeometryManager::device_update_mesh(
|
||||
float2 *tri_patch_uv = dscene->tri_patch_uv.alloc(vert_size);
|
||||
|
||||
foreach (Geometry *geom, scene->geometry) {
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
mesh->pack_shaders(scene, &tri_shader[mesh->prim_offset]);
|
||||
mesh->pack_normals(&vnormal[mesh->vert_offset]);
|
||||
@ -992,7 +993,7 @@ void GeometryManager::device_update_mesh(
|
||||
if (for_displacement) {
|
||||
float4 *prim_tri_verts = dscene->prim_tri_verts.alloc(tri_size * 3);
|
||||
foreach (Geometry *geom, scene->geometry) {
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
for (size_t i = 0; i < mesh->num_triangles(); ++i) {
|
||||
Mesh::Triangle t = mesh->get_triangle(i);
|
||||
@ -1122,18 +1123,16 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
|
||||
}
|
||||
}
|
||||
|
||||
if (need_update && geom->has_volume && geom->type == Geometry::MESH) {
|
||||
if (need_update && geom->type == Geometry::VOLUME) {
|
||||
/* Create volume meshes if there is voxel data. */
|
||||
if (geom->has_voxel_attributes()) {
|
||||
if (!volume_images_updated) {
|
||||
progress.set_status("Updating Meshes Volume Bounds");
|
||||
device_update_volume_images(device, scene, progress);
|
||||
volume_images_updated = true;
|
||||
}
|
||||
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
create_volume_mesh(mesh, progress);
|
||||
}
|
||||
Volume *volume = static_cast<Volume *>(geom);
|
||||
create_volume_mesh(volume, progress);
|
||||
}
|
||||
|
||||
if (geom->type == Geometry::HAIR) {
|
||||
@ -1238,7 +1237,7 @@ void GeometryManager::device_update(Device *device,
|
||||
geom->need_update = true;
|
||||
}
|
||||
|
||||
if (geom->need_update && geom->type == Geometry::MESH) {
|
||||
if (geom->need_update && (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME)) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
|
||||
/* Update normals. */
|
||||
|
@ -40,6 +40,7 @@ class RenderStats;
|
||||
class Scene;
|
||||
class SceneParams;
|
||||
class Shader;
|
||||
class Volume;
|
||||
|
||||
/* Geometry
|
||||
*
|
||||
@ -52,6 +53,7 @@ class Geometry : public Node {
|
||||
enum Type {
|
||||
MESH,
|
||||
HAIR,
|
||||
VOLUME,
|
||||
};
|
||||
|
||||
Type type;
|
||||
@ -166,7 +168,7 @@ class GeometryManager {
|
||||
protected:
|
||||
bool displace(Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress &progress);
|
||||
|
||||
void create_volume_mesh(Mesh *mesh, Progress &progress);
|
||||
void create_volume_mesh(Volume *volume, Progress &progress);
|
||||
|
||||
/* Attributes */
|
||||
void update_osl_attributes(Device *device,
|
||||
|
@ -250,7 +250,7 @@ void LightManager::test_enabled_lights(Scene *scene)
|
||||
bool LightManager::object_usable_as_light(Object *object)
|
||||
{
|
||||
Geometry *geom = object->geometry;
|
||||
if (geom->type != Geometry::MESH) {
|
||||
if (geom->type != Geometry::MESH && geom->type != Geometry::VOLUME) {
|
||||
return false;
|
||||
}
|
||||
/* Skip objects with NaNs */
|
||||
|
@ -135,7 +135,8 @@ NODE_DEFINE(Mesh)
|
||||
return type;
|
||||
}
|
||||
|
||||
Mesh::Mesh() : Geometry(node_type, Geometry::MESH), subd_attributes(this, ATTR_PRIM_SUBD)
|
||||
Mesh::Mesh(const NodeType *node_type_, Type geom_type_)
|
||||
: Geometry(node_type_, geom_type_), subd_attributes(this, ATTR_PRIM_SUBD)
|
||||
{
|
||||
vert_offset = 0;
|
||||
|
||||
@ -145,10 +146,6 @@ Mesh::Mesh() : Geometry(node_type, Geometry::MESH), subd_attributes(this, ATTR_P
|
||||
|
||||
num_subd_verts = 0;
|
||||
|
||||
volume_clipping = 0.001f;
|
||||
volume_step_size = 0.0f;
|
||||
volume_object_space = false;
|
||||
|
||||
num_ngons = 0;
|
||||
|
||||
subdivision_type = SUBDIVISION_NONE;
|
||||
@ -157,6 +154,10 @@ Mesh::Mesh() : Geometry(node_type, Geometry::MESH), subd_attributes(this, ATTR_P
|
||||
patch_table = NULL;
|
||||
}
|
||||
|
||||
Mesh::Mesh() : Mesh(node_type, Geometry::MESH)
|
||||
{
|
||||
}
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
delete patch_table;
|
||||
|
@ -52,6 +52,9 @@ struct PackedPatchTable;
|
||||
/* Mesh */
|
||||
|
||||
class Mesh : public Geometry {
|
||||
protected:
|
||||
Mesh(const NodeType *node_type_, Type geom_type_);
|
||||
|
||||
public:
|
||||
NODE_DECLARE
|
||||
|
||||
@ -133,10 +136,6 @@ class Mesh : public Geometry {
|
||||
array<int> triangle_patch; /* must be < 0 for non subd triangles */
|
||||
array<float2> vert_patch_uv;
|
||||
|
||||
float volume_clipping;
|
||||
float volume_step_size;
|
||||
bool volume_object_space;
|
||||
|
||||
array<SubdFace> subd_faces;
|
||||
array<int> subd_face_corners;
|
||||
int num_ngons;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "render/mesh.h"
|
||||
#include "render/particles.h"
|
||||
#include "render/scene.h"
|
||||
#include "render/volume.h"
|
||||
|
||||
#include "util/util_foreach.h"
|
||||
#include "util/util_logging.h"
|
||||
@ -270,7 +271,7 @@ uint Object::visibility_for_tracing() const
|
||||
|
||||
float Object::compute_volume_step_size() const
|
||||
{
|
||||
if (geometry->type != Geometry::MESH) {
|
||||
if (geometry->type != Geometry::MESH && geometry->type != Geometry::VOLUME) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
|
||||
@ -299,7 +300,10 @@ float Object::compute_volume_step_size() const
|
||||
/* Compute step size from voxel grids. */
|
||||
float step_size = FLT_MAX;
|
||||
|
||||
foreach (Attribute &attr, mesh->attributes.attributes) {
|
||||
if (geometry->type == Geometry::VOLUME) {
|
||||
Volume *volume = static_cast<Volume *>(geometry);
|
||||
|
||||
foreach (Attribute &attr, volume->attributes.attributes) {
|
||||
if (attr.element == ATTR_ELEMENT_VOXEL) {
|
||||
ImageHandle &handle = attr.data_voxel();
|
||||
const ImageMetaData &metadata = handle.metadata();
|
||||
@ -308,7 +312,7 @@ float Object::compute_volume_step_size() const
|
||||
}
|
||||
|
||||
/* User specified step size. */
|
||||
float voxel_step_size = mesh->volume_step_size;
|
||||
float voxel_step_size = volume->step_size;
|
||||
|
||||
if (voxel_step_size == 0.0f) {
|
||||
/* Auto detect step size. */
|
||||
@ -322,7 +326,7 @@ float Object::compute_volume_step_size() const
|
||||
}
|
||||
voxel_step_size = min3(fabs(transform_direction(&voxel_tfm, size)));
|
||||
}
|
||||
else if (mesh->volume_object_space) {
|
||||
else if (volume->object_space) {
|
||||
/* User specified step size in object space. */
|
||||
float3 size = make_float3(voxel_step_size, voxel_step_size, voxel_step_size);
|
||||
voxel_step_size = min3(fabs(transform_direction(&tfm, size)));
|
||||
@ -333,6 +337,7 @@ float Object::compute_volume_step_size() const
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (step_size == FLT_MAX) {
|
||||
/* Fall back to 1/10th of bounds for procedural volumes. */
|
||||
@ -365,14 +370,14 @@ static float object_surface_area(UpdateObjectTransformState *state,
|
||||
const Transform &tfm,
|
||||
Geometry *geom)
|
||||
{
|
||||
if (geom->type != Geometry::MESH) {
|
||||
if (geom->type != Geometry::MESH && geom->type != Geometry::VOLUME) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (mesh->has_volume) {
|
||||
if (mesh->has_volume || geom->type == Geometry::VOLUME) {
|
||||
/* Volume density automatically adjust to object scale. */
|
||||
if (mesh->volume_object_space) {
|
||||
if (geom->type == Geometry::VOLUME && static_cast<Volume *>(geom)->object_space) {
|
||||
const float3 unit = normalize(make_float3(1.0f, 1.0f, 1.0f));
|
||||
return 1.0f / len(transform_direction(&tfm, unit));
|
||||
}
|
||||
@ -527,7 +532,9 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
|
||||
kobject.dupli_uv[1] = ob->dupli_uv[1];
|
||||
int totalsteps = geom->motion_steps;
|
||||
kobject.numsteps = (totalsteps - 1) / 2;
|
||||
kobject.numverts = (geom->type == Geometry::MESH) ? static_cast<Mesh *>(geom)->verts.size() : 0;
|
||||
kobject.numverts = (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) ?
|
||||
static_cast<Mesh *>(geom)->verts.size() :
|
||||
0;
|
||||
kobject.patch_map_offset = 0;
|
||||
kobject.attribute_map_offset = 0;
|
||||
uint32_t hash_name = util_murmur_hash3(ob->name.c_str(), ob->name.length(), 0);
|
||||
@ -819,7 +826,7 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, P
|
||||
bool apply = (geometry_users[geom] == 1) && !geom->has_surface_bssrdf &&
|
||||
!geom->has_true_displacement();
|
||||
|
||||
if (geom->type == Geometry::MESH) {
|
||||
if (geom->type == Geometry::MESH || geom->type == Geometry::VOLUME) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
apply = apply && mesh->subdivision_type == Mesh::SUBDIVISION_NONE;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2016 Blender Foundation
|
||||
* Copyright 2020 Blender Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "render/volume.h"
|
||||
#include "render/attribute.h"
|
||||
#include "render/image_vdb.h"
|
||||
#include "render/mesh.h"
|
||||
#include "render/scene.h"
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
@ -34,6 +34,33 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
NODE_DEFINE(Volume)
|
||||
{
|
||||
NodeType *type = NodeType::add("volume", create, NodeType::NONE, Geometry::node_base_type);
|
||||
|
||||
SOCKET_INT_ARRAY(triangles, "Triangles", array<int>());
|
||||
SOCKET_POINT_ARRAY(verts, "Vertices", array<float3>());
|
||||
SOCKET_INT_ARRAY(shader, "Shader", array<int>());
|
||||
|
||||
SOCKET_FLOAT(clipping, "Clipping", 0.001f);
|
||||
SOCKET_FLOAT(step_size, "Step Size", 0.0f);
|
||||
SOCKET_BOOLEAN(object_space, "Object Space", false);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
Volume::Volume() : Mesh(node_type, Geometry::VOLUME)
|
||||
{
|
||||
clipping = 0.001f;
|
||||
step_size = 0.0f;
|
||||
object_space = false;
|
||||
}
|
||||
|
||||
void Volume::clear()
|
||||
{
|
||||
Mesh::clear(true);
|
||||
}
|
||||
|
||||
struct QuadData {
|
||||
int v0, v1, v2, v3;
|
||||
|
||||
@ -458,15 +485,15 @@ static openvdb::GridBase::ConstPtr openvdb_grid_from_device_texture(device_textu
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
|
||||
void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress)
|
||||
{
|
||||
string msg = string_printf("Computing Volume Mesh %s", mesh->name.c_str());
|
||||
string msg = string_printf("Computing Volume Mesh %s", volume->name.c_str());
|
||||
progress.set_status("Updating Mesh", msg);
|
||||
|
||||
VolumeMeshBuilder builder;
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
foreach (Attribute &attr, mesh->attributes.attributes) {
|
||||
foreach (Attribute &attr, volume->attributes.attributes) {
|
||||
if (attr.element != ATTR_ELEMENT_VOXEL) {
|
||||
continue;
|
||||
}
|
||||
@ -491,20 +518,20 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
|
||||
|
||||
if (image_memory->data_elements == 1) {
|
||||
grid = openvdb_grid_from_device_texture<openvdb::FloatGrid>(
|
||||
image_memory, mesh->volume_clipping, handle.metadata().transform_3d);
|
||||
image_memory, volume->clipping, handle.metadata().transform_3d);
|
||||
}
|
||||
else if (image_memory->data_elements == 3) {
|
||||
grid = openvdb_grid_from_device_texture<openvdb::Vec3fGrid>(
|
||||
image_memory, mesh->volume_clipping, handle.metadata().transform_3d);
|
||||
image_memory, volume->clipping, handle.metadata().transform_3d);
|
||||
}
|
||||
else if (image_memory->data_elements == 4) {
|
||||
grid = openvdb_grid_from_device_texture<openvdb::Vec4fGrid>(
|
||||
image_memory, mesh->volume_clipping, handle.metadata().transform_3d);
|
||||
image_memory, volume->clipping, handle.metadata().transform_3d);
|
||||
}
|
||||
}
|
||||
|
||||
if (grid) {
|
||||
builder.add_grid(grid, do_clipping, mesh->volume_clipping);
|
||||
builder.add_grid(grid, do_clipping, volume->clipping);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -517,7 +544,7 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
|
||||
Shader *volume_shader = NULL;
|
||||
int pad_size = 0;
|
||||
|
||||
foreach (Shader *shader, mesh->used_shaders) {
|
||||
foreach (Shader *shader, volume->used_shaders) {
|
||||
if (!shader->has_volume) {
|
||||
continue;
|
||||
}
|
||||
@ -544,7 +571,8 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
|
||||
* volumes or meshes. The proper solution would be to improve intersection in
|
||||
* the kernel to support robust handling of multiple overlapping faces or use
|
||||
* an all-hit intersection similar to shadows. */
|
||||
const float face_overlap_avoidance = 0.1f * hash_uint_to_float(hash_string(mesh->name.c_str()));
|
||||
const float face_overlap_avoidance = 0.1f *
|
||||
hash_uint_to_float(hash_string(volume->name.c_str()));
|
||||
|
||||
/* Create mesh. */
|
||||
vector<float3> vertices;
|
||||
@ -552,20 +580,20 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
|
||||
vector<float3> face_normals;
|
||||
builder.create_mesh(vertices, indices, face_normals, face_overlap_avoidance);
|
||||
|
||||
mesh->clear(true);
|
||||
mesh->reserve_mesh(vertices.size(), indices.size() / 3);
|
||||
mesh->used_shaders.push_back(volume_shader);
|
||||
mesh->need_update_rebuild = true;
|
||||
volume->clear();
|
||||
volume->reserve_mesh(vertices.size(), indices.size() / 3);
|
||||
volume->used_shaders.push_back(volume_shader);
|
||||
volume->need_update_rebuild = true;
|
||||
|
||||
for (size_t i = 0; i < vertices.size(); ++i) {
|
||||
mesh->add_vertex(vertices[i]);
|
||||
volume->add_vertex(vertices[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < indices.size(); i += 3) {
|
||||
mesh->add_triangle(indices[i], indices[i + 1], indices[i + 2], 0, false);
|
||||
volume->add_triangle(indices[i], indices[i + 1], indices[i + 2], 0, false);
|
||||
}
|
||||
|
||||
Attribute *attr_fN = mesh->attributes.add(ATTR_STD_FACE_NORMAL);
|
||||
Attribute *attr_fN = volume->attributes.add(ATTR_STD_FACE_NORMAL);
|
||||
float3 *fN = attr_fN->data_float3();
|
||||
|
||||
for (size_t i = 0; i < face_normals.size(); ++i) {
|
38
intern/cycles/render/volume.h
Normal file
38
intern/cycles/render/volume.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2020 Blender Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "graph/node.h"
|
||||
|
||||
#include "render/mesh.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Volume : public Mesh {
|
||||
public:
|
||||
NODE_DECLARE
|
||||
|
||||
Volume();
|
||||
|
||||
float clipping;
|
||||
float step_size;
|
||||
bool object_space;
|
||||
|
||||
virtual void clear() override;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
Loading…
Reference in New Issue
Block a user