Skip to main content

Primitive 3D pixel drawing, rendering, and simulation foundations for Python.

Project description

py3dengine

py3dengine is a Python rendering and simulation toolkit for primitive 3D scenes. It provides a small, inspectable engine for drawing pixels and 3D primitives, lighting them, applying textures, simulating simple physics, and running live demo experiences.

The project is intentionally direct: scenes are Python objects, render settings are explicit, and the CPU renderer remains a readable correctness reference. Live demos can use the built-in native PixelWindow presenter for simple windows, and the optional py_gpu bridge is prepared as a separate package for GPU backend work.

Install the engine:

python -m pip install py3dengine

Import it:

import py_3d as p3d

The PyPI distribution name is py3dengine; the Python import package is py_3d.

py3dengine banner

Contents

What It Includes

py3dengine currently includes:

  • A CPU reference renderer for triangles, generated primitives, z-buffering, lighting, textures, edge highlighting, text bulletins, and render-to-image output.
  • A live OpenGL-oriented presentation path for demos that need mouse look, window resizing, HUD overlays, menus, and interactive scenes.
  • Scene primitives including points, lines, triangles, boxes, spheres, bowls, planes, capsules, meshes, lamp primitives, and blob surfaces.
  • Materials with diffuse response, absorption, emission, roughness, fuzziness, specular highlights, shininess, reflectivity, texture sampling, and experimental direct-light ray-traced shadows.
  • Simple physics bodies for spheres, boxes, planes, bowls, kinematic objects, pairwise impulses, friction, restitution, rolling contacts, angular velocity, squishiness, damping, and explicit collision boundaries.
  • Early fluid and gas scaffolds, including bounded FluidBlob surfaces and vector-particle fluid behavior.
  • HUD overlay primitives for text, rectangles, images, opacity, and simple animations over live scenes.
  • Native portal surfaces, linked portal pairs, simple inventory slots, and inventory-backed cube placement for small games.
  • A reusable sky prefab with sun angle, day/night time, cycle length, cloud toggles, star toggles, and directional sun light derived from the sky state.
  • Import helpers for OBJ, STL, prepared *.py3dmesh.json assets, PNG textures, and planar UV projection.
  • User-facing demo scripts under USER/demos/ for live scenes, still previews, videos, and feature tests.

The project is still pre-alpha. The API is usable for experiments and demos, and the public surface will keep getting tightened as the renderer grows.

Install And Setup

Install from PyPI:

python -m pip install py3dengine

Install from a local checkout:

python -m venv .venv
python -m pip install -U pip
python -m pip install -e ".[dev]"

Optional video helpers:

python -m pip install -e ".[video]"

Optional GPU bridge after py_gpu is published:

python -m pip install py_gpu

or, once both packages are live:

python -m pip install "py3dengine[gpu]"

Video rendering needs an FFmpeg executable. The optional video extra can help locate bundled encoder binaries, but a normal FFmpeg install also works.

Quick Start

Render a lit triangle into an off-screen buffer:

from py_3d import Camera, Material, RenderEngine, RenderSettings, Scene, Sun, Triangle

scene = Scene()
scene.add(
    Triangle(
        (-1.0, -1.0, 0.0),
        (1.0, -1.0, 0.0),
        (0.0, 1.0, 0.0),
        Material(color=(220, 80, 40)),
    )
)
scene.add_light(Sun(direction=(0.0, 0.0, -1.0), color=(255, 255, 255), intensity=1.0))

camera = Camera(position=(0.0, 0.0, -4.0), target=(0.0, 0.0, 0.0))
settings = RenderSettings(width=320, height=240, background=(8, 10, 14))

buffer = RenderEngine().render(scene, camera, settings)
buffer.to_ppm("triangle.ppm")

Run the same idea from the example folder:

python examples/offline_triangle.py

The renderer returns a PixelBuffer. A buffer can be inspected in tests, written as PPM or PNG, resized, used as a texture, or passed to higher-level display code.

Visual Showcase

The screenshots below are generated from the repository. Regenerate them with the commands in Development Commands.

Menu And Settings

Demo launcher Launcher settings
demo launcher menu demo launcher settings
Live settings menu
live settings menu

The launcher is a game-style menu for choosing experiences, rendering previews, opening settings, and launching demos. Live demos use a tabbed settings menu with buttons, mouse interaction, scrollable option groups, and Apply/Done/Exit controls.

