Merge branch 'master' into temp_bmesh_multires
This commit is contained in:
commit
6c3b29a14c
|
@ -4,7 +4,6 @@ Mesh with Random Vertex Colors
|
|||
"""
|
||||
import bpy
|
||||
import gpu
|
||||
import bgl
|
||||
import numpy as np
|
||||
from random import random
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
|
@ -31,9 +30,10 @@ batch = batch_for_shader(
|
|||
|
||||
|
||||
def draw():
|
||||
bgl.glEnable(bgl.GL_DEPTH_TEST)
|
||||
gpu.state.depth_test_set('LESS_EQUAL')
|
||||
gpu.state.depth_mask_set(True)
|
||||
batch.draw(shader)
|
||||
bgl.glDisable(bgl.GL_DEPTH_TEST)
|
||||
gpu.state.depth_mask_set(False)
|
||||
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
|
|
@ -6,11 +6,11 @@ To use this example you have to provide an image that should be displayed.
|
|||
"""
|
||||
import bpy
|
||||
import gpu
|
||||
import bgl
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
|
||||
IMAGE_NAME = "Untitled"
|
||||
image = bpy.data.images[IMAGE_NAME]
|
||||
texture = gpu.texture.from_image(image)
|
||||
|
||||
shader = gpu.shader.from_builtin('2D_IMAGE')
|
||||
batch = batch_for_shader(
|
||||
|
@ -21,16 +21,9 @@ batch = batch_for_shader(
|
|||
},
|
||||
)
|
||||
|
||||
if image.gl_load():
|
||||
raise Exception()
|
||||
|
||||
|
||||
def draw():
|
||||
bgl.glActiveTexture(bgl.GL_TEXTURE0)
|
||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, image.bindcode)
|
||||
|
||||
shader.bind()
|
||||
shader.uniform_int("image", 0)
|
||||
shader.uniform_sampler("image", texture)
|
||||
batch.draw(shader)
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ Generate a texture using Offscreen Rendering
|
|||
"""
|
||||
import bpy
|
||||
import gpu
|
||||
import bgl
|
||||
from mathutils import Matrix
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from gpu_extras.presets import draw_circle_2d
|
||||
|
@ -20,8 +19,8 @@ from gpu_extras.presets import draw_circle_2d
|
|||
offscreen = gpu.types.GPUOffScreen(512, 512)
|
||||
|
||||
with offscreen.bind():
|
||||
bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||
bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
|
||||
fb = gpu.state.active_framebuffer_get()
|
||||
fb.clear(color=(0.0, 0.0, 0.0, 0.0))
|
||||
with gpu.matrix.push_pop():
|
||||
# reset matrices -> use normalized device coordinates [-1, 1]
|
||||
gpu.matrix.load_matrix(Matrix.Identity(4))
|
||||
|
@ -75,13 +74,10 @@ batch = batch_for_shader(
|
|||
|
||||
|
||||
def draw():
|
||||
bgl.glActiveTexture(bgl.GL_TEXTURE0)
|
||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, offscreen.color_texture)
|
||||
|
||||
shader.bind()
|
||||
shader.uniform_float("modelMatrix", Matrix.Translation((1, 2, 3)) @ Matrix.Scale(3, 4))
|
||||
shader.uniform_float("viewProjectionMatrix", bpy.context.region_data.perspective_matrix)
|
||||
shader.uniform_float("image", 0)
|
||||
shader.uniform_sampler("image", offscreen.texture_color)
|
||||
batch.draw(shader)
|
||||
|
||||
|
||||
|
|
|
@ -7,11 +7,10 @@ If it already exists, it will override the existing one.
|
|||
|
||||
Currently almost all of the execution time is spent in the last line.
|
||||
In the future this will hopefully be solved by implementing the Python buffer protocol
|
||||
for :class:`bgl.Buffer` and :class:`bpy.types.Image.pixels` (aka ``bpy_prop_array``).
|
||||
for :class:`gpu.types.Buffer` and :class:`bpy.types.Image.pixels` (aka ``bpy_prop_array``).
|
||||
"""
|
||||
import bpy
|
||||
import gpu
|
||||
import bgl
|
||||
import random
|
||||
from mathutils import Matrix
|
||||
from gpu_extras.presets import draw_circle_2d
|
||||
|
@ -25,8 +24,8 @@ RING_AMOUNT = 10
|
|||
offscreen = gpu.types.GPUOffScreen(WIDTH, HEIGHT)
|
||||
|
||||
with offscreen.bind():
|
||||
bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||
bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
|
||||
fb = gpu.state.active_framebuffer_get()
|
||||
fb.clear(color=(0.0, 0.0, 0.0, 0.0))
|
||||
with gpu.matrix.push_pop():
|
||||
# reset matrices -> use normalized device coordinates [-1, 1]
|
||||
gpu.matrix.load_matrix(Matrix.Identity(4))
|
||||
|
@ -37,9 +36,7 @@ with offscreen.bind():
|
|||
(random.uniform(-1, 1), random.uniform(-1, 1)),
|
||||
(1, 1, 1, 1), random.uniform(0.1, 1), 20)
|
||||
|
||||
buffer = bgl.Buffer(bgl.GL_BYTE, WIDTH * HEIGHT * 4)
|
||||
bgl.glReadBuffer(bgl.GL_BACK)
|
||||
bgl.glReadPixels(0, 0, WIDTH, HEIGHT, bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer)
|
||||
buffer = fb.read_color(0, 0, WIDTH, HEIGHT, 4, 0, 'UBYTE')
|
||||
|
||||
offscreen.free()
|
||||
|
||||
|
@ -48,4 +45,6 @@ if not IMAGE_NAME in bpy.data.images:
|
|||
bpy.data.images.new(IMAGE_NAME, WIDTH, HEIGHT)
|
||||
image = bpy.data.images[IMAGE_NAME]
|
||||
image.scale(WIDTH, HEIGHT)
|
||||
|
||||
buffer.dimensions = WIDTH * HEIGHT * 4
|
||||
image.pixels = [v / 255 for v in buffer]
|
||||
|
|
|
@ -7,7 +7,6 @@ You could also make this independent of a specific camera,
|
|||
but Blender does not expose good functions to create view and projection matrices yet.
|
||||
"""
|
||||
import bpy
|
||||
import bgl
|
||||
import gpu
|
||||
from gpu_extras.presets import draw_texture_2d
|
||||
|
||||
|
@ -34,8 +33,8 @@ def draw():
|
|||
view_matrix,
|
||||
projection_matrix)
|
||||
|
||||
bgl.glDisable(bgl.GL_DEPTH_TEST)
|
||||
draw_texture_2d(offscreen.color_texture, (10, 10), WIDTH, HEIGHT)
|
||||
gpu.state.depth_mask_set(False)
|
||||
draw_texture_2d(offscreen.texture_color, (10, 10), WIDTH, HEIGHT)
|
||||
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* T76453: Prevent Long enum lists */
|
||||
.field-list > dd > p {
|
||||
.field-list > dd p {
|
||||
max-height: 245px;
|
||||
overflow-y: auto !important;
|
||||
word-break: break-word;
|
||||
|
|
|
@ -118,6 +118,7 @@ typedef struct CLG_LogType {
|
|||
typedef struct CLG_LogRef {
|
||||
const char *identifier;
|
||||
CLG_LogType *type;
|
||||
struct CLG_LogRef *next;
|
||||
} CLG_LogRef;
|
||||
|
||||
void CLG_log_str(CLG_LogType *lg,
|
||||
|
|
|
@ -81,6 +81,8 @@ typedef struct CLG_IDFilter {
|
|||
typedef struct CLogContext {
|
||||
/** Single linked list of types. */
|
||||
CLG_LogType *types;
|
||||
/** Single linked list of references. */
|
||||
CLG_LogRef *refs;
|
||||
#ifdef WITH_CLOG_PTHREADS
|
||||
pthread_mutex_t types_lock;
|
||||
#endif
|
||||
|
@ -673,6 +675,12 @@ static void CLG_ctx_free(CLogContext *ctx)
|
|||
MEM_freeN(item);
|
||||
}
|
||||
|
||||
while (ctx->refs != NULL) {
|
||||
CLG_LogRef *item = ctx->refs;
|
||||
ctx->refs = item->next;
|
||||
item->type = NULL;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < 2; i++) {
|
||||
while (ctx->filters[i] != NULL) {
|
||||
CLG_IDFilter *item = ctx->filters[i];
|
||||
|
@ -769,6 +777,10 @@ void CLG_logref_init(CLG_LogRef *clg_ref)
|
|||
pthread_mutex_lock(&g_ctx->types_lock);
|
||||
#endif
|
||||
if (clg_ref->type == NULL) {
|
||||
/* Add to the refs list so we can NULL the pointers to 'type' when CLG_exit() is called. */
|
||||
clg_ref->next = g_ctx->refs;
|
||||
g_ctx->refs = clg_ref;
|
||||
|
||||
CLG_LogType *clg_ty = clg_ctx_type_find_by_name(g_ctx, clg_ref->identifier);
|
||||
if (clg_ty == NULL) {
|
||||
clg_ty = clg_ctx_type_register(g_ctx, clg_ref->identifier);
|
||||
|
|
|
@ -71,6 +71,16 @@ if(WITH_CYCLES_STANDALONE)
|
|||
target_link_libraries(cycles ${LIBRARIES})
|
||||
cycles_target_link_libraries(cycles)
|
||||
|
||||
if(APPLE)
|
||||
if(WITH_OPENCOLORIO)
|
||||
set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit")
|
||||
endif()
|
||||
if(WITH_OPENIMAGEDENOISE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
|
||||
# OpenImageDenoise uses BNNS from the Accelerate framework.
|
||||
set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework Accelerate")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
set_target_properties(cycles PROPERTIES INSTALL_RPATH $ORIGIN/lib)
|
||||
endif()
|
||||
|
|
|
@ -552,8 +552,9 @@ class CYCLES_RENDER_PT_light_paths_fast_gi(CyclesButtonsPanel, Panel):
|
|||
|
||||
if world:
|
||||
light = world.light_settings
|
||||
sub.prop(light, "ao_factor", text="AO Factor")
|
||||
layout.prop(light, "distance", text="AO Distance")
|
||||
col = layout.column(align=True)
|
||||
col.prop(light, "ao_factor", text="AO Factor")
|
||||
col.prop(light, "distance", text="AO Distance")
|
||||
|
||||
|
||||
class CYCLES_RENDER_PT_motion_blur(CyclesButtonsPanel, Panel):
|
||||
|
|
|
@ -560,10 +560,12 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
|
|||
if (!cancel && !motion) {
|
||||
sync_background_light(b_v3d, use_portal);
|
||||
|
||||
/* handle removed data and modified pointers */
|
||||
/* Handle removed data and modified pointers, as this may free memory, delete Nodes in the
|
||||
* right order to ensure that dependent data is freed after their users. Objects should be
|
||||
* freed before particle systems and geometries. */
|
||||
light_map.post_sync();
|
||||
geometry_map.post_sync();
|
||||
object_map.post_sync();
|
||||
geometry_map.post_sync();
|
||||
particle_system_map.post_sync();
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "util/util_path.h"
|
||||
#include "util/util_string.h"
|
||||
#include "util/util_task.h"
|
||||
#include "util/util_tbb.h"
|
||||
#include "util/util_types.h"
|
||||
|
||||
#ifdef WITH_OSL
|
||||
|
@ -288,9 +289,11 @@ static PyObject *render_func(PyObject * /*self*/, PyObject *args)
|
|||
RNA_pointer_create(NULL, &RNA_Depsgraph, (ID *)PyLong_AsVoidPtr(pydepsgraph), &depsgraphptr);
|
||||
BL::Depsgraph b_depsgraph(depsgraphptr);
|
||||
|
||||
/* Allow Blender to execute other Python scripts, and isolate TBB tasks so we
|
||||
* don't get deadlocks with Blender threads accessing shared data like images. */
|
||||
python_thread_state_save(&session->python_thread_state);
|
||||
|
||||
session->render(b_depsgraph);
|
||||
tbb::this_task_arena::isolate([&] { session->render(b_depsgraph); });
|
||||
|
||||
python_thread_state_restore(&session->python_thread_state);
|
||||
|
||||
|
@ -327,7 +330,8 @@ static PyObject *bake_func(PyObject * /*self*/, PyObject *args)
|
|||
|
||||
python_thread_state_save(&session->python_thread_state);
|
||||
|
||||
session->bake(b_depsgraph, b_object, pass_type, pass_filter, width, height);
|
||||
tbb::this_task_arena::isolate(
|
||||
[&] { session->bake(b_depsgraph, b_object, pass_type, pass_filter, width, height); });
|
||||
|
||||
python_thread_state_restore(&session->python_thread_state);
|
||||
|
||||
|
@ -373,7 +377,7 @@ static PyObject *reset_func(PyObject * /*self*/, PyObject *args)
|
|||
|
||||
python_thread_state_save(&session->python_thread_state);
|
||||
|
||||
session->reset_session(b_data, b_depsgraph);
|
||||
tbb::this_task_arena::isolate([&] { session->reset_session(b_data, b_depsgraph); });
|
||||
|
||||
python_thread_state_restore(&session->python_thread_state);
|
||||
|
||||
|
@ -395,7 +399,7 @@ static PyObject *sync_func(PyObject * /*self*/, PyObject *args)
|
|||
|
||||
python_thread_state_save(&session->python_thread_state);
|
||||
|
||||
session->synchronize(b_depsgraph);
|
||||
tbb::this_task_arena::isolate([&] { session->synchronize(b_depsgraph); });
|
||||
|
||||
python_thread_state_restore(&session->python_thread_state);
|
||||
|
||||
|
|
|
@ -237,6 +237,9 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg
|
|||
sync->sync_recalc(b_depsgraph, b_v3d);
|
||||
}
|
||||
|
||||
BL::Object b_camera_override(b_engine.camera_override());
|
||||
sync->sync_camera(b_render, b_camera_override, width, height, "");
|
||||
|
||||
BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL);
|
||||
BL::RegionView3D b_null_region_view3d(PointerRNA_NULL);
|
||||
BufferParams buffer_params = BlenderSync::get_buffer_params(b_render,
|
||||
|
|
|
@ -367,9 +367,17 @@ void Node::copy_value(const SocketType &socket, const Node &other, const SocketT
|
|||
case SocketType::TRANSFORM_ARRAY:
|
||||
copy_array<Transform>(this, socket, &other, other_socket);
|
||||
break;
|
||||
case SocketType::NODE_ARRAY:
|
||||
case SocketType::NODE_ARRAY: {
|
||||
copy_array<void *>(this, socket, &other, other_socket);
|
||||
|
||||
array<Node *> &node_array = get_socket_value<array<Node *>>(this, socket);
|
||||
|
||||
for (Node *node : node_array) {
|
||||
node->reference();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
|
@ -379,6 +387,14 @@ void Node::copy_value(const SocketType &socket, const Node &other, const SocketT
|
|||
const void *src = ((char *)&other) + other_socket.struct_offset;
|
||||
void *dst = ((char *)this) + socket.struct_offset;
|
||||
memcpy(dst, src, socket.size());
|
||||
|
||||
if (socket.type == SocketType::NODE) {
|
||||
Node *node = get_socket_value<Node *>(this, socket);
|
||||
|
||||
if (node) {
|
||||
node->reference();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -773,6 +789,26 @@ void Node::set_owner(const NodeOwner *owner_)
|
|||
owner = owner_;
|
||||
}
|
||||
|
||||
void Node::dereference_all_used_nodes()
|
||||
{
|
||||
foreach (const SocketType &socket, type->inputs) {
|
||||
if (socket.type == SocketType::NODE) {
|
||||
Node *node = get_socket_value<Node *>(this, socket);
|
||||
|
||||
if (node) {
|
||||
node->dereference();
|
||||
}
|
||||
}
|
||||
else if (socket.type == SocketType::NODE_ARRAY) {
|
||||
const array<Node *> &nodes = get_socket_value<array<Node *>>(this, socket);
|
||||
|
||||
for (Node *node : nodes) {
|
||||
node->dereference();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Node::socket_is_modified(const SocketType &input) const
|
||||
{
|
||||
return (socket_modified & input.modified_flag_bit) != 0;
|
||||
|
@ -803,6 +839,25 @@ template<typename T> void Node::set_if_different(const SocketType &input, T valu
|
|||
socket_modified |= input.modified_flag_bit;
|
||||
}
|
||||
|
||||
void Node::set_if_different(const SocketType &input, Node *value)
|
||||
{
|
||||
if (get_socket_value<Node *>(this, input) == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node *old_node = get_socket_value<Node *>(this, input);
|
||||
if (old_node) {
|
||||
old_node->dereference();
|
||||
}
|
||||
|
||||
if (value) {
|
||||
value->reference();
|
||||
}
|
||||
|
||||
get_socket_value<Node *>(this, input) = value;
|
||||
socket_modified |= input.modified_flag_bit;
|
||||
}
|
||||
|
||||
template<typename T> void Node::set_if_different(const SocketType &input, array<T> &value)
|
||||
{
|
||||
if (!socket_is_modified(input)) {
|
||||
|
@ -815,6 +870,27 @@ template<typename T> void Node::set_if_different(const SocketType &input, array<
|
|||
socket_modified |= input.modified_flag_bit;
|
||||
}
|
||||
|
||||
void Node::set_if_different(const SocketType &input, array<Node *> &value)
|
||||
{
|
||||
if (!socket_is_modified(input)) {
|
||||
if (get_socket_value<array<Node *>>(this, input) == value) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
array<Node *> &old_nodes = get_socket_value<array<Node *>>(this, input);
|
||||
for (Node *old_node : old_nodes) {
|
||||
old_node->dereference();
|
||||
}
|
||||
|
||||
for (Node *new_node : value) {
|
||||
new_node->reference();
|
||||
}
|
||||
|
||||
get_socket_value<array<Node *>>(this, input).steal_data(value);
|
||||
socket_modified |= input.modified_flag_bit;
|
||||
}
|
||||
|
||||
void Node::print_modified_sockets() const
|
||||
{
|
||||
printf("Node : %s\n", name.c_str());
|
||||
|
|
|
@ -177,8 +177,32 @@ struct Node {
|
|||
const NodeOwner *get_owner() const;
|
||||
void set_owner(const NodeOwner *owner_);
|
||||
|
||||
int reference_count() const
|
||||
{
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
void reference()
|
||||
{
|
||||
ref_count += 1;
|
||||
}
|
||||
|
||||
void dereference()
|
||||
{
|
||||
ref_count -= 1;
|
||||
}
|
||||
|
||||
/* Set the reference count to zero. This should only be called when we know for sure that the
|
||||
* Node is not used by anyone else. For now, this is only the case when "deleting" shaders, as
|
||||
* they are never actually deleted. */
|
||||
void clear_reference_count()
|
||||
{
|
||||
ref_count = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
const NodeOwner *owner;
|
||||
int ref_count{0};
|
||||
|
||||
template<typename T> static T &get_socket_value(const Node *node, const SocketType &socket)
|
||||
{
|
||||
|
@ -189,7 +213,19 @@ struct Node {
|
|||
|
||||
template<typename T> void set_if_different(const SocketType &input, T value);
|
||||
|
||||
/* Explicit overload for Node sockets so we can handle reference counting. The old Node is
|
||||
* dereferenced, and the new one is referenced. */
|
||||
void set_if_different(const SocketType &input, Node *value);
|
||||
|
||||
template<typename T> void set_if_different(const SocketType &input, array<T> &value);
|
||||
|
||||
/* Explicit overload for Node sockets so we can handle reference counting. The old Nodes are
|
||||
* dereferenced, and the new ones are referenced. */
|
||||
void set_if_different(const SocketType &input, array<Node *> &value);
|
||||
|
||||
/* Call this function in derived classes' destructors to ensure that used Nodes are dereferenced
|
||||
* properly. */
|
||||
void dereference_all_used_nodes();
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
|
|
@ -148,16 +148,17 @@ struct NodeType {
|
|||
#define NODE_DECLARE \
|
||||
static const NodeType *get_node_type(); \
|
||||
template<typename T> static const NodeType *register_type(); \
|
||||
static Node *create(const NodeType *type);
|
||||
static Node *create(const NodeType *type); \
|
||||
static const NodeType *node_type;
|
||||
|
||||
#define NODE_DEFINE(structname) \
|
||||
const NodeType *structname::node_type = structname::register_type<structname>(); \
|
||||
Node *structname::create(const NodeType *) \
|
||||
{ \
|
||||
return new structname(); \
|
||||
} \
|
||||
const NodeType *structname::get_node_type() \
|
||||
{ \
|
||||
static const NodeType *node_type = register_type<structname>(); \
|
||||
return node_type; \
|
||||
} \
|
||||
template<typename T> const NodeType *structname::register_type()
|
||||
|
@ -169,6 +170,8 @@ struct NodeType {
|
|||
#define NODE_ABSTRACT_DEFINE(structname) \
|
||||
const NodeType *structname::get_node_base_type() \
|
||||
{ \
|
||||
/* Base types constructed in this getter to ensure correct initialization \
|
||||
* order. Regular types are not so they are auto-registered for XML parsing. */ \
|
||||
static const NodeType *node_base_type = register_base_type<structname>(); \
|
||||
return node_base_type; \
|
||||
} \
|
||||
|
|
|
@ -606,10 +606,10 @@ ccl_device_noinline
|
|||
t = ray->t;
|
||||
}
|
||||
else if (bounce == 0) {
|
||||
/* Restore original position if nothing was hit after the first bounce.
|
||||
* Otherwise if the ray_offset() to avoid self-intersection is relatively
|
||||
* large compared to the scattering radius, we go never backup high enough
|
||||
* to exit the surface. */
|
||||
/* Restore original position if nothing was hit after the first bounce,
|
||||
* without the ray_offset() that was added to avoid self-intersection.
|
||||
* Otherwise if that offset is relatively large compared to the scattering
|
||||
* radius, we never go back up high enough to exit the surface. */
|
||||
ray->P = sd->P;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ set(INC_SYS
|
|||
|
||||
set(SRC
|
||||
alembic.cpp
|
||||
alembic_read.cpp
|
||||
attribute.cpp
|
||||
background.cpp
|
||||
bake.cpp
|
||||
|
@ -67,6 +68,7 @@ set(SRC
|
|||
|
||||
set(SRC_HEADERS
|
||||
alembic.h
|
||||
alembic_read.h
|
||||
attribute.h
|
||||
bake.h
|
||||
background.h
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -152,6 +152,10 @@ template<typename T> class DataStore {
|
|||
double last_loaded_time = std::numeric_limits<double>::max();
|
||||
|
||||
public:
|
||||
/* Keys used to compare values. */
|
||||
Alembic::AbcCoreAbstract::ArraySample::Key key1;
|
||||
Alembic::AbcCoreAbstract::ArraySample::Key key2;
|
||||
|
||||
void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling_)
|
||||
{
|
||||
time_sampling = time_sampling_;
|
||||
|
@ -225,6 +229,11 @@ template<typename T> class DataStore {
|
|||
index_data_map.push_back({time, data_index.source_time, data_index.index});
|
||||
}
|
||||
|
||||
void add_no_data(double time)
|
||||
{
|
||||
index_data_map.push_back({time, time, -1ul});
|
||||
}
|
||||
|
||||
bool is_constant() const
|
||||
{
|
||||
return data.size() <= 1;
|
||||
|
@ -284,7 +293,7 @@ struct CachedData {
|
|||
DataStore<array<int3>> triangles{};
|
||||
/* triangle "loops" are the polygons' vertices indices used for indexing face varying attributes
|
||||
* (like UVs) */
|
||||
DataStore<array<int3>> triangles_loops{};
|
||||
DataStore<array<int>> uv_loops{};
|
||||
DataStore<array<int>> shader{};
|
||||
|
||||
/* subd data */
|
||||
|
@ -362,16 +371,18 @@ class AlembicObject : public Node {
|
|||
void set_object(Object *object);
|
||||
Object *get_object();
|
||||
|
||||
void load_all_data(AlembicProcedural *proc,
|
||||
Alembic::AbcGeom::IPolyMeshSchema &schema,
|
||||
Progress &progress);
|
||||
void load_all_data(AlembicProcedural *proc,
|
||||
Alembic::AbcGeom::ISubDSchema &schema,
|
||||
Progress &progress);
|
||||
void load_all_data(AlembicProcedural *proc,
|
||||
const Alembic::AbcGeom::ICurvesSchema &schema,
|
||||
Progress &progress,
|
||||
float default_radius);
|
||||
void load_data_in_cache(CachedData &cached_data,
|
||||
AlembicProcedural *proc,
|
||||
Alembic::AbcGeom::IPolyMeshSchema &schema,
|
||||
Progress &progress);
|
||||
void load_data_in_cache(CachedData &cached_data,
|
||||
AlembicProcedural *proc,
|
||||
Alembic::AbcGeom::ISubDSchema &schema,
|
||||
Progress &progress);
|
||||
void load_data_in_cache(CachedData &cached_data,
|
||||
AlembicProcedural *proc,
|
||||
const Alembic::AbcGeom::ICurvesSchema &schema,
|
||||
Progress &progress);
|
||||
|
||||
bool has_data_loaded() const;
|
||||
|
||||
|
@ -397,33 +408,21 @@ class AlembicObject : public Node {
|
|||
|
||||
CachedData &get_cached_data()
|
||||
{
|
||||
return cached_data;
|
||||
return cached_data_;
|
||||
}
|
||||
|
||||
bool is_constant() const
|
||||
{
|
||||
return cached_data.is_constant();
|
||||
return cached_data_.is_constant();
|
||||
}
|
||||
|
||||
Object *object = nullptr;
|
||||
|
||||
bool data_loaded = false;
|
||||
|
||||
CachedData cached_data;
|
||||
CachedData cached_data_;
|
||||
|
||||
void update_shader_attributes(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
|
||||
Progress &progress);
|
||||
|
||||
void read_attribute(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
|
||||
const ustring &attr_name,
|
||||
Progress &progress);
|
||||
|
||||
template<typename SchemaType>
|
||||
void read_face_sets(SchemaType &schema,
|
||||
array<int> &polygon_to_shader,
|
||||
Alembic::AbcGeom::ISampleSelector sample_sel);
|
||||
|
||||
void setup_transform_cache(float scale);
|
||||
void setup_transform_cache(CachedData &cached_data, float scale);
|
||||
|
||||
AttributeRequestSet get_requested_attributes();
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright 2021 Blender Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_ALEMBIC
|
||||
|
||||
# include <Alembic/AbcCoreFactory/All.h>
|
||||
# include <Alembic/AbcGeom/All.h>
|
||||
|
||||
# include "util/util_vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class AlembicProcedural;
|
||||
class AttributeRequestSet;
|
||||
class Progress;
|
||||
struct CachedData;
|
||||
|
||||
/* Maps a FaceSet whose name matches that of a Shader to the index of said shader in the Geometry's
|
||||
* used_shaders list. */
|
||||
struct FaceSetShaderIndexPair {
|
||||
Alembic::AbcGeom::IFaceSet face_set;
|
||||
int shader_index;
|
||||
};
|
||||
|
||||
/* Data of an IPolyMeshSchema that we need to read. */
|
||||
struct PolyMeshSchemaData {
|
||||
Alembic::AbcGeom::TimeSamplingPtr time_sampling;
|
||||
size_t num_samples;
|
||||
Alembic::AbcGeom::MeshTopologyVariance topology_variance;
|
||||
|
||||
Alembic::AbcGeom::IP3fArrayProperty positions;
|
||||
Alembic::AbcGeom::IInt32ArrayProperty face_indices;
|
||||
Alembic::AbcGeom::IInt32ArrayProperty face_counts;
|
||||
|
||||
Alembic::AbcGeom::IN3fGeomParam normals;
|
||||
|
||||
vector<FaceSetShaderIndexPair> shader_face_sets;
|
||||
|
||||
// Unsupported for now.
|
||||
Alembic::AbcGeom::IV3fArrayProperty velocities;
|
||||
};
|
||||
|
||||
void read_geometry_data(AlembicProcedural *proc,
|
||||
CachedData &cached_data,
|
||||
const PolyMeshSchemaData &data,
|
||||
Progress &progress);
|
||||
|
||||
/* Data of an ISubDSchema that we need to read. */
|
||||
struct SubDSchemaData {
|
||||
Alembic::AbcGeom::TimeSamplingPtr time_sampling;
|
||||
size_t num_samples;
|
||||
Alembic::AbcGeom::MeshTopologyVariance topology_variance;
|
||||
|
||||
Alembic::AbcGeom::IInt32ArrayProperty face_counts;
|
||||
Alembic::AbcGeom::IInt32ArrayProperty face_indices;
|
||||
Alembic::AbcGeom::IP3fArrayProperty positions;
|
||||
|
||||
Alembic::AbcGeom::IInt32ArrayProperty crease_indices;
|
||||
Alembic::AbcGeom::IInt32ArrayProperty crease_lengths;
|
||||
Alembic::AbcGeom::IFloatArrayProperty crease_sharpnesses;
|
||||
|
||||
vector<FaceSetShaderIndexPair> shader_face_sets;
|
||||
|
||||
// Those are unsupported for now.
|
||||
Alembic::AbcGeom::IInt32ArrayProperty corner_indices;
|
||||
Alembic::AbcGeom::IFloatArrayProperty corner_sharpnesses;
|
||||
Alembic::AbcGeom::IInt32Property face_varying_interpolate_boundary;
|
||||
Alembic::AbcGeom::IInt32Property face_varying_propagate_corners;
|
||||
Alembic::AbcGeom::IInt32Property interpolate_boundary;
|
||||
Alembic::AbcGeom::IInt32ArrayProperty holes;
|
||||
Alembic::AbcGeom::IStringProperty subdivision_scheme;
|
||||
Alembic::AbcGeom::IV3fArrayProperty velocities;
|
||||
};
|
||||
|
||||
void read_geometry_data(AlembicProcedural *proc,
|
||||
CachedData &cached_data,
|
||||
const SubDSchemaData &data,
|
||||
Progress &progress);
|
||||
|
||||
/* Data of a ICurvesSchema that we need to read. */
|
||||
struct CurvesSchemaData {
|
||||
Alembic::AbcGeom::TimeSamplingPtr time_sampling;
|
||||
size_t num_samples;
|
||||
Alembic::AbcGeom::MeshTopologyVariance topology_variance;
|
||||
|
||||
Alembic::AbcGeom::IP3fArrayProperty positions;
|
||||
|
||||
Alembic::AbcGeom::IInt32ArrayProperty num_vertices;
|
||||
|
||||
float default_radius;
|
||||
float radius_scale;
|
||||
|
||||
// Those are unsupported for now.
|
||||
Alembic::AbcGeom::IV3fArrayProperty velocities;
|
||||
// if this property is invalid then the weight for every point is 1
|
||||
Alembic::AbcGeom::IFloatArrayProperty position_weights;
|
||||
Alembic::AbcGeom::IN3fGeomParam normals;
|
||||
Alembic::AbcGeom::IFloatGeomParam widths;
|
||||
Alembic::AbcGeom::IUcharArrayProperty orders;
|
||||
Alembic::AbcGeom::IFloatArrayProperty knots;
|
||||
|
||||
// TODO(@kevindietrich): type, basis, wrap
|
||||
};
|
||||
|
||||
void read_geometry_data(AlembicProcedural *proc,
|
||||
CachedData &cached_data,
|
||||
const CurvesSchemaData &data,
|
||||
Progress &progress);
|
||||
|
||||
void read_attributes(AlembicProcedural *proc,
|
||||
CachedData &cache,
|
||||
const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
|
||||
const Alembic::AbcGeom::IV2fGeomParam &default_uvs_param,
|
||||
const AttributeRequestSet &requested_attributes,
|
||||
Progress &progress);
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
|
@ -59,6 +59,7 @@ Background::Background() : Node(get_node_type())
|
|||
|
||||
Background::~Background()
|
||||
{
|
||||
dereference_all_used_nodes();
|
||||
}
|
||||
|
||||
void Background::device_update(Device *device, DeviceScene *dscene, Scene *scene)
|
||||
|
|
|
@ -79,6 +79,7 @@ Geometry::Geometry(const NodeType *node_type, const Type type)
|
|||
|
||||
Geometry::~Geometry()
|
||||
{
|
||||
dereference_all_used_nodes();
|
||||
delete bvh;
|
||||
}
|
||||
|
||||
|
|
|
@ -159,6 +159,7 @@ NODE_DEFINE(Light)
|
|||
|
||||
Light::Light() : Node(get_node_type())
|
||||
{
|
||||
dereference_all_used_nodes();
|
||||
}
|
||||
|
||||
void Light::tag_update(Scene *scene)
|
||||
|
@ -864,7 +865,7 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
|
|||
const float min_spread_angle = 1.0f * M_PI_F / 180.0f;
|
||||
const float spread_angle = 0.5f * (M_PI_F - max(light->spread, min_spread_angle));
|
||||
/* Normalization computed using:
|
||||
* integrate cos(x) (1 - tan(x) * tan(a)) * sin(x) from x = a to pi/2. */
|
||||
* integrate cos(x) * (1 - tan(x) * tan(a)) * sin(x) from x = 0 to pi/2 - a. */
|
||||
const float tan_spread = tanf(spread_angle);
|
||||
const float normalize_spread = 2.0f / (2.0f + (2.0f * spread_angle - M_PI_F) * tan_spread);
|
||||
|
||||
|
|
|
@ -91,10 +91,10 @@ void OSLShaderManager::reset(Scene * /*scene*/)
|
|||
shading_system_init();
|
||||
}
|
||||
|
||||
void OSLShaderManager::device_update(Device *device,
|
||||
DeviceScene *dscene,
|
||||
Scene *scene,
|
||||
Progress &progress)
|
||||
void OSLShaderManager::device_update_specific(Device *device,
|
||||
DeviceScene *dscene,
|
||||
Scene *scene,
|
||||
Progress &progress)
|
||||
{
|
||||
if (!need_update())
|
||||
return;
|
||||
|
@ -1149,7 +1149,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
|
|||
shader->has_integrator_dependency = false;
|
||||
|
||||
/* generate surface shader */
|
||||
if (shader->used && graph && output->input("Surface")->link) {
|
||||
if (shader->reference_count() && graph && output->input("Surface")->link) {
|
||||
shader->osl_surface_ref = compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
|
||||
|
||||
if (has_bump)
|
||||
|
@ -1165,7 +1165,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
|
|||
}
|
||||
|
||||
/* generate volume shader */
|
||||
if (shader->used && graph && output->input("Volume")->link) {
|
||||
if (shader->reference_count() && graph && output->input("Volume")->link) {
|
||||
shader->osl_volume_ref = compile_type(shader, shader->graph, SHADER_TYPE_VOLUME);
|
||||
shader->has_volume = true;
|
||||
}
|
||||
|
@ -1173,7 +1173,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
|
|||
shader->osl_volume_ref = OSL::ShaderGroupRef();
|
||||
|
||||
/* generate displacement shader */
|
||||
if (shader->used && graph && output->input("Displacement")->link) {
|
||||
if (shader->reference_count() && graph && output->input("Displacement")->link) {
|
||||
shader->osl_displacement_ref = compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT);
|
||||
shader->has_displacement = true;
|
||||
}
|
||||
|
|
|
@ -79,7 +79,10 @@ class OSLShaderManager : public ShaderManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
|
||||
void device_update_specific(Device *device,
|
||||
DeviceScene *dscene,
|
||||
Scene *scene,
|
||||
Progress &progress) override;
|
||||
void device_free(Device *device, DeviceScene *dscene, Scene *scene);
|
||||
|
||||
/* osl compile and query */
|
||||
|
|
|
@ -143,21 +143,27 @@ void Scene::free_memory(bool final)
|
|||
delete bvh;
|
||||
bvh = NULL;
|
||||
|
||||
foreach (Shader *s, shaders)
|
||||
delete s;
|
||||
/* delete procedurals before other types as they may hold pointers to those types */
|
||||
/* The order of deletion is important to make sure data is freed based on possible dependencies
|
||||
* as the Nodes' reference counts are decremented in the destructors:
|
||||
*
|
||||
* - Procedurals can create and hold pointers to any other types.
|
||||
* - Objects can hold pointers to Geometries and ParticleSystems
|
||||
* - Lights and Geometries can hold pointers to Shaders.
|
||||
*
|
||||
* Similarly, we first delete all nodes and their associated device data, and then the managers
|
||||
* and their associated device data.
|
||||
*/
|
||||
foreach (Procedural *p, procedurals)
|
||||
delete p;
|
||||
foreach (Geometry *g, geometry)
|
||||
delete g;
|
||||
foreach (Object *o, objects)
|
||||
delete o;
|
||||
foreach (Light *l, lights)
|
||||
delete l;
|
||||
foreach (Geometry *g, geometry)
|
||||
delete g;
|
||||
foreach (ParticleSystem *p, particle_systems)
|
||||
delete p;
|
||||
foreach (Light *l, lights)
|
||||
delete l;
|
||||
|
||||
shaders.clear();
|
||||
geometry.clear();
|
||||
objects.clear();
|
||||
lights.clear();
|
||||
|
@ -169,7 +175,25 @@ void Scene::free_memory(bool final)
|
|||
film->device_free(device, &dscene, this);
|
||||
background->device_free(device, &dscene);
|
||||
integrator->device_free(device, &dscene, true);
|
||||
}
|
||||
|
||||
if (final) {
|
||||
delete camera;
|
||||
delete dicing_camera;
|
||||
delete film;
|
||||
delete background;
|
||||
delete integrator;
|
||||
}
|
||||
|
||||
/* Delete Shaders after every other nodes to ensure that we do not try to decrement the reference
|
||||
* count on some dangling pointer. */
|
||||
foreach (Shader *s, shaders)
|
||||
delete s;
|
||||
|
||||
shaders.clear();
|
||||
|
||||
/* Now that all nodes have been deleted, we can safely delete managers and device data. */
|
||||
if (device) {
|
||||
object_manager->device_free(device, &dscene, true);
|
||||
geometry_manager->device_free(device, &dscene, true);
|
||||
shader_manager->device_free(device, &dscene, this);
|
||||
|
@ -189,11 +213,6 @@ void Scene::free_memory(bool final)
|
|||
|
||||
if (final) {
|
||||
delete lookup_tables;
|
||||
delete camera;
|
||||
delete dicing_camera;
|
||||
delete film;
|
||||
delete background;
|
||||
delete integrator;
|
||||
delete object_manager;
|
||||
delete geometry_manager;
|
||||
delete shader_manager;
|
||||
|
@ -504,9 +523,6 @@ bool Scene::update(Progress &progress, bool &kernel_switch_needed)
|
|||
{
|
||||
/* update scene */
|
||||
if (need_update()) {
|
||||
/* Updated used shader tag so we know which features are need for the kernel. */
|
||||
shader_manager->update_shaders_used(this);
|
||||
|
||||
/* Update max_closures. */
|
||||
KernelIntegrator *kintegrator = &dscene.data.integrator;
|
||||
if (params.background) {
|
||||
|
@ -566,9 +582,6 @@ bool Scene::load_kernels(Progress &progress, bool lock_scene)
|
|||
return false;
|
||||
}
|
||||
|
||||
progress.add_skip_time(timer, false);
|
||||
VLOG(1) << "Total time spent loading kernels: " << time_dt() - timer.get_start();
|
||||
|
||||
kernels_loaded = true;
|
||||
loaded_kernel_features = requested_features;
|
||||
return true;
|
||||
|
@ -587,7 +600,7 @@ int Scene::get_max_closure_count()
|
|||
int max_closures = 0;
|
||||
for (int i = 0; i < shaders.size(); i++) {
|
||||
Shader *shader = shaders[i];
|
||||
if (shader->used) {
|
||||
if (shader->reference_count()) {
|
||||
int num_closures = shader->graph->get_num_closures();
|
||||
max_closures = max(max_closures, num_closures);
|
||||
}
|
||||
|
@ -748,9 +761,10 @@ template<> void Scene::delete_node_impl(ParticleSystem *node)
|
|||
particle_system_manager->tag_update(this);
|
||||
}
|
||||
|
||||
template<> void Scene::delete_node_impl(Shader * /*node*/)
|
||||
template<> void Scene::delete_node_impl(Shader *shader)
|
||||
{
|
||||
/* don't delete unused shaders, not supported */
|
||||
shader->clear_reference_count();
|
||||
}
|
||||
|
||||
template<> void Scene::delete_node_impl(Procedural *node)
|
||||
|
@ -817,9 +831,12 @@ template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const No
|
|||
particle_system_manager->tag_update(this);
|
||||
}
|
||||
|
||||
template<> void Scene::delete_nodes(const set<Shader *> & /*nodes*/, const NodeOwner * /*owner*/)
|
||||
template<> void Scene::delete_nodes(const set<Shader *> &nodes, const NodeOwner * /*owner*/)
|
||||
{
|
||||
/* don't delete unused shaders, not supported */
|
||||
for (Shader *shader : nodes) {
|
||||
shader->clear_reference_count();
|
||||
}
|
||||
}
|
||||
|
||||
template<> void Scene::delete_nodes(const set<Procedural *> &nodes, const NodeOwner *owner)
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
#include "device/device.h"
|
||||
|
||||
#include "render/alembic.h"
|
||||
#include "render/background.h"
|
||||
#include "render/camera.h"
|
||||
#include "render/colorspace.h"
|
||||
|
@ -27,6 +26,7 @@
|
|||
#include "render/nodes.h"
|
||||
#include "render/object.h"
|
||||
#include "render/osl.h"
|
||||
#include "render/procedural.h"
|
||||
#include "render/scene.h"
|
||||
#include "render/shader.h"
|
||||
#include "render/svm.h"
|
||||
|
@ -218,7 +218,6 @@ Shader::Shader() : Node(get_node_type())
|
|||
displacement_method = DISPLACE_BUMP;
|
||||
|
||||
id = -1;
|
||||
used = false;
|
||||
|
||||
need_update_uvs = true;
|
||||
need_update_attribute = true;
|
||||
|
@ -382,8 +381,9 @@ void Shader::tag_used(Scene *scene)
|
|||
{
|
||||
/* if an unused shader suddenly gets used somewhere, it needs to be
|
||||
* recompiled because it was skipped for compilation before */
|
||||
if (!used) {
|
||||
if (!reference_count()) {
|
||||
tag_modified();
|
||||
/* We do not reference here as the shader will be referenced when added to a socket. */
|
||||
scene->shader_manager->tag_update(scene, ShaderManager::SHADER_MODIFIED);
|
||||
}
|
||||
}
|
||||
|
@ -461,52 +461,28 @@ int ShaderManager::get_shader_id(Shader *shader, bool smooth)
|
|||
return id;
|
||||
}
|
||||
|
||||
void ShaderManager::update_shaders_used(Scene *scene)
|
||||
void ShaderManager::device_update(Device *device,
|
||||
DeviceScene *dscene,
|
||||
Scene *scene,
|
||||
Progress &progress)
|
||||
{
|
||||
if (!need_update()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* figure out which shaders are in use, so SVM/OSL can skip compiling them
|
||||
* for speed and avoid loading image textures into memory */
|
||||
uint id = 0;
|
||||
foreach (Shader *shader, scene->shaders) {
|
||||
shader->used = false;
|
||||
shader->id = id++;
|
||||
}
|
||||
|
||||
scene->default_surface->used = true;
|
||||
scene->default_light->used = true;
|
||||
scene->default_background->used = true;
|
||||
scene->default_empty->used = true;
|
||||
/* Those shaders should always be compiled as they are used as fallback if a shader cannot be
|
||||
* found, e.g. bad shader index for the triangle shaders on a Mesh. */
|
||||
assert(scene->default_surface->reference_count() != 0);
|
||||
assert(scene->default_light->reference_count() != 0);
|
||||
assert(scene->default_background->reference_count() != 0);
|
||||
assert(scene->default_empty->reference_count() != 0);
|
||||
|
||||
if (scene->background->get_shader())
|
||||
scene->background->get_shader()->used = true;
|
||||
|
||||
#ifdef WITH_ALEMBIC
|
||||
foreach (Procedural *procedural, scene->procedurals) {
|
||||
AlembicProcedural *abc_proc = static_cast<AlembicProcedural *>(procedural);
|
||||
|
||||
foreach (Node *abc_node, abc_proc->get_objects()) {
|
||||
AlembicObject *abc_object = static_cast<AlembicObject *>(abc_node);
|
||||
|
||||
foreach (Node *node, abc_object->get_used_shaders()) {
|
||||
Shader *shader = static_cast<Shader *>(node);
|
||||
shader->used = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
foreach (Geometry *geom, scene->geometry)
|
||||
foreach (Node *node, geom->get_used_shaders()) {
|
||||
Shader *shader = static_cast<Shader *>(node);
|
||||
shader->used = true;
|
||||
}
|
||||
|
||||
foreach (Light *light, scene->lights)
|
||||
if (light->get_shader())
|
||||
const_cast<Shader *>(light->get_shader())->used = true;
|
||||
device_update_specific(device, dscene, scene, progress);
|
||||
}
|
||||
|
||||
void ShaderManager::device_update_common(Device *device,
|
||||
|
@ -639,6 +615,7 @@ void ShaderManager::add_default(Scene *scene)
|
|||
Shader *shader = scene->create_node<Shader>();
|
||||
shader->name = "default_surface";
|
||||
shader->set_graph(graph);
|
||||
shader->reference();
|
||||
scene->default_surface = shader;
|
||||
shader->tag_update(scene);
|
||||
}
|
||||
|
@ -657,6 +634,8 @@ void ShaderManager::add_default(Scene *scene)
|
|||
shader->set_graph(graph);
|
||||
scene->default_volume = shader;
|
||||
shader->tag_update(scene);
|
||||
/* No default reference for the volume to avoid compiling volume kernels if there are no actual
|
||||
* volumes in the scene */
|
||||
}
|
||||
|
||||
/* default light */
|
||||
|
@ -673,6 +652,7 @@ void ShaderManager::add_default(Scene *scene)
|
|||
Shader *shader = scene->create_node<Shader>();
|
||||
shader->name = "default_light";
|
||||
shader->set_graph(graph);
|
||||
shader->reference();
|
||||
scene->default_light = shader;
|
||||
shader->tag_update(scene);
|
||||
}
|
||||
|
@ -684,6 +664,7 @@ void ShaderManager::add_default(Scene *scene)
|
|||
Shader *shader = scene->create_node<Shader>();
|
||||
shader->name = "default_background";
|
||||
shader->set_graph(graph);
|
||||
shader->reference();
|
||||
scene->default_background = shader;
|
||||
shader->tag_update(scene);
|
||||
}
|
||||
|
@ -695,6 +676,7 @@ void ShaderManager::add_default(Scene *scene)
|
|||
Shader *shader = scene->create_node<Shader>();
|
||||
shader->name = "default_empty";
|
||||
shader->set_graph(graph);
|
||||
shader->reference();
|
||||
scene->default_empty = shader;
|
||||
shader->tag_update(scene);
|
||||
}
|
||||
|
@ -735,7 +717,7 @@ void ShaderManager::get_requested_features(Scene *scene,
|
|||
requested_features->nodes_features = 0;
|
||||
for (int i = 0; i < scene->shaders.size(); i++) {
|
||||
Shader *shader = scene->shaders[i];
|
||||
if (!shader->used) {
|
||||
if (!shader->reference_count()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -132,7 +132,6 @@ class Shader : public Node {
|
|||
|
||||
/* determined before compiling */
|
||||
uint id;
|
||||
bool used;
|
||||
|
||||
#ifdef WITH_OSL
|
||||
/* osl shading state references */
|
||||
|
@ -187,10 +186,11 @@ class ShaderManager {
|
|||
}
|
||||
|
||||
/* device update */
|
||||
virtual void device_update(Device *device,
|
||||
DeviceScene *dscene,
|
||||
Scene *scene,
|
||||
Progress &progress) = 0;
|
||||
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
|
||||
virtual void device_update_specific(Device *device,
|
||||
DeviceScene *dscene,
|
||||
Scene *scene,
|
||||
Progress &progress) = 0;
|
||||
virtual void device_free(Device *device, DeviceScene *dscene, Scene *scene) = 0;
|
||||
|
||||
void device_update_common(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
|
||||
|
@ -208,7 +208,6 @@ class ShaderManager {
|
|||
static void add_default(Scene *scene);
|
||||
|
||||
/* Selective nodes compilation. */
|
||||
void update_shaders_used(Scene *scene);
|
||||
void get_requested_features(Scene *scene, DeviceRequestedFeatures *requested_features);
|
||||
|
||||
static void free_memory();
|
||||
|
|
|
@ -69,10 +69,10 @@ void SVMShaderManager::device_update_shader(Scene *scene,
|
|||
<< summary.full_report();
|
||||
}
|
||||
|
||||
void SVMShaderManager::device_update(Device *device,
|
||||
DeviceScene *dscene,
|
||||
Scene *scene,
|
||||
Progress &progress)
|
||||
void SVMShaderManager::device_update_specific(Device *device,
|
||||
DeviceScene *dscene,
|
||||
Scene *scene,
|
||||
Progress &progress)
|
||||
{
|
||||
if (!need_update())
|
||||
return;
|
||||
|
@ -776,7 +776,7 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
|
|||
add_node(NODE_ENTER_BUMP_EVAL, bump_state_offset);
|
||||
}
|
||||
|
||||
if (shader->used) {
|
||||
if (shader->reference_count()) {
|
||||
CompilerState state(graph);
|
||||
if (clin->link) {
|
||||
bool generate = false;
|
||||
|
|
|
@ -46,7 +46,10 @@ class SVMShaderManager : public ShaderManager {
|
|||
|
||||
void reset(Scene *scene);
|
||||
|
||||
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
|
||||
void device_update_specific(Device *device,
|
||||
DeviceScene *dscene,
|
||||
Scene *scene,
|
||||
Progress &progress) override;
|
||||
void device_free(Device *device, DeviceScene *dscene, Scene *scene);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -276,7 +276,7 @@ extern int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle);
|
|||
* wait (block) until the next event before returning.
|
||||
* \return Indication of the presence of events.
|
||||
*/
|
||||
extern int GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, int waitForEvent);
|
||||
extern bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent);
|
||||
|
||||
/**
|
||||
* Retrieves events from the queue and send them to the event consumers.
|
||||
|
|
|
@ -416,7 +416,10 @@ typedef enum {
|
|||
GHOST_kGrabNormal,
|
||||
/** Wrap the mouse location to prevent limiting screen bounds. */
|
||||
GHOST_kGrabWrap,
|
||||
/** Hide the mouse while grabbing and restore the original location on release (numbuts). */
|
||||
/**
|
||||
* Hide the mouse while grabbing and restore the original location on release
|
||||
* (used for number buttons and some other draggable UI elements).
|
||||
*/
|
||||
GHOST_kGrabHide,
|
||||
} GHOST_TGrabCursorMode;
|
||||
|
||||
|
|
|
@ -248,11 +248,11 @@ int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle)
|
|||
return (int)system->getFullScreen();
|
||||
}
|
||||
|
||||
int GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, int waitForEvent)
|
||||
bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent)
|
||||
{
|
||||
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
|
||||
|
||||
return (int)system->processEvents(waitForEvent ? true : false);
|
||||
return system->processEvents(waitForEvent);
|
||||
}
|
||||
|
||||
void GHOST_DispatchEvents(GHOST_SystemHandle systemhandle)
|
||||
|
|
|
@ -477,7 +477,7 @@ int main(int argc, char **argv)
|
|||
|
||||
/* Enter main loop */
|
||||
while (!sExitRequested) {
|
||||
if (!GHOST_ProcessEvents(shSystem, 0)) {
|
||||
if (!GHOST_ProcessEvents(shSystem, false)) {
|
||||
#ifdef WIN32
|
||||
/* If there were no events, be nice to other applications */
|
||||
Sleep(10);
|
||||
|
|
|
@ -926,7 +926,7 @@ void multitestapp_exit(MultiTestApp *app)
|
|||
void multitestapp_run(MultiTestApp *app)
|
||||
{
|
||||
while (!app->exit) {
|
||||
GHOST_ProcessEvents(app->sys, 1);
|
||||
GHOST_ProcessEvents(app->sys, true);
|
||||
GHOST_DispatchEvents(app->sys);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,6 +119,7 @@ class SpellChecker:
|
|||
"dirtree",
|
||||
"editcurve",
|
||||
"editmesh",
|
||||
"faceforward",
|
||||
"filebrowser",
|
||||
"filelist",
|
||||
"filename", "filenames",
|
||||
|
@ -200,6 +201,7 @@ class SpellChecker:
|
|||
"selfcollision",
|
||||
"shadowbuffer", "shadowbuffers",
|
||||
"singletexture",
|
||||
"softbox",
|
||||
"spellcheck", "spellchecking",
|
||||
"startup",
|
||||
"stateful",
|
||||
|
|
|
@ -657,16 +657,14 @@ class Gizmo(StructRNA):
|
|||
use_blend = color[3] < 1.0
|
||||
|
||||
if use_blend:
|
||||
# TODO: wrap GPU_blend from GPU state.
|
||||
from bgl import glEnable, glDisable, GL_BLEND
|
||||
glEnable(GL_BLEND)
|
||||
gpu.state.blend_set('ALPHA')
|
||||
|
||||
with gpu.matrix.push_pop():
|
||||
gpu.matrix.multiply_matrix(matrix)
|
||||
batch.draw()
|
||||
|
||||
if use_blend:
|
||||
glDisable(GL_BLEND)
|
||||
gpu.state.blend_set('NONE')
|
||||
|
||||
@staticmethod
|
||||
def new_custom_shape(type, verts):
|
||||
|
|
|
@ -57,12 +57,12 @@ def draw_circle_2d(position, color, radius, segments=32):
|
|||
batch.draw()
|
||||
|
||||
|
||||
def draw_texture_2d(texture_id, position, width, height):
|
||||
def draw_texture_2d(texture, position, width, height):
|
||||
"""
|
||||
Draw a 2d texture.
|
||||
|
||||
:arg texture_id: OpenGL id of the texture (e.g. :class:`bpy.types.Image.bindcode`).
|
||||
:type texture_id: int
|
||||
:arg texture: GPUTexture to draw (e.g. gpu.texture.from_image(image) for :class:`bpy.types.Image`).
|
||||
:type texture: :class:`gpu.types.GPUTexture`
|
||||
:arg position: Position of the lower left corner.
|
||||
:type position: 2D Vector
|
||||
:arg width: Width of the image when drawn (not necessarily
|
||||
|
@ -72,7 +72,6 @@ def draw_texture_2d(texture_id, position, width, height):
|
|||
:type height: float
|
||||
"""
|
||||
import gpu
|
||||
import bgl
|
||||
from . batch import batch_for_shader
|
||||
|
||||
coords = ((0, 0), (1, 0), (1, 1), (0, 1))
|
||||
|
@ -83,14 +82,20 @@ def draw_texture_2d(texture_id, position, width, height):
|
|||
{"pos": coords, "texCoord": coords},
|
||||
)
|
||||
|
||||
bgl.glActiveTexture(bgl.GL_TEXTURE0)
|
||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, texture_id)
|
||||
|
||||
with gpu.matrix.push_pop():
|
||||
gpu.matrix.translate(position)
|
||||
gpu.matrix.scale((width, height))
|
||||
|
||||
shader = gpu.shader.from_builtin('2D_IMAGE')
|
||||
shader.bind()
|
||||
shader.uniform_int("image", 0)
|
||||
|
||||
if isinstance(texture, int):
|
||||
# Call the legacy bgl to not break the existing API
|
||||
import bgl
|
||||
bgl.glActiveTexture(bgl.GL_TEXTURE0)
|
||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, texture)
|
||||
shader.uniform_int("image", 0)
|
||||
else:
|
||||
shader.uniform_sampler("image", texture)
|
||||
|
||||
batch.draw(shader)
|
||||
|
|
|
@ -2115,4 +2115,9 @@ url_manual_mapping = (
|
|||
("bpy.ops.ed*", "interface/undo_redo.html#bpy-ops-ed"),
|
||||
("bpy.ops.ui*", "interface/index.html#bpy-ops-ui"),
|
||||
("bpy.ops.wm*", "interface/index.html#bpy-ops-wm"),
|
||||
("bpy.ops.file.pack_all", "blend/packed_data#pack-resources"),
|
||||
("bpy.ops.file.unpack_all", "blend/packed_data#unpack-resources"),
|
||||
("bpy.ops.file.autopack_toggle", "blend/packed_data#automatically-pack-resources"),
|
||||
("bpy.ops.file.pack_libraries", "blend/packed_data#pack-linked-libraries"),
|
||||
("bpy.ops.file.unpack_libraries", "blend/packed_data#unpack-linked-libraries"),
|
||||
)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
def write_sysinfo(filepath):
|
||||
import sys
|
||||
import platform
|
||||
|
||||
import subprocess
|
||||
|
||||
|
@ -63,7 +64,7 @@ def write_sysinfo(filepath):
|
|||
))
|
||||
|
||||
output.write("build date: %s, %s\n" % (prepr(bpy.app.build_date), prepr(bpy.app.build_time)))
|
||||
output.write("platform: %s\n" % prepr(bpy.app.build_platform))
|
||||
output.write("platform: %s\n" % prepr(platform.platform()))
|
||||
output.write("binary path: %s\n" % prepr(bpy.app.binary_path))
|
||||
output.write("build cflags: %s\n" % prepr(bpy.app.build_cflags))
|
||||
output.write("build cxxflags: %s\n" % prepr(bpy.app.build_cxxflags))
|
||||
|
|
|
@ -1289,7 +1289,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
|
|||
row = layout.row(align=True)
|
||||
row.prop(gp_settings, "fill_factor")
|
||||
row = layout.row(align=True)
|
||||
row.prop(gp_settings, "fill_leak", text="Leak Size")
|
||||
row.prop(gp_settings, "dilate_pixels")
|
||||
row = layout.row(align=True)
|
||||
row.prop(brush, "size", text="Thickness")
|
||||
layout.use_property_split = use_property_split_prev
|
||||
|
|
|
@ -504,8 +504,6 @@ class TOPBAR_MT_file_external_data(Menu):
|
|||
icon = 'CHECKBOX_HLT' if bpy.data.use_autopack else 'CHECKBOX_DEHLT'
|
||||
layout.operator("file.autopack_toggle", icon=icon)
|
||||
|
||||
layout.separator()
|
||||
|
||||
pack_all = layout.row()
|
||||
pack_all.operator("file.pack_all")
|
||||
pack_all.active = not bpy.data.use_autopack
|
||||
|
@ -516,8 +514,16 @@ class TOPBAR_MT_file_external_data(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("file.pack_libraries")
|
||||
layout.operator("file.unpack_libraries")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("file.make_paths_relative")
|
||||
layout.operator("file.make_paths_absolute")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("file.report_missing_files")
|
||||
layout.operator("file.find_missing_files")
|
||||
|
||||
|
|
|
@ -938,7 +938,8 @@ class VIEW3D_MT_transform_base:
|
|||
layout.operator("transform.bend", text="Bend")
|
||||
layout.operator("transform.push_pull", text="Push/Pull")
|
||||
|
||||
if context.mode != 'OBJECT':
|
||||
if context.mode in {'EDIT_MESH', 'EDIT_ARMATURE', 'EDIT_SURFACE', 'EDIT_CURVE',
|
||||
'EDIT_LATTICE', 'EDIT_METABALL'}:
|
||||
layout.operator("transform.vertex_warp", text="Warp")
|
||||
layout.operator_context = 'EXEC_REGION_WIN'
|
||||
layout.operator("transform.vertex_random", text="Randomize").offset = 0.1
|
||||
|
|
|
@ -1537,6 +1537,9 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel):
|
|||
row.prop(gp_settings, "extend_stroke_factor")
|
||||
row.prop(gp_settings, "show_fill_extend", text="", icon='GRID')
|
||||
|
||||
col.separator()
|
||||
col.prop(gp_settings, "fill_leak", text="Leak Size")
|
||||
|
||||
col.separator()
|
||||
col.prop(gp_settings, "fill_simplify_level", text="Simplify")
|
||||
if gp_settings.fill_draw_mode != 'STROKE':
|
||||
|
|
|
@ -505,6 +505,9 @@ geometry_node_categories = [
|
|||
NodeItem("ShaderNodeSeparateRGB"),
|
||||
NodeItem("ShaderNodeCombineRGB"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_CURVE", "Curve", items=[
|
||||
NodeItem("GeometryNodeCurveToMesh"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[
|
||||
NodeItem("GeometryNodeBoundBox"),
|
||||
NodeItem("GeometryNodeTransform"),
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import bpy
|
||||
import bgl
|
||||
import blf
|
||||
import gpu
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
|
@ -17,16 +16,16 @@ def draw_callback_px(self, context):
|
|||
|
||||
# 50% alpha, 2 pixel width line
|
||||
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
||||
bgl.glEnable(bgl.GL_BLEND)
|
||||
bgl.glLineWidth(2)
|
||||
gpu.state.blend_set('ALPHA')
|
||||
gpu.state.line_width_set(2.0)
|
||||
batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": self.mouse_path})
|
||||
shader.bind()
|
||||
shader.uniform_float("color", (0.0, 0.0, 0.0, 0.5))
|
||||
batch.draw(shader)
|
||||
|
||||
# restore opengl defaults
|
||||
bgl.glLineWidth(1)
|
||||
bgl.glDisable(bgl.GL_BLEND)
|
||||
gpu.state.line_width_set(1.0)
|
||||
gpu.state.blend_set('NONE')
|
||||
|
||||
|
||||
class ModalDrawOperator(bpy.types.Operator):
|
||||
|
|
|
@ -140,7 +140,7 @@ void BKE_pose_channel_free_bbone_cache(struct bPoseChannel_Runtime *runtime);
|
|||
void BKE_pose_channels_free(struct bPose *pose);
|
||||
void BKE_pose_channels_free_ex(struct bPose *pose, bool do_id_user);
|
||||
|
||||
void BKE_pose_channels_hash_make(struct bPose *pose);
|
||||
void BKE_pose_channels_hash_ensure(struct bPose *pose);
|
||||
void BKE_pose_channels_hash_free(struct bPose *pose);
|
||||
|
||||
void BKE_pose_channels_remove(struct Object *ob,
|
||||
|
@ -161,7 +161,7 @@ void BKE_pose_channel_session_uuid_generate(struct bPoseChannel *pchan);
|
|||
struct bPoseChannel *BKE_pose_channel_find_name(const struct bPose *pose, const char *name);
|
||||
struct bPoseChannel *BKE_pose_channel_active(struct Object *ob);
|
||||
struct bPoseChannel *BKE_pose_channel_active_or_first_selected(struct Object *ob);
|
||||
struct bPoseChannel *BKE_pose_channel_verify(struct bPose *pose, const char *name);
|
||||
struct bPoseChannel *BKE_pose_channel_ensure(struct bPose *pose, const char *name);
|
||||
struct bPoseChannel *BKE_pose_channel_get_mirrored(const struct bPose *pose, const char *name);
|
||||
|
||||
void BKE_pose_check_uuids_unique_and_report(const struct bPose *pose);
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_float2.hh"
|
||||
|
@ -130,6 +132,48 @@ inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1,
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Mix two values of the same type.
|
||||
*
|
||||
* This is just basic linear interpolation.
|
||||
* \{ */
|
||||
|
||||
template<typename T> T mix2(const float factor, const T &a, const T &b);
|
||||
|
||||
template<> inline bool mix2(const float factor, const bool &a, const bool &b)
|
||||
{
|
||||
return ((1.0f - factor) * a + factor * b) >= 0.5f;
|
||||
}
|
||||
|
||||
template<> inline int mix2(const float factor, const int &a, const int &b)
|
||||
{
|
||||
return static_cast<int>((1.0f - factor) * a + factor * b);
|
||||
}
|
||||
|
||||
template<> inline float mix2(const float factor, const float &a, const float &b)
|
||||
{
|
||||
return (1.0f - factor) * a + factor * b;
|
||||
}
|
||||
|
||||
template<> inline float2 mix2(const float factor, const float2 &a, const float2 &b)
|
||||
{
|
||||
return float2::interpolate(a, b, factor);
|
||||
}
|
||||
|
||||
template<> inline float3 mix2(const float factor, const float3 &a, const float3 &b)
|
||||
{
|
||||
return float3::interpolate(a, b, factor);
|
||||
}
|
||||
|
||||
template<> inline Color4f mix2(const float factor, const Color4f &a, const Color4f &b)
|
||||
{
|
||||
Color4f result;
|
||||
interp_v4_v4v4(result, a, b, factor);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Mix a dynamic amount of values with weights for many elements.
|
||||
*
|
||||
|
|
|
@ -39,7 +39,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 0
|
||||
#define BLENDER_FILE_SUBVERSION 1
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and show a warning if the file
|
||||
|
|
|
@ -36,25 +36,13 @@ typedef enum GeometryComponentType {
|
|||
GEO_COMPONENT_TYPE_POINT_CLOUD = 1,
|
||||
GEO_COMPONENT_TYPE_INSTANCES = 2,
|
||||
GEO_COMPONENT_TYPE_VOLUME = 3,
|
||||
GEO_COMPONENT_TYPE_CURVE = 4,
|
||||
} GeometryComponentType;
|
||||
|
||||
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
|
||||
|
||||
bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set);
|
||||
|
||||
typedef enum InstancedDataType {
|
||||
INSTANCE_DATA_TYPE_OBJECT = 0,
|
||||
INSTANCE_DATA_TYPE_COLLECTION = 1,
|
||||
} InstancedDataType;
|
||||
|
||||
typedef struct InstancedData {
|
||||
InstancedDataType type;
|
||||
union {
|
||||
struct Object *object;
|
||||
struct Collection *collection;
|
||||
} data;
|
||||
} InstancedData;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "BLI_map.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_user_counter.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_geometry_set.h"
|
||||
|
@ -39,6 +40,7 @@ struct Mesh;
|
|||
struct Object;
|
||||
struct PointCloud;
|
||||
struct Volume;
|
||||
class CurveEval;
|
||||
|
||||
enum class GeometryOwnershipType {
|
||||
/* The geometry is owned. This implies that it can be changed. */
|
||||
|
@ -363,18 +365,25 @@ struct GeometrySet {
|
|||
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
static GeometrySet create_with_pointcloud(
|
||||
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
static GeometrySet create_with_curve(
|
||||
CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
|
||||
/* Utility methods for access. */
|
||||
bool has_mesh() const;
|
||||
bool has_pointcloud() const;
|
||||
bool has_instances() const;
|
||||
bool has_volume() const;
|
||||
bool has_curve() const;
|
||||
|
||||
const Mesh *get_mesh_for_read() const;
|
||||
const PointCloud *get_pointcloud_for_read() const;
|
||||
const Volume *get_volume_for_read() const;
|
||||
const CurveEval *get_curve_for_read() const;
|
||||
|
||||
Mesh *get_mesh_for_write();
|
||||
PointCloud *get_pointcloud_for_write();
|
||||
Volume *get_volume_for_write();
|
||||
CurveEval *get_curve_for_write();
|
||||
|
||||
/* Utility methods for replacement. */
|
||||
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
|
@ -382,6 +391,8 @@ struct GeometrySet {
|
|||
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
void replace_volume(Volume *volume,
|
||||
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
void replace_curve(CurveEval *curve,
|
||||
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
};
|
||||
|
||||
/** A geometry component that can store a mesh. */
|
||||
|
@ -463,12 +474,113 @@ class PointCloudComponent : public GeometryComponent {
|
|||
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
|
||||
};
|
||||
|
||||
/** A geometry component that stores curve data, in other words, a group of splines. */
|
||||
class CurveComponent : public GeometryComponent {
|
||||
private:
|
||||
CurveEval *curve_ = nullptr;
|
||||
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
|
||||
|
||||
public:
|
||||
CurveComponent();
|
||||
~CurveComponent();
|
||||
GeometryComponent *copy() const override;
|
||||
|
||||
void clear();
|
||||
bool has_curve() const;
|
||||
void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
|
||||
CurveEval *release();
|
||||
|
||||
const CurveEval *get_for_read() const;
|
||||
CurveEval *get_for_write();
|
||||
|
||||
int attribute_domain_size(const AttributeDomain domain) const final;
|
||||
|
||||
bool is_empty() const final;
|
||||
|
||||
bool owns_direct_data() const override;
|
||||
void ensure_owns_direct_data() override;
|
||||
|
||||
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
|
||||
|
||||
private:
|
||||
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
|
||||
};
|
||||
|
||||
class InstanceReference {
|
||||
public:
|
||||
enum class Type {
|
||||
/**
|
||||
* An empty instance. This allows an `InstanceReference` to be default constructed without
|
||||
* being in an invalid state. There might also be other use cases that we haven't explored much
|
||||
* yet (such as changing the instance later on, and "disabling" some instances).
|
||||
*/
|
||||
None,
|
||||
Object,
|
||||
Collection,
|
||||
};
|
||||
|
||||
private:
|
||||
Type type_ = Type::None;
|
||||
/** Depending on the type this is either null, an Object or Collection pointer. */
|
||||
void *data_ = nullptr;
|
||||
|
||||
public:
|
||||
InstanceReference() = default;
|
||||
|
||||
InstanceReference(Object &object) : type_(Type::Object), data_(&object)
|
||||
{
|
||||
}
|
||||
|
||||
InstanceReference(Collection &collection) : type_(Type::Collection), data_(&collection)
|
||||
{
|
||||
}
|
||||
|
||||
Type type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
Object &object() const
|
||||
{
|
||||
BLI_assert(type_ == Type::Object);
|
||||
return *(Object *)data_;
|
||||
}
|
||||
|
||||
Collection &collection() const
|
||||
{
|
||||
BLI_assert(type_ == Type::Collection);
|
||||
return *(Collection *)data_;
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
return blender::get_default_hash(data_);
|
||||
}
|
||||
|
||||
friend bool operator==(const InstanceReference &a, const InstanceReference &b)
|
||||
{
|
||||
return a.data_ == b.data_;
|
||||
}
|
||||
};
|
||||
|
||||
/** A geometry component that stores instances. */
|
||||
class InstancesComponent : public GeometryComponent {
|
||||
private:
|
||||
blender::Vector<blender::float4x4> transforms_;
|
||||
blender::Vector<int> ids_;
|
||||
blender::Vector<InstancedData> instanced_data_;
|
||||
/**
|
||||
* Indexed set containing information about the data that is instanced.
|
||||
* Actual instances store an index ("handle") into this set.
|
||||
*/
|
||||
blender::VectorSet<InstanceReference> references_;
|
||||
|
||||
/** Index into `references_`. Determines what data is instanced. */
|
||||
blender::Vector<int> instance_reference_handles_;
|
||||
/** Transformation of the instances. */
|
||||
blender::Vector<blender::float4x4> instance_transforms_;
|
||||
/**
|
||||
* IDs of the instances. They are used for consistency over multiple frames for things like
|
||||
* motion blur.
|
||||
*/
|
||||
blender::Vector<int> instance_ids_;
|
||||
|
||||
/* These almost unique ids are generated based on `ids_`, which might not contain unique ids at
|
||||
* all. They are *almost* unique, because under certain very unlikely circumstances, they are not
|
||||
|
@ -483,14 +595,20 @@ class InstancesComponent : public GeometryComponent {
|
|||
GeometryComponent *copy() const override;
|
||||
|
||||
void clear();
|
||||
void add_instance(Object *object, blender::float4x4 transform, const int id = -1);
|
||||
void add_instance(Collection *collection, blender::float4x4 transform, const int id = -1);
|
||||
void add_instance(InstancedData data, blender::float4x4 transform, const int id = -1);
|
||||
|
||||
blender::Span<InstancedData> instanced_data() const;
|
||||
blender::Span<blender::float4x4> transforms() const;
|
||||
blender::Span<int> ids() const;
|
||||
blender::MutableSpan<blender::float4x4> transforms();
|
||||
void reserve(int min_capacity);
|
||||
|
||||
int add_reference(InstanceReference reference);
|
||||
void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1);
|
||||
|
||||
blender::Span<InstanceReference> references() const;
|
||||
|
||||
blender::Span<int> instance_reference_handles() const;
|
||||
blender::MutableSpan<blender::float4x4> instance_transforms();
|
||||
blender::Span<blender::float4x4> instance_transforms() const;
|
||||
blender::MutableSpan<int> instance_ids();
|
||||
blender::Span<int> instance_ids() const;
|
||||
|
||||
int instances_amount() const;
|
||||
|
||||
blender::Span<int> almost_unique_ids() const;
|
||||
|
|
|
@ -70,12 +70,15 @@ enum {
|
|||
/** That ID is used as library override's reference by its owner. */
|
||||
IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE = (1 << 5),
|
||||
|
||||
/** That ID pointer is not overridable. */
|
||||
IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE = (1 << 6),
|
||||
|
||||
/**
|
||||
* Indicates that this is an internal runtime ID pointer, like e.g. `ID.newid` or `ID.original`.
|
||||
* \note Those should be ignored in most cases, and won't be processed/generated anyway unless
|
||||
* `IDWALK_DO_INTERNAL_RUNTIME_POINTERS` option is enabled.
|
||||
*/
|
||||
IDWALK_CB_INTERNAL = (1 << 6),
|
||||
IDWALK_CB_INTERNAL = (1 << 7),
|
||||
|
||||
/**
|
||||
* This ID usage is fully refcounted.
|
||||
|
|
|
@ -578,9 +578,9 @@ void BKE_mesh_polygons_flip(struct MPoly *mpoly,
|
|||
struct CustomData *ldata,
|
||||
int totpoly);
|
||||
|
||||
/* merge verts */
|
||||
/* Enum for merge_mode of CDDM_merge_verts.
|
||||
* Refer to mesh.c for details. */
|
||||
/* Merge verts. */
|
||||
/* Enum for merge_mode of #BKE_mesh_merge_verts.
|
||||
* Refer to mesh_merge.c for details. */
|
||||
enum {
|
||||
MESH_MERGE_VERTS_DUMP_IF_MAPPED,
|
||||
MESH_MERGE_VERTS_DUMP_IF_EQUAL,
|
||||
|
|
|
@ -58,7 +58,14 @@ struct NlaTrack *BKE_nlatrack_copy(struct Main *bmain,
|
|||
struct NlaTrack *nlt,
|
||||
const bool use_same_actions,
|
||||
const int flag);
|
||||
void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, ListBase *src, const int flag);
|
||||
void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, const ListBase *src, const int flag);
|
||||
|
||||
/* Copy NLA tracks from #adt_source to #adt_dest, and update the active track/strip pointers to
|
||||
* point at those copies. */
|
||||
void BKE_nla_tracks_copy_from_adt(struct Main *bmain,
|
||||
struct AnimData *adt_dest,
|
||||
const struct AnimData *adt_source,
|
||||
int flag);
|
||||
|
||||
struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt,
|
||||
struct NlaTrack *prev,
|
||||
|
|
|
@ -410,6 +410,9 @@ typedef struct bNodeTreeType {
|
|||
|
||||
void (*node_add_init)(struct bNodeTree *ntree, struct bNode *bnode);
|
||||
|
||||
/* Check if the socket type is valid for this tree type. */
|
||||
bool (*valid_socket_type)(enum eNodeSocketDatatype socket_type, struct bNodeTreeType *ntreetype);
|
||||
|
||||
/* RNA integration */
|
||||
ExtensionRNA rna_ext;
|
||||
} bNodeTreeType;
|
||||
|
@ -1415,6 +1418,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
|||
#define GEO_NODE_BOUNDING_BOX 1042
|
||||
#define GEO_NODE_SWITCH 1043
|
||||
#define GEO_NODE_ATTRIBUTE_TRANSFER 1044
|
||||
#define GEO_NODE_CURVE_TO_MESH 1045
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -376,6 +376,7 @@ struct MovieClip *BKE_object_movieclip_get(struct Scene *scene,
|
|||
|
||||
void BKE_object_runtime_reset(struct Object *object);
|
||||
void BKE_object_runtime_reset_on_copy(struct Object *object, const int flag);
|
||||
void BKE_object_runtime_free_data(struct Object *object);
|
||||
|
||||
void BKE_object_batch_cache_dirty_tag(struct Object *ob);
|
||||
|
||||
|
|
|
@ -0,0 +1,478 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "FN_generic_virtual_array.hh"
|
||||
|
||||
#include "BLI_float3.hh"
|
||||
#include "BLI_float4x4.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
|
||||
struct Curve;
|
||||
|
||||
class Spline;
|
||||
using SplinePtr = std::unique_ptr<Spline>;
|
||||
|
||||
/**
|
||||
* A spline is an abstraction of a single branch-less curve section, its evaluation methods,
|
||||
* and data. The spline data itself is just control points and a set of attributes by the set
|
||||
* of "evaluated" data is often used instead.
|
||||
*
|
||||
* Any derived class of Spline has to manage two things:
|
||||
* 1. Interpolating arbitrary attribute data from the control points to evaluated points.
|
||||
* 2. Evaluating the positions based on the stored control point data.
|
||||
*
|
||||
* Beyond that, everything is the base class's responsibility, with minor exceptions. Further
|
||||
* evaluation happens in a layer on top of the evaluated points generated by the derived types.
|
||||
*
|
||||
* There are a few methods to evaluate a spline:
|
||||
* 1. #evaluated_positions and #interpolate_to_evaluated_points give data at the initial
|
||||
* evaluated points, depending on the resolution.
|
||||
* 2. #lookup_evaluated_factor and #lookup_evaluated_factor are meant for one-off lookups
|
||||
* along the length of a curve.
|
||||
*
|
||||
* Commonly used evaluated data is stored in caches on the spline itself so that operations on
|
||||
* splines don't need to worry about taking ownership of evaluated data when they don't need to.
|
||||
*/
|
||||
class Spline {
|
||||
public:
|
||||
enum class Type {
|
||||
Bezier,
|
||||
NURBS,
|
||||
Poly,
|
||||
};
|
||||
|
||||
protected:
|
||||
Type type_;
|
||||
bool is_cyclic_ = false;
|
||||
|
||||
public:
|
||||
enum NormalCalculationMode {
|
||||
ZUp,
|
||||
Minimum,
|
||||
Tangent,
|
||||
};
|
||||
/* Only #Zup is supported at the moment. */
|
||||
NormalCalculationMode normal_mode;
|
||||
|
||||
protected:
|
||||
/** Direction of the spline at each evaluated point. */
|
||||
mutable blender::Vector<blender::float3> evaluated_tangents_cache_;
|
||||
mutable std::mutex tangent_cache_mutex_;
|
||||
mutable bool tangent_cache_dirty_ = true;
|
||||
|
||||
/** Normal direction vectors for each evaluated point. */
|
||||
mutable blender::Vector<blender::float3> evaluated_normals_cache_;
|
||||
mutable std::mutex normal_cache_mutex_;
|
||||
mutable bool normal_cache_dirty_ = true;
|
||||
|
||||
/** Accumulated lengths along the evaluated points. */
|
||||
mutable blender::Vector<float> evaluated_lengths_cache_;
|
||||
mutable std::mutex length_cache_mutex_;
|
||||
mutable bool length_cache_dirty_ = true;
|
||||
|
||||
public:
|
||||
virtual ~Spline() = default;
|
||||
Spline(const Type type) : type_(type)
|
||||
{
|
||||
}
|
||||
Spline(Spline &other)
|
||||
: type_(other.type_), is_cyclic_(other.is_cyclic_), normal_mode(other.normal_mode)
|
||||
{
|
||||
}
|
||||
|
||||
virtual SplinePtr copy() const = 0;
|
||||
|
||||
Spline::Type type() const;
|
||||
|
||||
/** Return the number of control points. */
|
||||
virtual int size() const = 0;
|
||||
int segments_size() const;
|
||||
bool is_cyclic() const;
|
||||
void set_cyclic(const bool value);
|
||||
|
||||
virtual blender::MutableSpan<blender::float3> positions() = 0;
|
||||
virtual blender::Span<blender::float3> positions() const = 0;
|
||||
virtual blender::MutableSpan<float> radii() = 0;
|
||||
virtual blender::Span<float> radii() const = 0;
|
||||
virtual blender::MutableSpan<float> tilts() = 0;
|
||||
virtual blender::Span<float> tilts() const = 0;
|
||||
|
||||
virtual void translate(const blender::float3 &translation);
|
||||
virtual void transform(const blender::float4x4 &matrix);
|
||||
|
||||
/**
|
||||
* Mark all caches for re-computation. This must be called after any operation that would
|
||||
* change the generated positions, tangents, normals, mapping, etc. of the evaluated points.
|
||||
*/
|
||||
virtual void mark_cache_invalid() = 0;
|
||||
virtual int evaluated_points_size() const = 0;
|
||||
int evaluated_edges_size() const;
|
||||
|
||||
float length() const;
|
||||
|
||||
virtual blender::Span<blender::float3> evaluated_positions() const = 0;
|
||||
|
||||
blender::Span<float> evaluated_lengths() const;
|
||||
blender::Span<blender::float3> evaluated_tangents() const;
|
||||
blender::Span<blender::float3> evaluated_normals() const;
|
||||
|
||||
void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
|
||||
|
||||
struct LookupResult {
|
||||
/**
|
||||
* The index of the evaluated point before the result location. In other words, the index of
|
||||
* the edge that the result lies on. If the sampled factor/length is the very end of the
|
||||
* spline, this will be the second to last index, if it's the very beginning, this will be 0.
|
||||
*/
|
||||
int evaluated_index;
|
||||
/**
|
||||
* The index of the evaluated point after the result location, accounting for wrapping when
|
||||
* the spline is cyclic. If the sampled factor/length is the very end of the spline, this will
|
||||
* be the last index (#evaluated_points_size - 1).
|
||||
*/
|
||||
int next_evaluated_index;
|
||||
/**
|
||||
* The portion of the way from the evaluated point at #evaluated_index to the next point.
|
||||
* If the sampled factor/length is the very end of the spline, this will be the 1.0f
|
||||
*/
|
||||
float factor;
|
||||
};
|
||||
LookupResult lookup_evaluated_factor(const float factor) const;
|
||||
LookupResult lookup_evaluated_length(const float length) const;
|
||||
|
||||
/**
|
||||
* Interpolate a virtual array of data with the size of the number of control points to the
|
||||
* evaluated points. For poly splines, the lifetime of the returned virtual array must not
|
||||
* exceed the lifetime of the input data.
|
||||
*/
|
||||
virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const = 0;
|
||||
|
||||
protected:
|
||||
virtual void correct_end_tangents() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Bézier spline is made up of a many curve segments, possibly achieving continuity of curvature
|
||||
* by constraining the alignment of curve handles. Evaluation stores the positions and a map of
|
||||
* factors and indices in a list of floats, which is then used to interpolate any other data.
|
||||
*/
|
||||
class BezierSpline final : public Spline {
|
||||
public:
|
||||
enum class HandleType {
|
||||
/** The handle can be moved anywhere, and doesn't influence the point's other handle. */
|
||||
Free,
|
||||
/** The location is automatically calculated to be smooth. */
|
||||
Auto,
|
||||
/** The location is calculated to point to the next/previous control point. */
|
||||
Vector,
|
||||
/** The location is constrained to point in the opposite direction as the other handle. */
|
||||
Align,
|
||||
};
|
||||
|
||||
private:
|
||||
blender::Vector<HandleType> handle_types_left_;
|
||||
blender::Vector<blender::float3> handle_positions_left_;
|
||||
blender::Vector<blender::float3> positions_;
|
||||
blender::Vector<HandleType> handle_types_right_;
|
||||
blender::Vector<blender::float3> handle_positions_right_;
|
||||
blender::Vector<float> radii_;
|
||||
blender::Vector<float> tilts_;
|
||||
int resolution_;
|
||||
|
||||
/** Start index in evaluated points array for every control point. */
|
||||
mutable blender::Vector<int> offset_cache_;
|
||||
mutable std::mutex offset_cache_mutex_;
|
||||
mutable bool offset_cache_dirty_ = true;
|
||||
|
||||
/** Cache of evaluated positions. */
|
||||
mutable blender::Vector<blender::float3> evaluated_position_cache_;
|
||||
mutable std::mutex position_cache_mutex_;
|
||||
mutable bool position_cache_dirty_ = true;
|
||||
|
||||
/** Cache of "index factors" based calculated from the evaluated positions. */
|
||||
mutable blender::Vector<float> evaluated_mapping_cache_;
|
||||
mutable std::mutex mapping_cache_mutex_;
|
||||
mutable bool mapping_cache_dirty_ = true;
|
||||
|
||||
public:
|
||||
virtual SplinePtr copy() const final;
|
||||
BezierSpline() : Spline(Type::Bezier)
|
||||
{
|
||||
}
|
||||
BezierSpline(const BezierSpline &other)
|
||||
: Spline((Spline &)other),
|
||||
handle_types_left_(other.handle_types_left_),
|
||||
handle_positions_left_(other.handle_positions_left_),
|
||||
positions_(other.positions_),
|
||||
handle_types_right_(other.handle_types_right_),
|
||||
handle_positions_right_(other.handle_positions_right_),
|
||||
radii_(other.radii_),
|
||||
tilts_(other.tilts_),
|
||||
resolution_(other.resolution_)
|
||||
{
|
||||
}
|
||||
|
||||
int size() const final;
|
||||
int resolution() const;
|
||||
void set_resolution(const int value);
|
||||
|
||||
void add_point(const blender::float3 position,
|
||||
const HandleType handle_type_start,
|
||||
const blender::float3 handle_position_start,
|
||||
const HandleType handle_type_end,
|
||||
const blender::float3 handle_position_end,
|
||||
const float radius,
|
||||
const float tilt);
|
||||
|
||||
blender::MutableSpan<blender::float3> positions() final;
|
||||
blender::Span<blender::float3> positions() const final;
|
||||
blender::MutableSpan<float> radii() final;
|
||||
blender::Span<float> radii() const final;
|
||||
blender::MutableSpan<float> tilts() final;
|
||||
blender::Span<float> tilts() const final;
|
||||
blender::Span<HandleType> handle_types_left() const;
|
||||
blender::MutableSpan<HandleType> handle_types_left();
|
||||
blender::Span<blender::float3> handle_positions_left() const;
|
||||
blender::MutableSpan<blender::float3> handle_positions_left();
|
||||
blender::Span<HandleType> handle_types_right() const;
|
||||
blender::MutableSpan<HandleType> handle_types_right();
|
||||
blender::Span<blender::float3> handle_positions_right() const;
|
||||
blender::MutableSpan<blender::float3> handle_positions_right();
|
||||
|
||||
void translate(const blender::float3 &translation) override;
|
||||
void transform(const blender::float4x4 &matrix) override;
|
||||
|
||||
bool point_is_sharp(const int index) const;
|
||||
|
||||
void mark_cache_invalid() final;
|
||||
int evaluated_points_size() const final;
|
||||
|
||||
blender::Span<int> control_point_offsets() const;
|
||||
blender::Span<float> evaluated_mappings() const;
|
||||
blender::Span<blender::float3> evaluated_positions() const final;
|
||||
struct InterpolationData {
|
||||
int control_point_index;
|
||||
int next_control_point_index;
|
||||
/**
|
||||
* Linear interpolation weight between the two indices, from 0 to 1.
|
||||
* Higher means next control point.
|
||||
*/
|
||||
float factor;
|
||||
};
|
||||
InterpolationData interpolation_data_from_index_factor(const float index_factor) const;
|
||||
|
||||
virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const;
|
||||
|
||||
private:
|
||||
void correct_end_tangents() const final;
|
||||
bool segment_is_vector(const int start_index) const;
|
||||
void evaluate_bezier_segment(const int index,
|
||||
const int next_index,
|
||||
blender::MutableSpan<blender::float3> positions) const;
|
||||
blender::Array<int> evaluated_point_offsets() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data for Non-Uniform Rational B-Splines. The mapping from control points to evaluated points is
|
||||
* influenced by a vector of knots, weights for each point, and the order of the spline. Every
|
||||
* mapping of data to evaluated points is handled the same way, but the positions are cached in
|
||||
* the spline.
|
||||
*/
|
||||
class NURBSpline final : public Spline {
|
||||
public:
|
||||
enum class KnotsMode {
|
||||
Normal,
|
||||
EndPoint,
|
||||
Bezier,
|
||||
};
|
||||
KnotsMode knots_mode;
|
||||
|
||||
struct BasisCache {
|
||||
/** The influence at each control point `i + #start_index`. */
|
||||
blender::Vector<float> weights;
|
||||
/**
|
||||
* An offset for the start of #weights: the first control point index with a non-zero weight.
|
||||
*/
|
||||
int start_index;
|
||||
};
|
||||
|
||||
private:
|
||||
blender::Vector<blender::float3> positions_;
|
||||
blender::Vector<float> radii_;
|
||||
blender::Vector<float> tilts_;
|
||||
blender::Vector<float> weights_;
|
||||
int resolution_;
|
||||
/**
|
||||
* Defines the number of nearby control points that influence a given evaluated point. Higher
|
||||
* orders give smoother results. The number of control points must be greater than or equal to
|
||||
* this value.
|
||||
*/
|
||||
uint8_t order_;
|
||||
|
||||
/**
|
||||
* Determines where and how the control points affect the evaluated points. The length should
|
||||
* always be the value returned by #knots_size(), and each value should be greater than or equal
|
||||
* to the previous. Only invalidated when a point is added or removed.
|
||||
*/
|
||||
mutable blender::Vector<float> knots_;
|
||||
mutable std::mutex knots_mutex_;
|
||||
mutable bool knots_dirty_ = true;
|
||||
|
||||
/** Cache of control point influences on each evaluated point. */
|
||||
mutable blender::Vector<BasisCache> basis_cache_;
|
||||
mutable std::mutex basis_cache_mutex_;
|
||||
mutable bool basis_cache_dirty_ = true;
|
||||
|
||||
/**
|
||||
* Cache of position data calculated from the basis cache. Though it is interpolated
|
||||
* in the same way as any other attribute, it is stored to save unnecessary recalculation.
|
||||
*/
|
||||
mutable blender::Vector<blender::float3> evaluated_position_cache_;
|
||||
mutable std::mutex position_cache_mutex_;
|
||||
mutable bool position_cache_dirty_ = true;
|
||||
|
||||
public:
|
||||
SplinePtr copy() const final;
|
||||
NURBSpline() : Spline(Type::NURBS)
|
||||
{
|
||||
}
|
||||
NURBSpline(const NURBSpline &other)
|
||||
: Spline((Spline &)other),
|
||||
knots_mode(other.knots_mode),
|
||||
positions_(other.positions_),
|
||||
radii_(other.radii_),
|
||||
tilts_(other.tilts_),
|
||||
weights_(other.weights_),
|
||||
resolution_(other.resolution_),
|
||||
order_(other.order_)
|
||||
{
|
||||
}
|
||||
|
||||
int size() const final;
|
||||
int resolution() const;
|
||||
void set_resolution(const int value);
|
||||
uint8_t order() const;
|
||||
void set_order(const uint8_t value);
|
||||
|
||||
void add_point(const blender::float3 position,
|
||||
const float radius,
|
||||
const float tilt,
|
||||
const float weight);
|
||||
|
||||
bool check_valid_size_and_order() const;
|
||||
int knots_size() const;
|
||||
|
||||
blender::MutableSpan<blender::float3> positions() final;
|
||||
blender::Span<blender::float3> positions() const final;
|
||||
blender::MutableSpan<float> radii() final;
|
||||
blender::Span<float> radii() const final;
|
||||
blender::MutableSpan<float> tilts() final;
|
||||
blender::Span<float> tilts() const final;
|
||||
blender::Span<float> knots() const;
|
||||
|
||||
blender::MutableSpan<float> weights();
|
||||
blender::Span<float> weights() const;
|
||||
|
||||
void mark_cache_invalid() final;
|
||||
int evaluated_points_size() const final;
|
||||
|
||||
blender::Span<blender::float3> evaluated_positions() const final;
|
||||
|
||||
blender::fn::GVArrayPtr interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const final;
|
||||
|
||||
protected:
|
||||
void correct_end_tangents() const final;
|
||||
void calculate_knots() const;
|
||||
void calculate_basis_cache() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Poly spline is like a bezier spline with a resolution of one. The main reason to distinguish
|
||||
* the two is for reduced complexity and increased performance, since interpolating data to control
|
||||
* points does not change it.
|
||||
*/
|
||||
class PolySpline final : public Spline {
|
||||
public:
|
||||
blender::Vector<blender::float3> positions_;
|
||||
blender::Vector<float> radii_;
|
||||
blender::Vector<float> tilts_;
|
||||
|
||||
private:
|
||||
public:
|
||||
SplinePtr copy() const final;
|
||||
PolySpline() : Spline(Type::Poly)
|
||||
{
|
||||
}
|
||||
PolySpline(const PolySpline &other)
|
||||
: Spline((Spline &)other),
|
||||
positions_(other.positions_),
|
||||
radii_(other.radii_),
|
||||
tilts_(other.tilts_)
|
||||
{
|
||||
}
|
||||
|
||||
int size() const final;
|
||||
|
||||
void add_point(const blender::float3 position, const float radius, const float tilt);
|
||||
|
||||
blender::MutableSpan<blender::float3> positions() final;
|
||||
blender::Span<blender::float3> positions() const final;
|
||||
blender::MutableSpan<float> radii() final;
|
||||
blender::Span<float> radii() const final;
|
||||
blender::MutableSpan<float> tilts() final;
|
||||
blender::Span<float> tilts() const final;
|
||||
|
||||
void mark_cache_invalid() final;
|
||||
int evaluated_points_size() const final;
|
||||
|
||||
blender::Span<blender::float3> evaluated_positions() const final;
|
||||
|
||||
blender::fn::GVArrayPtr interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const final;
|
||||
|
||||
protected:
|
||||
void correct_end_tangents() const final;
|
||||
};
|
||||
|
||||
/**
|
||||
* A #CurveEval corresponds to the #Curve object data. The name is different for clarity, since
|
||||
* more of the data is stored in the splines, but also just to be different than the name in DNA.
|
||||
*/
|
||||
class CurveEval {
|
||||
public:
|
||||
blender::Vector<SplinePtr> splines;
|
||||
|
||||
CurveEval *copy();
|
||||
|
||||
void translate(const blender::float3 &translation);
|
||||
void transform(const blender::float4x4 &matrix);
|
||||
void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
|
||||
};
|
||||
|
||||
std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve);
|
|
@ -112,6 +112,7 @@ set(SRC
|
|||
intern/curve_convert.c
|
||||
intern/curve_decimate.c
|
||||
intern/curve_deform.c
|
||||
intern/curve_eval.cc
|
||||
intern/curveprofile.c
|
||||
intern/customdata.c
|
||||
intern/customdata_file.c
|
||||
|
@ -133,6 +134,7 @@ set(SRC
|
|||
intern/fmodifier.c
|
||||
intern/font.c
|
||||
intern/freestyle.c
|
||||
intern/geometry_component_curve.cc
|
||||
intern/geometry_component_instances.cc
|
||||
intern/geometry_component_mesh.cc
|
||||
intern/geometry_component_pointcloud.cc
|
||||
|
@ -241,6 +243,10 @@ set(SRC
|
|||
intern/softbody.c
|
||||
intern/sound.c
|
||||
intern/speaker.c
|
||||
intern/spline_base.cc
|
||||
intern/spline_bezier.cc
|
||||
intern/spline_nurbs.cc
|
||||
intern/spline_poly.cc
|
||||
intern/studiolight.c
|
||||
intern/subdiv.c
|
||||
intern/subdiv_ccg.c
|
||||
|
@ -323,6 +329,7 @@ set(SRC
|
|||
BKE_customdata_file.h
|
||||
BKE_data_transfer.h
|
||||
BKE_deform.h
|
||||
BKE_spline.hh
|
||||
BKE_displist.h
|
||||
BKE_displist_tangent.h
|
||||
BKE_duplilist.h
|
||||
|
|
|
@ -1859,9 +1859,11 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
|
|||
BKE_id_free(nullptr, mesh_orco);
|
||||
}
|
||||
|
||||
/* Ensure normals calculation below is correct. */
|
||||
BLI_assert((mesh_input->flag & ME_AUTOSMOOTH) == (mesh_final->flag & ME_AUTOSMOOTH));
|
||||
BLI_assert(mesh_input->smoothresh == mesh_final->smoothresh);
|
||||
/* Ensure normals calculation below is correct (normal settings have transferred properly).
|
||||
* However, nodes modifiers might create meshes from scratch or transfer meshes from other
|
||||
* objects with different settings, and in general it doesn't make sense to guarentee that
|
||||
* the settings are the same as the original mesh. If necessary, this could become a modifier
|
||||
* type flag. */
|
||||
BLI_assert(mesh_input->smoothresh == mesh_cage->smoothresh);
|
||||
|
||||
/* Compute normals. */
|
||||
|
|
|
@ -635,7 +635,7 @@ bPoseChannel *BKE_pose_channel_find_name(const bPose *pose, const char *name)
|
|||
* \note Use with care, not on Armature poses but for temporal ones.
|
||||
* \note (currently used for action constraints and in rebuild_pose).
|
||||
*/
|
||||
bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name)
|
||||
bPoseChannel *BKE_pose_channel_ensure(bPose *pose, const char *name)
|
||||
{
|
||||
bPoseChannel *chan;
|
||||
|
||||
|
@ -815,7 +815,7 @@ void BKE_pose_copy_data_ex(bPose **dst,
|
|||
*/
|
||||
if (outPose->chanbase.first != outPose->chanbase.last) {
|
||||
outPose->chanhash = NULL;
|
||||
BKE_pose_channels_hash_make(outPose);
|
||||
BKE_pose_channels_hash_ensure(outPose);
|
||||
}
|
||||
|
||||
outPose->iksolver = src->iksolver;
|
||||
|
@ -945,7 +945,7 @@ bool BKE_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan)
|
|||
* Removes the hash for quick lookup of channels, must
|
||||
* be done when adding/removing channels.
|
||||
*/
|
||||
void BKE_pose_channels_hash_make(bPose *pose)
|
||||
void BKE_pose_channels_hash_ensure(bPose *pose)
|
||||
{
|
||||
if (!pose->chanhash) {
|
||||
bPoseChannel *pchan;
|
||||
|
@ -1191,7 +1191,7 @@ void BKE_pose_free(bPose *pose)
|
|||
* and ID-Props, used when duplicating bones in editmode.
|
||||
* (unlike copy_pose_channel_data which only does posing-related stuff).
|
||||
*
|
||||
* \note use when copying bones in editmode (on returned value from #BKE_pose_channel_verify)
|
||||
* \note use when copying bones in editmode (on returned value from #BKE_pose_channel_ensure)
|
||||
*/
|
||||
void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_from)
|
||||
{
|
||||
|
@ -1774,7 +1774,7 @@ void what_does_obaction(Object *ob,
|
|||
* allocation and also will make lookup slower.
|
||||
*/
|
||||
if (pose->chanbase.first != pose->chanbase.last) {
|
||||
BKE_pose_channels_hash_make(pose);
|
||||
BKE_pose_channels_hash_ensure(pose);
|
||||
}
|
||||
if (pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
|
||||
BKE_pose_update_constraint_flags(pose);
|
||||
|
|
|
@ -354,7 +354,7 @@ AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag)
|
|||
}
|
||||
|
||||
/* duplicate NLA data */
|
||||
BKE_nla_tracks_copy(bmain, &dadt->nla_tracks, &adt->nla_tracks, flag);
|
||||
BKE_nla_tracks_copy_from_adt(bmain, dadt, adt, flag);
|
||||
|
||||
/* duplicate drivers (F-Curves) */
|
||||
BKE_fcurves_copy(&dadt->drivers, &adt->drivers);
|
||||
|
|
|
@ -2444,7 +2444,7 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected)
|
|||
static int rebuild_pose_bone(
|
||||
bPose *pose, Bone *bone, bPoseChannel *parchan, int counter, Bone **r_last_visited_bone_p)
|
||||
{
|
||||
bPoseChannel *pchan = BKE_pose_channel_verify(pose, bone->name); /* verify checks and/or adds */
|
||||
bPoseChannel *pchan = BKE_pose_channel_ensure(pose, bone->name); /* verify checks and/or adds */
|
||||
|
||||
pchan->bone = bone;
|
||||
pchan->parent = parchan;
|
||||
|
@ -2562,7 +2562,7 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_
|
|||
/* and a check for garbage */
|
||||
BKE_pose_channels_clear_with_null_bone(pose, do_id_user);
|
||||
|
||||
BKE_pose_channels_hash_make(pose);
|
||||
BKE_pose_channels_hash_ensure(pose);
|
||||
|
||||
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||
/* Find the custom B-Bone handles. */
|
||||
|
|
|
@ -143,10 +143,8 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_
|
|||
static int attribute_domain_priority(const AttributeDomain domain)
|
||||
{
|
||||
switch (domain) {
|
||||
#if 0
|
||||
case ATTR_DOMAIN_CURVE:
|
||||
return 0;
|
||||
#endif
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return 1;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
|
|
|
@ -1014,6 +1014,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
|
|||
brush->gpencil_settings->draw_smoothfac = 0.1f;
|
||||
brush->gpencil_settings->draw_smoothlvl = 1;
|
||||
brush->gpencil_settings->draw_subdivide = 1;
|
||||
brush->gpencil_settings->dilate_pixels = 1;
|
||||
|
||||
brush->gpencil_settings->flag |= GP_BRUSH_FILL_SHOW_EXTENDLINES;
|
||||
|
||||
|
|
|
@ -2836,7 +2836,7 @@ static void actcon_get_tarmat(struct Depsgraph *depsgraph,
|
|||
* including rotation order, otherwise this fails. */
|
||||
pchan = cob->pchan;
|
||||
|
||||
tchan = BKE_pose_channel_verify(&pose, pchan->name);
|
||||
tchan = BKE_pose_channel_ensure(&pose, pchan->name);
|
||||
tchan->rotmode = pchan->rotmode;
|
||||
|
||||
/* evaluate action using workob (it will only set the PoseChannel in question) */
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "DNA_curve_types.h"
|
||||
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
using blender::float3;
|
||||
using blender::float4x4;
|
||||
using blender::Span;
|
||||
|
||||
CurveEval *CurveEval::copy()
|
||||
{
|
||||
CurveEval *new_curve = new CurveEval();
|
||||
|
||||
for (SplinePtr &spline : this->splines) {
|
||||
new_curve->splines.append(spline->copy());
|
||||
}
|
||||
|
||||
return new_curve;
|
||||
}
|
||||
|
||||
void CurveEval::translate(const float3 &translation)
|
||||
{
|
||||
for (SplinePtr &spline : this->splines) {
|
||||
spline->translate(translation);
|
||||
spline->mark_cache_invalid();
|
||||
}
|
||||
}
|
||||
|
||||
void CurveEval::transform(const float4x4 &matrix)
|
||||
{
|
||||
for (SplinePtr &spline : this->splines) {
|
||||
spline->transform(matrix);
|
||||
}
|
||||
}
|
||||
|
||||
void CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
|
||||
{
|
||||
for (const SplinePtr &spline : this->splines) {
|
||||
spline->bounds_min_max(min, max, use_evaluated);
|
||||
}
|
||||
}
|
||||
|
||||
static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
|
||||
{
|
||||
switch (dna_handle_type) {
|
||||
case HD_FREE:
|
||||
return BezierSpline::HandleType::Free;
|
||||
case HD_AUTO:
|
||||
return BezierSpline::HandleType::Auto;
|
||||
case HD_VECT:
|
||||
return BezierSpline::HandleType::Vector;
|
||||
case HD_ALIGN:
|
||||
return BezierSpline::HandleType::Align;
|
||||
case HD_AUTO_ANIM:
|
||||
return BezierSpline::HandleType::Auto;
|
||||
case HD_ALIGN_DOUBLESIDE:
|
||||
return BezierSpline::HandleType::Align;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return BezierSpline::HandleType::Auto;
|
||||
}
|
||||
|
||||
static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode)
|
||||
{
|
||||
switch (twist_mode) {
|
||||
case CU_TWIST_Z_UP:
|
||||
return Spline::NormalCalculationMode::ZUp;
|
||||
case CU_TWIST_MINIMUM:
|
||||
return Spline::NormalCalculationMode::Minimum;
|
||||
case CU_TWIST_TANGENT:
|
||||
return Spline::NormalCalculationMode::Tangent;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return Spline::NormalCalculationMode::Minimum;
|
||||
}
|
||||
|
||||
static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag)
|
||||
{
|
||||
switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) {
|
||||
case CU_NURB_ENDPOINT:
|
||||
return NURBSpline::KnotsMode::EndPoint;
|
||||
case CU_NURB_BEZIER:
|
||||
return NURBSpline::KnotsMode::Bezier;
|
||||
default:
|
||||
return NURBSpline::KnotsMode::Normal;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return NURBSpline::KnotsMode::Normal;
|
||||
}
|
||||
|
||||
std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
|
||||
{
|
||||
std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
|
||||
|
||||
const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve));
|
||||
|
||||
curve->splines.reserve(BLI_listbase_count(nurbs));
|
||||
|
||||
/* TODO: Optimize by reserving the correct points size. */
|
||||
LISTBASE_FOREACH (const Nurb *, nurb, nurbs) {
|
||||
switch (nurb->type) {
|
||||
case CU_BEZIER: {
|
||||
std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
|
||||
spline->set_resolution(nurb->resolu);
|
||||
spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
|
||||
|
||||
for (const BezTriple &bezt : Span(nurb->bezt, nurb->pntsu)) {
|
||||
spline->add_point(bezt.vec[1],
|
||||
handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1),
|
||||
bezt.vec[0],
|
||||
handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2),
|
||||
bezt.vec[2],
|
||||
bezt.radius,
|
||||
bezt.tilt);
|
||||
}
|
||||
|
||||
curve->splines.append(std::move(spline));
|
||||
break;
|
||||
}
|
||||
case CU_NURBS: {
|
||||
std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>();
|
||||
spline->set_resolution(nurb->resolu);
|
||||
spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
|
||||
spline->set_order(nurb->orderu);
|
||||
spline->knots_mode = knots_mode_from_dna_nurb(nurb->flagu);
|
||||
|
||||
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
|
||||
spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]);
|
||||
}
|
||||
|
||||
curve->splines.append(std::move(spline));
|
||||
break;
|
||||
}
|
||||
case CU_POLY: {
|
||||
std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
|
||||
spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
|
||||
|
||||
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
|
||||
spline->add_point(bp.vec, bp.radius, bp.tilt);
|
||||
}
|
||||
|
||||
curve->splines.append(std::move(spline));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Note: Normal mode is stored separately in each spline to facilitate combining splines
|
||||
* from multiple curve objects, where the value may be different. */
|
||||
const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve(
|
||||
dna_curve.twist_mode);
|
||||
for (SplinePtr &spline : curve->splines) {
|
||||
spline->normal_mode = normal_mode;
|
||||
}
|
||||
|
||||
return curve;
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -1015,7 +1015,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
|
|||
{
|
||||
const float total_length = BKE_curveprofile_total_length(profile);
|
||||
const float segment_length = total_length / n_segments;
|
||||
float length_travelled = 0.0f;
|
||||
float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0);
|
||||
float distance_to_previous_table_point = 0.0f;
|
||||
int i_table = 0;
|
||||
|
@ -1029,7 +1028,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
|
|||
for (int i = 1; i < n_segments; i++) {
|
||||
/* Travel over all of the points that fit inside this segment. */
|
||||
while (distance_to_next_table_point < segment_left) {
|
||||
length_travelled += distance_to_next_table_point;
|
||||
segment_left -= distance_to_next_table_point;
|
||||
i_table++;
|
||||
distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table);
|
||||
|
@ -1057,7 +1055,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
|
|||
/* We sampled in between this table point and the next, so the next travel step is smaller. */
|
||||
distance_to_next_table_point -= segment_left;
|
||||
distance_to_previous_table_point += segment_left;
|
||||
length_travelled += segment_left;
|
||||
segment_left = segment_length;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
|
||||
#include "attribute_access_intern.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Geometry Component Implementation
|
||||
* \{ */
|
||||
|
||||
CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
|
||||
{
|
||||
}
|
||||
|
||||
CurveComponent::~CurveComponent()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
GeometryComponent *CurveComponent::copy() const
|
||||
{
|
||||
CurveComponent *new_component = new CurveComponent();
|
||||
if (curve_ != nullptr) {
|
||||
new_component->curve_ = curve_->copy();
|
||||
new_component->ownership_ = GeometryOwnershipType::Owned;
|
||||
}
|
||||
return new_component;
|
||||
}
|
||||
|
||||
void CurveComponent::clear()
|
||||
{
|
||||
BLI_assert(this->is_mutable());
|
||||
if (curve_ != nullptr) {
|
||||
if (ownership_ == GeometryOwnershipType::Owned) {
|
||||
delete curve_;
|
||||
}
|
||||
curve_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool CurveComponent::has_curve() const
|
||||
{
|
||||
return curve_ != nullptr;
|
||||
}
|
||||
|
||||
/* Clear the component and replace it with the new curve. */
|
||||
void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership)
|
||||
{
|
||||
BLI_assert(this->is_mutable());
|
||||
this->clear();
|
||||
curve_ = curve;
|
||||
ownership_ = ownership;
|
||||
}
|
||||
|
||||
CurveEval *CurveComponent::release()
|
||||
{
|
||||
BLI_assert(this->is_mutable());
|
||||
CurveEval *curve = curve_;
|
||||
curve_ = nullptr;
|
||||
return curve;
|
||||
}
|
||||
|
||||
const CurveEval *CurveComponent::get_for_read() const
|
||||
{
|
||||
return curve_;
|
||||
}
|
||||
|
||||
CurveEval *CurveComponent::get_for_write()
|
||||
{
|
||||
BLI_assert(this->is_mutable());
|
||||
if (ownership_ == GeometryOwnershipType::ReadOnly) {
|
||||
curve_ = curve_->copy();
|
||||
ownership_ = GeometryOwnershipType::Owned;
|
||||
}
|
||||
return curve_;
|
||||
}
|
||||
|
||||
bool CurveComponent::is_empty() const
|
||||
{
|
||||
return curve_ == nullptr;
|
||||
}
|
||||
|
||||
bool CurveComponent::owns_direct_data() const
|
||||
{
|
||||
return ownership_ == GeometryOwnershipType::Owned;
|
||||
}
|
||||
|
||||
void CurveComponent::ensure_owns_direct_data()
|
||||
{
|
||||
BLI_assert(this->is_mutable());
|
||||
if (ownership_ != GeometryOwnershipType::Owned) {
|
||||
curve_ = curve_->copy();
|
||||
ownership_ = GeometryOwnershipType::Owned;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Attribute Access
|
||||
* \{ */
|
||||
|
||||
int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
|
||||
{
|
||||
if (curve_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
if (domain == ATTR_DOMAIN_POINT) {
|
||||
int total = 0;
|
||||
for (const SplinePtr &spline : curve_->splines) {
|
||||
total += spline->size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
if (domain == ATTR_DOMAIN_CURVE) {
|
||||
return curve_->splines.size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
|
||||
using AsReadAttribute = GVArrayPtr (*)(const CurveEval &data);
|
||||
using AsWriteAttribute = GVMutableArrayPtr (*)(CurveEval &data);
|
||||
using UpdateOnWrite = void (*)(Spline &spline);
|
||||
const AsReadAttribute as_read_attribute_;
|
||||
const AsWriteAttribute as_write_attribute_;
|
||||
|
||||
public:
|
||||
BuiltinSplineAttributeProvider(std::string attribute_name,
|
||||
const CustomDataType attribute_type,
|
||||
const WritableEnum writable,
|
||||
const AsReadAttribute as_read_attribute,
|
||||
const AsWriteAttribute as_write_attribute)
|
||||
: BuiltinAttributeProvider(std::move(attribute_name),
|
||||
ATTR_DOMAIN_CURVE,
|
||||
attribute_type,
|
||||
BuiltinAttributeProvider::NonCreatable,
|
||||
writable,
|
||||
BuiltinAttributeProvider::NonDeletable),
|
||||
as_read_attribute_(as_read_attribute),
|
||||
as_write_attribute_(as_write_attribute)
|
||||
{
|
||||
}
|
||||
|
||||
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
|
||||
{
|
||||
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
|
||||
const CurveEval *curve = curve_component.get_for_read();
|
||||
if (curve == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return as_read_attribute_(*curve);
|
||||
}
|
||||
|
||||
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
|
||||
{
|
||||
if (writable_ != Writable) {
|
||||
return {};
|
||||
}
|
||||
CurveComponent &curve_component = static_cast<CurveComponent &>(component);
|
||||
CurveEval *curve = curve_component.get_for_write();
|
||||
if (curve == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return as_write_attribute_(*curve);
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &UNUSED(component)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_create(GeometryComponent &UNUSED(component),
|
||||
const AttributeInit &UNUSED(initializer)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool exists(const GeometryComponent &component) const final
|
||||
{
|
||||
return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
static int get_spline_resolution(const SplinePtr &spline)
|
||||
{
|
||||
if (const BezierSpline *bezier_spline = dynamic_cast<const BezierSpline *>(spline.get())) {
|
||||
return bezier_spline->resolution();
|
||||
}
|
||||
if (const NURBSpline *nurb_spline = dynamic_cast<const NURBSpline *>(spline.get())) {
|
||||
return nurb_spline->resolution();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void set_spline_resolution(SplinePtr &spline, const int resolution)
|
||||
{
|
||||
if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) {
|
||||
bezier_spline->set_resolution(std::max(resolution, 1));
|
||||
}
|
||||
if (NURBSpline *nurb_spline = dynamic_cast<NURBSpline *>(spline.get())) {
|
||||
nurb_spline->set_resolution(std::max(resolution, 1));
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr make_resolution_read_attribute(const CurveEval &curve)
|
||||
{
|
||||
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, int, get_spline_resolution>>(
|
||||
curve.splines.as_span());
|
||||
}
|
||||
|
||||
static GVMutableArrayPtr make_resolution_write_attribute(CurveEval &curve)
|
||||
{
|
||||
return std::make_unique<fn::GVMutableArray_For_DerivedSpan<SplinePtr,
|
||||
int,
|
||||
get_spline_resolution,
|
||||
set_spline_resolution>>(
|
||||
curve.splines.as_mutable_span());
|
||||
}
|
||||
|
||||
static bool get_cyclic_value(const SplinePtr &spline)
|
||||
{
|
||||
return spline->is_cyclic();
|
||||
}
|
||||
|
||||
static void set_cyclic_value(SplinePtr &spline, const bool value)
|
||||
{
|
||||
if (spline->is_cyclic() != value) {
|
||||
spline->set_cyclic(value);
|
||||
spline->mark_cache_invalid();
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr make_cyclic_read_attribute(const CurveEval &curve)
|
||||
{
|
||||
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value>>(
|
||||
curve.splines.as_span());
|
||||
}
|
||||
|
||||
static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve)
|
||||
{
|
||||
return std::make_unique<
|
||||
fn::GVMutableArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value, set_cyclic_value>>(
|
||||
curve.splines.as_mutable_span());
|
||||
}
|
||||
|
||||
/**
|
||||
* In this function all the attribute providers for a curve component are created. Most data
|
||||
* in this function is statically allocated, because it does not change over time.
|
||||
*/
|
||||
static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
{
|
||||
static BuiltinSplineAttributeProvider resolution("resolution",
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
make_resolution_read_attribute,
|
||||
make_resolution_write_attribute);
|
||||
|
||||
static BuiltinSplineAttributeProvider cyclic("cyclic",
|
||||
CD_PROP_BOOL,
|
||||
BuiltinAttributeProvider::Writable,
|
||||
make_cyclic_read_attribute,
|
||||
make_cyclic_write_attribute);
|
||||
|
||||
return ComponentAttributeProviders({&resolution, &cyclic}, {});
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const
|
||||
{
|
||||
static blender::bke::ComponentAttributeProviders providers =
|
||||
blender::bke::create_attribute_providers_for_curve();
|
||||
return &providers;
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -42,72 +42,86 @@ InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_
|
|||
GeometryComponent *InstancesComponent::copy() const
|
||||
{
|
||||
InstancesComponent *new_component = new InstancesComponent();
|
||||
new_component->transforms_ = transforms_;
|
||||
new_component->instanced_data_ = instanced_data_;
|
||||
new_component->ids_ = ids_;
|
||||
new_component->instance_reference_handles_ = instance_reference_handles_;
|
||||
new_component->instance_transforms_ = instance_transforms_;
|
||||
new_component->instance_ids_ = instance_ids_;
|
||||
new_component->references_ = references_;
|
||||
return new_component;
|
||||
}
|
||||
|
||||
void InstancesComponent::reserve(int min_capacity)
|
||||
{
|
||||
instance_reference_handles_.reserve(min_capacity);
|
||||
instance_transforms_.reserve(min_capacity);
|
||||
instance_ids_.reserve(min_capacity);
|
||||
}
|
||||
|
||||
void InstancesComponent::clear()
|
||||
{
|
||||
instanced_data_.clear();
|
||||
transforms_.clear();
|
||||
ids_.clear();
|
||||
instance_reference_handles_.clear();
|
||||
instance_transforms_.clear();
|
||||
instance_ids_.clear();
|
||||
|
||||
references_.clear();
|
||||
}
|
||||
|
||||
void InstancesComponent::add_instance(Object *object, float4x4 transform, const int id)
|
||||
void InstancesComponent::add_instance(const int instance_handle,
|
||||
const float4x4 &transform,
|
||||
const int id)
|
||||
{
|
||||
InstancedData data;
|
||||
data.type = INSTANCE_DATA_TYPE_OBJECT;
|
||||
data.data.object = object;
|
||||
this->add_instance(data, transform, id);
|
||||
BLI_assert(instance_handle >= 0);
|
||||
BLI_assert(instance_handle < references_.size());
|
||||
instance_reference_handles_.append(instance_handle);
|
||||
instance_transforms_.append(transform);
|
||||
instance_ids_.append(id);
|
||||
}
|
||||
|
||||
void InstancesComponent::add_instance(Collection *collection, float4x4 transform, const int id)
|
||||
blender::Span<int> InstancesComponent::instance_reference_handles() const
|
||||
{
|
||||
InstancedData data;
|
||||
data.type = INSTANCE_DATA_TYPE_COLLECTION;
|
||||
data.data.collection = collection;
|
||||
this->add_instance(data, transform, id);
|
||||
return instance_reference_handles_;
|
||||
}
|
||||
|
||||
void InstancesComponent::add_instance(InstancedData data, float4x4 transform, const int id)
|
||||
blender::MutableSpan<blender::float4x4> InstancesComponent::instance_transforms()
|
||||
{
|
||||
instanced_data_.append(data);
|
||||
transforms_.append(transform);
|
||||
ids_.append(id);
|
||||
return instance_transforms_;
|
||||
}
|
||||
blender::Span<blender::float4x4> InstancesComponent::instance_transforms() const
|
||||
{
|
||||
return instance_transforms_;
|
||||
}
|
||||
|
||||
Span<InstancedData> InstancesComponent::instanced_data() const
|
||||
blender::MutableSpan<int> InstancesComponent::instance_ids()
|
||||
{
|
||||
return instanced_data_;
|
||||
return instance_ids_;
|
||||
}
|
||||
blender::Span<int> InstancesComponent::instance_ids() const
|
||||
{
|
||||
return instance_ids_;
|
||||
}
|
||||
|
||||
Span<float4x4> InstancesComponent::transforms() const
|
||||
/**
|
||||
* Returns a handle for the given reference.
|
||||
* If the reference exists already, the handle of the existing reference is returned.
|
||||
* Otherwise a new handle is added.
|
||||
*/
|
||||
int InstancesComponent::add_reference(InstanceReference reference)
|
||||
{
|
||||
return transforms_;
|
||||
return references_.index_of_or_add_as(reference);
|
||||
}
|
||||
|
||||
Span<int> InstancesComponent::ids() const
|
||||
blender::Span<InstanceReference> InstancesComponent::references() const
|
||||
{
|
||||
return ids_;
|
||||
}
|
||||
|
||||
MutableSpan<float4x4> InstancesComponent::transforms()
|
||||
{
|
||||
return transforms_;
|
||||
return references_;
|
||||
}
|
||||
|
||||
int InstancesComponent::instances_amount() const
|
||||
{
|
||||
const int size = instanced_data_.size();
|
||||
BLI_assert(transforms_.size() == size);
|
||||
return size;
|
||||
return instance_transforms_.size();
|
||||
}
|
||||
|
||||
bool InstancesComponent::is_empty() const
|
||||
{
|
||||
return transforms_.size() == 0;
|
||||
return this->instance_reference_handles_.size() == 0;
|
||||
}
|
||||
|
||||
bool InstancesComponent::owns_direct_data() const
|
||||
|
@ -178,8 +192,8 @@ static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
|
|||
blender::Span<int> InstancesComponent::almost_unique_ids() const
|
||||
{
|
||||
std::lock_guard lock(almost_unique_ids_mutex_);
|
||||
if (almost_unique_ids_.size() != ids_.size()) {
|
||||
almost_unique_ids_ = generate_unique_instance_ids(ids_);
|
||||
if (almost_unique_ids_.size() != instance_ids_.size()) {
|
||||
almost_unique_ids_ = generate_unique_instance_ids(instance_ids_);
|
||||
}
|
||||
return almost_unique_ids_;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "BKE_mesh_wrapper.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_pointcloud.h"
|
||||
#include "BKE_spline.hh"
|
||||
#include "BKE_volume.h"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
|
@ -60,6 +61,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
|
|||
return new InstancesComponent();
|
||||
case GEO_COMPONENT_TYPE_VOLUME:
|
||||
return new VolumeComponent();
|
||||
case GEO_COMPONENT_TYPE_CURVE:
|
||||
return new CurveComponent();
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
|
@ -182,6 +185,11 @@ void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
|
|||
if (volume != nullptr) {
|
||||
BKE_volume_min_max(volume, *r_min, *r_max);
|
||||
}
|
||||
const CurveEval *curve = this->get_curve_for_read();
|
||||
if (curve != nullptr) {
|
||||
/* Using the evaluated positions is somewhat arbitrary, but it is probably expected. */
|
||||
curve->bounds_min_max(*r_min, *r_max, true);
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
|
||||
|
@ -252,6 +260,13 @@ const Volume *GeometrySet::get_volume_for_read() const
|
|||
return (component == nullptr) ? nullptr : component->get_for_read();
|
||||
}
|
||||
|
||||
/* Returns a read-only curve or null. */
|
||||
const CurveEval *GeometrySet::get_curve_for_read() const
|
||||
{
|
||||
const CurveComponent *component = this->get_component_for_read<CurveComponent>();
|
||||
return (component == nullptr) ? nullptr : component->get_for_read();
|
||||
}
|
||||
|
||||
/* Returns true when the geometry set has a point cloud component that has a point cloud. */
|
||||
bool GeometrySet::has_pointcloud() const
|
||||
{
|
||||
|
@ -273,6 +288,13 @@ bool GeometrySet::has_volume() const
|
|||
return component != nullptr && component->has_volume();
|
||||
}
|
||||
|
||||
/* Returns true when the geometry set has a curve component that has a curve. */
|
||||
bool GeometrySet::has_curve() const
|
||||
{
|
||||
const CurveComponent *component = this->get_component_for_read<CurveComponent>();
|
||||
return component != nullptr && component->has_curve();
|
||||
}
|
||||
|
||||
/* Create a new geometry set that only contains the given mesh. */
|
||||
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
|
||||
{
|
||||
|
@ -292,6 +314,15 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
|
|||
return geometry_set;
|
||||
}
|
||||
|
||||
/* Create a new geometry set that only contains the given curve. */
|
||||
GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership)
|
||||
{
|
||||
GeometrySet geometry_set;
|
||||
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
|
||||
component.replace(curve, ownership);
|
||||
return geometry_set;
|
||||
}
|
||||
|
||||
/* Clear the existing mesh and replace it with the given one. */
|
||||
void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
|
||||
{
|
||||
|
@ -299,6 +330,13 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
|
|||
component.replace(mesh, ownership);
|
||||
}
|
||||
|
||||
/* Clear the existing curve and replace it with the given one. */
|
||||
void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership)
|
||||
{
|
||||
CurveComponent &component = this->get_component_for_write<CurveComponent>();
|
||||
component.replace(curve, ownership);
|
||||
}
|
||||
|
||||
/* Clear the existing point cloud and replace with the given one. */
|
||||
void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
|
||||
{
|
||||
|
@ -334,6 +372,13 @@ Volume *GeometrySet::get_volume_for_write()
|
|||
return component.get_for_write();
|
||||
}
|
||||
|
||||
/* Returns a mutable curve or null. No ownership is transferred. */
|
||||
CurveEval *GeometrySet::get_curve_for_write()
|
||||
{
|
||||
CurveComponent &component = this->get_component_for_write<CurveComponent>();
|
||||
return component.get_for_write();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "BKE_mesh_wrapper.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_pointcloud.h"
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
|
@ -50,6 +51,16 @@ static void add_final_mesh_as_geometry_component(const Object &object, GeometryS
|
|||
}
|
||||
}
|
||||
|
||||
static void add_curve_data_as_geometry_component(const Object &object, GeometrySet &geometry_set)
|
||||
{
|
||||
BLI_assert(object.type == OB_CURVE);
|
||||
if (object.data != nullptr) {
|
||||
std::unique_ptr<CurveEval> curve = curve_eval_from_dna_curve(*(Curve *)object.data);
|
||||
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
|
||||
curve_component.replace(curve.release(), GeometryOwnershipType::Owned);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances.
|
||||
*/
|
||||
|
@ -73,6 +84,9 @@ static GeometrySet object_get_geometry_set_for_read(const Object &object)
|
|||
if (object.type == OB_MESH) {
|
||||
add_final_mesh_as_geometry_component(object, geometry_set);
|
||||
}
|
||||
else if (object.type == OB_CURVE) {
|
||||
add_curve_data_as_geometry_component(object, geometry_set);
|
||||
}
|
||||
|
||||
/* TODO: Cover the case of point-clouds without modifiers-- they may not be covered by the
|
||||
* #geometry_set_eval case above. */
|
||||
|
@ -135,21 +149,28 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set,
|
|||
const InstancesComponent &instances_component =
|
||||
*geometry_set.get_component_for_read<InstancesComponent>();
|
||||
|
||||
Span<float4x4> transforms = instances_component.transforms();
|
||||
Span<InstancedData> instances = instances_component.instanced_data();
|
||||
for (const int i : instances.index_range()) {
|
||||
const InstancedData &data = instances[i];
|
||||
Span<float4x4> transforms = instances_component.instance_transforms();
|
||||
Span<int> handles = instances_component.instance_reference_handles();
|
||||
Span<InstanceReference> references = instances_component.references();
|
||||
for (const int i : transforms.index_range()) {
|
||||
const InstanceReference &reference = references[handles[i]];
|
||||
const float4x4 instance_transform = transform * transforms[i];
|
||||
|
||||
if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
|
||||
BLI_assert(data.data.object != nullptr);
|
||||
const Object &object = *data.data.object;
|
||||
geometry_set_collect_recursive_object(object, instance_transform, r_sets);
|
||||
}
|
||||
else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
|
||||
BLI_assert(data.data.collection != nullptr);
|
||||
const Collection &collection = *data.data.collection;
|
||||
geometry_set_collect_recursive_collection_instance(collection, instance_transform, r_sets);
|
||||
switch (reference.type()) {
|
||||
case InstanceReference::Type::Object: {
|
||||
Object &object = reference.object();
|
||||
geometry_set_collect_recursive_object(object, instance_transform, r_sets);
|
||||
break;
|
||||
}
|
||||
case InstanceReference::Type::Collection: {
|
||||
Collection &collection = reference.collection();
|
||||
geometry_set_collect_recursive_collection_instance(
|
||||
collection, instance_transform, r_sets);
|
||||
break;
|
||||
}
|
||||
case InstanceReference::Type::None: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -253,19 +274,24 @@ static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_se
|
|||
return true;
|
||||
}
|
||||
|
||||
for (const InstancedData &data : instances_component->instanced_data()) {
|
||||
if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
|
||||
BLI_assert(data.data.object != nullptr);
|
||||
const Object &object = *data.data.object;
|
||||
if (!object_instance_attribute_foreach(object, callback, limit, count)) {
|
||||
return false;
|
||||
for (const InstanceReference &reference : instances_component->references()) {
|
||||
switch (reference.type()) {
|
||||
case InstanceReference::Type::Object: {
|
||||
const Object &object = reference.object();
|
||||
if (!object_instance_attribute_foreach(object, callback, limit, count)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
|
||||
BLI_assert(data.data.collection != nullptr);
|
||||
const Collection &collection = *data.data.collection;
|
||||
if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
|
||||
return false;
|
||||
case InstanceReference::Type::Collection: {
|
||||
const Collection &collection = reference.collection();
|
||||
if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case InstanceReference::Type::None: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -492,6 +518,28 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
|
|||
}
|
||||
}
|
||||
|
||||
static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComponent &result)
|
||||
{
|
||||
CurveEval *new_curve = new CurveEval();
|
||||
for (const GeometryInstanceGroup &set_group : set_groups) {
|
||||
const GeometrySet &set = set_group.geometry_set;
|
||||
if (!set.has_curve()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const CurveEval &source_curve = *set.get_curve_for_read();
|
||||
for (const SplinePtr &source_spline : source_curve.splines) {
|
||||
for (const float4x4 &transform : set_group.transforms) {
|
||||
SplinePtr new_spline = source_spline->copy();
|
||||
new_spline->transform(transform);
|
||||
new_curve->splines.append(std::move(new_spline));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.replace(new_curve);
|
||||
}
|
||||
|
||||
static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
|
||||
bool convert_points_to_vertices,
|
||||
GeometrySet &result)
|
||||
|
@ -558,6 +606,12 @@ static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups,
|
|||
UNUSED_VARS(set_groups, dst_component);
|
||||
}
|
||||
|
||||
static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result)
|
||||
{
|
||||
CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
|
||||
join_curve_splines(set_groups, dst_component);
|
||||
}
|
||||
|
||||
GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set)
|
||||
{
|
||||
if (!geometry_set.has_instances() && !geometry_set.has_pointcloud()) {
|
||||
|
@ -589,6 +643,7 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set)
|
|||
join_instance_groups_mesh(set_groups, false, new_geometry_set);
|
||||
join_instance_groups_pointcloud(set_groups, new_geometry_set);
|
||||
join_instance_groups_volume(set_groups, new_geometry_set);
|
||||
join_instance_groups_curve(set_groups, new_geometry_set);
|
||||
|
||||
return new_geometry_set;
|
||||
}
|
||||
|
|
|
@ -1688,7 +1688,6 @@ void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
|
|||
|
||||
float vec1[3];
|
||||
float vec2[3];
|
||||
float vec3[3];
|
||||
|
||||
/* initial vector (p0 -> p1) */
|
||||
sub_v3_v3v3(vec1, &pt1->x, &pt0->x);
|
||||
|
@ -1697,8 +1696,7 @@ void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
|
|||
sub_v3_v3v3(vec2, &pt3->x, &pt0->x);
|
||||
|
||||
/* vector orthogonal to polygon plane */
|
||||
cross_v3_v3v3(vec3, vec1, vec2);
|
||||
cross_v3_v3v3(r_normal, vec1, vec3);
|
||||
cross_v3_v3v3(r_normal, vec1, vec2);
|
||||
|
||||
/* Normalize vector */
|
||||
normalize_v3(r_normal);
|
||||
|
|
|
@ -757,9 +757,9 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob)
|
|||
{
|
||||
bGPdata *gpd = (bGPdata *)ob->data;
|
||||
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
|
||||
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
|
||||
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
|
||||
const bool is_render = (bool)(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
|
||||
const bool is_curve_edit = (bool)(GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd) && !is_render);
|
||||
const bool is_multiedit = (bool)(GPENCIL_MULTIEDIT_SESSIONS_ON(gpd) && !is_render);
|
||||
const bool do_modifiers = (bool)((!is_multiedit) && (!is_curve_edit) &&
|
||||
(ob->greasepencil_modifiers.first != NULL) &&
|
||||
(!GPENCIL_SIMPLIFY_MODIF(scene)));
|
||||
|
|
|
@ -408,17 +408,19 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
|
|||
store_premultiplied,
|
||||
limit_gl_texture_size);
|
||||
|
||||
GPU_texture_wrap_mode(*tex, true, false);
|
||||
if (*tex) {
|
||||
GPU_texture_wrap_mode(*tex, true, false);
|
||||
|
||||
if (GPU_mipmap_enabled()) {
|
||||
GPU_texture_generate_mipmap(*tex);
|
||||
if (ima) {
|
||||
ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
|
||||
if (GPU_mipmap_enabled()) {
|
||||
GPU_texture_generate_mipmap(*tex);
|
||||
if (ima) {
|
||||
ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
|
||||
}
|
||||
GPU_texture_mipmap_mode(*tex, true, true);
|
||||
}
|
||||
else {
|
||||
GPU_texture_mipmap_mode(*tex, false, true);
|
||||
}
|
||||
GPU_texture_mipmap_mode(*tex, true, true);
|
||||
}
|
||||
else {
|
||||
GPU_texture_mipmap_mode(*tex, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -427,7 +429,9 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
|
|||
BKE_image_release_ibuf(ima, ibuf_intern, NULL);
|
||||
}
|
||||
|
||||
GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y);
|
||||
if (*tex) {
|
||||
GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y);
|
||||
}
|
||||
|
||||
return *tex;
|
||||
}
|
||||
|
|
|
@ -435,8 +435,8 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa
|
|||
|
||||
for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
|
||||
to_id_entry = to_id_entry->next) {
|
||||
if ((to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) != 0) {
|
||||
/* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers) as
|
||||
if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
|
||||
/* Never consider non-overridable relationships ('from', 'parents', 'owner' etc. pointers) as
|
||||
* actual dependencies. */
|
||||
continue;
|
||||
}
|
||||
|
@ -480,10 +480,8 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat
|
|||
|
||||
for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
|
||||
to_id_entry = to_id_entry->next) {
|
||||
if ((to_id_entry->usage_flag &
|
||||
(IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) != 0) {
|
||||
/* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers), nor
|
||||
* override references or embedded ID pointers, as actual dependencies. */
|
||||
if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
|
||||
/* Never consider non-overridable relationships as actual dependencies. */
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -578,10 +576,8 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data
|
|||
|
||||
for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
|
||||
to_id_entry = to_id_entry->next) {
|
||||
if ((to_id_entry->usage_flag &
|
||||
(IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) != 0) {
|
||||
/* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers), nor
|
||||
* override references or embedded ID pointers, as actual dependencies. */
|
||||
if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
|
||||
/* Never consider non-overridable relationships as actual dependencies. */
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1182,8 +1178,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
|
|||
|
||||
for (MainIDRelationsEntryItem *entry_item = entry->to_ids; entry_item != NULL;
|
||||
entry_item = entry_item->next) {
|
||||
if (entry_item->usage_flag &
|
||||
(IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
|
||||
if (entry_item->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) {
|
||||
continue;
|
||||
}
|
||||
ID *id_to = *entry_item->id_pointer.to;
|
||||
|
@ -1224,6 +1219,13 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
|
|||
}
|
||||
do_continue = true;
|
||||
|
||||
/* In complex non-supported cases, with several different override hierarchies sharing
|
||||
* relations between each-other, we may end up not actually updating/replacing the given
|
||||
* root id (see e.g. pro/shots/110_rextoria/110_0150_A/110_0150_A.anim.blend of sprites
|
||||
* project repository, r2687).
|
||||
* This can lead to infinite loop here, at least avoid this. */
|
||||
id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
|
||||
|
||||
CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name);
|
||||
const bool success = BKE_lib_override_library_resync(
|
||||
bmain, scene, view_layer, id, override_resync_residual_storage, false, false);
|
||||
|
@ -2052,8 +2054,8 @@ static void lib_override_library_id_hierarchy_recursive_reset(Main *bmain, ID *i
|
|||
|
||||
for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
|
||||
to_id_entry = to_id_entry->next) {
|
||||
if ((to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) != 0) {
|
||||
/* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers) as
|
||||
if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
|
||||
/* Never consider non-overridable relationships ('from', 'parents', 'owner' etc. pointers) as
|
||||
* actual dependencies. */
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -56,11 +56,19 @@ typedef struct LibraryForeachIDData {
|
|||
*/
|
||||
ID *self_id;
|
||||
|
||||
/** Flags controlling the bahaviour of the 'foreach id' looping code. */
|
||||
int flag;
|
||||
/** Generic flags to be passed to all callback calls for current processed data. */
|
||||
int cb_flag;
|
||||
/** Callback flags that are forbidden for all callback calls for current processed data. */
|
||||
int cb_flag_clear;
|
||||
|
||||
/* Function to call for every ID pointers of current processed data, and its opaque user data
|
||||
* pointer. */
|
||||
LibraryIDLinkCallback callback;
|
||||
void *user_data;
|
||||
/** Store the returned value from the callback, to decide how to continue the processing of ID
|
||||
* pointers for current data. */
|
||||
int status;
|
||||
|
||||
/* To handle recursion. */
|
||||
|
@ -73,13 +81,25 @@ bool BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int
|
|||
if (!(data->status & IDWALK_STOP)) {
|
||||
const int flag = data->flag;
|
||||
ID *old_id = *id_pp;
|
||||
const int callback_return = data->callback(&(struct LibraryIDLinkCallbackData){
|
||||
.user_data = data->user_data,
|
||||
.bmain = data->bmain,
|
||||
.id_owner = data->owner_id,
|
||||
.id_self = data->self_id,
|
||||
.id_pointer = id_pp,
|
||||
.cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear)});
|
||||
|
||||
/* Update the callback flags with the ones defined (or forbidden) in `data` by the generic
|
||||
* caller code. */
|
||||
cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear);
|
||||
|
||||
/* Update the callback flags with some extra information regarding overrides: all 'loopback',
|
||||
* 'internal', 'embedded' etc. ID pointers are never overridable. */
|
||||
if (cb_flag & (IDWALK_CB_INTERNAL | IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK |
|
||||
IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
|
||||
cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE;
|
||||
}
|
||||
|
||||
const int callback_return = data->callback(
|
||||
&(struct LibraryIDLinkCallbackData){.user_data = data->user_data,
|
||||
.bmain = data->bmain,
|
||||
.id_owner = data->owner_id,
|
||||
.id_self = data->self_id,
|
||||
.id_pointer = id_pp,
|
||||
.cb_flag = cb_flag});
|
||||
if (flag & IDWALK_READONLY) {
|
||||
BLI_assert(*(id_pp) == old_id);
|
||||
}
|
||||
|
@ -132,7 +152,10 @@ void BKE_lib_query_idpropertiesForeachIDLink_callback(IDProperty *id_prop, void
|
|||
BLI_assert(id_prop->type == IDP_ID);
|
||||
|
||||
LibraryForeachIDData *data = (LibraryForeachIDData *)user_data;
|
||||
BKE_LIB_FOREACHID_PROCESS_ID(data, id_prop->data.pointer, IDWALK_CB_USER);
|
||||
const int cb_flag = IDWALK_CB_USER | ((id_prop->flag & IDP_FLAG_OVERRIDABLE_LIBRARY) ?
|
||||
0 :
|
||||
IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE);
|
||||
BKE_LIB_FOREACHID_PROCESS_ID(data, id_prop->data.pointer, cb_flag);
|
||||
}
|
||||
|
||||
bool BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp)
|
||||
|
|
|
@ -1027,11 +1027,15 @@ static Object *object_for_curve_to_mesh_create(Object *object)
|
|||
*
|
||||
* Note that there are extra fields in there like bevel and path, but those are not needed during
|
||||
* conversion, so they are not copied to save unnecessary allocations. */
|
||||
if (object->runtime.curve_cache != NULL) {
|
||||
if (temp_object->runtime.curve_cache == NULL) {
|
||||
temp_object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache),
|
||||
"CurveCache for curve types");
|
||||
}
|
||||
|
||||
if (object->runtime.curve_cache != NULL) {
|
||||
BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp);
|
||||
}
|
||||
|
||||
/* Constructive modifiers will use mesh to store result. */
|
||||
if (object->runtime.data_eval != NULL) {
|
||||
BKE_id_copy_ex(
|
||||
|
@ -1057,17 +1061,25 @@ static Object *object_for_curve_to_mesh_create(Object *object)
|
|||
return temp_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate `object->runtime.curve_cache` which is then used to create the mesh.
|
||||
*/
|
||||
static void curve_to_mesh_eval_ensure(Object *object)
|
||||
{
|
||||
if (object->runtime.curve_cache == NULL) {
|
||||
object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
|
||||
}
|
||||
Curve *curve = (Curve *)object->data;
|
||||
Curve remapped_curve = *curve;
|
||||
Object remapped_object = *object;
|
||||
remapped_object.runtime.bb = NULL;
|
||||
BKE_object_runtime_reset(&remapped_object);
|
||||
|
||||
remapped_object.data = &remapped_curve;
|
||||
|
||||
if (object->runtime.curve_cache == NULL) {
|
||||
object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
|
||||
}
|
||||
|
||||
/* Temporarily share the curve-cache with the temporary object, owned by `object`. */
|
||||
remapped_object.runtime.curve_cache = object->runtime.curve_cache;
|
||||
|
||||
/* Clear all modifiers for the bevel object.
|
||||
*
|
||||
* This is because they can not be reliably evaluated for an original object (at least because
|
||||
|
@ -1078,8 +1090,8 @@ static void curve_to_mesh_eval_ensure(Object *object)
|
|||
Object bevel_object = {{NULL}};
|
||||
if (remapped_curve.bevobj != NULL) {
|
||||
bevel_object = *remapped_curve.bevobj;
|
||||
bevel_object.runtime.bb = NULL;
|
||||
BLI_listbase_clear(&bevel_object.modifiers);
|
||||
BKE_object_runtime_reset(&bevel_object);
|
||||
remapped_curve.bevobj = &bevel_object;
|
||||
}
|
||||
|
||||
|
@ -1087,8 +1099,8 @@ static void curve_to_mesh_eval_ensure(Object *object)
|
|||
Object taper_object = {{NULL}};
|
||||
if (remapped_curve.taperobj != NULL) {
|
||||
taper_object = *remapped_curve.taperobj;
|
||||
taper_object.runtime.bb = NULL;
|
||||
BLI_listbase_clear(&taper_object.modifiers);
|
||||
BKE_object_runtime_reset(&taper_object);
|
||||
remapped_curve.taperobj = &taper_object;
|
||||
}
|
||||
|
||||
|
@ -1110,12 +1122,12 @@ static void curve_to_mesh_eval_ensure(Object *object)
|
|||
BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true);
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(remapped_object.runtime.bb);
|
||||
MEM_SAFE_FREE(taper_object.runtime.bb);
|
||||
MEM_SAFE_FREE(bevel_object.runtime.bb);
|
||||
/* Owned by `object` & needed by the caller to create the mesh. */
|
||||
remapped_object.runtime.curve_cache = NULL;
|
||||
|
||||
BKE_object_free_curve_cache(&bevel_object);
|
||||
BKE_object_free_curve_cache(&taper_object);
|
||||
BKE_object_runtime_free_data(&remapped_object);
|
||||
BKE_object_runtime_free_data(&taper_object);
|
||||
BKE_object_runtime_free_data(&taper_object);
|
||||
}
|
||||
|
||||
static Mesh *mesh_new_from_curve_type_object(Object *object)
|
||||
|
|
|
@ -950,7 +950,7 @@ static void movieclip_load_get_size(MovieClip *clip)
|
|||
int width, height;
|
||||
MovieClipUser user = {0};
|
||||
|
||||
user.framenr = 1;
|
||||
user.framenr = BKE_movieclip_remap_clip_to_scene_frame(clip, 1);
|
||||
BKE_movieclip_get_size(clip, &user, &width, &height);
|
||||
|
||||
if (width && height) {
|
||||
|
|
|
@ -254,7 +254,7 @@ NlaTrack *BKE_nlatrack_copy(Main *bmain,
|
|||
* \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_...
|
||||
* flags in BKE_lib_id.h
|
||||
*/
|
||||
void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int flag)
|
||||
void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, const int flag)
|
||||
{
|
||||
NlaTrack *nlt, *nlt_d;
|
||||
|
||||
|
@ -275,6 +275,54 @@ void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int fl
|
|||
}
|
||||
}
|
||||
|
||||
/* Set adt_dest->actstrip to the strip with the same index as adt_source->actstrip. */
|
||||
static void update_active_strip(AnimData *adt_dest,
|
||||
NlaTrack *track_dest,
|
||||
const AnimData *adt_source,
|
||||
NlaTrack *track_source)
|
||||
{
|
||||
BLI_assert(BLI_listbase_count(&track_source->strips) == BLI_listbase_count(&track_dest->strips));
|
||||
|
||||
NlaStrip *strip_dest = track_dest->strips.first;
|
||||
LISTBASE_FOREACH (NlaStrip *, strip_source, &track_source->strips) {
|
||||
if (strip_source == adt_source->actstrip) {
|
||||
adt_dest->actstrip = strip_dest;
|
||||
}
|
||||
|
||||
strip_dest = strip_dest->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set adt_dest->act_track to the track with the same index as adt_source->act_track. */
|
||||
static void update_active_track(AnimData *adt_dest, const AnimData *adt_source)
|
||||
{
|
||||
BLI_assert(BLI_listbase_count(&adt_source->nla_tracks) ==
|
||||
BLI_listbase_count(&adt_dest->nla_tracks));
|
||||
|
||||
NlaTrack *track_dest = adt_dest->nla_tracks.first;
|
||||
LISTBASE_FOREACH (NlaTrack *, track_source, &adt_source->nla_tracks) {
|
||||
if (track_source == adt_source->act_track) {
|
||||
adt_dest->act_track = track_dest;
|
||||
/* Assumption: the active strip is on the active track. */
|
||||
update_active_strip(adt_dest, track_dest, adt_source, track_source);
|
||||
}
|
||||
|
||||
track_dest = track_dest->next;
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_nla_tracks_copy_from_adt(Main *bmain,
|
||||
AnimData *adt_dest,
|
||||
const AnimData *adt_source,
|
||||
const int flag)
|
||||
{
|
||||
adt_dest->act_track = NULL;
|
||||
adt_dest->actstrip = NULL;
|
||||
|
||||
BKE_nla_tracks_copy(bmain, &adt_dest->nla_tracks, &adt_source->nla_tracks, flag);
|
||||
update_active_track(adt_dest, adt_source);
|
||||
}
|
||||
|
||||
/* Adding ------------------------------------------- */
|
||||
|
||||
/* Add a NLA Track to the given AnimData
|
||||
|
|
|
@ -4947,6 +4947,7 @@ static void registerGeometryNodes()
|
|||
register_node_type_geo_boolean();
|
||||
register_node_type_geo_bounding_box();
|
||||
register_node_type_geo_collection_info();
|
||||
register_node_type_geo_curve_to_mesh();
|
||||
register_node_type_geo_edge_split();
|
||||
register_node_type_geo_is_viewport();
|
||||
register_node_type_geo_join_geometry();
|
||||
|
|
|
@ -4343,7 +4343,7 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
|
|||
}
|
||||
/* Speed optimization for animation lookups. */
|
||||
if (ob->pose != NULL) {
|
||||
BKE_pose_channels_hash_make(ob->pose);
|
||||
BKE_pose_channels_hash_ensure(ob->pose);
|
||||
if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
|
||||
BKE_pose_update_constraint_flags(ob->pose);
|
||||
}
|
||||
|
@ -5113,6 +5113,20 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag))
|
|||
runtime->geometry_set_eval = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function frees memory used by the runtime data, but not the runtime field itself.
|
||||
*
|
||||
* All runtime data is cleared to ensure it's not used again,
|
||||
* in keeping with other `_free_data(..)` functions.
|
||||
*/
|
||||
void BKE_object_runtime_free_data(Object *object)
|
||||
{
|
||||
/* Currently this is all that's needed. */
|
||||
BKE_object_free_derived_caches(object);
|
||||
|
||||
BKE_object_runtime_reset(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an associated armature object.
|
||||
*/
|
||||
|
|
|
@ -842,38 +842,38 @@ static void make_duplis_instances_component(const DupliContext *ctx)
|
|||
return;
|
||||
}
|
||||
|
||||
Span<float4x4> instance_offset_matrices = component->transforms();
|
||||
Span<float4x4> instance_offset_matrices = component->instance_transforms();
|
||||
Span<int> instance_reference_handles = component->instance_reference_handles();
|
||||
Span<int> almost_unique_ids = component->almost_unique_ids();
|
||||
Span<InstancedData> instanced_data = component->instanced_data();
|
||||
Span<InstanceReference> references = component->references();
|
||||
|
||||
for (int i = 0; i < component->instances_amount(); i++) {
|
||||
const InstancedData &data = instanced_data[i];
|
||||
for (int64_t i : instance_offset_matrices.index_range()) {
|
||||
const InstanceReference &reference = references[instance_reference_handles[i]];
|
||||
const int id = almost_unique_ids[i];
|
||||
|
||||
if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
|
||||
Object *object = data.data.object;
|
||||
if (object != nullptr) {
|
||||
switch (reference.type()) {
|
||||
case InstanceReference::Type::Object: {
|
||||
Object &object = reference.object();
|
||||
float matrix[4][4];
|
||||
mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrices[i].values);
|
||||
make_dupli(ctx, object, matrix, id);
|
||||
make_dupli(ctx, &object, matrix, id);
|
||||
|
||||
float space_matrix[4][4];
|
||||
mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object->imat);
|
||||
mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object.imat);
|
||||
mul_m4_m4_pre(space_matrix, ctx->object->obmat);
|
||||
make_recursive_duplis(ctx, object, space_matrix, id);
|
||||
make_recursive_duplis(ctx, &object, space_matrix, id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
|
||||
Collection *collection = data.data.collection;
|
||||
if (collection != nullptr) {
|
||||
case InstanceReference::Type::Collection: {
|
||||
Collection &collection = reference.collection();
|
||||
float collection_matrix[4][4];
|
||||
unit_m4(collection_matrix);
|
||||
sub_v3_v3(collection_matrix[3], collection->instance_offset);
|
||||
sub_v3_v3(collection_matrix[3], collection.instance_offset);
|
||||
mul_m4_m4_pre(collection_matrix, instance_offset_matrices[i].values);
|
||||
mul_m4_m4_pre(collection_matrix, ctx->object->obmat);
|
||||
|
||||
eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
|
||||
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, object, mode) {
|
||||
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (&collection, object, mode) {
|
||||
if (object == ctx->object) {
|
||||
continue;
|
||||
}
|
||||
|
@ -885,6 +885,10 @@ static void make_duplis_instances_component(const DupliContext *ctx)
|
|||
make_recursive_duplis(ctx, object, collection_matrix, id);
|
||||
}
|
||||
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
|
||||
break;
|
||||
}
|
||||
case InstanceReference::Type::None: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -212,9 +212,9 @@ bool ray_face_intersection_quad(const float ray_start[3],
|
|||
float *depth);
|
||||
bool ray_face_intersection_tri(const float ray_start[3],
|
||||
struct IsectRayPrecalc *isect_precalc,
|
||||
const float *t0,
|
||||
const float *t1,
|
||||
const float *t2,
|
||||
const float t0[3],
|
||||
const float t1[3],
|
||||
const float t2[3],
|
||||
float *depth);
|
||||
|
||||
bool ray_face_nearest_quad(const float ray_start[3],
|
||||
|
|
|
@ -1805,7 +1805,7 @@ int BKE_ptcache_mem_pointers_seek(int point_index, PTCacheMem *pm, void *cur[BPH
|
|||
}
|
||||
|
||||
for (i = 0; i < BPHYS_TOT_DATA; i++) {
|
||||
cur[i] = data_types & (1 << i) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL;
|
||||
cur[i] = (data_types & (1 << i)) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
|
|
@ -742,7 +742,8 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data)
|
|||
BKE_LIB_FOREACHID_PROCESS(data, view_layer->mat_override, IDWALK_CB_USER);
|
||||
|
||||
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
|
||||
BKE_LIB_FOREACHID_PROCESS(data, base->object, IDWALK_CB_NOP);
|
||||
BKE_LIB_FOREACHID_PROCESS(
|
||||
data, base->object, IDWALK_CB_NOP | IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE);
|
||||
}
|
||||
|
||||
scene_foreach_layer_collection(data, &view_layer->layer_collections);
|
||||
|
|
|
@ -499,7 +499,6 @@ static void ccd_mesh_free(ccd_Mesh *ccdm)
|
|||
}
|
||||
MEM_freeN(ccdm->mima);
|
||||
MEM_freeN(ccdm);
|
||||
ccdm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "FN_generic_virtual_array.hh"
|
||||
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
using blender::float3;
|
||||
using blender::IndexRange;
|
||||
using blender::MutableSpan;
|
||||
using blender::Span;
|
||||
|
||||
Spline::Type Spline::type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
void Spline::translate(const blender::float3 &translation)
|
||||
{
|
||||
for (float3 &position : this->positions()) {
|
||||
position += translation;
|
||||
}
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
void Spline::transform(const blender::float4x4 &matrix)
|
||||
{
|
||||
for (float3 &position : this->positions()) {
|
||||
position = matrix * position;
|
||||
}
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
int Spline::evaluated_edges_size() const
|
||||
{
|
||||
const int eval_size = this->evaluated_points_size();
|
||||
if (eval_size == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this->is_cyclic_ ? eval_size : eval_size - 1;
|
||||
}
|
||||
|
||||
float Spline::length() const
|
||||
{
|
||||
return this->evaluated_lengths().last();
|
||||
}
|
||||
|
||||
int Spline::segments_size() const
|
||||
{
|
||||
const int points_len = this->size();
|
||||
|
||||
return is_cyclic_ ? points_len : points_len - 1;
|
||||
}
|
||||
|
||||
bool Spline::is_cyclic() const
|
||||
{
|
||||
return is_cyclic_;
|
||||
}
|
||||
|
||||
void Spline::set_cyclic(const bool value)
|
||||
{
|
||||
is_cyclic_ = value;
|
||||
}
|
||||
|
||||
static void accumulate_lengths(Span<float3> positions,
|
||||
const bool is_cyclic,
|
||||
MutableSpan<float> lengths)
|
||||
{
|
||||
float length = 0.0f;
|
||||
for (const int i : IndexRange(positions.size() - 1)) {
|
||||
length += float3::distance(positions[i], positions[i + 1]);
|
||||
lengths[i] = length;
|
||||
}
|
||||
if (is_cyclic) {
|
||||
lengths.last() = length + float3::distance(positions.last(), positions.first());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return non-owning access to the cache of accumulated lengths along the spline. Each item is the
|
||||
* length of the subsequent segment, i.e. the first value is the length of the first segment rather
|
||||
* than 0. This calculation is rather trivial, and only depends on the evaluated positions.
|
||||
* However, the results are used often, so it makes sense to cache it.
|
||||
*/
|
||||
Span<float> Spline::evaluated_lengths() const
|
||||
{
|
||||
if (!length_cache_dirty_) {
|
||||
return evaluated_lengths_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{length_cache_mutex_};
|
||||
if (!length_cache_dirty_) {
|
||||
return evaluated_lengths_cache_;
|
||||
}
|
||||
|
||||
const int total = evaluated_edges_size();
|
||||
evaluated_lengths_cache_.resize(total);
|
||||
|
||||
Span<float3> positions = this->evaluated_positions();
|
||||
accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_);
|
||||
|
||||
length_cache_dirty_ = false;
|
||||
return evaluated_lengths_cache_;
|
||||
}
|
||||
|
||||
static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next)
|
||||
{
|
||||
const float3 dir_prev = (middle - prev).normalized();
|
||||
const float3 dir_next = (next - middle).normalized();
|
||||
|
||||
return (dir_prev + dir_next).normalized();
|
||||
}
|
||||
|
||||
static void calculate_tangents(Span<float3> positions,
|
||||
const bool is_cyclic,
|
||||
MutableSpan<float3> tangents)
|
||||
{
|
||||
if (positions.size() == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const int i : IndexRange(1, positions.size() - 2)) {
|
||||
tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]);
|
||||
}
|
||||
|
||||
if (is_cyclic) {
|
||||
const float3 &second_to_last = positions[positions.size() - 2];
|
||||
const float3 &last = positions.last();
|
||||
const float3 &first = positions.first();
|
||||
const float3 &second = positions[1];
|
||||
tangents.first() = direction_bisect(last, first, second);
|
||||
tangents.last() = direction_bisect(second_to_last, last, first);
|
||||
}
|
||||
else {
|
||||
tangents.first() = (positions[1] - positions[0]).normalized();
|
||||
tangents.last() = (positions.last() - positions[positions.size() - 2]).normalized();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return non-owning access to the direction of the curve at each evaluated point.
|
||||
*/
|
||||
Span<float3> Spline::evaluated_tangents() const
|
||||
{
|
||||
if (!tangent_cache_dirty_) {
|
||||
return evaluated_tangents_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{tangent_cache_mutex_};
|
||||
if (!tangent_cache_dirty_) {
|
||||
return evaluated_tangents_cache_;
|
||||
}
|
||||
|
||||
const int eval_size = this->evaluated_points_size();
|
||||
evaluated_tangents_cache_.resize(eval_size);
|
||||
|
||||
Span<float3> positions = this->evaluated_positions();
|
||||
|
||||
if (eval_size == 1) {
|
||||
evaluated_tangents_cache_.first() = float3(1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
else {
|
||||
calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_);
|
||||
this->correct_end_tangents();
|
||||
}
|
||||
|
||||
tangent_cache_dirty_ = false;
|
||||
return evaluated_tangents_cache_;
|
||||
}
|
||||
|
||||
static float3 rotate_direction_around_axis(const float3 &direction,
|
||||
const float3 &axis,
|
||||
const float angle)
|
||||
{
|
||||
BLI_ASSERT_UNIT_V3(direction);
|
||||
BLI_ASSERT_UNIT_V3(axis);
|
||||
|
||||
const float3 axis_scaled = axis * float3::dot(direction, axis);
|
||||
const float3 diff = direction - axis_scaled;
|
||||
const float3 cross = float3::cross(axis, diff);
|
||||
|
||||
return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
|
||||
}
|
||||
|
||||
static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals)
|
||||
{
|
||||
for (const int i : normals.index_range()) {
|
||||
normals[i] = float3::cross(tangents[i], float3(0.0f, 0.0f, 1.0f)).normalized();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return non-owning access to the direction vectors perpendicular to the tangents at every
|
||||
* evaluated point. The method used to generate the normal vectors depends on Spline.normal_mode.
|
||||
*/
|
||||
Span<float3> Spline::evaluated_normals() const
|
||||
{
|
||||
if (!normal_cache_dirty_) {
|
||||
return evaluated_normals_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{normal_cache_mutex_};
|
||||
if (!normal_cache_dirty_) {
|
||||
return evaluated_normals_cache_;
|
||||
}
|
||||
|
||||
const int eval_size = this->evaluated_points_size();
|
||||
evaluated_normals_cache_.resize(eval_size);
|
||||
|
||||
Span<float3> tangents = evaluated_tangents();
|
||||
MutableSpan<float3> normals = evaluated_normals_cache_;
|
||||
|
||||
/* Only Z up normals are supported at the moment. */
|
||||
calculate_normals_z_up(tangents, normals);
|
||||
|
||||
/* Rotate the generated normals with the interpolated tilt data. */
|
||||
blender::fn::GVArray_Typed<float> tilts{
|
||||
this->interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(this->tilts()))};
|
||||
for (const int i : normals.index_range()) {
|
||||
normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]);
|
||||
}
|
||||
|
||||
normal_cache_dirty_ = false;
|
||||
return evaluated_normals_cache_;
|
||||
}
|
||||
|
||||
Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const
|
||||
{
|
||||
return this->lookup_evaluated_length(this->length() * factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* \note This does not support extrapolation currently.
|
||||
*/
|
||||
Spline::LookupResult Spline::lookup_evaluated_length(const float length) const
|
||||
{
|
||||
BLI_assert(length >= 0.0f && length <= this->length());
|
||||
|
||||
Span<float> lengths = this->evaluated_lengths();
|
||||
|
||||
const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length);
|
||||
const int index = offset - lengths.begin();
|
||||
const int next_index = (index == this->size() - 1) ? 0 : index + 1;
|
||||
|
||||
const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
|
||||
const float factor = (length - previous_length) / (lengths[index] - previous_length);
|
||||
|
||||
return LookupResult{index, next_index, factor};
|
||||
}
|
||||
|
||||
void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
|
||||
{
|
||||
Span<float3> positions = use_evaluated ? this->evaluated_positions() : this->positions();
|
||||
for (const float3 &position : positions) {
|
||||
minmax_v3v3_v3(min, max, position);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,478 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
using blender::Array;
|
||||
using blender::float3;
|
||||
using blender::IndexRange;
|
||||
using blender::MutableSpan;
|
||||
using blender::Span;
|
||||
|
||||
SplinePtr BezierSpline::copy() const
|
||||
{
|
||||
return std::make_unique<BezierSpline>(*this);
|
||||
}
|
||||
|
||||
int BezierSpline::size() const
|
||||
{
|
||||
const int size = positions_.size();
|
||||
BLI_assert(size == handle_types_left_.size());
|
||||
BLI_assert(size == handle_positions_left_.size());
|
||||
BLI_assert(size == handle_types_right_.size());
|
||||
BLI_assert(size == handle_positions_right_.size());
|
||||
BLI_assert(size == radii_.size());
|
||||
BLI_assert(size == tilts_.size());
|
||||
return size;
|
||||
}
|
||||
|
||||
int BezierSpline::resolution() const
|
||||
{
|
||||
return resolution_;
|
||||
}
|
||||
|
||||
void BezierSpline::set_resolution(const int value)
|
||||
{
|
||||
BLI_assert(value > 0);
|
||||
resolution_ = value;
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
void BezierSpline::add_point(const float3 position,
|
||||
const HandleType handle_type_start,
|
||||
const float3 handle_position_start,
|
||||
const HandleType handle_type_end,
|
||||
const float3 handle_position_end,
|
||||
const float radius,
|
||||
const float tilt)
|
||||
{
|
||||
handle_types_left_.append(handle_type_start);
|
||||
handle_positions_left_.append(handle_position_start);
|
||||
positions_.append(position);
|
||||
handle_types_right_.append(handle_type_end);
|
||||
handle_positions_right_.append(handle_position_end);
|
||||
radii_.append(radius);
|
||||
tilts_.append(tilt);
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
MutableSpan<float3> BezierSpline::positions()
|
||||
{
|
||||
return positions_;
|
||||
}
|
||||
Span<float3> BezierSpline::positions() const
|
||||
{
|
||||
return positions_;
|
||||
}
|
||||
MutableSpan<float> BezierSpline::radii()
|
||||
{
|
||||
return radii_;
|
||||
}
|
||||
Span<float> BezierSpline::radii() const
|
||||
{
|
||||
return radii_;
|
||||
}
|
||||
MutableSpan<float> BezierSpline::tilts()
|
||||
{
|
||||
return tilts_;
|
||||
}
|
||||
Span<float> BezierSpline::tilts() const
|
||||
{
|
||||
return tilts_;
|
||||
}
|
||||
Span<BezierSpline::HandleType> BezierSpline::handle_types_left() const
|
||||
{
|
||||
return handle_types_left_;
|
||||
}
|
||||
MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_left()
|
||||
{
|
||||
return handle_types_left_;
|
||||
}
|
||||
Span<float3> BezierSpline::handle_positions_left() const
|
||||
{
|
||||
return handle_positions_left_;
|
||||
}
|
||||
MutableSpan<float3> BezierSpline::handle_positions_left()
|
||||
{
|
||||
return handle_positions_left_;
|
||||
}
|
||||
Span<BezierSpline::HandleType> BezierSpline::handle_types_right() const
|
||||
{
|
||||
return handle_types_right_;
|
||||
}
|
||||
MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_right()
|
||||
{
|
||||
return handle_types_right_;
|
||||
}
|
||||
Span<float3> BezierSpline::handle_positions_right() const
|
||||
{
|
||||
return handle_positions_right_;
|
||||
}
|
||||
MutableSpan<float3> BezierSpline::handle_positions_right()
|
||||
{
|
||||
return handle_positions_right_;
|
||||
}
|
||||
|
||||
void BezierSpline::translate(const blender::float3 &translation)
|
||||
{
|
||||
for (float3 &position : this->positions()) {
|
||||
position += translation;
|
||||
}
|
||||
for (float3 &handle_position : this->handle_positions_left()) {
|
||||
handle_position += translation;
|
||||
}
|
||||
for (float3 &handle_position : this->handle_positions_right()) {
|
||||
handle_position += translation;
|
||||
}
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
void BezierSpline::transform(const blender::float4x4 &matrix)
|
||||
{
|
||||
for (float3 &position : this->positions()) {
|
||||
position = matrix * position;
|
||||
}
|
||||
for (float3 &handle_position : this->handle_positions_left()) {
|
||||
handle_position = matrix * handle_position;
|
||||
}
|
||||
for (float3 &handle_position : this->handle_positions_right()) {
|
||||
handle_position = matrix * handle_position;
|
||||
}
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
bool BezierSpline::point_is_sharp(const int index) const
|
||||
{
|
||||
return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) ||
|
||||
ELEM(handle_types_right_[index], HandleType::Vector, HandleType::Free);
|
||||
}
|
||||
|
||||
bool BezierSpline::segment_is_vector(const int index) const
|
||||
{
|
||||
if (index == this->size() - 1) {
|
||||
BLI_assert(is_cyclic_);
|
||||
return handle_types_right_.last() == HandleType::Vector &&
|
||||
handle_types_left_.first() == HandleType::Vector;
|
||||
}
|
||||
return handle_types_right_[index] == HandleType::Vector &&
|
||||
handle_types_left_[index + 1] == HandleType::Vector;
|
||||
}
|
||||
|
||||
void BezierSpline::mark_cache_invalid()
|
||||
{
|
||||
offset_cache_dirty_ = true;
|
||||
position_cache_dirty_ = true;
|
||||
mapping_cache_dirty_ = true;
|
||||
tangent_cache_dirty_ = true;
|
||||
normal_cache_dirty_ = true;
|
||||
length_cache_dirty_ = true;
|
||||
}
|
||||
|
||||
int BezierSpline::evaluated_points_size() const
|
||||
{
|
||||
const int points_len = this->size();
|
||||
BLI_assert(points_len > 0);
|
||||
|
||||
const int last_offset = this->control_point_offsets().last();
|
||||
if (is_cyclic_ && points_len > 1) {
|
||||
return last_offset + (this->segment_is_vector(points_len - 1) ? 0 : resolution_);
|
||||
}
|
||||
|
||||
return last_offset + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the spline is not cyclic, the direction for the first and last points is just the
|
||||
* direction formed by the corresponding handles and control points. In the unlikely situation
|
||||
* that the handles define a zero direction, fallback to using the direction defined by the
|
||||
* first and last evaluated segments already calculated in #Spline::evaluated_tangents().
|
||||
*/
|
||||
void BezierSpline::correct_end_tangents() const
|
||||
{
|
||||
if (is_cyclic_) {
|
||||
return;
|
||||
}
|
||||
|
||||
MutableSpan<float3> tangents(evaluated_tangents_cache_);
|
||||
|
||||
if (handle_positions_left_.first() != positions_.first()) {
|
||||
tangents.first() = (positions_.first() - handle_positions_left_.first()).normalized();
|
||||
}
|
||||
if (handle_positions_right_.last() != positions_.last()) {
|
||||
tangents.last() = (handle_positions_right_.last() - positions_.last()).normalized();
|
||||
}
|
||||
}
|
||||
|
||||
static void bezier_forward_difference_3d(const float3 &point_0,
|
||||
const float3 &point_1,
|
||||
const float3 &point_2,
|
||||
const float3 &point_3,
|
||||
MutableSpan<float3> result)
|
||||
{
|
||||
BLI_assert(result.size() > 0);
|
||||
const float inv_len = 1.0f / static_cast<float>(result.size());
|
||||
const float inv_len_squared = inv_len * inv_len;
|
||||
const float inv_len_cubed = inv_len_squared * inv_len;
|
||||
|
||||
const float3 rt1 = 3.0f * (point_1 - point_0) * inv_len;
|
||||
const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared;
|
||||
const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed;
|
||||
|
||||
float3 q0 = point_0;
|
||||
float3 q1 = rt1 + rt2 + rt3;
|
||||
float3 q2 = 2.0f * rt2 + 6.0f * rt3;
|
||||
float3 q3 = 6.0f * rt3;
|
||||
for (const int i : result.index_range()) {
|
||||
result[i] = q0;
|
||||
q0 += q1;
|
||||
q1 += q2;
|
||||
q2 += q3;
|
||||
}
|
||||
}
|
||||
|
||||
void BezierSpline::evaluate_bezier_segment(const int index,
|
||||
const int next_index,
|
||||
MutableSpan<float3> positions) const
|
||||
{
|
||||
if (this->segment_is_vector(index)) {
|
||||
BLI_assert(positions.size() == 1);
|
||||
positions.first() = positions_[index];
|
||||
}
|
||||
else {
|
||||
bezier_forward_difference_3d(positions_[index],
|
||||
handle_positions_right_[index],
|
||||
handle_positions_left_[next_index],
|
||||
positions_[next_index],
|
||||
positions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns access to a cache of offsets into the evaluated point array for each control point.
|
||||
* This is important because while most control point edges generate the number of edges specified
|
||||
* by the resolution, vector segments only generate one edge.
|
||||
*/
|
||||
Span<int> BezierSpline::control_point_offsets() const
|
||||
{
|
||||
if (!offset_cache_dirty_) {
|
||||
return offset_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{offset_cache_mutex_};
|
||||
if (!offset_cache_dirty_) {
|
||||
return offset_cache_;
|
||||
}
|
||||
|
||||
const int points_len = this->size();
|
||||
offset_cache_.resize(points_len);
|
||||
|
||||
MutableSpan<int> offsets = offset_cache_;
|
||||
|
||||
int offset = 0;
|
||||
for (const int i : IndexRange(points_len - 1)) {
|
||||
offsets[i] = offset;
|
||||
offset += this->segment_is_vector(i) ? 1 : resolution_;
|
||||
}
|
||||
offsets.last() = offset;
|
||||
|
||||
offset_cache_dirty_ = false;
|
||||
return offsets;
|
||||
}
|
||||
|
||||
static void calculate_mappings_linear_resolution(Span<int> offsets,
|
||||
const int size,
|
||||
const int resolution,
|
||||
const bool is_cyclic,
|
||||
MutableSpan<float> r_mappings)
|
||||
{
|
||||
const float first_segment_len_inv = 1.0f / offsets[1];
|
||||
for (const int i : IndexRange(0, offsets[1])) {
|
||||
r_mappings[i] = i * first_segment_len_inv;
|
||||
}
|
||||
|
||||
const int grain_size = std::max(2048 / resolution, 1);
|
||||
blender::parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) {
|
||||
for (const int i_control_point : range) {
|
||||
const int segment_len = offsets[i_control_point + 1] - offsets[i_control_point];
|
||||
const float segment_len_inv = 1.0f / segment_len;
|
||||
for (const int i : IndexRange(segment_len)) {
|
||||
r_mappings[offsets[i_control_point] + i] = i_control_point + i * segment_len_inv;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (is_cyclic) {
|
||||
const int last_segment_len = offsets[size - 1] - offsets[size - 2];
|
||||
const float last_segment_len_inv = 1.0f / last_segment_len;
|
||||
for (const int i : IndexRange(last_segment_len)) {
|
||||
r_mappings[offsets[size - 1] + i] = size - 1 + i * last_segment_len_inv;
|
||||
}
|
||||
}
|
||||
else {
|
||||
r_mappings.last() = size - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-owning access to an array of values containing the information necessary to
|
||||
* interpolate values from the original control points to evaluated points. The control point
|
||||
* index is the integer part of each value, and the factor used for interpolating to the next
|
||||
* control point is the remaining factional part.
|
||||
*/
|
||||
Span<float> BezierSpline::evaluated_mappings() const
|
||||
{
|
||||
if (!mapping_cache_dirty_) {
|
||||
return evaluated_mapping_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{mapping_cache_mutex_};
|
||||
if (!mapping_cache_dirty_) {
|
||||
return evaluated_mapping_cache_;
|
||||
}
|
||||
|
||||
const int size = this->size();
|
||||
const int eval_size = this->evaluated_points_size();
|
||||
evaluated_mapping_cache_.resize(eval_size);
|
||||
MutableSpan<float> mappings = evaluated_mapping_cache_;
|
||||
|
||||
if (eval_size == 1) {
|
||||
mappings.first() = 0.0f;
|
||||
mapping_cache_dirty_ = false;
|
||||
return mappings;
|
||||
}
|
||||
|
||||
Span<int> offsets = this->control_point_offsets();
|
||||
|
||||
calculate_mappings_linear_resolution(offsets, size, resolution_, is_cyclic_, mappings);
|
||||
|
||||
mapping_cache_dirty_ = false;
|
||||
return mappings;
|
||||
}
|
||||
|
||||
Span<float3> BezierSpline::evaluated_positions() const
|
||||
{
|
||||
if (!position_cache_dirty_) {
|
||||
return evaluated_position_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{position_cache_mutex_};
|
||||
if (!position_cache_dirty_) {
|
||||
return evaluated_position_cache_;
|
||||
}
|
||||
|
||||
const int eval_size = this->evaluated_points_size();
|
||||
evaluated_position_cache_.resize(eval_size);
|
||||
|
||||
MutableSpan<float3> positions = evaluated_position_cache_;
|
||||
|
||||
Span<int> offsets = this->control_point_offsets();
|
||||
BLI_assert(offsets.last() <= eval_size);
|
||||
|
||||
const int grain_size = std::max(512 / resolution_, 1);
|
||||
blender::parallel_for(IndexRange(this->size() - 1), grain_size, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
this->evaluate_bezier_segment(
|
||||
i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i]));
|
||||
}
|
||||
});
|
||||
|
||||
const int i_last = this->size() - 1;
|
||||
if (is_cyclic_) {
|
||||
this->evaluate_bezier_segment(i_last, 0, positions.slice(offsets.last(), resolution_));
|
||||
}
|
||||
else {
|
||||
/* Since evualating the bezier segment doesn't add the final point,
|
||||
* it must be added manually in the non-cyclic case. */
|
||||
positions.last() = positions_.last();
|
||||
}
|
||||
|
||||
position_cache_dirty_ = false;
|
||||
return positions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the data encoded in #evaulated_mappings into its parts-- the information necessary
|
||||
* to interpolate data from control points to evaluated points between them. The next control
|
||||
* point index result will not overflow the size of the vector.
|
||||
*/
|
||||
BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor(
|
||||
const float index_factor) const
|
||||
{
|
||||
const int points_len = this->size();
|
||||
|
||||
if (is_cyclic_) {
|
||||
if (index_factor < points_len) {
|
||||
const int index = std::floor(index_factor);
|
||||
const int next_index = (index < points_len - 1) ? index + 1 : 0;
|
||||
return InterpolationData{index, next_index, index_factor - index};
|
||||
}
|
||||
return InterpolationData{points_len - 1, 0, 1.0f};
|
||||
}
|
||||
|
||||
if (index_factor < points_len - 1) {
|
||||
const int index = std::floor(index_factor);
|
||||
const int next_index = index + 1;
|
||||
return InterpolationData{index, next_index, index_factor - index};
|
||||
}
|
||||
return InterpolationData{points_len - 2, points_len - 1, 1.0f};
|
||||
}
|
||||
|
||||
/* Use a spline argument to avoid adding this to the header. */
|
||||
template<typename T>
|
||||
static void interpolate_to_evaluated_points_impl(const BezierSpline &spline,
|
||||
const blender::VArray<T> &source_data,
|
||||
MutableSpan<T> result_data)
|
||||
{
|
||||
Span<float> mappings = spline.evaluated_mappings();
|
||||
|
||||
for (const int i : result_data.index_range()) {
|
||||
BezierSpline::InterpolationData interp = spline.interpolation_data_from_index_factor(
|
||||
mappings[i]);
|
||||
|
||||
const T &value = source_data[interp.control_point_index];
|
||||
const T &next_value = source_data[interp.next_control_point_index];
|
||||
|
||||
result_data[i] = blender::attribute_math::mix2(interp.factor, value, next_value);
|
||||
}
|
||||
}
|
||||
|
||||
blender::fn::GVArrayPtr BezierSpline::interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const
|
||||
{
|
||||
BLI_assert(source_data.size() == this->size());
|
||||
|
||||
const int eval_size = this->evaluated_points_size();
|
||||
if (eval_size == 1) {
|
||||
return source_data.shallow_copy();
|
||||
}
|
||||
|
||||
blender::fn::GVArrayPtr new_varray;
|
||||
blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(eval_size);
|
||||
interpolate_to_evaluated_points_impl<T>(*this, source_data.typed<T>(), values);
|
||||
new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
|
||||
std::move(values));
|
||||
}
|
||||
});
|
||||
|
||||
return new_varray;
|
||||
}
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
using blender::Array;
|
||||
using blender::float3;
|
||||
using blender::IndexRange;
|
||||
using blender::MutableSpan;
|
||||
using blender::Span;
|
||||
|
||||
SplinePtr NURBSpline::copy() const
|
||||
{
|
||||
return std::make_unique<NURBSpline>(*this);
|
||||
}
|
||||
|
||||
int NURBSpline::size() const
|
||||
{
|
||||
const int size = positions_.size();
|
||||
BLI_assert(size == radii_.size());
|
||||
BLI_assert(size == tilts_.size());
|
||||
BLI_assert(size == weights_.size());
|
||||
return size;
|
||||
}
|
||||
|
||||
int NURBSpline::resolution() const
|
||||
{
|
||||
return resolution_;
|
||||
}
|
||||
|
||||
void NURBSpline::set_resolution(const int value)
|
||||
{
|
||||
BLI_assert(value > 0);
|
||||
resolution_ = value;
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
uint8_t NURBSpline::order() const
|
||||
{
|
||||
return order_;
|
||||
}
|
||||
|
||||
void NURBSpline::set_order(const uint8_t value)
|
||||
{
|
||||
BLI_assert(value >= 2 && value <= 6);
|
||||
order_ = value;
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
void NURBSpline::add_point(const float3 position,
|
||||
const float radius,
|
||||
const float tilt,
|
||||
const float weight)
|
||||
{
|
||||
positions_.append(position);
|
||||
radii_.append(radius);
|
||||
tilts_.append(tilt);
|
||||
weights_.append(weight);
|
||||
knots_dirty_ = true;
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
MutableSpan<float3> NURBSpline::positions()
|
||||
{
|
||||
return positions_;
|
||||
}
|
||||
Span<float3> NURBSpline::positions() const
|
||||
{
|
||||
return positions_;
|
||||
}
|
||||
MutableSpan<float> NURBSpline::radii()
|
||||
{
|
||||
return radii_;
|
||||
}
|
||||
Span<float> NURBSpline::radii() const
|
||||
{
|
||||
return radii_;
|
||||
}
|
||||
MutableSpan<float> NURBSpline::tilts()
|
||||
{
|
||||
return tilts_;
|
||||
}
|
||||
Span<float> NURBSpline::tilts() const
|
||||
{
|
||||
return tilts_;
|
||||
}
|
||||
MutableSpan<float> NURBSpline::weights()
|
||||
{
|
||||
return weights_;
|
||||
}
|
||||
Span<float> NURBSpline::weights() const
|
||||
{
|
||||
return weights_;
|
||||
}
|
||||
|
||||
void NURBSpline::mark_cache_invalid()
|
||||
{
|
||||
basis_cache_dirty_ = true;
|
||||
position_cache_dirty_ = true;
|
||||
tangent_cache_dirty_ = true;
|
||||
normal_cache_dirty_ = true;
|
||||
length_cache_dirty_ = true;
|
||||
}
|
||||
|
||||
int NURBSpline::evaluated_points_size() const
|
||||
{
|
||||
if (!this->check_valid_size_and_order()) {
|
||||
return 0;
|
||||
}
|
||||
return resolution_ * this->segments_size();
|
||||
}
|
||||
|
||||
void NURBSpline::correct_end_tangents() const
|
||||
{
|
||||
}
|
||||
|
||||
bool NURBSpline::check_valid_size_and_order() const
|
||||
{
|
||||
if (this->size() < order_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_cyclic_ && this->knots_mode == KnotsMode::Bezier) {
|
||||
if (order_ == 4) {
|
||||
if (this->size() < 5) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (order_ != 3) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int NURBSpline::knots_size() const
|
||||
{
|
||||
const int size = this->size() + order_;
|
||||
return is_cyclic_ ? size + order_ - 1 : size;
|
||||
}
|
||||
|
||||
void NURBSpline::calculate_knots() const
|
||||
{
|
||||
const KnotsMode mode = this->knots_mode;
|
||||
const int length = this->size();
|
||||
const int order = order_;
|
||||
|
||||
knots_.resize(this->knots_size());
|
||||
|
||||
MutableSpan<float> knots = knots_;
|
||||
|
||||
if (mode == NURBSpline::KnotsMode::Normal || is_cyclic_) {
|
||||
for (const int i : knots.index_range()) {
|
||||
knots[i] = static_cast<float>(i);
|
||||
}
|
||||
}
|
||||
else if (mode == NURBSpline::KnotsMode::EndPoint) {
|
||||
float k = 0.0f;
|
||||
for (const int i : IndexRange(1, knots.size())) {
|
||||
knots[i - 1] = k;
|
||||
if (i >= order && i <= length) {
|
||||
k += 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mode == NURBSpline::KnotsMode::Bezier) {
|
||||
BLI_assert(ELEM(order, 3, 4));
|
||||
if (order == 3) {
|
||||
float k = 0.6f;
|
||||
for (const int i : knots.index_range()) {
|
||||
if (i >= order && i <= length) {
|
||||
k += 0.5f;
|
||||
}
|
||||
knots[i] = std::floor(k);
|
||||
}
|
||||
}
|
||||
else {
|
||||
float k = 0.34f;
|
||||
for (const int i : knots.index_range()) {
|
||||
knots[i] = std::floor(k);
|
||||
k += 1.0f / 3.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_cyclic_) {
|
||||
const int b = length + order - 1;
|
||||
if (order > 2) {
|
||||
for (const int i : IndexRange(1, order - 2)) {
|
||||
if (knots[b] != knots[b - i]) {
|
||||
if (i == order - 1) {
|
||||
knots[length + order - 2] += 1.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int c = order;
|
||||
for (int i = b; i < this->knots_size(); i++) {
|
||||
knots[i] = knots[i - 1] + (knots[c] - knots[c - 1]);
|
||||
c--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Span<float> NURBSpline::knots() const
|
||||
{
|
||||
if (!knots_dirty_) {
|
||||
BLI_assert(knots_.size() == this->size() + order_);
|
||||
return knots_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{knots_mutex_};
|
||||
if (!knots_dirty_) {
|
||||
BLI_assert(knots_.size() == this->size() + order_);
|
||||
return knots_;
|
||||
}
|
||||
|
||||
this->calculate_knots();
|
||||
|
||||
knots_dirty_ = false;
|
||||
|
||||
return knots_;
|
||||
}
|
||||
|
||||
static void calculate_basis_for_point(const float parameter,
|
||||
const int points_len,
|
||||
const int order,
|
||||
Span<float> knots,
|
||||
MutableSpan<float> basis_buffer,
|
||||
NURBSpline::BasisCache &basis_cache)
|
||||
{
|
||||
/* Clamp parameter due to floating point inaccuracy. TODO: Look into using doubles. */
|
||||
const float t = std::clamp(parameter, knots[0], knots[points_len + order - 1]);
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
for (const int i : IndexRange(points_len + order - 1)) {
|
||||
const bool knots_equal = knots[i] == knots[i + 1];
|
||||
if (knots_equal || t < knots[i] || t > knots[i + 1]) {
|
||||
basis_buffer[i] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
basis_buffer[i] = 1.0f;
|
||||
start = std::max(i - order - 1, 0);
|
||||
end = i;
|
||||
basis_buffer.slice(i + 1, points_len + order - 1 - i).fill(0.0f);
|
||||
break;
|
||||
}
|
||||
basis_buffer[points_len + order - 1] = 0.0f;
|
||||
|
||||
for (const int i_order : IndexRange(2, order - 1)) {
|
||||
if (end + i_order >= points_len + order) {
|
||||
end = points_len + order - 1 - i_order;
|
||||
}
|
||||
for (const int i : IndexRange(start, end - start + 1)) {
|
||||
float new_basis = 0.0f;
|
||||
if (basis_buffer[i] != 0.0f) {
|
||||
new_basis += ((t - knots[i]) * basis_buffer[i]) / (knots[i + i_order - 1] - knots[i]);
|
||||
}
|
||||
|
||||
if (basis_buffer[i + 1] != 0.0f) {
|
||||
new_basis += ((knots[i + i_order] - t) * basis_buffer[i + 1]) /
|
||||
(knots[i + i_order] - knots[i + 1]);
|
||||
}
|
||||
|
||||
basis_buffer[i] = new_basis;
|
||||
}
|
||||
}
|
||||
|
||||
/* Shrink the range of calculated values to avoid storing unnecessary zeros. */
|
||||
while (basis_buffer[start] == 0.0f && start < end) {
|
||||
start++;
|
||||
}
|
||||
while (basis_buffer[end] == 0.0f && end > start) {
|
||||
end--;
|
||||
}
|
||||
|
||||
basis_cache.weights.clear();
|
||||
basis_cache.weights.extend(basis_buffer.slice(start, end - start + 1));
|
||||
basis_cache.start_index = start;
|
||||
}
|
||||
|
||||
void NURBSpline::calculate_basis_cache() const
|
||||
{
|
||||
if (!basis_cache_dirty_) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock{basis_cache_mutex_};
|
||||
if (!basis_cache_dirty_) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int points_len = this->size();
|
||||
const int eval_size = this->evaluated_points_size();
|
||||
BLI_assert(this->evaluated_edges_size() > 0);
|
||||
basis_cache_.resize(eval_size);
|
||||
|
||||
const int order = this->order();
|
||||
Span<float> control_weights = this->weights();
|
||||
Span<float> knots = this->knots();
|
||||
|
||||
MutableSpan<BasisCache> basis_cache(basis_cache_);
|
||||
|
||||
/* This buffer is reused by each basis calculation to store temporary values.
|
||||
* Theoretically it could be optimized away in the future. */
|
||||
Array<float> basis_buffer(this->knots_size());
|
||||
|
||||
const float start = knots[order - 1];
|
||||
const float end = is_cyclic_ ? knots[points_len + order - 1] : knots[points_len];
|
||||
const float step = (end - start) / this->evaluated_edges_size();
|
||||
float parameter = start;
|
||||
for (const int i : IndexRange(eval_size)) {
|
||||
BasisCache &basis = basis_cache[i];
|
||||
calculate_basis_for_point(
|
||||
parameter, points_len + (is_cyclic_ ? order - 1 : 0), order, knots, basis_buffer, basis);
|
||||
BLI_assert(basis.weights.size() <= order);
|
||||
|
||||
for (const int j : basis.weights.index_range()) {
|
||||
const int point_index = (basis.start_index + j) % points_len;
|
||||
basis.weights[j] *= control_weights[point_index];
|
||||
}
|
||||
|
||||
parameter += step;
|
||||
}
|
||||
|
||||
basis_cache_dirty_ = false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void interpolate_to_evaluated_points_impl(Span<NURBSpline::BasisCache> weights,
|
||||
const blender::VArray<T> &source_data,
|
||||
MutableSpan<T> result_data)
|
||||
{
|
||||
const int points_len = source_data.size();
|
||||
BLI_assert(result_data.size() == weights.size());
|
||||
blender::attribute_math::DefaultMixer<T> mixer(result_data);
|
||||
|
||||
for (const int i : result_data.index_range()) {
|
||||
Span<float> point_weights = weights[i].weights;
|
||||
const int start_index = weights[i].start_index;
|
||||
|
||||
for (const int j : point_weights.index_range()) {
|
||||
const int point_index = (start_index + j) % points_len;
|
||||
mixer.mix_in(i, source_data[point_index], point_weights[j]);
|
||||
}
|
||||
}
|
||||
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
blender::fn::GVArrayPtr NURBSpline::interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const
|
||||
{
|
||||
BLI_assert(source_data.size() == this->size());
|
||||
|
||||
this->calculate_basis_cache();
|
||||
Span<BasisCache> weights(basis_cache_);
|
||||
|
||||
blender::fn::GVArrayPtr new_varray;
|
||||
blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
|
||||
Array<T> values(this->evaluated_points_size());
|
||||
interpolate_to_evaluated_points_impl<T>(weights, source_data.typed<T>(), values);
|
||||
new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
|
||||
std::move(values));
|
||||
}
|
||||
});
|
||||
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
Span<float3> NURBSpline::evaluated_positions() const
|
||||
{
|
||||
if (!position_cache_dirty_) {
|
||||
return evaluated_position_cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock{position_cache_mutex_};
|
||||
if (!position_cache_dirty_) {
|
||||
return evaluated_position_cache_;
|
||||
}
|
||||
|
||||
const int eval_size = this->evaluated_points_size();
|
||||
evaluated_position_cache_.resize(eval_size);
|
||||
|
||||
blender::fn::GVArray_Typed<float3> evaluated_positions{
|
||||
this->interpolate_to_evaluated_points(blender::fn::GVArray_For_Span<float3>(positions_))};
|
||||
|
||||
evaluated_positions->materialize(evaluated_position_cache_);
|
||||
|
||||
position_cache_dirty_ = false;
|
||||
return evaluated_position_cache_;
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
using blender::float3;
|
||||
using blender::MutableSpan;
|
||||
using blender::Span;
|
||||
|
||||
SplinePtr PolySpline::copy() const
|
||||
{
|
||||
return std::make_unique<PolySpline>(*this);
|
||||
}
|
||||
|
||||
int PolySpline::size() const
|
||||
{
|
||||
const int size = positions_.size();
|
||||
BLI_assert(size == radii_.size());
|
||||
BLI_assert(size == tilts_.size());
|
||||
return size;
|
||||
}
|
||||
|
||||
void PolySpline::add_point(const float3 position, const float radius, const float tilt)
|
||||
{
|
||||
positions_.append(position);
|
||||
radii_.append(radius);
|
||||
tilts_.append(tilt);
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
MutableSpan<float3> PolySpline::positions()
|
||||
{
|
||||
return positions_;
|
||||
}
|
||||
Span<float3> PolySpline::positions() const
|
||||
{
|
||||
return positions_;
|
||||
}
|
||||
MutableSpan<float> PolySpline::radii()
|
||||
{
|
||||
return radii_;
|
||||
}
|
||||
Span<float> PolySpline::radii() const
|
||||
{
|
||||
return radii_;
|
||||
}
|
||||
MutableSpan<float> PolySpline::tilts()
|
||||
{
|
||||
return tilts_;
|
||||
}
|
||||
Span<float> PolySpline::tilts() const
|
||||
{
|
||||
return tilts_;
|
||||
}
|
||||
|
||||
void PolySpline::mark_cache_invalid()
|
||||
{
|
||||
tangent_cache_dirty_ = true;
|
||||
normal_cache_dirty_ = true;
|
||||
length_cache_dirty_ = true;
|
||||
}
|
||||
|
||||
int PolySpline::evaluated_points_size() const
|
||||
{
|
||||
return this->size();
|
||||
}
|
||||
|
||||
void PolySpline::correct_end_tangents() const
|
||||
{
|
||||
}
|
||||
|
||||
Span<float3> PolySpline::evaluated_positions() const
|
||||
{
|
||||
return this->positions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Poly spline interpolation from control points to evaluated points is a special case, since
|
||||
* the result data is the same as the input data. This function returns a GVArray that points to
|
||||
* the original data. Therefore the lifetime of the returned virtual array must not be longer than
|
||||
* the source data.
|
||||
*/
|
||||
blender::fn::GVArrayPtr PolySpline::interpolate_to_evaluated_points(
|
||||
const blender::fn::GVArray &source_data) const
|
||||
{
|
||||
BLI_assert(source_data.size() == this->size());
|
||||
|
||||
return source_data.shallow_copy();
|
||||
}
|
|
@ -171,6 +171,9 @@ static void text_free_data(ID *id)
|
|||
|
||||
static void text_blend_write(BlendWriter *writer, ID *id, const void *id_address)
|
||||
{
|
||||
if (id->us < 1 && !BLO_write_is_undo(writer)) {
|
||||
return;
|
||||
}
|
||||
Text *text = (Text *)id;
|
||||
|
||||
/* Note: we are clearing local temp data here, *not* the flag in the actual 'real' ID. */
|
||||
|
@ -231,8 +234,6 @@ static void text_blend_read_data(BlendDataReader *reader, ID *id)
|
|||
}
|
||||
|
||||
text->flags = (text->flags) & ~TXT_ISEXT;
|
||||
|
||||
id_us_ensure_real(&text->id);
|
||||
}
|
||||
|
||||
IDTypeInfo IDType_ID_TXT = {
|
||||
|
@ -293,8 +294,10 @@ Text *BKE_text_add(Main *bmain, const char *name)
|
|||
Text *ta;
|
||||
|
||||
ta = BKE_id_new(bmain, ID_TXT, name);
|
||||
/* Texts always have 'real' user (see also read code). */
|
||||
id_us_ensure_real(&ta->id);
|
||||
/* Texts have no users by default... Set the fake user flag to ensure that this text block
|
||||
* doesn't get deleted by default when cleaning up data blocks. */
|
||||
id_us_min(&ta->id);
|
||||
id_fake_user_set(&ta->id);
|
||||
|
||||
return ta;
|
||||
}
|
||||
|
@ -468,7 +471,7 @@ bool BKE_text_reload(Text *text)
|
|||
* \param is_internal: If \a true, this text data-block only exists in memory,
|
||||
* not as a file on disk.
|
||||
*
|
||||
* \note text data-blocks have no user by default, only the 'real user' flag.
|
||||
* \note text data-blocks have no real user but have 'fake user' enabled by default
|
||||
*/
|
||||
Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const bool is_internal)
|
||||
{
|
||||
|
@ -489,9 +492,8 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const
|
|||
}
|
||||
|
||||
ta = BKE_libblock_alloc(bmain, ID_TXT, BLI_path_basename(filepath_abs), 0);
|
||||
/* Texts have no user by default... Only the 'real' user flag. */
|
||||
id_us_ensure_real(&ta->id);
|
||||
id_us_min(&ta->id);
|
||||
id_fake_user_set(&ta->id);
|
||||
|
||||
BLI_listbase_clear(&ta->lines);
|
||||
ta->curl = ta->sell = NULL;
|
||||
|
|
|
@ -245,6 +245,13 @@ struct float3 {
|
|||
return result;
|
||||
}
|
||||
|
||||
static float3 cross(const float3 &a, const float3 &b)
|
||||
{
|
||||
float3 result;
|
||||
cross_v3_v3v3(result, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
static float3 project(const float3 &a, const float3 &b)
|
||||
{
|
||||
float3 result;
|
||||
|
|
|
@ -45,6 +45,37 @@ struct float4x4 {
|
|||
return mat;
|
||||
}
|
||||
|
||||
static float4x4 from_normalized_axis_data(const float3 location,
|
||||
const float3 forward,
|
||||
const float3 up)
|
||||
{
|
||||
BLI_ASSERT_UNIT_V3(forward);
|
||||
BLI_ASSERT_UNIT_V3(up);
|
||||
float4x4 matrix;
|
||||
const float3 cross = float3::cross(forward, up);
|
||||
matrix.values[0][0] = forward.x;
|
||||
matrix.values[1][0] = cross.x;
|
||||
matrix.values[2][0] = up.x;
|
||||
matrix.values[3][0] = location.x;
|
||||
|
||||
matrix.values[0][1] = forward.y;
|
||||
matrix.values[1][1] = cross.y;
|
||||
matrix.values[2][1] = up.y;
|
||||
matrix.values[3][1] = location.y;
|
||||
|
||||
matrix.values[0][2] = forward.z;
|
||||
matrix.values[1][2] = cross.z;
|
||||
matrix.values[2][2] = up.z;
|
||||
matrix.values[3][2] = location.z;
|
||||
|
||||
matrix.values[0][3] = 0.0f;
|
||||
matrix.values[1][3] = 0.0f;
|
||||
matrix.values[2][3] = 0.0f;
|
||||
matrix.values[3][3] = 1.0f;
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
static float4x4 identity()
|
||||
{
|
||||
float4x4 mat;
|
||||
|
@ -116,6 +147,19 @@ struct float4x4 {
|
|||
return scale;
|
||||
}
|
||||
|
||||
void apply_scale(const float scale)
|
||||
{
|
||||
values[0][0] *= scale;
|
||||
values[0][1] *= scale;
|
||||
values[0][2] *= scale;
|
||||
values[1][0] *= scale;
|
||||
values[1][1] *= scale;
|
||||
values[1][2] *= scale;
|
||||
values[2][0] *= scale;
|
||||
values[2][1] *= scale;
|
||||
values[2][2] *= scale;
|
||||
}
|
||||
|
||||
float4x4 inverted() const
|
||||
{
|
||||
float4x4 result;
|
||||
|
|
|
@ -621,19 +621,23 @@ class Map {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility iterator that reduces the amount of code when implementing the actual iterators.
|
||||
* This uses the "curiously recurring template pattern" (CRTP).
|
||||
*/
|
||||
template<typename SubIterator> struct BaseIterator {
|
||||
/* Common base class for all iterators below. */
|
||||
struct BaseIterator {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
protected:
|
||||
/* We could have separate base iterators for const and non-const iterators, but that would add
|
||||
* more complexity than benefits right now. */
|
||||
Slot *slots_;
|
||||
int64_t total_slots_;
|
||||
int64_t current_slot_;
|
||||
|
||||
BaseIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
|
||||
friend Map;
|
||||
|
||||
public:
|
||||
BaseIterator(const Slot *slots, const int64_t total_slots, const int64_t current_slot)
|
||||
: slots_(const_cast<Slot *>(slots)), total_slots_(total_slots), current_slot_(current_slot)
|
||||
{
|
||||
}
|
||||
|
@ -667,11 +671,29 @@ class Map {
|
|||
return !(a != b);
|
||||
}
|
||||
|
||||
protected:
|
||||
Slot ¤t_slot() const
|
||||
{
|
||||
return slots_[current_slot_];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A utility iterator that reduces the amount of code when implementing the actual iterators.
|
||||
* This uses the "curiously recurring template pattern" (CRTP).
|
||||
*/
|
||||
template<typename SubIterator> class BaseIteratorRange : public BaseIterator {
|
||||
public:
|
||||
BaseIteratorRange(const Slot *slots, int64_t total_slots, int64_t current_slot)
|
||||
: BaseIterator(slots, total_slots, current_slot)
|
||||
{
|
||||
}
|
||||
|
||||
SubIterator begin() const
|
||||
{
|
||||
for (int64_t i = 0; i < total_slots_; i++) {
|
||||
if (slots_[i].is_occupied()) {
|
||||
return SubIterator(slots_, total_slots_, i);
|
||||
for (int64_t i = 0; i < this->total_slots_; i++) {
|
||||
if (this->slots_[i].is_occupied()) {
|
||||
return SubIterator(this->slots_, this->total_slots_, i);
|
||||
}
|
||||
}
|
||||
return this->end();
|
||||
|
@ -679,23 +701,18 @@ class Map {
|
|||
|
||||
SubIterator end() const
|
||||
{
|
||||
return SubIterator(slots_, total_slots_, total_slots_);
|
||||
}
|
||||
|
||||
Slot ¤t_slot() const
|
||||
{
|
||||
return slots_[current_slot_];
|
||||
return SubIterator(this->slots_, this->total_slots_, this->total_slots_);
|
||||
}
|
||||
};
|
||||
|
||||
class KeyIterator final : public BaseIterator<KeyIterator> {
|
||||
class KeyIterator final : public BaseIteratorRange<KeyIterator> {
|
||||
public:
|
||||
using value_type = Key;
|
||||
using pointer = const Key *;
|
||||
using reference = const Key &;
|
||||
|
||||
KeyIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
|
||||
: BaseIterator<KeyIterator>(slots, total_slots, current_slot)
|
||||
: BaseIteratorRange<KeyIterator>(slots, total_slots, current_slot)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -705,14 +722,14 @@ class Map {
|
|||
}
|
||||
};
|
||||
|
||||
class ValueIterator final : public BaseIterator<ValueIterator> {
|
||||
class ValueIterator final : public BaseIteratorRange<ValueIterator> {
|
||||
public:
|
||||
using value_type = Value;
|
||||
using pointer = const Value *;
|
||||
using reference = const Value &;
|
||||
|
||||
ValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
|
||||
: BaseIterator<ValueIterator>(slots, total_slots, current_slot)
|
||||
: BaseIteratorRange<ValueIterator>(slots, total_slots, current_slot)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -722,14 +739,14 @@ class Map {
|
|||
}
|
||||
};
|
||||
|
||||
class MutableValueIterator final : public BaseIterator<MutableValueIterator> {
|
||||
class MutableValueIterator final : public BaseIteratorRange<MutableValueIterator> {
|
||||
public:
|
||||
using value_type = Value;
|
||||
using pointer = Value *;
|
||||
using reference = Value &;
|
||||
|
||||
MutableValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
|
||||
: BaseIterator<MutableValueIterator>(slots, total_slots, current_slot)
|
||||
MutableValueIterator(Slot *slots, int64_t total_slots, int64_t current_slot)
|
||||
: BaseIteratorRange<MutableValueIterator>(slots, total_slots, current_slot)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -754,14 +771,14 @@ class Map {
|
|||
}
|
||||
};
|
||||
|
||||
class ItemIterator final : public BaseIterator<ItemIterator> {
|
||||
class ItemIterator final : public BaseIteratorRange<ItemIterator> {
|
||||
public:
|
||||
using value_type = Item;
|
||||
using pointer = Item *;
|
||||
using reference = Item &;
|
||||
|
||||
ItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
|
||||
: BaseIterator<ItemIterator>(slots, total_slots, current_slot)
|
||||
: BaseIteratorRange<ItemIterator>(slots, total_slots, current_slot)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -772,14 +789,14 @@ class Map {
|
|||
}
|
||||
};
|
||||
|
||||
class MutableItemIterator final : public BaseIterator<MutableItemIterator> {
|
||||
class MutableItemIterator final : public BaseIteratorRange<MutableItemIterator> {
|
||||
public:
|
||||
using value_type = MutableItem;
|
||||
using pointer = MutableItem *;
|
||||
using reference = MutableItem &;
|
||||
|
||||
MutableItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
|
||||
: BaseIterator<MutableItemIterator>(slots, total_slots, current_slot)
|
||||
MutableItemIterator(Slot *slots, int64_t total_slots, int64_t current_slot)
|
||||
: BaseIteratorRange<MutableItemIterator>(slots, total_slots, current_slot)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -839,6 +856,19 @@ class Map {
|
|||
return MutableItemIterator(slots_.data(), slots_.size(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the key-value-pair that the iterator is currently pointing at.
|
||||
* It is valid to call this method while iterating over the map. However, after this method has
|
||||
* been called, the removed element must not be accessed anymore.
|
||||
*/
|
||||
void remove(const BaseIterator &iterator)
|
||||
{
|
||||
Slot &slot = iterator.current_slot();
|
||||
BLI_assert(slot.is_occupied());
|
||||
slot.remove();
|
||||
removed_slots_++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print common statistics like size and collision count. This is useful for debugging purposes.
|
||||
*/
|
||||
|
|
|
@ -119,10 +119,10 @@ float dist_signed_to_plane_v3(const float p[3], const float plane[4]);
|
|||
float dist_to_plane_v3(const float p[3], const float plane[4]);
|
||||
|
||||
/* plane3 versions */
|
||||
float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[4]);
|
||||
float dist_squared_to_plane3_v3(const float p[3], const float plane[4]);
|
||||
float dist_signed_to_plane3_v3(const float p[3], const float plane[4]);
|
||||
float dist_to_plane3_v3(const float p[3], const float plane[4]);
|
||||
float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[3]);
|
||||
float dist_squared_to_plane3_v3(const float p[3], const float plane[3]);
|
||||
float dist_signed_to_plane3_v3(const float p[3], const float plane[3]);
|
||||
float dist_to_plane3_v3(const float p[3], const float plane[3]);
|
||||
|
||||
float dist_squared_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]);
|
||||
float dist_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]);
|
||||
|
|
|
@ -41,7 +41,7 @@ bool BLI_eigen_solve_selfadjoint_m3(const float m3[3][3],
|
|||
float r_eigen_values[3],
|
||||
float r_eigen_vectors[3][3]);
|
||||
|
||||
void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[], float r_V[3][3]);
|
||||
void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[3], float r_V[3][3]);
|
||||
|
||||
/***************************** Simple Solvers ************************************/
|
||||
|
||||
|
|
|
@ -357,6 +357,9 @@ IMesh trimesh_nary_intersect(const IMesh &tm_in,
|
|||
bool use_self,
|
||||
IMeshArena *arena);
|
||||
|
||||
/** Return an IMesh that is a triangulation of a mesh with general polygonal faces. */
|
||||
IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena);
|
||||
|
||||
/** This has the side effect of populating verts in the #IMesh. */
|
||||
void write_obj_mesh(IMesh &m, const std::string &objname);
|
||||
|
||||
|
|
|
@ -397,6 +397,23 @@ class VectorSet {
|
|||
return this->index_of_try__impl(key, hash_(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index of the key in the vector. If the key is not in the set, add it and return its
|
||||
* index.
|
||||
*/
|
||||
int64_t index_of_or_add(const Key &key)
|
||||
{
|
||||
return this->index_of_or_add_as(key);
|
||||
}
|
||||
int64_t index_of_or_add(Key &&key)
|
||||
{
|
||||
return this->index_of_or_add_as(std::move(key));
|
||||
}
|
||||
template<typename ForwardKey> int64_t index_of_or_add_as(ForwardKey &&key)
|
||||
{
|
||||
return this->index_of_or_add__impl(std::forward<ForwardKey>(key), hash_(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer to the beginning of the array containing all keys.
|
||||
*/
|
||||
|
@ -483,6 +500,14 @@ class VectorSet {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all keys from the vector set.
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
this->noexcept_reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of collisions that the probing strategy has to go through to find the key or
|
||||
* determine that it is not in the set.
|
||||
|
@ -652,6 +677,26 @@ class VectorSet {
|
|||
VECTOR_SET_SLOT_PROBING_END();
|
||||
}
|
||||
|
||||
template<typename ForwardKey>
|
||||
int64_t index_of_or_add__impl(ForwardKey &&key, const uint64_t hash)
|
||||
{
|
||||
this->ensure_can_add();
|
||||
|
||||
VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
|
||||
if (slot.contains(key, is_equal_, hash, keys_)) {
|
||||
return slot.index();
|
||||
}
|
||||
if (slot.is_empty()) {
|
||||
const int64_t index = this->size();
|
||||
new (keys_ + index) Key(std::forward<ForwardKey>(key));
|
||||
slot.occupy(index, hash);
|
||||
occupied_and_removed_slots_++;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
VECTOR_SET_SLOT_PROBING_END();
|
||||
}
|
||||
|
||||
Key pop__impl()
|
||||
{
|
||||
BLI_assert(this->size() > 0);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue