Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--intern/cycles/blender/blender_mesh.cpp2
-rw-r--r--intern/cycles/render/CMakeLists.txt1
-rw-r--r--intern/cycles/render/attribute.cpp18
-rw-r--r--intern/cycles/render/attribute.h2
-rw-r--r--intern/cycles/render/image.cpp10
-rw-r--r--intern/cycles/render/image.h2
-rw-r--r--intern/cycles/render/mesh.cpp87
-rw-r--r--intern/cycles/render/mesh.h13
-rw-r--r--intern/cycles/render/mesh_volume.cpp581
-rw-r--r--intern/cycles/render/scene.cpp3
-rw-r--r--intern/cycles/util/util_math_int3.h16
11 files changed, 718 insertions, 17 deletions
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
index 42ceb4d5c8e..998390e92a9 100644
--- a/intern/cycles/blender/blender_mesh.cpp
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -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;
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index 580d76e7777..b7248354abd 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -23,6 +23,7 @@ set(SRC
mesh.cpp
mesh_displace.cpp
mesh_subdivision.cpp
+ mesh_volume.cpp
nodes.cpp
object.cpp
osl.cpp
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index 6816f8ca3f3..8c77687d9cc 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -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 */
diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h
index 9e23345675d..5cb6c75aab2 100644
--- a/intern/cycles/render/attribute.h
+++ b/intern/cycles/render/attribute.h
@@ -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
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index de46599e1db..9c5e32e8219 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -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)
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index 989416e089a..5391490d993 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -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
diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp
index 5bcb47deb65..23b855acdc9 100644
--- a/intern/cycles/render/mesh.cpp
+++ b/intern/cycles/render/mesh.cpp
@@ -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)
diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h
index 07d8bbdbf31..c0d1513bee0 100644
--- a/intern/cycles/render/mesh.h
+++ b/intern/cycles/render/mesh.h
@@ -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
diff --git a/intern/cycles/render/mesh_volume.cpp b/intern/cycles/render/mesh_volume.cpp
new file mode 100644
index 00000000000..be43154fd93
--- /dev/null
+++ b/intern/cycles/render/mesh_volume.cpp
@@ -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
diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp
index e34601a36a9..24923b650e3 100644
--- a/intern/cycles/render/scene.cpp
+++ b/intern/cycles/render/scene.cpp
@@ -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;
diff --git a/intern/cycles/util/util_math_int3.h b/intern/cycles/util/util_math_int3.h
index fa7a02636de..1bc6ca7f376 100644
--- a/intern/cycles/util/util_math_int3.h
+++ b/intern/cycles/util/util_math_int3.h
@@ -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__ */