Rendering Samples

2D primitives Lit triangle Lit sphere
2D primitives lit triangle lit sphere
Wire box Texture import Textured sphere
wire box texture import textured sphere and polygons

Physics And Materials

Ramp and wall Floor bounce Wall bank
physics ramp wall physics floor bounce physics wall bank
Bumpy ball Smooth bumpy ball Collision override
bumpy ball physics bumpy ball smooth render collision boundary override

Environment Demos

Poly fruit bowl Smooth fruit bowl Ray-traced shadows
fruit bowl poly fruit bowl smooth fruit bowl ray traced shadows
Mirror prelight Edge highlight Fan cloth water
mirror bowl prelight fruit bowl edges fan cloth water
Slime fluid Rocket tube Sea lion asset
slime fluid blobs rocket tube gas field sea lion mesh asset

Demo Menu

Open the main demo menu:

python USER/demos/00_list_experiences.py

Starter JSON CLI

Install the project, then ask the package for its starter commands:

python -m pip install -e .
py3dengine --help

If Windows installed py3dengine.exe outside PATH, use either:

python -m py_3d.cli --help
python init_example_game.py

Use the CLI to pull editable prefab JSON docs or create a starter two-cube scene with an environment file, object file, and runnable Python file:

py3dengine --write-prefab-docs USER/prefab-docs
py3dengine init --output USER/projects/starter-cubes
python USER/projects/starter-cubes/main.py

Generate a complete beginner game project with JSON config and a runner:

python init_example_game.py
python USER-GAMES/example-game/run_game.py --render-preview
python USER-GAMES/example-game/run_game.py

List the experiences without opening a window:

python USER/demos/00_list_experiences.py --list

Launch an experience from the command line:

python USER/demos/00_list_experiences.py --run 1 --quality safe

Dry-run the launch command:

python USER/demos/00_list_experiences.py --run 1 --dry-run --quality high

Render a preview still for a registered experience:

python USER/demos/00_list_experiences.py --render-preview 3

Current menu entries:

Script Purpose
10_live_fruit_bowl_gpu.py Textured fruit, bowl physics, grab/drop, reflections, quality settings, and sky controls.
11_live_capsule_walk.py FPS, over-shoulder, and free-camera movement with player model primitives.
12_live_fruit_bowl_mirror_prelight.py Higher-spec mirror/prelight version of the fruit bowl scene.
13_live_fruit_bowl_poly_lamp.py Low-poly wood bowl, hanging lamp primitive, and baked sign text.
14_render_sea_lion_asset.py Prepared mesh asset render with preserved UVs and procedural skin texture.
15_render_fan_cloth_water.py Fan, spring cloth, vector-fluid water, and bowl interaction.
16_live_fruit_bowl_rgb_bulbs.py Fruit bowl live variant with standalone R/G/B bulbs blinking through eight states.
17_render_hud_demo.py Renders FPS and third-person HUD example PNGs.
20_render_feature_previews.py Batch still-image feature previews.
30_render_environment_videos.py Batch environment video renders.
40_run_feature_tests.py User-facing test runner for demos and environments.

More detail lives in docs/live-demos.md.

Portal Game Example

The sibling portal-python repo is a thin game shell that calls py_3d and py_gpu. The engine owns the native PortalSurface, PortalPair, Inventory, cube placement helpers, and native PixelWindow display path; the game mostly loads JSON and runs a single level without an external game-window package.

From PowerShell:

cd C:\Users\Danie\OneDrive\Desktop\portal-python
python -m pip install -e ..\py_3d
python -m pip install -e ..\py_gpu
python run_game.py --render-preview --place-cube
python run_game.py

If you want to force the reference renderer while testing:

python run_game.py --renderer cpu --render-preview --place-cube

Rendering Examples

Scene, Camera, Settings

A render uses three main inputs:

  • Scene: objects, lights, background color, and text bulletins.
  • Camera: position, target, and field of view.
  • RenderSettings: image size, background, quality, shading, wireframe, shadows, tone mapping, and primitive tessellation.
from py_3d import Box, Camera, Lamp, Material, Plane, RenderEngine, RenderSettings, Scene, Sphere, Sun

