Rebuild local Blender WASM environment and recovery evidence
This commit is contained in:
57
tools/web/generated/WBV2-P0-003.py
Normal file
57
tools/web/generated/WBV2-P0-003.py
Normal file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
CONTRACT_TEXT_NAME = "WBV2-P0-003-CapabilityContract"
|
||||
CONTRACT_ID = "workflow:freeze_capability_error_budget_contract"
|
||||
|
||||
|
||||
def contract_payload():
|
||||
payload = {
|
||||
"schemaVersion": 1,
|
||||
"contractId": CONTRACT_ID,
|
||||
"capability": {"level": "L0", "status": "BOUNDED"},
|
||||
"revision": {"base": 0, "current": 1},
|
||||
"budget": {"maxErrors": 8, "maxMessageBytes": 256, "maxUnknownFields": 16},
|
||||
"errors": [
|
||||
{"code": "PROTOCOL_MISMATCH", "severity": "error", "recoverable": True},
|
||||
{"code": "REVISION_CONFLICT", "severity": "error", "recoverable": True},
|
||||
{"code": "OPEN_CANCELLED", "severity": "info", "recoverable": True},
|
||||
{"code": "CAPABILITY_CONTRACT_INVALID", "severity": "error", "recoverable": True},
|
||||
{"code": "CAPABILITY_CONTRACT_BUDGET_EXCEEDED", "severity": "error", "recoverable": True},
|
||||
{"code": "CAPABILITY_CONTRACT_UNSUPPORTED", "severity": "error", "recoverable": False},
|
||||
{"code": "CAPABILITY_CONTRACT_SCHEMA_HASH_MISMATCH", "severity": "fatal", "recoverable": False},
|
||||
{"code": "CAPABILITY_CONTRACT_MIGRATION_BLOCKED", "severity": "error", "recoverable": True},
|
||||
],
|
||||
"cancellation": {"code": "OPEN_CANCELLED", "preservesRevision": True},
|
||||
"migration": {
|
||||
"currentSchemaVersion": 1,
|
||||
"supportedSchemaVersions": [1],
|
||||
"preserveUnknown": True,
|
||||
"strategy": "RETAIN_UNKNOWN",
|
||||
},
|
||||
"extensions": {"futureField": {"kind": "opaque", "value": "preserve-me"}},
|
||||
}
|
||||
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
|
||||
payload["schemaHash"] = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
||||
return payload
|
||||
|
||||
|
||||
def main(output: pathlib.Path) -> None:
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
text = bpy.data.texts.new(CONTRACT_TEXT_NAME)
|
||||
text.write(json.dumps(contract_payload(), sort_keys=True, separators=(",", ":")))
|
||||
text.use_fake_user = True
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output.resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P0-003.py -- OUTPUT")
|
||||
main(pathlib.Path(arguments[0]))
|
||||
69
tools/web/generated/WBV2-P1-001.py
Normal file
69
tools/web/generated/WBV2-P1-001.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
TEXT_NAME = "WBV2-P1-001-RuntimeReceipt"
|
||||
RECEIPT_ID = "workflow:stabilize_runtime_variants_and_source_receipts"
|
||||
|
||||
|
||||
def sha256_file(path):
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def runtime_receipt():
|
||||
root = pathlib.Path(__file__).resolve().parents[3]
|
||||
source_hash = sha256_file(root / "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp")
|
||||
manifest = json.loads((root / "web/app/public/engine-manifest.json").read_text(encoding="utf-8"))
|
||||
variants = []
|
||||
for variant in manifest["variants"]:
|
||||
resources = {}
|
||||
for role, resource in variant["resources"].items():
|
||||
resource_path = root / "web/app/public" / resource["url"].lstrip("/")
|
||||
resources[role] = {
|
||||
"sha256": sha256_file(resource_path),
|
||||
"url": resource["url"],
|
||||
}
|
||||
variants.append({
|
||||
"id": variant["id"],
|
||||
"memory": variant["memory"],
|
||||
"resources": resources,
|
||||
})
|
||||
return {
|
||||
"schemaVersion": 1,
|
||||
"receiptId": RECEIPT_ID,
|
||||
"releaseId": manifest["releaseId"],
|
||||
"engineVersion": manifest["engineVersion"],
|
||||
"sourceSha256": source_hash,
|
||||
"buildSha256": sha256_file(root / "web/app/src/vendor/blender/web_engine.wasm"),
|
||||
"variants": variants,
|
||||
"fallback": {
|
||||
"policy": "AUTO",
|
||||
"deterministicOrder": ["pthread", "single"],
|
||||
"reasonCode": "PTHREAD_INITIALIZATION_FAILED",
|
||||
},
|
||||
"session": {
|
||||
"generation": 1,
|
||||
"oneEnginePerSession": True,
|
||||
},
|
||||
"extensions": {"futureField": {"kind": "opaque", "value": "preserve-me"}},
|
||||
}
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
text = bpy.data.texts.new(TEXT_NAME)
|
||||
text.write(json.dumps(runtime_receipt(), sort_keys=True, separators=(",", ":")))
|
||||
text.use_fake_user = True
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output.resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P1-001.py -- OUTPUT")
|
||||
main(pathlib.Path(arguments[0]))
|
||||
55
tools/web/generated/WBV2-P1-002.py
Normal file
55
tools/web/generated/WBV2-P1-002.py
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
TEXT_NAME = "WBV2-P1-002-SceneIRContract"
|
||||
CONTRACT_ID = "workflow:unify_sceneir_stable_ids_and_binary_deltas"
|
||||
|
||||
|
||||
def contract():
|
||||
return {
|
||||
"schemaVersion": 1,
|
||||
"contractId": CONTRACT_ID,
|
||||
"stableIds": {
|
||||
"objectId": "object:WBV2P1002CubeObject",
|
||||
"meshId": "mesh:WBV2P1002CubeMesh",
|
||||
},
|
||||
"binaryDelta": {
|
||||
"schemaVersion": 1,
|
||||
"baseRevision": 1,
|
||||
"generation": 1,
|
||||
"patches": [{"field": "positions", "byteOffset": 0, "byteLength": 12}],
|
||||
},
|
||||
"extensions": {"futureField": {"kind": "opaque", "value": "preserve-me"}},
|
||||
}
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WBV2P1002CubeMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1, -1, -1), (1, -1, -1), (1, 1, -1), (-1, 1, -1),
|
||||
(-1, -1, 1), (1, -1, 1), (1, 1, 1), (-1, 1, 1)],
|
||||
[],
|
||||
[(0, 1, 2, 3), (4, 7, 6, 5), (0, 4, 5, 1),
|
||||
(1, 5, 6, 2), (2, 6, 7, 3), (4, 0, 3, 7)],
|
||||
)
|
||||
mesh.update()
|
||||
obj = bpy.data.objects.new("WBV2P1002CubeObject", mesh)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
text = bpy.data.texts.new(TEXT_NAME)
|
||||
text.write(json.dumps(contract(), sort_keys=True, separators=(",", ":")))
|
||||
text.use_fake_user = True
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output.resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P1-002.py -- OUTPUT")
|
||||
main(pathlib.Path(arguments[0]))
|
||||
43
tools/web/generated/WBV2-P1-003.py
Normal file
43
tools/web/generated/WBV2-P1-003.py
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
TEXT_NAME = "WBV2-P1-003-TransactionContract"
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WBV2P1003CubeMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1, -1, -1), (1, -1, -1), (1, 1, -1), (-1, 1, -1),
|
||||
(-1, -1, 1), (1, -1, 1), (1, 1, 1), (-1, 1, 1)],
|
||||
[],
|
||||
[(0, 1, 2, 3), (4, 7, 6, 5), (0, 4, 5, 1),
|
||||
(1, 5, 6, 2), (2, 6, 7, 3), (4, 0, 3, 7)],
|
||||
)
|
||||
mesh.update()
|
||||
obj = bpy.data.objects.new("WBV2P1003CubeObject", mesh)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
text = bpy.data.texts.new(TEXT_NAME)
|
||||
text.write(json.dumps({
|
||||
"schemaVersion": 1,
|
||||
"contractId": "workflow:unify_main_transaction_history_and_save",
|
||||
"initialVisibility": True,
|
||||
"mutation": {"command": "setObjectVisibility", "visible": False},
|
||||
"history": {"failedCommandAddsHistory": False, "undoRedo": "SUPPORTED"},
|
||||
"revision": {"staleCommit": "REJECTED"},
|
||||
"save": {"atomic": True, "byteStable": True},
|
||||
}, sort_keys=True, separators=(",", ":")))
|
||||
text.use_fake_user = True
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output.resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P1-003.py -- OUTPUT")
|
||||
main(pathlib.Path(arguments[0]))
|
||||
73
tools/web/generated/WBV2-P1-004.py
Normal file
73
tools/web/generated/WBV2-P1-004.py
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
TEXT_NAME = "WBV2-P1-004-WorkerLifecycleContract"
|
||||
CONTRACT_ID = "workflow:harden_worker_cancel_restart_and_dispose"
|
||||
|
||||
|
||||
def contract():
|
||||
value = {
|
||||
"schemaVersion": 1,
|
||||
"contractId": CONTRACT_ID,
|
||||
"cancel": {
|
||||
"code": "OPEN_CANCELLED",
|
||||
"preservesRevision": True,
|
||||
"resultPublished": False,
|
||||
},
|
||||
"generation": {
|
||||
"afterRestart": 2,
|
||||
"initial": 1,
|
||||
"staleResult": "REJECTED",
|
||||
},
|
||||
"owners": {
|
||||
"cpu": {"afterCancel": 0, "afterDispose": 0, "afterRestart": 0, "peak": 1},
|
||||
"gpu": {"afterCancel": 0, "afterDispose": 0, "afterRestart": 0, "peak": 1},
|
||||
"wasm": {"afterCancel": 0, "afterDispose": 0, "afterRestart": 0, "peak": 1},
|
||||
},
|
||||
"budget": {
|
||||
"maxInputBytes": 1024 * 1024,
|
||||
"maxLiveRequests": 1,
|
||||
"releaseAfterCancel": True,
|
||||
},
|
||||
"dispose": {
|
||||
"doubleDispose": "NOOP",
|
||||
"idempotent": True,
|
||||
"released": True,
|
||||
},
|
||||
"extensions": {"futureField": {"kind": "opaque", "value": "preserve-me"}},
|
||||
}
|
||||
encoded = json.dumps(value, sort_keys=True, separators=(",", ":"))
|
||||
value["schemaHash"] = hashlib.sha256(encoded.encode("utf-8")).hexdigest()
|
||||
return value
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WBV2P1004LifecycleMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1, -1, -1), (1, -1, -1), (1, 1, -1), (-1, 1, -1),
|
||||
(-1, -1, 1), (1, -1, 1), (1, 1, 1), (-1, 1, 1)],
|
||||
[],
|
||||
[(0, 1, 2, 3), (4, 7, 6, 5), (0, 4, 5, 1),
|
||||
(1, 5, 6, 2), (2, 6, 7, 3), (4, 0, 3, 7)],
|
||||
)
|
||||
mesh.update()
|
||||
obj = bpy.data.objects.new("WBV2P1004LifecycleObject", mesh)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
text = bpy.data.texts.new(TEXT_NAME)
|
||||
text.write(json.dumps(contract(), sort_keys=True, separators=(",", ":")))
|
||||
text.use_fake_user = True
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output.resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P1-004.py -- OUTPUT")
|
||||
main(pathlib.Path(arguments[0]))
|
||||
66
tools/web/generated/WBV2-P1-005.py
Normal file
66
tools/web/generated/WBV2-P1-005.py
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
TEXT_NAME = "WBV2-P1-005-StorageJournalContract"
|
||||
CONTRACT_ID = "workflow:harden_opfs_indexeddb_journal_recovery"
|
||||
|
||||
|
||||
def contract():
|
||||
value = {
|
||||
"schemaVersion": 1,
|
||||
"contractId": CONTRACT_ID,
|
||||
"journal": {
|
||||
"interruptedAt": "BEFORE_PUBLISH",
|
||||
"oldProjectUsable": True,
|
||||
"recovery": "ROLLBACK_OLD_REVISION",
|
||||
"stagedRevision": 8,
|
||||
"oldRevision": 7,
|
||||
},
|
||||
"hash": {
|
||||
"expected": "sha256:project-revision-8",
|
||||
"mismatch": "QUARANTINED",
|
||||
"verified": False,
|
||||
},
|
||||
"storage": {
|
||||
"indexeddb": {"afterRecovery": 0, "journalEntriesBefore": 1, "metadataRevision": 7},
|
||||
"opfs": {"afterRecovery": 0, "stagingFilesBefore": 1, "projectReadable": True},
|
||||
},
|
||||
"restart": {"generationAfter": 2, "generationBefore": 1, "status": "RECOVERED"},
|
||||
"budget": {"maxJournalEntries": 32, "maxProjectBytes": 4 * 1024 * 1024},
|
||||
"extensions": {"futureField": {"kind": "opaque", "value": "preserve-me"}},
|
||||
}
|
||||
encoded = json.dumps(value, sort_keys=True, separators=(",", ":"))
|
||||
value["schemaHash"] = hashlib.sha256(encoded.encode("utf-8")).hexdigest()
|
||||
return value
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WBV2P1005StorageMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1, -1, -1), (1, -1, -1), (1, 1, -1), (-1, 1, -1),
|
||||
(-1, -1, 1), (1, -1, 1), (1, 1, 1), (-1, 1, 1)],
|
||||
[],
|
||||
[(0, 1, 2, 3), (4, 7, 6, 5), (0, 4, 5, 1),
|
||||
(1, 5, 6, 2), (2, 6, 7, 3), (4, 0, 3, 7)],
|
||||
)
|
||||
mesh.update()
|
||||
obj = bpy.data.objects.new("WBV2P1005StorageObject", mesh)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
text = bpy.data.texts.new(TEXT_NAME)
|
||||
text.write(json.dumps(contract(), sort_keys=True, separators=(",", ":")))
|
||||
text.use_fake_user = True
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output.resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P1-005.py -- OUTPUT")
|
||||
main(pathlib.Path(arguments[0]))
|
||||
37
tools/web/generated/WBV2-P2-001.py
Normal file
37
tools/web/generated/WBV2-P2-001.py
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P2001Scene"
|
||||
root = scene.collection
|
||||
|
||||
collection = bpy.data.collections.new("WBV2P2001OutlinerCollection")
|
||||
root.children.link(collection)
|
||||
mesh = bpy.data.meshes.new("WBV2P2001StableMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1, -1, 0), (1, -1, 0), (0, 1, 0)],
|
||||
[],
|
||||
[(0, 1, 2)],
|
||||
)
|
||||
mesh.update()
|
||||
obj = bpy.data.objects.new("WBV2P2001SelectedObject", mesh)
|
||||
collection.objects.link(obj)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
|
||||
# Keep the standard workspace/screen in the file so editorWorkflow has a
|
||||
# real context while the data tree remains deliberately minimal.
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output.resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P2-001.py -- OUTPUT")
|
||||
main(pathlib.Path(arguments[0]))
|
||||
44
tools/web/generated/WBV2-P2-002.py
Normal file
44
tools/web/generated/WBV2-P2-002.py
Normal file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def triangle(name):
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
mesh.from_pydata([(-1, 0, 0), (1, 0, 0), (0, 1, 0)], [], [(0, 1, 2)])
|
||||
mesh.update()
|
||||
return mesh
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P2002Scene"
|
||||
root = scene.collection
|
||||
source = bpy.data.collections.new("WBV2P2002Source")
|
||||
target = bpy.data.collections.new("WBV2P2002Target")
|
||||
root.children.link(source)
|
||||
root.children.link(target)
|
||||
|
||||
parent = bpy.data.objects.new("WBV2P2002Parent", triangle("WBV2P2002ParentMesh"))
|
||||
source.objects.link(parent)
|
||||
parent.location = (1.0, 0.0, 0.0)
|
||||
|
||||
child = bpy.data.objects.new("WBV2P2002Child", triangle("WBV2P2002ChildMesh"))
|
||||
source.objects.link(child)
|
||||
child.parent = parent
|
||||
child.location = (0.0, 2.0, 0.0)
|
||||
|
||||
bpy.context.view_layer.objects.active = parent
|
||||
parent.select_set(True)
|
||||
child.select_set(False)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output.resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P2-002.py -- OUTPUT")
|
||||
main(pathlib.Path(arguments[0]))
|
||||
39
tools/web/generated/WBV2-P2-003.py
Normal file
39
tools/web/generated/WBV2-P2-003.py
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
OPERATIONS = ("Translate", "Merge", "Dissolve", "Extrude", "Inset", "Bevel", "LoopCut")
|
||||
|
||||
|
||||
def square_mesh(name):
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
mesh.from_pydata(
|
||||
[(-1, -1, 0), (1, -1, 0), (1, 1, 0), (-1, 1, 0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
mesh.update()
|
||||
return mesh
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
collection = bpy.context.scene.collection
|
||||
for label in OPERATIONS:
|
||||
mesh = square_mesh("WBV2P2003%sMesh" % label)
|
||||
obj = bpy.data.objects.new("WBV2P2003%sObject" % label, mesh)
|
||||
collection.objects.link(obj)
|
||||
active = bpy.data.objects["WBV2P2003TranslateObject"]
|
||||
bpy.context.view_layer.objects.active = active
|
||||
active.select_set(True)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output.resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P2-003.py -- OUTPUT")
|
||||
main(pathlib.Path(arguments[0]))
|
||||
36
tools/web/generated/WBV2-P2-004.py
Normal file
36
tools/web/generated/WBV2-P2-004.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P2004ViewportScene"
|
||||
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
|
||||
cube = bpy.context.object
|
||||
cube.name = "WBV2P2004ViewportCube"
|
||||
cube.data.name = "WBV2P2004ViewportCubeMesh"
|
||||
cube.select_set(True)
|
||||
bpy.context.view_layer.objects.active = cube
|
||||
|
||||
camera_data = bpy.data.cameras.new("WBV2P2004ViewportCamera")
|
||||
camera = bpy.data.objects.new("WBV2P2004ViewportCamera", camera_data)
|
||||
scene.collection.objects.link(camera)
|
||||
camera.location = (6.5, -6.5, 4.75)
|
||||
camera.rotation_euler = (Vector((0, 0, 0)) - camera.location).to_track_quat("-Z", "Y").to_euler()
|
||||
camera_data.lens = 52
|
||||
scene.camera = camera
|
||||
scene.world = bpy.data.worlds.new("WBV2P2004ViewportWorld")
|
||||
scene.world.color = (0.055, 0.065, 0.08)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P2-004.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
27
tools/web/generated/WBV2-P2-005.py
Normal file
27
tools/web/generated/WBV2-P2-005.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P2005WorkflowScene"
|
||||
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
|
||||
cube = bpy.context.object
|
||||
cube.name = "WBV2P2005WorkflowCube"
|
||||
cube.data.name = "WBV2P2005WorkflowCubeMesh"
|
||||
cube.select_set(True)
|
||||
bpy.context.view_layer.objects.active = cube
|
||||
scene.world = bpy.data.worlds.new("WBV2P2005WorkflowWorld")
|
||||
scene.world.color = (0.055, 0.065, 0.08)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P2-005.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
45
tools/web/generated/WBV2-P3-001.py
Normal file
45
tools/web/generated/WBV2-P3-001.py
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P3001MaterialScene"
|
||||
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
|
||||
cube = bpy.context.object
|
||||
cube.name = "WBV2P3001MaterialCube"
|
||||
cube.data.name = "WBV2P3001MaterialCubeMesh"
|
||||
material = bpy.data.materials.new("WBV2P3001PrincipledMaterial")
|
||||
material.use_nodes = True
|
||||
nodes = material.node_tree.nodes
|
||||
principled = nodes.get("Principled BSDF")
|
||||
if principled is None:
|
||||
raise RuntimeError("material fixture could not create Principled BSDF")
|
||||
principled.inputs["Base Color"].default_value = (0.12, 0.34, 0.56, 1.0)
|
||||
principled.inputs["Roughness"].default_value = 0.23
|
||||
principled.inputs["Metallic"].default_value = 0.67
|
||||
principled.inputs["IOR"].default_value = 1.42
|
||||
unsupported = nodes.new("ShaderNodeTexNoise")
|
||||
unsupported.name = "WBV2P3001UnsupportedNoise"
|
||||
unsupported.label = "retained unsupported node"
|
||||
cube.data.materials.append(material)
|
||||
cube.select_set(True)
|
||||
bpy.context.view_layer.objects.active = cube
|
||||
if cube.data.uv_layers.active is None:
|
||||
cube.data.uv_layers.new(name="UVMap")
|
||||
cube.data.uv_layers.active.name = "WBV2P3001UV"
|
||||
cube.data.uv_layers.active_index = 0
|
||||
scene.world = bpy.data.worlds.new("WBV2P3001MaterialWorld")
|
||||
scene.world.color = (0.055, 0.065, 0.08)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P3-001.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
45
tools/web/generated/WBV2-P3-002.py
Normal file
45
tools/web/generated/WBV2-P3-002.py
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P3002AnimationScene"
|
||||
scene.frame_start = 1
|
||||
scene.frame_end = 40
|
||||
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
|
||||
cube = bpy.context.object
|
||||
cube.name = "WBV2P3002AnimatedCube"
|
||||
cube.data.name = "WBV2P3002AnimatedCubeMesh"
|
||||
cube.select_set(True)
|
||||
bpy.context.view_layer.objects.active = cube
|
||||
keyframes = {
|
||||
1: ((0, 0, 0), (0, 0, 0), (1, 1, 1)),
|
||||
10: ((1, 2, 0.5), (0.1, 0.2, 0.3), (1.2, 0.8, 1.1)),
|
||||
20: ((2, 0, 1), (0.4, 0.1, 0.6), (0.9, 1.3, 1.0)),
|
||||
}
|
||||
for frame, (location, rotation, scale) in keyframes.items():
|
||||
scene.frame_set(frame)
|
||||
cube.location = location
|
||||
cube.rotation_euler = rotation
|
||||
cube.scale = scale
|
||||
cube.keyframe_insert(data_path="location", frame=frame)
|
||||
cube.keyframe_insert(data_path="rotation_euler", frame=frame)
|
||||
cube.keyframe_insert(data_path="scale", frame=frame)
|
||||
if cube.animation_data and cube.animation_data.action:
|
||||
cube.animation_data.action.name = "WBV2P3002TRSAction"
|
||||
scene.frame_set(1)
|
||||
scene.world = bpy.data.worlds.new("WBV2P3002AnimationWorld")
|
||||
scene.world.color = (0.055, 0.065, 0.08)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P3-002.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
91
tools/web/generated/WBV2-P3-003.py
Normal file
91
tools/web/generated/WBV2-P3-003.py
Normal file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P3003RigPoseSkinScene"
|
||||
scene.frame_start = 1
|
||||
scene.frame_end = 20
|
||||
|
||||
mesh = bpy.data.meshes.new("WBV2P3003SkinnedMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1, -1, 0), (1, -1, 0), (1, 1, 0), (-1, 1, 0),
|
||||
(-1, -1, 2), (1, -1, 2), (1, 1, 2), (-1, 1, 2)],
|
||||
[],
|
||||
[(0, 1, 2), (0, 2, 3), (4, 6, 5), (4, 7, 6),
|
||||
(0, 4, 5), (0, 5, 1), (1, 5, 6), (1, 6, 2),
|
||||
(2, 6, 7), (2, 7, 3), (4, 0, 3), (4, 3, 7)],
|
||||
)
|
||||
mesh.update()
|
||||
mesh_object = bpy.data.objects.new("WBV2P3003SkinnedMeshObject", mesh)
|
||||
bpy.context.collection.objects.link(mesh_object)
|
||||
|
||||
basis = mesh_object.shape_key_add(name="Basis")
|
||||
smile = mesh_object.shape_key_add(name="WBV2P3003Smile")
|
||||
smile.data[2].co.z += 0.35
|
||||
smile.data[6].co.z += 0.35
|
||||
smile.value = 0.4
|
||||
del basis
|
||||
|
||||
armature_data = bpy.data.armatures.new("WBV2P3003Armature")
|
||||
armature_object = bpy.data.objects.new("WBV2P3003ArmatureObject", armature_data)
|
||||
bpy.context.collection.objects.link(armature_object)
|
||||
bpy.context.view_layer.objects.active = armature_object
|
||||
armature_object.select_set(True)
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
root = armature_data.edit_bones.new("WBV2P3003Root")
|
||||
root.head = (0, 0, 0)
|
||||
root.tail = (0, 0, 1)
|
||||
tip = armature_data.edit_bones.new("WBV2P3003Tip")
|
||||
tip.head = (0, 0, 1)
|
||||
tip.tail = (0, 0, 2)
|
||||
tip.parent = root
|
||||
tip.use_connect = True
|
||||
bpy.ops.object.mode_set(mode="POSE")
|
||||
pose_root = armature_object.pose.bones["WBV2P3003Root"]
|
||||
pose_root.rotation_mode = "XYZ"
|
||||
for frame, angle in ((1, 0.0), (10, 0.35), (20, -0.2)):
|
||||
scene.frame_set(frame)
|
||||
pose_root.rotation_euler[1] = angle
|
||||
pose_root.keyframe_insert(data_path="rotation_euler", index=1, frame=frame)
|
||||
unsupported = armature_object.pose.bones["WBV2P3003Tip"].constraints.new("LIMIT_ROTATION")
|
||||
unsupported.name = "WBV2P3003UnsupportedLimitRotation"
|
||||
unsupported.use_limit_x = True
|
||||
unsupported.min_x = -0.25
|
||||
unsupported.max_x = 0.25
|
||||
unsupported.influence = 0.65
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
root_group = mesh_object.vertex_groups.new(name="WBV2P3003Root")
|
||||
tip_group = mesh_object.vertex_groups.new(name="WBV2P3003Tip")
|
||||
root_group.add([0, 1, 2, 3], 1.0, "REPLACE")
|
||||
root_group.add([4, 5, 6, 7], 0.2, "REPLACE")
|
||||
tip_group.add([4, 5, 6, 7], 0.8, "REPLACE")
|
||||
tip_group.add([0, 1, 2, 3], 0.0, "REPLACE")
|
||||
armature_modifier = mesh_object.modifiers.new(name="WBV2P3003ArmatureDeform", type="ARMATURE")
|
||||
armature_modifier.object = armature_object
|
||||
|
||||
bounded = mesh_object.constraints.new("LIMIT_LOCATION")
|
||||
bounded.name = "WBV2P3003BoundedObjectConstraint"
|
||||
bounded.use_min_x = True
|
||||
bounded.use_max_x = True
|
||||
bounded.min_x = -2.0
|
||||
bounded.max_x = 2.0
|
||||
bounded.influence = 0.8
|
||||
|
||||
scene.frame_set(1)
|
||||
scene.world = bpy.data.worlds.new("WBV2P3003RigWorld")
|
||||
scene.world.color = (0.03, 0.04, 0.05)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P3-003.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
98
tools/web/generated/WBV2-P3-004.py
Normal file
98
tools/web/generated/WBV2-P3-004.py
Normal file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P3004GLBRoundTripScene"
|
||||
scene.frame_start = 1
|
||||
scene.frame_end = 20
|
||||
scene.render.fps = 24
|
||||
|
||||
mesh = bpy.data.meshes.new("WBV2P3004RoundTripMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1, -1, 0), (1, -1, 0), (1, 1, 0), (-1, 1, 0),
|
||||
(-1, -1, 2), (1, -1, 2), (1, 1, 2), (-1, 1, 2)],
|
||||
[],
|
||||
[(0, 1, 2), (0, 2, 3), (4, 6, 5), (4, 7, 6),
|
||||
(0, 4, 5), (0, 5, 1), (1, 5, 6), (1, 6, 2),
|
||||
(2, 6, 7), (2, 7, 3), (4, 0, 3), (4, 3, 7)],
|
||||
)
|
||||
mesh.update()
|
||||
uv = mesh.uv_layers.new(name="WBV2P3004UV")
|
||||
for loop_index, loop in enumerate(mesh.loops):
|
||||
uv.data[loop_index].uv = ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0))[loop.vertex_index % 4]
|
||||
mesh_object = bpy.data.objects.new("WBV2P3004RoundTripMeshObject", mesh)
|
||||
bpy.context.collection.objects.link(mesh_object)
|
||||
|
||||
mesh_object.shape_key_add(name="Basis")
|
||||
morph = mesh_object.shape_key_add(name="WBV2P3004Morph")
|
||||
morph.data[2].co.z += 0.3
|
||||
morph.data[6].co.z += 0.3
|
||||
morph.value = 0.5
|
||||
|
||||
material = bpy.data.materials.new("WBV2P3004PBRMaterial")
|
||||
material.use_nodes = True
|
||||
material.diffuse_color = (0.22, 0.48, 0.82, 1.0)
|
||||
principled = material.node_tree.nodes.get("Principled BSDF")
|
||||
principled.inputs["Base Color"].default_value = (0.22, 0.48, 0.82, 1.0)
|
||||
principled.inputs["Metallic"].default_value = 0.35
|
||||
principled.inputs["Roughness"].default_value = 0.42
|
||||
principled.inputs["Emission Color"].default_value = (0.0, 0.0, 0.0, 1.0)
|
||||
principled.inputs["Emission Strength"].default_value = 1.0
|
||||
image = bpy.data.images.load(str(pathlib.Path("tests/files/web/resources/udim_1001.png").resolve()), check_existing=False)
|
||||
image.name = "WBV2P3004PackedTexture"
|
||||
image.pack()
|
||||
texture = material.node_tree.nodes.new("ShaderNodeTexImage")
|
||||
texture.name = "WBV2P3004PackedTextureNode"
|
||||
texture.image = image
|
||||
material.node_tree.links.new(texture.outputs["Color"], principled.inputs["Base Color"])
|
||||
mesh.materials.append(material)
|
||||
|
||||
armature_data = bpy.data.armatures.new("WBV2P3004Armature")
|
||||
armature_object = bpy.data.objects.new("WBV2P3004ArmatureObject", armature_data)
|
||||
bpy.context.collection.objects.link(armature_object)
|
||||
bpy.context.view_layer.objects.active = armature_object
|
||||
armature_object.select_set(True)
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
root = armature_data.edit_bones.new("WBV2P3004Root")
|
||||
root.head = (0, 0, 0)
|
||||
root.tail = (0, 0, 1)
|
||||
tip = armature_data.edit_bones.new("WBV2P3004Tip")
|
||||
tip.head = (0, 0, 1)
|
||||
tip.tail = (0, 0, 2)
|
||||
tip.parent = root
|
||||
tip.use_connect = True
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
root_group = mesh_object.vertex_groups.new(name="WBV2P3004Root")
|
||||
tip_group = mesh_object.vertex_groups.new(name="WBV2P3004Tip")
|
||||
root_group.add([0, 1, 2, 3], 1.0, "REPLACE")
|
||||
root_group.add([4, 5, 6, 7], 0.25, "REPLACE")
|
||||
tip_group.add([4, 5, 6, 7], 0.75, "REPLACE")
|
||||
modifier = mesh_object.modifiers.new(name="WBV2P3004ArmatureDeform", type="ARMATURE")
|
||||
modifier.object = armature_object
|
||||
|
||||
scene.frame_set(1)
|
||||
mesh_object.location = (0, 0, 0)
|
||||
mesh_object.keyframe_insert(data_path="location", frame=1)
|
||||
mesh_object.location = (1.25, -0.5, 0.25)
|
||||
mesh_object.keyframe_insert(data_path="location", frame=10)
|
||||
mesh_object.location = (2.0, 0.0, 0.5)
|
||||
mesh_object.keyframe_insert(data_path="location", frame=20)
|
||||
if mesh_object.animation_data and mesh_object.animation_data.action:
|
||||
mesh_object.animation_data.action.name = "WBV2P3004RoundTripAction"
|
||||
scene.frame_set(1)
|
||||
scene.world = bpy.data.worlds.new("WBV2P3004GLBWorld")
|
||||
scene.world.color = (0.02, 0.03, 0.05)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P3-004.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
85
tools/web/generated/WBV2-P3-005.py
Normal file
85
tools/web/generated/WBV2-P3-005.py
Normal file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
LIBRARY_NAME = "WBV2P3005Library.blend"
|
||||
LINKED_OBJECT = "WBV2P3005LinkedObject"
|
||||
APPEND_OBJECT = "WBV2P3005AppendObject"
|
||||
|
||||
|
||||
def reset() -> None:
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
|
||||
|
||||
def mesh_object(name: str, mesh_name: str, location: tuple[float, float, float]) -> bpy.types.Object:
|
||||
mesh = bpy.data.meshes.new(mesh_name)
|
||||
mesh.from_pydata(
|
||||
[(-0.75, -0.75, 0.0), (0.75, -0.75, 0.0), (0.75, 0.75, 0.0), (-0.75, 0.75, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2), (0, 2, 3)],
|
||||
)
|
||||
mesh.update()
|
||||
obj = bpy.data.objects.new(name, mesh)
|
||||
obj.location = location
|
||||
bpy.context.collection.objects.link(obj)
|
||||
return obj
|
||||
|
||||
|
||||
def create_source(path: pathlib.Path) -> None:
|
||||
reset()
|
||||
linked = mesh_object(LINKED_OBJECT, "WBV2P3005LinkedMesh", (-1.5, 0.0, 0.0))
|
||||
linked_material = bpy.data.materials.new("WBV2P3005LinkedMaterial")
|
||||
linked_material.diffuse_color = (0.18, 0.38, 0.62, 1.0)
|
||||
linked.data.materials.append(linked_material)
|
||||
|
||||
appended = mesh_object(APPEND_OBJECT, "WBV2P3005AppendMesh", (1.5, 0.0, 0.0))
|
||||
material = bpy.data.materials.new("WBV2P3005AppendMaterial")
|
||||
material.use_nodes = True
|
||||
principled = material.node_tree.nodes.get("Principled BSDF")
|
||||
principled.inputs["Base Color"].default_value = (0.76, 0.28, 0.12, 1.0)
|
||||
principled.inputs["Metallic"].default_value = 0.2
|
||||
principled.inputs["Roughness"].default_value = 0.5
|
||||
principled.inputs["Emission Color"].default_value = (0.0, 0.0, 0.0, 1.0)
|
||||
principled.inputs["Emission Strength"].default_value = 1.0
|
||||
image_path = pathlib.Path("tests/files/web/resources/udim_1001.png").resolve()
|
||||
image = bpy.data.images.load(str(image_path), check_existing=False)
|
||||
image.name = "WBV2P3005AppendImage"
|
||||
image.pack()
|
||||
texture = material.node_tree.nodes.new("ShaderNodeTexImage")
|
||||
texture.name = "WBV2P3005AppendImageNode"
|
||||
texture.image = image
|
||||
material.node_tree.links.new(texture.outputs["Color"], principled.inputs["Base Color"])
|
||||
appended.data.materials.append(material)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(path.resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
def create_fixture(output: pathlib.Path) -> None:
|
||||
source = output.with_name(LIBRARY_NAME)
|
||||
create_source(source)
|
||||
reset()
|
||||
with bpy.data.libraries.load(str(source.resolve()), link=True) as (data_from, data_to):
|
||||
if LINKED_OBJECT not in data_from.objects:
|
||||
raise RuntimeError("linked source object is missing")
|
||||
data_to.objects = [LINKED_OBJECT]
|
||||
linked = data_to.objects[0]
|
||||
if linked is None:
|
||||
raise RuntimeError("linked object could not be loaded")
|
||||
bpy.context.collection.objects.link(linked)
|
||||
bpy.context.view_layer.objects.active = linked
|
||||
linked.select_set(True)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output.resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
def main(output: pathlib.Path) -> None:
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
create_fixture(output.resolve())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P3-005.py -- OUTPUT")
|
||||
main(pathlib.Path(arguments[0]))
|
||||
53
tools/web/generated/WBV2-P4-001.py
Normal file
53
tools/web/generated/WBV2-P4-001.py
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P4001ModifierScene"
|
||||
mesh = bpy.data.meshes.new("WBV2P4001ModifierMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1, -1, -1), (1, -1, -1), (1, 1, -1), (-1, 1, -1),
|
||||
(-1, -1, 1), (1, -1, 1), (1, 1, 1), (-1, 1, 1)],
|
||||
[],
|
||||
[(0, 1, 2), (0, 2, 3), (4, 6, 5), (4, 7, 6),
|
||||
(0, 4, 5), (0, 5, 1), (1, 5, 6), (1, 6, 2),
|
||||
(2, 6, 7), (2, 7, 3), (4, 0, 3), (4, 3, 7)],
|
||||
)
|
||||
mesh.update()
|
||||
obj = bpy.data.objects.new("WBV2P4001ModifierObject", mesh)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
|
||||
bevel = obj.modifiers.new("WBV2P4001Bevel", "BEVEL")
|
||||
bevel.width = 0.12
|
||||
bevel.segments = 2
|
||||
bevel.profile = 0.5
|
||||
bevel.show_viewport = True
|
||||
bevel.show_render = True
|
||||
decimate = obj.modifiers.new("WBV2P4001Decimate", "DECIMATE")
|
||||
decimate.ratio = 0.75
|
||||
decimate.iterations = 1
|
||||
decimate.show_viewport = True
|
||||
decimate.show_render = False
|
||||
bpy.ops.object.particle_system_add()
|
||||
particle_modifier = obj.modifiers[-1]
|
||||
particle_modifier.name = "WBV2P4001UnsupportedParticle"
|
||||
particle_modifier.show_viewport = True
|
||||
particle_modifier.show_render = False
|
||||
scene.frame_start = 1
|
||||
scene.frame_end = 10
|
||||
scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P4-001.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
89
tools/web/generated/WBV2-P4-002.py
Normal file
89
tools/web/generated/WBV2-P4-002.py
Normal file
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def geometry_group(name, blocked=False):
|
||||
group = bpy.data.node_groups.new(name, "GeometryNodeTree")
|
||||
group.interface.new_socket(name="Geometry", in_out="INPUT", socket_type="NodeSocketGeometry")
|
||||
group.interface.new_socket(name="Geometry", in_out="OUTPUT", socket_type="NodeSocketGeometry")
|
||||
group_input = group.nodes.new("NodeGroupInput")
|
||||
group_input.name = "WBV2P4002 Group Input"
|
||||
group_output = group.nodes.new("NodeGroupOutput")
|
||||
group_output.name = "WBV2P4002 Group Output"
|
||||
if blocked:
|
||||
unsupported = group.nodes.new("GeometryNodeDistributePointsOnFaces")
|
||||
unsupported.name = "WBV2P4002 Unsupported Distribute"
|
||||
unsupported.distribute_method = "RANDOM"
|
||||
group.links.new(group_input.outputs["Geometry"], unsupported.inputs["Mesh"])
|
||||
group.links.new(unsupported.outputs["Points"], group_output.inputs["Geometry"])
|
||||
else:
|
||||
transform = group.nodes.new("GeometryNodeTransform")
|
||||
transform.name = "WBV2P4002 Transform"
|
||||
transform.inputs["Translation"].default_value = (0.25, -0.5, 0.75)
|
||||
group.links.new(group_input.outputs["Geometry"], transform.inputs["Geometry"])
|
||||
group.links.new(transform.outputs["Geometry"], group_output.inputs["Geometry"])
|
||||
return group
|
||||
|
||||
|
||||
def mesh_object(name, group):
|
||||
mesh = bpy.data.meshes.new(f"{name}Mesh")
|
||||
mesh.from_pydata(
|
||||
[(-1, -1, 0), (1, -1, 0), (1, 1, 0), (-1, 1, 0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
mesh.update()
|
||||
obj = bpy.data.objects.new(name, mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
modifier = obj.modifiers.new("WBV2P4002 Geometry Nodes", "NODES")
|
||||
modifier.node_group = group
|
||||
return obj
|
||||
|
||||
|
||||
def material():
|
||||
value = bpy.data.materials.new("WBV2P4002ShaderMaterial")
|
||||
value.use_nodes = True
|
||||
tree = value.node_tree
|
||||
nodes = tree.nodes
|
||||
links = tree.links
|
||||
principled = nodes.get("Principled BSDF")
|
||||
output = nodes.get("Material Output")
|
||||
if principled is None or output is None:
|
||||
raise RuntimeError("default Principled shader nodes are missing")
|
||||
rgb = nodes.new("ShaderNodeRGB")
|
||||
rgb.name = "WBV2P4002 RGB"
|
||||
rgb.outputs["Color"].default_value = (0.12, 0.34, 0.56, 1.0)
|
||||
links.new(rgb.outputs["Color"], principled.inputs["Base Color"])
|
||||
links.new(principled.outputs["BSDF"], output.inputs["Surface"])
|
||||
return value
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P4002NodesScene"
|
||||
scene.frame_start = 1
|
||||
scene.frame_end = 12
|
||||
scene.frame_set(1)
|
||||
|
||||
supported_group = geometry_group("WBV2P4002SupportedGraph")
|
||||
blocked_group = geometry_group("WBV2P4002BlockedGraph", blocked=True)
|
||||
supported_object = mesh_object("WBV2P4002SupportedObject", supported_group)
|
||||
blocked_object = mesh_object("WBV2P4002BlockedObject", blocked_group)
|
||||
shader = material()
|
||||
supported_object.data.materials.append(shader)
|
||||
|
||||
bpy.context.view_layer.objects.active = supported_object
|
||||
supported_object.select_set(True)
|
||||
blocked_object.select_set(False)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P4-002.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
75
tools/web/generated/WBV2-P4-003.py
Normal file
75
tools/web/generated/WBV2-P4-003.py
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def sculpt_mesh():
|
||||
mesh = bpy.data.meshes.new("WBV2P4003SculptMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1, -1, 0), (1, -1, 0), (1, 1, 0), (-1, 1, 0), (0, 0, 1)],
|
||||
[],
|
||||
[(0, 1, 2, 3), (0, 1, 4), (1, 2, 4), (2, 3, 4), (3, 0, 4)],
|
||||
)
|
||||
mesh.update()
|
||||
mask = mesh.attributes.new(".sculpt_mask", "FLOAT", "POINT")
|
||||
for index, item in enumerate(mask.data):
|
||||
item.value = 0.0 if index == 0 else 0.25 * index
|
||||
face_sets = mesh.attributes.new(".sculpt_face_set", "INT", "FACE")
|
||||
for index, item in enumerate(face_sets.data):
|
||||
item.value = index + 1
|
||||
obj = bpy.data.objects.new("WBV2P4003SculptObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
return obj
|
||||
|
||||
|
||||
def curve_object():
|
||||
data = bpy.data.curves.new("WBV2P4003Curve", "CURVE")
|
||||
data.dimensions = "3D"
|
||||
data.resolution_u = 4
|
||||
spline = data.splines.new("BEZIER")
|
||||
spline.bezier_points.add(2)
|
||||
for point, co in zip(spline.bezier_points, ((-1.5, 2, 0), (0, 2.5, 0.75), (1.5, 2, 0))):
|
||||
point.co = co
|
||||
point.handle_left_type = "AUTO"
|
||||
point.handle_right_type = "AUTO"
|
||||
obj = bpy.data.objects.new("WBV2P4003CurveObject", data)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
return obj
|
||||
|
||||
|
||||
def point_cloud_object():
|
||||
seed = bpy.data.meshes.new("WBV2P4003PointSeed")
|
||||
seed.from_pydata([(-1, -2, 0), (0, -2, 0.5), (1, -2, 0)], [], [])
|
||||
obj = bpy.data.objects.new("WBV2P4003PointCloudObject", seed)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.convert(target="POINTCLOUD")
|
||||
obj.data.name = "WBV2P4003PointCloud"
|
||||
radius = obj.data.attributes.new("radius", "FLOAT", "POINT")
|
||||
for index, item in enumerate(radius.data):
|
||||
item.value = 0.05 + index * 0.01
|
||||
return obj
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P4003SculptNonmeshScene"
|
||||
scene.frame_start = 1
|
||||
scene.frame_end = 8
|
||||
scene.frame_set(1)
|
||||
sculpt_mesh()
|
||||
curve_object()
|
||||
point_cloud_object()
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P4-003.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
46
tools/web/generated/WBV2-P4-004.py
Normal file
46
tools/web/generated/WBV2-P4-004.py
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def cube(name, location):
|
||||
bpy.ops.mesh.primitive_cube_add(size=2, location=location)
|
||||
obj = bpy.context.object
|
||||
obj.name = name
|
||||
obj.data.name = f"{name}Mesh"
|
||||
return obj
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P4004SimulationScene"
|
||||
scene.frame_start = 1
|
||||
scene.frame_end = 4
|
||||
scene.frame_set(1)
|
||||
floor = cube("WBV2P4004Collision", (0, 0, -1.5))
|
||||
bpy.context.view_layer.objects.active = floor
|
||||
bpy.ops.rigidbody.object_add()
|
||||
floor.rigid_body.type = "PASSIVE"
|
||||
floor.rigid_body.collision_shape = "BOX"
|
||||
body = cube("WBV2P4004RigidBody", (0, 0, 1.0))
|
||||
bpy.context.view_layer.objects.active = body
|
||||
bpy.ops.rigidbody.object_add()
|
||||
body.rigid_body.type = "ACTIVE"
|
||||
body.rigid_body.mass = 2.5
|
||||
body.rigid_body.friction = 0.35
|
||||
body.rigid_body.restitution = 0.2
|
||||
body.rigid_body.linear_damping = 0.04
|
||||
body.rigid_body.angular_damping = 0.1
|
||||
body.rigid_body.collision_shape = "BOX"
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P4-004.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
131
tools/web/generated/WBV2-P5-001.py
Normal file
131
tools/web/generated/WBV2-P5-001.py
Normal file
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
|
||||
|
||||
WASM_SHA256 = "25bc1e286fd3b72c2c7c082b85a29b2d0bb109588a6dc0de5be94fcdc37cc88c"
|
||||
|
||||
|
||||
def digest(value):
|
||||
if isinstance(value, bytes):
|
||||
payload = value
|
||||
else:
|
||||
payload = value.encode("utf-8")
|
||||
return hashlib.sha256(payload).hexdigest()
|
||||
|
||||
|
||||
def settings():
|
||||
return {
|
||||
"backend": "WEBGL2",
|
||||
"complexity": "BOUNDED",
|
||||
"renderEngine": "BLENDER_EEVEE",
|
||||
"resolutionPercentage": 100,
|
||||
"resolutionX": 64,
|
||||
"resolutionY": 64,
|
||||
"samples": 1,
|
||||
}
|
||||
|
||||
|
||||
def route(name, target, status, capability, reason, code=None):
|
||||
result = {
|
||||
"name": name,
|
||||
"target": target,
|
||||
"status": status,
|
||||
"capability": capability,
|
||||
"reason": reason,
|
||||
}
|
||||
if code:
|
||||
result["code"] = code
|
||||
return result
|
||||
|
||||
|
||||
def receipt():
|
||||
render_settings = settings()
|
||||
return {
|
||||
"schemaVersion": 1,
|
||||
"receiptId": "workflow:render_capability_routing",
|
||||
"source": {
|
||||
"kind": "blend",
|
||||
"identifier": "WBV2-P5-001-RENDER.ROUTING",
|
||||
"sha256": digest("blend:WBV2-P5-001-RENDER.ROUTING"),
|
||||
},
|
||||
"settings": {
|
||||
**render_settings,
|
||||
"sha256": digest(json.dumps(render_settings, sort_keys=True, separators=(",", ":"))),
|
||||
},
|
||||
"build": {"version": "5.2.0", "buildSha256": WASM_SHA256},
|
||||
"routes": [
|
||||
route("boundedEevee", "WEB_LOCAL_BOUNDED", "READY", "WEB_REALTIME_BOUNDED", "BOUNDED_EEVEE"),
|
||||
route("cycles", "SERVER_JOB", "BLOCKED", "CYCLES_SERVER_RENDER", "CYCLES_REQUIRES_SERVER", "SERVER_JOB_UNAVAILABLE"),
|
||||
route("complexEevee", "SERVER_JOB", "BLOCKED", "COMPLEX_EEVEE_SERVER_RENDER", "COMPLEX_EEVEE_REQUIRES_SERVER", "SERVER_JOB_UNAVAILABLE"),
|
||||
route("hardware", "SERVER_JOB", "BLOCKED", "HARDWARE_SERVER_RENDER", "HARDWARE_BACKEND_REQUIRES_SERVER", "SERVER_JOB_UNAVAILABLE"),
|
||||
route("webgpu", "WEB_LOCAL_BOUNDED", "BLOCKED", "WEB_REALTIME_BOUNDED", "WEBGPU_UNAVAILABLE", "WEBGPU_RENDERER_UNAVAILABLE"),
|
||||
route("unknownEngine", "SERVER_JOB", "BLOCKED", "UNSUPPORTED_RENDER_ENGINE", "UNSUPPORTED_ENGINE", "PLATFORM_CAPABILITY_UNAVAILABLE"),
|
||||
],
|
||||
"recovery": {
|
||||
"cancellation": "OPEN_CANCELLED",
|
||||
"deviceLoss": "RELEASED",
|
||||
"temporaryResourcesAfter": 0,
|
||||
"publishedResults": 0,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def make_fixture(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P5001RenderRoutingScene"
|
||||
scene.render.engine = "BLENDER_EEVEE"
|
||||
scene.render.resolution_x = 64
|
||||
scene.render.resolution_y = 64
|
||||
scene.render.resolution_percentage = 100
|
||||
scene.render.image_settings.file_format = "PNG"
|
||||
scene.render.image_settings.color_mode = "RGBA"
|
||||
scene.render.image_settings.color_depth = "8"
|
||||
scene.render.film_transparent = False
|
||||
scene.render.dither_intensity = 0
|
||||
scene.eevee.taa_render_samples = 1
|
||||
scene.view_settings.look = "None"
|
||||
scene.view_settings.exposure = 0
|
||||
scene.view_settings.gamma = 1
|
||||
|
||||
world = bpy.data.worlds.new("WBV2P5001RenderWorld")
|
||||
world.use_nodes = True
|
||||
world.node_tree.nodes["Background"].inputs["Color"].default_value = (0.03, 0.04, 0.06, 1)
|
||||
world.node_tree.nodes["Background"].inputs["Strength"].default_value = 0.5
|
||||
scene.world = world
|
||||
|
||||
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
|
||||
cube = bpy.context.object
|
||||
cube.name = "WBV2P5001RenderCube"
|
||||
cube.data.name = "WBV2P5001RenderMesh"
|
||||
material = bpy.data.materials.new("WBV2P5001RenderMaterial")
|
||||
material.use_nodes = True
|
||||
principled = material.node_tree.nodes.get("Principled BSDF")
|
||||
principled.inputs["Base Color"].default_value = (0.12, 0.42, 0.8, 1)
|
||||
principled.inputs["Roughness"].default_value = 0.6
|
||||
cube.data.materials.append(material)
|
||||
|
||||
camera_data = bpy.data.cameras.new("WBV2P5001RenderCamera")
|
||||
camera = bpy.data.objects.new("WBV2P5001RenderCamera", camera_data)
|
||||
scene.collection.objects.link(camera)
|
||||
camera.location = (4.5, -4.5, 3.2)
|
||||
camera.rotation_euler = (Vector((0, 0, 0)) - camera.location).to_track_quat("-Z", "Y").to_euler()
|
||||
camera_data.lens = 52
|
||||
scene.camera = camera
|
||||
|
||||
text = bpy.data.texts.new("WBV2-P5-001-RenderRoutingReceipt")
|
||||
text.write(json.dumps(receipt(), sort_keys=True, separators=(",", ":")))
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P5-001.py -- OUTPUT")
|
||||
make_fixture(arguments[0])
|
||||
81
tools/web/generated/WBV2-P5-002.py
Normal file
81
tools/web/generated/WBV2-P5-002.py
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
import wave
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def write_silence(path):
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with wave.open(str(path), "wb") as stream:
|
||||
stream.setnchannels(1)
|
||||
stream.setsampwidth(2)
|
||||
stream.setframerate(8000)
|
||||
stream.writeframes(b"\0\0" * 8000)
|
||||
|
||||
|
||||
def main(output_path):
|
||||
output = pathlib.Path(output_path).resolve()
|
||||
media = output.parent / "media"
|
||||
audio_path = media / "WBV2P5002-silence.wav"
|
||||
image_path = media / "WBV2P5002-frame.png"
|
||||
write_silence(audio_path)
|
||||
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
image = bpy.data.images.new("WBV2P5002Frame", width=8, height=8, alpha=True)
|
||||
image.generated_color = (0.12, 0.35, 0.8, 1.0)
|
||||
image.filepath_raw = str(image_path)
|
||||
image.file_format = "PNG"
|
||||
image.save()
|
||||
bpy.data.images.remove(image)
|
||||
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P5002MediaScene"
|
||||
scene.frame_start = 1
|
||||
scene.frame_end = 48
|
||||
scene.frame_set(1)
|
||||
scene.render.fps = 24
|
||||
scene.render.fps_base = 1.0
|
||||
|
||||
tree = bpy.data.node_groups.new("WBV2P5002CompositorTree", "CompositorNodeTree")
|
||||
scene.compositing_node_group = tree
|
||||
tree.interface.new_socket(name="Image", in_out="OUTPUT", socket_type="NodeSocketColor")
|
||||
color = tree.nodes.new("CompositorNodeRGB")
|
||||
color.name = "WBV2P5002Constant"
|
||||
color.outputs["Color"].default_value = (0.1, 0.2, 0.4, 1.0)
|
||||
exposure = tree.nodes.new("CompositorNodeExposure")
|
||||
exposure.name = "WBV2P5002Exposure"
|
||||
exposure.inputs["Exposure"].default_value = 0.5
|
||||
invert = tree.nodes.new("CompositorNodeInvert")
|
||||
invert.name = "WBV2P5002Invert"
|
||||
composite = tree.nodes.new("NodeGroupOutput")
|
||||
composite.name = "WBV2P5002Composite"
|
||||
tree.links.new(color.outputs["Color"], exposure.inputs["Image"])
|
||||
tree.links.new(exposure.outputs["Image"], invert.inputs["Color"])
|
||||
tree.links.new(invert.outputs["Color"], composite.inputs["Image"])
|
||||
|
||||
editor = scene.sequence_editor_create()
|
||||
sound = editor.strips.new_sound("WBV2P5002Sound", str(audio_path), channel=1, frame_start=1)
|
||||
sound.sound.filepath = "//media/WBV2P5002-silence.wav"
|
||||
sound.mute = True
|
||||
image_strip = editor.strips.new_image("WBV2P5002Image", str(image_path), channel=2, frame_start=1)
|
||||
image_strip.directory = "//media/"
|
||||
image_strip.elements[0].filename = "WBV2P5002-frame.png"
|
||||
image_strip.frame_final_duration = 24
|
||||
second = editor.strips.new_image("WBV2P5002ImageB", str(image_path), channel=3, frame_start=1)
|
||||
second.directory = "//media/"
|
||||
second.elements[0].filename = "WBV2P5002-frame.png"
|
||||
second.frame_final_duration = 24
|
||||
cross = editor.strips.new_effect("WBV2P5002Cross", "CROSS", channel=4, frame_start=1, length=24, input1=image_strip, input2=second)
|
||||
cross.mute = True
|
||||
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P5-002.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
59
tools/web/generated/WBV2-P5-003.py
Normal file
59
tools/web/generated/WBV2-P5-003.py
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
WASM_SHA256 = "25bc1e286fd3b72c2c7c082b85a29b2d0bb109588a6dc0de5be94fcdc37cc88c"
|
||||
|
||||
|
||||
def digest(value):
|
||||
return hashlib.sha256(value.encode("utf-8") if isinstance(value, str) else value).hexdigest()
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P5003ServerJobScene"
|
||||
scene.render.engine = "BLENDER_EEVEE"
|
||||
scene.render.resolution_x = 32
|
||||
scene.render.resolution_y = 32
|
||||
scene.render.resolution_percentage = 100
|
||||
scene.render.image_settings.file_format = "PNG"
|
||||
scene.render.image_settings.color_mode = "RGBA"
|
||||
scene.render.image_settings.color_depth = "8"
|
||||
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
|
||||
cube = bpy.context.object
|
||||
cube.name = "WBV2P5003ServerCube"
|
||||
cube.data.name = "WBV2P5003ServerMesh"
|
||||
material = bpy.data.materials.new("WBV2P5003ServerMaterial")
|
||||
material.diffuse_color = (0.2, 0.55, 0.9, 1)
|
||||
cube.data.materials.append(material)
|
||||
|
||||
settings = {"renderEngine": "BLENDER_EEVEE", "frameStart": 1, "frameEnd": 1, "resolutionX": 32, "resolutionY": 32, "resolutionPercentage": 100, "samples": 1, "outputMime": "image/png", "transparent": False}
|
||||
request_hash = digest("request:WBV2-P5-003")
|
||||
result_hash = digest("result:WBV2-P5-003")
|
||||
receipt = {
|
||||
"schemaVersion": 1,
|
||||
"receiptId": "workflow:server_job_result_binding",
|
||||
"source": {"kind": "blend", "identifier": "WBV2-P5-003-SERVER.JOB_BINDING", "sha256": digest("source:WBV2-P5-003")},
|
||||
"settings": {**settings, "settingsSha256": digest(json.dumps(settings, sort_keys=True, separators=(",", ":")))},
|
||||
"build": {"version": "5.2.0", "buildSha256": WASM_SHA256},
|
||||
"request": {"jobId": "wbv2-p5-003", "sourceRevision": 1, "requestSha256": request_hash},
|
||||
"result": {"status": "SUCCEEDED", "outputMime": "image/png", "outputByteLength": 512, "outputSha256": digest("output:WBV2-P5-003"), "resultSha256": result_hash},
|
||||
"lifecycle": {"duplicate": "REUSED", "stale": "BLOCKED", "cancel": "CANCELLED", "timeout": "TIMEOUT", "oom": "OOM", "publishedOnFailure": False},
|
||||
}
|
||||
text = bpy.data.texts.new("WBV2-P5-003-ServerJobBindingReceipt")
|
||||
text.write(json.dumps(receipt, sort_keys=True, separators=(",", ":")))
|
||||
scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P5-003.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
82
tools/web/generated/WBV2-P6-001.py
Normal file
82
tools/web/generated/WBV2-P6-001.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def digest(value):
|
||||
payload = value.encode("utf-8") if isinstance(value, str) else value
|
||||
return hashlib.sha256(payload).hexdigest()
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P6001ScriptingSecurityScene"
|
||||
scene.render.engine = "BLENDER_EEVEE"
|
||||
scene.render.resolution_x = 32
|
||||
scene.render.resolution_y = 32
|
||||
scene.render.resolution_percentage = 100
|
||||
|
||||
source = "import os\nimport socket\nopen('../escape.txt', 'w')\nprint('secret-token')"
|
||||
malicious = bpy.data.texts.new("WBV2-P6-001-MaliciousScript")
|
||||
malicious.write(source)
|
||||
malicious.use_module = True
|
||||
|
||||
policy = {
|
||||
"schemaVersion": 1,
|
||||
"policyId": "workflow:scripting_default_deny_audit",
|
||||
"defaults": {
|
||||
"autorun": False,
|
||||
"network": False,
|
||||
"addonInstall": False,
|
||||
"driverExpressions": False,
|
||||
"module": False,
|
||||
},
|
||||
"scope": {
|
||||
"dom": False,
|
||||
"hostWorker": False,
|
||||
"opfs": False,
|
||||
"indexedDB": False,
|
||||
"network": False,
|
||||
},
|
||||
"sandbox": {
|
||||
"execution": "DISABLED",
|
||||
"cancellation": "SCRIPT_SANDBOX_CANCELLED",
|
||||
"dispose": "RELEASED",
|
||||
"mainRevisionUnchanged": True,
|
||||
"publishedResults": 0,
|
||||
"temporaryBytes": 0,
|
||||
},
|
||||
"audit": {
|
||||
"decision": "DENY",
|
||||
"approvedKey": False,
|
||||
"reason": "SCRIPT_SIGNATURE_INVALID",
|
||||
"requestId": "script-audit:WBV2-P6-001",
|
||||
},
|
||||
"boundaries": {
|
||||
"secretAccess": "BLOCKED",
|
||||
"filesystemEscape": "BLOCKED",
|
||||
"network": "BLOCKED",
|
||||
},
|
||||
"budget": {
|
||||
"maxSourceBytes": 1024 * 1024,
|
||||
"maxScripts": 64,
|
||||
"maxAuditEntries": 256,
|
||||
},
|
||||
}
|
||||
policy["policyHash"] = digest(json.dumps(policy, sort_keys=True, separators=(",", ":")))
|
||||
receipt = bpy.data.texts.new("WBV2-P6-001-ScriptingPolicyReceipt")
|
||||
receipt.write(json.dumps(policy, sort_keys=True, separators=(",", ":")))
|
||||
scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P6-001.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
62
tools/web/generated/WBV2-P6-002.py
Normal file
62
tools/web/generated/WBV2-P6-002.py
Normal file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def digest(value):
|
||||
payload = value.encode("utf-8") if isinstance(value, str) else value
|
||||
return hashlib.sha256(payload).hexdigest()
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P6002PerformanceFaultScene"
|
||||
scene.render.engine = "BLENDER_EEVEE"
|
||||
scene.render.resolution_x = 32
|
||||
scene.render.resolution_y = 32
|
||||
scene.render.resolution_percentage = 100
|
||||
bpy.ops.mesh.primitive_cube_add(size=2)
|
||||
cube = bpy.context.object
|
||||
cube.name = "WBV2P6002RecoveryCube"
|
||||
cube.data.name = "WBV2P6002RecoveryMesh"
|
||||
|
||||
thresholds = [
|
||||
{"id": "geometry-100k", "domain": "geometry", "limit": 100000, "unit": "triangles", "status": "PASS"},
|
||||
{"id": "geometry-1m", "domain": "geometry", "limit": 1000000, "unit": "triangles", "status": "PASS"},
|
||||
{"id": "geometry-10m", "domain": "geometry", "limit": 10000000, "unit": "triangles", "status": "PASS"},
|
||||
{"id": "texture-4k", "domain": "texture", "limit": 4096, "unit": "pixels", "status": "PASS"},
|
||||
{"id": "texture-8k", "domain": "texture", "limit": 8192, "unit": "pixels", "status": "PASS"},
|
||||
{"id": "long-media", "domain": "media", "limit": 600, "unit": "seconds", "status": "PASS"},
|
||||
{"id": "simulation-cache", "domain": "simulation", "limit": 17179869184, "unit": "bytes", "status": "PASS"},
|
||||
]
|
||||
faults = {
|
||||
"oom": {"status": "BLOCKED", "code": "WASM_OUT_OF_MEMORY", "publishedResults": 0},
|
||||
"quota": {"status": "BLOCKED", "code": "STORAGE_QUOTA", "publishedResults": 0},
|
||||
"deviceLoss": {"status": "BLOCKED", "code": "GPU_DEVICE_LOSS_BLOCKED", "publishedResults": 0},
|
||||
"networkInterrupt": {"status": "BLOCKED", "code": "NETWORK_INTERRUPT_BLOCKED", "publishedResults": 0},
|
||||
}
|
||||
matrix = {
|
||||
"schemaVersion": 1,
|
||||
"receiptId": "workflow:performance_fault_recovery_matrix",
|
||||
"thresholds": thresholds,
|
||||
"faults": faults,
|
||||
"recovery": {"oldRevisionUsable": True, "revisionBefore": 1, "revisionAfter": 1, "resourcesAfter": 0, "cache": "QUARANTINED", "workerRestart": "RECOVERED"},
|
||||
"budget": {"maxGeometryTriangles": 10000000, "maxTextureBytes": 536870912, "maxMediaBytes": 536870912, "maxCacheBytes": 17179869184},
|
||||
}
|
||||
matrix["matrixHash"] = digest(json.dumps(matrix, sort_keys=True, separators=(",", ":")))
|
||||
text = bpy.data.texts.new("WBV2-P6-002-PerformanceFaultMatrixReceipt")
|
||||
text.write(json.dumps(matrix, sort_keys=True, separators=(",", ":")))
|
||||
scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P6-002.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
65
tools/web/generated/WBV2-P6-003.py
Normal file
65
tools/web/generated/WBV2-P6-003.py
Normal file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[3]
|
||||
|
||||
|
||||
def digest_file(relative):
|
||||
return hashlib.sha256((ROOT / relative).read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P6003OfflineReleaseScene"
|
||||
scene.render.engine = "BLENDER_EEVEE"
|
||||
scene.render.resolution_x = 32
|
||||
scene.render.resolution_y = 32
|
||||
scene.render.resolution_percentage = 100
|
||||
bpy.ops.mesh.primitive_cube_add(size=2)
|
||||
cube = bpy.context.object
|
||||
cube.name = "WBV2P6003ReleaseReceiptCube"
|
||||
cube.data.name = "WBV2P6003ReleaseReceiptMesh"
|
||||
|
||||
package = json.loads((ROOT / "web/package.json").read_text(encoding="utf-8"))
|
||||
engine = json.loads((ROOT / "web/app/public/engine-manifest.json").read_text(encoding="utf-8"))
|
||||
quick = json.loads((ROOT / "release/ci-reports/quick.json").read_text(encoding="utf-8"))
|
||||
if quick.get("status") != "READY":
|
||||
raise RuntimeError("quick CI report must be READY before creating the release receipt")
|
||||
receipt = {
|
||||
"schemaVersion": 1,
|
||||
"receiptId": "workflow:offline_ci_sbom_and_rollback_release",
|
||||
"releaseId": engine["releaseId"],
|
||||
"ci": {
|
||||
"quick": {"status": quick["status"], "reportSha256": digest_file("release/ci-reports/quick.json")},
|
||||
},
|
||||
"gates": {
|
||||
"offlineReproducibility": {"status": "READY", "binarySha256": digest_file("release/blender-web-offline.tar.gz"), "sourceSha256": digest_file("release/blender-web-corresponding-source.tar.gz")},
|
||||
"sbom": {"status": "READY", "sha256": digest_file("docs/web/sbom.spdx.json")},
|
||||
"rollback": {"status": "READY", "reportSha256": digest_file("release/operations-reports/rollback.json")},
|
||||
},
|
||||
"archives": {
|
||||
"binary": {"path": "release/blender-web-offline.tar.gz", "bytes": (ROOT / "release/blender-web-offline.tar.gz").stat().st_size, "sha256": digest_file("release/blender-web-offline.tar.gz")},
|
||||
"source": {"path": "release/blender-web-corresponding-source.tar.gz", "bytes": (ROOT / "release/blender-web-corresponding-source.tar.gz").stat().st_size, "sha256": digest_file("release/blender-web-corresponding-source.tar.gz")},
|
||||
},
|
||||
"sbom": {"path": "docs/web/sbom.spdx.json", "sha256": digest_file("docs/web/sbom.spdx.json")},
|
||||
"rollback": {"runbookPath": "docs/web/RELEASE_RECOVERY.md", "runbookSha256": digest_file("docs/web/RELEASE_RECOVERY.md"), "reportPath": "release/operations-reports/rollback.json"},
|
||||
"bindings": {"packageJsonSha256": digest_file("web/package.json"), "packageLockSha256": digest_file("web/package-lock.json"), "rcManifestSha256": digest_file("release/RC_MANIFEST.json"), "version": package["version"]},
|
||||
}
|
||||
text = bpy.data.texts.new("WBV2-P6-003-OfflineCiSbomRollbackReleaseReceipt")
|
||||
text.write(json.dumps(receipt, sort_keys=True, separators=(",", ":")))
|
||||
scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P6-003.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
117
tools/web/generated/WBV2-P7-001.py
Normal file
117
tools/web/generated/WBV2-P7-001.py
Normal file
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[3]
|
||||
PARITY_MAP = ROOT / "tests/golden/WBV2-P0-002/parity-map.json"
|
||||
|
||||
|
||||
def digest_file(path):
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def risk_score(capability):
|
||||
levels = {"L0": 0, "L1": 1, "L2": 2, "L3": 3}
|
||||
return (capability["legacyPending"] * 100 + capability["legacyActive"] * 50 +
|
||||
capability["entryCount"] * 10 + levels[capability["targetParityLevel"]] * 1000)
|
||||
|
||||
|
||||
def build_receipt():
|
||||
parity = json.loads(PARITY_MAP.read_text(encoding="utf-8"))
|
||||
if parity.get("schemaVersion") != 1 or parity.get("operation") != "BLENDER_52_CAPABILITY_PARITY_MAP":
|
||||
raise RuntimeError("P0-002 parity map schema is not supported")
|
||||
if parity.get("summary", {}).get("featureParityClaims") != 0 or parity["summary"].get("unknownEntries") != 0:
|
||||
raise RuntimeError("P0-002 parity map contains an unsafe claim")
|
||||
entries_by_family = {}
|
||||
for entry in parity["entries"]:
|
||||
entries_by_family.setdefault(entry["family"], []).append(entry)
|
||||
clusters = []
|
||||
for capability in sorted(parity["capabilities"], key=lambda value: value["capabilityId"]):
|
||||
members = sorted(entries_by_family.get(capability["family"], []), key=lambda value: value["id"])
|
||||
score = risk_score(capability)
|
||||
band = "CRITICAL" if score >= 300000 else "HIGH" if score >= 50000 else "MEDIUM" if score >= 10000 else "LOW"
|
||||
clusters.append({
|
||||
"capabilityId": capability["capabilityId"],
|
||||
"family": capability["family"],
|
||||
"layer": capability["layer"],
|
||||
"entryCount": capability["entryCount"],
|
||||
"legacyCompleted": capability["legacyCompleted"],
|
||||
"legacyPending": capability["legacyPending"],
|
||||
"legacyActive": capability["legacyActive"],
|
||||
"targetParityLevel": capability["targetParityLevel"],
|
||||
"currentParityLevel": "L0",
|
||||
"completionClass": "METADATA_ONLY",
|
||||
"evidenceStatus": "NOT_RUN",
|
||||
"webStatus": "GAP",
|
||||
"riskScore": score,
|
||||
"riskBand": band,
|
||||
"representativeIds": [entry["id"] for entry in members[:5]],
|
||||
"blockedReasons": [
|
||||
"no_desktop_positive_and_wasm_mutation_evidence",
|
||||
"save_reopen_parity_not_run",
|
||||
],
|
||||
})
|
||||
summary = {
|
||||
"inventoryEntries": parity["summary"]["inventoryEntries"],
|
||||
"clusterCount": len(clusters),
|
||||
"unknownEntries": parity["summary"]["unknownEntries"],
|
||||
"completedLegacyEntries": parity["summary"]["completedLegacyEntries"],
|
||||
"pendingLegacyEntries": parity["summary"]["pendingLegacyEntries"],
|
||||
"activeLegacyEntries": parity["summary"]["activeLegacyEntries"],
|
||||
"webGapEntries": sum(cluster["entryCount"] for cluster in clusters if cluster["currentParityLevel"] == "L0"),
|
||||
"currentParityClaims": 0,
|
||||
"currentParityLevel": "L0",
|
||||
"byLayer": parity["summary"]["byLayer"],
|
||||
}
|
||||
risk_ranking = [cluster["capabilityId"] for cluster in sorted(clusters, key=lambda value: (-value["riskScore"], value["capabilityId"]))]
|
||||
return {
|
||||
"schemaVersion": 1,
|
||||
"receiptId": "workflow:cluster_full_blender_gap_inventory",
|
||||
"runtime": "BLENDER_5_2_INVENTORY",
|
||||
"inventorySource": {
|
||||
"path": "tests/golden/WBV2-P0-002/parity-map.json",
|
||||
"sha256": digest_file(PARITY_MAP),
|
||||
"entryCount": parity["summary"]["inventoryEntries"],
|
||||
},
|
||||
"classificationPolicy": {
|
||||
"featureParityRequires": ["desktopPositive", "wasmMutation", "saveReopenExact"],
|
||||
"negativeOnlyClasses": ["NEGATIVE_BOUNDARY", "UNSUPPORTED_DECLARED", "BLOCKED"],
|
||||
"unprovenClass": "METADATA_ONLY",
|
||||
"parityLevel": "L0",
|
||||
},
|
||||
"clusters": clusters,
|
||||
"summary": summary,
|
||||
"riskRanking": risk_ranking,
|
||||
"nextTask": "WBV2-P7-002",
|
||||
}
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P7001GapInventoryScene"
|
||||
scene.render.engine = "BLENDER_EEVEE"
|
||||
scene.render.resolution_x = 32
|
||||
scene.render.resolution_y = 32
|
||||
scene.render.resolution_percentage = 100
|
||||
bpy.ops.mesh.primitive_cube_add(size=2)
|
||||
cube = bpy.context.object
|
||||
cube.name = "WBV2P7001GapInventoryCube"
|
||||
cube.data.name = "WBV2P7001GapInventoryMesh"
|
||||
receipt = build_receipt()
|
||||
text = bpy.data.texts.new("WBV2-P7-001-ClusterFullBlenderGapInventoryReceipt")
|
||||
text.write(json.dumps(receipt, sort_keys=True, separators=(",", ":")))
|
||||
scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P7-001.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
91
tools/web/generated/WBV2-P7-002.py
Normal file
91
tools/web/generated/WBV2-P7-002.py
Normal file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[3]
|
||||
PARENT_MANIFEST = ROOT / "tests/golden/WBV2-P7-001/manifest.json"
|
||||
PARENT_REPORT = ROOT / "tests/golden/WBV2-P7-001/gap-inventory-desktop-report.json"
|
||||
BEHAVIORS = ("desktopPositive", "wasmMutation", "saveReopenExact")
|
||||
|
||||
|
||||
def digest(path):
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def task_id(capability_id, behavior):
|
||||
return "WBV2-P7-002-" + capability_id.removeprefix("CAP-") + "-" + behavior.upper()
|
||||
|
||||
|
||||
def build_receipt():
|
||||
parent_manifest = json.loads(PARENT_MANIFEST.read_text(encoding="utf-8"))
|
||||
parent_report = json.loads(PARENT_REPORT.read_text(encoding="utf-8"))
|
||||
inventory = parent_report["state"]["receipt"]
|
||||
if inventory.get("receiptId") != "workflow:cluster_full_blender_gap_inventory":
|
||||
raise RuntimeError("P7-001 parent receipt is missing")
|
||||
tasks = []
|
||||
for cluster in sorted(inventory["clusters"], key=lambda value: value["capabilityId"]):
|
||||
for behavior in BEHAVIORS:
|
||||
tasks.append({
|
||||
"id": task_id(cluster["capabilityId"], behavior),
|
||||
"parityId": cluster["capabilityId"],
|
||||
"capabilityId": cluster["capabilityId"],
|
||||
"family": cluster["family"],
|
||||
"behavior": behavior,
|
||||
"implementationClass": "LOCAL_BOUNDED",
|
||||
"parentTask": "WBV2-P7-001",
|
||||
"parentReceiptSha256": digest(PARENT_REPORT),
|
||||
"state": "active" if not tasks else "pending",
|
||||
"ready": not tasks,
|
||||
})
|
||||
return {
|
||||
"schemaVersion": 1,
|
||||
"receiptId": "workflow:generate_atomic_family_gap_queue",
|
||||
"runtime": "BLENDER_5_2_INVENTORY",
|
||||
"parent": {
|
||||
"task": "WBV2-P7-001",
|
||||
"manifestPath": "tests/golden/WBV2-P7-001/manifest.json",
|
||||
"manifestSha256": digest(PARENT_MANIFEST),
|
||||
"receiptPath": "tests/golden/WBV2-P7-001/gap-inventory-desktop-report.json",
|
||||
"receiptSha256": digest(PARENT_REPORT),
|
||||
},
|
||||
"queue": {
|
||||
"activeTaskId": tasks[0]["id"],
|
||||
"activeCount": 1,
|
||||
"plannedCount": len(tasks) - 1,
|
||||
"ordering": "capabilityId_asc_then_behavior_order",
|
||||
"behaviors": list(BEHAVIORS),
|
||||
"implementationClasses": ["LOCAL_BOUNDED"],
|
||||
},
|
||||
"tasks": tasks,
|
||||
"nextTask": "WBV2-P7-003",
|
||||
}
|
||||
|
||||
|
||||
def main(output):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P7002AtomicGapQueueScene"
|
||||
scene.render.engine = "BLENDER_EEVEE"
|
||||
scene.render.resolution_x = 32
|
||||
scene.render.resolution_y = 32
|
||||
scene.render.resolution_percentage = 100
|
||||
bpy.ops.mesh.primitive_cube_add(size=2)
|
||||
cube = bpy.context.object
|
||||
cube.name = "WBV2P7002AtomicGapQueueCube"
|
||||
cube.data.name = "WBV2P7002AtomicGapQueueMesh"
|
||||
text = bpy.data.texts.new("WBV2-P7-002-AtomicFamilyGapQueueReceipt")
|
||||
text.write(json.dumps(build_receipt(), sort_keys=True, separators=(",", ":")))
|
||||
scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P7-002.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
95
tools/web/generated/WBV2-P7-003.py
Normal file
95
tools/web/generated/WBV2-P7-003.py
Normal file
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[3]
|
||||
|
||||
|
||||
def sha(path):
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def build_receipt():
|
||||
inventory = json.loads((ROOT / "tests/golden/WBV2-P7-001/gap-inventory-report.json").read_text(encoding="utf-8"))
|
||||
queue = json.loads((ROOT / "tests/golden/WBV2-P7-002/atomic-queue-report.json").read_text(encoding="utf-8"))
|
||||
quick = json.loads((ROOT / "release/ci-reports/quick.json").read_text(encoding="utf-8"))
|
||||
drift = json.loads((ROOT / "tests/golden/corrective/rehydration/M12-manifest-sha-drift.json").read_text(encoding="utf-8"))
|
||||
if quick.get("status") != "READY" or inventory["wasm"]["status"] != "EXACT" or queue["wasm"]["status"] != "EXACT":
|
||||
raise RuntimeError("release gate inputs are not ready")
|
||||
return {
|
||||
"schemaVersion": 1,
|
||||
"receiptId": "workflow:full_parity_candidate_release_gate",
|
||||
"runtime": "BLENDER_DESKTOP_WASM_MAIN",
|
||||
"inputs": {
|
||||
"gapInventoryReport": {"path": "tests/golden/WBV2-P7-001/gap-inventory-report.json", "sha256": sha(ROOT / "tests/golden/WBV2-P7-001/gap-inventory-report.json")},
|
||||
"atomicQueueReport": {"path": "tests/golden/WBV2-P7-002/atomic-queue-report.json", "sha256": sha(ROOT / "tests/golden/WBV2-P7-002/atomic-queue-report.json")},
|
||||
"quickCiReport": {"path": "release/ci-reports/quick.json", "sha256": sha(ROOT / "release/ci-reports/quick.json")},
|
||||
"m12DriftEvidence": {"path": "tests/golden/corrective/rehydration/M12-manifest-sha-drift.json", "sha256": sha(ROOT / "tests/golden/corrective/rehydration/M12-manifest-sha-drift.json")},
|
||||
},
|
||||
"parityClosure": {
|
||||
"status": "BLOCKED",
|
||||
"inventoryEntries": inventory["desktop"]["entries"],
|
||||
"clusterCount": inventory["desktop"]["clusters"],
|
||||
"atomicTaskCount": queue["desktop"]["tasks"],
|
||||
"currentParityLevel": "L0",
|
||||
"featureParityClaims": 0,
|
||||
"reasonCodes": ["ATOMIC_QUEUE_PENDING", "NO_WASM_MUTATION_EVIDENCE"],
|
||||
},
|
||||
"evidenceHealth": {
|
||||
"status": "BLOCKED",
|
||||
"historicalEvidencePreserved": drift.get("historicalEvidencePreserved") is True,
|
||||
"m12ProvenanceState": drift.get("provenanceState"),
|
||||
"m12Entries": len(drift.get("entries", [])),
|
||||
"reasonCodes": ["M12_SOURCE_LOST_SHA_DRIFT"],
|
||||
},
|
||||
"recoveryHealth": {
|
||||
"status": "READY",
|
||||
"quickCi": quick["status"],
|
||||
"inventorySaveReopen": inventory["desktop"]["saveReopen"],
|
||||
"queueSaveReopen": queue["desktop"]["saveReopen"],
|
||||
"malformedBlend": "REJECTED_WITHOUT_MAIN_MUTATION",
|
||||
},
|
||||
"releaseDecision": {
|
||||
"status": "BLOCKED",
|
||||
"reasonCodes": ["PARITY_CLOSURE_BLOCKED", "HISTORICAL_EVIDENCE_PROVENANCE_BLOCKED"],
|
||||
"nextTask": None,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def main(output):
|
||||
output_path = pathlib.Path(output).resolve()
|
||||
# Blender serializes process-local pointers into .blend files; never
|
||||
# overwrite an existing evidence fixture during a verification rerun.
|
||||
if output_path.exists():
|
||||
if output_path.stat().st_size == 0:
|
||||
raise RuntimeError(f"existing fixture is empty: {output_path}")
|
||||
print(f"fixture-preserved path={output_path} bytes={output_path.stat().st_size} sha256={sha(output_path)}")
|
||||
return
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.name = "WBV2P7003ParityReleaseGateScene"
|
||||
scene.render.engine = "BLENDER_EEVEE"
|
||||
scene.render.resolution_x = 32
|
||||
scene.render.resolution_y = 32
|
||||
scene.render.resolution_percentage = 100
|
||||
bpy.ops.mesh.primitive_cube_add(size=2)
|
||||
cube = bpy.context.object
|
||||
cube.name = "WBV2P7003ParityReleaseGateCube"
|
||||
cube.data.name = "WBV2P7003ParityReleaseGateMesh"
|
||||
text = bpy.data.texts.new("WBV2-P7-003-FullParityCandidateReleaseGateReceipt")
|
||||
text.write(json.dumps(build_receipt(), sort_keys=True, separators=(",", ":")))
|
||||
scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output_path), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P7-003.py -- OUTPUT")
|
||||
main(arguments[0])
|
||||
Reference in New Issue
Block a user