Grease Pencil: Add LineArt modifier

This adds the LineArt grease pencil modifier.

It takes objects or collections as input and generates various grease
pencil lines from these objects with the help of the active scene
camera. For example it can generate contour lines, intersection lines
and crease lines to name a few.

This is really useful as artists can then use 3D meshes to automatically
generate grease pencil lines for characters, enviroments or other
visualization purposes.

These lines can then be baked and edited as regular grease pencil lines.

Reviewed By: Sebastian Parborg, Antonio Vazquez, Matias Mendiola

Differential Revision: http://developer.blender.org/D8758
This commit is contained in:
YimingWu 2021-03-16 19:35:53 +01:00 committed by Sebastian Parborg
parent 877238e2b7
commit 3e87d8a431
Notes: blender-bot 2023-02-14 05:37:19 +01:00
Referenced by commit 2907227db2, Fix T103880: Crash with Line Art Grease-Pencil modifier
Referenced by issue #90175, Properties Editor - Materials Tab -  Lineart Panel - dysfunctional decorators
Referenced by issue #86822, context.collection not available in Object/Object Data Properties
Referenced by issue #85691, Geometry of external objects loses its attributes after subdivision
Referenced by issue #80194, LineArt: New module to generate lines over meshes using grease pencil as target
62 changed files with 8056 additions and 28 deletions

View File

@ -67,6 +67,7 @@ _modules = [
"properties_scene",
"properties_texture",
"properties_world",
"properties_collection",
# Generic Space Modules
#
@ -104,6 +105,8 @@ import bpy
if bpy.app.build_options.freestyle:
_modules.append("properties_freestyle")
_modules.append("properties_lineart")
__import__(name=__name__, fromlist=_modules)
_namespace = globals()
_modules_loaded = [_namespace[name] for name in _modules]

View File

@ -0,0 +1,98 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# 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.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
from bpy.types import Panel
class CollectionButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "collection"
def lineart_make_line_type_entry(col, line_type, text_disp, expand, search_from):
col.prop(line_type, "use", text=text_disp)
if line_type.use and expand:
col.prop_search(line_type, "layer", search_from,
"layers", icon='GREASEPENCIL')
col.prop_search(line_type, "material", search_from,
"materials", icon='SHADING_TEXTURE')
class COLLECTION_PT_collection_flags(CollectionButtonsPanel, Panel):
bl_label = "Restrictions"
@classmethod
def poll(cls, context):
vl = context.view_layer
vlc = vl.active_layer_collection
return (vlc.name != 'Master Collection')
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
collection = context.collection
vl = context.view_layer
vlc = vl.active_layer_collection
col = layout.column(align=True)
col.prop(collection, "hide_select", text="Selectable", toggle=False, invert_checkbox=True)
col.prop(collection, "hide_render", toggle=False)
col = layout.column(align=True)
col.prop(vlc, "holdout", toggle=False)
col.prop(vlc, "indirect_only", toggle=False)
class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel):
bl_label = "Line Art"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
collection = context.collection
row = layout.row()
row.prop(collection, "lineart_usage")
class COLLECTION_PT_instancing(CollectionButtonsPanel, Panel):
bl_label = "Instancing"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
collection = context.collection
row = layout.row()
row.prop(collection, "instance_offset")
classes = (
COLLECTION_PT_collection_flags,
COLLECTION_PT_lineart_collection,
COLLECTION_PT_instancing,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)

View File

@ -0,0 +1,59 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# 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.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
from bpy.types import Panel
class LineartButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
class OBJECT_PT_lineart(LineartButtonsPanel, Panel):
bl_label = "Line Art"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
ob = context.object
return (ob.type in {'MESH', 'FONT', 'CURVE', 'SURFACE'})
def draw(self, context):
layout = self.layout
lineart = context.object.lineart
layout.use_property_split = True
layout.prop(lineart, 'usage')
layout.use_property_split = True
row = layout.row(heading="Override Crease")
row.prop(lineart, "use_crease_override", text="")
row.prop(lineart, "crease_threshold", slider=True, text="")
classes = (
OBJECT_PT_lineart,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)

View File

@ -274,6 +274,38 @@ class MATERIAL_PT_viewport(MaterialButtonsPanel, Panel):
col.prop(mat, "roughness")
class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel):
bl_label = "Line Art"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
mat = context.material
return mat and not mat.grease_pencil
def draw(self, context):
layout = self.layout
mat = context.material
lineart = mat.lineart
layout.prop(lineart, "use_transparency")
if lineart.use_transparency:
layout.label(text="Transparency Masks:")
row = layout.row(align=True)
row.prop(lineart, "transparency_mask_0", text="0", toggle=True)
row.prop(lineart, "transparency_mask_1", text="1", toggle=True)
row.prop(lineart, "transparency_mask_2", text="2", toggle=True)
row.prop(lineart, "transparency_mask_3", text="3", toggle=True)
row.prop(lineart, "transparency_mask_4", text="4", toggle=True)
row.prop(lineart, "transparency_mask_5", text="5", toggle=True)
row.prop(lineart, "transparency_mask_6", text="6", toggle=True)
row.prop(lineart, "transparency_mask_7", text="7", toggle=True)
classes = (
MATERIAL_MT_context_menu,
MATERIAL_UL_matslots,
@ -282,6 +314,7 @@ classes = (
EEVEE_MATERIAL_PT_surface,
EEVEE_MATERIAL_PT_volume,
EEVEE_MATERIAL_PT_settings,
MATERIAL_PT_lineart,
MATERIAL_PT_viewport,
EEVEE_MATERIAL_PT_viewport_settings,
MATERIAL_PT_custom_props,

View File

@ -3829,11 +3829,10 @@ class VIEW3D_MT_edit_mesh_context_menu(Menu):
col.operator("mesh.mark_sharp")
col.operator("mesh.mark_sharp", text="Clear Sharp").clear = True
if render.use_freestyle:
col.separator()
col.separator()
col.operator("mesh.mark_freestyle_edge").clear = False
col.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
col.operator("mesh.mark_freestyle_edge").clear = False
col.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
col.separator()
@ -4028,11 +4027,10 @@ class VIEW3D_MT_edit_mesh_edges_data(Menu):
props.use_verts = True
props.clear = True
if render.use_freestyle:
layout.separator()
layout.separator()
layout.operator("mesh.mark_freestyle_edge").clear = False
layout.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
layout.operator("mesh.mark_freestyle_edge").clear = False
layout.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
class VIEW3D_MT_edit_mesh_edges(Menu):

View File

@ -56,6 +56,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_layer_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_light_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_lightprobe_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_lineart_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_linestyle_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_listBase.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_mask_types.h

View File

@ -82,6 +82,8 @@ struct Collection *BKE_collection_master_add(void);
bool BKE_collection_has_object(struct Collection *collection, const struct Object *ob);
bool BKE_collection_has_object_recursive(struct Collection *collection, struct Object *ob);
bool BKE_collection_has_object_recursive_instanced(struct Collection *collection,
struct Object *ob);
struct Collection *BKE_collection_object_find(struct Main *bmain,
struct Scene *scene,
struct Collection *collection,
@ -123,6 +125,7 @@ bool BKE_collection_object_cyclic_check(struct Main *bmain,
/* Object list cache. */
struct ListBase BKE_collection_object_cache_get(struct Collection *collection);
ListBase BKE_collection_object_cache_instanced_get(struct Collection *collection);
void BKE_collection_object_cache_free(struct Collection *collection);
struct Base *BKE_collection_or_layer_objects(const struct ViewLayer *view_layer,

View File

@ -77,6 +77,7 @@ typedef struct Global {
* * 1112: Disable new Cloth internal springs handling (09/2014).
* * 1234: Disable new dyntopo code fixing skinny faces generation (04/2015).
* * 3001: Enable additional Fluid modifier (Mantaflow) options (02/2020).
* * 4000: Line Art state output and debugging logs (03/2021).
* * 16384 and above: Reserved for python (add-ons) usage.
*/
short debug_value;

View File

@ -212,6 +212,10 @@ void BKE_gpencil_layer_mask_sort(struct bGPdata *gpd, struct bGPDlayer *gpl);
void BKE_gpencil_layer_mask_sort_all(struct bGPdata *gpd);
void BKE_gpencil_layer_frames_sort(struct bGPDlayer *gpl, bool *r_has_duplicate_frames);
struct bGPDlayer *BKE_gpencil_layer_get_by_name(struct bGPdata *gpd,
char *name,
int first_if_not_found);
/* Brush */
struct Material *BKE_gpencil_brush_material_get(struct Brush *brush);
void BKE_gpencil_brush_material_set(struct Brush *brush, struct Material *material);
@ -231,6 +235,7 @@ struct Material *BKE_gpencil_object_material_new(struct Main *bmain,
int *r_index);
int BKE_gpencil_object_material_index_get(struct Object *ob, struct Material *ma);
int BKE_gpencil_object_material_get_index_name(struct Object *ob, char *name);
struct Material *BKE_gpencil_object_material_from_brush_get(struct Object *ob,
struct Brush *brush);

View File

@ -206,7 +206,8 @@ typedef struct GpencilModifierTypeInfo {
* This function is optional.
*/
void (*updateDepsgraph)(struct GpencilModifierData *md,
const struct ModifierUpdateDepsgraphContext *ctx);
const struct ModifierUpdateDepsgraphContext *ctx,
const int mode);
/**
* Should return true if the modifier needs to be recalculated on time

View File

@ -122,7 +122,9 @@ static void collection_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons
}
collection_dst->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
collection_dst->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
BLI_listbase_clear(&collection_dst->object_cache);
BLI_listbase_clear(&collection_dst->object_cache_instanced);
BLI_listbase_clear(&collection_dst->gobject);
BLI_listbase_clear(&collection_dst->children);
@ -214,8 +216,10 @@ static void collection_blend_write(BlendWriter *writer, ID *id, const void *id_a
if (collection->id.us > 0 || BLO_write_is_undo(writer)) {
/* Clean up, important in undo case to reduce false detection of changed data-blocks. */
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
collection->tag = 0;
BLI_listbase_clear(&collection->object_cache);
BLI_listbase_clear(&collection->object_cache_instanced);
BLI_listbase_clear(&collection->parents);
/* write LibData */
@ -246,8 +250,10 @@ void BKE_collection_blend_read_data(BlendDataReader *reader, Collection *collect
BKE_previewimg_blend_read(reader, collection->preview);
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
collection->tag = 0;
BLI_listbase_clear(&collection->object_cache);
BLI_listbase_clear(&collection->object_cache_instanced);
BLI_listbase_clear(&collection->parents);
#ifdef USE_COLLECTION_COMPAT_28
@ -773,7 +779,10 @@ const char *BKE_collection_ui_name_get(struct Collection *collection)
/** \name Object List Cache
* \{ */
static void collection_object_cache_fill(ListBase *lb, Collection *collection, int parent_restrict)
static void collection_object_cache_fill(ListBase *lb,
Collection *collection,
int parent_restrict,
bool with_instances)
{
int child_restrict = collection->flag | parent_restrict;
@ -784,6 +793,10 @@ static void collection_object_cache_fill(ListBase *lb, Collection *collection, i
base = MEM_callocN(sizeof(Base), "Object Base");
base->object = cob->ob;
BLI_addtail(lb, base);
if (with_instances && cob->ob->instance_collection) {
collection_object_cache_fill(
lb, cob->ob->instance_collection, child_restrict, with_instances);
}
}
/* Only collection flags are checked here currently, object restrict flag is checked
@ -798,7 +811,7 @@ static void collection_object_cache_fill(ListBase *lb, Collection *collection, i
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
collection_object_cache_fill(lb, child->collection, child_restrict);
collection_object_cache_fill(lb, child->collection, child_restrict, with_instances);
}
}
@ -809,7 +822,7 @@ ListBase BKE_collection_object_cache_get(Collection *collection)
BLI_mutex_lock(&cache_lock);
if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE)) {
collection_object_cache_fill(&collection->object_cache, collection, 0);
collection_object_cache_fill(&collection->object_cache, collection, 0, false);
collection->flag |= COLLECTION_HAS_OBJECT_CACHE;
}
BLI_mutex_unlock(&cache_lock);
@ -818,11 +831,29 @@ ListBase BKE_collection_object_cache_get(Collection *collection)
return collection->object_cache;
}
ListBase BKE_collection_object_cache_instanced_get(Collection *collection)
{
if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE_INSTANCED)) {
static ThreadMutex cache_lock = BLI_MUTEX_INITIALIZER;
BLI_mutex_lock(&cache_lock);
if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE_INSTANCED)) {
collection_object_cache_fill(&collection->object_cache_instanced, collection, 0, true);
collection->flag |= COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
}
BLI_mutex_unlock(&cache_lock);
}
return collection->object_cache_instanced;
}
static void collection_object_cache_free(Collection *collection)
{
/* Clear own cache an for all parents, since those are affected by changes as well. */
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
BLI_freelistN(&collection->object_cache);
BLI_freelistN(&collection->object_cache_instanced);
LISTBASE_FOREACH (CollectionParent *, parent, &collection->parents) {
collection_object_cache_free(parent->collection);
@ -928,6 +959,16 @@ bool BKE_collection_has_object_recursive(Collection *collection, Object *ob)
return (BLI_findptr(&objects, ob, offsetof(Base, object)));
}
bool BKE_collection_has_object_recursive_instanced(Collection *collection, Object *ob)
{
if (ELEM(NULL, collection, ob)) {
return false;
}
const ListBase objects = BKE_collection_object_cache_instanced_get(collection);
return (BLI_findptr(&objects, ob, offsetof(Base, object)));
}
static Collection *collection_next_find(Main *bmain, Scene *scene, Collection *collection)
{
if (scene && collection == scene->master_collection) {

View File

@ -1663,6 +1663,31 @@ bGPDlayer *BKE_gpencil_layer_active_get(bGPdata *gpd)
return NULL;
}
bGPDlayer *BKE_gpencil_layer_get_by_name(bGPdata *gpd, char *name, int first_if_not_found)
{
bGPDlayer *gpl;
int i = 0;
/* error checking */
if (ELEM(NULL, gpd, gpd->layers.first)) {
return NULL;
}
/* loop over layers until found (assume only one active) */
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
if (STREQ(name, gpl->info)) {
return gpl;
}
i++;
}
/* no such layer */
if (first_if_not_found) {
return gpd->layers.first;
}
return NULL;
}
/**
* Set active grease pencil layer.
* \param gpd: Grease pencil data-block
@ -2421,6 +2446,21 @@ int BKE_gpencil_object_material_index_get(Object *ob, Material *ma)
return -1;
}
int BKE_gpencil_object_material_get_index_name(Object *ob, char *name)
{
short *totcol = BKE_object_material_len_p(ob);
Material *read_ma = NULL;
for (short i = 0; i < *totcol; i++) {
read_ma = BKE_object_material_get(ob, i + 1);
/* Material names are like "MAMaterial.001" */
if (STREQ(name, &read_ma->id.name[2])) {
return i;
}
}
return -1;
}
/**
* Create a default palette.
* \param bmain: Main pointer

View File

@ -176,12 +176,14 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
CustomData_MeshMasks cddata_masks = scene->customdata_mask;
CustomData_MeshMasks_update(&cddata_masks, &CD_MASK_BAREMESH);
if (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER) {
/* Make sure Freestyle edge/face marks appear in DM for render (see T40315). */
/* Make sure Freestyle edge/face marks appear in DM for render (see T40315). Due to Line Art
* impementation, edge marks should also be shown in viewport. */
#ifdef WITH_FREESTYLE
cddata_masks.emask |= CD_MASK_FREESTYLE_EDGE;
cddata_masks.pmask |= CD_MASK_FREESTYLE_FACE;
cddata_masks.emask |= CD_MASK_FREESTYLE_EDGE;
cddata_masks.pmask |= CD_MASK_FREESTYLE_FACE;
cddata_masks.vmask |= CD_MASK_MDEFORMVERT;
#endif
if (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER) {
/* Always compute UVs, vertex colors as orcos for render. */
cddata_masks.lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL;
cddata_masks.vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;

View File

@ -117,6 +117,9 @@ MINLINE float sasqrt(float fac);
MINLINE float interpf(float a, float b, float t);
MINLINE double interpd(double a, double b, double t);
MINLINE float ratiof(float min, float max, float pos);
MINLINE double ratiod(double min, double max, double pos);
/* NOTE: Compilers will upcast all types smaller than int to int when performing arithmetic
* operation. */
MINLINE int square_s(short a);

View File

@ -180,6 +180,18 @@ MINLINE double interpd(double target, double origin, double fac)
return (fac * target) + (1.0f - fac) * origin;
}
MINLINE float ratiof(float min, float max, float pos)
{
float range = max - min;
return range == 0 ? 0 : ((pos - min) / range);
}
MINLINE double ratiod(double min, double max, double pos)
{
double range = max - min;
return range == 0 ? 0 : ((pos - min) / range);
}
/* used for zoom values*/
MINLINE float power_of_2(float val)
{

View File

@ -2085,7 +2085,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
if (mti->updateDepsgraph) {
DepsNodeHandle handle = create_node_handle(obdata_ubereval_key);
ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle);
mti->updateDepsgraph(md, &ctx);
mti->updateDepsgraph(md, &ctx, graph_->mode);
}
if (BKE_object_modifier_gpencil_use_time(object, md)) {
TimeSourceKey time_src_key;

View File

@ -36,6 +36,7 @@ set(SRC
annotate_paint.c
drawgpencil.c
editaction_gpencil.c
gpencil_add_lineart.c
gpencil_add_monkey.c
gpencil_add_stroke.c
gpencil_armature.c

View File

@ -0,0 +1,120 @@
/*
* 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) 2017 Blender Foundation
* This is a new part of Blender
*/
/** \file
* \ingroup edgpencil
*/
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "DNA_gpencil_types.h"
#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "ED_gpencil.h"
/* Definition of the most important info from a color */
typedef struct ColorTemplate {
const char *name;
float line[4];
float fill[4];
} ColorTemplate;
/* Add color an ensure duplications (matched by name) */
static int gpencil_lineart_material(Main *bmain,
Object *ob,
const ColorTemplate *pct,
const bool fill)
{
short *totcol = BKE_object_material_len_p(ob);
Material *ma = NULL;
for (short i = 0; i < *totcol; i++) {
ma = BKE_gpencil_material(ob, i + 1);
if (STREQ(ma->id.name, pct->name)) {
return i;
}
}
int idx;
/* create a new one */
ma = BKE_gpencil_object_material_new(bmain, ob, pct->name, &idx);
copy_v4_v4(ma->gp_style->stroke_rgba, pct->line);
srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba);
copy_v4_v4(ma->gp_style->fill_rgba, pct->fill);
srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba);
if (fill) {
ma->gp_style->flag |= GP_MATERIAL_FILL_SHOW;
}
return idx;
}
/* ***************************************************************** */
/* Color Data */
static const ColorTemplate gp_stroke_material_black = {
"Black",
{0.0f, 0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 0.0f, 0.0f},
};
/* ***************************************************************** */
/* LineArt API */
/* Add a Simple LineArt setup. */
void ED_gpencil_create_lineart(bContext *C, Object *ob)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
bGPdata *gpd = (bGPdata *)ob->data;
/* create colors */
int color_black = gpencil_lineart_material(bmain, ob, &gp_stroke_material_black, false);
/* set first color as active and in brushes */
ob->actcol = color_black + 1;
/* layers */
bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
/* frames */
BKE_gpencil_frame_addnew(lines, CFRA);
/* update depsgraph */
/* To trigger modifier update, this is still needed although we don't have any strokes. */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
}

View File

@ -250,6 +250,7 @@ void ED_gpencil_brush_draw_eraser(struct Brush *brush, int x, int y);
void ED_gpencil_create_monkey(struct bContext *C, struct Object *ob, float mat[4][4]);
void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4][4]);
void ED_gpencil_create_lineart(struct bContext *C, struct Object *ob);
/* ------------ Object Utilities ------------ */
struct Object *ED_gpencil_add_object(struct bContext *C,

View File

@ -7746,7 +7746,7 @@ void MESH_OT_symmetry_snap(struct wmOperatorType *ot)
/** \} */
#ifdef WITH_FREESTYLE
#if defined(WITH_FREESTYLE)
/* -------------------------------------------------------------------- */
/** \name Mark Edge (Freestyle) Operator

View File

@ -267,7 +267,7 @@ void MESH_OT_paint_mask_slice(struct wmOperatorType *ot);
struct wmKeyMap *point_normals_modal_keymap(wmKeyConfig *keyconf);
#ifdef WITH_FREESTYLE
#if defined(WITH_FREESTYLE)
void MESH_OT_mark_freestyle_edge(struct wmOperatorType *ot);
void MESH_OT_mark_freestyle_face(struct wmOperatorType *ot);
#endif

View File

@ -130,7 +130,7 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_loop_multi_select);
WM_operatortype_append(MESH_OT_mark_seam);
WM_operatortype_append(MESH_OT_mark_sharp);
#ifdef WITH_FREESTYLE
#if defined(WITH_FREESTYLE)
WM_operatortype_append(MESH_OT_mark_freestyle_edge);
#endif
WM_operatortype_append(MESH_OT_vertices_smooth);

View File

@ -31,6 +31,7 @@
#include "DNA_camera_types.h"
#include "DNA_collection_types.h"
#include "DNA_curve_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_key_types.h"
#include "DNA_light_types.h"
@ -68,6 +69,7 @@
#include "BKE_geometry_set.h"
#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_hair.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
@ -1305,7 +1307,7 @@ static bool object_gpencil_add_poll(bContext *C)
static int object_gpencil_add_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
Object *ob = CTX_data_active_object(C), *ob_orig = ob;
bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? ob->data : NULL;
const int type = RNA_enum_get(op->ptr, "type");
@ -1333,6 +1335,11 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
ob_name = "Stroke";
break;
}
case GP_LRT_OBJECT:
case GP_LRT_COLLECTION: {
ob_name = "Line Art";
break;
}
default: {
break;
}
@ -1373,6 +1380,46 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
ED_gpencil_create_monkey(C, ob, mat);
break;
}
case GP_LRT_SCENE:
case GP_LRT_COLLECTION:
case GP_LRT_OBJECT: {
float radius = RNA_float_get(op->ptr, "radius");
float mat[4][4];
ED_object_new_primitive_matrix(C, ob, loc, rot, mat);
mul_v3_fl(mat[0], radius);
mul_v3_fl(mat[1], radius);
mul_v3_fl(mat[2], radius);
ED_gpencil_create_lineart(C, ob);
gpd = ob->data;
/* Add Line Art modifier */
LineartGpencilModifierData *md = (LineartGpencilModifierData *)BKE_gpencil_modifier_new(
eGpencilModifierType_Lineart);
BLI_addtail(&ob->greasepencil_modifiers, md);
BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, (GpencilModifierData *)md);
if (type == GP_LRT_COLLECTION) {
md->source_type = LRT_SOURCE_COLLECTION;
md->source_collection = CTX_data_collection(C);
}
else if (type == GP_LRT_OBJECT) {
md->source_type = LRT_SOURCE_OBJECT;
md->source_object = ob_orig;
}
else {
/* Whole scene. */
md->source_type = LRT_SOURCE_SCENE;
}
/* Only created one layer and one material. */
strcpy(md->target_layer, ((bGPDlayer *)gpd->layers.first)->info);
md->target_material = BKE_gpencil_material(ob, 1);
/* Stroke object is drawn in front of meshes by default. */
ob->dtx |= OB_DRAW_IN_FRONT;
}
case GP_EMPTY:
/* do nothing */
break;
@ -1393,6 +1440,41 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
static const EnumPropertyItem *object_gpencil_add_options(bContext *C,
PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
EnumPropertyItem *item = NULL;
const EnumPropertyItem *item_ref = rna_enum_object_gpencil_type_items;
int totitem = 0;
int i = 0;
int orig_count = RNA_enum_items_count(item_ref);
/* Default types. */
for (i = 0; i < orig_count; i++) {
if (item_ref[i].value == GP_LRT_OBJECT || item_ref[i].value == GP_LRT_COLLECTION ||
item_ref[i].value == GP_LRT_SCENE) {
if (item_ref[i].value == GP_LRT_SCENE) {
/* separator before line art types */
RNA_enum_item_add_separator(&item, &totitem);
}
else if (item_ref[i].value == GP_LRT_OBJECT) {
Object *ob = CTX_data_active_object(C);
if (!ob || ob->type != OB_MESH) {
continue;
}
}
}
RNA_enum_item_add(&item, &totitem, &item_ref[i]);
}
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
void OBJECT_OT_gpencil_add(wmOperatorType *ot)
{
/* identifiers */
@ -1413,6 +1495,7 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot)
ED_object_add_generic_props(ot, false);
ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_gpencil_type_items, 0, "Type", "");
RNA_def_enum_funcs(ot->prop, object_gpencil_add_options);
}
/** \} */

View File

@ -37,6 +37,8 @@
#include "object_intern.h"
#include "MOD_gpencil_lineart.h"
/* ************************** registration **********************************/
void ED_operatortypes_object(void)
@ -154,6 +156,9 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy);
WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy_to_selected);
/* grease pencil line art */
WM_operatortypes_lineart();
/* Shader FX. */
WM_operatortype_append(OBJECT_OT_shaderfx_add);
WM_operatortype_append(OBJECT_OT_shaderfx_remove);

