Merge branch 'master' into sculpt-dev

This commit is contained in:
Pablo Dobarro 2021-03-02 17:05:00 +01:00
commit f2024f7d99
49 changed files with 2007 additions and 1346 deletions

View File

@ -107,11 +107,7 @@ def get_argspec(func, strip_self=True, doc=None, source=None):
try:
func = func.__func__
except AttributeError:
try:
# py 2.X
func = func.im_func
except AttributeError:
pass
pass
# is callable?
if not hasattr(func, '__call__'):
return ''
@ -141,14 +137,10 @@ def get_argspec(func, strip_self=True, doc=None, source=None):
argspec = inspect.formatargspec(*inspect.getfullargspec(func))
except:
try:
# py 2.X
argspec = inspect.formatargspec(*inspect.getargspec(func))
argspec = inspect.formatargvalues(
*inspect.getargvalues(func))
except:
try:
argspec = inspect.formatargvalues(
*inspect.getargvalues(func))
except:
argspec = ''
argspec = ''
if strip_self:
argspec = argspec.replace('self, ', '')
return argspec

View File

@ -4470,6 +4470,15 @@ def km_sculpt(params):
{"properties": [("mode", 'INVERT')]}),
("sculpt.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("mode", 'SMOOTH')]}),
# Expand
("sculpt.expand", {"type": 'A', "value": 'PRESS', "shift": True},
{"properties": [("target", "MASK"), ("falloff_type", "GEODESIC"), ("invert", True)]}),
("sculpt.expand", {"type": 'A', "value": 'PRESS', "shift": True, "alt": True},
{"properties": [("target", "MASK"), ("falloff_type", "NORMALS"), ("invert", False)]}),
("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True},
{"properties": [("target", "FACE_SETS"), ("falloff_type", "GEODESIC"), ("invert", False), ("use_modify_active", False)]}),
("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True, "alt": True},
{"properties": [("target", "FACE_SETS"), ("falloff_type", "BOUNDARY_FACE_SET"),("invert", False), ("use_modify_active", True)]}),
# Partial Visibility Show/hide
("sculpt.face_set_change_visibility", {"type": 'H', "value": 'PRESS'},
{"properties": [("mode", 'TOGGLE')]}),
@ -4477,9 +4486,6 @@ def km_sculpt(params):
{"properties": [("mode", 'HIDE_ACTIVE')]}),
("sculpt.face_set_change_visibility", {"type": 'H', "value": 'PRESS', "alt": True},
{"properties": [("mode", 'SHOW_ALL')]}),
## ("sculpt.mask_expand", {"type": 'W', "value": 'PRESS', "shift": True},
## {"properties": [("use_normals", False), ("keep_previous_mask", False), ("invert", False), ("smooth_iterations", 0), ("create_face_set", True)]}),
("sculpt.face_set_edit", {"type": 'W', "value": 'PRESS', "ctrl": True},
{"properties": [("mode", 'GROW')]}),
("sculpt.face_set_edit", {"type": 'W', "value": 'PRESS', "ctrl": True, "alt": True},
@ -4500,18 +4506,6 @@ def km_sculpt(params):
("paint.mask_lasso_gesture", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("wm.context_toggle", {"type": 'M', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'scene.tool_settings.sculpt.show_mask')]}),
## ("sculpt.mask_expand", {"type": 'A', "value": 'PRESS', "shift": True},
## {"properties": [("use_normals", False), ("keep_previous_mask", False), ("invert", True), ("smooth_iterations", 2), ("create_face_set", False)]}),
##("sculpt.mask_expand", {"type": 'A', "value": 'PRESS', "shift": True, 'alt': True},
## {"properties": [("use_normals", True), ("keep_previous_mask", True), ("invert", False), ("smooth_iterations", 0), ("create_face_set", False)]}),
("sculpt.expand", {"type": 'A', "value": 'PRESS', "shift": True},
{"properties": [("target", "MASK"), ("falloff_type", "GEODESIC"), ("invert", True)]}),
("sculpt.expand", {"type": 'A', "value": 'PRESS', "shift": True, "alt": True},
{"properties": [("target", "MASK"), ("falloff_type", "NORMALS"), ("invert", False)]}),
("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True},
{"properties": [("target", "FACE_SETS"), ("falloff_type", "GEODESIC"), ("invert", False), ("use_modify_active", False)]}),
("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True, "alt": True},
{"properties": [("target", "FACE_SETS"), ("falloff_type", "BOUNDARY_FACE_SET"),("invert", False), ("use_modify_active", True)]}),
# Dynamic topology
("sculpt.dynamic_topology_toggle", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
("sculpt.dyntopo_detail_size_edit", {"type": 'D', "value": 'PRESS', "shift": True}, None),
@ -5612,6 +5606,8 @@ def km_sculpt_expand_modal(_params):
return keymap
# Fallback for gizmos that don't have custom a custom key-map.
def km_generic_gizmo(_params):
keymap = (

View File

@ -38,6 +38,8 @@ struct Object;
struct RenderResult;
struct CryptomatteSession *BKE_cryptomatte_init(void);
struct CryptomatteSession *BKE_cryptomatte_init_from_render_result(
const struct RenderResult *render_result);
void BKE_cryptomatte_free(struct CryptomatteSession *session);
void BKE_cryptomatte_add_layer(struct CryptomatteSession *session, const char *layer_name);

View File

@ -86,4 +86,21 @@ struct CryptomatteLayer {
std::optional<std::string> operator[](float encoded_hash) const;
};
struct CryptomatteStampDataCallbackData {
struct CryptomatteSession *session;
blender::Map<std::string, std::string> hash_to_layer_name;
/**
* Extract the hash from a stamp data key.
*
* Cryptomatte keys are formatted as "cryptomatte/{layer_hash}/{attribute}".
*/
static blender::StringRef extract_layer_hash(blender::StringRefNull key);
/* C type callback function (StampCallback). */
static void extract_layer_names(void *_data, const char *propname, char *propvalue, int len);
/* C type callback function (StampCallback). */
static void extract_layer_manifest(void *_data, const char *propname, char *propvalue, int len);
};
} // namespace blender::bke::cryptomatte

View File

@ -481,13 +481,16 @@ typedef struct SculptSession {
struct MPropCol *vcol;
float *vmask;
/* Mesh connectivity */
/* Mesh connectivity maps. */
/* Vertices to adjacent polys. */
struct MeshElemMap *pmap;
int *pmap_mem;
/* Edges to adjacent polys. */
struct MeshElemMap *epmap;
int *epmap_mem;
/* Vertices to adjacent edges. */
struct MeshElemMap *vemap;
int *vemap_mem;

View File

@ -2002,7 +2002,7 @@ void BKE_pose_blend_read_lib(BlendLibReader *reader, Object *ob, bPose *pose)
IDP_BlendReadLib(reader, pchan->prop);
BLO_read_id_address(reader, arm->id.lib, &pchan->custom);
BLO_read_id_address(reader, ob->id.lib, &pchan->custom);
if (UNLIKELY(pchan->bone == NULL)) {
rebuild = true;
}

View File

@ -1886,7 +1886,7 @@ OutputAttributePtr GeometryComponent::attribute_try_get_for_output(const StringR
if (!attribute) {
this->attribute_try_create(attribute_name, domain, data_type);
attribute = this->attribute_try_get_for_write(attribute_name);
if (default_value != nullptr) {
if (attribute && default_value != nullptr) {
void *data = attribute->get_span_for_write_only().data();
cpp_type->fill_initialized(default_value, data, attribute->size());
attribute->apply_span();

View File

@ -37,6 +37,8 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "RE_pipeline.h"
#include "MEM_guardedalloc.h"
#include <cctype>
@ -51,6 +53,7 @@ struct CryptomatteSession {
CryptomatteSession();
CryptomatteSession(const Main *bmain);
CryptomatteSession(StampData *metadata);
blender::bke::cryptomatte::CryptomatteLayer &add_layer(std::string layer_name);
std::optional<std::string> operator[](float encoded_hash) const;
@ -80,6 +83,22 @@ CryptomatteSession::CryptomatteSession(const Main *bmain)
}
}
CryptomatteSession::CryptomatteSession(StampData *stamp_data)
{
blender::bke::cryptomatte::CryptomatteStampDataCallbackData callback_data;
callback_data.session = this;
BKE_stamp_info_callback(
&callback_data,
stamp_data,
blender::bke::cryptomatte::CryptomatteStampDataCallbackData::extract_layer_names,
false);
BKE_stamp_info_callback(
&callback_data,
stamp_data,
blender::bke::cryptomatte::CryptomatteStampDataCallbackData::extract_layer_manifest,
false);
}
blender::bke::cryptomatte::CryptomatteLayer &CryptomatteSession::add_layer(std::string layer_name)
{
return layers.lookup_or_add_default(layer_name);
@ -102,6 +121,13 @@ CryptomatteSession *BKE_cryptomatte_init(void)
return session;
}
struct CryptomatteSession *BKE_cryptomatte_init_from_render_result(
const struct RenderResult *render_result)
{
CryptomatteSession *session = new CryptomatteSession(render_result->stamp_data);
return session;
}
void BKE_cryptomatte_add_layer(struct CryptomatteSession *session, const char *layer_name)
{
session->add_layer(layer_name);
@ -158,22 +184,30 @@ float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash)
char *BKE_cryptomatte_entries_to_matte_id(NodeCryptomatte *node_storage)
{
DynStr *matte_id = BLI_dynstr_new();
std::stringstream ss;
ss.precision(9);
bool first = true;
LISTBASE_FOREACH (CryptomatteEntry *, entry, &node_storage->entries) {
if (!first) {
BLI_dynstr_append(matte_id, ",");
ss << ',';
}
if (BLI_strnlen(entry->name, sizeof(entry->name)) != 0) {
BLI_dynstr_nappend(matte_id, entry->name, sizeof(entry->name));
blender::StringRef entry_name(entry->name, BLI_strnlen(entry->name, sizeof(entry->name)));
if (!entry_name.is_empty()) {
ss << entry_name;
}
else {
BLI_dynstr_appendf(matte_id, "<%.9g>", entry->encoded_hash);
ss << '<' << std::scientific << entry->encoded_hash << '>';
}
first = false;
}
char *result = BLI_dynstr_get_cstring(matte_id);
BLI_dynstr_free(matte_id);
/* Convert result to C string. */
const std::string result_string = ss.str();
const char *c_str = result_string.c_str();
size_t result_len = result_string.size() + 1;
char *result = static_cast<char *>(MEM_mallocN(sizeof(char) * result_len, __func__));
memcpy(result, c_str, result_len);
return result;
}
@ -502,4 +536,65 @@ std::string CryptomatteLayer::manifest() const
return blender::bke::cryptomatte::manifest::to_manifest(this);
}
blender::StringRef CryptomatteStampDataCallbackData::extract_layer_hash(blender::StringRefNull key)
{
BLI_assert(key.startswith("cryptomatte/"));
size_t start_index = key.find_first_of('/');
size_t end_index = key.find_last_of('/');
if (start_index == blender::StringRef::not_found) {
return "";
}
if (end_index == blender::StringRef::not_found) {
return "";
}
if (end_index <= start_index) {
return "";
}
return key.substr(start_index + 1, end_index - start_index - 1);
}
void CryptomatteStampDataCallbackData::extract_layer_names(void *_data,
const char *propname,
char *propvalue,
int UNUSED(len))
{
CryptomatteStampDataCallbackData *data = static_cast<CryptomatteStampDataCallbackData *>(_data);
blender::StringRefNull key(propname);
if (!key.startswith("cryptomatte/")) {
return;
}
if (!key.endswith("/name")) {
return;
}
blender::StringRef layer_hash = extract_layer_hash(key);
data->hash_to_layer_name.add(layer_hash, propvalue);
}
/* C type callback function (StampCallback). */
void CryptomatteStampDataCallbackData::extract_layer_manifest(void *_data,
const char *propname,
char *propvalue,
int UNUSED(len))
{
CryptomatteStampDataCallbackData *data = static_cast<CryptomatteStampDataCallbackData *>(_data);
blender::StringRefNull key(propname);
if (!key.startswith("cryptomatte/")) {
return;
}
if (!key.endswith("/manifest")) {
return;
}
blender::StringRef layer_hash = extract_layer_hash(key);
if (!data->hash_to_layer_name.contains(layer_hash)) {
return;
}
blender::StringRef layer_name = data->hash_to_layer_name.lookup(layer_hash);
blender::bke::cryptomatte::CryptomatteLayer &layer = data->session->add_layer(layer_name);
blender::bke::cryptomatte::manifest::from_manifest(layer, propvalue);
}
} // namespace blender::bke::cryptomatte

View File

@ -17,7 +17,15 @@
*/
#include "testing/testing.h"
#include "BKE_cryptomatte.h"
#include "BKE_cryptomatte.hh"
#include "BKE_image.h"
#include "DNA_node_types.h"
#include "RE_pipeline.h"
#include "MEM_guardedalloc.h"
namespace blender::bke::cryptomatte::tests {
@ -41,7 +49,7 @@ TEST(cryptomatte, extract_layer_name)
ASSERT_EQ("", BKE_cryptomatte_extract_layer_name(""));
}
TEST(cryptomatte, cryptomatte_layer)
TEST(cryptomatte, layer)
{
blender::bke::cryptomatte::CryptomatteLayer layer;
ASSERT_EQ("{}", layer.manifest());
@ -53,7 +61,7 @@ TEST(cryptomatte, cryptomatte_layer)
ASSERT_EQ("{\"Object\":\"0000007b\",\"Object2\":\"0758946e\"}", layer.manifest());
}
TEST(cryptomatte, cryptomatte_layer_quoted)
TEST(cryptomatte, layer_quoted)
{
blender::bke::cryptomatte::CryptomatteLayer layer;
layer.add_hash("\"Object\"", 123);
@ -66,7 +74,7 @@ static void test_cryptomatte_manifest(std::string expected, std::string manifest
blender::bke::cryptomatte::CryptomatteLayer::read_from_manifest(manifest)->manifest());
}
TEST(cryptomatte, cryptomatte_layer_from_manifest)
TEST(cryptomatte, layer_from_manifest)
{
test_cryptomatte_manifest("{}", "{}");
test_cryptomatte_manifest("{\"Object\":\"12345678\"}", "{\"Object\": \"12345678\"}");
@ -82,4 +90,103 @@ TEST(cryptomatte, cryptomatte_layer_from_manifest)
"{\"Object\\\"01\\\"\":\"12345678\",\"Object\":\"12345678\", \"Object2\":\"87654321\"}");
}
TEST(cryptomatte, extract_layer_hash_from_metadata_key)
{
EXPECT_EQ("eb4c67b",
blender::bke::cryptomatte::CryptomatteStampDataCallbackData::extract_layer_hash(
"cryptomatte/eb4c67b/conversion"));
EXPECT_EQ("qwerty",
blender::bke::cryptomatte::CryptomatteStampDataCallbackData::extract_layer_hash(
"cryptomatte/qwerty/name"));
/* Check if undefined behaviors are handled. */
EXPECT_EQ("",
blender::bke::cryptomatte::CryptomatteStampDataCallbackData::extract_layer_hash(
"cryptomatte/name"));
EXPECT_EQ("",
blender::bke::cryptomatte::CryptomatteStampDataCallbackData::extract_layer_hash(
"cryptomatte/"));
}
static void validate_cryptomatte_session_from_stamp_data(void *UNUSED(data),
const char *propname,
char *propvalue,
int UNUSED(len))
{
blender::StringRefNull prop_name(propname);
if (!prop_name.startswith("cryptomatte/")) {
return;
}
if (prop_name == "cryptomatte/87f095e/name") {
EXPECT_STREQ("viewlayername.layer1", propvalue);
}
else if (prop_name == "cryptomatte/87f095e/hash") {
EXPECT_STREQ("MurmurHash3_32", propvalue);
}
else if (prop_name == "cryptomatte/87f095e/conversion") {
EXPECT_STREQ("uint32_to_float32", propvalue);
}
else if (prop_name == "cryptomatte/87f095e/manifest") {
EXPECT_STREQ("{\"Object\":\"12345678\"}", propvalue);
}
else if (prop_name == "cryptomatte/c42daa7/name") {
EXPECT_STREQ("viewlayername.layer2", propvalue);
}
else if (prop_name == "cryptomatte/c42daa7/hash") {
EXPECT_STREQ("MurmurHash3_32", propvalue);
}
else if (prop_name == "cryptomatte/c42daa7/conversion") {
EXPECT_STREQ("uint32_to_float32", propvalue);
}
else if (prop_name == "cryptomatte/c42daa7/manifest") {
EXPECT_STREQ("{\"Object2\":\"87654321\"}", propvalue);
}
else {
EXPECT_EQ("Unhandled", std::string(propname) + ": " + propvalue);
}
}
TEST(cryptomatte, session_from_stamp_data)
{
/* Create CryptomatteSession from stamp data. */
RenderResult *render_result = static_cast<RenderResult *>(
MEM_callocN(sizeof(RenderResult), __func__));
BKE_render_result_stamp_data(render_result, "cryptomatte/qwerty/name", "layer1");
BKE_render_result_stamp_data(
render_result, "cryptomatte/qwerty/manifest", "{\"Object\":\"12345678\"}");
BKE_render_result_stamp_data(render_result, "cryptomatte/uiop/name", "layer2");
BKE_render_result_stamp_data(
render_result, "cryptomatte/uiop/manifest", "{\"Object2\":\"87654321\"}");
CryptomatteSession *session = BKE_cryptomatte_init_from_render_result(render_result);
EXPECT_NE(session, nullptr);
RE_FreeRenderResult(render_result);
/* Create StampData from CryptomatteSession. */
ViewLayer view_layer;
BLI_strncpy(view_layer.name, "viewlayername", sizeof(view_layer.name));
RenderResult *render_result2 = static_cast<RenderResult *>(
MEM_callocN(sizeof(RenderResult), __func__));
BKE_cryptomatte_store_metadata(session, render_result2, &view_layer);
/* Validate StampData. */
BKE_stamp_info_callback(
nullptr, render_result2->stamp_data, validate_cryptomatte_session_from_stamp_data, false);
RE_FreeRenderResult(render_result2);
BKE_cryptomatte_free(session);
}
TEST(cryptomatte, T86026)
{
NodeCryptomatte storage = {{0.0f}};
CryptomatteEntry entry = {nullptr};
BLI_addtail(&storage.entries, &entry);
entry.encoded_hash = 4.76190593e-07;
char *matte_id = BKE_cryptomatte_entries_to_matte_id(&storage);
EXPECT_STREQ("<4.761905927e-07>", matte_id);
MEM_freeN(matte_id);
}
} // namespace blender::bke::cryptomatte::tests

View File

@ -3555,7 +3555,8 @@ void nodeSetActive(bNodeTree *ntree, bNode *node)
tnode->flag &= ~NODE_ACTIVE_ID;
}
}
if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
if ((node->typeinfo->nclass == NODE_CLASS_TEXTURE) ||
(node->typeinfo->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE)) {
tnode->flag &= ~NODE_ACTIVE_TEXTURE;
}
}
@ -3564,7 +3565,8 @@ void nodeSetActive(bNodeTree *ntree, bNode *node)
if (node->id) {
node->flag |= NODE_ACTIVE_ID;
}
if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
if ((node->typeinfo->nclass == NODE_CLASS_TEXTURE) ||
(node->typeinfo->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE)) {
node->flag |= NODE_ACTIVE_TEXTURE;
}
}

View File

