Capture M16 gap execution artifacts
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
#include "BLO_core_file_reader.hh"
|
||||
|
||||
#include "DNA_genfile.h"
|
||||
#include "DNA_action_types.h"
|
||||
#include "DNA_anim_enums.h"
|
||||
#include "DNA_curve_enums.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
@@ -549,6 +550,7 @@ std::string id_prefix(const std::string &type_name)
|
||||
if (type_name == "bSound") return "sound";
|
||||
if (type_name == "Speaker") return "speaker";
|
||||
if (type_name == "Tex") return "texture";
|
||||
if (type_name == "bAction" || type_name == "Action") return "action";
|
||||
return "datablock";
|
||||
}
|
||||
|
||||
@@ -564,7 +566,8 @@ bool is_exported_id_type(const std::string &type_name)
|
||||
type_name == "VFont" || type_name == "Mask" || type_name == "Library" ||
|
||||
type_name == "WorkSpace" || type_name == "bScreen" || type_name == "Brush" ||
|
||||
type_name == "FreestyleLineStyle" || type_name == "Lattice" ||
|
||||
type_name == "ParticleSettings" || type_name == "bSound" || type_name == "Speaker" || type_name == "Tex";
|
||||
type_name == "ParticleSettings" || type_name == "bSound" || type_name == "Speaker" || type_name == "Tex" ||
|
||||
type_name == "bAction" || type_name == "Action";
|
||||
}
|
||||
|
||||
std::string unique_id(const std::string &prefix,
|
||||
@@ -1248,6 +1251,36 @@ const char *editor_region_kind(const int64_t region_type)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<json> editor_view2d_rect(const SDNA &sdna,
|
||||
const ElementRef &view2d,
|
||||
const std::string &member_name)
|
||||
{
|
||||
const std::optional<ElementRef> rect = embedded_element(sdna, view2d, member_name);
|
||||
if (!rect) return std::nullopt;
|
||||
const auto stable_float = [](const float value) {
|
||||
return std::round(double(value) * 1000000.0) / 1000000.0;
|
||||
};
|
||||
return json{{"xmin", stable_float(read_float(sdna, *rect, "xmin").value_or(0.0f))},
|
||||
{"xmax", stable_float(read_float(sdna, *rect, "xmax").value_or(0.0f))},
|
||||
{"ymin", stable_float(read_float(sdna, *rect, "ymin").value_or(0.0f))},
|
||||
{"ymax", stable_float(read_float(sdna, *rect, "ymax").value_or(0.0f))}};
|
||||
}
|
||||
|
||||
std::optional<json> editor_view2d_state(const SDNA &sdna, const ElementRef ®ion)
|
||||
{
|
||||
const std::optional<ElementRef> view2d = embedded_element(sdna, region, "v2d");
|
||||
if (!view2d) return std::nullopt;
|
||||
const std::optional<json> current = editor_view2d_rect(sdna, *view2d, "cur");
|
||||
const std::optional<ElementRef> mask = embedded_element(sdna, *view2d, "mask");
|
||||
if (!current || !mask) return std::nullopt;
|
||||
return json{{"cur", *current},
|
||||
{"mask",
|
||||
{{"xmin", read_integer(sdna, *mask, "xmin").value_or(0)},
|
||||
{"xmax", read_integer(sdna, *mask, "xmax").value_or(0)},
|
||||
{"ymin", read_integer(sdna, *mask, "ymin").value_or(0)},
|
||||
{"ymax", read_integer(sdna, *mask, "ymax").value_or(0)}}}};
|
||||
}
|
||||
|
||||
std::optional<json> editor_workflow_from_records(
|
||||
const ParsedBlend &blend,
|
||||
const std::vector<IdRecord> &records,
|
||||
@@ -1328,10 +1361,16 @@ std::optional<json> editor_workflow_from_records(
|
||||
break;
|
||||
}
|
||||
const int64_t flags = read_integer(*blend.sdna, region, "flag").value_or(0);
|
||||
regions.push_back({{"id", record.id + ":area:" + std::to_string(area_index) +
|
||||
":region:" + std::to_string(regions.size())},
|
||||
{"kind", kind},
|
||||
{"visible", (flags & RGN_FLAG_HIDDEN) == 0}});
|
||||
json region_record = {{"id", record.id + ":area:" + std::to_string(area_index) +
|
||||
":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 (const std::optional<json> view2d = editor_view2d_state(*blend.sdna, region)) {
|
||||
region_record["view2d"] = *view2d;
|
||||
}
|
||||
}
|
||||
regions.push_back(std::move(region_record));
|
||||
has_main_region |= std::strcmp(kind, "MAIN") == 0;
|
||||
}
|
||||
if (!valid || regions.empty() || !has_main_region) {
|
||||
@@ -1345,12 +1384,23 @@ std::optional<json> editor_workflow_from_records(
|
||||
double(rect.x_max - rect.x_min) / screen_width;
|
||||
const double height = rect.y_max == screen_y_max ? 1.0 - y :
|
||||
double(rect.y_max - rect.y_min) / screen_height;
|
||||
areas.push_back({{"id", record.id + ":area:" + std::to_string(area_index)},
|
||||
{"editor", editor},
|
||||
{"regions", std::move(regions)},
|
||||
{"rect",
|
||||
{{"x", x}, {"y", y}, {"width", width}, {"height", height}}},
|
||||
{"maximized", false}});
|
||||
json area_record = {{"id", record.id + ":area:" + std::to_string(area_index)},
|
||||
{"editor", editor},
|
||||
{"regions", std::move(regions)},
|
||||
{"rect",
|
||||
{{"x", x}, {"y", y}, {"width", width}, {"height", height}}},
|
||||
{"maximized", false}};
|
||||
if (std::strcmp(editor, "DOPE_SHEET") == 0) {
|
||||
for (const ElementRef &space : linked_list_elements(blend, area, "spacedata")) {
|
||||
if (space.type_name != "SpaceAction") continue;
|
||||
if (const std::optional<ElementRef> ads = embedded_element(*blend.sdna, space, "ads")) {
|
||||
const std::string filter = read_string(*blend.sdna, *ads, "searchstr");
|
||||
if (!filter.empty()) area_record["filterText"] = filter;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
areas.push_back(std::move(area_record));
|
||||
}
|
||||
if (!valid || areas.empty()) continue;
|
||||
area_count += areas.size();
|
||||
@@ -2545,6 +2595,7 @@ json fcurve_channel(const ParsedBlend &blend, const ElementRef &fcurve)
|
||||
const std::optional<uint64_t> path_pointer = read_pointer(*blend.sdna, fcurve, "rna_path");
|
||||
const std::string path = path_pointer ? read_raw_string(blend, *path_pointer, 2048) : std::string();
|
||||
const int64_t array_index = read_integer(*blend.sdna, fcurve, "array_index").value_or(-1);
|
||||
const int64_t flags = read_integer(*blend.sdna, fcurve, "flag").value_or(0);
|
||||
const int64_t extrapolation = read_integer(*blend.sdna, fcurve, "extend").value_or(FCURVE_EXTRAPOLATE_CONSTANT);
|
||||
const std::string channel_path = path.empty() || array_index < 0 ?
|
||||
path :
|
||||
@@ -2614,12 +2665,74 @@ json fcurve_channel(const ParsedBlend &blend, const ElementRef &fcurve)
|
||||
if (channel_interpolation.empty()) channel_interpolation = interpolation;
|
||||
else if (channel_interpolation != interpolation) channel_interpolation = "MIXED";
|
||||
}
|
||||
return { {"path", channel_path},
|
||||
json result = { {"path", channel_path},
|
||||
{"editable", (flags & FCURVE_PROTECTED) == 0},
|
||||
{"enabled", (flags & FCURVE_DISABLED) == 0},
|
||||
{"selected", (flags & FCURVE_SELECTED) != 0},
|
||||
{"interpolation", channel_interpolation.empty() ? "BEZIER" : channel_interpolation},
|
||||
{"extrapolation", extrapolation == FCURVE_EXTRAPOLATE_LINEAR ? "LINEAR" : "CONSTANT"},
|
||||
{"keyframes", std::move(keyframes)},
|
||||
{"frameStart", std::isfinite(frame_start) ? frame_start : 0.0f},
|
||||
{"frameEnd", std::isfinite(frame_end) ? frame_end : 0.0f} };
|
||||
if (const std::optional<uint64_t> group_pointer = read_pointer(*blend.sdna, fcurve, "grp")) {
|
||||
if (const std::optional<ElementRef> group = element_for_pointer(blend, *group_pointer)) {
|
||||
const int64_t group_flags = read_integer(*blend.sdna, *group, "flag").value_or(0);
|
||||
result["group"] = read_string(*blend.sdna, *group, "name");
|
||||
result["expanded"] = (group_flags & AGRP_EXPANDED) != 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const char *driver_type_name(const int64_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case 0:
|
||||
return "AVERAGE";
|
||||
case 1:
|
||||
return "SCRIPTED";
|
||||
case 2:
|
||||
return "SUM";
|
||||
case 3:
|
||||
return "MIN";
|
||||
case 4:
|
||||
return "MAX";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
json driver_fcurve(const ParsedBlend &blend, const ElementRef &fcurve)
|
||||
{
|
||||
const std::optional<uint64_t> driver_pointer = read_pointer(*blend.sdna, fcurve, "driver");
|
||||
if (!driver_pointer) return json();
|
||||
const std::optional<ElementRef> driver = element_for_pointer(blend, *driver_pointer);
|
||||
if (!driver) return json();
|
||||
const std::optional<uint64_t> path_pointer = read_pointer(*blend.sdna, fcurve, "rna_path");
|
||||
const std::string path = path_pointer ? read_raw_string(blend, *path_pointer, 2048) : std::string();
|
||||
const int64_t array_index = read_integer(*blend.sdna, fcurve, "array_index").value_or(-1);
|
||||
const int64_t fcurve_flags = read_integer(*blend.sdna, fcurve, "flag").value_or(0);
|
||||
const int64_t driver_type = read_integer(*blend.sdna, *driver, "type").value_or(-1);
|
||||
const int64_t driver_flags = read_integer(*blend.sdna, *driver, "flag").value_or(0);
|
||||
json variables = json::array();
|
||||
for (const ElementRef &variable : linked_list_elements(blend, *driver, "variables")) {
|
||||
variables.push_back({{"name", read_string(*blend.sdna, variable, "name")},
|
||||
{"type", read_integer(*blend.sdna, variable, "type").value_or(-1)},
|
||||
{"targetCount", std::clamp<int64_t>(
|
||||
read_integer(*blend.sdna, variable, "num_targets").value_or(0),
|
||||
0,
|
||||
8)}});
|
||||
}
|
||||
return { {"path", path},
|
||||
{"arrayIndex", array_index},
|
||||
{"editable", (fcurve_flags & FCURVE_PROTECTED) == 0},
|
||||
{"enabled", (fcurve_flags & FCURVE_DISABLED) == 0},
|
||||
{"expression", read_string(*blend.sdna, *driver, "expression")},
|
||||
{"type", driver_type_name(driver_type)},
|
||||
{"typeCode", driver_type},
|
||||
{"flags", driver_flags},
|
||||
{"influence", read_float(*blend.sdna, *driver, "influence").value_or(1.0f)},
|
||||
{"variables", std::move(variables)}};
|
||||
}
|
||||
|
||||
void append_fcurve_channels(const ParsedBlend &blend,
|
||||
@@ -3210,6 +3323,7 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
json collections = json::array();
|
||||
json scenes = json::array();
|
||||
std::unordered_set<std::string> visited_animations;
|
||||
std::unordered_set<uint64_t> linked_action_pointers;
|
||||
std::unordered_map<std::string, json> modifier_stacks_by_mesh_id;
|
||||
std::unordered_map<std::string, std::array<float, 16>> mesh_bind_matrices;
|
||||
std::unordered_map<std::string, std::string> armature_id_by_object_id;
|
||||
@@ -3255,6 +3369,7 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
if (const std::optional<uint64_t> adt_pointer = read_pointer(*blend.sdna, element, "adt")) {
|
||||
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);
|
||||
}
|
||||
int track_index = 0;
|
||||
@@ -3267,6 +3382,7 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
const std::optional<uint64_t> action_pointer = read_pointer(*blend.sdna, strip, "act");
|
||||
std::string action_id = "action:missing:" + record.id;
|
||||
if (action_pointer) {
|
||||
linked_action_pointers.insert(*action_pointer);
|
||||
append_action_animation(blend, record.id, *action_pointer, animations, visited_animations);
|
||||
if (const std::optional<ElementRef> action = element_for_pointer(blend, *action_pointer)) {
|
||||
const std::string action_name = read_id_name(*blend.sdna, *action);
|
||||
@@ -3350,6 +3466,16 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
{"influence", read_float(*blend.sdna, constraint, "enforce").value_or(1.0f)}});
|
||||
}
|
||||
if (!constraints.empty()) node["constraints"] = std::move(constraints);
|
||||
if (const std::optional<uint64_t> adt_pointer = read_pointer(*blend.sdna, element, "adt")) {
|
||||
if (const std::optional<ElementRef> adt = element_for_pointer(blend, *adt_pointer)) {
|
||||
json drivers = json::array();
|
||||
for (const ElementRef &fcurve : linked_list_elements(blend, *adt, "drivers")) {
|
||||
json driver = driver_fcurve(blend, fcurve);
|
||||
if (!driver.is_null()) drivers.push_back(std::move(driver));
|
||||
}
|
||||
if (!drivers.empty()) node["drivers"] = std::move(drivers);
|
||||
}
|
||||
}
|
||||
if (parent_pointer) {
|
||||
const auto parent = ids_by_pointer.find(*parent_pointer);
|
||||
node["parentId"] = parent != ids_by_pointer.end() ? json(parent->second) : json(nullptr);
|
||||
@@ -4194,6 +4320,19 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
}
|
||||
}
|
||||
|
||||
for (const IdRecord &record : records) {
|
||||
if ((record.type_name != "bAction" && record.type_name != "Action") ||
|
||||
linked_action_pointers.contains(record.pointer))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const size_t animation_count = animations.size();
|
||||
append_action_animation(blend, "unlinked", record.pointer, animations, visited_animations);
|
||||
if (animations.size() > animation_count) {
|
||||
animations.back()["unlinked"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
sort_by_id(nodes);
|
||||
sort_by_id(meshes);
|
||||
sort_by_id(materials);
|
||||
|
||||
Reference in New Issue
Block a user