View File

@ -33,6 +33,7 @@
#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_collection_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_material_types.h"
#include "DNA_node_types.h"
@ -153,6 +154,29 @@ static bool buttons_context_path_world(ButsContextPath *path)
return false;
}
static bool buttons_context_path_collection(ButsContextPath *path, wmWindow *window)
{
PointerRNA *ptr = &path->ptr[path->len - 1];
/* if we already have a (pinned) collection, we're done */
if (RNA_struct_is_a(ptr->type, &RNA_Collection)) {
return true;
}
/* if we have a view layer, use the view layer's active collection */
if (buttons_context_path_view_layer(path, window)) {
ViewLayer *view_layer = path->ptr[path->len - 1].data;
Collection *c = view_layer->active_collection->collection;
if (c) {
RNA_id_pointer_create(&c->id, &path->ptr[path->len]);
path->len++;
return true;
}
}
/* no path to a collection possible */
return false;
}
static bool buttons_context_path_linestyle(ButsContextPath *path, wmWindow *window)
{
PointerRNA *ptr = &path->ptr[path->len - 1];
@ -575,6 +599,9 @@ static bool buttons_context_path(
case BCONTEXT_WORLD:
found = buttons_context_path_world(path);
break;
case BCONTEXT_COLLECTION: /* This is for Line Art collection flags */
found = buttons_context_path_collection(path, window);
break;
case BCONTEXT_TOOL:
found = true;
break;
@ -858,6 +885,10 @@ int /*eContextResult*/ buttons_context(const bContext *C,
set_pointer_type(path, result, &RNA_World);
return CTX_RESULT_OK;
}
if (CTX_data_equals(member, "collection")) {
set_pointer_type(path, result, &RNA_Collection);
return 1;
}
if (CTX_data_equals(member, "object")) {
set_pointer_type(path, result, &RNA_Object);
return CTX_RESULT_OK;

View File

@ -206,6 +206,14 @@ int ED_buttons_tabs_list(SpaceProperties *sbuts, short *context_tabs_array)
context_tabs_array[length] = -1;
length++;
}
if (sbuts->pathflag & (1 << BCONTEXT_VIEW_LAYER)) {
context_tabs_array[length] = BCONTEXT_COLLECTION;
length++;
}
if (length != 0) {
context_tabs_array[length] = -1;
length++;
}
if (sbuts->pathflag & (1 << BCONTEXT_OBJECT)) {
context_tabs_array[length] = BCONTEXT_OBJECT;
length++;
@ -271,6 +279,8 @@ static const char *buttons_main_region_context_string(const short mainb)
return "view_layer";
case BCONTEXT_WORLD:
return "world";
case BCONTEXT_COLLECTION:
return "collection";
case BCONTEXT_OBJECT:
return "object";
case BCONTEXT_DATA:

View File

@ -53,6 +53,7 @@ set(SRC
intern/MOD_gpencilbuild.c
intern/MOD_gpencilcolor.c
intern/MOD_gpencilhook.c
intern/MOD_gpencillineart.c
intern/MOD_gpencillattice.c
intern/MOD_gpencilmirror.c
intern/MOD_gpencilmultiply.c
@ -70,6 +71,16 @@ set(SRC
MOD_gpencil_modifiertypes.h
intern/MOD_gpencil_ui_common.h
intern/MOD_gpencil_util.h
# Lineart code
intern/lineart/lineart_ops.c
intern/lineart/lineart_cpu.c
intern/lineart/lineart_chain.c
intern/lineart/lineart_util.c
intern/lineart/lineart_intern.h
intern/lineart/MOD_lineart.h
)
set(LIB
@ -79,6 +90,7 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_gpencil_modifiers "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@ -0,0 +1,31 @@
/*
* 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.
*/
/** \file
* \ingroup modifiers
*/
#pragma once
#include "DNA_windowmanager_types.h"
/* Operator types should be in exposed header. */
void OBJECT_OT_lineart_bake_strokes(struct wmOperatorType *ot);
void OBJECT_OT_lineart_bake_strokes_all(struct wmOperatorType *ot);
void OBJECT_OT_lineart_clear(struct wmOperatorType *ot);
void OBJECT_OT_lineart_clear_all(struct wmOperatorType *ot);
void WM_operatortypes_lineart(void);

View File

@ -43,6 +43,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Armature;
extern GpencilModifierTypeInfo modifierType_Gpencil_Time;
extern GpencilModifierTypeInfo modifierType_Gpencil_Multiply;
extern GpencilModifierTypeInfo modifierType_Gpencil_Texture;
extern GpencilModifierTypeInfo modifierType_Gpencil_Lineart;
/* MOD_gpencil_util.c */
void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]);

View File

@ -74,6 +74,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[])
INIT_GP_TYPE(Time);
INIT_GP_TYPE(Multiply);
INIT_GP_TYPE(Texture);
INIT_GP_TYPE(Lineart);
#undef INIT_GP_TYPE
}

View File

@ -179,7 +179,9 @@ static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
return !mmd->object || mmd->object->type != OB_ARMATURE;
}
static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
static void updateDepsgraph(GpencilModifierData *md,
const ModifierUpdateDepsgraphContext *ctx,
const int UNUSED(mode))
{
ArmatureGpencilModifierData *lmd = (ArmatureGpencilModifierData *)md;
if (lmd->object != NULL) {

View File

@ -318,7 +318,9 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec
generate_geometry(md, depsgraph, scene, ob);
}
static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
static void updateDepsgraph(GpencilModifierData *md,
const ModifierUpdateDepsgraphContext *ctx,
const int UNUSED(mode))
{
ArrayGpencilModifierData *lmd = (ArrayGpencilModifierData *)md;
if (lmd->object != NULL) {

View File

@ -330,7 +330,9 @@ static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
return !mmd->object;
}
static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
static void updateDepsgraph(GpencilModifierData *md,
const ModifierUpdateDepsgraphContext *ctx,
const int UNUSED(mode))
{
HookGpencilModifierData *lmd = (HookGpencilModifierData *)md;
if (lmd->object != NULL) {

View File

@ -195,7 +195,9 @@ static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
return !mmd->object || mmd->object->type != OB_LATTICE;
}
static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
static void updateDepsgraph(GpencilModifierData *md,
const ModifierUpdateDepsgraphContext *ctx,
const int UNUSED(mode))
{
LatticeGpencilModifierData *lmd = (LatticeGpencilModifierData *)md;
if (lmd->object != NULL) {

View File

@ -0,0 +1,477 @@
/*
* 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) 2017, Blender Foundation
* This is a new part of Blender
*/
/** \file
* \ingroup modifiers
*/
#include <stdio.h>
#include "BLI_utildefines.h"
#include "BLI_blenlib.h"
#include "BLI_math_vector.h"
#include "BLT_translation.h"
#include "DNA_collection_types.h"
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_lineart_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "lineart/MOD_lineart.h"
#include "BKE_collection.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_screen.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "BKE_modifier.h"
#include "RNA_access.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "MOD_gpencil_modifiertypes.h"
#include "MOD_gpencil_ui_common.h"
#include "MOD_gpencil_util.h"
#include "WM_api.h"
#include "WM_types.h"
static void initData(GpencilModifierData *md)
{
LineartGpencilModifierData *gpmd = (LineartGpencilModifierData *)md;
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(LineartGpencilModifierData), modifier);
}
static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
{
BKE_gpencil_modifier_copydata_generic(md, target);
}
static void generate_strokes_actual(
GpencilModifierData *md, Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, bGPDframe *gpf)
{
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
if (G.debug_value == 4000) {
printf("LRT: Generating from modifier.\n");
}
MOD_lineart_gpencil_generate(
lmd->render_buffer,
depsgraph,
ob,
gpl,
gpf,
lmd->source_type,
lmd->source_type == LRT_SOURCE_OBJECT ? (void *)lmd->source_object :
(void *)lmd->source_collection,
lmd->level_start,
lmd->use_multiple_levels ? lmd->level_end : lmd->level_start,
lmd->target_material ? BKE_gpencil_object_material_index_get(ob, lmd->target_material) : 0,
lmd->line_types,
lmd->transparency_flags,
lmd->transparency_mask,
lmd->thickness,
lmd->opacity,
lmd->pre_sample_length,
lmd->source_vertex_group,
lmd->vgname,
lmd->flags);
}
static bool isModifierDisabled(GpencilModifierData *md)
{
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
if ((lmd->target_layer[0] == '\0') || (lmd->target_material == NULL)) {
return true;
}
if (lmd->source_type == LRT_SOURCE_OBJECT && !lmd->source_object) {
return true;
}
if (lmd->source_type == LRT_SOURCE_COLLECTION && !lmd->source_collection) {
return true;
}
/* Preventing calculation in depsgraph when baking frames. */
if (lmd->flags & LRT_GPENCIL_IS_BAKED) {
return true;
}
return false;
}
static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob)
{
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
bGPdata *gpd = ob->data;
/* Guard early, don't trigger calculation when no gpencil frame is present. Probably should
* disable in the isModifierDisabled() function but we need addtional arg for depsgraph and
* gpd. */
bGPDlayer *gpl = BKE_gpencil_layer_get_by_name(gpd, lmd->target_layer, 1);
if (gpl == NULL) {
return;
}
/* Need to call this or we don't get active frame (user may haven't selected any one). */
BKE_gpencil_frame_active_set(depsgraph, gpd);
bGPDframe *gpf = gpl->actframe;
if (gpf == NULL) {
BKE_gpencil_frame_addnew(gpl, DEG_get_evaluated_scene(depsgraph)->r.cfra);
return;
}
/* Check all parameters required are filled. */
if (isModifierDisabled(md)) {
return;
}
MOD_lineart_compute_feature_lines(depsgraph, lmd);
generate_strokes_actual(md, depsgraph, ob, gpl, gpf);
MOD_lineart_destroy_render_data(lmd);
WM_main_add_notifier(NA_EDITED | NC_GPENCIL, NULL);
}
static void bakeModifier(Main *UNUSED(bmain),
Depsgraph *depsgraph,
GpencilModifierData *md,
Object *ob)
{
bGPdata *gpd = ob->data;
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
bGPDlayer *gpl = BKE_gpencil_layer_get_by_name(gpd, lmd->target_layer, 1);
if (gpl == NULL) {
return;
}
bGPDframe *gpf = gpl->actframe;
if (gpf == NULL) {
return;
}
MOD_lineart_compute_feature_lines(depsgraph, lmd);
generate_strokes_actual(md, depsgraph, ob, gpl, gpf);
MOD_lineart_destroy_render_data(lmd);
}
static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
{
return isModifierDisabled(md);
}
static void updateDepsgraph(GpencilModifierData *md,
const ModifierUpdateDepsgraphContext *ctx,
const int mode)
{
DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
if (lmd->source_type == LRT_SOURCE_OBJECT && lmd->source_object) {
DEG_add_object_relation(
ctx->node, lmd->source_object, DEG_OB_COMP_GEOMETRY, "Line Art Modifier");
DEG_add_object_relation(
ctx->node, lmd->source_object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
}
else {
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (ctx->scene->master_collection, ob, mode) {
if (ob->type == OB_MESH || ob->type == OB_MBALL || ob->type == OB_CURVE ||
ob->type == OB_SURF || ob->type == OB_FONT) {
if (!(ob->lineart.usage & COLLECTION_LRT_EXCLUDE)) {
DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_GEOMETRY, "Line Art Modifier");
DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
}
}
}
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
}
DEG_add_object_relation(
ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
}
static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
{
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
walk(userData, ob, (ID **)&lmd->target_material, IDWALK_CB_USER);
walk(userData, ob, (ID **)&lmd->source_collection, IDWALK_CB_NOP);
walk(userData, ob, (ID **)&lmd->source_object, IDWALK_CB_NOP);
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
const int source_type = RNA_enum_get(ptr, "source_type");
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
uiLayoutSetPropSep(layout, true);
uiLayoutSetEnabled(layout, !is_baked);
uiItemR(layout, ptr, "source_type", 0, NULL, ICON_NONE);
if (source_type == LRT_SOURCE_OBJECT) {
uiItemR(layout, ptr, "source_object", 0, NULL, ICON_OBJECT_DATA);
}
else if (source_type == LRT_SOURCE_COLLECTION) {
uiItemR(layout, ptr, "source_collection", 0, NULL, ICON_OUTLINER_COLLECTION);
}
else {
/* Source is Scene. */
}
uiLayout *col = uiLayoutColumnWithHeading(layout, true, IFACE_("Edge Types"));
uiItemR(col, ptr, "use_contour", 0, "Contour", ICON_NONE);
uiItemR(col, ptr, "use_material", 0, "Material", ICON_NONE);
uiItemR(col, ptr, "use_edge_mark", 0, "Edge Marks", ICON_NONE);
uiItemR(col, ptr, "use_intersection", 0, "Intersection", ICON_NONE);
uiLayout *row = uiLayoutRow(col, true);
/* Don't automatically add the "animate property" button. */
uiLayoutSetPropDecorate(row, false);
const bool use_crease = RNA_boolean_get(ptr, "use_crease");
uiLayout *sub = uiLayoutRow(row, true);
uiItemR(sub, ptr, "use_crease", 0, "Crease", ICON_NONE);
sub = uiLayoutRow(sub, true);
uiLayoutSetEnabled(sub, use_crease);
uiItemR(sub, ptr, "crease_threshold", UI_ITEM_R_SLIDER, "", ICON_NONE);
uiItemDecoratorR(row, ptr, "crease_threshold", 0);
uiItemPointerR(layout, ptr, "target_layer", &obj_data_ptr, "layers", NULL, ICON_GREASEPENCIL);
uiItemPointerR(
layout, ptr, "target_material", &obj_data_ptr, "materials", NULL, ICON_SHADING_TEXTURE);
uiItemR(layout, ptr, "remove_doubles", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "allow_overlapping_edges", 0, "Overlapping Edges As Contour", ICON_NONE);
gpencil_modifier_panel_end(layout, ptr);
}
static void style_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
uiLayout *layout = panel->layout;
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
uiLayoutSetPropSep(layout, true);
uiLayoutSetEnabled(layout, !is_baked);
uiItemR(layout, ptr, "thickness", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
uiItemR(layout, ptr, "opacity", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
}
static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
uiLayout *layout = panel->layout;
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
uiLayoutSetPropSep(layout, true);
uiLayoutSetEnabled(layout, !is_baked);
const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels");
const bool use_transparency = RNA_boolean_get(ptr, "use_transparency");
uiItemR(layout, ptr, "use_multiple_levels", 0, "Multiple Levels", ICON_NONE);
if (use_multiple_levels) {
uiLayout *col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "level_start", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "level_end", 0, NULL, ICON_NONE);
}
else {
uiItemR(layout, ptr, "level_start", 0, "Level", ICON_NONE);
}
uiItemR(layout, ptr, "use_transparency", 0, "Transparency", ICON_NONE);
uiLayout *col = uiLayoutColumn(layout, true);
if (use_transparency) {
uiItemR(col, ptr, "transparency_match", 0, "Match", ICON_NONE);
}
if (use_transparency) {
uiLayout *row = uiLayoutRow(col, true);
uiItemR(row, ptr, "transparency_mask_0", UI_ITEM_R_TOGGLE, "0", ICON_NONE);
uiItemR(row, ptr, "transparency_mask_1", UI_ITEM_R_TOGGLE, "1", ICON_NONE);
uiItemR(row, ptr, "transparency_mask_2", UI_ITEM_R_TOGGLE, "2", ICON_NONE);
uiItemR(row, ptr, "transparency_mask_3", UI_ITEM_R_TOGGLE, "3", ICON_NONE);
uiItemR(row, ptr, "transparency_mask_4", UI_ITEM_R_TOGGLE, "4", ICON_NONE);
uiItemR(row, ptr, "transparency_mask_5", UI_ITEM_R_TOGGLE, "5", ICON_NONE);
uiItemR(row, ptr, "transparency_mask_6", UI_ITEM_R_TOGGLE, "6", ICON_NONE);
uiItemR(row, ptr, "transparency_mask_7", UI_ITEM_R_TOGGLE, "7", ICON_NONE);
}
}
static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
uiLayout *layout = panel->layout;
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
uiLayoutSetPropSep(layout, true);
uiLayoutSetEnabled(layout, !is_baked);
uiLayout *col = uiLayoutColumnWithHeading(layout, true, IFACE_("Chain"));
uiItemR(col, ptr, "fuzzy_intersections", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "fuzzy_everything", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "chaining_geometry_threshold", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "chaining_image_threshold", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "pre_sample_length", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
uiItemR(layout, ptr, "angle_splitting_threshold", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
}
static void vgroup_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
PointerRNA ob_ptr;
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
uiLayout *layout = panel->layout;
bool is_baked = RNA_boolean_get(ptr, "is_baked");
uiLayoutSetPropSep(layout, true);
uiLayoutSetEnabled(layout, !is_baked);
uiLayout *col = uiLayoutColumn(layout, true);
uiLayout *row = uiLayoutRow(col, true);
uiItemR(row, ptr, "source_vertex_group", 0, "Filter Source", ICON_GROUP_VERTEX);
uiItemR(row, ptr, "invert_source_vertex_group", UI_ITEM_R_TOGGLE, "", ICON_ARROW_LEFTRIGHT);
uiItemR(col, ptr, "match_output_vertex_group", 0, NULL, ICON_NONE);
bool match_output = RNA_boolean_get(ptr, "match_output_vertex_group");
if (!match_output) {
uiItemPointerR(col, ptr, "vertex_group", &ob_ptr, "vertex_groups", "Target", ICON_NONE);
}
uiItemR(layout, ptr, "soft_selection", 0, NULL, ICON_NONE);
}
static void baking_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
bool is_baked = RNA_boolean_get(ptr, "is_baked");
uiLayoutSetPropSep(layout, true);
if (is_baked) {
uiLayout *col = uiLayoutColumn(layout, false);
uiLayoutSetPropSep(col, false);
uiItemL(col, "Modifier has baked data.", ICON_NONE);
uiItemR(col, ptr, "is_baked", UI_ITEM_R_TOGGLE, "Continue Without Clearing", ICON_NONE);
}
uiLayout *col = uiLayoutColumn(layout, false);
uiLayoutSetEnabled(col, !is_baked);
uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_bake_strokes");
uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_bake_strokes_all");
col = uiLayoutColumn(layout, false);
uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_clear");
uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_clear_all");
}
static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = gpencil_modifier_panel_register(
region_type, eGpencilModifierType_Lineart, panel_draw);
gpencil_modifier_subpanel_register(
region_type, "style", "Style", NULL, style_panel_draw, panel_type);
gpencil_modifier_subpanel_register(
region_type, "occlusion", "Occlusion", NULL, occlusion_panel_draw, panel_type);
gpencil_modifier_subpanel_register(
region_type, "chaining", "Chaining", NULL, chaining_panel_draw, panel_type);
gpencil_modifier_subpanel_register(
region_type, "vgroup", "Vertex Weight Transfer", NULL, vgroup_panel_draw, panel_type);
gpencil_modifier_subpanel_register(
region_type, "baking", "Baking", NULL, baking_panel_draw, panel_type);
}
GpencilModifierTypeInfo modifierType_Gpencil_Lineart = {
/* name. */ "Line Art",
/* structName. */ "LineartGpencilModifierData",
/* structSize. */ sizeof(LineartGpencilModifierData),
/* type. */ eGpencilModifierTypeType_Gpencil,
/* flags. */ eGpencilModifierTypeFlag_SupportsEditmode,
/* copyData. */ copyData,
/* deformStroke. */ NULL,
/* generateStrokes. */ generateStrokes,
/* bakeModifier. */ bakeModifier,
/* remapTime. */ NULL,
/* initData. */ initData,
/* freeData. */ NULL,
/* isDisabled. */ isDisabled,
/* updateDepsgraph. */ updateDepsgraph,
/* dependsOnTime. */ NULL,
/* foreachIDLink. */ foreachIDLink,
/* foreachTexLink. */ NULL,
/* panelRegister. */ panelRegister,
};

View File

@ -207,7 +207,9 @@ static bool isDisabled(GpencilModifierData *UNUSED(md), int UNUSED(userRenderPar
return false;
}
static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
static void updateDepsgraph(GpencilModifierData *md,
const ModifierUpdateDepsgraphContext *ctx,
const int UNUSED(mode))
{
MirrorGpencilModifierData *lmd = (MirrorGpencilModifierData *)md;
if (lmd->object != NULL) {

View File

@ -311,7 +311,9 @@ static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
return !mmd->object;
}
static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
static void updateDepsgraph(GpencilModifierData *md,
const ModifierUpdateDepsgraphContext *ctx,
const int UNUSED(mode))
{
TintGpencilModifierData *lmd = (TintGpencilModifierData *)md;
if (lmd->object != NULL) {

View File

@ -0,0 +1,552 @@
/*
* 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) 2008 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup editors
*/
#pragma once
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_threads.h"
#include "DNA_lineart_types.h"
#include <math.h>
#include <string.h>
typedef struct LineartStaticMemPoolNode {
Link item;
size_t size;
size_t used_byte;
/* User memory starts here */
} LineartStaticMemPoolNode;
typedef struct LineartStaticMemPool {
ListBase pools;
SpinLock lock_mem;
} LineartStaticMemPool;
typedef struct LineartRenderTriangleAdjacent {
struct LineartRenderLine *rl[3];
} LineartRenderTriangleAdjacent;
typedef struct LineartRenderTriangle {
struct LineartRenderVert *v[3];
/* first culled in line list to use adjacent triangle info, then go through triangle list. */
double gn[3];
/* Material flag is removed to save space. */
unsigned char transparency_mask;
unsigned char flags; /* eLineartTriangleFlags */
/* Only use single link list, because we don't need to go back in order.
* This variable is also reused to store the pointer to adjacent lines of this triangle before
* intersection statge */
struct LinkNode *intersecting_verts;
} LineartRenderTriangle;
typedef struct LineartRenderTriangleThread {
struct LineartRenderTriangle base;
/* This variable is used to store per-thread triangle-line testing pair,
* also re-used to store triangle-triangle pair for intersection testing stage.
* Do not directly use LineartRenderTriangleThread.
* The size of LineartRenderTriangle is dynamically allocated to contain set thread number of
* "testing" field. Worker threads will test lines against the "base" triangle.
* At least one thread is present, thus we always have at least testing[0]. */
struct LineartRenderLine *testing[1];
} LineartRenderTriangleThread;
typedef enum eLineArtElementNodeFlag {
LRT_ELEMENT_IS_ADDITIONAL = (1 << 0),
LRT_ELEMENT_BORDER_ONLY = (1 << 1),
LRT_ELEMENT_NO_INTERSECTION = (1 << 2),
} eLineArtElementNodeFlag;
typedef struct LineartRenderElementLinkNode {
struct LineartRenderElementLinkNode *next, *prev;
void *pointer;
int element_count;
void *object_ref;
eLineArtElementNodeFlag flags;
/* Per object value, always set, if not enabled by ObjectLineArt, then it's set to global. */
float crease_threshold;
} LineartRenderElementLinkNode;
typedef struct LineartRenderLineSegment {
struct LineartRenderLineSegment *next, *prev;
/** at==0: left at==1: right (this is in 2D projected space) */
double at;
/** Occlusion level after "at" point */
unsigned char occlusion;
/** For determining lines beind a glass window material.
* the size of this variable should also be dynamically decided, 1 byte to 8 byte,
* allows 8 to 64 materials for "transparent mask". 1 byte (8 materials) should be
* enought for most cases.
*/
unsigned char transparency_mask;
} LineartRenderLineSegment;
typedef struct LineartRenderVert {
double gloc[3];
double fbcoord[4];
/* Scene global index. */
int index;
/** Intersection data flag is here, when LRT_VERT_HAS_INTERSECTION_DATA is set,
* size of the struct is extended to include intersection data.
* See eLineArtVertFlags.
*/
char flag;
} LineartRenderVert;
typedef struct LineartRenderVertIntersection {
struct LineartRenderVert base;
/* Use vert index because we only use this to check vertex equal. This way we save 8 Bytes. */
int isec1, isec2;
struct LineartRenderTriangle *intersecting_with;
} LineartRenderVertIntersection;
typedef enum eLineArtVertFlags {
LRT_VERT_HAS_INTERSECTION_DATA = (1 << 0),
LRT_VERT_EDGE_USED = (1 << 1),
} eLineArtVertFlags;
typedef struct LineartRenderLine {
/* We only need link node kind of list here. */
struct LineartRenderLine *next;
struct LineartRenderVert *l, *r;
/* Local vertex index for two ends, not puting in RenderVert because all verts are loaded, so as
* long as fewer than half of the mesh edges are becoming a feature line, we save more memory. */
int l_obindex, r_obindex;
struct LineartRenderTriangle *tl, *tr;
ListBase segments;
char min_occ;
/** Also for line type determination on chainning */
unsigned char flags;
/** Still need this entry because culled lines will not add to object reln node,
* TODO: If really need more savings, we can allocate this in a "extended" way too, but we need
* another bit in flags to be able to show the difference.
*/
struct Object *object_ref;
} LineartRenderLine;
typedef struct LineartRenderLineChain {
struct LineartRenderLineChain *next, *prev;
ListBase chain;
/** Calculated before draw cmd. */
float length;
/** Used when re-connecting and gp stroke generation */
char picked;
char level;
/** Chain now only contains one type of segments */
int type;
unsigned char transparency_mask;
struct Object *object_ref;
} LineartRenderLineChain;
typedef struct LineartRenderLineChainItem {
struct LineartRenderLineChainItem *next, *prev;
/** Need z value for fading */
float pos[3];
/** For restoring position to 3d space */
float gpos[3];
float normal[3];
char line_type;
char occlusion;
unsigned char transparency_mask;
size_t index;
} LineartRenderLineChainItem;
typedef struct LineartChainRegisterEntry {
struct LineartChainRegisterEntry *next, *prev;
LineartRenderLineChain *rlc;
LineartRenderLineChainItem *rlci;
char picked;
/** left/right mark.
* Because we revert list in chaining so we need the flag. */
char is_left;
} LineartChainRegisterEntry;
typedef struct LineartRenderBuffer {
struct LineartRenderBuffer *prev, *next;
int thread_count;
int w, h;
int tile_size_w, tile_size_h;
int tile_count_x, tile_count_y;
double width_per_tile, height_per_tile;
double view_projection[4][4];
struct LineartBoundingArea *initial_bounding_areas;
unsigned int bounding_area_count;
ListBase vertex_buffer_pointers;
ListBase line_buffer_pointers;
ListBase triangle_buffer_pointers;
/* This one's memory is not from main pool and is free()ed after culling stage. */
ListBase triangle_adjacent_pointers;
ListBase intersecting_vertex_buffer;
/** Use the one comes with Line Art. */
LineartStaticMemPool render_data_pool;
ListBase wasted_cuts;
SpinLock lock_cuts;
/* Render status */
double view_vector[3];
int triangle_size;
unsigned int contour_count;
unsigned int contour_processed;
LineartRenderLine *contour_managed;
/* Now changed to linknodes. */
LineartRenderLine *contours;
unsigned int intersection_count;
unsigned int intersection_processed;
LineartRenderLine *intersection_managed;
LineartRenderLine *intersection_lines;
unsigned int crease_count;
unsigned int crease_processed;
LineartRenderLine *crease_managed;
LineartRenderLine *crease_lines;
unsigned int material_line_count;
unsigned int material_processed;
LineartRenderLine *material_managed;
LineartRenderLine *material_lines;
unsigned int edge_mark_count;
unsigned int edge_mark_processed;
LineartRenderLine *edge_mark_managed;
LineartRenderLine *edge_marks;
ListBase chains;
/** For managing calculation tasks for multiple threads. */
SpinLock lock_task;
/* settings */
int max_occlusion_level;
double crease_angle;
double crease_cos;
int draw_material_preview;
double material_transparency;
bool use_contour;
bool use_crease;
bool use_material;
bool use_edge_marks;
bool use_intersections;
bool fuzzy_intersections;
bool fuzzy_everything;
bool allow_boundaries;
bool allow_overlapping_edges;
bool remove_doubles;
/** Keep an copy of these data so when line art is running it's self-contained. */
bool cam_is_persp;
float cam_obmat[4][4];
double camera_pos[3];
double near_clip, far_clip;
float shift_x, shift_y;
float crease_threshold;
float chaining_image_threshold;
float chaining_geometry_threshold;
float angle_splitting_threshold;
} LineartRenderBuffer;
#define DBL_TRIANGLE_LIM 1e-8
#define DBL_EDGE_LIM 1e-9
#define LRT_MEMORY_POOL_64MB (1 << 26)
typedef enum eLineartTriangleFlags {
LRT_CULL_DONT_CARE = 0,
LRT_CULL_USED = (1 << 0),
LRT_CULL_DISCARD = (1 << 1),
LRT_CULL_GENERATED = (1 << 2),
LRT_TRIANGLE_INTERSECTION_ONLY = (1 << 3),
LRT_TRIANGLE_NO_INTERSECTION = (1 << 4),
} eLineartTriangleFlags;
/** Controls how many lines a worker thread is processing at one request.
* There's no significant performance impact on choosing different values.
* Don't make it too small so that the worker thread won't request too many times. */
#define LRT_THREAD_LINE_COUNT 1000
typedef struct LineartRenderTaskInfo {
struct LineartRenderBuffer *rb;
int thread_id;
LineartRenderLine *contour;
LineartRenderLine *contour_end;
LineartRenderLine *intersection;
LineartRenderLine *intersection_end;
LineartRenderLine *crease;
LineartRenderLine *crease_end;
LineartRenderLine *material;
LineartRenderLine *material_end;
LineartRenderLine *edge_mark;
LineartRenderLine *edge_mark_end;
} LineartRenderTaskInfo;
/** Bounding area diagram:
*
* +----+ <----U (Upper edge Y value)
* | |
* +----+ <----B (Bottom edge Y value)
* ^ ^
* L R (Left/Right edge X value)
*
* Example structure when subdividing 1 bounding areas:
* 1 area can be divided into 4 smaller children to
* accomodate image areas with denser triangle distribution.
* +--+--+-----+
* +--+--+ |
* +--+--+-----+
* | | |
* +-----+-----+
* lp/rp/up/bp is the list for
* storing pointers to adjacent bounding areas.
*/
typedef struct LineartBoundingArea {
double l, r, u, b;
double cx, cy;
/** 1,2,3,4 quadrant */
struct LineartBoundingArea *child;
ListBase lp;
ListBase rp;
ListBase up;
ListBase bp;
short triangle_count;
ListBase linked_triangles;
ListBase linked_lines;
/** Reserved for image space reduction && multithread chainning */
ListBase linked_chains;
} LineartBoundingArea;
#define LRT_TILE(tile, r, c, CCount) tile[r * CCount + c]
#define LRT_CLAMP(a, Min, Max) a = a < Min ? Min : (a > Max ? Max : a)
#define LRT_MAX3_INDEX(a, b, c) (a > b ? (a > c ? 0 : (b > c ? 1 : 2)) : (b > c ? 1 : 2))
#define LRT_MIN3_INDEX(a, b, c) (a < b ? (a < c ? 0 : (b < c ? 1 : 2)) : (b < c ? 1 : 2))
#define LRT_MAX3_INDEX_ABC(x, y, z) (x > y ? (x > z ? a : (y > z ? b : c)) : (y > z ? b : c))
#define LRT_MIN3_INDEX_ABC(x, y, z) (x < y ? (x < z ? a : (y < z ? b : c)) : (y < z ? b : c))
#define LRT_ABC(index) (index == 0 ? a : (index == 1 ? b : c))
#define LRT_DOUBLE_CLOSE_ENOUGH(a, b) (((a) + DBL_EDGE_LIM) >= (b) && ((a)-DBL_EDGE_LIM) <= (b))
BLI_INLINE int lineart_LineIntersectTest2d(
const double *a1, const double *a2, const double *b1, const double *b2, double *aRatio)
{
#define USE_VECTOR_LINE_INTERSECTION
#ifdef USE_VECTOR_LINE_INTERSECTION
/* from isect_line_line_v2_point() */
double s10[2], s32[2];
double div;
sub_v2_v2v2_db(s10, a2, a1);
sub_v2_v2v2_db(s32, b2, b1);
div = cross_v2v2_db(s10, s32);
if (div != 0.0f) {
const double u = cross_v2v2_db(a2, a1);
const double v = cross_v2v2_db(b2, b1);
const double rx = ((s32[0] * u) - (s10[0] * v)) / div;
const double ry = ((s32[1] * u) - (s10[1] * v)) / div;
double rr;
if (fabs(a2[0] - a1[0]) > fabs(a2[1] - a1[1])) {
*aRatio = ratiod(a1[0], a2[0], rx);
if (fabs(b2[0] - b1[0]) > fabs(b2[1] - b1[1])) {
rr = ratiod(b1[0], b2[0], rx);
}
else {
rr = ratiod(b1[1], b2[1], ry);
}
if ((*aRatio) > 0 && (*aRatio) < 1 && rr > 0 && rr < 1) {
return 1;
}
return 0;
}
*aRatio = ratiod(a1[1], a2[1], ry);
if (fabs(b2[0] - b1[0]) > fabs(b2[1] - b1[1])) {
rr = ratiod(b1[0], b2[0], rx);
}
else {
rr = ratiod(b1[1], b2[1], ry);
}
if ((*aRatio) > 0 && (*aRatio) < 1 && rr > 0 && rr < 1) {
return 1;
}
return 0;
}
return 0;
#else
double k1, k2;
double x;
double y;
double ratio;
double x_diff = (a2[0] - a1[0]);
double x_diff2 = (b2[0] - b1[0]);
if (LRT_DOUBLE_CLOSE_ENOUGH(x_diff, 0)) {
if (LRT_DOUBLE_CLOSE_ENOUGH(x_diff2, 0)) {
*aRatio = 0;
return 0;
}
double r2 = ratiod(b1[0], b2[0], a1[0]);
x = interpd(b2[0], b1[0], r2);
y = interpd(b2[1], b1[1], r2);
*aRatio = ratio = ratiod(a1[1], a2[1], y);
}
else {
if (LRT_DOUBLE_CLOSE_ENOUGH(x_diff2, 0)) {
ratio = ratiod(a1[0], a2[0], b1[0]);
x = interpd(a2[0], a1[0], ratio);
*aRatio = ratio;
}
else {
k1 = (a2[1] - a1[1]) / x_diff;
k2 = (b2[1] - b1[1]) / x_diff2;
if ((k1 == k2))
return 0;
x = (a1[1] - b1[1] - k1 * a1[0] + k2 * b1[0]) / (k2 - k1);
ratio = (x - a1[0]) / x_diff;
*aRatio = ratio;
}
}
if (LRT_DOUBLE_CLOSE_ENOUGH(b1[0], b2[0])) {
y = interpd(a2[1], a1[1], ratio);
if (y > MAX2(b1[1], b2[1]) || y < MIN2(b1[1], b2[1]))
return 0;
}
else if (ratio <= 0 || ratio > 1 || (b1[0] > b2[0] && x > b1[0]) ||
(b1[0] < b2[0] && x < b1[0]) || (b2[0] > b1[0] && x > b2[0]) ||
(b2[0] < b1[0] && x < b2[0]))
return 0;
return 1;
#endif
}
struct Depsgraph;
struct Scene;
struct LineartRenderBuffer;
struct LineartGpencilModifierData;
void MOD_lineart_destroy_render_data(struct LineartGpencilModifierData *lmd);
void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb);
void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb);
void MOD_lineart_chain_connect(LineartRenderBuffer *rb, const bool do_geometry_space);
void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold);
void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad);
int MOD_lineart_chain_count(const LineartRenderLineChain *rlc);
void MOD_lineart_chain_clear_picked_flag(struct LineartRenderBuffer *rb);
int MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph,
struct LineartGpencilModifierData *lmd);
struct Scene;
LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartRenderBuffer *rb,
double x,
double y);
LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, double x, double y);
struct bGPDlayer;
struct bGPDframe;
struct GpencilModifierData;
void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb,
struct Depsgraph *depsgraph,
struct Object *ob,
struct bGPDlayer *gpl,
struct bGPDframe *gpf,
char source_type,
void *source_reference,
int level_start,
int level_end,
int mat_nr,
short line_types,
unsigned char transparency_flags,
unsigned char transparency_mask,
short thickness,
float opacity,
float pre_sample_length,
const char *source_vgname,
const char *vgname,
int modifier_flags);
float MOD_lineart_chain_compute_length(LineartRenderLineChain *rlc);
struct wmOperatorType;
void ED_operatortypes_lineart(void);

View File

@ -0,0 +1,980 @@
/*
* 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) 2019 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup editors
*/
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BKE_customdata.h"
#include "BKE_object.h"
#include "DEG_depsgraph_query.h"
#include "DNA_camera_types.h"
#include "DNA_lineart_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_scene_types.h"
#include "MOD_lineart.h"
#include "bmesh.h"
#include "lineart_intern.h"
#include <math.h>
#define LRT_OTHER_RV(rl, rv) ((rv) == (rl)->l ? (rl)->r : ((rv) == (rl)->r ? (rl)->l : NULL))
/* Get a connected line, only for lines who has the exact given vert, or (in the case of
* intersection lines) who has a vert that has the exact same position. */
static LineartRenderLine *lineart_line_get_connected(LineartBoundingArea *ba,
LineartRenderVert *rv,
LineartRenderVert **new_rv,
int match_flag)
{
LISTBASE_FOREACH (LinkData *, lip, &ba->linked_lines) {
LineartRenderLine *nrl = lip->data;
if ((!(nrl->flags & LRT_EDGE_FLAG_ALL_TYPE)) || (nrl->flags & LRT_EDGE_FLAG_CHAIN_PICKED)) {
continue;
}
if (match_flag && ((nrl->flags & LRT_EDGE_FLAG_ALL_TYPE) & match_flag) == 0) {
continue;
}
*new_rv = LRT_OTHER_RV(nrl, rv);
if (*new_rv) {
return nrl;
}
if (nrl->flags & LRT_EDGE_FLAG_INTERSECTION) {
if (rv->fbcoord[0] == nrl->l->fbcoord[0] && rv->fbcoord[1] == nrl->l->fbcoord[1]) {
*new_rv = LRT_OTHER_RV(nrl, nrl->l);
return nrl;
}
if (rv->fbcoord[0] == nrl->r->fbcoord[0] && rv->fbcoord[1] == nrl->r->fbcoord[1]) {
*new_rv = LRT_OTHER_RV(nrl, nrl->r);
return nrl;
}
}
}
return NULL;
}
static LineartRenderLineChain *lineart_chain_create(LineartRenderBuffer *rb)
{
LineartRenderLineChain *rlc;
rlc = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartRenderLineChain));
BLI_addtail(&rb->chains, rlc);
return rlc;
}
static bool lineart_point_overlapping(LineartRenderLineChainItem *rlci,
float x,
float y,
double threshold)
{
if (!rlci) {
return false;
}
if (((rlci->pos[0] + threshold) >= x) && ((rlci->pos[0] - threshold) <= x) &&
((rlci->pos[1] + threshold) >= y) && ((rlci->pos[1] - threshold) <= y)) {
return true;
}
return false;
}
static LineartRenderLineChainItem *lineart_chain_append_point(LineartRenderBuffer *rb,
LineartRenderLineChain *rlc,
float *fbcoord,
float *gpos,
float *normal,
char type,
int level,
unsigned char transparency_mask,
size_t index)
{
LineartRenderLineChainItem *rlci;
if (lineart_point_overlapping(rlc->chain.last, fbcoord[0], fbcoord[1], 1e-5)) {
/* Because the new chain point is overlapping, just replace the type and occlusion level of the
* current point. This makes it so that the line to the point after this one has the correct
* type and level. */
LineartRenderLineChainItem *old_rlci = rlc->chain.last;
old_rlci->line_type = type;
old_rlci->occlusion = level;
return old_rlci;
}
rlci = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartRenderLineChainItem));
copy_v2_v2(rlci->pos, fbcoord);
copy_v3_v3(rlci->gpos, gpos);
rlci->index = index;
copy_v3_v3(rlci->normal, normal);
rlci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
rlci->occlusion = level;
rlci->transparency_mask = transparency_mask;
BLI_addtail(&rlc->chain, rlci);
return rlci;
}
static LineartRenderLineChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb,
LineartRenderLineChain *rlc,
float *fbcoord,
float *gpos,
float *normal,
char type,
int level,
unsigned char transparency_mask,
size_t index)
{
LineartRenderLineChainItem *rlci;
if (lineart_point_overlapping(rlc->chain.first, fbcoord[0], fbcoord[1], 1e-5)) {
return rlc->chain.first;
}
rlci = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartRenderLineChainItem));
copy_v2_v2(rlci->pos, fbcoord);
copy_v3_v3(rlci->gpos, gpos);
rlci->index = index;
copy_v3_v3(rlci->normal, normal);
rlci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
rlci->occlusion = level;
rlci->transparency_mask = transparency_mask;
BLI_addhead(&rlc->chain, rlci);
return rlci;
}
void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
{
LineartRenderLineChain *rlc;
LineartRenderLineChainItem *rlci;
LineartBoundingArea *ba;
LineartRenderLineSegment *rls;
int last_occlusion;
unsigned char last_transparency;
/* Used when converting from double. */
float use_fbcoord[2];
float use_gpos[3];
#define VERT_COORD_TO_FLOAT(a) \
copy_v2fl_v2db(use_fbcoord, (a)->fbcoord); \
copy_v3fl_v3db(use_gpos, (a)->gloc);
#define POS_TO_FLOAT(lpos, gpos) \
copy_v2fl_v2db(use_fbcoord, lpos); \
copy_v3fl_v3db(use_gpos, gpos);
LRT_ITER_ALL_LINES_BEGIN
{
if ((!(rl->flags & LRT_EDGE_FLAG_ALL_TYPE)) || (rl->flags & LRT_EDGE_FLAG_CHAIN_PICKED)) {
LRT_ITER_ALL_LINES_NEXT
continue;
}
rl->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
rlc = lineart_chain_create(rb);
/* One chain can only have one object_ref,
* so we assign it based on the first segment we found. */
rlc->object_ref = rl->object_ref;
LineartRenderLine *new_rl = rl;
LineartRenderVert *new_rv;
float N[3] = {0};
if (rl->tl) {
N[0] += rl->tl->gn[0];
N[1] += rl->tl->gn[1];
N[2] += rl->tl->gn[2];
}
if (rl->tr) {
N[0] += rl->tr->gn[0];
N[1] += rl->tr->gn[1];
N[2] += rl->tr->gn[2];
}
if (rl->tl || rl->tr) {
normalize_v3(N);
}
/* Step 1: grow left. */
ba = MOD_lineart_get_bounding_area(rb, rl->l->fbcoord[0], rl->l->fbcoord[1]);
new_rv = rl->l;
rls = rl->segments.first;
VERT_COORD_TO_FLOAT(new_rv);
lineart_chain_prepend_point(rb,
rlc,
use_fbcoord,
use_gpos,
N,
rl->flags,
rls->occlusion,
rls->transparency_mask,
rl->l_obindex);
while (ba && (new_rl = lineart_line_get_connected(ba, new_rv, &new_rv, rl->flags))) {
new_rl->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
if (new_rl->tl || new_rl->tr) {
zero_v3(N);
if (new_rl->tl) {
N[0] += new_rl->tl->gn[0];
N[1] += new_rl->tl->gn[1];
N[2] += new_rl->tl->gn[2];
}
if (new_rl->tr) {
N[0] += new_rl->tr->gn[0];
N[1] += new_rl->tr->gn[1];
N[2] += new_rl->tr->gn[2];
}
normalize_v3(N);
}
if (new_rv == new_rl->l) {
for (rls = new_rl->segments.last; rls; rls = rls->prev) {
double gpos[3], lpos[3];
double *lfb = new_rl->l->fbcoord, *rfb = new_rl->r->fbcoord;
double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_rl->l->fbcoord, new_rl->r->fbcoord, rls->at);
interp_v3_v3v3_db(gpos, new_rl->l->gloc, new_rl->r->gloc, global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_prepend_point(rb,
rlc,
use_fbcoord,
use_gpos,
N,
new_rl->flags,
rls->occlusion,
rls->transparency_mask,
new_rl->l_obindex);
last_occlusion = rls->occlusion;
last_transparency = rls->transparency_mask;
}
}
else if (new_rv == new_rl->r) {
rls = new_rl->segments.first;
last_occlusion = rls->occlusion;
last_transparency = rls->transparency_mask;
rls = rls->next;
for (; rls; rls = rls->next) {
double gpos[3], lpos[3];
double *lfb = new_rl->l->fbcoord, *rfb = new_rl->r->fbcoord;
double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_rl->l->fbcoord, new_rl->r->fbcoord, rls->at);
interp_v3_v3v3_db(gpos, new_rl->l->gloc, new_rl->r->gloc, global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_prepend_point(rb,
rlc,
use_fbcoord,
use_gpos,
N,
new_rl->flags,
last_occlusion,
last_transparency,
new_rl->r_obindex);
last_occlusion = rls->occlusion;
last_transparency = rls->transparency_mask;
}
VERT_COORD_TO_FLOAT(new_rl->r);
lineart_chain_prepend_point(rb,
rlc,
use_fbcoord,
use_gpos,
N,
new_rl->flags,
last_occlusion,
last_transparency,
new_rl->r_obindex);
}
ba = MOD_lineart_get_bounding_area(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]);
}
/* Restore normal value. */
if (rl->tl || rl->tr) {
zero_v3(N);
if (rl->tl) {
N[0] += rl->tl->gn[0];
N[1] += rl->tl->gn[1];
N[2] += rl->tl->gn[2];
}
if (rl->tr) {
N[0] += rl->tr->gn[0];
N[1] += rl->tr->gn[1];
N[2] += rl->tr->gn[2];
}
normalize_v3(N);
}
/* Step 2: Adding all cuts from the given line, so we can continue connecting the right side
* of the line. */
rls = rl->segments.first;
last_occlusion = ((LineartRenderLineSegment *)rls)->occlusion;
last_transparency = ((LineartRenderLineSegment *)rls)->transparency_mask;
for (rls = rls->next; rls; rls = rls->next) {
double gpos[3], lpos[3];
double *lfb = rl->l->fbcoord, *rfb = rl->r->fbcoord;
double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
interp_v3_v3v3_db(lpos, rl->l->fbcoord, rl->r->fbcoord, rls->at);
interp_v3_v3v3_db(gpos, rl->l->gloc, rl->r->gloc, global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
rlc,
use_fbcoord,
use_gpos,
N,
rl->flags,
rls->occlusion,
rls->transparency_mask,
rl->l_obindex);
last_occlusion = rls->occlusion;
}
VERT_COORD_TO_FLOAT(rl->r)
lineart_chain_append_point(rb,
rlc,
use_fbcoord,
use_gpos,
N,
rl->flags,
last_occlusion,
last_transparency,
rl->r_obindex);
/* Step 3: grow right. */
ba = MOD_lineart_get_bounding_area(rb, rl->r->fbcoord[0], rl->r->fbcoord[1]);
new_rv = rl->r;
while (ba && (new_rl = lineart_line_get_connected(ba, new_rv, &new_rv, rl->flags))) {
new_rl->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
if (new_rl->tl || new_rl->tr) {
zero_v3(N);
if (new_rl->tl) {
N[0] += new_rl->tl->gn[0];
N[1] += new_rl->tl->gn[1];
N[2] += new_rl->tl->gn[2];
}
if (new_rl->tr) {
N[0] += new_rl->tr->gn[0];
N[1] += new_rl->tr->gn[1];
N[2] += new_rl->tr->gn[2];
}
normalize_v3(N);
}
/* Fix leading vertex type. */
rlci = rlc->chain.last;
rlci->line_type = new_rl->flags & LRT_EDGE_FLAG_ALL_TYPE;
if (new_rv == new_rl->l) {
rls = new_rl->segments.last;
last_occlusion = rls->occlusion;
last_transparency = rls->transparency_mask;
/* Fix leading vertex occlusion. */
rlci->occlusion = last_occlusion;
for (rls = new_rl->segments.last; rls; rls = rls->prev) {
double gpos[3], lpos[3];
double *lfb = new_rl->l->fbcoord, *rfb = new_rl->r->fbcoord;
double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_rl->l->fbcoord, new_rl->r->fbcoord, rls->at);
interp_v3_v3v3_db(gpos, new_rl->l->gloc, new_rl->r->gloc, global_at);
last_occlusion = rls->prev ? rls->prev->occlusion : last_occlusion;
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
rlc,
use_fbcoord,
use_gpos,
N,
new_rl->flags,
last_occlusion,
last_transparency,
new_rl->l_obindex);
}
}
else if (new_rv == new_rl->r) {
rls = new_rl->segments.first;
last_occlusion = rls->occlusion;
last_transparency = rls->transparency_mask;
rlci->occlusion = last_occlusion;
rls = rls->next;
for (; rls; rls = rls->next) {
double gpos[3], lpos[3];
double *lfb = new_rl->l->fbcoord, *rfb = new_rl->r->fbcoord;
double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_rl->l->fbcoord, new_rl->r->fbcoord, rls->at);
interp_v3_v3v3_db(gpos, new_rl->l->gloc, new_rl->r->gloc, global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
rlc,
use_fbcoord,
use_gpos,
N,
new_rl->flags,
rls->occlusion,
rls->transparency_mask,
new_rl->r_obindex);
last_occlusion = rls->occlusion;
last_transparency = rls->transparency_mask;
}
VERT_COORD_TO_FLOAT(new_rl->r)
lineart_chain_append_point(rb,
rlc,
use_fbcoord,
use_gpos,
N,
new_rl->flags,
last_occlusion,
last_transparency,
new_rl->r_obindex);
}
ba = MOD_lineart_get_bounding_area(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]);
}
if (rb->fuzzy_everything) {
rlc->type = LRT_EDGE_FLAG_CONTOUR;
}
else {
rlc->type = (rl->flags & LRT_EDGE_FLAG_ALL_TYPE);
}
}
LRT_ITER_ALL_LINES_END
}
static LineartBoundingArea *lineart_bounding_area_get_rlci_recursive(
LineartRenderBuffer *rb, LineartBoundingArea *root, LineartRenderLineChainItem *rlci)
{
if (root->child == NULL) {
return root;
}
LineartBoundingArea *ch = root->child;
#define IN_BOUND(ba, rlci) \
ba.l <= rlci->pos[0] && ba.r >= rlci->pos[0] && ba.b <= rlci->pos[1] && ba.u >= rlci->pos[1]
if (IN_BOUND(ch[0], rlci)) {
return lineart_bounding_area_get_rlci_recursive(rb, &ch[0], rlci);
}
if (IN_BOUND(ch[1], rlci)) {
return lineart_bounding_area_get_rlci_recursive(rb, &ch[1], rlci);
}
if (IN_BOUND(ch[2], rlci)) {
return lineart_bounding_area_get_rlci_recursive(rb, &ch[2], rlci);
}
if (IN_BOUND(ch[3], rlci)) {
return lineart_bounding_area_get_rlci_recursive(rb, &ch[3], rlci);
}
#undef IN_BOUND
return NULL;
}
static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartRenderBuffer *rb,
LineartRenderLineChainItem *rlci)
{
if (!rlci) {
return NULL;
}
LineartBoundingArea *root = MOD_lineart_get_parent_bounding_area(rb, rlci->pos[0], rlci->pos[1]);
if (root == NULL) {
return NULL;
}
return lineart_bounding_area_get_rlci_recursive(rb, root, rlci);
}
/* Here we will try to connect geometry space chains together in image space. However we can't
* chain two chains together if their end and start points lie on the border between two bounding
* areas, this happens either when 1) the geometry is way too dense, or 2) the chaining threshold
* is too big that it covers multiple small bounding areas. */
static void lineart_bounding_area_link_point_recursive(LineartRenderBuffer *rb,
LineartBoundingArea *root,
LineartRenderLineChain *rlc,
LineartRenderLineChainItem *rlci)
{
if (root->child == NULL) {
LineartChainRegisterEntry *cre = lineart_list_append_pointer_pool_sized(
&root->linked_chains, &rb->render_data_pool, rlc, sizeof(LineartChainRegisterEntry));
cre->rlci = rlci;
if (rlci == rlc->chain.first) {
cre->is_left = 1;
}
}
else {
LineartBoundingArea *ch = root->child;
#define IN_BOUND(ba, rlci) \
ba.l <= rlci->pos[0] && ba.r >= rlci->pos[0] && ba.b <= rlci->pos[1] && ba.u >= rlci->pos[1]
if (IN_BOUND(ch[0], rlci)) {
lineart_bounding_area_link_point_recursive(rb, &ch[0], rlc, rlci);
}
else if (IN_BOUND(ch[1], rlci)) {
lineart_bounding_area_link_point_recursive(rb, &ch[1], rlc, rlci);
}
else if (IN_BOUND(ch[2], rlci)) {
lineart_bounding_area_link_point_recursive(rb, &ch[2], rlc, rlci);
}
else if (IN_BOUND(ch[3], rlci)) {
lineart_bounding_area_link_point_recursive(rb, &ch[3], rlc, rlci);
}
#undef IN_BOUND
}
}
static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartRenderLineChain *rlc)
{
LineartRenderLineChainItem *pl = rlc->chain.first;
LineartRenderLineChainItem *pr = rlc->chain.last;
LineartBoundingArea *ba1 = MOD_lineart_get_parent_bounding_area(rb, pl->pos[0], pl->pos[1]);
LineartBoundingArea *ba2 = MOD_lineart_get_parent_bounding_area(rb, pr->pos[0], pr->pos[1]);
if (ba1) {
lineart_bounding_area_link_point_recursive(rb, ba1, rlc, pl);
}
if (ba2) {
lineart_bounding_area_link_point_recursive(rb, ba2, rlc, pr);
}
}
void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb)
{
LineartRenderLineChain *rlc, *new_rlc;
LineartRenderLineChainItem *rlci, *next_rlci;
ListBase swap = {0};
swap.first = rb->chains.first;
swap.last = rb->chains.last;
rb->chains.last = rb->chains.first = NULL;
while ((rlc = BLI_pophead(&swap)) != NULL) {
rlc->next = rlc->prev = NULL;
BLI_addtail(&rb->chains, rlc);
LineartRenderLineChainItem *first_rlci = (LineartRenderLineChainItem *)rlc->chain.first;
int fixed_occ = first_rlci->occlusion;
unsigned char fixed_mask = first_rlci->transparency_mask;
rlc->level = fixed_occ;
rlc->transparency_mask = fixed_mask;
for (rlci = first_rlci->next; rlci; rlci = next_rlci) {
next_rlci = rlci->next;
if (rlci->occlusion != fixed_occ || rlci->transparency_mask != fixed_mask) {
if (next_rlci) {
if (lineart_point_overlapping(next_rlci, rlci->pos[0], rlci->pos[1], 1e-5)) {
continue;
}
}
else {
/* Set the same occlusion level for the end vertex, so when further connection is needed
* the backwards occlusion info is also correct. */
rlci->occlusion = fixed_occ;
/* No need to split at the last point anyway. */
break;
}
new_rlc = lineart_chain_create(rb);
new_rlc->chain.first = rlci;
new_rlc->chain.last = rlc->chain.last;
rlc->chain.last = rlci->prev;
((LineartRenderLineChainItem *)rlc->chain.last)->next = 0;
rlci->prev = 0;
/* End the previous one. */
lineart_chain_append_point(rb,
rlc,
rlci->pos,
rlci->gpos,
rlci->normal,
rlci->line_type,
fixed_occ,
fixed_mask,
rlci->index);
new_rlc->object_ref = rlc->object_ref;
new_rlc->type = rlc->type;
rlc = new_rlc;
fixed_occ = rlci->occlusion;
fixed_mask = rlci->transparency_mask;
rlc->level = fixed_occ;
rlc->transparency_mask = fixed_mask;
}
}
}
LISTBASE_FOREACH (LineartRenderLineChain *, irlc, &rb->chains) {
lineart_bounding_area_link_chain(rb, irlc);
}
}
/* Note: segment type (crease/material/contour...) is ambiguous after this. */
static void lineart_chain_connect(LineartRenderBuffer *UNUSED(rb),
LineartRenderLineChain *onto,
LineartRenderLineChain *sub,
int reverse_1,
int reverse_2)
{
LineartRenderLineChainItem *rlci;
if (onto->type == LRT_EDGE_FLAG_INTERSECTION) {
if (sub->object_ref) {
onto->object_ref = sub->object_ref;
onto->type = LRT_EDGE_FLAG_CONTOUR;
}
}
else if (sub->type == LRT_EDGE_FLAG_INTERSECTION) {
if (onto->type != LRT_EDGE_FLAG_INTERSECTION) {
onto->type = LRT_EDGE_FLAG_CONTOUR;
}
}
if (!reverse_1) { /* L--R L-R. */
if (reverse_2) { /* L--R R-L. */
BLI_listbase_reverse(&sub->chain);
}
rlci = sub->chain.first;
if (lineart_point_overlapping(onto->chain.last, rlci->pos[0], rlci->pos[1], 1e-5)) {
BLI_pophead(&sub->chain);
if (sub->chain.first == NULL) {
return;
}
}
((LineartRenderLineChainItem *)onto->chain.last)->next = sub->chain.first;
((LineartRenderLineChainItem *)sub->chain.first)->prev = onto->chain.last;
onto->chain.last = sub->chain.last;
}
else { /* L-R L--R. */
if (!reverse_2) { /* R-L L--R. */
BLI_listbase_reverse(&sub->chain);
}
rlci = onto->chain.first;
if (lineart_point_overlapping(sub->chain.last, rlci->pos[0], rlci->pos[1], 1e-5)) {
BLI_pophead(&onto->chain);
if (onto->chain.first == NULL) {
return;
}
}
((LineartRenderLineChainItem *)sub->chain.last)->next = onto->chain.first;
((LineartRenderLineChainItem *)onto->chain.first)->prev = sub->chain.last;
onto->chain.first = sub->chain.first;
}
}
static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuffer *rb,
LineartBoundingArea *ba,
LineartRenderLineChain *rlc,
LineartRenderLineChainItem *rlci,
int occlusion,
unsigned char transparency_mask,
float dist,
int do_geometry_space,
float *result_new_len,
LineartBoundingArea *caller_ba)
{
LineartChainRegisterEntry *closest_cre = NULL;
/* Keep using for loop because cre could be removed from the iteration before getting to the
* next one. */
LISTBASE_FOREACH_MUTABLE (LineartChainRegisterEntry *, cre, &ba->linked_chains) {
if (cre->rlc->object_ref != rlc->object_ref) {
if (!rb->fuzzy_everything) {
if (rb->fuzzy_intersections) {
/* If none of those are intersection lines... */
if ((!(cre->rlc->type & LRT_EDGE_FLAG_INTERSECTION)) &&
(!(rlc->type & LRT_EDGE_FLAG_INTERSECTION))) {
continue; /* We don't want to chain along different objects at the moment. */
}
}
else {
continue;
}
}
}
if (cre->rlc->picked || cre->picked) {
continue;
}
if (cre->rlc == rlc || (!cre->rlc->chain.first) || (cre->rlc->level != occlusion) ||
(cre->rlc->transparency_mask != transparency_mask)) {
continue;
}
if (!rb->fuzzy_everything) {
if (cre->rlc->type != rlc->type) {
if (rb->fuzzy_intersections) {
if (!(cre->rlc->type == LRT_EDGE_FLAG_INTERSECTION ||
rlc->type == LRT_EDGE_FLAG_INTERSECTION)) {
continue; /* Fuzzy intersetions but no intersection line found. */
}
}
else { /* Line type different but no fuzzy. */
continue;
}
}
}
float new_len = do_geometry_space ? len_v3v3(cre->rlci->gpos, rlci->gpos) :
len_v2v2(cre->rlci->pos, rlci->pos);
if (new_len < dist) {
closest_cre = cre;
dist = new_len;
if (result_new_len) {
(*result_new_len) = new_len;
}
}
}
/* We want a closer point anyway. So using modified dist is fine. */
float adjacent_new_len = dist;
LineartChainRegisterEntry *adjacent_closest;
#define LRT_TEST_ADJACENT_AREAS(dist_to, list) \
if (dist_to < dist && dist_to > 0) { \
LISTBASE_FOREACH (LinkData *, ld, list) { \
LineartBoundingArea *sba = (LineartBoundingArea *)ld->data; \
adjacent_closest = lineart_chain_get_closest_cre(rb, \
sba, \
rlc, \
rlci, \
occlusion, \
transparency_mask, \
dist, \
do_geometry_space, \
&adjacent_new_len, \
ba); \
if (adjacent_new_len < dist) { \
dist = adjacent_new_len; \
closest_cre = adjacent_closest; \
} \
} \
}
if (!do_geometry_space && !caller_ba) {
LRT_TEST_ADJACENT_AREAS(rlci->pos[0] - ba->l, &ba->lp);
LRT_TEST_ADJACENT_AREAS(ba->r - rlci->pos[0], &ba->rp);
LRT_TEST_ADJACENT_AREAS(ba->u - rlci->pos[1], &ba->up);
LRT_TEST_ADJACENT_AREAS(rlci->pos[1] - ba->b, &ba->bp);
}
if (result_new_len) {
(*result_new_len) = dist;
}
return closest_cre;
}
/* This function only connects two different chains. It will not do any clean up or smart chaining.
* So no: removing overlapping chains, removal of short isolated segments, and no loop reduction is
* implemented yet. */
void MOD_lineart_chain_connect(LineartRenderBuffer *rb, const bool do_geometry_space)
{
LineartRenderLineChain *rlc;
LineartRenderLineChainItem *rlci_l, *rlci_r;
LineartBoundingArea *ba_l, *ba_r;
LineartChainRegisterEntry *closest_cre_l, *closest_cre_r, *closest_cre;
float dist = do_geometry_space ? rb->chaining_geometry_threshold : rb->chaining_image_threshold;
float dist_l, dist_r;
int occlusion, reverse_main;
unsigned char transparency_mask;
ListBase swap = {0};
if ((!do_geometry_space && rb->chaining_image_threshold < 0.0001) ||
(do_geometry_space && rb->chaining_geometry_threshold < 0.0001)) {
return;
}
swap.first = rb->chains.first;
swap.last = rb->chains.last;
rb->chains.last = rb->chains.first = NULL;
while ((rlc = BLI_pophead(&swap)) != NULL) {
rlc->next = rlc->prev = NULL;
if (rlc->picked) {
continue;
}
BLI_addtail(&rb->chains, rlc);
occlusion = rlc->level;
transparency_mask = rlc->transparency_mask;
rlci_l = rlc->chain.first;
rlci_r = rlc->chain.last;
while ((ba_l = lineart_bounding_area_get_end_point(rb, rlci_l)) &&
(ba_r = lineart_bounding_area_get_end_point(rb, rlci_r))) {
closest_cre_l = lineart_chain_get_closest_cre(rb,
ba_l,
rlc,
rlci_l,
occlusion,
transparency_mask,
dist,
do_geometry_space,
&dist_l,
NULL);
closest_cre_r = lineart_chain_get_closest_cre(rb,
ba_r,
rlc,
rlci_r,
occlusion,
transparency_mask,
dist,
do_geometry_space,
&dist_r,
NULL);
if (closest_cre_l && closest_cre_r) {
if (dist_l < dist_r) {
closest_cre = closest_cre_l;
reverse_main = 1;
}
else {
closest_cre = closest_cre_r;
reverse_main = 0;
}
}
else if (closest_cre_l) {
closest_cre = closest_cre_l;
reverse_main = 1;
}
else if (closest_cre_r) {
closest_cre = closest_cre_r;
BLI_remlink(&ba_r->linked_chains, closest_cre_r);
reverse_main = 0;
}
else {
break;
}
closest_cre->picked = 1;
closest_cre->rlc->picked = 1;
if (closest_cre->is_left) {
lineart_chain_connect(rb, rlc, closest_cre->rlc, reverse_main, 0);
}
else {
lineart_chain_connect(rb, rlc, closest_cre->rlc, reverse_main, 1);
}
BLI_remlink(&swap, closest_cre->rlc);
rlci_l = rlc->chain.first;
rlci_r = rlc->chain.last;
}
rlc->picked = 1;
}
}
/* Length is in image space. */
float MOD_lineart_chain_compute_length(LineartRenderLineChain *rlc)
{
LineartRenderLineChainItem *rlci;
float offset_accum = 0;
float dist;
float last_point[2];
rlci = rlc->chain.first;
copy_v2_v2(last_point, rlci->pos);
for (rlci = rlc->chain.first; rlci; rlci = rlci->next) {
dist = len_v2v2(rlci->pos, last_point);
offset_accum += dist;
copy_v2_v2(last_point, rlci->pos);
}
return offset_accum;
}
void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold)
{
LineartRenderLineChain *rlc, *next_rlc;
for (rlc = rb->chains.first; rlc; rlc = next_rlc) {
next_rlc = rlc->next;
if (MOD_lineart_chain_compute_length(rlc) < threshold) {
BLI_remlink(&rb->chains, rlc);
}
}
}
int MOD_lineart_chain_count(const LineartRenderLineChain *rlc)
{
int count = 0;
LISTBASE_FOREACH (LineartRenderLineChainItem *, rlci, &rlc->chain) {
count++;
}
return count;
}
void MOD_lineart_chain_clear_picked_flag(LineartRenderBuffer *rb)
{
if (rb == NULL) {
return;
}
LISTBASE_FOREACH (LineartRenderLineChain *, rlc, &rb->chains) {
rlc->picked = 0;
}
}
/* This should always be the last stage!, see the end of
* MOD_lineart_chain_split_for_fixed_occlusion().*/
void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad)
{
LineartRenderLineChain *rlc, *new_rlc;
LineartRenderLineChainItem *rlci, *next_rlci, *prev_rlci;
ListBase swap = {0};
swap.first = rb->chains.first;
swap.last = rb->chains.last;
rb->chains.last = rb->chains.first = NULL;
while ((rlc = BLI_pophead(&swap)) != NULL) {
rlc->next = rlc->prev = NULL;
BLI_addtail(&rb->chains, rlc);
LineartRenderLineChainItem *first_rlci = (LineartRenderLineChainItem *)rlc->chain.first;
for (rlci = first_rlci->next; rlci; rlci = next_rlci) {
next_rlci = rlci->next;
prev_rlci = rlci->prev;
float angle = M_PI;
if (next_rlci && prev_rlci) {
angle = angle_v2v2v2(prev_rlci->pos, rlci->pos, next_rlci->pos);
}
else {
break; /* No need to split at the last point anyway.*/
}
if (angle < angle_threshold_rad) {
new_rlc = lineart_chain_create(rb);
new_rlc->chain.first = rlci;
new_rlc->chain.last = rlc->chain.last;
rlc->chain.last = rlci->prev;
((LineartRenderLineChainItem *)rlc->chain.last)->next = 0;
rlci->prev = 0;
/* End the previous one. */
lineart_chain_append_point(rb,
rlc,
rlci->pos,
rlci->gpos,
rlci->normal,
rlci->line_type,
rlc->level,
rlci->transparency_mask,
rlci->index);
new_rlc->object_ref = rlc->object_ref;
new_rlc->type = rlc->type;
new_rlc->level = rlc->level;
new_rlc->transparency_mask = rlc->transparency_mask;
rlc = new_rlc;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,113 @@
/*
* 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) 2019 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup editors
*/
#pragma once
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_threads.h"
#include "DNA_lineart_types.h"
#include <math.h>
#include <string.h>
struct LineartStaticMemPool;
struct LineartStaticMemPoolNode;
struct LineartRenderLine;
struct LineartRenderBuffer;
void *lineart_list_append_pointer_pool(ListBase *h, struct LineartStaticMemPool *smp, void *data);
void *lineart_list_append_pointer_pool_sized(ListBase *h,
struct LineartStaticMemPool *smp,
void *data,
int size);
void *list_push_pointer_static(ListBase *h, struct LineartStaticMemPool *smp, void *p);
void *list_push_pointer_static_sized(ListBase *h,
struct LineartStaticMemPool *smp,
void *p,
int size);
void *lineart_list_pop_pointer_no_free(ListBase *h);
void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip);
struct LineartStaticMemPoolNode *lineart_mem_new_static_pool(struct LineartStaticMemPool *smp,
size_t size);
void *lineart_mem_aquire(struct LineartStaticMemPool *smp, size_t size);
void *lineart_mem_aquire_thread(struct LineartStaticMemPool *smp, size_t size);
void lineart_mem_destroy(struct LineartStaticMemPool *smp);
void lineart_prepend_line_direct(struct LineartRenderLine **first, void *node);
void lineart_prepend_pool(LinkNode **first, struct LineartStaticMemPool *smp, void *link);
void lineart_matrix_ortho_44d(double (*mProjection)[4],
double xMin,
double xMax,
double yMin,
double yMax,
double zMin,
double zMax);
void lineart_matrix_perspective_44d(
double (*mProjection)[4], double fFov_rad, double fAspect, double zMin, double zMax);
int lineart_count_intersection_segment_count(struct LineartRenderBuffer *rb);
void lineart_count_and_print_render_buffer_memory(struct LineartRenderBuffer *rb);
#define LRT_ITER_ALL_LINES_BEGIN \
LineartRenderLine *rl, *next_rl, **current_list; \
rl = rb->contours; \
for (current_list = &rb->contours; rl; rl = next_rl) { \
next_rl = rl->next;
#define LRT_ITER_ALL_LINES_NEXT \
while (!next_rl) { \
if (current_list == &rb->contours) { \
current_list = &rb->crease_lines; \
} \
else if (current_list == &rb->crease_lines) { \
current_list = &rb->material_lines; \
} \
else if (current_list == &rb->material_lines) { \
current_list = &rb->edge_marks; \
} \
else if (current_list == &rb->edge_marks) { \
current_list = &rb->intersection_lines; \
} \
else { \
break; \
} \
next_rl = *current_list; \
}
#define LRT_ITER_ALL_LINES_END \
LRT_ITER_ALL_LINES_NEXT \
}
#define LRT_BOUND_AREA_CROSSES(b1, b2) \
((b1)[0] < (b2)[1] && (b1)[1] > (b2)[0] && (b1)[3] < (b2)[2] && (b1)[2] > (b2)[3])
/* Initial bounding area row/column count, setting 4 is the simplest way algorithm could function
* efficiently. */
#define LRT_BA_ROWS 4

View File

@ -0,0 +1,439 @@
/*
* 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) 2019 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup editors
*/
#include <stdlib.h>
#include "MEM_guardedalloc.h"
#include "BKE_collection.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "DEG_depsgraph_query.h"
#include "BLI_utildefines.h"
#include "WM_api.h"
#include "WM_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_scene_types.h"
#include "UI_resources.h"
#include "MOD_gpencil_lineart.h"
#include "MOD_lineart.h"
#include "lineart_intern.h"
static void clear_strokes(Object *ob, GpencilModifierData *md, int frame)
{
if (md->type != eGpencilModifierType_Lineart) {
return;
}
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
bGPdata *gpd = ob->data;
bGPDlayer *gpl = BKE_gpencil_layer_get_by_name(gpd, lmd->target_layer, 1);
if (!gpl) {
return;
}
bGPDframe *gpf = BKE_gpencil_layer_frame_find(gpl, frame);
if (!gpf) {
/* No greasepencil frame found. */
return;
}
BKE_gpencil_layer_frame_delete(gpl, gpf);
}
static bool bake_strokes(Object *ob, Depsgraph *dg, GpencilModifierData *md, int frame)
{
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
bGPdata *gpd = ob->data;
bGPDlayer *gpl = BKE_gpencil_layer_get_by_name(gpd, lmd->target_layer, 1);
if (!gpl) {
return false;
}
bool only_use_existing_gp_frames = false;
bGPDframe *gpf = (only_use_existing_gp_frames ?
BKE_gpencil_layer_frame_find(gpl, frame) :
BKE_gpencil_layer_frame_get(gpl, frame, GP_GETFRAME_ADD_NEW));
if (!gpf) {
/* No greasepencil frame created or found. */
return false;
}
MOD_lineart_compute_feature_lines(dg, lmd);
MOD_lineart_gpencil_generate(
lmd->render_buffer,
dg,
ob,
gpl,
gpf,
lmd->source_type,
lmd->source_type == LRT_SOURCE_OBJECT ? (void *)lmd->source_object :
(void *)lmd->source_collection,
lmd->level_start,
lmd->use_multiple_levels ? lmd->level_end : lmd->level_start,
lmd->target_material ? BKE_gpencil_object_material_index_get(ob, lmd->target_material) : 0,
lmd->line_types,
lmd->transparency_flags,
lmd->transparency_mask,
lmd->thickness,
lmd->opacity,
lmd->pre_sample_length,
lmd->source_vertex_group,
lmd->vgname,
lmd->flags);
MOD_lineart_destroy_render_data(lmd);
return true;
}
typedef struct LineartBakeJob {
wmWindowManager *wm;
void *owner;
short *stop, *do_update;
float *progress;
/* C or ob must have one != NULL. */
bContext *C;
LinkNode *objects;
Scene *scene;
Depsgraph *dg;
int frame;
int frame_begin;
int frame_end;
int frame_orig;
int frame_increment;
bool overwrite_frames;
} LineartBakeJob;
static bool lineart_gpencil_bake_single_target(LineartBakeJob *bj, Object *ob, int frame)
{
bool touched = false;
if (ob->type != OB_GPENCIL || G.is_break) {
return false;
}
if (bj->overwrite_frames) {
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
clear_strokes(ob, md, frame);
}
}
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
if (bake_strokes(ob, bj->dg, md, frame)) {
touched = true;
}
}
return touched;
}
static void lineart_gpencil_guard_modifiers(LineartBakeJob *bj)
{
for (LinkNode *l = bj->objects; l; l = l->next) {
Object *ob = l->link;
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
if (md->type == eGpencilModifierType_Lineart) {
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
lmd->flags |= LRT_GPENCIL_IS_BAKED;
}
}
}
}
static void lineart_gpencil_bake_startjob(void *customdata,
short *stop,
short *do_update,
float *progress)
{
LineartBakeJob *bj = (LineartBakeJob *)customdata;
bj->stop = stop;
bj->do_update = do_update;
bj->progress = progress;
lineart_gpencil_guard_modifiers(bj);
for (int frame = bj->frame_begin; frame <= bj->frame_end; frame += bj->frame_increment) {
if (G.is_break) {
G.is_break = false;
break;
}
BKE_scene_frame_set(bj->scene, frame);
BKE_scene_graph_update_for_newframe(bj->dg);
for (LinkNode *l = bj->objects; l; l = l->next) {
Object *ob = l->link;
if (lineart_gpencil_bake_single_target(bj, ob, frame)) {
DEG_id_tag_update((struct ID *)ob->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(bj->C, NC_GPENCIL | ND_DATA | NA_EDITED, ob);
}
}
/* Update and refresh the progress bar. */
*bj->progress = (float)(frame - bj->frame_begin) / (bj->frame_end - bj->frame_begin);
*bj->do_update = true;
}
/* This need to be reset manually. */
G.is_break = false;
/* Restore original frame. */
BKE_scene_frame_set(bj->scene, bj->frame_orig);
BKE_scene_graph_update_for_newframe(bj->dg);
}
static void lineart_gpencil_bake_endjob(void *customdata)
{
LineartBakeJob *bj = customdata;
WM_set_locked_interface(CTX_wm_manager(bj->C), false);
WM_main_add_notifier(NC_SCENE | ND_FRAME, bj->scene);
for (LinkNode *l = bj->objects; l; l = l->next) {
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, (Object *)l->link);
}
BLI_linklist_free(bj->objects, NULL);
}
static int lineart_gpencil_bake_common(bContext *C,
wmOperator *op,
bool bake_all_targets,
bool do_background)
{
LineartBakeJob *bj = MEM_callocN(sizeof(LineartBakeJob), "LineartBakeJob");
if (!bake_all_targets) {
Object *ob = CTX_data_active_object(C);
if (!ob || ob->type != OB_GPENCIL) {
WM_report(RPT_ERROR, "No active object or active object isn't a GPencil object.");
return OPERATOR_FINISHED;
}
BLI_linklist_prepend(&bj->objects, ob);
}
else {
/* CTX_DATA_BEGIN is not available for interating in objects while using the Job system. */
CTX_DATA_BEGIN (C, Object *, ob, visible_objects) {
if (ob->type == OB_GPENCIL) {
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
if (md->type == eGpencilModifierType_Lineart) {
BLI_linklist_prepend(&bj->objects, ob);
}
}
}
}
CTX_DATA_END;
}
bj->C = C;
Scene *scene = CTX_data_scene(C);
bj->scene = scene;
bj->dg = CTX_data_depsgraph_pointer(C);
bj->frame_begin = scene->r.sfra;
bj->frame_end = scene->r.efra;
bj->frame_orig = scene->r.cfra;
bj->frame_increment = scene->r.frame_step;
bj->overwrite_frames = true;
if (do_background) {
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
CTX_wm_window(C),
scene,
"Line Art",
WM_JOB_PROGRESS,
WM_JOB_TYPE_LINEART);
WM_jobs_customdata_set(wm_job, bj, MEM_freeN);
WM_jobs_timer(wm_job, 0.1, NC_GPENCIL | ND_DATA | NA_EDITED, NC_GPENCIL | ND_DATA | NA_EDITED);
WM_jobs_callbacks(
wm_job, lineart_gpencil_bake_startjob, NULL, NULL, lineart_gpencil_bake_endjob);
WM_set_locked_interface(CTX_wm_manager(C), true);
WM_jobs_start(CTX_wm_manager(C), wm_job);
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
float pseduo_progress;
short pseduo_do_update;
lineart_gpencil_bake_startjob(bj, NULL, &pseduo_do_update, &pseduo_progress);
BLI_linklist_free(bj->objects, NULL);
MEM_freeN(bj);
return OPERATOR_FINISHED;
}
static int lineart_gpencil_bake_strokes_all_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
return lineart_gpencil_bake_common(C, op, true, true);
}
static int lineart_gpencil_bake_strokes_all_exec(bContext *C, wmOperator *op)
{
return lineart_gpencil_bake_common(C, op, true, false);
}
static int lineart_gpencil_bake_strokes_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
return lineart_gpencil_bake_common(C, op, false, true);
}
static int lineart_gpencil_bake_strokes_exec(bContext *C, wmOperator *op)
{
return lineart_gpencil_bake_common(C, op, false, false);
return OPERATOR_FINISHED;
}
static int lineart_gpencil_bake_strokes_commom_modal(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
Scene *scene = (Scene *)op->customdata;
/* no running blender, remove handler and pass through. */
if (0 == WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_LINEART)) {
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
}
return OPERATOR_PASS_THROUGH;
}
static void lineart_gpencil_clear_strokes_exec_common(Object *ob)
{
if (ob->type != OB_GPENCIL) {
return;
}
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
if (md->type != eGpencilModifierType_Lineart) {
continue;
}
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
bGPdata *gpd = ob->data;
bGPDlayer *gpl = BKE_gpencil_layer_get_by_name(gpd, lmd->target_layer, 1);
if (!gpl) {
continue;
}
BKE_gpencil_free_frames(gpl);
md->mode |= eGpencilModifierMode_Realtime | eGpencilModifierMode_Render;
lmd->flags &= (~LRT_GPENCIL_IS_BAKED);
}
DEG_id_tag_update((struct ID *)ob->data, ID_RECALC_GEOMETRY);
}
static int lineart_gpencil_clear_strokes_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = CTX_data_active_object(C);
lineart_gpencil_clear_strokes_exec_common(ob);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, ob);
return OPERATOR_FINISHED;
}
static int lineart_gpencil_clear_strokes_all_exec(bContext *C, wmOperator *op)
{
CTX_DATA_BEGIN (C, Object *, ob, visible_objects) {
lineart_gpencil_clear_strokes_exec_common(ob);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, ob);
}
CTX_DATA_END;
BKE_report(op->reports, RPT_INFO, "All line art objects are now cleared.");
return OPERATOR_FINISHED;
}
/* Bake all line art modifiers on the current object. */
void OBJECT_OT_lineart_bake_strokes(wmOperatorType *ot)
{
ot->name = "Bake Line Art";
ot->description = "Bake Line Art for current GPencil object";
ot->idname = "OBJECT_OT_lineart_bake_strokes";
ot->invoke = lineart_gpencil_bake_strokes_invoke;
ot->exec = lineart_gpencil_bake_strokes_exec;
ot->modal = lineart_gpencil_bake_strokes_commom_modal;
}
/* Bake all lineart objects in the scene. */
void OBJECT_OT_lineart_bake_strokes_all(wmOperatorType *ot)
{
ot->name = "Bake Line Art (All)";
ot->description = "Bake all GPencil objects who has at least one Line Art modifier";
ot->idname = "OBJECT_OT_lineart_bake_strokes_all";
ot->invoke = lineart_gpencil_bake_strokes_all_invoke;
ot->exec = lineart_gpencil_bake_strokes_all_exec;
ot->modal = lineart_gpencil_bake_strokes_commom_modal;
}
/* clear all line art modifiers on the current object. */
void OBJECT_OT_lineart_clear(wmOperatorType *ot)
{
ot->name = "Clear Baked Line Art";
ot->description = "Clear all strokes in current GPencil obejct";
ot->idname = "OBJECT_OT_lineart_clear";
ot->exec = lineart_gpencil_clear_strokes_exec;
}
/* clear all lineart objects in the scene. */
void OBJECT_OT_lineart_clear_all(wmOperatorType *ot)
{
ot->name = "Clear Baked Line Art (All)";
ot->description = "Clear all strokes in all GPencil obejcts who has a Line Art modifier";
ot->idname = "OBJECT_OT_lineart_clear_all";
ot->exec = lineart_gpencil_clear_strokes_all_exec;
}
void WM_operatortypes_lineart(void)
{
WM_operatortype_append(OBJECT_OT_lineart_bake_strokes);
WM_operatortype_append(OBJECT_OT_lineart_bake_strokes_all);
WM_operatortype_append(OBJECT_OT_lineart_clear);
WM_operatortype_append(OBJECT_OT_lineart_clear_all);
}