@ -109,9 +109,9 @@ static void node_error_message_log(bNodeTree &ntree,
}
}
static NodeUIStorage &find_node_ui_storage(bNodeTree &ntree,
const NodeTreeEvaluationContext &context,
const bNode &node)
static NodeUIStorage &node_ui_storage_ensure(bNodeTree &ntree,
const NodeTreeEvaluationContext &context,
const bNode &node)
{
ui_storage_ensure(ntree);
NodeTreeUIStorage &ui_storage = *ntree.ui_storage;
@ -133,7 +133,7 @@ void BKE_nodetree_error_message_add(bNodeTree &ntree,
{
node_error_message_log(ntree, node, message, type);
NodeUIStorage &node_ui_storage = find_node_ui_storage(ntree, context, node);
NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
node_ui_storage.warnings.append({type, std::move(message)});
}
@ -142,6 +142,6 @@ void BKE_nodetree_attribute_hint_add(bNodeTree &ntree,
const bNode &node,
const StringRef attribute_name)
{
NodeUIStorage &node_ui_storage = find_node_ui_storage(ntree, context, node);
NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
node_ui_storage.attribute_name_hints.add_as(attribute_name);
}

View File

@ -1477,10 +1477,13 @@ void BKE_sculptsession_free(Object *ob)
MEM_SAFE_FREE(ss->pmap);
MEM_SAFE_FREE(ss->pmap_mem);
MEM_SAFE_FREE(ss->epmap);
MEM_SAFE_FREE(ss->epmap_mem);
MEM_SAFE_FREE(ss->vemap);
MEM_SAFE_FREE(ss->vemap_mem);
if (ss->bm_log) {
BM_log_free(ss->bm_log);
}

View File

@ -70,6 +70,8 @@
#include "SEQ_proxy.h"
#include "SEQ_render.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
#include "SEQ_transform.h"
#include "BLO_readfile.h"
#include "readfile.h"
@ -333,6 +335,37 @@ static void seq_convert_transform_crop_lb_2(const Scene *scene,
}
}
static void seq_update_meta_disp_range(Editing *ed)
{
if (ed == NULL) {
return;
}
LISTBASE_FOREACH_BACKWARD (MetaStack *, ms, &ed->metastack) {
/* Update ms->disp_range from meta. */
if (ms->disp_range[0] == ms->disp_range[1]) {
copy_v2_v2_int(ms->disp_range, &ms->parseq->startdisp);
}
/* Update meta strip endpoints. */
SEQ_transform_set_left_handle_frame(ms->parseq, ms->disp_range[0]);
SEQ_transform_set_right_handle_frame(ms->parseq, ms->disp_range[1]);
SEQ_transform_fix_single_image_seq_offsets(ms->parseq);
/* Recalculate effects using meta strip. */
LISTBASE_FOREACH (Sequence *, seq, ms->oldbasep) {
if (seq->seq2) {
seq->start = seq->startdisp = max_ii(seq->seq1->startdisp, seq->seq2->startdisp);
seq->enddisp = min_ii(seq->seq1->enddisp, seq->seq2->enddisp);
}
}
/* Ensure that active seqbase points to active meta strip seqbase. */
MetaStack *active_ms = SEQ_meta_stack_active_get(ed);
SEQ_seqbase_active_set(ed, &active_ms->parseq->seqbase);
}
}
void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
{
if (!MAIN_VERSION_ATLEAST(bmain, 290, 1)) {
@ -607,6 +640,10 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
*/
{
/* Keep this block, even when empty. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
seq_update_meta_disp_range(SEQ_editing_get(scene, false));
}
}
}

View File

@ -18,7 +18,6 @@
#include "COM_MetaData.h"
#include "BKE_cryptomatte.hh"
#include "BKE_image.h"
#include "RE_pipeline.h"
@ -69,3 +68,39 @@ void MetaData::addToRenderResult(RenderResult *render_result) const
BKE_render_result_stamp_data(render_result, entry.key.c_str(), entry.value.c_str());
}
}
void MetaDataExtractCallbackData::addMetaData(blender::StringRef key, blender::StringRefNull value)
{
if (!meta_data) {
meta_data = std::make_unique<MetaData>();
}
meta_data->add(key, value);
}
void MetaDataExtractCallbackData::setCryptomatteKeys(blender::StringRef cryptomatte_layer_name)
{
manifest_key = blender::bke::cryptomatte::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name,
"manifest");
hash_key = blender::bke::cryptomatte::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name,
"hash");
conversion_key = blender::bke::cryptomatte::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name,
"conversion");
}
void MetaDataExtractCallbackData::extract_cryptomatte_meta_data(void *_data,
const char *propname,
char *propvalue,
int UNUSED(len))
{
MetaDataExtractCallbackData *data = static_cast<MetaDataExtractCallbackData *>(_data);
blender::StringRefNull key(propname);
if (key == data->hash_key) {
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_HASH, propvalue);
}
else if (key == data->conversion_key) {
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_CONVERSION, propvalue);
}
else if (key == data->manifest_key) {
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_MANIFEST, propvalue);
}
}

View File

@ -20,6 +20,7 @@
#include <string>
#include "BKE_cryptomatte.hh"
#include "BLI_map.hh"
#include "MEM_guardedalloc.h"
@ -54,3 +55,18 @@ class MetaData {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:MetaData")
#endif
};
struct MetaDataExtractCallbackData {
std::unique_ptr<MetaData> meta_data;
std::string hash_key;
std::string conversion_key;
std::string manifest_key;
void addMetaData(blender::StringRef key, blender::StringRefNull value);
void setCryptomatteKeys(blender::StringRef cryptomatte_layer_name);
/* C type callback function (StampCallback). */
static void extract_cryptomatte_meta_data(void *_data,
const char *propname,
char *propvalue,
int UNUSED(len));
};

View File

@ -34,12 +34,12 @@ ImageNode::ImageNode(bNode *editorNode) : Node(editorNode)
/* pass */
}
NodeOperation *ImageNode::doMultilayerCheck(NodeConverter &converter,
RenderLayer *rl,
RenderLayer *render_layer,
RenderPass *render_pass,
Image *image,
ImageUser *user,
int framenumber,
int outputsocketIndex,
int passindex,
int view,
DataType datatype) const
{
@ -47,19 +47,18 @@ NodeOperation *ImageNode::doMultilayerCheck(NodeConverter &converter,
MultilayerBaseOperation *operation = nullptr;
switch (datatype) {
case COM_DT_VALUE:
operation = new MultilayerValueOperation(passindex, view);
operation = new MultilayerValueOperation(render_layer, render_pass, view);
break;
case COM_DT_VECTOR:
operation = new MultilayerVectorOperation(passindex, view);
operation = new MultilayerVectorOperation(render_layer, render_pass, view);
break;
case COM_DT_COLOR:
operation = new MultilayerColorOperation(passindex, view);
operation = new MultilayerColorOperation(render_layer, render_pass, view);
break;
default:
break;
}
operation->setImage(image);
operation->setRenderLayer(rl);
operation->setImageUser(user);
operation->setFramenumber(framenumber);
@ -128,16 +127,15 @@ void ImageNode::convertToOperations(NodeConverter &converter,
}
if (rpass) {
int passindex = BLI_findindex(&rl->passes, rpass);
switch (rpass->channels) {
case 1:
operation = doMultilayerCheck(converter,
rl,
rpass,
image,
imageuser,
framenumber,
index,
passindex,
view,
COM_DT_VALUE);
break;
@ -146,22 +144,22 @@ void ImageNode::convertToOperations(NodeConverter &converter,
case 3:
operation = doMultilayerCheck(converter,
rl,
rpass,
image,
imageuser,
framenumber,
index,
passindex,
view,
COM_DT_VECTOR);
break;
case 4:
operation = doMultilayerCheck(converter,
rl,
rpass,
image,
imageuser,
framenumber,
index,
passindex,
view,
COM_DT_COLOR);
break;

View File

@ -24,6 +24,7 @@
#include "DNA_node_types.h"
#include "RE_engine.h"
#include "RE_pipeline.h"
/**
* \brief ImageNode
@ -33,11 +34,11 @@ class ImageNode : public Node {
private:
NodeOperation *doMultilayerCheck(NodeConverter &converter,
RenderLayer *rl,
RenderPass *render_pass,
Image *image,
ImageUser *user,
int framenumber,
int outputsocketIndex,
int passindex,
int view,
DataType datatype) const;

View File

@ -21,10 +21,14 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
MultilayerBaseOperation::MultilayerBaseOperation(int passindex, int view)
MultilayerBaseOperation::MultilayerBaseOperation(RenderLayer *render_layer,
RenderPass *render_pass,
int view)
{
this->m_passId = passindex;
this->m_passId = BLI_findindex(&render_layer->passes, render_pass);
this->m_view = view;
this->m_renderLayer = render_layer;
this->m_renderPass = render_pass;
}
ImBuf *MultilayerBaseOperation::getImBuf()
@ -45,6 +49,32 @@ ImBuf *MultilayerBaseOperation::getImBuf()
return nullptr;
}
std::unique_ptr<MetaData> MultilayerColorOperation::getMetaData() const
{
BLI_assert(this->m_buffer);
MetaDataExtractCallbackData callback_data = {nullptr};
RenderResult *render_result = this->m_image->rr;
if (render_result && render_result->stamp_data) {
RenderLayer *render_layer = this->m_renderLayer;
RenderPass *render_pass = this->m_renderPass;
std::string full_layer_name =
std::string(render_layer->name,
BLI_strnlen(render_layer->name, sizeof(render_layer->name))) +
"." +
std::string(render_pass->name, BLI_strnlen(render_pass->name, sizeof(render_pass->name)));
blender::StringRef cryptomatte_layer_name =
blender::bke::cryptomatte::BKE_cryptomatte_extract_layer_name(full_layer_name);
callback_data.setCryptomatteKeys(cryptomatte_layer_name);
BKE_stamp_info_callback(&callback_data,
render_result->stamp_data,
MetaDataExtractCallbackData::extract_cryptomatte_meta_data,
false);
}
return std::move(callback_data.meta_data);
}
void MultilayerColorOperation::executePixelSampled(float output[4],
float x,
float y,

View File

@ -24,34 +24,34 @@ class MultilayerBaseOperation : public BaseImageOperation {
private:
int m_passId;
int m_view;
RenderLayer *m_renderlayer;
protected:
RenderLayer *m_renderLayer;
RenderPass *m_renderPass;
ImBuf *getImBuf();
public:
/**
* Constructor
*/
MultilayerBaseOperation(int passindex, int view);
void setRenderLayer(RenderLayer *renderlayer)
{
this->m_renderlayer = renderlayer;
}
MultilayerBaseOperation(RenderLayer *render_layer, RenderPass *render_pass, int view);
};
class MultilayerColorOperation : public MultilayerBaseOperation {
public:
MultilayerColorOperation(int passindex, int view) : MultilayerBaseOperation(passindex, view)
MultilayerColorOperation(RenderLayer *render_layer, RenderPass *render_pass, int view)
: MultilayerBaseOperation(render_layer, render_pass, view)
{
this->addOutputSocket(COM_DT_COLOR);
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
std::unique_ptr<MetaData> getMetaData() const override;
};
class MultilayerValueOperation : public MultilayerBaseOperation {
public:
MultilayerValueOperation(int passindex, int view) : MultilayerBaseOperation(passindex, view)
MultilayerValueOperation(RenderLayer *render_layer, RenderPass *render_pass, int view)
: MultilayerBaseOperation(render_layer, render_pass, view)
{
this->addOutputSocket(COM_DT_VALUE);
}
@ -60,7 +60,8 @@ class MultilayerValueOperation : public MultilayerBaseOperation {
class MultilayerVectorOperation : public MultilayerBaseOperation {
public:
MultilayerVectorOperation(int passindex, int view) : MultilayerBaseOperation(passindex, view)
MultilayerVectorOperation(RenderLayer *render_layer, RenderPass *render_pass, int view)
: MultilayerBaseOperation(render_layer, render_pass, view)
{
this->addOutputSocket(COM_DT_VECTOR);
}

View File

@ -26,7 +26,6 @@
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BKE_cryptomatte.hh"
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_main.h"

View File

@ -20,7 +20,6 @@
#include "COM_MetaData.h"
#include "BKE_cryptomatte.hh"
#include "BKE_image.h"
#include "BKE_scene.h"
@ -217,62 +216,18 @@ void RenderLayersProg::determineResolution(unsigned int resolution[2],
}
}
struct CallbackData {
std::unique_ptr<MetaData> meta_data;
std::string hash_key;
std::string conversion_key;
std::string manifest_key;
void addMetaData(blender::StringRef key, blender::StringRefNull value)
{
if (!meta_data) {
meta_data = std::make_unique<MetaData>();
}
meta_data->add(key, value);
}
void setCryptomatteKeys(blender::StringRef cryptomatte_layer_name)
{
manifest_key = blender::bke::cryptomatte::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name,
"manifest");
hash_key = blender::bke::cryptomatte::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name,
"hash");
conversion_key = blender::bke::cryptomatte::BKE_cryptomatte_meta_data_key(
cryptomatte_layer_name, "conversion");
}
};
/* C type callback function (StampCallback). */
static void extract_cryptomatte_meta_data(void *_data,
const char *propname,
char *propvalue,
int UNUSED(len))
{
CallbackData *data = static_cast<CallbackData *>(_data);
blender::StringRefNull key(propname);
if (key == data->hash_key) {
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_HASH, propvalue);
}
else if (key == data->conversion_key) {
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_CONVERSION, propvalue);
}
else if (key == data->manifest_key) {
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_MANIFEST, propvalue);
}
}
std::unique_ptr<MetaData> RenderLayersProg::getMetaData() const
{
Scene *scene = this->getScene();
Render *re = (scene) ? RE_GetSceneRender(scene) : nullptr;
RenderResult *rr = nullptr;
CallbackData callback_data = {nullptr};
RenderResult *render_result = nullptr;
MetaDataExtractCallbackData callback_data = {nullptr};
if (re) {
rr = RE_AcquireResultRead(re);
render_result = RE_AcquireResultRead(re);
}
if (rr && rr->stamp_data) {
if (render_result && render_result->stamp_data) {
ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, getLayerId());
if (view_layer) {
std::string full_layer_name = std::string(
@ -283,8 +238,10 @@ std::unique_ptr<MetaData> RenderLayersProg::getMetaData() const
full_layer_name);
callback_data.setCryptomatteKeys(cryptomatte_layer_name);
BKE_stamp_info_callback(
&callback_data, rr->stamp_data, extract_cryptomatte_meta_data, false);
BKE_stamp_info_callback(&callback_data,
render_result->stamp_data,
MetaDataExtractCallbackData::extract_cryptomatte_meta_data,
false);
}
}

View File

@ -273,9 +273,12 @@ int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *UNUSED(sldata),
float minimal_overblur = 1.0f / sqrtf(sample_count);
float user_overblur = scene_eval->eevee.bokeh_overblur / 100.0f;
effects->dof_coc_params[1] *= minimal_overblur + user_overblur;
minimal_overblur *= effects->dof_coc_params[1];
user_overblur *= effects->dof_coc_params[1];
effects->dof_coc_params[1] = minimal_overblur + user_overblur;
/* Avoid dilating the shape. Over-blur only soften. */
effects->dof_jitter_radius -= effects->dof_coc_params[1];
effects->dof_jitter_radius -= minimal_overblur + user_overblur * 0.5f;
}
}
else {

View File

@ -196,7 +196,7 @@ static uint eevee_lightcache_memsize_get(LightCache *lcache)
return size;
}
static bool eevee_lightcache_version_check(LightCache *lcache)
static bool eevee_lightcache_version_check(const LightCache *lcache)
{
switch (lcache->type) {
case LIGHTCACHE_TYPE_STATIC:
@ -313,6 +313,10 @@ static bool EEVEE_lightcache_validate(const LightCache *light_cache,
const int grid_len,
const int irr_size[3])
{
if (!eevee_lightcache_version_check(light_cache)) {
return false;
}
if (light_cache && !(light_cache->flag & LIGHTCACHE_INVALID)) {
/* See if we need the same amount of texture space. */
if ((irr_size[0] == light_cache->grid_tx.tex_size[0]) &&

View File

@ -151,7 +151,6 @@ void DRW_stats_group_end(void)
void DRW_stats_query_start(const char *name)
{
GPU_debug_group_begin(name);
drw_stats_timer_start_ex(name, false);
drw_stats_timer_start_ex(name, true);
}

View File

@ -1641,7 +1641,6 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
cursor_draw_point_screen_space(
pcontext->pos, pcontext->region, pcontext->ss->pivot_pos, pcontext->vc.obact->obmat, 2);
}
if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
paint_cursor_preview_boundary_data_update(pcontext, update_previews);
paint_cursor_preview_boundary_data_pivot_draw(pcontext);

View File

@ -10539,7 +10539,6 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_project_lasso_gesture);
WM_operatortype_append(SCULPT_OT_project_box_gesture);
WM_operatortype_append(SCULPT_OT_expand);
WM_operatortype_append(SCULPT_OT_sample_color);
WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors);
@ -10551,4 +10550,6 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_face_sets_init);
WM_operatortype_append(SCULPT_OT_reset_brushes);
WM_operatortype_append(SCULPT_OT_ipmask_filter);
WM_operatortype_append(SCULPT_OT_expand);
}

View File

@ -521,6 +521,7 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object,
const bool init_boundary_distances = brush ? brush->boundary_falloff_type !=
BRUSH_BOUNDARY_FALLOFF_CONSTANT :
false;
sculpt_boundary_indices_init(ss, boundary, init_boundary_distances, boundary_initial_vertex);
const float boundary_radius = brush ? radius * (1.0f + brush->boundary_offset) : radius;

View File

@ -47,6 +47,7 @@
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_subdiv_ccg.h"
@ -76,9 +77,9 @@
#include <stdlib.h>
/* Sculpt Expand. */
/* Operator for creating selections and patterns in Scupt Mode. Expand can create masks, face sets
/* Operator for creating selections and patterns in Sculpt Mode. Expand can create masks, face sets
* and fill vertex colors. */
/* The main functinality of the operator
/* The main functionality of the operator
* - The operator initializes a value per vertex, called "falloff". There are multiple algorithms
* to generate these falloff values which will create different patterns in the result when using
* the operator. These falloff values require algorithms that rely on mesh connectivity, so they
@ -98,9 +99,9 @@
*/
#define SCULPT_EXPAND_VERTEX_NONE -1
/* Used for defining an unitialized active component index for an unused symmetry pass. */
/* Used for defining an uninitialized active component index for an unused symmetry pass. */
#define EXPAND_ACTIVE_COMPOMENT_NONE -1
#define EXPAND_ACTIVE_COMPONENT_NONE -1
/* Defines how much each time the texture distortion is increased/decreased when using the modal
* keymap. */
#define SCULPT_EXPAND_TEXTURE_DISTORTION_STEP 0.01f
@ -138,30 +139,13 @@ enum {
SCULPT_EXPAND_MODAL_TEXTURE_DISTORTION_DECREASE,
};
static EnumPropertyItem prop_sculpt_expand_falloff_type_items[] = {
{SCULPT_EXPAND_FALLOFF_GEODESIC, "GEODESIC", 0, "Geodesic", ""},
{SCULPT_EXPAND_FALLOFF_TOPOLOGY, "TOPOLOGY", 0, "Topology", ""},
{SCULPT_EXPAND_FALLOFF_TOPOLOGY_DIAGONALS, "TOPOLOGY_DIAGONALS", 0, "Topology Diagonals", ""},
{SCULPT_EXPAND_FALLOFF_NORMALS, "NORMALS", 0, "Normals", ""},
{SCULPT_EXPAND_FALLOFF_SPHERICAL, "SPHERICAL", 0, "Spherical", ""},
{SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY, "BOUNDARY_TOPOLOGY", 0, "Boundary Topology", ""},
{SCULPT_EXPAND_FALLOFF_BOUNDARY_FACE_SET, "BOUNDARY_FACE_SET", 0, "Boundary Face Set", ""},
{SCULPT_EXPAND_FALLOFF_ACTIVE_FACE_SET, "ACTIVE_FACE_SET", 0, "Active Face Set", ""},
{0, NULL, 0, NULL, NULL},
};
static EnumPropertyItem prop_sculpt_expand_target_type_items[] = {
{SCULPT_EXPAND_TARGET_MASK, "MASK", 0, "Mask", ""},
{SCULPT_EXPAND_TARGET_FACE_SETS, "FACE_SETS", 0, "Face Sets", ""},
{SCULPT_EXPAND_TARGET_COLORS, "COLOR", 0, "Color", ""},
{0, NULL, 0, NULL, NULL},
};
/* Functions for getting the state of mesh elements (vertices and base mesh faces). */
/* Functions for getting the state of mesh elements (vertices and base mesh faces). When the main
* functions for getting the state of an element return true it means that data associated to that
* element will be modified by expand. */
/* Returns true if the vertex is in a connected component with correctly initialized falloff
* values. */
static bool sculpt_expand_is_vert_in_active_compoment(SculptSession *ss,
static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss,
ExpandCache *expand_cache,
const int v)
{
@ -180,7 +164,7 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss,
const int f)
{
const MLoop *loop = &ss->mloop[ss->mpoly[f].loopstart];
return sculpt_expand_is_vert_in_active_compoment(ss, expand_cache, loop->v);
return sculpt_expand_is_vert_in_active_component(ss, expand_cache, loop->v);
}
/* Returns the falloff value of a vertex. This function includes texture distortion, which is not
@ -190,11 +174,11 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss,
const int v)
{
if (expand_cache->texture_distortion_strength == 0.0f) {
return expand_cache->falloff[v];
return expand_cache->vert_falloff[v];
}
if (!expand_cache->brush->mtex.tex) {
return expand_cache->falloff[v];
return expand_cache->vert_falloff[v];
}
float rgba[4];
@ -203,8 +187,8 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss,
expand_cache->scene, expand_cache->brush, vertex_co, rgba, 0, ss->tex_pool);
const float distortion = (avg - 0.5f) * expand_cache->texture_distortion_strength *
expand_cache->max_falloff;
return expand_cache->falloff[v] + distortion;
expand_cache->max_vert_falloff;
return expand_cache->vert_falloff[v] + distortion;
}
/* Returns the maximum valid falloff value stored in the falloff array, taking the maximum possible
@ -212,18 +196,19 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss,
static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache)
{
if (expand_cache->texture_distortion_strength == 0.0f) {
return expand_cache->max_falloff;
return expand_cache->max_vert_falloff;
}
if (!expand_cache->brush->mtex.tex) {
return expand_cache->max_falloff;
return expand_cache->max_vert_falloff;
}
return expand_cache->max_falloff +
(0.5f * expand_cache->texture_distortion_strength * expand_cache->max_falloff);
return expand_cache->max_vert_falloff +
(0.5f * expand_cache->texture_distortion_strength * expand_cache->max_vert_falloff);
}
/* Main function to get the state of a vertex for the current state and settings of a ExpandCache.
* Retruns true when the target data should be modified by expand.
*/
static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int v)
{
@ -231,7 +216,7 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache
return false;
}
if (!sculpt_expand_is_vert_in_active_compoment(ss, expand_cache, v)) {
if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, v)) {
return false;
}
@ -267,7 +252,8 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache
return enabled;
}
/* Main function to get the state of a face for the current state and settings of a ExpandCache. */
/* Main function to get the state of a face for the current state and settings of a ExpandCache.
* Returs true when the traget data should be modified by expand. */
static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_cache, const int f)
{
if (ss->face_sets[f] <= 0) {
@ -310,8 +296,8 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_
return enabled;
}
/* For target modes that support gradients, this function returns the corresponding gradient value
* for a enabled vertex. */
/* For target modes that support gradients (such as sculpt masks or colors), this function returns
* the corresponding gradient value for an enabled vertex. */
static float sculpt_expand_gradient_value_get(SculptSession *ss,
ExpandCache *expand_cache,
const int v)
@ -331,6 +317,9 @@ static float sculpt_expand_gradient_value_get(SculptSession *ss,
float linear_falloff;
if (expand_cache->invert) {
/* Active factor is the result of a modulus operation using loop_len, so they will never be
* equal and loop_len - active_factor should never be 0. */
BLI_assert((loop_len - active_factor) != 0.0f);
linear_falloff = (falloff_factor - active_factor) / (loop_len - active_factor);
}
else {
@ -394,6 +383,25 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss,
/* Functions implementing different algorithms for initializing falloff values. */
/* Utility function to get the closet vertex after flipping an original vertex possition based on
* an symmetry pass iteration index. */
static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob,
const char symm_it,
const int original_vertex)
{
SculptSession *ss = ob->sculpt;
int symm_vertex = SCULPT_EXPAND_VERTEX_NONE;
if (symm_it == 0) {
symm_vertex = original_vertex;
}
else {
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, original_vertex), symm_it);
symm_vertex = SCULPT_nearest_vertex_get(NULL, ob, location, FLT_MAX, false);
}
return symm_vertex;
}
/* Geodesic: Initializes the falloff with geodesic distances from the given active vertex, taking
* symmetry into account. */
static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const int v)
@ -513,10 +521,10 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd,
return dists;
}
/* Spherical: Initialices the falloff based on the distance from a vertex, taking symmetry into
/* Spherical: Initializes the falloff based on the distance from a vertex, taking symmetry into
* account. */
static float *sculpt_expand_spherical_falloff_create(Sculpt *sd, Object *ob, const int v)
static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@ -531,15 +539,7 @@ static float *sculpt_expand_spherical_falloff_create(Sculpt *sd, Object *ob, con
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
continue;
}
int symm_vertex = SCULPT_EXPAND_VERTEX_NONE;
if (symm_it == 0) {
symm_vertex = v;
}
else {
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, v), symm_it);
symm_vertex = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
}
const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
if (symm_vertex != -1) {
const float *co = SCULPT_vertex_co_get(ss, symm_vertex);
for (int i = 0; i < totvert; i++) {
@ -553,8 +553,8 @@ static float *sculpt_expand_spherical_falloff_create(Sculpt *sd, Object *ob, con
/* Boundary: This falloff mode uses the code from sculpt_boundary to initialize the closest mesh
* boundary to a falloff value of 0. Then, it propagates that falloff to the rest of the mesh so it
* stays parallalel to the boundary, increasing the falloff value by 1 on each step. */
static float *sculpt_expand_boundary_topology_falloff_create(Sculpt *sd, Object *ob, const int v)
* stays parallel to the boundary, increasing the falloff value by 1 on each step. */
static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const int v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@ -562,23 +562,20 @@ static float *sculpt_expand_boundary_topology_falloff_create(Sculpt *sd, Object
BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices");
GSQueue *queue = BLI_gsqueue_new(sizeof(int));
/* Search and initialize a boundary per symmetry pass, them mark those vertices as visited. */
/* Search and initialize a boundary per symmetry pass, then mark those vertices as visited. */
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
for (char symm_it = 0; symm_it <= symm; symm_it++) {
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
continue;
}
int symm_vertex = SCULPT_EXPAND_VERTEX_NONE;
if (symm_it == 0) {
symm_vertex = v;
}
else {
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, v), symm_it);
symm_vertex = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
}
const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
SculptBoundary *boundary = SCULPT_boundary_data_init(ob, NULL, symm_vertex, FLT_MAX);
if (!boundary) {
continue;
}
for (int i = 0; i < boundary->num_vertices; i++) {
BLI_gsqueue_push(queue, &boundary->vertices[i]);
BLI_BITMAP_ENABLE(visited_vertices, boundary->vertices[i]);
@ -593,15 +590,15 @@ static float *sculpt_expand_boundary_topology_falloff_create(Sculpt *sd, Object
/* Propagate the values from the boundaries to the rest of the mesh. */
while (!BLI_gsqueue_is_empty(queue)) {
int v;
BLI_gsqueue_pop(queue, &v);
int v_next;
BLI_gsqueue_pop(queue, &v_next);
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v, ni) {
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_next, ni) {
if (BLI_BITMAP_TEST(visited_vertices, ni.index)) {
continue;
}
dists[ni.index] = dists[v] + 1.0f;
dists[ni.index] = dists[v_next] + 1.0f;
BLI_BITMAP_ENABLE(visited_vertices, ni.index);
BLI_gsqueue_push(queue, &ni.index);
}
@ -615,14 +612,14 @@ static float *sculpt_expand_boundary_topology_falloff_create(Sculpt *sd, Object
/* Topology diagonals. This falloff is similar to topology, but it also considers the diagonals of
* the base mesh faces when checking a vertex neighbor. For this reason, this is not implement
* using the general flodfill and sculpt neighbors accessors. */
static float *sculpt_expand_diagonals_falloff_create(Sculpt *sd, Object *ob, const int v)
* using the general floodfill and sculpt neighbors accessors. */
static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist");
/* This algorithm uses mesh data (polys and loops), so this falloff type can't be initialize for
/* This algorithm uses mesh data (polys and loops), so this falloff type can't be initialized for
* Multires. It also does not make sense to implement it for dyntopo as the result will be the
* same as Topology falloff. */
if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
@ -637,15 +634,8 @@ static float *sculpt_expand_diagonals_falloff_create(Sculpt *sd, Object *ob, con
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
continue;
}
int symm_vertex = SCULPT_EXPAND_VERTEX_NONE;
if (symm_it == 0) {
symm_vertex = v;
}
else {
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, v), symm_it);
symm_vertex = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
}
const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
BLI_gsqueue_push(queue, &symm_vertex);
BLI_BITMAP_ENABLE(visited_vertices, symm_vertex);
@ -658,16 +648,16 @@ static float *sculpt_expand_diagonals_falloff_create(Sculpt *sd, Object *ob, con
/* Propagate the falloff increasing the value by 1 each time a new vertex is visited. */
Mesh *mesh = ob->data;
while (!BLI_gsqueue_is_empty(queue)) {
int v;
BLI_gsqueue_pop(queue, &v);
for (int j = 0; j < ss->pmap[v].count; j++) {
MPoly *p = &ss->mpoly[ss->pmap[v].indices[j]];
int v_next;
BLI_gsqueue_pop(queue, &v_next);
for (int j = 0; j < ss->pmap[v_next].count; j++) {
MPoly *p = &ss->mpoly[ss->pmap[v_next].indices[j]];
for (int l = 0; l < p->totloop; l++) {
const int neighbor_v = mesh->mloop[p->loopstart + l].v;
if (BLI_BITMAP_TEST(visited_vertices, neighbor_v)) {
continue;
}
dists[neighbor_v] = dists[v] + 1.0f;
dists[neighbor_v] = dists[v_next] + 1.0f;
BLI_BITMAP_ENABLE(visited_vertices, neighbor_v);
BLI_gsqueue_push(queue, &neighbor_v);
}
@ -679,25 +669,27 @@ static float *sculpt_expand_diagonals_falloff_create(Sculpt *sd, Object *ob, con
return dists;
}
/* Functions to update the max_falloff value in the ExpandCache. This funcions are called after
/* Functions to update the max_falloff value in the ExpandCache. These funcions are called after
* initializing a new falloff to make sure that this value is always updated. */
/* Updates the max_falloff value for vertices in a ExpandCache based on the current values of the
* falloff, skipping any invalid values initialized to FLT_MAX and not initialized components. */
static void sculpt_expand_update_max_falloff_value(SculptSession *ss, ExpandCache *expand_cache)
static void sculpt_expand_update_max_vert_falloff_value(SculptSession *ss,
ExpandCache *expand_cache)
{
const int totvert = SCULPT_vertex_count_get(ss);
expand_cache->max_falloff = -FLT_MAX;
expand_cache->max_vert_falloff = -FLT_MAX;
for (int i = 0; i < totvert; i++) {
if (expand_cache->falloff[i] == FLT_MAX) {
if (expand_cache->vert_falloff[i] == FLT_MAX) {
continue;
}
if (!sculpt_expand_is_vert_in_active_compoment(ss, expand_cache, i)) {
if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) {
continue;
}
expand_cache->max_falloff = max_ff(expand_cache->max_falloff, expand_cache->falloff[i]);
expand_cache->max_vert_falloff = max_ff(expand_cache->max_vert_falloff,
expand_cache->vert_falloff[i]);
}
}
@ -723,7 +715,7 @@ static void sculpt_expand_update_max_face_falloff_factor(SculptSession *ss,
}
/* Functions to get falloff values for faces from the values from the vertices. This is used for
* expanding Face Sets. Depending on the data type of the ScultpSession, this needs to get the per
* expanding Face Sets. Depending on the data type of the SculptSession, this needs to get the per
* face falloff value from the connected vertices of each face or from the grids stored per loops
* for each face. */
static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss,
@ -739,7 +731,7 @@ static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss,
for (int l = 0; l < poly->totloop; l++) {
const int grid_loop_index = (poly->loopstart + l) * key->grid_area;
for (int g = 0; g < key->grid_area; g++) {
accum += expand_cache->falloff[grid_loop_index + g];
accum += expand_cache->vert_falloff[grid_loop_index + g];
}
}
expand_cache->face_falloff[p] = accum / (poly->totloop * key->grid_area);
@ -753,7 +745,7 @@ static void sculpt_expand_vertex_to_faces_falloff(Mesh *mesh, ExpandCache *expan
float accum = 0.0f;
for (int l = 0; l < poly->totloop; l++) {
MLoop *loop = &mesh->mloop[l + poly->loopstart];
accum += expand_cache->falloff[loop->v];
accum += expand_cache->vert_falloff[loop->v];
}
expand_cache->face_falloff[p] = accum / poly->totloop;
}
@ -764,7 +756,7 @@ static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *s
Mesh *mesh,
ExpandCache *expand_cache)
{
BLI_assert(expand_cache->falloff != NULL);
BLI_assert(expand_cache->vert_falloff != NULL);
if (!expand_cache->face_falloff) {
expand_cache->face_falloff = MEM_malloc_arrayN(
@ -805,20 +797,20 @@ static void sculpt_expand_geodesics_from_state_boundary(Object *ob,
}
MEM_freeN(boundary_vertices);
MEM_SAFE_FREE(expand_cache->falloff);
MEM_SAFE_FREE(expand_cache->vert_falloff);
MEM_SAFE_FREE(expand_cache->face_falloff);
expand_cache->falloff = SCULPT_geodesic_distances_create(ob, initial_vertices, FLT_MAX);
expand_cache->vert_falloff = SCULPT_geodesic_distances_create(ob, initial_vertices, FLT_MAX);
BLI_gset_free(initial_vertices, NULL);
}
/* Geodesic recursion: Initializes falloff values using topology steps from the boundary of the
/* Topology recursion: Initializes falloff values using topology steps from the boundary of the
* current vertices state, increasing the value by 1 each time a new vertex is visited. */
static void sculpt_expand_topology_from_state_boundary(Object *ob,
ExpandCache *expand_cache,
BLI_bitmap *enabled_vertices)
{
MEM_SAFE_FREE(expand_cache->falloff);
MEM_SAFE_FREE(expand_cache->vert_falloff);
MEM_SAFE_FREE(expand_cache->face_falloff);
SculptSession *ss = ob->sculpt;
@ -842,7 +834,7 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob,
SCULPT_floodfill_execute(ss, &flood, expand_topology_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
expand_cache->falloff = dists;
expand_cache->vert_falloff = dists;
}
/* Main function to create a recursion step from the current ExpandCache state. */
@ -857,9 +849,9 @@ static void sculpt_expand_resursion_step_add(Object *ob,
BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache);
/* Each time a new recursion step is created, reset the distortion strength. This is the expedted
/* Each time a new recursion step is created, reset the distortion strength. This is the expected
* result from the recursion, as otherwise the new falloff will render with undesired distortion
* from the beginning */
* from the beginning. */
expand_cache->texture_distortion_strength = 0.0f;
switch (recursion_type) {
@ -871,7 +863,7 @@ static void sculpt_expand_resursion_step_add(Object *ob,
break;
}
sculpt_expand_update_max_falloff_value(ss, expand_cache);
sculpt_expand_update_max_vert_falloff_value(ss, expand_cache);
if (expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) {
sculpt_expand_mesh_face_falloff_from_vertex_falloff(ss, ob->data, expand_cache);
sculpt_expand_update_max_face_falloff_factor(ss, expand_cache);
@ -880,7 +872,9 @@ static void sculpt_expand_resursion_step_add(Object *ob,
MEM_freeN(enabled_vertices);
}
/* Face Set Boundary falloff: */
/* Face Set Boundary falloff. */
/* When internal falloff is set to true, the falloff will fill the active Face Set with a gradient,
* otherwise the active Face Set will be filled with a constant falloff of 0.0f. */
static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
ExpandCache *expand_cache,
const int active_face_set,
@ -915,17 +909,17 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
SCULPT_vertex_has_unique_face_set(ss, i))) {
continue;
}
expand_cache->falloff[i] *= -1.0f;
expand_cache->vert_falloff[i] *= -1.0f;
}
float min_factor = FLT_MAX;
for (int i = 0; i < totvert; i++) {
min_factor = min_ff(expand_cache->falloff[i], min_factor);
min_factor = min_ff(expand_cache->vert_falloff[i], min_factor);
}
const float increase_factor = fabsf(min_factor);
const float additional_falloff = fabsf(min_factor);
for (int i = 0; i < totvert; i++) {
expand_cache->falloff[i] += increase_factor;
expand_cache->vert_falloff[i] += additional_falloff;
}
}
else {
@ -933,7 +927,7 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
continue;
}
expand_cache->falloff[i] = 0.0f;
expand_cache->vert_falloff[i] = 0.0f;
}
}
}
@ -947,7 +941,7 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create(
const int v,
eSculptExpandFalloffType falloff_type)
{
MEM_SAFE_FREE(expand_cache->falloff);
MEM_SAFE_FREE(expand_cache->vert_falloff);
expand_cache->falloff_type = falloff_type;
SculptSession *ss = ob->sculpt;
@ -955,27 +949,27 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create(
switch (falloff_type) {
case SCULPT_EXPAND_FALLOFF_GEODESIC:
expand_cache->falloff = has_topology_info ?
sculpt_expand_geodesic_falloff_create(sd, ob, v) :
sculpt_expand_spherical_falloff_create(sd, ob, v);
expand_cache->vert_falloff = has_topology_info ?
sculpt_expand_geodesic_falloff_create(sd, ob, v) :
sculpt_expand_spherical_falloff_create(ob, v);
break;
case SCULPT_EXPAND_FALLOFF_TOPOLOGY:
expand_cache->falloff = sculpt_expand_topology_falloff_create(sd, ob, v);
expand_cache->vert_falloff = sculpt_expand_topology_falloff_create(sd, ob, v);
break;
case SCULPT_EXPAND_FALLOFF_TOPOLOGY_DIAGONALS:
expand_cache->falloff = has_topology_info ?
sculpt_expand_diagonals_falloff_create(sd, ob, v) :
sculpt_expand_topology_falloff_create(sd, ob, v);
expand_cache->vert_falloff = has_topology_info ?
sculpt_expand_diagonals_falloff_create(ob, v) :
sculpt_expand_topology_falloff_create(sd, ob, v);
break;
case SCULPT_EXPAND_FALLOFF_NORMALS:
expand_cache->falloff = sculpt_expand_normal_falloff_create(
expand_cache->vert_falloff = sculpt_expand_normal_falloff_create(
sd, ob, v, SCULPT_EXPAND_NORMALS_FALLOFF_EDGE_SENSITIVITY);
break;
case SCULPT_EXPAND_FALLOFF_SPHERICAL:
expand_cache->falloff = sculpt_expand_spherical_falloff_create(sd, ob, v);
expand_cache->vert_falloff = sculpt_expand_spherical_falloff_create(ob, v);
break;
case SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY:
expand_cache->falloff = sculpt_expand_boundary_topology_falloff_create(sd, ob, v);
expand_cache->vert_falloff = sculpt_expand_boundary_topology_falloff_create(ob, v);
break;
case SCULPT_EXPAND_FALLOFF_BOUNDARY_FACE_SET:
sculpt_expand_initialize_from_face_set_boundary(
@ -988,7 +982,7 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create(
}
/* Update max falloff values and propagate to base mesh faces if needed. */
sculpt_expand_update_max_falloff_value(ss, expand_cache);
sculpt_expand_update_max_vert_falloff_value(ss, expand_cache);
if (expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) {
sculpt_expand_mesh_face_falloff_from_vertex_falloff(ss, ob->data, expand_cache);
sculpt_expand_update_max_face_falloff_factor(ss, expand_cache);
@ -1027,13 +1021,12 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss,
MLoop *loop = &ss->mloop[l + poly->loopstart];
if (!BLI_BITMAP_TEST(enabled_vertices, loop->v)) {
any_disabled = true;
break;
}
}
if (any_disabled) {
const int face_set = expand_cache->original_face_sets[p];
if (BLI_gset_haskey(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set))) {
BLI_gset_remove(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set), NULL);
}
BLI_gset_remove(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set), NULL);
}
}
@ -1042,14 +1035,14 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss,
expand_cache->invert = prev_invert_state;
}
/* Functions to free a ExpandCache */
/* Functions to free a ExpandCache. */
static void sculpt_expand_cache_data_free(ExpandCache *expand_cache)
{
if (expand_cache->snap_enabled_face_sets) {
BLI_gset_free(expand_cache->snap_enabled_face_sets, NULL);
}
MEM_SAFE_FREE(expand_cache->nodes);
MEM_SAFE_FREE(expand_cache->falloff);
MEM_SAFE_FREE(expand_cache->vert_falloff);
MEM_SAFE_FREE(expand_cache->face_falloff);
MEM_SAFE_FREE(expand_cache->original_mask);
MEM_SAFE_FREE(expand_cache->original_face_sets);
@ -1312,8 +1305,8 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c
const int totface = ss->totfaces;
/* Face Sets are always stored as they are needed for snapping. */
expand_cache->initial_face_sets = MEM_malloc_arrayN(totvert, sizeof(int), "initial face set");
expand_cache->original_face_sets = MEM_malloc_arrayN(totvert, sizeof(int), "original face set");
expand_cache->initial_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "initial face set");
expand_cache->original_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "original face set");
for (int i = 0; i < totface; i++) {
expand_cache->initial_face_sets[i] = ss->face_sets[i];
expand_cache->original_face_sets[i] = ss->face_sets[i];
@ -1351,14 +1344,14 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v
/* Update the active factor in the cache. */
if (vertex == SCULPT_EXPAND_VERTEX_NONE) {
/* This means that the cursor is not over the mesh, so a valid active fallof can't be
* determined. In this situations, don't evalue enabled states and default all vertices in
/* This means that the cursor is not over the mesh, so a valid active falloff can't be
* determined. In this situations, don't evaluate enabled states and default all vertices in
* connected components to enabled. */
expand_cache->active_falloff = expand_cache->max_falloff;
expand_cache->active_falloff = expand_cache->max_vert_falloff;
expand_cache->all_enabled = true;
}
else {
expand_cache->active_falloff = expand_cache->falloff[vertex];
expand_cache->active_falloff = expand_cache->vert_falloff[vertex];
expand_cache->all_enabled = false;
}
@ -1403,7 +1396,7 @@ static int sculpt_expand_target_vertex_update_and_get(bContext *C,
{
SculptSession *ss = ob->sculpt;
SculptCursorGeometryInfo sgi;
if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false, false)) {
if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) {
return SCULPT_active_vertex_get(ss);
}
else {
@ -1432,7 +1425,7 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache
BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(
ss, enabled_vertices, use_mesh_boundary);
/* Ignore invert state, as this is the expected behaivour in most cases and mask are created in
/* Ignore invert state, as this is the expected behaviour in most cases and mask are created in
* inverted state by default. */
expand_cache->invert = initial_invert_state;
@ -1446,7 +1439,7 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache
continue;
}
if (!sculpt_expand_is_vert_in_active_compoment(ss, expand_cache, i)) {
if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) {
continue;
}
@ -1476,6 +1469,15 @@ static void sculpt_expand_finish(bContext *C)
SculptSession *ss = ob->sculpt;
SCULPT_undo_push_end();
/* Tag all nodes to redraw to avoid artifacts after the fast partial updates. */
PBVHNode **nodes;
int totnode;
BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
for (int n = 0; n < totnode; n++) {
BKE_pbvh_node_mark_update_mask(nodes[n]);
}
MEM_freeN(nodes);
switch (ss->expand_cache->target) {
case SCULPT_EXPAND_TARGET_MASK:
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
@ -1492,7 +1494,7 @@ static void sculpt_expand_finish(bContext *C)
ED_workspace_status_text(C, NULL);
}
/* Finds and stores in the ExpandCache the sculpt connected component index for each symmetry passn
/* Finds and stores in the ExpandCache the sculpt connected component index for each symmetry pass
* needed for expand. */
static void sculpt_expand_find_active_connected_components_from_vert(Object *ob,
ExpandCache *expand_cache,
@ -1500,7 +1502,7 @@ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob,
{
SculptSession *ss = ob->sculpt;
for (int i = 0; i < EXPAND_SYMM_AREAS; i++) {
expand_cache->active_connected_components[i] = EXPAND_ACTIVE_COMPOMENT_NONE;
expand_cache->active_connected_components[i] = EXPAND_ACTIVE_COMPONENT_NONE;
}
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
@ -1509,17 +1511,11 @@ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob,
continue;
}
int vertex_symm = SCULPT_EXPAND_VERTEX_NONE;
if (symm_it == 0) {
vertex_symm = initial_vertex;
}
else {
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, initial_vertex), symm_it);
vertex_symm = SCULPT_nearest_vertex_get(NULL, ob, location, FLT_MAX, false);
}
expand_cache->active_connected_components[symm_it] =
ss->vertex_info.connected_component[vertex_symm];
const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
ob, symm_it, initial_vertex);
expand_cache->active_connected_components[(int)symm_it] =
ss->vertex_info.connected_component[symm_vertex];
}
}
@ -1542,7 +1538,7 @@ static void sculpt_expand_set_initial_components_for_mouse(bContext *C,
expand_cache->initial_active_face_set = SCULPT_active_face_set_get(ss);
if (expand_cache->next_face_set == SCULPT_FACE_SET_NONE) {
/* Only set the next face set once, otherwise this ID will constaintly update to a new one each
/* Only set the next face set once, otherwise this ID will constantly update to a new one each
* time this function is called for using a new initial vertex from a different cursor
* position. */
if (expand_cache->modify_active_face_set) {
@ -1554,7 +1550,7 @@ static void sculpt_expand_set_initial_components_for_mouse(bContext *C,
}
/* The new mouse position can be over a different connected component, so this needs to be
* udpated. */
* updated. */
sculpt_expand_find_active_connected_components_from_vert(ob, expand_cache, initial_vertex);
}
@ -1576,7 +1572,11 @@ static void sculpt_expand_move_propagation_origin(bContext *C,
sculpt_expand_set_initial_components_for_mouse(C, ob, expand_cache, new_mouse);
sculpt_expand_falloff_factors_from_vertex_and_symm_create(
expand_cache, sd, ob, expand_cache->initial_active_vertex, expand_cache->falloff_type);
expand_cache,
sd,
ob,
expand_cache->initial_active_vertex,
expand_cache->move_preview_falloff_type);
}
/* Ensures that the SculptSession contains the required data needed for Expand. */
@ -1591,7 +1591,7 @@ static void sculpt_expand_ensure_sculptsession_data(Object *ob)
}
}
/* Returns the active Face Sets ID from the enabled face or grid in the ScultpSession. */
/* Returns the active Face Sets ID from the enabled face or grid in the SculptSession. */
static int sculpt_expand_active_face_set_id_get(SculptSession *ss, ExpandCache *expand_cache)
{
switch (BKE_pbvh_type(ss->pbvh)) {
@ -1676,11 +1676,28 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
case SCULPT_EXPAND_MODAL_MOVE_TOGGLE: {
if (expand_cache->move) {
expand_cache->move = false;
sculpt_expand_falloff_factors_from_vertex_and_symm_create(
expand_cache,
sd,
ob,
expand_cache->initial_active_vertex,
expand_cache->move_original_falloff_type);
break;
}
else {
expand_cache->move = true;
expand_cache->move_original_falloff_type = expand_cache->falloff_type;
copy_v2_v2(expand_cache->initial_mouse_move, mouse);
copy_v2_v2(expand_cache->original_mouse_move, expand_cache->initial_mouse);
if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_GEODESIC &&
SCULPT_vertex_count_get(ss) > expand_cache->max_geodesic_move_preview) {
/* Set to spherical falloff for preview in high poly meshes as it is the fastest one.
* In most cases it should match closely the preview from geodesic. */
expand_cache->move_preview_falloff_type = SCULPT_EXPAND_FALLOFF_SPHERICAL;
}
else {
expand_cache->move_preview_falloff_type = expand_cache->falloff_type;
}
}
break;
}
@ -1748,6 +1765,19 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
break;
}
case SCULPT_EXPAND_MODAL_TEXTURE_DISTORTION_INCREASE: {
if (expand_cache->texture_distortion_strength == 0.0f) {
if (expand_cache->brush->mtex.tex == NULL) {
BKE_report(op->reports,
RPT_WARNING,
"Active brush does not contain any texture to distort the expand boundary");
break;
}
if (expand_cache->brush->mtex.brush_map_mode != MTEX_MAP_MODE_3D) {
BKE_report(op->reports,
RPT_WARNING,
"Texture mapping not set to 3D, results may be unpredictable");
}
}
expand_cache->texture_distortion_strength += SCULPT_EXPAND_TEXTURE_DISTORTION_STEP;
break;
}
@ -1760,7 +1790,7 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
}
}
/* Handle epxnad origin movement if enabled */
/* Handle expand origin movement if enabled. */
if (expand_cache->move) {
sculpt_expand_move_propagation_origin(C, ob, event, expand_cache);
}
@ -1786,23 +1816,34 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
static void sculpt_expand_delete_face_set_id(
int *r_face_sets, Mesh *mesh, MeshElemMap *pmap, const int totface, const int delete_id)
{
/* Check that all the face sets IDs in the mesh are not equal to delete_id befor attempting to
* delete it. */
bool all_same_id = true;
for (int i = 0; i < totface; i++) {
if (r_face_sets[i] != delete_id) {
all_same_id = false;
break;
}
}
if (all_same_id) {
return;
}
BLI_LINKSTACK_DECLARE(queue, int);
BLI_LINKSTACK_DECLARE(queue_next, int);
BLI_LINKSTACK_DECLARE(queue, void *);
BLI_LINKSTACK_DECLARE(queue_next, void *);
BLI_LINKSTACK_INIT(queue);
BLI_LINKSTACK_INIT(queue_next);
for (int i = 0; i < totface; i++) {
if (r_face_sets[i] == delete_id) {
BLI_LINKSTACK_PUSH(queue, i);
BLI_LINKSTACK_PUSH(queue, POINTER_FROM_INT(i));
}
}
while (BLI_LINKSTACK_SIZE(queue)) {
int f_index;
while (f_index = BLI_LINKSTACK_POP(queue)) {
while (BLI_LINKSTACK_SIZE(queue)) {
const int f_index = POINTER_AS_INT(BLI_LINKSTACK_POP(queue));
int other_id = delete_id;
const MPoly *c_poly = &mesh->mpoly[f_index];
for (int l = 0; l < c_poly->totloop; l++) {
@ -1821,7 +1862,7 @@ static void sculpt_expand_delete_face_set_id(
r_face_sets[f_index] = other_id;
}
else {
BLI_LINKSTACK_PUSH(queue_next, f_index);
BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(f_index));
}
}
@ -1843,6 +1884,7 @@ static void sculpt_expand_cache_initial_config_set(bContext *C,
expand_cache->target = RNA_enum_get(op->ptr, "target");
expand_cache->modify_active_face_set = RNA_boolean_get(op->ptr, "use_modify_active");
expand_cache->reposition_pivot = RNA_boolean_get(op->ptr, "use_reposition_pivot");
expand_cache->max_geodesic_move_preview = RNA_int_get(op->ptr, "max_geodesic_move_preview");
/* These can be exposed in RNA if needed. */
expand_cache->loop_count = 1;
@ -1864,7 +1906,7 @@ static void sculpt_expand_cache_initial_config_set(bContext *C,
expand_cache->blend_mode = expand_cache->brush->blend;
}
/* Does the undo sculpt push for the affected target data of the ExpandCache */
/* Does the undo sculpt push for the affected target data of the ExpandCache. */
static void sculpt_expand_undo_push(Object *ob, ExpandCache *expand_cache)
{
SculptSession *ss = ob->sculpt;
@ -1913,6 +1955,21 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
}
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, needs_colors);
/* Do nothing when the mesh has 0 vertices. */
const int totvert = SCULPT_vertex_count_get(ss);
if (totvert == 0) {
sculpt_expand_cache_free(ss);
return OPERATOR_CANCELLED;
}
/* Face Set operations are not supported in dyntopo. */
if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS &&
BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
sculpt_expand_cache_free(ss);
return OPERATOR_CANCELLED;
}
sculpt_expand_ensure_sculptsession_data(ob);
/* Initialize undo. */
@ -2039,6 +2096,29 @@ void SCULPT_OT_expand(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
static EnumPropertyItem prop_sculpt_expand_falloff_type_items[] = {
{SCULPT_EXPAND_FALLOFF_GEODESIC, "GEODESIC", 0, "Geodesic", ""},
{SCULPT_EXPAND_FALLOFF_TOPOLOGY, "TOPOLOGY", 0, "Topology", ""},
{SCULPT_EXPAND_FALLOFF_TOPOLOGY_DIAGONALS,
"TOPOLOGY_DIAGONALS",
0,
"Topology Diagonals",
""},
{SCULPT_EXPAND_FALLOFF_NORMALS, "NORMALS", 0, "Normals", ""},
{SCULPT_EXPAND_FALLOFF_SPHERICAL, "SPHERICAL", 0, "Spherical", ""},
{SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY, "BOUNDARY_TOPOLOGY", 0, "Boundary Topology", ""},
{SCULPT_EXPAND_FALLOFF_BOUNDARY_FACE_SET, "BOUNDARY_FACE_SET", 0, "Boundary Face Set", ""},
{SCULPT_EXPAND_FALLOFF_ACTIVE_FACE_SET, "ACTIVE_FACE_SET", 0, "Active Face Set", ""},
{0, NULL, 0, NULL, NULL},
};
static EnumPropertyItem prop_sculpt_expand_target_type_items[] = {
{SCULPT_EXPAND_TARGET_MASK, "MASK", 0, "Mask", ""},
{SCULPT_EXPAND_TARGET_FACE_SETS, "FACE_SETS", 0, "Face Sets", ""},
{SCULPT_EXPAND_TARGET_COLORS, "COLOR", 0, "Color", ""},
{0, NULL, 0, NULL, NULL},
};
RNA_def_enum(ot->srna,
"target",
prop_sculpt_expand_target_type_items,
@ -2050,7 +2130,7 @@ void SCULPT_OT_expand(wmOperatorType *ot)
"falloff_type",
prop_sculpt_expand_falloff_type_items,
SCULPT_EXPAND_FALLOFF_GEODESIC,
"Fallof Type",
"Falloff Type",
"Initial falloff of the expand operation");
ot->prop = RNA_def_boolean(
@ -2060,12 +2140,34 @@ void SCULPT_OT_expand(wmOperatorType *ot)
false,
"Preserve Previous",
"Preserve the previous state of the target data");
ot->prop = RNA_def_boolean(
ot->srna, "use_falloff_gradient", false, "Falloff Gradient", "Expand Using a Falloff");
ot->prop = RNA_def_boolean(ot->srna,
"use_falloff_gradient",
false,
"Falloff Gradient",
"Expand Using a linear falloff");
ot->prop = RNA_def_boolean(ot->srna,
"use_modify_active",
false,
"Modify Active",
"Modify the active Face Set instead of creating a new one");
ot->prop = RNA_def_boolean(
ot->srna, "use_modify_active", false, "Modify Active", "Modify Active");
ot->srna,
"use_reposition_pivot",
true,
"Reposition Pivot",
"Reposition the sculpt transform pivot to the boundary of the expand active area");
ot->prop = RNA_def_boolean(
ot->srna, "use_reposition_pivot", true, "Reposition Pivot", "Reposition pivot");
ot->prop = RNA_def_int(ot->srna,
"max_geodesic_move_preview",
10000,
0,
INT_MAX,
"Max Vertex Count for Geodesic Move Preview",
"Maximum number of vertices in the mesh for using geodesic falloff when "
"moving the origin of expand. If the total number of vertices is greater "
"than this value, the falloff will be set to spherical when moving",
0,
1000000);
}

View File

@ -76,6 +76,7 @@
#include <stdlib.h>
#define SCULPT_GEODESIC_VERTEX_NONE -1
/* Propagate distance from v1 and v2 to v0. */
static bool sculpt_geodesic_mesh_test_dist_add(
MVert *mvert, const int v0, const int v1, const int v2, float *dists, GSet *initial_vertices)
{
@ -144,8 +145,9 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
&ss->vemap, &ss->vemap_mem, mesh->medge, mesh->totvert, mesh->totedge);
}
BLI_LINKSTACK_DECLARE(queue, int);
BLI_LINKSTACK_DECLARE(queue_next, int);
/* Both contain edge indices encoded as *void. */
BLI_LINKSTACK_DECLARE(queue, void *);
BLI_LINKSTACK_DECLARE(queue_next, void *);
BLI_LINKSTACK_INIT(queue);
BLI_LINKSTACK_INIT(queue_next);
@ -159,18 +161,32 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
}
}
/* Masks vertices that are further than limit radius from an initial vertex. As there is no need
* to define a distance to them the algorithm can stop earlier by skipping them. */
BLI_bitmap *affected_vertex = BLI_BITMAP_NEW(totvert, "affected vertex");
GSetIterator gs_iter;
GSET_ITER (gs_iter, initial_vertices) {
const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
float *v_co = verts[v].co;
for (int i = 0; i < totvert; i++) {
if (len_squared_v3v3(v_co, verts[i].co) <= limit_radius_sq) {
BLI_BITMAP_ENABLE(affected_vertex, i);
if (limit_radius == FLT_MAX) {
/* In this case, no need to loop through all initial vertices to check distances as they are
* all going to be affected. */
BLI_bitmap_set_all(affected_vertex, true, totvert);
}
else {
/* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. When
* this optimization is needed, it is expected for the tool to request the distance to a low
* number of vertices (usually just 1 or 2). */
GSET_ITER (gs_iter, initial_vertices) {
const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
float *v_co = verts[v].co;
for (int i = 0; i < totvert; i++) {
if (len_squared_v3v3(v_co, verts[i].co) <= limit_radius_sq) {
BLI_BITMAP_ENABLE(affected_vertex, i);
}
}
}
}
/* Add edges adjacent to an initial vertex to the queue. */
for (int i = 0; i < totedge; i++) {
const int v1 = edges[i].v1;
const int v2 = edges[i].v2;
@ -178,13 +194,13 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
continue;
}
if (dists[v1] != FLT_MAX || dists[v2] != FLT_MAX) {
BLI_LINKSTACK_PUSH(queue, i);
BLI_LINKSTACK_PUSH(queue, POINTER_FROM_INT(i));
}
}
do {
while (BLI_LINKSTACK_SIZE(queue)) {
const int e = BLI_LINKSTACK_POP(queue);
const int e = POINTER_AS_INT(BLI_LINKSTACK_POP(queue));
int v1 = edges[e].v1;
int v2 = edges[e].v2;
@ -197,23 +213,24 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
}
if (ss->epmap[e].count != 0) {
for (int pi = 0; pi < ss->epmap[e].count; pi++) {
const int poly = ss->epmap[e].indices[pi];
for (int poly_map_index = 0; poly_map_index < ss->epmap[e].count; poly_map_index++) {
const int poly = ss->epmap[e].indices[poly_map_index];
if (ss->face_sets[poly] <= 0) {
continue;
}
const MPoly *mpoly = &mesh->mpoly[poly];
for (int li = 0; li < mpoly->totloop; li++) {
const MLoop *mloop = &mesh->mloop[li + mpoly->loopstart];
for (int loop_index = 0; loop_index < mpoly->totloop; loop_index++) {
const MLoop *mloop = &mesh->mloop[loop_index + mpoly->loopstart];
const int v_other = mloop->v;
if (ELEM(v_other, v1, v2)) {
continue;
}
if (sculpt_geodesic_mesh_test_dist_add(
verts, v_other, v1, v2, dists, initial_vertices)) {
for (int ei = 0; ei < ss->vemap[v_other].count; ei++) {
const int e_other = ss->vemap[v_other].indices[ei];
for (int edge_map_index = 0; edge_map_index < ss->vemap[v_other].count;
edge_map_index++) {
const int e_other = ss->vemap[v_other].indices[edge_map_index];
int ev_other;
if (edges[e_other].v1 == (uint)v_other) {
ev_other = edges[e_other].v2;
@ -227,7 +244,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
if (BLI_BITMAP_TEST(affected_vertex, v_other) ||
BLI_BITMAP_TEST(affected_vertex, ev_other)) {
BLI_BITMAP_ENABLE(edge_tag, e_other);
BLI_LINKSTACK_PUSH(queue_next, e_other);
BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(e_other));
}
}
}
@ -255,7 +272,8 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
}
/* For sculpt mesh data that does not support a geodesic distances algorithm, fallback to the
* distance to each vertex. */
* distance to each vertex. In this case, only one of the initial vertices will be used to
* calculate the distance. */
static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices)
{
@ -269,7 +287,11 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices
first_affected = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
break;
}
if (first_affected == SCULPT_GEODESIC_VERTEX_NONE) {
for (int i = 0; i < totvert; i++) {
dists[i] = FLT_MAX;
}
return dists;
}

View File

@ -192,6 +192,8 @@ void SCULPT_connected_components_ensure(Object *ob);
/* Boundary Info needs to be initialized in order to use this function. */
bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index);
void SCULPT_connected_components_ensure(Object *ob);
/* Sculpt Visibility API */
void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible);
@ -369,6 +371,11 @@ float *SCULPT_boundary_automasking_init(Object *ob,
float *automask_factor);
/* Geodesic distances. */
/* Returns an array indexed by vertex index containing the geodesic distance to the closest vertex
in the initial vertex set. The caller is responsible for freeing the array.
Geodesic distances will only work when used with PBVH_FACES, for other types of PBVH it will
fallback to euclidean distances to one of the initial vertices in the set. */
float *SCULPT_geodesic_distances_create(struct Object *ob,
struct GSet *initial_vertices,
const float limit_radius);
@ -1117,47 +1124,6 @@ void SCULPT_filter_to_orientation_space(float r_v[3], struct FilterCache *filter
void SCULPT_filter_to_object_space(float r_v[3], struct FilterCache *filter_cache);
void SCULPT_filter_zero_disabled_axis_components(float r_v[3], struct FilterCache *filter_cache);
typedef enum eSculptGradientType {
SCULPT_GRADIENT_LINEAR,
SCULPT_GRADIENT_SPHERICAL,
SCULPT_GRADIENT_RADIAL,
SCULPT_GRADIENT_ANGLE,
SCULPT_GRADIENT_REFLECTED,
} eSculptGradientType;
typedef struct SculptGradientContext {
eSculptGradientType gradient_type;
ViewContext vc;
int symm;
int update_type;
float line_points[2][2];
float line_length;
float depth_point[3];
float gradient_plane[4];
float initial_location[3];
float gradient_line[3];
float initial_projected_location[2];
float strength;
void (*sculpt_gradient_begin)(struct bContext *);
void (*sculpt_gradient_apply_for_element)(struct Sculpt *,
struct SculptSession *,
SculptOrigVertData *orig_data,
PBVHVertexIter *vd,
float gradient_value,
float fade_value);
void (*sculpt_gradient_node_update)(struct PBVHNode *);
void (*sculpt_gradient_end)(struct bContext *);
} SculptGradientContext;
/* Sculpt Expand. */
typedef enum eSculptExpandFalloffType {
SCULPT_EXPAND_FALLOFF_GEODESIC,
@ -1187,17 +1153,17 @@ typedef struct ExpandCache {
/* Target data elements that the expand operation will affect. */
eSculptExpandTargetType target;
/* Falloff data. */
/* Falloff data. */
eSculptExpandFalloffType falloff_type;
/* Indexed by vertex index, precalculated falloff value of that vertex (without any falloff
* editing modification applied). */
float *falloff;
/* Max falloff value in *falloff. */
float max_falloff;
float *vert_falloff;
/* Max falloff value in *vert_falloff. */
float max_vert_falloff;
/* Indexed by base mesh poly index, precalculatd falloff value of that face. This values are
* calculated from the per vertex falloff (*falloff) when needed. */
/* Indexed by base mesh poly index, precalculated falloff value of that face. These values are
* calculated from the per vertex falloff (*vert_falloff) when needed. */
float *face_falloff;
float max_face_falloff;
@ -1215,6 +1181,15 @@ typedef struct ExpandCache {
int initial_active_vertex;
int initial_active_face_set;
/* Maximum number of vertices allowed in the SculptSession for previewing the falloff using
* geodesic distances. */
int max_geodesic_move_preview;
/* Original falloff type before starting the move operation. */
eSculptExpandFalloffType move_original_falloff_type;
/* Falloff type using when moving the origin for preview. */
eSculptExpandFalloffType move_preview_falloff_type;
/* Face set ID that is going to be used when creating a new Face Set. */
int next_face_set;
@ -1222,7 +1197,7 @@ typedef struct ExpandCache {
int update_face_set;
/* Mouse position since the last time the origin was moved. Used for reference when moving the
* intial position of Expand. */
* initial position of Expand. */
float original_mouse_move[2];
/* Active components checks. */
@ -1250,7 +1225,7 @@ typedef struct ExpandCache {
/* Expand state options. */
/* Number of loops (times that the falloff is going to be repeated. */
/* Number of loops (times that the falloff is going to be repeated). */
int loop_count;
/* Invert the falloff result. */
@ -1262,7 +1237,7 @@ typedef struct ExpandCache {
/* When set to true, the mask or colors will be applied as a gradient. */
bool falloff_gradient;
/* When set to true, Expand will use the Brush fallof curve data to shape the gradient. */
/* When set to true, Expand will use the Brush falloff curve data to shape the gradient. */
bool brush_gradient;
/* When set to true, Expand will move the origin (initial active vertex and cursor position)
@ -1298,6 +1273,46 @@ typedef struct ExpandCache {
float (*original_colors)[4];
} ExpandCache;
typedef enum eSculptGradientType {
SCULPT_GRADIENT_LINEAR,
SCULPT_GRADIENT_SPHERICAL,
SCULPT_GRADIENT_RADIAL,
SCULPT_GRADIENT_ANGLE,
SCULPT_GRADIENT_REFLECTED,
} eSculptGradientType;
typedef struct SculptGradientContext {
eSculptGradientType gradient_type;
ViewContext vc;
int symm;
int update_type;
float line_points[2][2];
float line_length;
float depth_point[3];
float gradient_plane[4];
float initial_location[3];
float gradient_line[3];
float initial_projected_location[2];
float strength;
void (*sculpt_gradient_begin)(struct bContext *);
void (*sculpt_gradient_apply_for_element)(struct Sculpt *,
struct SculptSession *,
SculptOrigVertData *orig_data,
PBVHVertexIter *vd,
float gradient_value,
float fade_value);
void (*sculpt_gradient_node_update)(struct PBVHNode *);
void (*sculpt_gradient_end)(struct bContext *);
} SculptGradientContext;
/* IPMask filter vertex callback function. */
typedef float(SculptIPMaskFilterStepVertexCB)(struct SculptSession *, int, float *);
@ -1407,6 +1422,10 @@ bool SCULPT_get_redraw_rect(struct ARegion *region,
/* Operators. */
/* Expand. */
void SCULPT_OT_expand(struct wmOperatorType *ot);
void sculpt_expand_modal_keymap(struct wmKeyConfig *keyconf);
/* Gestures. */
void SCULPT_OT_face_set_lasso_gesture(struct wmOperatorType *ot);
void SCULPT_OT_face_set_box_gesture(struct wmOperatorType *ot);

View File

@ -64,13 +64,42 @@
#include "ED_screen.h"
#include "WM_api.h"
#include "WM_types.h"
#include "../interface/interface_intern.h"
#include "buttons_intern.h" /* own include */
static ScrArea *find_area_properties(const bContext *C);
static SpaceProperties *find_space_properties(const bContext *C);
/************************* Texture User **************************/
static void buttons_texture_user_node_property_add(ListBase *users,
ID *id,
PointerRNA ptr,
PropertyRNA *prop,
bNodeTree *ntree,
bNode *node,
const char *category,
int icon,
const char *name)
{
ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
user->id = id;
user->ptr = ptr;
user->prop = prop;
user->ntree = ntree;
user->node = node;
user->category = category;
user->icon = icon;
user->name = name;
user->index = BLI_listbase_count(users);
BLI_addtail(users, user);
}
static void buttons_texture_user_property_add(ListBase *users,
ID *id,
PointerRNA ptr,
@ -139,20 +168,66 @@ static void buttons_texture_users_find_nodetree(ListBase *users,
}
}
static void buttons_texture_modifier_geonodes_users_add(Object *ob,
NodesModifierData *nmd,
bNodeTree *node_tree,
ListBase *users)
{
PointerRNA ptr;
PropertyRNA *prop;
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type == NODE_GROUP && node->id) {
/* Recurse into the node group */
buttons_texture_modifier_geonodes_users_add(ob, nmd, (bNodeTree *)node->id, users);
}
else if (node->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) {
RNA_pointer_create(&node_tree->id, &RNA_Node, node, &ptr);
prop = RNA_struct_find_property(&ptr, "texture");
if (prop == NULL) {
continue;
}
PointerRNA texptr = RNA_property_pointer_get(&ptr, prop);
Tex *tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? (Tex *)texptr.data : NULL;
if (tex != NULL) {
buttons_texture_user_node_property_add(users,
&ob->id,
ptr,
prop,
node_tree,
node,
N_("Geometry Nodes"),
RNA_struct_ui_icon(ptr.type),
nmd->modifier.name);
}
}
}
}
static void buttons_texture_modifier_foreach(void *userData,
Object *ob,
ModifierData *md,
const char *propname)
{
PointerRNA ptr;
PropertyRNA *prop;
ListBase *users = userData;
RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr);
prop = RNA_struct_find_property(&ptr, propname);
if (md->type == eModifierType_Nodes) {
NodesModifierData *nmd = (NodesModifierData *)md;
if (nmd->node_group != NULL) {
buttons_texture_modifier_geonodes_users_add(ob, nmd, nmd->node_group, users);
}
}
else {
PointerRNA ptr;
PropertyRNA *prop;
buttons_texture_user_property_add(
users, &ob->id, ptr, prop, N_("Modifiers"), RNA_struct_ui_icon(ptr.type), md->name);
RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr);
prop = RNA_struct_find_property(&ptr, propname);
buttons_texture_user_property_add(
users, &ob->id, ptr, prop, N_("Modifiers"), RNA_struct_ui_icon(ptr.type), md->name);
}
}
static void buttons_texture_modifier_gpencil_foreach(void *userData,
@ -325,31 +400,32 @@ void buttons_texture_context_compute(const bContext *C, SpaceProperties *sbuts)
ct->texture = NULL;
if (ct->user) {
if (ct->user->node != NULL) {
/* Detect change of active texture node in same node tree, in that
* case we also automatically switch to the other node. */
if ((ct->user->node->flag & NODE_ACTIVE_TEXTURE) == 0) {
ButsTextureUser *user;
for (user = ct->users.first; user; user = user->next) {
if (user->ntree == ct->user->ntree && user->node != ct->user->node) {
if (user->node->flag & NODE_ACTIVE_TEXTURE) {
ct->user = user;
ct->index = BLI_findindex(&ct->users, user);
break;
}
}
}
}
}
if (ct->user->ptr.data) {
PointerRNA texptr;
Tex *tex;
/* get texture datablock pointer if it's a property */
/* Get texture datablock pointer if it's a property. */
texptr = RNA_property_pointer_get(&ct->user->ptr, ct->user->prop);
tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? texptr.data : NULL;
ct->texture = tex;
}
else if (ct->user->node && !(ct->user->node->flag & NODE_ACTIVE_TEXTURE)) {
ButsTextureUser *user;
/* detect change of active texture node in same node tree, in that
* case we also automatically switch to the other node */
for (user = ct->users.first; user; user = user->next) {
if (user->ntree == ct->user->ntree && user->node != ct->user->node) {
if (user->node->flag & NODE_ACTIVE_TEXTURE) {
ct->user = user;
ct->index = BLI_findindex(&ct->users, user);
break;
}
}
}
}
}
}
}
@ -357,7 +433,7 @@ void buttons_texture_context_compute(const bContext *C, SpaceProperties *sbuts)
static void template_texture_select(bContext *C, void *user_p, void *UNUSED(arg))
{
/* callback when selecting a texture user in the menu */
SpaceProperties *sbuts = CTX_wm_space_properties(C);
SpaceProperties *sbuts = find_space_properties(C);
ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
ButsTextureUser *user = (ButsTextureUser *)user_p;
PointerRNA texptr;
@ -371,8 +447,15 @@ static void template_texture_select(bContext *C, void *user_p, void *UNUSED(arg)
if (user->node) {
ED_node_set_active(CTX_data_main(C), user->ntree, user->node, NULL);
ct->texture = NULL;
/* Not totally sure if we should also change selection? */
LISTBASE_FOREACH (bNode *, node, &user->ntree->nodes) {
nodeSetSelected(node, false);
}
nodeSetSelected(user->node, true);
WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
}
else {
if (user->ptr.data) {
texptr = RNA_property_pointer_get(&user->ptr, user->prop);
tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? texptr.data : NULL;
@ -511,16 +594,53 @@ void uiTemplateTextureUser(uiLayout *layout, bContext *C)
/************************* Texture Show **************************/
static ScrArea *find_area_properties(const bContext *C)
{
bScreen *screen = CTX_wm_screen(C);
Object *ob = CTX_data_active_object(C);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype == SPACE_PROPERTIES) {
/* Only if unpinned, or if pinned object matches. */
SpaceProperties *sbuts = area->spacedata.first;
ID *pinid = sbuts->pinid;
if (pinid == NULL || ((GS(pinid->name) == ID_OB) && (Object *)pinid == ob)) {
return area;
}
}
}
return NULL;
}
static SpaceProperties *find_space_properties(const bContext *C)
{
ScrArea *area = find_area_properties(C);
if (area != NULL) {
return area->spacedata.first;
}
return NULL;
}
static void template_texture_show(bContext *C, void *data_p, void *prop_p)
{
SpaceProperties *sbuts = CTX_wm_space_properties(C);
ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
ButsTextureUser *user;
if (data_p == NULL || prop_p == NULL) {
return;
}
ScrArea *area = find_area_properties(C);
if (area == NULL) {
return;
}
SpaceProperties *sbuts = (SpaceProperties *)area->spacedata.first;
ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
if (!ct) {
return;
}
ButsTextureUser *user;
for (user = ct->users.first; user; user = user->next) {
if (user->ptr.data == data_p && user->prop == prop_p) {
break;
@ -537,48 +657,65 @@ static void template_texture_show(bContext *C, void *data_p, void *prop_p)
sbuts->preview = 1;
/* redraw editor */
ED_area_tag_redraw(CTX_wm_area(C));
ED_area_tag_redraw(area);
}
}
/* Button to quickly show texture in Properties Editor texture tab. */
void uiTemplateTextureShow(uiLayout *layout, const bContext *C, PointerRNA *ptr, PropertyRNA *prop)
{
/* button to quickly show texture in texture tab */
SpaceProperties *sbuts = CTX_wm_space_properties(C);
ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
ButsTextureUser *user;
/* only show button in other tabs in properties editor */
if (!ct || sbuts->mainb == BCONTEXT_TEXTURE) {
/* Only show the button if there is actually a texture assigned. */
Tex *texture = RNA_property_pointer_get(ptr, prop).data;
if (texture == NULL) {
return;
}
/* Only show the button if we are not in the Properties Editor's texture tab. */
SpaceProperties *sbuts_context = CTX_wm_space_properties(C);
if (sbuts_context != NULL && sbuts_context->mainb == BCONTEXT_TEXTURE) {
return;
}
SpaceProperties *sbuts = find_space_properties(C);
ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL;
/* find corresponding texture user */
for (user = ct->users.first; user; user = user->next) {
if (user->ptr.data == ptr->data && user->prop == prop) {
break;
ButsTextureUser *user;
bool user_found = false;
if (ct != NULL) {
for (user = ct->users.first; user; user = user->next) {
if (user->ptr.data == ptr->data && user->prop == prop) {
user_found = true;
break;
}
}
}
/* draw button */
if (user) {
uiBlock *block = uiLayoutGetBlock(layout);
uiBut *but;
but = uiDefIconBut(block,
UI_BTYPE_BUT,
0,
ICON_PROPERTIES,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0.0,
0.0,
0.0,
0.0,
TIP_("Show texture in texture tab"));
UI_but_func_set(but, template_texture_show, user->ptr.data, user->prop);
/* Draw button (disabled if we cannot find a Properties Editor to display this in). */
uiBlock *block = uiLayoutGetBlock(layout);
uiBut *but;
but = uiDefIconBut(block,
UI_BTYPE_BUT,
0,
ICON_PROPERTIES,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0.0,
0.0,
0.0,
0.0,
TIP_("Show texture in texture tab"));
UI_but_func_set(but,
template_texture_show,
user_found ? user->ptr.data : NULL,
user_found ? user->prop : NULL);
if (ct == NULL) {
UI_but_disable(but, TIP_("No (unpinned) Properties Editor found to display texture in"));
}
else if (!user_found) {
UI_but_disable(but, TIP_("No texture user found"));
}
}

View File

@ -36,6 +36,7 @@
#include "DNA_mask_types.h"
#include "DNA_scene_types.h"
#include "DNA_sound_types.h"
#include "BKE_context.h"
#include "BKE_lib_id.h"
@ -44,6 +45,8 @@
#include "BKE_movieclip.h"
#include "BKE_report.h"
#include "IMB_imbuf.h"
#include "WM_api.h"
#include "WM_types.h"
@ -89,8 +92,6 @@ typedef struct SequencerAddData {
#define SEQPROP_NOCHAN (1 << 3)
#define SEQPROP_FIT_METHOD (1 << 4)
#define SELECT 1
static const EnumPropertyItem scale_fit_methods[] = {
{SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"},
{SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image to completely fill the canvas"},
@ -216,7 +217,7 @@ static void sequencer_generic_invoke_xy__internal(bContext *C, wmOperator *op, i
}
}
static void seq_load_operator_info(SeqLoadInfo *seq_load, bContext *C, wmOperator *op)
static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@ -224,69 +225,56 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, bContext *C, wmOperato
const bool relative = (prop = RNA_struct_find_property(op->ptr, "relative_path")) &&
RNA_property_boolean_get(op->ptr, prop);
int is_file = -1;
memset(seq_load, 0, sizeof(SeqLoadInfo));
memset(load_data, 0, sizeof(SeqLoadData));
seq_load->start_frame = RNA_int_get(op->ptr, "frame_start");
seq_load->end_frame = seq_load->start_frame;
seq_load->channel = RNA_int_get(op->ptr, "channel");
seq_load->len = 1;
seq_load->fit_method = RNA_enum_get(op->ptr, "fit_method");
SEQ_tool_settings_fit_method_set(CTX_data_scene(C), seq_load->fit_method);
load_data->start_frame = RNA_int_get(op->ptr, "frame_start");
load_data->channel = RNA_int_get(op->ptr, "channel");
load_data->image.end_frame = load_data->start_frame;
load_data->image.len = 1;
load_data->fit_method = RNA_enum_get(op->ptr, "fit_method");
SEQ_tool_settings_fit_method_set(CTX_data_scene(C), load_data->fit_method);
if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) {
/* Full path, file is set by the caller. */
RNA_property_string_get(op->ptr, prop, seq_load->path);
RNA_property_string_get(op->ptr, prop, load_data->path);
is_file = 1;
}
else if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
/* Full path, file is set by the caller. */
RNA_property_string_get(op->ptr, prop, seq_load->path);
RNA_property_string_get(op->ptr, prop, load_data->path);
is_file = 0;
}
if ((is_file != -1) && relative) {
BLI_path_rel(seq_load->path, BKE_main_blendfile_path(bmain));
BLI_path_rel(load_data->path, BKE_main_blendfile_path(bmain));
}
if ((prop = RNA_struct_find_property(op->ptr, "frame_end"))) {
seq_load->end_frame = RNA_property_int_get(op->ptr, prop);
}
if ((prop = RNA_struct_find_property(op->ptr, "replace_sel")) &&
RNA_property_boolean_get(op->ptr, prop)) {
seq_load->flag |= SEQ_LOAD_REPLACE_SEL;
load_data->image.end_frame = RNA_property_int_get(op->ptr, prop);
}
if ((prop = RNA_struct_find_property(op->ptr, "cache")) &&
RNA_property_boolean_get(op->ptr, prop)) {
seq_load->flag |= SEQ_LOAD_SOUND_CACHE;
load_data->flags |= SEQ_LOAD_SOUND_CACHE;
}
if ((prop = RNA_struct_find_property(op->ptr, "mono")) &&
RNA_property_boolean_get(op->ptr, prop)) {
seq_load->flag |= SEQ_LOAD_SOUND_MONO;
}
if ((prop = RNA_struct_find_property(op->ptr, "sound")) &&
RNA_property_boolean_get(op->ptr, prop)) {
seq_load->flag |= SEQ_LOAD_MOVIE_SOUND;
load_data->flags |= SEQ_LOAD_SOUND_MONO;
}
if ((prop = RNA_struct_find_property(op->ptr, "use_framerate")) &&
RNA_property_boolean_get(op->ptr, prop)) {
seq_load->flag |= SEQ_LOAD_SYNC_FPS;
load_data->flags |= SEQ_LOAD_MOVIE_SYNC_FPS;
}
/* Create consecutive array of strips. */
seq_load->flag |= SEQ_LOAD_FRAME_ADVANCE;
if (is_file == 1) {
BLI_strncpy(seq_load->name, BLI_path_basename(seq_load->path), sizeof(seq_load->name));
BLI_strncpy(load_data->name, BLI_path_basename(load_data->path), sizeof(load_data->name));
}
else if ((prop = RNA_struct_find_property(op->ptr, "files"))) {
RNA_PROP_BEGIN (op->ptr, itemptr, prop) {
char *name = RNA_string_get_alloc(&itemptr, "name", NULL, 0);
BLI_strncpy(seq_load->name, name, sizeof(seq_load->name));
BLI_strncpy(load_data->name, name, sizeof(load_data->name));
MEM_freeN(name);
break;
}
@ -299,21 +287,27 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, bContext *C, wmOperato
SequencerAddData *sad = op->customdata;
ImageFormatData *imf = &sad->im_format;
seq_load->views_format = imf->views_format;
seq_load->flag |= SEQ_USE_VIEWS;
seq_load->stereo3d_format = &imf->stereo3d_format;
load_data->use_multiview = true;
load_data->views_format = imf->views_format;
load_data->stereo3d_format = &imf->stereo3d_format;
}
}
}
/**
* Apply generic operator options.
*/
static void sequencer_add_apply_overlap(bContext *C, wmOperator *op, Sequence *seq)
static void seq_load_apply_generic_options(bContext *C, wmOperator *op, Sequence *seq)
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
if (seq == NULL) {
return;
}
if (RNA_boolean_get(op->ptr, "replace_sel")) {
seq->flag |= SELECT;
SEQ_select_active_set(scene, seq);
}
if (RNA_boolean_get(op->ptr, "overlap") == false) {
if (SEQ_transform_test_overlap(ed->seqbasep, seq)) {
SEQ_transform_seqbase_shuffle(ed->seqbasep, seq, scene);
@ -321,17 +315,6 @@ static void sequencer_add_apply_overlap(bContext *C, wmOperator *op, Sequence *s
}
}
static void sequencer_add_apply_replace_sel(bContext *C, wmOperator *op, Sequence *seq)
{
Scene *scene = CTX_data_scene(C);
if (RNA_boolean_get(op->ptr, "replace_sel")) {
ED_sequencer_deselect_all(scene);
SEQ_select_active_set(scene, seq);
seq->flag |= SELECT;
}
}
static bool seq_effect_add_properties_poll(const bContext *UNUSED(C),
wmOperator *op,
const PropertyRNA *prop)
@ -356,34 +339,24 @@ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, true);
Scene *sce_seq;
Sequence *seq;
int start_frame, channel;
start_frame = RNA_int_get(op->ptr, "frame_start");
channel = RNA_int_get(op->ptr, "channel");
sce_seq = BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene"));
const Editing *ed = SEQ_editing_get(scene, true);
Scene *sce_seq = BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene"));
if (sce_seq == NULL) {
BKE_report(op->reports, RPT_ERROR, "Scene not found");
return OPERATOR_CANCELLED;
}
seq = SEQ_sequence_alloc(ed->seqbasep, start_frame, channel, SEQ_TYPE_SCENE);
seq->blend_mode = SEQ_TYPE_CROSS;
seq->scene = sce_seq;
seq->len = sce_seq->r.efra - sce_seq->r.sfra + 1;
if (RNA_boolean_get(op->ptr, "replace_sel")) {
ED_sequencer_deselect_all(scene);
}
BLI_strncpy(seq->name + 2, sce_seq->id.name + 2, sizeof(seq->name) - 2);
SEQ_sequence_base_unique_name_recursive(&ed->seqbase, seq);
SeqLoadData load_data;
load_data_init_from_operator(&load_data, C, op);
load_data.scene = sce_seq;
SEQ_time_update_sequence_bounds(scene, seq);
SEQ_sort(scene);
sequencer_add_apply_replace_sel(C, op, seq);
sequencer_add_apply_overlap(C, op, seq);
SEQ_relations_invalidate_cache_composite(scene, seq);
Sequence *seq = SEQ_add_scene_strip(scene, ed->seqbasep, &load_data);
seq_load_apply_generic_options(C, op, seq);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
DEG_relations_tag_update(bmain);
@ -430,36 +403,24 @@ static int sequencer_add_movieclip_strip_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, true);
MovieClip *clip;
Sequence *seq;
int start_frame, channel;
start_frame = RNA_int_get(op->ptr, "frame_start");
channel = RNA_int_get(op->ptr, "channel");
clip = BLI_findlink(&bmain->movieclips, RNA_enum_get(op->ptr, "clip"));
const Editing *ed = SEQ_editing_get(scene, true);
MovieClip *clip = BLI_findlink(&bmain->movieclips, RNA_enum_get(op->ptr, "clip"));
if (clip == NULL) {
BKE_report(op->reports, RPT_ERROR, "Movie clip not found");
return OPERATOR_CANCELLED;
}
seq = SEQ_sequence_alloc(ed->seqbasep, start_frame, channel, SEQ_TYPE_MOVIECLIP);
seq->blend_mode = SEQ_TYPE_CROSS;
seq->clip = clip;
seq->len = BKE_movieclip_get_duration(clip);
if (RNA_boolean_get(op->ptr, "replace_sel")) {
ED_sequencer_deselect_all(scene);
}
id_us_ensure_real(&seq->clip->id);
SeqLoadData load_data;
load_data_init_from_operator(&load_data, C, op);
load_data.clip = clip;
BLI_strncpy(seq->name + 2, clip->id.name + 2, sizeof(seq->name) - 2);
SEQ_sequence_base_unique_name_recursive(&ed->seqbase, seq);
SEQ_time_update_sequence_bounds(scene, seq);
SEQ_sort(scene);
sequencer_add_apply_replace_sel(C, op, seq);
sequencer_add_apply_overlap(C, op, seq);
SEQ_relations_invalidate_cache_composite(scene, seq);
Sequence *seq = SEQ_add_movieclip_strip(scene, ed->seqbasep, &load_data);
seq_load_apply_generic_options(C, op, seq);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@ -506,36 +467,24 @@ static int sequencer_add_mask_strip_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, true);
Mask *mask;
Sequence *seq;
int start_frame, channel;
start_frame = RNA_int_get(op->ptr, "frame_start");
channel = RNA_int_get(op->ptr, "channel");
mask = BLI_findlink(&bmain->masks, RNA_enum_get(op->ptr, "mask"));
const Editing *ed = SEQ_editing_get(scene, true);
Mask *mask = BLI_findlink(&bmain->masks, RNA_enum_get(op->ptr, "mask"));
if (mask == NULL) {
BKE_report(op->reports, RPT_ERROR, "Mask not found");
return OPERATOR_CANCELLED;
}
seq = SEQ_sequence_alloc(ed->seqbasep, start_frame, channel, SEQ_TYPE_MASK);
seq->blend_mode = SEQ_TYPE_CROSS;
seq->mask = mask;
seq->len = BKE_mask_get_duration(mask);
if (RNA_boolean_get(op->ptr, "replace_sel")) {
ED_sequencer_deselect_all(scene);
}
id_us_ensure_real(&seq->mask->id);
SeqLoadData load_data;
load_data_init_from_operator(&load_data, C, op);
load_data.mask = mask;
BLI_strncpy(seq->name + 2, mask->id.name + 2, sizeof(seq->name) - 2);
SEQ_sequence_base_unique_name_recursive(&ed->seqbase, seq);
SEQ_time_update_sequence_bounds(scene, seq);
SEQ_sort(scene);
sequencer_add_apply_replace_sel(C, op, seq);
sequencer_add_apply_overlap(C, op, seq);
SEQ_relations_invalidate_cache_composite(scene, seq);
Sequence *seq = SEQ_add_mask_strip(scene, ed->seqbasep, &load_data);
seq_load_apply_generic_options(C, op, seq);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@ -577,75 +526,6 @@ void SEQUENCER_OT_mask_strip_add(struct wmOperatorType *ot)
ot->prop = prop;
}
static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoadFn seq_load_fn)
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, true);
SeqLoadInfo seq_load;
int tot_files;
seq_load_operator_info(&seq_load, C, op);
if (seq_load.flag & SEQ_LOAD_REPLACE_SEL) {
ED_sequencer_deselect_all(scene);
}
tot_files = RNA_property_collection_length(op->ptr, RNA_struct_find_property(op->ptr, "files"));
if (tot_files > 1) {
char dir_only[FILE_MAX];
char file_only[FILE_MAX];
RNA_BEGIN (op->ptr, itemptr, "files") {
Sequence *seq;
RNA_string_get(op->ptr, "directory", dir_only);
RNA_string_get(&itemptr, "name", file_only);
BLI_join_dirfile(seq_load.path, sizeof(seq_load.path), dir_only, file_only);
/* Set seq_load.name, otherwise all video/audio files get the same name. */
BLI_strncpy(seq_load.name, file_only, sizeof(seq_load.name));
seq = seq_load_fn(C, ed->seqbasep, &seq_load);
if (seq) {
if (seq_load.seq_sound) {
sequencer_add_apply_overlap(C, op, seq_load.seq_sound);
}
sequencer_add_apply_overlap(C, op, seq);
}
}
RNA_END;
}
else { /* Single file./ */
Sequence *seq;
seq = seq_load_fn(C, ed->seqbasep, &seq_load);
if (seq) {
if (seq_load.seq_sound) {
sequencer_add_apply_overlap(C, op, seq_load.seq_sound);
}
sequencer_add_apply_overlap(C, op, seq);
}
}
if (op->customdata) {
MEM_freeN(op->customdata);
}
if (seq_load.tot_success == 0) {
BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", seq_load.path);
return OPERATOR_CANCELLED;
}
SEQ_sort(scene);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
static void sequencer_add_init(bContext *UNUSED(C), wmOperator *op)
{
op->customdata = MEM_callocN(sizeof(SequencerAddData), __func__);
@ -668,9 +548,98 @@ static bool sequencer_add_draw_check_fn(PointerRNA *UNUSED(ptr),
return !(STR_ELEM(prop_id, "filepath", "directory", "filename"));
}
static void sequencer_add_movie_multiple_strips(bContext *C,
wmOperator *op,
SeqLoadData *load_data)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene, true);
RNA_BEGIN (op->ptr, itemptr, "files") {
char dir_only[FILE_MAX];
char file_only[FILE_MAX];
RNA_string_get(op->ptr, "directory", dir_only);
RNA_string_get(&itemptr, "name", file_only);
BLI_join_dirfile(load_data->path, sizeof(load_data->path), dir_only, file_only);
BLI_strncpy(load_data->name, file_only, sizeof(load_data->name));
Sequence *seq_movie = NULL;
Sequence *seq_sound = NULL;
load_data->channel++;
seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data);
load_data->channel--;
if (seq_movie == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path);
}
else {
if (RNA_boolean_get(op->ptr, "sound")) {
seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
}
load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp;
seq_load_apply_generic_options(C, op, seq_sound);
seq_load_apply_generic_options(C, op, seq_movie);
}
}
RNA_END;
}
static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoadData *load_data)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene, true);
Sequence *seq_movie = NULL;
Sequence *seq_sound = NULL;
load_data->channel++;
seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data);
load_data->channel--;
if (seq_movie == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path);
return false;
}
if (RNA_boolean_get(op->ptr, "sound")) {
seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
}
seq_load_apply_generic_options(C, op, seq_sound);
seq_load_apply_generic_options(C, op, seq_movie);
return true;
}
static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op)
{
return sequencer_add_generic_strip_exec(C, op, SEQ_add_movie_strip);
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
SeqLoadData load_data;
load_data_init_from_operator(&load_data, C, op);
if (RNA_boolean_get(op->ptr, "replace_sel")) {
ED_sequencer_deselect_all(scene);
}
const int tot_files = RNA_property_collection_length(op->ptr,
RNA_struct_find_property(op->ptr, "files"));
if (tot_files > 1) {
sequencer_add_movie_multiple_strips(C, op, &load_data);
}
else {
if (!sequencer_add_movie_single_strip(C, op, &load_data)) {
return OPERATOR_CANCELLED;
}
}
if (op->customdata) {
MEM_freeN(op->customdata);
}
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
static int sequencer_add_movie_strip_invoke(bContext *C,
@ -681,7 +650,8 @@ static int sequencer_add_movie_strip_invoke(bContext *C,
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
/* Only enable "use_framerate" if there aren't any existing strips, unless overridden by user. */
/* Only enable "use_framerate" if there aren't any existing strips, unless overridden by user.
*/
if (ed && ed->seqbasep && ed->seqbasep->first) {
RNA_boolean_set(op->ptr, "use_framerate", false);
}
@ -761,9 +731,80 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot)
"Use framerate from the movie to keep sound and video in sync");
}
static void sequencer_add_sound_multiple_strips(bContext *C,
wmOperator *op,
SeqLoadData *load_data)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, true);
RNA_BEGIN (op->ptr, itemptr, "files") {
char dir_only[FILE_MAX];
char file_only[FILE_MAX];
RNA_string_get(op->ptr, "directory", dir_only);
RNA_string_get(&itemptr, "name", file_only);
BLI_join_dirfile(load_data->path, sizeof(load_data->path), dir_only, file_only);
BLI_strncpy(load_data->name, file_only, sizeof(load_data->name));
Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
if (seq == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path);
}
else {
seq_load_apply_generic_options(C, op, seq);
load_data->start_frame += seq->enddisp - seq->startdisp;
}
}
RNA_END;
}
static bool sequencer_add_sound_single_strip(bContext *C, wmOperator *op, SeqLoadData *load_data)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, true);
Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
if (seq == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path);
return false;
}
seq_load_apply_generic_options(C, op, seq);
return true;
}
static int sequencer_add_sound_strip_exec(bContext *C, wmOperator *op)
{
return sequencer_add_generic_strip_exec(C, op, SEQ_add_sound_strip);
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
SeqLoadData load_data;
load_data_init_from_operator(&load_data, C, op);
if (RNA_boolean_get(op->ptr, "replace_sel")) {
ED_sequencer_deselect_all(scene);
}
const int tot_files = RNA_property_collection_length(op->ptr,
RNA_struct_find_property(op->ptr, "files"));
if (tot_files > 1) {
sequencer_add_sound_multiple_strips(C, op, &load_data);
}
else {
if (!sequencer_add_sound_single_strip(C, op, &load_data)) {
return OPERATOR_CANCELLED;
}
}
if (op->customdata) {
MEM_freeN(op->customdata);
}
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
static int sequencer_add_sound_strip_invoke(bContext *C,
@ -873,78 +914,82 @@ void sequencer_image_seq_reserve_frames(
}
}
static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op)
static int sequencer_add_image_strip_calculate_length(wmOperator *op,
const int start_frame,
int *minframe,
int *numdigits)
{
int minframe, numdigits;
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, true);
SeqLoadInfo seq_load;
Sequence *seq;
Strip *strip;
StripElem *se;
const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders");
seq_load_operator_info(&seq_load, C, op);
/* Images are unique in how they handle this - 1 per strip elem. */
if (use_placeholders) {
seq_load.len = sequencer_image_seq_get_minmax_frame(
op, seq_load.start_frame, &minframe, &numdigits);
return sequencer_image_seq_get_minmax_frame(op, start_frame, minframe, numdigits);
}
else {
seq_load.len = RNA_property_collection_length(op->ptr,
RNA_struct_find_property(op->ptr, "files"));
return RNA_property_collection_length(op->ptr, RNA_struct_find_property(op->ptr, "files"));
}
}
if (seq_load.len == 0) {
return OPERATOR_CANCELLED;
}
static void sequencer_add_image_strip_load_files(
wmOperator *op, Sequence *seq, SeqLoadData *load_data, const int minframe, const int numdigits)
{
const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders");
if (seq_load.flag & SEQ_LOAD_REPLACE_SEL) {
ED_sequencer_deselect_all(scene);
}
/* Main adding function. */
seq = SEQ_add_image_strip(C, ed->seqbasep, &seq_load);
strip = seq->strip;
se = strip->stripdata;
seq->blend_mode = SEQ_TYPE_ALPHAOVER;
SEQ_add_image_set_directory(seq, load_data->path);
if (use_placeholders) {
sequencer_image_seq_reserve_frames(op, se, seq_load.len, minframe, numdigits);
sequencer_image_seq_reserve_frames(
op, seq->strip->stripdata, load_data->image.len, minframe, numdigits);
}
else {
size_t strip_frame = 0;
RNA_BEGIN (op->ptr, itemptr, "files") {
char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0);
BLI_strncpy(se->name, filename, sizeof(se->name));
SEQ_add_image_load_file(seq, strip_frame, filename);
MEM_freeN(filename);
se++;
strip_frame++;
}
RNA_END;
}
}
if (seq_load.len == 1) {
if (seq_load.start_frame < seq_load.end_frame) {
seq->endstill = seq_load.end_frame - seq_load.start_frame;
}
static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, true);
SeqLoadData load_data;
load_data_init_from_operator(&load_data, C, op);
int minframe, numdigits;
load_data.image.len = sequencer_add_image_strip_calculate_length(
op, load_data.start_frame, &minframe, &numdigits);
if (load_data.image.len == 0) {
return OPERATOR_CANCELLED;
}
SEQ_render_init_colorspace(seq);
SEQ_time_update_sequence_bounds(scene, seq);
SEQ_sort(scene);
if (RNA_boolean_get(op->ptr, "replace_sel")) {
ED_sequencer_deselect_all(scene);
}
/* Last active name. */
BLI_strncpy(ed->act_imagedir, strip->dir, sizeof(ed->act_imagedir));
sequencer_add_apply_overlap(C, op, seq);
Sequence *seq = SEQ_add_image_strip(CTX_data_main(C), scene, ed->seqbasep, &load_data);
sequencer_add_image_strip_load_files(op, seq, &load_data, minframe, numdigits);
SEQ_add_image_init_alpha_mode(seq);
/* Adjust length. */
if (load_data.image.len == 1) {
SEQ_transform_set_right_handle_frame(seq, load_data.image.end_frame);
SEQ_time_update_sequence(scene, seq);
}
seq_load_apply_generic_options(C, op, seq);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
if (op->customdata) {
MEM_freeN(op->customdata);
}
SEQ_relations_invalidate_cache_composite(scene, seq);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
@ -1016,80 +1061,46 @@ static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, true);
Sequence *seq;
struct SeqEffectHandle sh;
Sequence *seq1, *seq2, *seq3;
const char *error_msg;
int start_frame, end_frame, channel, type;
start_frame = RNA_int_get(op->ptr, "frame_start");
end_frame = RNA_int_get(op->ptr, "frame_end");
channel = RNA_int_get(op->ptr, "channel");
type = RNA_enum_get(op->ptr, "type");
SeqLoadData load_data;
load_data_init_from_operator(&load_data, C, op);
load_data.effect.type = RNA_enum_get(op->ptr, "type");
if (!seq_effect_find_selected(scene, NULL, type, &seq1, &seq2, &seq3, &error_msg)) {
Sequence *seq1, *seq2, *seq3;
if (!seq_effect_find_selected(
scene, NULL, load_data.effect.type, &seq1, &seq2, &seq3, &error_msg)) {
BKE_report(op->reports, RPT_ERROR, error_msg);
return OPERATOR_CANCELLED;
}
/* Check its start and end frames are valid. */
if (seq1 == NULL && end_frame <= start_frame) {
end_frame = start_frame + 1;
RNA_int_set(op->ptr, "frame_end", end_frame);
if (RNA_boolean_get(op->ptr, "replace_sel")) {
ED_sequencer_deselect_all(scene);
}
seq = SEQ_sequence_alloc(ed->seqbasep, start_frame, channel, type);
BLI_strncpy(seq->name + 2, SEQ_sequence_give_name(seq), sizeof(seq->name) - 2);
SEQ_sequence_base_unique_name_recursive(&ed->seqbase, seq);
sh = SEQ_effect_handle_get(seq);
sh.init(seq);
seq->seq1 = seq1;
seq->seq2 = seq2;
seq->seq3 = seq3;
if (!seq1) {
seq->len = 1; /* Effect is generator, set non zero length. */
SEQ_transform_set_right_handle_frame(seq, end_frame);
}
seq->flag |= SEQ_USE_EFFECT_DEFAULT_FADE;
SEQ_time_update_sequence(scene, seq);
if (seq->type == SEQ_TYPE_COLOR) {
SolidColorVars *colvars = (SolidColorVars *)seq->effectdata;
RNA_float_get_array(op->ptr, "color", colvars->col);
seq->blend_mode = SEQ_TYPE_CROSS;
}
else if (seq->type == SEQ_TYPE_ADJUSTMENT) {
seq->blend_mode = SEQ_TYPE_CROSS;
}
else if (seq->type == SEQ_TYPE_TEXT) {
seq->blend_mode = SEQ_TYPE_ALPHAOVER;
}
else if (SEQ_effect_get_num_inputs(seq->type) == 1) {
seq->blend_mode = seq1->blend_mode;
}
load_data.effect.seq1 = seq1;
load_data.effect.seq2 = seq2;
load_data.effect.seq3 = seq3;
/* Set channel. If unset, use lowest free one above strips. */
if (!RNA_struct_property_is_set(op->ptr, "channel")) {
if (seq->seq1) {
int chan = max_iii(seq->seq1 ? seq->seq1->machine : 0,
seq->seq2 ? seq->seq2->machine : 0,
seq->seq3 ? seq->seq3->machine : 0);
if (seq1 != NULL) {
int chan = max_iii(
seq1 ? seq1->machine : 0, seq2 ? seq2->machine : 0, seq3 ? seq3->machine : 0);
if (chan < MAXSEQ) {
seq->machine = chan;
load_data.channel = chan;
}
}
}
sequencer_add_apply_replace_sel(C, op, seq);
sequencer_add_apply_overlap(C, op, seq);
Sequence *seq = SEQ_add_effect_strip(scene, ed->seqbasep, &load_data);
seq_load_apply_generic_options(C, op, seq);
SEQ_relations_update_changed_seq_and_deps(scene, seq, 1, 1); /* Runs SEQ_time_update_sequence. */
SEQ_sort(scene);
if (seq->type == SEQ_TYPE_COLOR) {
SolidColorVars *colvars = (SolidColorVars *)seq->effectdata;
RNA_float_get_array(op->ptr, "color", colvars->col);
}
SEQ_relations_invalidate_cache_composite(scene, seq);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);

View File

@ -1870,93 +1870,28 @@ void SEQUENCER_OT_images_separate(wmOperatorType *ot)
/** \name Toggle Meta Strip Operator
* \{ */
void recurs_sel_seq(Sequence *seqm)
{
Sequence *seq;
seq = seqm->seqbase.first;
while (seq) {
if (seqm->flag & (SEQ_LEFTSEL + SEQ_RIGHTSEL)) {
seq->flag &= ~SEQ_ALLSEL;
}
else if (seqm->flag & SELECT) {
seq->flag |= SELECT;
}
else {
seq->flag &= ~SEQ_ALLSEL;
}
if (seq->seqbase.first) {
recurs_sel_seq(seq);
}
seq = seq->next;
}
}
static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
Sequence *last_seq = SEQ_select_active_get(scene);
MetaStack *ms;
Sequence *active_seq = SEQ_select_active_get(scene);
if (last_seq && last_seq->type == SEQ_TYPE_META && last_seq->flag & SELECT) {
if (active_seq && active_seq->type == SEQ_TYPE_META && active_seq->flag & SELECT) {
/* Enter metastrip. */
ms = MEM_mallocN(sizeof(MetaStack), "metastack");
BLI_addtail(&ed->metastack, ms);
ms->parseq = last_seq;
ms->oldbasep = ed->seqbasep;
copy_v2_v2_int(ms->disp_range, &ms->parseq->startdisp);
ed->seqbasep = &last_seq->seqbase;
SEQ_meta_stack_alloc(ed, active_seq);
SEQ_seqbase_active_set(ed, &active_seq->seqbase);
SEQ_select_active_set(scene, NULL);
}
else {
/* Exit metastrip if possible. */
Sequence *seq;
if (BLI_listbase_is_empty(&ed->metastack)) {
return OPERATOR_CANCELLED;
}
ms = ed->metastack.last;
BLI_remlink(&ed->metastack, ms);
ed->seqbasep = ms->oldbasep;
/* For old files, update from meta. */
if (ms->disp_range[0] == ms->disp_range[1]) {
copy_v2_v2_int(ms->disp_range, &ms->parseq->startdisp);
}
/* Recalc all: the meta can have effects connected to it. */
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
SEQ_time_update_sequence(scene, seq);
}
/* 2.73+, keeping endpoints is important!
* Moving them around means you can't usefully use metas in a complex edit. */
#if 1
SEQ_transform_set_left_handle_frame(ms->parseq, ms->disp_range[0]);
SEQ_transform_set_right_handle_frame(ms->parseq, ms->disp_range[1]);
SEQ_transform_fix_single_image_seq_offsets(ms->parseq);
SEQ_time_update_sequence(scene, ms->parseq);
#else
if (SEQ_transform_test_overlap(ed->seqbasep, ms->parseq)) {
SEQ_transform_seqbase_shuffle(ed->seqbasep, ms->parseq, scene);
}
#endif
MetaStack *ms = SEQ_meta_stack_active_get(ed);
SEQ_seqbase_active_set(ed, ms->oldbasep);
SEQ_select_active_set(scene, ms->parseq);
ms->parseq->flag |= SELECT;
recurs_sel_seq(ms->parseq);
MEM_freeN(ms);
SEQ_meta_stack_free(ed, ms);
}
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@ -1990,48 +1925,44 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
Sequence *active_seq = SEQ_select_active_get(scene);
ListBase *active_seqbase = SEQ_active_seqbase_get(ed);
Sequence *seq, *seqm, *next, *last_seq = SEQ_select_active_get(scene);
int channel_max = 1;
if (SEQ_transform_seqbase_isolated_sel_check(ed->seqbasep) == false) {
if (SEQ_transform_seqbase_isolated_sel_check(active_seqbase) == false) {
BKE_report(op->reports, RPT_ERROR, "Please select all related strips");
return OPERATOR_CANCELLED;
}
SEQ_prefetch_stop(scene);
/* Remove all selected from main list, and put in meta. */
int channel_max = 1, meta_start_frame = MAXFRAME, meta_end_frame = MINFRAME;
Sequence *seqm = SEQ_sequence_alloc(active_seqbase, 1, 1, SEQ_TYPE_META);
seqm = SEQ_sequence_alloc(ed->seqbasep, 1, 1, SEQ_TYPE_META); /* Channel number set later. */
strcpy(seqm->name + 2, "MetaStrip");
seqm->flag = SELECT;
seq = ed->seqbasep->first;
while (seq) {
next = seq->next;
if (seq != seqm && (seq->flag & SELECT)) {
SEQ_relations_invalidate_cache_composite(scene, seq);
channel_max = max_ii(seq->machine, channel_max);
/* Sequence is moved within the same edit, no need to re-generate the UUID. */
BLI_remlink(ed->seqbasep, seq);
/* Remove all selected from main list, and put in meta.
* Sequence is moved within the same edit, no need to re-generate the UUID. */
LISTBASE_FOREACH_MUTABLE (Sequence *, seq, active_seqbase) {
if (seq != seqm && seq->flag & SELECT) {
BLI_remlink(active_seqbase, seq);
BLI_addtail(&seqm->seqbase, seq);
SEQ_relations_invalidate_cache_preprocessed(scene, seq);
channel_max = max_ii(seq->machine, channel_max);
meta_start_frame = min_ii(seq->startdisp, meta_start_frame);
meta_end_frame = max_ii(seq->enddisp, meta_end_frame);
}
seq = next;
}
seqm->machine = last_seq ? last_seq->machine : channel_max;
seqm->machine = active_seq ? active_seq->machine : channel_max;
strcpy(seqm->name + 2, "MetaStrip");
SEQ_sequence_base_unique_name_recursive(&ed->seqbase, seqm);
seqm->start = meta_start_frame;
seqm->len = meta_end_frame - meta_start_frame;
SEQ_time_update_sequence(scene, seqm);
SEQ_select_active_set(scene, seqm);
if (SEQ_transform_test_overlap(ed->seqbasep, seqm)) {
SEQ_transform_seqbase_shuffle(ed->seqbasep, seqm, scene);
if (SEQ_transform_test_overlap(active_seqbase, seqm)) {
SEQ_transform_seqbase_shuffle(active_seqbase, seqm, scene);
}
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seqm);
SEQ_relations_invalidate_cache_composite(scene, seqm);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
@ -2058,95 +1989,43 @@ void SEQUENCER_OT_meta_make(wmOperatorType *ot)
/** \name UnMeta Strip Operator
* \{ */
static int seq_depends_on_meta(Sequence *seq, Sequence *seqm)
{
if (seq == seqm) {
return 1;
}
if (seq->seq1 && seq_depends_on_meta(seq->seq1, seqm)) {
return 1;
}
if (seq->seq2 && seq_depends_on_meta(seq->seq2, seqm)) {
return 1;
}
if (seq->seq3 && seq_depends_on_meta(seq->seq3, seqm)) {
return 1;
}
return 0;
}
static void recurs_del_seq_flag(Scene *scene, ListBase *lb, short flag, short deleteall)
{
Sequence *seq, *seqn;
Sequence *last_seq = SEQ_select_active_get(scene);
seq = lb->first;
while (seq) {
seqn = seq->next;
if ((seq->flag & flag) || deleteall) {
BLI_remlink(lb, seq);
if (seq == last_seq) {
SEQ_select_active_set(scene, NULL);
}
if (seq->type == SEQ_TYPE_META) {
recurs_del_seq_flag(scene, &seq->seqbase, flag, 1);
}
SEQ_sequence_free(scene, seq, true);
}
seq = seqn;
}
}
static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
Sequence *active_seq = SEQ_select_active_get(scene);
Sequence *seq, *last_seq = SEQ_select_active_get(scene); /* last_seq checks (ed == NULL) */
if (last_seq == NULL || last_seq->type != SEQ_TYPE_META) {
if (active_seq == NULL || active_seq->type != SEQ_TYPE_META) {
return OPERATOR_CANCELLED;
}
SEQ_prefetch_stop(scene);
for (seq = last_seq->seqbase.first; seq != NULL; seq = seq->next) {
SEQ_relations_invalidate_cache_composite(scene, seq);
LISTBASE_FOREACH (Sequence *, seq, &active_seq->seqbase) {
SEQ_relations_invalidate_cache_preprocessed(scene, seq);
}
/* This moves strips from meta to parent, sating within same edit and no new strips are
* allocated. If the UUID was unique already (as it should) it will stay unique.
* No need to re-generate the UUIDs. */
BLI_movelisttolist(ed->seqbasep, &last_seq->seqbase);
/* Remove all selected from meta, and put in main list.
* Sequence is moved within the same edit, no need to re-generate the UUID. */
BLI_movelisttolist(ed->seqbasep, &active_seq->seqbase);
BLI_listbase_clear(&active_seq->seqbase);
BLI_listbase_clear(&last_seq->seqbase);
ListBase *active_seqbase = SEQ_active_seqbase_get(ed);
SEQ_edit_flag_for_removal(scene, active_seqbase, active_seq);
SEQ_edit_remove_flagged_sequences(scene, active_seqbase);
BLI_remlink(ed->seqbasep, last_seq);
SEQ_sequence_free(scene, last_seq, true);
/* Empty meta strip, delete all effects depending on it. */
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if ((seq->type & SEQ_TYPE_EFFECT) && seq_depends_on_meta(seq, last_seq)) {
seq->flag |= SEQ_FLAG_DELETE;
}
}
recurs_del_seq_flag(scene, ed->seqbasep, SEQ_FLAG_DELETE, 0);
/* Test for effects and overlap
* don't use SEQ_CURRENT_BEGIN since that would be recursive. */
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
/* Test for effects and overlap. */
LISTBASE_FOREACH (Sequence *, seq, active_seqbase) {
if (seq->flag & SELECT) {
seq->flag &= ~SEQ_OVERLAP;
if (SEQ_transform_test_overlap(ed->seqbasep, seq)) {
SEQ_transform_seqbase_shuffle(ed->seqbasep, seq, scene);
if (SEQ_transform_test_overlap(active_seqbase, seq)) {
SEQ_transform_seqbase_shuffle(active_seqbase, seq, scene);
}
}
}
SEQ_sort(scene);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;

View File

@ -360,6 +360,31 @@ static void select_neighbor_from_last(Scene *scene, int lr)
}
#endif
void recurs_sel_seq(Sequence *seq_meta)
{
Sequence *seq;
seq = seq_meta->seqbase.first;
while (seq) {
if (seq_meta->flag & (SEQ_LEFTSEL + SEQ_RIGHTSEL)) {
seq->flag &= ~SEQ_ALLSEL;
}
else if (seq_meta->flag & SELECT) {
seq->flag |= SELECT;
}
else {
seq->flag &= ~SEQ_ALLSEL;
}
if (seq->seqbase.first) {
recurs_sel_seq(seq);
}
seq = seq->next;
}
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -90,35 +90,17 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_recursive, int *r_c
Scene *scene = t->scene;
int cfra = CFRA;
int left = SEQ_transform_get_left_handle_frame(seq, true);
int right = SEQ_transform_get_right_handle_frame(seq, true);
int left = SEQ_transform_get_left_handle_frame(seq, false);
int right = SEQ_transform_get_right_handle_frame(seq, false);
if (seq->depth == 0 && ((seq->flag & SELECT) == 0 || (seq->flag & SEQ_LOCK))) {
*r_recursive = false;
*r_count = 0;
*r_flag = 0;
}
else if (seq->type == SEQ_TYPE_META) {
/* for meta's we only ever need to extend their children, no matter what depth
* just check the meta's are in the bounds */
if (t->frame_side == 'R' && right <= cfra) {
*r_recursive = false;
}
else if (t->frame_side == 'L' && left >= cfra) {
*r_recursive = false;
}
else {
*r_recursive = true;
}
*r_count = 1;
*r_flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
}
else {
*r_recursive = false; /* not a meta, so no thinking here */
*r_count = 1; /* unless its set to 0, extend will never set 2 handles at once */
*r_recursive = false;
*r_count = 1; /* unless its set to 0, extend will never set 2 handles at once */
*r_flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
if (t->frame_side == 'R') {
@ -183,26 +165,9 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_recursive, int *r_c
else {
/* Nested, different rules apply */
#ifdef SEQ_TX_NESTED_METAS
*r_flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
*r_count = 1; /* ignore the selection for nested */
*r_recursive = (seq->type == SEQ_TYPE_META);
#else
if (seq->type == SEQ_TYPE_META) {
/* Meta's can only directly be moved between channels since they
* don't have their start and length set directly (children affect that)
* since this Meta is nested we don't need any of its data in fact.
* SEQ_time_update_sequence() will update its settings when run on the top-level meta. */
*r_flag = 0;
*r_count = 0;
*r_recursive = true;
}
else {
*r_flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
*r_count = 1; /* ignore the selection for nested */
*r_recursive = false;
}
#endif
}
}
}
@ -645,8 +610,6 @@ void createTransSeqData(TransInfo *t)
/* commented _only_ because the meta may have animation data which
* needs moving too T28158. */
#define SEQ_TX_NESTED_METAS
BLI_INLINE void trans_update_seq(Scene *sce, Sequence *seq, int old_start, int sel_flag)
{
if (seq->depth == 0) {
@ -693,17 +656,10 @@ static void flushTransSeq(TransInfo *t)
switch (tdsq->sel_flag) {
case SELECT:
#ifdef SEQ_TX_NESTED_METAS
if ((seq->depth != 0 || SEQ_transform_sequence_can_be_translated(seq))) {
/* for meta's, their children move */
seq->start = new_frame - tdsq->start_offset;
}
#else
if (seq->type != SEQ_TYPE_META && (seq->depth != 0 || seq_tx_test(seq))) {
/* for meta's, their children move */
seq->start = new_frame - tdsq->start_offset;
}
#endif
if (seq->depth == 0) {
seq->machine = round_fl_to_int(td2d->loc[1]);
CLAMP(seq->machine, 1, MAXSEQ);

View File

@ -1149,7 +1149,7 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
}
}
else {
pos = (int64_t)(position - anim->preseek) * AV_TIME_BASE / frame_rate;
pos = (int64_t)position * AV_TIME_BASE / frame_rate;
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,

View File

@ -425,22 +425,6 @@ static void rna_Itasc_update_rebuild(Main *bmain, Scene *scene, PointerRNA *ptr)
rna_Itasc_update(bmain, scene, ptr);
}
static void rna_PoseChannel_bone_custom_set(PointerRNA *ptr,
PointerRNA value,
struct ReportList *UNUSED(reports))
{
bPoseChannel *pchan = (bPoseChannel *)ptr->data;
if (pchan->custom) {
id_us_min(&pchan->custom->id);
pchan->custom = NULL;
}
pchan->custom = value.data;
id_us_plus(&pchan->custom->id);
}
static PointerRNA rna_PoseChannel_bone_group_get(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
@ -1368,9 +1352,8 @@ static void rna_def_pose_channel(BlenderRNA *brna)
prop = RNA_def_property(srna, "custom_shape", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "custom");
RNA_def_property_struct_type(prop, "Object");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_pointer_funcs(prop, NULL, "rna_PoseChannel_bone_custom_set", NULL, NULL);
RNA_def_property_ui_text(
prop, "Custom Object", "Object that defines custom draw type for this bone");
RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable");

View File

@ -53,6 +53,7 @@
# include "SEQ_add.h"
# include "SEQ_edit.h"
# include "SEQ_effects.h"
# include "SEQ_relations.h"
# include "SEQ_render.h"
# include "SEQ_sequencer.h"
@ -80,34 +81,6 @@ static void rna_Sequence_swap_internal(Sequence *seq_self,
}
}
static Sequence *alloc_generic_sequence(
ListBase *seqbase, const char *name, int frame_start, int channel, int type, const char *file)
{
Sequence *seq;
StripElem *se;
seq = SEQ_sequence_alloc(seqbase, frame_start, channel, type);
BLI_strncpy(seq->name + 2, name, sizeof(seq->name) - 2);
SEQ_sequence_base_unique_name_recursive(seqbase, seq);
Strip *strip = seq->strip;
/* Don't allocate StripElem for clip, mask and scene types. This struct is not handled in
* seq_dupli() function. */
if (file && !ELEM(type, SEQ_TYPE_MOVIECLIP, SEQ_TYPE_MASK, SEQ_TYPE_SCENE)) {
strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem");
BLI_split_dirfile(file, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name));
SEQ_render_init_colorspace(seq);
}
else {
strip->stripdata = NULL;
}
return seq;
}
static Sequence *rna_Sequences_new_clip(ID *id,
ListBase *seqbase,
Main *bmain,
@ -117,15 +90,10 @@ static Sequence *rna_Sequences_new_clip(ID *id,
int frame_start)
{
Scene *scene = (Scene *)id;
Sequence *seq;
seq = alloc_generic_sequence(
seqbase, name, frame_start, channel, SEQ_TYPE_MOVIECLIP, clip->filepath);
seq->clip = clip;
seq->len = BKE_movieclip_get_duration(clip);
id_us_plus((ID *)clip);
SEQ_time_update_sequence_bounds(scene, seq);
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, NULL, frame_start, channel);
load_data.clip = clip;
Sequence *seq = SEQ_add_movieclip_strip(scene, seqbase, &load_data);
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@ -165,15 +133,10 @@ static Sequence *rna_Sequences_new_mask(ID *id,
int frame_start)
{
Scene *scene = (Scene *)id;
Sequence *seq;
seq = alloc_generic_sequence(seqbase, name, frame_start, channel, SEQ_TYPE_MASK, mask->id.name);
seq->mask = mask;
seq->len = BKE_mask_get_duration(mask);
id_us_plus((ID *)mask);
SEQ_time_update_sequence_bounds(scene, seq);
SEQ_relations_invalidate_cache_composite(scene, seq);
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, NULL, frame_start, channel);
load_data.mask = mask;
Sequence *seq = SEQ_add_mask_strip(scene, seqbase, &load_data);
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@ -202,15 +165,10 @@ static Sequence *rna_Sequences_new_scene(ID *id,
int frame_start)
{
Scene *scene = (Scene *)id;
Sequence *seq;
seq = alloc_generic_sequence(seqbase, name, frame_start, channel, SEQ_TYPE_SCENE, NULL);
seq->scene = sce_seq;
seq->len = sce_seq->r.efra - sce_seq->r.sfra + 1;
id_us_plus((ID *)sce_seq);
SEQ_time_update_sequence_bounds(scene, seq);
SEQ_relations_invalidate_cache_composite(scene, seq);
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, NULL, frame_start, channel);
load_data.scene = sce_seq;
Sequence *seq = SEQ_add_scene_strip(scene, seqbase, &load_data);
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@ -244,27 +202,24 @@ static Sequence *rna_Sequences_meta_new_scene(ID *id,
static Sequence *rna_Sequences_new_image(ID *id,
ListBase *seqbase,
Main *bmain,
ReportList *reports,
ReportList *UNUSED(reports),
const char *name,
const char *file,
int channel,
int frame_start)
{
Scene *scene = (Scene *)id;
Sequence *seq;
seq = alloc_generic_sequence(seqbase, name, frame_start, channel, SEQ_TYPE_IMAGE, file);
seq->len = 1;
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, file, frame_start, channel);
load_data.image.len = 1;
Sequence *seq = SEQ_add_image_strip(bmain, scene, seqbase, &load_data);
if (seq->strip->stripdata->name[0] == '\0') {
BKE_report(reports, RPT_ERROR, "Sequences.new_image: unable to open image file");
BLI_remlink(seqbase, seq);
SEQ_sequence_free(scene, seq, true);
return NULL;
}
SEQ_time_update_sequence_bounds(scene, seq);
SEQ_relations_invalidate_cache_composite(scene, seq);
char dir[FILE_MAX], filename[FILE_MAX];
BLI_split_dirfile(file, dir, filename, sizeof(dir), sizeof(filename));
SEQ_add_image_set_directory(seq, dir);
SEQ_add_image_load_file(seq, 0, filename);
SEQ_add_image_init_alpha_mode(seq);
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@ -299,48 +254,47 @@ static Sequence *rna_Sequences_meta_new_image(ID *id,
id, &seq->seqbase, bmain, reports, name, file, channel, frame_start);
}
static Sequence *rna_Sequences_new_movie(
ID *id, ListBase *seqbase, const char *name, const char *file, int channel, int frame_start)
static Sequence *rna_Sequences_new_movie(ID *id,
ListBase *seqbase,
Main *bmain,
const char *name,
const char *file,
int channel,
int frame_start)
{
Scene *scene = (Scene *)id;
Sequence *seq;
StripAnim *sanim;
seq = alloc_generic_sequence(seqbase, name, frame_start, channel, SEQ_TYPE_MOVIE, file);
struct anim *an = openanim(file, IB_rect, 0, NULL);
if (an == NULL) {
/* Without anim, the strip gets duration 0, which makes it impossible to select in the UI. */
seq->len = 1;
}
else {
sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim");
BLI_addtail(&seq->anims, sanim);
sanim->anim = an;
seq->anim_preseek = IMB_anim_get_preseek(an);
seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN);
}
SEQ_time_update_sequence_bounds(scene, seq);
SEQ_relations_invalidate_cache_composite(scene, seq);
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, file, frame_start, channel);
load_data.allow_invalid_file = true;
Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data);
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene);
return seq;
}
static Sequence *rna_Sequences_editing_new_movie(
ID *id, Editing *ed, const char *name, const char *file, int channel, int frame_start)
static Sequence *rna_Sequences_editing_new_movie(ID *id,
Editing *ed,
Main *bmain,
const char *name,
const char *file,
int channel,
int frame_start)
{
return rna_Sequences_new_movie(id, &ed->seqbase, name, file, channel, frame_start);
return rna_Sequences_new_movie(id, &ed->seqbase, bmain, name, file, channel, frame_start);
}
static Sequence *rna_Sequences_meta_new_movie(
ID *id, Sequence *seq, const char *name, const char *file, int channel, int frame_start)
static Sequence *rna_Sequences_meta_new_movie(ID *id,
Sequence *seq,
Main *bmain,
const char *name,
const char *file,
int channel,
int frame_start)
{
return rna_Sequences_new_movie(id, &seq->seqbase, name, file, channel, frame_start);
return rna_Sequences_new_movie(id, &seq->seqbase, bmain, name, file, channel, frame_start);
}
# ifdef WITH_AUDASPACE
@ -354,22 +308,15 @@ static Sequence *rna_Sequences_new_sound(ID *id,
int frame_start)
{
Scene *scene = (Scene *)id;
Sequence *seq;
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, file, frame_start, channel);
load_data.allow_invalid_file = true;
Sequence *seq = SEQ_add_sound_strip(bmain, scene, seqbase, &load_data);
bSound *sound = BKE_sound_new_file(bmain, file);
SoundInfo info;
if (!BKE_sound_info_get(bmain, sound, &info)) {
BKE_id_free(bmain, sound);
if (seq == NULL) {
BKE_report(reports, RPT_ERROR, "Sequences.new_sound: unable to open sound file");
return NULL;
}
seq = alloc_generic_sequence(
seqbase, name, frame_start, channel, SEQ_TYPE_SOUND_RAM, sound->filepath);
seq->sound = sound;
seq->len = ceil((double)info.length * FPS);
SEQ_time_update_sequence_bounds(scene, seq);
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@ -432,8 +379,7 @@ static Sequence *rna_Sequences_new_effect(ID *id,
{
Scene *scene = (Scene *)id;
Sequence *seq;
struct SeqEffectHandle sh;
int num_inputs = SEQ_effect_get_num_inputs(type);
const int num_inputs = SEQ_effect_get_num_inputs(type);
switch (num_inputs) {
case 0:
@ -469,26 +415,14 @@ static Sequence *rna_Sequences_new_effect(ID *id,
return NULL;
}
seq = alloc_generic_sequence(seqbase, name, frame_start, channel, type, NULL);
sh = SEQ_effect_handle_get(seq);
seq->seq1 = seq1;
seq->seq2 = seq2;
seq->seq3 = seq3;
sh.init(seq);
if (!seq1) { /* effect has no deps */
seq->len = 1;
SEQ_transform_set_right_handle_frame(seq, frame_end);
}
seq->flag |= SEQ_USE_EFFECT_DEFAULT_FADE;
SEQ_time_update_sequence(scene, seq);
SEQ_time_update_sequence_bounds(scene, seq);
SEQ_relations_invalidate_cache_composite(scene, seq);
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, NULL, frame_start, channel);
load_data.effect.end_frame = frame_end;
load_data.effect.type = type;
load_data.effect.seq1 = seq1;
load_data.effect.seq2 = seq2;
load_data.effect.seq3 = seq3;
seq = SEQ_add_effect_strip(scene, seqbase, &load_data);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene);
@ -865,7 +799,7 @@ void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, const bool metastri
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "new_movie", new_movie_func_name);
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
RNA_def_function_ui_description(func, "Add a new movie sequence");
parm = RNA_def_string(func, "name", "Name", 0, "", "Name for the new sequence");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);

View File

@ -233,6 +233,11 @@ static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *u
&settings);
}
static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData)
{
walk(userData, ob, md, "texture");
}
static bool isDisabled(const struct Scene *UNUSED(scene),
ModifierData *md,
bool UNUSED(useRenderParams))
@ -1348,7 +1353,7 @@ ModifierTypeInfo modifierType_Nodes = {
/* dependsOnTime */ nullptr,
/* dependsOnNormals */ nullptr,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ nullptr,
/* foreachTexLink */ foreachTexLink,
/* freeRuntimeData */ nullptr,
/* panelRegister */ panelRegister,
/* blendWrite */ blendWrite,

View File

@ -47,6 +47,9 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
static const float3 scale_default = float3(1.0f);
OutputAttributePtr scale_attribute = component.attribute_try_get_for_output(
"scale", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, &scale_default);
if (!scale_attribute) {
return;
}
ReadAttributePtr attribute = params.get_input_attribute(
"Factor", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr);
if (!attribute) {

View File

@ -32,48 +32,73 @@ struct Scene;
struct Sequence;
struct bContext;
/* api for adding new sequence strips */
typedef struct SeqLoadInfo {
int start_frame;
int end_frame;
int channel;
int flag; /* use sound, replace sel */
int type;
int len; /* only for image strips */
char path[1024]; /* 1024 = FILE_MAX */
eSeqImageFitMethod fit_method;
/* SeqLoadData.flags */
typedef enum eSeqLoadFlags {
SEQ_LOAD_SOUND_CACHE = (1 << 1),
SEQ_LOAD_SOUND_MONO = (1 << 2),
SEQ_LOAD_MOVIE_SYNC_FPS = (1 << 3),
} eSeqLoadFlags;
/* multiview */
/* Api for adding new sequence strips. */
typedef struct SeqLoadData {
int start_frame;
int channel;
char name[64]; /* Strip name. */
char path[1024]; /* 1024 = FILE_MAX */
struct {
int len;
int end_frame;
} image; /* Only for image strips. */
struct Scene *scene; /* Only for scene strips. */
struct MovieClip *clip; /* Only for clip strips. */
struct Mask *mask; /* Only for mask strips. */
struct {
int type;
int end_frame;
struct Sequence *seq1;
struct Sequence *seq2;
struct Sequence *seq3;
} effect; /* Only for effect strips. */
eSeqLoadFlags flags;
eSeqImageFitMethod fit_method;
bool use_multiview;
char views_format;
struct Stereo3dFormat *stereo3d_format;
bool allow_invalid_file; /* Used by RNA API to create placeholder strips. */
} SeqLoadData;
/* return values */
char name[64];
struct Sequence *seq_sound; /* for movie's */
int tot_success;
int tot_error;
} SeqLoadInfo;
/* SeqLoadInfo.flag */
#define SEQ_LOAD_REPLACE_SEL (1 << 0)
#define SEQ_LOAD_FRAME_ADVANCE (1 << 1)
#define SEQ_LOAD_MOVIE_SOUND (1 << 2)
#define SEQ_LOAD_SOUND_CACHE (1 << 3)
#define SEQ_LOAD_SYNC_FPS (1 << 4)
#define SEQ_LOAD_SOUND_MONO (1 << 5)
/* use as an api function */
typedef struct Sequence *(*SeqLoadFn)(struct bContext *, ListBase *, struct SeqLoadInfo *);
struct Sequence *SEQ_add_image_strip(struct bContext *C,
ListBase *seqbasep,
struct SeqLoadInfo *seq_load);
struct Sequence *SEQ_add_sound_strip(struct bContext *C,
ListBase *seqbasep,
struct SeqLoadInfo *seq_load);
struct Sequence *SEQ_add_movie_strip(struct bContext *C,
ListBase *seqbasep,
struct SeqLoadInfo *seq_load);
void SEQ_add_load_data_init(struct SeqLoadData *load_data,
const char *name,
const char *path,
const int start_frame,
const int channel);
struct Sequence *SEQ_add_image_strip(struct Main *bmain,
struct Scene *scene,
struct ListBase *seqbase,
struct SeqLoadData *load_data);
struct Sequence *SEQ_add_sound_strip(struct Main *bmain,
struct Scene *scene,
struct ListBase *seqbase,
struct SeqLoadData *load_data);
struct Sequence *SEQ_add_movie_strip(struct Main *bmain,
struct Scene *scene,
struct ListBase *seqbase,
struct SeqLoadData *load_data);
struct Sequence *SEQ_add_scene_strip(struct Scene *scene,
struct ListBase *seqbase,
struct SeqLoadData *load_data);
struct Sequence *SEQ_add_movieclip_strip(struct Scene *scene,
struct ListBase *seqbase,
struct SeqLoadData *load_data);
struct Sequence *SEQ_add_mask_strip(struct Scene *scene,
struct ListBase *seqbase,
struct SeqLoadData *load_data);
struct Sequence *SEQ_add_effect_strip(struct Scene *scene,
struct ListBase *seqbase,
struct SeqLoadData *load_data);
void SEQ_add_image_set_directory(struct Sequence *seq, char *path);
void SEQ_add_image_load_file(struct Sequence *seq, size_t strip_frame, char *filename);
void SEQ_add_image_init_alpha_mode(struct Sequence *seq);
void SEQ_add_reload_new_file(struct Main *bmain,
struct Scene *scene,
struct Sequence *seq,

View File

@ -62,8 +62,12 @@ struct Editing *SEQ_editing_get(struct Scene *scene, bool alloc);
struct Editing *SEQ_editing_ensure(struct Scene *scene);
void SEQ_editing_free(struct Scene *scene, const bool do_id_user);
struct ListBase *SEQ_active_seqbase_get(const struct Editing *ed);
void SEQ_seqbase_active_set(struct Editing *ed, struct ListBase *seqbase);
struct Sequence *SEQ_sequence_alloc(ListBase *lb, int timeline_frame, int machine, int type);
void SEQ_sequence_free(struct Scene *scene, struct Sequence *seq, const bool do_clean_animdata);
struct MetaStack *SEQ_meta_stack_alloc(struct Editing *ed, struct Sequence *seq_meta);
struct MetaStack *SEQ_meta_stack_active_get(const struct Editing *ed);
void SEQ_meta_stack_free(struct Editing *ed, struct MetaStack *ms);
void SEQ_offset_animdata(struct Scene *scene, struct Sequence *seq, int ofs);
void SEQ_dupe_animdata(struct Scene *scene, const char *name_src, const char *name_dst);
struct Sequence *SEQ_sequence_dupli_recursive(const struct Scene *scene_src,

View File

@ -203,34 +203,6 @@ void SEQ_render_pixel_from_sequencer_space_v4(struct Scene *scene, float pixel[4
}
}
void SEQ_render_init_colorspace(Sequence *seq)
{
if (seq->strip && seq->strip->stripdata) {
char name[FILE_MAX];
ImBuf *ibuf;
BLI_join_dirfile(name, sizeof(name), seq->strip->dir, seq->strip->stripdata->name);
BLI_path_abs(name, BKE_main_blendfile_path_from_global());
/* initialize input color space */
if (seq->type == SEQ_TYPE_IMAGE) {
ibuf = IMB_loadiffname(
name, IB_test | IB_alphamode_detect, seq->strip->colorspace_settings.name);
/* byte images are default to straight alpha, however sequencer
* works in premul space, so mark strip to be premultiplied first
*/
seq->alpha_mode = SEQ_ALPHA_STRAIGHT;
if (ibuf) {
if (ibuf->flags & IB_alphamode_premul) {
seq->alpha_mode = IMA_ALPHA_PREMUL;
}
IMB_freeImBuf(ibuf);
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
@ -611,6 +583,7 @@ static bool seq_need_scale_to_render_size(const Sequence *seq, bool is_proxy_ima
return true;
}
if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->type == SEQ_TYPE_MASK ||
seq->type == SEQ_TYPE_META ||
(seq->type == SEQ_TYPE_SCENE && ((seq->flag & SEQ_SCENE_STRIPS) != 0))) {
return true;
}

View File

@ -344,6 +344,58 @@ ListBase *SEQ_active_seqbase_get(const Editing *ed)
return ed->seqbasep;
}
/**
* Set seqbase that is being viewed currently. This can be main seqbase or meta strip seqbase
*
* \param ed: sequence editor data
* \param seqbase: ListBase with strips
*/
void SEQ_seqbase_active_set(Editing *ed, ListBase *seqbase)
{
ed->seqbasep = seqbase;
}
/**
* Create and initialize MetaStack, append it to ed->metastack ListBase
*
* \param ed: sequence editor data
* \param seq_meta: meta strip
* \return pointer to created meta stack
*/
MetaStack *SEQ_meta_stack_alloc(Editing *ed, Sequence *seq_meta)
{
MetaStack *ms = MEM_mallocN(sizeof(MetaStack), "metastack");
BLI_addtail(&ed->metastack, ms);
ms->parseq = seq_meta;
ms->oldbasep = ed->seqbasep;
copy_v2_v2_int(ms->disp_range, &ms->parseq->startdisp);
return ms;
}
/**
* Free MetaStack and remoove it from ed->metastack ListBase
*
* \param ed: sequence editor data
* \param ms: meta stack
*/
void SEQ_meta_stack_free(Editing *ed, MetaStack *ms)
{
BLI_remlink(&ed->metastack, ms);
MEM_freeN(ms);
}
/**
* Get MetaStack that coresponds to current level that is being viewed
*
* \param ed: sequence editor data
* \return pointer to meta stack
*/
MetaStack *SEQ_meta_stack_active_get(const Editing *ed)
{
return ed->metastack.last;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -29,6 +29,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_mask_types.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "DNA_sound_types.h"
@ -54,7 +55,9 @@
#include "IMB_metadata.h"
#include "SEQ_add.h"
#include "SEQ_effects.h"
#include "SEQ_relations.h"
#include "SEQ_render.h"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
@ -65,168 +68,369 @@
#include "proxy.h"
#include "utils.h"
static void seq_load_apply(Main *bmain, Scene *scene, Sequence *seq, SeqLoadInfo *seq_load)
/**
* Initialize common SeqLoadData members
*
* \param load_data: SeqLoadData to be initialized
* \param name: strip name (can be NULL)
* \param path: path to file that is used as strip input (can be NULL)
* \param start_frame: timeline frame where strip will be created
* \param channel: timeline channel where strip will be created
*
*/
void SEQ_add_load_data_init(SeqLoadData *load_data,
const char *name,
const char *path,
const int start_frame,
const int channel)
{
if (seq) {
BLI_strncpy_utf8(seq->name + 2, seq_load->name, sizeof(seq->name) - 2);
BLI_utf8_invalid_strip(seq->name + 2, strlen(seq->name + 2));
SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);
memset(load_data, 0, sizeof(SeqLoadData));
if (name != NULL) {
BLI_strncpy(load_data->name, name, sizeof(load_data->name));
}
if (path != NULL) {
BLI_strncpy(load_data->path, path, sizeof(load_data->path));
}
load_data->start_frame = start_frame;
load_data->channel = channel;
}
if (seq_load->flag & SEQ_LOAD_FRAME_ADVANCE) {
seq_load->start_frame += (seq->enddisp - seq->startdisp);
}
static void seq_add_generic_update(Scene *scene, ListBase *seqbase, Sequence *seq)
{
SEQ_sequence_base_unique_name_recursive(seqbase, seq);
SEQ_time_update_sequence_bounds(scene, seq);
SEQ_sort(scene);
SEQ_relations_invalidate_cache_composite(scene, seq);
}
if (seq_load->flag & SEQ_LOAD_REPLACE_SEL) {
seq_load->flag |= SELECT;
SEQ_select_active_set(scene, seq);
}
if (seq_load->flag & SEQ_LOAD_SOUND_MONO) {
seq->sound->flags |= SOUND_FLAGS_MONO;
BKE_sound_load(bmain, seq->sound);
}
if (seq_load->flag & SEQ_LOAD_SOUND_CACHE) {
if (seq->sound) {
seq->sound->flags |= SOUND_FLAGS_CACHING;
}
}
seq_load->tot_success++;
static void seq_add_set_name(Sequence *seq, SeqLoadData *load_data)
{
if (load_data->name != NULL) {
BLI_strncpy(seq->name + 2, load_data->name, sizeof(seq->name) - 2);
}
else {
seq_load->tot_error++;
if (seq->type == SEQ_TYPE_SCENE) {
BLI_strncpy(seq->name + 2, load_data->scene->id.name + 2, sizeof(seq->name) - 2);
}
else if (seq->type == SEQ_TYPE_MOVIECLIP) {
BLI_strncpy(seq->name + 2, load_data->clip->id.name + 2, sizeof(seq->name) - 2);
}
else if (seq->type == SEQ_TYPE_MASK) {
BLI_strncpy(seq->name + 2, load_data->mask->id.name + 2, sizeof(seq->name) - 2);
}
else if ((seq->type & SEQ_TYPE_EFFECT) != 0) {
BLI_strncpy(seq->name + 2, SEQ_sequence_give_name(seq), sizeof(seq->name) - 2);
}
else { /* Image, sound and movie. */
BLI_strncpy_utf8(seq->name + 2, load_data->name, sizeof(seq->name) - 2);
BLI_utf8_invalid_strip(seq->name + 2, strlen(seq->name + 2));
}
}
}
/* NOTE: this function doesn't fill in image names */
Sequence *SEQ_add_image_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo *seq_load)
/**
* Add scene strip.
*
* \param scene: Scene where strips will be added
* \param seqbase: ListBase where strips will be added
* \param load_data: SeqLoadData with information necessary to create strip
* \return created strip
*/
Sequence *SEQ_add_scene_strip(Scene *scene, ListBase *seqbase, struct SeqLoadData *load_data)
{
Scene *scene = CTX_data_scene(C); /* only for active seq */
Sequence *seq;
Strip *strip;
Sequence *seq = SEQ_sequence_alloc(
seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_SCENE);
seq->blend_mode = SEQ_TYPE_CROSS;
seq->scene = load_data->scene;
seq->len = load_data->scene->r.efra - load_data->scene->r.sfra + 1;
id_us_ensure_real((ID *)load_data->scene);
seq_add_set_name(seq, load_data);
seq_add_generic_update(scene, seqbase, seq);
return seq;
}
seq = SEQ_sequence_alloc(seqbasep, seq_load->start_frame, seq_load->channel, SEQ_TYPE_IMAGE);
seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */
/**
* Add movieclip strip.
*
* \param scene: Scene where strips will be added
* \param seqbase: ListBase where strips will be added
* \param load_data: SeqLoadData with information necessary to create strip
* \return created strip
*/
Sequence *SEQ_add_movieclip_strip(Scene *scene, ListBase *seqbase, struct SeqLoadData *load_data)
{
Sequence *seq = SEQ_sequence_alloc(
seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_MOVIECLIP);
seq->blend_mode = SEQ_TYPE_CROSS;
seq->clip = load_data->clip;
seq->len = BKE_movieclip_get_duration(load_data->clip);
id_us_ensure_real((ID *)load_data->clip);
seq_add_set_name(seq, load_data);
seq_add_generic_update(scene, seqbase, seq);
return seq;
}
/* basic defaults */
seq->len = seq_load->len ? seq_load->len : 1;
/**
* Add mask strip.
*
* \param scene: Scene where strips will be added
* \param seqbase: ListBase where strips will be added
* \param load_data: SeqLoadData with information necessary to create strip
* \return created strip
*/
Sequence *SEQ_add_mask_strip(Scene *scene, ListBase *seqbase, struct SeqLoadData *load_data)
{
Sequence *seq = SEQ_sequence_alloc(
seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_MASK);
seq->blend_mode = SEQ_TYPE_CROSS;
seq->mask = load_data->mask;
seq->len = BKE_mask_get_duration(load_data->mask);
id_us_ensure_real((ID *)load_data->mask);
seq_add_set_name(seq, load_data);
seq_add_generic_update(scene, seqbase, seq);
return seq;
}
strip = seq->strip;
strip->stripdata = MEM_callocN(seq->len * sizeof(StripElem), "stripelem");
BLI_strncpy(strip->dir, seq_load->path, sizeof(strip->dir));
/**
* Add effect strip.
*
* \param scene: Scene where strips will be added
* \param seqbase: ListBase where strips will be added
* \param load_data: SeqLoadData with information necessary to create strip
* \return created strip
*/
Sequence *SEQ_add_effect_strip(Scene *scene, ListBase *seqbase, struct SeqLoadData *load_data)
{
Sequence *seq = SEQ_sequence_alloc(
seqbase, load_data->start_frame, load_data->channel, load_data->effect.type);
if (seq_load->stereo3d_format) {
*seq->stereo3d_format = *seq_load->stereo3d_format;
seq->flag |= SEQ_USE_EFFECT_DEFAULT_FADE;
struct SeqEffectHandle sh = SEQ_effect_handle_get(seq);
sh.init(seq);
seq->seq1 = load_data->effect.seq1;
seq->seq2 = load_data->effect.seq2;
seq->seq3 = load_data->effect.seq3;
if (seq->type == SEQ_TYPE_COLOR) {
seq->blend_mode = SEQ_TYPE_CROSS;
}
else if (seq->type == SEQ_TYPE_ADJUSTMENT) {
seq->blend_mode = SEQ_TYPE_CROSS;
}
else if (seq->type == SEQ_TYPE_TEXT) {
seq->blend_mode = SEQ_TYPE_ALPHAOVER;
}
else if (SEQ_effect_get_num_inputs(seq->type) == 1) {
seq->blend_mode = seq->seq1->blend_mode;
}
seq->views_format = seq_load->views_format;
seq->flag |= seq_load->flag & SEQ_USE_VIEWS;
if (!load_data->effect.seq1) {
seq->len = 1; /* Effect is generator, set non zero length. */
SEQ_transform_set_right_handle_frame(seq, load_data->image.end_frame);
}
SEQ_relations_update_changed_seq_and_deps(scene, seq, 1, 1); /* Runs SEQ_time_update_sequence. */
seq_add_set_name(seq, load_data);
seq_add_generic_update(scene, seqbase, seq);
seq_load_apply(CTX_data_main(C), scene, seq, seq_load);
return seq;
}
/**
* Set directory used by image strip.
*
* \param seq: image strip to be changed
* \param path: directory path
*/
void SEQ_add_image_set_directory(Sequence *seq, char *path)
{
BLI_strncpy(seq->strip->dir, path, sizeof(seq->strip->dir));
}
/**
* Set directory used by image strip.
*
* \param seq: image strip to be changed
* \param strip_frame: frame index of strip to be changed
* \param filename: image filename (only filename, not complete path)
*/
void SEQ_add_image_load_file(Sequence *seq, size_t strip_frame, char *filename)
{
StripElem *se = SEQ_render_give_stripelem(seq, seq->start + strip_frame);
BLI_strncpy(se->name, filename, sizeof(se->name));
}
/**
* Set image strip alpha mode
*
* \param seq: image strip to be changed
*/
void SEQ_add_image_init_alpha_mode(Sequence *seq)
{
if (seq->strip && seq->strip->stripdata) {
char name[FILE_MAX];
ImBuf *ibuf;
BLI_join_dirfile(name, sizeof(name), seq->strip->dir, seq->strip->stripdata->name);
BLI_path_abs(name, BKE_main_blendfile_path_from_global());
/* Initialize input color space. */
if (seq->type == SEQ_TYPE_IMAGE) {
ibuf = IMB_loadiffname(
name, IB_test | IB_alphamode_detect, seq->strip->colorspace_settings.name);
/* Byte images are default to straight alpha, however sequencer
* works in premul space, so mark strip to be premultiplied first.
*/
seq->alpha_mode = SEQ_ALPHA_STRAIGHT;
if (ibuf) {
if (ibuf->flags & IB_alphamode_premul) {
seq->alpha_mode = IMA_ALPHA_PREMUL;
}
IMB_freeImBuf(ibuf);
}
}
}
}
/**
* Add image strip.
* NOTE: Use SEQ_add_image_set_directory() and SEQ_add_image_load_file() to load image sequences
*
* \param main: Main reference
* \param scene: Scene where strips will be added
* \param seqbase: ListBase where strips will be added
* \param load_data: SeqLoadData with information necessary to create strip
* \return created strip
*/
Sequence *SEQ_add_image_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data)
{
Sequence *seq = SEQ_sequence_alloc(
seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_IMAGE);
seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */
seq->len = load_data->image.len;
Strip *strip = seq->strip;
strip->stripdata = MEM_callocN(load_data->image.len * sizeof(StripElem), "stripelem");
/* Multiview settings. */
if (load_data->use_multiview) {
seq->flag |= SEQ_USE_VIEWS;
seq->views_format = load_data->views_format;
}
if (load_data->stereo3d_format) {
seq->stereo3d_format = load_data->stereo3d_format;
}
/* Set initial scale based on load_data->fit_method. */
char file_path[FILE_MAX];
BLI_join_dirfile(file_path, sizeof(file_path), seq_load->path, seq_load->name);
BLI_path_abs(file_path, BKE_main_blendfile_path(CTX_data_main(C)));
BLI_join_dirfile(file_path, sizeof(file_path), load_data->path, load_data->name);
BLI_path_abs(file_path, BKE_main_blendfile_path(bmain));
ImBuf *ibuf = IMB_loadiffname(file_path, IB_rect, seq->strip->colorspace_settings.name);
if (ibuf != NULL) {
SEQ_set_scale_to_fit(
seq, ibuf->x, ibuf->y, scene->r.xsch, scene->r.ysch, seq_load->fit_method);
seq, ibuf->x, ibuf->y, scene->r.xsch, scene->r.ysch, load_data->fit_method);
IMB_freeImBuf(ibuf);
}
SEQ_relations_invalidate_cache_composite(scene, seq);
/* Set Last active directory. */
BLI_strncpy(scene->ed->act_imagedir, seq->strip->dir, sizeof(scene->ed->act_imagedir));
seq_add_set_name(seq, load_data);
seq_add_generic_update(scene, seqbase, seq);
return seq;
}
#ifdef WITH_AUDASPACE
Sequence *SEQ_add_sound_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo *seq_load)
/**
* Add sound strip.
* NOTE: Use SEQ_add_image_set_directory() and SEQ_add_image_load_file() to load image sequences
*
* \param main: Main reference
* \param scene: Scene where strips will be added
* \param seqbase: ListBase where strips will be added
* \param load_data: SeqLoadData with information necessary to create strip
* \return created strip
*/
Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C); /* only for sound */
Editing *ed = SEQ_editing_get(scene, false);
bSound *sound;
Sequence *seq; /* generic strip vars */
Strip *strip;
StripElem *se;
sound = BKE_sound_new_file(bmain, seq_load->path); /* handles relative paths */
bSound *sound = BKE_sound_new_file(bmain, load_data->path); /* Handles relative paths. */
SoundInfo info;
if (!BKE_sound_info_get(bmain, sound, &info)) {
bool sound_loaded = BKE_sound_info_get(bmain, sound, &info);
if (!sound_loaded && !load_data->allow_invalid_file) {
BKE_id_free(bmain, sound);
return NULL;
}
if (info.specs.channels == SOUND_CHANNELS_INVALID) {
if (info.specs.channels == SOUND_CHANNELS_INVALID && !load_data->allow_invalid_file) {
BKE_id_free(bmain, sound);
return NULL;
}
seq = SEQ_sequence_alloc(seqbasep, seq_load->start_frame, seq_load->channel, SEQ_TYPE_SOUND_RAM);
Sequence *seq = SEQ_sequence_alloc(
seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_SOUND_RAM);
seq->sound = sound;
BLI_strncpy(seq->name + 2, "Sound", SEQ_NAME_MAXSTR - 2);
SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);
/* basic defaults */
/* We add a very small negative offset here, because
* ceil(132.0) == 133.0, not nice with videos, see T47135. */
seq->len = (int)ceil((double)info.length * FPS - 1e-4);
strip = seq->strip;
/* we only need 1 element to store the filename */
strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem");
BLI_split_dirfile(seq_load->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name));
seq->scene_sound = NULL;
SEQ_time_update_sequence_bounds(scene, seq);
/* We add a very small negative offset here, because
* ceil(132.0) == 133.0, not nice with videos, see T47135. */
seq->len = MAX2(1, (int)ceil((double)info.length * FPS - 1e-4));
/* last active name */
BLI_strncpy(ed->act_sounddir, strip->dir, FILE_MAXDIR);
Strip *strip = seq->strip;
/* We only need 1 element to store the filename. */
StripElem *se = strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem");
BLI_split_dirfile(load_data->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name));
seq_load_apply(bmain, scene, seq, seq_load);
if (seq != NULL && seq->sound != NULL) {
if (load_data->flags & SEQ_LOAD_SOUND_MONO) {
seq->sound->flags |= SOUND_FLAGS_MONO;
}
/* TODO(sergey): Shall we tag here or in the operator? */
DEG_relations_tag_update(bmain);
if (load_data->flags & SEQ_LOAD_SOUND_CACHE) {
if (seq->sound) {
seq->sound->flags |= SOUND_FLAGS_CACHING;
}
}
}
/* Set Last active directory. */
BLI_strncpy(scene->ed->act_sounddir, strip->dir, FILE_MAXDIR);
seq_add_set_name(seq, load_data);
seq_add_generic_update(scene, seqbase, seq);
return seq;
}
#else // WITH_AUDASPACE
Sequence *SEQ_add_sound_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo *seq_load)
{
(void)C;
(void)seqbasep;
(void)seq_load;
return NULL;
}
Sequence *SEQ_add_sound_strip(Main *UNUSED(bmain),
Scene *UNUSED(scene),
ListBase *UNUSED(seqbase),
const SeqLoadData *UNUSED(load_data))
#endif // WITH_AUDASPACE
Sequence *SEQ_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo *seq_load)
/**
* Add movie strip.
*
* \param main: Main reference
* \param scene: Scene where strips will be added
* \param seqbase: ListBase where strips will be added
* \param load_data: SeqLoadData with information necessary to create strip
* \return created strip
*/
Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C); /* only for sound */
char path[sizeof(seq_load->path)];
Sequence *seq; /* generic strip vars */
Strip *strip;
StripElem *se;
char colorspace[64] = "\0"; /* MAX_COLORSPACE_NAME */
bool is_multiview_loaded = false;
const bool is_multiview = (seq_load->flag & SEQ_USE_VIEWS) != 0;
const int totfiles = seq_num_files(scene, seq_load->views_format, is_multiview);
struct anim **anim_arr;
int i;
BLI_strncpy(path, seq_load->path, sizeof(path));
char path[sizeof(load_data->path)];
BLI_strncpy(path, load_data->path, sizeof(path));
BLI_path_abs(path, BKE_main_blendfile_path(bmain));
anim_arr = MEM_callocN(sizeof(struct anim *) * totfiles, "Video files");
char colorspace[64] = "\0"; /* MAX_COLORSPACE_NAME */
bool is_multiview_loaded = false;
const int totfiles = seq_num_files(scene, load_data->views_format, load_data->use_multiview);
struct anim **anim_arr = MEM_callocN(sizeof(struct anim *) * totfiles, "Video files");
int i;
if (is_multiview && (seq_load->views_format == R_IMF_VIEWS_INDIVIDUAL)) {
if (load_data->use_multiview && (load_data->views_format == R_IMF_VIEWS_INDIVIDUAL)) {
char prefix[FILE_MAX];
const char *ext = NULL;
size_t j = 0;
@ -245,38 +449,30 @@ Sequence *SEQ_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo *seq_
j++;
}
}
if (j == 0) {
MEM_freeN(anim_arr);
return NULL;
}
is_multiview_loaded = true;
}
}
if (is_multiview_loaded == false) {
anim_arr[0] = openanim(path, IB_rect, 0, colorspace);
if (anim_arr[0] == NULL) {
MEM_freeN(anim_arr);
return NULL;
}
}
if (seq_load->flag & SEQ_LOAD_MOVIE_SOUND) {
seq_load->channel++;
if (anim_arr[0] == NULL && !load_data->allow_invalid_file) {
MEM_freeN(anim_arr);
return NULL;
}
seq = SEQ_sequence_alloc(seqbasep, seq_load->start_frame, seq_load->channel, SEQ_TYPE_MOVIE);
/* multiview settings */
if (seq_load->stereo3d_format) {
*seq->stereo3d_format = *seq_load->stereo3d_format;
seq->views_format = seq_load->views_format;
Sequence *seq = SEQ_sequence_alloc(
seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_MOVIE);
/* Multiview settings. */
if (load_data->use_multiview) {
seq->flag |= SEQ_USE_VIEWS;
seq->views_format = load_data->views_format;
}
if (load_data->stereo3d_format) {
seq->stereo3d_format = load_data->stereo3d_format;
}
seq->flag |= seq_load->flag & SEQ_USE_VIEWS;
seq->type = SEQ_TYPE_MOVIE;
seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */
for (i = 0; i < totfiles; i++) {
if (anim_arr[i]) {
@ -289,51 +485,38 @@ Sequence *SEQ_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo *seq_
}
}
IMB_anim_load_metadata(anim_arr[0]);
seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */
seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]);
if (anim_arr[0] != NULL) {
seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]);
seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN);
const float width = IMB_anim_get_image_width(anim_arr[0]);
const float height = IMB_anim_get_image_height(anim_arr[0]);
SEQ_set_scale_to_fit(seq, width, height, scene->r.xsch, scene->r.ysch, seq_load->fit_method);
IMB_anim_load_metadata(anim_arr[0]);
BLI_strncpy(seq->name + 2, "Movie", SEQ_NAME_MAXSTR - 2);
SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);
/* Adjust scene's frame rate settings to match. */
if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) {
IMB_anim_get_fps(anim_arr[0], &scene->r.frs_sec, &scene->r.frs_sec_base, true);
}
/* adjust scene's frame rate settings to match */
if (seq_load->flag & SEQ_LOAD_SYNC_FPS) {
IMB_anim_get_fps(anim_arr[0], &scene->r.frs_sec, &scene->r.frs_sec_base, true);
/* Set initial scale based on load_data->fit_method. */
const float width = IMB_anim_get_image_width(anim_arr[0]);
const float height = IMB_anim_get_image_height(anim_arr[0]);
SEQ_set_scale_to_fit(seq, width, height, scene->r.xsch, scene->r.ysch, load_data->fit_method);
}
/* basic defaults */
seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN);
strip = seq->strip;
seq->len = MAX2(1, seq->len);
BLI_strncpy(seq->strip->colorspace_settings.name,
colorspace,
sizeof(seq->strip->colorspace_settings.name));
/* we only need 1 element for MOVIE strips */
Strip *strip = seq->strip;
/* We only need 1 element for MOVIE strips. */
StripElem *se;
strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem");
BLI_split_dirfile(load_data->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name));
BLI_split_dirfile(seq_load->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name));
SEQ_time_update_sequence_bounds(scene, seq);
if (seq_load->name[0] == '\0') {
BLI_strncpy(seq_load->name, se->name, sizeof(seq_load->name));
}
if (seq_load->flag & SEQ_LOAD_MOVIE_SOUND) {
int start_frame_back = seq_load->start_frame;
seq_load->channel--;
seq_load->seq_sound = SEQ_add_sound_strip(C, seqbasep, seq_load);
seq_load->start_frame = start_frame_back;
}
/* can be NULL */
seq_load_apply(CTX_data_main(C), scene, seq, seq_load);
SEQ_relations_invalidate_cache_composite(scene, seq);
seq_add_set_name(seq, load_data);
seq_add_generic_update(scene, seqbase, seq);
MEM_freeN(anim_arr);
return seq;
@ -525,9 +708,9 @@ void SEQ_add_movie_reload_if_needed(struct Main *bmain,
bool must_reload = false;
/* The Sequence struct allows for multiple anim structs to be associated with one strip. This
* function will return true only if there is at least one 'anim' AND all anims can produce
* frames. */
/* The Sequence struct allows for multiple anim structs to be associated with one strip.
* This function will return true only if there is at least one 'anim' AND all anims can
* produce frames. */
if (BLI_listbase_is_empty(&seq->anims)) {
/* No anim present, so reloading is always necessary. */

View File

@ -164,7 +164,6 @@ void SEQ_time_update_sequence_bounds(Scene *scene, Sequence *seq)
void SEQ_time_update_sequence(Scene *scene, Sequence *seq)
{
Sequence *seqm;
int min, max;
/* check all metas recursively */
seqm = seq->seqbase.first;
@ -212,27 +211,6 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq)
}
}
else {
if (seq->type == SEQ_TYPE_META) {
seqm = seq->seqbase.first;
if (seqm) {
min = MAXFRAME * 2;
max = -MAXFRAME * 2;
while (seqm) {
if (seqm->startdisp < min) {
min = seqm->startdisp;
}
if (seqm->enddisp > max) {
max = seqm->enddisp;
}
seqm = seqm->next;
}
seq->start = min + seq->anim_startofs;
seq->len = max - min;
seq->len -= seq->anim_startofs;
seq->len -= seq->anim_endofs;
}
seq_update_sound_bounds_recursive(scene, seq);
}
SEQ_time_update_sequence_bounds(scene, seq);
}
}

