{"id":50460392,"url":"https://github.com/kmarchais/pyvista-blender","last_synced_at":"2026-06-01T04:04:47.942Z","repository":{"id":359855850,"uuid":"1247765573","full_name":"kmarchais/pyvista-blender","owner":"kmarchais","description":"Render PyVista plotter scenes through Blender (bpy) for photoreal output.","archived":false,"fork":false,"pushed_at":"2026-05-23T18:53:59.000Z","size":16167,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-23T20:24:48.642Z","etag":null,"topics":["blender","bpy","cycles","eevee","python","pyvista","rendering","scientific-visualization"],"latest_commit_sha":null,"homepage":"https://kmarchais.github.io/pyvista-blender","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kmarchais.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-23T18:47:49.000Z","updated_at":"2026-05-23T18:54:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kmarchais/pyvista-blender","commit_stats":null,"previous_names":["kmarchais/pyvista-blender"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/kmarchais/pyvista-blender","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kmarchais%2Fpyvista-blender","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kmarchais%2Fpyvista-blender/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kmarchais%2Fpyvista-blender/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kmarchais%2Fpyvista-blender/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kmarchais","download_url":"https://codeload.github.com/kmarchais/pyvista-blender/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kmarchais%2Fpyvista-blender/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33759214,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blender","bpy","cycles","eevee","python","pyvista","rendering","scientific-visualization"],"created_at":"2026-06-01T04:04:47.659Z","updated_at":"2026-06-01T04:04:47.936Z","avatar_url":"https://github.com/kmarchais.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pyvista-blender\n\n\u003e [!WARNING]\n\u003e **Very early development.** APIs, defaults, and module layout will\n\u003e change without notice until the first stable release. Bug reports,\n\u003e missing-feature requests, and PRs are all very welcome — please open\n\u003e an [issue](https://github.com/kmarchais/pyvista-blender/issues) if\n\u003e something doesn't work or behaves surprisingly.\n\nRender [PyVista](https://docs.pyvista.org/) plotter scenes through\n[Blender](https://www.blender.org/) (`bpy`) — photoreal output, full\nanimation export, no file round-trip.\n\nBuild the scene once in PyVista. Pick VTK's preview or Blender's\nCycles / Eevee Next for the final render. Export entire deforming,\ncolour-evolving, lit animations to a single `.blend` that plays\nnatively in Blender with **no Python script inside**.\n\n## Status\n\nPre-release (`0.1.0.dev0`). The offline render path, the desktop and\nbrowser interactive viewports, the Jupyter inline backend, and the\nfull animation export to `.blend` are implemented and tested. The full\nruff (`select = [\"ALL\"]`) + ty + format gate is clean.\nSee the [features list in the docs](./docs/index.md#features) for the\nfull surface and the [architecture page](./docs/architecture.md) for\nthe translation pipeline.\n\n## Install\n\n```bash\npip install pyvista-blender\n# or\nuv add pyvista-blender\n```\n\nFor the Jupyter inline backend and the Trame-served browser viewport\n(`pl.blender.show(backend=\"web\")`), install `pyvista[jupyter]`\nalongside; the bridge reuses pyvista's Trame stack rather than\nshipping its own extras.\n\n```bash\npip install pyvista-blender 'pyvista[jupyter]'\n```\n\nRequires Python **3.11** (Blender 4.5 LTS via `bpy\u003e=4.5,\u003c5`) **or\n3.13** (Blender 5.x via `bpy\u003e=5.0,\u003c6`). Python 3.10, 3.12, and 3.14\nhave no matching `bpy` wheel on PyPI; `requires-python` blocks them.\n\n## Quick start\n\n```python\nimport pyvista as pv\n\npl = pv.Plotter(off_screen=True, window_size=(1920, 1080))\npl.add_mesh(pv.read(\"data.vtu\"), scalars=\"von_mises\", cmap=\"viridis\", pbr=True)\npl.add_light(pv.Light(position=(5, -5, 5), light_type=\"scene light\"))\npl.camera_position = \"iso\"\n\n# Offline render via Cycles + OptiX denoise. No `import pyvista_blender`\n# needed — the namespace registers itself on install through PyVista\n# 0.48's plotter-component entry point.\npl.blender.render(\"frame.png\", samples=128)\n\n# Interactive hybrid viewport: VTK 60 fps during drag, Cycles render\n# on mouse release. One window, mouse-driven, no auto-exec prompts.\npl.blender.show()\n```\n\n## API surface\n\n| Entry point                                                             | What it does                                                                           |\n| ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |\n| `pl.blender.render(path, ...)`                                          | Render a single frame to PNG via Cycles or Eevee Next.                                 |\n| `pl.blender.animate(path, updater, frames, fps=...)`                    | Render an animation as gif / mp4 / webm / mov / mkv via `imageio` + `imageio-ffmpeg`.  |\n| `pl.blender.show()`                                                     | Hybrid VTK / Cycles interactive viewport — VTK during drag, settled Cycles on release. |\n| `pl.blender.add_glyph(source, geom, *, orient, scale, factor)`          | Geometry-Nodes-instanced glyphs; memory cost scales `N + V` instead of `N · V`.        |\n| `pl.blender.export_blend(path)`                                         | Save the live PyVista scene as a `.blend` for finishing in Blender.                    |\n| `pl.blender.export_animation_blend(path, updater, frames, fps, **bake)` | Bake a full animation into a `.blend` that plays natively on file open.                |\n| `pl.blender.orbit_camera(n_frames=...)`                                 | Build an `updater` for `animate` / `export_animation_blend`.                           |\n\nThree-tier config resolution everywhere: per-call kwarg → component\nattribute (`pl.blender.engine = ...`) → module default\n(`pyvista_blender.config.*`).\n\n## Animation export to `.blend`\n\n`export_animation_blend` bakes each animation channel through its own\nopt-in kwarg. Every backend plays back natively when the file is\nopened in Blender — **no Python in the `.blend`, no auto-execution\nprompt**, no need to \"Run Scripts\" on load.\n\n| Kwarg                               | Mechanism                                                                                                                 | File layout               |\n| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------- |\n| `bake_camera=True` (default)        | Keyframes on `scene.camera.location` + `rotation_quaternion` per frame                                                    | inside `.blend`           |\n| `bake_deformation=\"shape_keys\"`     | One Shape Key per frame, value-keyframed with linear interpolation                                                        | inside `.blend`           |\n| `bake_deformation=\"mdd\"` (= `True`) | Lightwave MDD sidecar + `MESH_CACHE` modifier                                                                             | `.blend` + `__\u003cmesh\u003e.mdd` |\n| `bake_scalars=True`                 | Packed PNG (rows = frames, columns = vertices / cells) + Geometry Nodes graph; works for point-data and cell-data scalars | inside `.blend`           |\n| `bake_lights=True`                  | Per-light keyframes on location, rotation, `energy`, `color`                                                              | inside `.blend`           |\n\nExample: full multi-channel animation in one self-contained file.\n\n```python\nimport numpy as np\nimport pyvista as pv\n\npl = pv.Plotter(off_screen=True, window_size=(1280, 720))\nplane = pv.Plane(i_resolution=60, j_resolution=60)\nplane.cell_data[\"heat\"] = np.zeros(plane.n_cells, dtype=np.float32)\npl.add_mesh(plane, scalars=\"heat\", cmap=\"inferno\", clim=[-1.0, 1.0])\n\nlight = pv.Light(position=(5, 0, 4), light_type=\"scene light\", intensity=1.0)\npl.add_light(light)\n\nn_frames = 60\norbit = pl.blender.orbit_camera(n_frames=n_frames)\ncenters_xy = plane.cell_centers().points[:, :2]\n\n\ndef update(frame: int) -\u003e None:\n    orbit(frame)\n    angle = -frame * (2.0 * np.pi / n_frames)\n    light.position = (5.0 * np.cos(angle), 5.0 * np.sin(angle), 5.0)\n    light.intensity = 0.4 + 0.6 * (0.5 + 0.5 * np.sin(frame * 0.3))\n    r = np.linalg.norm(centers_xy, axis=1)\n    plane.cell_data[\"heat\"] = np.sin(2.0 * r - frame * 0.2).astype(np.float32)\n\n\npl.blender.export_animation_blend(\n    \"scene.blend\",\n    update,\n    frames=range(n_frames),\n    fps=30,\n    bake_camera=True,\n    bake_lights=True,\n    bake_scalars=True,\n)\npl.close()\n```\n\nOpen `scene.blend` in Blender 4.5+ / 5.x and press \u003ckbd\u003eSpace\u003c/kbd\u003e:\ncamera orbits, the explicit light orbits in the opposite direction\nwhile pulsing, the heat field travels as a radial wave with flat per-\ncell shading. One file, ~500 KB, nothing to ship alongside.\n\n## Development\n\n```bash\nuv sync --group dev --no-install-package bpy  # fast local sync (no 200 MB bpy wheel)\nuvx prek install                              # install pre-commit hooks\nuv run pytest                                 # ~10 s with bpy installed\nuv run ruff check src/ tests/\nuv run ruff format --check src/ tests/\nuv run ty check\n```\n\nReal `bpy` is only needed for the `@pytest.mark.bpy` tests. CI runs\nthe full suite against the live wheel.\n\n## License\n\nGPL-3.0-or-later. Forced by `bpy`'s license; every source file\ncarries an SPDX header. See [`LICENSE`](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkmarchais%2Fpyvista-blender","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkmarchais%2Fpyvista-blender","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkmarchais%2Fpyvista-blender/lists"}