Initial Grease Pencil 3.0 stage #106848

Merged
Falk David merged 224 commits from filedescriptor/blender:grease-pencil-v3 into main 2023-05-30 11:14:22 +02:00
105 changed files with 2830 additions and 810 deletions
Showing only changes of commit 7a604627cf - Show all commits

View File

@ -322,6 +322,11 @@ def external_script_initialize_if_needed(args: argparse.Namespace,
blender_url = make_utils.git_get_remote_url(args.git_command, origin_name)
external_url = resolve_external_url(blender_url, repo_name)
# When running `make update` from a freshly cloned fork check whether the fork of the submodule is
# available, If not, switch to the submodule relative to the main blender repository.
if origin_name == "origin" and not make_utils.git_is_remote_repository(args.git_command, external_url):
external_url = resolve_external_url("https://projects.blender.org/blender/blender", repo_name)
call((args.git_command, "clone", "--origin", origin_name, external_url, str(external_dir)))

View File

@ -536,12 +536,11 @@ void CUDADevice::free_host(void *shared_pointer)
cuMemFreeHost(shared_pointer);
}
bool CUDADevice::transform_host_pointer(void *&device_pointer, void *&shared_pointer)
void CUDADevice::transform_host_pointer(void *&device_pointer, void *&shared_pointer)
{
CUDAContextScope scope(this);
cuda_assert(cuMemHostGetDevicePointer_v2((CUdeviceptr *)&device_pointer, shared_pointer, 0));
return true;
}
void CUDADevice::copy_host_to_device(void *device_pointer, void *host_pointer, size_t size)

View File

@ -68,7 +68,7 @@ class CUDADevice : public GPUDevice {
virtual void free_device(void *device_pointer) override;
virtual bool alloc_host(void *&shared_pointer, size_t size) override;
virtual void free_host(void *shared_pointer) override;
virtual bool transform_host_pointer(void *&device_pointer, void *&shared_pointer) override;
virtual void transform_host_pointer(void *&device_pointer, void *&shared_pointer) override;
virtual void copy_host_to_device(void *device_pointer, void *host_pointer, size_t size) override;
void mem_alloc(device_memory &mem) override;

View File

@ -648,7 +648,7 @@ GPUDevice::Mem *GPUDevice::generic_alloc(device_memory &mem, size_t pitch_paddin
}
if (mem_alloc_result) {
assert(transform_host_pointer(device_pointer, shared_pointer));
transform_host_pointer(device_pointer, shared_pointer);
map_host_used += size;
status = " in host memory";
}

View File

@ -391,7 +391,7 @@ class GPUDevice : public Device {
/* This function should return device pointer corresponding to shared pointer, which
* is host buffer, allocated in `alloc_host`. The function should `true`, if such
* address transformation is possible and `false` otherwise. */
virtual bool transform_host_pointer(void *&device_pointer, void *&shared_pointer) = 0;
virtual void transform_host_pointer(void *&device_pointer, void *&shared_pointer) = 0;
virtual void copy_host_to_device(void *device_pointer, void *host_pointer, size_t size) = 0;
};

View File

@ -499,12 +499,11 @@ void HIPDevice::free_host(void *shared_pointer)
hipHostFree(shared_pointer);
}
bool HIPDevice::transform_host_pointer(void *&device_pointer, void *&shared_pointer)
void HIPDevice::transform_host_pointer(void *&device_pointer, void *&shared_pointer)
{
HIPContextScope scope(this);
hip_assert(hipHostGetDevicePointer((hipDeviceptr_t *)&device_pointer, shared_pointer, 0));
return true;
}
void HIPDevice::copy_host_to_device(void *device_pointer, void *host_pointer, size_t size)

View File

@ -61,7 +61,7 @@ class HIPDevice : public GPUDevice {
virtual void free_device(void *device_pointer) override;
virtual bool alloc_host(void *&shared_pointer, size_t size) override;
virtual void free_host(void *shared_pointer) override;
virtual bool transform_host_pointer(void *&device_pointer, void *&shared_pointer) override;
virtual void transform_host_pointer(void *&device_pointer, void *&shared_pointer) override;
virtual void copy_host_to_device(void *device_pointer, void *host_pointer, size_t size) override;
void mem_alloc(device_memory &mem) override;

View File

@ -1072,7 +1072,7 @@ void RenderScheduler::update_start_resolution_divider()
}
/* Calculate the maximum resolution divider possible while keeping the long axis of the viewport
* above our prefered minimum axis size (128) */
* above our preferred minimum axis size (128). */
const int long_viewport_axis = max(buffer_params_.width, buffer_params_.height);
const int max_res_divider_for_desired_size = long_viewport_axis / 128;

View File

@ -741,21 +741,21 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
endif()
# SYCL_CPP_FLAGS is a variable that the user can set to pass extra compiler options
set(sycl_compiler_flags
${CMAKE_CURRENT_SOURCE_DIR}/${SRC_KERNEL_DEVICE_ONEAPI}
-fsycl
-fsycl-unnamed-lambda
-fdelayed-template-parsing
-mllvm -inlinedefault-threshold=250
-mllvm -inlinehint-threshold=350
-fsycl-device-code-split=per_kernel
-fsycl-max-parallel-link-jobs=${SYCL_OFFLINE_COMPILER_PARALLEL_JOBS}
-shared
-DWITH_ONEAPI
-ffast-math
-O2
-o"${cycles_kernel_oneapi_lib}"
-I"${CMAKE_CURRENT_SOURCE_DIR}/.."
${SYCL_CPP_FLAGS}
${CMAKE_CURRENT_SOURCE_DIR}/${SRC_KERNEL_DEVICE_ONEAPI}
-fsycl
-fsycl-unnamed-lambda
-fdelayed-template-parsing
-mllvm -inlinedefault-threshold=250
-mllvm -inlinehint-threshold=350
-fsycl-device-code-split=per_kernel
-fsycl-max-parallel-link-jobs=${SYCL_OFFLINE_COMPILER_PARALLEL_JOBS}
-shared
-DWITH_ONEAPI
-ffast-math
-O2
-o"${cycles_kernel_oneapi_lib}"
-I"${CMAKE_CURRENT_SOURCE_DIR}/.."
${SYCL_CPP_FLAGS}
)
if(WITH_CYCLES_ONEAPI_HOST_TASK_EXECUTION)

View File

