Initial Grease Pencil 3.0 stage #106848
|
@ -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)))
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 ¢roid_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 ¢roid_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 ¢roid_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 ¢roid_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) {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, [&]() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -30,6 +30,8 @@ enum eShaderType {
|
|||
FILM_COMP,
|
||||
FILM_CRYPTOMATTE_POST,
|
||||
|
||||
DEFERRED_LIGHT,
|
||||
|
||||
DEBUG_SURFELS,
|
||||
|
||||
DOF_BOKEH_LUT,
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -405,30 +405,30 @@ class TreeViewLayoutBuilder {
|
|||
void build_row(AbstractTreeViewItem &item) const;
|
||||
|
||||
uiBlock &block() const;
|
||||
uiLayout *current_layout() const;
|
||||
uiLayout ¤t_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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 ®ion = *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;
|
||||
}
|
||||
|
|
|
@ -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 ®ion,
|
||||
bNodeTree &ntree,
|
||||
const Span<float2> socket_locations,
|
||||
const bNode &node,
|
||||
uiBlock &block)
|
||||
static void reroute_node_draw(
|
||||
const bContext &C, ARegion ®ion, 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 ®ion,
|
||||
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 ®ion,
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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 ®ion,
|
||||
|
@ -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);
|
||||
|
|
|
@ -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 ®ion = *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 ®ion)
|
|||
{
|
||||
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 ®ion)
|
|||
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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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, \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -697,7 +697,7 @@ ModifierTypeInfo modifierType_Ocean = {
|
|||
/*icon*/ ICON_MOD_OCEAN,
|
||||
|
||||
/*copyData*/ copyData,
|
||||
/*deformMatrices_DM*/ nullptr,
|
||||
/*deformMatrices*/ nullptr,
|
||||
|
||||
/*deformMatrices*/ nullptr,
|
||||
/*deformVertsEM*/ nullptr,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 ¶ms,
|
||||
const NodeGeometryPointsToVolume &storage,
|
||||
GeometrySet &r_geometry_set,
|
||||
openvdb::GridClass gridClass);
|
||||
#endif
|
||||
|
||||
class FieldAtIndexInput final : public bke::GeometryFieldInput {
|
||||
private:
|
||||
Field<int> index_field_;
|
||||
|
|
|
@ -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 ¶ms)
|
||||
{
|
||||
if (U.experimental.use_new_volume_nodes) {
|
||||
blender::nodes::search_node_add_ops_for_basic_node(params);
|
||||
}
|
||||
}
|
||||
|
||||
static void search_link_ops(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
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 ¶ms)
|
||||
{
|
||||
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);
|
||||
}
|
|
@ -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 ¶ms)
|
||||
{
|
||||
if (U.experimental.use_new_volume_nodes) {
|
||||
blender::nodes::search_node_add_ops_for_basic_node(params);
|
||||
}
|
||||
}
|
||||
|
||||
static void search_link_ops(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
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 ¶ms)
|
||||
{
|
||||
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);
|
||||
}
|
|
@ -127,15 +127,15 @@ static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams ¶
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -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 ¶ms)
|
||||
{
|
||||
if (U.experimental.use_new_volume_nodes) {
|
||||
blender::nodes::search_node_add_ops_for_basic_node(params);
|
||||
}
|
||||
}
|
||||
|
||||
static void search_link_ops(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
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 ¶ms)
|
||||
{
|
||||
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);
|
||||
}
|
|
@ -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 ¶ms)
|
||||
{
|
||||
if (U.experimental.use_new_volume_nodes) {
|
||||
blender::nodes::search_node_add_ops_for_basic_node(params);
|
||||
}
|
||||
}
|
||||
|
||||
static void search_link_ops(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
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
Loading…
Reference in New Issue