[Bf-blender-cvs] [47c9dda0761] greasepencil-object: GP Build Modifier: Initial WIP implementation of this modifier
Joshua Leung
noreply at git.blender.org
Thu Nov 23 15:33:01 CET 2017
Commit: 47c9dda076183ab6019303bf47075cff75336cfd
Author: Joshua Leung
Date: Thu Nov 23 17:31:49 2017 +1300
Branches: greasepencil-object
https://developer.blender.org/rB47c9dda076183ab6019303bf47075cff75336cfd
GP Build Modifier: Initial WIP implementation of this modifier
It's likely that there are still many bugs here, so caution is advised.
===================================================================
M source/blender/makesdna/DNA_modifier_types.h
M source/blender/modifiers/intern/MOD_gpencilbuild.c
===================================================================
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 83d36bc9c5d..0ed34224af2 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -1769,14 +1769,57 @@ typedef enum eGpencilArray_Flag {
typedef struct GpencilBuildModifierData {
ModifierData modifier;
- float start, length;
- int flag;
- int seed; /* (int) random seed */
+
+ char layername[64]; /* if set, restrict modifier to operating on this layer */
+
+ float start_frame; /* If GP_BUILD_RESTRICT_TIME is set, the defines the frame range where GP frames are considered */
+ float end_frame;
+
+ /* TODO: color (paletteslot + colorname) used for appearance of stroke tips being drawn */
+
+ float start_delay; /* For each pair of gp keys, number of frames before strokes start appearing */
+ float length; /* For each pair of gp keys, number of frames that build effect must be completed within */
+
+ short flag; /* (eGpencilBuild_Flag) Options for controlling modifier behaviour */
+
+ short mode; /* (eGpencilBuild_Mode) How are strokes ordered */
+ short direction; /* (eGpencilBuild_Direction) In what order do stroke points appear/disappear */
+
+ short time_alignment; /* (eGpencilBuild_TimeAlignment) For the "Concurrent" mode, when should "shorter" strips start/end */
} GpencilBuildModifierData;
+typedef enum eGpencilBuild_Mode {
+ /* Strokes are shown one by one until all have appeared */
+ GP_BUILD_MODE_SEQUENTIAL = 0,
+ /* All strokes start at the same time */
+ GP_BUILD_MODE_CONCURRENT = 1,
+} eGpencilBuild_Mode;
+
+typedef enum eGpencilBuild_Direction {
+ /* Show in forward order */
+ GP_BUILD_DIRECTION_GROW = 0,
+ /* Hide in reverse order */
+ GP_BUILD_DIRECTION_SHRINK = 1,
+ /* Hide in forward order */
+ GP_BUILD_DIRECTION_FADE = 2,
+} eGpencilBuild_Direction;
+
+typedef enum eGpencilBuild_TimeAlignment {
+ /* All strokes start at same time */
+ GP_BUILD_TIMEALIGN_START = 0,
+ /* All strokes end at same time */
+ GP_BUILD_TIMEALIGN_END = 1,
+
+ /* TODO: Random Offsets, Stretch-to-Fill */
+} eGpencilBuild_TimeAlignment;
+
typedef enum eGpencilBuild_Flag {
- GP_BUILD_FLAG_RANDOMIZE = (1 << 0), /* order of vertices is randomized */
- GP_BUILD_FLAG_REVERSE = (1 << 1), /* frame range is reversed, resulting in a deconstruction effect */
+ /* Restrict modifier to particular layer/passes? */
+ GP_BUILD_INVERSE_LAYER = (1 << 0),
+ GP_BUILD_INVERSE_PASS = (1 << 1),
+
+ /* Restrict modifier to only operating between the nominated frames */
+ GP_BUILD_RESTRICT_TIME = (1 << 2),
} eGpencilBuild_Flag;
diff --git a/source/blender/modifiers/intern/MOD_gpencilbuild.c b/source/blender/modifiers/intern/MOD_gpencilbuild.c
index 94f3f1ad378..6abea33479c 100644
--- a/source/blender/modifiers/intern/MOD_gpencilbuild.c
+++ b/source/blender/modifiers/intern/MOD_gpencilbuild.c
@@ -30,11 +30,16 @@
#include <stdio.h>
+#include "MEM_guardedalloc.h"
+
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
#include "DNA_gpencil_types.h"
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
#include "BLI_utildefines.h"
+
#include "BKE_context.h"
#include "BKE_gpencil.h"
@@ -45,7 +50,17 @@
static void initData(ModifierData *md)
{
- //GpencilBuildModifierData *gpmd = (GpencilBuildModifierData *)md;
+ GpencilBuildModifierData *gpmd = (GpencilBuildModifierData *)md;
+
+ /* We deliberately set this range to the half the default
+ * frame-range to have an immediate effect ot suggest use-cases
+ */
+ gpmd->start_frame = 1;
+ gpmd->end_frame = 125;
+
+ /* Init default length of each build effect - Nothing special */
+ gpmd->start_delay = 0.0f;
+ gpmd->length = 100.0f;
}
static void copyData(ModifierData *md, ModifierData *target)
@@ -53,13 +68,436 @@ static void copyData(ModifierData *md, ModifierData *target)
modifier_copyData_generic(md, target);
}
+static bool dependsOnTime(ModifierData *UNUSED(md))
+{
+ return true;
+}
+
+/* ******************************************** */
+/* Build Modifier - Stroke generation logic
+ *
+ * There are two modes for how the strokes are sequenced (at a macro-level):
+ * - Sequential Mode - Strokes appear/disappear one after the other. Only a single one changes at a time.
+ * - Concurrent Mode - Multiple strokes appear/disappear at once.
+ *
+ * Assumptions:
+ * - Stroke points are generally equally spaced. This implies that we can just add/remove points,
+ * without worrying about distances between them / adding extra interpolated points between
+ * an visible point and one about to be added/removed (or any similar tapering effects).
+
+ * - All strokes present are fully visible (i.e. we don't have to ignore any)
+ */
+
+/* Remove a particular stroke, freeing the palcolor too, since it is a local copy */
+static void clear_stroke(bGPDstroke *gps, int modifier_index)
+{
+ if (modifier_index > -1) {
+ /* This is part of the modifier stack, meaning that we're working on
+ * "derived data" with local copies of the palcolors that we need to free
+ */
+ MEM_SAFE_FREE(gps->palcolor);
+ }
+
+ BKE_gpencil_free_stroke(gps);
+}
+
+/* Clear all strokes in frame */
+static void gpf_clear_all_strokes(bGPDframe *gpf, int modifier_index)
+{
+ bGPDstroke *gps, *gps_next;
+ for (gps = gpf->strokes.first; gps; gps = gps_next) {
+ gps_next = gps->next;
+ clear_stroke(gps, modifier_index);
+ }
+ BLI_listbase_clear(&gpf->strokes);
+}
+
+/* Reduce the number of points in the stroke
+ *
+ * Note: This won't be called if all points are present/removed
+ * TODO: Allow blending of growing/shrinking tip (e.g. for more gradual transitions)
+ */
+static void reduce_stroke_points(bGPDstroke *gps, const int num_points, const eGpencilBuild_Direction direction)
+{
+ bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * num_points, "GP Build Modifier - Reduced Points");
+
+ /* Which end should points be removed from */
+ // TODO: free stroke weights
+ switch (direction) {
+ case GP_BUILD_DIRECTION_GROW: /* Show in forward order = Remove ungrown-points from end of stroke */
+ case GP_BUILD_DIRECTION_SHRINK: /* Hide in reverse order = Remove dead-points from end of stroke */
+ {
+ /* copy over point data */
+ memcpy(new_points, gps->points, sizeof(bGPDspoint) * num_points);
+
+ /* free unused point weights */
+ for (int i = num_points; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ BKE_gpencil_free_point_weights(pt);
+ }
+
+ break;
+ }
+
+ /* Hide in forward order = Remove points from start of stroke */
+ case GP_BUILD_DIRECTION_FADE:
+ {
+ /* num_points is the number of points left after reducing.
+ * We need to know how many to remove
+ */
+ const int offset = gps->totpoints - num_points;
+
+ /* copy over point data */
+ memcpy(new_points, gps->points + offset, sizeof(bGPDspoint) * num_points);
+
+ /* free unused point weights */
+ for (int i = 0; i < offset; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ BKE_gpencil_free_point_weights(pt);
+ }
+
+ break;
+ }
+
+ default:
+ printf("ERROR: Unknown direction %d in %s()\n", direction, __func__);
+ break;
+ }
+
+ /* replace stroke geometry */
+ MEM_freeN(gps->points);
+ gps->points = new_points;
+ gps->totpoints = num_points;
+
+ /* mark stroke as needing to have its geometry caches rebuilt */
+ gps->flag |= GP_STROKE_RECALC_CACHES;
+ gps->tot_triangles = 0;
+}
+
+/* --------------------------------------------- */
+
+/* Stroke Data Table Entry - This represents one stroke being generated */
+typedef struct tStrokeBuildDetails {
+ bGPDstroke *gps;
+
+ /* Indices - first/last indices for the stroke's points (overall) */
+ size_t start_idx, end_idx;
+
+ /* Number of points - Cache for more convenient access */
+ int totpoints;
+} tStrokeBuildDetails;
+
+
+/* Sequential - Show strokes one after the other */
+static void build_sequential(GpencilBuildModifierData *mmd, bGPDlayer *gpl, bGPDframe *gpf, float fac, int modifier_index)
+{
+ const size_t tot_strokes = BLI_listbase_count(&gpf->strokes);
+ bGPDstroke *gps;
+ size_t i;
+
+ /* 1) Compute proportion of time each stroke should occupy */
+ /* NOTE: This assumes that the total number of points won't overflow! */
+ tStrokeBuildDetails *table = MEM_callocN(sizeof(tStrokeBuildDetails) * tot_strokes, "tStrokeBuildDetails");
+ size_t totpoints = 0;
+
+ /* 1.1) First pass - Tally up points */
+ for (gps = gpf->strokes.first, i = 0; gps; gps = gps->next, i++) {
+ tStrokeBuildDetails *cell = &table[i];
+
+ cell->gps = gps;
+ cell->totpoints = gps->totpoints;
+ }
+
+ /* 1.2) Second pass - Compute the overall indices for points */
+ for (i = 0; i < tot_strokes; i++) {
+ tStrokeBuildDetails *cell = &table[i];
+
+ if (i == 0) {
+ cell->start_idx = 0;
+ }
+ else {
+ cell->start_idx = (cell - 1)->end_idx;
+ }
+ cell->end_idx = cell->start_idx + cell->totpoints - 1;
+ }
+
+
+ /* 2) Determine the global indices for points that should be visible */
+ size_t first_visible = 0;
+ size_t last_visible = 0;
+
+ switch (mmd->direction) {
+ /* Show in forward order
+ * - As fac increases, the number of visible points increases
+ */
+ case GP_BUILD_DIRECTION_GROW:
+ first_visible = 0; /* always visible */
+ last_visible = (size_t)roundf(totpoints * fac);
+ break;
+
+ /* Hide in reverse order
+ * - As fac increases, the number of points visible at the end decreases
+ */
+ case GP_BUILD_DIRECTION_SHRINK:
+ first_visible = 0; /* always visible (until last point removed) */
+ last_visible = (size_t)(totpoints * (1.0f - fac));
+ break;
+
+ /* Hide in forward order
+ * - As fac increases, the early points start getting hidden
+ */
+ case GP_BUILD_DIRECTION_FADE:
+ first_visible = (size_t)(totpoints * fac);
+ last_visible = totpoints; /* i.e. visible until the end, unless first overlaps this */
+ break;
+ }
+
+
+ /* 3) Go through all strokes, deciding which to keep, and/or how much of each to keep */
+ for (i = 0; i < tot_strokes; i++) {
+ tStrokeBuildDetails *cell = &table[i];
+
+ /* Determine what portion of the stroke is visible */
+ if (
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list