View File

@ -0,0 +1,233 @@
/*
* 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) 2019 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup editors
*/
#include <stdio.h>
#include <stdlib.h>
/* #include <time.h> */
#include "MEM_guardedalloc.h"
#include "MOD_lineart.h"
#include <math.h>
#include "lineart_intern.h"
/* Line art memory and list helper */
void *lineart_list_append_pointer_pool(ListBase *h, LineartStaticMemPool *smp, void *data)
{
LinkData *lip;
if (h == NULL) {
return 0;
}
lip = lineart_mem_aquire(smp, sizeof(LinkData));
lip->data = data;
BLI_addtail(h, lip);
return lip;
}
void *lineart_list_append_pointer_pool_sized(ListBase *h,
LineartStaticMemPool *smp,
void *data,
int size)
{
LinkData *lip;
if (h == NULL) {
return 0;
}
lip = lineart_mem_aquire(smp, size);
lip->data = data;
BLI_addtail(h, lip);
return lip;
}
void *lineart_list_pop_pointer_no_free(ListBase *h)
{
LinkData *lip;
void *rev = 0;
if (h == NULL) {
return 0;
}
lip = BLI_pophead(h);
rev = lip ? lip->data : 0;
return rev;
}
void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip)
{
BLI_remlink(h, (void *)lip);
}
LineartStaticMemPoolNode *lineart_mem_new_static_pool(LineartStaticMemPool *smp, size_t size)
{
size_t set_size = size;
if (set_size < LRT_MEMORY_POOL_64MB) {
set_size = LRT_MEMORY_POOL_64MB; /* Prevent too many small allocations. */
}
size_t total_size = size + sizeof(LineartStaticMemPoolNode);
LineartStaticMemPoolNode *smpn = MEM_callocN(total_size, "mempool");
smpn->size = total_size;
smpn->used_byte = sizeof(LineartStaticMemPoolNode);
BLI_addhead(&smp->pools, smpn);
return smpn;
}
void *lineart_mem_aquire(LineartStaticMemPool *smp, size_t size)
{
LineartStaticMemPoolNode *smpn = smp->pools.first;
void *ret;
if (!smpn || (smpn->used_byte + size) > smpn->size) {
smpn = lineart_mem_new_static_pool(smp, size);
}
ret = ((unsigned char *)smpn) + smpn->used_byte;
smpn->used_byte += size;
return ret;
}
void *lineart_mem_aquire_thread(LineartStaticMemPool *smp, size_t size)
{
void *ret;
BLI_spin_lock(&smp->lock_mem);
LineartStaticMemPoolNode *smpn = smp->pools.first;
if (!smpn || (smpn->used_byte + size) > smpn->size) {
smpn = lineart_mem_new_static_pool(smp, size);
}
ret = ((unsigned char *)smpn) + smpn->used_byte;
smpn->used_byte += size;
BLI_spin_unlock(&smp->lock_mem);
return ret;
}
void lineart_mem_destroy(LineartStaticMemPool *smp)
{
LineartStaticMemPoolNode *smpn;
while ((smpn = BLI_pophead(&smp->pools)) != NULL) {
MEM_freeN(smpn);
}
}
void lineart_prepend_line_direct(LineartRenderLine **first, void *node)
{
LineartRenderLine *ln = (LineartRenderLine *)node;
ln->next = (*first);
(*first) = ln;
}
void lineart_prepend_pool(LinkNode **first, LineartStaticMemPool *smp, void *link)
{
LinkNode *ln = lineart_mem_aquire_thread(smp, sizeof(LinkNode));
ln->next = (*first);
ln->link = link;
(*first) = ln;
}
/* =======================================================================[str] */
void lineart_matrix_perspective_44d(
double (*mProjection)[4], double fFov_rad, double fAspect, double zMin, double zMax)
{
double yMax;
double yMin;
double xMin;
double xMax;
if (fAspect < 1) {
yMax = zMin * tan(fFov_rad * 0.5f);
yMin = -yMax;
xMin = yMin * fAspect;
xMax = -xMin;
}
else {
xMax = zMin * tan(fFov_rad * 0.5f);
xMin = -xMax;
yMin = xMin / fAspect;
yMax = -yMin;
}
unit_m4_db(mProjection);
mProjection[0][0] = (2.0f * zMin) / (xMax - xMin);
mProjection[1][1] = (2.0f * zMin) / (yMax - yMin);
mProjection[2][0] = (xMax + xMin) / (xMax - xMin);
mProjection[2][1] = (yMax + yMin) / (yMax - yMin);
mProjection[2][2] = -((zMax + zMin) / (zMax - zMin));
mProjection[2][3] = -1.0f;
mProjection[3][2] = -((2.0f * (zMax * zMin)) / (zMax - zMin));
mProjection[3][3] = 0.0f;
}
void lineart_matrix_ortho_44d(double (*mProjection)[4],
double xMin,
double xMax,
double yMin,
double yMax,
double zMin,
double zMax)
{
unit_m4_db(mProjection);
mProjection[0][0] = 2.0f / (xMax - xMin);
mProjection[1][1] = 2.0f / (yMax - yMin);
mProjection[2][2] = -2.0f / (zMax - zMin);
mProjection[3][0] = -((xMax + xMin) / (xMax - xMin));
mProjection[3][1] = -((yMax + yMin) / (yMax - yMin));
mProjection[3][2] = -((zMax + zMin) / (zMax - zMin));
mProjection[3][3] = 1.0f;
}
void lineart_count_and_print_render_buffer_memory(LineartRenderBuffer *rb)
{
size_t total = 0;
size_t sum_this = 0;
size_t count_this = 0;
LISTBASE_FOREACH (LineartStaticMemPoolNode *, smpn, &rb->render_data_pool.pools) {
count_this++;
sum_this += LRT_MEMORY_POOL_64MB;
}
printf("LANPR Memory allocated %lu Standalone nodes, total %lu Bytes.\n", count_this, sum_this);
total += sum_this;
sum_this = 0;
count_this = 0;
LISTBASE_FOREACH (LineartRenderElementLinkNode *, reln, &rb->line_buffer_pointers) {
count_this++;
sum_this += reln->element_count * sizeof(LineartRenderLine);
}
printf(" allocated %lu edge blocks, total %lu Bytes.\n", count_this, sum_this);
total += sum_this;
sum_this = 0;
count_this = 0;
LISTBASE_FOREACH (LineartRenderElementLinkNode *, reln, &rb->triangle_buffer_pointers) {
count_this++;
sum_this += reln->element_count * rb->triangle_size;
}
printf(" allocated %lu triangle blocks, total %lu Bytes.\n", count_this, sum_this);
total += sum_this;
sum_this = 0;
count_this = 0;
}