@ -205,8 +205,8 @@ LightTree::LightTree(vector<LightTreePrimitive> &prims,
}
max_lights_in_leaf_ = max_lights_in_leaf;
int num_prims = prims.size();
int num_local_lights = num_prims - num_distant_lights;
const int num_prims = prims.size();
const int num_local_lights = num_prims - num_distant_lights;
/* The amount of nodes is estimated to be twice the amount of primitives */
nodes_.reserve(2 * num_prims);
@ -240,8 +240,8 @@ int LightTree::recursive_build(
OrientationBounds bcone = OrientationBounds::empty;
BoundBox centroid_bounds = BoundBox::empty;
float energy_total = 0.0;
int num_prims = end - start;
int current_index = nodes_.size();
const int num_prims = end - start;
for (int i = start; i < end; i++) {
const LightTreePrimitive &prim = prims.at(i);
@ -254,13 +254,13 @@ int LightTree::recursive_build(
nodes_.emplace_back(bbox, bcone, energy_total, bit_trail);
bool try_splitting = num_prims > 1 && len(centroid_bounds.size()) > 0.0f;
const bool try_splitting = num_prims > 1 && len(centroid_bounds.size()) > 0.0f;
int split_dim = -1, split_bucket = 0, num_left_prims = 0;
bool should_split = false;
if (try_splitting) {
/* Find the best place to split the primitives into 2 nodes.
* If the best split cost is no better than making a leaf node, make a leaf instead. */
float min_cost = min_split_saoh(
const float min_cost = min_split_saoh(
centroid_bounds, start, end, bbox, bcone, split_dim, split_bucket, num_left_prims, prims);
should_split = num_prims > max_lights_in_leaf_ || min_cost < energy_total;
}
@ -295,8 +295,8 @@ int LightTree::recursive_build(
}
float LightTree::min_split_saoh(const BoundBox &centroid_bbox,
int start,
int end,
const int start,
const int end,
const BoundBox &bbox,
const OrientationBounds &bcone,
int &split_dim,
@ -329,7 +329,7 @@ float LightTree::min_split_saoh(const BoundBox &centroid_bbox,
const float inv_extent = 1 / (centroid_bbox.size()[dim]);
/* Fill in buckets with primitives. */
vector<LightTreeBucketInfo> buckets(LightTreeBucketInfo::num_buckets);
std::array<LightTreeBucketInfo, LightTreeBucketInfo::num_buckets> buckets;
for (int i = start; i < end; i++) {
const LightTreePrimitive &prim = prims[i];
@ -348,7 +348,7 @@ float LightTree::min_split_saoh(const BoundBox &centroid_bbox,
}
/* Calculate the cost of splitting at each point between partitions. */
vector<float> bucket_costs(LightTreeBucketInfo::num_buckets - 1);
std::array<float, LightTreeBucketInfo::num_buckets - 1> bucket_costs;
float energy_L, energy_R;
BoundBox bbox_L, bbox_R;
OrientationBounds bcone_L, bcone_R;
@ -379,9 +379,10 @@ float LightTree::min_split_saoh(const BoundBox &centroid_bbox,
/* Calculate the cost of splitting using the heuristic as described in the paper. */
const float area_L = has_area ? bbox_L.area() : len(bbox_L.size());
const float area_R = has_area ? bbox_R.area() : len(bbox_R.size());
float left = (bbox_L.valid()) ? energy_L * area_L * bcone_L.calculate_measure() : 0.0f;
float right = (bbox_R.valid()) ? energy_R * area_R * bcone_R.calculate_measure() : 0.0f;
float regularization = max_extent * inv_extent;
const float left = (bbox_L.valid()) ? energy_L * area_L * bcone_L.calculate_measure() : 0.0f;
const float right = (bbox_R.valid()) ? energy_R * area_R * bcone_R.calculate_measure() :
0.0f;
const float regularization = max_extent * inv_extent;
bucket_costs[split] = regularization * (left + right) * inv_total_cost;
if (bucket_costs[split] < min_cost) {

View File

@ -389,6 +389,8 @@ class NODE_MT_geometry_node_GEO_MESH_OPERATIONS(Menu):
node_add_menu.add_node_type(layout, "GeometryNodeMeshBoolean")
node_add_menu.add_node_type(layout, "GeometryNodeMeshToCurve")
node_add_menu.add_node_type(layout, "GeometryNodeMeshToPoints")
if _context.preferences.experimental.use_new_volume_nodes:
node_add_menu.add_node_type(layout, "GeometryNodeMeshToSDFVolume")
node_add_menu.add_node_type(layout, "GeometryNodeMeshToVolume")
node_add_menu.add_node_type(layout, "GeometryNodeScaleElements")
node_add_menu.add_node_type(layout, "GeometryNodeSplitEdges")
@ -453,6 +455,8 @@ class NODE_MT_category_GEO_POINT(Menu):
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodePoints")
node_add_menu.add_node_type(layout, "GeometryNodePointsToVertices")
if _context.preferences.experimental.use_new_volume_nodes:
node_add_menu.add_node_type(layout, "GeometryNodePointsToSDFVolume")
node_add_menu.add_node_type(layout, "GeometryNodePointsToVolume")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeSetPointRadius")
@ -593,6 +597,11 @@ class NODE_MT_category_GEO_VOLUME(Menu):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeVolumeCube")
node_add_menu.add_node_type(layout, "GeometryNodeVolumeToMesh")
if _context.preferences.experimental.use_new_volume_nodes:
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodeMeanFilterSDFVolume")
node_add_menu.add_node_type(layout, "GeometryNodeOffsetSDFVolume")
node_add_menu.add_node_type(layout, "GeometryNodeSDFVolumeSphere")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)

View File

@ -29,7 +29,7 @@
* It's mostly used for modifiers, and has the advantages of not taking much
* resources.
*
* BMesh is a full-on brep, used for editmode, some modifiers, etc. It's much
* BMesh is a full-on BREP, used for edit-mode, some modifiers, etc. It's much
* more capable (if memory-intensive) then CDDM.
*
* DerivedMesh is somewhat hackish. Many places assumes that a DerivedMesh is

View File

@ -1555,6 +1555,11 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define GEO_NODE_IMAGE 1191
#define GEO_NODE_INTERPOLATE_CURVES 1192
#define GEO_NODE_EDGES_TO_FACE_GROUPS 1193
#define GEO_NODE_POINTS_TO_SDF_VOLUME 1194
#define GEO_NODE_MESH_TO_SDF_VOLUME 1195
#define GEO_NODE_SDF_VOLUME_SPHERE 1196
#define GEO_NODE_MEAN_FILTER_SDF_VOLUME 1197
#define GEO_NODE_OFFSET_SDF_VOLUME 1198
/** \} */

View File

@ -151,13 +151,6 @@ class bNodeTreeRuntime : NonCopyable, NonMovable {
Vector<bNode *> root_frames;
Vector<bNodeSocket *> interface_inputs;
Vector<bNodeSocket *> interface_outputs;
/**
* The location of all sockets in the tree, calculated while drawing the nodes.
* Indexed with #bNodeSocket::index_in_tree(). In the node tree's "world space"
* (the same as #bNode::runtime::totr).
*/
Vector<float2> all_socket_locations;
};
/**
@ -183,6 +176,13 @@ class bNodeSocketRuntime : NonCopyable, NonMovable {
*/
short total_inputs = 0;
/**
* The location of the socket in the tree, calculated while drawing the nodes and invalid if the
* node tree hasn't been drawn yet. In the node tree's "world space" (the same as
* #bNode::runtime::totr).
*/
float2 location;
/** Only valid when #topology_cache_is_dirty is false. */
Vector<bNodeLink *> directly_linked_links;
Vector<bNodeSocket *> directly_linked_sockets;

View File

@ -76,6 +76,7 @@ VolumeGrid *BKE_volume_grid_get_for_write(struct Volume *volume, int grid_index)
const VolumeGrid *BKE_volume_grid_active_get_for_read(const struct Volume *volume);
/* Tries to find a grid with the given name. Make sure that the volume has been loaded. */
const VolumeGrid *BKE_volume_grid_find_for_read(const struct Volume *volume, const char *name);
VolumeGrid *BKE_volume_grid_find_for_write(struct Volume *volume, const char *name);
/* Tries to set the name of the velocity field. If no such grid exists with the given base name,
* this will try common post-fixes in order to detect velocity fields split into multiple grids.

View File

@ -370,6 +370,26 @@ CustomDataLayer *BKE_id_attribute_duplicate(ID *id, const char *name, ReportList
return BKE_id_attribute_search(id, uniquename, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
}
static int color_name_to_index(ID *id, const char *name)
{
const CustomDataLayer *layer = BKE_id_attribute_search(
id, name, CD_MASK_COLOR_ALL, ATTR_DOMAIN_MASK_COLOR);
return BKE_id_attribute_to_index(id, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
}
static int color_clamp_index(ID *id, int index)
{
const int length = BKE_id_attributes_length(id, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
return min_ii(index, length - 1);
}
static const char *color_name_from_index(ID *id, int index)
{
const CustomDataLayer *layer = BKE_id_attribute_from_index(
id, index, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
return layer ? layer->name : nullptr;
}
bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports)
{
using namespace blender;
@ -391,31 +411,43 @@ bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports)
if (BMEditMesh *em = mesh->edit_mesh) {
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
if (CustomData *data = info[domain].customdata) {
int layer_index = CustomData_get_named_layer_index_notype(data, name);
if (layer_index >= 0) {
if (data->layers[layer_index].type == CD_PROP_FLOAT2) {
/* free associated UV map bool layers */
char buffer_src[MAX_CUSTOMDATA_LAYER_NAME];
BM_data_layer_free_named(
em->bm, data, BKE_uv_map_vert_select_name_get(name, buffer_src));
BM_data_layer_free_named(
em->bm, data, BKE_uv_map_edge_select_name_get(name, buffer_src));
BM_data_layer_free_named(em->bm, data, BKE_uv_map_pin_name_get(name, buffer_src));
}
const std::string name_copy = name;
const int layer_index = CustomData_get_named_layer_index_notype(data, name_copy.c_str());
if (layer_index == -1) {
continue;
}
/* Because it's possible that name is owned by the layer and will be freed
* when freeing the layer, do these checks before freeing. */
const bool is_active_color_attribute = name == StringRef(mesh->active_color_attribute);
const bool is_default_color_attribute = name == StringRef(mesh->default_color_attribute);
if (BM_data_layer_free_named(em->bm, data, name)) {
if (is_active_color_attribute) {
MEM_SAFE_FREE(mesh->active_color_attribute);
}
else if (is_default_color_attribute) {
MEM_SAFE_FREE(mesh->default_color_attribute);
}
return true;
const eCustomDataType type = eCustomDataType(data->layers[layer_index].type);
const bool is_active_color_attribute = name_copy.c_str() ==
StringRef(mesh->active_color_attribute);
const bool is_default_color_attribute = name_copy.c_str() ==
StringRef(mesh->default_color_attribute);
const int active_color_index = color_name_to_index(id, mesh->active_color_attribute);
const int default_color_index = color_name_to_index(id, mesh->default_color_attribute);
if (!BM_data_layer_free_named(em->bm, data, name_copy.c_str())) {
BLI_assert_unreachable();
}
if (is_active_color_attribute) {
BKE_id_attributes_active_color_set(
id, color_name_from_index(id, color_clamp_index(id, active_color_index)));
}
if (is_default_color_attribute) {
BKE_id_attributes_default_color_set(
id, color_name_from_index(id, color_clamp_index(id, default_color_index)));
}
if (type == CD_PROP_FLOAT2 && domain == ATTR_DOMAIN_CORNER) {
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
BM_data_layer_free_named(
em->bm, data, BKE_uv_map_vert_select_name_get(name_copy.c_str(), buffer));
BM_data_layer_free_named(
em->bm, data, BKE_uv_map_edge_select_name_get(name_copy.c_str(), buffer));
BM_data_layer_free_named(
em->bm, data, BKE_uv_map_pin_name_get(name_copy.c_str(), buffer));
}
return true;
}
}
return false;
@ -423,21 +455,44 @@ bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports)
}
std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(*id);
if (!attributes) {
return false;
}
if (GS(id->name) == ID_ME) {
std::optional<blender::bke::AttributeMetaData> metadata = attributes->lookup_meta_data(name);
if (metadata->data_type == CD_PROP_FLOAT2) {
/* remove UV sub-attributes. */
char buffer_src[MAX_CUSTOMDATA_LAYER_NAME];
BKE_id_attribute_remove(id, BKE_uv_map_vert_select_name_get(name, buffer_src), reports);
BKE_id_attribute_remove(id, BKE_uv_map_edge_select_name_get(name, buffer_src), reports);
BKE_id_attribute_remove(id, BKE_uv_map_pin_name_get(name, buffer_src), reports);
const std::string name_copy = name;
std::optional<blender::bke::AttributeMetaData> metadata = attributes->lookup_meta_data(
name_copy);
if (!metadata) {
return false;
}
/* Update active and default color attributes. */
Mesh *mesh = reinterpret_cast<Mesh *>(id);
const bool is_active_color_attribute = name_copy == StringRef(mesh->active_color_attribute);
const bool is_default_color_attribute = name_copy == StringRef(mesh->default_color_attribute);
const int active_color_index = color_name_to_index(id, mesh->active_color_attribute);
const int default_color_index = color_name_to_index(id, mesh->default_color_attribute);
if (!attributes->remove(name_copy)) {
BLI_assert_unreachable();
}
if (is_active_color_attribute) {
BKE_id_attributes_active_color_set(
id, color_name_from_index(id, color_clamp_index(id, active_color_index)));
}
if (is_default_color_attribute) {
BKE_id_attributes_default_color_set(
id, color_name_from_index(id, color_clamp_index(id, default_color_index)));
}
if (metadata->data_type == CD_PROP_FLOAT2 && metadata->domain == ATTR_DOMAIN_CORNER) {
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
attributes->remove(BKE_uv_map_vert_select_name_get(name_copy.c_str(), buffer));
attributes->remove(BKE_uv_map_edge_select_name_get(name_copy.c_str(), buffer));
attributes->remove(BKE_uv_map_pin_name_get(name_copy.c_str(), buffer));
}
return true;
}
return attributes->remove(name);

View File

@ -743,11 +743,9 @@ static GeometrySet curve_calc_modifiers_post(Depsgraph *depsgraph,
Mesh *mesh = geometry_set.get_mesh_for_write();
if (mti->type == eModifierTypeType_OnlyDeform) {
int totvert;
float(*vertex_coords)[3] = BKE_mesh_vert_coords_alloc(mesh, &totvert);
mti->deformVerts(md, &mectx_deform, mesh, vertex_coords, totvert);
BKE_mesh_vert_coords_apply(mesh, vertex_coords);
MEM_freeN(vertex_coords);
mti->deformVerts(
md, &mectx_deform, mesh, BKE_mesh_vert_positions_for_write(mesh), mesh->totvert);
BKE_mesh_tag_positions_changed(mesh);
}
else {
Mesh *output_mesh = mti->modifyMesh(md, &mectx_apply, mesh);

View File

@ -685,11 +685,9 @@ void BKE_mball_data_update(Depsgraph *depsgraph, Scene *scene, Object *ob)
mesh->totcol = mball->totcol;
if (ob->parent && ob->parent->type == OB_LATTICE && ob->partype == PARSKEL) {
int verts_num;
float(*positions)[3] = BKE_mesh_vert_coords_alloc(mesh, &verts_num);
BKE_lattice_deform_coords(ob->parent, ob, positions, verts_num, 0, nullptr, 1.0f);
BKE_mesh_vert_coords_apply(mesh, positions);
MEM_freeN(positions);
BKE_lattice_deform_coords(
ob->parent, ob, BKE_mesh_vert_positions_for_write(mesh), mesh->totvert, 0, nullptr, 1.0f);
BKE_mesh_tag_positions_changed(mesh);
}
ob->runtime.geometry_set_eval = new GeometrySet(GeometrySet::create_with_mesh(mesh));

View File

@ -472,8 +472,6 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
const Span<MPoly> polys = me->polys();
const Span<MLoop> loops = me->loops();
int totedges = 0;
/* only to detect edge polylines */
int *edge_users;
@ -497,7 +495,6 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
edl->edge = &mesh_edges[i];
BLI_addtail(&edges, edl);
totedges++;
}
}
MEM_freeN(edge_users);
@ -519,7 +516,6 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
appendPolyLineVert(&polyline, endVert);
totpoly++;
BLI_freelinkN(&edges, edges.last);
totedges--;
while (ok) { /* while connected edges are found... */
EdgeLink *edl = (EdgeLink *)edges.last;
@ -531,10 +527,9 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
if (edge->v1 == endVert) {
endVert = edge->v2;
appendPolyLineVert(&polyline, edge->v2);
appendPolyLineVert(&polyline, endVert);
totpoly++;
BLI_freelinkN(&edges, edl);
totedges--;
ok = true;
}
else if (edge->v2 == endVert) {
@ -542,7 +537,6 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
appendPolyLineVert(&polyline, endVert);
totpoly++;
BLI_freelinkN(&edges, edl);
totedges--;
ok = true;
}
else if (edge->v1 == startVert) {
@ -550,7 +544,6 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
prependPolyLineVert(&polyline, startVert);
totpoly++;
BLI_freelinkN(&edges, edl);
totedges--;
ok = true;
}
else if (edge->v2 == startVert) {
@ -558,7 +551,6 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
prependPolyLineVert(&polyline, startVert);
totpoly++;
BLI_freelinkN(&edges, edl);
totedges--;
ok = true;
}
@ -582,18 +574,18 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
/* create new 'nurb' within the curve */
nu = MEM_new<Nurb>("MeshNurb", blender::dna::shallow_zero_initialize());
nu->pntsu = polys.size();
nu->pntsu = totpoly;
nu->pntsv = 1;
nu->orderu = 4;
nu->flagu = CU_NURB_ENDPOINT | (closed ? CU_NURB_CYCLIC : 0); /* endpoint */
nu->resolu = 12;
nu->bp = (BPoint *)MEM_calloc_arrayN(polys.size(), sizeof(BPoint), "bpoints");
nu->bp = (BPoint *)MEM_calloc_arrayN(totpoly, sizeof(BPoint), "bpoints");
/* add points */
vl = (VertLink *)polyline.first;
int i;
for (i = 0, bp = nu->bp; i < polys.size(); i++, bp++, vl = (VertLink *)vl->next) {
for (i = 0, bp = nu->bp; i < totpoly; i++, bp++, vl = (VertLink *)vl->next) {
copy_v3_v3(bp->vec, positions[vl->index]);
bp->f1 = SELECT;
bp->radius = bp->weight = 1.0;

View File

@ -1655,6 +1655,19 @@ void BKE_mesh_legacy_convert_mpoly_to_material_indices(Mesh *mesh)
/** \name Generic UV Map Conversion
* \{ */
static const bool *layers_find_bool_named(const Span<CustomDataLayer> layers,
const blender::StringRef name)
{
for (const CustomDataLayer &layer : layers) {
if (layer.type == CD_PROP_BOOL) {
if (layer.name == name) {
return static_cast<const bool *>(layer.data);
}
}
}
return nullptr;
}
void BKE_mesh_legacy_convert_uvs_to_struct(
Mesh *mesh,
blender::ResourceScope &temp_mloopuv_for_convert,
@ -1662,6 +1675,7 @@ void BKE_mesh_legacy_convert_uvs_to_struct(
{
using namespace blender;
using namespace blender::bke;
const int loops_num = mesh->totloop;
Vector<CustomDataLayer, 16> new_layer_to_write;
/* Don't write the boolean UV map sublayers which will be written in the legacy #MLoopUV type. */
@ -1686,20 +1700,19 @@ void BKE_mesh_legacy_convert_uvs_to_struct(
new_layer_to_write.append(layer);
continue;
}
const Span<float2> coords{static_cast<const float2 *>(layer.data), mesh->totloop};
const Span<float2> coords{static_cast<const float2 *>(layer.data), loops_num};
CustomDataLayer mloopuv_layer = layer;
mloopuv_layer.type = CD_MLOOPUV;
MutableSpan<MLoopUV> mloopuv = temp_mloopuv_for_convert.construct<Array<MLoopUV>>(
mesh->totloop);
MutableSpan<MLoopUV> mloopuv = temp_mloopuv_for_convert.construct<Array<MLoopUV>>(loops_num);
mloopuv_layer.data = mloopuv.data();
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
const bool *vert_selection = static_cast<const bool *>(CustomData_get_layer_named(
&mesh->ldata, CD_PROP_BOOL, BKE_uv_map_vert_select_name_get(layer.name, buffer)));
const bool *edge_selection = static_cast<const bool *>(CustomData_get_layer_named(
&mesh->ldata, CD_PROP_BOOL, BKE_uv_map_edge_select_name_get(layer.name, buffer)));
const bool *pin = static_cast<const bool *>(CustomData_get_layer_named(
&mesh->ldata, CD_PROP_BOOL, BKE_uv_map_pin_name_get(layer.name, buffer)));
const bool *vert_selection = layers_find_bool_named(
loop_layers_to_write, BKE_uv_map_vert_select_name_get(layer.name, buffer));
const bool *edge_selection = layers_find_bool_named(
loop_layers_to_write, BKE_uv_map_edge_select_name_get(layer.name, buffer));
const bool *pin = layers_find_bool_named(loop_layers_to_write,
BKE_uv_map_pin_name_get(layer.name, buffer));
threading::parallel_for(mloopuv.index_range(), 2048, [&](IndexRange range) {
for (const int i : range) {

View File

@ -262,9 +262,8 @@ void BKE_mesh_remap_find_best_match_from_mesh(const float (*vert_positions_dst)[
float best_match = FLT_MAX, match;
const int numverts_src = me_src->totvert;
float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, nullptr);
mesh_calc_eigen_matrix(nullptr, (const float(*)[3])vcos_src, numverts_src, mat_src);
const float(*vcos_src)[3] = BKE_mesh_vert_positions(me_src);
mesh_calc_eigen_matrix(nullptr, vcos_src, numverts_src, mat_src);
mesh_calc_eigen_matrix(vert_positions_dst, nullptr, numverts_dst, mat_dst);
BLI_space_transform_global_from_matrices(r_space_transform, mat_dst, mat_src);
@ -289,8 +288,6 @@ void BKE_mesh_remap_find_best_match_from_mesh(const float (*vert_positions_dst)[
}
BLI_space_transform_global_from_matrices(r_space_transform, best_mat_dst, mat_src);
MEM_freeN(vcos_src);
}
/** \} */
@ -516,7 +513,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
}
else if (ELEM(mode, MREMAP_MODE_VERT_EDGE_NEAREST, MREMAP_MODE_VERT_EDGEINTERP_NEAREST)) {
const blender::Span<MEdge> edges_src = me_src->edges();
float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, nullptr);
const float(*vcos_src)[3] = BKE_mesh_vert_positions(me_src);
BKE_bvhtree_from_mesh_get(&treedata, me_src, BVHTREE_FROM_EDGES, 2);
nearest.index = -1;
@ -561,8 +558,6 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
BKE_mesh_remap_item_define_invalid(r_map, i);
}
}
MEM_freeN(vcos_src);
}
else if (ELEM(mode,
MREMAP_MODE_VERT_POLY_NEAREST,
@ -570,7 +565,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
MREMAP_MODE_VERT_POLYINTERP_VNORPROJ)) {
const blender::Span<MPoly> polys_src = me_src->polys();
const blender::Span<MLoop> loops_src = me_src->loops();
float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, nullptr);
const float(*vcos_src)[3] = BKE_mesh_vert_positions(me_src);
const blender::Span<blender::float3> vert_normals_dst = me_dst->vert_normals();
size_t tmp_buff_size = MREMAP_DEFAULT_BUFSIZE;
@ -598,7 +593,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
const MLoopTri *lt = &treedata.looptri[rayhit.index];
const int sources_num = mesh_remap_interp_poly_data_get(polys_src[lt->poly],
loops_src,
(const float(*)[3])vcos_src,
vcos_src,
rayhit.co,
&tmp_buff_size,
&vcos,
@ -635,7 +630,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
int index;
mesh_remap_interp_poly_data_get(polys_src[lt->poly],
loops_src,
(const float(*)[3])vcos_src,
vcos_src,
nearest.co,
&tmp_buff_size,
&vcos,
@ -650,7 +645,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
else if (mode == MREMAP_MODE_VERT_POLYINTERP_NEAREST) {
const int sources_num = mesh_remap_interp_poly_data_get(polys_src[lt->poly],
loops_src,
(const float(*)[3])vcos_src,
vcos_src,
nearest.co,
&tmp_buff_size,
&vcos,
@ -670,7 +665,6 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
}
}
MEM_freeN(vcos_src);
MEM_freeN(vcos);
MEM_freeN(indices);
MEM_freeN(weights);
@ -721,7 +715,7 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode,
if (mode == MREMAP_MODE_EDGE_VERT_NEAREST) {
const int num_verts_src = me_src->totvert;
const blender::Span<MEdge> edges_src = me_src->edges();
float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, nullptr);
const float(*vcos_src)[3] = BKE_mesh_vert_positions(me_src);
MeshElemMap *vert_to_edge_src_map;
int *vert_to_edge_src_map_mem;
@ -840,7 +834,6 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode,
}
}
MEM_freeN(vcos_src);
MEM_freeN(v_dst_to_src_map);
MEM_freeN(vert_to_edge_src_map);
MEM_freeN(vert_to_edge_src_map_mem);
@ -874,7 +867,7 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode,
const blender::Span<MEdge> edges_src = me_src->edges();
const blender::Span<MPoly> polys_src = me_src->polys();
const blender::Span<MLoop> loops_src = me_src->loops();
float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, nullptr);
const float(*vcos_src)[3] = BKE_mesh_vert_positions(me_src);
BKE_bvhtree_from_mesh_get(&treedata, me_src, BVHTREE_FROM_LOOPTRI, 2);
@ -900,8 +893,8 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode,
for (; nloops--; ml_src++) {
const MEdge *edge_src = &edges_src[ml_src->e];
float *co1_src = vcos_src[edge_src->v1];
float *co2_src = vcos_src[edge_src->v2];
const float *co1_src = vcos_src[edge_src->v1];
const float *co2_src = vcos_src[edge_src->v2];
float co_src[3];
float dist_sq;
@ -921,8 +914,6 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode,
BKE_mesh_remap_item_define_invalid(r_map, i);
}
}
MEM_freeN(vcos_src);
}
else if (mode == MREMAP_MODE_EDGE_EDGEINTERP_VNORPROJ) {
const int num_rays_min = 5, num_rays_max = 100;
@ -1308,7 +1299,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
const blender::Span<blender::float3> positions_src = me_src->vert_positions();
const int num_verts_src = me_src->totvert;
float(*vcos_src)[3] = nullptr;
const float(*vcos_src)[3] = nullptr;
const blender::Span<MEdge> edges_src = me_src->edges();
const blender::Span<MPoly> polys_src = me_src->polys();
const blender::Span<MLoop> loops_src = me_src->loops();
@ -1328,7 +1319,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
size_t islands_res_buff_size = MREMAP_DEFAULT_BUFSIZE;
if (!use_from_vert) {
vcos_src = BKE_mesh_vert_coords_alloc(me_src, nullptr);
vcos_src = BKE_mesh_vert_positions(me_src);
vcos_interp = static_cast<float(*)[3]>(
MEM_mallocN(sizeof(*vcos_interp) * buff_size_interp, __func__));
@ -2041,7 +2032,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
if (mode == MREMAP_MODE_LOOP_POLY_NEAREST) {
mesh_remap_interp_poly_data_get(poly,
loops_src,
(const float(*)[3])vcos_src,
vcos_src,
hit_co,
&buff_size_interp,
&vcos_interp,
@ -2060,18 +2051,17 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
&full_weight);
}
else {
const int sources_num = mesh_remap_interp_poly_data_get(
poly,
loops_src,
(const float(*)[3])vcos_src,
hit_co,
&buff_size_interp,
&vcos_interp,
true,
&indices_interp,
&weights_interp,
true,
nullptr);
const int sources_num = mesh_remap_interp_poly_data_get(poly,
loops_src,
vcos_src,
hit_co,
&buff_size_interp,
&vcos_interp,
true,
&indices_interp,
&weights_interp,
true,
nullptr);
mesh_remap_item_define(r_map,
lidx_dst,
@ -2113,9 +2103,6 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
BLI_astar_solution_free(&as_solution);
}
if (vcos_src) {
MEM_freeN(vcos_src);
}
if (vert_to_loop_map_src) {
MEM_freeN(vert_to_loop_map_src);
}

View File

@ -2204,10 +2204,8 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool
const bool is_deformed = check_sculpt_object_deformed(ob, true);
if (is_deformed && me_eval_deform != nullptr) {
int totvert;
float(*v_cos)[3] = BKE_mesh_vert_coords_alloc(me_eval_deform, &totvert);
BKE_pbvh_vert_coords_apply(pbvh, v_cos, totvert);
MEM_freeN(v_cos);
BKE_pbvh_vert_coords_apply(
pbvh, BKE_mesh_vert_positions(me_eval_deform), me_eval_deform->totvert);
}
return pbvh;

View File

@ -1530,7 +1530,6 @@ void BKE_shrinkwrap_mesh_nearest_surface_deform(bContext *C, Object *ob_source,
Scene *sce = CTX_data_scene(C);
ShrinkwrapModifierData ssmd = {{nullptr}};
ModifierEvalContext ctx = {depsgraph, ob_source, ModifierApplyFlag(0)};
int totvert;
ssmd.target = ob_target;
ssmd.shrinkType = MOD_SHRINKWRAP_NEAREST_SURFACE;
@ -1538,13 +1537,17 @@ void BKE_shrinkwrap_mesh_nearest_surface_deform(bContext *C, Object *ob_source,
ssmd.keepDist = 0.0f;
Mesh *src_me = static_cast<Mesh *>(ob_source->data);
float(*vertexCos)[3] = BKE_mesh_vert_coords_alloc(src_me, &totvert);
shrinkwrapModifier_deform(&ssmd, &ctx, sce, ob_source, src_me, nullptr, -1, vertexCos, totvert);
BKE_mesh_vert_coords_apply(src_me, vertexCos);
MEM_freeN(vertexCos);
shrinkwrapModifier_deform(&ssmd,
&ctx,
sce,
ob_source,
src_me,
nullptr,
-1,
BKE_mesh_vert_positions_for_write(src_me),
src_me->totvert);
BKE_mesh_tag_positions_changed(src_me);
}
void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object *ob_target)

View File

@ -1313,6 +1313,19 @@ const VolumeGrid *BKE_volume_grid_find_for_read(const Volume *volume, const char
return nullptr;
}
VolumeGrid *BKE_volume_grid_find_for_write(Volume *volume, const char *name)
{
int num_grids = BKE_volume_num_grids(volume);
for (int i = 0; i < num_grids; i++) {
VolumeGrid *grid = BKE_volume_grid_get_for_write(volume, i);
if (STREQ(BKE_volume_grid_name(grid), name)) {
return grid;
}
}
return nullptr;
}
/* Grid Loading */
bool BKE_volume_grid_load(const Volume *volume, const VolumeGrid *grid)

View File

@ -10,6 +10,24 @@
namespace blender::array_utils {
/**
* Fill the destination span by copying all values from the `src` array. Threaded based on
* grain-size.
*/
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size = 4096);
/**
* Fill the destination span by copying all values from the `src` array. Threaded based on
* grain-size.
*/
template<typename T>
inline void copy(const Span<T> src, MutableSpan<T> dst, const int64_t grain_size = 4096)
{
BLI_assert(src.size() == dst.size());
threading::parallel_for(src.index_range(), grain_size, [&](const IndexRange range) {
dst.slice(range).copy_from(src.slice(range));
});
}
/**
* Fill the destination span by copying masked values from the `src` array. Threaded based on
* grain-size.

View File

@ -420,7 +420,7 @@ struct CartesianBasis {
};
/**
* Create an CartesianBasis for converting from \a a orientation to \a b orientation.
* Create an CartesianBasis using two orthogonal axes.
* The third axis is chosen by right hand rule to follow blender coordinate system.
* \a forward is Y axis in blender coordinate system.
* \a up is Z axis in blender coordinate system.

View File

@ -4,6 +4,15 @@
namespace blender::array_utils {
void copy(const GVArray &src, GMutableSpan dst, const int64_t grain_size)
{
BLI_assert(src.type() == dst.type());
BLI_assert(src.size() == dst.size());
threading::parallel_for(src.index_range(), grain_size, [&](const IndexRange range) {
src.materialize_to_uninitialized(range, dst.data());
});
}
void copy(const GVArray &src,
const IndexMask selection,
GMutableSpan dst,

View File

@ -431,6 +431,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_transparency_lib.glsl
engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl
engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl
engines/eevee_next/shaders/eevee_deferred_light_frag.glsl
engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl
engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl
engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl
@ -450,6 +451,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl
engines/eevee_next/shaders/eevee_film_frag.glsl
engines/eevee_next/shaders/eevee_film_lib.glsl
engines/eevee_next/shaders/eevee_gbuffer_lib.glsl
engines/eevee_next/shaders/eevee_geom_curves_vert.glsl
engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl
engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl
@ -490,6 +492,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl
engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl
engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl
engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl
engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl
engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
engines/eevee_next/shaders/eevee_surf_forward_frag.glsl

View File

@ -280,7 +280,7 @@ static void eevee_draw_scene(void *vedata)
SET_FLAG_FROM_TEST(clear_bits, (stl->effects->enabled_effects & EFFECT_SSS), GPU_STENCIL_BIT);
GPU_framebuffer_clear(fbl->main_fb, clear_bits, clear_col, clear_depth, clear_stencil);
/* Depth prepass */
/* Depth pre-pass. */
DRW_stats_group_start("Prepass");
DRW_draw_pass(psl->depth_ps);
DRW_stats_group_end();

View File

@ -105,6 +105,9 @@
#define RBUFS_AOV_COLOR_SLOT 5
#define RBUFS_AOV_VALUE_SLOT 6
#define RBUFS_CRYPTOMATTE_SLOT 7
/* G-buffer reuses render passes slots. */
#define GBUF_CLOSURE_SLOT RBUFS_LIGHT_SLOT
#define GBUF_COLOR_SLOT RBUFS_DIFF_COLOR_SLOT
/* Uniform Buffers. */
/* Only during prepass. */

View File

@ -649,7 +649,7 @@ void Film::accumulate(const DRWView *view, GPUTexture *combined_final_tx)
draw::View drw_view("MainView", view);
DRW_manager_get()->submit(accumulate_ps_, drw_view);
inst_.manager->submit(accumulate_ps_, drw_view);
combined_tx_.swap();
weight_tx_.swap();

View File

@ -0,0 +1,89 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup eevee
*
* Gbuffer layout used for deferred shading pipeline.
*/
#pragma once
#include "DRW_render.h"
#include "eevee_material.hh"
#include "eevee_shader_shared.hh"
namespace blender::eevee {
class Instance;
/**
* Full-screen textures containing geometric and surface data.
* Used by deferred shading passes. Only one gbuffer is allocated per view
* and is reused for each deferred layer. This is why there can only be temporary
* texture inside it.
*
* Everything is stored inside two array texture, one for each format. This is to fit the
* limitation of the number of images we can bind on a single shader.
*
* First layer is always for reflection. All parameters to shoot a reflection ray are inside
* this layer.
*
* - Layer 1 : Reflection
* - R : Normal packed X
* - G : Normal packed Y
* - B : Roughness
* - A : Unused (Could be used for anisotropic roughness)
*
* Second layer is either for diffuse or transmission. Material mixing both are not
* physically based and are uncommon. So in order to save bandwidth and texture memory, we only
* store one. We use random sampling to mix between both. All parameters to shoot a refraction
* ray are inside this layer.
*
* - Layer 2 : Refraction
* - R : Normal packed X
* - G : Normal packed Y
* - B : Roughness (isotropic)
* - A : IOR
*
* - Layer 2 : Diffuse / Sub-Surface Scattering
* - R : Normal packed X
* - G : Normal packed Y
* - B : Thickness
* - A : Unused (Could be used for diffuse roughness)
*
* Layer 3 is only allocated if Sub-Surface Scattering is needed. All parameters for
* screen-space scattering are inside this layer.
*
* - Layer 3 : Sub-Surface Scattering
* - R : Scattering radius R
* - G : Scattering radius G
* - B : Scattering radius B
* - A : Object ID
*
* For each output closure, we also output the color to apply after the lighting computation.
* The color is stored with a 2 exponent that allows input color with component higher than 1.
* Color degradation is expected to happen in this case.
*/
struct GBuffer {
/* TODO(fclem): Use texture from pool once they support texture array. */
Texture closure_tx = {"GbufferClosure"};
Texture color_tx = {"GbufferColor"};
void acquire(int2 extent, eClosureBits closure_bits_)
{
const bool use_sss = (closure_bits_ & CLOSURE_SSS) != 0;
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE;
closure_tx.ensure_2d_array(GPU_RGBA16, extent, use_sss ? 3 : 2, usage);
color_tx.ensure_2d_array(GPU_RGB10_A2, extent, 2, usage);
}
void release()
{
/* TODO(fclem): Use texture from pool once they support texture array. */
// closure_tx.release();
// color_tx.release();
}
};
} // namespace blender::eevee

View File

@ -105,6 +105,7 @@ void Instance::begin_sync()
velocity.begin_sync(); /* NOTE: Also syncs camera. */
lights.begin_sync();
shadows.begin_sync();
pipelines.begin_sync();
cryptomatte.begin_sync();
gpencil_engine_enabled = false;
@ -114,7 +115,6 @@ void Instance::begin_sync()
depth_of_field.sync();
motion_blur.sync();
hiz_buffer.sync();
pipelines.sync();
main_view.sync();
world.sync();
film.sync();
@ -206,6 +206,7 @@ void Instance::end_sync()
sampling.end_sync();
film.end_sync();
cryptomatte.end_sync();
pipelines.end_sync();
}
void Instance::render_sync()

View File

@ -19,6 +19,7 @@
#include "eevee_cryptomatte.hh"
#include "eevee_depth_of_field.hh"
#include "eevee_film.hh"
#include "eevee_gbuffer.hh"
#include "eevee_hizbuffer.hh"
#include "eevee_irradiance_cache.hh"
#include "eevee_light.hh"
@ -54,6 +55,7 @@ class Instance {
MotionBlurModule motion_blur;
DepthOfField depth_of_field;
Cryptomatte cryptomatte;
GBuffer gbuffer;
HiZBuffer hiz_buffer;
Sampling sampling;
Camera camera;

View File

@ -193,11 +193,6 @@ MaterialPass MaterialModule::material_pass_get(Object *ob,
inst_.sampling.reset();
}
if ((pipeline_type == MAT_PIPE_DEFERRED) &&
GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_SHADER_TO_RGBA)) {
pipeline_type = MAT_PIPE_FORWARD;
}
if (ELEM(pipeline_type,
MAT_PIPE_FORWARD,
MAT_PIPE_FORWARD_PREPASS,
@ -240,10 +235,6 @@ Material &MaterialModule::material_sync(Object *ob,
(has_motion ? MAT_PIPE_DEFERRED_PREPASS_VELOCITY :
MAT_PIPE_DEFERRED_PREPASS);
/* TEST until we have deferred pipeline up and running. */
surface_pipe = MAT_PIPE_FORWARD;
prepass_pipe = has_motion ? MAT_PIPE_FORWARD_PREPASS_VELOCITY : MAT_PIPE_FORWARD_PREPASS;
MaterialKey material_key(blender_mat, geometry_type, surface_pipe);
Material &mat = material_map_.lookup_or_add_cb(material_key, [&]() {

View File

@ -278,4 +278,229 @@ void ForwardPipeline::render(View &view,
/** \} */
/* -------------------------------------------------------------------- */
/** \name Deferred Layer
* \{ */
void DeferredLayer::begin_sync()
{
{
prepass_ps_.init();
{
/* Common resources. */
/* Textures. */
prepass_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
/* Uniform Buf. */
prepass_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get());
inst_.velocity.bind_resources(&prepass_ps_);
inst_.sampling.bind_resources(&prepass_ps_);
}
DRWState state_depth_only = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
DRWState state_depth_color = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS |
DRW_STATE_WRITE_COLOR;
prepass_double_sided_static_ps_ = &prepass_ps_.sub("DoubleSided.Static");
prepass_double_sided_static_ps_->state_set(state_depth_only);
prepass_single_sided_static_ps_ = &prepass_ps_.sub("SingleSided.Static");
prepass_single_sided_static_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK);
prepass_double_sided_moving_ps_ = &prepass_ps_.sub("DoubleSided.Moving");
prepass_double_sided_moving_ps_->state_set(state_depth_color);
prepass_single_sided_moving_ps_ = &prepass_ps_.sub("SingleSided.Moving");
prepass_single_sided_moving_ps_->state_set(state_depth_color | DRW_STATE_CULL_BACK);
}
{
gbuffer_ps_.init();
gbuffer_ps_.clear_stencil(0x00u);
gbuffer_ps_.state_stencil(0x01u, 0x01u, 0x01u);
{
/* Common resources. */
/* G-buffer. */
gbuffer_ps_.bind_image(GBUF_CLOSURE_SLOT, &inst_.gbuffer.closure_tx);
gbuffer_ps_.bind_image(GBUF_COLOR_SLOT, &inst_.gbuffer.color_tx);
/* RenderPasses. */
gbuffer_ps_.bind_image(RBUFS_NORMAL_SLOT, &inst_.render_buffers.normal_tx);
/* TODO(fclem): Pack all render pass into the same texture. */
// gbuffer_ps_.bind_image(RBUFS_DIFF_COLOR_SLOT, &inst_.render_buffers.diffuse_color_tx);
gbuffer_ps_.bind_image(RBUFS_SPEC_COLOR_SLOT, &inst_.render_buffers.specular_color_tx);
gbuffer_ps_.bind_image(RBUFS_EMISSION_SLOT, &inst_.render_buffers.emission_tx);
/* AOVs. */
gbuffer_ps_.bind_image(RBUFS_AOV_COLOR_SLOT, &inst_.render_buffers.aov_color_tx);
gbuffer_ps_.bind_image(RBUFS_AOV_VALUE_SLOT, &inst_.render_buffers.aov_value_tx);
/* Cryptomatte. */
gbuffer_ps_.bind_image(RBUFS_CRYPTOMATTE_SLOT, &inst_.render_buffers.cryptomatte_tx);
/* Storage Buf. */
gbuffer_ps_.bind_ssbo(RBUFS_AOV_BUF_SLOT, &inst_.film.aovs_info);
/* Textures. */
gbuffer_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
/* Uniform Buf. */
gbuffer_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get());
inst_.sampling.bind_resources(&gbuffer_ps_);
inst_.cryptomatte.bind_resources(&gbuffer_ps_);
}
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM | DRW_STATE_DEPTH_EQUAL |
DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS;
gbuffer_double_sided_ps_ = &gbuffer_ps_.sub("DoubleSided");
gbuffer_double_sided_ps_->state_set(state);
gbuffer_single_sided_ps_ = &gbuffer_ps_.sub("SingleSided");
gbuffer_single_sided_ps_->state_set(state | DRW_STATE_CULL_BACK);
}
}
void DeferredLayer::end_sync()
{
/* Use stencil test to reject pixel not written by this layer. */
/* WORKAROUND: Stencil write is only here to avoid rasterizer discard. */
DRWState state = DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_EQUAL;
/* Allow output to combined pass for the last pass. */
DRWState state_write_color = state | DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
if (closure_bits_ & (CLOSURE_DIFFUSE | CLOSURE_REFLECTION)) {
const bool is_last_eval_pass = true;
eval_light_ps_.init();
eval_light_ps_.state_set(is_last_eval_pass ? state_write_color : state);
eval_light_ps_.state_stencil(0x00u, 0x01u, 0xFFu);
eval_light_ps_.shader_set(inst_.shaders.static_shader_get(DEFERRED_LIGHT));
eval_light_ps_.bind_image("out_diffuse_light_img", &diffuse_light_tx_);
eval_light_ps_.bind_image("out_specular_light_img", &specular_light_tx_);
eval_light_ps_.bind_texture("gbuffer_closure_tx", &inst_.gbuffer.closure_tx);
eval_light_ps_.bind_texture("gbuffer_color_tx", &inst_.gbuffer.color_tx);
eval_light_ps_.push_constant("is_last_eval_pass", is_last_eval_pass);
eval_light_ps_.bind_image(RBUFS_LIGHT_SLOT, &inst_.render_buffers.light_tx);
eval_light_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
inst_.lights.bind_resources(&eval_light_ps_);
inst_.shadows.bind_resources(&eval_light_ps_);
inst_.sampling.bind_resources(&eval_light_ps_);
inst_.hiz_buffer.bind_resources(&eval_light_ps_);
eval_light_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS);
eval_light_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
}
PassMain::Sub *DeferredLayer::prepass_add(::Material *blender_mat,
GPUMaterial *gpumat,
bool has_motion)
{
PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ?
(has_motion ? prepass_single_sided_moving_ps_ :
prepass_single_sided_static_ps_) :
(has_motion ? prepass_double_sided_moving_ps_ :
prepass_double_sided_static_ps_);
return &pass->sub(GPU_material_get_name(gpumat));
}
PassMain::Sub *DeferredLayer::material_add(::Material *blender_mat, GPUMaterial *gpumat)
{
closure_bits_ |= shader_closure_bits_from_flag(gpumat);
PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ?
gbuffer_single_sided_ps_ :
gbuffer_double_sided_ps_;
return &pass->sub(GPU_material_get_name(gpumat));
}
void DeferredLayer::render(View &view,
Framebuffer &prepass_fb,
Framebuffer &combined_fb,
int2 extent)
{
GPU_framebuffer_bind(prepass_fb);
inst_.manager->submit(prepass_ps_, view);
inst_.hiz_buffer.set_dirty();
inst_.shadows.set_view(view);
inst_.gbuffer.acquire(extent, closure_bits_);
GPU_framebuffer_bind(combined_fb);
inst_.manager->submit(gbuffer_ps_, view);
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE;
diffuse_light_tx_.acquire(extent, GPU_RGBA16F, usage);
specular_light_tx_.acquire(extent, GPU_RGBA16F, usage);
diffuse_light_tx_.clear(float4(0.0f));
specular_light_tx_.clear(float4(0.0f));
inst_.manager->submit(eval_light_ps_, view);
diffuse_light_tx_.release();
specular_light_tx_.release();
inst_.gbuffer.release();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Deferred Pipeline
*
* Closure data are written to intermediate buffer allowing screen space processing.
* \{ */
void DeferredPipeline::begin_sync()
{
opaque_layer_.begin_sync();
refraction_layer_.begin_sync();
}
void DeferredPipeline::end_sync()
{
opaque_layer_.end_sync();
refraction_layer_.end_sync();
}
PassMain::Sub *DeferredPipeline::prepass_add(::Material *blender_mat,
GPUMaterial *gpumat,
bool has_motion)
{
if (blender_mat->blend_flag & MA_BL_SS_REFRACTION) {
return refraction_layer_.prepass_add(blender_mat, gpumat, has_motion);
}
else {
return opaque_layer_.prepass_add(blender_mat, gpumat, has_motion);
}
}
PassMain::Sub *DeferredPipeline::material_add(::Material *blender_mat, GPUMaterial *gpumat)
{
if (blender_mat->blend_flag & MA_BL_SS_REFRACTION) {
return refraction_layer_.material_add(blender_mat, gpumat);
}
else {
return opaque_layer_.material_add(blender_mat, gpumat);
}
}
void DeferredPipeline::render(View &view,
Framebuffer &prepass_fb,
Framebuffer &combined_fb,
int2 extent)
{
DRW_stats_group_start("Deferred.Opaque");
opaque_layer_.render(view, prepass_fb, combined_fb, extent);
DRW_stats_group_end();
DRW_stats_group_start("Deferred.Refract");
refraction_layer_.render(view, prepass_fb, combined_fb, extent);
DRW_stats_group_end();
}
/** \} */
} // namespace blender::eevee

View File

@ -113,6 +113,77 @@ class ForwardPipeline {
/** \} */
/* -------------------------------------------------------------------- */
/** \name Deferred lighting.
* \{ */
class DeferredLayer {
private:
Instance &inst_;
PassMain prepass_ps_ = {"Prepass"};
PassMain::Sub *prepass_single_sided_static_ps_ = nullptr;
PassMain::Sub *prepass_single_sided_moving_ps_ = nullptr;
PassMain::Sub *prepass_double_sided_static_ps_ = nullptr;
PassMain::Sub *prepass_double_sided_moving_ps_ = nullptr;
PassMain gbuffer_ps_ = {"Shading"};
PassMain::Sub *gbuffer_single_sided_ps_ = nullptr;
PassMain::Sub *gbuffer_double_sided_ps_ = nullptr;
PassSimple eval_light_ps_ = {"EvalLights"};
/* Closures bits from the materials in this pass. */
eClosureBits closure_bits_;
/**
* Accumulation textures for all stages of lighting evaluation (Light, SSR, SSSS, SSGI ...).
* These are split and separate from the main radiance buffer in order to accumulate light for
* the render passes and avoid too much bandwidth waste. Otherwise, we would have to load the
* BSDF color and do additive blending for each of the lighting step.
*
* NOTE: Not to be confused with the render passes.
*/
TextureFromPool diffuse_light_tx_ = {"diffuse_light_accum_tx"};
TextureFromPool specular_light_tx_ = {"specular_light_accum_tx"};
public:
DeferredLayer(Instance &inst) : inst_(inst){};
void begin_sync();
void end_sync();
PassMain::Sub *prepass_add(::Material *blender_mat, GPUMaterial *gpumat, bool has_motion);
PassMain::Sub *material_add(::Material *blender_mat, GPUMaterial *gpumat);
void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, int2 extent);
};
class DeferredPipeline {
private:
Instance &inst_;
/* Gbuffer filling passes. We could have an arbitrary number of them but for now we just have
* a hardcoded number of them. */
DeferredLayer opaque_layer_;
DeferredLayer refraction_layer_;
DeferredLayer volumetric_layer_;
public:
DeferredPipeline(Instance &inst)
: inst_(inst), opaque_layer_(inst), refraction_layer_(inst), volumetric_layer_(inst){};
void begin_sync();
void end_sync();
PassMain::Sub *prepass_add(::Material *material, GPUMaterial *gpumat, bool has_motion);
PassMain::Sub *material_add(::Material *material, GPUMaterial *gpumat);
void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, int2 extent);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Utility texture
*
@ -197,22 +268,25 @@ class UtilityTexture : public Texture {
class PipelineModule {
public:
WorldPipeline world;
// DeferredPipeline deferred;
DeferredPipeline deferred;
ForwardPipeline forward;
ShadowPipeline shadow;
// VelocityPipeline velocity;
UtilityTexture utility_tx;
public:
PipelineModule(Instance &inst) : world(inst), forward(inst), shadow(inst){};
PipelineModule(Instance &inst) : world(inst), deferred(inst), forward(inst), shadow(inst){};
void sync()
void begin_sync()
{
// deferred.sync();
deferred.begin_sync();
forward.sync();
shadow.sync();
// velocity.sync();
}
void end_sync()
{
deferred.end_sync();
}
PassMain::Sub *material_add(Object *ob,
@ -222,7 +296,7 @@ class PipelineModule {
{
switch (pipeline_type) {
case MAT_PIPE_DEFERRED_PREPASS:
// return deferred.prepass_add(blender_mat, gpumat, false);
return deferred.prepass_add(blender_mat, gpumat, false);
case MAT_PIPE_FORWARD_PREPASS:
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
return forward.prepass_transparent_add(ob, blender_mat, gpumat);
@ -230,7 +304,7 @@ class PipelineModule {
return forward.prepass_opaque_add(blender_mat, gpumat, false);
case MAT_PIPE_DEFERRED_PREPASS_VELOCITY:
// return deferred.prepass_add(blender_mat, gpumat, true);
return deferred.prepass_add(blender_mat, gpumat, true);
case MAT_PIPE_FORWARD_PREPASS_VELOCITY:
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
return forward.prepass_transparent_add(ob, blender_mat, gpumat);
@ -238,7 +312,7 @@ class PipelineModule {
return forward.prepass_opaque_add(blender_mat, gpumat, true);
case MAT_PIPE_DEFERRED:
// return deferred.material_add(blender_mat, gpumat);
return deferred.material_add(blender_mat, gpumat);
case MAT_PIPE_FORWARD:
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
return forward.material_transparent_add(ob, blender_mat, gpumat);

View File

@ -86,6 +86,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_film_comp";
case FILM_CRYPTOMATTE_POST:
return "eevee_film_cryptomatte_post";
case DEFERRED_LIGHT:
return "eevee_deferred_light";
case HIZ_DEBUG:
return "eevee_hiz_debug";
case HIZ_UPDATE:
@ -241,6 +243,11 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
}
}
/* WORKAROUND: Needed because node_tree isn't present in test shaders. */
if (pipeline_type == MAT_PIPE_DEFERRED) {
info.define("MAT_RENDER_PASS_SUPPORT");
}
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
info.define("MAT_TRANSPARENT");
/* Transparent material do not have any velocity specific pipeline. */

View File

@ -30,6 +30,8 @@ enum eShaderType {
FILM_COMP,
FILM_CRYPTOMATTE_POST,
DEFERRED_LIGHT,
DEBUG_SURFELS,
DOF_BOKEH_LUT,

View File

@ -123,8 +123,7 @@ void ShadingView::render()
/* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */
inst_.lights.set_view(render_view_new_, extent_);
// inst_.pipelines.deferred.render(
// render_view_, rt_buffer_opaque_, rt_buffer_refract_, depth_tx_, combined_tx_);
inst_.pipelines.deferred.render(render_view_new_, prepass_fb_, combined_fb_, extent_);
// inst_.lightprobes.draw_cache_display();

View File

@ -0,0 +1,97 @@
/**
* Compute light objects lighting contribution using Gbuffer data.
*
* Output light either directly to the radiance buffers or to temporary radiance accumulation
* buffer that will be processed by other deferred lighting passes.
*/
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl)
void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
float depth = texelFetch(hiz_tx, ivec2(gl_FragCoord.xy), 0).r;
vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth);
/* TODO(fclem): High precision derivative. */
vec3 Ng = safe_normalize(cross(dFdx(P), dFdy(P)));
vec3 V = cameraVec(P);
float vP_z = dot(cameraForward, P) - dot(cameraForward, cameraPos);
vec4 gbuffer_0_packed = texelFetch(gbuffer_closure_tx, ivec3(texel, 0), 0);
vec4 gbuffer_1_packed = texelFetch(gbuffer_closure_tx, ivec3(texel, 1), 0);
ClosureReflection reflection_data;
reflection_data.N = gbuffer_normal_unpack(gbuffer_0_packed.xy);
reflection_data.roughness = gbuffer_0_packed.z;
ClosureDiffuse diffuse_data;
diffuse_data.N = gbuffer_normal_unpack(gbuffer_1_packed.xy);
/* These are only set for SSS case. */
diffuse_data.sss_radius = vec3(0.0);
diffuse_data.sss_id = 0u;
float thickness = 0.0;
bool is_refraction = gbuffer_is_refraction(gbuffer_1_packed);
if (is_refraction) {
/* Still evaluate the diffuse light so that dithered SSS / Refraction combination still
* produces a complete diffuse light buffer that will be correctly convolved by the SSSS.
* The refraction pixels will just set the diffuse radiance to 0. */
}
else if (false /* TODO */) {
vec4 gbuffer_2_packed = texelFetch(gbuffer_closure_tx, ivec3(texel, 2), 0);
diffuse_data.sss_radius = gbuffer_sss_radii_unpack(gbuffer_2_packed.xyz);
diffuse_data.sss_id = gbuffer_object_id_unorm16_unpack(gbuffer_2_packed.w);
thickness = gbuffer_thickness_pack(gbuffer_1_packed.z);
}
vec3 diffuse_light = vec3(0.0);
vec3 reflection_light = vec3(0.0);
light_eval(
diffuse_data, reflection_data, P, Ng, V, vP_z, thickness, diffuse_light, reflection_light);
if (is_last_eval_pass) {
/* Apply color and output lighting to render-passes. */
vec4 color_0_packed = texelFetch(gbuffer_color_tx, ivec3(texel, 0), 0);
vec4 color_1_packed = texelFetch(gbuffer_color_tx, ivec3(texel, 1), 0);
reflection_data.color = gbuffer_color_unpack(color_0_packed);
diffuse_data.color = gbuffer_color_unpack(color_1_packed);
if (is_refraction) {
diffuse_data.color = vec3(0.0);
}
reflection_light *= reflection_data.color;
diffuse_light *= diffuse_data.color;
/* Add radiance to light pass. */
imageStore(
rp_light_img, ivec3(texel, RENDER_PASS_LAYER_DIFFUSE_LIGHT), vec4(diffuse_light, 1.0));
imageStore(
rp_light_img, ivec3(texel, RENDER_PASS_LAYER_SPECULAR_LIGHT), vec4(reflection_light, 1.0));
/* Add radiance to combined pass. */
out_radiance = vec4(diffuse_light + reflection_light, 0.0);
out_transmittance = vec4(1.0);
}
else {
/* Store lighting for next deferred pass. */
/* Output diffuse light along with object ID for sub-surface screen space processing. */
vec4 diffuse_radiance;
diffuse_radiance.xyz = diffuse_light;
diffuse_radiance.w = gbuffer_object_id_f16_pack(diffuse_data.sss_id);
imageStore(out_diffuse_light_img, texel, diffuse_radiance);
imageStore(out_specular_light_img, texel, vec4(reflection_light, 0.0));
/* Final radiance will be amended by the last pass.
* This should do nothing as color write should be disabled in this case. */
out_radiance = vec4(0.0);
out_transmittance = vec4(0.0);
}
}

View File

@ -0,0 +1,106 @@
/**
* G-buffer: Packing and upacking of G-buffer data.
*
* See #GBuffer for a breakdown of the G-buffer layout.
*/
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
vec2 gbuffer_normal_pack(vec3 N)
{
N /= length_manhattan(N);
N.xy = (N.z >= 0.0) ? N.xy : ((1.0 - abs(N.yx)) * sign(N.xy));
N.xy = N.xy * 0.5 + 0.5;
return N.xy;
}
vec3 gbuffer_normal_unpack(vec2 N_packed)
{
N_packed = N_packed * 2.0 - 1.0;
vec3 N = vec3(N_packed.x, N_packed.y, 1.0 - abs(N_packed.x) - abs(N_packed.y));
float t = clamp(-N.z, 0.0, 1.0);
N.x += (N.x >= 0.0) ? -t : t;
N.y += (N.y >= 0.0) ? -t : t;
return normalize(N);
}
float gbuffer_ior_pack(float ior)
{
return (ior > 1.0) ? (1.0 - 0.5 / ior) : (0.5 * ior);
}
float gbuffer_ior_unpack(float ior_packed)
{
return (ior_packed > 0.5) ? (-1.0 / (ior_packed * 2.0 + 2.0)) : (2.0 * ior_packed);
}
float gbuffer_thickness_pack(float thickness)
{
/* TODO(fclem): Something better. */
return gbuffer_ior_pack(thickness);
}
float gbuffer_thickness_unpack(float thickness_packed)
{
/* TODO(fclem): Something better. */
return gbuffer_ior_unpack(thickness_packed);
}
vec3 gbuffer_sss_radii_pack(vec3 sss_radii)
{
/* TODO(fclem): Something better. */
return vec3(
gbuffer_ior_pack(sss_radii.x), gbuffer_ior_pack(sss_radii.y), gbuffer_ior_pack(sss_radii.z));
}
vec3 gbuffer_sss_radii_unpack(vec3 sss_radii_packed)
{
/* TODO(fclem): Something better. */
return vec3(gbuffer_ior_unpack(sss_radii_packed.x),
gbuffer_ior_unpack(sss_radii_packed.y),
gbuffer_ior_unpack(sss_radii_packed.z));
}
vec4 gbuffer_color_pack(vec3 color)
{
float max_comp = max(color.x, max(color.y, color.z));
/* Store 2bit exponent inside Alpha. Allows values up to 8 with some color degradation.
* Above 8, the result will be clampped when writing the data to the output buffer. */
float exponent = (max_comp > 1) ? ((max_comp > 2) ? ((max_comp > 4) ? 3.0 : 2.0) : 1.0) : 0.0;
/* TODO(fclem): Could try dithering to avoid banding artifacts on higher exponents. */
return vec4(color / exp2(exponent), exponent / 3.0);
}
vec3 gbuffer_color_unpack(vec4 color_packed)
{
float exponent = color_packed.a * 3.0;
return color_packed.rgb * exp2(exponent);
}
float gbuffer_object_id_unorm16_pack(uint object_id)
{
return float(object_id & 0xFFFFu) / float(0xFFFF);
}
uint gbuffer_object_id_unorm16_unpack(float object_id_packed)
{
return uint(object_id_packed * float(0xFFFF));
}
float gbuffer_object_id_f16_pack(uint object_id)
{
/* TODO(fclem): Make use of all the 16 bits in a half float.
* This here only correctly represent values up to 1024. */
return float(object_id);
}
uint gbuffer_object_id_f16_unpack(float object_id_packed)
{
return uint(object_id_packed);
}
bool gbuffer_is_refraction(vec4 gbuffer)
{
return gbuffer.w < 1.0;
}

View File

@ -29,14 +29,16 @@ bool closure_select(float weight, inout float total_weight, inout float r)
float x = weight / total_weight;
bool chosen = (r < x);
/* Assuming that if r is in the interval [0,x] or [x,1], it's still uniformly distributed within
* that interval, so you remapping to [0,1] again to explore this space of probability. */
* that interval, so remapping to [0,1] again to explore this space of probability. */
r = (chosen) ? (r / x) : ((r - x) / (1.0 - x));
return chosen;
}
#define SELECT_CLOSURE(destination, random, candidate) \
if (closure_select(candidate.weight, destination.weight, random)) { \
float tmp = destination.weight; \
destination = candidate; \
destination.weight = tmp; \
}
float g_closure_rand;

View File

@ -82,7 +82,7 @@ void main()
{
vec2 screen_uv = gl_FragCoord.xy / vec2(fb_resolution);
float opaque_depth = texelFetch(hiz_tx, int2(gl_FragCoord.xy), fb_lod).r;
float opaque_depth = texelFetch(hiz_tx, ivec2(gl_FragCoord.xy), fb_lod).r;
vec3 ws_opaque = get_world_space_from_depth(screen_uv, opaque_depth);
vec3 ws_near_plane = get_world_space_from_depth(screen_uv, 0);

View File

@ -0,0 +1,215 @@
/* -------------------------------------------------------------------- */
/** \name Spherical Harmonics Functions
*
* `L` denote the row and `M` the column in the spherical harmonics table (1).
* `p` denote positive column and `n` negative ones.
*
* Use precomputed constants to avoid constant folding differences across compilers.
* Note that (2) doesn't use Condon-Shortley phase whereas our implementation does.
*
* Reference:
* (1) https://en.wikipedia.org/wiki/Spherical_harmonics#/media/File:Sphericalfunctions.svg
* (2) https://en.wikipedia.org/wiki/Table_of_spherical_harmonics#Real_spherical_harmonics
* (3) https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
*
* \{ */
/* L0 Band. */
float spherical_harmonics_L0_M0(vec3 v)
{
return 0.282094792;
}
/* L1 Band. */
float spherical_harmonics_L1_Mn1(vec3 v)
{
return -0.488602512 * v.y;
}
float spherical_harmonics_L1_M0(vec3 v)
{
return 0.488602512 * v.z;
}
float spherical_harmonics_L1_Mp1(vec3 v)
{
return -0.488602512 * v.x;
}
/* L2 Band. */
float spherical_harmonics_L2_Mn2(vec3 v)
{
return 1.092548431 * (v.x * v.y);
}
float spherical_harmonics_L2_Mn1(vec3 v)
{
return -1.092548431 * (v.y * v.z);
}
float spherical_harmonics_L2_M0(vec3 v)
{
return 0.315391565 * (3.0 * v.z * v.z - 1.0);
}
float spherical_harmonics_L2_Mp1(vec3 v)
{
return -1.092548431 * (v.x * v.z);
}
float spherical_harmonics_L2_Mp2(vec3 v)
{
return 0.546274215 * (v.x * v.x - v.y * v.y);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Structure
* \{ */
struct SphericalHarmonicBandL0 {
vec3 M0;
};
struct SphericalHarmonicBandL1 {
vec3 Mn1;
vec3 M0;
vec3 Mp1;
};
struct SphericalHarmonicBandL2 {
vec3 Mn2;
vec3 Mn1;
vec3 M0;
vec3 Mp1;
vec3 Mp2;
};
struct SphericalHarmonicL0 {
SphericalHarmonicBandL0 L0;
};
struct SphericalHarmonicL1 {
SphericalHarmonicBandL0 L0;
SphericalHarmonicBandL1 L1;
};
struct SphericalHarmonicL2 {
SphericalHarmonicBandL0 L0;
SphericalHarmonicBandL1 L1;
SphericalHarmonicBandL2 L2;
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Encode
*
* Decompose an input signal into spherical harmonic coefficients.
* \{ */
void spherical_harmonics_L0_encode_signal_sample(vec3 direction,
vec3 amplitude,
inout SphericalHarmonicBandL0 r_L0)
{
r_L0.M0 += spherical_harmonics_L0_M0(direction) * amplitude;
}
void spherical_harmonics_L1_encode_signal_sample(vec3 direction,
vec3 amplitude,
inout SphericalHarmonicBandL1 r_L1)
{
r_L1.Mn1 += spherical_harmonics_L1_Mn1(direction) * amplitude;
r_L1.M0 += spherical_harmonics_L1_M0(direction) * amplitude;
r_L1.Mp1 += spherical_harmonics_L1_Mp1(direction) * amplitude;
}
void spherical_harmonics_L2_encode_signal_sample(vec3 direction,
vec3 amplitude,
inout SphericalHarmonicBandL2 r_L2)
{
r_L2.Mn2 += spherical_harmonics_L2_Mn2(direction) * amplitude;
r_L2.Mn1 += spherical_harmonics_L2_Mn1(direction) * amplitude;
r_L2.M0 += spherical_harmonics_L2_M0(direction) * amplitude;
r_L2.Mp1 += spherical_harmonics_L2_Mp1(direction) * amplitude;
r_L2.Mp2 += spherical_harmonics_L2_Mp2(direction) * amplitude;
}
void spherical_harmonics_encode_signal_sample(vec3 direction,
vec3 amplitude,
inout SphericalHarmonicL0 sh)
{
spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0);
}
void spherical_harmonics_encode_signal_sample(vec3 direction,
vec3 amplitude,
inout SphericalHarmonicL1 sh)
{
spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0);
spherical_harmonics_L1_encode_signal_sample(direction, amplitude, sh.L1);
}
void spherical_harmonics_encode_signal_sample(vec3 direction,
vec3 amplitude,
inout SphericalHarmonicL2 sh)
{
spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0);
spherical_harmonics_L1_encode_signal_sample(direction, amplitude, sh.L1);
spherical_harmonics_L2_encode_signal_sample(direction, amplitude, sh.L2);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Decode
*
* Evaluate an encoded signal in a given unit vector direction.
* \{ */
vec3 spherical_harmonics_L0_evaluate(vec3 direction, SphericalHarmonicBandL0 L0)
{
return spherical_harmonics_L0_M0(direction) * L0.M0;
}
vec3 spherical_harmonics_L1_evaluate(vec3 direction, SphericalHarmonicBandL1 L1)
{
return spherical_harmonics_L1_Mn1(direction) * L1.Mn1 +
spherical_harmonics_L1_M0(direction) * L1.M0 +
spherical_harmonics_L1_Mp1(direction) * L1.Mp1;
}
vec3 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2)
{
return spherical_harmonics_L2_Mn2(direction) * L2.Mn2 +
spherical_harmonics_L2_Mn1(direction) * L2.Mn1 +
spherical_harmonics_L2_M0(direction) * L2.M0 +
spherical_harmonics_L2_Mp1(direction) * L2.Mp1 +
spherical_harmonics_L2_Mp2(direction) * L2.Mp2;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Evaluation
* \{ */
/**
* Convolve a spherical harmonic encoded irradiance signal as a lambertian reflection.
* Returns the lambertian radiance (cosine lobe divided by PI) so the coefficients simplify to 1,
* 2/3 and 1/4. See this reference for more explanation:
* https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
*/
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh)
{
return spherical_harmonics_L0_evaluate(N, sh.L0);
}
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh)
{
return spherical_harmonics_L0_evaluate(N, sh.L0) +
spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0);
}
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh)
{
return spherical_harmonics_L0_evaluate(N, sh.L0) +
spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0) +
spherical_harmonics_L2_evaluate(N, sh.L2) * (1.0 / 4.0);
}
/** \} */

View File

@ -3,17 +3,136 @@
* Deferred lighting evaluation: Lighting is evaluated in a separate pass.
*
* Outputs shading parameter per pixel using a randomized set of BSDFs.
**/
* Some render-pass are written during this pass.
*/
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
vec4 closure_to_rgba(Closure cl)
{
vec4 out_color;
out_color.rgb = g_emission;
out_color.a = saturate(1.0 - avg(g_transmittance));
/* Reset for the next closure tree. */
closure_weights_reset();
return out_color;
}
void main()
{
init_globals();
float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r;
g_closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE));
fragment_displacement();
nodetree_surface();
g_holdout = saturate(g_holdout);
out_transmittance = vec4(1.0 - g_holdout);
float transmittance_mono = saturate(avg(g_transmittance));
float thickness = nodetree_thickness();
g_diffuse_data.color *= g_diffuse_data.weight;
g_reflection_data.color *= g_reflection_data.weight;
g_refraction_data.color *= g_refraction_data.weight;
/* TODO(fclem): This feels way too complex for what is it. */
bool has_any_bsdf_weight = g_diffuse_data.weight != 0.0 || g_reflection_data.weight != 0.0 ||
g_refraction_data.weight != 0.0;
vec3 out_normal = has_any_bsdf_weight ? vec3(0.0) : g_data.N;
out_normal += g_diffuse_data.N * g_diffuse_data.weight;
out_normal += g_reflection_data.N * g_reflection_data.weight;
out_normal += g_refraction_data.N * g_refraction_data.weight;
out_normal = safe_normalize(out_normal);
vec3 specular_color = g_reflection_data.color + g_refraction_data.color;
/* ----- Render Passes output ----- */
ivec2 out_texel = ivec2(gl_FragCoord.xy);
#ifdef MAT_RENDER_PASS_SUPPORT /* Needed because node_tree isn't present in test shaders. */
/* Some render pass can be written during the gbuffer pass. Light passes are written later. */
vec4 cryptomatte_output = vec4(cryptomatte_object_buf[resource_id], node_tree.crypto_hash, 0.0);
imageStore(rp_cryptomatte_img, out_texel, cryptomatte_output);
imageStore(rp_normal_img, out_texel, vec4(out_normal, 1.0));
/* TODO(fclem): For now, just don't do anything. In the future all render passes should be in an
* array texture and have a UBO with indirection to the correct layer. */
// imageStore(rp_diffuse_color_img, out_texel, vec4(g_diffuse_data.color, 1.0));
imageStore(rp_specular_color_img, out_texel, vec4(specular_color, 1.0));
imageStore(rp_emission_img, out_texel, vec4(g_emission, 1.0));
#endif
/* ----- GBuffer output ----- */
if (true) {
/* Reflection. */
vec4 out_reflect = vec4(gbuffer_normal_pack(g_reflection_data.N),
g_reflection_data.roughness,
g_reflection_data.roughness);
imageStore(out_gbuff_closure_img, ivec3(out_texel, 0), out_reflect);
vec4 color = gbuffer_color_pack(g_reflection_data.color);
imageStore(out_gbuff_color_img, ivec3(out_texel, 0), color);
}
/* TODO(fclem) other RNG. */
float refract_rand = fract(g_closure_rand * 6.1803398875);
float combined_weight = g_refraction_data.weight + g_diffuse_data.weight;
bool output_refraction = combined_weight > 0.0 &&
(refract_rand * combined_weight) < g_refraction_data.weight;
if (output_refraction) {
/* Refraction. */
vec4 closure;
closure.xy = gbuffer_normal_pack(g_refraction_data.N);
closure.z = g_refraction_data.roughness;
closure.w = gbuffer_ior_pack(g_refraction_data.ior);
/* Clamp to just bellow 1 to be able to distinguish between refraction and diffuse.
* Ceiling value is chosen by the storage format (16bit UNORM). */
closure.w = min(closure.w, float(0xFFFFu - 1u) / float(0xFFFFu));
imageStore(out_gbuff_closure_img, ivec3(out_texel, 1), closure);
vec4 color = gbuffer_color_pack(g_refraction_data.color);
imageStore(out_gbuff_color_img, ivec3(out_texel, 1), color);
}
else {
/* Diffuse. */
vec4 closure;
closure.xy = gbuffer_normal_pack(g_diffuse_data.N);
closure.z = gbuffer_thickness_pack(thickness);
/* Used to detect the refraction case. Could be used for roughness. */
closure.w = 1.0;
imageStore(out_gbuff_closure_img, ivec3(out_texel, 1), closure);
vec4 color = gbuffer_color_pack(g_diffuse_data.color);
imageStore(out_gbuff_color_img, ivec3(out_texel, 1), color);
}
if (true) {
/* SubSurface Scattering. */
vec4 closure;
closure.xyz = gbuffer_sss_radii_pack(g_diffuse_data.sss_radius);
closure.w = gbuffer_object_id_unorm16_pack(g_diffuse_data.sss_id);
imageStore(out_gbuff_closure_img, ivec3(out_texel, 2), closure);
}
/* ----- Radiance output ----- */
/* Only output emission during the gbuffer pass. */
out_radiance = vec4(g_emission, 0.0);
out_radiance.rgb *= 1.0 - g_holdout;
out_transmittance.rgb = g_transmittance;
out_transmittance.a = saturate(avg(g_transmittance));
}

View File

@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "eevee_defines.hh"
#include "gpu_shader_create_info.hh"
#define image_out(slot, qualifier, format, name) \
image(slot, format, qualifier, ImageType::FLOAT_2D, name, Frequency::PASS)
#define image_array_out(slot, qualifier, format, name) \
image(slot, format, qualifier, ImageType::FLOAT_2D_ARRAY, name, Frequency::PASS)
/**
* Specific deferred pass accumulate the computed lighting to either:
* - a split diffuse / specular temporary light buffer.
* or to
* - the combined pass & the light render-pass (if needed).
*
* This is in order to minimize the number of blending step.
*/
GPU_SHADER_CREATE_INFO(eevee_deferred_base)
/* Early fragment test is needed to avoid processing fragments without correct GBuffer data. */
.early_fragment_test(true)
/* Select which output to write to. */
.push_constant(Type::BOOL, "is_last_eval_pass")
/* Combined pass output. */
.fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0)
.fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1)
/* Light pass output. */
.image_array_out(RBUFS_LIGHT_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_light_img")
/* Chaining to next pass. */
.image_out(2, Qualifier::READ_WRITE, GPU_RGBA16F, "out_diffuse_light_img")
.image_out(3, Qualifier::READ_WRITE, GPU_RGBA16F, "out_specular_light_img");
GPU_SHADER_CREATE_INFO(eevee_deferred_light)
.fragment_source("eevee_deferred_light_frag.glsl")
.sampler(0, ImageType::FLOAT_2D_ARRAY, "gbuffer_closure_tx")
.sampler(1, ImageType::FLOAT_2D_ARRAY, "gbuffer_color_tx")
.additional_info("eevee_shared",
"eevee_utility_texture",
"eevee_light_data",
"eevee_shadow_data",
"eevee_deferred_base",
"eevee_hiz_data",
"draw_view",
"draw_fullscreen")
.do_static_compilation(true);
#undef image_array_out

View File

@ -88,11 +88,11 @@ GPU_SHADER_CREATE_INFO(eevee_aov_out)
GPU_SHADER_CREATE_INFO(eevee_render_pass_out)
.define("MAT_RENDER_PASS_SUPPORT")
.image_out(RBUFS_NORMAL_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_normal_img")
.image_array_out(RBUFS_LIGHT_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_light_img")
.image_out(RBUFS_DIFF_COLOR_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_color_img")
.image_out(RBUFS_SPEC_COLOR_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img")
.image_out(RBUFS_EMISSION_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img");
.image_out(RBUFS_NORMAL_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_normal_img")
.image_array_out(RBUFS_LIGHT_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_light_img")
.image_out(RBUFS_DIFF_COLOR_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_diffuse_color_img")
.image_out(RBUFS_SPEC_COLOR_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_specular_color_img")
.image_out(RBUFS_EMISSION_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_emission_img");
GPU_SHADER_CREATE_INFO(eevee_cryptomatte_out)
.storage_buf(CRYPTOMATTE_BUF_SLOT, Qualifier::READ, "vec2", "cryptomatte_object_buf[]")
@ -101,23 +101,29 @@ GPU_SHADER_CREATE_INFO(eevee_cryptomatte_out)
GPU_SHADER_CREATE_INFO(eevee_surf_deferred)
.vertex_out(eevee_surf_iface)
/* NOTE: This removes the possibility of using gl_FragDepth. */
// .early_fragment_test(true)
/* Direct output. */
.early_fragment_test(true)
/* Direct output. (Emissive, Holdout) */
.fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0)
.fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1)
/* Gbuffer. */
// .image_out(0, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_transmit_color")
// .image_out(1, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_transmit_data")
// .image_out(2, Qualifier::WRITE, GPU_RGBA16F, "gbuff_transmit_normal")
// .image_out(3, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_reflection_color")
// .image_out(4, Qualifier::WRITE, GPU_RGBA16F, "gbuff_reflection_normal")
// .image_out(5, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_emission")
/* Render-passes. */
// .image_out(6, Qualifier::READ_WRITE, GPU_RGBA16F, "rpass_volume_light")
/* Everything is stored inside a two layered target, one for each format. This is to fit the
* limitation of the number of images we can bind on a single shader. */
.image_array_out(GBUF_CLOSURE_SLOT, Qualifier::WRITE, GPU_RGBA16, "out_gbuff_closure_img")
.image_array_out(GBUF_COLOR_SLOT, Qualifier::WRITE, GPU_RGB10_A2, "out_gbuff_color_img")
/* Render-passes need to be declared manually to avoid overlap with the G-buffer which reuse
* some of binding points. */
.image_out(RBUFS_NORMAL_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_normal_img")
// .image_array_out(RBUFS_LIGHT_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_light_img")
/* TODO(fclem): Merge all render-pass into the same texture array. */
// .image_out(RBUFS_DIFF_COLOR_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_diffuse_color_img")
.image_out(RBUFS_SPEC_COLOR_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_specular_color_img")
.image_out(RBUFS_EMISSION_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_emission_img")
.fragment_source("eevee_surf_deferred_frag.glsl")
.additional_info("eevee_camera",
"eevee_utility_texture",
"eevee_sampling_data",
/* Added manually to avoid overlap. */
// "eevee_render_pass_out",
"eevee_cryptomatte_out",
"eevee_aov_out");
GPU_SHADER_CREATE_INFO(eevee_surf_forward)

View File

@ -225,7 +225,7 @@ static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, MeshBufferCa
int i;
BM_ITER_MESH_INDEX (f, &iter, mr->bm, BM_FACES_OF_MESH, i) {
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
const int mat = min_ii(f->mat_nr, mat_last);
const int mat = clamp_i(f->mat_nr, 0, mat_last);
tri_first_index[i] = mat_tri_offs[mat];
mat_tri_offs[mat] += f->len - 2;
}
@ -238,7 +238,7 @@ static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, MeshBufferCa
for (int i = 0; i < mr->poly_len; i++) {
if (!(mr->use_hide && mr->hide_poly && mr->hide_poly[i])) {
const MPoly &poly = mr->polys[i];
const int mat = min_ii(mr->material_indices ? mr->material_indices[i] : 0, mat_last);
const int mat = mr->material_indices ? clamp_i(mr->material_indices[i], 0, mat_last) : 0;
tri_first_index[i] = mat_tri_offs[mat];
mat_tri_offs[mat] += poly.totloop - 2;
}
@ -263,7 +263,7 @@ static void mesh_render_data_mat_tri_len_bm_range_fn(void *__restrict userdata,
BMesh *bm = mr->bm;
BMFace *efa = BM_face_at_index(bm, iter);
if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
int mat = min_ii(efa->mat_nr, mr->mat_len - 1);
int mat = clamp_i(efa->mat_nr, 0, mr->mat_len - 1);
mat_tri_len[mat] += efa->len - 2;
}
}
@ -277,7 +277,9 @@ static void mesh_render_data_mat_tri_len_mesh_range_fn(void *__restrict userdata
const MPoly &poly = mr->polys[iter];
if (!(mr->use_hide && mr->hide_poly && mr->hide_poly[iter])) {
int mat = min_ii(mr->material_indices ? mr->material_indices[iter] : 0, mr->mat_len - 1);
const int mat = mr->material_indices ?
clamp_i(mr->material_indices[iter], 0, mr->mat_len - 1) :
0;
mat_tri_len[mat] += poly.totloop - 2;
}
}

View File

@ -1235,9 +1235,7 @@ static void sculpt_draw_cb(DRWSculptCallbackData *scd,
if (scd->use_mats) {
index = drw_pbvh_material_index_get(batches);
if (index >= scd->num_shading_groups) {
index = 0;
}
index = clamp_i(index, 0, scd->num_shading_groups - 1);
}
DRWShadingGroup *shgrp = scd->shading_groups[index];

View File

@ -206,7 +206,8 @@ void DRW_texture_pool_reset(DRWTexturePool *pool)
}
}
BLI_assert(pool->tmp_tex_acquired.is_empty());
BLI_assert_msg(pool->tmp_tex_acquired.is_empty(),
"Missing a TextureFromPool.release() before end of draw.");
for (GPUTexture *tmp_tex : pool->tmp_tex_pruned) {
GPU_texture_free(tmp_tex);
}

View File

@ -75,6 +75,12 @@ class View {
/** Disable a range in the multi-view array. Disabled view will not produce any instances. */
void disable(IndexRange range);
/** Enable or disable every visibility test (frustum culling, HiZ culling). */
void visibility_test(bool enable)
{
do_visibility_ = enable;
}
/**
* Update culling data using a compute shader.
* This is to be used if the matrices were updated externally

View File

@ -5,8 +5,8 @@
* \ingroup draw
*/
#include "BLI_array_utils.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_string.h"
#include "draw_subdivision.h"
#include "extract_mesh.hh"
@ -91,8 +91,9 @@ static void extract_uv_init(const MeshRenderData *mr,
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, v_len);
float2 *uv_data = static_cast<float2 *>(GPU_vertbuf_get_data(vbo));
for (int i = 0; i < MAX_MTFACE; i++) {
MutableSpan<float2> uv_data(static_cast<float2 *>(GPU_vertbuf_get_data(vbo)), v_len);
int vbo_index = 0;
for (const int i : IndexRange(MAX_MTFACE)) {
if (uv_layers & (1 << i)) {
if (mr->extract_type == MR_EXTRACT_BMESH) {
int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_PROP_FLOAT2, i);
@ -102,18 +103,17 @@ static void extract_uv_init(const MeshRenderData *mr,
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
do {
float *luv = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_ofs);
memcpy(uv_data, luv, sizeof(*uv_data));
uv_data++;
uv_data[vbo_index] = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_ofs);
vbo_index++;
} while ((l_iter = l_iter->next) != l_first);
}
}
else {
const float2 *layer_data = static_cast<const float2 *>(
CustomData_get_layer_n(cd_ldata, CD_PROP_FLOAT2, i));
for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) {
memcpy(uv_data, layer_data, sizeof(*uv_data));
}
const Span<float2> uv_map(
static_cast<const float2 *>(CustomData_get_layer_n(cd_ldata, CD_PROP_FLOAT2, i)),
mr->loop_len);
array_utils::copy(uv_map, uv_data.slice(vbo_index, mr->loop_len));
vbo_index += mr->loop_len;
}
}
}

View File

@ -3651,7 +3651,12 @@ static bool get_normalized_fcurve_bounds(FCurve *fcu,
rctf *r_bounds)
{
const bool fcu_selection_only = false;
BKE_fcurve_calc_bounds(fcu, fcu_selection_only, include_handles, range, r_bounds);
const bool found_bounds = BKE_fcurve_calc_bounds(
fcu, fcu_selection_only, include_handles, range, r_bounds);
if (!found_bounds) {
return false;
}
const short mapping_flag = ANIM_get_normalization_flags(ac);

View File

@ -107,36 +107,6 @@ static int geometry_attribute_add_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
static void next_color_attribute(ID *id, const StringRefNull name, bool is_render)
{
const CustomDataLayer *layer = BKE_id_attributes_color_find(id, name.c_str());
int index = BKE_id_attribute_to_index(id, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
index++;
layer = BKE_id_attribute_from_index(id, index, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
if (!layer) {
index = 0;
layer = BKE_id_attribute_from_index(id, index, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
}
if (layer) {
if (is_render) {
BKE_id_attributes_active_color_set(id, layer->name);
}
else {
BKE_id_attributes_default_color_set(id, layer->name);
}
}
}
static void next_color_attributes(ID *id, const StringRefNull name)
{
next_color_attribute(id, name, false); /* active */
next_color_attribute(id, name, true); /* render */
}
void GEOMETRY_OT_attribute_add(wmOperatorType *ot)
{
/* identifiers */
@ -182,8 +152,6 @@ static int geometry_attribute_remove_exec(bContext *C, wmOperator *op)
ID *id = static_cast<ID *>(ob->data);
CustomDataLayer *layer = BKE_id_attributes_active_get(id);
next_color_attributes(id, layer->name);
if (!BKE_id_attribute_remove(id, layer->name, op->reports)) {
return OPERATOR_CANCELLED;
}
@ -436,8 +404,6 @@ static int geometry_color_attribute_remove_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
next_color_attributes(id, active_name);
if (!BKE_id_attribute_remove(id, active_name.c_str(), op->reports)) {
return OPERATOR_CANCELLED;
}

View File

@ -162,7 +162,7 @@ class GridViewBuilder {
/** Build \a grid_view into the previously provided block, clipped by \a view_bounds (view space,
* typically `View2D.cur`). */
void build_grid_view(AbstractGridView &grid_view, const View2D &v2d);
void build_grid_view(AbstractGridView &grid_view, const View2D &v2d, uiLayout &layout);
};
/** \} */

View File

@ -306,12 +306,8 @@ class BasicTreeViewItem : public AbstractTreeViewItem {
* \{ */
class TreeViewBuilder {
uiBlock &block_;
public:
TreeViewBuilder(uiBlock &block);
void build_tree_view(AbstractTreeView &tree_view);
static void build_tree_view(AbstractTreeView &tree_view, uiLayout &layout);
};
/** \} */

View File

@ -314,7 +314,7 @@ class GridViewLayoutBuilder {
friend class GridViewBuilder;
public:
GridViewLayoutBuilder(uiBlock &block);
GridViewLayoutBuilder(uiLayout &layout);
void build_from_view(const AbstractGridView &grid_view, const View2D &v2d) const;
@ -324,7 +324,7 @@ class GridViewLayoutBuilder {
uiLayout *current_layout() const;
};
GridViewLayoutBuilder::GridViewLayoutBuilder(uiBlock &block) : block_(block)
GridViewLayoutBuilder::GridViewLayoutBuilder(uiLayout &layout) : block_(*uiLayoutGetBlock(&layout))
{
}
@ -340,7 +340,7 @@ void GridViewLayoutBuilder::build_grid_tile(uiLayout &grid_layout,
void GridViewLayoutBuilder::build_from_view(const AbstractGridView &grid_view,
const View2D &v2d) const
{
uiLayout *prev_layout = current_layout();
uiLayout *parent_layout = current_layout();
uiLayout &layout = *uiLayoutColumn(current_layout(), false);
const GridViewStyle &style = grid_view.get_style();
@ -377,7 +377,7 @@ void GridViewLayoutBuilder::build_from_view(const AbstractGridView &grid_view,
}
}
UI_block_layout_set_current(&block_, prev_layout);
UI_block_layout_set_current(&block_, parent_layout);
build_visible_helper.fill_layout_after_visible(block_);
}
@ -393,13 +393,20 @@ GridViewBuilder::GridViewBuilder(uiBlock &block) : block_(block)
{
}
void GridViewBuilder::build_grid_view(AbstractGridView &grid_view, const View2D &v2d)
void GridViewBuilder::build_grid_view(AbstractGridView &grid_view,
const View2D &v2d,
uiLayout &layout)
{
uiBlock &block = *uiLayoutGetBlock(&layout);
grid_view.build_items();
grid_view.update_from_old(block_);
grid_view.update_from_old(block);
grid_view.change_state_delayed();
GridViewLayoutBuilder builder(block_);
/* Ensure the given layout is actually active. */
UI_block_layout_set_current(&block, &layout);
GridViewLayoutBuilder builder(layout);
builder.build_from_view(grid_view, v2d);
}

View File

@ -405,30 +405,30 @@ class TreeViewLayoutBuilder {
void build_row(AbstractTreeViewItem &item) const;
uiBlock &block() const;
uiLayout *current_layout() const;
uiLayout &current_layout() const;
private:
/* Created through #TreeViewBuilder. */
TreeViewLayoutBuilder(uiBlock &block);
/* Created through #TreeViewBuilder (friend class). */
TreeViewLayoutBuilder(uiLayout &layout);
static void polish_layout(const uiBlock &block);
};
TreeViewLayoutBuilder::TreeViewLayoutBuilder(uiBlock &block) : block_(block)
TreeViewLayoutBuilder::TreeViewLayoutBuilder(uiLayout &layout) : block_(*uiLayoutGetBlock(&layout))
{
}
void TreeViewLayoutBuilder::build_from_tree(const AbstractTreeView &tree_view)
{
uiLayout *prev_layout = current_layout();
uiLayout &parent_layout = current_layout();
uiLayout *box = uiLayoutBox(prev_layout);
uiLayout *box = uiLayoutBox(&parent_layout);
uiLayoutColumn(box, false);
tree_view.foreach_item([this](AbstractTreeViewItem &item) { build_row(item); },
AbstractTreeView::IterOptions::SkipCollapsed);
UI_block_layout_set_current(&block(), prev_layout);
UI_block_layout_set_current(&block(), &parent_layout);
}
void TreeViewLayoutBuilder::polish_layout(const uiBlock &block)
@ -450,10 +450,10 @@ void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const
{
uiBlock &block_ = block();
uiLayout *prev_layout = current_layout();
uiLayout &prev_layout = current_layout();
eUIEmbossType previous_emboss = UI_block_emboss_get(&block_);
uiLayout *overlap = uiLayoutOverlap(prev_layout);
uiLayout *overlap = uiLayoutOverlap(&prev_layout);
uiLayoutRow(overlap, false);
/* Every item gets one! Other buttons can be overlapped on top. */
@ -475,7 +475,7 @@ void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const
polish_layout(block_);
UI_block_emboss_set(&block_, previous_emboss);
UI_block_layout_set_current(&block_, prev_layout);
UI_block_layout_set_current(&block_, &prev_layout);
}
uiBlock &TreeViewLayoutBuilder::block() const
@ -483,24 +483,25 @@ uiBlock &TreeViewLayoutBuilder::block() const
return block_;
}
uiLayout *TreeViewLayoutBuilder::current_layout() const
uiLayout &TreeViewLayoutBuilder::current_layout() const
{
return block().curlayout;
return *block().curlayout;
}
/* ---------------------------------------------------------------------- */
TreeViewBuilder::TreeViewBuilder(uiBlock &block) : block_(block)
void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view, uiLayout &layout)
{
}
uiBlock &block = *uiLayoutGetBlock(&layout);
void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view)
{
tree_view.build_tree();
tree_view.update_from_old(block_);
tree_view.update_from_old(block);
tree_view.change_state_delayed();
TreeViewLayoutBuilder builder(block_);
/* Ensure the given layout is actually active. */
UI_block_layout_set_current(&block, &layout);
TreeViewLayoutBuilder builder(layout);
builder.build_from_tree(tree_view);
}

View File

@ -179,6 +179,10 @@ static float sculpt_automasking_normal_calc(SculptSession *ss,
static bool sculpt_automasking_is_constrained_by_radius(const Brush *br)
{
if (br == nullptr) {
return false;
}
/* 2D falloff is not constrained by radius. */
if (br->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
return false;

View File

@ -782,6 +782,5 @@ void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library
std::make_unique<ed::asset_browser::AssetCatalogTreeView>(
asset_library, params, *space_file));
ui::TreeViewBuilder builder(*block);
builder.build_tree_view(*tree_view);
ui::TreeViewBuilder::build_tree_view(*tree_view, *layout);
}

View File

@ -1589,12 +1589,11 @@ void draw_nodespace_back_pix(const bContext &C,
GPU_matrix_pop();
}
static float2 socket_link_connection_location(const Span<float2> socket_locations,
const bNode &node,
static float2 socket_link_connection_location(const bNode &node,
const bNodeSocket &socket,
const bNodeLink &link)
{
const float2 socket_location = socket_locations[socket.index_in_tree()];
const float2 socket_location = socket.runtime->location;
if (socket.is_multi_input() && socket.is_input() && !(node.flag & NODE_HIDDEN)) {
return node_link_calculate_multi_input_position(
socket_location, link.multi_input_socket_index, socket.runtime->total_inputs);
@ -1628,13 +1627,11 @@ static void calculate_inner_link_bezier_points(std::array<float2, 4> &points)
}
}
static std::array<float2, 4> node_link_bezier_points(const Span<float2> socket_locations,
const bNodeLink &link)
static std::array<float2, 4> node_link_bezier_points(const bNodeLink &link)
{
std::array<float2, 4> points;
points[0] = socket_link_connection_location(
socket_locations, *link.fromnode, *link.fromsock, link);
points[3] = socket_link_connection_location(socket_locations, *link.tonode, *link.tosock, link);
points[0] = socket_link_connection_location(*link.fromnode, *link.fromsock, link);
points[3] = socket_link_connection_location(*link.tonode, *link.tosock, link);
calculate_inner_link_bezier_points(points);
return points;
}
@ -1650,11 +1647,10 @@ static bool node_link_draw_is_visible(const View2D &v2d, const std::array<float2
return true;
}
void node_link_bezier_points_evaluated(const Span<float2> socket_locations,
const bNodeLink &link,
void node_link_bezier_points_evaluated(const bNodeLink &link,
std::array<float2, NODE_LINK_RESOL + 1> &coords)
{
const std::array<float2, 4> points = node_link_bezier_points(socket_locations, link);
const std::array<float2, 4> points = node_link_bezier_points(link);
/* The extra +1 in size is required by these functions and would be removed ideally. */
BKE_curve_forward_diff_bezier(points[0].x,
@ -2041,9 +2037,7 @@ static NodeLinkDrawConfig nodelink_get_draw_config(const bContext &C,
const bNodeTree &node_tree = *snode.edittree;
draw_config.dim_factor = selected ? 1.0f :
node_link_dim_factor(
node_tree.runtime->all_socket_locations, v2d, link);
draw_config.dim_factor = selected ? 1.0f : node_link_dim_factor(v2d, link);
bTheme *btheme = UI_GetTheme();
draw_config.dash_alpha = btheme->space_node.dash_alpha;
@ -2166,9 +2160,7 @@ void node_draw_link_bezier(const bContext &C,
const int th_col3,
const bool selected)
{
const bNodeTree &node_tree = *snode.edittree;
const std::array<float2, 4> points = node_link_bezier_points(
node_tree.runtime->all_socket_locations, link);
const std::array<float2, 4> points = node_link_bezier_points(link);
if (!node_link_draw_is_visible(v2d, points)) {
return;
}
@ -2227,19 +2219,13 @@ void node_draw_link(const bContext &C,
static std::array<float2, 4> node_link_bezier_points_dragged(const SpaceNode &snode,
const bNodeLink &link)
{
const bNodeTree &node_tree = *snode.edittree;
const float2 cursor = snode.runtime->cursor * UI_SCALE_FAC;
std::array<float2, 4> points;
points[0] = link.fromsock ?
socket_link_connection_location(node_tree.runtime->all_socket_locations,
*link.fromnode,
*link.fromsock,
link) :
cursor;
points[3] = link.tosock ?
socket_link_connection_location(
node_tree.runtime->all_socket_locations, *link.tonode, *link.tosock, link) :
socket_link_connection_location(*link.fromnode, *link.fromsock, link) :
cursor;
points[3] = link.tosock ? socket_link_connection_location(*link.tonode, *link.tosock, link) :
cursor;
calculate_inner_link_bezier_points(points);
return points;
}

View File

@ -109,12 +109,10 @@ bNode *add_static_node(const bContext &C, int type, const float2 &location)
/** \name Add Reroute Operator
* \{ */
std::optional<float2> link_path_intersection(const Span<float2> socket_locations,
const bNodeLink &link,
const Span<float2> path)
std::optional<float2> link_path_intersection(const bNodeLink &link, const Span<float2> path)
{
std::array<float2, NODE_LINK_RESOL + 1> coords;
node_link_bezier_points_evaluated(socket_locations, link, coords);
node_link_bezier_points_evaluated(link, coords);
for (const int i : path.index_range().drop_back(1)) {
for (const int j : IndexRange(NODE_LINK_RESOL)) {
@ -140,7 +138,6 @@ static int add_reroute_exec(bContext *C, wmOperator *op)
const ARegion &region = *CTX_wm_region(C);
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &ntree = *snode.edittree;
const Span<float2> socket_locations = ntree.runtime->all_socket_locations;
Vector<float2> path;
RNA_BEGIN (op->ptr, itemptr, "path") {
@ -172,10 +169,10 @@ static int add_reroute_exec(bContext *C, wmOperator *op)
Map<bNodeSocket *, RerouteCutsForSocket> cuts_per_socket;
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
if (node_link_is_hidden_or_dimmed(socket_locations, region.v2d, *link)) {
if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
continue;
}
const std::optional<float2> cut = link_path_intersection(socket_locations, *link, path);
const std::optional<float2> cut = link_path_intersection(*link, path);
if (!cut) {
continue;
}

View File

@ -285,9 +285,6 @@ void node_sort(bNodeTree &ntree)
ntree.runtime->nodes_by_id.add_new(sort_nodes[i]);
sort_nodes[i]->runtime->index_in_tree = i;
}
/* Nodes have been reordered; the socket locations are invalid until the node tree is redrawn. */
ntree.runtime->all_socket_locations.clear();
}
static Array<uiBlock *> node_uiblocks_init(const bContext &C, const Span<bNode *> nodes)
@ -338,8 +335,7 @@ static void node_update_basis(const bContext &C,
const TreeDrawContext & /*tree_draw_ctx*/,
bNodeTree &ntree,
bNode &node,
uiBlock &block,
MutableSpan<float2> socket_locations)
uiBlock &block)
{
PointerRNA nodeptr;
RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr);
@ -415,8 +411,7 @@ static void node_update_basis(const bContext &C,
buty = min_ii(buty, dy - NODE_DY);
/* Round the socket location to stop it from jiggling. */
socket_locations[socket->index_in_tree()] = float2(round(loc.x + NODE_WIDTH(node)),
round(dy - NODE_DYS));
socket->runtime->location = float2(round(loc.x + NODE_WIDTH(node)), round(dy - NODE_DYS));
dy = buty;
if (socket->next) {
@ -553,7 +548,7 @@ static void node_update_basis(const bContext &C,
buty = min_ii(buty, dy - NODE_DY);
/* Round the socket vertical position to stop it from jiggling. */
socket_locations[socket->index_in_tree()] = float2(loc.x, round(dy - NODE_DYS));
socket->runtime->location = float2(loc.x, round(dy - NODE_DYS));
dy = buty - multi_input_socket_offset * 0.5;
if (socket->next) {
@ -583,7 +578,7 @@ static void node_update_basis(const bContext &C,
/**
* Based on settings in node, sets drawing rect info.
*/
static void node_update_hidden(bNode &node, uiBlock &block, MutableSpan<float2> socket_locations)
static void node_update_hidden(bNode &node, uiBlock &block)
{
int totin = 0, totout = 0;
@ -623,7 +618,7 @@ static void node_update_hidden(bNode &node, uiBlock &block, MutableSpan<float2>
for (bNodeSocket *socket : node.output_sockets()) {
if (socket->is_visible()) {
/* Round the socket location to stop it from jiggling. */
socket_locations[socket->index_in_tree()] = {
socket->runtime->location = {
round(node.runtime->totr.xmax - hiddenrad + sinf(rad) * hiddenrad),
round(node.runtime->totr.ymin + hiddenrad + cosf(rad) * hiddenrad)};
rad += drad;
@ -636,7 +631,7 @@ static void node_update_hidden(bNode &node, uiBlock &block, MutableSpan<float2>
for (bNodeSocket *socket : node.input_sockets()) {
if (socket->is_visible()) {
/* Round the socket location to stop it from jiggling. */
socket_locations[socket->index_in_tree()] = {
socket->runtime->location = {
round(node.runtime->totr.xmin + hiddenrad + sinf(rad) * hiddenrad),
round(node.runtime->totr.ymin + hiddenrad + cosf(rad) * hiddenrad)};
rad += drad;
@ -1216,7 +1211,6 @@ void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, ui
static void node_socket_draw_nested(const bContext &C,
const bNodeTree &ntree,
const Span<float2> socket_locations,
PointerRNA &node_ptr,
uiBlock &block,
const bNodeSocket &sock,
@ -1228,7 +1222,7 @@ static void node_socket_draw_nested(const bContext &C,
const float size,
const bool selected)
{
const float2 location = socket_locations[sock.index_in_tree()];
const float2 location = sock.runtime->location;
float color[4];
float outline_color[4];
@ -1430,7 +1424,6 @@ static void node_draw_shadow(const SpaceNode &snode,
static void node_draw_sockets(const View2D &v2d,
const bContext &C,
const bNodeTree &ntree,
const Span<float2> socket_locations,
const bNode &node,
uiBlock &block,
const bool draw_outputs,
@ -1490,7 +1483,6 @@ static void node_draw_sockets(const View2D &v2d,
node_socket_draw_nested(C,
ntree,
socket_locations,
node_ptr,
block,
*sock,
@ -1517,7 +1509,6 @@ static void node_draw_sockets(const View2D &v2d,
node_socket_draw_nested(C,
ntree,
socket_locations,
node_ptr,
block,
*sock,
@ -1556,7 +1547,6 @@ static void node_draw_sockets(const View2D &v2d,
if (select_all || (sock->flag & SELECT)) {
node_socket_draw_nested(C,
ntree,
socket_locations,
node_ptr,
block,
*sock,
@ -1584,7 +1574,6 @@ static void node_draw_sockets(const View2D &v2d,
if (select_all || (sock->flag & SELECT)) {
node_socket_draw_nested(C,
ntree,
socket_locations,
node_ptr,
block,
*sock,
@ -1630,7 +1619,7 @@ static void node_draw_sockets(const View2D &v2d,
node_socket_color_get(C, ntree, node_ptr, *socket, color);
node_socket_outline_color_get(socket->flag & SELECT, socket->type, outline_color);
const float2 location = socket_locations[socket->index_in_tree()];
const float2 location = socket->runtime->location;
node_socket_draw_multi_input(color, outline_color, width, height, location);
}
}
@ -2138,7 +2127,6 @@ static void node_draw_basis(const bContext &C,
const View2D &v2d,
const SpaceNode &snode,
bNodeTree &ntree,
const Span<float2> socket_locations,
const bNode &node,
uiBlock &block,
bNodeInstanceKey key)
@ -2439,7 +2427,7 @@ static void node_draw_basis(const bContext &C,
/* Skip slow socket drawing if zoom is small. */
if (scale > 0.2f) {
node_draw_sockets(v2d, C, ntree, socket_locations, node, block, true, false);
node_draw_sockets(v2d, C, ntree, node, block, true, false);
}
/* Preview. */
@ -2463,7 +2451,6 @@ static void node_draw_hidden(const bContext &C,
const View2D &v2d,
const SpaceNode &snode,
bNodeTree &ntree,
const Span<float2> socket_locations,
bNode &node,
uiBlock &block)
{
@ -2633,7 +2620,7 @@ static void node_draw_hidden(const bContext &C,
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
node_draw_sockets(v2d, C, ntree, socket_locations, node, block, true, false);
node_draw_sockets(v2d, C, ntree, node, block, true, false);
UI_block_end(&C, &block);
UI_block_draw(&C, &block);
@ -2781,13 +2768,13 @@ static void frame_node_prepare_for_draw(bNode &node, Span<bNode *> nodes)
node.runtime->totr = rect;
}
static void reroute_node_prepare_for_draw(bNode &node, MutableSpan<float2> socket_locations)
static void reroute_node_prepare_for_draw(bNode &node)
{
const float2 loc = node_to_view(node, float2(0));
/* Reroute node has exactly one input and one output, both in the same place. */
socket_locations[node.input_socket(0).index_in_tree()] = loc;
socket_locations[node.output_socket(0).index_in_tree()] = loc;
node.input_socket(0).runtime->location = loc;
node.output_socket(0).runtime->location = loc;
const float size = 8.0f;
node.width = size * 2;
@ -2801,8 +2788,7 @@ static void node_update_nodetree(const bContext &C,
TreeDrawContext &tree_draw_ctx,
bNodeTree &ntree,
Span<bNode *> nodes,
Span<uiBlock *> blocks,
MutableSpan<float2> socket_locations)
Span<uiBlock *> blocks)
{
/* Make sure socket "used" tags are correct, for displaying value buttons. */
SpaceNode *snode = CTX_wm_space_node(&C);
@ -2818,14 +2804,14 @@ static void node_update_nodetree(const bContext &C,
}
if (node.is_reroute()) {
reroute_node_prepare_for_draw(node, socket_locations);
reroute_node_prepare_for_draw(node);
}
else {
if (node.flag & NODE_HIDDEN) {
node_update_hidden(node, block, socket_locations);
node_update_hidden(node, block);
}
else {
node_update_basis(C, tree_draw_ctx, ntree, node, block, socket_locations);
node_update_basis(C, tree_draw_ctx, ntree, node, block);
}
}
}
@ -2968,12 +2954,8 @@ static void frame_node_draw(const bContext &C,
UI_block_draw(&C, &block);
}
static void reroute_node_draw(const bContext &C,
ARegion &region,
bNodeTree &ntree,
const Span<float2> socket_locations,
const bNode &node,
uiBlock &block)
static void reroute_node_draw(
const bContext &C, ARegion &region, bNodeTree &ntree, const bNode &node, uiBlock &block)
{
/* Skip if out of view. */
const rctf &rct = node.runtime->totr;
@ -3011,8 +2993,7 @@ static void reroute_node_draw(const bContext &C,
/* Only draw input socket as they all are placed on the same position highlight
* if node itself is selected, since we don't display the node body separately. */
node_draw_sockets(
region.v2d, C, ntree, socket_locations, node, block, false, node.flag & SELECT);
node_draw_sockets(region.v2d, C, ntree, node, block, false, node.flag & SELECT);
UI_block_end(&C, &block);
UI_block_draw(&C, &block);
@ -3023,7 +3004,6 @@ static void node_draw(const bContext &C,
ARegion &region,
const SpaceNode &snode,
bNodeTree &ntree,
const Span<float2> socket_locations,
bNode &node,
uiBlock &block,
bNodeInstanceKey key)
@ -3032,15 +3012,15 @@ static void node_draw(const bContext &C,
frame_node_draw(C, tree_draw_ctx, region, snode, ntree, node, block);
}
else if (node.is_reroute()) {
reroute_node_draw(C, region, ntree, socket_locations, node, block);
reroute_node_draw(C, region, ntree, node, block);
}
else {
const View2D &v2d = region.v2d;
if (node.flag & NODE_HIDDEN) {
node_draw_hidden(C, tree_draw_ctx, v2d, snode, ntree, socket_locations, node, block);
node_draw_hidden(C, tree_draw_ctx, v2d, snode, ntree, node, block);
}
else {
node_draw_basis(C, tree_draw_ctx, v2d, snode, ntree, socket_locations, node, block, key);
node_draw_basis(C, tree_draw_ctx, v2d, snode, ntree, node, block, key);
}
}
}
@ -3052,7 +3032,6 @@ static void node_draw_nodetree(const bContext &C,
ARegion &region,
SpaceNode &snode,
bNodeTree &ntree,
const Span<float2> socket_locations,
Span<bNode *> nodes,
Span<uiBlock *> blocks,
bNodeInstanceKey parent_key)
@ -3074,8 +3053,7 @@ static void node_draw_nodetree(const bContext &C,
}
const bNodeInstanceKey key = BKE_node_instance_key(parent_key, &ntree, nodes[i]);
node_draw(
C, tree_draw_ctx, region, snode, ntree, socket_locations, *nodes[i], *blocks[i], key);
node_draw(C, tree_draw_ctx, region, snode, ntree, *nodes[i], *blocks[i], key);
}
/* Node lines. */
@ -3105,8 +3083,7 @@ static void node_draw_nodetree(const bContext &C,
}
const bNodeInstanceKey key = BKE_node_instance_key(parent_key, &ntree, nodes[i]);
node_draw(
C, tree_draw_ctx, region, snode, ntree, socket_locations, *nodes[i], *blocks[i], key);
node_draw(C, tree_draw_ctx, region, snode, ntree, *nodes[i], *blocks[i], key);
}
}
@ -3213,19 +3190,9 @@ static void draw_nodetree(const bContext &C,
else if (ntree.type == NTREE_COMPOSIT) {
tree_draw_ctx.used_by_realtime_compositor = realtime_compositor_is_in_use(C);
}
ntree.runtime->all_socket_locations.reinitialize(ntree.all_sockets().size());
node_update_nodetree(
C, tree_draw_ctx, ntree, nodes, blocks, ntree.runtime->all_socket_locations);
node_draw_nodetree(C,
tree_draw_ctx,
region,
*snode,
ntree,
ntree.runtime->all_socket_locations,
nodes,
blocks,
parent_key);
node_update_nodetree(C, tree_draw_ctx, ntree, nodes, blocks);
node_draw_nodetree(C, tree_draw_ctx, region, *snode, ntree, nodes, blocks, parent_key);
}
/**

View File

@ -1099,12 +1099,10 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
}
}
static bool cursor_isect_multi_input_socket(const Span<float2> socket_locations,
const float2 &cursor,
const bNodeSocket &socket)
static bool cursor_isect_multi_input_socket(const float2 &cursor, const bNodeSocket &socket)
{
const float node_socket_height = node_socket_calculate_height(socket);
const float2 location = socket_locations[socket.index_in_tree()];
const float2 location = socket.runtime->location;
/* `.xmax = socket->locx + NODE_SOCKSIZE * 5.5f`
* would be the same behavior as for regular sockets.
* But keep it smaller because for multi-input socket you
@ -1131,11 +1129,6 @@ bNodeSocket *node_find_indicated_socket(SpaceNode &snode,
bNodeTree &node_tree = *snode.edittree;
node_tree.ensure_topology_cache();
const Span<float2> socket_locations = node_tree.runtime->all_socket_locations;
if (socket_locations.size() != node_tree.all_sockets().size()) {
/* Sockets haven't been drawn yet, e.g. when the file is currently opening. */
return nullptr;
}
const Span<bNode *> nodes = node_tree.all_nodes();
if (nodes.is_empty()) {
return nullptr;
@ -1160,9 +1153,9 @@ bNodeSocket *node_find_indicated_socket(SpaceNode &snode,
if (in_out & SOCK_IN) {
for (bNodeSocket *sock : node.input_sockets()) {
if (sock->is_visible()) {
const float2 location = socket_locations[sock->index_in_tree()];
const float2 location = sock->runtime->location;
if (sock->flag & SOCK_MULTI_INPUT && !(node.flag & NODE_HIDDEN)) {
if (cursor_isect_multi_input_socket(socket_locations, cursor, *sock)) {
if (cursor_isect_multi_input_socket(cursor, *sock)) {
if (!socket_is_occluded(location, node, snode)) {
return sock;
}
@ -1179,7 +1172,7 @@ bNodeSocket *node_find_indicated_socket(SpaceNode &snode,
if (in_out & SOCK_OUT) {
for (bNodeSocket *sock : node.output_sockets()) {
if (sock->is_visible()) {
const float2 location = socket_locations[sock->index_in_tree()];
const float2 location = sock->runtime->location;
if (BLI_rctf_isect_pt(&rect, location.x, location.y)) {
if (!socket_is_occluded(location, node, snode)) {
return sock;
@ -1199,16 +1192,14 @@ bNodeSocket *node_find_indicated_socket(SpaceNode &snode,
/** \name Node Link Dimming
* \{ */
float node_link_dim_factor(const Span<float2> socket_locations,
const View2D &v2d,
const bNodeLink &link)
float node_link_dim_factor(const View2D &v2d, const bNodeLink &link)
{
if (link.fromsock == nullptr || link.tosock == nullptr) {
return 1.0f;
}
const float2 from = socket_locations[link.fromsock->index_in_tree()];
const float2 to = socket_locations[link.tosock->index_in_tree()];
const float2 from = link.fromsock->runtime->location;
const float2 to = link.tosock->runtime->location;
const float min_endpoint_distance = std::min(
std::max(BLI_rctf_length_x(&v2d.cur, from.x), BLI_rctf_length_y(&v2d.cur, from.y)),
@ -1221,11 +1212,9 @@ float node_link_dim_factor(const Span<float2> socket_locations,
return std::clamp(1.0f - min_endpoint_distance / viewport_width * 10.0f, 0.05f, 1.0f);
}
bool node_link_is_hidden_or_dimmed(const Span<float2> socket_locations,
const View2D &v2d,
const bNodeLink &link)
bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link)
{
return nodeLinkIsHidden(&link) || node_link_dim_factor(socket_locations, v2d, link) < 0.5f;
return nodeLinkIsHidden(&link) || node_link_dim_factor(v2d, link) < 0.5f;
}
/** \} */

View File

@ -247,13 +247,10 @@ void node_draw_link_bezier(const bContext &C,
int th_col3,
bool selected);
void node_link_bezier_points_evaluated(Span<float2> all_socket_locations,
const bNodeLink &link,
void node_link_bezier_points_evaluated(const bNodeLink &link,
std::array<float2, NODE_LINK_RESOL + 1> &coords);
std::optional<float2> link_path_intersection(Span<float2> socket_locations,
const bNodeLink &link,
Span<float2> path);
std::optional<float2> link_path_intersection(const bNodeLink &link, Span<float2> path);
void draw_nodespace_back_pix(const bContext &C,
ARegion &region,
@ -325,12 +322,8 @@ int node_render_changed_exec(bContext *, wmOperator *);
bNodeSocket *node_find_indicated_socket(SpaceNode &snode,
const float2 &cursor,
eNodeSocketInOut in_out);
float node_link_dim_factor(Span<float2> socket_locations,
const View2D &v2d,
const bNodeLink &link);
bool node_link_is_hidden_or_dimmed(Span<float2> socket_locations,
const View2D &v2d,
const bNodeLink &link);
float node_link_dim_factor(const View2D &v2d, const bNodeLink &link);
bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link);
void NODE_OT_duplicate(wmOperatorType *ot);
void NODE_OT_delete(wmOperatorType *ot);

View File

@ -122,10 +122,6 @@ static void pick_input_link_by_link_intersect(const bContext &C,
{
SpaceNode *snode = CTX_wm_space_node(&C);
bNodeTree &node_tree = *snode->edittree;
const Span<float2> socket_locations = node_tree.runtime->all_socket_locations;
if (socket_locations.is_empty()) {
return;
}
float2 drag_start;
RNA_float_get_array(op.ptr, "drag_start", drag_start);
@ -140,7 +136,7 @@ static void pick_input_link_by_link_intersect(const bContext &C,
for (bNodeLink *link : socket->directly_linked_links()) {
/* Test if the cursor is near a link. */
std::array<float2, NODE_LINK_RESOL + 1> coords;
node_link_bezier_points_evaluated(socket_locations, *link, coords);
node_link_bezier_points_evaluated(*link, coords);
for (const int i : IndexRange(coords.size() - 1)) {
const float distance = dist_squared_to_line_segment_v2(cursor, coords[i], coords[i + 1]);
@ -293,12 +289,11 @@ struct LinkAndPosition {
float2 multi_socket_position;
};
static void sort_multi_input_socket_links_with_drag(const Span<float2> socket_locations,
bNodeSocket &socket,
static void sort_multi_input_socket_links_with_drag(bNodeSocket &socket,
bNodeLink &drag_link,
const float2 &cursor)
{
const float2 &socket_location = socket_locations[socket.index_in_tree()];
const float2 &socket_location = socket.runtime->location;
Vector<LinkAndPosition, 8> links;
for (bNodeLink *link : socket.directly_linked_links()) {
@ -646,8 +641,7 @@ static int view_socket(const bContext &C,
}
}
if (viewer_node == nullptr) {
const float2 socket_location =
btree.runtime->all_socket_locations[bsocket_to_view.index_in_tree()];
const float2 socket_location = bsocket_to_view.runtime->location;
const int viewer_type = get_default_viewer_type(&C);
const float2 location{socket_location.x / UI_SCALE_FAC + 100,
socket_location.y / UI_SCALE_FAC};
@ -1116,12 +1110,7 @@ static void node_link_cancel(bContext *C, wmOperator *op)
static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cursor)
{
SpaceNode &snode = *CTX_wm_space_node(&C);
bNodeTree &node_tree = *snode.edittree;
bNodeLinkDrag &nldrag = *static_cast<bNodeLinkDrag *>(op.customdata);
const Span<float2> socket_locations = node_tree.runtime->all_socket_locations;
if (socket_locations.is_empty()) {
return;
}
if (nldrag.in_out == SOCK_OUT) {
if (bNodeSocket *tsock = node_find_indicated_socket(snode, cursor, SOCK_IN)) {
@ -1152,7 +1141,7 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur
continue;
}
if (tsock && tsock->is_multi_input()) {
sort_multi_input_socket_links_with_drag(socket_locations, *tsock, link, cursor);
sort_multi_input_socket_links_with_drag(*tsock, link, cursor);
}
}
}
@ -1525,15 +1514,14 @@ static int cut_links_exec(bContext *C, wmOperator *op)
bNodeTree &node_tree = *snode.edittree;
node_tree.ensure_topology_cache();
const Span<float2> socket_locations = node_tree.runtime->all_socket_locations;
Set<bNodeLink *> links_to_remove;
LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
if (node_link_is_hidden_or_dimmed(socket_locations, region.v2d, *link)) {
if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
continue;
}
if (link_path_intersection(socket_locations, *link, path)) {
if (link_path_intersection(*link, path)) {
if (!found) {
/* TODO(sergey): Why did we kill jobs twice? */
@ -1611,7 +1599,6 @@ static int mute_links_exec(bContext *C, wmOperator *op)
SpaceNode &snode = *CTX_wm_space_node(C);
const ARegion &region = *CTX_wm_region(C);
bNodeTree &ntree = *snode.edittree;
const Span<float2> socket_locations = ntree.runtime->all_socket_locations;
Vector<float2> path;
RNA_BEGIN (op->ptr, itemptr, "path") {
@ -1636,10 +1623,10 @@ static int mute_links_exec(bContext *C, wmOperator *op)
Set<bNodeLink *> affected_links;
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
if (node_link_is_hidden_or_dimmed(socket_locations, region.v2d, *link)) {
if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
continue;
}
if (!link_path_intersection(socket_locations, *link, path)) {
if (!link_path_intersection(*link, path)) {
continue;
}
affected_links.add(link);
@ -2088,10 +2075,6 @@ void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion &region)
{
bNodeTree &node_tree = *snode.edittree;
node_tree.ensure_topology_cache();
const Span<float2> socket_locations = node_tree.runtime->all_socket_locations;
if (socket_locations.is_empty()) {
return;
}
node_insert_on_link_flags_clear(node_tree);
@ -2104,12 +2087,12 @@ void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion &region)
bNodeLink *selink = nullptr;
float dist_best = FLT_MAX;
LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
if (node_link_is_hidden_or_dimmed(socket_locations, region.v2d, *link)) {
if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
continue;
}
std::array<float2, NODE_LINK_RESOL + 1> coords;
node_link_bezier_points_evaluated(socket_locations, *link, coords);
node_link_bezier_points_evaluated(*link, coords);
float dist = FLT_MAX;
/* Loop over link coords to find shortest dist to upper left node edge of a intersected line

View File

@ -54,15 +54,17 @@
using blender::MutableSpan;
/** Pixels from bottom of strip. */
#define REMOVE_GIZMO_HEIGHT (14.0f * UI_SCALE_FAC)
/** Size in pixels. */
#define RETIME_HANDLE_TRIANGLE_SIZE (14.0f * UI_SCALE_FAC)
/** Size in pixels. */
#define RETIME_HANDLE_MOUSEOVER_THRESHOLD (16.0f * UI_SCALE_FAC)
/** Factor based on icon size. */
#define RETIME_BUTTON_SIZE 0.6f
static float remove_gizmo_height_get(const View2D *v2d)
{
const float max_size = (SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM) * UI_view2d_scale_get_y(v2d);
return min_ff(14.0f * UI_SCALE_FAC, max_size * 0.4f);
}
static float strip_y_rescale(const Sequence *seq, const float y_value)
{
const float y_range = SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM;
@ -147,8 +149,9 @@ static rctf strip_box_get(const bContext *C, const Sequence *seq)
static rctf remove_box_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
rctf rect = strip_box_get(C, seq);
rect.ymax = rect.ymin + REMOVE_GIZMO_HEIGHT;
rect.ymax = rect.ymin + remove_gizmo_height_get(v2d);
return rect;
}
@ -336,7 +339,7 @@ static void retime_handle_draw(const bContext *C,
return; /* Handle out of strip bounds. */
}
const int ui_triangle_size = RETIME_HANDLE_TRIANGLE_SIZE;
const int ui_triangle_size = remove_gizmo_height_get(v2d);
const float bottom = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0.0f)) + 2;
const float top = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 1.0f)) - 2;
const float handle_position = UI_view2d_view_to_region_x(v2d, handle_x);
@ -457,8 +460,9 @@ static int gizmo_retime_handle_test_select(bContext *C, wmGizmo *gz, const int m
return -1;
}
const View2D *v2d = UI_view2d_fromcontext(C);
rctf strip_box = strip_box_get(C, seq);
BLI_rctf_resize_x(&strip_box, BLI_rctf_size_x(&strip_box) + 2 * RETIME_HANDLE_TRIANGLE_SIZE);
BLI_rctf_resize_x(&strip_box, BLI_rctf_size_x(&strip_box) + 2 * remove_gizmo_height_get(v2d));
if (!mouse_is_inside_box(&strip_box, mval)) {
return -1;
}
@ -537,9 +541,9 @@ static int gizmo_retime_remove_test_select(bContext *C, wmGizmo *gz, const int m
return -1; /* Last handle can not be removed. */
}
const View2D *v2d = UI_view2d_fromcontext(C);
rctf box = remove_box_get(C, seq);
BLI_rctf_resize_x(&box, BLI_rctf_size_x(&box) + 2 * RETIME_HANDLE_TRIANGLE_SIZE);
BLI_rctf_resize_x(&box, BLI_rctf_size_x(&box) + 2 * remove_gizmo_height_get(v2d));
if (!mouse_is_inside_box(&box, mval)) {
return -1;
}

View File

@ -219,8 +219,7 @@ void spreadsheet_data_set_panel_draw(const bContext *C, Panel *panel)
std::make_unique<GeometryDataSetTreeView>(
spreadsheet_get_display_geometry_set(sspreadsheet, object), *C));
ui::TreeViewBuilder builder(*block);
builder.build_tree_view(*tree_view);
ui::TreeViewBuilder::build_tree_view(*tree_view, *layout);
}
} // namespace blender::ed::spreadsheet

View File

@ -24,6 +24,7 @@ set(SRC
intern/mesh_to_curve_convert.cc
intern/mesh_to_volume.cc
intern/point_merge_by_distance.cc
intern/points_to_volume.cc
intern/realize_instances.cc
intern/resample_curves.cc
intern/reverse_uv_sampler.cc
@ -42,6 +43,7 @@ set(SRC
GEO_mesh_to_curve.hh
GEO_mesh_to_volume.hh
GEO_point_merge_by_distance.hh
GEO_points_to_volume.hh
GEO_realize_instances.hh
GEO_resample_curves.hh
GEO_reverse_uv_sampler.hh

View File

@ -36,20 +36,25 @@ struct MeshToVolumeResolution {
*/
float volume_compute_voxel_size(const Depsgraph *depsgraph,
FunctionRef<void(float3 &r_min, float3 &r_max)> bounds_fn,
const MeshToVolumeResolution resolution,
MeshToVolumeResolution resolution,
float exterior_band_width,
const float4x4 &transform);
/**
* Add a new VolumeGrid to the Volume by converting the supplied mesh
* Add a new fog VolumeGrid to the Volume by converting the supplied mesh.
*/
VolumeGrid *volume_grid_add_from_mesh(Volume *volume,
const StringRefNull name,
const Mesh *mesh,
const float4x4 &mesh_to_volume_space_transform,
float voxel_size,
bool fill_volume,
float exterior_band_width,
float interior_band_width,
float density);
VolumeGrid *fog_volume_grid_add_from_mesh(Volume *volume,
StringRefNull name,
const Mesh *mesh,
const float4x4 &mesh_to_volume_space_transform,
float voxel_size,
bool fill_volume,
float exterior_band_width,
float interior_band_width,
float density);
/**
* Add a new SDF VolumeGrid to the Volume by converting the supplied mesh.
*/
VolumeGrid *sdf_volume_grid_add_from_mesh(
Volume *volume, StringRefNull name, const Mesh &mesh, float voxel_size, float half_band_width);
#endif
} // namespace blender::geometry

View File

@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_function_ref.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_string_ref.hh"
#include "DNA_modifier_types.h"
#pragma once
struct Volume;
struct VolumeGrid;
/** \file
* \ingroup geo
*/
namespace blender::geometry {
#ifdef WITH_OPENVDB
/**
* Add a new fog VolumeGrid to the Volume by converting the supplied points.
*/
VolumeGrid *fog_volume_grid_add_from_points(Volume *volume,
StringRefNull name,
Span<float3> positions,
Span<float> radii,
float voxel_size,
float density);
/**
* Add a new SDF VolumeGrid to the Volume by converting the supplied points.
*/
VolumeGrid *sdf_volume_grid_add_from_points(Volume *volume,
StringRefNull name,
Span<float3> positions,
Span<float> radii,
float voxel_size);
#endif
} // namespace blender::geometry

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_matrix.hh"
#include "BLI_task.hh"
#include "BKE_mesh.hh"
#include "BKE_mesh_runtime.h"
@ -97,13 +98,14 @@ float volume_compute_voxel_size(const Depsgraph *depsgraph,
return voxel_size;
}
static openvdb::FloatGrid::Ptr mesh_to_volume_grid(const Mesh *mesh,
const float4x4 &mesh_to_volume_space_transform,
const float voxel_size,
const bool fill_volume,
const float exterior_band_width,
const float interior_band_width,
const float density)
static openvdb::FloatGrid::Ptr mesh_to_fog_volume_grid(
const Mesh *mesh,
const float4x4 &mesh_to_volume_space_transform,
const float voxel_size,
const bool fill_volume,
const float exterior_band_width,
const float interior_band_width,
const float density)
{
if (voxel_size == 0.0f) {
return nullptr;
@ -120,47 +122,80 @@ static openvdb::FloatGrid::Ptr mesh_to_volume_grid(const Mesh *mesh,
const float exterior = MAX2(0.001f, exterior_band_width / voxel_size);
const float interior = MAX2(0.001f, interior_band_width / voxel_size);
openvdb::FloatGrid::Ptr new_grid;
if (fill_volume) {
/* Setting the interior bandwidth to FLT_MAX, will make it fill the entire volume. */
new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
mesh_adapter, {}, exterior, FLT_MAX);
}
else {
new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
mesh_adapter, {}, exterior, interior);
}
/* Setting the interior bandwidth to FLT_MAX, will make it fill the entire volume. */
openvdb::FloatGrid::Ptr new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
mesh_adapter, {}, exterior, fill_volume ? FLT_MAX : interior);
/* Give each grid cell a fixed density for now. */
openvdb::tools::foreach (
new_grid->beginValueOn(),
[density](const openvdb::FloatGrid::ValueOnIter &iter) { iter.setValue(density); });
new_grid->setGridClass(openvdb::GRID_FOG_VOLUME);
return new_grid;
}
VolumeGrid *volume_grid_add_from_mesh(Volume *volume,
const StringRefNull name,
const Mesh *mesh,
const float4x4 &mesh_to_volume_space_transform,
const float voxel_size,
const bool fill_volume,
const float exterior_band_width,
const float interior_band_width,
const float density)
static openvdb::FloatGrid::Ptr mesh_to_sdf_volume_grid(const Mesh &mesh,
const float voxel_size,
const float half_band_width)
{
if (voxel_size <= 0.0f || half_band_width <= 0.0f) {
return nullptr;
}
const Span<float3> positions = mesh.vert_positions();
const Span<MLoop> loops = mesh.loops();
const Span<MLoopTri> looptris = mesh.looptris();
std::vector<openvdb::Vec3s> points(positions.size());
std::vector<openvdb::Vec3I> triangles(looptris.size());
threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
for (const int i : range) {
const float3 &co = positions[i];
points[i] = openvdb::Vec3s(co.x, co.y, co.z) - 0.5f * voxel_size;
}
});
threading::parallel_for(looptris.index_range(), 2048, [&](const IndexRange range) {
for (const int i : range) {
const MLoopTri &loop_tri = looptris[i];
triangles[i] = openvdb::Vec3I(
loops[loop_tri.tri[0]].v, loops[loop_tri.tri[1]].v, loops[loop_tri.tri[2]].v);
}
});
openvdb::math::Transform::Ptr transform = openvdb::math::Transform::createLinearTransform(
voxel_size);
openvdb::FloatGrid::Ptr new_grid = openvdb::tools::meshToLevelSet<openvdb::FloatGrid>(
*transform, points, triangles, half_band_width);
return new_grid;
}
VolumeGrid *fog_volume_grid_add_from_mesh(Volume *volume,
const StringRefNull name,
const Mesh *mesh,
const float4x4 &mesh_to_volume_space_transform,
const float voxel_size,
const bool fill_volume,
const float exterior_band_width,
const float interior_band_width,
const float density)
{
VolumeGrid *c_grid = BKE_volume_grid_add(volume, name.c_str(), VOLUME_GRID_FLOAT);
openvdb::FloatGrid::Ptr grid = openvdb::gridPtrCast<openvdb::FloatGrid>(
BKE_volume_grid_openvdb_for_write(volume, c_grid, false));
/* Generate grid from mesh */
openvdb::FloatGrid::Ptr mesh_grid = mesh_to_volume_grid(mesh,
mesh_to_volume_space_transform,
voxel_size,
fill_volume,
exterior_band_width,
interior_band_width,
density);
openvdb::FloatGrid::Ptr mesh_grid = mesh_to_fog_volume_grid(mesh,
mesh_to_volume_space_transform,
voxel_size,
fill_volume,
exterior_band_width,
interior_band_width,
density);
if (mesh_grid != nullptr) {
/* Merge the generated grid. Should be cheap because grid has just been created. */
@ -168,9 +203,17 @@ VolumeGrid *volume_grid_add_from_mesh(Volume *volume,
/* Change transform so that the index space is correctly transformed to object space. */
grid->transform().postScale(voxel_size);
}
/* Set class to "Fog Volume". */
grid->setGridClass(openvdb::GRID_FOG_VOLUME);
return c_grid;
}
VolumeGrid *sdf_volume_grid_add_from_mesh(Volume *volume,
const StringRefNull name,
const Mesh &mesh,
const float voxel_size,
const float half_band_width)
{
openvdb::FloatGrid::Ptr mesh_grid = mesh_to_sdf_volume_grid(mesh, voxel_size, half_band_width);
return mesh_grid ? BKE_volume_grid_add_vdb(*volume, name, std::move(mesh_grid)) : nullptr;
}
} // namespace blender::geometry
#endif

View File

@ -0,0 +1,96 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_matrix.hh"
#include "BKE_volume.h"
#include "GEO_points_to_volume.hh"
#ifdef WITH_OPENVDB
# include <openvdb/openvdb.h>
# include <openvdb/tools/LevelSetUtil.h>
# include <openvdb/tools/ParticlesToLevelSet.h>
namespace blender::geometry {
/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */
struct OpenVDBParticleList {
using PosType = openvdb::Vec3R;
Span<float3> positions;
Span<float> radii;
size_t size() const
{
return size_t(positions.size());
}
void getPos(size_t n, openvdb::Vec3R &xyz) const
{
xyz = &positions[n].x;
}
void getPosRad(size_t n, openvdb::Vec3R &xyz, openvdb::Real &radius) const
{
xyz = &positions[n].x;
radius = radii[n];
}
};
static openvdb::FloatGrid::Ptr points_to_sdf_grid(const Span<float3> positions,
const Span<float> radii)
{
/* Create a new grid that will be filled. #ParticlesToLevelSet requires
* the background value to be positive */
openvdb::FloatGrid::Ptr new_grid = openvdb::FloatGrid::create(1.0f);
/* Create a narrow-band level set grid based on the positions and radii. */
openvdb::tools::ParticlesToLevelSet op{*new_grid};
/* Don't ignore particles based on their radius. */
op.setRmin(0.0f);
op.setRmax(FLT_MAX);
OpenVDBParticleList particles{positions, radii};
op.rasterizeSpheres(particles);
op.finalize();
return new_grid;
}
VolumeGrid *fog_volume_grid_add_from_points(Volume *volume,
const StringRefNull name,
const Span<float3> positions,
const Span<float> radii,
const float voxel_size,
const float density)
{
openvdb::FloatGrid::Ptr new_grid = points_to_sdf_grid(positions, radii);
new_grid->transform().postScale(voxel_size);
new_grid->setGridClass(openvdb::GRID_FOG_VOLUME);
/* Convert the level set to a fog volume. This also sets the background value to zero. Inside the
* fog there will be a density of 1. */
openvdb::tools::sdfToFogVolume(*new_grid);
/* Take the desired density into account. */
openvdb::tools::foreach (new_grid->beginValueOn(),
[&](const openvdb::FloatGrid::ValueOnIter &iter) {
iter.modifyValue([&](float &value) { value *= density; });
});
return BKE_volume_grid_add_vdb(*volume, name, std::move(new_grid));
}
VolumeGrid *sdf_volume_grid_add_from_points(Volume *volume,
const StringRefNull name,
const Span<float3> positions,
const Span<float> radii,
const float voxel_size)
{
openvdb::FloatGrid::Ptr new_grid = points_to_sdf_grid(positions, radii);
new_grid->transform().postScale(voxel_size);
new_grid->setGridClass(openvdb::GRID_LEVEL_SET);
return BKE_volume_grid_add_vdb(*volume, name, std::move(new_grid));
}
} // namespace blender::geometry
#endif

View File

@ -604,6 +604,7 @@ list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR})
set(SRC_SHADER_CREATE_INFOS
../draw/engines/basic/shaders/infos/basic_depth_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh

View File

@ -1,18 +1,23 @@
/* Float Math */
/* WORKAROUND: To be removed once we port all code to use gpu_shader_math_base_lib.glsl. */
#ifndef GPU_SHADER_MATH_BASE_LIB_GLSL
float safe_divide(float a, float b)
{
return (b != 0.0) ? a / b : 0.0;
}
#endif
/* fmod function compatible with OSL (copy from OSL/dual.h) */
float compatible_fmod(float a, float b)
{
if (b != 0.0f) {
if (b != 0.0) {
int N = int(a / b);
return a - N * b;
}
return 0.0f;
return 0.0;
}
float compatible_pow(float x, float y)
@ -59,11 +64,16 @@ vec3 wrap(vec3 a, vec3 b, vec3 c)
return vec3(wrap(a.x, b.x, c.x), wrap(a.y, b.y, c.y), wrap(a.z, b.z, c.z));
}
/* WORKAROUND: To be removed once we port all code to use gpu_shader_math_base_lib.glsl. */
#ifndef GPU_SHADER_MATH_BASE_LIB_GLSL
float hypot(float x, float y)
{
return sqrt(x * x + y * y);
}
#endif
int floor_to_int(float x)
{
return int(floor(x));
@ -76,6 +86,9 @@ int quick_floor(float x)
/* Vector Math */
/* WORKAROUND: To be removed once we port all code to use gpu_shader_math_base_lib.glsl. */
#ifndef GPU_SHADER_MATH_BASE_LIB_GLSL
vec2 safe_divide(vec2 a, vec2 b)
{
return vec2(safe_divide(a.x, b.x), safe_divide(a.y, b.y));
@ -107,6 +120,8 @@ vec4 safe_divide(vec4 a, float b)
return (b != 0.0) ? a / b : vec4(0.0);
}
#endif
vec3 compatible_fmod(vec3 a, vec3 b)
{
return vec3(compatible_fmod(a.x, b.x), compatible_fmod(a.y, b.y), compatible_fmod(a.z, b.z));

View File

@ -60,7 +60,7 @@ set(LIB
blender_add_lib(bf_ply "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if (WITH_GTESTS)
if(WITH_GTESTS)
set(TEST_SRC
tests/io_ply_importer_test.cc
tests/io_ply_exporter_test.cc

View File

@ -64,18 +64,10 @@ void FileBuffer::write_newline()
write_fstring("\n");
}
void FileBuffer::ensure_space(size_t at_least)
{
if (blocks_.empty() || (blocks_.back().capacity() - blocks_.back().size() < at_least)) {
VectorChar &b = blocks_.emplace_back(VectorChar());
b.reserve(std::max(at_least, buffer_chunk_size_));
}
}
void FileBuffer::write_bytes(Span<char> bytes)
{
ensure_space(bytes.size());
VectorChar &bb = blocks_.back();
VectorChar &bb = blocks_.last();
bb.insert(bb.end(), bytes.begin(), bytes.end());
}

View File

@ -6,11 +6,8 @@
#pragma once
#include <string>
#include <type_traits>
#include <vector>
#include "BLI_array.hh"
#include "BLI_compiler_attrs.h"
#include "BLI_fileops.h"
#include "BLI_string_ref.hh"
@ -33,7 +30,7 @@ namespace blender::io::ply {
*/
class FileBuffer : private NonMovable {
using VectorChar = Vector<char>;
std::vector<VectorChar> blocks_;
Vector<VectorChar> blocks_;
size_t buffer_chunk_size_;
const char *filepath_;
FILE *outfile_;
@ -75,7 +72,13 @@ class FileBuffer : private NonMovable {
protected:
/* Ensure the last block contains at least this amount of free space.
* If not, add a new block with max of block size & the amount of space needed. */
void ensure_space(size_t at_least);
void ensure_space(size_t at_least)
{
if (blocks_.is_empty() || (blocks_.last().capacity() - blocks_.last().size() < at_least)) {
blocks_.append(VectorChar());
blocks_.last().reserve(std::max(at_least, buffer_chunk_size_));
}
}
template<typename... T> void write_fstring(const char *fmt, T &&...args)
{
@ -84,7 +87,7 @@ class FileBuffer : private NonMovable {
fmt::format_to(fmt::appender(buf), fmt, std::forward<T>(args)...);
size_t len = buf.size();
ensure_space(len);
VectorChar &bb = blocks_.back();
VectorChar &bb = blocks_.last();
bb.insert(bb.end(), buf.begin(), buf.end());
}

View File

@ -426,37 +426,107 @@ class ply_exporter_ply_data_test : public ply_export_test {
}
};
TEST_F(ply_exporter_ply_data_test, CubeLoadPLYDataVertices)
TEST_F(ply_exporter_ply_data_test, CubeLoadPLYData)
{
PLYExportParams params = {};
PlyData plyData = load_ply_data_from_blendfile(
"io_tests" SEP_STR "blend_geometry" SEP_STR "cube_all_data.blend", params);
PlyData plyData = load_ply_data_from_blendfile("io_tests/blend_geometry/cube_all_data.blend",
params);
EXPECT_EQ(plyData.vertices.size(), 8);
EXPECT_EQ(plyData.uv_coordinates.size(), 0);
}
TEST_F(ply_exporter_ply_data_test, CubeLoadPLYDataUV)
{
PLYExportParams params = {};
params.export_uv = true;
PlyData plyData = load_ply_data_from_blendfile(
"io_tests" SEP_STR "blend_geometry" SEP_STR "cube_all_data.blend", params);
PlyData plyData = load_ply_data_from_blendfile("io_tests/blend_geometry/cube_all_data.blend",
params);
EXPECT_EQ(plyData.vertices.size(), 8);
EXPECT_EQ(plyData.uv_coordinates.size(), 8);
}
TEST_F(ply_exporter_ply_data_test, CubeLooseEdgesLoadPLYData)
{
PLYExportParams params = {};
params.forward_axis = IO_AXIS_Y;
params.up_axis = IO_AXIS_Z;
params.global_scale = 1.0f;
PlyData plyData = load_ply_data_from_blendfile(
"io_tests/blend_geometry/cube_loose_edges_verts.blend", params);
float3 exp_vertices[] = {
{1, 1, 1},
{1, 1, -1},
{1, -1, 1},
{1, -1, -1},
{-1, 1, 1},
{-1, 1, -1},
{-1, -1, 1},
{-1, -1, -1},
};
std::pair<int, int> exp_edges[] = {{7, 6}, {6, 4}};
uint32_t exp_face_sizes[] = {4, 4};
uint32_t exp_faces[] = {5, 1, 3, 7, 5, 4, 0, 1};
EXPECT_EQ(plyData.vertices.size(), ARRAY_SIZE(exp_vertices));
EXPECT_EQ(plyData.uv_coordinates.size(), 0);
EXPECT_EQ(plyData.edges.size(), ARRAY_SIZE(exp_edges));
EXPECT_EQ(plyData.face_sizes.size(), ARRAY_SIZE(exp_face_sizes));
EXPECT_EQ(plyData.face_vertices.size(), ARRAY_SIZE(exp_faces));
EXPECT_EQ_ARRAY(exp_vertices, plyData.vertices.data(), ARRAY_SIZE(exp_vertices));
EXPECT_EQ_ARRAY(exp_edges, plyData.edges.data(), ARRAY_SIZE(exp_edges));
EXPECT_EQ_ARRAY(exp_face_sizes, plyData.face_sizes.data(), ARRAY_SIZE(exp_face_sizes));
EXPECT_EQ_ARRAY(exp_faces, plyData.face_vertices.data(), ARRAY_SIZE(exp_faces));
}
TEST_F(ply_exporter_ply_data_test, CubeLooseEdgesLoadPLYDataUV)
{
PLYExportParams params = {};
params.forward_axis = IO_AXIS_Y;
params.up_axis = IO_AXIS_Z;
params.global_scale = 1.0f;
params.export_uv = true;
PlyData plyData = load_ply_data_from_blendfile(
"io_tests/blend_geometry/cube_loose_edges_verts.blend", params);
float3 exp_vertices[] = {
{1, 1, 1},
{1, 1, -1},
{1, -1, 1},
{1, -1, -1},
{-1, 1, 1},
{-1, 1, -1},
{-1, 1, -1},
{-1, -1, 1},
{-1, -1, -1},
};
float2 exp_uv[] = {
{0.625f, 0.5f},
{0.375f, 0.5f},
{0, 0},
{0.375f, 0.75f},
{0.625f, 0.25f},
{0.125f, 0.5f},
{0.375f, 0.25f},
{0, 0},
{0.125f, 0.75f},
};
std::pair<int, int> exp_edges[] = {{8, 7}, {7, 4}};
uint32_t exp_face_sizes[] = {4, 4};
uint32_t exp_faces[] = {5, 1, 3, 8, 6, 4, 0, 1};
EXPECT_EQ(plyData.vertices.size(), 9);
EXPECT_EQ(plyData.uv_coordinates.size(), 9);
EXPECT_EQ(plyData.edges.size(), ARRAY_SIZE(exp_edges));
EXPECT_EQ(plyData.face_sizes.size(), ARRAY_SIZE(exp_face_sizes));
EXPECT_EQ(plyData.face_vertices.size(), ARRAY_SIZE(exp_faces));
EXPECT_EQ_ARRAY(exp_vertices, plyData.vertices.data(), ARRAY_SIZE(exp_vertices));
EXPECT_EQ_ARRAY(exp_uv, plyData.uv_coordinates.data(), ARRAY_SIZE(exp_uv));
EXPECT_EQ_ARRAY(exp_edges, plyData.edges.data(), ARRAY_SIZE(exp_edges));
EXPECT_EQ_ARRAY(exp_face_sizes, plyData.face_sizes.data(), ARRAY_SIZE(exp_face_sizes));
EXPECT_EQ_ARRAY(exp_faces, plyData.face_vertices.data(), ARRAY_SIZE(exp_faces));
}
TEST_F(ply_exporter_ply_data_test, SuzanneLoadPLYDataUV)
{
PLYExportParams params = {};
params.export_uv = true;
PlyData plyData = load_ply_data_from_blendfile(
"io_tests" SEP_STR "blend_geometry" SEP_STR "suzanne_all_data.blend", params);
PlyData plyData = load_ply_data_from_blendfile("io_tests/blend_geometry/suzanne_all_data.blend",
params);
EXPECT_EQ(plyData.uv_coordinates.size(), 542);
}
TEST_F(ply_exporter_ply_data_test, CubeLoadPLYDataUVDisabled)
{
PLYExportParams params = {};
params.export_uv = false;
PlyData plyData = load_ply_data_from_blendfile(
"io_tests" SEP_STR "blend_geometry" SEP_STR "cube_all_data.blend", params);
EXPECT_EQ(plyData.uv_coordinates.size(), 0);
}
} // namespace blender::io::ply

View File

@ -786,10 +786,27 @@ void USDMaterialReader::convert_usd_primvar_reader_float2(
/* Set the texmap name. */
pxr::UsdShadeInput varname_input = usd_shader.GetInput(usdtokens::varname);
/* First check if the shader's "varname" input is connected to another source,
* and use that instead if so. */
if (varname_input) {
for (const pxr::UsdShadeConnectionSourceInfo &source_info :
varname_input.GetConnectedSources()) {
pxr::UsdShadeShader shader = pxr::UsdShadeShader(source_info.source.GetPrim());
pxr::UsdShadeInput secondary_varname_input = shader.GetInput(source_info.sourceName);
if (secondary_varname_input) {
varname_input = secondary_varname_input;
break;
}
}
}
if (varname_input) {
pxr::VtValue varname_val;
if (varname_input.Get(&varname_val) && varname_val.IsHolding<pxr::TfToken>()) {
std::string varname = varname_val.Get<pxr::TfToken>().GetString();
/* The varname input may be a string or TfToken, so just cast it to a string.
* The Cast function is defined to provide an empty result if it fails. */
if (varname_input.Get(&varname_val) && varname_val.CanCastToTypeid(typeid(std::string))) {
std::string varname = varname_val.Cast<std::string>().Get<std::string>();
if (!varname.empty()) {
NodeShaderUVMap *storage = (NodeShaderUVMap *)uv_map->storage;
BLI_strncpy(storage->uv_map, varname.c_str(), sizeof(storage->uv_map));

View File

@ -7,14 +7,13 @@
#pragma once
#include <cstdio>
#include <string>
#include <type_traits>
#include <vector>
#include "BLI_compiler_attrs.h"
#include "BLI_fileops.h"
#include "BLI_string_ref.hh"
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
/* SEP macro from BLI path utils clashes with SEP symbol in fmt headers. */
#undef SEP
@ -32,8 +31,8 @@ namespace blender::io::obj {
*/
class FormatHandler : NonCopyable, NonMovable {
private:
typedef std::vector<char> VectorChar;
std::vector<VectorChar> blocks_;
using VectorChar = Vector<char>;
Vector<VectorChar> blocks_;
size_t buffer_chunk_size_;
public:
@ -202,9 +201,9 @@ class FormatHandler : NonCopyable, NonMovable {
* If not, add a new block with max of block size & the amount of space needed. */
void ensure_space(size_t at_least)
{
if (blocks_.empty() || (blocks_.back().capacity() - blocks_.back().size() < at_least)) {
VectorChar &b = blocks_.emplace_back(VectorChar());
b.reserve(std::max(at_least, buffer_chunk_size_));
if (blocks_.is_empty() || (blocks_.last().capacity() - blocks_.last().size() < at_least)) {
blocks_.append(VectorChar());
blocks_.last().reserve(std::max(at_least, buffer_chunk_size_));
}
}
@ -215,7 +214,7 @@ class FormatHandler : NonCopyable, NonMovable {
fmt::format_to(fmt::appender(buf), fmt, std::forward<T>(args)...);
size_t len = buf.size();
ensure_space(len);
VectorChar &bb = blocks_.back();
VectorChar &bb = blocks_.last();
bb.insert(bb.end(), buf.begin(), buf.end());
}
};

View File

@ -330,10 +330,10 @@
.edge_types = LRT_EDGE_FLAG_INIT_TYPE, \
.thickness = 25, \
.opacity = 1.0f, \
.flags = LRT_GPENCIL_MATCH_OUTPUT_VGROUP, \
.crease_threshold = DEG2RAD(140.0f), \
.calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | \
LRT_USE_CREASE_ON_SHARP_EDGES | LRT_FILTER_FACE_MARK_KEEP_CONTOUR, \
LRT_USE_CREASE_ON_SHARP_EDGES | LRT_FILTER_FACE_MARK_KEEP_CONTOUR | \
LRT_GPENCIL_MATCH_OUTPUT_VGROUP, \
/* Do not split by default, this is for better chaining quality. */ \
.angle_splitting_threshold = 0.0f, \
.chaining_image_threshold = 0.001f, \

View File

@ -725,7 +725,7 @@ static void rna_MeshPolygon_material_index_set(PointerRNA *ptr, int value)
Mesh *mesh = rna_mesh(ptr);
int *material_indices = BKE_mesh_material_indices_for_write(mesh);
const int index = rna_MeshPolygon_index_get(ptr);
material_indices[index] = value;
material_indices[index] = max_ii(0, value);
}
static void rna_MeshPolygon_center_get(PointerRNA *ptr, float *values)

View File

@ -10000,6 +10000,32 @@ static void def_geo_points_to_volume(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_points_to_sdf_volume(StructRNA *srna)
{
PropertyRNA *prop;
static EnumPropertyItem resolution_mode_items[] = {
{GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT,
"VOXEL_AMOUNT",
0,
"Amount",
"Specify the approximate number of voxels along the diagonal"},
{GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE,
"VOXEL_SIZE",
0,
"Size",
"Specify the voxel side length"},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryPointsToVolume", "storage");
prop = RNA_def_property(srna, "resolution_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, resolution_mode_items);
RNA_def_property_ui_text(prop, "Resolution Mode", "How the voxel size is specified");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_uv_unwrap(StructRNA *srna)
{
PropertyRNA *prop;
@ -10147,6 +10173,32 @@ static void def_geo_mesh_to_volume(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_mesh_to_sdf_volume(StructRNA *srna)
{
PropertyRNA *prop;
static EnumPropertyItem resolution_mode_items[] = {
{MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT,
"VOXEL_AMOUNT",
0,
"Amount",
"Desired number of voxels along one axis"},
{MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE,
"VOXEL_SIZE",
0,
"Size",
"Desired voxel side length"},
{0, NULL, 0, NULL, NULL},
};
RNA_def_struct_sdna_from(srna, "NodeGeometryMeshToVolume", "storage");
prop = RNA_def_property(srna, "resolution_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, resolution_mode_items);
RNA_def_property_ui_text(prop, "Resolution Mode", "How the voxel size is specified");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_mesh_circle(StructRNA *srna)
{
PropertyRNA *prop;

View File

@ -3328,6 +3328,7 @@ static void rna_def_text(StructRNA *srna)
prop = RNA_def_property(srna, "text", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Text", "Text that will be displayed");
RNA_def_property_flag(prop, PROP_TEXTEDIT_UPDATE);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "use_shadow", PROP_BOOLEAN, PROP_NONE);

View File

@ -5497,7 +5497,7 @@ static void rna_def_userdef_system(BlenderRNA *brna)
prop = RNA_def_property(srna, "ui_scale", PROP_FLOAT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_float_sdna(prop, NULL, "ui_scale");
RNA_def_property_float_sdna(prop, NULL, "scale_factor");
RNA_def_property_ui_text(
prop,
"UI Scale",

View File

@ -173,15 +173,15 @@ static Volume *mesh_to_volume(ModifierData *md,
}
/* Convert mesh to grid and add to volume. */
geometry::volume_grid_add_from_mesh(volume,
"density",
mesh,
mesh_to_own_object_space_transform,
voxel_size,
mvmd->fill_volume,
mvmd->exterior_band_width,
mvmd->interior_band_width,
mvmd->density);
geometry::fog_volume_grid_add_from_mesh(volume,
"density",
mesh,
mesh_to_own_object_space_transform,
voxel_size,
mvmd->fill_volume,
mvmd->exterior_band_width,
mvmd->interior_band_width,
mvmd->density);
return volume;

View File

@ -697,7 +697,7 @@ ModifierTypeInfo modifierType_Ocean = {
/*icon*/ ICON_MOD_OCEAN,
/*copyData*/ copyData,
/*deformMatrices_DM*/ nullptr,
/*deformMatrices*/ nullptr,
/*deformMatrices*/ nullptr,
/*deformVertsEM*/ nullptr,

View File

@ -516,12 +516,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
/* Get our vertex coordinates. */
if (index_num != verts_num) {
float(*tv_cos)[3] = BKE_mesh_vert_coords_alloc(mesh, nullptr);
const float(*tv_cos)[3] = BKE_mesh_vert_positions(mesh);
v_cos = static_cast<float(*)[3]>(MEM_malloc_arrayN(index_num, sizeof(float[3]), __func__));
for (i = 0; i < index_num; i++) {
copy_v3_v3(v_cos[i], tv_cos[indices[i]]);
}
MEM_freeN(tv_cos);
}
else {
v_cos = BKE_mesh_vert_coords_alloc(mesh, nullptr);

View File

@ -357,6 +357,7 @@ DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS",Ins
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "Retrieve whether the nodes are being evaluated for the viewport rather than the final render")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "Merge separately generated geometries into a single one")
DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "Provide a selection of faces that use the specified material")
DefNode(GeometryNode, GEO_NODE_MEAN_FILTER_SDF_VOLUME, 0, "MEAN_FILTER_SDF_VOLUME", MeanFilterSDFVolume, "Mean Filter SDF Volume", "Smooth the surface of an SDF volume by applying a mean filter")
DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, def_geo_merge_by_distance,"MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "Merge vertices or points within a given distance")
DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "Cut, subtract, or join multiple mesh inputs")
DefNode(GeometryNode, GEO_NODE_MESH_FACE_GROUP_BOUNDARIES, 0, "MESH_FACE_SET_BOUNDARIES", MeshFaceSetBoundaries, "Face Group Boundaries", "Find edges on the boundaries between groups of faces with the same ID value")
@ -370,6 +371,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRI
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "Generate a spherical mesh with quads, except for triangles at the top and bottom")
DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "Generate a curve from a mesh")
DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "Generate a point cloud from a mesh's vertices")
DefNode(GeometryNode, GEO_NODE_MESH_TO_SDF_VOLUME, def_geo_mesh_to_sdf_volume, "MESH_TO_SDF_VOLUME", MeshToSDFVolume, "Mesh to SDF Volume", "Create an SDF volume with the shape of the input mesh's surface")
DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh to Volume", "Create a fog volume with the shape of the input mesh's surface")
DefNode(GeometryNode, GEO_NODE_MESH_TOPOLOGY_CORNERS_OF_FACE, 0, "CORNERS_OF_FACE", CornersOfFace, "Corners of Face", "Retrieve corners that make up a face")
DefNode(GeometryNode, GEO_NODE_MESH_TOPOLOGY_CORNERS_OF_VERTEX, 0, "CORNERS_OF_VERTEX", CornersOfVertex, "Corners of Vertex", "Retrieve face corners connected to vertices")
@ -380,6 +382,8 @@ DefNode(GeometryNode, GEO_NODE_MESH_TOPOLOGY_OFFSET_CORNER_IN_FACE, 0, "OFFSET_C
DefNode(GeometryNode, GEO_NODE_MESH_TOPOLOGY_VERTEX_OF_CORNER, 0, "VERTEX_OF_CORNER", VertexOfCorner, "Vertex of Corner", "Retrieve the vertex each face corner is attached to")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "Retrieve information from an object")
DefNode(GeometryNode, GEO_NODE_OFFSET_POINT_IN_CURVE, 0, "OFFSET_POINT_IN_CURVE", OffsetPointInCurve, "Offset Point in Curve", "Offset a control point index within its curve")
DefNode(GeometryNode, GEO_NODE_OFFSET_SDF_VOLUME, 0, "OFFSET_SDF_VOLUME", OffsetSDFVolume, "Offset SDF Volume", "Move the surface of an SDF volume inwards or outwards")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_SDF_VOLUME, def_geo_points_to_sdf_volume, "POINTS_TO_SDF_VOLUME", PointsToSDFVolume, "Points to SDF Volume", "Generate an SDF volume sphere around every point")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "Generate a mesh vertex for each point cloud point")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "Generate a fog volume sphere around every point")
DefNode(GeometryNode, GEO_NODE_POINTS, 0, "POINTS", Points, "Points", "Generate a point cloud with positions and radii defined by fields")
@ -398,6 +402,7 @@ DefNode(GeometryNode, GEO_NODE_SAMPLE_NEAREST, def_geo_sample_nearest, "SAMPLE_N
DefNode(GeometryNode, GEO_NODE_SAMPLE_UV_SURFACE, def_geo_sample_uv_surface, "SAMPLE_UV_SURFACE", SampleUVSurface, "Sample UV Surface", "Calculate the interpolated values of a mesh attribute at a UV coordinate")
DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "Scale groups of connected edges and faces")
DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "Scale geometry instances in local or global space")
DefNode(GeometryNode, GEO_NODE_SDF_VOLUME_SPHERE, 0, "SDF_VOLUME_SPHERE", SDFVolumeSphere, "SDF Volume Sphere", "Generate an SDF Volume Sphere")
DefNode(GeometryNode, GEO_NODE_SELF_OBJECT, 0, "SELF_OBJECT", SelfObject, "Self Object", "Retrieve the object that contains the geometry nodes modifier currently being executed")
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS",SeparateComponents, "Separate Components","Split a geometry into a separate output for each type of data in the geometry")
DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry,"SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "Split a geometry into two geometry outputs based on a selection")

View File

@ -112,6 +112,7 @@ set(SRC
nodes/node_geo_join_geometry.cc
nodes/node_geo_material_replace.cc
nodes/node_geo_material_selection.cc
nodes/node_geo_mean_filter_sdf_volume.cc
nodes/node_geo_merge_by_distance.cc
nodes/node_geo_mesh_face_group_boundaries.cc
nodes/node_geo_mesh_primitive_circle.cc
@ -124,6 +125,7 @@ set(SRC
nodes/node_geo_mesh_primitive_uv_sphere.cc
nodes/node_geo_mesh_subdivide.cc
nodes/node_geo_mesh_to_curve.cc
nodes/node_geo_mesh_to_sdf_volume.cc
nodes/node_geo_mesh_to_points.cc
nodes/node_geo_mesh_to_volume.cc
nodes/node_geo_mesh_topology_corners_of_face.cc
@ -135,8 +137,10 @@ set(SRC
nodes/node_geo_mesh_topology_vertex_of_corner.cc
nodes/node_geo_object_info.cc
nodes/node_geo_offset_point_in_curve.cc
nodes/node_geo_offset_sdf_volume.cc
nodes/node_geo_points.cc
nodes/node_geo_points_to_vertices.cc
nodes/node_geo_points_to_sdf_volume.cc
nodes/node_geo_points_to_volume.cc
nodes/node_geo_proximity.cc
nodes/node_geo_raycast.cc
@ -149,6 +153,7 @@ set(SRC
nodes/node_geo_sample_uv_surface.cc
nodes/node_geo_scale_elements.cc
nodes/node_geo_scale_instances.cc
nodes/node_geo_sdf_volume_sphere.cc
nodes/node_geo_self_object.cc
nodes/node_geo_separate_components.cc
nodes/node_geo_separate_geometry.cc

View File

@ -96,6 +96,7 @@ void register_geometry_nodes()
register_node_type_geo_join_geometry();
register_node_type_geo_material_replace();
register_node_type_geo_material_selection();
register_node_type_geo_mean_filter_sdf_volume();
register_node_type_geo_merge_by_distance();
register_node_type_geo_mesh_face_group_boundaries();
register_node_type_geo_mesh_primitive_circle();
@ -109,6 +110,7 @@ void register_geometry_nodes()
register_node_type_geo_mesh_subdivide();
register_node_type_geo_mesh_to_curve();
register_node_type_geo_mesh_to_points();
register_node_type_geo_mesh_to_sdf_volume();
register_node_type_geo_mesh_to_volume();
register_node_type_geo_mesh_topology_corners_of_face();
register_node_type_geo_mesh_topology_corners_of_vertex();
@ -119,7 +121,9 @@ void register_geometry_nodes()
register_node_type_geo_mesh_topology_vertex_of_corner();
register_node_type_geo_object_info();
register_node_type_geo_offset_point_in_curve();
register_node_type_geo_offset_sdf_volume();
register_node_type_geo_points_to_vertices();
register_node_type_geo_points_to_sdf_volume();
register_node_type_geo_points_to_volume();
register_node_type_geo_points();
register_node_type_geo_proximity();
@ -133,6 +137,7 @@ void register_geometry_nodes()
register_node_type_geo_sample_uv_surface();
register_node_type_geo_scale_elements();
register_node_type_geo_scale_instances();
register_node_type_geo_sdf_volume_sphere();
register_node_type_geo_self_object();
register_node_type_geo_separate_components();
register_node_type_geo_separate_geometry();

View File

@ -94,6 +94,7 @@ void register_node_type_geo_join_geometry();
void register_node_type_geo_material_replace();
void register_node_type_geo_material_selection();
void register_node_type_geo_merge_by_distance();
void register_node_type_geo_mean_filter_sdf_volume();
void register_node_type_geo_mesh_face_group_boundaries();
void register_node_type_geo_mesh_primitive_circle();
void register_node_type_geo_mesh_primitive_cone();
@ -106,6 +107,7 @@ void register_node_type_geo_mesh_primitive_uv_sphere();
void register_node_type_geo_mesh_subdivide();
void register_node_type_geo_mesh_to_curve();
void register_node_type_geo_mesh_to_points();
void register_node_type_geo_mesh_to_sdf_volume();
void register_node_type_geo_mesh_to_volume();
void register_node_type_geo_mesh_topology_corners_of_face();
void register_node_type_geo_mesh_topology_corners_of_vertex();
@ -116,7 +118,9 @@ void register_node_type_geo_mesh_topology_offset_corner_in_face();
void register_node_type_geo_mesh_topology_vertex_of_corner();
void register_node_type_geo_object_info();
void register_node_type_geo_offset_point_in_curve();
void register_node_type_geo_offset_sdf_volume();
void register_node_type_geo_points_to_vertices();
void register_node_type_geo_points_to_sdf_volume();
void register_node_type_geo_points_to_volume();
void register_node_type_geo_points();
void register_node_type_geo_proximity();
@ -130,6 +134,7 @@ void register_node_type_geo_sample_nearest();
void register_node_type_geo_sample_uv_surface();
void register_node_type_geo_scale_elements();
void register_node_type_geo_scale_instances();
void register_node_type_geo_sdf_volume_sphere();
void register_node_type_geo_select_by_handle_type();
void register_node_type_geo_self_object();
void register_node_type_geo_separate_components();

View File

@ -26,6 +26,10 @@
#include "node_geometry_register.hh"
#include "node_util.h"
#ifdef WITH_OPENVDB
# include <openvdb/Types.h>
#endif
struct BVHTreeFromMesh;
void geo_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass);
@ -90,6 +94,17 @@ int apply_offset_in_cyclic_range(IndexRange range, int start_index, int offset);
std::optional<eCustomDataType> node_data_type_to_custom_data_type(eNodeSocketDatatype type);
std::optional<eCustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket);
#ifdef WITH_OPENVDB
/**
* Initializes the VolumeComponent of a GeometrySet with a new Volume from points.
* The grid class should be either openvdb::GRID_FOG_VOLUME or openvdb::GRID_LEVEL_SET.
*/
void initialize_volume_component_from_points(GeoNodeExecParams &params,
const NodeGeometryPointsToVolume &storage,
GeometrySet &r_geometry_set,
openvdb::GridClass gridClass);
#endif
class FieldAtIndexInput final : public bke::GeometryFieldInput {
private:
Field<int> index_field_;

View File

@ -0,0 +1,106 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DEG_depsgraph_query.h"
#ifdef WITH_OPENVDB
# include <openvdb/tools/LevelSetFilter.h>
#endif
#include "node_geometry_util.hh"
#include "BKE_geometry_set.h"
#include "BKE_volume.h"
#include "DNA_node_types.h"
#include "NOD_add_node_search.hh"
#include "NOD_socket_search_link.hh"
#include "UI_interface.h"
#include "UI_resources.h"
namespace blender::nodes::node_geo_mean_filter_sdf_volume_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME);
b.add_input<decl::Int>(N_("Iterations")).min(1).max(256).default_value(1);
b.add_input<decl::Int>(N_("Width")).min(0).default_value(1);
b.add_output<decl::Geometry>(N_("Volume"));
}
static void search_node_add_ops(GatherAddNodeSearchParams &params)
{
if (U.experimental.use_new_volume_nodes) {
blender::nodes::search_node_add_ops_for_basic_node(params);
}
}
static void search_link_ops(GatherLinkSearchOpParams &params)
{
if (U.experimental.use_new_volume_nodes) {
blender::nodes::search_link_ops_for_basic_node(params);
}
}
#ifdef WITH_OPENVDB
static void sdf_volume_mean_filter(Volume &volume, const GeoNodeExecParams &params)
{
VolumeGrid *volume_grid = BKE_volume_grid_find_for_write(&volume, "distance");
if (volume_grid == nullptr) {
return;
}
openvdb::GridBase::Ptr base_grid = BKE_volume_grid_openvdb_for_write(
&volume, volume_grid, false);
if (!base_grid->isType<openvdb::FloatGrid>()) {
return;
}
openvdb::FloatGrid::Ptr levelset_grid = openvdb::gridPtrCast<openvdb::FloatGrid>(base_grid);
openvdb::tools::LevelSetFilter<openvdb::FloatGrid> filter(*levelset_grid);
int iterations = params.get_input<int>("Iterations");
for (int i = 0; i < iterations; i++) {
filter.mean(params.get_input<int>("Width"));
}
}
#endif
static void node_geo_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (!geometry_set.has_volume()) {
return;
}
VolumeComponent &component = geometry_set.get_component_for_write<VolumeComponent>();
Volume *volume = component.get_for_write();
BKE_volume_load(volume, DEG_get_bmain(params.depsgraph()));
sdf_volume_mean_filter(*volume, params);
});
params.set_output("Volume", std::move(geometry_set));
#else
params.set_default_remaining_outputs();
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
#endif
}
} // namespace blender::nodes::node_geo_mean_filter_sdf_volume_cc
void register_node_type_geo_mean_filter_sdf_volume()
{
namespace file_ns = blender::nodes::node_geo_mean_filter_sdf_volume_cc;
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_MEAN_FILTER_SDF_VOLUME, "Mean Filter SDF Volume", NODE_CLASS_GEOMETRY);
node_type_size(&ntype, 160, 120, 700);
ntype.declare = file_ns::node_declare;
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.gather_add_node_search_ops = file_ns::search_node_add_ops;
ntype.gather_link_search_ops = file_ns::search_link_ops;
nodeRegisterType(&ntype);
}

View File

@ -0,0 +1,182 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DEG_depsgraph_query.h"
#include "node_geometry_util.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_object.h"
#include "BKE_volume.h"
#include "GEO_mesh_to_volume.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "NOD_add_node_search.hh"
#include "NOD_socket_search_link.hh"
#include "UI_interface.h"
#include "UI_resources.h"
namespace blender::nodes::node_geo_mesh_to_sdf_volume_cc {
NODE_STORAGE_FUNCS(NodeGeometryMeshToVolume)
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
b.add_input<decl::Float>(N_("Voxel Size"))
.default_value(0.3f)
.min(0.01f)
.max(FLT_MAX)
.subtype(PROP_DISTANCE);
b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f).max(FLT_MAX);
b.add_input<decl::Float>(N_("Half-Band Width"))
.description(N_("Half the width of the narrow band in voxel units"))
.default_value(3.0f)
.min(1.01f)
.max(10.0f);
b.add_output<decl::Geometry>(N_("Volume"));
}
static void search_node_add_ops(GatherAddNodeSearchParams &params)
{
if (U.experimental.use_new_volume_nodes) {
blender::nodes::search_node_add_ops_for_basic_node(params);
}
}
static void search_link_ops(GatherLinkSearchOpParams &params)
{
if (U.experimental.use_new_volume_nodes) {
blender::nodes::search_link_ops_for_basic_node(params);
}
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE);
}
static void node_init(bNodeTree * /*tree*/, bNode *node)
{
NodeGeometryMeshToVolume *data = MEM_cnew<NodeGeometryMeshToVolume>(__func__);
data->resolution_mode = MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT;
node->storage = data;
}
static void node_update(bNodeTree *ntree, bNode *node)
{
NodeGeometryMeshToVolume &data = node_storage(*node);
bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
nodeSetSocketAvailability(ntree,
voxel_amount_socket,
data.resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT);
nodeSetSocketAvailability(
ntree, voxel_size_socket, data.resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE);
}
#ifdef WITH_OPENVDB
static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams &params)
{
const NodeGeometryMeshToVolume &storage = node_storage(params.node());
const float half_band_width = params.get_input<float>("Half-Band Width");
geometry::MeshToVolumeResolution resolution;
resolution.mode = (MeshToVolumeModifierResolutionMode)storage.resolution_mode;
if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT) {
resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount");
if (resolution.settings.voxel_amount <= 0.0f) {
return nullptr;
}
}
else if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE) {
resolution.settings.voxel_size = params.get_input<float>("Voxel Size");
if (resolution.settings.voxel_size <= 0.0f) {
return nullptr;
}
}
if (mesh.totpoly == 0) {
return nullptr;
}
const float4x4 mesh_to_volume_space_transform = float4x4::identity();
auto bounds_fn = [&](float3 &r_min, float3 &r_max) {
float3 min{std::numeric_limits<float>::max()};
float3 max{-std::numeric_limits<float>::max()};
BKE_mesh_wrapper_minmax(&mesh, min, max);
r_min = min;
r_max = max;
};
const float voxel_size = geometry::volume_compute_voxel_size(
params.depsgraph(), bounds_fn, resolution, half_band_width, mesh_to_volume_space_transform);
if (voxel_size < 1e-5f) {
/* The voxel size is too small. */
return nullptr;
}
Volume *volume = reinterpret_cast<Volume *>(BKE_id_new_nomain(ID_VO, nullptr));
/* Convert mesh to grid and add to volume. */
geometry::sdf_volume_grid_add_from_mesh(volume, "distance", mesh, voxel_size, half_band_width);
return volume;
}
#endif /* WITH_OPENVDB */
static void node_geo_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
GeometrySet geometry_set(params.extract_input<GeometrySet>("Mesh"));
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (geometry_set.has_mesh()) {
Volume *volume = create_volume_from_mesh(*geometry_set.get_mesh_for_read(), params);
geometry_set.replace_volume(volume);
geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_VOLUME});
}
});
params.set_output("Volume", std::move(geometry_set));
#else
params.set_default_remaining_outputs();
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
return;
#endif
}
} // namespace blender::nodes::node_geo_mesh_to_sdf_volume_cc
void register_node_type_geo_mesh_to_sdf_volume()
{
namespace file_ns = blender::nodes::node_geo_mesh_to_sdf_volume_cc;
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_MESH_TO_SDF_VOLUME, "Mesh to SDF Volume", NODE_CLASS_GEOMETRY);
ntype.declare = file_ns::node_declare;
node_type_size(&ntype, 180, 120, 300);
ntype.initfunc = file_ns::node_init;
ntype.updatefunc = file_ns::node_update;
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.draw_buttons = file_ns::node_layout;
ntype.gather_add_node_search_ops = file_ns::search_node_add_ops;
ntype.gather_link_search_ops = file_ns::search_link_ops;
node_type_storage(
&ntype, "NodeGeometryMeshToVolume", node_free_standard_storage, node_copy_standard_storage);
nodeRegisterType(&ntype);
}

View File

@ -127,15 +127,15 @@ static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams &para
Volume *volume = reinterpret_cast<Volume *>(BKE_id_new_nomain(ID_VO, nullptr));
/* Convert mesh to grid and add to volume. */
geometry::volume_grid_add_from_mesh(volume,
"density",
&mesh,
mesh_to_volume_space_transform,
voxel_size,
fill_volume,
exterior_band_width,
interior_band_width,
density);
geometry::fog_volume_grid_add_from_mesh(volume,
"density",
&mesh,
mesh_to_volume_space_transform,
voxel_size,
fill_volume,
exterior_band_width,
interior_band_width,
density);
return volume;
}

View File

@ -0,0 +1,100 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DEG_depsgraph_query.h"
#ifdef WITH_OPENVDB
# include <openvdb/tools/LevelSetFilter.h>
#endif
#include "node_geometry_util.hh"
#include "BKE_geometry_set.h"
#include "BKE_volume.h"
#include "DNA_node_types.h"
#include "NOD_add_node_search.hh"
#include "NOD_socket_search_link.hh"
#include "UI_interface.h"
#include "UI_resources.h"
namespace blender::nodes::node_geo_offset_sdf_volume_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME);
b.add_input<decl::Float>(N_("Distance")).default_value(0.1f).subtype(PROP_DISTANCE);
b.add_output<decl::Geometry>(N_("Volume"));
}
static void search_node_add_ops(GatherAddNodeSearchParams &params)
{
if (U.experimental.use_new_volume_nodes) {
blender::nodes::search_node_add_ops_for_basic_node(params);
}
}
static void search_link_ops(GatherLinkSearchOpParams &params)
{
if (U.experimental.use_new_volume_nodes) {
blender::nodes::search_link_ops_for_basic_node(params);
}
}
#ifdef WITH_OPENVDB
static void sdf_volume_offset(Volume &volume, const GeoNodeExecParams &params)
{
VolumeGrid *volume_grid = BKE_volume_grid_find_for_write(&volume, "distance");
if (volume_grid == nullptr) {
return;
}
openvdb::GridBase::Ptr base_grid = BKE_volume_grid_openvdb_for_write(
&volume, volume_grid, false);
if (!base_grid->isType<openvdb::FloatGrid>()) {
return;
}
openvdb::FloatGrid::Ptr levelset_grid = openvdb::gridPtrCast<openvdb::FloatGrid>(base_grid);
openvdb::tools::LevelSetFilter<openvdb::FloatGrid> filter(*levelset_grid);
filter.offset(-params.get_input<float>("Distance"));
}
#endif
static void node_geo_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (!geometry_set.has_volume()) {
return;
}
VolumeComponent &component = geometry_set.get_component_for_write<VolumeComponent>();
Volume *volume = component.get_for_write();
BKE_volume_load(volume, DEG_get_bmain(params.depsgraph()));
sdf_volume_offset(*volume, params);
});
params.set_output("Volume", std::move(geometry_set));
#else
params.set_default_remaining_outputs();
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
#endif
}
} // namespace blender::nodes::node_geo_offset_sdf_volume_cc
void register_node_type_geo_offset_sdf_volume()
{
namespace file_ns = blender::nodes::node_geo_offset_sdf_volume_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_OFFSET_SDF_VOLUME, "Offset SDF Volume", NODE_CLASS_GEOMETRY);
ntype.declare = file_ns::node_declare;
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.gather_add_node_search_ops = file_ns::search_node_add_ops;
ntype.gather_link_search_ops = file_ns::search_link_ops;
nodeRegisterType(&ntype);
}

View File

@ -0,0 +1,128 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifdef WITH_OPENVDB
# include <openvdb/Types.h>
#endif
#include "node_geometry_util.hh"
#include "GEO_points_to_volume.hh"
#include "NOD_add_node_search.hh"
#include "NOD_socket_search_link.hh"
#include "UI_interface.h"
#include "UI_resources.h"
namespace blender::nodes::node_geo_points_to_sdf_volume_cc {
NODE_STORAGE_FUNCS(NodeGeometryPointsToVolume)
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Points"));
b.add_input<decl::Float>(N_("Voxel Size"))
.default_value(0.3f)
.min(0.01f)
.subtype(PROP_DISTANCE)
.make_available([](bNode &node) {
node_storage(node).resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE;
});
b.add_input<decl::Float>(N_("Voxel Amount"))
.default_value(64.0f)
.min(0.0f)
.make_available([](bNode &node) {
node_storage(node).resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
});
b.add_input<decl::Float>(N_("Radius"))
.default_value(0.5f)
.min(0.0f)
.subtype(PROP_DISTANCE)
.field_on_all();
b.add_output<decl::Geometry>(N_("Volume"));
}
static void search_node_add_ops(GatherAddNodeSearchParams &params)
{
if (U.experimental.use_new_volume_nodes) {
blender::nodes::search_node_add_ops_for_basic_node(params);
}
}
static void search_link_ops(GatherLinkSearchOpParams &params)
{
if (U.experimental.use_new_volume_nodes) {
blender::nodes::search_link_ops_for_basic_node(params);
}
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE);
}
static void node_init(bNodeTree * /*tree*/, bNode *node)
{
NodeGeometryPointsToVolume *data = MEM_cnew<NodeGeometryPointsToVolume>(__func__);
data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
node->storage = data;
}
static void node_update(bNodeTree *ntree, bNode *node)
{
const NodeGeometryPointsToVolume &storage = node_storage(*node);
bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
nodeSetSocketAvailability(ntree,
voxel_amount_socket,
storage.resolution_mode ==
GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT);
nodeSetSocketAvailability(ntree,
voxel_size_socket,
storage.resolution_mode ==
GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
}
static void node_geo_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
const NodeGeometryPointsToVolume &storage = node_storage(params.node());
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
initialize_volume_component_from_points(
params, storage, geometry_set, openvdb::GRID_LEVEL_SET);
});
params.set_output("Volume", std::move(geometry_set));
#else
params.set_default_remaining_outputs();
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
#endif
}
} // namespace blender::nodes::node_geo_points_to_sdf_volume_cc
void register_node_type_geo_points_to_sdf_volume()
{
namespace file_ns = blender::nodes::node_geo_points_to_sdf_volume_cc;
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_POINTS_TO_SDF_VOLUME, "Points to SDF Volume", NODE_CLASS_GEOMETRY);
node_type_storage(&ntype,
"NodeGeometryPointsToVolume",
node_free_standard_storage,
node_copy_standard_storage);
node_type_size(&ntype, 170, 120, 700);
ntype.initfunc = file_ns::node_init;
ntype.updatefunc = file_ns::node_update;
ntype.declare = file_ns::node_declare;
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.draw_buttons = file_ns::node_layout;
ntype.gather_add_node_search_ops = file_ns::search_node_add_ops;
ntype.gather_link_search_ops = file_ns::search_link_ops;
nodeRegisterType(&ntype);
}

Some files were not shown because too many files have changed in this diff Show More