Merge branch 'master' into temp_bmesh_multires

This commit is contained in:
Joseph Eagar 2021-05-05 08:52:14 -07:00
commit 6c3b29a14c
212 changed files with 7070 additions and 2269 deletions

View File

@ -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')

View File

@ -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)

View File

@ -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)

View File

@ -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]

View File

@ -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')

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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()

View File

@ -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):

View File

@ -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();
}

View File

@ -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);

View File

@ -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,

View File

@ -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());

View File

@ -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

View File

@ -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; \
} \

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -79,6 +79,7 @@ Geometry::Geometry(const NodeType *node_type, const Type type)
Geometry::~Geometry()
{
dereference_all_used_nodes();
delete bvh;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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 */

View File

@ -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)

View File

@ -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;
}

View File

@ -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();

View File

@ -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;

View File

@ -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:

View File

@ -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.

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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",

View File

@ -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):

View File

@ -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)

View File

@ -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"),
)

View File

@ -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))

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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':

View File

@ -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"),

View File

@ -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):

View File

@ -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);

View File

@ -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.
*

View File

@ -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

View 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

View File

@ -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;

View File

@ -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.

View File

@ -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,

View File

@ -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,

View File

@ -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
/** \} */

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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. */

View File

@ -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);

View File

@ -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);

View File

@ -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. */

View File

@ -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:

View File

@ -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;

View File

@ -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) */

View File

@ -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;
}
/** \} */

View File

@ -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;
}
}

View File

@ -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;
}
/** \} */

View File

@ -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_;
}

View File

@ -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();
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -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;
}

View File

@ -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);

View File

@ -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)));

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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)

View File

@ -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) {

View File

@ -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

View File

@ -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();

View File

@ -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.
*/

View File

@ -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;
}
}
}

View File

@ -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],

View File

@ -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;

View File

@ -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);

View File

@ -499,7 +499,6 @@ static void ccd_mesh_free(ccd_Mesh *ccdm)
}
MEM_freeN(ccdm->mima);
MEM_freeN(ccdm);
ccdm = NULL;
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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_;
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 &current_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 &current_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.
*/

View File

@ -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]);

View File

@ -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 ************************************/

View File

@ -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);

View File

@ -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