View File

@ -46,6 +46,14 @@ typedef struct CollectionChild {
struct Collection *collection;
} CollectionChild;
enum CollectionFeatureLine_Usage {
COLLECTION_LRT_INCLUDE = 0,
COLLECTION_LRT_OCCLUSION_ONLY = (1 << 0),
COLLECTION_LRT_EXCLUDE = (1 << 1),
COLLECTION_LRT_INTERSECTION_ONLY = (1 << 2),
COLLECTION_LRT_NO_INTERSECTION = (1 << 3),
};
typedef struct Collection {
ID id;
@ -63,8 +71,10 @@ typedef struct Collection {
/* Runtime-only, always cleared on file load. */
short tag;
/** Line Art engine specific */
short lineart_usage;
int16_t color_tag;
char _pad[2];
/* Runtime. Cache of objects in this collection and all its
* children. This is created on demand when e.g. some physics
@ -72,6 +82,9 @@ typedef struct Collection {
* collections due to memory usage reasons. */
ListBase object_cache;
/* Need this for line art sub-collection selections. */
ListBase object_cache_instanced;
/* Runtime. List of collections that are a parent of this
* datablock. */
ListBase parents;
@ -89,6 +102,7 @@ enum {
COLLECTION_RESTRICT_RENDER = (1 << 3), /* Disable in renders. */
COLLECTION_HAS_OBJECT_CACHE = (1 << 4), /* Runtime: object_cache is populated. */
COLLECTION_IS_MASTER = (1 << 5), /* Is master collection embedded in the scene. */
COLLECTION_HAS_OBJECT_CACHE_INSTANCED = (1 << 6), /* for object_cache_instanced. */
};
/* Collection->tag */

View File

@ -283,4 +283,17 @@
.colorband = NULL, \
}
#define _DNA_DEFAULT_LineartGpencilModifierData \
{ \
.line_types = LRT_EDGE_FLAG_ALL_TYPE, \
.thickness = 25, \
.opacity = 1.0f, \
.flags = LRT_GPENCIL_MATCH_OUTPUT_VGROUP | LRT_GPENCIL_SOFT_SELECTION, \
.crease_threshold = DEG2RAD(140.0f), \
.calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_REMOVE_DOUBLES | LRT_ALLOW_OVERLAPPING_EDGES | LRT_ALLOW_CLIPPING_BOUNDARIES, \
.angle_splitting_threshold = DEG2RAD(60.0f), \
.chaining_geometry_threshold = 0.001f, \
.chaining_image_threshold = 0.001f, \
}
/* clang-format off */

View File

@ -53,6 +53,7 @@ typedef enum GpencilModifierType {
eGpencilModifierType_Time = 16,
eGpencilModifierType_Multiply = 17,
eGpencilModifierType_Texture = 18,
eGpencilModifierType_Lineart = 19,
/* Keep last. */
NUM_GREASEPENCIL_MODIFIER_TYPES,
} GpencilModifierType;
@ -809,6 +810,78 @@ typedef enum eTextureGpencil_Mode {
STROKE_AND_FILL = 2,
} eTextureGpencil_Mode;
typedef enum eLineartGpencilModifierSource {
LRT_SOURCE_COLLECTION = 0,
LRT_SOURCE_OBJECT = 1,
LRT_SOURCE_SCENE = 2,
} eLineartGpencilModifierSource;
typedef enum eLineArtGPencilModifierFlags {
LRT_GPENCIL_INVERT_SOURCE_VGROUP = (1 << 0),
LRT_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 1),
LRT_GPENCIL_SOFT_SELECTION = (1 << 2),
LRT_GPENCIL_IS_BAKED = (1 << 3),
} eLineArtGPencilModifierFlags;
typedef enum eLineartGpencilTransparencyFlags {
LRT_GPENCIL_TRANSPARENCY_ENABLE = (1 << 0),
/** Set to true means using "and" instead of "or" logic on mask bits. */
LRT_GPENCIL_TRANSPARENCY_MATCH = (1 << 1),
} eLineartGpencilTransparencyFlags;
typedef struct LineartGpencilModifierData {
GpencilModifierData modifier;
short line_types; /* line type enable flags, bits in eLineartEdgeFlag */
char source_type; /* Object or Collection, from eLineartGpencilModifierSource */
char use_multiple_levels;
short level_start;
short level_end;
struct Object *source_object;
struct Collection *source_collection;
struct Material *target_material;
char target_layer[64];
/** These two variables are to pass on vertex group information from mesh to strokes.
* vgname specifies which vertex groups our strokes from source_vertex_group will go to. */
char source_vertex_group[64];
char vgname[64];
float opacity;
short thickness;
unsigned char transparency_flags; /* eLineartGpencilTransparencyFlags */
unsigned char transparency_mask;
/** 0-1 range for cosine angle */
float crease_threshold;
/** 0-PI angle, for splitting strokes at sharp points */
float angle_splitting_threshold;
/* CPU mode */
float chaining_geometry_threshold;
float chaining_image_threshold;
float pre_sample_length;
/* Ported from SceneLineArt flags. */
int calculation_flags;
/* Additional Switches. */
int flags;
int _pad;
/* Runtime only. */
void *render_buffer;
} LineartGpencilModifierData;
#ifdef __cplusplus
}
#endif

