419 lines
13 KiB
C++
419 lines
13 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*/
|
|
|
|
#include <cstring>
|
|
#include <optional>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_curves_types.h"
|
|
#include "DNA_material_types.h"
|
|
#include "DNA_object_types.h"
|
|
|
|
#include "BLI_index_range.hh"
|
|
#include "BLI_math_matrix.hh"
|
|
#include "BLI_rand.hh"
|
|
#include "BLI_resource_scope.hh"
|
|
#include "BLI_span.hh"
|
|
#include "BLI_string.h"
|
|
#include "BLI_utildefines.h"
|
|
#include "BLI_vector.hh"
|
|
|
|
#include "BKE_anim_data.hh"
|
|
#include "BKE_attribute_legacy_convert.hh"
|
|
#include "BKE_curves.hh"
|
|
#include "BKE_customdata.hh"
|
|
#include "BKE_geometry_fields.hh"
|
|
#include "BKE_geometry_set.hh"
|
|
#include "BKE_idtype.hh"
|
|
#include "BKE_lib_id.hh"
|
|
#include "BKE_lib_query.hh"
|
|
#include "BKE_material.hh"
|
|
#include "BKE_modifier.hh"
|
|
#include "BKE_object.hh"
|
|
#include "BKE_object_types.hh"
|
|
|
|
#include "BLT_translation.hh"
|
|
|
|
#include "DEG_depsgraph_query.hh"
|
|
|
|
#include "BLO_read_write.hh"
|
|
|
|
#include "NOD_geometry_nodes_bundle.hh"
|
|
|
|
namespace blender {
|
|
|
|
static const char *ATTR_POSITION = "position";
|
|
|
|
static void curves_init_data(ID *id)
|
|
{
|
|
Curves *curves = id_cast<Curves *>(id);
|
|
INIT_DEFAULT_STRUCT_AFTER(curves, id);
|
|
|
|
new (&curves->geometry) bke::CurvesGeometry();
|
|
}
|
|
|
|
static void curves_copy_data(Main * /*bmain*/,
|
|
std::optional<Library *> /*owner_library*/,
|
|
ID *id_dst,
|
|
const ID *id_src,
|
|
const int /*flag*/)
|
|
{
|
|
Curves *curves_dst = id_cast<Curves *>(id_dst);
|
|
const Curves *curves_src = id_cast<const Curves *>(id_src);
|
|
curves_dst->mat = MEM_dupalloc(curves_src->mat);
|
|
|
|
new (&curves_dst->geometry) bke::CurvesGeometry(curves_src->geometry.wrap());
|
|
|
|
if (curves_src->surface_uv_map != nullptr) {
|
|
curves_dst->surface_uv_map = BLI_strdup(curves_src->surface_uv_map);
|
|
}
|
|
|
|
curves_dst->batch_cache = nullptr;
|
|
}
|
|
|
|
static void curves_free_data(ID *id)
|
|
{
|
|
Curves *curves = id_cast<Curves *>(id);
|
|
BKE_animdata_free(&curves->id, false);
|
|
|
|
curves->geometry.wrap().~CurvesGeometry();
|
|
|
|
BKE_curves_batch_cache_free(curves);
|
|
|
|
MEM_SAFE_DELETE(curves->mat);
|
|
MEM_SAFE_DELETE(curves->surface_uv_map);
|
|
}
|
|
|
|
static void curves_foreach_id(ID *id, LibraryForeachIDData *data)
|
|
{
|
|
Curves *curves = id_cast<Curves *>(id);
|
|
for (int i = 0; i < curves->totcol; i++) {
|
|
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curves->mat[i], IDWALK_CB_USER);
|
|
}
|
|
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curves->surface, IDWALK_CB_NOP);
|
|
}
|
|
|
|
static void curves_foreach_working_space_color(ID *id,
|
|
const IDTypeForeachColorFunctionCallback &fn)
|
|
{
|
|
Curves *curves = reinterpret_cast<Curves *>(id);
|
|
curves->geometry.wrap().attribute_storage.wrap().foreach_working_space_color(fn);
|
|
}
|
|
|
|
static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_address)
|
|
{
|
|
Curves *curves = id_cast<Curves *>(id);
|
|
|
|
/* Only for forward compatibility. */
|
|
curves->attributes_active_index_legacy = curves->geometry.attributes_active_index;
|
|
|
|
ResourceScope scope;
|
|
bke::CurvesGeometry::BlendWriteData write_data(writer, scope);
|
|
curves->geometry.wrap().blend_write_prepare(write_data, !BLO_write_is_undo(writer));
|
|
|
|
/* Write LibData */
|
|
writer->write_id_struct(id_address, curves);
|
|
BKE_id_blend_write(writer, &curves->id);
|
|
|
|
/* Direct data */
|
|
curves->geometry.wrap().blend_write(*writer, curves->id, write_data);
|
|
|
|
writer->write_string(curves->surface_uv_map);
|
|
|
|
writer->write_pointer_array(curves->totcol, curves->mat);
|
|
}
|
|
|
|
static void curves_blend_read_data(BlendDataReader *reader, ID *id)
|
|
{
|
|
Curves *curves = id_cast<Curves *>(id);
|
|
|
|
/* Geometry */
|
|
curves->geometry.wrap().blend_read(*reader);
|
|
|
|
BLO_read_string(reader, &curves->surface_uv_map);
|
|
|
|
/* Materials */
|
|
BLO_read_pointer_array_and_validate_size(reader, &curves->mat, &curves->totcol);
|
|
}
|
|
|
|
IDTypeInfo IDType_ID_CV = {
|
|
.id_code = Curves::id_type,
|
|
.id_filter = FILTER_ID_CV,
|
|
.dependencies_id_types = FILTER_ID_MA | FILTER_ID_OB,
|
|
.main_listbase_index = INDEX_ID_CV,
|
|
.struct_size = sizeof(Curves),
|
|
.name = "Curves",
|
|
.name_plural = N_("hair_curves"),
|
|
.translation_context = BLT_I18NCONTEXT_ID_CURVES,
|
|
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
|
.asset_type_info = nullptr,
|
|
|
|
.init_data = curves_init_data,
|
|
.copy_data = curves_copy_data,
|
|
.free_data = curves_free_data,
|
|
.make_local = nullptr,
|
|
.foreach_id = curves_foreach_id,
|
|
.foreach_cache = nullptr,
|
|
.foreach_path = nullptr,
|
|
.foreach_working_space_color = curves_foreach_working_space_color,
|
|
.owner_pointer_get = nullptr,
|
|
|
|
.blend_write = curves_blend_write,
|
|
.blend_read_data = curves_blend_read_data,
|
|
.blend_read_after_liblink = nullptr,
|
|
|
|
.blend_read_undo_preserve = nullptr,
|
|
|
|
.lib_override_apply_post = nullptr,
|
|
};
|
|
|
|
Curves *BKE_curves_add(Main *bmain, const char *name)
|
|
{
|
|
Curves *curves = BKE_id_new<Curves>(bmain, name);
|
|
|
|
return curves;
|
|
}
|
|
|
|
bool BKE_curves_attribute_required(const Curves * /*curves*/, const StringRef name)
|
|
{
|
|
return name == ATTR_POSITION;
|
|
}
|
|
|
|
void BKE_curves_material_remap(Curves *curves_id, const uint *remap, const int remap_num)
|
|
{
|
|
BKE_material_attr_indices_remap(
|
|
curves_id->geometry.wrap().attributes_for_write(), remap, remap_num);
|
|
}
|
|
|
|
Curves *BKE_curves_copy_for_eval(const Curves *curves_src)
|
|
{
|
|
return reinterpret_cast<Curves *>(
|
|
BKE_id_copy_ex(nullptr, &curves_src->id, nullptr, LIB_ID_COPY_LOCALIZE));
|
|
}
|
|
|
|
static void curves_evaluate_modifiers(Depsgraph *depsgraph,
|
|
Scene *scene,
|
|
Object *object,
|
|
bke::GeometrySet &geometry_set)
|
|
{
|
|
/* Modifier evaluation modes. */
|
|
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
|
|
int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
|
|
if (BKE_object_is_in_editmode(object)) {
|
|
required_mode = ModifierMode(required_mode | eModifierMode_Editmode);
|
|
}
|
|
ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
|
|
const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
|
|
|
|
BKE_modifiers_clear_errors(object);
|
|
|
|
/* Get effective list of modifiers to execute. Some effects like shape keys
|
|
* are added as virtual modifiers before the user created modifiers. */
|
|
VirtualModifierData virtual_modifier_data;
|
|
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtual_modifier_data);
|
|
|
|
/* Evaluate modifiers. */
|
|
for (; md; md = md->next) {
|
|
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
|
|
|
|
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
|
|
continue;
|
|
}
|
|
|
|
bke::ScopedModifierTimer modifier_timer{*md};
|
|
|
|
if (mti->modify_geometry_set != nullptr) {
|
|
mti->modify_geometry_set(md, &mectx, &geometry_set);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BKE_curves_data_update(Depsgraph *depsgraph, Scene *scene, Object *object)
|
|
{
|
|
using namespace blender::bke;
|
|
/* Free any evaluated data and restore original data. */
|
|
BKE_object_free_derived_caches(object);
|
|
|
|
/* Evaluate modifiers. */
|
|
Curves *curves = id_cast<Curves *>(object->data);
|
|
GeometrySet geometry_set = GeometrySet::from_curves(curves, GeometryOwnershipType::ReadOnly);
|
|
if (ELEM(object->mode, OB_MODE_EDIT, OB_MODE_SCULPT_CURVES)) {
|
|
/* Try to propagate deformation data through modifier evaluation, so that sculpt mode can work
|
|
* on evaluated curves. */
|
|
GeometryComponentEditData &edit_component =
|
|
geometry_set.get_component_for_write<GeometryComponentEditData>();
|
|
edit_component.curves_edit_hints_ = std::make_unique<CurvesEditHints>(
|
|
*id_cast<const Curves *>(DEG_get_original(object)->data));
|
|
}
|
|
bke::curves_store_surface_in_geometry_bundle(*depsgraph, *curves, geometry_set);
|
|
curves_evaluate_modifiers(depsgraph, scene, object, geometry_set);
|
|
|
|
/* Assign evaluated object. */
|
|
Curves *curves_eval = const_cast<Curves *>(geometry_set.get_curves());
|
|
if (curves_eval == nullptr) {
|
|
curves_eval = curves_new_nomain(0, 0);
|
|
BKE_object_eval_assign_data(object, &curves_eval->id, true);
|
|
}
|
|
else {
|
|
BKE_object_eval_assign_data(object, &curves_eval->id, false);
|
|
}
|
|
object->runtime->geometry_set_eval = new GeometrySet(std::move(geometry_set));
|
|
}
|
|
|
|
/* Draw Cache */
|
|
|
|
void (*BKE_curves_batch_cache_dirty_tag_cb)(Curves *curves, int mode) = nullptr;
|
|
void (*BKE_curves_batch_cache_free_cb)(Curves *curves) = nullptr;
|
|
|
|
void BKE_curves_batch_cache_dirty_tag(Curves *curves, int mode)
|
|
{
|
|
if (curves->batch_cache) {
|
|
BKE_curves_batch_cache_dirty_tag_cb(curves, mode);
|
|
}
|
|
}
|
|
|
|
void BKE_curves_batch_cache_free(Curves *curves)
|
|
{
|
|
if (curves->batch_cache) {
|
|
BKE_curves_batch_cache_free_cb(curves);
|
|
}
|
|
}
|
|
|
|
namespace bke {
|
|
|
|
Curves *curves_new_nomain(const int points_num, const int curves_num)
|
|
{
|
|
BLI_assert(points_num >= 0);
|
|
BLI_assert(curves_num >= 0);
|
|
Curves *curves_id = BKE_id_new_nomain<Curves>(nullptr);
|
|
CurvesGeometry &curves = curves_id->geometry.wrap();
|
|
curves.resize(points_num, curves_num);
|
|
return curves_id;
|
|
}
|
|
|
|
Curves *curves_new_nomain_single(const int points_num, const CurveType type)
|
|
{
|
|
Curves *curves_id = curves_new_nomain(points_num, 1);
|
|
CurvesGeometry &curves = curves_id->geometry.wrap();
|
|
curves.offsets_for_write().last() = points_num;
|
|
curves.fill_curve_types(type);
|
|
return curves_id;
|
|
}
|
|
|
|
Curves *curves_new_nomain(CurvesGeometry curves)
|
|
{
|
|
Curves *curves_id = BKE_id_new_nomain<Curves>(nullptr);
|
|
curves_id->geometry.wrap() = std::move(curves);
|
|
return curves_id;
|
|
}
|
|
|
|
void curves_copy_parameters(const Curves &src, Curves &dst)
|
|
{
|
|
dst.flag = src.flag;
|
|
MEM_SAFE_DELETE(dst.mat);
|
|
dst.mat = MEM_new_array_uninitialized<Material *>(size_t(src.totcol), __func__);
|
|
dst.totcol = src.totcol;
|
|
MutableSpan(dst.mat, dst.totcol).copy_from(Span(src.mat, src.totcol));
|
|
dst.symmetry = src.symmetry;
|
|
dst.selection_domain = src.selection_domain;
|
|
dst.surface = src.surface;
|
|
MEM_SAFE_DELETE(dst.surface_uv_map);
|
|
if (src.surface_uv_map != nullptr) {
|
|
dst.surface_uv_map = BLI_strdup(src.surface_uv_map);
|
|
}
|
|
dst.surface_collision_distance = src.surface_collision_distance;
|
|
}
|
|
|
|
void curves_store_surface_in_geometry_bundle(const Depsgraph &depsgraph,
|
|
const Curves &curves_id,
|
|
GeometrySet &geometry_set)
|
|
{
|
|
if (!curves_id.surface) {
|
|
return;
|
|
}
|
|
if (!curves_id.surface_uv_map) {
|
|
return;
|
|
}
|
|
nodes::Bundle &bundle = geometry_set.bundle_for_write();
|
|
bundle.add(*nodes::BundleKey::from_ustr("surface_object"_ustr),
|
|
DEG_get_evaluated(&depsgraph, curves_id.surface));
|
|
bundle.add(*nodes::BundleKey::from_ustr("surface_uv_map_name"_ustr),
|
|
std::string(curves_id.surface_uv_map));
|
|
}
|
|
|
|
CurvesSurfaceTransforms::CurvesSurfaceTransforms(const Object &curves_ob, const Object *surface_ob)
|
|
{
|
|
this->curves_to_world = curves_ob.object_to_world();
|
|
this->world_to_curves = math::invert(this->curves_to_world);
|
|
|
|
if (surface_ob != nullptr) {
|
|
this->surface_to_world = surface_ob->object_to_world();
|
|
this->world_to_surface = math::invert(this->surface_to_world);
|
|
this->surface_to_curves = this->world_to_curves * this->surface_to_world;
|
|
this->curves_to_surface = this->world_to_surface * this->curves_to_world;
|
|
this->surface_to_curves_normal = math::transpose(math::invert(this->surface_to_curves));
|
|
}
|
|
}
|
|
|
|
bool CurvesEditHints::is_valid() const
|
|
{
|
|
const int point_num = this->curves_id_orig.geometry.point_num;
|
|
if (this->positions().has_value()) {
|
|
if (this->positions()->size() != point_num) {
|
|
return false;
|
|
}
|
|
}
|
|
if (this->deform_mats.has_value()) {
|
|
if (this->deform_mats->size() != point_num) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::optional<Span<float3>> CurvesEditHints::positions() const
|
|
{
|
|
if (!this->positions_data.has_value()) {
|
|
return std::nullopt;
|
|
}
|
|
const int points_num = this->curves_id_orig.geometry.wrap().points_num();
|
|
return Span(static_cast<const float3 *>(this->positions_data.data), points_num);
|
|
}
|
|
|
|
std::optional<MutableSpan<float3>> CurvesEditHints::positions_for_write()
|
|
{
|
|
if (!this->positions_data.has_value()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
const int points_num = this->curves_id_orig.geometry.wrap().points_num();
|
|
ImplicitSharingPtrAndData &data = this->positions_data;
|
|
if (data.sharing_info->is_mutable()) {
|
|
data.sharing_info->tag_ensured_mutable();
|
|
}
|
|
else {
|
|
auto *new_sharing_info = new ImplicitSharedValue<Array<float3>>(*this->positions());
|
|
data.sharing_info = ImplicitSharingPtr<>(new_sharing_info);
|
|
data.data = new_sharing_info->data.data();
|
|
}
|
|
|
|
return MutableSpan(const_cast<float3 *>(static_cast<const float3 *>(data.data)), points_num);
|
|
}
|
|
|
|
void curves_normals_point_domain_calc(const CurvesGeometry &curves, MutableSpan<float3> normals)
|
|
{
|
|
const bke::CurvesFieldContext context(curves, AttrDomain::Point);
|
|
fn::FieldEvaluator evaluator(context, curves.points_num());
|
|
evaluator.add_with_destination(bke::NormalFieldInput::get_field(), normals);
|
|
evaluator.evaluate();
|
|
}
|
|
|
|
} // namespace bke
|
|
} // namespace blender
|