Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel/intern/mball.cc')
-rw-r--r--source/blender/blenkernel/intern/mball.cc713
1 files changed, 713 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/mball.cc b/source/blender/blenkernel/intern/mball.cc
new file mode 100644
index 00000000000..2ff8e6fc061
--- /dev/null
+++ b/source/blender/blenkernel/intern/mball.cc
@@ -0,0 +1,713 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
+
+/** \file
+ * \ingroup bke
+ *
+ * MetaBalls are created from a single Object (with a name without number in it).
+ * All objects with the same name (but with a number in it) are added to this.
+ */
+
+#include <cctype>
+#include <cfloat>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "MEM_guardedalloc.h"
+
+/* Allow using deprecated functionality for .blend file I/O. */
+#define DNA_DEPRECATED_ALLOW
+
+#include "DNA_defaults.h"
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meta_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_string_utils.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "BKE_main.h"
+
+#include "BKE_anim_data.h"
+#include "BKE_curve.h"
+#include "BKE_displist.h"
+#include "BKE_geometry_set.hh"
+#include "BKE_idtype.h"
+#include "BKE_lattice.h"
+#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
+#include "BKE_material.h"
+#include "BKE_mball.h"
+#include "BKE_mball_tessellate.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph.h"
+
+#include "BLO_read_write.h"
+
+static void metaball_init_data(ID *id)
+{
+ MetaBall *metaball = (MetaBall *)id;
+
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(metaball, id));
+
+ MEMCPY_STRUCT_AFTER(metaball, DNA_struct_default_get(MetaBall), id);
+}
+
+static void metaball_copy_data(Main *UNUSED(bmain),
+ ID *id_dst,
+ const ID *id_src,
+ const int UNUSED(flag))
+{
+ MetaBall *metaball_dst = (MetaBall *)id_dst;
+ const MetaBall *metaball_src = (const MetaBall *)id_src;
+
+ BLI_duplicatelist(&metaball_dst->elems, &metaball_src->elems);
+
+ metaball_dst->mat = static_cast<Material **>(MEM_dupallocN(metaball_src->mat));
+
+ metaball_dst->editelems = nullptr;
+ metaball_dst->lastelem = nullptr;
+}
+
+static void metaball_free_data(ID *id)
+{
+ MetaBall *metaball = (MetaBall *)id;
+
+ MEM_SAFE_FREE(metaball->mat);
+
+ BLI_freelistN(&metaball->elems);
+ if (metaball->disp.first) {
+ BKE_displist_free(&metaball->disp);
+ }
+}
+
+static void metaball_foreach_id(ID *id, LibraryForeachIDData *data)
+{
+ MetaBall *metaball = (MetaBall *)id;
+ for (int i = 0; i < metaball->totcol; i++) {
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, metaball->mat[i], IDWALK_CB_USER);
+ }
+}
+
+static void metaball_blend_write(BlendWriter *writer, ID *id, const void *id_address)
+{
+ MetaBall *mb = (MetaBall *)id;
+
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ BLI_listbase_clear(&mb->disp);
+ mb->editelems = nullptr;
+ /* Must always be cleared (meta's don't have their own edit-data). */
+ mb->needs_flush_to_id = 0;
+ mb->lastelem = nullptr;
+
+ /* write LibData */
+ BLO_write_id_struct(writer, MetaBall, id_address, &mb->id);
+ BKE_id_blend_write(writer, &mb->id);
+
+ /* direct data */
+ BLO_write_pointer_array(writer, mb->totcol, mb->mat);
+ if (mb->adt) {
+ BKE_animdata_blend_write(writer, mb->adt);
+ }
+
+ LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) {
+ BLO_write_struct(writer, MetaElem, ml);
+ }
+}
+
+static void metaball_blend_read_data(BlendDataReader *reader, ID *id)
+{
+ MetaBall *mb = (MetaBall *)id;
+ BLO_read_data_address(reader, &mb->adt);
+ BKE_animdata_blend_read_data(reader, mb->adt);
+
+ BLO_read_pointer_array(reader, (void **)&mb->mat);
+
+ BLO_read_list(reader, &(mb->elems));
+
+ BLI_listbase_clear(&mb->disp);
+ mb->editelems = nullptr;
+ /* Must always be cleared (meta's don't have their own edit-data). */
+ mb->needs_flush_to_id = 0;
+ // mb->edit_elems.first = mb->edit_elems.last = nullptr;
+ mb->lastelem = nullptr;
+}
+
+static void metaball_blend_read_lib(BlendLibReader *reader, ID *id)
+{
+ MetaBall *mb = (MetaBall *)id;
+ for (int a = 0; a < mb->totcol; a++) {
+ BLO_read_id_address(reader, mb->id.lib, &mb->mat[a]);
+ }
+
+ BLO_read_id_address(reader, mb->id.lib, &mb->ipo); // XXX deprecated - old animation system
+}
+
+static void metaball_blend_read_expand(BlendExpander *expander, ID *id)
+{
+ MetaBall *mb = (MetaBall *)id;
+ for (int a = 0; a < mb->totcol; a++) {
+ BLO_expand(expander, mb->mat[a]);
+ }
+}
+
+IDTypeInfo IDType_ID_MB = {
+ /* id_code */ ID_MB,
+ /* id_filter */ FILTER_ID_MB,
+ /* main_listbase_index */ INDEX_ID_MB,
+ /* struct_size */ sizeof(MetaBall),
+ /* name */ "Metaball",
+ /* name_plural */ "metaballs",
+ /* translation_context */ BLT_I18NCONTEXT_ID_METABALL,
+ /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
+ /* asset_type_info */ nullptr,
+
+ /* init_data */ metaball_init_data,
+ /* copy_data */ metaball_copy_data,
+ /* free_data */ metaball_free_data,
+ /* make_local */ nullptr,
+ /* foreach_id */ metaball_foreach_id,
+ /* foreach_cache */ nullptr,
+ /* foreach_path */ nullptr,
+ /* owner_get */ nullptr,
+
+ /* blend_write */ metaball_blend_write,
+ /* blend_read_data */ metaball_blend_read_data,
+ /* blend_read_lib */ metaball_blend_read_lib,
+ /* blend_read_expand */ metaball_blend_read_expand,
+
+ /* blend_read_undo_preserve */ nullptr,
+
+ /* lib_override_apply_post */ nullptr,
+};
+
+/* Functions */
+
+MetaBall *BKE_mball_add(Main *bmain, const char *name)
+{
+ MetaBall *mb = static_cast<MetaBall *>(BKE_id_new(bmain, ID_MB, name));
+ return mb;
+}
+
+MetaElem *BKE_mball_element_add(MetaBall *mb, const int type)
+{
+ MetaElem *ml = MEM_cnew<MetaElem>(__func__);
+
+ unit_qt(ml->quat);
+
+ ml->rad = 2.0;
+ ml->s = 2.0;
+ ml->flag = MB_SCALE_RAD;
+
+ switch (type) {
+ case MB_BALL:
+ ml->type = MB_BALL;
+ ml->expx = ml->expy = ml->expz = 1.0;
+
+ break;
+ case MB_TUBE:
+ ml->type = MB_TUBE;
+ ml->expx = ml->expy = ml->expz = 1.0;
+
+ break;
+ case MB_PLANE:
+ ml->type = MB_PLANE;
+ ml->expx = ml->expy = ml->expz = 1.0;
+
+ break;
+ case MB_ELIPSOID:
+ ml->type = MB_ELIPSOID;
+ ml->expx = 1.2f;
+ ml->expy = 0.8f;
+ ml->expz = 1.0;
+
+ break;
+ case MB_CUBE:
+ ml->type = MB_CUBE;
+ ml->expx = ml->expy = ml->expz = 1.0;
+
+ break;
+ default:
+ break;
+ }
+
+ BLI_addtail(&mb->elems, ml);
+
+ return ml;
+}
+
+BoundBox *BKE_mball_boundbox_get(Object *ob)
+{
+ BLI_assert(ob->type == OB_MBALL);
+ if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
+ return ob->runtime.bb;
+ }
+ if (ob->runtime.bb == nullptr) {
+ ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
+ }
+
+ /* Expect that this function is only called for evaluated objects. */
+ const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
+ float min[3];
+ float max[3];
+ if (mesh_eval) {
+ INIT_MINMAX(min, max);
+ if (!BKE_mesh_minmax(mesh_eval, min, max)) {
+ copy_v3_fl(min, -1.0f);
+ copy_v3_fl(max, 1.0f);
+ }
+ }
+ else {
+ copy_v3_fl(min, 0.0f);
+ copy_v3_fl(max, 0.0f);
+ }
+
+ BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+ ob->runtime.bb->flag &= ~BOUNDBOX_DIRTY;
+
+ return ob->runtime.bb;
+}
+
+bool BKE_mball_is_basis(const Object *ob)
+{
+ /* Meta-Ball Basis Notes from Blender-2.5x
+ * =======================================
+ *
+ * NOTE(@campbellbarton): This is a can of worms.
+ *
+ * This really needs a rewrite/refactor its totally broken in anything other than basic cases
+ * Multiple Scenes + Set Scenes & mixing meta-ball basis _should_ work but fails to update the
+ * depsgraph on rename and linking into scenes or removal of basis meta-ball.
+ * So take care when changing this code.
+ *
+ * Main idiot thing here is that the system returns #BKE_mball_basis_find()
+ * objects which fail a #BKE_mball_is_basis() test.
+ *
+ * Not only that but the depsgraph and their areas depend on this behavior,
+ * so making small fixes here isn't worth it. */
+
+ /* Just a quick test. */
+ const int len = strlen(ob->id.name);
+ return (!isdigit(ob->id.name[len - 1]));
+}
+
+bool BKE_mball_is_same_group(const Object *ob1, const Object *ob2)
+{
+ int basis1nr, basis2nr;
+ char basis1name[MAX_ID_NAME], basis2name[MAX_ID_NAME];
+
+ if (ob1->id.name[2] != ob2->id.name[2]) {
+ /* Quick return in case first char of both ID's names is not the same... */
+ return false;
+ }
+
+ BLI_split_name_num(basis1name, &basis1nr, ob1->id.name + 2, '.');
+ BLI_split_name_num(basis2name, &basis2nr, ob2->id.name + 2, '.');
+
+ return STREQ(basis1name, basis2name);
+}
+
+bool BKE_mball_is_basis_for(const Object *ob1, const Object *ob2)
+{
+ return BKE_mball_is_same_group(ob1, ob2) && BKE_mball_is_basis(ob1);
+}
+
+bool BKE_mball_is_any_selected(const MetaBall *mb)
+{
+ LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) {
+ if (ml->flag & SELECT) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool BKE_mball_is_any_selected_multi(Base **bases, int bases_len)
+{
+ for (uint base_index = 0; base_index < bases_len; base_index++) {
+ Object *obedit = bases[base_index]->object;
+ MetaBall *mb = (MetaBall *)obedit->data;
+ if (BKE_mball_is_any_selected(mb)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool BKE_mball_is_any_unselected(const MetaBall *mb)
+{
+ LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) {
+ if ((ml->flag & SELECT) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void mball_data_properties_copy(MetaBall *mb_dst, MetaBall *mb_src)
+{
+ mb_dst->wiresize = mb_src->wiresize;
+ mb_dst->rendersize = mb_src->rendersize;
+ mb_dst->thresh = mb_src->thresh;
+ mb_dst->flag = mb_src->flag;
+ DEG_id_tag_update(&mb_dst->id, 0);
+}
+
+void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src)
+{
+ /**
+ * WARNING: This code does not cover all potential corner-cases. E.g. if:
+ * <pre>
+ * | Object | ObData |
+ * | ---------- | ---------- |
+ * | Meta_A | Meta_A |
+ * | Meta_A.001 | Meta_A.001 |
+ * | Meta_B | Meta_A |
+ * | Meta_B.001 | Meta_B.001 |
+ * </pre>
+ *
+ * Calling this function with `metaball_src` being `Meta_A.001` will update `Meta_A`, but NOT
+ * `Meta_B.001`. So in the 'Meta_B' family, the two metaballs will have unmatching settings now.
+ *
+ * Solving this case would drastically increase the complexity of this code though, so don't
+ * think it would be worth it.
+ */
+ for (Object *ob_src = static_cast<Object *>(bmain->objects.first);
+ ob_src != nullptr && !ID_IS_LINKED(ob_src);) {
+ if (ob_src->data != metaball_src) {
+ ob_src = static_cast<Object *>(ob_src->id.next);
+ continue;
+ }
+
+ /* In this code we take advantage of two facts:
+ * - MetaBalls of the same family have the same basis name,
+ * - IDs are sorted by name in their Main listbase.
+ * So, all MetaBall objects of the same family are contiguous in bmain list (potentially mixed
+ * with non-meta-ball objects with same basis names).
+ *
+ * Using this, it is possible to process the whole set of meta-balls with a single loop on the
+ * whole list of Objects, though additionally going backward on part of the list in some cases.
+ */
+ Object *ob_iter = nullptr;
+ int obactive_nr, ob_nr;
+ char obactive_name[MAX_ID_NAME], ob_name[MAX_ID_NAME];
+ BLI_split_name_num(obactive_name, &obactive_nr, ob_src->id.name + 2, '.');
+
+ for (ob_iter = static_cast<Object *>(ob_src->id.prev); ob_iter != nullptr;
+ ob_iter = static_cast<Object *>(ob_iter->id.prev)) {
+ if (ob_iter->id.name[2] != obactive_name[0]) {
+ break;
+ }
+ if (ob_iter->type != OB_MBALL || ob_iter->data == metaball_src) {
+ continue;
+ }
+ BLI_split_name_num(ob_name, &ob_nr, ob_iter->id.name + 2, '.');
+ if (!STREQ(obactive_name, ob_name)) {
+ break;
+ }
+
+ mball_data_properties_copy(static_cast<MetaBall *>(ob_iter->data), metaball_src);
+ }
+
+ for (ob_iter = static_cast<Object *>(ob_src->id.next); ob_iter != nullptr;
+ ob_iter = static_cast<Object *>(ob_iter->id.next)) {
+ if (ob_iter->id.name[2] != obactive_name[0] || ID_IS_LINKED(ob_iter)) {
+ break;
+ }
+ if (ob_iter->type != OB_MBALL || ob_iter->data == metaball_src) {
+ continue;
+ }
+ BLI_split_name_num(ob_name, &ob_nr, ob_iter->id.name + 2, '.');
+ if (!STREQ(obactive_name, ob_name)) {
+ break;
+ }
+
+ mball_data_properties_copy(static_cast<MetaBall *>(ob_iter->data), metaball_src);
+ }
+
+ ob_src = ob_iter;
+ }
+}
+
+Object *BKE_mball_basis_find(Scene *scene, Object *object)
+{
+ Object *bob = object;
+ int basisnr, obnr;
+ char basisname[MAX_ID_NAME], obname[MAX_ID_NAME];
+
+ BLI_split_name_num(basisname, &basisnr, object->id.name + 2, '.');
+
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ Object *ob = base->object;
+ if ((ob->type == OB_MBALL) && !(base->flag & BASE_FROM_DUPLI)) {
+ if (ob != bob) {
+ BLI_split_name_num(obname, &obnr, ob->id.name + 2, '.');
+
+ /* Object ob has to be in same "group" ... it means,
+ * that it has to have same base of its name. */
+ if (STREQ(obname, basisname)) {
+ if (obnr < basisnr) {
+ object = ob;
+ basisnr = obnr;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return object;
+}
+
+bool BKE_mball_minmax_ex(
+ const MetaBall *mb, float min[3], float max[3], const float obmat[4][4], const short flag)
+{
+ const float scale = obmat ? mat4_to_scale(obmat) : 1.0f;
+ bool changed = false;
+ float centroid[3], vec[3];
+
+ INIT_MINMAX(min, max);
+
+ LISTBASE_FOREACH (const MetaElem *, ml, &mb->elems) {
+ if ((ml->flag & flag) == flag) {
+ const float scale_mb = (ml->rad * 0.5f) * scale;
+
+ if (obmat) {
+ mul_v3_m4v3(centroid, obmat, &ml->x);
+ }
+ else {
+ copy_v3_v3(centroid, &ml->x);
+ }
+
+ /* TODO(@campbellbarton): non circle shapes cubes etc, probably nobody notices. */
+ for (int i = -1; i != 3; i += 2) {
+ copy_v3_v3(vec, centroid);
+ add_v3_fl(vec, scale_mb * i);
+ minmax_v3v3_v3(min, max, vec);
+ }
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+bool BKE_mball_minmax(const MetaBall *mb, float min[3], float max[3])
+{
+ INIT_MINMAX(min, max);
+
+ LISTBASE_FOREACH (const MetaElem *, ml, &mb->elems) {
+ minmax_v3v3_v3(min, max, &ml->x);
+ }
+
+ return (BLI_listbase_is_empty(&mb->elems) == false);
+}
+
+bool BKE_mball_center_median(const MetaBall *mb, float r_cent[3])
+{
+ int total = 0;
+
+ zero_v3(r_cent);
+
+ LISTBASE_FOREACH (const MetaElem *, ml, &mb->elems) {
+ add_v3_v3(r_cent, &ml->x);
+ total++;
+ }
+
+ if (total) {
+ mul_v3_fl(r_cent, 1.0f / (float)total);
+ }
+
+ return (total != 0);
+}
+
+bool BKE_mball_center_bounds(const MetaBall *mb, float r_cent[3])
+{
+ float min[3], max[3];
+
+ if (BKE_mball_minmax(mb, min, max)) {
+ mid_v3_v3v3(r_cent, min, max);
+ return true;
+ }
+
+ return false;
+}
+
+void BKE_mball_transform(MetaBall *mb, const float mat[4][4], const bool do_props)
+{
+ float quat[4];
+ const float scale = mat4_to_scale(mat);
+ const float scale_sqrt = sqrtf(scale);
+
+ mat4_to_quat(quat, mat);
+
+ LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) {
+ mul_m4_v3(mat, &ml->x);
+ mul_qt_qtqt(ml->quat, quat, ml->quat);
+
+ if (do_props) {
+ ml->rad *= scale;
+ /* hrmf, probably elems shouldn't be
+ * treating scale differently - campbell */
+ if (!MB_TYPE_SIZE_SQUARED(ml->type)) {
+ mul_v3_fl(&ml->expx, scale);
+ }
+ else {
+ mul_v3_fl(&ml->expx, scale_sqrt);
+ }
+ }
+ }
+}
+
+void BKE_mball_translate(MetaBall *mb, const float offset[3])
+{
+ LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) {
+ add_v3_v3(&ml->x, offset);
+ }
+}
+
+int BKE_mball_select_count(const MetaBall *mb)
+{
+ int sel = 0;
+ LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) {
+ if (ml->flag & SELECT) {
+ sel++;
+ }
+ }
+ return sel;
+}
+
+int BKE_mball_select_count_multi(Base **bases, int bases_len)
+{
+ int sel = 0;
+ for (uint ob_index = 0; ob_index < bases_len; ob_index++) {
+ const Object *obedit = bases[ob_index]->object;
+ const MetaBall *mb = (MetaBall *)obedit->data;
+ sel += BKE_mball_select_count(mb);
+ }
+ return sel;
+}
+
+bool BKE_mball_select_all(MetaBall *mb)
+{
+ bool changed = false;
+ LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) {
+ if ((ml->flag & SELECT) == 0) {
+ ml->flag |= SELECT;
+ changed = true;
+ }
+ }
+ return changed;
+}
+
+bool BKE_mball_select_all_multi_ex(Base **bases, int bases_len)
+{
+ bool changed_multi = false;
+ for (uint ob_index = 0; ob_index < bases_len; ob_index++) {
+ Object *obedit = bases[ob_index]->object;
+ MetaBall *mb = static_cast<MetaBall *>(obedit->data);
+ changed_multi |= BKE_mball_select_all(mb);
+ }
+ return changed_multi;
+}
+
+bool BKE_mball_deselect_all(MetaBall *mb)
+{
+ bool changed = false;
+ LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) {
+ if ((ml->flag & SELECT) != 0) {
+ ml->flag &= ~SELECT;
+ changed = true;
+ }
+ }
+ return changed;
+}
+
+bool BKE_mball_deselect_all_multi_ex(Base **bases, int bases_len)
+{
+ bool changed_multi = false;
+ for (uint ob_index = 0; ob_index < bases_len; ob_index++) {
+ Object *obedit = bases[ob_index]->object;
+ MetaBall *mb = static_cast<MetaBall *>(obedit->data);
+ changed_multi |= BKE_mball_deselect_all(mb);
+ DEG_id_tag_update(&mb->id, ID_RECALC_SELECT);
+ }
+ return changed_multi;
+}
+
+bool BKE_mball_select_swap(MetaBall *mb)
+{
+ bool changed = false;
+ LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) {
+ ml->flag ^= SELECT;
+ changed = true;
+ }
+ return changed;
+}
+
+bool BKE_mball_select_swap_multi_ex(Base **bases, int bases_len)
+{
+ bool changed_multi = false;
+ for (uint ob_index = 0; ob_index < bases_len; ob_index++) {
+ Object *obedit = bases[ob_index]->object;
+ MetaBall *mb = (MetaBall *)obedit->data;
+ changed_multi |= BKE_mball_select_swap(mb);
+ }
+ return changed_multi;
+}
+
+/* **** Depsgraph evaluation **** */
+
+void BKE_mball_data_update(Depsgraph *depsgraph, Scene *scene, Object *ob)
+{
+ BLI_assert(ob->type == OB_MBALL);
+
+ BKE_object_free_derived_caches(ob);
+
+ const Object *basis_object = BKE_mball_basis_find(scene, ob);
+ if (ob != basis_object) {
+ return;
+ }
+
+ Mesh *mesh = BKE_mball_polygonize(depsgraph, scene, ob);
+ if (mesh == nullptr) {
+ return;
+ }
+
+ const MetaBall *mball = static_cast<MetaBall *>(ob->data);
+ mesh->mat = static_cast<Material **>(MEM_dupallocN(mball->mat));
+ 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);
+ }
+
+ ob->runtime.geometry_set_eval = new GeometrySet(GeometrySet::create_with_mesh(mesh));
+
+ if (ob->runtime.bb == nullptr) {
+ ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
+ }
+ blender::float3 min(std::numeric_limits<float>::max());
+ blender::float3 max(-std::numeric_limits<float>::max());
+ if (!ob->runtime.geometry_set_eval->compute_boundbox_without_instances(&min, &max)) {
+ min = blender::float3(0);
+ max = blender::float3(0);
+ }
+ BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+};