2014-01-21 12:11:34 +01:00
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
* \ingroup bke
|
2014-01-21 12:11:34 +01:00
|
|
|
*/
|
|
|
|
|
2014-01-21 21:01:12 +01:00
|
|
|
#include <limits.h>
|
2014-01-21 12:11:34 +01:00
|
|
|
#include <stddef.h>
|
2020-03-19 09:33:03 +01:00
|
|
|
#include <stdlib.h>
|
2014-01-21 12:11:34 +01:00
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "BLI_listbase.h"
|
|
|
|
#include "BLI_string_utf8.h"
|
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
#include "BLI_alloca.h"
|
2014-01-21 12:11:34 +01:00
|
|
|
#include "BLI_math.h"
|
|
|
|
#include "BLI_rand.h"
|
|
|
|
|
|
|
|
#include "DNA_anim_types.h"
|
2018-08-29 15:32:50 +02:00
|
|
|
#include "DNA_collection_types.h"
|
2014-01-21 12:11:34 +01:00
|
|
|
#include "DNA_mesh_types.h"
|
2018-09-13 15:55:36 +02:00
|
|
|
#include "DNA_meshdata_types.h"
|
2014-01-21 12:11:34 +01:00
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "DNA_vfont_types.h"
|
|
|
|
|
Collections and groups unification
OVERVIEW
* In 2.7 terminology, all layers and groups are now collection datablocks.
* These collections are nestable, linkable, instanceable, overrideable, ..
which opens up new ways to set up scenes and link + override data.
* Viewport/render visibility and selectability are now a part of the collection
and shared across all view layers and linkable.
* View layers define which subset of the scene collection hierarchy is excluded
for each. For many workflows one view layer can be used, these are more of an
advanced feature now.
OUTLINER
* The outliner now has a "View Layer" display mode instead of "Collections",
which can display the collections and/or objects in the view layer.
* In this display mode, collections can be excluded with the right click menu.
These will then be greyed out and their objects will be excluded.
* To view collections not linked to any scene, the "Blender File" display mode
can be used, with the new filtering option to just see Colleciton datablocks.
* The outliner right click menus for collections and objects were reorganized.
* Drag and drop still needs to be improved. Like before, dragging the icon or
text gives different results, we'll unify this later.
LINKING AND OVERRIDES
* Collections can now be linked into the scene without creating an instance,
with the link/append operator or from the collections view in the outliner.
* Collections can get static overrides with the right click menu in the outliner,
but this is rather unreliable and not clearly communicated at the moment.
* We still need to improve the make override operator to turn collection instances
into collections with overrides directly in the scene.
PERFORMANCE
* We tried to make performance not worse than before and improve it in some
cases. The main thing that's still a bit slower is multiple scenes, we have to
change the layer syncing to only updated affected scenes.
* Collections keep a list of their parent collections for faster incremental
updates in syncing and caching.
* View layer bases are now in a object -> base hash to avoid quadratic time
lookups internally and in API functions like visible_get().
VERSIONING
* Compatibility with 2.7 files should be improved due to the new visibility
controls. Of course users may not want to set up their scenes differently
now to avoid having separate layers and groups.
* Compatibility with 2.8 is mostly there, and was tested on Eevee demo and Hero
files. There's a few things which are know to be not quite compatible, like
nested layer collections inside groups.
* The versioning code for 2.8 files is quite complicated, and isolated behind
#ifdef so it can be removed at the end of the release cycle.
KNOWN ISSUES
* The G-key group operators in the 3D viewport were left mostly as is, they
need to be modified still to fit better.
* Same for the groups panel in the object properties. This needs to be updated
still, or perhaps replaced by something better.
* Collections must all have a unique name. Less restrictive namespacing is to
be done later, we'll have to see how important this is as all objects within
the collections must also have a unique name anyway.
* Full scene copy and delete scene are exactly doing the right thing yet.
Differential Revision: https://developer.blender.org/D3383
https://code.blender.org/2018/05/collections-and-groups/
2018-04-30 15:57:22 +02:00
|
|
|
#include "BKE_collection.h"
|
2020-04-03 11:35:04 +02:00
|
|
|
#include "BKE_duplilist.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BKE_editmesh.h"
|
2020-08-13 13:56:15 +10:00
|
|
|
#include "BKE_editmesh_cache.h"
|
2014-01-21 12:11:34 +01:00
|
|
|
#include "BKE_font.h"
|
|
|
|
#include "BKE_global.h"
|
2017-12-01 11:24:21 -02:00
|
|
|
#include "BKE_idprop.h"
|
2014-01-21 12:11:34 +01:00
|
|
|
#include "BKE_lattice.h"
|
|
|
|
#include "BKE_main.h"
|
|
|
|
#include "BKE_mesh.h"
|
2018-09-13 15:55:36 +02:00
|
|
|
#include "BKE_mesh_iterators.h"
|
2018-06-05 16:58:08 +02:00
|
|
|
#include "BKE_mesh_runtime.h"
|
2014-01-21 12:11:34 +01:00
|
|
|
#include "BKE_object.h"
|
2016-12-28 17:30:58 +01:00
|
|
|
#include "BKE_particle.h"
|
2014-01-21 12:11:34 +01:00
|
|
|
#include "BKE_scene.h"
|
|
|
|
|
2017-04-06 15:37:46 +02:00
|
|
|
#include "DEG_depsgraph.h"
|
2018-04-06 12:07:27 +02:00
|
|
|
#include "DEG_depsgraph_query.h"
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2017-04-14 18:13:44 +03:00
|
|
|
#include "BLI_hash.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_strict_flags.h"
|
2014-01-21 22:48:27 +11:00
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Internal Duplicate Context
|
|
|
|
* \{ */
|
2014-01-21 12:11:34 +01:00
|
|
|
|
|
|
|
typedef struct DupliContext {
|
2019-04-17 06:17:24 +02:00
|
|
|
Depsgraph *depsgraph;
|
2019-04-21 13:44:06 +10:00
|
|
|
/** XXX child objects are selected from this group if set, could be nicer. */
|
|
|
|
Collection *collection;
|
|
|
|
/** Only to check if the object is in edit-mode. */
|
|
|
|
Object *obedit;
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
Scene *scene;
|
|
|
|
ViewLayer *view_layer;
|
|
|
|
Object *object;
|
|
|
|
float space_mat[4][4];
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
int persistent_id[MAX_DUPLI_RECUR];
|
|
|
|
int level;
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
const struct DupliGenerator *gen;
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-21 13:44:06 +10:00
|
|
|
/** Result containers. */
|
2020-08-13 09:49:49 +10:00
|
|
|
ListBase *duplilist; /* Legacy doubly-linked list. */
|
2014-01-21 12:11:34 +01:00
|
|
|
} DupliContext;
|
|
|
|
|
|
|
|
typedef struct DupliGenerator {
|
2020-08-13 09:49:49 +10:00
|
|
|
short type; /* Dupli Type, see members of #OB_DUPLI. */
|
2019-04-17 06:17:24 +02:00
|
|
|
void (*make_duplis)(const DupliContext *ctx);
|
2014-01-21 12:11:34 +01:00
|
|
|
} DupliGenerator;
|
|
|
|
|
|
|
|
static const DupliGenerator *get_dupli_generator(const DupliContext *ctx);
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/**
|
|
|
|
* Create initial context for root object.
|
|
|
|
*/
|
2019-09-14 08:10:50 +10:00
|
|
|
static void init_context(DupliContext *r_ctx,
|
|
|
|
Depsgraph *depsgraph,
|
|
|
|
Scene *scene,
|
|
|
|
Object *ob,
|
|
|
|
const float space_mat[4][4])
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
r_ctx->depsgraph = depsgraph;
|
|
|
|
r_ctx->scene = scene;
|
|
|
|
r_ctx->view_layer = DEG_get_evaluated_view_layer(depsgraph);
|
|
|
|
r_ctx->collection = NULL;
|
|
|
|
|
|
|
|
r_ctx->object = ob;
|
|
|
|
r_ctx->obedit = OBEDIT_FROM_OBACT(ob);
|
2019-04-22 09:39:35 +10:00
|
|
|
if (space_mat) {
|
2019-04-17 06:17:24 +02:00
|
|
|
copy_m4_m4(r_ctx->space_mat, space_mat);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
else {
|
2019-04-17 06:17:24 +02:00
|
|
|
unit_m4(r_ctx->space_mat);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
r_ctx->level = 0;
|
|
|
|
|
|
|
|
r_ctx->gen = get_dupli_generator(r_ctx);
|
|
|
|
|
|
|
|
r_ctx->duplilist = NULL;
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/**
|
|
|
|
* Create sub-context for recursive duplis.
|
|
|
|
*/
|
2019-04-17 06:17:24 +02:00
|
|
|
static void copy_dupli_context(
|
2019-09-14 08:10:50 +10:00
|
|
|
DupliContext *r_ctx, const DupliContext *ctx, Object *ob, const float mat[4][4], int index)
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
*r_ctx = *ctx;
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2019-04-27 12:07:07 +10:00
|
|
|
/* XXX annoying, previously was done by passing an ID* argument,
|
|
|
|
* this at least is more explicit. */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (ctx->gen->type == OB_DUPLICOLLECTION) {
|
2019-04-17 06:17:24 +02:00
|
|
|
r_ctx->collection = ctx->object->instance_collection;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
r_ctx->object = ob;
|
2019-04-22 09:39:35 +10:00
|
|
|
if (mat) {
|
2019-04-17 06:17:24 +02:00
|
|
|
mul_m4_m4m4(r_ctx->space_mat, (float(*)[4])ctx->space_mat, mat);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
r_ctx->persistent_id[r_ctx->level] = index;
|
|
|
|
++r_ctx->level;
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
r_ctx->gen = get_dupli_generator(r_ctx);
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/**
|
|
|
|
* Generate a dupli instance.
|
|
|
|
*
|
|
|
|
* \param mat: is transform of the object relative to current context (including #Object.obmat).
|
2014-01-21 12:11:34 +01:00
|
|
|
*/
|
2020-08-10 18:29:17 +10:00
|
|
|
static DupliObject *make_dupli(const DupliContext *ctx,
|
|
|
|
Object *ob,
|
|
|
|
const float mat[4][4],
|
|
|
|
int index)
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
DupliObject *dob;
|
|
|
|
int i;
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Add a #DupliObject instance to the result container. */
|
2019-04-17 06:17:24 +02:00
|
|
|
if (ctx->duplilist) {
|
|
|
|
dob = MEM_callocN(sizeof(DupliObject), "dupli object");
|
|
|
|
BLI_addtail(ctx->duplilist, dob);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dob->ob = ob;
|
|
|
|
mul_m4_m4m4(dob->mat, (float(*)[4])ctx->space_mat, mat);
|
|
|
|
dob->type = ctx->gen->type;
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Set persistent id, which is an array with a persistent index for each level
|
2019-04-17 06:17:24 +02:00
|
|
|
* (particle number, vertex number, ..). by comparing this we can find the same
|
2020-08-13 09:49:49 +10:00
|
|
|
* dupli-object between frames, which is needed for motion blur.
|
|
|
|
* The last level is ordered first in the array. */
|
2019-04-17 06:17:24 +02:00
|
|
|
dob->persistent_id[0] = index;
|
2019-04-22 09:39:35 +10:00
|
|
|
for (i = 1; i < ctx->level + 1; i++) {
|
2019-04-17 06:17:24 +02:00
|
|
|
dob->persistent_id[i] = ctx->persistent_id[ctx->level - i];
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Fill rest of values with #INT_MAX which index will never have as value. */
|
2019-04-22 09:39:35 +10:00
|
|
|
for (; i < MAX_DUPLI_RECUR; i++) {
|
2019-04-17 06:17:24 +02:00
|
|
|
dob->persistent_id[i] = INT_MAX;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Meta-balls never draw in duplis, they are instead merged into one by the basis
|
|
|
|
* meta-ball outside of the group. this does mean that if that meta-ball is not in the
|
2019-04-17 06:17:24 +02:00
|
|
|
* scene, they will not show up at all, limitation that should be solved once. */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (ob->type == OB_MBALL) {
|
2019-04-17 06:17:24 +02:00
|
|
|
dob->no_draw = true;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Random number.
|
|
|
|
* The logic here is designed to match Cycles. */
|
2019-04-17 06:17:24 +02:00
|
|
|
dob->random_id = BLI_hash_string(dob->ob->id.name + 2);
|
|
|
|
|
|
|
|
if (dob->persistent_id[0] != INT_MAX) {
|
2020-07-06 16:52:28 +02:00
|
|
|
for (i = 0; i < MAX_DUPLI_RECUR; i++) {
|
2019-04-17 06:17:24 +02:00
|
|
|
dob->random_id = BLI_hash_int_2d(dob->random_id, (unsigned int)dob->persistent_id[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
dob->random_id = BLI_hash_int_2d(dob->random_id, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->object != ob) {
|
|
|
|
dob->random_id ^= BLI_hash_int(BLI_hash_string(ctx->object->id.name + 2));
|
|
|
|
}
|
|
|
|
|
|
|
|
return dob;
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/**
|
2020-08-13 09:49:49 +10:00
|
|
|
* Recursive dupli-objects.
|
2020-08-10 18:29:17 +10:00
|
|
|
*
|
2020-08-13 09:49:49 +10:00
|
|
|
* \param space_mat: is the local dupli-space (excluding dupli #Object.obmat).
|
2014-01-21 12:11:34 +01:00
|
|
|
*/
|
2019-04-17 06:17:24 +02:00
|
|
|
static void make_recursive_duplis(const DupliContext *ctx,
|
|
|
|
Object *ob,
|
2019-09-14 08:10:50 +10:00
|
|
|
const float space_mat[4][4],
|
2019-04-17 06:17:24 +02:00
|
|
|
int index)
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Simple preventing of too deep nested collections with #MAX_DUPLI_RECUR. */
|
2019-04-17 06:17:24 +02:00
|
|
|
if (ctx->level < MAX_DUPLI_RECUR) {
|
|
|
|
DupliContext rctx;
|
|
|
|
copy_dupli_context(&rctx, ctx, ob, space_mat, index);
|
|
|
|
if (rctx.gen) {
|
|
|
|
rctx.gen->make_duplis(&rctx);
|
|
|
|
}
|
|
|
|
}
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Internal Child Duplicates (Used by Other Functions)
|
|
|
|
* \{ */
|
2014-01-21 12:11:34 +01:00
|
|
|
|
|
|
|
typedef void (*MakeChildDuplisFunc)(const DupliContext *ctx, void *userdata, Object *child);
|
|
|
|
|
|
|
|
static bool is_child(const Object *ob, const Object *parent)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
const Object *ob_parent = ob->parent;
|
|
|
|
while (ob_parent) {
|
2019-04-22 09:39:35 +10:00
|
|
|
if (ob_parent == parent) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return true;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
ob_parent = ob_parent->parent;
|
|
|
|
}
|
|
|
|
return false;
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/**
|
|
|
|
* Create duplis from every child in scene or collection.
|
|
|
|
*/
|
2019-04-17 06:17:24 +02:00
|
|
|
static void make_child_duplis(const DupliContext *ctx,
|
|
|
|
void *userdata,
|
|
|
|
MakeChildDuplisFunc make_child_duplis_cb)
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
Object *parent = ctx->object;
|
|
|
|
|
|
|
|
if (ctx->collection) {
|
|
|
|
eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (ctx->collection, ob, mode) {
|
|
|
|
if ((ob != ctx->obedit) && is_child(ob, parent)) {
|
|
|
|
DupliContext pctx;
|
|
|
|
copy_dupli_context(&pctx, ctx, ctx->object, NULL, _base_id);
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Meta-balls have a different dupli handling. */
|
2019-04-17 06:17:24 +02:00
|
|
|
if (ob->type != OB_MBALL) {
|
2020-08-13 09:49:49 +10:00
|
|
|
ob->flag |= OB_DONE; /* Doesn't render. */
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
make_child_duplis_cb(&pctx, userdata, ob);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int baseid = 0;
|
|
|
|
ViewLayer *view_layer = ctx->view_layer;
|
|
|
|
for (Base *base = view_layer->object_bases.first; base; base = base->next, baseid++) {
|
|
|
|
Object *ob = base->object;
|
|
|
|
if ((ob != ctx->obedit) && is_child(ob, parent)) {
|
|
|
|
DupliContext pctx;
|
|
|
|
copy_dupli_context(&pctx, ctx, ctx->object, NULL, baseid);
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Meta-balls have a different dupli-handling. */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (ob->type != OB_MBALL) {
|
2020-08-13 09:49:49 +10:00
|
|
|
ob->flag |= OB_DONE; /* Doesn't render. */
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
make_child_duplis_cb(&pctx, userdata, ob);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/** \} */
|
|
|
|
|
2020-08-13 13:30:25 +10:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Internal Data Access Utilities
|
|
|
|
* \{ */
|
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
static Mesh *mesh_data_from_duplicator_object(Object *ob,
|
|
|
|
BMEditMesh **r_em,
|
|
|
|
const float (**r_vert_coords)[3],
|
|
|
|
const float (**r_vert_normals)[3])
|
2020-08-13 13:30:25 +10:00
|
|
|
{
|
|
|
|
/* Gather mesh info. */
|
|
|
|
BMEditMesh *em = BKE_editmesh_from_object(ob);
|
|
|
|
Mesh *me_eval;
|
|
|
|
|
|
|
|
*r_em = NULL;
|
2020-08-13 13:56:15 +10:00
|
|
|
*r_vert_coords = NULL;
|
|
|
|
if (r_vert_normals != NULL) {
|
|
|
|
*r_vert_normals = NULL;
|
|
|
|
}
|
2020-08-13 13:30:25 +10:00
|
|
|
|
|
|
|
/* We do not need any render-specific handling anymore, depsgraph takes care of that. */
|
|
|
|
/* NOTE: Do direct access to the evaluated mesh: this function is used
|
|
|
|
* during meta balls evaluation. But even without those all the objects
|
|
|
|
* which are needed for correct instancing are already evaluated. */
|
|
|
|
if (em != NULL) {
|
|
|
|
/* Note that this will only show deformation if #eModifierMode_OnCage is enabled.
|
|
|
|
* We could change this but it matches 2.7x behavior. */
|
|
|
|
me_eval = em->mesh_eval_cage;
|
2020-08-13 13:56:15 +10:00
|
|
|
if ((me_eval == NULL) || (me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) {
|
|
|
|
EditMeshData *emd = me_eval ? me_eval->runtime.edit_data : NULL;
|
|
|
|
|
|
|
|
/* Only assign edit-mesh in the case we can't use `me_eval`. */
|
|
|
|
*r_em = em;
|
|
|
|
me_eval = NULL;
|
|
|
|
|
|
|
|
if ((emd != NULL) && (emd->vertexCos != NULL)) {
|
|
|
|
*r_vert_coords = emd->vertexCos;
|
|
|
|
if (r_vert_normals != NULL) {
|
|
|
|
BKE_editmesh_cache_ensure_vert_normals(em, emd);
|
|
|
|
*r_vert_normals = emd->vertexNos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-08-13 13:30:25 +10:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
me_eval = BKE_object_get_evaluated_mesh(ob);
|
|
|
|
}
|
|
|
|
return me_eval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Dupli-Collection Implementation (#OB_DUPLICOLLECTION)
|
|
|
|
* \{ */
|
2014-01-21 12:11:34 +01:00
|
|
|
|
Collections and groups unification
OVERVIEW
* In 2.7 terminology, all layers and groups are now collection datablocks.
* These collections are nestable, linkable, instanceable, overrideable, ..
which opens up new ways to set up scenes and link + override data.
* Viewport/render visibility and selectability are now a part of the collection
and shared across all view layers and linkable.
* View layers define which subset of the scene collection hierarchy is excluded
for each. For many workflows one view layer can be used, these are more of an
advanced feature now.
OUTLINER
* The outliner now has a "View Layer" display mode instead of "Collections",
which can display the collections and/or objects in the view layer.
* In this display mode, collections can be excluded with the right click menu.
These will then be greyed out and their objects will be excluded.
* To view collections not linked to any scene, the "Blender File" display mode
can be used, with the new filtering option to just see Colleciton datablocks.
* The outliner right click menus for collections and objects were reorganized.
* Drag and drop still needs to be improved. Like before, dragging the icon or
text gives different results, we'll unify this later.
LINKING AND OVERRIDES
* Collections can now be linked into the scene without creating an instance,
with the link/append operator or from the collections view in the outliner.
* Collections can get static overrides with the right click menu in the outliner,
but this is rather unreliable and not clearly communicated at the moment.
* We still need to improve the make override operator to turn collection instances
into collections with overrides directly in the scene.
PERFORMANCE
* We tried to make performance not worse than before and improve it in some
cases. The main thing that's still a bit slower is multiple scenes, we have to
change the layer syncing to only updated affected scenes.
* Collections keep a list of their parent collections for faster incremental
updates in syncing and caching.
* View layer bases are now in a object -> base hash to avoid quadratic time
lookups internally and in API functions like visible_get().
VERSIONING
* Compatibility with 2.7 files should be improved due to the new visibility
controls. Of course users may not want to set up their scenes differently
now to avoid having separate layers and groups.
* Compatibility with 2.8 is mostly there, and was tested on Eevee demo and Hero
files. There's a few things which are know to be not quite compatible, like
nested layer collections inside groups.
* The versioning code for 2.8 files is quite complicated, and isolated behind
#ifdef so it can be removed at the end of the release cycle.
KNOWN ISSUES
* The G-key group operators in the 3D viewport were left mostly as is, they
need to be modified still to fit better.
* Same for the groups panel in the object properties. This needs to be updated
still, or perhaps replaced by something better.
* Collections must all have a unique name. Less restrictive namespacing is to
be done later, we'll have to see how important this is as all objects within
the collections must also have a unique name anyway.
* Full scene copy and delete scene are exactly doing the right thing yet.
Differential Revision: https://developer.blender.org/D3383
https://code.blender.org/2018/05/collections-and-groups/
2018-04-30 15:57:22 +02:00
|
|
|
static void make_duplis_collection(const DupliContext *ctx)
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
Object *ob = ctx->object;
|
|
|
|
Collection *collection;
|
|
|
|
float collection_mat[4][4];
|
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (ob->instance_collection == NULL) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
collection = ob->instance_collection;
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Combine collection offset and `obmat`. */
|
2019-04-17 06:17:24 +02:00
|
|
|
unit_m4(collection_mat);
|
|
|
|
sub_v3_v3(collection_mat[3], collection->instance_offset);
|
|
|
|
mul_m4_m4m4(collection_mat, ob->obmat, collection_mat);
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Don't access 'ob->obmat' from now on. */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, cob, mode) {
|
|
|
|
if (cob != ob) {
|
|
|
|
float mat[4][4];
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Collection dupli-offset, should apply after everything else. */
|
2019-04-17 06:17:24 +02:00
|
|
|
mul_m4_m4m4(mat, collection_mat, cob->obmat);
|
|
|
|
|
|
|
|
make_dupli(ctx, cob, mat, _base_id);
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Recursion. */
|
2019-04-17 06:17:24 +02:00
|
|
|
make_recursive_duplis(ctx, cob, collection_mat, _base_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
Collections and groups unification
OVERVIEW
* In 2.7 terminology, all layers and groups are now collection datablocks.
* These collections are nestable, linkable, instanceable, overrideable, ..
which opens up new ways to set up scenes and link + override data.
* Viewport/render visibility and selectability are now a part of the collection
and shared across all view layers and linkable.
* View layers define which subset of the scene collection hierarchy is excluded
for each. For many workflows one view layer can be used, these are more of an
advanced feature now.
OUTLINER
* The outliner now has a "View Layer" display mode instead of "Collections",
which can display the collections and/or objects in the view layer.
* In this display mode, collections can be excluded with the right click menu.
These will then be greyed out and their objects will be excluded.
* To view collections not linked to any scene, the "Blender File" display mode
can be used, with the new filtering option to just see Colleciton datablocks.
* The outliner right click menus for collections and objects were reorganized.
* Drag and drop still needs to be improved. Like before, dragging the icon or
text gives different results, we'll unify this later.
LINKING AND OVERRIDES
* Collections can now be linked into the scene without creating an instance,
with the link/append operator or from the collections view in the outliner.
* Collections can get static overrides with the right click menu in the outliner,
but this is rather unreliable and not clearly communicated at the moment.
* We still need to improve the make override operator to turn collection instances
into collections with overrides directly in the scene.
PERFORMANCE
* We tried to make performance not worse than before and improve it in some
cases. The main thing that's still a bit slower is multiple scenes, we have to
change the layer syncing to only updated affected scenes.
* Collections keep a list of their parent collections for faster incremental
updates in syncing and caching.
* View layer bases are now in a object -> base hash to avoid quadratic time
lookups internally and in API functions like visible_get().
VERSIONING
* Compatibility with 2.7 files should be improved due to the new visibility
controls. Of course users may not want to set up their scenes differently
now to avoid having separate layers and groups.
* Compatibility with 2.8 is mostly there, and was tested on Eevee demo and Hero
files. There's a few things which are know to be not quite compatible, like
nested layer collections inside groups.
* The versioning code for 2.8 files is quite complicated, and isolated behind
#ifdef so it can be removed at the end of the release cycle.
KNOWN ISSUES
* The G-key group operators in the 3D viewport were left mostly as is, they
need to be modified still to fit better.
* Same for the groups panel in the object properties. This needs to be updated
still, or perhaps replaced by something better.
* Collections must all have a unique name. Less restrictive namespacing is to
be done later, we'll have to see how important this is as all objects within
the collections must also have a unique name anyway.
* Full scene copy and delete scene are exactly doing the right thing yet.
Differential Revision: https://developer.blender.org/D3383
https://code.blender.org/2018/05/collections-and-groups/
2018-04-30 15:57:22 +02:00
|
|
|
static const DupliGenerator gen_dupli_collection = {
|
2019-04-17 06:17:24 +02:00
|
|
|
OB_DUPLICOLLECTION, /* type */
|
|
|
|
make_duplis_collection /* make_duplis */
|
2014-01-21 12:11:34 +01:00
|
|
|
};
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Dupli-Vertices Implementation (#OB_DUPLIVERTS for Geometry)
|
|
|
|
* \{ */
|
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
/** Values shared between different mesh types. */
|
|
|
|
typedef struct VertexDupliData_Params {
|
|
|
|
/**
|
|
|
|
* It's important we use this context instead of the `ctx` passed into #make_child_duplis
|
|
|
|
* since these won't match in the case of recursion.
|
|
|
|
*/
|
|
|
|
const DupliContext *ctx;
|
|
|
|
|
|
|
|
bool use_rotation;
|
|
|
|
} VertexDupliData_Params;
|
|
|
|
|
|
|
|
typedef struct VertexDupliData_Mesh {
|
|
|
|
VertexDupliData_Params params;
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
int totvert;
|
2020-08-13 13:31:29 +10:00
|
|
|
const MVert *mvert;
|
|
|
|
|
2020-08-13 09:28:29 +10:00
|
|
|
const float (*orco)[3];
|
2020-08-13 13:56:15 +10:00
|
|
|
} VertexDupliData_Mesh;
|
2020-08-11 15:26:14 +10:00
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
typedef struct VertexDupliData_EditMesh {
|
|
|
|
VertexDupliData_Params params;
|
|
|
|
|
|
|
|
BMEditMesh *em;
|
|
|
|
|
|
|
|
/* Can be NULL. */
|
|
|
|
const float (*vert_coords)[3];
|
|
|
|
const float (*vert_normals)[3];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \note The edit-mesh may assign #DupliObject.orco in cases when a regular mesh wouldn't.
|
|
|
|
* For edit-meshes we only check for deformation, for regular meshes we check if #CD_ORCO exists.
|
|
|
|
*
|
|
|
|
* At the moment this isn't a meaningful difference since requesting #CD_ORCO causes the
|
|
|
|
* edit-mesh to be converted into a mesh.
|
|
|
|
*/
|
|
|
|
bool has_orco;
|
|
|
|
} VertexDupliData_EditMesh;
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2020-08-13 10:09:39 +10:00
|
|
|
/**
|
|
|
|
* \param no: The direction,
|
|
|
|
* currently this is copied from a `short[3]` normal without division.
|
2020-08-13 13:56:15 +10:00
|
|
|
* Can be null when \a use_rotation is false.
|
2020-08-13 10:09:39 +10:00
|
|
|
*/
|
2019-04-17 06:17:24 +02:00
|
|
|
static void get_duplivert_transform(const float co[3],
|
2020-08-13 10:09:39 +10:00
|
|
|
const float no[3],
|
2020-08-13 09:28:29 +10:00
|
|
|
const bool use_rotation,
|
|
|
|
const short axis,
|
|
|
|
const short upflag,
|
|
|
|
float r_mat[4][4])
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
float quat[4];
|
|
|
|
const float size[3] = {1.0f, 1.0f, 1.0f};
|
|
|
|
|
|
|
|
if (use_rotation) {
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Construct rotation matrix from normals. */
|
2020-08-13 10:09:39 +10:00
|
|
|
float no_flip[3];
|
|
|
|
negate_v3_v3(no_flip, no);
|
|
|
|
vec_to_quat(quat, no_flip, axis, upflag);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2019-04-22 09:39:35 +10:00
|
|
|
else {
|
2019-04-17 06:17:24 +02:00
|
|
|
unit_qt(quat);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 09:28:29 +10:00
|
|
|
loc_quat_size_to_mat4(r_mat, co, quat, size);
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
static DupliObject *vertex_dupli(const DupliContext *ctx,
|
|
|
|
Object *inst_ob,
|
|
|
|
const float child_imat[4][4],
|
|
|
|
int index,
|
|
|
|
const float co[3],
|
|
|
|
const float no[3],
|
|
|
|
const bool use_rotation)
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2020-08-13 09:49:49 +10:00
|
|
|
/* `obmat` is transform to vertex. */
|
2020-08-13 13:56:15 +10:00
|
|
|
float obmat[4][4];
|
|
|
|
get_duplivert_transform(co, no, use_rotation, inst_ob->trackflag, inst_ob->upflag, obmat);
|
|
|
|
|
|
|
|
float space_mat[4][4];
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Make offset relative to inst_ob using relative child transform. */
|
2020-08-13 13:56:15 +10:00
|
|
|
mul_mat3_m4_v3(child_imat, obmat[3]);
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Apply `obmat` _after_ the local vertex transform. */
|
2020-08-11 16:12:44 +02:00
|
|
|
mul_m4_m4m4(obmat, inst_ob->obmat, obmat);
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Space matrix is constructed by removing `obmat` transform,
|
|
|
|
* this yields the world-space transform for recursive duplis. */
|
2020-08-11 16:12:44 +02:00
|
|
|
mul_m4_m4m4(space_mat, obmat, inst_ob->imat);
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
DupliObject *dob = make_dupli(ctx, inst_ob, obmat, index);
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Recursion. */
|
2020-08-13 13:56:15 +10:00
|
|
|
make_recursive_duplis(ctx, inst_ob, space_mat, index);
|
|
|
|
|
|
|
|
return dob;
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
static void make_child_duplis_verts_from_mesh(const DupliContext *ctx,
|
|
|
|
void *userdata,
|
|
|
|
Object *inst_ob)
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2020-08-13 13:56:15 +10:00
|
|
|
VertexDupliData_Mesh *vdd = userdata;
|
|
|
|
const bool use_rotation = vdd->params.use_rotation;
|
|
|
|
|
|
|
|
const MVert *mvert = vdd->mvert;
|
|
|
|
const int totvert = vdd->totvert;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 13:37:43 +10:00
|
|
|
invert_m4_m4(inst_ob->imat, inst_ob->obmat);
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Relative transform from parent to child space. */
|
2020-08-13 13:56:15 +10:00
|
|
|
float child_imat[4][4];
|
|
|
|
mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat);
|
2020-08-11 16:12:44 +02:00
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
const MVert *mv = mvert;
|
|
|
|
for (int i = 0; i < totvert; i++, mv++) {
|
|
|
|
const float *co = mv->co;
|
|
|
|
const float no[3] = {UNPACK3(mv->no)};
|
|
|
|
DupliObject *dob = vertex_dupli(vdd->params.ctx, inst_ob, child_imat, i, co, no, use_rotation);
|
|
|
|
if (vdd->orco) {
|
|
|
|
copy_v3_v3(dob->orco, vdd->orco[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void make_child_duplis_verts_from_editmesh(const DupliContext *ctx,
|
|
|
|
void *userdata,
|
|
|
|
Object *inst_ob)
|
|
|
|
{
|
|
|
|
VertexDupliData_EditMesh *vdd = userdata;
|
|
|
|
BMEditMesh *em = vdd->em;
|
|
|
|
const bool use_rotation = vdd->params.use_rotation;
|
|
|
|
|
|
|
|
invert_m4_m4(inst_ob->imat, inst_ob->obmat);
|
|
|
|
/* Relative transform from parent to child space. */
|
|
|
|
float child_imat[4][4];
|
|
|
|
mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat);
|
|
|
|
|
|
|
|
BMVert *v;
|
|
|
|
BMIter iter;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
const float(*vert_coords)[3] = vdd->vert_coords;
|
|
|
|
const float(*vert_normals)[3] = vdd->vert_normals;
|
|
|
|
|
|
|
|
BM_ITER_MESH_INDEX (v, &iter, em->bm, BM_VERTS_OF_MESH, i) {
|
|
|
|
const float *co, *no;
|
|
|
|
if (vert_coords != NULL) {
|
|
|
|
co = vert_coords[i];
|
|
|
|
no = vert_normals ? vert_normals[i] : NULL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
co = v->co;
|
|
|
|
no = v->no;
|
|
|
|
}
|
|
|
|
|
|
|
|
DupliObject *dob = vertex_dupli(vdd->params.ctx, inst_ob, child_imat, i, co, no, use_rotation);
|
|
|
|
if (vdd->has_orco) {
|
|
|
|
copy_v3_v3(dob->orco, v->co);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2020-08-11 16:12:44 +02:00
|
|
|
static void make_duplis_verts(const DupliContext *ctx)
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2020-08-11 16:12:44 +02:00
|
|
|
Object *parent = ctx->object;
|
2020-08-13 13:56:15 +10:00
|
|
|
const bool use_rotation = parent->transflag & OB_DUPLIROT;
|
2020-08-11 15:26:14 +10:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Gather mesh info. */
|
2020-08-13 13:30:25 +10:00
|
|
|
BMEditMesh *em = NULL;
|
2020-08-13 13:56:15 +10:00
|
|
|
const float(*vert_coords)[3] = NULL;
|
|
|
|
const float(*vert_normals)[3] = NULL;
|
|
|
|
Mesh *me_eval = mesh_data_from_duplicator_object(
|
|
|
|
parent, &em, &vert_coords, use_rotation ? &vert_normals : NULL);
|
|
|
|
if (em == NULL && me_eval == NULL) {
|
2020-08-13 13:30:25 +10:00
|
|
|
return;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
VertexDupliData_Params vdd_params = {
|
|
|
|
.ctx = ctx,
|
|
|
|
.use_rotation = use_rotation,
|
|
|
|
};
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
if (em != NULL) {
|
|
|
|
VertexDupliData_EditMesh vdd = {
|
|
|
|
.params = vdd_params,
|
|
|
|
.em = em,
|
|
|
|
.vert_coords = vert_coords,
|
|
|
|
.vert_normals = vert_normals,
|
|
|
|
.has_orco = (vert_coords != NULL),
|
|
|
|
};
|
|
|
|
make_child_duplis(ctx, &vdd, make_child_duplis_verts_from_editmesh);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
VertexDupliData_Mesh vdd = {
|
|
|
|
.params = vdd_params,
|
|
|
|
.totvert = me_eval->totvert,
|
|
|
|
.mvert = me_eval->mvert,
|
|
|
|
.orco = CustomData_get_layer(&me_eval->vdata, CD_ORCO),
|
|
|
|
};
|
|
|
|
make_child_duplis(ctx, &vdd, make_child_duplis_verts_from_mesh);
|
|
|
|
}
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2017-08-01 09:06:34 +10:00
|
|
|
static const DupliGenerator gen_dupli_verts = {
|
2019-04-17 06:17:24 +02:00
|
|
|
OB_DUPLIVERTS, /* type */
|
|
|
|
make_duplis_verts /* make_duplis */
|
2014-01-21 12:11:34 +01:00
|
|
|
};
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Dupli-Vertices Implementation (#OB_DUPLIVERTS for 3D Text)
|
|
|
|
* \{ */
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
static Object *find_family_object(
|
|
|
|
Main *bmain, const char *family, size_t family_len, unsigned int ch, GHash *family_gh)
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
Object **ob_pt;
|
|
|
|
Object *ob;
|
|
|
|
void *ch_key = POINTER_FROM_UINT(ch);
|
|
|
|
|
|
|
|
if ((ob_pt = (Object **)BLI_ghash_lookup_p(family_gh, ch_key))) {
|
|
|
|
ob = *ob_pt;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
char ch_utf8[7];
|
|
|
|
size_t ch_utf8_len;
|
|
|
|
|
|
|
|
ch_utf8_len = BLI_str_utf8_from_unicode(ch, ch_utf8);
|
|
|
|
ch_utf8[ch_utf8_len] = '\0';
|
2020-08-13 09:49:49 +10:00
|
|
|
ch_utf8_len += 1; /* Compare with null terminator. */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
for (ob = bmain->objects.first; ob; ob = ob->id.next) {
|
|
|
|
if (STREQLEN(ob->id.name + 2 + family_len, ch_utf8, ch_utf8_len)) {
|
|
|
|
if (STREQLEN(ob->id.name + 2, family, family_len)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Inserted value can be NULL, just to save searches in future. */
|
2019-04-17 06:17:24 +02:00
|
|
|
BLI_ghash_insert(family_gh, ch_key, ob);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ob;
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void make_duplis_font(const DupliContext *ctx)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
Object *par = ctx->object;
|
|
|
|
GHash *family_gh;
|
|
|
|
Object *ob;
|
|
|
|
Curve *cu;
|
|
|
|
struct CharTrans *ct, *chartransdata = NULL;
|
|
|
|
float vec[3], obmat[4][4], pmat[4][4], fsize, xof, yof;
|
|
|
|
int text_len, a;
|
|
|
|
size_t family_len;
|
2019-11-22 12:26:54 -03:00
|
|
|
const char32_t *text = NULL;
|
2019-04-17 06:17:24 +02:00
|
|
|
bool text_free = false;
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Font dupli-verts not supported inside collections. */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (ctx->collection) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
copy_m4_m4(pmat, par->obmat);
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* In `par` the family name is stored, use this to find the other objects. */
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
BKE_vfont_to_curve_ex(
|
|
|
|
par, par->data, FO_DUPLI, NULL, &text, &text_len, &text_free, &chartransdata);
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (text == NULL || chartransdata == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
cu = par->data;
|
|
|
|
fsize = cu->fsize;
|
|
|
|
xof = cu->xof;
|
|
|
|
yof = cu->yof;
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
ct = chartransdata;
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Cache result. */
|
2019-04-17 06:17:24 +02:00
|
|
|
family_len = strlen(cu->family);
|
|
|
|
family_gh = BLI_ghash_int_new_ex(__func__, 256);
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2020-02-03 18:37:12 +01:00
|
|
|
/* Safety check even if it might fail badly when called for original object. */
|
|
|
|
const bool is_eval_curve = DEG_is_evaluated_id(&cu->id);
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/* Advance matching BLI_str_utf8_as_utf32. */
|
2019-04-17 06:17:24 +02:00
|
|
|
for (a = 0; a < text_len; a++, ct++) {
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* XXX That G.main is *really* ugly, but not sure what to do here.
|
|
|
|
* Definitively don't think it would be safe to put back `Main *bmain` pointer
|
|
|
|
* in #DupliContext as done in 2.7x? */
|
2019-04-17 06:17:24 +02:00
|
|
|
ob = find_family_object(G.main, cu->family, family_len, (unsigned int)text[a], family_gh);
|
2020-02-03 18:37:12 +01:00
|
|
|
|
|
|
|
if (is_eval_curve) {
|
|
|
|
/* Workaround for the above hack. */
|
|
|
|
ob = DEG_get_evaluated_object(ctx->depsgraph, ob);
|
|
|
|
}
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (ob) {
|
|
|
|
vec[0] = fsize * (ct->xof - xof);
|
|
|
|
vec[1] = fsize * (ct->yof - yof);
|
|
|
|
vec[2] = 0.0;
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
mul_m4_v3(pmat, vec);
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
copy_m4_m4(obmat, par->obmat);
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (UNLIKELY(ct->rot != 0.0f)) {
|
|
|
|
float rmat[4][4];
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
zero_v3(obmat[3]);
|
|
|
|
axis_angle_to_mat4_single(rmat, 'Z', -ct->rot);
|
|
|
|
mul_m4_m4m4(obmat, obmat, rmat);
|
|
|
|
}
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
copy_v3_v3(obmat[3], vec);
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
make_dupli(ctx, ob, obmat, a);
|
|
|
|
}
|
|
|
|
}
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (text_free) {
|
|
|
|
MEM_freeN((void *)text);
|
|
|
|
}
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
BLI_ghash_free(family_gh, NULL, NULL);
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
MEM_freeN(chartransdata);
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2017-08-01 09:06:34 +10:00
|
|
|
static const DupliGenerator gen_dupli_verts_font = {
|
2019-04-17 06:17:24 +02:00
|
|
|
OB_DUPLIVERTS, /* type */
|
|
|
|
make_duplis_font /* make_duplis */
|
2014-01-21 12:11:34 +01:00
|
|
|
};
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Dupli-Faces Implementation (#OB_DUPLIFACES)
|
|
|
|
* \{ */
|
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
/** Values shared between different mesh types. */
|
|
|
|
typedef struct FaceDupliData_Params {
|
|
|
|
/**
|
|
|
|
* It's important we use this context instead of the `ctx` passed into #make_child_duplis
|
|
|
|
* since these won't match in the case of recursion.
|
|
|
|
*/
|
|
|
|
const DupliContext *ctx;
|
|
|
|
|
|
|
|
bool use_scale;
|
|
|
|
} FaceDupliData_Params;
|
|
|
|
|
|
|
|
typedef struct FaceDupliData_Mesh {
|
|
|
|
FaceDupliData_Params params;
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
int totface;
|
2020-08-13 09:28:29 +10:00
|
|
|
const MPoly *mpoly;
|
|
|
|
const MLoop *mloop;
|
|
|
|
const MVert *mvert;
|
|
|
|
const float (*orco)[3];
|
|
|
|
const MLoopUV *mloopuv;
|
2020-08-13 13:56:15 +10:00
|
|
|
} FaceDupliData_Mesh;
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
typedef struct FaceDupliData_EditMesh {
|
|
|
|
FaceDupliData_Params params;
|
|
|
|
|
|
|
|
BMEditMesh *em;
|
|
|
|
|
|
|
|
bool has_orco, has_uvs;
|
|
|
|
int cd_loop_uv_offset;
|
|
|
|
/* Can be NULL. */
|
|
|
|
const float (*vert_coords)[3];
|
|
|
|
} FaceDupliData_EditMesh;
|
|
|
|
|
|
|
|
static void get_dupliface_transform_from_coords(const float coords[][3],
|
|
|
|
const int coords_len,
|
|
|
|
const bool use_scale,
|
|
|
|
const float scale_fac,
|
|
|
|
float r_mat[4][4])
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
float loc[3], quat[4], scale, size[3];
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Location. */
|
2020-08-13 13:56:15 +10:00
|
|
|
{
|
|
|
|
const float w = 1.0f / (float)coords_len;
|
|
|
|
zero_v3(loc);
|
|
|
|
for (int i = 0; i < coords_len; i++) {
|
|
|
|
madd_v3_v3fl(loc, coords[i], w);
|
|
|
|
}
|
|
|
|
}
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Rotation. */
|
2019-04-17 06:17:24 +02:00
|
|
|
{
|
2020-08-13 13:56:15 +10:00
|
|
|
float f_no[3];
|
|
|
|
cross_poly_v3(f_no, coords, (uint)coords_len);
|
|
|
|
normalize_v3(f_no);
|
|
|
|
tri_to_quat_ex(quat, coords[0], coords[1], coords[2], f_no);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Scale. */
|
2019-04-17 06:17:24 +02:00
|
|
|
if (use_scale) {
|
2020-08-13 13:56:15 +10:00
|
|
|
const float area = area_poly_v3(coords, (uint)coords_len);
|
2019-04-17 06:17:24 +02:00
|
|
|
scale = sqrtf(area) * scale_fac;
|
|
|
|
}
|
2019-04-22 09:39:35 +10:00
|
|
|
else {
|
2019-04-17 06:17:24 +02:00
|
|
|
scale = 1.0f;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
size[0] = size[1] = size[2] = scale;
|
|
|
|
|
2020-08-13 09:28:29 +10:00
|
|
|
loc_quat_size_to_mat4(r_mat, loc, quat, size);
|
2020-08-11 15:26:14 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
static DupliObject *face_dupli(const DupliContext *ctx,
|
|
|
|
Object *inst_ob,
|
|
|
|
const float child_imat[4][4],
|
|
|
|
const int index,
|
|
|
|
const bool use_scale,
|
|
|
|
const float scale_fac,
|
|
|
|
const float (*coords)[3],
|
|
|
|
const int coords_len)
|
|
|
|
{
|
|
|
|
float obmat[4][4];
|
|
|
|
float space_mat[4][4];
|
|
|
|
|
|
|
|
/* `obmat` is transform to face. */
|
|
|
|
get_dupliface_transform_from_coords(coords, coords_len, use_scale, scale_fac, obmat);
|
|
|
|
|
|
|
|
/* Make offset relative to inst_ob using relative child transform. */
|
|
|
|
mul_mat3_m4_v3(child_imat, obmat[3]);
|
|
|
|
|
|
|
|
/* XXX ugly hack to ensure same behavior as in master this should not be needed,
|
|
|
|
* #Object.parentinv is not consistent outside of parenting. */
|
|
|
|
{
|
|
|
|
float imat[3][3];
|
|
|
|
copy_m3_m4(imat, inst_ob->parentinv);
|
|
|
|
mul_m4_m3m4(obmat, imat, obmat);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Apply `obmat` _after_ the local face transform. */
|
|
|
|
mul_m4_m4m4(obmat, inst_ob->obmat, obmat);
|
|
|
|
|
|
|
|
/* Space matrix is constructed by removing `obmat` transform,
|
|
|
|
* this yields the world-space transform for recursive duplis. */
|
|
|
|
mul_m4_m4m4(space_mat, obmat, inst_ob->imat);
|
|
|
|
|
|
|
|
DupliObject *dob = make_dupli(ctx, inst_ob, obmat, index);
|
|
|
|
|
|
|
|
/* Recursion. */
|
|
|
|
make_recursive_duplis(ctx, inst_ob, space_mat, index);
|
|
|
|
|
|
|
|
return dob;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Wrap #face_dupli, needed since we can't #alloca in a loop. */
|
|
|
|
static DupliObject *face_dupli_from_mesh(const DupliContext *ctx,
|
|
|
|
Object *inst_ob,
|
|
|
|
const float child_imat[4][4],
|
|
|
|
const int index,
|
|
|
|
const bool use_scale,
|
|
|
|
const float scale_fac,
|
|
|
|
|
|
|
|
/* Mesh variables. */
|
|
|
|
const MPoly *mpoly,
|
|
|
|
const MLoop *mloopstart,
|
|
|
|
const MVert *mvert)
|
2020-08-11 15:26:14 +10:00
|
|
|
{
|
2020-08-13 13:56:15 +10:00
|
|
|
const int coords_len = mpoly->totloop;
|
|
|
|
float(*coords)[3] = BLI_array_alloca(coords, (size_t)coords_len);
|
|
|
|
|
|
|
|
const MLoop *ml = mloopstart;
|
|
|
|
for (int i = 0; i < coords_len; i++, ml++) {
|
|
|
|
copy_v3_v3(coords[i], mvert[ml->v].co);
|
|
|
|
}
|
|
|
|
|
|
|
|
return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords, coords_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Wrap #face_dupli, needed since we can't #alloca in a loop. */
|
|
|
|
static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
|
|
|
|
Object *inst_ob,
|
|
|
|
const float child_imat[4][4],
|
|
|
|
const int index,
|
|
|
|
const bool use_scale,
|
|
|
|
const float scale_fac,
|
|
|
|
|
|
|
|
/* Mesh variables. */
|
|
|
|
BMFace *f,
|
|
|
|
const float (*vert_coords)[3])
|
|
|
|
{
|
|
|
|
const int coords_len = f->len;
|
|
|
|
float(*coords)[3] = BLI_array_alloca(coords, (size_t)coords_len);
|
|
|
|
|
|
|
|
BMLoop *l_first, *l_iter;
|
|
|
|
int i = 0;
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
|
|
|
if (vert_coords != NULL) {
|
|
|
|
do {
|
|
|
|
copy_v3_v3(coords[i++], vert_coords[BM_elem_index_get(l_iter->v)]);
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
do {
|
|
|
|
copy_v3_v3(coords[i++], l_iter->v->co);
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
}
|
|
|
|
|
|
|
|
return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords, coords_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void make_child_duplis_faces_from_mesh(const DupliContext *ctx,
|
|
|
|
void *userdata,
|
|
|
|
Object *inst_ob)
|
|
|
|
{
|
|
|
|
FaceDupliData_Mesh *fdd = userdata;
|
2020-08-13 09:28:29 +10:00
|
|
|
const MPoly *mpoly = fdd->mpoly, *mp;
|
|
|
|
const MLoop *mloop = fdd->mloop;
|
|
|
|
const MVert *mvert = fdd->mvert;
|
|
|
|
const float(*orco)[3] = fdd->orco;
|
|
|
|
const MLoopUV *mloopuv = fdd->mloopuv;
|
2020-08-13 13:56:15 +10:00
|
|
|
const int totface = fdd->totface;
|
|
|
|
const bool use_scale = fdd->params.use_scale;
|
|
|
|
int a;
|
|
|
|
|
2020-08-11 16:12:44 +02:00
|
|
|
float child_imat[4][4];
|
2020-08-11 15:26:14 +10:00
|
|
|
|
2020-08-11 16:12:44 +02:00
|
|
|
invert_m4_m4(inst_ob->imat, inst_ob->obmat);
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Relative transform from parent to child space. */
|
2020-08-11 16:12:44 +02:00
|
|
|
mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat);
|
2020-08-13 13:56:15 +10:00
|
|
|
const float scale_fac = ctx->object->instance_faces_scale;
|
2020-08-11 15:26:14 +10:00
|
|
|
|
2020-08-11 16:12:44 +02:00
|
|
|
for (a = 0, mp = mpoly; a < totface; a++, mp++) {
|
2020-08-13 09:28:29 +10:00
|
|
|
const MLoop *loopstart = mloop + mp->loopstart;
|
2020-08-13 13:56:15 +10:00
|
|
|
DupliObject *dob = face_dupli_from_mesh(
|
|
|
|
fdd->params.ctx, inst_ob, child_imat, a, use_scale, scale_fac, mp, loopstart, mvert);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
const float w = 1.0f / (float)mp->totloop;
|
|
|
|
if (orco) {
|
|
|
|
for (int j = 0; j < mp->totloop; j++) {
|
|
|
|
madd_v3_v3fl(dob->orco, orco[loopstart[j].v], w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mloopuv) {
|
|
|
|
for (int j = 0; j < mp->totloop; j++) {
|
|
|
|
madd_v2_v2fl(dob->uv, mloopuv[mp->loopstart + j].uv, w);
|
|
|
|
}
|
|
|
|
}
|
2020-08-13 13:56:15 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void make_child_duplis_faces_from_editmesh(const DupliContext *ctx,
|
|
|
|
void *userdata,
|
|
|
|
Object *inst_ob)
|
|
|
|
{
|
|
|
|
FaceDupliData_EditMesh *fdd = userdata;
|
|
|
|
BMEditMesh *em = fdd->em;
|
|
|
|
float child_imat[4][4];
|
|
|
|
int a;
|
|
|
|
BMFace *f;
|
|
|
|
BMIter iter;
|
|
|
|
const bool use_scale = fdd->params.use_scale;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
const float(*vert_coords)[3] = fdd->vert_coords;
|
|
|
|
|
|
|
|
BLI_assert((vert_coords == NULL) || (em->bm->elem_index_dirty & BM_VERT) == 0);
|
|
|
|
|
|
|
|
invert_m4_m4(inst_ob->imat, inst_ob->obmat);
|
|
|
|
/* Relative transform from parent to child space. */
|
|
|
|
mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat);
|
|
|
|
const float scale_fac = ctx->object->instance_faces_scale;
|
|
|
|
|
|
|
|
BM_ITER_MESH_INDEX (f, &iter, em->bm, BM_FACES_OF_MESH, a) {
|
|
|
|
DupliObject *dob = face_dupli_from_editmesh(
|
|
|
|
fdd->params.ctx, inst_ob, child_imat, a, use_scale, scale_fac, f, vert_coords);
|
|
|
|
|
|
|
|
if (fdd->has_orco) {
|
|
|
|
const float w = 1.0f / (float)f->len;
|
|
|
|
BMLoop *l_first, *l_iter;
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
|
|
|
do {
|
|
|
|
madd_v3_v3fl(dob->orco, l_iter->v->co, w);
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
}
|
|
|
|
if (fdd->has_uvs) {
|
|
|
|
BM_face_uv_calc_center_median(f, fdd->cd_loop_uv_offset, dob->uv);
|
|
|
|
}
|
2020-08-11 15:26:14 +10:00
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-11 15:26:14 +10:00
|
|
|
static void make_duplis_faces(const DupliContext *ctx)
|
|
|
|
{
|
|
|
|
Object *parent = ctx->object;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Gather mesh info. */
|
2020-08-13 13:30:25 +10:00
|
|
|
BMEditMesh *em = NULL;
|
2020-08-13 13:56:15 +10:00
|
|
|
const float(*vert_coords)[3] = NULL;
|
|
|
|
Mesh *me_eval = mesh_data_from_duplicator_object(parent, &em, &vert_coords, NULL);
|
|
|
|
if (em == NULL && me_eval == NULL) {
|
2020-08-13 13:30:25 +10:00
|
|
|
return;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
FaceDupliData_Params fdd_params = {
|
|
|
|
.ctx = ctx,
|
|
|
|
.use_scale = parent->transflag & OB_DUPLIFACES_SCALE,
|
|
|
|
};
|
2020-08-13 13:30:25 +10:00
|
|
|
|
2020-08-13 13:56:15 +10:00
|
|
|
if (em != NULL) {
|
|
|
|
const int uv_idx = CustomData_get_render_layer(&em->bm->ldata, CD_MLOOPUV);
|
|
|
|
FaceDupliData_EditMesh fdd = {
|
|
|
|
.params = fdd_params,
|
|
|
|
.em = em,
|
|
|
|
.vert_coords = vert_coords,
|
|
|
|
.has_orco = (vert_coords != NULL),
|
|
|
|
.has_uvs = (uv_idx != -1),
|
|
|
|
.cd_loop_uv_offset = CustomData_get_n_offset(&em->bm->ldata, CD_MLOOPUV, uv_idx),
|
|
|
|
};
|
|
|
|
make_child_duplis(ctx, &fdd, make_child_duplis_faces_from_editmesh);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const int uv_idx = CustomData_get_render_layer(&me_eval->ldata, CD_MLOOPUV);
|
|
|
|
FaceDupliData_Mesh fdd = {
|
|
|
|
.params = fdd_params,
|
|
|
|
.totface = me_eval->totpoly,
|
|
|
|
.mpoly = me_eval->mpoly,
|
|
|
|
.mloop = me_eval->mloop,
|
|
|
|
.mvert = me_eval->mvert,
|
|
|
|
.mloopuv = CustomData_get_layer_n(&me_eval->ldata, CD_MLOOPUV, uv_idx),
|
|
|
|
.orco = CustomData_get_layer(&me_eval->vdata, CD_ORCO),
|
|
|
|
};
|
|
|
|
make_child_duplis(ctx, &fdd, make_child_duplis_faces_from_mesh);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2017-08-01 09:06:34 +10:00
|
|
|
static const DupliGenerator gen_dupli_faces = {
|
2019-04-17 06:17:24 +02:00
|
|
|
OB_DUPLIFACES, /* type */
|
|
|
|
make_duplis_faces /* make_duplis */
|
2014-01-21 12:11:34 +01:00
|
|
|
};
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Dupli-Particles Implementation (#OB_DUPLIPARTS)
|
|
|
|
* \{ */
|
|
|
|
|
2016-12-28 17:30:58 +01:00
|
|
|
static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem *psys)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
Scene *scene = ctx->scene;
|
|
|
|
Object *par = ctx->object;
|
|
|
|
eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
|
|
|
|
bool for_render = mode == DAG_EVAL_RENDER;
|
|
|
|
|
|
|
|
Object *ob = NULL, **oblist = NULL;
|
|
|
|
DupliObject *dob;
|
|
|
|
ParticleDupliWeight *dw;
|
|
|
|
ParticleSettings *part;
|
|
|
|
ParticleData *pa;
|
|
|
|
ChildParticle *cpa = NULL;
|
|
|
|
ParticleKey state;
|
|
|
|
ParticleCacheKey *cache;
|
|
|
|
float ctime, scale = 1.0f;
|
|
|
|
float tmat[4][4], mat[4][4], pamat[4][4], size = 0.0;
|
|
|
|
int a, b, hair = 0;
|
|
|
|
int totpart, totchild;
|
|
|
|
|
|
|
|
int no_draw_flag = PARS_UNEXIST;
|
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (psys == NULL) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
part = psys->part;
|
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (part == NULL) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (!psys_check_enabled(par, psys, for_render)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (!for_render) {
|
2019-04-17 06:17:24 +02:00
|
|
|
no_draw_flag |= PARS_NO_DISP;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* NOTE: in old animation system, used parent object's time-offset. */
|
2019-08-14 23:29:46 +10:00
|
|
|
ctime = DEG_get_ctime(ctx->depsgraph);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
totpart = psys->totpart;
|
|
|
|
totchild = psys->totchild;
|
|
|
|
|
|
|
|
if ((for_render || part->draw_as == PART_DRAW_REND) &&
|
|
|
|
ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) {
|
|
|
|
ParticleSimulationData sim = {NULL};
|
|
|
|
sim.depsgraph = ctx->depsgraph;
|
|
|
|
sim.scene = scene;
|
|
|
|
sim.ob = par;
|
|
|
|
sim.psys = psys;
|
|
|
|
sim.psmd = psys_get_modifier(par, psys);
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Make sure emitter `imat` is in global coordinates instead of render view coordinates. */
|
2019-04-17 06:17:24 +02:00
|
|
|
invert_m4_m4(par->imat, par->obmat);
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* First check for loops (particle system object used as dupli-object). */
|
2019-04-17 06:17:24 +02:00
|
|
|
if (part->ren_as == PART_DRAW_OB) {
|
2019-04-22 09:39:35 +10:00
|
|
|
if (ELEM(part->instance_object, NULL, par)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2020-08-13 09:49:49 +10:00
|
|
|
else { /* #PART_DRAW_GR. */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (part->instance_collection == NULL) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
const ListBase dup_collection_objects = BKE_collection_object_cache_get(
|
|
|
|
part->instance_collection);
|
2019-04-22 09:39:35 +10:00
|
|
|
if (BLI_listbase_is_empty(&dup_collection_objects)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
if (BLI_findptr(&dup_collection_objects, par, offsetof(Base, object))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* If we have a hair particle system, use the path cache. */
|
2019-04-17 06:17:24 +02:00
|
|
|
if (part->type == PART_HAIR) {
|
2019-04-22 09:39:35 +10:00
|
|
|
if (psys->flag & PSYS_HAIR_DONE) {
|
2019-04-17 06:17:24 +02:00
|
|
|
hair = (totchild == 0 || psys->childcache) && psys->pathcache;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
if (!hair) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* We use cache, update `totchild` according to cached data. */
|
2019-04-17 06:17:24 +02:00
|
|
|
totchild = psys->totchildcache;
|
|
|
|
totpart = psys->totcached;
|
|
|
|
}
|
|
|
|
|
|
|
|
RNG *rng = BLI_rng_new_srandom(31415926u + (unsigned int)psys->seed);
|
|
|
|
|
|
|
|
psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Gather list of objects or single object. */
|
2019-04-17 06:17:24 +02:00
|
|
|
int totcollection = 0;
|
|
|
|
|
2019-09-10 14:14:52 +02:00
|
|
|
const bool use_whole_collection = part->draw & PART_DRAW_WHOLE_GR;
|
|
|
|
const bool use_collection_count = part->draw & PART_DRAW_COUNT_GR && !use_whole_collection;
|
2019-04-17 06:17:24 +02:00
|
|
|
if (part->ren_as == PART_DRAW_GR) {
|
2019-09-10 14:14:52 +02:00
|
|
|
if (use_collection_count) {
|
2019-04-17 06:17:24 +02:00
|
|
|
psys_find_group_weights(part);
|
|
|
|
|
|
|
|
for (dw = part->instance_weights.first; dw; dw = dw->next) {
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (
|
|
|
|
part->instance_collection, object, mode) {
|
|
|
|
if (dw->ob == object) {
|
|
|
|
totcollection += dw->count;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (
|
|
|
|
part->instance_collection, object, mode) {
|
|
|
|
(void)object;
|
|
|
|
totcollection++;
|
|
|
|
}
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
|
|
|
|
}
|
|
|
|
|
|
|
|
oblist = MEM_callocN((size_t)totcollection * sizeof(Object *), "dupcollection object list");
|
|
|
|
|
2019-09-10 14:14:52 +02:00
|
|
|
if (use_collection_count) {
|
2019-04-17 06:17:24 +02:00
|
|
|
a = 0;
|
|
|
|
for (dw = part->instance_weights.first; dw; dw = dw->next) {
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (
|
|
|
|
part->instance_collection, object, mode) {
|
|
|
|
if (dw->ob == object) {
|
|
|
|
for (b = 0; b < dw->count; b++, a++) {
|
|
|
|
oblist[a] = dw->ob;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
a = 0;
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (
|
|
|
|
part->instance_collection, object, mode) {
|
|
|
|
oblist[a] = object;
|
|
|
|
a++;
|
|
|
|
}
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ob = part->instance_object;
|
|
|
|
}
|
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (totchild == 0 || part->draw & PART_DRAW_PARENT) {
|
2019-04-17 06:17:24 +02:00
|
|
|
a = 0;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
else {
|
2019-04-17 06:17:24 +02:00
|
|
|
a = totpart;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
for (pa = psys->particles; a < totpart + totchild; a++, pa++) {
|
|
|
|
if (a < totpart) {
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Handle parent particle. */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (pa->flag & no_draw_flag) {
|
2019-04-17 06:17:24 +02:00
|
|
|
continue;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
#if 0 /* UNUSED */
|
|
|
|
pa_num = pa->num;
|
|
|
|
#endif
|
2019-04-17 06:17:24 +02:00
|
|
|
size = pa->size;
|
|
|
|
}
|
|
|
|
else {
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Handle child particle. */
|
2019-04-17 06:17:24 +02:00
|
|
|
cpa = &psys->child[a - totpart];
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
#if 0 /* UNUSED */
|
|
|
|
pa_num = a;
|
|
|
|
#endif
|
2019-04-17 06:17:24 +02:00
|
|
|
size = psys_get_child_size(psys, cpa, ctime, NULL);
|
|
|
|
}
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Some hair paths might be non-existent so they can't be used for duplication. */
|
2019-04-17 06:17:24 +02:00
|
|
|
if (hair && psys->pathcache &&
|
|
|
|
((a < totpart && psys->pathcache[a]->segments < 0) ||
|
|
|
|
(a >= totpart && psys->childcache[a - totpart]->segments < 0))) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (part->ren_as == PART_DRAW_GR) {
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Prevent divide by zero below T28336. */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (totcollection == 0) {
|
2019-04-17 06:17:24 +02:00
|
|
|
continue;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* For collections, pick the object based on settings. */
|
2019-09-10 14:14:52 +02:00
|
|
|
if (part->draw & PART_DRAW_RAND_GR && !use_whole_collection) {
|
2019-04-17 06:17:24 +02:00
|
|
|
b = BLI_rng_get_int(rng) % totcollection;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
else {
|
2019-04-17 06:17:24 +02:00
|
|
|
b = a % totcollection;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
ob = oblist[b];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hair) {
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Hair we handle separate and compute transform based on hair keys. */
|
2019-04-17 06:17:24 +02:00
|
|
|
if (a < totpart) {
|
|
|
|
cache = psys->pathcache[a];
|
|
|
|
psys_get_dupli_path_transform(&sim, pa, NULL, cache, pamat, &scale);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
cache = psys->childcache[a - totpart];
|
|
|
|
psys_get_dupli_path_transform(&sim, NULL, cpa, cache, pamat, &scale);
|
|
|
|
}
|
|
|
|
|
|
|
|
copy_v3_v3(pamat[3], cache->co);
|
|
|
|
pamat[3][3] = 1.0f;
|
|
|
|
}
|
|
|
|
else {
|
2020-08-13 09:49:49 +10:00
|
|
|
/* First key. */
|
2019-04-17 06:17:24 +02:00
|
|
|
state.time = ctime;
|
|
|
|
if (psys_get_particle_state(&sim, a, &state, 0) == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
float tquat[4];
|
|
|
|
normalize_qt_qt(tquat, state.rot);
|
|
|
|
quat_to_mat4(pamat, tquat);
|
|
|
|
copy_v3_v3(pamat[3], state.co);
|
|
|
|
pamat[3][3] = 1.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (part->ren_as == PART_DRAW_GR && psys->part->draw & PART_DRAW_WHOLE_GR) {
|
|
|
|
b = 0;
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (
|
|
|
|
part->instance_collection, object, mode) {
|
|
|
|
copy_m4_m4(tmat, oblist[b]->obmat);
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Apply particle scale. */
|
2019-04-17 06:17:24 +02:00
|
|
|
mul_mat3_m4_fl(tmat, size * scale);
|
|
|
|
mul_v3_fl(tmat[3], size * scale);
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Collection dupli-offset, should apply after everything else. */
|
2019-04-17 06:17:24 +02:00
|
|
|
if (!is_zero_v3(part->instance_collection->instance_offset)) {
|
|
|
|
sub_v3_v3(tmat[3], part->instance_collection->instance_offset);
|
|
|
|
}
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Individual particle transform. */
|
2019-04-17 06:17:24 +02:00
|
|
|
mul_m4_m4m4(mat, pamat, tmat);
|
|
|
|
|
|
|
|
dob = make_dupli(ctx, object, mat, a);
|
|
|
|
dob->particle_system = psys;
|
|
|
|
|
|
|
|
psys_get_dupli_texture(psys, part, sim.psmd, pa, cpa, dob->uv, dob->orco);
|
|
|
|
|
|
|
|
b++;
|
|
|
|
}
|
|
|
|
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
float obmat[4][4];
|
|
|
|
copy_m4_m4(obmat, ob->obmat);
|
|
|
|
|
|
|
|
float vec[3];
|
|
|
|
copy_v3_v3(vec, obmat[3]);
|
|
|
|
zero_v3(obmat[3]);
|
|
|
|
|
2019-04-27 12:07:07 +10:00
|
|
|
/* Particle rotation uses x-axis as the aligned axis,
|
|
|
|
* so pre-rotate the object accordingly. */
|
2019-04-17 06:17:24 +02:00
|
|
|
if ((part->draw & PART_DRAW_ROTATE_OB) == 0) {
|
|
|
|
float xvec[3], q[4], size_mat[4][4], original_size[3];
|
|
|
|
|
|
|
|
mat4_to_size(original_size, obmat);
|
|
|
|
size_to_mat4(size_mat, original_size);
|
|
|
|
|
|
|
|
xvec[0] = -1.f;
|
|
|
|
xvec[1] = xvec[2] = 0;
|
|
|
|
vec_to_quat(q, xvec, ob->trackflag, ob->upflag);
|
|
|
|
quat_to_mat4(obmat, q);
|
|
|
|
obmat[3][3] = 1.0f;
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Add scaling if requested. */
|
2019-04-22 09:39:35 +10:00
|
|
|
if ((part->draw & PART_DRAW_NO_SCALE_OB) == 0) {
|
2019-04-17 06:17:24 +02:00
|
|
|
mul_m4_m4m4(obmat, obmat, size_mat);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
else if (part->draw & PART_DRAW_NO_SCALE_OB) {
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Remove scaling. */
|
2019-04-17 06:17:24 +02:00
|
|
|
float size_mat[4][4], original_size[3];
|
|
|
|
|
|
|
|
mat4_to_size(original_size, obmat);
|
|
|
|
size_to_mat4(size_mat, original_size);
|
|
|
|
invert_m4(size_mat);
|
|
|
|
|
|
|
|
mul_m4_m4m4(obmat, obmat, size_mat);
|
|
|
|
}
|
|
|
|
|
|
|
|
mul_m4_m4m4(tmat, pamat, obmat);
|
|
|
|
mul_mat3_m4_fl(tmat, size * scale);
|
|
|
|
|
|
|
|
copy_m4_m4(mat, tmat);
|
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (part->draw & PART_DRAW_GLOBAL_OB) {
|
2019-04-17 06:17:24 +02:00
|
|
|
add_v3_v3v3(mat[3], mat[3], vec);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
dob = make_dupli(ctx, ob, mat, a);
|
|
|
|
dob->particle_system = psys;
|
|
|
|
psys_get_dupli_texture(psys, part, sim.psmd, pa, cpa, dob->uv, dob->orco);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BLI_rng_free(rng);
|
|
|
|
}
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Clean up. */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (oblist) {
|
2019-04-17 06:17:24 +02:00
|
|
|
MEM_freeN(oblist);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
if (psys->lattice_deform_data) {
|
2020-06-13 15:41:34 +10:00
|
|
|
BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
|
2019-04-17 06:17:24 +02:00
|
|
|
psys->lattice_deform_data = NULL;
|
|
|
|
}
|
2016-12-28 17:30:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void make_duplis_particles(const DupliContext *ctx)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
ParticleSystem *psys;
|
|
|
|
int psysid;
|
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Particle system take up one level in id, the particles another. */
|
2019-04-17 06:17:24 +02:00
|
|
|
for (psys = ctx->object->particlesystem.first, psysid = 0; psys; psys = psys->next, psysid++) {
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Particles create one more level for persistent `psys` index. */
|
2019-04-17 06:17:24 +02:00
|
|
|
DupliContext pctx;
|
|
|
|
copy_dupli_context(&pctx, ctx, ctx->object, NULL, psysid);
|
|
|
|
make_duplis_particle_system(&pctx, psys);
|
|
|
|
}
|
2016-12-28 17:30:58 +01:00
|
|
|
}
|
|
|
|
|
2017-08-01 09:06:34 +10:00
|
|
|
static const DupliGenerator gen_dupli_particles = {
|
2019-04-17 06:17:24 +02:00
|
|
|
OB_DUPLIPARTS, /* type */
|
|
|
|
make_duplis_particles /* make_duplis */
|
2016-12-28 17:30:58 +01:00
|
|
|
};
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Dupli-Generator Selector For The Given Context
|
|
|
|
* \{ */
|
2014-01-21 12:11:34 +01:00
|
|
|
|
|
|
|
static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
int transflag = ctx->object->transflag;
|
|
|
|
int restrictflag = ctx->object->restrictflag;
|
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if ((transflag & OB_DUPLI) == 0) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return NULL;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-13 09:49:49 +10:00
|
|
|
/* Should the dupli's be generated for this object? - Respect restrict flags. */
|
2019-04-17 06:17:24 +02:00
|
|
|
if (DEG_get_mode(ctx->depsgraph) == DAG_EVAL_RENDER ? (restrictflag & OB_RESTRICT_RENDER) :
|
2019-05-14 17:45:47 -03:00
|
|
|
(restrictflag & OB_RESTRICT_VIEWPORT)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return NULL;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
if (transflag & OB_DUPLIPARTS) {
|
|
|
|
return &gen_dupli_particles;
|
|
|
|
}
|
|
|
|
else if (transflag & OB_DUPLIVERTS) {
|
|
|
|
if (ctx->object->type == OB_MESH) {
|
|
|
|
return &gen_dupli_verts;
|
|
|
|
}
|
|
|
|
else if (ctx->object->type == OB_FONT) {
|
|
|
|
return &gen_dupli_verts_font;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (transflag & OB_DUPLIFACES) {
|
2019-04-22 09:39:35 +10:00
|
|
|
if (ctx->object->type == OB_MESH) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return &gen_dupli_faces;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
else if (transflag & OB_DUPLICOLLECTION) {
|
|
|
|
return &gen_dupli_collection;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Dupli-Container Implementation
|
|
|
|
* \{ */
|
2014-01-21 12:11:34 +01:00
|
|
|
|
2020-08-10 18:29:17 +10:00
|
|
|
/**
|
|
|
|
* \return a #ListBase of #DupliObject.
|
|
|
|
*/
|
2018-06-06 14:39:05 +02:00
|
|
|
ListBase *object_duplilist(Depsgraph *depsgraph, Scene *sce, Object *ob)
|
2014-01-21 12:11:34 +01:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
ListBase *duplilist = MEM_callocN(sizeof(ListBase), "duplilist");
|
|
|
|
DupliContext ctx;
|
|
|
|
init_context(&ctx, depsgraph, sce, ob, NULL);
|
|
|
|
if (ctx.gen) {
|
|
|
|
ctx.duplilist = duplilist;
|
|
|
|
ctx.gen->make_duplis(&ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return duplilist;
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void free_object_duplilist(ListBase *lb)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
BLI_freelistN(lb);
|
|
|
|
MEM_freeN(lb);
|
2014-01-21 12:11:34 +01:00
|
|
|
}
|
2020-08-10 18:29:17 +10:00
|
|
|
|
|
|
|
/** \} */
|