Merge branch 'master' into sculpt-dev
This commit is contained in:
commit
f2024f7d99
|
@ -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
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]) &&
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue