Capture M16 gap execution artifacts
Some checks are pending
M6 deployable RC / quick (push) Waiting to run
M6 deployable RC / chromium (push) Blocked by required conditions
M6 deployable RC / release (push) Blocked by required conditions

This commit is contained in:
mes123456
2026-08-21 21:09:40 -04:00
parent e8ce4f5d0c
commit 46c9ebfcb6
339 changed files with 20028 additions and 108 deletions

View File

@@ -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 &region)
{
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);