View File

@ -543,17 +543,36 @@ typedef struct wmTabletData {
/**
* Each event should have full modifier state.
* event comes from event manager and from keymap.
*
*
* Previous State
* ==============
*
* Events hold information about the previous event,
* this is used for detecting click and double-click events (the timer is needed for double-click).
* See #wm_event_add_ghostevent for implementation details.
*
* Notes:
*
* - The previous values are only set for mouse button and keyboard events.
* See: #ISMOUSE_BUTTON & #ISKEYBOARD macros.
*
* - Previous x/y are exceptions: #wmEvent.prevx & #wmEvent.prevy
* these are set on mouse motion, see #MOUSEMOVE & track-pad events.
*
* - Modal key-map handling sets `prevval` & `prevtype` to `val` & `type`,
* this allows modal keys-maps to check the original values (needed in some cases).
*/
typedef struct wmEvent {
struct wmEvent *next, *prev;
/** Event code itself (short, is also in keymap). */
/** Event code itself (short, is also in key-map). */
short type;
/** Press, release, scroll-value. */
short val;
/** Mouse pointer position, screen coord. */
int x, y;
/** Region mouse position, name convention pre 2.5 :). */
/** Region relative mouse position (name convention before Blender 2.5). */
int mval[2];
/**
* From, ghost if utf8 is enabled for the platform,
@ -572,17 +591,25 @@ typedef struct wmEvent {
*/
char is_repeat;
/** Previous state, used for double click and the 'click'. */
/** The previous value of `type`. */
short prevtype;
/** The previous value of `val`. */
short prevval;
int prevx, prevy;
/** The time when the key is pressed, see #PIL_check_seconds_timer. */
double prevclicktime;
/** The location when the key is pressed (used to enforce drag thresholds). */
int prevclickx, prevclicky;
/**
* The previous value of #wmEvent.x #wmEvent.y,
* Unlike other previous state variables, this is set on any mouse motion.
* Use `prevclickx` & `prevclicky` for the value at time of pressing.
*/
int prevx, prevy;
/** Modifier states. */
/** 'oskey' is apple or windows-key, value denotes order of pressed. */
short shift, ctrl, alt, oskey;
/** rawkey modifier. */
/** Raw-key modifier (allow using any key as a modifier). */
short keymodifier;
/** Set in case a #KM_PRESS went by unhandled. */
@ -592,17 +619,19 @@ typedef struct wmEvent {
/** Tablet info, available for mouse move and button events. */
wmTabletData tablet;
/* custom data */
/* Custom data. */
/** Custom data type, stylus, 6dof, see wm_event_types.h */
short custom;
short customdatafree;
int pad2;
/** Ascii, unicode, mouse coords, angles, vectors, dragdrop info. */
/** Ascii, unicode, mouse-coords, angles, vectors, NDOF data, drag-drop info. */
void *customdata;
/* True if the operating system inverted the delta x/y values and resulting
* prev x/y values, for natural scroll direction. For absolute scroll direction,
* the delta must be negated again. */
/**
* True if the operating system inverted the delta x/y values and resulting
* `prevx`, `prevy` values, for natural scroll direction.
* For absolute scroll direction, the delta must be negated again.
*/
char is_direction_inverted;
} wmEvent;

View File

@ -280,8 +280,11 @@ int WM_event_drag_threshold(const struct wmEvent *event)
if (WM_event_is_tablet(event)) {
drag_threshold = U.drag_threshold_tablet;
}
else if (ISMOUSE(event->type)) {
/* Mouse button or mouse motion. */
else if (ISMOUSE(event->prevtype)) {
BLI_assert(event->prevtype != MOUSEMOVE);
/* Using the previous type is important is we want to check the last pressed/released button,
* The `event->type` would include #MOUSEMOVE which is always the case when dragging
* and does not help us know which threshold to use. */
drag_threshold = U.drag_threshold_mouse;
}
else {

View File

@ -149,23 +149,25 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add)
}
wmEvent *event = wm_event_add(win, event_to_add);
/* Logic for setting previous value is documented on the #wmEvent struct,
* see #wm_event_add_ghostevent for the implementation of logic this follows. */
win->eventstate->x = event->x;
win->eventstate->y = event->y;
win->eventstate->prevval = event->prevval = win->eventstate->val;
win->eventstate->prevtype = event->prevtype = win->eventstate->type;
win->eventstate->prevx = event->prevx = win->eventstate->x;
win->eventstate->prevy = event->prevy = win->eventstate->y;
if (event->type == MOUSEMOVE) {
/* Pass. */
win->eventstate->prevx = event->prevx = win->eventstate->x;
win->eventstate->prevy = event->prevy = win->eventstate->y;
}
else {
else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) {
win->eventstate->prevval = event->prevval = win->eventstate->val;
win->eventstate->prevtype = event->prevtype = win->eventstate->type;
win->eventstate->val = event->val;
win->eventstate->type = event->type;
if (ISMOUSE_BUTTON(event->type)) {
if (event->val == KM_PRESS) {
if (event->val == KM_PRESS) {
if (event->is_repeat == false) {
win->eventstate->prevclickx = event->x;
win->eventstate->prevclicky = event->y;
}
@ -4431,8 +4433,9 @@ static wmEvent *wm_event_add_trackpad(wmWindow *win, const wmEvent *event, int d
return event_new;
}
/* Windows store own event queues, no bContext here. */
/* Time is in 1000s of seconds, from Ghost. */
/**
* Windows store own event queues #wmWindow.queue (no #bContext here).
*/
void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void *customdata)
{
if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) {
@ -4455,6 +4458,35 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
event = *evt;
event.is_repeat = false;
/**
* Always support accessing the last key press/release. This is set from `win->eventstate`,
* so it will always be a valid event type to store in the previous state.
*
* Note that these values are intentionally _not_ set in the `win->eventstate`,
* as copying these values only makes sense when `win->eventstate->{val/type}` would be
* written to (which only happens for some kinds of events).
* If this was done it could leave `win->eventstate` previous and current value
* set to the same key press/release state which doesn't make sense.
*/
event.prevtype = event.type;
event.prevval = event.val;
/* Ensure the event state is correct, any deviation from this may cause bugs. */
#ifndef NDEBUG
if ((evt->type || evt->val) && /* Ignore cleared event state. */
!(ISMOUSE_BUTTON(evt->type) || ISKEYBOARD(evt->type))) {
CLOG_WARN(WM_LOG_HANDLERS,
"Non-keyboard/mouse button found in 'win->eventstate->type = %d'",
evt->type);
}
if ((evt->prevtype || evt->prevval) && /* Ignore cleared event state. */
!(ISMOUSE_BUTTON(evt->prevtype) || ISKEYBOARD(evt->prevtype))) {
CLOG_WARN(WM_LOG_HANDLERS,
"Non-keyboard/mouse button found in 'win->eventstate->prevtype = %d'",
evt->prevtype);
}
#endif
switch (type) {
/* Mouse move, also to inactive window (X11 does this). */
case GHOST_kEventCursorMove: {
@ -4479,6 +4511,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
oevent = *oevt;
/* See comment for this operation on `event` for details. */
oevent.prevtype = oevent.type;
oevent.prevval = oevent.val;
copy_v2_v2_int(&oevent.x, &event.x);
oevent.type = MOUSEMOVE;
{
@ -4574,8 +4610,12 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
if (owin) {
wmEvent oevent = *(owin->eventstate);
oevent.x = event.x;
oevent.y = event.y;
/* See comment for this operation on `event` for details. */
oevent.prevtype = oevent.type;
oevent.prevval = oevent.val;
copy_v2_v2_int(&oevent.x, &event.x);
oevent.type = event.type;
oevent.val = event.val;
oevent.tablet = event.tablet;
@ -4729,7 +4769,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
/* Double click test - only for press. */
if (event.val == KM_PRESS) {
/* Don't reset timer & location when holding the key generates repeat events. */
if ((evt->prevtype != event.type) || (evt->prevval != KM_PRESS)) {
if (event.is_repeat == false) {
wm_event_prev_click_set(&event, evt);
}
}