scene = Scene()
scene.add(
    Sphere((0, 0.6, 0), 0.6, Material(color=(90, 150, 235), specular=0.35, shininess=30)),
    Box((1.0, 0.3, 0.2), (0.6, 0.6, 0.6), Material(color=(230, 160, 70), roughness=0.5)),
    Plane((0, -0.05, 0), (0, 1, 0), Material(color=(60, 90, 80)), size=5.0),
)
scene.add_light(Sun(direction=(-0.4, -0.8, -1.0), intensity=0.8))
scene.add_light(Lamp(position=(-1.2, 1.6, -1.8), color=(120, 170, 255), intensity=2.2))

camera = Camera(position=(2.0, 1.2, -4.0), target=(0.2, 0.35, 0.0), fov_degrees=48)
settings = RenderSettings(width=640, height=360, smooth_shading=True, ambient=0.04)

RenderEngine().render(scene, camera, settings).to_png("scene.png")

Wireframe And Edge Highlights

Wireframe mode is useful for checking generated geometry:

settings = RenderSettings(width=640, height=360, wireframe=True)

Edge highlighting keeps filled geometry but outlines boundaries and sharp normal changes:

settings = RenderSettings(
    width=640,
    height=360,
    smooth_shading=True,
    edge_highlight=True,
    edge_highlight_threshold_degrees=35,
)

Ray-Traced Direct Shadows

The CPU renderer can trace direct-light shadow rays. This is slower than the normal lighting path and is best for stills, comparison renders, and high-spec showcases:

settings = RenderSettings(
    width=480,
    height=270,
    ray_traced_shadows=True,
    shadow_samples=4,
    shadow_softness=0.12,
    reflection_bounces=2,
)

Material.light_transmission controls how much direct light passes through a material during shadow checks. 0.0 is opaque; 1.0 is fully transmissive.

More rendering detail lives in docs/rendering.md.

Textures And Materials

Materials are deliberately plain Python data:

from py_3d import Material, PixelBuffer

texture = PixelBuffer.from_png("assets/tv-test.png")
material = Material(
    color=(255, 255, 255),
    texture=texture,
    diffuse=0.9,
    roughness=0.35,
    fuzziness=0.05,
    specular=0.25,
    shininess=28.0,
    reflectivity=0.2,
)

Generated primitives such as Sphere, Bowl, and Capsule can produce UVs. For custom triangles or imported meshes, UVs can be supplied directly or created with planar projection:

from py_3d import planar_project_triangles

triangles = planar_project_triangles(
    triangles,
    center=(0, 0, 0),
    u_axis=(1, 0, 0),
    v_axis=(0, 1, 0),
    scale=1.0,
)

Physics Examples

The physics layer is simple and explicit. A world steps dynamic bodies against static objects and other dynamic bodies:

from py_3d import PhysicsWorld, SphereBody, StaticPlane

world = PhysicsWorld(gravity=(0, -9.81, 0))
ball = SphereBody(center=(0, 2, 0), radius=0.35, mass=1.0, friction=0.4, bounciness=0.35)
floor = StaticPlane(point=(0, 0, 0), normal=(0, 1, 0), friction=0.8)

world.add_dynamic(ball)
world.add_static(floor)

for _ in range(120):
    world.step(1.0 / 60.0)

Render geometry and collision geometry are related but not forced to be the same. A bumpy visual sphere can keep a simple sphere collider, or a banana-like mesh can use a compound sphere collider. This keeps demos stable while allowing visual detail to grow.

Run the physics gallery:

python examples/physics_gallery.py
python examples/collision_boundary_demo.py
python examples/bumpy_ball_demo.py --smooth-shading --output renderings-tests/bumpy_ball_smooth.png

Sky, HUD, And Live Menus

Sky Prefab

SkyPrefab manages background color, sun direction, clouds, stars, and optional day/night cycling:

from py_3d import RenderSettings, Scene, SkyPrefab

sky = SkyPrefab(time_of_day=16.0, clouds_enabled=True, stars_enabled=True)
scene = sky.apply(Scene())
settings = sky.settings_for(RenderSettings(width=960, height=540))

Manual sun angle is also available:

sky.adjust_sun_angle(elevation_delta=8.0, azimuth_delta=-12.0)

HUD Overlay

Live renderers can draw simple 2D HUD elements over a 3D scene:

