|
|
|
|
@@ -99,6 +99,7 @@
|
|
|
|
|
#include "DNA_volume_types.h"
|
|
|
|
|
|
|
|
|
|
#include "BLI_listbase_iterator.hh"
|
|
|
|
|
#include "BLI_listbase_wrapper.hh"
|
|
|
|
|
#include "BLI_listbase.h"
|
|
|
|
|
#include "BLI_color.hh"
|
|
|
|
|
#include "BLI_math_matrix.h"
|
|
|
|
|
@@ -107,6 +108,28 @@
|
|
|
|
|
#include "BLI_string.h"
|
|
|
|
|
#include "BLI_string_utf8.h"
|
|
|
|
|
|
|
|
|
|
#ifdef LISTBASE_FOREACH
|
|
|
|
|
# undef LISTBASE_FOREACH
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef LISTBASE_FOREACH_MUTABLE
|
|
|
|
|
# undef LISTBASE_FOREACH_MUTABLE
|
|
|
|
|
#endif
|
|
|
|
|
template<typename T> struct WebListWrapper {
|
|
|
|
|
blender::ListBase *list;
|
|
|
|
|
struct Iterator {
|
|
|
|
|
T *item;
|
|
|
|
|
Iterator &operator++() { item = reinterpret_cast<T *>(reinterpret_cast<blender::Link *>(item)->next); return *this; }
|
|
|
|
|
bool operator!=(const Iterator &other) const { return item != other.item; }
|
|
|
|
|
T *operator*() const { return item; }
|
|
|
|
|
};
|
|
|
|
|
explicit WebListWrapper(blender::ListBase *value) : list(value) {}
|
|
|
|
|
Iterator begin() const { return {reinterpret_cast<T *>(list->first)}; }
|
|
|
|
|
Iterator end() const { return {nullptr}; }
|
|
|
|
|
};
|
|
|
|
|
#define LISTBASE_FOREACH(type, var, list) \
|
|
|
|
|
for (type var : WebListWrapper<std::remove_pointer_t<type>>(list))
|
|
|
|
|
#define LISTBASE_FOREACH_MUTABLE(type, var, list) LISTBASE_FOREACH(type, var, list)
|
|
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
|
|
#include "bmesh.hh"
|
|
|
|
|
@@ -258,14 +281,14 @@ bool web_operator_number3(const json &value, std::vector<float> &output)
|
|
|
|
|
bool web_operator_object_exists(WebBlendMainState *state, const std::string &object_id)
|
|
|
|
|
{
|
|
|
|
|
if (state == nullptr || state->main == nullptr || object_id.empty()) return false;
|
|
|
|
|
LISTBASE_FOREACH (Object *, object, &state->main->objects) {
|
|
|
|
|
for (Object *object : WebListWrapper<Object>(&state->main->objects)) {
|
|
|
|
|
const std::string name = id_name(object->id);
|
|
|
|
|
if (object_id == name || object_id == "object:" + name) return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool web_engine_blend_main_list_operators(std::string &catalog_json, std::string &error)
|
|
|
|
|
bool web_engine_blend_main_list_operators_impl(std::string &catalog_json, std::string &error)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
catalog_json = web_operator_catalog_json().dump();
|
|
|
|
|
@@ -539,7 +562,7 @@ json web_macro_source(const WebMacroSpec &spec)
|
|
|
|
|
{"evidence", "bounded WebEngine macro plan source"}}});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool web_engine_blend_main_list_macros(std::string &catalog_json, std::string &error)
|
|
|
|
|
bool web_engine_blend_main_list_macros_impl(std::string &catalog_json, std::string &error)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
const json operator_catalog = web_operator_catalog_json();
|
|
|
|
|
@@ -577,7 +600,7 @@ bool web_engine_blend_main_list_macros(std::string &catalog_json, std::string &e
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool web_engine_blend_main_prepare_macro(WebBlendMainState *state,
|
|
|
|
|
bool web_engine_blend_main_prepare_macro_impl(WebBlendMainState *state,
|
|
|
|
|
const char *macro_id,
|
|
|
|
|
const char *properties_json,
|
|
|
|
|
std::string &plan_json,
|
|
|
|
|
@@ -1611,6 +1634,25 @@ json geometry_socket_json(const bNodeSocket &socket, const eNodeSocketInOut dire
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
bool web_engine_blend_main_list_operators(std::string &catalog_json, std::string &error)
|
|
|
|
|
{
|
|
|
|
|
return web_engine_blend_main_list_operators_impl(catalog_json, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool web_engine_blend_main_list_macros(std::string &catalog_json, std::string &error)
|
|
|
|
|
{
|
|
|
|
|
return web_engine_blend_main_list_macros_impl(catalog_json, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool web_engine_blend_main_prepare_macro(WebBlendMainState *state,
|
|
|
|
|
const char *macro_id,
|
|
|
|
|
const char *properties_json,
|
|
|
|
|
std::string &plan_json,
|
|
|
|
|
std::string &error)
|
|
|
|
|
{
|
|
|
|
|
return web_engine_blend_main_prepare_macro_impl(state, macro_id, properties_json, plan_json, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool web_engine_blend_main_prepare_operator(WebBlendMainState *state,
|
|
|
|
|
const char *operator_id,
|
|
|
|
|
const char *properties_json,
|
|
|
|
|
@@ -1824,7 +1866,14 @@ bool web_engine_blend_main_add_modifier(WebBlendMainState *state,
|
|
|
|
|
error = "meshId or modifierType was not found in editable authoritative Main";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const eModifierType type = BKE_modifiertype_from_idname(modifier_type);
|
|
|
|
|
ModifierType type = eModifierType_None;
|
|
|
|
|
for (int candidate = eModifierType_None + 1; candidate < NUM_MODIFIER_TYPES; candidate++) {
|
|
|
|
|
const ModifierTypeInfo *info = BKE_modifier_get_info(ModifierType(candidate));
|
|
|
|
|
if (info != nullptr && STREQ(info->idname, modifier_type)) {
|
|
|
|
|
type = ModifierType(candidate);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (type == eModifierType_None) {
|
|
|
|
|
error = "modifierType is not supported by Blender metadata";
|
|
|
|
|
return false;
|
|
|
|
|
@@ -1836,7 +1885,7 @@ bool web_engine_blend_main_add_modifier(WebBlendMainState *state,
|
|
|
|
|
}
|
|
|
|
|
if (name != nullptr && name[0] != '\0') STRNCPY_UTF8(modifier->name, name);
|
|
|
|
|
BKE_modifier_unique_name(&object->modifiers, modifier);
|
|
|
|
|
BKE_modifiers_persistent_uid_init(*object, modifier);
|
|
|
|
|
BKE_modifiers_persistent_uid_init(*object, *modifier);
|
|
|
|
|
BLI_addtail(&object->modifiers, modifier);
|
|
|
|
|
object->id.recalc |= ID_RECALC_GEOMETRY;
|
|
|
|
|
modifier_uuid = std::string(object->id.name + 2) + ":" + std::to_string(modifier->persistent_uid);
|
|
|
|
|
@@ -1930,10 +1979,10 @@ bool web_engine_blend_main_set_modifier_parameters(WebBlendMainState *state,
|
|
|
|
|
for (ModifierData &modifier : object->modifiers) {
|
|
|
|
|
if (modifier.persistent_uid != uid) continue;
|
|
|
|
|
PointerRNA pointer;
|
|
|
|
|
RNA_pointer_create(&object->id, &RNA_Modifier, &modifier, &pointer);
|
|
|
|
|
pointer = RNA_pointer_create_id_subdata(object->id, RNA_Modifier, &modifier);
|
|
|
|
|
for (auto it = parameters.begin(); it != parameters.end(); ++it) {
|
|
|
|
|
PropertyRNA *property = RNA_struct_find_property(&pointer, it.key().c_str());
|
|
|
|
|
if (property == nullptr || RNA_property_is_readonly(&pointer, property)) {
|
|
|
|
|
if (property == nullptr || !RNA_property_editable(&pointer, property)) {
|
|
|
|
|
error = "modifier parameter is not editable in Blender metadata: " + it.key();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -1959,7 +2008,7 @@ bool web_engine_blend_main_set_modifier_parameters(WebBlendMainState *state,
|
|
|
|
|
int enum_value = 0;
|
|
|
|
|
if (value.is_number_integer()) enum_value = value.get<int>();
|
|
|
|
|
else if (value.is_string()) {
|
|
|
|
|
if (!RNA_property_enum_value_from_identifier(&pointer, property, value.get<std::string>().c_str(), &enum_value)) {
|
|
|
|
|
if (!RNA_property_enum_value(nullptr, &pointer, property, value.get<std::string>().c_str(), &enum_value)) {
|
|
|
|
|
error = "modifier enum parameter has an unknown value: " + it.key();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -2000,10 +2049,11 @@ static ID *web_rna_find_id(WebBlendMainState *state, const char *type_filter, co
|
|
|
|
|
{
|
|
|
|
|
if (state == nullptr || state->main == nullptr || name == nullptr || name[0] == '\0') return nullptr;
|
|
|
|
|
ID *found = nullptr;
|
|
|
|
|
ID *id = nullptr;
|
|
|
|
|
FOREACH_MAIN_ID_BEGIN (state->main, id) {
|
|
|
|
|
if (id->name[0] == '\0' || !STREQ(id->name + 2, name)) continue;
|
|
|
|
|
PointerRNA pointer;
|
|
|
|
|
RNA_id_pointer_create(id, &pointer);
|
|
|
|
|
pointer = RNA_id_pointer_create(id);
|
|
|
|
|
const char *identifier = RNA_struct_identifier(pointer.type);
|
|
|
|
|
if (type_filter != nullptr && type_filter[0] != '\0' &&
|
|
|
|
|
(identifier == nullptr || !STREQ(identifier, type_filter))) continue;
|
|
|
|
|
@@ -2044,7 +2094,7 @@ static bool web_rna_read_value(PointerRNA *pointer, PropertyRNA *property, json
|
|
|
|
|
|
|
|
|
|
static bool web_rna_set_value(PointerRNA *pointer, PropertyRNA *property, const json &value, std::string &error)
|
|
|
|
|
{
|
|
|
|
|
if (web_rna_property_blocked(property) || RNA_property_is_readonly(pointer, property)) {
|
|
|
|
|
if (web_rna_property_blocked(property) || !RNA_property_editable(pointer, property)) {
|
|
|
|
|
error = "RNA property is readonly, context-only, or contains an unsupported pointer";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -2072,7 +2122,7 @@ static bool web_rna_set_value(PointerRNA *pointer, PropertyRNA *property, const
|
|
|
|
|
int enum_value = 0;
|
|
|
|
|
if (item.is_number_integer()) enum_value = item.get<int>();
|
|
|
|
|
else if (item.is_string()) {
|
|
|
|
|
if (!RNA_property_enum_value_from_identifier(pointer, property, item.get<std::string>().c_str(), &enum_value)) return false;
|
|
|
|
|
if (!RNA_property_enum_value(nullptr, pointer, property, item.get<std::string>().c_str(), &enum_value)) return false;
|
|
|
|
|
}
|
|
|
|
|
else return false;
|
|
|
|
|
RNA_property_enum_set(pointer, property, enum_value);
|
|
|
|
|
@@ -2105,9 +2155,10 @@ bool web_engine_blend_main_list_rna_data_blocks(WebBlendMainState *state,
|
|
|
|
|
if (state == nullptr || state->main == nullptr) { error = "RNA reflection requires an authoritative Main"; return false; }
|
|
|
|
|
json data_blocks = json::array();
|
|
|
|
|
int count = 0;
|
|
|
|
|
ID *id = nullptr;
|
|
|
|
|
FOREACH_MAIN_ID_BEGIN (state->main, id) {
|
|
|
|
|
PointerRNA pointer;
|
|
|
|
|
RNA_id_pointer_create(id, &pointer);
|
|
|
|
|
pointer = RNA_id_pointer_create(id);
|
|
|
|
|
const char *identifier = RNA_struct_identifier(pointer.type);
|
|
|
|
|
if (identifier == nullptr || (type_filter != nullptr && type_filter[0] != '\0' && !STREQ(identifier, type_filter))) continue;
|
|
|
|
|
const bool editable = id->lib == nullptr;
|
|
|
|
|
@@ -2130,9 +2181,9 @@ bool web_engine_blend_main_read_rna_properties(WebBlendMainState *state,
|
|
|
|
|
ID *id = web_rna_find_id(state, type_filter, name);
|
|
|
|
|
if (id == nullptr) { error = "RNA data-block was not found in authoritative Main"; return false; }
|
|
|
|
|
PointerRNA pointer;
|
|
|
|
|
RNA_id_pointer_create(id, &pointer);
|
|
|
|
|
pointer = RNA_id_pointer_create(id);
|
|
|
|
|
json properties = json::array();
|
|
|
|
|
PropertyRNA *iterator_property = RNA_struct_iterator_property(&pointer);
|
|
|
|
|
PropertyRNA *iterator_property = RNA_struct_iterator_property(pointer.type);
|
|
|
|
|
CollectionPropertyIterator iterator;
|
|
|
|
|
RNA_property_collection_begin(&pointer, iterator_property, &iterator);
|
|
|
|
|
for (; iterator.valid && static_cast<int>(properties.size()) < std::clamp(limit, 1, 1024); RNA_property_collection_next(&iterator)) {
|
|
|
|
|
@@ -2142,7 +2193,7 @@ bool web_engine_blend_main_read_rna_properties(WebBlendMainState *state,
|
|
|
|
|
{"name", RNA_property_ui_name(property)},
|
|
|
|
|
{"type", RNA_property_type(property) == PROP_BOOLEAN ? "BOOLEAN" : RNA_property_type(property) == PROP_INT ? "INT" : RNA_property_type(property) == PROP_FLOAT ? "FLOAT" : RNA_property_type(property) == PROP_STRING ? "STRING" : "ENUM"},
|
|
|
|
|
{"arrayLength", std::max(1, RNA_property_array_length(&pointer, property))},
|
|
|
|
|
{"readonly", RNA_property_is_readonly(&pointer, property)}};
|
|
|
|
|
{"readonly", !RNA_property_editable(&pointer, property)}};
|
|
|
|
|
const char *description = RNA_property_ui_description(property);
|
|
|
|
|
if (description != nullptr && description[0] != '\0') entry["description"] = description;
|
|
|
|
|
json value;
|
|
|
|
|
@@ -2164,7 +2215,7 @@ bool web_engine_blend_main_set_rna_property(WebBlendMainState *state,
|
|
|
|
|
ID *id = web_rna_find_id(state, type_filter, name);
|
|
|
|
|
if (id == nullptr || id->lib != nullptr || property_name == nullptr || value_json == nullptr) { error = "RNA data-block is missing or linked and cannot be edited"; return false; }
|
|
|
|
|
PointerRNA pointer;
|
|
|
|
|
RNA_id_pointer_create(id, &pointer);
|
|
|
|
|
pointer = RNA_id_pointer_create(id);
|
|
|
|
|
PropertyRNA *property = RNA_struct_find_property(&pointer, property_name);
|
|
|
|
|
if (property == nullptr) { error = "RNA property was not found in Blender metadata"; return false; }
|
|
|
|
|
json value;
|
|
|
|
|
@@ -2186,7 +2237,7 @@ bool web_engine_blend_main_read_rna_property_schema(WebBlendMainState *state,
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
PointerRNA pointer;
|
|
|
|
|
RNA_id_pointer_create(id, &pointer);
|
|
|
|
|
pointer = RNA_id_pointer_create(id);
|
|
|
|
|
PropertyRNA *property = RNA_struct_find_property(&pointer, property_name);
|
|
|
|
|
if (property == nullptr || (web_rna_property_blocked(property) && RNA_property_type(property) != PROP_COLLECTION)) {
|
|
|
|
|
error = "RNA property is unsupported, context-only, or contains a pointer";
|
|
|
|
|
@@ -2207,7 +2258,7 @@ bool web_engine_blend_main_read_rna_property_schema(WebBlendMainState *state,
|
|
|
|
|
{"name", RNA_property_ui_name(property)},
|
|
|
|
|
{"type", type_name},
|
|
|
|
|
{"arrayLength", std::max(1, RNA_property_array_length(&pointer, property))},
|
|
|
|
|
{"readonly", RNA_property_is_readonly(&pointer, property)}};
|
|
|
|
|
{"readonly", !RNA_property_editable(&pointer, property)}};
|
|
|
|
|
const char *description = RNA_property_ui_description(property);
|
|
|
|
|
if (description != nullptr && description[0] != '\0') schema["description"] = description;
|
|
|
|
|
if (type == PROP_COLLECTION) schema["collection"] = json{{"nestedPath", property_name}};
|
|
|
|
|
@@ -2228,7 +2279,7 @@ bool web_engine_blend_main_set_rna_array_property(WebBlendMainState *state,
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
PointerRNA pointer;
|
|
|
|
|
RNA_id_pointer_create(id, &pointer);
|
|
|
|
|
pointer = RNA_id_pointer_create(id);
|
|
|
|
|
PropertyRNA *property = RNA_struct_find_property(&pointer, property_name);
|
|
|
|
|
if (property == nullptr || RNA_property_array_length(&pointer, property) <= 1) {
|
|
|
|
|
error = "RNA property is not a bounded array";
|
|
|
|
|
@@ -2262,7 +2313,7 @@ bool web_engine_blend_main_set_rna_enum_property(WebBlendMainState *state,
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
PointerRNA pointer;
|
|
|
|
|
RNA_id_pointer_create(id, &pointer);
|
|
|
|
|
pointer = RNA_id_pointer_create(id);
|
|
|
|
|
PropertyRNA *property = RNA_struct_find_property(&pointer, property_name);
|
|
|
|
|
if (property == nullptr || RNA_property_type(property) != PROP_ENUM) {
|
|
|
|
|
error = "RNA property is not an enum";
|
|
|
|
|
@@ -2349,7 +2400,7 @@ bool web_engine_blend_main_read_rna_collection(WebBlendMainState *state,
|
|
|
|
|
start = end + 1;
|
|
|
|
|
}
|
|
|
|
|
PointerRNA pointer;
|
|
|
|
|
RNA_id_pointer_create(id, &pointer);
|
|
|
|
|
pointer = RNA_id_pointer_create(id);
|
|
|
|
|
json items;
|
|
|
|
|
if (!web_rna_read_collection_path(&pointer, segments, 0, limit, items, error)) return false;
|
|
|
|
|
result_json = json{{"path", path_value}, {"items", items}}.dump();
|
|
|
|
|
@@ -3113,7 +3164,7 @@ bool web_engine_blend_main_sculpt_stroke(WebBlendMainState *state,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MutableSpan<float3> positions = mesh->vert_positions_for_write();
|
|
|
|
|
const std::optional<VArraySpan<float>> mask = mesh->attributes().lookup<float>(".sculpt_mask", bke::AttrDomain::Point);
|
|
|
|
|
const VArray<float> mask = mesh->attributes().lookup<float>(".sculpt_mask", bke::AttrDomain::Point).varray;
|
|
|
|
|
std::vector<std::vector<int>> neighbors;
|
|
|
|
|
if (brush == "SMOOTH") {
|
|
|
|
|
neighbors.resize(size_t(mesh->verts_num));
|
|
|
|
|
@@ -3140,7 +3191,7 @@ bool web_engine_blend_main_sculpt_stroke(WebBlendMainState *state,
|
|
|
|
|
const float distance = std::sqrt(dx * dx + dy * dy + dz * dz);
|
|
|
|
|
if (distance >= sample.radius) continue;
|
|
|
|
|
const float falloff = (1.0f - distance / sample.radius);
|
|
|
|
|
const float mask_factor = !mask ? 1.0f : std::max(0.0f, 1.0f - (*mask)[vertex]);
|
|
|
|
|
const float mask_factor = !mask ? 1.0f : std::max(0.0f, 1.0f - mask[vertex]);
|
|
|
|
|
const float influence = sample.strength * sample.pressure * falloff * falloff * mask_factor;
|
|
|
|
|
if (influence == 0.0f) continue;
|
|
|
|
|
if (brush == "SMOOTH") {
|
|
|
|
|
@@ -3230,7 +3281,7 @@ bool web_engine_blend_main_create_primitive(WebBlendMainState *state,
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Mesh *mesh = static_cast<Mesh *>(object->data);
|
|
|
|
|
Mesh *mesh = reinterpret_cast<Mesh *>(object->data);
|
|
|
|
|
BMeshCreateParams create_params{};
|
|
|
|
|
BMesh *bm = BM_mesh_create(&bm_mesh_allocsize_default, &create_params);
|
|
|
|
|
float matrix[4][4];
|
|
|
|
|
@@ -4072,10 +4123,10 @@ bool web_engine_blend_main_remove_action(WebBlendMainState *state,
|
|
|
|
|
error = "removeAction requires an editable Action";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (Object *object : state->main->objects) {
|
|
|
|
|
if (object->adt != nullptr && object->adt->action == action) {
|
|
|
|
|
animrig::unassign_action(object->id);
|
|
|
|
|
object->id.recalc |= ID_RECALC_ANIMATION | ID_RECALC_TRANSFORM;
|
|
|
|
|
for (Object &object : state->main->objects) {
|
|
|
|
|
if (object.adt != nullptr && object.adt->action == action) {
|
|
|
|
|
animrig::unassign_action(object.id);
|
|
|
|
|
object.id.recalc |= ID_RECALC_ANIMATION | ID_RECALC_TRANSFORM;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BKE_id_delete(state->main, &action->id);
|
|
|
|
|
@@ -4279,7 +4330,7 @@ bool web_engine_blend_main_set_driver_expression(WebBlendMainState *state,
|
|
|
|
|
error = "setDriverExpression requires an editable FCurve and bounded expression";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (fcurve->driver == nullptr) fcurve->driver = static_cast<ChannelDriver *>(MEM_callocN(sizeof(ChannelDriver), "Web FCurve driver"));
|
|
|
|
|
if (fcurve->driver == nullptr) fcurve->driver = MEM_new<ChannelDriver>("Web FCurve driver");
|
|
|
|
|
if (fcurve->driver == nullptr) { error = "Blender could not allocate an FCurve driver"; return false; }
|
|
|
|
|
BLI_strncpy(fcurve->driver->expression, expression, sizeof(fcurve->driver->expression));
|
|
|
|
|
fcurve->driver->type = DRIVER_TYPE_PYTHON;
|
|
|
|
|
@@ -4304,14 +4355,17 @@ bool web_engine_blend_main_set_action_groups(WebBlendMainState *state,
|
|
|
|
|
const std::string name = entry["name"].get<std::string>();
|
|
|
|
|
if (name.empty() || name.size() > 63 || entry["paths"].size() > 4096) { error = "setActionGroups group is invalid"; return false; }
|
|
|
|
|
bActionGroup *group = static_cast<bActionGroup *>(BLI_findstring(&action->groups, name.c_str(), offsetof(bActionGroup, name)));
|
|
|
|
|
if (group == nullptr) group = BKE_action_group_add(action, name.c_str());
|
|
|
|
|
if (group == nullptr) {
|
|
|
|
|
group = MEM_new<bActionGroup>("Web Action group");
|
|
|
|
|
STRNCPY(group->name, name.c_str());
|
|
|
|
|
BLI_addtail(&action->groups, group);
|
|
|
|
|
}
|
|
|
|
|
if (group == nullptr) { error = "Blender could not allocate an Action group"; return false; }
|
|
|
|
|
for (const json &path_entry : entry["paths"]) {
|
|
|
|
|
if (!path_entry.is_string()) { error = "setActionGroups path is invalid"; return false; }
|
|
|
|
|
FCurve *fcurve = web_find_action_fcurve(action, path_entry.get<std::string>().c_str());
|
|
|
|
|
if (fcurve == nullptr) { error = "Action group FCurve channel was not found"; return false; }
|
|
|
|
|
BKE_action_groups_remove_channel(action, fcurve);
|
|
|
|
|
BKE_action_groups_add_channel(action, group, fcurve);
|
|
|
|
|
fcurve->grp = group;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
action->id.recalc |= ID_RECALC_ANIMATION;
|
|
|
|
|
@@ -4340,20 +4394,20 @@ bool web_engine_blend_main_set_keying_set(WebBlendMainState *state,
|
|
|
|
|
keying_set = candidate;
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (KS_Path *, path, &candidate->paths) {
|
|
|
|
|
BLI_remlink(&candidate->paths, path);
|
|
|
|
|
MEM_SAFE_FREE(path->rna_path);
|
|
|
|
|
MEM_freeN(path);
|
|
|
|
|
if (path->rna_path != nullptr) MEM_delete_void(static_cast<void *>(path->rna_path));
|
|
|
|
|
MEM_delete(path);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (keying_set == nullptr) {
|
|
|
|
|
keying_set = static_cast<KeyingSet *>(MEM_callocN(sizeof(KeyingSet), "Web Keying Set"));
|
|
|
|
|
keying_set = MEM_new<KeyingSet>("Web Keying Set");
|
|
|
|
|
BLI_strncpy(keying_set->name, name, sizeof(keying_set->name));
|
|
|
|
|
BLI_strncpy(keying_set->idname, name, sizeof(keying_set->idname));
|
|
|
|
|
BLI_addtail(&scene->keyingsets, keying_set);
|
|
|
|
|
}
|
|
|
|
|
for (const json &entry : paths) {
|
|
|
|
|
if (!entry.is_object() || !entry.contains("path") || !entry["path"].is_string()) { error = "setKeyingSet path is invalid"; return false; }
|
|
|
|
|
KS_Path *path = static_cast<KS_Path *>(MEM_callocN(sizeof(KS_Path), "Web Keying Set Path"));
|
|
|
|
|
KS_Path *path = MEM_new<KS_Path>("Web Keying Set Path");
|
|
|
|
|
path->rna_path = BLI_strdup(entry["path"].get<std::string>().c_str());
|
|
|
|
|
path->array_index = entry.value("arrayIndex", -1);
|
|
|
|
|
path->id = &scene->id;
|
|
|
|
|
@@ -4387,19 +4441,19 @@ bool web_engine_blend_main_set_timeline_markers(WebBlendMainState *state,
|
|
|
|
|
if (!markers.is_array() || markers.size() > 10000) { error = "setTimelineMarkers marker array is invalid"; return false; }
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (TimeMarker *, marker, &scene->markers) {
|
|
|
|
|
BLI_remlink(&scene->markers, marker);
|
|
|
|
|
MEM_freeN(marker);
|
|
|
|
|
MEM_delete(marker);
|
|
|
|
|
}
|
|
|
|
|
for (const json &entry : markers) {
|
|
|
|
|
if (!entry.is_object() || !entry.contains("frame") || !entry["frame"].is_number_integer() || !entry.contains("name") || !entry["name"].is_string()) {
|
|
|
|
|
error = "setTimelineMarkers marker entry is invalid";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
TimeMarker *marker = static_cast<TimeMarker *>(MEM_callocN(sizeof(TimeMarker), "Web timeline marker"));
|
|
|
|
|
TimeMarker *marker = MEM_new<TimeMarker>("Web timeline marker");
|
|
|
|
|
marker->frame = entry["frame"].get<int>();
|
|
|
|
|
BLI_strncpy(marker->name, entry["name"].get<std::string>().c_str(), sizeof(marker->name));
|
|
|
|
|
BLI_addtail(&scene->markers, marker);
|
|
|
|
|
}
|
|
|
|
|
scene->id.recalc |= ID_RECALC_COPY_ON_WRITE;
|
|
|
|
|
scene->id.recalc |= ID_RECALC_SYNC_TO_EVAL;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -5911,8 +5965,8 @@ bool web_engine_blend_main_set_curve_bevel(WebBlendMainState *state,
|
|
|
|
|
}
|
|
|
|
|
if (bevel_depth >= 0.0f) curve->bevel_radius = bevel_depth;
|
|
|
|
|
if (bevel_resolution >= 0) curve->bevresol = short(bevel_resolution);
|
|
|
|
|
if (bevel_factor_start >= 0.0f) curve->bevel_factor_start = bevel_factor_start;
|
|
|
|
|
if (bevel_factor_end >= 0.0f) curve->bevel_factor_end = bevel_factor_end;
|
|
|
|
|
if (bevel_factor_start >= 0.0f) curve->bevfac1 = bevel_factor_start;
|
|
|
|
|
if (bevel_factor_end >= 0.0f) curve->bevfac2 = bevel_factor_end;
|
|
|
|
|
curve->id.recalc |= ID_RECALC_GEOMETRY;
|
|
|
|
|
for (Object &object : state->main->objects) {
|
|
|
|
|
if (object.data == &curve->id) object.id.recalc |= ID_RECALC_GEOMETRY;
|
|
|
|
|
@@ -5949,7 +6003,7 @@ bool web_engine_blend_main_set_curve_taper(WebBlendMainState *state,
|
|
|
|
|
curve->taperobj = taper_object;
|
|
|
|
|
if (curve->taperobj != nullptr) id_us_plus(&curve->taperobj->id);
|
|
|
|
|
}
|
|
|
|
|
if (taper_radius_mode >= 0) curve->taper_radius_mode = short(taper_radius_mode);
|
|
|
|
|
if (taper_radius_mode >= 0) curve->taper_radius_mode = eCurveTaperRadiusMode(taper_radius_mode);
|
|
|
|
|
curve->id.recalc |= ID_RECALC_GEOMETRY;
|
|
|
|
|
for (Object &object : state->main->objects) {
|
|
|
|
|
if (object.data == &curve->id) object.id.recalc |= ID_RECALC_GEOMETRY;
|
|
|
|
|
@@ -5967,7 +6021,7 @@ bool web_engine_blend_main_convert_curve_type(WebBlendMainState *state,
|
|
|
|
|
if (error.empty()) error = "NON_MESH_DATA_UNSUPPORTED: Curve data-block was not found";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
short object_type = 0;
|
|
|
|
|
ObjectType object_type = OB_EMPTY;
|
|
|
|
|
if (STREQ(target_type, "CURVE")) object_type = OB_CURVES_LEGACY;
|
|
|
|
|
else if (STREQ(target_type, "SURFACE")) object_type = OB_SURF;
|
|
|
|
|
else {
|
|
|
|
|
@@ -6541,7 +6595,7 @@ static void web_armature_tag_update(WebBlendMainState *state, bArmature *armatur
|
|
|
|
|
BKE_armature_where_is(armature);
|
|
|
|
|
armature->id.recalc |= ID_RECALC_GEOMETRY;
|
|
|
|
|
for (Object &candidate : state->main->objects) {
|
|
|
|
|
if (candidate.type == OB_ARMATURE && candidate.data == armature) {
|
|
|
|
|
if (candidate.type == OB_ARMATURE && candidate.data == &armature->id) {
|
|
|
|
|
BKE_pose_rebuild(state->main, &candidate, armature, true);
|
|
|
|
|
candidate.id.recalc |= ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM;
|
|
|
|
|
}
|
|
|
|
|
@@ -6556,7 +6610,7 @@ static Bone *web_armature_add_bone(bArmature *armature,
|
|
|
|
|
float roll,
|
|
|
|
|
bool connected)
|
|
|
|
|
{
|
|
|
|
|
Bone *bone = static_cast<Bone *>(MEM_callocN(sizeof(Bone), "WebEngine armature edit bone"));
|
|
|
|
|
Bone *bone = MEM_new<Bone>("WebEngine armature edit bone");
|
|
|
|
|
BLI_strncpy(bone->name, name, sizeof(bone->name));
|
|
|
|
|
bone->parent = parent;
|
|
|
|
|
bone->roll = roll;
|
|
|
|
|
@@ -6631,13 +6685,11 @@ bool web_engine_blend_main_set_armature_bones(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bArmature *armature = static_cast<bArmature *>(object->data);
|
|
|
|
|
while (Bone *bone = static_cast<Bone *>(armature->bonebase.first)) {
|
|
|
|
|
BKE_armature_bone_free(armature, bone);
|
|
|
|
|
}
|
|
|
|
|
bArmature *armature = reinterpret_cast<bArmature *>(object->data);
|
|
|
|
|
BKE_armature_bonelist_free(&armature->bonebase, false);
|
|
|
|
|
std::vector<Bone *> created(bones.size(), nullptr);
|
|
|
|
|
for (size_t index = 0; index < bones.size(); index++) {
|
|
|
|
|
Bone *bone = static_cast<Bone *>(MEM_callocN(sizeof(Bone), "WebEngine armature bone"));
|
|
|
|
|
Bone *bone = MEM_new<Bone>("WebEngine armature bone");
|
|
|
|
|
BLI_strncpy(bone->name, bones[index].name.c_str(), sizeof(bone->name));
|
|
|
|
|
bone->roll = bones[index].roll;
|
|
|
|
|
bone->weight = 1.0f;
|
|
|
|
|
@@ -6663,7 +6715,7 @@ bool web_engine_blend_main_set_armature_bones(
|
|
|
|
|
BKE_armature_where_is(armature);
|
|
|
|
|
armature->id.recalc |= ID_RECALC_GEOMETRY;
|
|
|
|
|
for (Object &candidate : state->main->objects) {
|
|
|
|
|
if (candidate.type == OB_ARMATURE && candidate.data == armature) {
|
|
|
|
|
if (candidate.type == OB_ARMATURE && candidate.data == &armature->id) {
|
|
|
|
|
BKE_pose_rebuild(state->main, &candidate, armature, true);
|
|
|
|
|
candidate.id.recalc |= ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM;
|
|
|
|
|
}
|
|
|
|
|
@@ -6693,7 +6745,7 @@ bool web_engine_blend_main_set_pose_bone_transform(WebBlendMainState *state,
|
|
|
|
|
for (int axis = 0; axis < 3; axis++) {
|
|
|
|
|
channel->loc[axis] = translation[axis];
|
|
|
|
|
channel->eul[axis] = rotation_euler[axis];
|
|
|
|
|
channel->size[axis] = scale[axis];
|
|
|
|
|
channel->scale[axis] = scale[axis];
|
|
|
|
|
}
|
|
|
|
|
object->id.recalc |= ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM;
|
|
|
|
|
return true;
|
|
|
|
|
@@ -6712,7 +6764,7 @@ bool web_engine_blend_main_armature_edit_operations(
|
|
|
|
|
error = "ARMATURE_EDIT_INVALID: armature and a bounded operation batch are required";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
bArmature *armature = static_cast<bArmature *>(object->data);
|
|
|
|
|
bArmature *armature = reinterpret_cast<bArmature *>(object->data);
|
|
|
|
|
BKE_armature_where_is(armature);
|
|
|
|
|
auto finite_vec = [](const std::array<float, 3> &value) {
|
|
|
|
|
return std::all_of(value.begin(), value.end(), [](float component) { return std::isfinite(component); });
|
|
|
|
|
@@ -6740,7 +6792,9 @@ bool web_engine_blend_main_armature_edit_operations(
|
|
|
|
|
error = "ARMATURE_EDIT_INVALID: DELETE bone was not found";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
BKE_armature_bone_free(armature, bone);
|
|
|
|
|
BKE_armature_bonelist_free(&bone->childbase, false);
|
|
|
|
|
BLI_remlink(bone->parent != nullptr ? &bone->parent->childbase : &armature->bonebase, bone);
|
|
|
|
|
MEM_delete(bone);
|
|
|
|
|
}
|
|
|
|
|
else if (operation.operation == "RENAME") {
|
|
|
|
|
if (bone == nullptr || !valid_name(operation.name) ||
|
|
|
|
|
@@ -6794,7 +6848,7 @@ bool web_engine_blend_main_set_bone_roll(WebBlendMainState *state,
|
|
|
|
|
{
|
|
|
|
|
Object *object = find_object(state != nullptr ? state->main : nullptr, object_id);
|
|
|
|
|
bArmature *armature = object != nullptr && object->type == OB_ARMATURE ?
|
|
|
|
|
static_cast<bArmature *>(object->data) : nullptr;
|
|
|
|
|
reinterpret_cast<bArmature *>(object->data) : nullptr;
|
|
|
|
|
Bone *bone = armature != nullptr ? web_armature_find_bone(&armature->bonebase, bone_name) : nullptr;
|
|
|
|
|
if (bone == nullptr || !std::isfinite(roll)) {
|
|
|
|
|
error = "ARMATURE_ROLL_INVALID: armature bone was not found or roll is not finite";
|
|
|
|
|
@@ -6812,7 +6866,7 @@ bool web_engine_blend_main_set_bone_symmetry(WebBlendMainState *state,
|
|
|
|
|
{
|
|
|
|
|
Object *object = find_object(state != nullptr ? state->main : nullptr, object_id);
|
|
|
|
|
bArmature *armature = object != nullptr && object->type == OB_ARMATURE ?
|
|
|
|
|
static_cast<bArmature *>(object->data) : nullptr;
|
|
|
|
|
reinterpret_cast<bArmature *>(object->data) : nullptr;
|
|
|
|
|
if (armature == nullptr) {
|
|
|
|
|
error = "ARMATURE_NOT_FOUND: object must identify an armature object";
|
|
|
|
|
return false;
|
|
|
|
|
@@ -6831,7 +6885,7 @@ bool web_engine_blend_main_set_bone_parenting(WebBlendMainState *state,
|
|
|
|
|
{
|
|
|
|
|
Object *object = find_object(state != nullptr ? state->main : nullptr, object_id);
|
|
|
|
|
bArmature *armature = object != nullptr && object->type == OB_ARMATURE ?
|
|
|
|
|
static_cast<bArmature *>(object->data) : nullptr;
|
|
|
|
|
reinterpret_cast<bArmature *>(object->data) : nullptr;
|
|
|
|
|
Bone *bone = armature != nullptr ? web_armature_find_bone(&armature->bonebase, bone_name) : nullptr;
|
|
|
|
|
Bone *parent = armature != nullptr && parent_name != nullptr && parent_name[0] != '\0' ?
|
|
|
|
|
web_armature_find_bone(&armature->bonebase, parent_name) : nullptr;
|
|
|
|
|
@@ -6887,7 +6941,7 @@ bool web_engine_blend_main_set_pose_constraint(WebBlendMainState *state,
|
|
|
|
|
error = "POSE_CONSTRAINT_INVALID: constraint to remove was not found";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
BKE_constraint_remove(&channel->constraints, constraint);
|
|
|
|
|
BKE_constraint_remove_ex(&channel->constraints, object, constraint);
|
|
|
|
|
object->id.recalc |= ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
@@ -6906,11 +6960,11 @@ bool web_engine_blend_main_set_pose_constraint(WebBlendMainState *state,
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (constraint != nullptr && constraint->type != type->second) {
|
|
|
|
|
BKE_constraint_remove(&channel->constraints, constraint);
|
|
|
|
|
BKE_constraint_remove_ex(&channel->constraints, object, constraint);
|
|
|
|
|
constraint = nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (constraint == nullptr) {
|
|
|
|
|
constraint = BKE_constraint_add_for_pose(object, channel, edit.constraint_name.c_str(), type->second);
|
|
|
|
|
constraint = BKE_constraint_add_for_pose(object, channel, edit.constraint_name.c_str(), eBConstraint_Types(type->second));
|
|
|
|
|
}
|
|
|
|
|
Object **target_slot = nullptr;
|
|
|
|
|
char *subtarget = nullptr;
|
|
|
|
|
@@ -10335,14 +10389,14 @@ bool web_engine_blend_main_create_object_data(WebBlendMainState *state,
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const std::string type(object_type);
|
|
|
|
|
int blender_type = OB_EMPTY;
|
|
|
|
|
ObjectType blender_type = OB_EMPTY;
|
|
|
|
|
ID *data = nullptr;
|
|
|
|
|
if (type == "CAMERA") {
|
|
|
|
|
blender_type = OB_CAMERA;
|
|
|
|
|
data = &BKE_camera_add(state->main, name)->id;
|
|
|
|
|
}
|
|
|
|
|
else if (type == "LIGHT") {
|
|
|
|
|
blender_type = OB_LIGHT;
|
|
|
|
|
blender_type = OB_LAMP;
|
|
|
|
|
data = &BKE_light_add(state->main, name)->id;
|
|
|
|
|
}
|
|
|
|
|
else if (type == "ARMATURE") {
|
|
|
|
|
@@ -10382,13 +10436,13 @@ bool web_engine_blend_main_set_object_selection(WebBlendMainState *state,
|
|
|
|
|
error = "LINKED_DATA_MUTATION_BLOCKED: Object selection target is not editable";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
BKE_view_layer_synced_ensure(state->scene, state->view_layer);
|
|
|
|
|
BKE_view_layer_synced_ensure(*state->main, state->scene, state->view_layer);
|
|
|
|
|
Base *base = BKE_view_layer_base_find(state->view_layer, object);
|
|
|
|
|
if (base == nullptr) {
|
|
|
|
|
error = "OBJECT_EDITOR_INVALID: Object is not available in the active ViewLayer";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
BKE_base_select(base, selected ? BA_SELECT : BA_DESELECT);
|
|
|
|
|
SET_FLAG_FROM_TEST(base->flag, selected, BASE_SELECTED);
|
|
|
|
|
if (active) state->view_layer->basact = base;
|
|
|
|
|
else if (!selected && state->view_layer->basact == base) state->view_layer->basact = nullptr;
|
|
|
|
|
return true;
|
|
|
|
|
|