Cycles volume: fast empty space optimization by generating a tight mesh

around the volume.

We generate a tight mesh around the active voxels of the volume in order
to effectively skip empty space, and start volume ray marching as close
to interesting volume data as possible. See code comments for details on
how the mesh generation algorithm works.

This gives up to 2x speedups in some scenes.

Reviewed by: brecht, dingto

Reviewers: #cycles

Subscribers: lvxejay, jtheninja, brecht

Differential Revision: https://developer.blender.org/D3038
This commit is contained in:
Kévin Dietrich 2018-03-01 11:54:01 +01:00
parent 172614fb7d
commit 7377d411b4
Notes: blender-bot 2023-10-13 01:54:23 +02:00
Referenced by commit f2a2d5492b, Cycles: Fix building of OpenCL kernels after volume optimization commit
Referenced by issue #54279, Viewport artifacts after adding or moving objects
11 changed files with 718 additions and 17 deletions

View File

@ -335,6 +335,8 @@ static void create_mesh_volume_attribute(BL::Object& b_ob,
if(!b_domain)
return;
mesh->volume_isovalue = b_domain.clipping();
Attribute *attr = mesh->attributes.add(std);
VoxelAttribute *volume_data = attr->data_voxel();
ImageMetaData metadata;

View File

@ -23,6 +23,7 @@ set(SRC
mesh.cpp
mesh_displace.cpp
mesh_subdivision.cpp
mesh_volume.cpp
nodes.cpp
object.cpp
osl.cpp

View File

@ -535,9 +535,23 @@ void AttributeSet::resize(bool reserve_only)
}
}
void AttributeSet::clear()
void AttributeSet::clear(bool preserve_voxel_data)
{
attributes.clear();
if(preserve_voxel_data) {
list<Attribute>::iterator it;
for(it = attributes.begin(); it != attributes.end();) {
if(it->element == ATTR_ELEMENT_VOXEL || it->std == ATTR_STD_GENERATED_TRANSFORM) {
it++;
}
else {
attributes.erase(it++);
}
}
}
else {
attributes.clear();
}
}
/* AttributeRequest */

View File

