feat(web): persist advanced Main capability overrides
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-08-29 04:36:13 +08:00
parent 58c2ae046d
commit e9b9d343a1
7 changed files with 132 additions and 12 deletions

View File

@@ -41,6 +41,7 @@
#include "BKE_object_deform.h"
#include "BKE_packedFile.hh"
#include "BKE_scene.hh"
#include "BKE_text.h"
#include "BKE_vfont.hh"
#include "BKE_volume.hh"
#include "BKE_fcurve.hh"
@@ -72,6 +73,7 @@
#include "DNA_particle_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#include "DNA_text_types.h"
#include "DNA_world_types.h"
#include "DNA_userdef_enums.h"
#include "DNA_vfont_types.h"
@@ -134,6 +136,75 @@ std::string id_name(const ID &id)
return id.name[0] != '\0' && id.name[1] != '\0' ? std::string(id.name + 2) : std::string();
}
constexpr char WEB_ENGINE_GEOMETRY_OVERRIDE_TEXT[] = "WBV2 Geometry Node Overrides";
constexpr char WEB_ENGINE_PHYSICS_OVERRIDE_TEXT[] = "WBV2 Physics Simulation Override";
std::string text_contents(Text &text)
{
size_t length = 0;
char *buffer = txt_to_buf(&text, &length);
if (buffer == nullptr) return {};
std::string result(buffer, length);
MEM_delete(buffer);
return result;
}
Text *find_text(Main *main, const char *name)
{
if (main == nullptr || name == nullptr) return nullptr;
for (Text &text : main->texts) {
if (id_name(text.id) == name) return &text;
}
return nullptr;
}
bool write_text(Main *main, const char *name, const std::string &contents)
{
if (main == nullptr || name == nullptr || contents.size() > 64 * 1024 * 1024) return false;
Text *text = find_text(main, name);
if (text == nullptr) text = BKE_text_add(main, name);
if (text == nullptr) return false;
BKE_text_clear(text);
BKE_text_write(text, contents.c_str(), int(contents.size()));
return true;
}
void load_persisted_geometry_overrides(WebBlendMainState *state)
{
if (state == nullptr || state->main == nullptr) return;
Text *text = find_text(state->main, WEB_ENGINE_GEOMETRY_OVERRIDE_TEXT);
if (text == nullptr) return;
try {
const json payload = json::parse(text_contents(*text));
if (!payload.is_object()) return;
for (const auto &[graph_id, graph] : payload.items()) {
if (graph.is_object()) state->geometry_node_graph_overrides[graph_id] = graph.dump();
}
}
catch (...) {
/* Invalid internal text is ignored; regular Main data remains authoritative. */
}
}
void load_persisted_physics_override(WebBlendMainState *state)
{
if (state == nullptr || state->main == nullptr || !state->physics_simulation_override.empty()) return;
Text *text = find_text(state->main, WEB_ENGINE_PHYSICS_OVERRIDE_TEXT);
if (text == nullptr) return;
const std::string contents = text_contents(*text);
if (!contents.empty()) state->physics_simulation_override = contents;
}
bool persist_geometry_overrides(WebBlendMainState *state)
{
json payload = json::object();
for (const auto &[graph_id, graph_text] : state->geometry_node_graph_overrides) {
try { payload[graph_id] = json::parse(graph_text); }
catch (...) { return false; }
}
return write_text(state->main, WEB_ENGINE_GEOMETRY_OVERRIDE_TEXT, payload.dump());
}
Object *find_object(Main *main, const char *object_id)
{
if (main == nullptr || object_id == nullptr || strncmp(object_id, "object:", 7) != 0) {
@@ -869,6 +940,8 @@ WebBlendMainState *web_engine_blend_main_open(const uint8_t *data,
for (blender::ViewLayer &candidate : state->scene->view_layers) {
blender::BKE_view_layer_synced_ensure(*state->main, state->scene, &candidate);
}
load_persisted_geometry_overrides(state);
load_persisted_physics_override(state);
return state;
}
@@ -4344,7 +4417,17 @@ bool web_engine_blend_main_set_geometry_node_graph(WebBlendMainState *state,
error = "GN_INVALID_GRAPH: Geometry Node graph schema or bounds are invalid";
return false;
}
state->geometry_node_graph_overrides[graph_id] = graph.dump();
const std::string graph_text = graph.dump();
const auto previous = state->geometry_node_graph_overrides.find(graph_id);
const bool had_previous = previous != state->geometry_node_graph_overrides.end();
const std::string previous_text = had_previous ? previous->second : std::string();
state->geometry_node_graph_overrides[graph_id] = graph_text;
if (!persist_geometry_overrides(state)) {
if (!had_previous) state->geometry_node_graph_overrides.erase(graph_id);
else state->geometry_node_graph_overrides[graph_id] = previous_text;
error = "GN_INVALID_GRAPH: Geometry Node graph could not be persisted in Main";
return false;
}
return true;
}
catch (const std::exception &exception) {
@@ -4508,7 +4591,12 @@ bool web_engine_blend_main_set_physics_simulation(WebBlendMainState *state,
return false;
}
}
state->physics_simulation_override = manifest.dump();
const std::string manifest_text = manifest.dump();
if (!write_text(state->main, WEB_ENGINE_PHYSICS_OVERRIDE_TEXT, manifest_text)) {
error = "PHYSICS_MANIFEST_INVALID: physics manifest could not be persisted in Main";
return false;
}
state->physics_simulation_override = manifest_text;
return true;
}
catch (const std::exception &exception) {

View File

@@ -3,7 +3,6 @@
- task: `WBV2-P27-002`; parent: `WBV2-P27-001`; status: `in_progress`
- source prerequisite: `PASS`; Blender 5.2.0 `web_engine` compiled from `blender-5.2.0` with local Emscripten 3.1.69
- compiled artifacts copied to `web/app/src/vendor/blender/` and `web/app/public/vendor/blender/`
- application package rebuilt with `web_engine-C1wwLZyc.wasm` and `web-engine.worker-C3G0ZxMq.js`
- build command exit: `0`; log: `tests/golden/WBV2-P27-002/source-build.log`
- Chromium production receipt, malformed/transaction/persistence/determinism verification: `DEFERRED_BY_IMPLEMENTATION_FIRST_MODE`
- inventory unchanged: `6900` total, `281` legacy completed, `6618` legacy pending, `290/6609` effective carried forward

View File

@@ -25,7 +25,7 @@
},
"sourceMainState": {
"path": "blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp",
"sha256": "f48e424f53969cf2d9bce3ad9be565179d415301739f07785a1ed3271e430307"
"sha256": "dace3c17f47a2c267ed9a0dec649ec5fe3091873c99e0f1824d24b94f25e9127"
},
"sourceMainHeader": {
"path": "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h",
@@ -37,7 +37,7 @@
},
"wasm": {
"path": "web/app/src/vendor/blender/web_engine.wasm",
"sha256": "7981e66ccc37f3183c8c6244a667de6a2a2b39aa6934c9cefc280ea93773ccda"
"sha256": "08a5caccd99f9d51221d0a0adea3db1a57ab5d2718b6b026dc7f4af096054d07"
},
"publicLoader": {
"path": "web/app/public/vendor/blender/web_engine.js",
@@ -45,19 +45,19 @@
},
"publicWasm": {
"path": "web/app/public/vendor/blender/web_engine.wasm",
"sha256": "7981e66ccc37f3183c8c6244a667de6a2a2b39aa6934c9cefc280ea93773ccda"
"sha256": "08a5caccd99f9d51221d0a0adea3db1a57ab5d2718b6b026dc7f4af096054d07"
},
"webDistIndex": {
"path": "web/dist/index.html",
"sha256": "f63ea65367e10bea54c4d10428c2401db6d2aca9006c18692ced5e55b0e1527f"
"sha256": "6987bd43643d2c9f8abef1db4abab4df76e5a4acf567f374a01e073d9c410bff"
},
"webDistWorker": {
"path": "web/dist/assets/web-engine.worker-C3G0ZxMq.js",
"sha256": "b9083c4a38c16ba9ece02cb87c3c1a37cbea458b39db514c3abb8bd276ff9ff1"
"path": "web/dist/assets/web-engine.worker-Bm3nUTqn.js",
"sha256": "de3fcef592876e81b572dcc9e3d2118f4089e6170939310974d64866f493490b"
},
"webDistWasm": {
"path": "web/dist/assets/web_engine-C1wwLZyc.wasm",
"sha256": "7981e66ccc37f3183c8c6244a667de6a2a2b39aa6934c9cefc280ea93773ccda"
"path": "web/dist/assets/web_engine-CDU-hUyU.wasm",
"sha256": "08a5caccd99f9d51221d0a0adea3db1a57ab5d2718b6b026dc7f4af096054d07"
}
},
"verification": {

View File

@@ -1 +1,26 @@
ninja: no work to do.
[1/3] Building CXX object source/blender/web_engine/CMakeFiles/blender_web_engine_core.dir/web_engine_main_state.cpp.o
In file included from /home/mes123456/workinf_Blender_Wasm/blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp:35:
In file included from /home/mes123456/workinf_Blender_Wasm/blender-5.2.0/source/blender/blenkernel/BKE_node.hh:20:
In file included from /home/mes123456/workinf_Blender_Wasm/blender-5.2.0/source/blender/makesdna/DNA_node_types.h:15:
In file included from /home/mes123456/workinf_Blender_Wasm/blender-5.2.0/source/blender/makesdna/DNA_scene_types.h:35:
/home/mes123456/workinf_Blender_Wasm/blender-5.2.0/source/blender/makesdna/DNA_view3d_types.h:126:26: warning: implicit conversion from 'int' to 'char' changes value from 128 to -128 [-Wconstant-conversion]
126 | RV3D_VIEWLOCK_INIT = 1 << 7,
| ~~^~~~
In file included from /home/mes123456/workinf_Blender_Wasm/blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp:35:
In file included from /home/mes123456/workinf_Blender_Wasm/blender-5.2.0/source/blender/blenkernel/BKE_node.hh:20:
In file included from /home/mes123456/workinf_Blender_Wasm/blender-5.2.0/source/blender/makesdna/DNA_node_types.h:15:
/home/mes123456/workinf_Blender_Wasm/blender-5.2.0/source/blender/makesdna/DNA_scene_types.h:2053:33: warning: implicit conversion from 'int' to 'char' changes value from 128 to -128 [-Wconstant-conversion]
2053 | UVCALC_UNWRAP_USE_WEIGHTS = 1 << 7,
| ~~^~~~
/home/mes123456/workinf_Blender_Wasm/blender-5.2.0/source/blender/makesdna/DNA_scene_types.h:2324:25: warning: implicit conversion from 'blender::eSnapMode' to 'char' changes value from 128 to -128 [-Wconstant-conversion]
2324 | char snap_node_mode = SCE_SNAP_TO_GRID;
| ^~~~~~~~~~~~~~~~
/home/mes123456/workinf_Blender_Wasm/blender-5.2.0/source/blender/makesdna/DNA_scene_types.h:2324:25: warning: implicit conversion from 'blender::eSnapMode' to 'char' changes value from 128 to -128 [-Wconstant-conversion]
2324 | char snap_node_mode = SCE_SNAP_TO_GRID;
| ^~~~~~~~~~~~~~~~
/home/mes123456/workinf_Blender_Wasm/blender-5.2.0/source/blender/makesdna/DNA_scene_types.h:2200:26: note: in defaulted default constructor for 'blender::ToolSettings' first required here
2200 | DNA_DEFINE_CXX_METHODS(ToolSettings)
| ^
4 warnings generated.
[2/3] Linking CXX static library lib/libblender_web_engine_core.a
[3/3] Linking CXX executable bin/web_engine.js

View File

@@ -13,10 +13,15 @@ const buildDir = "build_web_blender6";
const jsPath = "web/app/src/vendor/blender/web_engine.js";
const wasmPath = "web/app/src/vendor/blender/web_engine.wasm";
const logPath = `${out}/source-build.log`;
const distAssets = fs.readdirSync(abs("web/dist/assets"));
const distWorker = distAssets.find((file) => /^web-engine\.worker-.*\.js$/u.test(file));
const distWasm = distAssets.find((file) => /^web_engine-.*\.wasm$/u.test(file));
assert.ok(fs.existsSync(abs(`${buildDir}/bin/web_engine.js`)));
assert.ok(fs.existsSync(abs(`${buildDir}/bin/web_engine.wasm`)));
assert.equal(sha(`${buildDir}/bin/web_engine.js`), sha(jsPath));
assert.equal(sha(`${buildDir}/bin/web_engine.wasm`), sha(wasmPath));
assert.ok(distWorker);
assert.ok(distWasm);
const report = {
schemaVersion: 1,
task: "WBV2-P27-002",
@@ -41,6 +46,9 @@ const report = {
wasm: { path: wasmPath, sha256: sha(wasmPath) },
publicLoader: { path: "web/app/public/vendor/blender/web_engine.js", sha256: sha("web/app/public/vendor/blender/web_engine.js") },
publicWasm: { path: "web/app/public/vendor/blender/web_engine.wasm", sha256: sha("web/app/public/vendor/blender/web_engine.wasm") },
webDistIndex: { path: "web/dist/index.html", sha256: sha("web/dist/index.html") },
webDistWorker: { path: `web/dist/assets/${distWorker}`, sha256: sha(`web/dist/assets/${distWorker}`) },
webDistWasm: { path: `web/dist/assets/${distWasm}`, sha256: sha(`web/dist/assets/${distWasm}`) },
},
verification: { chromium: "DEFERRED", productionReceipts: false, malformedBoundary: false, undoRedo: false, saveReopen: false, deterministicSecondRun: false, completionOverlay: false },
inventory: { total: 6900, legacyCompleted: 281, legacyPending: 6618, effectiveCompleted: 290, effectivePending: 6609 },

Binary file not shown.

Binary file not shown.