View File

@ -34,6 +34,7 @@ extern "C" {
struct AnimData;
struct Curve;
struct MDeformVert;
struct Curve;
#define GP_DEFAULT_PIX_FACTOR 1.0f
#define GP_DEFAULT_GRID_LINES 4
@ -412,6 +413,8 @@ typedef enum eGPDframe_Flag {
GP_FRAME_PAINT = (1 << 0),
/* for editing in Action Editor */
GP_FRAME_SELECT = (1 << 1),
/* Line Art generation */
GP_FRAME_LRT_CLEARED = (1 << 2),
} eGPDframe_Flag;
/* ***************************************** */

View File

@ -0,0 +1,70 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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) 2010 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __DNA_LRT_TYPES_H__
#define __DNA_LRT_TYPES_H__
/** \file DNA_lineart_types.h
* \ingroup DNA
*/
#include "DNA_ID.h"
#include "DNA_listBase.h"
struct Object;
struct Material;
/* Notice that we need to have this file although no struct defines.
* Edge flags and usage flags are used by with scene/object/gpencil modifier bits, and those values
* needs to stay consistent throughout. */
typedef enum eLineartMainFlags {
LRT_INTERSECTION_AS_CONTOUR = (1 << 0),
LRT_EVERYTHING_AS_CONTOUR = (1 << 1),
LRT_ALLOW_DUPLI_OBJECTS = (1 << 2),
LRT_ALLOW_OVERLAPPING_EDGES = (1 << 3),
LRT_ALLOW_CLIPPING_BOUNDARIES = (1 << 4),
LRT_REMOVE_DOUBLES = (1 << 5),
} eLineartMainFlags;
typedef enum eLineartEdgeFlag {
LRT_EDGE_FLAG_EDGE_MARK = (1 << 0),
LRT_EDGE_FLAG_CONTOUR = (1 << 1),
LRT_EDGE_FLAG_CREASE = (1 << 2),
LRT_EDGE_FLAG_MATERIAL = (1 << 3),
LRT_EDGE_FLAG_INTERSECTION = (1 << 4),
/** floating edge, unimplemented yet */
LRT_EDGE_FLAG_FLOATING = (1 << 5),
/** also used as discarded line mark */
LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 6),
LRT_EDGE_FLAG_CLIPPED = (1 << 7),
/* Maxed out for 8 bits, DON'T ADD ANYMORE until improvements on the data structure. */
} eLineartEdgeFlag;
#define LRT_EDGE_FLAG_ALL_TYPE 0x3f
#endif

