Changeset View
Changeset View
Standalone View
Standalone View
intern/cycles/render/volume.cpp
| Show First 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| struct QuadData { | struct QuadData { | ||||
| int v0, v1, v2, v3; | int v0, v1, v2, v3; | ||||
| float3 normal; | 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 int add_vertex(int3 v, | |||||
| vector<int3> &vertices, | |||||
| int3 res, | |||||
| unordered_map<size_t, int> &used_verts) | |||||
| { | |||||
| size_t vert_key = v.x + v.y * (res.x + 1) + v.z * (res.x + 1) * (res.y + 1); | |||||
| unordered_map<size_t, int>::iterator it = used_verts.find(vert_key); | |||||
| if (it != used_verts.end()) { | |||||
| return it->second; | |||||
| } | |||||
| int vertex_offset = vertices.size(); | |||||
| used_verts[vert_key] = vertex_offset; | |||||
| vertices.push_back(v); | |||||
| return vertex_offset; | |||||
| } | |||||
| static void create_quad(int3 corners[8], | |||||
| vector<int3> &vertices, | |||||
| vector<QuadData> &quads, | |||||
| int3 res, | |||||
| unordered_map<size_t, int> &used_verts, | |||||
| int face_index) | |||||
| { | |||||
| QuadData quad; | |||||
| quad.v0 = add_vertex(corners[quads_indices[face_index][0]], vertices, res, used_verts); | |||||
| quad.v1 = add_vertex(corners[quads_indices[face_index][1]], vertices, res, used_verts); | |||||
| quad.v2 = add_vertex(corners[quads_indices[face_index][2]], vertices, res, used_verts); | |||||
| quad.v3 = add_vertex(corners[quads_indices[face_index][3]], vertices, res, used_verts); | |||||
| quad.normal = quads_normals[face_index]; | |||||
| quads.push_back(quad); | |||||
| } | |||||
| /* Create a mesh from a volume. | /* Create a mesh from a volume. | ||||
| * | * | ||||
| * The way the algorithm works is as follows: | * The way the algorithm works is as follows: | ||||
| * | * | ||||
| * - The topologies of input OpenVDB grids are merged into a temporary grid. | * - The topologies of input OpenVDB grids are merged into a temporary grid. | ||||
| * - Voxels of the temporary grid are dilated to account for the padding necessary for volume | * - Voxels of the temporary grid are dilated to account for the padding necessary for volume | ||||
| * sampling. | * sampling. | ||||
| * - Quads are created on the boundary between active and inactive leaf nodes of the temporary | * - Quads are created on the boundary between active and inactive leaf nodes of the temporary | ||||
| ▲ Show 20 Lines • Show All 139 Lines • ▼ Show 20 Lines | void VolumeMeshBuilder::create_mesh(vector<float3> &vertices, | ||||
| generate_vertices_and_quads(vertices_is, quads); | generate_vertices_and_quads(vertices_is, quads); | ||||
| convert_object_space(vertices_is, vertices, face_overlap_avoidance); | convert_object_space(vertices_is, vertices, face_overlap_avoidance); | ||||
| convert_quads_to_tris(quads, indices, face_normals); | convert_quads_to_tris(quads, indices, face_normals); | ||||
| } | } | ||||
| void VolumeMeshBuilder::generate_vertices_and_quads(vector<ccl::int3> &vertices_is, | |||||
| vector<QuadData> &quads) | |||||
| { | |||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| const openvdb::MaskGrid::TreeType &tree = topology_grid->tree(); | struct CreateLeafCubes { | ||||
| tree.evalLeafBoundingBox(bbox); | /* Descend to leaf nodes, but no further. */ | ||||
| CreateLeafCubes(const openvdb::MaskGrid::TreeType tree, | |||||
JacquesLucke: Are copying the tree on purpose? | |||||
| vector<ccl::int3> &vertices_is, | |||||
| vector<QuadData> &quads, | |||||
| openvdb::CoordBBox bbox) | |||||
| : tree(tree), vertices_is(vertices_is), quads(quads) | |||||
| { | |||||
| resolution = make_int3(bbox.dim().x(), bbox.dim().y(), bbox.dim().z()); | |||||
| } | |||||
| template<openvdb::Index LEVEL> inline bool descent() | |||||
JacquesLuckeUnsubmitted Not Done Inline Actions"A function defined entirely inside a class/struct/union definition, whether it's a member function or a non-member friend function, is implicitly an inline function." JacquesLucke: "A function defined entirely inside a class/struct/union definition, whether it's a member… | |||||
| { | |||||
| return LEVEL > 0; | |||||
| } | |||||
Not Done Inline ActionsThis function does not seem to be used. JacquesLucke: This function does not seem to be used. | |||||
| /* Iterate over leaf nodes. In case of intermediate nodes that contain | |||||
| * a constant value, iterate over bounding boxes of what would have been | |||||
| * the leaf nodes if they existed, for a consistent mesh topology with | |||||
| * leaf nodes. */ | |||||
| template<openvdb::Index LEVEL> inline void operator()(const openvdb::CoordBBox &bbox) | |||||
| { | |||||
| if (LEVEL > 0) { | |||||
| /* Active tile. */ | |||||
| openvdb::Coord dim = bbox.dim(); | |||||
| assert(dim.x() % LEAF_DIM == 0 && dim.y() % LEAF_DIM == 0 && dim.z() % LEAF_DIM == 0); | |||||
| for (int x = 0; x < dim.x(); x += LEAF_DIM) { | |||||
| for (int y = 0; y < dim.y(); y += LEAF_DIM) { | |||||
| for (int z = 0; z < dim.z(); z += LEAF_DIM) { | |||||
| openvdb::CoordBBox leaf_bbox = openvdb::CoordBBox::createCube( | |||||
| bbox.min() + openvdb::Coord(x, y, z), LEAF_DIM); | |||||
| create_leaf(leaf_bbox); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* Leaf node. */ | |||||
| create_leaf(bbox); | |||||
| } | |||||
| } | |||||
| const int3 resolution = make_int3(bbox.dim().x(), bbox.dim().y(), bbox.dim().z()); | protected: | ||||
| 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), | |||||
| }; | |||||
| const int LEAF_DIM = openvdb::MaskGrid::TreeType::LeafNodeType::DIM; | |||||
| const openvdb::MaskGrid::TreeType tree; | |||||
| vector<ccl::int3> &vertices_is; | |||||
| vector<QuadData> &quads; | |||||
| unordered_map<size_t, int> used_verts; | unordered_map<size_t, int> used_verts; | ||||
| int3 resolution; | |||||
| for (auto iter = tree.cbeginLeaf(); iter; ++iter) { | bool probe_leaf(const openvdb::Coord coord) | ||||
| openvdb::CoordBBox leaf_bbox = iter->getNodeBoundingBox(); | { | ||||
| /* +1 to convert from exclusive to include bounds. */ | /* Probe if there is a leaf node or active tile at the coordinate. */ | ||||
| leaf_bbox.max() = leaf_bbox.max().offsetBy(1); | return tree.probeLeaf(coord) || tree.isValueOn(coord); | ||||
| } | |||||
| int3 min = make_int3(leaf_bbox.min().x(), leaf_bbox.min().y(), leaf_bbox.min().z()); | void create_leaf(const openvdb::CoordBBox &bbox) | ||||
| int3 max = make_int3(leaf_bbox.max().x(), leaf_bbox.max().y(), leaf_bbox.max().z()); | { | ||||
| /* +1 to convert from exclusive to inclusive bounds. */ | |||||
| int3 min = make_int3(bbox.min().x(), bbox.min().y(), bbox.min().z()); | |||||
| int3 max = make_int3(bbox.max().x() + 1, bbox.max().y() + 1, bbox.max().z() + 1); | |||||
| int3 corners[8] = { | int3 corners[8] = { | ||||
| make_int3(min[0], min[1], min[2]), | make_int3(min[0], min[1], min[2]), | ||||
| make_int3(max[0], min[1], min[2]), | make_int3(max[0], min[1], min[2]), | ||||
| make_int3(max[0], max[1], min[2]), | make_int3(max[0], max[1], min[2]), | ||||
| make_int3(min[0], max[1], min[2]), | make_int3(min[0], max[1], min[2]), | ||||
| make_int3(min[0], min[1], max[2]), | make_int3(min[0], min[1], max[2]), | ||||
| make_int3(max[0], min[1], max[2]), | make_int3(max[0], min[1], max[2]), | ||||
| make_int3(max[0], max[1], max[2]), | make_int3(max[0], max[1], max[2]), | ||||
| make_int3(min[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 leaf. | /* Only create a quad if on the border between an active and an inactive leaf. | ||||
| * | * | ||||
| * We verify that a leaf exists by probing a coordinate that is at its center, | * We verify that a leaf exists by probing a coordinate that is at its center, | ||||
| * to do so we compute the center of the current leaf and offset this coordinate | * to do so we compute the center of the current leaf and offset this coordinate | ||||
| * by the size of a leaf in each direction. | * by the size of a leaf in each direction. | ||||
| */ | */ | ||||
| static const int LEAF_DIM = openvdb::MaskGrid::TreeType::LeafNodeType::DIM; | const openvdb::Coord center = bbox.min() + openvdb::Coord(LEAF_DIM / 2); | ||||
| auto center = leaf_bbox.min() + openvdb::Coord(LEAF_DIM / 2); | |||||
| if (!tree.probeLeaf(openvdb::Coord(center.x() - LEAF_DIM, center.y(), center.z()))) { | if (!probe_leaf(openvdb::Coord(center.x() - LEAF_DIM, center.y(), center.z()))) { | ||||
| create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_X_MIN); | create_quad(corners, QUAD_X_MIN); | ||||
| } | } | ||||
| if (!tree.probeLeaf(openvdb::Coord(center.x() + LEAF_DIM, center.y(), center.z()))) { | if (!probe_leaf(openvdb::Coord(center.x() + LEAF_DIM, center.y(), center.z()))) { | ||||
| create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_X_MAX); | create_quad(corners, QUAD_X_MAX); | ||||
| } | } | ||||
| if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y() - LEAF_DIM, center.z()))) { | if (!probe_leaf(openvdb::Coord(center.x(), center.y() - LEAF_DIM, center.z()))) { | ||||
| create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Y_MIN); | create_quad(corners, QUAD_Y_MIN); | ||||
| } | } | ||||
| if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y() + LEAF_DIM, center.z()))) { | if (!probe_leaf(openvdb::Coord(center.x(), center.y() + LEAF_DIM, center.z()))) { | ||||
| create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Y_MAX); | create_quad(corners, QUAD_Y_MAX); | ||||
| } | } | ||||
| if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y(), center.z() - LEAF_DIM))) { | if (!probe_leaf(openvdb::Coord(center.x(), center.y(), center.z() - LEAF_DIM))) { | ||||
| create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Z_MIN); | create_quad(corners, QUAD_Z_MIN); | ||||
| } | } | ||||
| if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y(), center.z() + LEAF_DIM))) { | if (!probe_leaf(openvdb::Coord(center.x(), center.y(), center.z() + LEAF_DIM))) { | ||||
| create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Z_MAX); | create_quad(corners, QUAD_Z_MAX); | ||||
| } | } | ||||
| } | } | ||||
| int create_vertex(const int3 v) | |||||
| { | |||||
| const size_t vert_key = v.x + v.y * (resolution.x + 1) + | |||||
| v.z * (resolution.x + 1) * (resolution.y + 1); | |||||
| unordered_map<size_t, int>::iterator it = used_verts.find(vert_key); | |||||
| if (it != used_verts.end()) { | |||||
| return it->second; | |||||
| } | |||||
| const int vertex_offset = vertices_is.size(); | |||||
| used_verts[vert_key] = vertex_offset; | |||||
| vertices_is.push_back(v); | |||||
| return vertex_offset; | |||||
| } | |||||
| void create_quad(const int3 corners[8], const int face_index) | |||||
| { | |||||
| QuadData quad; | |||||
| quad.v0 = create_vertex(corners[quads_indices[face_index][0]]); | |||||
| quad.v1 = create_vertex(corners[quads_indices[face_index][1]]); | |||||
| quad.v2 = create_vertex(corners[quads_indices[face_index][2]]); | |||||
| quad.v3 = create_vertex(corners[quads_indices[face_index][3]]); | |||||
| quad.normal = quads_normals[face_index]; | |||||
| quads.push_back(quad); | |||||
| } | |||||
| }; | |||||
| #endif | |||||
| void VolumeMeshBuilder::generate_vertices_and_quads(vector<ccl::int3> &vertices_is, | |||||
| vector<QuadData> &quads) | |||||
| { | |||||
| #ifdef WITH_OPENVDB | |||||
| const openvdb::MaskGrid::TreeType &tree = topology_grid->tree(); | |||||
| tree.evalLeafBoundingBox(bbox); | |||||
| CreateLeafCubes op(tree, vertices_is, quads, bbox); | |||||
| tree.visitActiveBBox(op); | |||||
| #else | #else | ||||
| (void)vertices_is; | (void)vertices_is; | ||||
| (void)quads; | (void)quads; | ||||
| #endif | #endif | ||||
| } | } | ||||
| void VolumeMeshBuilder::convert_object_space(const vector<int3> &vertices, | void VolumeMeshBuilder::convert_object_space(const vector<int3> &vertices, | ||||
| vector<float3> &out_vertices, | vector<float3> &out_vertices, | ||||
| ▲ Show 20 Lines • Show All 255 Lines • Show Last 20 Lines | |||||
Are copying the tree on purpose?