Merge branch 'master' into blender2.8
This commit is contained in:
commit
1852e702ab
|
@ -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;
|
||||
|
|
|
@ -23,6 +23,7 @@ set(SRC
|
|||
mesh.cpp
|
||||
mesh_displace.cpp
|
||||
mesh_subdivision.cpp
|
||||
mesh_volume.cpp
|
||||
nodes.cpp
|
||||
object.cpp
|
||||
osl.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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
Loading…
Reference in New Issue