@hraness/slopcamera 3.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE.md +60 -0
- package/PRIVACY.md +46 -0
- package/README.md +311 -0
- package/SECURITY.md +21 -0
- package/apps/desktop/README.md +966 -0
- package/apps/desktop/analysis/protocol.ts +223 -0
- package/apps/desktop/application/capability-binding.ts +594 -0
- package/apps/desktop/application/context.ts +97 -0
- package/apps/desktop/application/creative-iteration.ts +1385 -0
- package/apps/desktop/application/default-registry.ts +146 -0
- package/apps/desktop/application/directing-contract.ts +218 -0
- package/apps/desktop/application/directing-plan.ts +178 -0
- package/apps/desktop/application/errors.ts +18 -0
- package/apps/desktop/application/gateway-port.ts +867 -0
- package/apps/desktop/application/html-overlay-browser-runtime.ts +687 -0
- package/apps/desktop/application/html-overlay-integrity.ts +337 -0
- package/apps/desktop/application/html-overlay-renderer.ts +45 -0
- package/apps/desktop/application/index.ts +13 -0
- package/apps/desktop/application/operation-completion-checkpoint.ts +333 -0
- package/apps/desktop/application/operation-effects.ts +94 -0
- package/apps/desktop/application/operation.ts +224 -0
- package/apps/desktop/application/operations/analysis/faces.ts +244 -0
- package/apps/desktop/application/operations/analysis/music.ts +248 -0
- package/apps/desktop/application/operations/analysis/project-auto-zooms.ts +302 -0
- package/apps/desktop/application/operations/analysis/project-inactivity.ts +381 -0
- package/apps/desktop/application/operations/analysis/scenes.ts +272 -0
- package/apps/desktop/application/operations/analysis/shared.ts +202 -0
- package/apps/desktop/application/operations/derive/edit-batch.ts +967 -0
- package/apps/desktop/application/operations/derive/follow-faces.ts +454 -0
- package/apps/desktop/application/operations/edit/bind-candidate-revision.ts +91 -0
- package/apps/desktop/application/operations/edit/create-candidate-revision.ts +377 -0
- package/apps/desktop/application/operations/edit/create-revision.ts +255 -0
- package/apps/desktop/application/operations/edit/freeze-revision.ts +182 -0
- package/apps/desktop/application/operations/gateway/image.ts +41 -0
- package/apps/desktop/application/operations/gateway/index.ts +17 -0
- package/apps/desktop/application/operations/gateway/shared.ts +122 -0
- package/apps/desktop/application/operations/gateway/speech.ts +41 -0
- package/apps/desktop/application/operations/gateway/transcription.ts +42 -0
- package/apps/desktop/application/operations/gateway/video.ts +41 -0
- package/apps/desktop/application/operations/index.ts +31 -0
- package/apps/desktop/application/operations/iteration/create-candidate.ts +240 -0
- package/apps/desktop/application/operations/iteration/create-matrix.ts +117 -0
- package/apps/desktop/application/operations/iteration/select.ts +155 -0
- package/apps/desktop/application/operations/iteration/shared.ts +250 -0
- package/apps/desktop/application/operations/media/audio-effects.ts +376 -0
- package/apps/desktop/application/operations/media/binding.ts +79 -0
- package/apps/desktop/application/operations/media/capabilities.ts +112 -0
- package/apps/desktop/application/operations/media/color-grade.ts +343 -0
- package/apps/desktop/application/operations/media/html-overlay.ts +802 -0
- package/apps/desktop/application/operations/media/index.ts +8 -0
- package/apps/desktop/application/operations/media/ingest.ts +303 -0
- package/apps/desktop/application/operations/media/overlay.ts +729 -0
- package/apps/desktop/application/operations/media/shared.ts +845 -0
- package/apps/desktop/application/operations/media/transform-platform.ts +120 -0
- package/apps/desktop/application/operations/project/commit-edits.ts +2054 -0
- package/apps/desktop/application/operations/project/promote-selection.ts +495 -0
- package/apps/desktop/application/operations/project/snapshot.ts +95 -0
- package/apps/desktop/application/operations/recording/index.ts +5 -0
- package/apps/desktop/application/operations/recording/pause.ts +34 -0
- package/apps/desktop/application/operations/recording/resume.ts +34 -0
- package/apps/desktop/application/operations/recording/shared.ts +199 -0
- package/apps/desktop/application/operations/recording/start.ts +169 -0
- package/apps/desktop/application/operations/recording/stop.ts +34 -0
- package/apps/desktop/application/operations/render/bind-candidate-output.ts +324 -0
- package/apps/desktop/application/operations/render/materialize-selection.ts +322 -0
- package/apps/desktop/application/operations/render/project-caption-plan.ts +287 -0
- package/apps/desktop/application/operations/render/project-plan.ts +939 -0
- package/apps/desktop/application/operations/render/project-spatial-receipt.ts +70 -0
- package/apps/desktop/application/operations/render/project.ts +1922 -0
- package/apps/desktop/application/operations/shared.ts +67 -0
- package/apps/desktop/application/operations/slopcamera-portable.ts +1188 -0
- package/apps/desktop/application/operations/slopcamera-visuals.ts +784 -0
- package/apps/desktop/application/operations/spatial-project.ts +209 -0
- package/apps/desktop/application/operations/spatial-render.ts +415 -0
- package/apps/desktop/application/operations/spatial-scene.ts +97 -0
- package/apps/desktop/application/operations/studio.ts +143 -0
- package/apps/desktop/application/output-publication-lease.ts +163 -0
- package/apps/desktop/application/project-publication-lease.ts +233 -0
- package/apps/desktop/application/project-store.ts +196 -0
- package/apps/desktop/application/receipts.ts +533 -0
- package/apps/desktop/application/registry.ts +182 -0
- package/apps/desktop/application/spatial-assets.ts +675 -0
- package/apps/desktop/application/spatial-compositor-cadence.ts +23 -0
- package/apps/desktop/application/spatial-project-authority.ts +35 -0
- package/apps/desktop/application/spatial-project-lease.ts +126 -0
- package/apps/desktop/application/spatial-project-render-preparation.ts +225 -0
- package/apps/desktop/application/spatial-project-store.ts +286 -0
- package/apps/desktop/application/spatial-render-projection.ts +201 -0
- package/apps/desktop/application/spatial-render.ts +677 -0
- package/apps/desktop/application/spatial-spz.ts +44 -0
- package/apps/desktop/application/spatial-world-import.ts +135 -0
- package/apps/desktop/application/spatial-world-provenance.ts +46 -0
- package/apps/desktop/application/studio-port.ts +58 -0
- package/apps/desktop/application/verified-receipt-reconciliation.ts +1574 -0
- package/apps/desktop/capture/protocol.ts +689 -0
- package/apps/desktop/cli/analyzer.ts +406 -0
- package/apps/desktop/cli/args.ts +3181 -0
- package/apps/desktop/cli/asset-ingest.ts +747 -0
- package/apps/desktop/cli/atomic-render-effects.ts +210 -0
- package/apps/desktop/cli/atomic-render-platform.ts +205 -0
- package/apps/desktop/cli/atomic-render.ts +50 -0
- package/apps/desktop/cli/audio-analysis.ts +252 -0
- package/apps/desktop/cli/bounded-map.ts +39 -0
- package/apps/desktop/cli/bundle-service.ts +256 -0
- package/apps/desktop/cli/capabilities.ts +212 -0
- package/apps/desktop/cli/capture-bundle.ts +1117 -0
- package/apps/desktop/cli/command-host-resources.ts +251 -0
- package/apps/desktop/cli/commands.ts +6836 -0
- package/apps/desktop/cli/directing-blob.ts +291 -0
- package/apps/desktop/cli/directing-media.ts +517 -0
- package/apps/desktop/cli/directing-quote.ts +89 -0
- package/apps/desktop/cli/directing-service.ts +288 -0
- package/apps/desktop/cli/emoji-assets.ts +538 -0
- package/apps/desktop/cli/errors.ts +29 -0
- package/apps/desktop/cli/face-analysis-service.ts +940 -0
- package/apps/desktop/cli/filter-script.ts +76 -0
- package/apps/desktop/cli/gateway-application-port.ts +1142 -0
- package/apps/desktop/cli/gateway-credential.ts +81 -0
- package/apps/desktop/cli/gateway-media-artifacts.ts +723 -0
- package/apps/desktop/cli/gateway-media-catalog.ts +922 -0
- package/apps/desktop/cli/gateway-media-service.ts +3106 -0
- package/apps/desktop/cli/gateway-media-signature.ts +122 -0
- package/apps/desktop/cli/gateway-provider-options.ts +562 -0
- package/apps/desktop/cli/gateway-scene-provider.ts +391 -0
- package/apps/desktop/cli/help.ts +501 -0
- package/apps/desktop/cli/html-overlay-gpu.ts +97 -0
- package/apps/desktop/cli/html-overlay-renderer.ts +2478 -0
- package/apps/desktop/cli/io.ts +290 -0
- package/apps/desktop/cli/main.ts +123 -0
- package/apps/desktop/cli/media-effects-service.ts +885 -0
- package/apps/desktop/cli/media-ingest-model.ts +364 -0
- package/apps/desktop/cli/media-ingest-platform.ts +227 -0
- package/apps/desktop/cli/media-ingest-program.ts +200 -0
- package/apps/desktop/cli/media-ingest.ts +30 -0
- package/apps/desktop/cli/music-analysis-service.ts +434 -0
- package/apps/desktop/cli/mutation-lock.ts +530 -0
- package/apps/desktop/cli/native-assets.d.ts +12 -0
- package/apps/desktop/cli/native-media-runtime.macos.ts +7 -0
- package/apps/desktop/cli/overlay-playback.ts +177 -0
- package/apps/desktop/cli/paths.ts +265 -0
- package/apps/desktop/cli/portable-surface.ts +242 -0
- package/apps/desktop/cli/project-camera-receipt.ts +112 -0
- package/apps/desktop/cli/project-face-camera.ts +394 -0
- package/apps/desktop/cli/project-inactivity-service.ts +552 -0
- package/apps/desktop/cli/project-media-integrity.ts +121 -0
- package/apps/desktop/cli/project-renderer.ts +1526 -0
- package/apps/desktop/cli/project-service.ts +519 -0
- package/apps/desktop/cli/project-state-transaction.ts +520 -0
- package/apps/desktop/cli/query-limits.ts +1 -0
- package/apps/desktop/cli/recording-controller.ts +1567 -0
- package/apps/desktop/cli/recording-daemon.ts +931 -0
- package/apps/desktop/cli/recording-ref.ts +56 -0
- package/apps/desktop/cli/renamed-environment.ts +6 -0
- package/apps/desktop/cli/renderer.ts +1439 -0
- package/apps/desktop/cli/run-cli-test-helper.ts +60 -0
- package/apps/desktop/cli/scene-analysis-service.ts +879 -0
- package/apps/desktop/cli/spatial-compositor-verifier.ts +213 -0
- package/apps/desktop/cli/spatial-project-service.ts +90 -0
- package/apps/desktop/cli/spatial-scene-service.ts +105 -0
- package/apps/desktop/cli/spatial-world-service.ts +39 -0
- package/apps/desktop/cli/speech-analysis-service.ts +682 -0
- package/apps/desktop/cli/studio-assemble.ts +58 -0
- package/apps/desktop/cli/studio-command.ts +60 -0
- package/apps/desktop/cli/studio-custody.ts +60 -0
- package/apps/desktop/cli/studio-encode.ts +216 -0
- package/apps/desktop/cli/studio-exr.ts +52 -0
- package/apps/desktop/cli/studio-files.ts +179 -0
- package/apps/desktop/cli/studio-output-validation.ts +162 -0
- package/apps/desktop/cli/studio-process.ts +145 -0
- package/apps/desktop/cli/studio-runtime.ts +77 -0
- package/apps/desktop/cli/studio-scaffold.ts +70 -0
- package/apps/desktop/cli/studio-service.ts +301 -0
- package/apps/desktop/cli/studio-spatial-asset.ts +248 -0
- package/apps/desktop/cli/studio-template-names.ts +3 -0
- package/apps/desktop/cli/studio-workflow.ts +19 -0
- package/apps/desktop/cli/time.ts +25 -0
- package/apps/desktop/cli/workflow-code.ts +584 -0
- package/apps/desktop/cli/workflow-runs.ts +576 -0
- package/apps/desktop/code/advanced.ts +139 -0
- package/apps/desktop/code/application-node-planner.ts +1288 -0
- package/apps/desktop/code/compiler.ts +329 -0
- package/apps/desktop/code/contracts.ts +13 -0
- package/apps/desktop/code/creative-recipes.ts +94 -0
- package/apps/desktop/code/define-workflow.ts +115 -0
- package/apps/desktop/code/editing.ts +284 -0
- package/apps/desktop/code/file-candidate-provenance.ts +420 -0
- package/apps/desktop/code/file-candidate.ts +69 -0
- package/apps/desktop/code/graph-builder.ts +10 -0
- package/apps/desktop/code/host-resource-policy.ts +55 -0
- package/apps/desktop/code/plan-contracts.ts +159 -0
- package/apps/desktop/code/planning.ts +361 -0
- package/apps/desktop/code/public.ts +134 -0
- package/apps/desktop/code/reference-projection.ts +62 -0
- package/apps/desktop/code/run-contracts.ts +466 -0
- package/apps/desktop/code/run-store.ts +2478 -0
- package/apps/desktop/code/runtime-identity.ts +328 -0
- package/apps/desktop/code/scheduler.ts +3067 -0
- package/apps/desktop/code/semantic-builder.ts +1436 -0
- package/apps/desktop/code/source-bundle.ts +1641 -0
- package/apps/desktop/code/source-typecheck.ts +211 -0
- package/apps/desktop/code/testing.ts +79 -0
- package/apps/desktop/code/worker-client.ts +1675 -0
- package/apps/desktop/code/worker-effects.ts +141 -0
- package/apps/desktop/code/worker-entry.ts +435 -0
- package/apps/desktop/code/worker-lease-guardian.ts +179 -0
- package/apps/desktop/code/worker-process-identity.ts +484 -0
- package/apps/desktop/code/worker-protocol.ts +211 -0
- package/apps/desktop/code/workflow-effects.ts +132 -0
- package/apps/desktop/contracts/analysis.ts +949 -0
- package/apps/desktop/contracts/edit.ts +576 -0
- package/apps/desktop/contracts/index.ts +9 -0
- package/apps/desktop/contracts/media-effects.ts +139 -0
- package/apps/desktop/contracts/project-render.ts +644 -0
- package/apps/desktop/contracts/project.ts +740 -0
- package/apps/desktop/contracts/recording.ts +1434 -0
- package/apps/desktop/contracts/render.ts +334 -0
- package/apps/desktop/contracts/runtime.ts +155 -0
- package/apps/desktop/contracts/spatial-compositor.ts +58 -0
- package/apps/desktop/contracts/spatial-project.ts +159 -0
- package/apps/desktop/contracts/spatial-world.ts +48 -0
- package/apps/desktop/contracts/video-effects.ts +120 -0
- package/apps/desktop/core/alignment-apply.ts +400 -0
- package/apps/desktop/core/audio-alignment.ts +466 -0
- package/apps/desktop/core/auto-zoom.ts +209 -0
- package/apps/desktop/core/canonical-json.ts +14 -0
- package/apps/desktop/core/emoji.ts +210 -0
- package/apps/desktop/core/events.ts +409 -0
- package/apps/desktop/core/face-framing.ts +533 -0
- package/apps/desktop/core/face-tracks.ts +326 -0
- package/apps/desktop/core/inactivity.ts +137 -0
- package/apps/desktop/core/index.ts +30 -0
- package/apps/desktop/core/intervals.ts +123 -0
- package/apps/desktop/core/montage.ts +236 -0
- package/apps/desktop/core/music-analysis.ts +518 -0
- package/apps/desktop/core/plan.ts +244 -0
- package/apps/desktop/core/project-auto-zoom.ts +75 -0
- package/apps/desktop/core/project-camera-spatial.ts +676 -0
- package/apps/desktop/core/project-camera.ts +403 -0
- package/apps/desktop/core/project-captions.ts +888 -0
- package/apps/desktop/core/project-inactivity.ts +125 -0
- package/apps/desktop/core/project-metadata.ts +927 -0
- package/apps/desktop/core/project-plan.ts +375 -0
- package/apps/desktop/core/project-render-plan.ts +303 -0
- package/apps/desktop/core/project-time.ts +469 -0
- package/apps/desktop/core/render-plan.ts +442 -0
- package/apps/desktop/core/scene-cache.ts +174 -0
- package/apps/desktop/core/scene-sampling.ts +448 -0
- package/apps/desktop/core/spatial-compositor.ts +22 -0
- package/apps/desktop/core/spatial-durability.ts +99 -0
- package/apps/desktop/core/spatial-project.ts +169 -0
- package/apps/desktop/core/spatial-shot-clock.ts +32 -0
- package/apps/desktop/core/speech-analysis.ts +333 -0
- package/apps/desktop/core/storage.ts +970 -0
- package/apps/desktop/core/time-map.ts +91 -0
- package/apps/desktop/core/time.ts +61 -0
- package/apps/desktop/core/video-effects.ts +660 -0
- package/apps/desktop/dist/cli/NebulaSans-Bold-26se8aek.otf +0 -0
- package/apps/desktop/dist/cli/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
- package/apps/desktop/dist/cli/NebulaSans-Book-5ax05zvn.woff2 +0 -0
- package/apps/desktop/dist/cli/NebulaSans-Book-8cenzchw.otf +0 -0
- package/apps/desktop/dist/cli/main.js +5086 -0
- package/apps/desktop/html-overlay/catalog.ts +171 -0
- package/apps/desktop/html-overlay/contracts.ts +372 -0
- package/apps/desktop/html-overlay/execution-profile.ts +77 -0
- package/apps/desktop/html-overlay/index.ts +98 -0
- package/apps/desktop/html-overlay/libraries.ts +263 -0
- package/apps/desktop/html-overlay/random.ts +49 -0
- package/apps/desktop/html-overlay/runtime.ts +813 -0
- package/apps/desktop/html-overlay/scaffolds.ts +792 -0
- package/apps/desktop/html-overlay/spark-runtime.ts +88 -0
- package/apps/desktop/html-overlay/spatial-splat-runtime.ts +60 -0
- package/apps/desktop/html-overlay/spatial.ts +720 -0
- package/apps/desktop/studio/assets/README.md +39 -0
- package/apps/desktop/studio/assets/contracts.ts +129 -0
- package/apps/desktop/studio/assets/network.ts +64 -0
- package/apps/desktop/studio/assets/poly-haven.ts +148 -0
- package/apps/desktop/studio/drivers/blender_driver.py +466 -0
- package/apps/desktop/studio/drivers/cadquery_driver.py +176 -0
- package/apps/desktop/studio/education/driver.py +308 -0
- package/apps/desktop/studio/education/lesson.py +180 -0
- package/apps/desktop/studio/education/toolkit.py +249 -0
- package/apps/desktop/workflows/chaptered-demo.ts +48 -0
- package/apps/desktop/workflows/creative-iteration.ts +107 -0
- package/apps/desktop/workflows/creative-selection.ts +92 -0
- package/apps/desktop/workflows/fragments.ts +486 -0
- package/apps/desktop/workflows/index.ts +93 -0
- package/apps/desktop/workflows/polished-screen-demo.ts +128 -0
- package/apps/desktop/workflows/scene-direction.ts +12 -0
- package/apps/desktop/workflows/social-variants.ts +75 -0
- package/apps/desktop/workflows/talking-head-cleanup.ts +53 -0
- package/dist/NebulaSans-Bold-26se8aek.otf +0 -0
- package/dist/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
- package/dist/NebulaSans-Book-5ax05zvn.woff2 +0 -0
- package/dist/NebulaSans-Book-8cenzchw.otf +0 -0
- package/dist/cli.js +479 -0
- package/dist/code/advanced.js +270 -0
- package/dist/code/index.js +2240 -0
- package/dist/generate.js +24 -0
- package/dist/host-resources.js +26 -0
- package/dist/index-42zsesc1.js +2257 -0
- package/dist/index-7308egqr.js +48 -0
- package/dist/index-8txs6fkn.js +712 -0
- package/dist/index-mcy8z0br.js +192 -0
- package/dist/index-p63wavx0.js +2029 -0
- package/dist/index-r7gdhmsp.js +424 -0
- package/dist/index-sh6xbav6.js +1219 -0
- package/dist/index-z1w83f81.js +4 -0
- package/dist/index-z7239b4h.js +2363 -0
- package/dist/index-zfeg9hmw.js +2013 -0
- package/dist/index.js +241 -0
- package/dist/operations.js +29 -0
- package/dist/skill-install-dh0ntqba.js +10 -0
- package/dist/vectorize/worker.js +142 -0
- package/dist/workflow.js +16 -0
- package/docs/README.md +38 -0
- package/docs/studio.md +215 -0
- package/examples/studio/blender/README.md +26 -0
- package/examples/studio/blender/character.py +152 -0
- package/examples/studio/blender/cloth.py +101 -0
- package/examples/studio/blender/color_chart.py +29 -0
- package/examples/studio/blender/fluid.py +78 -0
- package/examples/studio/blender/import_model.py +74 -0
- package/examples/studio/blender/product.py +53 -0
- package/examples/studio/blender/shaded_street.py +481 -0
- package/examples/studio/blender/studio_scene.py +147 -0
- package/examples/studio/cadquery/README.md +13 -0
- package/examples/studio/cadquery/bracket.py +36 -0
- package/examples/studio/cadquery/import_step.py +12 -0
- package/examples/studio/education/README.md +46 -0
- package/examples/studio/education/lesson.json +99 -0
- package/examples/studio/education/scene.py +7 -0
- package/examples/studio/hybrid-scene.ts +96 -0
- package/examples/studio/native-workflow.ts +14 -0
- package/examples/studio/vgpu/README.md +64 -0
- package/examples/studio/vgpu/heat-field.mjs +290 -0
- package/package.json +304 -0
- package/packages/scene/src/index.ts +1 -0
- package/packages/scene/src/scene.ts +215 -0
- package/schema/diagram.schema.json +334 -0
- package/skills/slopcamera/SKILL.md +45 -0
- package/skills/slopcamera/agents/openai.yaml +4 -0
- package/skills/slopcamera/references/customization.md +77 -0
- package/skills/slopcamera/references/diagrams.md +153 -0
- package/skills/slopcamera/references/directed-scenes.md +42 -0
- package/skills/slopcamera/references/directing-video.md +18 -0
- package/skills/slopcamera/references/educational-video.md +26 -0
- package/skills/slopcamera/references/gateway-media.md +134 -0
- package/skills/slopcamera/references/install.md +62 -0
- package/skills/slopcamera/references/native-studio.md +31 -0
- package/skills/slopcamera/references/reference-led-3d.md +140 -0
- package/skills/slopcamera/references/rubber-stamp-examples/poster-example-1.jpg +0 -0
- package/skills/slopcamera/references/rubber-stamp-examples/poster-example-2.jpg +0 -0
- package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-1.png +0 -0
- package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-2.png +0 -0
- package/skills/slopcamera/references/rubber-stamp-field-notes.md +144 -0
- package/skills/slopcamera/references/vectorization.md +29 -0
- package/skills/slopcamera/references/video-projects.md +187 -0
- package/skills/slopcamera/references/visual-communication.md +120 -0
- package/skills/slopcamera/references/workflows-sdk.md +45 -0
- package/skills/slopcamera/scripts/compose-rubber-stamp-field-note.ts +229 -0
- package/src/artifacts.ts +106 -0
- package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/PROVENANCE.md +21 -0
- package/src/assets.d.ts +9 -0
- package/src/cli.ts +601 -0
- package/src/cloud-errors.ts +26 -0
- package/src/code/advanced.ts +10 -0
- package/src/code/boundary.ts +21 -0
- package/src/code/canonical-json.ts +129 -0
- package/src/code/compiler.ts +953 -0
- package/src/code/contracts.ts +576 -0
- package/src/code/define-workflow.ts +334 -0
- package/src/code/errors.ts +41 -0
- package/src/code/graph-builder.ts +945 -0
- package/src/code/index.ts +121 -0
- package/src/code/json-snapshot.ts +899 -0
- package/src/code/json-utf8.ts +88 -0
- package/src/code/portable-builder.ts +136 -0
- package/src/code/projection.ts +378 -0
- package/src/code/public-operations.ts +450 -0
- package/src/code/runtime.ts +580 -0
- package/src/code/sha256.ts +36 -0
- package/src/config.ts +180 -0
- package/src/desktop.ts +253 -0
- package/src/fs.ts +10 -0
- package/src/generate.ts +704 -0
- package/src/host-resource-posix.ts +161 -0
- package/src/host-resources.ts +1749 -0
- package/src/icons.ts +58 -0
- package/src/index.ts +200 -0
- package/src/label-layout.ts +130 -0
- package/src/layout.ts +413 -0
- package/src/lint.ts +127 -0
- package/src/mcp/boundary.ts +354 -0
- package/src/mcp/index.ts +33 -0
- package/src/mcp/server.ts +272 -0
- package/src/mcp/tools.ts +963 -0
- package/src/mcp/types.ts +71 -0
- package/src/operations.ts +775 -0
- package/src/parse.ts +861 -0
- package/src/process-environment.ts +17 -0
- package/src/render.ts +437 -0
- package/src/skill-install.ts +49 -0
- package/src/spatial-scene/authoring.ts +27 -0
- package/src/spatial-scene/camera-track.ts +83 -0
- package/src/spatial-scene/contracts.ts +277 -0
- package/src/spatial-scene/evaluate.ts +145 -0
- package/src/spatial-scene/gltf.ts +558 -0
- package/src/spatial-scene/identity.ts +260 -0
- package/src/spatial-scene/index.ts +10 -0
- package/src/spatial-scene/inspect.ts +68 -0
- package/src/spatial-scene/math.ts +280 -0
- package/src/spatial-scene/patch.ts +131 -0
- package/src/spatial-scene/time.ts +53 -0
- package/src/studio/contracts.ts +129 -0
- package/src/studio/index.ts +2 -0
- package/src/studio/plan.ts +156 -0
- package/src/studio/shared.ts +41 -0
- package/src/theme.ts +68 -0
- package/src/tldr.ts +474 -0
- package/src/types.ts +212 -0
- package/src/vectorize/archive.ts +101 -0
- package/src/vectorize/command.ts +797 -0
- package/src/vectorize/index.ts +27 -0
- package/src/vectorize/limits.ts +61 -0
- package/src/vectorize/metrics.ts +351 -0
- package/src/vectorize/pixels.ts +323 -0
- package/src/vectorize/supervisor.ts +339 -0
- package/src/vectorize/svg.ts +188 -0
- package/src/vectorize/tool.ts +542 -0
- package/src/vectorize/types.ts +113 -0
- package/src/vectorize/vectorize.ts +634 -0
- package/src/vectorize/worker-protocol.ts +40 -0
- package/src/vectorize/worker.ts +209 -0
- package/src/version.ts +1 -0
- package/src/workflow.ts +409 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Light and photograph one retained GLB, glTF or OBJ/MTL dependency set."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import bpy
|
|
5
|
+
from mathutils import Vector
|
|
6
|
+
from bpy_extras.object_utils import world_to_camera_view
|
|
7
|
+
from studio_scene import reset, floor, studio_lights, camera, aim
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def build(context):
|
|
11
|
+
reset()
|
|
12
|
+
relative = context["parameters"].get("input", "assets/bracket.glb")
|
|
13
|
+
source = (Path(context["sourceRoot"]) / relative).resolve(strict=True)
|
|
14
|
+
if not source.is_relative_to(Path(context["sourceRoot"]).resolve()):
|
|
15
|
+
raise ValueError("Imported asset must be a declared retained bundle input")
|
|
16
|
+
if source.suffix.lower() in (".glb", ".gltf"):
|
|
17
|
+
bpy.ops.import_scene.gltf(filepath=str(source))
|
|
18
|
+
elif source.suffix.lower() == ".obj":
|
|
19
|
+
bpy.ops.wm.obj_import(filepath=str(source))
|
|
20
|
+
else:
|
|
21
|
+
raise ValueError("This fixture accepts GLB, glTF or OBJ; STEP uses the CadQuery adapter")
|
|
22
|
+
environment = context["parameters"].get("environment")
|
|
23
|
+
if environment:
|
|
24
|
+
environment_path = (Path(context["sourceRoot"]) / environment).resolve(strict=True)
|
|
25
|
+
if not environment_path.is_relative_to(Path(context["sourceRoot"]).resolve()) or environment_path.suffix.lower() not in (".hdr", ".exr"):
|
|
26
|
+
raise ValueError("Environment must be an explicit retained linear HDR/EXR input")
|
|
27
|
+
world = bpy.context.scene.world
|
|
28
|
+
texture = world.node_tree.nodes.new("ShaderNodeTexEnvironment")
|
|
29
|
+
texture.image = bpy.data.images.load(str(environment_path), check_existing=True)
|
|
30
|
+
texture.image.colorspace_settings.name = "Linear Rec.709"
|
|
31
|
+
world.node_tree.links.new(texture.outputs["Color"], world.node_tree.nodes["Background"].inputs["Color"])
|
|
32
|
+
world.node_tree.nodes["Background"].inputs["Strength"].default_value = 0.5
|
|
33
|
+
# Preserve explicitly imported image bytes inside the published native scene.
|
|
34
|
+
# Simulation caches remain separately declared files in their relative tree.
|
|
35
|
+
for image in bpy.data.images:
|
|
36
|
+
if image.source == "FILE" and not image.packed_file:
|
|
37
|
+
image.pack()
|
|
38
|
+
objects = [obj for obj in bpy.context.scene.objects if obj.type == "MESH"]
|
|
39
|
+
if not objects:
|
|
40
|
+
raise ValueError("Imported asset contains no meshes")
|
|
41
|
+
points = [obj.matrix_world @ Vector(corner) for obj in objects for corner in obj.bound_box]
|
|
42
|
+
minimum = Vector(tuple(min(p[axis] for p in points) for axis in range(3)))
|
|
43
|
+
maximum = Vector(tuple(max(p[axis] for p in points) for axis in range(3)))
|
|
44
|
+
center = (minimum + maximum) / 2
|
|
45
|
+
span = max(maximum - minimum)
|
|
46
|
+
if span <= 0:
|
|
47
|
+
raise ValueError("Imported mesh has empty bounds")
|
|
48
|
+
floor(size=span * 25, z=minimum.z, color=(0.04, 0.065, 0.09))
|
|
49
|
+
studio_lights(scale=span)
|
|
50
|
+
shot = camera(center + Vector((1.5 * span, -2 * span, span)), center, lens=60, fstop=8)
|
|
51
|
+
def frame_bounds():
|
|
52
|
+
# Use the admitted aspect ratio and projected imported bounds, including
|
|
53
|
+
# assembly transforms. A fixed camera distance clips tall or round assets.
|
|
54
|
+
for _ in range(12):
|
|
55
|
+
bpy.context.view_layer.update()
|
|
56
|
+
projected = [world_to_camera_view(bpy.context.scene, shot, point) for point in points]
|
|
57
|
+
factor = max(max(abs(point.x - 0.5), abs(point.y - 0.5)) / 0.41 for point in projected)
|
|
58
|
+
if min(point.z for point in projected) > 0 and factor <= 1:
|
|
59
|
+
shot.data.dof.focus_distance = (shot.location - center).length
|
|
60
|
+
return
|
|
61
|
+
shot.location = center + (shot.location - center) * max(1.05, factor * 1.03)
|
|
62
|
+
raise RuntimeError("Unable to fit imported asset bounds into the admitted camera frame")
|
|
63
|
+
frame_bounds()
|
|
64
|
+
interval = context.get("render")
|
|
65
|
+
if interval:
|
|
66
|
+
shot.keyframe_insert("location", frame=interval["startFrame"])
|
|
67
|
+
shot.keyframe_insert("rotation_euler", frame=interval["startFrame"])
|
|
68
|
+
shot.location = center + Vector((span, -2.3 * span, span * 0.8))
|
|
69
|
+
aim(shot, center)
|
|
70
|
+
frame_bounds()
|
|
71
|
+
end = max(interval["startFrame"] + 1, interval["endFrameExclusive"] - 1)
|
|
72
|
+
shot.keyframe_insert("location", frame=end)
|
|
73
|
+
shot.keyframe_insert("rotation_euler", frame=end)
|
|
74
|
+
bpy.context.scene.frame_set(interval["startFrame"])
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Cinematic machined lens instrument: reflective metal, glass, fine surface detail."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
import bpy
|
|
5
|
+
from studio_scene import reset, material, cube, cylinder, sphere, torus, camera, aim, floor, studio_lights
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def build(context):
|
|
9
|
+
scene = reset()
|
|
10
|
+
brass = material("Satin champagne brass", (0.56, 0.31, 0.105), metallic=0.88, roughness=0.23, texture=True)
|
|
11
|
+
dark = material("Black anodized aluminum", (0.022, 0.032, 0.045), metallic=0.75, roughness=0.27)
|
|
12
|
+
rubber = material("Focus grip", (0.018, 0.022, 0.023), roughness=0.58, texture=True)
|
|
13
|
+
glass = material("Optical glass", (0.63, 0.9, 0.92), roughness=0.045, transmission=1)
|
|
14
|
+
silver = material("Steel fasteners", (0.5, 0.58, 0.64), metallic=1, roughness=0.17)
|
|
15
|
+
floor(size=10, color=(0.035, 0.053, 0.067))
|
|
16
|
+
cube("Display plinth", (0, 0.025, 0.035), (0.30, 0.26, 0.07), dark, 0.014)
|
|
17
|
+
cube("Instrument base", (0, 0.015, 0.10), (0.19, 0.15, 0.075), brass, 0.014)
|
|
18
|
+
cube("Support neck", (0, 0.03, 0.17), (0.105, 0.075, 0.08), dark, 0.012)
|
|
19
|
+
rotation = (math.pi / 2, 0, 0)
|
|
20
|
+
cylinder("Lens barrel", (0, 0.0, 0.23), 0.081, 0.16, brass, 0.006, rotation)
|
|
21
|
+
cylinder("Rubber focus collar", (0, -0.035, 0.23), 0.084, 0.055, rubber, 0.002, rotation)
|
|
22
|
+
for index in range(64):
|
|
23
|
+
angle = index * math.tau / 64
|
|
24
|
+
rib = cube("Machined focus rib", (0.084 * math.sin(angle), -0.035, 0.23 + 0.084 * math.cos(angle)),
|
|
25
|
+
(0.002, 0.05, 0.004), dark, 0.0006)
|
|
26
|
+
rib.rotation_euler.y = angle
|
|
27
|
+
torus("Front rim", (0, -0.085, 0.23), 0.072, 0.007, brass, rotation)
|
|
28
|
+
cylinder("Lens interior", (0, -0.068, 0.23), 0.066, 0.012, dark, rotation=rotation)
|
|
29
|
+
sphere("Rounded front optic", (0, -0.091, 0.23), (0.064, 0.018, 0.064), glass)
|
|
30
|
+
for index in range(6):
|
|
31
|
+
angle = index * math.tau / 6
|
|
32
|
+
cylinder("Steel rim screw", (0.075 * math.sin(angle), -0.092, 0.23 + 0.075 * math.cos(angle)),
|
|
33
|
+
0.0028, 0.0035, silver, 0.0003, rotation, vertices=16)
|
|
34
|
+
for index in range(5):
|
|
35
|
+
cube("Vent slot", ((index - 2) * 0.027, -0.063, 0.105), (0.014, 0.003, 0.029), dark, 0.003)
|
|
36
|
+
cylinder("Control knob", (0.095, 0.026, 0.16), 0.022, 0.016, dark, 0.002, (0, math.pi / 2, 0))
|
|
37
|
+
cylinder("Control knob cap", (0.105, 0.026, 0.16), 0.014, 0.002, brass, 0.001, (0, math.pi / 2, 0))
|
|
38
|
+
studio_lights(scale=0.20)
|
|
39
|
+
shot = camera((0.47, -0.68, 0.39), (0, -0.015, 0.17), lens=49, fstop=4.5)
|
|
40
|
+
interval = context.get("render") or {"startFrame": 1, "endFrameExclusive": 25}
|
|
41
|
+
start = interval["startFrame"]
|
|
42
|
+
end = max(start + 1, interval["endFrameExclusive"] - 1)
|
|
43
|
+
shot.keyframe_insert("location", frame=start)
|
|
44
|
+
shot.keyframe_insert("rotation_euler", frame=start)
|
|
45
|
+
shot.location = (0.28, -0.74, 0.34)
|
|
46
|
+
aim(shot, (0, -0.02, 0.18))
|
|
47
|
+
shot.keyframe_insert("location", frame=end)
|
|
48
|
+
shot.keyframe_insert("rotation_euler", frame=end)
|
|
49
|
+
shot.data.dof.focus_distance = 0.79
|
|
50
|
+
shot.data.dof.keyframe_insert("focus_distance", frame=start)
|
|
51
|
+
shot.data.dof.focus_distance = 0.72
|
|
52
|
+
shot.data.dof.keyframe_insert("focus_distance", frame=end)
|
|
53
|
+
scene.frame_set(start)
|
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
"""Original, deterministic shaded-street film set and native character.
|
|
2
|
+
|
|
3
|
+
Bundle this file with character.py and studio_scene.py. Parameters select a
|
|
4
|
+
reference shot or a sampled three-second camera move; cityOnly excludes the
|
|
5
|
+
live rig for the portable static mesh export. speakingEnvelope optionally
|
|
6
|
+
keyframes amplitude-driven mouth motion; it is not phoneme or viseme synthesis. No downloads or provider calls.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import json
|
|
11
|
+
import math
|
|
12
|
+
from pathlib import Path, PurePosixPath
|
|
13
|
+
import random
|
|
14
|
+
|
|
15
|
+
import bpy
|
|
16
|
+
from mathutils import Matrix, Vector
|
|
17
|
+
|
|
18
|
+
import character
|
|
19
|
+
from studio_scene import aim, area, cube, cylinder, finish, material, reset, sphere, torus
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def ink(name, rgb, metal=0.0, rough=0.5):
|
|
23
|
+
value = material(name, rgb, metallic=metal, roughness=rough)
|
|
24
|
+
# Default glTF dielectric parameters avoid optional material extensions.
|
|
25
|
+
value.node_tree.nodes["Principled BSDF"].inputs["IOR"].default_value = 1.5
|
|
26
|
+
return value
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def city_palette():
|
|
30
|
+
return {
|
|
31
|
+
"sand": ink("Pale limestone", (0.56, 0.46, 0.32), rough=0.8),
|
|
32
|
+
"cream": ink("Warm lime plaster", (0.67, 0.61, 0.47), rough=0.85),
|
|
33
|
+
"ochre": ink("Sun-warmed ochre", (0.54, 0.27, 0.105), rough=0.82),
|
|
34
|
+
"rose": ink("Dusty terracotta", (0.53, 0.28, 0.20), rough=0.83),
|
|
35
|
+
"teal": ink("Painted oxidized teal", (0.035, 0.19, 0.20), metal=0.12, rough=0.42),
|
|
36
|
+
"glass": ink("Deep blue window glass", (0.024, 0.068, 0.086), metal=0.25, rough=0.19),
|
|
37
|
+
"copper": ink("Brushed copper trim", (0.47, 0.19, 0.065), metal=0.75, rough=0.28),
|
|
38
|
+
"road": ink("Warm asphalt", (0.105, 0.106, 0.096), rough=0.95),
|
|
39
|
+
"paver": ink("Plaza paving", (0.43, 0.40, 0.31), rough=0.88),
|
|
40
|
+
"paver2": ink("Plaza paving variation", (0.47, 0.445, 0.35), rough=0.9),
|
|
41
|
+
"bark": ink("Tree bark", (0.14, 0.071, 0.032), rough=0.9),
|
|
42
|
+
"leaf": ink("Canopy sage", (0.105, 0.235, 0.08), rough=0.87),
|
|
43
|
+
"leaf2": ink("Sunward leaves", (0.22, 0.32, 0.10), rough=0.89),
|
|
44
|
+
"line": ink("Street marking ivory", (0.60, 0.57, 0.43), rough=0.9),
|
|
45
|
+
"dark": ink("Graphite metal", (0.028, 0.043, 0.046), metal=0.28, rough=0.36),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def tree(location, palette, rng, scale=1):
|
|
50
|
+
x, y = location
|
|
51
|
+
cylinder("Tree trunk", (x, y, 1.35 * scale), .16 * scale, 2.7 * scale, palette["bark"], vertices=12)
|
|
52
|
+
for index in range(7):
|
|
53
|
+
angle = index * math.tau / 7
|
|
54
|
+
center = (x + math.cos(angle) * .66 * scale,
|
|
55
|
+
y + math.sin(angle) * .66 * scale,
|
|
56
|
+
(2.65 + rng.uniform(-.18, .24)) * scale)
|
|
57
|
+
bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=2, radius=1, location=center)
|
|
58
|
+
obj = bpy.context.object
|
|
59
|
+
obj.scale = (1.02 * scale, .90 * scale, (.86 + rng.random() * .20) * scale)
|
|
60
|
+
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
|
61
|
+
finish(obj, "Sculpted canopy cluster", palette["leaf2" if index % 3 == 0 else "leaf"], smooth=True)
|
|
62
|
+
cube("Tree planter", (x, y, .20), (1.25, 1.25, .40), palette["sand"], .06)
|
|
63
|
+
cube("Planter soil", (x, y, .408), (1.05, 1.05, .025), palette["bark"], .02)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def building(side, y, floors, tint, palette, rng):
|
|
67
|
+
x, width, depth, height = side * 6.7, 4.2, 4.15, floors * 2.15
|
|
68
|
+
cube("City facade", (x, y, height / 2), (width, depth, height), palette[tint], .07)
|
|
69
|
+
cube("Stone plinth", (x, y, .28), (width + .16, depth + .12, .56), palette["sand"], .025)
|
|
70
|
+
cube("Roof cornice", (x, y, height - .12), (width + .26, depth + .22, .25), palette["cream"], .035)
|
|
71
|
+
cube("Flat roof", (x, y, height + .045), (width - .10, depth - .10, .10), palette["dark"], .01)
|
|
72
|
+
cube("Roof plant housing", (x + side * .7, y + .5, height + .45), (.8, 1.15, .7), palette["teal"], .035)
|
|
73
|
+
face = side * (abs(x) - width / 2)
|
|
74
|
+
for floor in range(floors):
|
|
75
|
+
z = .95 + floor * 2.15
|
|
76
|
+
for offset in (-1.25, 0, 1.25):
|
|
77
|
+
cube("Window limestone reveal", (face - side * .04, y + offset, z), (.10, .93, 1.34), palette["cream"], .02)
|
|
78
|
+
cube("Inset blue window", (face - side * .102, y + offset, z + .015), (.028, .75, 1.13), palette["glass"], .012)
|
|
79
|
+
cube("Window vertical mullion", (face - side * .123, y + offset, z + .015), (.027, .035, 1.12), palette["teal"], .003)
|
|
80
|
+
cube("Window crossbar", (face - side * .125, y + offset, z + .08), (.028, .74, .033), palette["teal"], .003)
|
|
81
|
+
cube("Window sill", (face - side * .16, y + offset, z - .67), (.34, 1.0, .09), palette["sand"], .012)
|
|
82
|
+
if floor > 0 and floor % 2 == 1:
|
|
83
|
+
cube("Facade string course", (face - side * .06, y, z - 1.0), (.16, depth + .07, .10), palette["cream"], .01)
|
|
84
|
+
for yy in (-1.0, 1.0):
|
|
85
|
+
cube("Shop awning", (face - side * .42, y + yy, 1.91), (.9, 1.65, .14), palette["teal"], .025)
|
|
86
|
+
# A few roof details give the approach real depth without downloaded assets.
|
|
87
|
+
if floors >= 4:
|
|
88
|
+
cylinder("Roof water vessel", (x, y - .65, height + .65), .52, 1.15, palette["copper"], .035, vertices=24)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def city(canopy=False):
|
|
92
|
+
palette, rng = city_palette(), random.Random(20260909)
|
|
93
|
+
cube("City ground", (0, 10, -.19), (34, 48, .32), palette["sand"], .03)
|
|
94
|
+
cube("Street asphalt", (0, 15, -.004), (6.4, 32, .08), palette["road"], .01)
|
|
95
|
+
for side in (-1, 1):
|
|
96
|
+
cube("Sidewalk", (side * 3.89, 14, .055), (1.36, 33, .18), palette["paver"], .025)
|
|
97
|
+
cube("Street curb", (side * 3.23, 14, .065), (.12, 33, .21), palette["cream"], .018)
|
|
98
|
+
for index, yy in enumerate((5.4, 10.0, 14.6, 19.2, 23.8)):
|
|
99
|
+
building(side, yy, (3, 4, 3, 5, 4)[(index + (1 if side == 1 else 0)) % 5],
|
|
100
|
+
("cream", "rose", "ochre", "sand", "cream")[(index + (2 if side == 1 else 0)) % 5], palette, rng)
|
|
101
|
+
# A clean foreground plaza provides a calm speaking frame and physical floor.
|
|
102
|
+
for row in range(9):
|
|
103
|
+
for column in range(12):
|
|
104
|
+
cube("Plaza stone", ((column - 5.5) * .80, -4.0 + row * .80, .018),
|
|
105
|
+
(.78, .78, .10), palette["paver2" if rng.random() < .25 else "paver"], .012)
|
|
106
|
+
for yy in range(5, 29, 4):
|
|
107
|
+
cube("Dashed center line", (0, yy, .042), (.085, 1.45, .012), palette["line"], .003)
|
|
108
|
+
for xx in (-2.2, -1.3, -.4, .5, 1.4, 2.3):
|
|
109
|
+
cube("Crosswalk stone stripe", (xx, 3.0, .046), (.43, 1.10, .018), palette["line"], .003)
|
|
110
|
+
for side in (-1, 1):
|
|
111
|
+
for yy in (6, 15, 24):
|
|
112
|
+
cylinder("Street lamp stem", (side * 3.66, yy, 1.72), .045, 3.44, palette["dark"], vertices=12)
|
|
113
|
+
cube("Street lamp head", (side * 3.66, yy, 3.46), (.43, .30, .12), palette["copper"], .02)
|
|
114
|
+
# The base city already includes distant green; the comparative street stays exposed.
|
|
115
|
+
for location in ((-3.65, 24.7), (3.65, 20.2), (-4.0, -1.6)):
|
|
116
|
+
tree(location, palette, rng, .92)
|
|
117
|
+
if canopy:
|
|
118
|
+
for side in (-1, 1):
|
|
119
|
+
for yy in (5.5, 10.8, 16.1):
|
|
120
|
+
tree((side * 3.67, yy), palette, rng, 1.35)
|
|
121
|
+
# Original small electric tram: roof, glazing, inset wheels and fine trim.
|
|
122
|
+
cube("Teal electric tram", (0, 14.4, .89), (1.65, 3.4, 1.25), palette["teal"], .17)
|
|
123
|
+
cube("Tram roof", (0, 14.4, 1.57), (1.71, 3.43, .14), palette["cream"], .08)
|
|
124
|
+
cube("Tram front glazing", (0, 12.682, 1.06), (1.30, .022, .62), palette["glass"], .04)
|
|
125
|
+
for side in (-1, 1):
|
|
126
|
+
for yy in (13.5, 14.4, 15.3):
|
|
127
|
+
cube("Tram side glazing", (side * .834, yy, 1.08), (.025, .71, .60), palette["glass"], .02)
|
|
128
|
+
for yy in (13.3, 15.4):
|
|
129
|
+
cylinder("Tram wheel", (side * .70, yy, .34), .28, .14, palette["dark"], rotation=(0, math.pi / 2, 0), vertices=24)
|
|
130
|
+
return palette
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def presenter():
|
|
134
|
+
character.build({"render": {"startFrame": 1}})
|
|
135
|
+
scene = bpy.context.scene
|
|
136
|
+
for obj in list(scene.objects):
|
|
137
|
+
if obj.type in ("LIGHT", "CAMERA") or obj.name == "Seamless studio floor":
|
|
138
|
+
bpy.data.objects.remove(obj, do_unlink=True)
|
|
139
|
+
for obj in scene.objects:
|
|
140
|
+
if obj.animation_data:
|
|
141
|
+
obj.animation_data_clear()
|
|
142
|
+
if obj.type == "MESH" and obj.data.shape_keys:
|
|
143
|
+
obj.data.shape_keys.animation_data_clear()
|
|
144
|
+
for block in obj.data.shape_keys.key_blocks:
|
|
145
|
+
if block.name != "Basis":
|
|
146
|
+
block.value = .34 if block.name == "Smile" else 0
|
|
147
|
+
goal = bpy.data.objects.get("Right hand IK goal")
|
|
148
|
+
goal.location = (.72, -.13, 1.16)
|
|
149
|
+
copper = bpy.data.materials["Copper joint collars"]
|
|
150
|
+
porcelain = bpy.data.materials["Warm porcelain shell"]
|
|
151
|
+
dark = bpy.data.materials["Graphite facial details"]
|
|
152
|
+
# Native geometric details distinguish the guide from a stock primitive rig.
|
|
153
|
+
torus("Copper neck ring", (0, 0, 1.218), .133, .019, copper)
|
|
154
|
+
cylinder("Sun medallion base", (0, -.160, 1.035), .085, .028, copper,
|
|
155
|
+
rotation=(math.pi / 2, 0, 0), vertices=40)
|
|
156
|
+
sphere("Sun medallion center", (0, -.183, 1.035), (.039, .012, .039), porcelain, segments=24)
|
|
157
|
+
for angle in [index * math.tau / 8 for index in range(8)]:
|
|
158
|
+
ray = cube("Medallion ray", (math.sin(angle) * .060, -.185, 1.035 + math.cos(angle) * .060),
|
|
159
|
+
(.010, .008, .021), porcelain, .003)
|
|
160
|
+
ray.rotation_euler.y = angle
|
|
161
|
+
for side in (-1, 1):
|
|
162
|
+
cylinder("Copper ear dial", (side * .268, -.012, 1.47), .056, .022, copper,
|
|
163
|
+
rotation=(0, math.pi / 2, 0), vertices=32)
|
|
164
|
+
sphere("Eye catchlight", (side * .095 - .009, -.205, 1.532), (.007, .003, .010), porcelain, segments=16)
|
|
165
|
+
cube("Expressive brow", (side * .095, -.18, 1.590), (.080, .018, .017), dark, .007)
|
|
166
|
+
return scene
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def speaking_blink_value(index):
|
|
170
|
+
return {49: .45, 50: 1.0, 51: .45}.get(index % 85, 0)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def animate_speaking_catchlights(first, sample_count):
|
|
174
|
+
"""Hide geometric eye highlights whenever the native eyelids close."""
|
|
175
|
+
glints = [obj for obj in bpy.context.scene.objects if obj.name.startswith("Eye catchlight")]
|
|
176
|
+
if len(glints) != 2:
|
|
177
|
+
raise ValueError("The original presenter requires two eye catchlights")
|
|
178
|
+
for index in range(sample_count):
|
|
179
|
+
for obj in glints:
|
|
180
|
+
obj.hide_render = speaking_blink_value(index) > 0
|
|
181
|
+
obj.keyframe_insert("hide_render", frame=first + index)
|
|
182
|
+
return [obj.name for obj in glints]
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def speaking_animation(parameters):
|
|
186
|
+
"""Optional explicit audio-amplitude samples, one per native frame at 24fps.
|
|
187
|
+
|
|
188
|
+
The caller retains the audio/envelope derivation. These are ordinary saved
|
|
189
|
+
shape-key curves, with no handler or audio decoder required during reload.
|
|
190
|
+
"""
|
|
191
|
+
values = parameters.get("speakingEnvelope")
|
|
192
|
+
if values is None:
|
|
193
|
+
return None
|
|
194
|
+
first = parameters.get("speakingStartFrame", 1)
|
|
195
|
+
if not isinstance(values, list) or not 2 <= len(values) <= 480:
|
|
196
|
+
raise ValueError("speakingEnvelope requires 2..480 amplitude samples")
|
|
197
|
+
if type(first) is not int or first < 1 or first + len(values) > 481:
|
|
198
|
+
raise ValueError("speakingStartFrame and envelope must fit frames 1..480")
|
|
199
|
+
if any(type(value) not in (int, float) or not math.isfinite(value) or not 0 <= value <= 1 for value in values):
|
|
200
|
+
raise ValueError("speakingEnvelope values must be finite amplitudes in [0, 1]")
|
|
201
|
+
if parameters.get("cityOnly"):
|
|
202
|
+
raise ValueError("speakingEnvelope requires the native presenter")
|
|
203
|
+
for obj in bpy.context.scene.objects:
|
|
204
|
+
if obj.type != "MESH" or not obj.data.shape_keys:
|
|
205
|
+
continue
|
|
206
|
+
blocks = obj.data.shape_keys.key_blocks
|
|
207
|
+
for index, amplitude in enumerate(values):
|
|
208
|
+
frame = first + index
|
|
209
|
+
for name, scale in (("MouthOpen", .80), ("JawOpen", .25)):
|
|
210
|
+
if name in blocks:
|
|
211
|
+
blocks[name].value = amplitude * scale
|
|
212
|
+
blocks[name].keyframe_insert("value", frame=frame)
|
|
213
|
+
if "Blink" in blocks:
|
|
214
|
+
# A restrained native blink, independent of the speech envelope.
|
|
215
|
+
blocks["Blink"].value = speaking_blink_value(index)
|
|
216
|
+
blocks["Blink"].keyframe_insert("value", frame=frame)
|
|
217
|
+
animate_speaking_catchlights(first, len(values))
|
|
218
|
+
return {"kind": "amplitude-mouth-v1", "startFrame": first,
|
|
219
|
+
"endFrameExclusive": first + len(values), "frameRate": {"numerator": 24, "denominator": 1},
|
|
220
|
+
"samples": values, "limitations": "Audio-amplitude mouth motion, not phoneme-aligned lip sync."}
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
POSES = {
|
|
224
|
+
"establish": ((-8.2, -10.0, 11.8), (0, 8.0, 2.2)),
|
|
225
|
+
"approach": ((-1.7, -5.0, 3.8), (0, .6, 1.2)),
|
|
226
|
+
"speaker": ((.05, -1.60, 1.53), (0, -.015, 1.24)),
|
|
227
|
+
"street": ((.35, -4.6, 3.3), (0, 12.0, 2.0)),
|
|
228
|
+
"panel": ((3.5, -5.4, 2.4), (.9, .1, 1.35)),
|
|
229
|
+
"final": ((5.0, -8.0, 5.1), (0, 3.3, 1.4)),
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def camera_record(name, position, target, width, height):
|
|
234
|
+
rotation = (Vector(target) - Vector(position)).to_track_quat("-Z", "Y")
|
|
235
|
+
conversion = Matrix.Rotation(-math.pi / 2, 4, "X")
|
|
236
|
+
transform = conversion @ Matrix.LocRotScale(Vector(position), rotation, Vector((1, 1, 1)))
|
|
237
|
+
quaternion = transform.to_quaternion()
|
|
238
|
+
return {"cameraId": "camera_shaded_street", "name": name,
|
|
239
|
+
"pose": {"position": list(transform.translation),
|
|
240
|
+
"rotation": [quaternion.x, quaternion.y, quaternion.z, quaternion.w]},
|
|
241
|
+
"projection": {"kind": "perspective", "width": width, "height": height,
|
|
242
|
+
"fx": width * 1100 / 720, "fy": height * 1100 / 1280,
|
|
243
|
+
"cx": width / 2, "cy": height / 2, "near": .05, "far": 120}}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def install_camera(record, native_position, native_target):
|
|
247
|
+
data = bpy.data.cameras.new("Shaded street portrait camera")
|
|
248
|
+
camera = bpy.data.objects.new("Shaded street portrait camera", data)
|
|
249
|
+
bpy.context.collection.objects.link(camera)
|
|
250
|
+
bpy.context.scene.camera = camera
|
|
251
|
+
apply = globals().get("SLOPCAMERA_APPLY_SPATIAL_CAMERA")
|
|
252
|
+
if apply:
|
|
253
|
+
apply(record, camera)
|
|
254
|
+
else:
|
|
255
|
+
# Exact centered, square-pixel fallback for initial fixed reference jobs.
|
|
256
|
+
# The calibrated injected helper owns arbitrary/off-center projections.
|
|
257
|
+
projection = record["projection"]
|
|
258
|
+
camera.location = native_position
|
|
259
|
+
aim(camera, native_target)
|
|
260
|
+
data.sensor_fit = "VERTICAL"
|
|
261
|
+
data.sensor_height = 24
|
|
262
|
+
data.lens = projection["fy"] / projection["height"] * data.sensor_height
|
|
263
|
+
data.clip_start, data.clip_end = projection["near"], projection["far"]
|
|
264
|
+
data.dof.use_dof = True
|
|
265
|
+
data.dof.focus_distance = (Vector(native_target) - Vector(native_position)).length
|
|
266
|
+
data.dof.aperture_fstop = 5.6 if record["name"] == "speaker" else 11
|
|
267
|
+
data.dof.aperture_blades = 8
|
|
268
|
+
return camera
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def merge_static_city():
|
|
272
|
+
"""Explicit preview LOD: omit tiny bevels, retain larger single-segment ones."""
|
|
273
|
+
bpy.context.view_layer.update()
|
|
274
|
+
graph = bpy.context.evaluated_depsgraph_get()
|
|
275
|
+
meshes = [obj for obj in bpy.context.scene.objects if obj.type == "MESH"]
|
|
276
|
+
for obj in meshes:
|
|
277
|
+
for modifier in list(obj.modifiers):
|
|
278
|
+
if modifier.type == "BEVEL":
|
|
279
|
+
if modifier.width <= .04:
|
|
280
|
+
obj.modifiers.remove(modifier)
|
|
281
|
+
else:
|
|
282
|
+
modifier.segments = 1
|
|
283
|
+
bpy.context.view_layer.update()
|
|
284
|
+
evaluated = bpy.data.meshes.new_from_object(obj.evaluated_get(graph), depsgraph=graph)
|
|
285
|
+
obj.modifiers.clear()
|
|
286
|
+
obj.data = evaluated
|
|
287
|
+
bpy.ops.object.select_all(action="DESELECT")
|
|
288
|
+
for obj in meshes:
|
|
289
|
+
obj.select_set(True)
|
|
290
|
+
bpy.context.view_layer.objects.active = meshes[0]
|
|
291
|
+
bpy.ops.object.join()
|
|
292
|
+
obj = bpy.context.object
|
|
293
|
+
obj.name = "Original shaded street — static city geometry"
|
|
294
|
+
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def apply_panel_texture(context, relative_path):
|
|
298
|
+
"""Explicit opaque PNG source -> packed unit-emission world-space surface.
|
|
299
|
+
|
|
300
|
+
The panel is a display: its sRGB pixels emit light without scene-light
|
|
301
|
+
multiplication. Geometry still controls perspective, occlusion and focus.
|
|
302
|
+
Closest texture sampling keeps source pixels crisp; oblique views still
|
|
303
|
+
depend on the native renderer sampling/antialiasing quality.
|
|
304
|
+
"""
|
|
305
|
+
if not isinstance(relative_path, str) or not relative_path or "\\" in relative_path:
|
|
306
|
+
raise ValueError("panelTexture must be a relative explicit bundle PNG path")
|
|
307
|
+
relative = PurePosixPath(relative_path)
|
|
308
|
+
if relative.is_absolute() or str(relative) != relative_path or any(part in (".", "..") for part in relative.parts):
|
|
309
|
+
raise ValueError("panelTexture must stay inside the explicit source bundle")
|
|
310
|
+
root = Path(context["sourceRoot"]).resolve()
|
|
311
|
+
candidate = root / relative_path
|
|
312
|
+
if candidate.is_symlink():
|
|
313
|
+
raise ValueError("panelTexture requires a regular retained file, not a symlink")
|
|
314
|
+
source = candidate.resolve()
|
|
315
|
+
if not source.is_relative_to(root) or not source.is_file() or source.suffix.lower() != ".png":
|
|
316
|
+
raise ValueError("panelTexture requires a retained bundle PNG file")
|
|
317
|
+
source_bytes = source.stat().st_size
|
|
318
|
+
if not 33 <= source_bytes <= 64 * 1024 * 1024:
|
|
319
|
+
raise ValueError("panelTexture PNG bytes exceed the explicit bound")
|
|
320
|
+
with source.open("rb") as stream:
|
|
321
|
+
data = stream.read(64 * 1024 * 1024 + 1)
|
|
322
|
+
if len(data) != source_bytes or len(data) < 33 or data[:8] != b"\x89PNG\r\n\x1a\n" or data[12:16] != b"IHDR" or data[24] != 8 or data[25] not in (2, 6):
|
|
323
|
+
raise ValueError("panelTexture requires a bounded RGB/RGBA8 PNG")
|
|
324
|
+
width, height = int.from_bytes(data[16:20], "big"), int.from_bytes(data[20:24], "big")
|
|
325
|
+
if not 1 <= width <= 4096 or not 1 <= height <= 4096 or width * 16 != height * 9:
|
|
326
|
+
raise ValueError("panelTexture must have exact portrait9:16 dimensions <=4096")
|
|
327
|
+
image = bpy.data.images.load(str(source), check_existing=False)
|
|
328
|
+
image.colorspace_settings.name = "sRGB"
|
|
329
|
+
if tuple(image.size) != (width, height):
|
|
330
|
+
raise ValueError("panelTexture decoded dimensions disagree")
|
|
331
|
+
if data[25] == 6:
|
|
332
|
+
from array import array
|
|
333
|
+
pixels = array("f", [0]) * (width * height * 4)
|
|
334
|
+
image.pixels.foreach_get(pixels)
|
|
335
|
+
if any(abs(pixels[index] - 1) > 1e-6 for index in range(3, len(pixels), 4)):
|
|
336
|
+
raise ValueError("panelTexture must be opaque; flatten alpha explicitly")
|
|
337
|
+
image.pack()
|
|
338
|
+
placeholder = bpy.data.objects.get("Inset media surface")
|
|
339
|
+
if placeholder is None:
|
|
340
|
+
raise ValueError("panelTexture requires the original native panel mount")
|
|
341
|
+
bpy.data.objects.remove(placeholder, do_unlink=True)
|
|
342
|
+
x, y, z, panel_width, panel_height = 2.3, .837, 1.47, 1.35, 2.4
|
|
343
|
+
mesh = bpy.data.meshes.new("Exact retained panel image surface")
|
|
344
|
+
mesh.from_pydata([(x-panel_width/2, y, z-panel_height/2), (x+panel_width/2, y, z-panel_height/2),
|
|
345
|
+
(x+panel_width/2, y, z+panel_height/2), (x-panel_width/2, y, z+panel_height/2)], [], [(0, 1, 2, 3)])
|
|
346
|
+
mesh.update()
|
|
347
|
+
uv = mesh.uv_layers.new(name="Full raster UV")
|
|
348
|
+
for loop, coordinate in zip(mesh.loops, [(0, 0), (1, 0), (1, 1), (0, 1)]):
|
|
349
|
+
uv.data[loop.index].uv = coordinate
|
|
350
|
+
plane = bpy.data.objects.new("Retained world-panel raster", mesh)
|
|
351
|
+
bpy.context.scene.collection.objects.link(plane)
|
|
352
|
+
display = bpy.data.materials.new("Display-referred panel raster")
|
|
353
|
+
display.use_nodes = True
|
|
354
|
+
nodes = display.node_tree.nodes
|
|
355
|
+
nodes.clear()
|
|
356
|
+
texture = nodes.new("ShaderNodeTexImage")
|
|
357
|
+
texture.image, texture.interpolation, texture.extension = image, "Closest", "EXTEND"
|
|
358
|
+
emission = nodes.new("ShaderNodeEmission")
|
|
359
|
+
emission.inputs["Strength"].default_value = 1
|
|
360
|
+
output = nodes.new("ShaderNodeOutputMaterial")
|
|
361
|
+
display.node_tree.links.new(texture.outputs["Color"], emission.inputs["Color"])
|
|
362
|
+
display.node_tree.links.new(emission.outputs[0], output.inputs["Surface"])
|
|
363
|
+
mesh.materials.append(display)
|
|
364
|
+
return {"path": relative_path, "sha256": hashlib.sha256(data).hexdigest(), "bytes": len(data),
|
|
365
|
+
"width": width, "height": height, "packed": True, "canonicalCenter": [x, z, -y],
|
|
366
|
+
"nativeCenter": [x, y, z], "panelWidth": panel_width, "panelHeight": panel_height,
|
|
367
|
+
"uv": "full raster, bottom-left origin", "color": "opaque sRGB -> linear emission1 -> Standard view",
|
|
368
|
+
"contentTime": "held retained raster, not a live runtime inside Blender"}
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def camera_motion(camera, motion, width, height, global_start_frame):
|
|
372
|
+
"""Sample a three-second native move; every integer frame has an exact pose."""
|
|
373
|
+
if motion not in ("approach", "return"):
|
|
374
|
+
raise ValueError("Unknown shaded street motion")
|
|
375
|
+
begin = POSES["establish"] if motion == "approach" else ((2.3, -1.2255, 1.47), (2.3, .837, 1.47))
|
|
376
|
+
end = POSES["speaker"] if motion == "approach" else POSES["final"]
|
|
377
|
+
camera.rotation_mode = "QUATERNION"
|
|
378
|
+
apply = globals().get("SLOPCAMERA_APPLY_SPATIAL_CAMERA")
|
|
379
|
+
samples, calibrated = [], []
|
|
380
|
+
# Calibrate every pose before adding animation owners. The injected helper
|
|
381
|
+
# deliberately rejects cameras whose constraints/animation could override it.
|
|
382
|
+
for index in range(73):
|
|
383
|
+
u = min(1, index / (64 if motion == "approach" else 72))
|
|
384
|
+
weight = u * u * u * (10 + u * (-15 + 6 * u))
|
|
385
|
+
position, target = [tuple(a + (b - a) * weight for a, b in zip(start, finish)) for start, finish in zip(begin, end)]
|
|
386
|
+
record = camera_record(motion, position, target, width, height)
|
|
387
|
+
if apply:
|
|
388
|
+
apply(record, camera)
|
|
389
|
+
else:
|
|
390
|
+
camera.location = position
|
|
391
|
+
camera.rotation_quaternion = (Vector(target) - Vector(position)).to_track_quat("-Z", "Y")
|
|
392
|
+
focus_weight = max(0, min(1, (index - 48) / 16)) if motion == "approach" else 0
|
|
393
|
+
focus_weight = focus_weight * focus_weight * (3 - 2 * focus_weight)
|
|
394
|
+
calibrated.append((camera.matrix_world.copy(), (Vector(target) - Vector(position)).length,
|
|
395
|
+
11 + (5.6 - 11) * focus_weight))
|
|
396
|
+
samples.append({"frameIndex": index, "nativeFrame": index + 1,
|
|
397
|
+
"exactTimeUs": {"numerator": index * 1000000, "denominator": 24},
|
|
398
|
+
"globalFrameIndex": index + global_start_frame, "camera": record})
|
|
399
|
+
for index, (matrix, focus_distance, fstop) in enumerate(calibrated):
|
|
400
|
+
camera.matrix_world = matrix
|
|
401
|
+
camera.data.dof.focus_distance = focus_distance
|
|
402
|
+
camera.data.dof.aperture_fstop = fstop
|
|
403
|
+
camera.keyframe_insert("location", frame=index + 1)
|
|
404
|
+
camera.keyframe_insert("rotation_quaternion", frame=index + 1)
|
|
405
|
+
camera.data.keyframe_insert("dof.focus_distance", frame=index + 1)
|
|
406
|
+
camera.data.keyframe_insert("dof.aperture_fstop", frame=index + 1)
|
|
407
|
+
return samples
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def build(context):
|
|
411
|
+
parameters = context["parameters"]
|
|
412
|
+
city_only = bool(parameters.get("cityOnly", False))
|
|
413
|
+
if city_only and parameters.get("panelTexture") is not None:
|
|
414
|
+
raise ValueError("panelTexture requires a native panel; cityOnly exports exclude it")
|
|
415
|
+
# The reused studio reset names an AgX-only look. Enter that view while it
|
|
416
|
+
# builds, then explicitly pin Standard/None for this display-referred set.
|
|
417
|
+
bpy.context.scene.view_settings.view_transform = "AgX"
|
|
418
|
+
scene = reset() if city_only else presenter()
|
|
419
|
+
speaking = speaking_animation(parameters)
|
|
420
|
+
palette = city(bool(parameters.get("canopy", False)))
|
|
421
|
+
if city_only:
|
|
422
|
+
merge_static_city()
|
|
423
|
+
# A blank physical panel is an authored mounting point for later exact media.
|
|
424
|
+
if not city_only:
|
|
425
|
+
cube("Copper media frame", (2.3, .90, 1.47), (1.49, .095, 2.54), palette["copper"], .07)
|
|
426
|
+
cube("Inset media surface", (2.3, .837, 1.47), (1.35, .032, 2.40), palette["teal"], .045)
|
|
427
|
+
for xx in (1.83, 2.77):
|
|
428
|
+
cube("Panel support", (xx, .95, .20), (.08, .12, .40), palette["dark"], .015)
|
|
429
|
+
panel_raster = apply_panel_texture(context, parameters["panelTexture"]) if parameters.get("panelTexture") is not None else None
|
|
430
|
+
scene.world.node_tree.nodes["Background"].inputs["Color"].default_value = (.31, .43, .60, 1)
|
|
431
|
+
scene.world.node_tree.nodes["Background"].inputs["Strength"].default_value = .38
|
|
432
|
+
sunlight = bpy.data.lights.new("Golden afternoon sun", "SUN")
|
|
433
|
+
sunlight.energy = 1.8
|
|
434
|
+
sunlight.color = (1.0, .77, .50)
|
|
435
|
+
sunlight.angle = math.radians(3)
|
|
436
|
+
sun = bpy.data.objects.new("Golden afternoon sun", sunlight)
|
|
437
|
+
bpy.context.collection.objects.link(sun)
|
|
438
|
+
sun.location = (-12, -10, 18)
|
|
439
|
+
aim(sun, (0, 7, 0))
|
|
440
|
+
area("Soft open-sky portrait fill", (1.5, -4, 4), (0, 0, 1.2), 100, (.65, .82, 1), 4)
|
|
441
|
+
area("Warm presenter rim", (-2.5, 2, 4), (0, 0, 1.1), 130, (1, .64, .34), 2)
|
|
442
|
+
scene.view_settings.view_transform = "Standard"
|
|
443
|
+
scene.view_settings.look = "None"
|
|
444
|
+
scene.view_settings.exposure = 0
|
|
445
|
+
scene.view_settings.gamma = 1
|
|
446
|
+
scene.render.image_settings.color_mode = "RGB"
|
|
447
|
+
scene.render.resolution_percentage = 100
|
|
448
|
+
scene.render.pixel_aspect_x = scene.render.pixel_aspect_y = 1
|
|
449
|
+
shot = parameters.get("shot", "speaker")
|
|
450
|
+
if shot not in POSES:
|
|
451
|
+
raise ValueError("Unknown shaded street reference shot")
|
|
452
|
+
render = context.get("render") or {"width": 720, "height": 1280}
|
|
453
|
+
position, target = POSES[shot]
|
|
454
|
+
record = camera_record(shot, position, target, render["width"], render["height"])
|
|
455
|
+
camera = install_camera(record, position, target)
|
|
456
|
+
motion = parameters.get("motion")
|
|
457
|
+
samples = camera_motion(camera, motion, render["width"], render["height"],
|
|
458
|
+
parameters.get("globalStartFrame", 0 if motion == "approach" else 312)) if motion else []
|
|
459
|
+
scene.frame_set(render.get("startFrame", 1))
|
|
460
|
+
# This ledger is a retained working observation, not a claimed output format.
|
|
461
|
+
observation = {"kind": "slopcamera.shaded-street-reference", "schemaVersion": 1,
|
|
462
|
+
"shot": shot, "cityOnly": city_only, "canopy": bool(parameters.get("canopy", False)),
|
|
463
|
+
"camera": record, "nativeCameraMatrix": [list(row) for row in camera.matrix_world],
|
|
464
|
+
"motion": motion, "cameraSamples": samples, "speaking": speaking, "panelRaster": panel_raster,
|
|
465
|
+
"panel": {"position": [2.3, 1.47, -.837], "rotation": [0, 0, 0, 1], "width": 1.35, "height": 2.4},
|
|
466
|
+
"geometryPolicy": "static-preview-lod-v1: omit cosmetic bevels <=4cm; larger bevels one segment; static world transforms baked" if city_only else "native authored geometry and live rig",
|
|
467
|
+
"coordinates": "Blender right-handed Z-up meters; camera record right-handed Y-up meters",
|
|
468
|
+
"intendedFilm": {"frameRate": {"numerator": 24, "denominator": 1}, "frames": 432,
|
|
469
|
+
"shots": [{"name": name, "startFrame": start, "endFrameExclusive": end}
|
|
470
|
+
for name, start, end in [("city", 0, 72), ("speaker", 72, 216),
|
|
471
|
+
("diagram", 216, 312), ("return", 312, 384), ("end-hold", 384, 432)]]},
|
|
472
|
+
"meshes": len([obj for obj in scene.objects if obj.type == "MESH"])}
|
|
473
|
+
bpy.context.view_layer.update()
|
|
474
|
+
observation["nativeCameraMatrix"] = [list(row) for row in camera.matrix_world]
|
|
475
|
+
points = [obj.matrix_world @ Vector(corner) for obj in scene.objects if obj.type == "MESH" for corner in obj.bound_box]
|
|
476
|
+
observation["nativeMeshBounds"] = {"min": [min(point[axis] for point in points) for axis in range(3)],
|
|
477
|
+
"max": [max(point[axis] for point in points) for axis in range(3)]}
|
|
478
|
+
scene["slopcamera_shaded_street_reference"] = json.dumps(observation, separators=(",", ":"))
|
|
479
|
+
with (Path(context["workingRoot"]) / "creative-metadata.json").open("x") as stream:
|
|
480
|
+
json.dump(observation, stream, indent=2)
|
|
481
|
+
stream.write("\n")
|