@ -123,7 +123,7 @@ public:
void remove(Attribute *attribute);
void resize(bool reserve_only = false);
void clear();
void clear(bool preserve_voxel_data = false);
};
/* AttributeRequest

View File

@ -84,6 +84,16 @@ bool ImageManager::set_animation_frame_update(int frame)
return false;
}
device_memory *ImageManager::image_memory(int flat_slot)
{
ImageDataType type;
int slot = flattened_slot_to_type_index(flat_slot, &type);
Image *img = images[type][slot];
return img->mem;
}
bool ImageManager::get_image_metadata(const string& filename,
void *builtin_data,
ImageMetaData& metadata)

View File

@ -85,6 +85,8 @@ public:
void set_osl_texture_system(void *texture_system);
bool set_animation_frame_update(int frame);
device_memory *image_memory(int flat_slot);
bool need_update;
/* NOTE: Here pixels_size is a size of storage, which equals to

View File

@ -446,6 +446,7 @@ Mesh::Mesh()
geometry_flags = GEOMETRY_NONE;
volume_isovalue = 0.001f;
has_volume = false;
has_surface_bssrdf = false;
@ -533,7 +534,7 @@ void Mesh::reserve_subd_faces(int numfaces, int num_ngons_, int numcorners)
subd_attributes.resize(true);
}
void Mesh::clear()
void Mesh::clear(bool preserve_voxel_data)
{
/* clear all verts and triangles */
verts.clear();
@ -556,15 +557,18 @@ void Mesh::clear()
subd_creases.clear();
attributes.clear();
curve_attributes.clear();
subd_attributes.clear();
used_shaders.clear();
attributes.clear(preserve_voxel_data);
if(!preserve_voxel_data) {
used_shaders.clear();
geometry_flags = GEOMETRY_NONE;
}
transform_applied = false;
transform_negative_scaled = false;
transform_normal = transform_identity();
geometry_flags = GEOMETRY_NONE;
delete patch_table;
patch_table = NULL;
@ -1892,17 +1896,22 @@ void MeshManager::device_update_bvh(Device *device, DeviceScene *dscene, Scene *
delete bvh;
}
void MeshManager::device_update_flags(Device * /*device*/,
DeviceScene * /*dscene*/,
Scene * scene,
Progress& /*progress*/)
void MeshManager::device_update_preprocess(Device *device,
Scene *scene,
Progress& progress)
{
if(!need_update && !need_flags_update) {
return;
}
/* update flags */
progress.set_status("Updating Meshes Flags");
/* Update flags. */
bool volume_images_updated = false;
foreach(Mesh *mesh, scene->meshes) {
mesh->has_volume = false;
foreach(const Shader *shader, mesh->used_shaders) {
if(shader->has_volume) {
mesh->has_volume = true;
@ -1911,7 +1920,29 @@ void MeshManager::device_update_flags(Device * /*device*/,
mesh->has_surface_bssrdf = true;
}
}
if(need_update && mesh->has_volume) {
/* Create volume meshes if there is voxel data. */
bool has_voxel_attributes = false;
foreach(Attribute& attr, mesh->attributes.attributes) {
if(attr.element == ATTR_ELEMENT_VOXEL) {
has_voxel_attributes = true;
}
}
if(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;
}
create_volume_mesh(scene, mesh, progress);
}
}
}
need_flags_update = false;
}
@ -1954,6 +1985,44 @@ void MeshManager::device_update_displacement_images(Device *device,
pool.wait_work();
}
void MeshManager::device_update_volume_images(Device *device,
Scene *scene,
Progress& progress)
{
progress.set_status("Updating Volume Images");
TaskPool pool;
ImageManager *image_manager = scene->image_manager;
set<int> volume_images;
foreach(Mesh *mesh, scene->meshes) {
if(!mesh->need_update) {
continue;
}
foreach(Attribute& attr, mesh->attributes.attributes) {
if(attr.element != ATTR_ELEMENT_VOXEL) {
continue;
}
VoxelAttribute *voxel = attr.data_voxel();
if(voxel->slot != -1) {
volume_images.insert(voxel->slot);
}
}
}
foreach(int slot, volume_images) {
pool.push(function_bind(&ImageManager::device_update_slot,
image_manager,
device,
scene,
slot,
&progress));
}
pool.wait_work();
}
void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
{
if(!need_update)

View File

@ -202,7 +202,8 @@ public:
array<int> triangle_patch; /* must be < 0 for non subd triangles */
array<float2> vert_patch_uv;
bool has_volume; /* Set in the device_update_flags(). */
float volume_isovalue;
bool has_volume; /* Set in the device_update_flags(). */
bool has_surface_bssrdf; /* Set in the device_update_flags(). */
array<float3> curve_keys;
@ -264,7 +265,7 @@ public:
void reserve_curves(int numcurves, int numkeys);
void resize_subd_faces(int numfaces, int num_ngons, int numcorners);
void reserve_subd_faces(int numfaces, int num_ngons, int numcorners);
void clear();
void clear(bool preserve_voxel_data = false);
void add_vertex(float3 P);
void add_vertex_slow(float3 P);
void add_triangle(int v0, int v1, int v2, int shader, bool smooth);
@ -335,13 +336,15 @@ public:
void update_osl_attributes(Device *device, Scene *scene, vector<AttributeRequestSet>& mesh_attributes);
void update_svm_attributes(Device *device, DeviceScene *dscene, Scene *scene, vector<AttributeRequestSet>& mesh_attributes);
void device_update_preprocess(Device *device, Scene *scene, Progress& progress);
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
void device_update_flags(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
void device_free(Device *device, DeviceScene *dscene);
void tag_update(Scene *scene);
void create_volume_mesh(Scene *scene, Mesh *mesh, Progress &progress);
protected:
/* Calculate verts/triangles/curves offsets in global arrays. */
void mesh_calc_offset(Scene *scene);
@ -370,6 +373,10 @@ protected:
void device_update_displacement_images(Device *device,
Scene *scene,
Progress& progress);
void device_update_volume_images(Device *device,
Scene *scene,
Progress& progress);
};
CCL_NAMESPACE_END

View File

@ -0,0 +1,581 @@
/*
* Copyright 2011-2016 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.
*/
#include "render/mesh.h"
#include "render/attribute.h"
#include "render/scene.h"
#include "util/util_foreach.h"
#include "util/util_logging.h"
#include "util/util_progress.h"
#include "util/util_types.h"
CCL_NAMESPACE_BEGIN
static size_t compute_voxel_index(const int3 &resolution, size_t x, size_t y, size_t z)
{
if(x == -1 || x >= resolution.x) {
return -1;
}
if(y == -1 || y >= resolution.y) {
return -1;
}
if(z == -1 || z >= resolution.z) {
return -1;
}
return x + y*resolution.x + z*resolution.x*resolution.y;
}
struct QuadData {
int v0, v1, v2, v3;
float3 normal;
};
enum {
QUAD_X_MIN = 0,
QUAD_X_MAX = 1,
QUAD_Y_MIN = 2,
QUAD_Y_MAX = 3,
QUAD_Z_MIN = 4,
QUAD_Z_MAX = 5,
};
const int quads_indices[6][4] = {
/* QUAD_X_MIN */
{ 4, 0, 3, 7 },
/* QUAD_X_MAX */
{ 1, 5, 6, 2 },
/* QUAD_Y_MIN */
{ 4, 5, 1, 0 },
/* QUAD_Y_MAX */
{ 3, 2, 6, 7 },
/* QUAD_Z_MIN */
{ 0, 1, 2, 3 },
/* QUAD_Z_MAX */
{ 5, 4, 7, 6 },
};
const float3 quads_normals[6] = {
/* QUAD_X_MIN */
make_float3(-1.0f, 0.0f, 0.0f),
/* QUAD_X_MAX */
make_float3(1.0f, 0.0f, 0.0f),
/* QUAD_Y_MIN */
make_float3(0.0f, -1.0f, 0.0f),
/* QUAD_Y_MAX */
make_float3(0.0f, 1.0f, 0.0f),
/* QUAD_Z_MIN */
make_float3(0.0f, 0.0f, -1.0f),
/* QUAD_Z_MAX */
make_float3(0.0f, 0.0f, 1.0f),
};
static void create_quad(int3 corners[8], vector<int3> &vertices, vector<QuadData> &quads, int face_index)
{
size_t vertex_offset = vertices.size();
QuadData quad;
quad.v0 = vertex_offset + 0;
quad.v1 = vertex_offset + 1;
quad.v2 = vertex_offset + 2;
quad.v3 = vertex_offset + 3;
quad.normal = quads_normals[face_index];
quads.push_back(quad);
vertices.push_back(corners[quads_indices[face_index][0]]);
vertices.push_back(corners[quads_indices[face_index][1]]);
vertices.push_back(corners[quads_indices[face_index][2]]);
vertices.push_back(corners[quads_indices[face_index][3]]);
}
struct VolumeParams {
int3 resolution;
float3 cell_size;
float3 start_point;
int pad_size;
};
static const int CUBE_SIZE = 8;
/* Create a mesh from a volume.
*
* The way the algorithm works is as follows:
*
* - the coordinates of active voxels from a dense volume (or 3d image) are
* gathered inside an auxialliary volume.
* - each set of coordinates of an CUBE_SIZE cube are mapped to the same
* coordinate of the auxilliary volume.
* - quads are created between active and non-active voxels in the auxialliary
* volume to generate a tight mesh around the volume.
*/
class VolumeMeshBuilder {
/* Auxilliary volume that is used to check if a node already added. */
vector<char> grid;
/* The resolution of the auxilliary volume, set to be equal to 1/CUBE_SIZE
* of the original volume on each axis. */
int3 res;
size_t number_of_nodes;
/* Offset due to padding in the original grid. Padding will transform the
* coordinates of the original grid from 0...res to -padding...res+padding,
* so some coordinates are negative, and we need to properly account for
* them. */
int3 pad_offset;
VolumeParams *params;
public:
VolumeMeshBuilder(VolumeParams *volume_params);
void add_node(int x, int y, int z);
void add_node_with_padding(int x, int y, int z);
void create_mesh(vector<float3> &vertices,
vector<int> &indices,
vector<float3> &face_normals);
private:
void generate_vertices_and_quads(vector<int3> &vertices_is,
vector<QuadData> &quads);
void deduplicate_vertices(vector<int3> &vertices,
vector<QuadData> &quads);
void convert_object_space(const vector<int3> &vertices,
vector<float3> &out_vertices);
void convert_quads_to_tris(const vector<QuadData> &quads,
vector<int> &tris,
vector<float3> &face_normals);
};
VolumeMeshBuilder::VolumeMeshBuilder(VolumeParams *volume_params)
{
params = volume_params;
number_of_nodes = 0;
const size_t x = divide_up(params->resolution.x, CUBE_SIZE);
const size_t y = divide_up(params->resolution.y, CUBE_SIZE);
const size_t z = divide_up(params->resolution.z, CUBE_SIZE);
/* Adding 2*pad_size since we pad in both positive and negative directions
* along the axis. */
const size_t px = divide_up(params->resolution.x + 2*params->pad_size, CUBE_SIZE);
const size_t py = divide_up(params->resolution.y + 2*params->pad_size, CUBE_SIZE);
const size_t pz = divide_up(params->resolution.z + 2*params->pad_size, CUBE_SIZE);
res = make_int3(px, py, pz);
pad_offset = make_int3(px - x, py - y, pz - z);
grid.resize(px*py*pz, 0);
}
void VolumeMeshBuilder::add_node(int x, int y, int z)
{
/* Map coordinates to index space. */
const int index_x = (x/CUBE_SIZE) + pad_offset.x;
const int index_y = (y/CUBE_SIZE) + pad_offset.y;
const int index_z = (z/CUBE_SIZE) + pad_offset.z;
assert((index_x >= 0) && (index_y >= 0) && (index_z >= 0));
const size_t index = compute_voxel_index(res, index_x, index_y, index_z);
/* We already have a node here. */
if(grid[index] == 1) {
return;
}
++number_of_nodes;
grid[index] = 1;
}
void VolumeMeshBuilder::add_node_with_padding(int x, int y, int z)
{
for(int px = x - params->pad_size; px < x + params->pad_size; ++px) {
for(int py = y - params->pad_size; py < y + params->pad_size; ++py) {
for(int pz = z - params->pad_size; pz < z + params->pad_size; ++pz) {
add_node(px, py, pz);
}
}
}
}
void VolumeMeshBuilder::create_mesh(vector<float3> &vertices,
vector<int> &indices,
vector<float3> &face_normals)
{
/* We create vertices in index space (is), and only convert them to object
* space when done. */
vector<int3> vertices_is;
vector<QuadData> quads;
generate_vertices_and_quads(vertices_is, quads);
deduplicate_vertices(vertices_is, quads);
convert_object_space(vertices_is, vertices);
convert_quads_to_tris(quads, indices, face_normals);
}
void VolumeMeshBuilder::generate_vertices_and_quads(
vector<ccl::int3> &vertices_is,
vector<QuadData> &quads)
{
/* Overallocation, we could count the number of quads and vertices to create
* in a pre-pass if memory becomes an issue. */
vertices_is.reserve(number_of_nodes*8);
quads.reserve(number_of_nodes*6);
for(int z = 0; z < res.z; ++z) {
for(int y = 0; y < res.y; ++y) {
for(int x = 0; x < res.x; ++x) {
size_t voxel_index = compute_voxel_index(res, x, y, z);
if(grid[voxel_index] == 0) {
continue;
}
/* Compute min and max coords of the node in index space. */
int3 min = make_int3((x - pad_offset.x)*CUBE_SIZE,
(y - pad_offset.y)*CUBE_SIZE,
(z - pad_offset.z)*CUBE_SIZE);
/* Maximum is just CUBE_SIZE voxels away from minimum on each axis. */
int3 max = make_int3(min.x + CUBE_SIZE, min.y + CUBE_SIZE, min.z + CUBE_SIZE);
int3 corners[8] = {
make_int3(min[0], min[1], min[2]),
make_int3(max[0], min[1], min[2]),
make_int3(max[0], max[1], min[2]),
make_int3(min[0], max[1], min[2]),
make_int3(min[0], min[1], max[2]),
make_int3(max[0], min[1], max[2]),
make_int3(max[0], max[1], max[2]),
make_int3(min[0], max[1], max[2]),
};
/* Only create a quad if on the border between an active and
* an inactive node.
*/
voxel_index = compute_voxel_index(res, x - 1, y, z);
if(voxel_index == -1 || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, QUAD_X_MIN);
}
voxel_index = compute_voxel_index(res, x + 1, y, z);
if(voxel_index == -1 || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, QUAD_X_MAX);
}
voxel_index = compute_voxel_index(res, x, y - 1, z);
if(voxel_index == -1 || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, QUAD_Y_MIN);
}
voxel_index = compute_voxel_index(res, x, y + 1, z);
if(voxel_index == -1 || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, QUAD_Y_MAX);
}
voxel_index = compute_voxel_index(res, x, y, z - 1);
if(voxel_index == -1 || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, QUAD_Z_MIN);
}
voxel_index = compute_voxel_index(res, x, y, z + 1);
if(voxel_index == -1 || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, QUAD_Z_MAX);
}
}
}
}
}
void VolumeMeshBuilder::deduplicate_vertices(vector<int3> &vertices,
vector<QuadData> &quads)
{
vector<int3> sorted_vertices = vertices;
std::sort(sorted_vertices.begin(), sorted_vertices.end());
vector<int3>::iterator it = std::unique(sorted_vertices.begin(), sorted_vertices.end());
sorted_vertices.resize(std::distance(sorted_vertices.begin(), it));
vector<QuadData> new_quads = quads;
for(size_t i = 0; i < vertices.size(); ++i) {
for(size_t j = 0; j < sorted_vertices.size(); ++j) {
if(vertices[i] != sorted_vertices[j]) {
continue;
}
for(int k = 0; k < quads.size(); ++k) {
if(quads[k].v0 == i) {
new_quads[k].v0 = j;
}
else if(quads[k].v1 == i) {
new_quads[k].v1 = j;
}
else if(quads[k].v2 == i) {
new_quads[k].v2 = j;
}
else if(quads[k].v3 == i) {
new_quads[k].v3 = j;
}
}
break;
}
}
vertices = sorted_vertices;
quads = new_quads;
}
void VolumeMeshBuilder::convert_object_space(const vector<int3> &vertices,
vector<float3> &out_vertices)
{
out_vertices.reserve(vertices.size());
for(size_t i = 0; i < vertices.size(); ++i) {
float3 vertex = make_float3(vertices[i].x, vertices[i].y, vertices[i].z);
vertex *= params->cell_size;
vertex += params->start_point;
out_vertices.push_back(vertex);
}
}
void VolumeMeshBuilder::convert_quads_to_tris(const vector<QuadData> &quads,
vector<int> &tris,
vector<float3> &face_normals)
{
int index_offset = 0;
tris.resize(quads.size()*6);
face_normals.reserve(quads.size()*2);
for(size_t i = 0; i < quads.size(); ++i) {
tris[index_offset++] = quads[i].v0;
tris[index_offset++] = quads[i].v2;
tris[index_offset++] = quads[i].v1;
face_normals.push_back(quads[i].normal);
tris[index_offset++] = quads[i].v0;
tris[index_offset++] = quads[i].v3;
tris[index_offset++] = quads[i].v2;
face_normals.push_back(quads[i].normal);
}
}
/* ************************************************************************** */
/* For debugging: render the created mesh using the default diffuse shader. */
//#define RENDER_DIFFUSE
struct VoxelAttributeGrid {
float *data;
int channels;
};
void MeshManager::create_volume_mesh(Scene *scene,
Mesh *mesh,
Progress& progress)
{
string msg = string_printf("Computing Volume Mesh %s", mesh->name.c_str());
progress.set_status("Updating Mesh", msg);
vector<VoxelAttributeGrid> voxel_grids;
/* Compute volume parameters. */
VolumeParams volume_params;
volume_params.resolution = make_int3(0, 0, 0);
foreach(Attribute& attr, mesh->attributes.attributes) {
if(attr.element != ATTR_ELEMENT_VOXEL) {
continue;
}
VoxelAttribute *voxel = attr.data_voxel();
device_memory *image_memory = scene->image_manager->image_memory(voxel->slot);
int3 resolution = make_int3(image_memory->data_width,
image_memory->data_height,
image_memory->data_depth);
if(volume_params.resolution == make_int3(0, 0, 0)) {
volume_params.resolution = resolution;
}
else if(volume_params.resolution != resolution) {
VLOG(1) << "Can't create volume mesh, all voxel grid resolutions must be equal\n";
return;
}
VoxelAttributeGrid voxel_grid;
voxel_grid.data = static_cast<float*>(image_memory->host_pointer);
voxel_grid.channels = image_memory->data_elements;
voxel_grids.push_back(voxel_grid);
}
if(voxel_grids.empty()) {
return;
}
int pad_size = 0;
foreach(Shader *shader, mesh->used_shaders) {
if(!shader->has_volume) {
continue;
}
if(shader->volume_interpolation_method == VOLUME_INTERPOLATION_LINEAR) {
pad_size = max(1, pad_size);
}
else if(shader->volume_interpolation_method == VOLUME_INTERPOLATION_CUBIC) {
pad_size = max(2, pad_size);
}
}
/* Compute start point and cell size from transform. */
Attribute *attr = mesh->attributes.find(ATTR_STD_GENERATED_TRANSFORM);
const int3 resolution = volume_params.resolution;
float3 start_point = make_float3(0.0f, 0.0f, 0.0f);
float3 cell_size = make_float3(1.0f/resolution.x,
1.0f/resolution.y,
1.0f/resolution.z);
if(attr) {
const Transform *tfm = attr->data_transform();
const Transform itfm = transform_inverse(*tfm);
start_point = transform_point(&itfm, start_point);
cell_size = transform_direction(&itfm, cell_size);
}
volume_params.start_point = start_point;
volume_params.cell_size = cell_size;
volume_params.pad_size = pad_size;
VolumeMeshBuilder builder(&volume_params);
const float isovalue = mesh->volume_isovalue;
for(int z = 0; z < resolution.z; ++z) {
for(int y = 0; y < resolution.y; ++y) {
for(int x = 0; x < resolution.x; ++x) {
size_t voxel_index = compute_voxel_index(resolution, x, y, z);
for(size_t i = 0; i < voxel_grids.size(); ++i) {
const VoxelAttributeGrid &voxel_grid = voxel_grids[i];
if(voxel_grid.channels == 1) {
if(voxel_grid.data[voxel_index] >= isovalue) {
builder.add_node_with_padding(x, y, z);
break;
}
}
else if(voxel_grid.channels == 3) {
voxel_index = compute_voxel_index(resolution, x*3, y, z);
if(voxel_grid.data[voxel_index] >= isovalue) {
builder.add_node_with_padding(x, y, z);
break;
}
if(voxel_grid.data[voxel_index + 1] >= isovalue) {
builder.add_node_with_padding(x, y, z);
break;
}
if(voxel_grid.data[voxel_index + 2] >= isovalue) {
builder.add_node_with_padding(x, y, z);
break;
}
}
else if(voxel_grid.channels == 4) {
voxel_index = compute_voxel_index(resolution, x*4, y, z);
/* check alpha first */
if(voxel_grid.data[voxel_index + 3] < isovalue) {
continue;
}
if(voxel_grid.data[voxel_index] >= isovalue) {
builder.add_node_with_padding(x, y, z);
continue;
}
if(voxel_grid.data[voxel_index + 1] >= isovalue) {
builder.add_node_with_padding(x, y, z);
continue;
}
if(voxel_grid.data[voxel_index + 2] >= isovalue) {
builder.add_node_with_padding(x, y, z);
continue;
}
}
}
}
}
}
vector<float3> vertices;
vector<int> indices;
vector<float3> face_normals;
builder.create_mesh(vertices, indices, face_normals);
#ifdef RENDER_DIFFUSE
int shader = mesh->used_shaders[0]->id;
#else
int shader = mesh->shader[0];
#endif
mesh->clear(true);
mesh->reserve_mesh(vertices.size(), indices.size()/3);
for(size_t i = 0; i < vertices.size(); ++i) {
mesh->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], shader, false);
}
Attribute *attr_fN = mesh->attributes.add(ATTR_STD_FACE_NORMAL);
float3 *fN = attr_fN->data_float3();
for(size_t i = 0; i < face_normals.size(); ++i) {
fN[i] = face_normals[i];
}
VLOG(1) << "Memory usage volume mesh: "
<< ((vertices.size() + face_normals.size())*sizeof(float3) + indices.size()*sizeof(int))/(1024.0*1024.0)
<< "Mb.";
VLOG(1) << "Memory usage volume grid: "
<< (resolution.x*resolution.y*resolution.z*sizeof(float))/(1024.0*1024.0)
<< "Mb.";
}
CCL_NAMESPACE_END

View File

@ -204,8 +204,7 @@ void Scene::device_update(Device *device_, Progress& progress)
if(progress.get_cancel() || device->have_error()) return;
progress.set_status("Updating Meshes Flags");
mesh_manager->device_update_flags(device, &dscene, this, progress);
mesh_manager->device_update_preprocess(device, this, progress);
if(progress.get_cancel() || device->have_error()) return;

View File

@ -78,6 +78,22 @@ ccl_device_inline int3 clamp(const int3& a, int3& mn, int mx)
}
#endif /* !__KERNEL_OPENCL__ */
ccl_device_inline bool operator==(const int3 &a, const int3 &b)
{
return a.x == b.x && a.y == b.y && a.z == b.z;
}
ccl_device_inline bool operator!=(const int3 &a, const int3 &b)
{
return !(a == b);
}
ccl_device_inline bool operator<(const int3 &a, const int3 &b)
{
return a.x < b.x && a.y < b.y && a.z < b.z;
}
CCL_NAMESPACE_END
#endif /* __UTIL_MATH_INT3_H__ */