View File

@ -145,6 +145,16 @@ typedef enum eMaterialGPencilStyle_Mode {
GP_MATERIAL_MODE_SQUARE = 2,
} eMaterialGPencilStyle_Mode;
typedef struct MaterialLineArt {
int flags; /* eMaterialLineArtFlags */
unsigned char transparency_mask;
unsigned char _pad[3];
} MaterialLineArt;
typedef enum eMaterialLineArtFlags {
LRT_MATERIAL_TRANSPARENCY_ENABLED = (1 << 0),
} eMaterialLineArtFlags;
typedef struct Material {
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
@ -210,6 +220,7 @@ typedef struct Material {
/** Grease pencil color. */
struct MaterialGPencilStyle *gp_style;
struct MaterialLineArt lineart;
} Material;
/* **************** MATERIAL ********************* */

View File

@ -66,6 +66,7 @@
.preview = NULL, \
.duplicator_visibility_flag = OB_DUPLI_FLAG_VIEWPORT | OB_DUPLI_FLAG_RENDER, \
.pc_ids = {NULL, NULL}, \
.lineart = { .crease_threshold = DEG2RAD(140.0f) }, \
}
/** \} */

View File

@ -26,6 +26,11 @@
#include "DNA_object_enums.h"
#include "DNA_customdata_types.h"
#include "DNA_defs.h"
#include "DNA_lineart_types.h"
#include "DNA_listBase.h"
#include "DNA_ID.h"
#include "DNA_action_types.h" /* bAnimVizSettings */
#include "DNA_customdata_types.h"
@ -200,6 +205,27 @@ typedef struct Object_Runtime {
short _pad2[3];
} Object_Runtime;
typedef struct ObjectLineArt {
short usage;
short flags;
/** if OBJECT_LRT_OWN_CREASE is set */
float crease_threshold;
} ObjectLineArt;
enum ObjectFeatureLine_Usage {
OBJECT_LRT_INHERENT = 0,
OBJECT_LRT_INCLUDE = (1 << 0),
OBJECT_LRT_OCCLUSION_ONLY = (1 << 1),
OBJECT_LRT_EXCLUDE = (1 << 2),
OBJECT_LRT_INTERSECTION_ONLY = (1 << 3),
OBJECT_LRT_NO_INTERSECTION = (1 << 4),
};
enum ObjectFeatureLine_Flags {
OBJECT_LRT_OWN_CREASE = (1 << 0),
};
typedef struct Object {
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
@ -405,6 +431,8 @@ typedef struct Object {
struct PreviewImage *preview;
ObjectLineArt lineart;
/** Runtime evaluation data (keep last). */
Object_Runtime runtime;
} Object;
@ -595,6 +623,9 @@ enum {
GP_EMPTY = 0,
GP_STROKE = 1,
GP_MONKEY = 2,
GP_LRT_SCENE = 3,
GP_LRT_OBJECT = 4,
GP_LRT_COLLECTION = 5,
};
/* boundtype */

View File

@ -228,6 +228,7 @@ typedef enum eSpaceButtons_Context {
BCONTEXT_TOOL = 14,
BCONTEXT_SHADERFX = 15,
BCONTEXT_OUTPUT = 16,
BCONTEXT_COLLECTION = 17,
/* Keep last. */
BCONTEXT_TOT,

View File

@ -315,6 +315,7 @@ SDNA_DEFAULT_DECL_STRUCT(TextureGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(ThickGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(TimeGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(TintGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(LineartGpencilModifierData);
#undef SDNA_DEFAULT_DECL_STRUCT
@ -539,6 +540,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(ThickGpencilModifierData),
SDNA_DEFAULT_DECL(TimeGpencilModifierData),
SDNA_DEFAULT_DECL(TintGpencilModifierData),
SDNA_DEFAULT_DECL(LineartGpencilModifierData),
};
#undef SDNA_DEFAULT_DECL
#undef SDNA_DEFAULT_DECL_EX

View File

@ -84,6 +84,7 @@ static const char *includefiles[] = {
"DNA_mesh_types.h",
"DNA_meshdata_types.h",
"DNA_modifier_types.h",
"DNA_lineart_types.h",
"DNA_lattice_types.h",
"DNA_object_types.h",
"DNA_object_force_types.h",
@ -1558,6 +1559,7 @@ int main(int argc, char **argv)
#include "DNA_layer_types.h"
#include "DNA_light_types.h"
#include "DNA_lightprobe_types.h"
#include "DNA_lineart_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_listBase.h"
#include "DNA_mask_types.h"

View File

@ -456,6 +456,7 @@ extern StructRNA RNA_NormalEditModifier;
extern StructRNA RNA_Object;
extern StructRNA RNA_ObjectBase;
extern StructRNA RNA_ObjectDisplay;
extern StructRNA RNA_ObjectLineArt;
extern StructRNA RNA_OceanModifier;
extern StructRNA RNA_OceanTexData;
extern StructRNA RNA_OffsetGpencilModifier;

View File

@ -443,6 +443,7 @@ set(LIB
bf_editor_undo
)
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_rna "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@ -22,6 +22,8 @@
#include "DNA_collection_types.h"
#include "DNA_lineart_types.h"
#include "BLI_utildefines.h"
#include "RNA_define.h"
@ -517,6 +519,31 @@ void RNA_def_collections(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Disable in Renders", "Globally disable in renders");
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_Collection_flag_update");
static const EnumPropertyItem rna_collection_lineart_usage[] = {
{COLLECTION_LRT_INCLUDE, "INCLUDE", 0, "Include", "Collection will produce feature lines"},
{COLLECTION_LRT_OCCLUSION_ONLY,
"OCCLUSION_ONLY",
0,
"Occlusion Only",
"Only use the collection to produce occlusion"},
{COLLECTION_LRT_EXCLUDE, "EXCLUDE", 0, "Exclude", "Don't use this collection in LRT"},
{COLLECTION_LRT_INTERSECTION_ONLY,
"INTERSECTION_ONLY",
0,
"Intersection Only",
"Only generate intersection lines with this collection"},
{COLLECTION_LRT_NO_INTERSECTION,
"NO_INTERSECTION",
0,
"No Intersection",
"Do not generate intersection lines for this collection"},
{0, NULL, 0, NULL, NULL}};
prop = RNA_def_property(srna, "lineart_usage", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_collection_lineart_usage);
RNA_def_property_ui_text(prop, "Usage", "How to use this collection in LRT");
RNA_def_property_update(prop, NC_SCENE, NULL);
prop = RNA_def_property(srna, "color_tag", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "color_tag");
RNA_def_property_enum_funcs(

View File

@ -88,6 +88,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_SUBSURF,
"Subdivide",
"Subdivide stroke adding more control points"},
{eGpencilModifierType_Lineart,
"GP_LINEART",
ICON_MOD_EDGESPLIT,
"Line Art",
"Generate Line Art strokes from selected source"},
{0, "", 0, N_("Deform"), ""},
{eGpencilModifierType_Armature,
"GP_ARMATURE",
@ -241,6 +246,8 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr)
return &RNA_MultiplyGpencilModifier;
case eGpencilModifierType_Texture:
return &RNA_TextureGpencilModifier;
case eGpencilModifierType_Lineart:
return &RNA_LineartGpencilModifier;
/* Default */
case eGpencilModifierType_None:
case NUM_GREASEPENCIL_MODIFIER_TYPES:
@ -311,6 +318,7 @@ RNA_GP_MOD_VGROUP_NAME_SET(Offset, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Armature, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Texture, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Tint, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Lineart, vgname);
# undef RNA_GP_MOD_VGROUP_NAME_SET
@ -2305,6 +2313,281 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
}
static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static const EnumPropertyItem modifier_lineart_source_type[] = {
{LRT_SOURCE_COLLECTION, "COLLECTION", 0, "Collection", ""},
{LRT_SOURCE_OBJECT, "OBJECT", 0, "Object", ""},
{LRT_SOURCE_SCENE, "SCENE", 0, "Scene", ""},
{0, NULL, 0, NULL, NULL},
};
srna = RNA_def_struct(brna, "LineartGpencilModifier", "GpencilModifier");
RNA_def_struct_ui_text(
srna, "Line Art Modifier", "Generate Line Art strokes from selected source");
RNA_def_struct_sdna(srna, "LineartGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT);
prop = RNA_def_property(srna, "fuzzy_intersections", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_INTERSECTION_AS_CONTOUR);
RNA_def_property_ui_text(prop,
"Intersection With Contour",
"Treat intersection and contour lines as if they were the same type so "
"they can be chained together");
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "fuzzy_everything", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_EVERYTHING_AS_CONTOUR);
RNA_def_property_ui_text(
prop, "All Lines", "Treat all lines as the same line type so they can be chained together");
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "allow_duplication", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_ALLOW_DUPLI_OBJECTS);
RNA_def_property_ui_text(
prop,
"Instanced Objects",
"Allow particle objects and face/vertiex duplication to show in line art");
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "allow_overlapping_edges", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_ALLOW_OVERLAPPING_EDGES);
RNA_def_property_ui_text(prop,
"Handle Overlapping Edges",
"Allow lines from edge split to show properly, may run slower");
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "allow_clipping_boundaries", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_ALLOW_CLIPPING_BOUNDARIES);
RNA_def_property_ui_text(
prop, "Clipping Boundaries", "Allow lines on near/far clipping plane to be shown");
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "crease_threshold", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_range(prop, 0, DEG2RAD(180.0f));
RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(180.0f), 0.01f, 1);
RNA_def_property_ui_text(
prop, "Crease Threshold", "Angles smaller than this will be treated as creases");
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "angle_splitting_threshold", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_ui_text(
prop, "Angle Splitting", "Angle in screen space below which a stroke is split in two");
/* Don't allow value very close to PI, or we get a lot of small segments.*/
RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(179.5f), 0.01f, 1);
RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f));
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "remove_doubles", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_REMOVE_DOUBLES);
RNA_def_property_ui_text(
prop, "Remove Doubles", "Remove doubles when line art is loading geometries");
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "chaining_geometry_threshold", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_ui_text(prop,
"Geometry Threshold",
"Segments where their geometric distance between them lower than this "
"will be chained together");
RNA_def_property_ui_range(prop, 0.0f, 0.5f, 0.001f, 3);
RNA_def_property_range(prop, 0.0f, 0.5f);
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "chaining_image_threshold", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_ui_text(
prop,
"Image Threshold",
"Segments with an image distance smaller than this will be chained together");
RNA_def_property_ui_range(prop, 0.0f, 0.3f, 0.001f, 4);
RNA_def_property_range(prop, 0.0f, 0.3f);
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "source_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, modifier_lineart_source_type);
RNA_def_property_ui_text(prop, "Source Type", "Lineart stroke source type");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "source_object", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_struct_type(prop, "Object");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Source Object", "Source object that this modifier grabs data from");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "source_collection", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_struct_type(prop, "Collection");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Source Collection", "Source collection that this modifier uses data from");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
/* types */
prop = RNA_def_property(srna, "use_contour", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_CONTOUR);
RNA_def_property_ui_text(prop, "Use Contour", "Include contour lines in the result");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "use_crease", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_CREASE);
RNA_def_property_ui_text(prop, "Use Crease", "Include sharp edges in the result");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "use_material", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_MATERIAL);
RNA_def_property_ui_text(
prop, "Use Material", "Include material separation lines in the result");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "use_edge_mark", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_EDGE_MARK);
RNA_def_property_ui_text(prop, "Use Edge Mark", "Include freestyle edge marks in the result");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "use_intersection", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "line_types", LRT_EDGE_FLAG_INTERSECTION);
RNA_def_property_ui_text(prop, "Use Intersection", "Include intersection lines in the result");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "use_multiple_levels", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_multiple_levels", 0);
RNA_def_property_ui_text(
prop, "Use Multiple Levels", "Select lines from multiple occlusion levels");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "level_start", PROP_INT, PROP_NONE);
RNA_def_property_ui_text(
prop, "Level Start", "Minimum level of occlusion level that gets selected");
RNA_def_property_range(prop, 0, 128);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "level_end", PROP_INT, PROP_NONE);
RNA_def_property_ui_text(
prop, "Level End", "Maximum level of occlusion level that gets selected");
RNA_def_property_range(prop, 0, 128);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "target_material", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_struct_type(prop, "Material");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Target Material", "Grease Pencil material assigned to the generated strokes");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "target_layer", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(
prop, "Target Layer", "Grease Pencil layer assigned to the generated strokes");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "source_vertex_group", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(
prop,
"Source Vertex Group",
"Matches the beginning of vertex group names from mesh objects, match all when left empty");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "vgname");
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_LineartGpencilModifier_vgname_set");
RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for selected strokes");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "invert_source_vertex_group", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_INVERT_SOURCE_VGROUP);
RNA_def_property_ui_text(prop, "Invert Source", "Invert source vertex group values");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "match_output_vertex_group", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_MATCH_OUTPUT_VGROUP);
RNA_def_property_ui_text(prop, "Match Output", "Match output vertex group");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "soft_selection", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_SOFT_SELECTION);
RNA_def_property_ui_text(
prop, "Soft selection", "Preserve original vertex weight instead of clipping to 0/1");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "is_baked", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_IS_BAKED);
RNA_def_property_ui_text(prop, "Is Baked", "This modifier has baked data");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "thickness", PROP_INT, PROP_NONE);
RNA_def_property_ui_text(prop, "Thickness", "The thickness that used to generate strokes");
RNA_def_property_ui_range(prop, 1, 100, 1, 1);
RNA_def_property_range(prop, 1, 200);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_ui_text(prop, "Opacity", "The strength value used to generate strokes");
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01f, 2);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "pre_sample_length", PROP_FLOAT, PROP_NONE);
RNA_def_property_ui_text(prop, "Pre Sample Length", "Sample strokes before sending out");
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01f, 2);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "use_transparency", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_flags", LRT_GPENCIL_TRANSPARENCY_ENABLE);
RNA_def_property_ui_text(
prop, "Use Transparency", "Use transparency mask from this material in Line Art");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "transparency_match", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_flags", LRT_GPENCIL_TRANSPARENCY_MATCH);
RNA_def_property_ui_text(prop, "Match Transparency", "Match all transparency bits");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "transparency_mask_0", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 0);
RNA_def_property_ui_text(prop, "Mask 0", "Mask bit 0");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "transparency_mask_1", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 1);
RNA_def_property_ui_text(prop, "Mask 1", "Mask bit 1");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "transparency_mask_2", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 2);
RNA_def_property_ui_text(prop, "Mask 2", "Mask bit 2");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "transparency_mask_3", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 3);
RNA_def_property_ui_text(prop, "Mask 3", "Mask bit 3");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "transparency_mask_4", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 4);
RNA_def_property_ui_text(prop, "Mask 4", "Mask bit 4");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "transparency_mask_5", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 5);
RNA_def_property_ui_text(prop, "Mask 5", "Mask bit 5");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "transparency_mask_6", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 6);
RNA_def_property_ui_text(prop, "mask 6", "Mask bit 6");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "transparency_mask_7", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 7);
RNA_def_property_ui_text(prop, "Mask 7", "Mask bit 7");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
}
void RNA_def_greasepencil_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@ -2379,6 +2662,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna)
rna_def_modifier_gpencilarmature(brna);
rna_def_modifier_gpencilmultiply(brna);
rna_def_modifier_gpenciltexture(brna);
rna_def_modifier_gpencillineart(brna);
}
#endif