from py_3d import HUDRect, HUDText, LiveHUD

hud = LiveHUD()
hud.set(
    HUDRect((14, 14), (230, 72), (4, 8, 13), alpha=0.72),
    HUDText("E grab/drop\nEsc settings", (26, 26), color=(235, 245, 255), scale=2),
)

HUD elements are rendered in order. Rectangles can create opaque or translucent panels, text can be scaled, images can be drawn from PixelBuffer, and HUDAnimation can cycle frames.

Live Menus

LiveMenu and LiveMenuOption provide the shared settings menu used by the live demos. Options can be grouped into tabs:

from py_3d.live import LiveMenu, LiveMenuOption, LiveMenuTheme

menu = LiveMenu(
    "Graphics Settings",
    (
        LiveMenuOption("quality", "Quality: High", "Render scale and mesh density", "Graphics"),
        LiveMenuOption("reflection_up", "Reflections +", "Raise reflection bounce budget", "Graphics"),
        LiveMenuOption("reflection_down", "Reflections -", "Lower reflection bounce budget", "Graphics"),
        LiveMenuOption("sky_cycle", "Day/Night Cycle", "Toggle animated sky clock", "Sky"),
        LiveMenuOption("done", "Done"),
        LiveMenuOption("apply", "Apply"),
        LiveMenuOption("cancel", "Exit Menu"),
    ),
    theme=LiveMenuTheme(panel=(0, 0, 0, 238)),
)

