GPencil 3.0: Initial commit
Adds the initial stage for the grease pencil 3.0 project.
This patch includes:
* New ID and new object type.
* New DNA structures.
* New drawing engine for grease pencil (gpencil-next).
* Tests for the new grease pencil data-type.
* A few operators for conversion, switching modes and (simple) drawing.
Exposed to the user:
* An experimental option to switch to the new grease pencil.
* This will switch the grease pencil render engine to gpencil-next which can only render the new object type.
Current grease pencil objects will no longer render.
* Changing this option currently requires a restart of blender (for the keymap to update).
* A conversion setting in the `Object` > `Convert To` operator.
* A drawing operator in `Draw Mode`.
Pull Request: https://projects.blender.org/blender/blender/pulls/106848
This commit is contained in:
@@ -382,6 +382,7 @@ def load():
|
||||
use_alt_click_leader=kc_prefs.use_alt_click_leader,
|
||||
use_pie_click_drag=kc_prefs.use_pie_click_drag,
|
||||
use_file_single_click=kc_prefs.use_file_single_click,
|
||||
experimental=prefs.experimental,
|
||||
use_transform_navigation=kc_prefs.use_transform_navigation,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -95,6 +95,8 @@ class Params:
|
||||
# Since this means with RMB select enabled in edit-mode for e.g.
|
||||
# `Ctrl-LMB` would be caught by box-select instead of add/extrude.
|
||||
"tool_maybe_tweak_event",
|
||||
# Access to bpy.context.preferences.experimental
|
||||
"experimental",
|
||||
# Changes some transformers modal key-map items to avoid conflicts with navigation operations
|
||||
"use_transform_navigation",
|
||||
)
|
||||
@@ -124,6 +126,7 @@ class Params:
|
||||
use_file_single_click=False,
|
||||
v3d_tilde_action='VIEW',
|
||||
v3d_alt_mmb_drag_action='RELATIVE',
|
||||
experimental=None,
|
||||
use_transform_navigation=False,
|
||||
):
|
||||
from sys import platform
|
||||
@@ -225,6 +228,8 @@ class Params:
|
||||
self.tool_maybe_tweak_event = {"type": self.tool_mouse, "value": self.tool_maybe_tweak_value}
|
||||
self.use_transform_navigation = use_transform_navigation
|
||||
|
||||
self.experimental = experimental
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Constants
|
||||
@@ -3830,8 +3835,18 @@ def km_grease_pencil_stroke_paint_draw_brush(params):
|
||||
{"items": items},
|
||||
)
|
||||
|
||||
items.extend([
|
||||
# Draw
|
||||
# Draw
|
||||
if params.experimental and params.experimental.use_grease_pencil_version3:
|
||||
items.extend([
|
||||
("grease_pencil.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'},
|
||||
{"properties": [("mode", 'NORMAL')]}),
|
||||
("grease_pencil.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
|
||||
{"properties": [("mode", 'INVERT')]}),
|
||||
("grease_pencil.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("mode", 'SMOOTH')]}),
|
||||
])
|
||||
else:
|
||||
items.extend([
|
||||
("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS'},
|
||||
{"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}),
|
||||
("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
@@ -3842,6 +3857,9 @@ def km_grease_pencil_stroke_paint_draw_brush(params):
|
||||
# Erase
|
||||
("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
|
||||
{"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}),
|
||||
])
|
||||
|
||||
items.extend([
|
||||
# Constrain Guides Speedlines
|
||||
# Freehand
|
||||
("gpencil.draw", {"type": 'O', "value": 'PRESS'}, None),
|
||||
|
||||
@@ -500,6 +500,8 @@ class GreasePencilMaterialsPanel:
|
||||
show_full_ui = (self.bl_space_type == 'PROPERTIES')
|
||||
|
||||
is_view3d = (self.bl_space_type == 'VIEW_3D')
|
||||
is_grease_pencil_version3 = context.preferences.experimental.use_grease_pencil_version3
|
||||
|
||||
tool_settings = context.scene.tool_settings
|
||||
gpencil_paint = tool_settings.gpencil_paint
|
||||
brush = gpencil_paint.brush if gpencil_paint else None
|
||||
@@ -550,7 +552,7 @@ class GreasePencilMaterialsPanel:
|
||||
icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA'
|
||||
row.prop(slot, "link", icon=icon_link, icon_only=True)
|
||||
|
||||
if ob.data.use_stroke_edit_mode:
|
||||
if not is_grease_pencil_version3 and ob.data.use_stroke_edit_mode:
|
||||
row = layout.row(align=True)
|
||||
row.operator("gpencil.stroke_change_color", text="Assign")
|
||||
row.operator("gpencil.material_select", text="Select").deselect = False
|
||||
|
||||
@@ -1970,6 +1970,13 @@ class _defs_gpencil_paint:
|
||||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
if context and context.preferences.experimental.use_grease_pencil_version3:
|
||||
return tuple([ToolDef.from_dict(dict(
|
||||
idname="builtin_brush.draw",
|
||||
label="Draw",
|
||||
icon="brush.gpencil_draw.draw",
|
||||
data_block='DRAW',
|
||||
))])
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
|
||||
@@ -2398,6 +2398,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
|
||||
({"property": "use_full_frame_compositor"}, ("blender/blender/issues/88150", "#88150")),
|
||||
({"property": "enable_eevee_next"}, ("blender/blender/issues/93220", "#93220")),
|
||||
({"property": "enable_workbench_next"}, ("blender/blender/issues/101619", "#101619")),
|
||||
({"property": "use_grease_pencil_version3"}, ("blender/blender/projects/40", "Grease Pencil 3.0")),
|
||||
({"property": "enable_overlay_next"}, ("blender/blender/issues/102179", "#102179")),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1981,7 +1981,7 @@ class VIEW3D_MT_paint_gpencil(Menu):
|
||||
layout.operator("gpencil.vertex_color_brightness_contrast", text="Brightness/Contrast")
|
||||
|
||||
|
||||
class VIEW3D_MT_select_gpencil(Menu):
|
||||
class VIEW3D_MT_select_edit_gpencil(Menu):
|
||||
bl_label = "Select"
|
||||
|
||||
def draw(self, context):
|
||||
@@ -5414,6 +5414,13 @@ class VIEW3D_MT_edit_gpencil_showhide(Menu):
|
||||
layout.operator("gpencil.hide", text="Hide Inactive Layers").unselected = True
|
||||
|
||||
|
||||
class VIEW3D_MT_edit_greasepencil(Menu):
|
||||
bl_label = "Grease Pencil"
|
||||
|
||||
def draw(self, _context):
|
||||
pass
|
||||
|
||||
|
||||
class VIEW3D_MT_edit_curves(Menu):
|
||||
bl_label = "Curves"
|
||||
|
||||
@@ -8163,7 +8170,7 @@ classes = (
|
||||
VIEW3D_MT_edit_lattice_context_menu,
|
||||
VIEW3D_MT_select_edit_lattice,
|
||||
VIEW3D_MT_select_edit_armature,
|
||||
VIEW3D_MT_select_gpencil,
|
||||
VIEW3D_MT_select_edit_gpencil,
|
||||
VIEW3D_MT_select_paint_mask,
|
||||
VIEW3D_MT_select_paint_mask_vertex,
|
||||
VIEW3D_MT_edit_curves_select_more_less,
|
||||
@@ -8267,6 +8274,7 @@ classes = (
|
||||
VIEW3D_MT_gpencil_simplify,
|
||||
VIEW3D_MT_gpencil_autoweights,
|
||||
VIEW3D_MT_gpencil_edit_context_menu,
|
||||
VIEW3D_MT_edit_greasepencil,
|
||||
VIEW3D_MT_edit_curve,
|
||||
VIEW3D_MT_edit_curve_ctrlpoints,
|
||||
VIEW3D_MT_edit_curve_segments,
|
||||
|
||||
@@ -32,6 +32,7 @@ set(SRC_DNA_INC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_legacy_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_modifier_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpu_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_grease_pencil_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_image_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_ipo_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_key_types.h
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
* \brief Low-level operations for grease pencil that cannot be defined in the C++ header yet.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
BKE_GREASEPENCIL_BATCH_DIRTY_ALL = 0,
|
||||
};
|
||||
|
||||
extern void (*BKE_grease_pencil_batch_cache_dirty_tag_cb)(GreasePencil *grease_pencil, int mode);
|
||||
extern void (*BKE_grease_pencil_batch_cache_free_cb)(GreasePencil *grease_pencil);
|
||||
|
||||
void BKE_grease_pencil_batch_cache_dirty_tag(GreasePencil *grease_pencil, int mode);
|
||||
void BKE_grease_pencil_batch_cache_free(GreasePencil *grease_pencil);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,381 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
* \brief Low-level operations for grease pencil.
|
||||
*/
|
||||
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_shared_cache.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
|
||||
#include "DNA_gpencil_legacy_types.h"
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
namespace greasepencil {
|
||||
|
||||
/**
|
||||
* A single point for a stroke that is currently being drawn.
|
||||
*/
|
||||
struct StrokePoint {
|
||||
float3 position;
|
||||
float radius;
|
||||
float opacity;
|
||||
float4 color;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stroke cache for a stroke that is currently being drawn.
|
||||
*/
|
||||
struct StrokeCache {
|
||||
Vector<StrokePoint> points;
|
||||
Vector<uint3> triangles;
|
||||
int mat = 0;
|
||||
|
||||
void clear()
|
||||
{
|
||||
this->points.clear_and_shrink();
|
||||
this->triangles.clear_and_shrink();
|
||||
this->mat = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class DrawingRuntime {
|
||||
public:
|
||||
/**
|
||||
* Triangle cache for all the strokes in the drawing.
|
||||
*/
|
||||
mutable SharedCache<Vector<uint3>> triangles_cache;
|
||||
|
||||
StrokeCache stroke_cache;
|
||||
};
|
||||
|
||||
class LayerGroup;
|
||||
class Layer;
|
||||
|
||||
/**
|
||||
* A TreeNode represents one node in the layer tree.
|
||||
* It can either be a layer or a group. The node has zero children if it is a layer or zero or
|
||||
more
|
||||
* children if it is a group.
|
||||
*/
|
||||
class TreeNode : public ::GreasePencilLayerTreeNode {
|
||||
public:
|
||||
TreeNode();
|
||||
explicit TreeNode(GreasePencilLayerTreeNodeType type);
|
||||
explicit TreeNode(GreasePencilLayerTreeNodeType type, StringRefNull name);
|
||||
TreeNode(const TreeNode &other);
|
||||
|
||||
public:
|
||||
/**
|
||||
* \returns true if this node is a LayerGroup.
|
||||
*/
|
||||
bool is_group() const
|
||||
{
|
||||
return this->type == GP_LAYER_TREE_GROUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* \returns true if this node is a Layer.
|
||||
*/
|
||||
bool is_layer() const
|
||||
{
|
||||
return this->type == GP_LAYER_TREE_LEAF;
|
||||
}
|
||||
|
||||
/**
|
||||
* \returns this tree node as a LayerGroup.
|
||||
* \note This results in undefined behavior if the node is not a LayerGroup.
|
||||
*/
|
||||
const LayerGroup &as_group() const;
|
||||
|
||||
/**
|
||||
* \returns this tree node as a Layer.
|
||||
* \note This results in undefined behavior if the node is not a Layer.
|
||||
*/
|
||||
const Layer &as_layer() const;
|
||||
|
||||
/**
|
||||
* \returns this tree node as a mutable LayerGroup.
|
||||
* \note This results in undefined behavior if the node is not a LayerGroup.
|
||||
*/
|
||||
LayerGroup &as_group_for_write();
|
||||
|
||||
/**
|
||||
* \returns this tree node as a mutable Layer.
|
||||
* \note This results in undefined behavior if the node is not a Layer.
|
||||
*/
|
||||
Layer &as_layer_for_write();
|
||||
};
|
||||
|
||||
/**
|
||||
* A layer mask stores a reference to a layer that will mask other layers.
|
||||
*/
|
||||
class LayerMask : public ::GreasePencilLayerMask {
|
||||
public:
|
||||
LayerMask();
|
||||
explicit LayerMask(StringRefNull name);
|
||||
LayerMask(const LayerMask &other);
|
||||
~LayerMask();
|
||||
};
|
||||
|
||||
class LayerRuntime {
|
||||
public:
|
||||
/**
|
||||
* This Map maps a scene frame number (key) to a GreasePencilFrame. This struct holds an index
|
||||
* (drawing_index) to the drawing in the GreasePencil->drawings array. The frame number indicates
|
||||
* the first frame the drawing is shown. The end time is implicitly defined by the next greater
|
||||
* frame number (key) in the map. If the value mapped to (index) is -1, no drawing is shown at
|
||||
* this frame.
|
||||
*
|
||||
* \example:
|
||||
*
|
||||
* {0: 0, 5: 1, 10: -1, 12: 2, 16: -1}
|
||||
*
|
||||
* In this example there are three drawings (drawing #0, drawing #1 and drawing #2). The first
|
||||
* drawing starts at frame 0 and ends at frame 5 (exclusive). The second drawing starts at
|
||||
* frame 5 and ends at frame 10. Finally, the third drawing starts at frame 12 and ends at
|
||||
* frame 16.
|
||||
*
|
||||
* | | | | | | | | | | |1|1|1|1|1|1|1|
|
||||
* Scene Frame: |0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|...
|
||||
* Drawing: [#0 ][#1 ] [#2 ]
|
||||
*
|
||||
* \note If a drawing references another data-block, all of the drawings in that data-block are
|
||||
* mapped sequentially to the frames (frame-by-frame). If another frame starts, the rest of the
|
||||
* referenced drawings are discarded. If the frame is longer than the number of referenced
|
||||
* drawings, then the last referenced drawing is held for the rest of the duration.
|
||||
*/
|
||||
Map<int, GreasePencilFrame> frames_;
|
||||
/**
|
||||
* Caches a sorted vector of the keys of `frames_`.
|
||||
*/
|
||||
mutable SharedCache<Vector<int>> sorted_keys_cache_;
|
||||
/**
|
||||
* A vector of LayerMask. This layer will be masked by the layers referenced in the masks.
|
||||
* A layer can have zero or more layer masks.
|
||||
*/
|
||||
Vector<LayerMask> masks_;
|
||||
};
|
||||
|
||||
/**
|
||||
* A layer maps drawings to scene frames. It can be thought of as one independent channel in the
|
||||
* timeline.
|
||||
*/
|
||||
class Layer : public ::GreasePencilLayer {
|
||||
public:
|
||||
Layer();
|
||||
explicit Layer(StringRefNull name);
|
||||
Layer(const Layer &other);
|
||||
~Layer();
|
||||
|
||||
StringRefNull name() const
|
||||
{
|
||||
return this->base.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* \returns the frames mapping.
|
||||
*/
|
||||
const Map<int, GreasePencilFrame> &frames() const;
|
||||
Map<int, GreasePencilFrame> &frames_for_write();
|
||||
|
||||
bool is_visible() const;
|
||||
bool is_locked() const;
|
||||
|
||||
/**
|
||||
* Inserts the frame into the layer. Fails if there exists a frame at \a frame_number already.
|
||||
* \returns true on success.
|
||||
*/
|
||||
bool insert_frame(int frame_number, const GreasePencilFrame &frame);
|
||||
bool insert_frame(int frame_number, GreasePencilFrame &&frame);
|
||||
|
||||
/**
|
||||
* Inserts the frame into the layer. If there exists a frame at \a frame_number already, it is
|
||||
* overwritten.
|
||||
* \returns true on success.
|
||||
*/
|
||||
bool overwrite_frame(int frame_number, const GreasePencilFrame &frame);
|
||||
bool overwrite_frame(int frame_number, GreasePencilFrame &&frame);
|
||||
|
||||
/**
|
||||
* Returns the sorted (start) frame numbers of the frames of this layer.
|
||||
* \note This will cache the keys lazily.
|
||||
*/
|
||||
Span<int> sorted_keys() const;
|
||||
|
||||
/**
|
||||
* \returns the index of the drawing at frame \a frame or -1 if there is no drawing.
|
||||
*/
|
||||
int drawing_index_at(const int frame) const;
|
||||
|
||||
void tag_frames_map_changed();
|
||||
|
||||
/**
|
||||
* Should be called whenever the keys in the frames map have changed. E.g. when new keys were
|
||||
* added, removed or updated.
|
||||
*/
|
||||
void tag_frames_map_keys_changed();
|
||||
};
|
||||
|
||||
class LayerGroupRuntime {
|
||||
public:
|
||||
/**
|
||||
* CacheMutex for `nodes_cache_` and `layer_cache_`;
|
||||
*/
|
||||
mutable CacheMutex nodes_cache_mutex_;
|
||||
/**
|
||||
* Caches all the nodes of this group in a single pre-ordered vector.
|
||||
*/
|
||||
mutable Vector<TreeNode *> nodes_cache_;
|
||||
/**
|
||||
* Caches all the layers in this group in a single pre-ordered vector.
|
||||
*/
|
||||
mutable Vector<Layer *> layer_cache_;
|
||||
};
|
||||
|
||||
/**
|
||||
* A LayerGroup is a grouping of zero or more Layers.
|
||||
*/
|
||||
class LayerGroup : public ::GreasePencilLayerTreeGroup {
|
||||
public:
|
||||
LayerGroup();
|
||||
explicit LayerGroup(StringRefNull name);
|
||||
LayerGroup(const LayerGroup &other);
|
||||
~LayerGroup();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Adds a group at the end of this group.
|
||||
*/
|
||||
LayerGroup &add_group(LayerGroup *group);
|
||||
LayerGroup &add_group(StringRefNull name);
|
||||
|
||||
/**
|
||||
* Adds a layer at the end of this group and returns it.
|
||||
*/
|
||||
Layer &add_layer(Layer *layer);
|
||||
Layer &add_layer(StringRefNull name);
|
||||
|
||||
/**
|
||||
* Returns the number of direct nodes in this group.
|
||||
*/
|
||||
int64_t num_direct_nodes() const;
|
||||
|
||||
/**
|
||||
* Returns the total number of nodes in this group.
|
||||
*/
|
||||
int64_t num_nodes_total() const;
|
||||
|
||||
/**
|
||||
* Removes a child from the group by index. Does not free the memory.
|
||||
* \note: Assumes the removed child is not the active layer.
|
||||
*/
|
||||
void remove_child(int64_t index);
|
||||
|
||||
/**
|
||||
* Returns a `Span` of pointers to all the `TreeNode`s in this group.
|
||||
*/
|
||||
Span<const TreeNode *> nodes() const;
|
||||
Span<TreeNode *> nodes_for_write();
|
||||
|
||||
/**
|
||||
* Returns a `Span` of pointers to all the `Layer`s in this group.
|
||||
*/
|
||||
Span<const Layer *> layers() const;
|
||||
Span<Layer *> layers_for_write();
|
||||
|
||||
/**
|
||||
* Returns a pointer to the layer with \a name. If no such layer was found, returns nullptr.
|
||||
*/
|
||||
const Layer *find_layer_by_name(StringRefNull name) const;
|
||||
Layer *find_layer_by_name(StringRefNull name);
|
||||
|
||||
/**
|
||||
* Print the nodes. For debugging purposes.
|
||||
*/
|
||||
void print_nodes(StringRefNull header) const;
|
||||
|
||||
private:
|
||||
void ensure_nodes_cache() const;
|
||||
void tag_nodes_cache_dirty() const;
|
||||
};
|
||||
|
||||
namespace convert {
|
||||
|
||||
void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
|
||||
GreasePencilDrawing &r_drawing);
|
||||
void legacy_gpencil_to_grease_pencil(Main &main, GreasePencil &grease_pencil, bGPdata &gpd);
|
||||
|
||||
} // namespace convert
|
||||
|
||||
} // namespace greasepencil
|
||||
|
||||
class GreasePencilRuntime {
|
||||
public:
|
||||
/**
|
||||
* Allocated and freed by the drawing code. See `DRW_grease_pencil_batch_cache_*` functions.
|
||||
*/
|
||||
void *batch_cache = nullptr;
|
||||
|
||||
public:
|
||||
GreasePencilRuntime() {}
|
||||
~GreasePencilRuntime() {}
|
||||
};
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
inline blender::bke::greasepencil::TreeNode &GreasePencilLayerTreeNode::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::bke::greasepencil::TreeNode *>(this);
|
||||
}
|
||||
inline const blender::bke::greasepencil::TreeNode &GreasePencilLayerTreeNode::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::bke::greasepencil::TreeNode *>(this);
|
||||
}
|
||||
|
||||
inline blender::bke::greasepencil::Layer &GreasePencilLayer::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::bke::greasepencil::Layer *>(this);
|
||||
}
|
||||
inline const blender::bke::greasepencil::Layer &GreasePencilLayer::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::bke::greasepencil::Layer *>(this);
|
||||
}
|
||||
|
||||
inline blender::bke::greasepencil::LayerGroup &GreasePencilLayerTreeGroup::wrap()
|
||||
{
|
||||
return *reinterpret_cast<blender::bke::greasepencil::LayerGroup *>(this);
|
||||
}
|
||||
inline const blender::bke::greasepencil::LayerGroup &GreasePencilLayerTreeGroup::wrap() const
|
||||
{
|
||||
return *reinterpret_cast<const blender::bke::greasepencil::LayerGroup *>(this);
|
||||
}
|
||||
|
||||
inline bool GreasePencil::has_active_layer() const
|
||||
{
|
||||
return (this->active_layer != nullptr);
|
||||
}
|
||||
|
||||
struct Main;
|
||||
struct Depsgraph;
|
||||
struct BoundBox;
|
||||
struct Scene;
|
||||
struct Object;
|
||||
|
||||
void *BKE_grease_pencil_add(Main *bmain, const char *name);
|
||||
GreasePencil *BKE_grease_pencil_new_nomain();
|
||||
BoundBox *BKE_grease_pencil_boundbox_get(Object *ob);
|
||||
void BKE_grease_pencil_data_update(struct Depsgraph *depsgraph,
|
||||
struct Scene *scene,
|
||||
struct Object *object);
|
||||
|
||||
bool BKE_grease_pencil_references_cyclic_check(const GreasePencil *id_reference,
|
||||
const GreasePencil *grease_pencil);
|
||||
@@ -273,6 +273,7 @@ extern IDTypeInfo IDType_ID_CV;
|
||||
extern IDTypeInfo IDType_ID_PT;
|
||||
extern IDTypeInfo IDType_ID_VO;
|
||||
extern IDTypeInfo IDType_ID_SIM;
|
||||
extern IDTypeInfo IDType_ID_GP;
|
||||
|
||||
/** Empty shell mostly, but needed for read code. */
|
||||
extern IDTypeInfo IDType_ID_LINK_PLACEHOLDER;
|
||||
|
||||
@@ -211,6 +211,7 @@ typedef struct Main {
|
||||
ListBase paintcurves;
|
||||
ListBase wm; /* Singleton (exception). */
|
||||
ListBase gpencils; /* Legacy Grease Pencil. */
|
||||
ListBase grease_pencils;
|
||||
ListBase movieclips;
|
||||
ListBase masks;
|
||||
ListBase linestyles;
|
||||
|
||||
@@ -149,6 +149,8 @@ set(SRC
|
||||
intern/gpencil_legacy.c
|
||||
intern/gpencil_modifier_legacy.c
|
||||
intern/gpencil_update_cache_legacy.c
|
||||
intern/grease_pencil_convert_legacy.cc
|
||||
intern/grease_pencil.cc
|
||||
intern/icons.cc
|
||||
intern/icons_rasterize.c
|
||||
intern/idprop.c
|
||||
@@ -393,6 +395,8 @@ set(SRC
|
||||
BKE_gpencil_legacy.h
|
||||
BKE_gpencil_modifier_legacy.h
|
||||
BKE_gpencil_update_cache_legacy.h
|
||||
BKE_grease_pencil.h
|
||||
BKE_grease_pencil.hh
|
||||
BKE_icons.h
|
||||
BKE_idprop.h
|
||||
BKE_idprop.hh
|
||||
@@ -836,6 +840,7 @@ if(WITH_GTESTS)
|
||||
intern/lib_remap_test.cc
|
||||
intern/nla_test.cc
|
||||
intern/tracking_test.cc
|
||||
intern/grease_pencil_test.cc
|
||||
)
|
||||
set(TEST_INC
|
||||
../editors/include
|
||||
|
||||
@@ -1184,6 +1184,8 @@ enum eContextObjectMode CTX_data_mode_enum_ex(const Object *obedit,
|
||||
return CTX_MODE_EDIT_LATTICE;
|
||||
case OB_CURVES:
|
||||
return CTX_MODE_EDIT_CURVES;
|
||||
case OB_GREASE_PENCIL:
|
||||
return CTX_MODE_EDIT_GPENCIL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,262 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_material.h"
|
||||
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_gpencil_legacy_types.h"
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
|
||||
namespace blender::bke::greasepencil::convert {
|
||||
|
||||
void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
|
||||
GreasePencilDrawing &r_drawing)
|
||||
{
|
||||
/* Construct an empty CurvesGeometry in-place. */
|
||||
new (&r_drawing.geometry) CurvesGeometry();
|
||||
r_drawing.base.type = GP_DRAWING;
|
||||
r_drawing.runtime = MEM_new<bke::greasepencil::DrawingRuntime>(__func__);
|
||||
|
||||
/* Get the number of points, number of strokes and the offsets for each stroke. */
|
||||
Vector<int> offsets;
|
||||
offsets.append(0);
|
||||
int num_strokes = 0;
|
||||
int num_points = 0;
|
||||
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf.strokes) {
|
||||
num_points += gps->totpoints;
|
||||
offsets.append(num_points);
|
||||
num_strokes++;
|
||||
}
|
||||
|
||||
/* Resize the CurvesGeometry. */
|
||||
CurvesGeometry &curves = r_drawing.geometry.wrap();
|
||||
curves.resize(num_points, num_strokes);
|
||||
if (num_strokes > 0) {
|
||||
curves.offsets_for_write().copy_from(offsets);
|
||||
}
|
||||
OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
|
||||
/* All strokes are poly curves. */
|
||||
curves.fill_curve_types(CURVE_TYPE_POLY);
|
||||
|
||||
/* Point Attributes. */
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
SpanAttributeWriter<float> radii = attributes.lookup_or_add_for_write_span<float>(
|
||||
"radius", ATTR_DOMAIN_POINT);
|
||||
SpanAttributeWriter<float> opacities = attributes.lookup_or_add_for_write_span<float>(
|
||||
"opacity", ATTR_DOMAIN_POINT);
|
||||
SpanAttributeWriter<float> delta_times = attributes.lookup_or_add_for_write_span<float>(
|
||||
"delta_time", ATTR_DOMAIN_POINT);
|
||||
SpanAttributeWriter<float> rotations = attributes.lookup_or_add_for_write_span<float>(
|
||||
"rotation", ATTR_DOMAIN_POINT);
|
||||
SpanAttributeWriter<ColorGeometry4f> vertex_colors =
|
||||
attributes.lookup_or_add_for_write_span<ColorGeometry4f>("vertex_color", ATTR_DOMAIN_POINT);
|
||||
SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
|
||||
".selection", ATTR_DOMAIN_POINT);
|
||||
|
||||
/* Curve Attributes. */
|
||||
SpanAttributeWriter<bool> stroke_cyclic = attributes.lookup_or_add_for_write_span<bool>(
|
||||
"cyclic", ATTR_DOMAIN_CURVE);
|
||||
/* TODO: This should be a `double` attribute. */
|
||||
SpanAttributeWriter<float> stroke_init_times = attributes.lookup_or_add_for_write_span<float>(
|
||||
"init_time", ATTR_DOMAIN_CURVE);
|
||||
SpanAttributeWriter<int8_t> stroke_start_caps = attributes.lookup_or_add_for_write_span<int8_t>(
|
||||
"start_cap", ATTR_DOMAIN_CURVE);
|
||||
SpanAttributeWriter<int8_t> stroke_end_caps = attributes.lookup_or_add_for_write_span<int8_t>(
|
||||
"end_cap", ATTR_DOMAIN_CURVE);
|
||||
SpanAttributeWriter<float> stroke_hardnesses = attributes.lookup_or_add_for_write_span<float>(
|
||||
"hardness", ATTR_DOMAIN_CURVE);
|
||||
SpanAttributeWriter<float> stroke_point_aspect_ratios =
|
||||
attributes.lookup_or_add_for_write_span<float>("point_aspect_ratio", ATTR_DOMAIN_CURVE);
|
||||
SpanAttributeWriter<float2> stroke_fill_translations =
|
||||
attributes.lookup_or_add_for_write_span<float2>("fill_translation", ATTR_DOMAIN_CURVE);
|
||||
SpanAttributeWriter<float> stroke_fill_rotations =
|
||||
attributes.lookup_or_add_for_write_span<float>("fill_rotation", ATTR_DOMAIN_CURVE);
|
||||
SpanAttributeWriter<float2> stroke_fill_scales = attributes.lookup_or_add_for_write_span<float2>(
|
||||
"fill_scale", ATTR_DOMAIN_CURVE);
|
||||
SpanAttributeWriter<ColorGeometry4f> stroke_fill_colors =
|
||||
attributes.lookup_or_add_for_write_span<ColorGeometry4f>("fill_color", ATTR_DOMAIN_CURVE);
|
||||
SpanAttributeWriter<int> stroke_materials = attributes.lookup_or_add_for_write_span<int>(
|
||||
"material_index", ATTR_DOMAIN_CURVE);
|
||||
|
||||
int stroke_i = 0;
|
||||
LISTBASE_FOREACH_INDEX (bGPDstroke *, gps, &gpf.strokes, stroke_i) {
|
||||
/* TODO: check if gps->editcurve is not nullptr and parse bezier curve instead. */
|
||||
|
||||
/* Write curve attributes. */
|
||||
stroke_cyclic.span[stroke_i] = (gps->flag & GP_STROKE_CYCLIC) != 0;
|
||||
/* TODO: This should be a `double` attribute. */
|
||||
stroke_init_times.span[stroke_i] = static_cast<float>(gps->inittime);
|
||||
stroke_start_caps.span[stroke_i] = static_cast<int8_t>(gps->caps[0]);
|
||||
stroke_end_caps.span[stroke_i] = static_cast<int8_t>(gps->caps[1]);
|
||||
stroke_hardnesses.span[stroke_i] = gps->hardeness;
|
||||
stroke_point_aspect_ratios.span[stroke_i] = gps->aspect_ratio[0] /
|
||||
max_ff(gps->aspect_ratio[1], 1e-8);
|
||||
stroke_fill_translations.span[stroke_i] = float2(gps->uv_translation);
|
||||
stroke_fill_rotations.span[stroke_i] = gps->uv_rotation;
|
||||
stroke_fill_scales.span[stroke_i] = float2(gps->uv_scale);
|
||||
stroke_fill_colors.span[stroke_i] = ColorGeometry4f(gps->vert_color_fill);
|
||||
stroke_materials.span[stroke_i] = gps->mat_nr;
|
||||
|
||||
/* Write point attributes. */
|
||||
IndexRange stroke_points_range = points_by_curve[stroke_i];
|
||||
if (stroke_points_range.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Span<bGPDspoint> stroke_points{gps->points, gps->totpoints};
|
||||
MutableSpan<float3> stroke_positions = positions.slice(stroke_points_range);
|
||||
MutableSpan<float> stroke_radii = radii.span.slice(stroke_points_range);
|
||||
MutableSpan<float> stroke_opacities = opacities.span.slice(stroke_points_range);
|
||||
MutableSpan<float> stroke_deltatimes = delta_times.span.slice(stroke_points_range);
|
||||
MutableSpan<float> stroke_rotations = rotations.span.slice(stroke_points_range);
|
||||
MutableSpan<ColorGeometry4f> stroke_vertex_colors = vertex_colors.span.slice(
|
||||
stroke_points_range);
|
||||
MutableSpan<bool> stroke_selections = selection.span.slice(stroke_points_range);
|
||||
|
||||
/* Do first point. */
|
||||
const bGPDspoint &first_pt = stroke_points.first();
|
||||
stroke_positions.first() = float3(first_pt.x, first_pt.y, first_pt.z);
|
||||
/* Store the actual radius of the stroke (without layer adjustment). */
|
||||
stroke_radii.first() = gps->thickness * first_pt.pressure;
|
||||
stroke_opacities.first() = first_pt.strength;
|
||||
stroke_deltatimes.first() = 0;
|
||||
stroke_rotations.first() = first_pt.uv_rot;
|
||||
stroke_vertex_colors.first() = ColorGeometry4f(first_pt.vert_color);
|
||||
stroke_selections.first() = (first_pt.flag & GP_SPOINT_SELECT) != 0;
|
||||
|
||||
/* Do the rest of the points. */
|
||||
for (const int i : stroke_points.index_range().drop_back(1)) {
|
||||
const int point_i = i + 1;
|
||||
const bGPDspoint &pt_prev = stroke_points[point_i - 1];
|
||||
const bGPDspoint &pt = stroke_points[point_i];
|
||||
stroke_positions[point_i] = float3(pt.x, pt.y, pt.z);
|
||||
/* Store the actual radius of the stroke (without layer adjustment). */
|
||||
stroke_radii[point_i] = gps->thickness * pt.pressure;
|
||||
stroke_opacities[point_i] = pt.strength;
|
||||
stroke_deltatimes[point_i] = pt.time - pt_prev.time;
|
||||
stroke_rotations[point_i] = pt.uv_rot;
|
||||
stroke_vertex_colors[point_i] = ColorGeometry4f(pt.vert_color);
|
||||
stroke_selections[point_i] = (pt.flag & GP_SPOINT_SELECT) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
radii.finish();
|
||||
opacities.finish();
|
||||
delta_times.finish();
|
||||
rotations.finish();
|
||||
vertex_colors.finish();
|
||||
selection.finish();
|
||||
|
||||
stroke_cyclic.finish();
|
||||
stroke_init_times.finish();
|
||||
stroke_start_caps.finish();
|
||||
stroke_end_caps.finish();
|
||||
stroke_hardnesses.finish();
|
||||
stroke_point_aspect_ratios.finish();
|
||||
stroke_fill_translations.finish();
|
||||
stroke_fill_rotations.finish();
|
||||
stroke_fill_scales.finish();
|
||||
stroke_fill_colors.finish();
|
||||
stroke_materials.finish();
|
||||
}
|
||||
|
||||
void legacy_gpencil_to_grease_pencil(Main &bmain, GreasePencil &grease_pencil, bGPdata &gpd)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
||||
int num_layers = 0;
|
||||
int num_drawings = 0;
|
||||
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd.layers) {
|
||||
num_drawings += BLI_listbase_count(&gpl->frames);
|
||||
num_layers++;
|
||||
}
|
||||
|
||||
grease_pencil.drawing_array_size = num_drawings;
|
||||
grease_pencil.drawing_array = reinterpret_cast<GreasePencilDrawingBase **>(
|
||||
MEM_cnew_array<GreasePencilDrawing *>(num_drawings, __func__));
|
||||
|
||||
int i = 0, layer_idx = 0;
|
||||
LayerGroup &root_group = grease_pencil.root_group.wrap();
|
||||
LISTBASE_FOREACH_INDEX (bGPDlayer *, gpl, &gpd.layers, layer_idx) {
|
||||
/* Create a new layer. */
|
||||
Layer &new_layer = grease_pencil.add_layer(
|
||||
root_group, StringRefNull(gpl->info, BLI_strnlen(gpl->info, 128)));
|
||||
|
||||
/* Flags. */
|
||||
SET_FLAG_FROM_TEST(new_layer.base.flag, (gpl->flag & GP_LAYER_HIDE), GP_LAYER_TREE_NODE_HIDE);
|
||||
SET_FLAG_FROM_TEST(
|
||||
new_layer.base.flag, (gpl->flag & GP_LAYER_LOCKED), GP_LAYER_TREE_NODE_LOCKED);
|
||||
SET_FLAG_FROM_TEST(
|
||||
new_layer.base.flag, (gpl->flag & GP_LAYER_SELECT), GP_LAYER_TREE_NODE_SELECT);
|
||||
SET_FLAG_FROM_TEST(
|
||||
new_layer.base.flag, (gpl->flag & GP_LAYER_FRAMELOCK), GP_LAYER_TREE_NODE_MUTE);
|
||||
SET_FLAG_FROM_TEST(
|
||||
new_layer.base.flag, (gpl->flag & GP_LAYER_USE_LIGHTS), GP_LAYER_TREE_NODE_USE_LIGHTS);
|
||||
SET_FLAG_FROM_TEST(new_layer.base.flag,
|
||||
(gpl->onion_flag & GP_LAYER_ONIONSKIN),
|
||||
GP_LAYER_TREE_NODE_USE_ONION_SKINNING);
|
||||
|
||||
new_layer.blend_mode = static_cast<int8_t>(gpl->blend_mode);
|
||||
|
||||
/* Convert the layer masks. */
|
||||
LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl->mask_layers) {
|
||||
LayerMask *new_mask = new LayerMask(mask->name);
|
||||
new_mask->flag = mask->flag;
|
||||
BLI_addtail(&new_layer.masks, new_mask);
|
||||
}
|
||||
new_layer.opacity = gpl->opacity;
|
||||
|
||||
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
|
||||
grease_pencil.drawing_array[i] = reinterpret_cast<GreasePencilDrawingBase *>(
|
||||
MEM_new<GreasePencilDrawing>(__func__));
|
||||
GreasePencilDrawing &drawing = *reinterpret_cast<GreasePencilDrawing *>(
|
||||
grease_pencil.drawing_array[i]);
|
||||
|
||||
/* Convert the frame to a drawing. */
|
||||
legacy_gpencil_frame_to_grease_pencil_drawing(*gpf, drawing);
|
||||
|
||||
GreasePencilFrame new_frame;
|
||||
new_frame.drawing_index = i;
|
||||
new_frame.type = gpf->key_type;
|
||||
SET_FLAG_FROM_TEST(new_frame.flag, (gpf->flag & GP_FRAME_SELECT), GP_FRAME_SELECTED);
|
||||
new_layer.insert_frame(gpf->framenum, std::move(new_frame));
|
||||
i++;
|
||||
}
|
||||
|
||||
if ((gpl->flag & GP_LAYER_ACTIVE) != 0) {
|
||||
grease_pencil.active_layer = static_cast<GreasePencilLayer *>(&new_layer);
|
||||
}
|
||||
|
||||
/* TODO: Update drawing user counts. */
|
||||
}
|
||||
|
||||
/* Convert the onion skinning settings. */
|
||||
grease_pencil.onion_skinning_settings.opacity = gpd.onion_factor;
|
||||
grease_pencil.onion_skinning_settings.mode = gpd.onion_mode;
|
||||
if (gpd.onion_keytype == -1) {
|
||||
grease_pencil.onion_skinning_settings.filter = GREASE_PENCIL_ONION_SKINNING_FILTER_ALL;
|
||||
}
|
||||
else {
|
||||
grease_pencil.onion_skinning_settings.filter = (1 << gpd.onion_keytype);
|
||||
}
|
||||
grease_pencil.onion_skinning_settings.num_frames_before = gpd.gstep;
|
||||
grease_pencil.onion_skinning_settings.num_frames_after = gpd.gstep_next;
|
||||
copy_v3_v3(grease_pencil.onion_skinning_settings.color_before, gpd.gcolor_prev);
|
||||
copy_v3_v3(grease_pencil.onion_skinning_settings.color_after, gpd.gcolor_next);
|
||||
|
||||
BKE_id_materials_copy(&bmain, &gpd.id, &grease_pencil.id);
|
||||
}
|
||||
|
||||
} // namespace blender::bke::greasepencil::convert
|
||||
@@ -0,0 +1,181 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_idtype.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_main.h"
|
||||
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
||||
namespace blender::bke::greasepencil::tests {
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/* Grease Pencil ID Tests. */
|
||||
|
||||
/* Note: Using a struct with constructor and destructor instead of a fixture here, to have all the
|
||||
* tests in the same group (`greasepencil`). */
|
||||
struct GreasePencilIDTestContext {
|
||||
Main *bmain = nullptr;
|
||||
|
||||
GreasePencilIDTestContext()
|
||||
{
|
||||
BKE_idtype_init();
|
||||
bmain = BKE_main_new();
|
||||
}
|
||||
~GreasePencilIDTestContext()
|
||||
{
|
||||
BKE_main_free(bmain);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(greasepencil, create_grease_pencil_id)
|
||||
{
|
||||
GreasePencilIDTestContext ctx;
|
||||
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(BKE_id_new(ctx.bmain, ID_GP, "GP"));
|
||||
EXPECT_EQ(grease_pencil.drawings().size(), 0);
|
||||
EXPECT_EQ(grease_pencil.root_group.wrap().num_nodes_total(), 0);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/* Drawing Array Tests. */
|
||||
|
||||
TEST(greasepencil, add_empty_drawings)
|
||||
{
|
||||
GreasePencilIDTestContext ctx;
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(BKE_id_new(ctx.bmain, ID_GP, "GP"));
|
||||
grease_pencil.add_empty_drawings(3);
|
||||
EXPECT_EQ(grease_pencil.drawings().size(), 3);
|
||||
}
|
||||
|
||||
TEST(greasepencil, remove_drawing)
|
||||
{
|
||||
GreasePencilIDTestContext ctx;
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(BKE_id_new(ctx.bmain, ID_GP, "GP"));
|
||||
grease_pencil.add_empty_drawings(3);
|
||||
|
||||
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(
|
||||
grease_pencil.drawings_for_write()[1]);
|
||||
drawing->geometry.wrap().resize(0, 10);
|
||||
|
||||
Layer &layer1 = grease_pencil.root_group.wrap().add_layer("Layer1");
|
||||
Layer &layer2 = grease_pencil.root_group.wrap().add_layer("Layer2");
|
||||
|
||||
layer1.insert_frame(0, GreasePencilFrame{0});
|
||||
layer1.insert_frame(10, GreasePencilFrame{1});
|
||||
layer1.insert_frame(20, GreasePencilFrame{2});
|
||||
layer1.tag_frames_map_keys_changed();
|
||||
|
||||
layer2.insert_frame(0, GreasePencilFrame{1});
|
||||
layer2.tag_frames_map_keys_changed();
|
||||
|
||||
grease_pencil.remove_drawing(1);
|
||||
EXPECT_EQ(grease_pencil.drawings().size(), 2);
|
||||
|
||||
static int expected_frames_size[] = {2, 0};
|
||||
static int expected_frames_pairs_layer0[][2] = {{0, 0}, {20, 1}};
|
||||
|
||||
Span<const Layer *> layers = grease_pencil.layers();
|
||||
EXPECT_EQ(layers[0]->frames().size(), expected_frames_size[0]);
|
||||
EXPECT_EQ(layers[1]->frames().size(), expected_frames_size[1]);
|
||||
EXPECT_EQ(layers[0]->frames().lookup(expected_frames_pairs_layer0[0][0]).drawing_index,
|
||||
expected_frames_pairs_layer0[0][1]);
|
||||
EXPECT_EQ(layers[0]->frames().lookup(expected_frames_pairs_layer0[1][0]).drawing_index,
|
||||
expected_frames_pairs_layer0[1][1]);
|
||||
}
|
||||
|
||||
TEST(greasepencil, overwrite_frame)
|
||||
{
|
||||
Layer layer1("Layer1");
|
||||
|
||||
layer1.insert_frame(0, GreasePencilFrame{0});
|
||||
layer1.tag_frames_map_keys_changed();
|
||||
|
||||
EXPECT_EQ(layer1.frames().lookup(0).drawing_index, 0);
|
||||
|
||||
layer1.overwrite_frame(0, GreasePencilFrame{42});
|
||||
EXPECT_EQ(layer1.frames().lookup(0).drawing_index, 42);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/* Layer Tree Tests. */
|
||||
|
||||
TEST(greasepencil, layer_tree_empty)
|
||||
{
|
||||
LayerGroup root;
|
||||
}
|
||||
|
||||
TEST(greasepencil, layer_tree_build_simple)
|
||||
{
|
||||
LayerGroup root;
|
||||
|
||||
LayerGroup &group = root.add_group("Group1");
|
||||
group.add_layer("Layer1");
|
||||
group.add_layer("Layer2");
|
||||
}
|
||||
|
||||
struct GreasePencilLayerTreeExample {
|
||||
StringRefNull names[7] = {"Group1", "Layer1", "Layer2", "Group2", "Layer3", "Layer4", "Layer5"};
|
||||
const bool is_layer[7] = {false, true, true, false, true, true, true};
|
||||
LayerGroup root;
|
||||
|
||||
GreasePencilLayerTreeExample()
|
||||
{
|
||||
LayerGroup &group = root.add_group(names[0]);
|
||||
group.add_layer(names[1]);
|
||||
group.add_layer(names[2]);
|
||||
|
||||
LayerGroup &group2 = group.add_group(names[3]);
|
||||
group2.add_layer(names[4]);
|
||||
group2.add_layer(names[5]);
|
||||
|
||||
root.add_layer(names[6]);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(greasepencil, layer_tree_pre_order_iteration)
|
||||
{
|
||||
GreasePencilLayerTreeExample ex;
|
||||
|
||||
Span<const TreeNode *> children = ex.root.nodes();
|
||||
for (const int i : children.index_range()) {
|
||||
const TreeNode &child = *children[i];
|
||||
EXPECT_STREQ(child.name, ex.names[i].data());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(greasepencil, layer_tree_pre_order_iteration2)
|
||||
{
|
||||
GreasePencilLayerTreeExample ex;
|
||||
|
||||
Span<const Layer *> layers = ex.root.layers();
|
||||
char name[64];
|
||||
for (const int i : layers.index_range()) {
|
||||
const Layer &layer = *layers[i];
|
||||
snprintf(name, 64, "%s%d", "Layer", i + 1);
|
||||
EXPECT_STREQ(layer.name().data(), name);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(greasepencil, layer_tree_total_size)
|
||||
{
|
||||
GreasePencilLayerTreeExample ex;
|
||||
EXPECT_EQ(ex.root.num_nodes_total(), 7);
|
||||
}
|
||||
|
||||
TEST(greasepencil, layer_tree_node_types)
|
||||
{
|
||||
GreasePencilLayerTreeExample ex;
|
||||
Span<const TreeNode *> children = ex.root.nodes();
|
||||
for (const int i : children.index_range()) {
|
||||
const TreeNode &child = *children[i];
|
||||
EXPECT_EQ(child.is_layer(), ex.is_layer[i]);
|
||||
EXPECT_EQ(child.is_group(), !ex.is_layer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::bke::greasepencil::tests
|
||||
@@ -95,6 +95,7 @@ static void id_type_init(void)
|
||||
INIT_TYPE(ID_PT);
|
||||
INIT_TYPE(ID_VO);
|
||||
INIT_TYPE(ID_SIM);
|
||||
INIT_TYPE(ID_GP);
|
||||
|
||||
/* Special naughty boy... */
|
||||
BLI_assert(IDType_ID_LINK_PLACEHOLDER.main_listbase_index == INDEX_ID_NULL);
|
||||
@@ -222,6 +223,7 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode)
|
||||
CASE_IDFILTER(CF);
|
||||
CASE_IDFILTER(CU_LEGACY);
|
||||
CASE_IDFILTER(GD_LEGACY);
|
||||
CASE_IDFILTER(GP);
|
||||
CASE_IDFILTER(GR);
|
||||
CASE_IDFILTER(CV);
|
||||
CASE_IDFILTER(IM);
|
||||
@@ -280,6 +282,7 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter)
|
||||
CASE_IDFILTER(CF);
|
||||
CASE_IDFILTER(CU_LEGACY);
|
||||
CASE_IDFILTER(GD_LEGACY);
|
||||
CASE_IDFILTER(GP);
|
||||
CASE_IDFILTER(GR);
|
||||
CASE_IDFILTER(CV);
|
||||
CASE_IDFILTER(IM);
|
||||
@@ -336,6 +339,7 @@ int BKE_idtype_idcode_to_index(const short idcode)
|
||||
CASE_IDINDEX(CF);
|
||||
CASE_IDINDEX(CU_LEGACY);
|
||||
CASE_IDINDEX(GD_LEGACY);
|
||||
CASE_IDINDEX(GP);
|
||||
CASE_IDINDEX(GR);
|
||||
CASE_IDINDEX(CV);
|
||||
CASE_IDINDEX(IM);
|
||||
@@ -395,6 +399,7 @@ short BKE_idtype_idcode_from_index(const int index)
|
||||
CASE_IDCODE(CF);
|
||||
CASE_IDCODE(CU_LEGACY);
|
||||
CASE_IDCODE(GD_LEGACY);
|
||||
CASE_IDCODE(GP);
|
||||
CASE_IDCODE(GR);
|
||||
CASE_IDCODE(CV);
|
||||
CASE_IDCODE(IM);
|
||||
|
||||
@@ -464,6 +464,8 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *owner_id, const bool include
|
||||
return FILTER_ID_IM;
|
||||
case ID_GD_LEGACY:
|
||||
return FILTER_ID_MA;
|
||||
case ID_GP:
|
||||
return FILTER_ID_GP | FILTER_ID_MA;
|
||||
case ID_WS:
|
||||
return FILTER_ID_SCE;
|
||||
case ID_CV:
|
||||
|
||||
@@ -628,6 +628,8 @@ ListBase *which_libbase(Main *bmain, short type)
|
||||
return &(bmain->wm);
|
||||
case ID_GD_LEGACY:
|
||||
return &(bmain->gpencils);
|
||||
case ID_GP:
|
||||
return &(bmain->grease_pencils);
|
||||
case ID_MC:
|
||||
return &(bmain->movieclips);
|
||||
case ID_MSK:
|
||||
@@ -671,6 +673,7 @@ int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/])
|
||||
|
||||
/* Referenced by nodes, objects, view, scene etc, before to free after. */
|
||||
lb[INDEX_ID_GD_LEGACY] = &(bmain->gpencils);
|
||||
lb[INDEX_ID_GP] = &(bmain->grease_pencils);
|
||||
|
||||
lb[INDEX_ID_NT] = &(bmain->nodetrees);
|
||||
lb[INDEX_ID_IM] = &(bmain->images);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "DNA_customdata_types.h"
|
||||
#include "DNA_defaults.h"
|
||||
#include "DNA_gpencil_legacy_types.h"
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
@@ -375,6 +376,10 @@ Material ***BKE_object_material_array_p(Object *ob)
|
||||
Volume *volume = static_cast<Volume *>(ob->data);
|
||||
return &(volume->mat);
|
||||
}
|
||||
if (ob->type == OB_GREASE_PENCIL) {
|
||||
GreasePencil *grease_pencil = static_cast<GreasePencil *>(ob->data);
|
||||
return &(grease_pencil->material_array);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -408,6 +413,10 @@ short *BKE_object_material_len_p(Object *ob)
|
||||
Volume *volume = static_cast<Volume *>(ob->data);
|
||||
return &(volume->totcol);
|
||||
}
|
||||
if (ob->type == OB_GREASE_PENCIL) {
|
||||
GreasePencil *grease_pencil = static_cast<GreasePencil *>(ob->data);
|
||||
return &(grease_pencil->material_array_size);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -431,6 +440,8 @@ Material ***BKE_id_material_array_p(ID *id)
|
||||
return &(((PointCloud *)id)->mat);
|
||||
case ID_VO:
|
||||
return &(((Volume *)id)->mat);
|
||||
case ID_GP:
|
||||
return &(((GreasePencil *)id)->material_array);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -457,6 +468,8 @@ short *BKE_id_material_len_p(ID *id)
|
||||
return &(((PointCloud *)id)->totcol);
|
||||
case ID_VO:
|
||||
return &(((Volume *)id)->totcol);
|
||||
case ID_GP:
|
||||
return &(((GreasePencil *)id)->material_array_size);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "DNA_fluid_types.h"
|
||||
#include "DNA_gpencil_legacy_types.h"
|
||||
#include "DNA_gpencil_modifier_types.h"
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
#include "DNA_light_types.h"
|
||||
@@ -89,6 +90,7 @@
|
||||
#include "BKE_gpencil_geom_legacy.h"
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_gpencil_modifier_legacy.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_icons.h"
|
||||
#include "BKE_idprop.h"
|
||||
#include "BKE_idtype.h"
|
||||
@@ -1467,7 +1469,8 @@ bool BKE_object_supports_modifiers(const Object *ob)
|
||||
OB_FONT,
|
||||
OB_LATTICE,
|
||||
OB_POINTCLOUD,
|
||||
OB_VOLUME);
|
||||
OB_VOLUME,
|
||||
OB_GREASE_PENCIL);
|
||||
}
|
||||
|
||||
bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
|
||||
@@ -1970,6 +1973,8 @@ bool BKE_object_is_in_editmode(const Object *ob)
|
||||
case OB_CURVES:
|
||||
/* Curves object has no edit mode data. */
|
||||
return ob->mode == OB_MODE_EDIT;
|
||||
case OB_GREASE_PENCIL:
|
||||
return ob->mode == OB_MODE_EDIT;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -1997,6 +2002,7 @@ bool BKE_object_data_is_in_editmode(const Object *ob, const ID *id)
|
||||
case ID_AR:
|
||||
return ((const bArmature *)id)->edbo != nullptr;
|
||||
case ID_CV:
|
||||
case ID_GP:
|
||||
if (ob) {
|
||||
return BKE_object_is_in_editmode(ob);
|
||||
}
|
||||
@@ -2052,6 +2058,10 @@ char *BKE_object_data_editmode_flush_ptr_get(struct ID *id)
|
||||
/* Curves have no edit mode data. */
|
||||
return nullptr;
|
||||
}
|
||||
case ID_GP: {
|
||||
/* Grease Pencil has no edit mode data. */
|
||||
return nullptr;
|
||||
}
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
@@ -2195,6 +2205,8 @@ static const char *get_obdata_defname(int type)
|
||||
return DATA_("GPencil");
|
||||
case OB_LIGHTPROBE:
|
||||
return DATA_("LightProbe");
|
||||
case OB_GREASE_PENCIL:
|
||||
return DATA_("GreasePencil");
|
||||
default:
|
||||
CLOG_ERROR(&LOG, "Internal error, bad type: %d", type);
|
||||
return CTX_DATA_(BLT_I18NCONTEXT_ID_ID, "Empty");
|
||||
@@ -2264,6 +2276,8 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name)
|
||||
return BKE_pointcloud_add_default(bmain, name);
|
||||
case OB_VOLUME:
|
||||
return BKE_volume_add(bmain, name);
|
||||
case OB_GREASE_PENCIL:
|
||||
return BKE_grease_pencil_add(bmain, name);
|
||||
case OB_EMPTY:
|
||||
return nullptr;
|
||||
default:
|
||||
@@ -2302,6 +2316,8 @@ int BKE_object_obdata_to_type(const ID *id)
|
||||
return OB_POINTCLOUD;
|
||||
case ID_VO:
|
||||
return OB_VOLUME;
|
||||
case ID_GP:
|
||||
return OB_GREASE_PENCIL;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
@@ -3813,6 +3829,8 @@ const BoundBox *BKE_object_boundbox_get(Object *ob)
|
||||
case OB_VOLUME:
|
||||
bb = BKE_volume_boundbox_get(ob);
|
||||
break;
|
||||
case OB_GREASE_PENCIL:
|
||||
bb = BKE_grease_pencil_boundbox_get(ob);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -3997,7 +4015,6 @@ void BKE_object_minmax(Object *ob, float r_min[3], float r_max[3], const bool us
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case OB_POINTCLOUD: {
|
||||
const BoundBox bb = *BKE_pointcloud_boundbox_get(ob);
|
||||
BKE_boundbox_minmax(&bb, ob->object_to_world, r_min, r_max);
|
||||
@@ -4010,6 +4027,12 @@ void BKE_object_minmax(Object *ob, float r_min[3], float r_max[3], const bool us
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
case OB_GREASE_PENCIL: {
|
||||
const BoundBox bb = *BKE_grease_pencil_boundbox_get(ob);
|
||||
BKE_boundbox_minmax(&bb, ob->object_to_world, r_min, r_max);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed == false) {
|
||||
@@ -5185,7 +5208,8 @@ bool BKE_object_supports_material_slots(struct Object *ob)
|
||||
OB_CURVES,
|
||||
OB_POINTCLOUD,
|
||||
OB_VOLUME,
|
||||
OB_GPENCIL_LEGACY);
|
||||
OB_GPENCIL_LEGACY,
|
||||
OB_GREASE_PENCIL);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include "BKE_effect.h"
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_gpencil_modifier_legacy.h"
|
||||
#include "BKE_grease_pencil.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_lattice.h"
|
||||
@@ -199,6 +201,9 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
|
||||
case OB_VOLUME:
|
||||
BKE_volume_data_update(depsgraph, scene, ob);
|
||||
break;
|
||||
case OB_GREASE_PENCIL:
|
||||
BKE_grease_pencil_data_update(depsgraph, scene, ob);
|
||||
break;
|
||||
}
|
||||
|
||||
/* particles */
|
||||
@@ -319,6 +324,10 @@ void BKE_object_batch_cache_dirty_tag(Object *ob)
|
||||
case OB_VOLUME:
|
||||
BKE_volume_batch_cache_dirty_tag((struct Volume *)ob->data, BKE_VOLUME_BATCH_DIRTY_ALL);
|
||||
break;
|
||||
case OB_GREASE_PENCIL:
|
||||
BKE_grease_pencil_batch_cache_dirty_tag((struct GreasePencil *)ob->data,
|
||||
BKE_GREASEPENCIL_BATCH_DIRTY_ALL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2902,6 +2902,8 @@ static const char *dataname(short id_code)
|
||||
return "Data from VO";
|
||||
case ID_SIM:
|
||||
return "Data from SIM";
|
||||
case ID_GP:
|
||||
return "Data from GP";
|
||||
}
|
||||
return "Data from Lib Block";
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
#include "BKE_fcurve_driver.h"
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_gpencil_modifier_legacy.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_idprop.h"
|
||||
#include "BKE_idtype.h"
|
||||
#include "BKE_image.h"
|
||||
@@ -618,6 +619,7 @@ void DepsgraphNodeBuilder::build_id(ID *id)
|
||||
case ID_CV:
|
||||
case ID_PT:
|
||||
case ID_VO:
|
||||
case ID_GP:
|
||||
build_object_data_geometry_datablock(id);
|
||||
break;
|
||||
case ID_SPK:
|
||||
@@ -968,6 +970,7 @@ void DepsgraphNodeBuilder::build_object_data(Object *object)
|
||||
case OB_CURVES:
|
||||
case OB_POINTCLOUD:
|
||||
case OB_VOLUME:
|
||||
case OB_GREASE_PENCIL:
|
||||
build_object_data_geometry(object);
|
||||
break;
|
||||
case OB_ARMATURE:
|
||||
@@ -1740,6 +1743,11 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata)
|
||||
op_node->set_as_entry();
|
||||
break;
|
||||
}
|
||||
case ID_GP: {
|
||||
op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
|
||||
op_node->set_as_entry();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert_msg(0, "Should not happen");
|
||||
break;
|
||||
|
||||
@@ -564,6 +564,7 @@ void DepsgraphRelationBuilder::build_id(ID *id)
|
||||
case ID_PT:
|
||||
case ID_VO:
|
||||
case ID_GD_LEGACY:
|
||||
case ID_GP:
|
||||
build_object_data_geometry_datablock(id);
|
||||
break;
|
||||
case ID_SPK:
|
||||
@@ -972,7 +973,8 @@ void DepsgraphRelationBuilder::build_object_data(Object *object)
|
||||
case OB_GPENCIL_LEGACY:
|
||||
case OB_CURVES:
|
||||
case OB_POINTCLOUD:
|
||||
case OB_VOLUME: {
|
||||
case OB_VOLUME:
|
||||
case OB_GREASE_PENCIL: {
|
||||
build_object_data_geometry(object);
|
||||
/* TODO(sergey): Only for until we support granular
|
||||
* update of curves. */
|
||||
@@ -2653,6 +2655,8 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ID_GP:
|
||||
break;
|
||||
default:
|
||||
BLI_assert_msg(0, "Should not happen");
|
||||
break;
|
||||
|
||||
@@ -593,6 +593,7 @@ NodeType geometry_tag_to_component(const ID *id)
|
||||
case OB_CURVES:
|
||||
case OB_POINTCLOUD:
|
||||
case OB_VOLUME:
|
||||
case OB_GREASE_PENCIL:
|
||||
return NodeType::GEOMETRY;
|
||||
case OB_ARMATURE:
|
||||
return NodeType::EVAL_POSE;
|
||||
@@ -619,6 +620,8 @@ NodeType geometry_tag_to_component(const ID *id)
|
||||
return NodeType::PARAMETERS;
|
||||
case ID_MSK:
|
||||
return NodeType::PARAMETERS;
|
||||
case ID_GP:
|
||||
return NodeType::GEOMETRY;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ void ObjectRuntimeBackup::restore_to_object(Object *object)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ELEM(object->type, OB_CURVES, OB_POINTCLOUD, OB_VOLUME)) {
|
||||
else if (ELEM(object->type, OB_CURVES, OB_POINTCLOUD, OB_VOLUME, OB_GREASE_PENCIL)) {
|
||||
if (object->id.recalc & ID_RECALC_GEOMETRY) {
|
||||
/* Free evaluated caches. */
|
||||
object->data = data_orig;
|
||||
|
||||
@@ -72,7 +72,8 @@ set(SRC
|
||||
intern/draw_attributes.cc
|
||||
intern/draw_cache_impl_curve.cc
|
||||
intern/draw_cache_impl_curves.cc
|
||||
intern/draw_cache_impl_gpencil.cc
|
||||
intern/draw_cache_impl_gpencil_legacy.cc
|
||||
intern/draw_cache_impl_grease_pencil.cc
|
||||
intern/draw_cache_impl_lattice.c
|
||||
intern/draw_cache_impl_mesh.cc
|
||||
intern/draw_cache_impl_particles.c
|
||||
@@ -186,9 +187,11 @@ set(SRC
|
||||
engines/gpencil/gpencil_cache_utils.c
|
||||
engines/gpencil/gpencil_draw_data.c
|
||||
engines/gpencil/gpencil_engine.c
|
||||
engines/gpencil/gpencil_engine.cc
|
||||
engines/gpencil/gpencil_engine.h
|
||||
engines/gpencil/gpencil_render.c
|
||||
engines/gpencil/gpencil_shader.c
|
||||
engines/gpencil/gpencil_shader.cc
|
||||
engines/gpencil/gpencil_shader_fx.c
|
||||
engines/select/select_draw_utils.c
|
||||
engines/select/select_engine.c
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_image.h"
|
||||
#include "DRW_gpu_wrapper.hh"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_pass.hh"
|
||||
|
||||
#include "gpencil_shader.hh"
|
||||
|
||||
#include "BLI_smaa_textures.h"
|
||||
|
||||
namespace blender::draw::greasepencil {
|
||||
|
||||
using namespace draw;
|
||||
|
||||
/** Final anti-aliasing post processing and compositing on top of render. */
|
||||
class AntiAliasing {
|
||||
private:
|
||||
ShaderModule &shaders_;
|
||||
|
||||
Texture smaa_search_tx_ = {"smaa_search",
|
||||
GPU_R8,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
int2(SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT)};
|
||||
Texture smaa_area_tx_ = {
|
||||
"smaa_area", GPU_RG8, GPU_TEXTURE_USAGE_SHADER_READ, int2(AREATEX_WIDTH, AREATEX_HEIGHT)};
|
||||
|
||||
TextureFromPool edge_detect_tx_ = {"edge_detect_tx"};
|
||||
Framebuffer edge_detect_fb_ = {"edge_detect_fb"};
|
||||
PassSimple edge_detect_ps_ = {"edge_detect_ps"};
|
||||
|
||||
TextureFromPool blend_weight_tx_ = {"blend_weight_tx"};
|
||||
Framebuffer blend_weight_fb_ = {"blend_weight_fb"};
|
||||
PassSimple blend_weight_ps_ = {"blend_weight_ps"};
|
||||
|
||||
Framebuffer output_fb_ = {"output_fb"};
|
||||
PassSimple resolve_ps_ = {"resolve_ps"};
|
||||
|
||||
bool draw_wireframe_ = false;
|
||||
float luma_weight_ = 1.0f;
|
||||
bool anti_aliasing_enabled_ = true;
|
||||
|
||||
public:
|
||||
AntiAliasing(ShaderModule &shaders) : shaders_(shaders)
|
||||
{
|
||||
GPU_texture_update(smaa_search_tx_, GPU_DATA_UBYTE, searchTexBytes);
|
||||
GPU_texture_update(smaa_area_tx_, GPU_DATA_UBYTE, areaTexBytes);
|
||||
|
||||
GPU_texture_filter_mode(smaa_search_tx_, true);
|
||||
GPU_texture_filter_mode(smaa_area_tx_, true);
|
||||
}
|
||||
|
||||
void init(const View3D *v3d, const Scene *scene)
|
||||
{
|
||||
if (v3d) {
|
||||
draw_wireframe_ = (v3d->shading.type == OB_WIRE);
|
||||
}
|
||||
|
||||
luma_weight_ = scene->grease_pencil_settings.smaa_threshold;
|
||||
anti_aliasing_enabled_ = true; // GPENCIL_SIMPLIFY_AA(scene);
|
||||
}
|
||||
|
||||
void begin_sync(TextureFromPool &color_tx, TextureFromPool &reveal_tx)
|
||||
{
|
||||
/* TODO(fclem): No global access. */
|
||||
const float *size = DRW_viewport_size_get();
|
||||
const float *sizeinv = DRW_viewport_invert_size_get();
|
||||
const float4 metrics = {sizeinv[0], sizeinv[1], size[0], size[1]};
|
||||
|
||||
anti_aliasing_pass(color_tx, reveal_tx, metrics);
|
||||
|
||||
/* Resolve pass. */
|
||||
PassSimple &pass = resolve_ps_;
|
||||
pass.init();
|
||||
pass.framebuffer_set(&output_fb_);
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
|
||||
pass.shader_set(shaders_.static_shader_get(ANTIALIASING_RESOLVE));
|
||||
/** \note use color_tx as dummy if AA is diabled. */
|
||||
pass.bind_texture("blendTex", anti_aliasing_enabled_ ? &blend_weight_tx_ : &color_tx);
|
||||
pass.bind_texture("colorTex", &color_tx);
|
||||
pass.bind_texture("revealTex", &reveal_tx);
|
||||
pass.push_constant("doAntiAliasing", anti_aliasing_enabled_);
|
||||
pass.push_constant("onlyAlpha", draw_wireframe_);
|
||||
pass.push_constant("viewportMetrics", metrics);
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
|
||||
void draw(Manager &manager, GPUTexture *dst_color_tx)
|
||||
{
|
||||
int2 render_size = {GPU_texture_width(dst_color_tx), GPU_texture_height(dst_color_tx)};
|
||||
|
||||
DRW_stats_group_start("Anti-Aliasing");
|
||||
|
||||
if (anti_aliasing_enabled_) {
|
||||
edge_detect_tx_.acquire(render_size, GPU_RG8);
|
||||
edge_detect_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(edge_detect_tx_));
|
||||
manager.submit(edge_detect_ps_);
|
||||
|
||||
blend_weight_tx_.acquire(render_size, GPU_RGBA8);
|
||||
blend_weight_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(blend_weight_tx_));
|
||||
manager.submit(blend_weight_ps_);
|
||||
edge_detect_tx_.release();
|
||||
}
|
||||
|
||||
output_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(dst_color_tx));
|
||||
manager.submit(resolve_ps_);
|
||||
blend_weight_tx_.release();
|
||||
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
private:
|
||||
void anti_aliasing_pass(TextureFromPool &color_tx,
|
||||
TextureFromPool &reveal_tx,
|
||||
const float4 metrics)
|
||||
{
|
||||
if (!anti_aliasing_enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stage 1: Edge detection. */
|
||||
edge_detect_ps_.init();
|
||||
edge_detect_ps_.framebuffer_set(&edge_detect_fb_);
|
||||
edge_detect_ps_.state_set(DRW_STATE_WRITE_COLOR);
|
||||
edge_detect_ps_.shader_set(shaders_.static_shader_get(ANTIALIASING_EDGE_DETECT));
|
||||
edge_detect_ps_.bind_texture("colorTex", &color_tx);
|
||||
edge_detect_ps_.bind_texture("revealTex", &reveal_tx);
|
||||
edge_detect_ps_.push_constant("viewportMetrics", metrics);
|
||||
edge_detect_ps_.push_constant("lumaWeight", luma_weight_);
|
||||
edge_detect_ps_.clear_color(float4(0.0f));
|
||||
edge_detect_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
|
||||
/* Stage 2: Blend Weight/Coord. */
|
||||
blend_weight_ps_.init();
|
||||
blend_weight_ps_.framebuffer_set(&blend_weight_fb_);
|
||||
blend_weight_ps_.state_set(DRW_STATE_WRITE_COLOR);
|
||||
blend_weight_ps_.shader_set(shaders_.static_shader_get(ANTIALIASING_BLEND_WEIGHT));
|
||||
blend_weight_ps_.bind_texture("edgesTex", &edge_detect_tx_);
|
||||
blend_weight_ps_.bind_texture("areaTex", smaa_area_tx_);
|
||||
blend_weight_ps_.bind_texture("searchTex", smaa_search_tx_);
|
||||
blend_weight_ps_.push_constant("viewportMetrics", metrics);
|
||||
blend_weight_ps_.clear_color(float4(0.0f));
|
||||
blend_weight_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::draw::greasepencil
|
||||
@@ -6,3 +6,16 @@
|
||||
|
||||
/* High bits are used to pass material ID to fragment shader. */
|
||||
#define GPENCIl_MATID_SHIFT 16u
|
||||
|
||||
/* Textures */
|
||||
#define GPENCIL_SCENE_DEPTH_TEX_SLOT 2
|
||||
#define GPENCIL_MASK_TEX_SLOT 3
|
||||
#define GPENCIL_FILL_TEX_SLOT 4
|
||||
#define GPENCIL_STROKE_TEX_SLOT 5
|
||||
/* SSBOs */
|
||||
#define GPENCIL_OBJECT_SLOT 0
|
||||
#define GPENCIL_LAYER_SLOT 1
|
||||
#define GPENCIL_MATERIAL_SLOT 2
|
||||
#define GPENCIL_LIGHT_SLOT 3
|
||||
/* UBOs */
|
||||
#define GPENCIL_SCENE_SLOT 2
|
||||
|
||||
@@ -233,6 +233,13 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
|
||||
mat_data->flag |= GP_FILL_HOLDOUT;
|
||||
}
|
||||
|
||||
if (gp_style->flag & GP_MATERIAL_STROKE_SHOW) {
|
||||
mat_data->flag |= GP_SHOW_STROKE;
|
||||
}
|
||||
if (gp_style->flag & GP_MATERIAL_FILL_SHOW) {
|
||||
mat_data->flag |= GP_SHOW_FILL;
|
||||
}
|
||||
|
||||
gp_style = gpencil_viewport_material_overrides(pd, ob, color_type, gp_style, lighting_mode);
|
||||
|
||||
/* Dots or Squares rotation. */
|
||||
|
||||
@@ -397,7 +397,7 @@ static void gpencil_sbuffer_cache_populate(gpIterPopulateData *iter)
|
||||
* Remember, sbuffer stroke indices start from 0. So we add last index to avoid
|
||||
* masking issues. */
|
||||
iter->grp = DRW_shgroup_create_sub(iter->grp);
|
||||
DRW_shgroup_uniform_block(iter->grp, "materials", iter->ubo_mat);
|
||||
DRW_shgroup_uniform_block(iter->grp, "gp_materials", iter->ubo_mat);
|
||||
DRW_shgroup_uniform_float_copy(iter->grp, "gpStrokeIndexOffset", iter->stroke_index_last);
|
||||
|
||||
const DRWContextState *ctx = DRW_context_state_get();
|
||||
@@ -445,8 +445,8 @@ static void gpencil_layer_cache_populate(bGPDlayer *gpl,
|
||||
|
||||
/* Iterator dependent uniforms. */
|
||||
DRWShadingGroup *grp = iter->grp = tgp_layer->base_shgrp;
|
||||
DRW_shgroup_uniform_block(grp, "lights", iter->ubo_lights);
|
||||
DRW_shgroup_uniform_block(grp, "materials", iter->ubo_mat);
|
||||
DRW_shgroup_uniform_block(grp, "gp_lights", iter->ubo_lights);
|
||||
DRW_shgroup_uniform_block(grp, "gp_materials", iter->ubo_mat);
|
||||
DRW_shgroup_uniform_texture(grp, "gpFillTexture", iter->tex_fill);
|
||||
DRW_shgroup_uniform_texture(grp, "gpStrokeTexture", iter->tex_stroke);
|
||||
DRW_shgroup_uniform_int_copy(grp, "gpMaterialOffset", iter->mat_ofs);
|
||||
@@ -493,7 +493,7 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
|
||||
|
||||
iter->grp = DRW_shgroup_create_sub(iter->grp);
|
||||
if (iter->ubo_mat != ubo_mat) {
|
||||
DRW_shgroup_uniform_block(iter->grp, "materials", ubo_mat);
|
||||
DRW_shgroup_uniform_block(iter->grp, "gp_materials", ubo_mat);
|
||||
iter->ubo_mat = ubo_mat;
|
||||
}
|
||||
if (tex_fill) {
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BKE_gpencil_modifier_legacy.h"
|
||||
#include "BLI_listbase_wrapper.hh"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
#include "DNA_shader_fx_types.h"
|
||||
#include "DRW_engine.h"
|
||||
#include "DRW_render.h"
|
||||
#include "ED_screen.h"
|
||||
#include "ED_view3d.h"
|
||||
#include "GPU_capabilities.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_pass.hh"
|
||||
|
||||
#define GP_LIGHT
|
||||
#include "gpencil_antialiasing.hh"
|
||||
#include "gpencil_defines.h"
|
||||
#include "gpencil_engine.h"
|
||||
#include "gpencil_layer.hh"
|
||||
#include "gpencil_light.hh"
|
||||
#include "gpencil_material.hh"
|
||||
#include "gpencil_object.hh"
|
||||
#include "gpencil_shader.hh"
|
||||
#include "gpencil_shader_shared.h"
|
||||
#include "gpencil_vfx.hh"
|
||||
|
||||
namespace blender::draw::greasepencil {
|
||||
|
||||
using namespace draw;
|
||||
|
||||
class Instance {
|
||||
private:
|
||||
ShaderModule &shaders;
|
||||
LayerModule layers;
|
||||
MaterialModule materials;
|
||||
ObjectModule objects;
|
||||
LightModule lights;
|
||||
VfxModule vfx;
|
||||
AntiAliasing anti_aliasing;
|
||||
|
||||
/** Contains all gpencil objects in the scene as well as their effect sub-passes. */
|
||||
PassSortable main_ps_ = {"gp_main_ps"};
|
||||
|
||||
/** Contains all composited GPencil object. */
|
||||
TextureFromPool depth_tx_ = {"gp_depth_tx"};
|
||||
TextureFromPool color_tx_ = {"gp_color_tx"};
|
||||
TextureFromPool reveal_tx_ = {"gp_reveal_tx"};
|
||||
Framebuffer main_fb_ = {"gp_main_fb"};
|
||||
|
||||
/** Texture format for all intermediate buffers. */
|
||||
eGPUTextureFormat texture_format_ = GPU_RGBA16F;
|
||||
|
||||
UniformBuffer<gpScene> scene_buf_;
|
||||
|
||||
/** Dummy textures. */
|
||||
static constexpr float dummy_px_[4] = {1.0f, 0.0f, 1.0f, 1.0f};
|
||||
Texture dummy_depth_tx_ = {"dummy_depth",
|
||||
GPU_DEPTH_COMPONENT32F,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
int2(1),
|
||||
(float *)dummy_px_};
|
||||
Texture dummy_color_tx_ = {
|
||||
"dummy_color", GPU_RGBA16F, GPU_TEXTURE_USAGE_SHADER_READ, int2(1), (float *)dummy_px_};
|
||||
|
||||
/** Scene depth used for manual depth testing. Default to dummy depth to skip depth test. */
|
||||
GPUTexture *scene_depth_tx_ = dummy_depth_tx_;
|
||||
|
||||
/** Context. */
|
||||
Depsgraph *depsgraph_ = nullptr;
|
||||
Object *camera_ = nullptr;
|
||||
|
||||
/** \note Needs not to be temporary variable since it is dereferenced later. */
|
||||
std::array<float4, 2> clear_colors_ = {float4(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
float4(1.0f, 1.0f, 1.0f, 1.0f)};
|
||||
|
||||
public:
|
||||
Instance()
|
||||
: shaders(*ShaderModule::module_get()),
|
||||
objects(layers, materials, shaders),
|
||||
vfx(shaders),
|
||||
anti_aliasing(shaders){};
|
||||
|
||||
void init(Depsgraph *depsgraph, const View3D *v3d, const RegionView3D *rv3d)
|
||||
{
|
||||
depsgraph_ = depsgraph;
|
||||
const Scene *scene = DEG_get_evaluated_scene(depsgraph_);
|
||||
|
||||
const bool is_viewport = (v3d != nullptr);
|
||||
|
||||
if (is_viewport) {
|
||||
/* Use lower precision for viewport. */
|
||||
texture_format_ = GPU_R11F_G11F_B10F;
|
||||
camera_ = (rv3d->persp == RV3D_CAMOB) ? v3d->camera : nullptr;
|
||||
}
|
||||
|
||||
objects.init(v3d, scene);
|
||||
lights.init(v3d);
|
||||
/* TODO(fclem): Vfx. */
|
||||
// vfx.init(use_vfx_, camera_, rv3d);
|
||||
anti_aliasing.init(v3d, scene);
|
||||
}
|
||||
|
||||
void begin_sync(Manager & /* manager */)
|
||||
{
|
||||
/* TODO(fclem): Remove global draw manager access. */
|
||||
View main_view("GPencil_MainView", DRW_view_default_get());
|
||||
|
||||
objects.begin_sync(depsgraph_, main_view);
|
||||
layers.begin_sync();
|
||||
materials.begin_sync();
|
||||
lights.begin_sync(depsgraph_);
|
||||
|
||||
main_ps_.init();
|
||||
PassMain::Sub &sub = main_ps_.sub("InitSubpass", -FLT_MAX);
|
||||
sub.framebuffer_set(&main_fb_);
|
||||
sub.clear_multi(clear_colors_);
|
||||
/* TODO(fclem): Textures. */
|
||||
sub.bind_texture(GPENCIL_SCENE_DEPTH_TEX_SLOT, &dummy_depth_tx_);
|
||||
sub.bind_texture(GPENCIL_MASK_TEX_SLOT, &dummy_color_tx_);
|
||||
sub.bind_texture(GPENCIL_FILL_TEX_SLOT, &dummy_color_tx_);
|
||||
sub.bind_texture(GPENCIL_STROKE_TEX_SLOT, &dummy_color_tx_);
|
||||
sub.bind_ubo(GPENCIL_SCENE_SLOT, &scene_buf_);
|
||||
objects.bind_resources(sub);
|
||||
layers.bind_resources(sub);
|
||||
materials.bind_resources(sub);
|
||||
lights.bind_resources(sub);
|
||||
|
||||
anti_aliasing.begin_sync(color_tx_, reveal_tx_);
|
||||
}
|
||||
|
||||
void object_sync(Manager &manager, ObjectRef &object_ref)
|
||||
{
|
||||
switch (object_ref.object->type) {
|
||||
case OB_GREASE_PENCIL:
|
||||
objects.sync_grease_pencil(manager, object_ref, main_fb_, main_ps_);
|
||||
break;
|
||||
case OB_LAMP:
|
||||
lights.sync(object_ref);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void end_sync(Manager & /* manager */)
|
||||
{
|
||||
objects.end_sync();
|
||||
layers.end_sync();
|
||||
materials.end_sync();
|
||||
lights.end_sync();
|
||||
}
|
||||
|
||||
void draw_viewport(Manager &manager,
|
||||
View &view,
|
||||
GPUTexture *dst_depth_tx,
|
||||
GPUTexture *dst_color_tx)
|
||||
{
|
||||
if (!objects.scene_has_visible_gpencil_object()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int2 render_size = {GPU_texture_width(dst_depth_tx), GPU_texture_height(dst_depth_tx)};
|
||||
|
||||
depth_tx_.acquire(render_size, GPU_DEPTH24_STENCIL8);
|
||||
color_tx_.acquire(render_size, texture_format_);
|
||||
reveal_tx_.acquire(render_size, texture_format_);
|
||||
main_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(color_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(reveal_tx_));
|
||||
|
||||
scene_buf_.render_size = float2(render_size);
|
||||
scene_buf_.push_update();
|
||||
|
||||
objects.acquire_temporary_buffers(render_size, texture_format_);
|
||||
|
||||
manager.submit(main_ps_, view);
|
||||
|
||||
objects.release_temporary_buffers();
|
||||
|
||||
anti_aliasing.draw(manager, dst_color_tx);
|
||||
|
||||
depth_tx_.release();
|
||||
color_tx_.release();
|
||||
reveal_tx_.release();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::draw::greasepencil
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Interface with legacy C DRW manager
|
||||
* \{ */
|
||||
|
||||
using namespace blender;
|
||||
|
||||
struct GPENCIL_NEXT_Data {
|
||||
DrawEngineType *engine_type;
|
||||
DRWViewportEmptyList *fbl;
|
||||
DRWViewportEmptyList *txl;
|
||||
DRWViewportEmptyList *psl;
|
||||
DRWViewportEmptyList *stl;
|
||||
draw::greasepencil::Instance *instance;
|
||||
|
||||
char info[GPU_INFO_SIZE];
|
||||
};
|
||||
|
||||
static void gpencil_engine_init(void *vedata)
|
||||
{
|
||||
/* TODO(fclem): Remove once it is minimum required. */
|
||||
if (!GPU_shader_storage_buffer_objects_support()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPENCIL_NEXT_Data *ved = reinterpret_cast<GPENCIL_NEXT_Data *>(vedata);
|
||||
if (ved->instance == nullptr) {
|
||||
ved->instance = new draw::greasepencil::Instance();
|
||||
}
|
||||
|
||||
const DRWContextState *ctx_state = DRW_context_state_get();
|
||||
|
||||
ved->instance->init(ctx_state->depsgraph, ctx_state->v3d, ctx_state->rv3d);
|
||||
}
|
||||
|
||||
static void gpencil_draw_scene(void *vedata)
|
||||
{
|
||||
GPENCIL_NEXT_Data *ved = reinterpret_cast<GPENCIL_NEXT_Data *>(vedata);
|
||||
if (!GPU_shader_storage_buffer_objects_support()) {
|
||||
STRNCPY(ved->info, "Error: No shader storage buffer support");
|
||||
return;
|
||||
}
|
||||
if (DRW_state_is_select() || DRW_state_is_depth()) {
|
||||
return;
|
||||
}
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
const DRWView *default_view = DRW_view_default_get();
|
||||
draw::Manager *manager = DRW_manager_get();
|
||||
draw::View view("DefaultView", default_view);
|
||||
ved->instance->draw_viewport(*manager, view, dtxl->depth, dtxl->color);
|
||||
}
|
||||
|
||||
static void gpencil_cache_init(void *vedata)
|
||||
{
|
||||
if (!GPU_shader_storage_buffer_objects_support()) {
|
||||
return;
|
||||
}
|
||||
draw::Manager *manager = DRW_manager_get();
|
||||
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->begin_sync(*manager);
|
||||
}
|
||||
|
||||
static void gpencil_cache_populate(void *vedata, Object *object)
|
||||
{
|
||||
if (!GPU_shader_storage_buffer_objects_support()) {
|
||||
return;
|
||||
}
|
||||
draw::Manager *manager = DRW_manager_get();
|
||||
|
||||
draw::ObjectRef ref;
|
||||
ref.object = object;
|
||||
ref.dupli_object = DRW_object_get_dupli(object);
|
||||
ref.dupli_parent = DRW_object_get_dupli_parent(object);
|
||||
|
||||
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->object_sync(*manager, ref);
|
||||
}
|
||||
|
||||
static void gpencil_cache_finish(void *vedata)
|
||||
{
|
||||
if (!GPU_shader_storage_buffer_objects_support()) {
|
||||
return;
|
||||
}
|
||||
draw::Manager *manager = DRW_manager_get();
|
||||
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->end_sync(*manager);
|
||||
}
|
||||
|
||||
static void gpencil_instance_free(void *instance)
|
||||
{
|
||||
if (!GPU_shader_storage_buffer_objects_support()) {
|
||||
return;
|
||||
}
|
||||
delete reinterpret_cast<draw::greasepencil::Instance *>(instance);
|
||||
}
|
||||
|
||||
static void gpencil_engine_free()
|
||||
{
|
||||
blender::draw::greasepencil::ShaderModule::module_free();
|
||||
}
|
||||
|
||||
static void gpencil_render_to_image(void * /*vedata*/,
|
||||
struct RenderEngine * /*engine*/,
|
||||
struct RenderLayer * /*layer*/,
|
||||
const struct rcti * /*rect*/)
|
||||
{
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
static const DrawEngineDataSize gpencil_data_size = DRW_VIEWPORT_DATA_SIZE(GPENCIL_NEXT_Data);
|
||||
|
||||
DrawEngineType draw_engine_gpencil_next_type = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
N_("Gpencil"),
|
||||
&gpencil_data_size,
|
||||
&gpencil_engine_init,
|
||||
&gpencil_engine_free,
|
||||
&gpencil_instance_free,
|
||||
&gpencil_cache_init,
|
||||
&gpencil_cache_populate,
|
||||
&gpencil_cache_finish,
|
||||
&gpencil_draw_scene,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&gpencil_render_to_image,
|
||||
nullptr,
|
||||
};
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -25,6 +25,7 @@ extern "C" {
|
||||
#include "gpencil_shader_shared.h"
|
||||
|
||||
extern DrawEngineType draw_engine_gpencil_type;
|
||||
extern DrawEngineType draw_engine_gpencil_next_type;
|
||||
|
||||
struct GPENCIL_Data;
|
||||
struct GPENCIL_StorageList;
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "DRW_gpu_wrapper.hh"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_pass.hh"
|
||||
|
||||
namespace blender::draw::greasepencil {
|
||||
|
||||
using namespace draw;
|
||||
|
||||
class LayerModule {
|
||||
private:
|
||||
/** Contains all Objects in the scene. Indexed by gpObject.layer_offset + layer_id. */
|
||||
StorageVectorBuffer<gpLayer> layers_buf_ = "gp_layers_buf";
|
||||
|
||||
public:
|
||||
void begin_sync()
|
||||
{
|
||||
layers_buf_.clear();
|
||||
}
|
||||
|
||||
void sync(const Object * /*object*/,
|
||||
const bke::greasepencil::Layer & /*layer*/,
|
||||
bool &do_layer_blending)
|
||||
{
|
||||
/* TODO(fclem): All of this is placeholder. */
|
||||
gpLayer gp_layer;
|
||||
gp_layer.vertex_color_opacity = 0.0f;
|
||||
gp_layer.opacity = 1.0f;
|
||||
gp_layer.thickness_offset = 0.0f;
|
||||
gp_layer.tint = float4(1.0f, 1.0f, 1.0f, 0.0f);
|
||||
gp_layer.stroke_index_offset = 0.0f;
|
||||
|
||||
layers_buf_.append(gp_layer);
|
||||
|
||||
do_layer_blending = false;
|
||||
}
|
||||
|
||||
void end_sync()
|
||||
{
|
||||
layers_buf_.push_update();
|
||||
}
|
||||
|
||||
void bind_resources(PassMain::Sub &sub)
|
||||
{
|
||||
sub.bind_ssbo(GPENCIL_LAYER_SLOT, &layers_buf_);
|
||||
}
|
||||
|
||||
uint object_offset_get() const
|
||||
{
|
||||
return layers_buf_.size();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::draw::greasepencil
|
||||
@@ -0,0 +1,130 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2022 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_image.h"
|
||||
#include "DRW_gpu_wrapper.hh"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_pass.hh"
|
||||
|
||||
namespace blender::draw::greasepencil {
|
||||
|
||||
using namespace draw;
|
||||
|
||||
class LightModule {
|
||||
private:
|
||||
/** Contains all lights in the scene. */
|
||||
StorageVectorBuffer<gpLight> lights_buf_ = "gp_lights_buf";
|
||||
|
||||
float studiolight_intensity_ = 1.0f;
|
||||
bool use_scene_lights_ = true;
|
||||
bool use_scene_world_ = true;
|
||||
|
||||
public:
|
||||
void init(const View3D *v3d)
|
||||
{
|
||||
if (v3d != nullptr) {
|
||||
use_scene_lights_ = V3D_USES_SCENE_LIGHTS(v3d);
|
||||
use_scene_world_ = V3D_USES_SCENE_WORLD(v3d);
|
||||
studiolight_intensity_ = v3d->shading.studiolight_intensity;
|
||||
}
|
||||
}
|
||||
|
||||
void begin_sync(Depsgraph *depsgraph)
|
||||
{
|
||||
lights_buf_.clear();
|
||||
|
||||
World *world = DEG_get_evaluated_scene(depsgraph)->world;
|
||||
if (world != nullptr && use_scene_world_) {
|
||||
ambient_sync(float3(world->horr, world->horg, world->horb));
|
||||
}
|
||||
else {
|
||||
ambient_sync(float3(studiolight_intensity_));
|
||||
}
|
||||
}
|
||||
|
||||
void sync(ObjectRef &object_ref)
|
||||
{
|
||||
if (!use_scene_lights_) {
|
||||
return;
|
||||
}
|
||||
const Object *ob = object_ref.object;
|
||||
const Light *la = static_cast<Light *>(ob->data);
|
||||
|
||||
float light_power;
|
||||
if (la->type == LA_AREA) {
|
||||
light_power = 1.0f / (4.0f * M_PI);
|
||||
}
|
||||
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
|
||||
light_power = 1.0f / (4.0f * M_PI * M_PI);
|
||||
}
|
||||
else {
|
||||
light_power = 1.0f / M_PI;
|
||||
}
|
||||
|
||||
gpLight light;
|
||||
float4x4 &mat = *reinterpret_cast<float4x4 *>(&light.right);
|
||||
switch (la->type) {
|
||||
case LA_SPOT:
|
||||
light.type = GP_LIGHT_TYPE_SPOT;
|
||||
light.spot_size = cosf(la->spotsize * 0.5f);
|
||||
light.spot_blend = (1.0f - light.spot_size) * la->spotblend;
|
||||
mat = float4x4(ob->world_to_object);
|
||||
break;
|
||||
case LA_AREA:
|
||||
/* Simulate area lights using a spot light. */
|
||||
light.type = GP_LIGHT_TYPE_SPOT;
|
||||
light.spot_size = cosf(M_PI_2);
|
||||
light.spot_blend = (1.0f - light.spot_size) * 1.0f;
|
||||
normalize_m4_m4(mat.ptr(), ob->object_to_world);
|
||||
invert_m4(mat.ptr());
|
||||
break;
|
||||
case LA_SUN:
|
||||
light.forward = math::normalize(float3(ob->object_to_world[2]));
|
||||
light.type = GP_LIGHT_TYPE_SUN;
|
||||
break;
|
||||
default:
|
||||
light.type = GP_LIGHT_TYPE_POINT;
|
||||
break;
|
||||
}
|
||||
light.position = float3(object_ref.object->object_to_world[3]);
|
||||
light.color = float3(la->r, la->g, la->b) * (la->energy * light_power);
|
||||
|
||||
lights_buf_.append(light);
|
||||
}
|
||||
|
||||
void end_sync()
|
||||
{
|
||||
/* Tag light list end. */
|
||||
gpLight light;
|
||||
light.color[0] = -1.0f;
|
||||
lights_buf_.append(light);
|
||||
|
||||
lights_buf_.push_update();
|
||||
}
|
||||
|
||||
void bind_resources(PassMain::Sub &sub)
|
||||
{
|
||||
sub.bind_ssbo(GPENCIL_LIGHT_SLOT, &lights_buf_);
|
||||
}
|
||||
|
||||
private:
|
||||
void ambient_sync(float3 color)
|
||||
{
|
||||
gpLight light;
|
||||
light.type = GP_LIGHT_TYPE_AMBIENT;
|
||||
light.color = color;
|
||||
|
||||
lights_buf_.append(light);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::draw::greasepencil
|
||||
@@ -0,0 +1,321 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2022 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_image.h"
|
||||
#include "DRW_gpu_wrapper.hh"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_pass.hh"
|
||||
|
||||
namespace blender::draw::greasepencil {
|
||||
|
||||
using namespace draw;
|
||||
|
||||
class MaterialModule {
|
||||
private:
|
||||
/** Contains all materials in the scene. Indexed by gpObject.material_offset + mat_id. */
|
||||
StorageVectorBuffer<gpMaterial> materials_buf_ = "gp_materials_buf";
|
||||
/** List of all the texture used. */
|
||||
Vector<GPUTexture *> texture_pool_;
|
||||
|
||||
int v3d_color_type_ = -1;
|
||||
int v3d_lighting_mode_ = V3D_LIGHTING_STUDIO;
|
||||
float v3d_xray_alpha_ = 1.0f;
|
||||
float3 v3d_single_color_ = {1.0f, 1.0f, 1.0f};
|
||||
|
||||
public:
|
||||
void init(const View3D *v3d)
|
||||
{
|
||||
if (v3d != nullptr) {
|
||||
const bool shading_mode_supports_xray = (v3d->shading.type <= OB_SOLID);
|
||||
v3d_color_type_ = (v3d->shading.type == OB_SOLID) ? v3d->shading.color_type : -1;
|
||||
v3d_lighting_mode_ = v3d->shading.light;
|
||||
v3d_xray_alpha_ = (shading_mode_supports_xray && XRAY_ENABLED(v3d)) ? XRAY_ALPHA(v3d) : 1.0f;
|
||||
v3d_single_color_ = float3(v3d->shading.single_color);
|
||||
}
|
||||
}
|
||||
|
||||
void begin_sync()
|
||||
{
|
||||
materials_buf_.clear();
|
||||
texture_pool_.clear();
|
||||
}
|
||||
|
||||
void sync(const Object *object, const int mat_slot, bool &do_mat_holdout)
|
||||
{
|
||||
const MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings((Object *)object,
|
||||
mat_slot + 1);
|
||||
|
||||
MaterialGPencilStyle gp_style_override;
|
||||
|
||||
gp_style = material_override(object, &gp_style_override, gp_style);
|
||||
|
||||
/* Material with holdout. */
|
||||
if (gp_style->flag & GP_MATERIAL_IS_STROKE_HOLDOUT) {
|
||||
do_mat_holdout = true;
|
||||
}
|
||||
if (gp_style->flag & GP_MATERIAL_IS_FILL_HOLDOUT) {
|
||||
do_mat_holdout = true;
|
||||
}
|
||||
|
||||
materials_buf_.append(material_sync(gp_style));
|
||||
}
|
||||
|
||||
void end_sync()
|
||||
{
|
||||
materials_buf_.push_update();
|
||||
}
|
||||
|
||||
void bind_resources(PassMain::Sub &sub)
|
||||
{
|
||||
sub.bind_ssbo(GPENCIL_MATERIAL_SLOT, &materials_buf_);
|
||||
}
|
||||
|
||||
uint object_offset_get() const
|
||||
{
|
||||
return materials_buf_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
/* Returns the correct flag for this texture. */
|
||||
gpMaterialFlag texture_sync(::Image *image, gpMaterialFlag use_flag, gpMaterialFlag premul_flag)
|
||||
{
|
||||
ImBuf *ibuf;
|
||||
ImageUser iuser = {nullptr};
|
||||
GPUTexture *gpu_tex = nullptr;
|
||||
void *lock;
|
||||
bool premul = false;
|
||||
|
||||
if (image == nullptr) {
|
||||
texture_pool_.append(nullptr);
|
||||
return GP_FLAG_NONE;
|
||||
}
|
||||
|
||||
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
|
||||
|
||||
if (ibuf != nullptr) {
|
||||
gpu_tex = BKE_image_get_gpu_texture(image, &iuser, ibuf);
|
||||
premul = (image->alpha_mode == IMA_ALPHA_PREMUL) != 0;
|
||||
}
|
||||
BKE_image_release_ibuf(image, ibuf, lock);
|
||||
|
||||
texture_pool_.append(gpu_tex);
|
||||
|
||||
return gpMaterialFlag(use_flag | (premul ? premul_flag : GP_FLAG_NONE));
|
||||
}
|
||||
|
||||
void uv_transform_sync(const float ofs[2],
|
||||
const float scale[2],
|
||||
const float rotation,
|
||||
float r_rot_scale[2][2],
|
||||
float r_offset[2])
|
||||
{
|
||||
/* OPTI this could use 3x2 matrices and reduce the number of operations drastically. */
|
||||
float mat[4][4];
|
||||
unit_m4(mat);
|
||||
/* Offset to center. */
|
||||
translate_m4(mat, 0.5f, 0.5f, 0.0f);
|
||||
/* Reversed order. */
|
||||
float3 tmp = {1.0f / scale[0], 1.0f / scale[1], 0.0};
|
||||
rescale_m4(mat, tmp);
|
||||
rotate_m4(mat, 'Z', -rotation);
|
||||
translate_m4(mat, ofs[0], ofs[1], 0.0f);
|
||||
/* Convert to 3x2 */
|
||||
copy_v2_v2(r_rot_scale[0], mat[0]);
|
||||
copy_v2_v2(r_rot_scale[1], mat[1]);
|
||||
copy_v2_v2(r_offset, mat[3]);
|
||||
}
|
||||
|
||||
/* Amend object fill color in order to avoid completely flat look. */
|
||||
void material_shade_color(float color[3])
|
||||
{
|
||||
if (v3d_lighting_mode_ == V3D_LIGHTING_FLAT) {
|
||||
return;
|
||||
}
|
||||
/* This is scene referred color, not gamma corrected and not per perceptual.
|
||||
* So we lower the threshold a bit. (1.0 / 3.0) */
|
||||
if (color[0] + color[1] + color[2] > 1.1) {
|
||||
add_v3_fl(color, -0.25f);
|
||||
}
|
||||
else {
|
||||
add_v3_fl(color, 0.15f);
|
||||
}
|
||||
CLAMP3(color, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
const MaterialGPencilStyle *material_override(const Object *object,
|
||||
MaterialGPencilStyle *gp_style_override,
|
||||
const MaterialGPencilStyle *gp_style)
|
||||
{
|
||||
switch (v3d_color_type_) {
|
||||
case V3D_SHADING_MATERIAL_COLOR:
|
||||
case V3D_SHADING_RANDOM_COLOR:
|
||||
/* Random uses a random color per layer and this is done using the layer tint.
|
||||
* A simple color by object, like meshes, is not practical in grease pencil. */
|
||||
copy_v4_v4(gp_style_override->stroke_rgba, gp_style->stroke_rgba);
|
||||
copy_v4_v4(gp_style_override->fill_rgba, gp_style->fill_rgba);
|
||||
gp_style = gp_style_override;
|
||||
gp_style_override->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID;
|
||||
gp_style_override->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
|
||||
break;
|
||||
case V3D_SHADING_TEXTURE_COLOR:
|
||||
*gp_style_override = blender::dna::shallow_copy(*gp_style);
|
||||
gp_style = gp_style_override;
|
||||
if ((gp_style_override->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) &&
|
||||
(gp_style_override->sima))
|
||||
{
|
||||
copy_v4_fl(gp_style_override->stroke_rgba, 1.0f);
|
||||
gp_style_override->mix_stroke_factor = 0.0f;
|
||||
}
|
||||
|
||||
if ((gp_style_override->fill_style == GP_MATERIAL_FILL_STYLE_TEXTURE) &&
|
||||
(gp_style_override->ima)) {
|
||||
copy_v4_fl(gp_style_override->fill_rgba, 1.0f);
|
||||
gp_style_override->mix_factor = 0.0f;
|
||||
}
|
||||
else if (gp_style_override->fill_style == GP_MATERIAL_FILL_STYLE_GRADIENT) {
|
||||
/* gp_style_override->fill_rgba is needed for correct gradient. */
|
||||
gp_style_override->mix_factor = 0.0f;
|
||||
}
|
||||
break;
|
||||
case V3D_SHADING_SINGLE_COLOR:
|
||||
gp_style = gp_style_override;
|
||||
gp_style_override->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID;
|
||||
gp_style_override->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
|
||||
copy_v3_v3(gp_style_override->fill_rgba, v3d_single_color_);
|
||||
gp_style_override->fill_rgba[3] = 1.0f;
|
||||
copy_v4_v4(gp_style_override->stroke_rgba, gp_style_override->fill_rgba);
|
||||
material_shade_color(gp_style_override->fill_rgba);
|
||||
break;
|
||||
case V3D_SHADING_OBJECT_COLOR:
|
||||
gp_style = gp_style_override;
|
||||
gp_style_override->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID;
|
||||
gp_style_override->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
|
||||
copy_v4_v4(gp_style_override->fill_rgba, object->color);
|
||||
copy_v4_v4(gp_style_override->stroke_rgba, object->color);
|
||||
material_shade_color(gp_style_override->fill_rgba);
|
||||
break;
|
||||
case V3D_SHADING_VERTEX_COLOR:
|
||||
gp_style = gp_style_override;
|
||||
gp_style_override->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID;
|
||||
gp_style_override->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
|
||||
copy_v4_fl(gp_style_override->fill_rgba, 1.0f);
|
||||
copy_v4_fl(gp_style_override->stroke_rgba, 1.0f);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return gp_style;
|
||||
}
|
||||
|
||||
gpMaterial material_sync(const MaterialGPencilStyle *gp_style)
|
||||
{
|
||||
gpMaterial material;
|
||||
material.flag = 0;
|
||||
|
||||
/* Dots/Square alignment. */
|
||||
if (gp_style->mode != GP_MATERIAL_MODE_LINE) {
|
||||
switch (gp_style->alignment_mode) {
|
||||
case GP_MATERIAL_FOLLOW_PATH:
|
||||
material.flag = GP_STROKE_ALIGNMENT_STROKE;
|
||||
break;
|
||||
case GP_MATERIAL_FOLLOW_OBJ:
|
||||
material.flag = GP_STROKE_ALIGNMENT_OBJECT;
|
||||
break;
|
||||
case GP_MATERIAL_FOLLOW_FIXED:
|
||||
default:
|
||||
material.flag = GP_STROKE_ALIGNMENT_FIXED;
|
||||
break;
|
||||
}
|
||||
if (gp_style->mode == GP_MATERIAL_MODE_DOT) {
|
||||
material.flag |= GP_STROKE_DOTS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Overlap. */
|
||||
if ((gp_style->mode != GP_MATERIAL_MODE_LINE) ||
|
||||
(gp_style->flag & GP_MATERIAL_DISABLE_STENCIL)) {
|
||||
material.flag |= GP_STROKE_OVERLAP;
|
||||
}
|
||||
|
||||
/* Material with holdout. */
|
||||
if (gp_style->flag & GP_MATERIAL_IS_STROKE_HOLDOUT) {
|
||||
material.flag |= GP_STROKE_HOLDOUT;
|
||||
}
|
||||
if (gp_style->flag & GP_MATERIAL_IS_FILL_HOLDOUT) {
|
||||
material.flag |= GP_FILL_HOLDOUT;
|
||||
}
|
||||
|
||||
/* Dots or Squares rotation. */
|
||||
material.alignment_rot[0] = cosf(gp_style->alignment_rotation);
|
||||
material.alignment_rot[1] = sinf(gp_style->alignment_rotation);
|
||||
|
||||
if (gp_style->flag & GP_MATERIAL_STROKE_SHOW) {
|
||||
material.flag |= GP_SHOW_STROKE;
|
||||
}
|
||||
if (gp_style->flag & GP_MATERIAL_FILL_SHOW) {
|
||||
material.flag |= GP_SHOW_FILL;
|
||||
}
|
||||
|
||||
/* Stroke Style */
|
||||
if ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && (gp_style->sima)) {
|
||||
material.flag |= texture_sync(
|
||||
gp_style->sima, GP_STROKE_TEXTURE_USE, GP_STROKE_TEXTURE_PREMUL);
|
||||
copy_v4_v4(material.stroke_color, gp_style->stroke_rgba);
|
||||
material.stroke_texture_mix = 1.0f - gp_style->mix_stroke_factor;
|
||||
material.stroke_u_scale = 500.0f / gp_style->texture_pixsize;
|
||||
}
|
||||
else /* if (gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_SOLID) */ {
|
||||
texture_sync(nullptr, GP_FLAG_NONE, GP_FLAG_NONE);
|
||||
material.flag &= ~GP_STROKE_TEXTURE_USE;
|
||||
copy_v4_v4(material.stroke_color, gp_style->stroke_rgba);
|
||||
material.stroke_texture_mix = 0.0f;
|
||||
}
|
||||
|
||||
/* Fill Style */
|
||||
if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_TEXTURE) && (gp_style->ima)) {
|
||||
material.flag |= texture_sync(gp_style->ima, GP_FILL_TEXTURE_USE, GP_FILL_TEXTURE_PREMUL);
|
||||
material.flag |= (gp_style->flag & GP_MATERIAL_TEX_CLAMP) ? GP_FILL_TEXTURE_CLIP : 0;
|
||||
uv_transform_sync(gp_style->texture_offset,
|
||||
gp_style->texture_scale,
|
||||
gp_style->texture_angle,
|
||||
(float(*)[2]) & material.fill_uv_rot_scale[0],
|
||||
material.fill_uv_offset);
|
||||
copy_v4_v4(material.fill_color, gp_style->fill_rgba);
|
||||
material.fill_texture_mix = 1.0f - gp_style->mix_factor;
|
||||
}
|
||||
else if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_GRADIENT) {
|
||||
texture_sync(nullptr, GP_FLAG_NONE, GP_FLAG_NONE);
|
||||
bool use_radial = (gp_style->gradient_type == GP_MATERIAL_GRADIENT_RADIAL);
|
||||
material.flag |= GP_FILL_GRADIENT_USE;
|
||||
material.flag |= use_radial ? GP_FILL_GRADIENT_RADIAL : 0;
|
||||
uv_transform_sync(gp_style->texture_offset,
|
||||
gp_style->texture_scale,
|
||||
gp_style->texture_angle,
|
||||
(float(*)[2]) & material.fill_uv_rot_scale[0],
|
||||
material.fill_uv_offset);
|
||||
copy_v4_v4(material.fill_color, gp_style->fill_rgba);
|
||||
copy_v4_v4(material.fill_mix_color, gp_style->mix_rgba);
|
||||
material.fill_texture_mix = 1.0f - gp_style->mix_factor;
|
||||
if (gp_style->flag & GP_MATERIAL_FLIP_FILL) {
|
||||
swap_v4_v4(material.fill_color, material.fill_mix_color);
|
||||
}
|
||||
}
|
||||
else /* if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID) */ {
|
||||
texture_sync(nullptr, GP_FLAG_NONE, GP_FLAG_NONE);
|
||||
copy_v4_v4(material.fill_color, gp_style->fill_rgba);
|
||||
material.fill_texture_mix = 0.0f;
|
||||
}
|
||||
return material;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::draw::greasepencil
|
||||
@@ -0,0 +1,251 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2022 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_image.h"
|
||||
#include "DRW_gpu_wrapper.hh"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_pass.hh"
|
||||
|
||||
#include "gpencil_layer.hh"
|
||||
#include "gpencil_material.hh"
|
||||
#include "gpencil_shader.hh"
|
||||
|
||||
namespace blender::draw::greasepencil {
|
||||
|
||||
using namespace draw;
|
||||
|
||||
class ObjectModule {
|
||||
private:
|
||||
LayerModule &layers_;
|
||||
MaterialModule &materials_;
|
||||
ShaderModule &shaders_;
|
||||
|
||||
/** Contains all Objects in the scene. Indexed by drw_ResourceID. */
|
||||
StorageArrayBuffer<gpObject> objects_buf_ = "gp_objects_buf";
|
||||
|
||||
/** Contains all gpencil objects in the scene as well as their effect sub-passes. */
|
||||
PassSortable main_ps_ = {"gp_main_ps"};
|
||||
|
||||
/** Contains all composited GPencil layers from one object if is uses VFX. */
|
||||
TextureFromPool object_color_tx_ = {"gp_color_object_tx"};
|
||||
TextureFromPool object_reveal_tx_ = {"gp_reveal_object_tx"};
|
||||
Framebuffer object_fb_ = {"gp_object_fb"};
|
||||
bool is_object_fb_needed_ = false;
|
||||
|
||||
/** Contains all strokes from one layer if is uses blending. (also used as target for VFX) */
|
||||
TextureFromPool layer_color_tx_ = {"gp_color_layer_tx"};
|
||||
TextureFromPool layer_reveal_tx_ = {"gp_reveal_layer_tx"};
|
||||
Framebuffer layer_fb_ = {"gp_layer_fb"};
|
||||
bool is_layer_fb_needed_ = false;
|
||||
|
||||
bool use_onion_ = true;
|
||||
bool use_stroke_fill_ = true;
|
||||
bool use_vfx_ = true;
|
||||
bool is_render_ = true;
|
||||
/** Forward vector used to sort gpencil objects. */
|
||||
float3 camera_forward_;
|
||||
/** Scene current frame. */
|
||||
float current_frame_ = 0;
|
||||
|
||||
/** \note Needs not to be temporary variable since it is dereferenced later. */
|
||||
std::array<float4, 2> clear_colors_ = {float4(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
float4(1.0f, 1.0f, 1.0f, 1.0f)};
|
||||
|
||||
public:
|
||||
ObjectModule(LayerModule &layers, MaterialModule &materials, ShaderModule &shaders)
|
||||
: layers_(layers), materials_(materials), shaders_(shaders){};
|
||||
|
||||
void init(const View3D *v3d, const Scene *scene)
|
||||
{
|
||||
const bool is_viewport = (v3d != nullptr);
|
||||
|
||||
if (is_viewport) {
|
||||
/* TODO(fclem): Avoid access to global DRW. */
|
||||
const struct bContext *evil_C = DRW_context_state_get()->evil_C;
|
||||
const bool playing = (evil_C != nullptr) ?
|
||||
ED_screen_animation_playing(CTX_wm_manager(evil_C)) != nullptr :
|
||||
false;
|
||||
const bool hide_overlay = ((v3d->flag2 & V3D_HIDE_OVERLAYS) != 0);
|
||||
const bool show_onion = ((v3d->gp_flag & V3D_GP_SHOW_ONION_SKIN) != 0);
|
||||
use_onion_ = show_onion && !hide_overlay && !playing;
|
||||
use_stroke_fill_ = GPENCIL_SIMPLIFY_FILL(scene, playing);
|
||||
use_vfx_ = GPENCIL_SIMPLIFY_FX(scene, playing);
|
||||
is_render_ = false;
|
||||
}
|
||||
else {
|
||||
use_stroke_fill_ = GPENCIL_SIMPLIFY_FILL(scene, false);
|
||||
use_vfx_ = GPENCIL_SIMPLIFY_FX(scene, false);
|
||||
}
|
||||
}
|
||||
|
||||
void begin_sync(Depsgraph *depsgraph, const View &main_view)
|
||||
{
|
||||
camera_forward_ = float3(main_view.viewinv()[2]);
|
||||
current_frame_ = DEG_get_ctime(depsgraph);
|
||||
|
||||
is_object_fb_needed_ = false;
|
||||
is_layer_fb_needed_ = false;
|
||||
|
||||
/* TODO(fclem): Shrink buffer. */
|
||||
// objects_buf_.shrink();
|
||||
}
|
||||
|
||||
void sync_grease_pencil(Manager &manager,
|
||||
ObjectRef &object_ref,
|
||||
Framebuffer &main_fb,
|
||||
PassSortable &main_ps)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
||||
Object *object = object_ref.object;
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
|
||||
|
||||
if (grease_pencil.drawings().is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool is_stroke_order_3d = false; /* TODO */
|
||||
bool do_material_holdout = false;
|
||||
bool do_layer_blending = false;
|
||||
bool object_has_vfx = false; // TODO: vfx.object_has_vfx(gpd);
|
||||
|
||||
uint material_offset = materials_.object_offset_get();
|
||||
for (auto i : IndexRange(BKE_object_material_count_eval(object))) {
|
||||
materials_.sync(object, i, do_material_holdout);
|
||||
}
|
||||
|
||||
uint layer_offset = layers_.object_offset_get();
|
||||
for (const Layer *layer : grease_pencil.layers()) {
|
||||
layers_.sync(object, *layer, do_layer_blending);
|
||||
}
|
||||
|
||||
/* Order rendering using camera Z distance. */
|
||||
float3 position = float3(object->object_to_world[3]);
|
||||
float camera_z = math::dot(position, camera_forward_);
|
||||
|
||||
PassMain::Sub &object_subpass = main_ps.sub("GPObject", camera_z);
|
||||
object_subpass.framebuffer_set((object_has_vfx) ? &object_fb_ : &main_fb);
|
||||
object_subpass.clear_depth(is_stroke_order_3d ? 1.0f : 0.0f);
|
||||
if (object_has_vfx) {
|
||||
object_subpass.clear_multi(clear_colors_);
|
||||
}
|
||||
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_BLEND_ALPHA_PREMUL;
|
||||
/* For 2D mode, we render all strokes with uniform depth (increasing with stroke id). */
|
||||
state |= (is_stroke_order_3d) ? DRW_STATE_DEPTH_LESS_EQUAL : DRW_STATE_DEPTH_GREATER;
|
||||
/* Always write stencil. Only used as optimization for blending. */
|
||||
state |= DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS;
|
||||
|
||||
object_subpass.state_set(state);
|
||||
object_subpass.shader_set(shaders_.static_shader_get(GREASE_PENCIL));
|
||||
|
||||
GPUVertBuf *position_tx = DRW_cache_grease_pencil_position_buffer_get(object, current_frame_);
|
||||
GPUVertBuf *color_tx = DRW_cache_grease_pencil_color_buffer_get(object, current_frame_);
|
||||
GPUBatch *geom = DRW_cache_grease_pencil_get(object, current_frame_);
|
||||
|
||||
/* TODO(fclem): Pass per frame object matrix here. */
|
||||
ResourceHandle handle = manager.resource_handle(object_ref);
|
||||
gpObject &ob = objects_buf_.get_or_resize(handle.resource_index());
|
||||
ob.is_shadeless = false;
|
||||
ob.stroke_order3d = false;
|
||||
ob.tint = float4(1.0); // frame_tint_get(gpd, frame.gpf, current_frame_);
|
||||
ob.layer_offset = layer_offset;
|
||||
ob.material_offset = material_offset;
|
||||
|
||||
if (do_layer_blending) {
|
||||
/* TODO: Do layer blending. */
|
||||
// for (const LayerData &layer : frame.layers) {
|
||||
// UNUSED_VARS(layer);
|
||||
// if (has_blending(layer)) {
|
||||
// object_subpass.framebuffer_set(*vfx_fb.current());
|
||||
// }
|
||||
|
||||
/* TODO(fclem): Only draw subrange of geometry for this layer. */
|
||||
object_subpass.draw(geom, handle);
|
||||
|
||||
/* TODO: Do layer blending. */
|
||||
// if (has_blending(layer)) {
|
||||
// layer_blend_sync(object_ref, object_subpass);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
else {
|
||||
/* Fast path. */
|
||||
object_subpass.bind_texture("gp_pos_tx", position_tx);
|
||||
object_subpass.bind_texture("gp_col_tx", color_tx);
|
||||
object_subpass.draw(geom, handle);
|
||||
}
|
||||
|
||||
/* TODO: Do object VFX. */
|
||||
#if 0
|
||||
if (object_has_vfx) {
|
||||
VfxContext vfx_ctx(object_subpass,
|
||||
layer_fb_,
|
||||
object_fb_,
|
||||
object_color_tx_,
|
||||
layer_color_tx_,
|
||||
object_reveal_tx_,
|
||||
layer_reveal_tx_,
|
||||
is_render_);
|
||||
|
||||
/* \note Update this boolean as the actual number of vfx drawn might differ. */
|
||||
object_has_vfx = vfx.object_sync(main_fb_, object_ref, vfx_ctx, do_material_holdout);
|
||||
|
||||
if (object_has_vfx || do_layer_blending) {
|
||||
is_layer_fb_needed_ = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void end_sync()
|
||||
{
|
||||
objects_buf_.push_update();
|
||||
}
|
||||
|
||||
void bind_resources(PassMain::Sub &sub)
|
||||
{
|
||||
sub.bind_ssbo(GPENCIL_OBJECT_SLOT, &objects_buf_);
|
||||
}
|
||||
|
||||
void acquire_temporary_buffers(int2 render_size, eGPUTextureFormat format)
|
||||
{
|
||||
object_color_tx_.acquire(render_size, format);
|
||||
object_reveal_tx_.acquire(render_size, format);
|
||||
object_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(object_color_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(object_reveal_tx_));
|
||||
if (is_layer_fb_needed_) {
|
||||
layer_color_tx_.acquire(render_size, format);
|
||||
layer_reveal_tx_.acquire(render_size, format);
|
||||
layer_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(layer_color_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(layer_reveal_tx_));
|
||||
}
|
||||
}
|
||||
|
||||
void release_temporary_buffers()
|
||||
{
|
||||
object_color_tx_.release();
|
||||
object_reveal_tx_.release();
|
||||
|
||||
layer_color_tx_.release();
|
||||
layer_reveal_tx_.release();
|
||||
}
|
||||
|
||||
bool scene_has_visible_gpencil_object() const
|
||||
{
|
||||
return objects_buf_.size() > 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::draw::greasepencil
|
||||
@@ -0,0 +1,116 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "gpencil_shader.hh"
|
||||
|
||||
namespace blender::draw::greasepencil {
|
||||
|
||||
ShaderModule *ShaderModule::g_shader_module = nullptr;
|
||||
|
||||
ShaderModule *ShaderModule::module_get()
|
||||
{
|
||||
if (g_shader_module == nullptr) {
|
||||
/* TODO(@fclem) thread-safety. */
|
||||
g_shader_module = new ShaderModule();
|
||||
}
|
||||
return g_shader_module;
|
||||
}
|
||||
|
||||
void ShaderModule::module_free()
|
||||
{
|
||||
if (g_shader_module != nullptr) {
|
||||
/* TODO(@fclem) thread-safety. */
|
||||
delete g_shader_module;
|
||||
g_shader_module = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ShaderModule::ShaderModule()
|
||||
{
|
||||
for (GPUShader *&shader : shaders_) {
|
||||
shader = nullptr;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Ensure all shader are described. */
|
||||
for (auto i : IndexRange(MAX_SHADER_TYPE)) {
|
||||
const char *name = static_shader_create_info_name_get(eShaderType(i));
|
||||
if (name == nullptr) {
|
||||
std::cerr << "GPencil: Missing case for eShaderType(" << i
|
||||
<< ") in static_shader_create_info_name_get()." << std::endl;
|
||||
BLI_assert(0);
|
||||
}
|
||||
const GPUShaderCreateInfo *create_info = GPU_shader_create_info_get(name);
|
||||
BLI_assert_msg(create_info != nullptr, "GPencil: Missing create info for static shader.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ShaderModule::~ShaderModule()
|
||||
{
|
||||
for (GPUShader *&shader : shaders_) {
|
||||
DRW_SHADER_FREE_SAFE(shader);
|
||||
}
|
||||
}
|
||||
|
||||
const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_type)
|
||||
{
|
||||
switch (shader_type) {
|
||||
case ANTIALIASING_EDGE_DETECT:
|
||||
return "gpencil_antialiasing_stage_0";
|
||||
case ANTIALIASING_BLEND_WEIGHT:
|
||||
return "gpencil_antialiasing_stage_1";
|
||||
case ANTIALIASING_RESOLVE:
|
||||
return "gpencil_antialiasing_stage_2";
|
||||
case GREASE_PENCIL:
|
||||
return "gpencil_geometry_next";
|
||||
case LAYER_BLEND:
|
||||
return "gpencil_layer_blend";
|
||||
case DEPTH_MERGE:
|
||||
return "gpencil_depth_merge";
|
||||
case MASK_INVERT:
|
||||
return "gpencil_mask_invert";
|
||||
case FX_COMPOSITE:
|
||||
return "gpencil_fx_composite";
|
||||
case FX_COLORIZE:
|
||||
return "gpencil_fx_colorize";
|
||||
case FX_BLUR:
|
||||
return "gpencil_fx_blur";
|
||||
case FX_GLOW:
|
||||
return "gpencil_fx_glow";
|
||||
case FX_PIXEL:
|
||||
return "gpencil_fx_pixelize";
|
||||
case FX_RIM:
|
||||
return "gpencil_fx_rim";
|
||||
case FX_SHADOW:
|
||||
return "gpencil_fx_shadow";
|
||||
case FX_TRANSFORM:
|
||||
return "gpencil_fx_transform";
|
||||
/* To avoid compiler warning about missing case. */
|
||||
case MAX_SHADER_TYPE:
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
GPUShader *ShaderModule::static_shader_get(eShaderType shader_type)
|
||||
{
|
||||
if (shaders_[shader_type] == nullptr) {
|
||||
const char *shader_name = static_shader_create_info_name_get(shader_type);
|
||||
|
||||
shaders_[shader_type] = GPU_shader_create_from_info_name(shader_name);
|
||||
|
||||
if (shaders_[shader_type] == nullptr) {
|
||||
std::cerr << "GPencil: error: Could not compile static shader \"" << shader_name << "\""
|
||||
<< std::endl;
|
||||
}
|
||||
BLI_assert(shaders_[shader_type] != nullptr);
|
||||
}
|
||||
return shaders_[shader_type];
|
||||
}
|
||||
|
||||
} // namespace blender::draw::greasepencil
|
||||
@@ -0,0 +1,65 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
namespace blender::draw::greasepencil {
|
||||
|
||||
enum eShaderType {
|
||||
/* SMAA antialiasing */
|
||||
ANTIALIASING_EDGE_DETECT = 0,
|
||||
ANTIALIASING_BLEND_WEIGHT,
|
||||
ANTIALIASING_RESOLVE,
|
||||
/* GPencil Object rendering */
|
||||
GREASE_PENCIL,
|
||||
/* All layer blend types in one shader! */
|
||||
LAYER_BLEND,
|
||||
/* Merge the final object depth to the depth buffer. */
|
||||
DEPTH_MERGE,
|
||||
/* Invert the content of the mask buffer. */
|
||||
MASK_INVERT,
|
||||
/* Final Compositing over rendered background. */
|
||||
FX_COMPOSITE,
|
||||
/* Effects. */
|
||||
FX_COLORIZE,
|
||||
FX_BLUR,
|
||||
FX_GLOW,
|
||||
FX_PIXEL,
|
||||
FX_RIM,
|
||||
FX_SHADOW,
|
||||
FX_TRANSFORM,
|
||||
|
||||
MAX_SHADER_TYPE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Shader module. shared between instances.
|
||||
*/
|
||||
class ShaderModule {
|
||||
private:
|
||||
std::array<GPUShader *, MAX_SHADER_TYPE> shaders_;
|
||||
|
||||
/** Shared shader module across all engine instances. */
|
||||
static ShaderModule *g_shader_module;
|
||||
|
||||
public:
|
||||
ShaderModule();
|
||||
~ShaderModule();
|
||||
|
||||
GPUShader *static_shader_get(eShaderType shader_type);
|
||||
|
||||
/** Only to be used by Instance constructor. */
|
||||
static ShaderModule *module_get();
|
||||
static void module_free();
|
||||
|
||||
private:
|
||||
const char *static_shader_create_info_name_get(eShaderType shader_type);
|
||||
};
|
||||
|
||||
} // namespace blender::draw::greasepencil
|
||||
@@ -1,11 +1,17 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
# pragma once
|
||||
|
||||
# include "GPU_shader_shared_utils.h"
|
||||
|
||||
# ifndef __cplusplus
|
||||
typedef struct gpScene gpScene;
|
||||
typedef struct gpMaterial gpMaterial;
|
||||
typedef struct gpLight gpLight;
|
||||
typedef struct gpObject gpObject;
|
||||
typedef struct gpLayer gpLayer;
|
||||
typedef enum gpMaterialFlag gpMaterialFlag;
|
||||
# ifdef GP_LIGHT
|
||||
typedef enum gpLightType gpLightType;
|
||||
@@ -14,6 +20,7 @@ typedef enum gpLightType gpLightType;
|
||||
#endif
|
||||
|
||||
enum gpMaterialFlag {
|
||||
GP_FLAG_NONE = 0u,
|
||||
GP_STROKE_ALIGNMENT_STROKE = 1u,
|
||||
GP_STROKE_ALIGNMENT_OBJECT = 2u,
|
||||
GP_STROKE_ALIGNMENT_FIXED = 3u,
|
||||
@@ -30,6 +37,8 @@ enum gpMaterialFlag {
|
||||
GP_FILL_TEXTURE_CLIP = (1u << 12u),
|
||||
GP_FILL_GRADIENT_USE = (1u << 13u),
|
||||
GP_FILL_GRADIENT_RADIAL = (1u << 14u),
|
||||
GP_SHOW_STROKE = (1u << 15u),
|
||||
GP_SHOW_FILL = (1u << 16u),
|
||||
GP_FILL_FLAGS = (GP_FILL_TEXTURE_USE | GP_FILL_TEXTURE_PREMUL | GP_FILL_TEXTURE_CLIP |
|
||||
GP_FILL_GRADIENT_USE | GP_FILL_GRADIENT_RADIAL | GP_FILL_HOLDOUT),
|
||||
};
|
||||
@@ -50,6 +59,12 @@ enum gpLightType {
|
||||
# define gpLightType uint
|
||||
#endif
|
||||
|
||||
struct gpScene {
|
||||
float2 render_size;
|
||||
float2 _pad0;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(gpScene, 16)
|
||||
|
||||
struct gpMaterial {
|
||||
float4 stroke_color;
|
||||
float4 fill_color;
|
||||
@@ -116,6 +131,38 @@ struct gpLight {
|
||||
BLI_STATIC_ASSERT_ALIGN(gpLight, 16)
|
||||
#endif
|
||||
|
||||
struct gpObject {
|
||||
/** Wether or not to apply lighting to the GPencil object. */
|
||||
bool1 is_shadeless;
|
||||
/** Switch between 2d and 3D stroke order. */
|
||||
bool1 stroke_order3d;
|
||||
/** Offset inside the layer buffer to the first layer data of this object. */
|
||||
uint layer_offset;
|
||||
/** Offset inside the material buffer to the first material data of this object. */
|
||||
uint material_offset;
|
||||
/** Color to multiply to the final mixed color. */
|
||||
float4 tint;
|
||||
/** Color to multiply to the final mixed color. */
|
||||
float3 normal;
|
||||
|
||||
float _pad0;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(gpObject, 16)
|
||||
|
||||
struct gpLayer {
|
||||
/** Amount of vertex color to blend with actual material color. */
|
||||
float vertex_color_opacity;
|
||||
/** Thickness change of all the strokes. */
|
||||
float thickness_offset;
|
||||
/** Thickness change of all the strokes. */
|
||||
float opacity;
|
||||
/** Offset to apply to stroke index to be able to insert a currently drawn stroke in between. */
|
||||
float stroke_index_offset;
|
||||
/** Color to multiply to the final mixed color. */
|
||||
float4 tint;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(gpLayer, 16)
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
# undef gpMaterialFlag
|
||||
# undef gpLightType
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2017 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
#include "BKE_camera.h"
|
||||
#include "BLI_listbase_wrapper.hh"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_gpencil_legacy_types.h"
|
||||
#include "DNA_shader_fx_types.h"
|
||||
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_pass.hh"
|
||||
|
||||
#include "gpencil_engine.h"
|
||||
#include "gpencil_shader.hh"
|
||||
#include "gpencil_shader_shared.h"
|
||||
|
||||
namespace blender::draw::greasepencil {
|
||||
|
||||
using namespace draw;
|
||||
|
||||
struct VfxContext {
|
||||
PassMain::Sub *object_subpass;
|
||||
SwapChain<GPUFrameBuffer **, 2> vfx_fb;
|
||||
SwapChain<GPUTexture **, 2> color_tx;
|
||||
SwapChain<GPUTexture **, 2> reveal_tx;
|
||||
bool is_viewport;
|
||||
|
||||
VfxContext(PassMain::Sub &object_subpass_,
|
||||
Framebuffer &layer_fb,
|
||||
Framebuffer &object_fb,
|
||||
TextureFromPool &object_color_tx,
|
||||
TextureFromPool &layer_color_tx,
|
||||
TextureFromPool &object_reveal_tx,
|
||||
TextureFromPool &layer_reveal_tx,
|
||||
bool is_render_)
|
||||
{
|
||||
object_subpass = &object_subpass_;
|
||||
/* These may not be allocated yet, use address of future pointer. */
|
||||
vfx_fb.current() = &layer_fb;
|
||||
vfx_fb.next() = &object_fb;
|
||||
|
||||
color_tx.current() = &object_color_tx;
|
||||
color_tx.next() = &layer_color_tx;
|
||||
reveal_tx.current() = &object_reveal_tx;
|
||||
reveal_tx.next() = &layer_reveal_tx;
|
||||
|
||||
is_viewport = (is_render_ == false);
|
||||
}
|
||||
|
||||
PassMain::Sub &create_vfx_pass(const char *name, GPUShader *shader)
|
||||
{
|
||||
PassMain::Sub &sub = object_subpass->sub(name);
|
||||
sub.framebuffer_set(vfx_fb.current());
|
||||
sub.shader_set(shader);
|
||||
sub.bind_texture("colorBuf", color_tx.current());
|
||||
sub.bind_texture("revealBuf", reveal_tx.current());
|
||||
|
||||
vfx_fb.swap();
|
||||
color_tx.swap();
|
||||
reveal_tx.swap();
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
/* Verify if the given fx is active. */
|
||||
bool effect_is_active(const bGPdata *gpd, const ShaderFxData *fx)
|
||||
{
|
||||
if (fx == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gpd == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
|
||||
if (((fx->mode & eShaderFxMode_Editmode) == 0) && (is_edit) && (is_viewport)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (((fx->mode & eShaderFxMode_Realtime) && (is_viewport == true)) ||
|
||||
((fx->mode & eShaderFxMode_Render) && (is_viewport == false)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class VfxModule {
|
||||
private:
|
||||
ShaderModule &shaders;
|
||||
/* Global switch for all vfx. */
|
||||
bool vfx_enabled_ = false;
|
||||
/* Global switch for all Depth Of Field blur. */
|
||||
bool dof_enabled_ = false;
|
||||
/* Pseudo depth of field parameter. Used to scale blur radius. */
|
||||
float dof_parameters_[2];
|
||||
|
||||
public:
|
||||
VfxModule(ShaderModule &shaders_) : shaders(shaders_){};
|
||||
|
||||
void init(bool enable, const Object *camera_object, const RegionView3D *rv3d)
|
||||
{
|
||||
vfx_enabled_ = enable;
|
||||
|
||||
const Camera *camera = (camera_object != nullptr) ?
|
||||
static_cast<const Camera *>(camera_object->data) :
|
||||
nullptr;
|
||||
|
||||
/* Pseudo DOF setup. */
|
||||
if (camera && (camera->dof.flag & CAM_DOF_ENABLED)) {
|
||||
const float *vp_size = DRW_viewport_size_get();
|
||||
float fstop = camera->dof.aperture_fstop;
|
||||
float sensor = BKE_camera_sensor_size(
|
||||
camera->sensor_fit, camera->sensor_x, camera->sensor_y);
|
||||
float focus_dist = BKE_camera_object_dof_distance(camera_object);
|
||||
float focal_len = camera->lens;
|
||||
|
||||
const float scale_camera = 0.001f;
|
||||
/* We want radius here for the aperture number. */
|
||||
float aperture = 0.5f * scale_camera * focal_len / fstop;
|
||||
float focal_len_scaled = scale_camera * focal_len;
|
||||
float sensor_scaled = scale_camera * sensor;
|
||||
|
||||
if (rv3d != nullptr) {
|
||||
sensor_scaled *= rv3d->viewcamtexcofac[0];
|
||||
}
|
||||
|
||||
dof_parameters_[1] = aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled));
|
||||
dof_parameters_[1] *= vp_size[0] / sensor_scaled;
|
||||
dof_parameters_[0] = -focus_dist * dof_parameters_[1];
|
||||
}
|
||||
else {
|
||||
/* Disable DoF blur scaling. Produce Circle of Confusion of 0 pixel. */
|
||||
dof_parameters_[0] = dof_parameters_[1] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return true if any vfx is needed */
|
||||
bool object_sync(Framebuffer &main_fb,
|
||||
ObjectRef &object_ref,
|
||||
VfxContext &vfx_ctx,
|
||||
bool do_material_holdout)
|
||||
{
|
||||
Object *object = object_ref.object;
|
||||
bGPdata *gpd = (bGPdata *)object->data;
|
||||
|
||||
int vfx_count = 0;
|
||||
|
||||
if (vfx_enabled_) {
|
||||
for (const ShaderFxData *fx : ListBaseWrapper<const ShaderFxData>(&object->shader_fx)) {
|
||||
if (!vfx_ctx.effect_is_active(gpd, fx)) {
|
||||
continue;
|
||||
}
|
||||
switch (fx->type) {
|
||||
case eShaderFxType_Blur:
|
||||
vfx_count += vfx_blur(*(const BlurShaderFxData *)fx, object, vfx_ctx);
|
||||
break;
|
||||
case eShaderFxType_Colorize:
|
||||
vfx_count += vfx_colorize(*(const ColorizeShaderFxData *)fx, object, vfx_ctx);
|
||||
break;
|
||||
case eShaderFxType_Flip:
|
||||
vfx_count += vfx_flip(*(const FlipShaderFxData *)fx, object, vfx_ctx);
|
||||
break;
|
||||
case eShaderFxType_Pixel:
|
||||
vfx_count += vfx_pixelize(*(const PixelShaderFxData *)fx, object, vfx_ctx);
|
||||
break;
|
||||
case eShaderFxType_Rim:
|
||||
vfx_count += vfx_rim(*(const RimShaderFxData *)fx, object, vfx_ctx);
|
||||
break;
|
||||
case eShaderFxType_Shadow:
|
||||
vfx_count += vfx_shadow(*(const ShadowShaderFxData *)fx, object, vfx_ctx);
|
||||
break;
|
||||
case eShaderFxType_Glow:
|
||||
vfx_count += vfx_glow(*(const GlowShaderFxData *)fx, object, vfx_ctx);
|
||||
break;
|
||||
case eShaderFxType_Swirl:
|
||||
vfx_count += vfx_swirl(*(const SwirlShaderFxData *)fx, object, vfx_ctx);
|
||||
break;
|
||||
case eShaderFxType_Wave:
|
||||
vfx_count += vfx_wave(*(const WaveShaderFxData *)fx, object, vfx_ctx);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (do_material_holdout) {
|
||||
vfx_count += 1;
|
||||
}
|
||||
|
||||
if (vfx_count > 0) {
|
||||
/* We need an extra pass to combine result to main buffer. */
|
||||
merge_sync(main_fb, vfx_ctx);
|
||||
}
|
||||
|
||||
return vfx_count > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int vfx_blur(const BlurShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
|
||||
{
|
||||
if ((fx.flag & FX_BLUR_DOF_MODE) && !dof_enabled_) {
|
||||
/* No blur outside camera view (or when DOF is disabled on the camera). */
|
||||
return 0;
|
||||
}
|
||||
|
||||
float winmat[4][4], persmat[4][4];
|
||||
float2 blur_size = {fx.radius[0], fx.radius[1]};
|
||||
|
||||
/* TODO(fclem): Replace by draw::View. */
|
||||
DRW_view_persmat_get(nullptr, persmat, false);
|
||||
const float w = fabsf(mul_project_m4_v3_zfac(persmat, object->object_to_world[3]));
|
||||
|
||||
if (fx.flag & FX_BLUR_DOF_MODE) {
|
||||
/* Compute circle of confusion size. */
|
||||
float coc = (dof_parameters_[0] / -w) - dof_parameters_[1];
|
||||
blur_size = float2(fabsf(coc));
|
||||
}
|
||||
else {
|
||||
/* Modify by distance to camera and object scale. */
|
||||
/* TODO(fclem): Replace by draw::View. */
|
||||
DRW_view_winmat_get(nullptr, winmat, false);
|
||||
/* TODO(fclem): Replace by this->render_size. */
|
||||
const float *vp_size = DRW_viewport_size_get();
|
||||
|
||||
float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR;
|
||||
float scale = mat4_to_scale(object->object_to_world);
|
||||
float distance_factor = world_pixel_scale * scale * winmat[1][1] * vp_size[1] / w;
|
||||
blur_size *= distance_factor;
|
||||
}
|
||||
|
||||
if ((fx.samples == 0.0f) || (blur_size[0] == 0.0f && blur_size[1] == 0.0f)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
GPUShader *sh = shaders.static_shader_get(eShaderType::FX_BLUR);
|
||||
|
||||
const float rot_sin = sin(fx.rotation);
|
||||
const float rot_cos = cos(fx.rotation);
|
||||
|
||||
if (blur_size[0] > 0.0f) {
|
||||
PassMain::Sub &sub = vfx_ctx.create_vfx_pass("Fx Blur H", sh);
|
||||
sub.state_set(DRW_STATE_WRITE_COLOR);
|
||||
sub.push_constant("offset", float2(blur_size[0] * rot_cos, blur_size[0] * rot_sin));
|
||||
sub.push_constant("sampCount", max_ii(1, min_ii(fx.samples, blur_size[0])));
|
||||
sub.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
if (blur_size[1] > 0.0f) {
|
||||
PassMain::Sub &sub = vfx_ctx.create_vfx_pass("Fx Blur V", sh);
|
||||
sub.state_set(DRW_STATE_WRITE_COLOR);
|
||||
sub.push_constant("offset", float2(-blur_size[1] * rot_sin, blur_size[1] * rot_cos));
|
||||
sub.push_constant("sampCount", max_ii(1, min_ii(fx.samples, blur_size[1])));
|
||||
sub.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
|
||||
/* Return number of passes. */
|
||||
return int(blur_size[0] > 0.0f) + int(blur_size[1] > 0.0f);
|
||||
}
|
||||
|
||||
int vfx_colorize(const ColorizeShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
|
||||
{
|
||||
UNUSED_VARS(fx, object, vfx_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfx_flip(const FlipShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
|
||||
{
|
||||
UNUSED_VARS(fx, object, vfx_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfx_pixelize(const PixelShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
|
||||
{
|
||||
UNUSED_VARS(fx, object, vfx_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfx_rim(const RimShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
|
||||
{
|
||||
UNUSED_VARS(fx, object, vfx_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfx_shadow(const ShadowShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
|
||||
{
|
||||
UNUSED_VARS(fx, object, vfx_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfx_glow(const GlowShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
|
||||
{
|
||||
UNUSED_VARS(fx, object, vfx_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfx_swirl(const SwirlShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
|
||||
{
|
||||
UNUSED_VARS(fx, object, vfx_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfx_wave(const WaveShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
|
||||
{
|
||||
UNUSED_VARS(fx, object, vfx_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void merge_sync(Framebuffer &main_fb, VfxContext &vfx_ctx)
|
||||
{
|
||||
PassMain::Sub &sub = vfx_ctx.object_subpass->sub("GPencil Object Composite");
|
||||
sub.framebuffer_set(&main_fb);
|
||||
|
||||
sub.shader_set(shaders.static_shader_get(FX_COMPOSITE));
|
||||
sub.bind_texture("colorBuf", vfx_ctx.color_tx.current());
|
||||
sub.bind_texture("revealBuf", vfx_ctx.reveal_tx.current());
|
||||
|
||||
sub.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_MUL);
|
||||
sub.push_constant("isFirstPass", true);
|
||||
sub.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
/* We cannot do custom blending on multi-target frame-buffers.
|
||||
* Workaround by doing 2 passes. */
|
||||
sub.state_set(DRW_STATE_WRITE_COLOR, DRW_STATE_BLEND_ADD_FULL);
|
||||
sub.push_constant("isFirstPass", false);
|
||||
sub.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::draw::greasepencil
|
||||
@@ -15,19 +15,19 @@ vec3 gpencil_lighting(void)
|
||||
{
|
||||
vec3 light_accum = vec3(0.0);
|
||||
for (int i = 0; i < GPENCIL_LIGHT_BUFFER_LEN; i++) {
|
||||
if (lights[i]._color.x == -1.0) {
|
||||
if (gp_lights[i]._color.x == -1.0) {
|
||||
break;
|
||||
}
|
||||
vec3 L = lights[i]._position - gp_interp.pos;
|
||||
vec3 L = gp_lights[i]._position - gp_interp.pos;
|
||||
float vis = 1.0;
|
||||
gpLightType type = floatBitsToUint(lights[i]._type);
|
||||
gpLightType type = floatBitsToUint(gp_lights[i]._type);
|
||||
/* Spot Attenuation. */
|
||||
if (type == GP_LIGHT_TYPE_SPOT) {
|
||||
mat3 rot_scale = mat3(lights[i]._right, lights[i]._up, lights[i]._forward);
|
||||
mat3 rot_scale = mat3(gp_lights[i]._right, gp_lights[i]._up, gp_lights[i]._forward);
|
||||
vec3 local_L = rot_scale * L;
|
||||
local_L /= abs(local_L.z);
|
||||
float ellipse = inversesqrt(length_squared(local_L));
|
||||
vis *= smoothstep(0.0, 1.0, (ellipse - lights[i]._spot_size) / lights[i]._spot_blend);
|
||||
vis *= smoothstep(0.0, 1.0, (ellipse - gp_lights[i]._spot_size) / gp_lights[i]._spot_blend);
|
||||
/* Also mask +Z cone. */
|
||||
vis *= step(0.0, local_L.z);
|
||||
}
|
||||
@@ -37,7 +37,7 @@ vec3 gpencil_lighting(void)
|
||||
vis /= L_len_sqr;
|
||||
}
|
||||
else {
|
||||
L = lights[i]._forward;
|
||||
L = gp_lights[i]._forward;
|
||||
L_len_sqr = 1.0;
|
||||
}
|
||||
/* Lambertian falloff */
|
||||
@@ -45,7 +45,7 @@ vec3 gpencil_lighting(void)
|
||||
L /= sqrt(L_len_sqr);
|
||||
vis *= clamp(dot(gpNormal, L), 0.0, 1.0);
|
||||
}
|
||||
light_accum += vis * lights[i]._color;
|
||||
light_accum += vis * gp_lights[i]._color;
|
||||
}
|
||||
/* Clamp to avoid NaNs. */
|
||||
return clamp(light_accum, 0.0, 1e10);
|
||||
@@ -68,7 +68,7 @@ void main()
|
||||
bool radial = flag_test(gp_interp.mat_flag, GP_FILL_GRADIENT_RADIAL);
|
||||
float fac = clamp(radial ? length(gp_interp.uv * 2.0 - 1.0) : gp_interp.uv.x, 0.0, 1.0);
|
||||
uint matid = gp_interp.mat_flag >> GPENCIl_MATID_SHIFT;
|
||||
col = mix(materials[matid].fill_color, materials[matid].fill_mix_color, fac);
|
||||
col = mix(gp_materials[matid].fill_color, gp_materials[matid].fill_mix_color, fac);
|
||||
}
|
||||
else /* SOLID */ {
|
||||
col = vec4(1.0);
|
||||
|
||||
@@ -27,12 +27,16 @@ void gpencil_color_output(vec4 stroke_col, vec4 vert_col, float vert_strength, f
|
||||
|
||||
void main()
|
||||
{
|
||||
#ifdef GPENCIL_NEXT
|
||||
PASS_RESOURCE_ID
|
||||
#endif
|
||||
|
||||
float vert_strength;
|
||||
vec4 vert_color;
|
||||
vec3 vert_N;
|
||||
|
||||
ivec4 ma1 = floatBitsToInt(texelFetch(gp_pos_tx, gpencil_stroke_point_id() * 3 + 1));
|
||||
gpMaterial gp_mat = materials[ma1.x + gpMaterialOffset];
|
||||
gpMaterial gp_mat = gp_materials[ma1.x + gpMaterialOffset];
|
||||
gpMaterialFlag gp_flag = floatBitsToUint(gp_mat._flag);
|
||||
|
||||
gl_Position = gpencil_vertex(vec4(viewportSize, 1.0 / viewportSize),
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
#include "gpencil_defines.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPencil Object rendering
|
||||
* \{ */
|
||||
@@ -26,8 +28,8 @@ GPU_SHADER_CREATE_INFO(gpencil_geometry)
|
||||
.sampler(3, ImageType::FLOAT_2D, "gpStrokeTexture")
|
||||
.sampler(4, ImageType::DEPTH_2D, "gpSceneDepthTexture")
|
||||
.sampler(5, ImageType::FLOAT_2D, "gpMaskTexture")
|
||||
.uniform_buf(4, "gpMaterial", "materials[GPENCIL_MATERIAL_BUFFER_LEN]", Frequency::BATCH)
|
||||
.uniform_buf(3, "gpLight", "lights[GPENCIL_LIGHT_BUFFER_LEN]", Frequency::BATCH)
|
||||
.uniform_buf(4, "gpMaterial", "gp_materials[GPENCIL_MATERIAL_BUFFER_LEN]", Frequency::BATCH)
|
||||
.uniform_buf(3, "gpLight", "gp_lights[GPENCIL_LIGHT_BUFFER_LEN]", Frequency::BATCH)
|
||||
.push_constant(Type::VEC2, "viewportSize")
|
||||
/* Per Object */
|
||||
.push_constant(Type::VEC3, "gpNormal")
|
||||
@@ -46,6 +48,39 @@ GPU_SHADER_CREATE_INFO(gpencil_geometry)
|
||||
.depth_write(DepthWrite::ANY)
|
||||
.additional_info("draw_gpencil");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_geometry_next)
|
||||
.do_static_compilation(true)
|
||||
.define("GP_LIGHT")
|
||||
.typedef_source("gpencil_defines.h")
|
||||
.sampler(GPENCIL_SCENE_DEPTH_TEX_SLOT, ImageType::DEPTH_2D, "gpSceneDepthTexture")
|
||||
.sampler(GPENCIL_MASK_TEX_SLOT, ImageType::FLOAT_2D, "gpMaskTexture")
|
||||
.sampler(GPENCIL_FILL_TEX_SLOT, ImageType::FLOAT_2D, "gpFillTexture")
|
||||
.sampler(GPENCIL_STROKE_TEX_SLOT, ImageType::FLOAT_2D, "gpStrokeTexture")
|
||||
.storage_buf(GPENCIL_OBJECT_SLOT, Qualifier::READ, "gpObject", "gp_object[]")
|
||||
.storage_buf(GPENCIL_LAYER_SLOT, Qualifier::READ, "gpLayer", "gp_layer[]")
|
||||
.storage_buf(GPENCIL_MATERIAL_SLOT, Qualifier::READ, "gpMaterial", "gp_materials[]")
|
||||
.storage_buf(GPENCIL_LIGHT_SLOT, Qualifier::READ, "gpLight", "gp_lights[]")
|
||||
.uniform_buf(GPENCIL_SCENE_SLOT, "gpScene", "gp_scene")
|
||||
/* Per Scene */
|
||||
.define("viewportSize", "gp_scene.render_size")
|
||||
/* Per Object */
|
||||
.define("gpNormal", "gp_object[resource_id].normal")
|
||||
.define("gpStrokeOrder3d", "gp_object[resource_id].stroke_order3d")
|
||||
.define("gpMaterialOffset", "gp_object[resource_id].material_offset")
|
||||
/* Per Layer */
|
||||
.define("layer_id", "gp_object[resource_id].layer_offset") /* TODO */
|
||||
.define("gpVertexColorOpacity", "gp_layer[layer_id].vertex_color_opacity")
|
||||
.define("gpLayerTint", "gp_layer[layer_id].tint")
|
||||
.define("gpLayerOpacity", "gp_layer[layer_id].opacity")
|
||||
.define("gpStrokeIndexOffset", "gp_layer[layer_id].stroke_index_offset")
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.fragment_out(1, Type::VEC4, "revealColor")
|
||||
.vertex_out(gpencil_geometry_iface)
|
||||
.vertex_source("gpencil_vert.glsl")
|
||||
.fragment_source("gpencil_frag.glsl")
|
||||
.additional_info("draw_gpencil_new")
|
||||
.depth_write(DepthWrite::ANY);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -340,7 +340,8 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
|
||||
OB_GPENCIL_LEGACY,
|
||||
OB_CURVES,
|
||||
OB_POINTCLOUD,
|
||||
OB_VOLUME);
|
||||
OB_VOLUME,
|
||||
OB_GREASE_PENCIL);
|
||||
const bool draw_surface = (ob->dt >= OB_WIRE) && (renderable || (ob->dt == OB_WIRE));
|
||||
const bool draw_facing = draw_surface && (pd->overlay.flag & V3D_OVERLAY_FACE_ORIENTATION) &&
|
||||
!is_select;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_curves_types.h"
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meta_types.h"
|
||||
@@ -3298,6 +3299,8 @@ void drw_batch_cache_validate(Object *ob)
|
||||
case OB_VOLUME:
|
||||
DRW_volume_batch_cache_validate((Volume *)ob->data);
|
||||
break;
|
||||
case OB_GREASE_PENCIL:
|
||||
DRW_grease_pencil_batch_cache_validate((GreasePencil *)ob->data);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ DRWVolumeGrid *DRW_volume_batch_cache_get_grid(struct Volume *volume,
|
||||
struct GPUBatch *DRW_cache_volume_face_wireframe_get(struct Object *ob);
|
||||
struct GPUBatch *DRW_cache_volume_selection_surface_get(struct Object *ob);
|
||||
|
||||
/* GPencil */
|
||||
/* GPencil (legacy) */
|
||||
|
||||
struct GPUBatch *DRW_cache_gpencil_get(struct Object *ob, int cfra);
|
||||
struct GPUVertBuf *DRW_cache_gpencil_position_buffer_get(struct Object *ob, int cfra);
|
||||
@@ -276,6 +276,12 @@ struct bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(struct Object *ob);
|
||||
*/
|
||||
void DRW_cache_gpencil_sbuffer_clear(struct Object *ob);
|
||||
|
||||
/* Grease Pencil */
|
||||
|
||||
struct GPUBatch *DRW_cache_grease_pencil_get(struct Object *ob, int cfra);
|
||||
struct GPUVertBuf *DRW_cache_grease_pencil_position_buffer_get(struct Object *ob, int cfra);
|
||||
struct GPUVertBuf *DRW_cache_grease_pencil_color_buffer_get(struct Object *ob, int cfra);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,7 @@ struct Mesh;
|
||||
struct PointCloud;
|
||||
struct Volume;
|
||||
struct bGPdata;
|
||||
struct GreasePencil;
|
||||
|
||||
#include "BKE_mesh_types.h"
|
||||
|
||||
@@ -63,6 +64,10 @@ void DRW_volume_batch_cache_dirty_tag(struct Volume *volume, int mode);
|
||||
void DRW_volume_batch_cache_validate(struct Volume *volume);
|
||||
void DRW_volume_batch_cache_free(struct Volume *volume);
|
||||
|
||||
void DRW_grease_pencil_batch_cache_dirty_tag(struct GreasePencil *grase_pencil, int mode);
|
||||
void DRW_grease_pencil_batch_cache_validate(struct GreasePencil *grase_pencil);
|
||||
void DRW_grease_pencil_batch_cache_free(struct GreasePencil *grase_pencil);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -0,0 +1,523 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Grease Pencil API for render engines
|
||||
*/
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
|
||||
#include "DRW_engine.h"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
|
||||
#include "draw_cache_impl.h"
|
||||
|
||||
#include "../engines/gpencil/gpencil_defines.h"
|
||||
#include "../engines/gpencil/gpencil_shader_shared.h"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
struct GreasePencilBatchCache {
|
||||
/** Instancing Data */
|
||||
GPUVertBuf *vbo;
|
||||
GPUVertBuf *vbo_col;
|
||||
/** Indices in material order, then stroke order with fill first. */
|
||||
GPUIndexBuf *ibo;
|
||||
/** Batches */
|
||||
GPUBatch *geom_batch;
|
||||
|
||||
/** Cache is dirty. */
|
||||
bool is_dirty;
|
||||
/** Last cached frame. */
|
||||
int cache_frame;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Vertex Formats
|
||||
* \{ */
|
||||
|
||||
/* MUST match the format below. */
|
||||
struct GreasePencilStrokeVert {
|
||||
/** Position and radius packed in the same attribute. */
|
||||
float pos[3], radius;
|
||||
/** Material Index, Stroke Index, Point Index, Packed aspect + hardness + rotation. */
|
||||
int32_t mat, stroke_id, point_id, packed_asp_hard_rot;
|
||||
/** UV and opacity packed in the same attribute. */
|
||||
float uv_fill[2], u_stroke, opacity;
|
||||
};
|
||||
|
||||
static GPUVertFormat *grease_pencil_stroke_format()
|
||||
{
|
||||
static GPUVertFormat format = {0};
|
||||
if (format.attr_len == 0) {
|
||||
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
GPU_vertformat_attr_add(&format, "ma", GPU_COMP_I32, 4, GPU_FETCH_INT);
|
||||
GPU_vertformat_attr_add(&format, "uv", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
}
|
||||
return &format;
|
||||
}
|
||||
|
||||
/* MUST match the format below. */
|
||||
struct GreasePencilColorVert {
|
||||
float vcol[4]; /* Vertex color */
|
||||
float fcol[4]; /* Fill color */
|
||||
};
|
||||
|
||||
static GPUVertFormat *grease_pencil_color_format()
|
||||
{
|
||||
static GPUVertFormat format = {0};
|
||||
if (format.attr_len == 0) {
|
||||
GPU_vertformat_attr_add(&format, "col", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
GPU_vertformat_attr_add(&format, "fcol", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
}
|
||||
return &format;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Utilities
|
||||
* \{ */
|
||||
|
||||
static bool grease_pencil_batch_cache_valid(const GreasePencil &grease_pencil, int cfra)
|
||||
{
|
||||
BLI_assert(grease_pencil.runtime != nullptr);
|
||||
const GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
|
||||
grease_pencil.runtime->batch_cache);
|
||||
return (cache && cache->is_dirty == false && cache->cache_frame == cfra);
|
||||
}
|
||||
|
||||
static GreasePencilBatchCache *grease_pencil_batch_cache_init(GreasePencil &grease_pencil,
|
||||
int cfra)
|
||||
{
|
||||
BLI_assert(grease_pencil.runtime != nullptr);
|
||||
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
|
||||
grease_pencil.runtime->batch_cache);
|
||||
if (cache == nullptr) {
|
||||
cache = MEM_new<GreasePencilBatchCache>(__func__);
|
||||
grease_pencil.runtime->batch_cache = cache;
|
||||
}
|
||||
else {
|
||||
*cache = {};
|
||||
}
|
||||
|
||||
cache->is_dirty = false;
|
||||
cache->cache_frame = cfra;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
static void grease_pencil_batch_cache_clear(GreasePencil &grease_pencil)
|
||||
{
|
||||
BLI_assert(grease_pencil.runtime != nullptr);
|
||||
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
|
||||
grease_pencil.runtime->batch_cache);
|
||||
if (cache == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPU_BATCH_DISCARD_SAFE(cache->geom_batch);
|
||||
GPU_VERTBUF_DISCARD_SAFE(cache->vbo);
|
||||
GPU_VERTBUF_DISCARD_SAFE(cache->vbo_col);
|
||||
GPU_INDEXBUF_DISCARD_SAFE(cache->ibo);
|
||||
|
||||
cache->is_dirty = true;
|
||||
}
|
||||
|
||||
static GreasePencilBatchCache *grease_pencil_batch_cache_get(GreasePencil &grease_pencil, int cfra)
|
||||
{
|
||||
BLI_assert(grease_pencil.runtime != nullptr);
|
||||
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
|
||||
grease_pencil.runtime->batch_cache);
|
||||
if (!grease_pencil_batch_cache_valid(grease_pencil, cfra)) {
|
||||
grease_pencil_batch_cache_clear(grease_pencil);
|
||||
return grease_pencil_batch_cache_init(grease_pencil, cfra);
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Vertex Buffers
|
||||
* \{ */
|
||||
|
||||
BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float hard)
|
||||
{
|
||||
int32_t packed = 0;
|
||||
/* Aspect uses 9 bits */
|
||||
float asp_normalized = (asp > 1.0f) ? (1.0f / asp) : asp;
|
||||
packed |= int32_t(unit_float_to_uchar_clamp(asp_normalized));
|
||||
/* Store if inversed in the 9th bit. */
|
||||
if (asp > 1.0f) {
|
||||
packed |= 1 << 8;
|
||||
}
|
||||
/* Rotation uses 9 bits */
|
||||
/* Rotation are in [-90°..90°] range, so we can encode the sign of the angle + the cosine
|
||||
* because the cosine will always be positive. */
|
||||
packed |= int32_t(unit_float_to_uchar_clamp(cosf(rot))) << 9;
|
||||
/* Store sine sign in 9th bit. */
|
||||
if (rot < 0.0f) {
|
||||
packed |= 1 << 17;
|
||||
}
|
||||
/* Hardness uses 8 bits */
|
||||
packed |= int32_t(unit_float_to_uchar_clamp(hard)) << 18;
|
||||
return packed;
|
||||
}
|
||||
|
||||
static void grease_pencil_batches_ensure(GreasePencil &grease_pencil, int cfra)
|
||||
{
|
||||
BLI_assert(grease_pencil.runtime != nullptr);
|
||||
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
|
||||
grease_pencil.runtime->batch_cache);
|
||||
|
||||
if (cache->vbo != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Should be discarded together. */
|
||||
BLI_assert(cache->vbo == nullptr && cache->ibo == nullptr);
|
||||
BLI_assert(cache->geom_batch == nullptr);
|
||||
|
||||
/* Get the visible drawings. */
|
||||
Vector<const GreasePencilDrawing *> drawings;
|
||||
grease_pencil.foreach_visible_drawing(
|
||||
cfra, [&](GreasePencilDrawing &drawing) { drawings.append(&drawing); });
|
||||
|
||||
/* First, count how many vertices and triangles are needed for the whole object. Also record the
|
||||
* offsets into the curves for the verticies and triangles. */
|
||||
int total_points_num = 0;
|
||||
int total_triangles_num = 0;
|
||||
int v_offset = 0;
|
||||
Vector<Array<int>> verts_start_offsets_per_visible_drawing;
|
||||
Vector<Array<int>> tris_start_offsets_per_visible_drawing;
|
||||
for (const int drawing_i : drawings.index_range()) {
|
||||
const GreasePencilDrawing &drawing = *drawings[drawing_i];
|
||||
const bke::CurvesGeometry &curves = drawing.geometry.wrap();
|
||||
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
const VArray<bool> cyclic = curves.cyclic();
|
||||
|
||||
int verts_start_offsets_size = curves.curves_num();
|
||||
int tris_start_offsets_size = curves.curves_num();
|
||||
if (drawing.has_stroke_buffer()) {
|
||||
verts_start_offsets_size++;
|
||||
/* TODO: triangles for stroke buffer. */
|
||||
// tris_start_offsets_size++;
|
||||
}
|
||||
Array<int> verts_start_offsets(verts_start_offsets_size);
|
||||
Array<int> tris_start_offsets(tris_start_offsets_size);
|
||||
|
||||
/* Calculate the vertex and triangle offsets for all the curves. */
|
||||
int t_offset = 0;
|
||||
int num_cyclic = 0;
|
||||
for (const int curve_i : curves.curves_range()) {
|
||||
IndexRange points = points_by_curve[curve_i];
|
||||
const bool is_cyclic = cyclic[curve_i];
|
||||
|
||||
if (is_cyclic) {
|
||||
num_cyclic++;
|
||||
}
|
||||
|
||||
tris_start_offsets[curve_i] = t_offset;
|
||||
if (points.size() >= 3) {
|
||||
t_offset += points.size() - 2;
|
||||
}
|
||||
|
||||
verts_start_offsets[curve_i] = v_offset;
|
||||
v_offset += 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
|
||||
}
|
||||
|
||||
/* One vertex is stored before and after as padding. Cyclic strokes have one extra
|
||||
* vertex.*/
|
||||
total_points_num += curves.points_num() + num_cyclic + curves.curves_num() * 2;
|
||||
total_triangles_num += (curves.points_num() + num_cyclic) * 2;
|
||||
total_triangles_num += drawing.triangles().size();
|
||||
|
||||
if (drawing.has_stroke_buffer()) {
|
||||
const int num_buffer_points = drawing.stroke_buffer().size();
|
||||
total_points_num += 1 + num_buffer_points + 1;
|
||||
total_triangles_num += num_buffer_points * 2;
|
||||
verts_start_offsets[curves.curves_range().size()] = v_offset;
|
||||
/* TODO: triangles for stroke buffer. */
|
||||
v_offset += 1 + num_buffer_points + 1;
|
||||
}
|
||||
|
||||
verts_start_offsets_per_visible_drawing.append(std::move(verts_start_offsets));
|
||||
tris_start_offsets_per_visible_drawing.append(std::move(tris_start_offsets));
|
||||
}
|
||||
|
||||
GPUUsageType vbo_flag = GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY;
|
||||
/* Create VBOs. */
|
||||
GPUVertFormat *format = grease_pencil_stroke_format();
|
||||
GPUVertFormat *format_col = grease_pencil_color_format();
|
||||
cache->vbo = GPU_vertbuf_create_with_format_ex(format, vbo_flag);
|
||||
cache->vbo_col = GPU_vertbuf_create_with_format_ex(format_col, vbo_flag);
|
||||
/* Add extra space at the end of the buffer because of quad load. */
|
||||
GPU_vertbuf_data_alloc(cache->vbo, total_points_num + 2);
|
||||
GPU_vertbuf_data_alloc(cache->vbo_col, total_points_num + 2);
|
||||
|
||||
GPUIndexBufBuilder ibo;
|
||||
MutableSpan<GreasePencilStrokeVert> verts = {
|
||||
static_cast<GreasePencilStrokeVert *>(GPU_vertbuf_get_data(cache->vbo)),
|
||||
GPU_vertbuf_get_vertex_len(cache->vbo)};
|
||||
MutableSpan<GreasePencilColorVert> cols = {
|
||||
static_cast<GreasePencilColorVert *>(GPU_vertbuf_get_data(cache->vbo_col)),
|
||||
GPU_vertbuf_get_vertex_len(cache->vbo_col)};
|
||||
/* Create IBO. */
|
||||
GPU_indexbuf_init(&ibo, GPU_PRIM_TRIS, total_triangles_num, 0xFFFFFFFFu);
|
||||
|
||||
/* Fill buffers with data. */
|
||||
for (const int drawing_i : drawings.index_range()) {
|
||||
const GreasePencilDrawing &drawing = *drawings[drawing_i];
|
||||
const bke::CurvesGeometry &curves = drawing.geometry.wrap();
|
||||
const bke::AttributeAccessor attributes = curves.attributes();
|
||||
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
const Span<float3> positions = curves.positions();
|
||||
const VArray<bool> cyclic = curves.cyclic();
|
||||
const VArray<float> radii = *attributes.lookup_or_default<float>(
|
||||
"radius", ATTR_DOMAIN_POINT, 1.0f);
|
||||
const VArray<float> opacities = *attributes.lookup_or_default<float>(
|
||||
"opacity", ATTR_DOMAIN_POINT, 1.0f);
|
||||
const VArray<int8_t> start_caps = *attributes.lookup_or_default<int8_t>(
|
||||
"start_cap", ATTR_DOMAIN_CURVE, 0);
|
||||
const VArray<int8_t> end_caps = *attributes.lookup_or_default<int8_t>(
|
||||
"end_cap", ATTR_DOMAIN_CURVE, 0);
|
||||
const VArray<int> materials = *attributes.lookup_or_default<int>(
|
||||
"material_index", ATTR_DOMAIN_CURVE, -1);
|
||||
const Span<uint3> triangles = drawing.triangles();
|
||||
const Span<int> verts_start_offsets = verts_start_offsets_per_visible_drawing[drawing_i];
|
||||
const Span<int> tris_start_offsets = tris_start_offsets_per_visible_drawing[drawing_i];
|
||||
|
||||
auto populate_point = [&](IndexRange verts_range,
|
||||
int curve_i,
|
||||
int8_t start_cap,
|
||||
int8_t end_cap,
|
||||
int point_i,
|
||||
int idx,
|
||||
GreasePencilStrokeVert &s_vert,
|
||||
GreasePencilColorVert &c_vert) {
|
||||
copy_v3_v3(s_vert.pos, positions[point_i]);
|
||||
s_vert.radius = radii[point_i] * ((end_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
|
||||
s_vert.opacity = opacities[point_i] *
|
||||
((start_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
|
||||
s_vert.point_id = verts_range[idx];
|
||||
s_vert.stroke_id = verts_range.first();
|
||||
s_vert.mat = materials[curve_i] % GPENCIL_MATERIAL_BUFFER_LEN;
|
||||
|
||||
/* TODO: Populate rotation, aspect and hardness. */
|
||||
s_vert.packed_asp_hard_rot = pack_rotation_aspect_hardness(0.0f, 1.0f, 1.0f);
|
||||
/* TODO: Populate stroke UVs. */
|
||||
s_vert.u_stroke = 0;
|
||||
/* TODO: Populate fill UVs. */
|
||||
s_vert.uv_fill[0] = s_vert.uv_fill[1] = 0;
|
||||
|
||||
/* TODO: Populate vertex color and fill color. */
|
||||
copy_v4_v4(c_vert.vcol, float4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
copy_v4_v4(c_vert.fcol, float4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
c_vert.fcol[3] = (int(c_vert.fcol[3] * 10000.0f) * 10.0f) + 1.0f;
|
||||
|
||||
int v_mat = (verts_range[idx] << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT;
|
||||
GPU_indexbuf_add_tri_verts(&ibo, v_mat + 0, v_mat + 1, v_mat + 2);
|
||||
GPU_indexbuf_add_tri_verts(&ibo, v_mat + 2, v_mat + 1, v_mat + 3);
|
||||
};
|
||||
|
||||
threading::parallel_for(curves.curves_range(), 512, [&](IndexRange range) {
|
||||
for (const int curve_i : range) {
|
||||
IndexRange points = points_by_curve[curve_i];
|
||||
const bool is_cyclic = cyclic[curve_i];
|
||||
const int verts_start_offset = verts_start_offsets[curve_i];
|
||||
const int tris_start_offset = tris_start_offsets[curve_i];
|
||||
const int num_verts = 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
|
||||
IndexRange verts_range = IndexRange(verts_start_offset, num_verts);
|
||||
MutableSpan<GreasePencilStrokeVert> verts_slice = verts.slice(verts_range);
|
||||
MutableSpan<GreasePencilColorVert> cols_slice = cols.slice(verts_range);
|
||||
|
||||
/* First vertex is not drawn. */
|
||||
verts_slice.first().mat = -1;
|
||||
|
||||
/* If the stroke has more than 2 points, add the triangle indices to the index buffer. */
|
||||
if (points.size() >= 3) {
|
||||
const Span<uint3> tris_slice = triangles.slice(tris_start_offset, points.size() - 2);
|
||||
for (const uint3 tri : tris_slice) {
|
||||
GPU_indexbuf_add_tri_verts(&ibo,
|
||||
(verts_range[1] + tri.x) << GP_VERTEX_ID_SHIFT,
|
||||
(verts_range[1] + tri.y) << GP_VERTEX_ID_SHIFT,
|
||||
(verts_range[1] + tri.z) << GP_VERTEX_ID_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write all the point attributes to the vertex buffers. Create a quad for each point. */
|
||||
for (const int i : IndexRange(points.size())) {
|
||||
const int idx = i + 1;
|
||||
populate_point(verts_range,
|
||||
curve_i,
|
||||
start_caps[curve_i],
|
||||
end_caps[curve_i],
|
||||
points[i],
|
||||
idx,
|
||||
verts_slice[idx],
|
||||
cols_slice[idx]);
|
||||
}
|
||||
|
||||
if (is_cyclic) {
|
||||
const int idx = points.size() + 1;
|
||||
populate_point(verts_range,
|
||||
curve_i,
|
||||
start_caps[curve_i],
|
||||
end_caps[curve_i],
|
||||
points[0],
|
||||
idx,
|
||||
verts_slice[idx],
|
||||
cols_slice[idx]);
|
||||
}
|
||||
|
||||
/* Last vertex is not drawn. */
|
||||
verts_slice.last().mat = -1;
|
||||
}
|
||||
});
|
||||
|
||||
if (drawing.has_stroke_buffer()) {
|
||||
Span<bke::greasepencil::StrokePoint> points = drawing.stroke_buffer();
|
||||
const int verts_start_offset = verts_start_offsets.last();
|
||||
const int num_verts = 1 + points.size() + 1;
|
||||
IndexRange verts_range = IndexRange(verts_start_offset, num_verts);
|
||||
MutableSpan<GreasePencilStrokeVert> verts_slice = verts.slice(verts_range);
|
||||
MutableSpan<GreasePencilColorVert> cols_slice = cols.slice(verts_range);
|
||||
const int material_nr = drawing.runtime->stroke_cache.mat;
|
||||
|
||||
verts_slice.first().mat = -1;
|
||||
for (const int i : IndexRange(points.size())) {
|
||||
const int idx = i + 1;
|
||||
GreasePencilStrokeVert &s_vert = verts_slice[idx];
|
||||
GreasePencilColorVert &c_vert = cols_slice[idx];
|
||||
const bke::greasepencil::StrokePoint &point = points[i];
|
||||
|
||||
copy_v3_v3(s_vert.pos, point.position);
|
||||
s_vert.radius = point.radius;
|
||||
s_vert.opacity = point.opacity;
|
||||
s_vert.point_id = verts_range[idx];
|
||||
s_vert.stroke_id = verts_range.first();
|
||||
s_vert.mat = material_nr;
|
||||
|
||||
/* TODO */
|
||||
s_vert.packed_asp_hard_rot = pack_rotation_aspect_hardness(0.0f, 1.0f, 1.0f);
|
||||
/* TODO */
|
||||
s_vert.u_stroke = 0;
|
||||
/* TODO */
|
||||
s_vert.uv_fill[0] = s_vert.uv_fill[1] = 0;
|
||||
|
||||
/* TODO */
|
||||
copy_v4_v4(c_vert.vcol, float4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
copy_v4_v4(c_vert.fcol, float4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
|
||||
/* TODO */
|
||||
c_vert.fcol[3] = (int(c_vert.fcol[3] * 10000.0f) * 10.0f) + 1.0f;
|
||||
|
||||
int v_mat = (verts_range[idx] << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT;
|
||||
GPU_indexbuf_add_tri_verts(&ibo, v_mat + 0, v_mat + 1, v_mat + 2);
|
||||
GPU_indexbuf_add_tri_verts(&ibo, v_mat + 2, v_mat + 1, v_mat + 3);
|
||||
}
|
||||
|
||||
verts_slice.last().mat = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark last 2 verts as invalid. */
|
||||
verts[total_points_num + 0].mat = -1;
|
||||
verts[total_points_num + 1].mat = -1;
|
||||
/* Also mark first vert as invalid. */
|
||||
verts[0].mat = -1;
|
||||
|
||||
/* Finish the IBO. */
|
||||
cache->ibo = GPU_indexbuf_build(&ibo);
|
||||
/* Create the batches */
|
||||
cache->geom_batch = GPU_batch_create(GPU_PRIM_TRIS, cache->vbo, cache->ibo);
|
||||
/* Allow creation of buffer texture. */
|
||||
GPU_vertbuf_use(cache->vbo);
|
||||
GPU_vertbuf_use(cache->vbo_col);
|
||||
|
||||
cache->is_dirty = false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw
|
||||
|
||||
void DRW_grease_pencil_batch_cache_dirty_tag(GreasePencil *grease_pencil, int mode)
|
||||
{
|
||||
using namespace blender::draw;
|
||||
BLI_assert(grease_pencil->runtime != nullptr);
|
||||
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
|
||||
grease_pencil->runtime->batch_cache);
|
||||
if (cache == nullptr) {
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case BKE_GREASEPENCIL_BATCH_DIRTY_ALL:
|
||||
cache->is_dirty = true;
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
void DRW_grease_pencil_batch_cache_validate(GreasePencil *grease_pencil)
|
||||
{
|
||||
using namespace blender::draw;
|
||||
BLI_assert(grease_pencil->runtime != nullptr);
|
||||
/* TODO: pass correct frame here? */
|
||||
if (!grease_pencil_batch_cache_valid(*grease_pencil, 0)) {
|
||||
grease_pencil_batch_cache_clear(*grease_pencil);
|
||||
/* TODO: pass correct frame here? */
|
||||
grease_pencil_batch_cache_init(*grease_pencil, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void DRW_grease_pencil_batch_cache_free(GreasePencil *grease_pencil)
|
||||
{
|
||||
using namespace blender::draw;
|
||||
grease_pencil_batch_cache_clear(*grease_pencil);
|
||||
MEM_delete(static_cast<GreasePencilBatchCache *>(grease_pencil->runtime->batch_cache));
|
||||
grease_pencil->runtime->batch_cache = nullptr;
|
||||
}
|
||||
|
||||
GPUBatch *DRW_cache_grease_pencil_get(Object *ob, int cfra)
|
||||
{
|
||||
using namespace blender::draw;
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
|
||||
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil, cfra);
|
||||
grease_pencil_batches_ensure(grease_pencil, cfra);
|
||||
|
||||
return cache->geom_batch;
|
||||
}
|
||||
|
||||
GPUVertBuf *DRW_cache_grease_pencil_position_buffer_get(Object *ob, int cfra)
|
||||
{
|
||||
using namespace blender::draw;
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
|
||||
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil, cfra);
|
||||
grease_pencil_batches_ensure(grease_pencil, cfra);
|
||||
|
||||
return cache->vbo;
|
||||
}
|
||||
|
||||
GPUVertBuf *DRW_cache_grease_pencil_color_buffer_get(Object *ob, int cfra)
|
||||
{
|
||||
using namespace blender::draw;
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
|
||||
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil, cfra);
|
||||
grease_pencil_batches_ensure(grease_pencil, cfra);
|
||||
|
||||
return cache->vbo_col;
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "BKE_editmesh.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_grease_pencil.h"
|
||||
#include "BKE_lattice.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_mball.h"
|
||||
@@ -1286,7 +1287,8 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer),
|
||||
|
||||
drw_engines_enable_from_engine(engine_type, drawtype);
|
||||
if (gpencil_engine_needed && ((drawtype >= OB_SOLID) || !use_xray)) {
|
||||
use_drw_engine(&draw_engine_gpencil_type);
|
||||
use_drw_engine(((U.experimental.use_grease_pencil_version3) ? &draw_engine_gpencil_next_type :
|
||||
&draw_engine_gpencil_type));
|
||||
}
|
||||
|
||||
if (is_compositor_enabled()) {
|
||||
@@ -1316,6 +1318,12 @@ static void drw_engines_data_validate(void)
|
||||
* For slow exact check use `DRW_render_check_grease_pencil` */
|
||||
static bool drw_gpencil_engine_needed(Depsgraph *depsgraph, View3D *v3d)
|
||||
{
|
||||
if (U.experimental.use_grease_pencil_version3) {
|
||||
const bool exclude_gpencil_rendering = v3d ? (v3d->object_type_exclude_viewport &
|
||||
(1 << OB_GREASE_PENCIL)) != 0 :
|
||||
false;
|
||||
return (!exclude_gpencil_rendering) && DEG_id_type_any_exists(depsgraph, ID_GP);
|
||||
}
|
||||
const bool exclude_gpencil_rendering = v3d ? (v3d->object_type_exclude_viewport &
|
||||
(1 << OB_GPENCIL_LEGACY)) != 0 :
|
||||
false;
|
||||
@@ -1880,7 +1888,8 @@ bool DRW_render_check_grease_pencil(Depsgraph *depsgraph)
|
||||
deg_iter_settings.depsgraph = depsgraph;
|
||||
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
|
||||
DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) {
|
||||
if (ob->type == OB_GPENCIL_LEGACY) {
|
||||
if (ob->type == OB_GPENCIL_LEGACY ||
|
||||
(U.experimental.use_grease_pencil_version3 && ob->type == OB_GREASE_PENCIL)) {
|
||||
if (DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF) {
|
||||
return true;
|
||||
}
|
||||
@@ -1895,10 +1904,13 @@ static void DRW_render_gpencil_to_image(RenderEngine *engine,
|
||||
struct RenderLayer *render_layer,
|
||||
const rcti *rect)
|
||||
{
|
||||
if (draw_engine_gpencil_type.render_to_image) {
|
||||
DrawEngineType *draw_engine = U.experimental.use_grease_pencil_version3 ?
|
||||
&draw_engine_gpencil_next_type :
|
||||
&draw_engine_gpencil_type;
|
||||
if (draw_engine->render_to_image) {
|
||||
ViewportEngineData *gpdata = DRW_view_data_engine_data_get_ensure(DST.view_data_active,
|
||||
&draw_engine_gpencil_type);
|
||||
draw_engine_gpencil_type.render_to_image(gpdata, engine, render_layer, rect);
|
||||
draw_engine);
|
||||
draw_engine->render_to_image(gpdata, engine, render_layer, rect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2470,7 +2482,9 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
|
||||
else if (!draw_surface) {
|
||||
/* grease pencil selection */
|
||||
if (drw_gpencil_engine_needed(depsgraph, v3d)) {
|
||||
use_drw_engine(&draw_engine_gpencil_type);
|
||||
use_drw_engine(((U.experimental.use_grease_pencil_version3) ?
|
||||
&draw_engine_gpencil_next_type :
|
||||
&draw_engine_gpencil_type));
|
||||
}
|
||||
|
||||
drw_engines_enable_overlays();
|
||||
@@ -2480,7 +2494,9 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
|
||||
drw_engines_enable_basic();
|
||||
/* grease pencil selection */
|
||||
if (drw_gpencil_engine_needed(depsgraph, v3d)) {
|
||||
use_drw_engine(&draw_engine_gpencil_type);
|
||||
use_drw_engine(((U.experimental.use_grease_pencil_version3) ?
|
||||
&draw_engine_gpencil_next_type :
|
||||
&draw_engine_gpencil_type));
|
||||
}
|
||||
|
||||
drw_engines_enable_overlays();
|
||||
@@ -2652,7 +2668,8 @@ void DRW_draw_depth_loop(struct Depsgraph *depsgraph,
|
||||
drw_manager_init(&DST, viewport, NULL);
|
||||
|
||||
if (use_gpencil) {
|
||||
use_drw_engine(&draw_engine_gpencil_type);
|
||||
use_drw_engine(((U.experimental.use_grease_pencil_version3) ? &draw_engine_gpencil_next_type :
|
||||
&draw_engine_gpencil_type));
|
||||
}
|
||||
if (use_basic) {
|
||||
drw_engines_enable_basic();
|
||||
@@ -3045,6 +3062,7 @@ void DRW_engines_register(void)
|
||||
RE_engines_register(&DRW_engine_viewport_workbench_type);
|
||||
|
||||
DRW_engine_register(&draw_engine_gpencil_type);
|
||||
DRW_engine_register(&draw_engine_gpencil_next_type);
|
||||
|
||||
DRW_engine_register(&draw_engine_overlay_type);
|
||||
DRW_engine_register(&draw_engine_overlay_next_type);
|
||||
@@ -3085,6 +3103,9 @@ void DRW_engines_register(void)
|
||||
BKE_volume_batch_cache_dirty_tag_cb = DRW_volume_batch_cache_dirty_tag;
|
||||
BKE_volume_batch_cache_free_cb = DRW_volume_batch_cache_free;
|
||||
|
||||
BKE_grease_pencil_batch_cache_dirty_tag_cb = DRW_grease_pencil_batch_cache_dirty_tag;
|
||||
BKE_grease_pencil_batch_cache_free_cb = DRW_grease_pencil_batch_cache_free;
|
||||
|
||||
BKE_subsurf_modifier_free_gpu_cache_cb = DRW_subdiv_cache_free;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +178,13 @@ vec4 gpencil_vertex(vec4 viewport_size,
|
||||
vec4 out_ndc;
|
||||
|
||||
if (gpencil_is_stroke_vertex()) {
|
||||
bool show_stroke = flag_test(material_flags, GP_SHOW_STROKE);
|
||||
if (!show_stroke) {
|
||||
/* We set the vertex at the camera origin to generate 0 fragments. */
|
||||
out_ndc = vec4(0.0, 0.0, -3e36, 0.0);
|
||||
return out_ndc;
|
||||
}
|
||||
|
||||
bool is_dot = flag_test(material_flags, GP_STROKE_ALIGNMENT);
|
||||
bool is_squares = !flag_test(material_flags, GP_STROKE_DOTS);
|
||||
|
||||
@@ -330,6 +337,14 @@ vec4 gpencil_vertex(vec4 viewport_size,
|
||||
}
|
||||
else {
|
||||
/* Fill vertex. */
|
||||
|
||||
bool show_fill = flag_test(material_flags, GP_SHOW_FILL);
|
||||
if (!show_fill) {
|
||||
/* We set the vertex at the camera origin to generate 0 fragments. */
|
||||
out_ndc = vec4(0.0, 0.0, -3e36, 0.0);
|
||||
return out_ndc;
|
||||
}
|
||||
|
||||
out_P = transform_point(ModelMatrix, pos1.xyz);
|
||||
out_ndc = point_world_to_ndc(out_P);
|
||||
out_uv = uv1.xy;
|
||||
|
||||
@@ -142,6 +142,22 @@ GPU_SHADER_CREATE_INFO(draw_gpencil)
|
||||
.push_constant(Type::FLOAT, "gpThicknessOffset")
|
||||
.additional_info("draw_modelmat", "draw_object_infos");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_gpencil_new)
|
||||
.typedef_source("gpencil_shader_shared.h")
|
||||
.define("DRW_GPENCIL_INFO")
|
||||
.sampler(0, ImageType::FLOAT_BUFFER, "gp_pos_tx")
|
||||
.sampler(1, ImageType::FLOAT_BUFFER, "gp_col_tx")
|
||||
/* Per Object */
|
||||
.define("gpThicknessScale", "1.0") /* TODO(fclem): Replace with object info. */
|
||||
.define("gpThicknessWorldScale", "1.0 / 2000.0") /* TODO(fclem): Same as above. */
|
||||
.define("gpThicknessIsScreenSpace", "(gpThicknessWorldScale < 0.0)")
|
||||
/* Per Layer */
|
||||
.define("gpThicknessOffset", "0.0") /* TODO(fclem): Remove. */
|
||||
.additional_info("draw_modelmat_new",
|
||||
"draw_resource_id_varying",
|
||||
"draw_view",
|
||||
"draw_object_infos_new");
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -697,6 +697,8 @@ static int acf_object_icon(bAnimListElem *ale)
|
||||
return ICON_OUTLINER_OB_EMPTY;
|
||||
case OB_GPENCIL_LEGACY:
|
||||
return ICON_OUTLINER_OB_GREASEPENCIL;
|
||||
case OB_GREASE_PENCIL:
|
||||
return ICON_OUTLINER_OB_GREASEPENCIL;
|
||||
default:
|
||||
return ICON_OBJECT_DATA;
|
||||
}
|
||||
|
||||
@@ -138,6 +138,18 @@ static bool gpencil_stroke_weightmode_poll_with_tool(bContext *C, const char gpe
|
||||
/* Poll callback for stroke painting (draw brush) */
|
||||
static bool gpencil_stroke_paintmode_draw_poll(bContext *C)
|
||||
{
|
||||
if (U.experimental.use_grease_pencil_version3) {
|
||||
Object *object = CTX_data_active_object(C);
|
||||
if (object == NULL || object->type != OB_GREASE_PENCIL) {
|
||||
return false;
|
||||
}
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
if (!ts || !ts->gp_paint) {
|
||||
return false;
|
||||
}
|
||||
const Brush *brush = BKE_paint_brush_for_read(&ts->gp_paint->paint);
|
||||
return WM_toolsystem_active_tool_is_brush(C) && brush->gpencil_tool == GPAINT_TOOL_DRAW;
|
||||
}
|
||||
return gpencil_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_DRAW);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup editors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void ED_operatortypes_grease_pencil_draw(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -2459,6 +2459,8 @@ int UI_icon_from_idcode(const int idcode)
|
||||
case ID_SIM:
|
||||
/* TODO: Use correct icon. */
|
||||
return ICON_PHYSICS;
|
||||
case ID_GP:
|
||||
return ICON_OUTLINER_DATA_GREASEPENCIL;
|
||||
|
||||
/* No icons for these ID-types. */
|
||||
case ID_LI:
|
||||
|
||||
@@ -1108,7 +1108,7 @@ static const char *template_id_browse_tip(const StructRNA *type)
|
||||
case ID_PA:
|
||||
return N_("Browse Particle Settings to be linked");
|
||||
case ID_GD_LEGACY:
|
||||
return N_("Browse Grease Pencil Data to be linked");
|
||||
return N_("Browse Grease Pencil (legacy) Data to be linked");
|
||||
case ID_MC:
|
||||
return N_("Browse Movie Clip to be linked");
|
||||
case ID_MSK:
|
||||
@@ -1131,6 +1131,8 @@ static const char *template_id_browse_tip(const StructRNA *type)
|
||||
return N_("Browse Volume Data to be linked");
|
||||
case ID_SIM:
|
||||
return N_("Browse Simulation to be linked");
|
||||
case ID_GP:
|
||||
return N_("Browse Grease Pencil Data to be linked");
|
||||
|
||||
/* Use generic text. */
|
||||
case ID_LI:
|
||||
|
||||
@@ -80,6 +80,7 @@ endif()
|
||||
if(WITH_EXPERIMENTAL_FEATURES)
|
||||
add_definitions(-DWITH_SIMULATION_DATABLOCK)
|
||||
add_definitions(-DWITH_POINT_CLOUD)
|
||||
add_definitions(-DWITH_GREASE_PENCIL_V3)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
@@ -59,7 +59,9 @@
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_gpencil_curve_legacy.h"
|
||||
#include "BKE_gpencil_geom_legacy.h"
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_gpencil_modifier_legacy.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_lattice.h"
|
||||
#include "BKE_layer.h"
|
||||
@@ -2788,8 +2790,16 @@ static const EnumPropertyItem convert_target_items[] = {
|
||||
{OB_GPENCIL_LEGACY,
|
||||
"GPENCIL",
|
||||
ICON_OUTLINER_OB_GREASEPENCIL,
|
||||
#ifdef WITH_GREASE_PENCIL_V3
|
||||
"Grease Pencil (legacy)",
|
||||
#else
|
||||
"Grease Pencil",
|
||||
#endif
|
||||
#ifdef WITH_GREASE_PENCIL_V3
|
||||
"Grease Pencil (legacy) from Curve or Mesh objects"},
|
||||
#else
|
||||
"Grease Pencil from Curve or Mesh objects"},
|
||||
#endif
|
||||
#ifdef WITH_POINT_CLOUD
|
||||
{OB_POINTCLOUD,
|
||||
"POINTCLOUD",
|
||||
@@ -2798,6 +2808,13 @@ static const EnumPropertyItem convert_target_items[] = {
|
||||
"Point Cloud from Mesh objects"},
|
||||
#endif
|
||||
{OB_CURVES, "CURVES", ICON_OUTLINER_OB_CURVES, "Curves", "Curves from evaluated curve data"},
|
||||
#ifdef WITH_GREASE_PENCIL_V3
|
||||
{OB_GREASE_PENCIL,
|
||||
"GREASEPENCIL",
|
||||
ICON_OUTLINER_OB_GREASEPENCIL,
|
||||
"Grease Pencil",
|
||||
"Grease Pencil from Grease Pencil (legacy)"},
|
||||
#endif
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
@@ -3086,6 +3103,30 @@ static int object_convert_exec(bContext *C, wmOperator *op)
|
||||
}
|
||||
ob_gpencil->actcol = actcol;
|
||||
}
|
||||
else if (U.experimental.use_grease_pencil_version3 && ob->type == OB_GPENCIL_LEGACY &&
|
||||
target == OB_GREASE_PENCIL) {
|
||||
ob->flag |= OB_DONE;
|
||||
|
||||
bGPdata *gpd = static_cast<bGPdata *>(ob->data);
|
||||
|
||||
if (keep_original) {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
else {
|
||||
newob = ob;
|
||||
}
|
||||
|
||||
GreasePencil *new_grease_pencil = static_cast<GreasePencil *>(
|
||||
BKE_id_new(bmain, ID_GP, newob->id.name + 2));
|
||||
newob->data = new_grease_pencil;
|
||||
newob->type = OB_GREASE_PENCIL;
|
||||
|
||||
bke::greasepencil::convert::legacy_gpencil_to_grease_pencil(
|
||||
*bmain, *new_grease_pencil, *gpd);
|
||||
|
||||
BKE_object_free_derived_caches(newob);
|
||||
BKE_object_free_modifiers(newob, 0);
|
||||
}
|
||||
else if (target == OB_CURVES) {
|
||||
ob->flag |= OB_DONE;
|
||||
|
||||
|
||||
@@ -686,8 +686,8 @@ static bool ED_object_editmode_load_free_ex(Main *bmain,
|
||||
ED_mball_editmball_free(obedit);
|
||||
}
|
||||
}
|
||||
else if (obedit->type == OB_CURVES) {
|
||||
/* Curves don't have specific edit mode data, so pass. */
|
||||
else if (ELEM(obedit->type, OB_CURVES, OB_GREASE_PENCIL)) {
|
||||
/* Object doesn't have specific edit mode data, so pass. */
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
@@ -878,6 +878,10 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag
|
||||
ok = true;
|
||||
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_CURVES, scene);
|
||||
}
|
||||
else if (ob->type == OB_GREASE_PENCIL) {
|
||||
ok = true;
|
||||
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_GREASE_PENCIL, scene);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
|
||||
@@ -82,6 +82,11 @@ static const char *object_mode_op_string(eObjectMode mode)
|
||||
if (mode == OB_MODE_EDIT_GPENCIL) {
|
||||
return "GPENCIL_OT_editmode_toggle";
|
||||
}
|
||||
if (U.experimental.use_grease_pencil_version3) {
|
||||
if (mode == OB_MODE_PAINT_GPENCIL) {
|
||||
return "GREASE_PENCIL_OT_draw_mode_toggle";
|
||||
}
|
||||
}
|
||||
if (mode == OB_MODE_PAINT_GPENCIL) {
|
||||
return "GPENCIL_OT_paintmode_toggle";
|
||||
}
|
||||
@@ -147,6 +152,11 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case OB_GREASE_PENCIL:
|
||||
if (mode & (OB_MODE_EDIT | OB_MODE_PAINT_GPENCIL)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -1916,6 +1916,11 @@ static void single_obdata_users(
|
||||
ob->data,
|
||||
BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS));
|
||||
break;
|
||||
case OB_GREASE_PENCIL:
|
||||
ob->data = ID_NEW_SET(
|
||||
ob->data,
|
||||
BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS));
|
||||
break;
|
||||
default:
|
||||
printf("ERROR %s: can't copy %s\n", __func__, id->name);
|
||||
BLI_assert_msg(0, "This should never happen.");
|
||||
|
||||
@@ -636,6 +636,9 @@ static int gather_frames_to_render_for_id(LibraryIDLinkCallbackData *cb_data)
|
||||
* system that requires specific handling here. */
|
||||
gather_frames_to_render_for_grease_pencil(oglrender, (bGPdata *)id);
|
||||
break;
|
||||
case ID_GP:
|
||||
/* TODO: gather frames. */
|
||||
break;
|
||||
}
|
||||
|
||||
AnimData *adt = BKE_animdata_from_id(id);
|
||||
|
||||
@@ -41,6 +41,8 @@ set(SRC
|
||||
curves_sculpt_slide.cc
|
||||
curves_sculpt_smooth.cc
|
||||
curves_sculpt_snake_hook.cc
|
||||
grease_pencil_draw_ops.cc
|
||||
grease_pencil_paint.cc
|
||||
paint_canvas.cc
|
||||
paint_cursor.cc
|
||||
paint_curve.cc
|
||||
@@ -86,6 +88,7 @@ set(SRC
|
||||
sculpt_uv.cc
|
||||
|
||||
curves_sculpt_intern.hh
|
||||
grease_pencil_intern.hh
|
||||
paint_intern.hh
|
||||
sculpt_intern.hh
|
||||
)
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
|
||||
#include "ED_grease_pencil_draw.h"
|
||||
#include "ED_object.h"
|
||||
#include "ED_screen.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_message.h"
|
||||
#include "WM_toolsystem.h"
|
||||
|
||||
#include "grease_pencil_intern.hh"
|
||||
#include "paint_intern.hh"
|
||||
|
||||
namespace blender::ed::sculpt_paint {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Brush Stroke Operator
|
||||
* \{ */
|
||||
|
||||
static bool start_brush_operation(bContext &C,
|
||||
wmOperator & /*op*/,
|
||||
PaintStroke *paint_stroke,
|
||||
const StrokeExtension & /*stroke_start*/)
|
||||
{
|
||||
// const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op.ptr, "mode"));
|
||||
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
const GpPaint &gp_paint = *scene.toolsettings->gp_paint;
|
||||
const Brush &brush = *BKE_paint_brush_for_read(&gp_paint.paint);
|
||||
GreasePencilStrokeOperation *operation = nullptr;
|
||||
switch (brush.gpencil_tool) {
|
||||
case GPAINT_TOOL_DRAW:
|
||||
/* FIXME: Somehow store the unique_ptr in the PaintStroke. */
|
||||
operation = greasepencil::new_paint_operation().release();
|
||||
break;
|
||||
}
|
||||
|
||||
if (operation) {
|
||||
paint_stroke_set_mode_data(paint_stroke, operation);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool stroke_get_location(bContext * /*C*/,
|
||||
float out[3],
|
||||
const float mouse[2],
|
||||
bool /*force_original*/)
|
||||
{
|
||||
out[0] = mouse[0];
|
||||
out[1] = mouse[1];
|
||||
out[2] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2])
|
||||
{
|
||||
PaintStroke *paint_stroke = static_cast<PaintStroke *>(op->customdata);
|
||||
|
||||
StrokeExtension stroke_extension;
|
||||
stroke_extension.mouse_position = float2(mouse);
|
||||
stroke_extension.pressure = 0.0f;
|
||||
stroke_extension.is_first = true;
|
||||
|
||||
if (!start_brush_operation(*C, *op, paint_stroke, stroke_extension)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void stroke_update_step(bContext *C,
|
||||
wmOperator * /*op*/,
|
||||
PaintStroke *stroke,
|
||||
PointerRNA *stroke_element)
|
||||
{
|
||||
GreasePencilStrokeOperation *operation = static_cast<GreasePencilStrokeOperation *>(
|
||||
paint_stroke_mode_data(stroke));
|
||||
|
||||
StrokeExtension stroke_extension;
|
||||
RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position);
|
||||
stroke_extension.pressure = RNA_float_get(stroke_element, "pressure");
|
||||
stroke_extension.is_first = false;
|
||||
|
||||
if (operation) {
|
||||
operation->on_stroke_extended(*C, stroke_extension);
|
||||
}
|
||||
}
|
||||
|
||||
static void stroke_redraw(const bContext *C, PaintStroke * /*stroke*/, bool /*final*/)
|
||||
{
|
||||
ED_region_tag_redraw(CTX_wm_region(C));
|
||||
}
|
||||
|
||||
static void stroke_done(const bContext *C, PaintStroke *stroke)
|
||||
{
|
||||
GreasePencilStrokeOperation *operation = static_cast<GreasePencilStrokeOperation *>(
|
||||
paint_stroke_mode_data(stroke));
|
||||
operation->on_stroke_done(*C);
|
||||
}
|
||||
|
||||
static int grease_pencil_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
const Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
const Brush *brush = BKE_paint_brush_for_read(paint);
|
||||
if (brush == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
op->customdata = paint_stroke_new(C,
|
||||
op,
|
||||
stroke_get_location,
|
||||
stroke_test_start,
|
||||
stroke_update_step,
|
||||
stroke_redraw,
|
||||
stroke_done,
|
||||
event->type);
|
||||
|
||||
const int return_value = op->type->modal(C, op, event);
|
||||
if (return_value == OPERATOR_FINISHED) {
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
WM_event_add_modal_handler(C, op);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int grease_pencil_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
|
||||
}
|
||||
|
||||
static void grease_pencil_stroke_cancel(bContext *C, wmOperator *op)
|
||||
{
|
||||
paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
|
||||
}
|
||||
|
||||
static void GREASE_PENCIL_OT_brush_stroke(struct wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Stroke Curves Sculpt";
|
||||
ot->idname = "GREASE_PENCIL_OT_brush_stroke";
|
||||
ot->description = "Sculpt curves using a brush";
|
||||
|
||||
ot->invoke = grease_pencil_stroke_invoke;
|
||||
ot->modal = grease_pencil_stroke_modal;
|
||||
ot->cancel = grease_pencil_stroke_cancel;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
paint_stroke_operator_properties(ot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Toggle Draw Mode
|
||||
* \{ */
|
||||
|
||||
static bool grease_pencil_poll(bContext *C)
|
||||
{
|
||||
Object *object = CTX_data_active_object(C);
|
||||
if (object == nullptr || object->type != OB_GREASE_PENCIL) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void grease_pencil_draw_mode_enter(bContext *C)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
wmMsgBus *mbus = CTX_wm_message_bus(C);
|
||||
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
GpPaint *grease_pencil_paint = scene->toolsettings->gp_paint;
|
||||
BKE_paint_ensure(scene->toolsettings, (Paint **)&grease_pencil_paint);
|
||||
|
||||
ob->mode = OB_MODE_PAINT_GPENCIL;
|
||||
|
||||
/* TODO: Setup cursor color. BKE_paint_init() could be used, but creates an additional brush. */
|
||||
/* TODO: Call ED_paint_cursor_start(...) */
|
||||
|
||||
paint_init_pivot(ob, scene);
|
||||
|
||||
/* Necessary to change the object mode on the evaluated object. */
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
|
||||
WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr);
|
||||
}
|
||||
|
||||
static void grease_pencil_draw_mode_exit(bContext *C)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
ob->mode = OB_MODE_OBJECT;
|
||||
}
|
||||
|
||||
static int grease_pencil_draw_mode_toggle_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
wmMsgBus *mbus = CTX_wm_message_bus(C);
|
||||
|
||||
const bool is_mode_set = ob->mode == OB_MODE_PAINT_GPENCIL;
|
||||
|
||||
if (is_mode_set) {
|
||||
if (!ED_object_mode_compat_set(C, ob, OB_MODE_PAINT_GPENCIL, op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_mode_set) {
|
||||
grease_pencil_draw_mode_exit(C);
|
||||
}
|
||||
else {
|
||||
grease_pencil_draw_mode_enter(C);
|
||||
}
|
||||
|
||||
WM_toolsystem_update_from_context_view3d(C);
|
||||
|
||||
/* Necessary to change the object mode on the evaluated object. */
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
|
||||
WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void GREASE_PENCIL_OT_draw_mode_toggle(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Grease Pencil Draw Mode Toggle";
|
||||
ot->idname = "GREASE_PENCIL_OT_draw_mode_toggle";
|
||||
ot->description = "Enter/Exit draw mode for grease pencil";
|
||||
|
||||
ot->exec = grease_pencil_draw_mode_toggle_exec;
|
||||
ot->poll = grease_pencil_poll;
|
||||
|
||||
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Registration
|
||||
* \{ */
|
||||
|
||||
void ED_operatortypes_grease_pencil_draw()
|
||||
{
|
||||
using namespace blender::ed::sculpt_paint;
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_brush_stroke);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_draw_mode_toggle);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,31 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paint_intern.hh"
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
namespace blender::ed::sculpt_paint {
|
||||
|
||||
struct StrokeExtension {
|
||||
bool is_first;
|
||||
float2 mouse_position;
|
||||
float pressure;
|
||||
};
|
||||
|
||||
class GreasePencilStrokeOperation {
|
||||
public:
|
||||
virtual ~GreasePencilStrokeOperation() = default;
|
||||
virtual void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) = 0;
|
||||
virtual void on_stroke_done(const bContext &C) = 0;
|
||||
};
|
||||
|
||||
namespace greasepencil {
|
||||
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_paint_operation();
|
||||
|
||||
} // namespace greasepencil
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
@@ -0,0 +1,176 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "grease_pencil_intern.hh"
|
||||
|
||||
namespace blender::ed::sculpt_paint::greasepencil {
|
||||
|
||||
class PaintOperation : public GreasePencilStrokeOperation {
|
||||
|
||||
public:
|
||||
~PaintOperation() override {}
|
||||
|
||||
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
|
||||
void on_stroke_done(const bContext &C) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility class that actually executes the update when the stroke is updated. That's useful
|
||||
* because it avoids passing a very large number of parameters between functions.
|
||||
*/
|
||||
struct PaintOperationExecutor {
|
||||
|
||||
PaintOperationExecutor(const bContext & /*C*/) {}
|
||||
|
||||
void execute(PaintOperation & /*self*/,
|
||||
const bContext &C,
|
||||
const StrokeExtension &stroke_extension)
|
||||
{
|
||||
using namespace blender::bke;
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
|
||||
Scene *scene = CTX_data_scene(&C);
|
||||
ARegion *region = CTX_wm_region(&C);
|
||||
Object *obact = CTX_data_active_object(&C);
|
||||
Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact);
|
||||
|
||||
/**
|
||||
* Note: We write to the evaluated object here, so that the additional copy from orig -> eval
|
||||
* is not needed for every update. After the stroke is done, the result is written to the
|
||||
* original object.
|
||||
*/
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob_eval->data);
|
||||
if (!grease_pencil.has_active_layer()) {
|
||||
/* TODO: create a new layer. */
|
||||
BLI_assert_unreachable();
|
||||
// grease_pencil.runtime->set_active_layer_index(0);
|
||||
}
|
||||
const bke::greasepencil::Layer &active_layer = grease_pencil.active_layer->wrap();
|
||||
int index = active_layer.drawing_index_at(scene->r.cfra);
|
||||
BLI_assert(index != -1);
|
||||
|
||||
GreasePencilDrawing &drawing = *reinterpret_cast<GreasePencilDrawing *>(
|
||||
grease_pencil.drawings()[index]);
|
||||
|
||||
float4 plane{0.0f, -1.0f, 0.0f, 0.0f};
|
||||
float3 proj_pos;
|
||||
ED_view3d_win_to_3d_on_plane(region, plane, stroke_extension.mouse_position, false, proj_pos);
|
||||
|
||||
bke::greasepencil::StrokePoint new_point{
|
||||
proj_pos, stroke_extension.pressure * 100.0f, 1.0f, float4(1.0f)};
|
||||
|
||||
drawing.runtime->stroke_cache.points.append(std::move(new_point));
|
||||
|
||||
BKE_grease_pencil_batch_cache_dirty_tag(&grease_pencil, BKE_GREASEPENCIL_BATCH_DIRTY_ALL);
|
||||
}
|
||||
};
|
||||
|
||||
void PaintOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
|
||||
{
|
||||
PaintOperationExecutor executor{C};
|
||||
executor.execute(*this, C, stroke_extension);
|
||||
}
|
||||
|
||||
void PaintOperation::on_stroke_done(const bContext &C)
|
||||
{
|
||||
using namespace blender::bke;
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
|
||||
Scene *scene = CTX_data_scene(&C);
|
||||
Object *obact = CTX_data_active_object(&C);
|
||||
Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact);
|
||||
|
||||
GreasePencil &grease_pencil_orig = *static_cast<GreasePencil *>(obact->data);
|
||||
GreasePencil &grease_pencil_eval = *static_cast<GreasePencil *>(ob_eval->data);
|
||||
BLI_assert(grease_pencil_orig.has_active_layer() && grease_pencil_eval.has_active_layer());
|
||||
const bke::greasepencil::Layer &active_layer_orig = grease_pencil_orig.active_layer->wrap();
|
||||
const bke::greasepencil::Layer &active_layer_eval = grease_pencil_eval.active_layer->wrap();
|
||||
int index_orig = active_layer_orig.drawing_index_at(scene->r.cfra);
|
||||
int index_eval = active_layer_eval.drawing_index_at(scene->r.cfra);
|
||||
BLI_assert(index_orig != -1 && index_eval != -1);
|
||||
|
||||
GreasePencilDrawing &drawing_orig = *reinterpret_cast<GreasePencilDrawing *>(
|
||||
grease_pencil_orig.drawings()[index_orig]);
|
||||
GreasePencilDrawing &drawing_eval = *reinterpret_cast<GreasePencilDrawing *>(
|
||||
grease_pencil_eval.drawings()[index_eval]);
|
||||
|
||||
const Span<bke::greasepencil::StrokePoint> stroke_points = drawing_eval.stroke_buffer();
|
||||
CurvesGeometry &curves = drawing_orig.geometry.wrap();
|
||||
|
||||
int num_old_curves = curves.curves_num();
|
||||
int num_old_points = curves.points_num();
|
||||
curves.resize(num_old_points + stroke_points.size(), num_old_curves + 1);
|
||||
|
||||
curves.offsets_for_write()[num_old_curves] = num_old_points;
|
||||
curves.offsets_for_write()[num_old_curves + 1] = num_old_points + stroke_points.size();
|
||||
|
||||
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
const IndexRange new_points_range = points_by_curve[curves.curves_num() - 1];
|
||||
const IndexRange new_curves_range = IndexRange(num_old_curves, 1);
|
||||
|
||||
/* Set position, radius and opacity attribute. */
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
SpanAttributeWriter<float> radii = attributes.lookup_for_write_span<float>("radius");
|
||||
SpanAttributeWriter<float> opacities = attributes.lookup_for_write_span<float>("opacity");
|
||||
for (const int i : IndexRange(stroke_points.size())) {
|
||||
const bke::greasepencil::StrokePoint &point = stroke_points[i];
|
||||
const int point_i = new_points_range[i];
|
||||
positions[point_i] = point.position;
|
||||
radii.span[point_i] = point.radius;
|
||||
opacities.span[point_i] = point.opacity;
|
||||
}
|
||||
|
||||
/* Set material index attribute. */
|
||||
int material_index = 0;
|
||||
SpanAttributeWriter<int> materials = attributes.lookup_for_write_span<int>("material_index");
|
||||
materials.span.slice(new_curves_range).fill(material_index);
|
||||
|
||||
/* Set curve_type attribute. */
|
||||
curves.fill_curve_types(new_curves_range, CURVE_TYPE_POLY);
|
||||
|
||||
/* Explicitly set all other attributes besides those processed above to default values. */
|
||||
Set<std::string> attributes_to_skip{
|
||||
{"position", "radius", "opacity", "material_index", "curve_type"}};
|
||||
attributes.for_all(
|
||||
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) {
|
||||
if (attributes_to_skip.contains(id.name())) {
|
||||
return true;
|
||||
}
|
||||
bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
|
||||
const CPPType &type = attribute.span.type();
|
||||
GMutableSpan new_data = attribute.span.slice(
|
||||
attribute.domain == ATTR_DOMAIN_POINT ? new_points_range : new_curves_range);
|
||||
type.fill_assign_n(type.default_value(), new_data.data(), new_data.size());
|
||||
attribute.finish();
|
||||
return true;
|
||||
});
|
||||
|
||||
drawing_eval.runtime->stroke_cache.clear();
|
||||
drawing_orig.tag_positions_changed();
|
||||
|
||||
radii.finish();
|
||||
opacities.finish();
|
||||
materials.finish();
|
||||
|
||||
DEG_id_tag_update(&grease_pencil_orig.id, ID_RECALC_GEOMETRY);
|
||||
WM_main_add_notifier(NC_GEOM | ND_DATA, &grease_pencil_orig.id);
|
||||
}
|
||||
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_paint_operation()
|
||||
{
|
||||
return std::make_unique<PaintOperation>();
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint::greasepencil
|
||||
@@ -814,6 +814,12 @@ static void paint_init_pivot_curves(Object *ob, float location[3])
|
||||
interp_v3_v3v3(location, bbox->vec[0], bbox->vec[6], 0.5f);
|
||||
}
|
||||
|
||||
static void paint_init_pivot_grease_pencil(Object *ob, float location[3])
|
||||
{
|
||||
const BoundBox *bbox = BKE_object_boundbox_get(ob);
|
||||
interp_v3_v3v3(location, bbox->vec[0], bbox->vec[6], 0.5f);
|
||||
}
|
||||
|
||||
void paint_init_pivot(Object *ob, Scene *scene)
|
||||
{
|
||||
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
|
||||
@@ -826,6 +832,9 @@ void paint_init_pivot(Object *ob, Scene *scene)
|
||||
case OB_CURVES:
|
||||
paint_init_pivot_curves(ob, location);
|
||||
break;
|
||||
case OB_GREASE_PENCIL:
|
||||
paint_init_pivot_grease_pencil(ob, location);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
ups->last_stroke_valid = false;
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "ED_geometry.h"
|
||||
#include "ED_gizmo_library.h"
|
||||
#include "ED_gpencil_legacy.h"
|
||||
#include "ED_grease_pencil_draw.h"
|
||||
#include "ED_lattice.h"
|
||||
#include "ED_markers.h"
|
||||
#include "ED_mask.h"
|
||||
@@ -91,6 +92,7 @@ void ED_spacetypes_init(void)
|
||||
ED_operatortypes_anim();
|
||||
ED_operatortypes_animchannels();
|
||||
ED_operatortypes_asset();
|
||||
ED_operatortypes_grease_pencil_draw();
|
||||
ED_operatortypes_gpencil();
|
||||
ED_operatortypes_object();
|
||||
ED_operatortypes_lattice();
|
||||
|
||||
@@ -186,7 +186,8 @@ static void stats_object(Object *ob,
|
||||
}
|
||||
case OB_CURVES:
|
||||
case OB_POINTCLOUD:
|
||||
case OB_VOLUME: {
|
||||
case OB_VOLUME:
|
||||
case OB_GREASE_PENCIL: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2368,6 +2368,8 @@ static BIFIconID tree_element_get_icon_from_id(const ID *id)
|
||||
}
|
||||
case OB_GPENCIL_LEGACY:
|
||||
return ICON_OUTLINER_OB_GREASEPENCIL;
|
||||
case OB_GREASE_PENCIL:
|
||||
return ICON_OUTLINER_OB_GREASEPENCIL;
|
||||
}
|
||||
|
||||
return ICON_NONE;
|
||||
|
||||
@@ -133,7 +133,8 @@ struct TreeElementIcon {
|
||||
ID_CV, \
|
||||
ID_PT, \
|
||||
ID_VO, \
|
||||
ID_SIM) || /* Only in 'blendfile' mode ... :/ */ \
|
||||
ID_SIM, \
|
||||
ID_GP) || /* Only in 'blendfile' mode ... :/ */ \
|
||||
ELEM(GS((_id)->name), \
|
||||
ID_SCR, \
|
||||
ID_WM, \
|
||||
|
||||
@@ -165,6 +165,7 @@ static void get_element_operation_type(
|
||||
case ID_PT:
|
||||
case ID_VO:
|
||||
case ID_SIM:
|
||||
case ID_GP:
|
||||
is_standard_id = true;
|
||||
break;
|
||||
case ID_WM:
|
||||
|
||||
@@ -74,6 +74,7 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t
|
||||
case ID_PAL:
|
||||
case ID_PC:
|
||||
case ID_CF:
|
||||
case ID_GP:
|
||||
return std::make_unique<TreeElementID>(legacy_te, id);
|
||||
case ID_IP:
|
||||
BLI_assert_unreachable();
|
||||
|
||||
@@ -53,6 +53,7 @@ set(SRC
|
||||
../include/ED_gizmo_library.h
|
||||
../include/ED_gizmo_utils.h
|
||||
../include/ED_gpencil_legacy.h
|
||||
../include/ED_grease_pencil_draw.h
|
||||
../include/ED_image.h
|
||||
../include/ED_info.h
|
||||
../include/ED_keyframes_draw.h
|
||||
|
||||
@@ -29,6 +29,7 @@ set(INC
|
||||
# For *_info.hh includes.
|
||||
../compositor/realtime_compositor
|
||||
../draw/engines/eevee_next
|
||||
../draw/engines/gpencil
|
||||
../draw/engines/select
|
||||
../draw/engines/workbench
|
||||
../draw/intern
|
||||
|
||||
@@ -129,6 +129,7 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_data_writer(const Hierarch
|
||||
case OB_LATTICE:
|
||||
case OB_ARMATURE:
|
||||
case OB_GPENCIL_LEGACY:
|
||||
case OB_GREASE_PENCIL:
|
||||
case OB_POINTCLOUD:
|
||||
return nullptr;
|
||||
case OB_TYPE_MAX:
|
||||
|
||||
@@ -1151,6 +1151,7 @@ typedef enum IDRecalcFlag {
|
||||
#define FILTER_ID_SCR (1ULL << 37)
|
||||
#define FILTER_ID_WM (1ULL << 38)
|
||||
#define FILTER_ID_LI (1ULL << 39)
|
||||
#define FILTER_ID_GP (1ULL << 40)
|
||||
|
||||
#define FILTER_ID_ALL \
|
||||
(FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU_LEGACY | \
|
||||
@@ -1159,7 +1160,7 @@ typedef enum IDRecalcFlag {
|
||||
FILTER_ID_NT | FILTER_ID_OB | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | \
|
||||
FILTER_ID_SPK | FILTER_ID_SO | FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | \
|
||||
FILTER_ID_CF | FILTER_ID_WS | FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | \
|
||||
FILTER_ID_SIM | FILTER_ID_KE | FILTER_ID_SCR | FILTER_ID_WM | FILTER_ID_LI)
|
||||
FILTER_ID_SIM | FILTER_ID_KE | FILTER_ID_SCR | FILTER_ID_WM | FILTER_ID_LI | FILTER_ID_GP)
|
||||
|
||||
/**
|
||||
* This enum defines the index assigned to each type of IDs in the array returned by
|
||||
@@ -1250,6 +1251,7 @@ enum {
|
||||
INDEX_ID_CA,
|
||||
INDEX_ID_SPK,
|
||||
INDEX_ID_LP,
|
||||
INDEX_ID_GP,
|
||||
|
||||
/* Collection and object types. */
|
||||
INDEX_ID_OB,
|
||||
|
||||
@@ -82,6 +82,7 @@ typedef enum ID_Type {
|
||||
ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */
|
||||
ID_VO = MAKE_ID2('V', 'O'), /* Volume */
|
||||
ID_SIM = MAKE_ID2('S', 'I'), /* Simulation (geometry node groups) */
|
||||
ID_GP = MAKE_ID2('G', 'P'), /* Grease Pencil */
|
||||
} ID_Type;
|
||||
|
||||
/* Only used as 'placeholder' in .blend files for directly linked data-blocks. */
|
||||
|
||||
@@ -0,0 +1,466 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup DNA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_curves_types.h"
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include "BLI_function_ref.hh"
|
||||
# include "BLI_map.hh"
|
||||
# include "BLI_math_vector_types.hh"
|
||||
# include "BLI_span.hh"
|
||||
namespace blender::bke {
|
||||
class GreasePencilRuntime;
|
||||
class GreasePencilDrawingRuntime;
|
||||
namespace greasepencil {
|
||||
class DrawingRuntime;
|
||||
class TreeNode;
|
||||
class Layer;
|
||||
class LayerRuntime;
|
||||
class LayerGroup;
|
||||
class LayerGroupRuntime;
|
||||
struct StrokePoint;
|
||||
} // namespace greasepencil
|
||||
} // namespace blender::bke
|
||||
using GreasePencilRuntimeHandle = blender::bke::GreasePencilRuntime;
|
||||
using GreasePencilDrawingRuntimeHandle = blender::bke::greasepencil::DrawingRuntime;
|
||||
using GreasePencilLayerRuntimeHandle = blender::bke::greasepencil::LayerRuntime;
|
||||
using GreasePencilLayerGroupRuntimeHandle = blender::bke::greasepencil::LayerGroupRuntime;
|
||||
#else
|
||||
typedef struct GreasePencilRuntimeHandle GreasePencilRuntimeHandle;
|
||||
typedef struct GreasePencilDrawingRuntimeHandle GreasePencilDrawingRuntimeHandle;
|
||||
typedef struct GreasePencilLayerRuntimeHandle GreasePencilLayerRuntimeHandle;
|
||||
typedef struct GreasePencilLayerGroupRuntimeHandle GreasePencilLayerGroupRuntimeHandle;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct GreasePencil;
|
||||
struct BlendDataReader;
|
||||
struct BlendWriter;
|
||||
struct Object;
|
||||
|
||||
typedef enum GreasePencilStrokeCapType {
|
||||
GP_STROKE_CAP_TYPE_ROUND = 0,
|
||||
GP_STROKE_CAP_TYPE_FLAT = 1,
|
||||
/* Keep last. */
|
||||
GP_STROKE_CAP_TYPE_MAX,
|
||||
} GreasePencilStrokeCapType;
|
||||
|
||||
/**
|
||||
* Type of drawing data.
|
||||
* If `GP_DRAWING` the node is a `GreasePencilDrawing`,
|
||||
* if `GP_DRAWING_REFERENCE` the node is a `GreasePencilDrawingReference`.
|
||||
*/
|
||||
typedef enum GreasePencilDrawingType {
|
||||
GP_DRAWING = 0,
|
||||
GP_DRAWING_REFERENCE = 1,
|
||||
} GreasePencilDrawingType;
|
||||
|
||||
/**
|
||||
* Flag for drawings and drawing references. #GreasePencilDrawingBase.flag
|
||||
*/
|
||||
typedef enum GreasePencilDrawingBaseFlag {
|
||||
/* TODO */
|
||||
GreasePencilDrawingBaseFlag_TODO
|
||||
} GreasePencilDrawingBaseFlag;
|
||||
|
||||
/**
|
||||
* Base class for drawings and drawing references (drawings from other objects).
|
||||
*/
|
||||
typedef struct GreasePencilDrawingBase {
|
||||
/**
|
||||
* One of `GreasePencilDrawingType`.
|
||||
* Indicates if this is an actual drawing or a drawing referenced from another object.
|
||||
*/
|
||||
int8_t type;
|
||||
char _pad[3];
|
||||
/**
|
||||
* Flag. Used to set e.g. the selection status. See `GreasePencilDrawingBaseFlag`.
|
||||
*/
|
||||
uint32_t flag;
|
||||
} GreasePencilDrawingBase;
|
||||
|
||||
/**
|
||||
* A grease pencil drawing is a set of strokes. The data is stored using the `CurvesGeometry` data
|
||||
* structure and the custom attributes within it.
|
||||
*/
|
||||
typedef struct GreasePencilDrawing {
|
||||
GreasePencilDrawingBase base;
|
||||
/**
|
||||
* The stroke data for this drawing.
|
||||
*/
|
||||
CurvesGeometry geometry;
|
||||
/**
|
||||
* Runtime data on the drawing.
|
||||
*/
|
||||
GreasePencilDrawingRuntimeHandle *runtime;
|
||||
#ifdef __cplusplus
|
||||
/**
|
||||
* The triangles for all the fills in the geometry.
|
||||
*/
|
||||
blender::Span<blender::uint3> triangles() const;
|
||||
void tag_positions_changed();
|
||||
/**
|
||||
* A buffer for a single stroke while drawing.
|
||||
*/
|
||||
blender::Span<blender::bke::greasepencil::StrokePoint> stroke_buffer() const;
|
||||
bool has_stroke_buffer() const;
|
||||
#endif
|
||||
} GreasePencilDrawing;
|
||||
|
||||
typedef struct GreasePencilDrawingReference {
|
||||
GreasePencilDrawingBase base;
|
||||
/**
|
||||
* A reference to another GreasePencil data-block.
|
||||
* If the data-block has multiple drawings, this drawing references all of them sequentially.
|
||||
* See the note in `GreasePencilLayer->frames()` for a detailed explanation of this.
|
||||
*/
|
||||
struct GreasePencil *id_reference;
|
||||
} GreasePencilDrawingReference;
|
||||
|
||||
/**
|
||||
* Flag for grease pencil frames. #GreasePencilFrame.flag
|
||||
*/
|
||||
typedef enum GreasePencilFrameFlag {
|
||||
GP_FRAME_SELECTED = (1 << 0),
|
||||
} GreasePencilFrameFlag;
|
||||
|
||||
/**
|
||||
* A GreasePencilFrame is a single keyframe in the timeline.
|
||||
* It references a drawing by index into the drawing array.
|
||||
*/
|
||||
typedef struct GreasePencilFrame {
|
||||
/**
|
||||
* Index into the GreasePencil->drawings array.
|
||||
*/
|
||||
int drawing_index;
|
||||
/**
|
||||
* Flag. Used to set e.g. the selection.
|
||||
*/
|
||||
uint32_t flag;
|
||||
/**
|
||||
* Keyframe type. See `eBezTriple_KeyframeType`.
|
||||
*/
|
||||
int8_t type;
|
||||
char _pad[3];
|
||||
} GreasePencilFrame;
|
||||
|
||||
typedef enum GreasePencilLayerFramesMapStorageFlag {
|
||||
GP_LAYER_FRAMES_STORAGE_DIRTY = (1 << 0),
|
||||
} GreasePencilLayerFramesMapStorageFlag;
|
||||
|
||||
/**
|
||||
* Storage for the Map in `blender::bke::greasepencil::Layer`.
|
||||
* See the description there for more detail.
|
||||
*/
|
||||
typedef struct GreasePencilLayerFramesMapStorage {
|
||||
/* Array of `frames` keys (sorted in ascending order). */
|
||||
int *keys;
|
||||
/* Array of `frames` values (order matches the keys array). */
|
||||
GreasePencilFrame *values;
|
||||
/* Size of the map (number of key-value pairs). */
|
||||
int size;
|
||||
/* Flag for the status of the storage. */
|
||||
int flag;
|
||||
} GreasePencilLayerFramesMapStorage;
|
||||
|
||||
/**
|
||||
* Flag for layer masks. #GreasePencilLayerMask.flag
|
||||
*/
|
||||
typedef enum GreasePencilLayerMaskFlag {
|
||||
GP_LAYER_MASK_HIDE = (1 << 0),
|
||||
GP_LAYER_MASK_INVERT = (1 << 1),
|
||||
} GreasePencilLayerMaskFlag;
|
||||
|
||||
/**
|
||||
* A grease pencil layer mask stores the name of a layer that is the mask.
|
||||
*/
|
||||
typedef struct GreasePencilLayerMask {
|
||||
struct GreasePencilLayerMask *next, *prev;
|
||||
/**
|
||||
* The name of the layer that is the mask.
|
||||
*/
|
||||
char *layer_name;
|
||||
/**
|
||||
* Layer mask flag. See `GreasePencilLayerMaskFlag`.
|
||||
*/
|
||||
uint16_t flag;
|
||||
char _pad[6];
|
||||
} GreasePencilLayerMask;
|
||||
|
||||
/**
|
||||
* Layer blending modes. #GreasePencilLayer.blend_mode
|
||||
*/
|
||||
typedef enum GreasePencilLayerBlendMode {
|
||||
GP_LAYER_BLEND_NONE = 0,
|
||||
GP_LAYER_BLEND_HARDLIGHT = 1,
|
||||
GP_LAYER_BLEND_ADD = 2,
|
||||
GP_LAYER_BLEND_SUBTRACT = 3,
|
||||
GP_LAYER_BLEND_MULTIPLY = 4,
|
||||
GP_LAYER_BLEND_DIVIDE = 5,
|
||||
} GreasePencilLayerBlendMode;
|
||||
|
||||
/**
|
||||
* Type of layer node.
|
||||
* If `GP_LAYER_TREE_LEAF` the node is a `GreasePencilLayerTreeLeaf`,
|
||||
* if `GP_LAYER_TREE_GROUP` the node is a `GreasePencilLayerTreeGroup`.
|
||||
*/
|
||||
typedef enum GreasePencilLayerTreeNodeType {
|
||||
GP_LAYER_TREE_LEAF = 0,
|
||||
GP_LAYER_TREE_GROUP = 1,
|
||||
} GreasePencilLayerTreeNodeType;
|
||||
|
||||
/**
|
||||
* Flags for layer tree nodes. #GreasePencilLayerTreeNode.flag
|
||||
*/
|
||||
typedef enum GreasePencilLayerTreeNodeFlag {
|
||||
GP_LAYER_TREE_NODE_HIDE = (1 << 0),
|
||||
GP_LAYER_TREE_NODE_LOCKED = (1 << 1),
|
||||
GP_LAYER_TREE_NODE_SELECT = (1 << 2),
|
||||
GP_LAYER_TREE_NODE_MUTE = (1 << 3),
|
||||
GP_LAYER_TREE_NODE_USE_LIGHTS = (1 << 4),
|
||||
GP_LAYER_TREE_NODE_USE_ONION_SKINNING = (1 << 5),
|
||||
} GreasePencilLayerTreeNodeFlag;
|
||||
|
||||
struct GreasePencilLayerTreeGroup;
|
||||
typedef struct GreasePencilLayerTreeNode {
|
||||
/* ListBase pointers. */
|
||||
struct GreasePencilLayerTreeNode *next, *prev;
|
||||
/* Parent pointer. Can be null. */
|
||||
struct GreasePencilLayerTreeGroup *parent;
|
||||
/**
|
||||
* Name of the layer/group. Dynamic length.
|
||||
*/
|
||||
char *name;
|
||||
/**
|
||||
* One of `GreasePencilLayerTreeNodeType`.
|
||||
* Indicates the type of struct this element is.
|
||||
*/
|
||||
int8_t type;
|
||||
/**
|
||||
* Color tag.
|
||||
*/
|
||||
uint8_t color[3];
|
||||
/**
|
||||
* Flag. Used to set e.g. the selection, visibility, ... status.
|
||||
* See `GreasePencilLayerTreeNodeFlag`.
|
||||
*/
|
||||
uint32_t flag;
|
||||
#ifdef __cplusplus
|
||||
blender::bke::greasepencil::TreeNode &wrap();
|
||||
const blender::bke::greasepencil::TreeNode &wrap() const;
|
||||
#endif
|
||||
} GreasePencilLayerTreeNode;
|
||||
|
||||
/**
|
||||
* A grease pencil layer is a collection of drawings mapped to a specific time on the timeline.
|
||||
*/
|
||||
typedef struct GreasePencilLayer {
|
||||
GreasePencilLayerTreeNode base;
|
||||
/* Only used for storage in the .blend file. */
|
||||
GreasePencilLayerFramesMapStorage frames_storage;
|
||||
/**
|
||||
* Layer blend mode. See `GreasePencilLayerBlendMode`.
|
||||
*/
|
||||
int8_t blend_mode;
|
||||
char _pad[3];
|
||||
/**
|
||||
* Opacity of the layer.
|
||||
*/
|
||||
float opacity;
|
||||
/**
|
||||
* List of `GreasePencilLayerMask`.
|
||||
*/
|
||||
ListBase masks;
|
||||
/**
|
||||
* Runtime struct pointer.
|
||||
*/
|
||||
GreasePencilLayerRuntimeHandle *runtime;
|
||||
#ifdef __cplusplus
|
||||
blender::bke::greasepencil::Layer &wrap();
|
||||
const blender::bke::greasepencil::Layer &wrap() const;
|
||||
#endif
|
||||
} GreasePencilLayer;
|
||||
|
||||
typedef struct GreasePencilLayerTreeGroup {
|
||||
GreasePencilLayerTreeNode base;
|
||||
/**
|
||||
* List of `GreasePencilLayerTreeNode`.
|
||||
*/
|
||||
ListBase children;
|
||||
/**
|
||||
* Runtime struct pointer.
|
||||
*/
|
||||
GreasePencilLayerGroupRuntimeHandle *runtime;
|
||||
#ifdef __cplusplus
|
||||
blender::bke::greasepencil::LayerGroup &wrap();
|
||||
const blender::bke::greasepencil::LayerGroup &wrap() const;
|
||||
#endif
|
||||
} GreasePencilLayerTreeGroup;
|
||||
|
||||
/**
|
||||
* Flag for the grease pencil data-block. #GreasePencil.flag
|
||||
*/
|
||||
typedef enum GreasePencilFlag {
|
||||
/* TODO */
|
||||
GreasePencilFlag_TODO
|
||||
} GreasePencilFlag;
|
||||
|
||||
/**
|
||||
* Onion skinning mode. #GreasePencilOnionSkinningSettings.mode
|
||||
*/
|
||||
typedef enum GreasePencilOnionSkinningMode {
|
||||
GP_ONION_SKINNING_MODE_ABSOLUTE = 0,
|
||||
GP_ONION_SKINNING_MODE_RELATIVE = 1,
|
||||
GP_ONION_SKINNING_MODE_SELECTED = 2,
|
||||
} GreasePencilOnionSkinningMode;
|
||||
|
||||
/**
|
||||
* Flag for filtering the onion skinning per keyframe type.
|
||||
* #GreasePencilOnionSkinningSettings.filter
|
||||
* \note needs to match order of `eBezTriple_KeyframeType`.
|
||||
*/
|
||||
typedef enum GreasePencilOnionSkinningFilter {
|
||||
GP_ONION_SKINNING_FILTER_KEYTYPE_KEYFRAME = (1 << 0),
|
||||
GP_ONION_SKINNING_FILTER_KEYTYPE_EXTREME = (1 << 1),
|
||||
GP_ONION_SKINNING_FILTER_KEYTYPE_BREAKDOWN = (1 << 2),
|
||||
GP_ONION_SKINNING_FILTER_KEYTYPE_JITTER = (1 << 3),
|
||||
GP_ONION_SKINNING_FILTER_KEYTYPE_MOVEHOLD = (1 << 4),
|
||||
} GreasePencilOnionSkinningFilter;
|
||||
|
||||
#define GREASE_PENCIL_ONION_SKINNING_FILTER_ALL \
|
||||
(GP_ONION_SKINNING_FILTER_KEYTYPE_KEYFRAME | GP_ONION_SKINNING_FILTER_KEYTYPE_EXTREME | \
|
||||
GP_ONION_SKINNING_FILTER_KEYTYPE_BREAKDOWN | GP_ONION_SKINNING_FILTER_KEYTYPE_JITTER | \
|
||||
GP_ONION_SKINNING_FILTER_KEYTYPE_MOVEHOLD)
|
||||
|
||||
/**
|
||||
* Per data-block Grease Pencil onion skinning settings.
|
||||
*/
|
||||
typedef struct GreasePencilOnionSkinningSettings {
|
||||
/**
|
||||
* Opacity for the ghost frames.
|
||||
*/
|
||||
float opacity;
|
||||
/**
|
||||
* Onion skinning mode. See `GreasePencilOnionSkinningMode`.
|
||||
*/
|
||||
int8_t mode;
|
||||
/**
|
||||
* Onion skinning filtering flag. See `GreasePencilOnionSkinningFilter`.
|
||||
*/
|
||||
uint8_t filter;
|
||||
char _pad[2];
|
||||
/**
|
||||
* Number of ghost frames shown before.
|
||||
*/
|
||||
int16_t num_frames_before;
|
||||
/**
|
||||
* Number of ghost frames shown after.
|
||||
*/
|
||||
int16_t num_frames_after;
|
||||
/**
|
||||
* Color of the ghost frames before.
|
||||
*/
|
||||
float color_before[3];
|
||||
/**
|
||||
* Color of the ghost frames after.
|
||||
*/
|
||||
float color_after[3];
|
||||
char _pad2[4];
|
||||
} GreasePencilOnionSkinningSettings;
|
||||
|
||||
/**
|
||||
* The grease pencil data-block.
|
||||
*/
|
||||
typedef struct GreasePencil {
|
||||
ID id;
|
||||
/** Animation data. */
|
||||
struct AnimData *adt;
|
||||
|
||||
/**
|
||||
* An array of pointers to drawings. The drawing can own its data or reference it from another
|
||||
* data-block. Note that the order of this array is arbitrary. The mapping of drawings to frames
|
||||
* is done by the layers. See the `Layer` class in `BKE_grease_pencil.hh`.
|
||||
*/
|
||||
GreasePencilDrawingBase **drawing_array;
|
||||
int drawing_array_size;
|
||||
char _pad[4];
|
||||
|
||||
/* Root group of the layer tree. */
|
||||
GreasePencilLayerTreeGroup root_group;
|
||||
|
||||
/**
|
||||
* Pointer to the active layer. Can be NULL.
|
||||
* This pointer does not own the data.
|
||||
*/
|
||||
GreasePencilLayer *active_layer;
|
||||
|
||||
/**
|
||||
* An array of materials.
|
||||
*/
|
||||
struct Material **material_array;
|
||||
short material_array_size;
|
||||
char _pad2[2];
|
||||
/**
|
||||
* Global flag on the data-block.
|
||||
*/
|
||||
uint32_t flag;
|
||||
/**
|
||||
* Onion skinning settings.
|
||||
*/
|
||||
GreasePencilOnionSkinningSettings onion_skinning_settings;
|
||||
/**
|
||||
* Runtime struct pointer.
|
||||
*/
|
||||
GreasePencilRuntimeHandle *runtime;
|
||||
#ifdef __cplusplus
|
||||
/* GreasePencilDrawingBase array functions. */
|
||||
void read_drawing_array(BlendDataReader *reader);
|
||||
void write_drawing_array(BlendWriter *writer);
|
||||
void free_drawing_array();
|
||||
|
||||
/* Layer tree read/write functions. */
|
||||
void read_layer_tree(BlendDataReader *reader);
|
||||
void write_layer_tree(BlendWriter *writer);
|
||||
|
||||
/* Drawings read/write access. */
|
||||
blender::Span<GreasePencilDrawingBase *> drawings() const;
|
||||
blender::MutableSpan<GreasePencilDrawingBase *> drawings_for_write();
|
||||
|
||||
/* Layers read/write access. */
|
||||
blender::Span<const blender::bke::greasepencil::Layer *> layers() const;
|
||||
blender::Span<blender::bke::greasepencil::Layer *> layers_for_write();
|
||||
|
||||
bool has_active_layer() const;
|
||||
blender::bke::greasepencil::Layer &add_layer(blender::bke::greasepencil::LayerGroup &group,
|
||||
blender::StringRefNull name);
|
||||
|
||||
const blender::bke::greasepencil::Layer *find_layer_by_name(blender::StringRefNull name) const;
|
||||
blender::bke::greasepencil::Layer *find_layer_by_name(blender::StringRefNull name);
|
||||
|
||||
void add_empty_drawings(int add_size);
|
||||
void remove_drawing(int index);
|
||||
|
||||
void foreach_visible_drawing(int frame,
|
||||
blender::FunctionRef<void(GreasePencilDrawing &)> function);
|
||||
|
||||
bool bounds_min_max(blender::float3 &min, blender::float3 &max) const;
|
||||
|
||||
/* For debugging purposes. */
|
||||
void print_layer_tree();
|
||||
#endif
|
||||
} GreasePencil;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -572,7 +572,6 @@ typedef enum ObjectType {
|
||||
|
||||
OB_ARMATURE = 25,
|
||||
|
||||
/** Grease Pencil object used in 3D view but not used for annotation in 2D. */
|
||||
OB_GPENCIL_LEGACY = 26,
|
||||
|
||||
OB_CURVES = 27,
|
||||
@@ -581,6 +580,8 @@ typedef enum ObjectType {
|
||||
|
||||
OB_VOLUME = 29,
|
||||
|
||||
OB_GREASE_PENCIL = 30,
|
||||
|
||||
/* Keep last. */
|
||||
OB_TYPE_MAX,
|
||||
} ObjectType;
|
||||
@@ -588,7 +589,7 @@ typedef enum ObjectType {
|
||||
/* check if the object type supports materials */
|
||||
#define OB_TYPE_SUPPORT_MATERIAL(_type) \
|
||||
(((_type) >= OB_MESH && (_type) <= OB_MBALL) || \
|
||||
((_type) >= OB_GPENCIL_LEGACY && (_type) <= OB_VOLUME))
|
||||
((_type) >= OB_GPENCIL_LEGACY && (_type) <= OB_GREASE_PENCIL))
|
||||
/** Does the object have some render-able geometry (unlike empties, cameras, etc.). True for
|
||||
* #OB_CURVES_LEGACY, since these often evaluate to objects with geometry. */
|
||||
#define OB_TYPE_IS_GEOMETRY(_type) \
|
||||
@@ -601,7 +602,8 @@ typedef enum ObjectType {
|
||||
OB_CURVES_LEGACY, \
|
||||
OB_CURVES, \
|
||||
OB_POINTCLOUD, \
|
||||
OB_VOLUME))
|
||||
OB_VOLUME, \
|
||||
OB_GREASE_PENCIL))
|
||||
#define OB_TYPE_SUPPORT_VGROUP(_type) (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL_LEGACY))
|
||||
#define OB_TYPE_SUPPORT_EDITMODE(_type) \
|
||||
(ELEM(_type, \
|
||||
@@ -612,13 +614,14 @@ typedef enum ObjectType {
|
||||
OB_MBALL, \
|
||||
OB_LATTICE, \
|
||||
OB_ARMATURE, \
|
||||
OB_CURVES))
|
||||
OB_CURVES, \
|
||||
OB_GREASE_PENCIL))
|
||||
#define OB_TYPE_SUPPORT_PARVERT(_type) \
|
||||
(ELEM(_type, OB_MESH, OB_SURF, OB_CURVES_LEGACY, OB_LATTICE))
|
||||
|
||||
/** Matches #OB_TYPE_SUPPORT_EDITMODE. */
|
||||
#define OB_DATA_SUPPORT_EDITMODE(_type) \
|
||||
(ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR, ID_CV))
|
||||
(ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR, ID_CV, ID_GP))
|
||||
|
||||
/* is this ID type used as object data */
|
||||
#define OB_DATA_SUPPORT_ID(_id_type) \
|
||||
@@ -635,7 +638,8 @@ typedef enum ObjectType {
|
||||
ID_AR, \
|
||||
ID_CV, \
|
||||
ID_PT, \
|
||||
ID_VO))
|
||||
ID_VO, \
|
||||
ID_GP))
|
||||
|
||||
#define OB_DATA_SUPPORT_ID_CASE \
|
||||
ID_ME: \
|
||||
@@ -650,7 +654,8 @@ typedef enum ObjectType {
|
||||
case ID_AR: \
|
||||
case ID_CV: \
|
||||
case ID_PT: \
|
||||
case ID_VO
|
||||
case ID_VO: \
|
||||
case ID_GP
|
||||
|
||||
/** #Object.partype: first 4 bits: type. */
|
||||
enum {
|
||||
|
||||
@@ -675,10 +675,11 @@ typedef struct UserDef_Experimental {
|
||||
char use_override_templates;
|
||||
char enable_eevee_next;
|
||||
char use_sculpt_texture_paint;
|
||||
char use_grease_pencil_version3;
|
||||
char enable_overlay_next;
|
||||
char enable_workbench_next;
|
||||
char use_new_volume_nodes;
|
||||
char _pad[5];
|
||||
char _pad[4];
|
||||
/** `makesdna` does not allow empty structs. */
|
||||
} UserDef_Experimental;
|
||||
|
||||
|
||||
@@ -90,8 +90,10 @@ set(DEFSRC
|
||||
|
||||
if(WITH_EXPERIMENTAL_FEATURES)
|
||||
add_definitions(-DWITH_SIMULATION_DATABLOCK)
|
||||
add_definitions(-DWITH_GREASE_PENCIL_V3)
|
||||
list(APPEND DEFSRC
|
||||
rna_simulation.c
|
||||
rna_grease_pencil.c
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -4523,6 +4523,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
|
||||
{"rna_dynamicpaint.c", NULL, RNA_def_dynamic_paint},
|
||||
{"rna_fcurve.c", "rna_fcurve_api.c", RNA_def_fcurve},
|
||||
{"rna_gpencil_legacy.c", NULL, RNA_def_gpencil},
|
||||
{"rna_grease_pencil.c", NULL, RNA_def_grease_pencil},
|
||||
{"rna_curves.c", NULL, RNA_def_curves},
|
||||
{"rna_image.c", "rna_image_api.c", RNA_def_image},
|
||||
{"rna_key.c", NULL, RNA_def_key},
|
||||
|
||||
@@ -40,7 +40,8 @@ const EnumPropertyItem rna_enum_id_type_items[] = {
|
||||
{ID_CU_LEGACY, "CURVE", ICON_CURVE_DATA, "Curve", ""},
|
||||
{ID_CV, "CURVES", ICON_CURVES_DATA, "Curves", ""},
|
||||
{ID_VF, "FONT", ICON_FONT_DATA, "Font", ""},
|
||||
{ID_GD_LEGACY, "GREASEPENCIL", ICON_GREASEPENCIL, "Grease Pencil", ""},
|
||||
{ID_GD_LEGACY, "GREASEPENCIL", ICON_GREASEPENCIL, "Grease Pencil (legacy)", ""},
|
||||
{ID_GP, "GREASEPENCIL_V3", ICON_GREASEPENCIL, "Grease Pencil", ""},
|
||||
{ID_IM, "IMAGE", ICON_IMAGE_DATA, "Image", ""},
|
||||
{ID_KE, "KEY", ICON_SHAPEKEY_DATA, "Key", ""},
|
||||
{ID_LT, "LATTICE", ICON_LATTICE_DATA, "Lattice", ""},
|
||||
@@ -484,6 +485,9 @@ StructRNA *ID_code_to_RNA_type(short idcode)
|
||||
return &RNA_Curve;
|
||||
case ID_GD_LEGACY:
|
||||
return &RNA_GreasePencil;
|
||||
case ID_GP:
|
||||
return &RNA_GreasePencilv3;
|
||||
break;
|
||||
case ID_GR:
|
||||
return &RNA_Collection;
|
||||
case ID_CV:
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup RNA
|
||||
*/
|
||||
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "rna_internal.h"
|
||||
|
||||
#ifdef RNA_RUNTIME
|
||||
|
||||
#else
|
||||
|
||||
static void rna_def_grease_pencil_data(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
||||
srna = RNA_def_struct(brna, "GreasePencilv3", "ID");
|
||||
RNA_def_struct_sdna(srna, "GreasePencil");
|
||||
RNA_def_struct_ui_text(srna, "Grease Pencil", "Grease Pencil data-block");
|
||||
RNA_def_struct_ui_icon(srna, ICON_OUTLINER_DATA_GREASEPENCIL);
|
||||
|
||||
/* Animation Data */
|
||||
rna_def_animdata_common(srna);
|
||||
}
|
||||
|
||||
void RNA_def_grease_pencil(BlenderRNA *brna)
|
||||
{
|
||||
rna_def_grease_pencil_data(brna);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -156,6 +156,7 @@ void RNA_def_depsgraph(struct BlenderRNA *brna);
|
||||
void RNA_def_dynamic_paint(struct BlenderRNA *brna);
|
||||
void RNA_def_fcurve(struct BlenderRNA *brna);
|
||||
void RNA_def_gpencil(struct BlenderRNA *brna);
|
||||
void RNA_def_grease_pencil(struct BlenderRNA *brna);
|
||||
void RNA_def_greasepencil_modifier(struct BlenderRNA *brna);
|
||||
void RNA_def_shader_fx(struct BlenderRNA *brna);
|
||||
void RNA_def_curves(struct BlenderRNA *brna);
|
||||
@@ -502,6 +503,7 @@ void RNA_def_main_actions(BlenderRNA *brna, PropertyRNA *cprop);
|
||||
void RNA_def_main_particles(BlenderRNA *brna, PropertyRNA *cprop);
|
||||
void RNA_def_main_palettes(BlenderRNA *brna, PropertyRNA *cprop);
|
||||
void RNA_def_main_gpencil_legacy(BlenderRNA *brna, PropertyRNA *cprop);
|
||||
void RNA_def_main_grease_pencil(BlenderRNA *brna, PropertyRNA *cprop);
|
||||
void RNA_def_main_movieclips(BlenderRNA *brna, PropertyRNA *cprop);
|
||||
void RNA_def_main_masks(BlenderRNA *brna, PropertyRNA *cprop);
|
||||
void RNA_def_main_linestyles(BlenderRNA *brna, PropertyRNA *cprop);
|
||||
|
||||
@@ -96,6 +96,7 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(collections)
|
||||
RNA_MAIN_LISTBASE_FUNCS_DEF(curves)
|
||||
RNA_MAIN_LISTBASE_FUNCS_DEF(fonts)
|
||||
RNA_MAIN_LISTBASE_FUNCS_DEF(gpencils)
|
||||
RNA_MAIN_LISTBASE_FUNCS_DEF(grease_pencils)
|
||||
RNA_MAIN_LISTBASE_FUNCS_DEF(hair_curves)
|
||||
RNA_MAIN_LISTBASE_FUNCS_DEF(images)
|
||||
RNA_MAIN_LISTBASE_FUNCS_DEF(lattices)
|
||||
@@ -332,9 +333,20 @@ void RNA_def_main(BlenderRNA *brna)
|
||||
{"grease_pencils",
|
||||
"GreasePencil",
|
||||
"rna_Main_gpencils_begin",
|
||||
# ifdef WITH_GREASE_PENCIL_V3
|
||||
"Grease Pencil (legacy)",
|
||||
"Grease Pencil (legacy) data-blocks",
|
||||
# else
|
||||
"Grease Pencil",
|
||||
"Grease Pencil data-blocks",
|
||||
# endif
|
||||
RNA_def_main_gpencil_legacy},
|
||||
{"grease_pencils_v3",
|
||||
"GreasePencilv3",
|
||||
"rna_Main_grease_pencils_begin",
|
||||
"Grease Pencil",
|
||||
"Grease Pencil data-blocks",
|
||||
RNA_def_main_grease_pencil},
|
||||
{"movieclips",
|
||||
"MovieClip",
|
||||
"rna_Main_movieclips_begin",
|
||||
|
||||
@@ -2058,6 +2058,16 @@ void RNA_def_main_gpencil_legacy(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
func, "do_ui_user", true, "", "Make sure interface does not reference this grease pencil");
|
||||
}
|
||||
|
||||
void RNA_def_main_grease_pencil(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
||||
RNA_def_property_srna(cprop, "BlendDataGreasePencilsV3");
|
||||
srna = RNA_def_struct(brna, "BlendDataGreasePencilsV3", NULL);
|
||||
RNA_def_struct_sdna(srna, "Main");
|
||||
RNA_def_struct_ui_text(srna, "Main Grease Pencils", "Collection of grease pencils");
|
||||
}
|
||||
|
||||
void RNA_def_main_movieclips(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
||||
@@ -254,7 +254,8 @@ const EnumPropertyItem rna_enum_object_type_items[] = {
|
||||
{OB_CURVES, "CURVES", ICON_OUTLINER_OB_CURVES, "Hair Curves", ""},
|
||||
{OB_POINTCLOUD, "POINTCLOUD", ICON_OUTLINER_OB_POINTCLOUD, "Point Cloud", ""},
|
||||
{OB_VOLUME, "VOLUME", ICON_OUTLINER_OB_VOLUME, "Volume", ""},
|
||||
{OB_GPENCIL_LEGACY, "GPENCIL", ICON_OUTLINER_OB_GREASEPENCIL, "Grease Pencil", ""},
|
||||
{OB_GPENCIL_LEGACY, "GPENCIL", ICON_OUTLINER_OB_GREASEPENCIL, "Grease Pencil (legacy)", ""},
|
||||
{OB_GREASE_PENCIL, "GREASEPENCIL", ICON_OUTLINER_OB_GREASEPENCIL, "Grease Pencil", ""},
|
||||
RNA_ENUM_ITEM_SEPR,
|
||||
{OB_ARMATURE, "ARMATURE", ICON_OUTLINER_OB_ARMATURE, "Armature", ""},
|
||||
{OB_LATTICE, "LATTICE", ICON_OUTLINER_OB_LATTICE, "Lattice", ""},
|
||||
@@ -602,6 +603,8 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr)
|
||||
return &RNA_LightProbe;
|
||||
case OB_GPENCIL_LEGACY:
|
||||
return &RNA_GreasePencil;
|
||||
case OB_GREASE_PENCIL:
|
||||
return &RNA_GreasePencilv3;
|
||||
case OB_CURVES:
|
||||
return &RNA_Curves;
|
||||
case OB_POINTCLOUD:
|
||||
|
||||
@@ -6681,6 +6681,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "enable_eevee_next", 1);
|
||||
RNA_def_property_ui_text(prop, "EEVEE Next", "Enable the new EEVEE codebase, requires restart");
|
||||
|
||||
prop = RNA_def_property(srna, "use_grease_pencil_version3", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "use_grease_pencil_version3", 1);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil 3.0", "Enable the new grease pencil 3.0 codebase");
|
||||
|
||||
prop = RNA_def_property(srna, "enable_workbench_next", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "enable_workbench_next", 1);
|
||||
RNA_def_property_ui_text(prop,
|
||||
|
||||
@@ -513,6 +513,7 @@ typedef struct wmNotifier {
|
||||
#define NS_MODE_POSE (9 << 8)
|
||||
#define NS_MODE_PARTICLE (10 << 8)
|
||||
#define NS_EDITMODE_CURVES (11 << 8)
|
||||
#define NS_EDITMODE_GREASE_PENCIL (12 << 8)
|
||||
|
||||
/* subtype 3d view editing */
|
||||
#define NS_VIEW3D_GPU (16 << 8)
|
||||
|
||||
Reference in New Issue
Block a user