View File

@ -127,6 +127,14 @@ static void rna_MaterialGpencil_update(Main *bmain, Scene *scene, PointerRNA *pt
WM_main_add_notifier(NC_GPENCIL | ND_DATA, ma);
}
static void rna_MaterialLineArt_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
Material *ma = (Material *)ptr->owner_id;
/* Need to tag geometry for line art modifier updates. */
DEG_id_tag_update(&ma->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_MATERIAL | ND_SHADING_DRAW, ma);
}
static void rna_Material_draw_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
Material *ma = (Material *)ptr->owner_id;
@ -671,6 +679,70 @@ static void rna_def_material_greasepencil(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Is Fill Visible", "True when opacity of fill is set high enough to be visible");
}
static void rna_def_material_lineart(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "MaterialLineArt", NULL);
RNA_def_struct_sdna(srna, "MaterialLineArt");
RNA_def_struct_ui_text(srna, "Material Line Art", "");
prop = RNA_def_property(srna, "use_transparency", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_MATERIAL_TRANSPARENCY_ENABLED);
RNA_def_property_ui_text(
prop, "Use Transparency", "Use transparency mask from this material in Line Art");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
prop = RNA_def_property(srna, "transparency_mask_0", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 0);
RNA_def_property_ui_text(prop, "Mask 0", "Mask bit 0");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
prop = RNA_def_property(srna, "transparency_mask_1", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 1);
RNA_def_property_ui_text(prop, "Mask 1", "Mask bit 1");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
prop = RNA_def_property(srna, "transparency_mask_2", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 2);
RNA_def_property_ui_text(prop, "Mask 2", "Mask bit 2");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
prop = RNA_def_property(srna, "transparency_mask_3", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 3);
RNA_def_property_ui_text(prop, "Mask 3", "Mask bit 3");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
prop = RNA_def_property(srna, "transparency_mask_4", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 4);
RNA_def_property_ui_text(prop, "Mask 4", "Mask bit 4");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
prop = RNA_def_property(srna, "transparency_mask_5", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 5);
RNA_def_property_ui_text(prop, "Mask 5", "Mask bit 5");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
prop = RNA_def_property(srna, "transparency_mask_6", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 6);
RNA_def_property_ui_text(prop, "mask 6", "Mask bit 6");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
prop = RNA_def_property(srna, "transparency_mask_7", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_mask", 1 << 7);
RNA_def_property_ui_text(prop, "Mask 7", "Mask bit 7");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
}
void RNA_def_material(BlenderRNA *brna)
{
@ -836,7 +908,13 @@ void RNA_def_material(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Is Grease Pencil", "True if this material has grease pencil data");
/* line art */
prop = RNA_def_property(srna, "lineart", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "lineart");
RNA_def_property_ui_text(prop, "Line Art Settings", "Line Art settings for material");
rna_def_material_greasepencil(brna);
rna_def_material_lineart(brna);
RNA_api_material(srna);
}

View File

@ -36,6 +36,7 @@
#include "DNA_shader_fx_types.h"
#include "DNA_workspace_types.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
@ -159,6 +160,21 @@ const EnumPropertyItem rna_enum_object_gpencil_type_items[] = {
{GP_EMPTY, "EMPTY", ICON_EMPTY_AXIS, "Blank", "Create an empty grease pencil object"},
{GP_STROKE, "STROKE", ICON_STROKE, "Stroke", "Create a simple stroke with basic colors"},
{GP_MONKEY, "MONKEY", ICON_MONKEY, "Monkey", "Construct a Suzanne grease pencil object"},
{GP_LRT_SCENE,
"LRT_SCENE",
ICON_SCENE_DATA,
"Scene Line Art",
"Quickly set up Line Art for the whole scene"},
{GP_LRT_COLLECTION,
"LRT_COLLECTION",
ICON_OUTLINER_COLLECTION,
"Collection Line Art",
"Quickly set up Line Art for active collection"},
{GP_LRT_OBJECT,
"LRT_OBJECT",
ICON_CUBE,
"Object Line Art",
"Quickly set up Line Art for active collection"},
{0, NULL, 0, NULL, NULL}};
static const EnumPropertyItem parent_type_items[] = {
@ -2083,6 +2099,12 @@ int rna_Object_use_dynamic_topology_sculpting_get(PointerRNA *ptr)
return (ss && ss->bm);
}
static void rna_object_lineart_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
DEG_id_tag_update(ptr->owner_id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ptr->owner_id);
}
#else
static void rna_def_vertex_group(BlenderRNA *brna)
@ -2645,6 +2667,59 @@ static void rna_def_object_display(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
static void rna_def_object_lineart(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static EnumPropertyItem prop_feature_line_usage_items[] = {
{OBJECT_LRT_INHERENT,
"INHEREIT",
0,
"Inhereit",
"Follow settings from the parent collection"},
{OBJECT_LRT_INCLUDE, "INCLUDE", 0, "Include", "Include this object into LRT calculation"},
{OBJECT_LRT_OCCLUSION_ONLY,
"OCCLUSION_ONLY",
0,
"Occlusion Only",
"Don't produce lines, only used as occlusion object"},
{OBJECT_LRT_EXCLUDE, "EXCLUDE", 0, "Exclude", "Don't use this object for LRT rendering"},
{OBJECT_LRT_INTERSECTION_ONLY,
"INTERSECTION_ONLY",
0,
"Intersection Only",
"Only to generate intersection lines with this object"},
{OBJECT_LRT_NO_INTERSECTION,
"NO_INTERSECTION",
0,
"No Intersection",
"Include this object but do not generate intersection lines"},
{0, NULL, 0, NULL, NULL},
};
srna = RNA_def_struct(brna, "ObjectLineArt", NULL);
RNA_def_struct_ui_text(srna, "Object Line Art", "Object lineart settings");
RNA_def_struct_sdna(srna, "ObjectLineArt");
prop = RNA_def_property(srna, "usage", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, prop_feature_line_usage_items);
RNA_def_property_ui_text(prop, "Usage", "How to use this object in line art calculation");
RNA_def_property_update(prop, 0, "rna_object_lineart_update");
prop = RNA_def_property(srna, "use_crease_override", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", OBJECT_LRT_OWN_CREASE);
RNA_def_property_ui_text(prop, "Own Crease", "Use own crease setting to overwrite scene global");
RNA_def_property_update(prop, 0, "rna_object_lineart_update");
prop = RNA_def_property(srna, "crease_threshold", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_range(prop, 0, DEG2RAD(180.0f));
RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(180.0f), 0.01f, 1);
RNA_def_property_ui_text(
prop, "Own Crease", "Angles smaller than this will be treated as creases");
RNA_def_property_update(prop, 0, "rna_object_lineart_update");
}
static void rna_def_object(BlenderRNA *brna)
{
StructRNA *srna;
@ -3414,6 +3489,11 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_pointer_funcs(prop, "rna_Object_display_get", NULL, NULL, NULL);
RNA_def_property_ui_text(prop, "Object Display", "Object display settings for 3D viewport");
/* Line Art */
prop = RNA_def_property(srna, "lineart", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "ObjectLineArt");
RNA_def_property_ui_text(prop, "Line Art", "Line Art settings for the object");
RNA_define_lib_overridable(false);
/* anim */
@ -3434,6 +3514,7 @@ void RNA_def_object(BlenderRNA *brna)
rna_def_face_map(brna);
rna_def_material_slot(brna);
rna_def_object_display(brna);
rna_def_object_lineart(brna);
RNA_define_animate_sdna(true);
}

View File

@ -458,6 +458,7 @@ static const EnumPropertyItem buttons_context_items[] = {
{BCONTEXT_OUTPUT, "OUTPUT", ICON_OUTPUT, "Output", "Output Properties"},
{BCONTEXT_VIEW_LAYER, "VIEW_LAYER", ICON_RENDER_RESULT, "View Layer", "View Layer Properties"},
{BCONTEXT_WORLD, "WORLD", ICON_WORLD, "World", "World Properties"},
{BCONTEXT_COLLECTION, "COLLECTION", ICON_GROUP, "Collection", "Collection Properties"},
{BCONTEXT_OBJECT, "OBJECT", ICON_OBJECT_DATA, "Object", "Object Properties"},
{BCONTEXT_CONSTRAINT,
"CONSTRAINT",

View File

@ -755,6 +755,7 @@ enum {
WM_JOB_TYPE_FSMENU_BOOKMARK_VALIDATE,
WM_JOB_TYPE_QUADRIFLOW_REMESH,
WM_JOB_TYPE_TRACE_IMAGE,
WM_JOB_TYPE_LINEART,
/* add as needed, bake, seq proxy build
* if having hard coded values is a problem */
};