The menu supports mouse hover, mouse click, mouse wheel scrolling, keyboard tab navigation, Apply/Done/Exit buttons, footer actions, optional background blur, custom themes, and paired *_up/*_down options rendered as compact -/+ buttons beside one setting row.

GPU Bridge

py3dengine exposes two GPU-related paths:

  • py_3d.PixelWindow for simple live app windows without external game-window packages.
  • py_3d.live.ModernGLLiveRenderer, which now chooses a direct GLFW/ModernGL window and swapchain when available, with persistent GPU buffers, shader-side lighting/materials, texture arrays, and a native pixel presenter fallback.
  • py_gpu, a separate optional package/repository, which exposes py_gpu.adapters.py3d.Py3DRasterRenderer.

Local development install:

python -m pip install -e .
python -m pip install -e ../py_gpu

Use the bridge:

from py_3d import RenderEngine
from py_gpu.adapters.py3d import Py3DRasterRenderer

engine = RenderEngine(Py3DRasterRenderer())

Benchmark the bridge:

python examples/gpu_render_benchmark.py --renderer py_gpu

The py_gpu package owns backend detection, batching contracts, raster backends, and the py_3d adapter.

Project Layout

src/py_3d/                 engine package
examples/                  runnable examples and render scripts
USER/demos/                game-style user demo launchers
USER/environments/         saved demo environments, renderings, and metadata
USER/primitives/           JSON primitive presets used by demos
renderings-tests/          generated documentation and regression images
tests/                     unit tests
docs/                      focused project documentation

Core modules:

Module Purpose
py_3d.buffer PixelBuffer and DepthBuffer.
py_3d.camera Camera projection and view transforms.
py_3d.draw Immediate 2D drawing helpers.
py_3d.primitives Renderable primitive objects.
py_3d.materials Material and texture settings.
py_3d.lights Lamp, Sun, and light samples.
py_3d.render CPU renderer, render settings, and renderer protocol.
py_3d.gpu GPU detection, scene batching, and optional adapter loading.
py_3d.live Live renderer, menu, fly camera, and scene batching.
py_3d.physics Physics world, bodies, static colliders, and kinematic bowl.
py_3d.fluid Blob and vector-fluid scaffolds.
py_3d.hud Live 2D overlay elements.
py_3d.sky Sky prefab, sun angle, stars, and clouds.
py_3d.importers OBJ and STL loading.
py_3d.assets Prepared mesh asset loading.
py_3d.textures UV projection helpers.

Development Commands

Run tests:

python -m pytest

Build the package:

python -m build

Regenerate documentation images:

python examples/render_menu_showcase.py
python examples/rendering_gallery.py
python examples/texture_demo.py
python examples/textured_sphere_polygons.py
python examples/physics_gallery.py
python examples/collision_boundary_demo.py
python examples/bumpy_ball_demo.py --output renderings-tests/bumpy_ball_physics.png
python examples/bumpy_ball_demo.py --smooth-shading --output renderings-tests/bumpy_ball_smooth.png
python examples/fruit_bowl_demo.py --no-smooth-shading --width 640 --height 360 --output USER/environments/fruit_bowl/renderings/fruit_bowl_poly.png --label "FRUIT BOWL POLY"
python examples/fruit_bowl_demo.py --smooth-shading --width 640 --height 360 --output USER/environments/fruit_bowl/renderings/fruit_bowl_smooth.png --label "FRUIT BOWL SMOOTH"
python examples/fruit_bowl_demo.py --ray-traced-shadows --reflection-bounces 2 --shadow-samples 4 --shadow-softness 0.12 --no-smooth-shading --width 480 --height 270 --sphere-segments 12 --sphere-rings 6 --output USER/environments/fruit_bowl/renderings/fruit_bowl_ray_traced.png --label "FRUIT BOWL RAY SHADOWS"
python examples/fruit_bowl_demo.py --bowl-material mirror --light-mode mirror-prelight --smooth-shading --width 640 --height 360 --output USER/environments/fruit_bowl/renderings/fruit_bowl_mirror_prelight.png --label "MIRROR BOWL PRELIGHT"
python examples/fruit_bowl_demo.py --edge-highlight --edge-highlight-angle 35 --smooth-shading --width 640 --height 360 --output USER/environments/fruit_bowl/renderings/fruit_bowl_edges_35deg.png --label "FRUIT BOWL EDGES 35 DEG"
python examples/fruit_bowl_rgb_bulbs_live.py --renderer py_gpu
python examples/fan_cloth_water_demo.py --quality fast --width 640 --height 360 --output USER/environments/fan_cloth_water/renderings/fan_cloth_water.png
python examples/hud_demo.py --output-dir renderings-tests
python examples/sea_lion_asset_demo.py --width 640 --height 420 --output USER/environments/sea_lion/renderings/sea_lion_asset.png
python examples/slime_fluid_demo.py --output USER/environments/slime_fluid/renderings/slime_fluid.png
python examples/rocket_tube_demo.py --width 640 --height 360 --output USER/environments/rocket_tube/renderings/rocket_tube.png

Release Notes

The main package is published as py3dengine through .github/workflows/publish.yml.

The optional GPU bridge publishes from the separate py_gpu repository.

See docs/publishing.md for release commands and Trusted Publisher values.

Project Direction

py3dengine is built around primitive, explicit, composable rendering. A scene is meant to be easy to inspect, a pixel color should be explainable, and demos should be playful without hiding the mechanics that make them work.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

py3dengine-0.0.4.tar.gz (131.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

py3dengine-0.0.4-py3-none-any.whl (116.0 kB view details)

Uploaded Python 3

File details

Details for the file py3dengine-0.0.4.tar.gz.

File metadata

  • Download URL: py3dengine-0.0.4.tar.gz
  • Upload date:
  • Size: 131.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for py3dengine-0.0.4.tar.gz
Algorithm Hash digest
SHA256 1267cae4e2a9c3aef0c2ac3c4babac9d66dcf32d2fbf6eaed5450d39289b62d3
MD5 367da13bfb231942a7470f3629400095
BLAKE2b-256 cdcb7fafba2f704c25f87adf70d2996e1cba727d34266d6c21e05a11b9a9ed45

See more details on using hashes here.

Provenance

The following attestation bundles were made for py3dengine-0.0.4.tar.gz:

Publisher: publish.yml on Daniel-J-Mueller/py_3d

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file py3dengine-0.0.4-py3-none-any.whl.

File metadata

  • Download URL: py3dengine-0.0.4-py3-none-any.whl
  • Upload date:
  • Size: 116.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for py3dengine-0.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 219de91b7d8f7ce6dfdc1e714665e1368ef88dc1f917764b202c439073084194
MD5 8eeb713569ca78cec18de1de824d90ed
BLAKE2b-256 b153ad08904b239f197e6c6de95638041a8a522324dabaf52f69c51995138234

See more details on using hashes here.

Provenance

The following attestation bundles were made for py3dengine-0.0.4-py3-none-any.whl:

Publisher: publish.yml on Daniel-J-Mueller/py_3d

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page