Continue Blender Web parity task handoff
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
#include "DNA_genfile.h"
|
||||
#include "DNA_action_types.h"
|
||||
#include "DNA_anim_enums.h"
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_curve_enums.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
#include "DNA_mask_types.h"
|
||||
@@ -334,6 +335,22 @@ std::vector<float> read_float_array(const SDNA &sdna,
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<int> read_byte_array(const SDNA &sdna,
|
||||
const ElementRef &element,
|
||||
const std::string &member_name,
|
||||
const size_t maximum_values)
|
||||
{
|
||||
const std::optional<MemberInfo> member = find_member(sdna, element.type_name, member_name);
|
||||
if (!member || member->size <= 0) return {};
|
||||
const size_t value_count = std::min(maximum_values, size_t(member->size));
|
||||
if (!bytes_available(element, *member, value_count)) return {};
|
||||
const uint8_t *data = element.block->bytes.data() + element.offset + size_t(member->offset);
|
||||
std::vector<int> result;
|
||||
result.reserve(value_count);
|
||||
for (size_t index = 0; index < value_count; index++) result.push_back(int(data[index]));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string read_string(const SDNA &sdna,
|
||||
const ElementRef &element,
|
||||
const std::string &member_name)
|
||||
@@ -1365,7 +1382,8 @@ std::optional<json> editor_workflow_from_records(
|
||||
":region:" + std::to_string(regions.size())},
|
||||
{"kind", kind},
|
||||
{"visible", (flags & RGN_FLAG_HIDDEN) == 0}};
|
||||
if (std::strcmp(editor, "DOPE_SHEET") == 0 && std::strcmp(kind, "MAIN") == 0) {
|
||||
if ((std::strcmp(editor, "DOPE_SHEET") == 0 || std::strcmp(editor, "GRAPH") == 0) &&
|
||||
std::strcmp(kind, "MAIN") == 0) {
|
||||
if (const std::optional<json> view2d = editor_view2d_state(*blend.sdna, region)) {
|
||||
region_record["view2d"] = *view2d;
|
||||
}
|
||||
@@ -1983,6 +2001,10 @@ std::array<float, 16> element_matrix(const ParsedBlend &blend,
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> read_raw_pointer_array(const ParsedBlend &blend,
|
||||
const uint64_t pointer,
|
||||
const size_t count);
|
||||
|
||||
json armature_from_data(const ParsedBlend &blend,
|
||||
const ElementRef &armature,
|
||||
const std::string &armature_id,
|
||||
@@ -2002,23 +2024,82 @@ json armature_from_data(const ParsedBlend &blend,
|
||||
const std::string name = read_string(*blend.sdna, bone, "name");
|
||||
if (name.empty()) return;
|
||||
const std::string id = "bone:" + armature_id + ":" + name;
|
||||
const std::vector<float> head = read_float_array(*blend.sdna, bone, "head", 3);
|
||||
const std::vector<float> tail = read_float_array(*blend.sdna, bone, "tail", 3);
|
||||
const std::vector<float> arm_head = read_float_array(*blend.sdna, bone, "arm_head", 3);
|
||||
const std::vector<float> arm_tail = read_float_array(*blend.sdna, bone, "arm_tail", 3);
|
||||
const std::vector<float> head = arm_head.size() == 3 ? arm_head :
|
||||
read_float_array(*blend.sdna, bone, "head", 3);
|
||||
const std::vector<float> tail = arm_tail.size() == 3 ? arm_tail :
|
||||
read_float_array(*blend.sdna, bone, "tail", 3);
|
||||
const std::array<float, 16> identity = {1, 0, 0, 0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 1};
|
||||
json entry = {{"id", id}};
|
||||
entry["name"] = name;
|
||||
const int64_t bone_flag = read_integer(*blend.sdna, bone, "flag").value_or(0);
|
||||
entry["selected"] = (bone_flag & BONE_SELECTED) != 0;
|
||||
entry["hidden"] = (bone_flag & BONE_HIDDEN_A) != 0;
|
||||
entry["connected"] = (bone_flag & BONE_CONNECTED) != 0;
|
||||
entry["parentId"] = parent_id.empty() ? json(nullptr) : json(parent_id);
|
||||
entry["head"] = float_array_or(head, {0.0f, 0.0f, 0.0f});
|
||||
entry["tail"] = float_array_or(tail, {0.0f, 0.0f, 1.0f});
|
||||
entry["restMatrix"] = element_matrix(blend, bone, "arm_mat", identity);
|
||||
if (const std::optional<ElementRef> color = embedded_element(*blend.sdna, bone, "color")) {
|
||||
json color_json = {{"paletteIndex", read_integer(*blend.sdna, *color, "palette_index").value_or(0)}};
|
||||
if (const std::optional<ElementRef> custom = embedded_element(*blend.sdna, *color, "custom")) {
|
||||
color_json["normal"] = read_byte_array(*blend.sdna, *custom, "solid", 4);
|
||||
color_json["select"] = read_byte_array(*blend.sdna, *custom, "select", 4);
|
||||
color_json["active"] = read_byte_array(*blend.sdna, *custom, "active", 4);
|
||||
color_json["flag"] = read_integer(*blend.sdna, *custom, "flag").value_or(0);
|
||||
}
|
||||
entry["color"] = std::move(color_json);
|
||||
}
|
||||
const auto pose_matrix = pose_matrices.find(name);
|
||||
if (pose_matrix != pose_matrices.end()) entry["poseMatrix"] = pose_matrix->second;
|
||||
bones.push_back(std::move(entry));
|
||||
for (const ElementRef &child : linked_list_elements(blend, bone, "childbase")) visit(child, id);
|
||||
};
|
||||
for (const ElementRef &root : linked_list_elements(blend, armature, "bonebase")) visit(root, "");
|
||||
return json{{"id", armature_id}, {"name", read_id_name(*blend.sdna, armature)}, {"bones", bones}};
|
||||
json bone_collections = json::array();
|
||||
const int64_t collection_count = std::clamp<int64_t>(
|
||||
read_integer(*blend.sdna, armature, "collection_array_num").value_or(0), 0, 10000);
|
||||
const std::optional<uint64_t> collection_pointer = read_pointer(*blend.sdna, armature, "collection_array");
|
||||
const std::vector<uint64_t> collection_pointers = collection_pointer && collection_count > 0 ?
|
||||
read_raw_pointer_array(blend,
|
||||
*collection_pointer,
|
||||
size_t(collection_count)) :
|
||||
std::vector<uint64_t>();
|
||||
std::vector<ElementRef> collection_elements;
|
||||
for (const uint64_t pointer : collection_pointers) {
|
||||
if (const std::optional<ElementRef> collection = element_for_pointer(blend, pointer)) {
|
||||
collection_elements.push_back(*collection);
|
||||
}
|
||||
}
|
||||
if (collection_elements.empty()) {
|
||||
collection_elements = linked_list_elements(blend, armature, "collections_legacy");
|
||||
}
|
||||
for (size_t index = 0; index < collection_elements.size(); index++) {
|
||||
const ElementRef &collection = collection_elements[index];
|
||||
const std::string name = read_string(*blend.sdna, collection, "name");
|
||||
if (name.empty()) continue;
|
||||
json members = json::array();
|
||||
for (const ElementRef &member : linked_list_elements(blend, collection, "bones")) {
|
||||
const std::optional<uint64_t> bone_pointer = read_pointer(*blend.sdna, member, "bone");
|
||||
if (!bone_pointer) continue;
|
||||
const std::optional<ElementRef> bone = element_for_pointer(blend, *bone_pointer);
|
||||
if (!bone) continue;
|
||||
const std::string bone_name = read_string(*blend.sdna, *bone, "name");
|
||||
if (!bone_name.empty()) members.push_back("bone:" + armature_id + ":" + bone_name);
|
||||
}
|
||||
bone_collections.push_back({{"id", "bone_collection:" + armature_id + ":" + name},
|
||||
{"name", name},
|
||||
{"index", int64_t(index)},
|
||||
{"visible", (read_integer(*blend.sdna, collection, "flags").value_or(0) & BONE_COLLECTION_VISIBLE) != 0},
|
||||
{"solo", (read_integer(*blend.sdna, collection, "flags").value_or(0) & BONE_COLLECTION_SOLO) != 0},
|
||||
{"boneIds", std::move(members)}});
|
||||
}
|
||||
return json{{"id", armature_id},
|
||||
{"name", read_id_name(*blend.sdna, armature)},
|
||||
{"bones", std::move(bones)},
|
||||
{"boneCollections", std::move(bone_collections)}};
|
||||
}
|
||||
|
||||
std::vector<int32_t> read_raw_int_array(const ParsedBlend &blend,
|
||||
@@ -2756,7 +2837,9 @@ void append_action_animation(const ParsedBlend &blend,
|
||||
const std::string &target_id,
|
||||
const uint64_t action_pointer,
|
||||
json &animations,
|
||||
std::unordered_set<std::string> &visited)
|
||||
std::unordered_set<std::string> &visited,
|
||||
const std::optional<int64_t> slot_handle = std::nullopt,
|
||||
const std::string &slot_identifier_hint = {})
|
||||
{
|
||||
const std::optional<ElementRef> action = element_for_pointer(blend, action_pointer);
|
||||
if (!action) return;
|
||||
@@ -2782,6 +2865,25 @@ void append_action_animation(const ParsedBlend &blend,
|
||||
const std::vector<uint64_t> data_pointers = data_pointer && data_count > 0 ?
|
||||
read_raw_pointer_array(blend, *data_pointer, size_t(data_count)) :
|
||||
std::vector<uint64_t>();
|
||||
const bool filter_slot = slot_handle.has_value() && *slot_handle != 0;
|
||||
std::optional<std::string> slot_identifier;
|
||||
int64_t slot_count = 0;
|
||||
const int64_t action_slot_count = std::clamp<int64_t>(
|
||||
read_integer(*blend.sdna, *action, "slot_array_num").value_or(0), 0, 10000);
|
||||
const std::optional<uint64_t> action_slot_pointer = read_pointer(*blend.sdna, *action, "slot_array");
|
||||
if (action_slot_pointer && action_slot_count > 0) {
|
||||
const std::vector<uint64_t> action_slot_pointers = read_raw_pointer_array(
|
||||
blend, *action_slot_pointer, size_t(action_slot_count));
|
||||
for (const uint64_t action_slot_value : action_slot_pointers) {
|
||||
const std::optional<ElementRef> action_slot = element_for_pointer(blend, action_slot_value);
|
||||
if (!action_slot) continue;
|
||||
slot_count++;
|
||||
if (slot_handle && read_integer(*blend.sdna, *action_slot, "handle").value_or(0) == *slot_handle) {
|
||||
slot_identifier = read_string(*blend.sdna, *action_slot, "identifier");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!slot_identifier && !slot_identifier_hint.empty()) slot_identifier = slot_identifier_hint;
|
||||
if (layer_pointer && layer_count > 0) {
|
||||
const std::vector<uint64_t> layer_pointers = read_raw_pointer_array(blend, *layer_pointer, size_t(layer_count));
|
||||
for (const uint64_t layer_value : layer_pointers) {
|
||||
@@ -2805,6 +2907,7 @@ void append_action_animation(const ParsedBlend &blend,
|
||||
for (const uint64_t bag_value : bag_pointers) {
|
||||
const std::optional<ElementRef> bag = element_for_pointer(blend, bag_value);
|
||||
if (!bag) continue;
|
||||
if (filter_slot && read_integer(*blend.sdna, *bag, "slot_handle").value_or(0) != *slot_handle) continue;
|
||||
const int64_t fcurve_count = std::clamp<int64_t>(read_integer(*blend.sdna, *bag, "fcurve_array_num").value_or(0), 0, 100000);
|
||||
const std::optional<uint64_t> fcurve_pointer = read_pointer(*blend.sdna, *bag, "fcurve_array");
|
||||
if (!fcurve_pointer) continue;
|
||||
@@ -2828,13 +2931,20 @@ void append_action_animation(const ParsedBlend &blend,
|
||||
if (!std::isfinite(frame_start)) frame_start = action_frame_start;
|
||||
if (!std::isfinite(frame_end)) frame_end = action_frame_end;
|
||||
const std::string action_name = read_id_name(*blend.sdna, *action);
|
||||
animations.push_back({{"id", "action:" + (action_name.empty() ? std::to_string(action_pointer) : action_name) + ":" + target_id},
|
||||
{"name", action_name.empty() ? "Action" : action_name},
|
||||
{"targetId", target_id},
|
||||
{"frameStart", std::isfinite(frame_start) ? frame_start : 0.0f},
|
||||
{"frameEnd", std::isfinite(frame_end) ? frame_end : 0.0f},
|
||||
{"markers", std::move(markers)},
|
||||
{"channels", std::move(channels)}});
|
||||
json animation = {{"id", "action:" + (action_name.empty() ? std::to_string(action_pointer) : action_name) + ":" + target_id},
|
||||
{"name", action_name.empty() ? "Action" : action_name},
|
||||
{"targetId", target_id},
|
||||
{"frameStart", std::isfinite(frame_start) ? frame_start : 0.0f},
|
||||
{"frameEnd", std::isfinite(frame_end) ? frame_end : 0.0f},
|
||||
{"markers", std::move(markers)},
|
||||
{"channels", std::move(channels)}};
|
||||
if (slot_identifier) {
|
||||
animation["slotIdentifier"] = *slot_identifier;
|
||||
animation["slotHandle"] = slot_handle.value_or(0);
|
||||
animation["slotAssigned"] = slot_handle.value_or(0) != 0;
|
||||
animation["slotCount"] = slot_count;
|
||||
}
|
||||
animations.push_back(std::move(animation));
|
||||
}
|
||||
|
||||
json mesh_geometry_from_attributes(const ParsedBlend &blend,
|
||||
@@ -3370,7 +3480,14 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
if (const std::optional<ElementRef> adt = element_for_pointer(blend, *adt_pointer)) {
|
||||
if (const std::optional<uint64_t> action_pointer = read_pointer(*blend.sdna, *adt, "action")) {
|
||||
linked_action_pointers.insert(*action_pointer);
|
||||
append_action_animation(blend, record.id, *action_pointer, animations, visited_animations);
|
||||
const int64_t slot_handle = read_integer(*blend.sdna, *adt, "slot_handle").value_or(0);
|
||||
append_action_animation(blend,
|
||||
record.id,
|
||||
*action_pointer,
|
||||
animations,
|
||||
visited_animations,
|
||||
slot_handle != 0 ? std::optional<int64_t>(slot_handle) : std::nullopt,
|
||||
read_string(*blend.sdna, *adt, "last_slot_identifier"));
|
||||
}
|
||||
int track_index = 0;
|
||||
for (const ElementRef &track : linked_list_elements(blend, *adt, "nla_tracks")) {
|
||||
@@ -3393,6 +3510,8 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
const int64_t strip_type = read_integer(*blend.sdna, strip, "type").value_or(0);
|
||||
const int64_t blend_mode = read_integer(*blend.sdna, strip, "blendmode").value_or(0);
|
||||
const int64_t extend_mode = read_integer(*blend.sdna, strip, "extendmode").value_or(0);
|
||||
const int64_t action_slot_handle = read_integer(*blend.sdna, strip, "action_slot_handle").value_or(0);
|
||||
const std::string action_slot_identifier = read_string(*blend.sdna, strip, "last_slot_identifier");
|
||||
const char *strip_type_name = strip_type == 0 ? "CLIP" : strip_type == 1 ? "TRANSITION" :
|
||||
strip_type == 2 ? "META" : strip_type == 3 ? "SOUND" : "UNKNOWN";
|
||||
json strip_ir = {
|
||||
@@ -3415,6 +3534,9 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
{"selected", (strip_flag & (1 << 1)) != 0},
|
||||
{"reverse", (strip_flag & (1 << 11)) != 0},
|
||||
{"useTimeWarp", (strip_flag & (1 << 6)) != 0},
|
||||
{"actionSlotHandle", action_slot_handle},
|
||||
{"actionSlotIdentifier", action_slot_identifier},
|
||||
{"actionSlotAssigned", action_slot_handle != 0},
|
||||
{"stripType", strip_type_name}};
|
||||
if (!action_pointer) strip_ir["unsupportedReason"] = "NLA Action data-block is missing";
|
||||
else if (blend_mode == 2 || blend_mode < 0 || blend_mode > 4) strip_ir["unsupportedReason"] = "NLA blend mode is outside the finite subset";
|
||||
@@ -3459,11 +3581,31 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
for (const ElementRef &constraint : linked_list_elements(blend, element, "constraints")) {
|
||||
const int64_t constraint_type = read_integer(*blend.sdna, constraint, "type").value_or(0);
|
||||
const int64_t flag = read_integer(*blend.sdna, constraint, "flag").value_or(0);
|
||||
constraints.push_back({{"name", read_string(*blend.sdna, constraint, "name")},
|
||||
{"typeCode", constraint_type},
|
||||
{"type", "CONSTRAINT_" + std::to_string(constraint_type)},
|
||||
{"enabled", (flag & 512) == 0},
|
||||
{"influence", read_float(*blend.sdna, constraint, "enforce").value_or(1.0f)}});
|
||||
json constraint_ir = {{"name", read_string(*blend.sdna, constraint, "name")},
|
||||
{"typeCode", constraint_type},
|
||||
{"type", "CONSTRAINT_" + std::to_string(constraint_type)},
|
||||
{"enabled", (flag & 512) == 0},
|
||||
{"influence", read_float(*blend.sdna, constraint, "enforce").value_or(1.0f)}};
|
||||
if (constraint_type == 12) {
|
||||
const std::optional<uint64_t> constraint_data_pointer = read_pointer(*blend.sdna, constraint, "data");
|
||||
if (constraint_data_pointer) {
|
||||
const std::optional<ElementRef> constraint_data = element_for_pointer(blend, *constraint_data_pointer);
|
||||
if (constraint_data) {
|
||||
const int64_t action_slot_handle = read_integer(*blend.sdna, *constraint_data, "action_slot_handle").value_or(0);
|
||||
const std::optional<uint64_t> action_pointer = read_pointer(*blend.sdna, *constraint_data, "act");
|
||||
if (action_pointer) {
|
||||
if (const std::optional<ElementRef> action = element_for_pointer(blend, *action_pointer)) {
|
||||
const std::string action_name = read_id_name(*blend.sdna, *action);
|
||||
constraint_ir["actionId"] = "action:" + (action_name.empty() ? std::to_string(*action_pointer) : action_name);
|
||||
}
|
||||
}
|
||||
constraint_ir["actionSlotHandle"] = action_slot_handle;
|
||||
constraint_ir["actionSlotIdentifier"] = read_string(*blend.sdna, *constraint_data, "last_slot_identifier");
|
||||
constraint_ir["actionSlotAssigned"] = action_slot_handle != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
constraints.push_back(std::move(constraint_ir));
|
||||
}
|
||||
if (!constraints.empty()) node["constraints"] = std::move(constraints);
|
||||
if (const std::optional<uint64_t> adt_pointer = read_pointer(*blend.sdna, element, "adt")) {
|
||||
|
||||
Reference in New Issue
Block a user