Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -0,0 +1,969 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup editors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "ED_grease_pencil.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Depsgraph;
|
||||
struct LineartBoundingArea;
|
||||
struct LineartEdge;
|
||||
struct LineartEdgeChain;
|
||||
struct LineartEdgeChainItem;
|
||||
struct LineartEdgeSegment;
|
||||
struct LineartElementLinkNode;
|
||||
struct LineartShadowSegment;
|
||||
struct LineartStaticMemPoolNode;
|
||||
struct LineartVert;
|
||||
struct LinkData;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
|
||||
struct LineartModifierRuntime {
|
||||
/* This list is constructed during `update_depsgraph()` call, and stays valid until the next
|
||||
* update. This way line art can load objects from this list instead of iterating over all
|
||||
* objects that may or may not have finished evaluating. */
|
||||
Set<const Object *> object_dependencies;
|
||||
};
|
||||
|
||||
struct LineartStaticMemPoolNode {
|
||||
Link item;
|
||||
size_t size;
|
||||
size_t used_byte;
|
||||
/* User memory starts here */
|
||||
};
|
||||
|
||||
struct LineartStaticMemPool {
|
||||
ListBaseT<LineartStaticMemPoolNode> pools;
|
||||
SpinLock lock_mem;
|
||||
};
|
||||
|
||||
struct LineartTriangleAdjacent {
|
||||
LineartEdge *e[3];
|
||||
};
|
||||
|
||||
struct LineartTriangle {
|
||||
LineartVert *v[3];
|
||||
|
||||
/* first culled in line list to use adjacent triangle info, then go through triangle list. */
|
||||
double gn[3];
|
||||
|
||||
uint8_t material_mask_bits;
|
||||
uint8_t intersection_mask;
|
||||
uint8_t mat_occlusion;
|
||||
uint8_t flags; /* #eLineartTriangleFlags */
|
||||
|
||||
/* target_reference = (obi->obindex | triangle_index) */
|
||||
/* higher 12 bits-------^ ^-----index in object, lower 20 bits */
|
||||
uint32_t target_reference;
|
||||
|
||||
uint8_t intersection_priority;
|
||||
|
||||
/**
|
||||
* Only use single link list, because we don't need to go back in order.
|
||||
* This variable is also reused to store the pointer to adjacent lines of this triangle before
|
||||
* intersection stage.
|
||||
*/
|
||||
LinkNode *intersecting_verts;
|
||||
};
|
||||
|
||||
struct LineartTriangleThread {
|
||||
LineartTriangle base;
|
||||
/**
|
||||
* This variable is used to store per-thread triangle-line testing pair,
|
||||
* also re-used to store triangle-triangle pair for intersection testing stage.
|
||||
* Do not directly use #LineartTriangleThread.
|
||||
* The size of #LineartTriangle is dynamically allocated to contain set thread number of
|
||||
* "testing_e" field. Worker threads will test lines against the "base" triangle.
|
||||
* At least one thread is present, thus we always have at least `testing_e[0]`.
|
||||
*/
|
||||
LineartEdge *testing_e[1];
|
||||
};
|
||||
|
||||
enum eLineArtElementNodeFlag {
|
||||
LRT_ELEMENT_IS_ADDITIONAL = (1 << 0),
|
||||
LRT_ELEMENT_BORDER_ONLY = (1 << 1),
|
||||
LRT_ELEMENT_NO_INTERSECTION = (1 << 2),
|
||||
LRT_ELEMENT_INTERSECTION_DATA = (1 << 3),
|
||||
};
|
||||
ENUM_OPERATORS(eLineArtElementNodeFlag);
|
||||
|
||||
struct LineartElementLinkNode {
|
||||
LineartElementLinkNode *next, *prev;
|
||||
void *pointer;
|
||||
int element_count;
|
||||
void *object_ref;
|
||||
eLineArtElementNodeFlag flags;
|
||||
|
||||
/* For edge element link nodes, used for shadow edge matching. */
|
||||
int obindex;
|
||||
int global_index_offset;
|
||||
|
||||
/** Per object value, always set, if not enabled by #ObjectLineArt, then it's set to global. */
|
||||
float crease_threshold;
|
||||
};
|
||||
|
||||
struct LineartEdgeSegment {
|
||||
LineartEdgeSegment *next, *prev;
|
||||
/** The point after which a property of the segment is changed, e.g. occlusion/material mask etc.
|
||||
* ratio==0: v1 ratio==1: v2 (this is in 2D projected space), */
|
||||
double ratio;
|
||||
/** Occlusion level after "ratio" point */
|
||||
uint8_t occlusion;
|
||||
|
||||
/* Used to filter line art occlusion edges */
|
||||
uint8_t material_mask_bits;
|
||||
|
||||
/* Lit/shaded flag for shadow is stored here.
|
||||
* TODO(Yiming): Transfer material masks from shadow results
|
||||
* onto here so then we can even filter transparent shadows. */
|
||||
uint32_t shadow_mask_bits;
|
||||
};
|
||||
|
||||
struct LineartShadowEdge {
|
||||
LineartShadowEdge *next, *prev;
|
||||
/* Two end points in frame-buffer coordinates viewed from the light source. */
|
||||
double fbc1[4], fbc2[4];
|
||||
double g1[3], g2[3];
|
||||
bool orig1, orig2;
|
||||
LineartEdge *e_ref;
|
||||
LineartEdge *e_ref_light_contour;
|
||||
LineartEdgeSegment *es_ref; /* Only for 3rd stage casting. */
|
||||
ListBaseT<LineartShadowSegment> shadow_segments;
|
||||
};
|
||||
|
||||
enum eLineartShadowSegmentFlag {
|
||||
LRT_SHADOW_CASTED = 1,
|
||||
LRT_SHADOW_FACING_LIGHT = 2,
|
||||
};
|
||||
|
||||
/* Represents a cutting point on a #LineartShadowEdge */
|
||||
struct LineartShadowSegment {
|
||||
LineartShadowSegment *next, *prev;
|
||||
/* eLineartShadowSegmentFlag */
|
||||
int flag;
|
||||
/* The point after which a property of the segment is changed. e.g. shadow mask/target_ref etc.
|
||||
* Coordinates in NDC during shadow calculation but transformed to global linear before cutting
|
||||
* onto edges during the loading stage of the "actual" rendering. */
|
||||
double ratio;
|
||||
/* Left and right pos, because when casting shadows at some point there will be
|
||||
* non-continuous cuts, see #lineart_shadow_edge_cut for detailed explanation. */
|
||||
double fbc1[4], fbc2[4];
|
||||
/* Global position. */
|
||||
double g1[4], g2[4];
|
||||
uint32_t target_reference;
|
||||
uint32_t shadow_mask_bits;
|
||||
};
|
||||
|
||||
struct LineartVert {
|
||||
double gloc[3];
|
||||
double fbcoord[4];
|
||||
|
||||
/* Scene global index. */
|
||||
int index;
|
||||
};
|
||||
|
||||
struct LineartEdge {
|
||||
LineartVert *v1, *v2;
|
||||
|
||||
/** These two variables are also used to specify original edge and segment during 3rd stage
|
||||
* reprojection, So we can easily find out the line which results come from. */
|
||||
LineartTriangle *t1, *t2;
|
||||
|
||||
ListBaseT<LineartEdgeSegment> segments;
|
||||
int8_t min_occ;
|
||||
|
||||
/** Also for line type determination on chaining. */
|
||||
uint16_t flags;
|
||||
uint8_t intersection_mask;
|
||||
|
||||
/**
|
||||
* Matches the shadow result, used to determine whether a line is in the shadow or not.
|
||||
* #edge_identifier usages:
|
||||
* - Intersection lines:
|
||||
* ((e->t1->target_reference << 32) | e->t2->target_reference);
|
||||
* - Other lines: LRT_EDGE_IDENTIFIER(obi, e);
|
||||
* - After shadow calculation: (search the shadow result and set reference to that);
|
||||
*/
|
||||
uint64_t edge_identifier;
|
||||
|
||||
/**
|
||||
* - Light contour: original_e->t1->target_reference | original_e->t2->target_reference.
|
||||
* - Cast shadow: triangle_projected_onto->target_reference.
|
||||
*/
|
||||
uint64_t target_reference;
|
||||
|
||||
/**
|
||||
* Still need this entry because culled lines will not add to object
|
||||
* #LineartElementLinkNode node (known as `eln` internally).
|
||||
*
|
||||
* TODO: If really need more savings, we can allocate this in a "extended" way too, but we need
|
||||
* another bit in flags to be able to show the difference.
|
||||
*/
|
||||
Object *object_ref;
|
||||
};
|
||||
|
||||
struct LineartEdgeChain {
|
||||
LineartEdgeChain *next, *prev;
|
||||
ListBaseT<LineartEdgeChainItem> chain;
|
||||
|
||||
/** Calculated before draw command. */
|
||||
float length;
|
||||
|
||||
/** Used when re-connecting and grease-pencil stroke generation. */
|
||||
uint8_t picked;
|
||||
uint8_t level;
|
||||
|
||||
/** Chain now only contains one type of segments */
|
||||
int type;
|
||||
/** Will only connect chains that has the same loop id. */
|
||||
int loop_id;
|
||||
uint8_t material_mask_bits;
|
||||
uint8_t intersection_mask;
|
||||
uint32_t shadow_mask_bits;
|
||||
|
||||
/* We need local index for correct weight transfer, line art index is global, thus
|
||||
* local_index=lineart_index-index_offset. */
|
||||
uint32_t index_offset;
|
||||
|
||||
Object *object_ref;
|
||||
Object *silhouette_backdrop;
|
||||
};
|
||||
|
||||
struct LineartEdgeChainItem {
|
||||
LineartEdgeChainItem *next, *prev;
|
||||
/** Need z value for fading, w value for image frame clipping. */
|
||||
float pos[4];
|
||||
/** For restoring position to 3d space. */
|
||||
float gpos[3];
|
||||
float normal[3];
|
||||
uint16_t line_type;
|
||||
uint8_t occlusion;
|
||||
uint8_t material_mask_bits;
|
||||
uint8_t intersection_mask;
|
||||
uint32_t shadow_mask_bits;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
struct LineartChainRegisterEntry {
|
||||
LineartChainRegisterEntry *next, *prev;
|
||||
LineartEdgeChain *ec;
|
||||
LineartEdgeChainItem *eci;
|
||||
int8_t picked;
|
||||
|
||||
/* left/right mark.
|
||||
* Because we revert list in chaining so we need the flag. */
|
||||
int8_t is_left;
|
||||
};
|
||||
|
||||
struct LineartAdjacentEdge {
|
||||
uint32_t v1;
|
||||
uint32_t v2;
|
||||
uint32_t e;
|
||||
};
|
||||
|
||||
enum eLineArtTileRecursiveLimit {
|
||||
/* If tile gets this small, it's already much smaller than a pixel. No need to continue
|
||||
* splitting. */
|
||||
LRT_TILE_RECURSIVE_PERSPECTIVE = 16,
|
||||
/* This is a tried-and-true safe value for high poly models that also needed ortho rendering. */
|
||||
LRT_TILE_RECURSIVE_ORTHO = 10,
|
||||
};
|
||||
|
||||
#define LRT_TILE_SPLITTING_TRIANGLE_LIMIT 100
|
||||
#define LRT_TILE_EDGE_COUNT_INITIAL 32
|
||||
|
||||
enum eLineartShadowCameraType {
|
||||
LRT_SHADOW_CAMERA_DIRECTIONAL = 1,
|
||||
LRT_SHADOW_CAMERA_POINT = 2,
|
||||
};
|
||||
|
||||
struct LineartPendingEdges {
|
||||
LineartEdge **array;
|
||||
int max;
|
||||
int next;
|
||||
};
|
||||
|
||||
struct LineartData {
|
||||
int w, h;
|
||||
int thread_count;
|
||||
int sizeof_triangle;
|
||||
|
||||
LineartStaticMemPool render_data_pool;
|
||||
/* A pointer to LineartCache::chain_data_pool, which acts as a cache for edge chains. */
|
||||
LineartStaticMemPool *chain_data_pool;
|
||||
/* Reference to LineartCache::shadow_data_pool, stay available until the final round of line art
|
||||
* calculation is finished. */
|
||||
LineartStaticMemPool *shadow_data_pool;
|
||||
|
||||
/* Storing shadow edge eln, array, and cuts for shadow information, so it's available when line
|
||||
* art runs the second time for occlusion. Either a reference to LineartCache::shadow_data_pool
|
||||
* (shadow stage) or a reference to LineartData::render_data_pool (final stage). */
|
||||
LineartStaticMemPool *edge_data_pool;
|
||||
|
||||
struct _qtree {
|
||||
|
||||
int count_x, count_y;
|
||||
double tile_width, tile_height;
|
||||
|
||||
/* When splitting bounding areas, if there's an ortho camera placed at a straight angle, there
|
||||
* will be a lot of triangles aligned in line which can not be separated by continue
|
||||
* subdividing the tile. So we set a strict limit when using ortho camera. See
|
||||
* eLineArtTileRecursiveLimit. */
|
||||
int recursive_level;
|
||||
|
||||
LineartBoundingArea *initials;
|
||||
|
||||
uint32_t initial_tile_count;
|
||||
|
||||
} qtree;
|
||||
|
||||
struct _geom {
|
||||
|
||||
ListBaseT<LineartElementLinkNode> vertex_buffer_pointers;
|
||||
ListBaseT<LineartElementLinkNode> line_buffer_pointers;
|
||||
ListBaseT<LineartElementLinkNode> triangle_buffer_pointers;
|
||||
|
||||
/** This one's memory is not from main pool and is free()ed after culling stage. */
|
||||
ListBaseT<LineartElementLinkNode> triangle_adjacent_pointers;
|
||||
|
||||
ListBaseT<LineartElementLinkNode> intersecting_vertex_buffer;
|
||||
|
||||
} geom;
|
||||
|
||||
struct _conf {
|
||||
|
||||
double view_projection[4][4];
|
||||
double view[4][4];
|
||||
|
||||
float overscan;
|
||||
|
||||
int max_occlusion_level;
|
||||
double crease_angle;
|
||||
double crease_cos;
|
||||
|
||||
int draw_material_preview;
|
||||
double material_transparency;
|
||||
|
||||
bool use_contour;
|
||||
bool use_crease;
|
||||
bool use_material;
|
||||
bool use_edge_marks;
|
||||
bool use_intersections;
|
||||
bool use_loose;
|
||||
bool use_light_contour;
|
||||
bool use_shadow;
|
||||
bool use_contour_secondary; /* From viewing camera, during shadow calculation. */
|
||||
|
||||
int shadow_selection; /* Needs to be numeric because it's not just on/off. */
|
||||
bool shadow_enclose_shapes;
|
||||
bool shadow_use_silhouette;
|
||||
|
||||
bool fuzzy_intersections;
|
||||
bool fuzzy_everything;
|
||||
bool allow_boundaries;
|
||||
bool allow_overlapping_edges;
|
||||
bool allow_duplicated_types;
|
||||
bool remove_doubles;
|
||||
bool use_loose_as_contour;
|
||||
bool use_loose_edge_chain;
|
||||
bool use_geometry_space_chain;
|
||||
bool use_image_boundary_trimming;
|
||||
bool use_back_face_culling;
|
||||
|
||||
bool filter_face_mark;
|
||||
bool filter_face_mark_invert;
|
||||
bool filter_face_mark_boundaries;
|
||||
bool filter_face_mark_keep_contour;
|
||||
|
||||
bool force_crease;
|
||||
bool sharp_as_crease;
|
||||
|
||||
bool chain_preserve_details;
|
||||
|
||||
bool do_shadow_cast;
|
||||
bool light_reference_available;
|
||||
|
||||
/* Keep an copy of these data so when line art is running itself contained. */
|
||||
bool cam_is_persp;
|
||||
/* "Secondary" ones are from viewing camera
|
||||
* (as opposed to shadow camera), during shadow calculation. */
|
||||
bool cam_is_persp_secondary;
|
||||
float cam_obmat[4][4];
|
||||
float cam_obmat_secondary[4][4];
|
||||
double camera_pos[3];
|
||||
double camera_pos_secondary[3];
|
||||
double active_camera_pos[3]; /* Stroke offset calculation may use active or selected camera. */
|
||||
double near_clip, far_clip;
|
||||
float shift_x, shift_y;
|
||||
|
||||
float crease_threshold;
|
||||
float chaining_image_threshold;
|
||||
float angle_splitting_threshold;
|
||||
|
||||
float chain_smooth_tolerance;
|
||||
|
||||
double view_vector[3];
|
||||
double view_vector_secondary[3]; /* For shadow. */
|
||||
} conf;
|
||||
|
||||
LineartElementLinkNode *isect_scheduled_up_to;
|
||||
int isect_scheduled_up_to_index;
|
||||
|
||||
/* NOTE: Data inside #pending_edges are allocated with MEM_xxx call instead of in pool. */
|
||||
struct LineartPendingEdges pending_edges;
|
||||
int scheduled_count;
|
||||
|
||||
/* Intermediate shadow results, list of LineartShadowEdge */
|
||||
LineartShadowEdge *shadow_edges;
|
||||
int shadow_edges_count;
|
||||
|
||||
ListBaseT<LineartEdgeChain> chains;
|
||||
|
||||
ListBaseT<LineartEdgeSegment> wasted_cuts;
|
||||
ListBaseT<LineartShadowSegment> wasted_shadow_cuts;
|
||||
SpinLock lock_cuts;
|
||||
SpinLock lock_task;
|
||||
};
|
||||
|
||||
struct LineartCache {
|
||||
ed::greasepencil::LineartLimitInfo LimitInfo;
|
||||
/** Separate memory pool for chain data and shadow, this goes to the cache, so when we free the
|
||||
* main pool, chains and shadows will still be available. */
|
||||
LineartStaticMemPool chain_data_pool;
|
||||
LineartStaticMemPool shadow_data_pool;
|
||||
|
||||
/** A copy of ld->chains so we have that data available after ld has been destroyed. */
|
||||
ListBaseT<LineartEdgeChain> chains;
|
||||
|
||||
/** Shadow-computed feature lines from original meshes to be matched with the second load of
|
||||
* meshes thus providing lit/shade info in the second run of line art. */
|
||||
ListBaseT<LineartElementLinkNode> shadow_elns;
|
||||
|
||||
/** Cache only contains edge types specified in this variable. */
|
||||
uint16_t all_enabled_edge_types;
|
||||
};
|
||||
|
||||
#define DBL_TRIANGLE_LIM 1e-8
|
||||
#define DBL_EDGE_LIM 1e-9
|
||||
|
||||
#define LRT_MEMORY_POOL_1MB (1 << 20)
|
||||
|
||||
enum eLineartTriangleFlags {
|
||||
LRT_CULL_DONT_CARE = 0,
|
||||
LRT_CULL_USED = (1 << 0),
|
||||
LRT_CULL_DISCARD = (1 << 1),
|
||||
LRT_CULL_GENERATED = (1 << 2),
|
||||
LRT_TRIANGLE_INTERSECTION_ONLY = (1 << 3),
|
||||
LRT_TRIANGLE_NO_INTERSECTION = (1 << 4),
|
||||
LRT_TRIANGLE_MAT_BACK_FACE_CULLING = (1 << 5),
|
||||
LRT_TRIANGLE_FORCE_INTERSECTION = (1 << 6),
|
||||
};
|
||||
|
||||
#define LRT_SHADOW_MASK_UNDEFINED 0
|
||||
#define LRT_SHADOW_MASK_ILLUMINATED (1 << 0)
|
||||
#define LRT_SHADOW_MASK_SHADED (1 << 1)
|
||||
#define LRT_SHADOW_MASK_ENCLOSED_SHAPE (1 << 2)
|
||||
#define LRT_SHADOW_MASK_INHIBITED (1 << 3)
|
||||
#define LRT_SHADOW_SILHOUETTE_ERASED_GROUP (1 << 4)
|
||||
#define LRT_SHADOW_SILHOUETTE_ERASED_OBJECT (1 << 5)
|
||||
#define LRT_SHADOW_MASK_ILLUMINATED_SHAPE (1 << 6)
|
||||
|
||||
#define LRT_SHADOW_TEST_SHAPE_BITS \
|
||||
(LRT_SHADOW_MASK_ILLUMINATED | LRT_SHADOW_MASK_SHADED | LRT_SHADOW_MASK_INHIBITED | \
|
||||
LRT_SHADOW_MASK_ILLUMINATED_SHAPE)
|
||||
|
||||
/**
|
||||
* Controls how many edges a worker thread is processing at one request.
|
||||
* There's no significant performance impact on choosing different values.
|
||||
* Don't make it too small so that the worker thread won't request too many times.
|
||||
*/
|
||||
#define LRT_THREAD_EDGE_COUNT 1000
|
||||
|
||||
struct LineartRenderTaskInfo {
|
||||
struct LineartData *ld;
|
||||
|
||||
int thread_id;
|
||||
|
||||
/**
|
||||
* #pending_edges here only stores a reference to a portion in
|
||||
* LineartData::pending_edges, assigned by the occlusion scheduler.
|
||||
*/
|
||||
struct LineartPendingEdges pending_edges;
|
||||
};
|
||||
|
||||
#define LRT_OBINDEX_SHIFT 20
|
||||
#define LRT_OBINDEX_LOWER 0x0FFFFF /* Lower 20 bits. */
|
||||
#define LRT_OBINDEX_HIGHER 0xFFF00000 /* Higher 12 bits. */
|
||||
#define LRT_EDGE_IDENTIFIER(obi, e) \
|
||||
(((uint64_t)(obi->obindex | (e->v1->index & LRT_OBINDEX_LOWER)) << 32) | \
|
||||
(obi->obindex | (e->v2->index & LRT_OBINDEX_LOWER)))
|
||||
#define LRT_LIGHT_CONTOUR_TARGET 0xFFFFFFFF
|
||||
|
||||
struct LineartObjectInfo {
|
||||
LineartObjectInfo *next;
|
||||
Object *original_ob;
|
||||
Object *original_ob_eval; /* For evaluated materials */
|
||||
Mesh *original_me;
|
||||
double model_view_proj[4][4];
|
||||
double model_view[4][4];
|
||||
double normal[4][4];
|
||||
LineartElementLinkNode *v_eln;
|
||||
int usage;
|
||||
uint8_t override_intersection_mask;
|
||||
uint8_t intersection_priority;
|
||||
int global_i_offset;
|
||||
|
||||
/* Shifted LRT_OBINDEX_SHIFT bits to be combined with object triangle index. */
|
||||
int obindex;
|
||||
|
||||
bool free_use_mesh;
|
||||
|
||||
/** NOTE: Data inside #pending_edges are allocated with MEM_xxx call instead of in pool. */
|
||||
LineartPendingEdges pending_edges;
|
||||
};
|
||||
|
||||
struct LineartObjectLoadTaskInfo {
|
||||
LineartData *ld;
|
||||
int thread_id;
|
||||
/* LinkNode styled list */
|
||||
LineartObjectInfo *pending;
|
||||
/* Used to spread the load across several threads. This can not overflow. */
|
||||
uint64_t total_faces;
|
||||
ListBaseT<LineartElementLinkNode> *shadow_elns;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bounding area diagram:
|
||||
* \code{.txt}
|
||||
* +----+ <----U (Upper edge Y value)
|
||||
* | |
|
||||
* +----+ <----B (Bottom edge Y value)
|
||||
* ^ ^
|
||||
* L R (Left/Right edge X value)
|
||||
* \endcode
|
||||
*
|
||||
* Example structure when subdividing 1 bounding areas:
|
||||
* 1 area can be divided into 4 smaller children to
|
||||
* accommodate image areas with denser triangle distribution.
|
||||
* \code{.txt}
|
||||
* +--+--+-----+
|
||||
* +--+--+ |
|
||||
* +--+--+-----+
|
||||
* | | |
|
||||
* +-----+-----+
|
||||
* \endcode
|
||||
*
|
||||
* lp/rp/up/bp is the list for
|
||||
* storing pointers to adjacent bounding areas.
|
||||
*/
|
||||
struct LineartBoundingArea {
|
||||
double l, r, u, b;
|
||||
double cx, cy;
|
||||
|
||||
/** 1,2,3,4 quadrant */
|
||||
LineartBoundingArea *child;
|
||||
|
||||
SpinLock lock;
|
||||
|
||||
ListBaseT<LinkData> lp;
|
||||
ListBaseT<LinkData> rp;
|
||||
ListBaseT<LinkData> up;
|
||||
ListBaseT<LinkData> bp;
|
||||
|
||||
uint32_t triangle_count;
|
||||
uint32_t max_triangle_count;
|
||||
uint32_t line_count;
|
||||
uint32_t max_line_count;
|
||||
uint32_t insider_triangle_count;
|
||||
|
||||
/* Use array for speeding up multiple accesses. */
|
||||
LineartTriangle **linked_triangles;
|
||||
LineartEdge **linked_lines;
|
||||
|
||||
/** Reserved for image space reduction && multi-thread chaining. */
|
||||
ListBaseT<LineartChainRegisterEntry> linked_chains;
|
||||
};
|
||||
|
||||
#define LRT_TILE(tile, r, c, CCount) tile[r * CCount + c]
|
||||
|
||||
#define LRT_CLAMP(a, Min, Max) a = a < Min ? Min : (a > Max ? Max : a)
|
||||
|
||||
#define LRT_MAX3_INDEX(a, b, c) (a > b ? (a > c ? 0 : (b > c ? 1 : 2)) : (b > c ? 1 : 2))
|
||||
|
||||
#define LRT_MIN3_INDEX(a, b, c) (a < b ? (a < c ? 0 : (b < c ? 1 : 2)) : (b < c ? 1 : 2))
|
||||
|
||||
#define LRT_MAX3_INDEX_ABC(x, y, z) (x > y ? (x > z ? a : (y > z ? b : c)) : (y > z ? b : c))
|
||||
|
||||
#define LRT_MIN3_INDEX_ABC(x, y, z) (x < y ? (x < z ? a : (y < z ? b : c)) : (y < z ? b : c))
|
||||
|
||||
#define DBL_LOOSER 1e-5
|
||||
#define LRT_DOUBLE_CLOSE_LOOSER(a, b) (((a) + DBL_LOOSER) >= (b) && ((a) - DBL_LOOSER) <= (b))
|
||||
#define LRT_DOUBLE_CLOSE_ENOUGH(a, b) (((a) + DBL_EDGE_LIM) >= (b) && ((a) - DBL_EDGE_LIM) <= (b))
|
||||
#define LRT_DOUBLE_CLOSE_ENOUGH_TRI(a, b) \
|
||||
(((a) + DBL_TRIANGLE_LIM) >= (b) && ((a) - DBL_TRIANGLE_LIM) <= (b))
|
||||
|
||||
#define LRT_CLOSE_LOOSER_v3(a, b) \
|
||||
(LRT_DOUBLE_CLOSE_LOOSER(a[0], b[0]) && LRT_DOUBLE_CLOSE_LOOSER(a[1], b[1]) && \
|
||||
LRT_DOUBLE_CLOSE_LOOSER(a[2], b[2]))
|
||||
|
||||
/* Notes on this function:
|
||||
*
|
||||
* r_ratio: The ratio on segment a1-a2. When r_ratio is very close to zero or one, it
|
||||
* fixes the value to zero or one, this makes it easier to identify "on the tip" situations.
|
||||
*
|
||||
* r_aligned: True when 1) a and b is exactly on the same straight line and 2) a and b share a
|
||||
* common end-point.
|
||||
*
|
||||
* IMPORTANT: if r_aligned is true, r_ratio will be either 0 or 1 depending on which point from
|
||||
* segment a is shared with segment b. If it's a1 then r_ratio is 0, else then r_ratio is 1. This
|
||||
* extra information is needed for line art occlusion stage to work correctly in such cases.
|
||||
*/
|
||||
BLI_INLINE int lineart_intersect_seg_seg(const double a1[2],
|
||||
const double a2[2],
|
||||
const double b1[2],
|
||||
const double b2[2],
|
||||
double *r_ratio,
|
||||
bool *r_aligned)
|
||||
{
|
||||
/* Legacy intersection math aligns better with occlusion function quirks. */
|
||||
// #define USE_VECTOR_LINE_INTERSECTION
|
||||
#ifdef USE_VECTOR_LINE_INTERSECTION
|
||||
|
||||
/* from isect_line_line_v2_point() */
|
||||
|
||||
double s10[2], s32[2];
|
||||
double div;
|
||||
|
||||
sub_v2_v2v2_db(s10, a2, a1);
|
||||
sub_v2_v2v2_db(s32, b2, b1);
|
||||
|
||||
div = cross_v2v2_db(s10, s32);
|
||||
if (div != 0.0f) {
|
||||
const double u = cross_v2v2_db(a2, a1);
|
||||
const double v = cross_v2v2_db(b2, b1);
|
||||
|
||||
const double rx = ((s32[0] * u) - (s10[0] * v)) / div;
|
||||
const double ry = ((s32[1] * u) - (s10[1] * v)) / div;
|
||||
double rr;
|
||||
|
||||
if (fabs(a2[0] - a1[0]) > fabs(a2[1] - a1[1])) {
|
||||
*r_ratio = ratiod(a1[0], a2[0], rx);
|
||||
if (fabs(b2[0] - b1[0]) > fabs(b2[1] - b1[1])) {
|
||||
rr = ratiod(b1[0], b2[0], rx);
|
||||
}
|
||||
else {
|
||||
rr = ratiod(b1[1], b2[1], ry);
|
||||
}
|
||||
if ((*r_ratio) > 0 && (*r_ratio) < 1 && rr > 0 && rr < 1) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
*r_ratio = ratiod(a1[1], a2[1], ry);
|
||||
if (fabs(b2[0] - b1[0]) > fabs(b2[1] - b1[1])) {
|
||||
rr = ratiod(b1[0], b2[0], rx);
|
||||
}
|
||||
else {
|
||||
rr = ratiod(b1[1], b2[1], ry);
|
||||
}
|
||||
if ((*r_ratio) > 0 && (*r_ratio) < 1 && rr > 0 && rr < 1) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
#else
|
||||
double k1, k2;
|
||||
double x;
|
||||
double y;
|
||||
double ratio;
|
||||
double x_diff = (a2[0] - a1[0]);
|
||||
double x_diff2 = (b2[0] - b1[0]);
|
||||
|
||||
*r_aligned = false;
|
||||
|
||||
if (LRT_DOUBLE_CLOSE_ENOUGH(x_diff, 0)) {
|
||||
if (LRT_DOUBLE_CLOSE_ENOUGH(x_diff2, 0)) {
|
||||
/* This means two segments are both vertical. */
|
||||
if ((LRT_DOUBLE_CLOSE_ENOUGH(a2[0], b1[0]) && LRT_DOUBLE_CLOSE_ENOUGH(a2[1], b1[1])) ||
|
||||
(LRT_DOUBLE_CLOSE_ENOUGH(a2[0], b2[0]) && LRT_DOUBLE_CLOSE_ENOUGH(a2[1], b2[1])))
|
||||
{
|
||||
*r_aligned = true;
|
||||
*r_ratio = 1;
|
||||
}
|
||||
else if ((LRT_DOUBLE_CLOSE_ENOUGH(a1[0], b1[0]) && LRT_DOUBLE_CLOSE_ENOUGH(a1[1], b1[1])) ||
|
||||
(LRT_DOUBLE_CLOSE_ENOUGH(a1[0], b2[0]) && LRT_DOUBLE_CLOSE_ENOUGH(a1[1], b2[1])))
|
||||
{
|
||||
*r_aligned = true;
|
||||
*r_ratio = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
double r2 = ratiod(b1[0], b2[0], a1[0]);
|
||||
x = interpd(b2[0], b1[0], r2);
|
||||
y = interpd(b2[1], b1[1], r2);
|
||||
*r_ratio = ratio = ratiod(a1[1], a2[1], y);
|
||||
}
|
||||
else {
|
||||
if (LRT_DOUBLE_CLOSE_ENOUGH(x_diff2, 0)) {
|
||||
ratio = ratiod(a1[0], a2[0], b1[0]);
|
||||
x = interpd(a2[0], a1[0], ratio);
|
||||
*r_ratio = ratio;
|
||||
}
|
||||
else {
|
||||
double y_diff = a2[1] - a1[1], y_diff2 = b2[1] - b1[1];
|
||||
k1 = y_diff / x_diff;
|
||||
k2 = y_diff2 / x_diff2;
|
||||
|
||||
if (LRT_DOUBLE_CLOSE_ENOUGH_TRI(k2, k1)) {
|
||||
/* This means two segments are parallel. This also handles k==0 (both completely
|
||||
* horizontal) cases. */
|
||||
if ((LRT_DOUBLE_CLOSE_ENOUGH(a2[0], b1[0]) && LRT_DOUBLE_CLOSE_ENOUGH(a2[1], b1[1])) ||
|
||||
(LRT_DOUBLE_CLOSE_ENOUGH(a2[0], b2[0]) && LRT_DOUBLE_CLOSE_ENOUGH(a2[1], b2[1])))
|
||||
{
|
||||
*r_aligned = true;
|
||||
*r_ratio = 1;
|
||||
}
|
||||
else if ((LRT_DOUBLE_CLOSE_ENOUGH(a1[0], b1[0]) &&
|
||||
LRT_DOUBLE_CLOSE_ENOUGH(a1[1], b1[1])) ||
|
||||
(LRT_DOUBLE_CLOSE_ENOUGH(a1[0], b2[0]) && LRT_DOUBLE_CLOSE_ENOUGH(a1[1], b2[1])))
|
||||
{
|
||||
*r_aligned = true;
|
||||
*r_ratio = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
x = (a1[1] - b1[1] - k1 * a1[0] + k2 * b1[0]) / (k2 - k1);
|
||||
|
||||
ratio = (x - a1[0]) / x_diff;
|
||||
|
||||
*r_ratio = ratio;
|
||||
}
|
||||
}
|
||||
|
||||
if (LRT_DOUBLE_CLOSE_ENOUGH(b1[0], b2[0])) {
|
||||
y = interpd(a2[1], a1[1], ratio);
|
||||
if (y > std::max(b1[1], b2[1]) || y < std::min(b1[1], b2[1])) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (ratio <= 0 || ratio > 1 || (b1[0] > b2[0] && x > b1[0]) ||
|
||||
(b1[0] < b2[0] && x < b1[0]) || (b2[0] > b1[0] && x > b2[0]) ||
|
||||
(b2[0] < b1[0] && x < b2[0]))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LRT_DOUBLE_CLOSE_ENOUGH_TRI(*r_ratio, 1)) {
|
||||
*r_ratio = 1;
|
||||
}
|
||||
else if (LRT_DOUBLE_CLOSE_ENOUGH_TRI(*r_ratio, 0)) {
|
||||
*r_ratio = 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This is a special convenience function to lineart_intersect_seg_seg which will return true when
|
||||
* the intersection point falls in the range of a1-a2 but not necessarily in the range of b1-b2. */
|
||||
BLI_INLINE int lineart_line_isec_2d_ignore_line2pos(const double a1[2],
|
||||
const double a2[2],
|
||||
const double b1[2],
|
||||
const double b2[2],
|
||||
double *r_a_ratio)
|
||||
{
|
||||
/* The define here is used to check how vector or slope method handles boundary cases. The result
|
||||
* of `lim(div->0)` and `lim(k->0)` could both produce some unwanted flickers in line art, the
|
||||
* influence of which is still not fully understood, so keep the switch there for further
|
||||
* investigations. */
|
||||
#define USE_VECTOR_LINE_INTERSECTION_IGN
|
||||
#ifdef USE_VECTOR_LINE_INTERSECTION_IGN
|
||||
|
||||
/* from isect_line_line_v2_point() */
|
||||
|
||||
double s10[2], s32[2];
|
||||
double div;
|
||||
|
||||
sub_v2_v2v2_db(s10, a2, a1);
|
||||
sub_v2_v2v2_db(s32, b2, b1);
|
||||
|
||||
div = cross_v2v2_db(s10, s32);
|
||||
if (div != 0.0f) {
|
||||
const double u = cross_v2v2_db(a2, a1);
|
||||
const double v = cross_v2v2_db(b2, b1);
|
||||
|
||||
const double rx = ((s32[0] * u) - (s10[0] * v)) / div;
|
||||
const double ry = ((s32[1] * u) - (s10[1] * v)) / div;
|
||||
|
||||
if (fabs(a2[0] - a1[0]) > fabs(a2[1] - a1[1])) {
|
||||
*r_a_ratio = ratiod(a1[0], a2[0], rx);
|
||||
if ((*r_a_ratio) >= -DBL_EDGE_LIM && (*r_a_ratio) <= 1 + DBL_EDGE_LIM) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
*r_a_ratio = ratiod(a1[1], a2[1], ry);
|
||||
if ((*r_a_ratio) >= -DBL_EDGE_LIM && (*r_a_ratio) <= 1 + DBL_EDGE_LIM) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
#else
|
||||
double k1, k2;
|
||||
double x;
|
||||
double y;
|
||||
double ratio;
|
||||
double x_diff = (a2[0] - a1[0]);
|
||||
double x_diff2 = (b2[0] - b1[0]);
|
||||
|
||||
if (LRT_DOUBLE_CLOSE_ENOUGH(x_diff, 0)) {
|
||||
if (LRT_DOUBLE_CLOSE_ENOUGH(x_diff2, 0)) {
|
||||
*r_a_ratio = 0;
|
||||
return 0;
|
||||
}
|
||||
double r2 = ratiod(b1[0], b2[0], a1[0]);
|
||||
x = interpd(b2[0], b1[0], r2);
|
||||
y = interpd(b2[1], b1[1], r2);
|
||||
*r_a_ratio = ratio = ratiod(a1[1], a2[1], y);
|
||||
}
|
||||
else {
|
||||
if (LRT_DOUBLE_CLOSE_ENOUGH(x_diff2, 0)) {
|
||||
ratio = ratiod(a1[0], a2[0], b1[0]);
|
||||
x = interpd(a2[0], a1[0], ratio);
|
||||
*r_a_ratio = ratio;
|
||||
}
|
||||
else {
|
||||
k1 = (a2[1] - a1[1]) / x_diff;
|
||||
k2 = (b2[1] - b1[1]) / x_diff2;
|
||||
|
||||
if ((k1 == k2)) {
|
||||
return 0;
|
||||
}
|
||||
x = (a1[1] - b1[1] - k1 * a1[0] + k2 * b1[0]) / (k2 - k1);
|
||||
|
||||
ratio = (x - a1[0]) / x_diff;
|
||||
|
||||
*r_a_ratio = ratio;
|
||||
}
|
||||
}
|
||||
|
||||
if (ratio <= 0 || ratio >= 1) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct bGPDframe;
|
||||
struct bGPDlayer;
|
||||
struct LineartGpencilModifierData;
|
||||
struct GreasePencilLineartModifierData;
|
||||
struct LineartData;
|
||||
struct Scene;
|
||||
|
||||
void MOD_lineart_destroy_render_data_v3(GreasePencilLineartModifierData *lmd);
|
||||
|
||||
void MOD_lineart_chain_feature_lines(LineartData *ld);
|
||||
void MOD_lineart_chain_split_for_fixed_occlusion(LineartData *ld);
|
||||
/**
|
||||
* This function only connects two different chains. It will not do any clean up or smart chaining.
|
||||
* So no: removing overlapping chains, removal of short isolated segments, and no loop reduction is
|
||||
* implemented yet.
|
||||
*/
|
||||
void MOD_lineart_chain_connect(LineartData *ld);
|
||||
void MOD_lineart_chain_discard_unused(LineartData *ld, float threshold, uint8_t max_occlusion);
|
||||
void MOD_lineart_chain_clip_at_border(LineartData *ld);
|
||||
/**
|
||||
* This should always be the last stage!, see the end of
|
||||
* #MOD_lineart_chain_split_for_fixed_occlusion().
|
||||
*/
|
||||
void MOD_lineart_chain_split_angle(LineartData *ld, float angle_threshold_rad);
|
||||
void MOD_lineart_smooth_chains(LineartData *ld, float tolerance);
|
||||
void MOD_lineart_chain_offset_towards_camera(LineartData *ld, float dist, bool use_custom_camera);
|
||||
void MOD_lineart_chain_find_silhouette_backdrop_objects(LineartData *ld);
|
||||
|
||||
int MOD_lineart_chain_count(const LineartEdgeChain *ec);
|
||||
void MOD_lineart_chain_clear_picked_flag(LineartCache *lc);
|
||||
void MOD_lineart_finalize_chains(LineartData *ld);
|
||||
|
||||
/**
|
||||
* This is the entry point of all line art calculations.
|
||||
*
|
||||
* \return True when a change is made.
|
||||
*/
|
||||
bool MOD_lineart_compute_feature_lines_v3(Depsgraph *depsgraph,
|
||||
GreasePencilLineartModifierData &lmd,
|
||||
LineartCache **cached_result,
|
||||
bool enable_stroke_depth_offset);
|
||||
|
||||
/**
|
||||
* This only gets initial "biggest" tile.
|
||||
*/
|
||||
LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartData *ld, double x, double y);
|
||||
|
||||
/**
|
||||
* Wrapper for more convenience.
|
||||
*/
|
||||
LineartBoundingArea *MOD_lineart_get_bounding_area(LineartData *ld, double x, double y);
|
||||
|
||||
namespace bke::greasepencil {
|
||||
class Drawing;
|
||||
}
|
||||
void MOD_lineart_gpencil_generate_v3(const LineartCache *cache,
|
||||
const float4x4 &mat,
|
||||
Depsgraph *depsgraph,
|
||||
bke::greasepencil::Drawing &drawing,
|
||||
int8_t source_type,
|
||||
Object *source_object,
|
||||
Collection *source_collection,
|
||||
int level_start,
|
||||
int level_end,
|
||||
int mat_nr,
|
||||
int16_t edge_types,
|
||||
uchar mask_switches,
|
||||
uchar material_mask_bits,
|
||||
uchar intersection_mask,
|
||||
float thickness,
|
||||
float opacity,
|
||||
const bool fill_strokes,
|
||||
uchar shadow_selection,
|
||||
uchar silhouette_mode,
|
||||
const char *source_vgname,
|
||||
const char *vgname,
|
||||
int modifier_flags,
|
||||
int modifier_calculation_flags);
|
||||
|
||||
/**
|
||||
* Length is in image space.
|
||||
*/
|
||||
float MOD_lineart_chain_compute_length(LineartEdgeChain *ec);
|
||||
|
||||
LineartCache *MOD_lineart_init_cache();
|
||||
void MOD_lineart_clear_cache(LineartCache **lc);
|
||||
|
||||
} // namespace blender
|
||||
File diff suppressed because it is too large
Load Diff
5592
blender-5.2.0/source/blender/modifiers/intern/lineart/lineart_cpu.cc
Normal file
5592
blender-5.2.0/source/blender/modifiers/intern/lineart/lineart_cpu.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,249 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup editors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "DNA_lineart_types.h"
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct LineartBoundingArea;
|
||||
struct LineartEdge;
|
||||
struct LineartData;
|
||||
struct LineartStaticMemPool;
|
||||
struct LineartStaticMemPoolNode;
|
||||
struct LineartElementLinkNode;
|
||||
struct GreasePencilLineartModifierData;
|
||||
|
||||
void *lineart_list_append_pointer_pool(ListBase *h, struct LineartStaticMemPool *smp, void *data);
|
||||
void *lineart_list_append_pointer_pool_sized(ListBase *h,
|
||||
struct LineartStaticMemPool *smp,
|
||||
void *data,
|
||||
int size);
|
||||
void *lineart_list_append_pointer_pool_thread(ListBase *h,
|
||||
struct LineartStaticMemPool *smp,
|
||||
void *data);
|
||||
void *lineart_list_append_pointer_pool_sized_thread(ListBase *h,
|
||||
LineartStaticMemPool *smp,
|
||||
void *data,
|
||||
int size);
|
||||
void *list_push_pointer_static(ListBase *h, struct LineartStaticMemPool *smp, void *p);
|
||||
void *list_push_pointer_static_sized(ListBase *h,
|
||||
struct LineartStaticMemPool *smp,
|
||||
void *p,
|
||||
int size);
|
||||
|
||||
void *lineart_list_pop_pointer_no_free(ListBase *h);
|
||||
void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip);
|
||||
|
||||
struct LineartStaticMemPoolNode *lineart_mem_new_static_pool(struct LineartStaticMemPool *smp,
|
||||
size_t size);
|
||||
void *lineart_mem_acquire(struct LineartStaticMemPool *smp, size_t size);
|
||||
void *lineart_mem_acquire_thread(struct LineartStaticMemPool *smp, size_t size);
|
||||
void lineart_mem_destroy(struct LineartStaticMemPool *smp);
|
||||
|
||||
void lineart_prepend_pool(LinkNode **first, struct LineartStaticMemPool *smp, void *link);
|
||||
|
||||
void lineart_matrix_ortho_44d(double (*mProjection)[4],
|
||||
double xMin,
|
||||
double xMax,
|
||||
double yMin,
|
||||
double yMax,
|
||||
double zMin,
|
||||
double zMax);
|
||||
void lineart_matrix_perspective_44d(
|
||||
double (*mProjection)[4], double fFov_rad, double fAspect, double zMin, double zMax);
|
||||
|
||||
int lineart_count_intersection_segment_count(struct LineartData *ld);
|
||||
|
||||
void lineart_count_and_print_render_buffer_memory(struct LineartData *ld);
|
||||
|
||||
#define LRT_ITER_ALL_LINES_BEGIN \
|
||||
{ \
|
||||
LineartEdge *e; \
|
||||
for (int _i = 0; _i < ld->pending_edges.next; _i++) { \
|
||||
e = ld->pending_edges.array[_i];
|
||||
|
||||
#define LRT_ITER_ALL_LINES_NEXT ; /* Doesn't do anything now with new array setup. */
|
||||
|
||||
#define LRT_ITER_ALL_LINES_END \
|
||||
LRT_ITER_ALL_LINES_NEXT \
|
||||
} \
|
||||
}
|
||||
|
||||
#define LRT_BOUND_AREA_CROSSES(b1, b2) \
|
||||
((b1)[0] < (b2)[1] && (b1)[1] > (b2)[0] && (b1)[3] < (b2)[2] && (b1)[2] > (b2)[3])
|
||||
|
||||
/* Initial bounding area row/column count, setting 10 is tested to be relatively optimal for the
|
||||
* performance under current algorithm. */
|
||||
#define LRT_BA_ROWS 10
|
||||
|
||||
#define LRT_EDGE_BA_MARCHING_BEGIN(fb1, fb2) \
|
||||
double x = fb1[0], y = fb1[1]; \
|
||||
LineartBoundingArea *ba = lineart_edge_first_bounding_area(ld, fb1, fb2); \
|
||||
LineartBoundingArea *nba = ba; \
|
||||
double k = (fb2[1] - fb1[1]) / (fb2[0] - fb1[0] + 1e-30); \
|
||||
int positive_x = (fb2[0] - fb1[0]) > 0 ? 1 : (fb2[0] == fb1[0] ? 0 : -1); \
|
||||
int positive_y = (fb2[1] - fb1[1]) > 0 ? 1 : (fb2[1] == fb1[1] ? 0 : -1); \
|
||||
while (nba)
|
||||
|
||||
#define LRT_EDGE_BA_MARCHING_NEXT(fb1, fb2) \
|
||||
/* Marching along `e->v1` to `e->v2`, searching each possible bounding areas it may touch. */ \
|
||||
nba = lineart_bounding_area_next(nba, fb1, fb2, x, y, k, positive_x, positive_y, &x, &y);
|
||||
|
||||
#define LRT_EDGE_BA_MARCHING_END
|
||||
|
||||
/**
|
||||
* All internal functions starting with lineart_main_ is called inside
|
||||
* #MOD_lineart_compute_feature_lines function.
|
||||
* This function handles all occlusion calculation.
|
||||
*/
|
||||
void lineart_main_occlusion_begin(struct LineartData *ld);
|
||||
/**
|
||||
* This function cuts triangles with near- or far-plane. Setting clip_far = true for cutting with
|
||||
* far-plane. For triangles that's crossing the plane, it will generate new 1 or 2 triangles with
|
||||
* new topology that represents the trimmed triangle. (which then became a triangle or a square
|
||||
* formed by two triangles)
|
||||
*/
|
||||
void lineart_main_cull_triangles(struct LineartData *ld, bool clip_far);
|
||||
/**
|
||||
* Adjacent data is only used during the initial stages of computing.
|
||||
* So we can free it using this function when it is not needed anymore.
|
||||
*/
|
||||
void lineart_main_free_adjacent_data(struct LineartData *ld);
|
||||
void lineart_main_perspective_division(struct LineartData *ld);
|
||||
void lineart_main_discard_out_of_frame_edges(struct LineartData *ld);
|
||||
void lineart_main_load_geometries(struct Depsgraph *depsgraph,
|
||||
struct Scene *scene,
|
||||
struct Object *camera,
|
||||
struct LineartData *ld,
|
||||
bool allow_duplicates,
|
||||
bool do_shadow_casting,
|
||||
ListBaseT<LineartElementLinkNode> *shadow_elns,
|
||||
Set<const Object *> *included_objects);
|
||||
/**
|
||||
* The calculated view vector will point towards the far-plane from the camera position.
|
||||
*/
|
||||
void lineart_main_get_view_vector(struct LineartData *ld);
|
||||
void lineart_main_bounding_area_make_initial(struct LineartData *ld);
|
||||
void lineart_main_bounding_areas_connect_post(struct LineartData *ld);
|
||||
void lineart_main_clear_linked_edges(struct LineartData *ld);
|
||||
/**
|
||||
* Link lines to their respective bounding areas.
|
||||
*/
|
||||
void lineart_main_link_lines(struct LineartData *ld);
|
||||
/**
|
||||
* Sequentially add triangles into render buffer, intersection lines between those triangles will
|
||||
* also be computed at the same time.
|
||||
*/
|
||||
void lineart_main_add_triangles(struct LineartData *ld);
|
||||
/**
|
||||
* This call would internally duplicate #original_ld, override necessary configurations for shadow
|
||||
* computations. It will return:
|
||||
*
|
||||
* 1) Generated shadow edges in format of `LineartElementLinkNode` which can be directly loaded
|
||||
* into later main view camera occlusion stage.
|
||||
* 2) Shadow render buffer if 3rd stage reprojection is need for silhouette/lit/shaded region
|
||||
* selection. Otherwise the shadow render buffer is deleted before this function returns.
|
||||
*/
|
||||
bool lineart_main_try_generate_shadow(
|
||||
struct Depsgraph *depsgraph,
|
||||
struct Scene *scene,
|
||||
struct LineartData *original_ld,
|
||||
struct LineartGpencilModifierData *lmd_legacy,
|
||||
struct LineartStaticMemPool *shadow_data_pool,
|
||||
struct LineartElementLinkNode **r_veln,
|
||||
struct LineartElementLinkNode **r_eeln,
|
||||
ListBaseT<LineartElementLinkNode> *r_calculated_edges_eln_list,
|
||||
struct LineartData **r_shadow_ld_if_reproject);
|
||||
bool lineart_main_try_generate_shadow_v3(
|
||||
struct Depsgraph *depsgraph,
|
||||
struct Scene *scene,
|
||||
struct LineartData *original_ld,
|
||||
struct GreasePencilLineartModifierData *lmd,
|
||||
struct LineartStaticMemPool *shadow_data_pool,
|
||||
struct LineartElementLinkNode **r_veln,
|
||||
struct LineartElementLinkNode **r_eeln,
|
||||
ListBaseT<LineartElementLinkNode> *r_calculated_edges_eln_list,
|
||||
struct LineartData **r_shadow_ld_if_reproject);
|
||||
/**
|
||||
* Does the 3rd stage reprojection, will not re-load objects because #shadow_ld is not deleted.
|
||||
* Only re-projects view camera edges and check visibility in light camera, then we can determine
|
||||
* whether an edge landed on a lit or shaded area.
|
||||
*/
|
||||
void lineart_main_make_enclosed_shapes(struct LineartData *ld, struct LineartData *shadow_ld);
|
||||
/**
|
||||
* Shadow segments needs to be transformed to view-camera space, just like any other objects.
|
||||
*/
|
||||
void lineart_main_transform_and_add_shadow(struct LineartData *ld,
|
||||
struct LineartElementLinkNode *veln,
|
||||
struct LineartElementLinkNode *eeln);
|
||||
|
||||
LineartElementLinkNode *lineart_find_matching_eln(ListBaseT<LineartElementLinkNode> *shadow_elns,
|
||||
int obindex);
|
||||
LineartElementLinkNode *lineart_find_matching_eln_obj(ListBaseT<LineartElementLinkNode> *elns,
|
||||
struct Object *ob);
|
||||
LineartEdge *lineart_find_matching_edge(struct LineartElementLinkNode *shadow_eln,
|
||||
uint64_t edge_identifier);
|
||||
/**
|
||||
* Cuts the original edge based on the occlusion results under light-camera, if segment
|
||||
* is occluded in light-camera, then that segment on the original edge must be shaded.
|
||||
*/
|
||||
void lineart_register_shadow_cuts(struct LineartData *ld,
|
||||
struct LineartEdge *e,
|
||||
struct LineartEdge *shadow_edge);
|
||||
void lineart_register_intersection_shadow_cuts(struct LineartData *ld,
|
||||
ListBaseT<LineartElementLinkNode> *shadow_elns);
|
||||
|
||||
bool lineart_edge_from_triangle(const struct LineartTriangle *tri,
|
||||
const struct LineartEdge *e,
|
||||
bool allow_overlapping_edges);
|
||||
/**
|
||||
* This function gets the tile for the point `e->v1`, and later use #lineart_bounding_area_next()
|
||||
* to get next along the way.
|
||||
*/
|
||||
LineartBoundingArea *lineart_edge_first_bounding_area(struct LineartData *ld,
|
||||
double *fbcoord1,
|
||||
double *fbcoord2);
|
||||
/**
|
||||
* This march along one render line in image space and
|
||||
* get the next bounding area the line is crossing.
|
||||
*/
|
||||
LineartBoundingArea *lineart_bounding_area_next(struct LineartBoundingArea *self,
|
||||
double *fbcoord1,
|
||||
double *fbcoord2,
|
||||
double x,
|
||||
double y,
|
||||
double k,
|
||||
int positive_x,
|
||||
int positive_y,
|
||||
double *next_x,
|
||||
double *next_y);
|
||||
/**
|
||||
* Cuts the edge in image space and mark occlusion level for each segment.
|
||||
*/
|
||||
void lineart_edge_cut(struct LineartData *ld,
|
||||
struct LineartEdge *e,
|
||||
double start,
|
||||
double end,
|
||||
uchar material_mask_bits,
|
||||
uchar mat_occlusion,
|
||||
uint32_t shadow_bits);
|
||||
void lineart_add_edge_to_array(struct LineartPendingEdges *pe, struct LineartEdge *e);
|
||||
void lineart_finalize_object_edge_array_reserve(struct LineartPendingEdges *pe, int count);
|
||||
void lineart_destroy_render_data_keep_init(struct LineartData *ld);
|
||||
|
||||
} // namespace blender
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,246 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup editors
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
/* #include <time.h> */
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
|
||||
#include "MOD_lineart.hh"
|
||||
|
||||
#include "lineart_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Line art memory and list helper */
|
||||
|
||||
void *lineart_list_append_pointer_pool(ListBase *h, LineartStaticMemPool *smp, void *data)
|
||||
{
|
||||
LinkData *lip;
|
||||
if (h == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
lip = static_cast<LinkData *>(lineart_mem_acquire(smp, sizeof(LinkData)));
|
||||
lip->data = data;
|
||||
BLI_addtail(h, lip);
|
||||
return lip;
|
||||
}
|
||||
void *lineart_list_append_pointer_pool_sized(ListBase *h,
|
||||
LineartStaticMemPool *smp,
|
||||
void *data,
|
||||
int size)
|
||||
{
|
||||
LinkData *lip;
|
||||
if (h == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
lip = static_cast<LinkData *>(lineart_mem_acquire(smp, size));
|
||||
lip->data = data;
|
||||
BLI_addtail(h, lip);
|
||||
return lip;
|
||||
}
|
||||
void *lineart_list_append_pointer_pool_thread(ListBase *h, LineartStaticMemPool *smp, void *data)
|
||||
{
|
||||
LinkData *lip;
|
||||
if (h == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
lip = static_cast<LinkData *>(lineart_mem_acquire_thread(smp, sizeof(LinkData)));
|
||||
lip->data = data;
|
||||
BLI_addtail(h, lip);
|
||||
return lip;
|
||||
}
|
||||
void *lineart_list_append_pointer_pool_sized_thread(ListBase *h,
|
||||
LineartStaticMemPool *smp,
|
||||
void *data,
|
||||
int size)
|
||||
{
|
||||
LinkData *lip;
|
||||
if (h == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
lip = static_cast<LinkData *>(lineart_mem_acquire_thread(smp, size));
|
||||
lip->data = data;
|
||||
BLI_addtail(h, lip);
|
||||
return lip;
|
||||
}
|
||||
|
||||
void *lineart_list_pop_pointer_no_free(ListBase *h)
|
||||
{
|
||||
LinkData *lip;
|
||||
void *rev = nullptr;
|
||||
if (h == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
lip = static_cast<LinkData *>(BLI_pophead(h));
|
||||
rev = lip ? lip->data : nullptr;
|
||||
return rev;
|
||||
}
|
||||
void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip)
|
||||
{
|
||||
BLI_remlink(h, static_cast<void *>(lip));
|
||||
}
|
||||
|
||||
LineartStaticMemPoolNode *lineart_mem_new_static_pool(LineartStaticMemPool *smp, size_t size)
|
||||
{
|
||||
size_t set_size = size;
|
||||
if (set_size < LRT_MEMORY_POOL_1MB) {
|
||||
set_size = LRT_MEMORY_POOL_1MB; /* Prevent too many small allocations. */
|
||||
}
|
||||
size_t total_size = set_size + sizeof(LineartStaticMemPoolNode);
|
||||
LineartStaticMemPoolNode *smpn = static_cast<LineartStaticMemPoolNode *>(
|
||||
MEM_new_zeroed(total_size, "mempool"));
|
||||
smpn->size = total_size;
|
||||
smpn->used_byte = sizeof(LineartStaticMemPoolNode);
|
||||
BLI_addhead(&smp->pools, smpn);
|
||||
return smpn;
|
||||
}
|
||||
void *lineart_mem_acquire(LineartStaticMemPool *smp, size_t size)
|
||||
{
|
||||
LineartStaticMemPoolNode *smpn = static_cast<LineartStaticMemPoolNode *>(smp->pools.first);
|
||||
void *ret;
|
||||
|
||||
if (!smpn || (smpn->used_byte + size) > smpn->size) {
|
||||
smpn = lineart_mem_new_static_pool(smp, size);
|
||||
}
|
||||
|
||||
ret = (reinterpret_cast<uchar *>(smpn)) + smpn->used_byte;
|
||||
|
||||
smpn->used_byte += size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
void *lineart_mem_acquire_thread(LineartStaticMemPool *smp, size_t size)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
BLI_spin_lock(&smp->lock_mem);
|
||||
|
||||
LineartStaticMemPoolNode *smpn = static_cast<LineartStaticMemPoolNode *>(smp->pools.first);
|
||||
|
||||
if (!smpn || (smpn->used_byte + size) > smpn->size) {
|
||||
smpn = lineart_mem_new_static_pool(smp, size);
|
||||
}
|
||||
|
||||
ret = (reinterpret_cast<uchar *>(smpn)) + smpn->used_byte;
|
||||
|
||||
smpn->used_byte += size;
|
||||
|
||||
BLI_spin_unlock(&smp->lock_mem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
void lineart_mem_destroy(LineartStaticMemPool *smp)
|
||||
{
|
||||
while (LineartStaticMemPoolNode *smpn = static_cast<LineartStaticMemPoolNode *>(
|
||||
BLI_pophead(&smp->pools)))
|
||||
{
|
||||
MEM_delete(smpn);
|
||||
}
|
||||
}
|
||||
|
||||
void lineart_prepend_pool(LinkNode **first, LineartStaticMemPool *smp, void *link)
|
||||
{
|
||||
LinkNode *ln = static_cast<LinkNode *>(lineart_mem_acquire_thread(smp, sizeof(LinkNode)));
|
||||
ln->next = (*first);
|
||||
ln->link = link;
|
||||
(*first) = ln;
|
||||
}
|
||||
|
||||
/* =======================================================================[str] */
|
||||
|
||||
void lineart_matrix_perspective_44d(
|
||||
double (*mProjection)[4], double fFov_rad, double fAspect, double zMin, double zMax)
|
||||
{
|
||||
double yMax;
|
||||
double yMin;
|
||||
double xMin;
|
||||
double xMax;
|
||||
|
||||
if (fAspect < 1) {
|
||||
yMax = zMin * tan(fFov_rad * 0.5f);
|
||||
yMin = -yMax;
|
||||
xMin = yMin * fAspect;
|
||||
xMax = -xMin;
|
||||
}
|
||||
else {
|
||||
xMax = zMin * tan(fFov_rad * 0.5f);
|
||||
xMin = -xMax;
|
||||
yMin = xMin / fAspect;
|
||||
yMax = -yMin;
|
||||
}
|
||||
|
||||
unit_m4_db(mProjection);
|
||||
|
||||
mProjection[0][0] = (2.0f * zMin) / (xMax - xMin);
|
||||
mProjection[1][1] = (2.0f * zMin) / (yMax - yMin);
|
||||
mProjection[2][0] = (xMax + xMin) / (xMax - xMin);
|
||||
mProjection[2][1] = (yMax + yMin) / (yMax - yMin);
|
||||
mProjection[2][2] = -((zMax + zMin) / (zMax - zMin));
|
||||
mProjection[2][3] = -1.0f;
|
||||
mProjection[3][2] = -((2.0f * (zMax * zMin)) / (zMax - zMin));
|
||||
mProjection[3][3] = 0.0f;
|
||||
}
|
||||
void lineart_matrix_ortho_44d(double (*mProjection)[4],
|
||||
double xMin,
|
||||
double xMax,
|
||||
double yMin,
|
||||
double yMax,
|
||||
double zMin,
|
||||
double zMax)
|
||||
{
|
||||
unit_m4_db(mProjection);
|
||||
|
||||
mProjection[0][0] = 2.0f / (xMax - xMin);
|
||||
mProjection[1][1] = 2.0f / (yMax - yMin);
|
||||
mProjection[2][2] = -2.0f / (zMax - zMin);
|
||||
mProjection[3][0] = -((xMax + xMin) / (xMax - xMin));
|
||||
mProjection[3][1] = -((yMax + yMin) / (yMax - yMin));
|
||||
mProjection[3][2] = -((zMax + zMin) / (zMax - zMin));
|
||||
mProjection[3][3] = 1.0f;
|
||||
}
|
||||
|
||||
void lineart_count_and_print_render_buffer_memory(LineartData *ld)
|
||||
{
|
||||
size_t total = 0;
|
||||
size_t count_this = ld->render_data_pool.pools.count();
|
||||
size_t sum_this = LRT_MEMORY_POOL_1MB * count_this;
|
||||
|
||||
printf("LANPR Memory allocated %zu Standalone nodes, total %zu Bytes.\n", count_this, sum_this);
|
||||
total += sum_this;
|
||||
sum_this = 0;
|
||||
count_this = 0;
|
||||
|
||||
for (LineartElementLinkNode &reln : ld->geom.line_buffer_pointers) {
|
||||
count_this++;
|
||||
sum_this += reln.element_count * sizeof(LineartEdge);
|
||||
}
|
||||
printf(" allocated %zu edge blocks, total %zu Bytes.\n", count_this, sum_this);
|
||||
total += sum_this;
|
||||
sum_this = 0;
|
||||
count_this = 0;
|
||||
|
||||
for (LineartElementLinkNode &reln : ld->geom.triangle_buffer_pointers) {
|
||||
count_this++;
|
||||
sum_this += reln.element_count * ld->sizeof_triangle;
|
||||
}
|
||||
printf(" allocated %zu triangle blocks, total %zu Bytes.\n", count_this, sum_this);
|
||||
total += sum_this;
|
||||
sum_this = 0;
|
||||
count_this = 0;
|
||||
|
||||
(void)total; /* Ignored. */
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user