Skip to main content

A Python 3D game framework powered by a C++ OpenGL backend.

Project description

speepy3d

speepy3d is a Python 3D game framework with a C++ OpenGL backend.

The backend is currently Windows-only through Win32/WGL. Version 0.3.0 adds a real shader/VBO renderer while keeping the old fixed-pipeline helpers for debug grid/axes.

Features

  • Native OpenGL window creation.
  • Keyboard and mouse state: is_key_down, is_mouse_down, mouse_delta.
  • Cursor helpers: set_cursor_locked, set_cursor_visible.
  • Timing: delta_time, fps, fpsbar=True, optional set_vsync.
  • Resize and close callbacks.
  • Input bindings: Input.action(), Input.axis(), Input.vec2(), mouse sensitivity and click-to-lock mouse.
  • Game layer helpers: GameApp, AssetManager, Timer, Profiler, controllers and particles.
  • Modern OpenGL basics: Shader, Material, Mesh, Texture, Renderer.
  • MeshBatch for building many cubes into one fast mesh.
  • Built-in Shader.basic(), Mesh.cube(), Mesh.plane(), Texture.checkerboard().
  • Mesh normals and basic ambient + directional lighting.
  • Simple OBJ loader: Mesh.from_obj("model.obj").
  • Simple glTF loader: Mesh.from_gltf("model.gltf").
  • Texture loading for .png, .jpg, .jpeg, .bmp and .ppm.
  • Math: Vec2, Vec3, Mat4.
  • Scene objects: Camera3D, Transform, Model, Scene, DirectionalLight.
  • Collision helpers: AABB, PhysicsBody, PhysicsWorld, move_and_collide, SphereCollider, Ray, raycast_aabb.
  • Debug drawing: DebugDraw.line(), DebugDraw.box(), DebugDraw.ray().
  • WAV sound helpers on Windows: Sound, Music.
  • Immediate UI: Label, Button, Panel, Menu, ContextMenu, UIManager.
  • Game loop helper: run(window, update, render).

Install

python -m pip install -e .

Modern Renderer Example

from speepy3d import Camera3D, DirectionalLight, Input, Model, Renderer, Scene, Texture, Vec3, Window

win = Window(800, 600, "speepy3d", fpsbar=True)
win.enable_depth_test(True)

camera = Camera3D(position=Vec3(0.0, 2.0, 6.0), pitch=-15.0)
renderer = Renderer(win)
controls = Input(win, mouse_sensitivity=0.08, mouse_locked_by_click=True)
controls.action("jump", "SPACE")
controls.axis("move_x", negative="A", positive="D")
controls.axis("move_z", negative="S", positive="W")

scene = Scene(
    camera=camera,
    ambient_light=Vec3(0.36, 0.40, 0.48),
    directional_light=DirectionalLight(direction=Vec3(-0.35, -1.0, -0.45), intensity=1.1),
)
cube = scene.add(Model.cube(1.5, texture=Texture.checkerboard()))
scene.add(Model.grid(10.0, 1.0))
scene.add(Model.axes(2.0))

while not win.should_close():
    win.poll_events()
    controls.update()

    dt = win.delta_time or 1.0 / 60.0
    raw_move = controls.vec2("move")
    move = camera.right * raw_move.x + camera.forward * raw_move.y
    if move.length > 0.0:
        camera.position += move.normalized() * camera.move_speed * dt

    controls.apply_look(camera)

    cube.transform.rotation.y += 60.0 * dt
    renderer.render(scene)
    win.swap_buffers()

Game Examples

python examples\snake3d.py
python examples\minicraft3d.py
python examples\platformer3d.py

Input

from speepy3d import Input

controls = Input(win, mouse_sensitivity=0.06, mouse_locked_by_click=True)
controls.action("jump", "SPACE")
controls.axis("move_x", negative="A", positive="D")
controls.axis("move_z", negative="S", positive="W")

while not win.should_close():
    win.poll_events()
    controls.update()

    if controls.action("jump"):
        jump()

    move = controls.vec2("move")

Game Layer

from speepy3d import GameApp, MeshBatch, PhysicsBody, PlatformerController, Vec3

app = GameApp(1280, 720, "short game")
app.set_camera((0, 3, 10), pitch=-15)

app.input.action("jump", "SPACE")
app.input.axis("move_x", negative="A", positive="D")

material = app.assets.material("grass", color=Vec3(0.25, 0.85, 0.35))
cube = app.assets.cube()
player = app.scene.spawn(cube, position=(0, 2, 0), material=material, name="player", tag="actor")

world = MeshBatch()
world.cube((0, 0, 0), size=(12, 1, 4), color=(0.3, 0.8, 0.35))
app.scene.add(world.build_model(material))

body = PhysicsBody.from_values(player.position, (1, 1, 1))
app.physics.static_box((0, 0, 0), (12, 1, 4))
controller = PlatformerController(body, app.input, app.physics)

@app.update
def update(dt):
    controller.update(dt)
    player.position = body.position
    app.debug.box(body.box)

@app.render
def render():
    app.draw_scene()
    app.ui.begin()
    app.ui.panel(20, 20, 220, 90)
    app.ui.label(f"FPS {app.fps:.0f}")
    app.profiler.draw(app.ui, 260, 20)
    app.ui.end()

app.run()

Useful systems:

app.timer.after(2.0, spawn_enemy)
app.timer.every(1.0, add_score)

app.particles.emit((0, 2, 0), count=24)

player = app.scene.find("player")
enemies = app.scene.find_by_tag("enemy")

Loading Assets

from speepy3d import Material, Mesh, Model, Shader, Texture, Vec3

mesh = Mesh.from_gltf("assets/crate.gltf")
material = Material(
    shader=Shader.basic(),
    texture=Texture.from_file("assets/crate.png"),
    color=Vec3(1.0, 1.0, 1.0),
    roughness=0.5,
)
model = Model(mesh, material=material)

Mesh.from_gltf() supports .gltf JSON files with triangle meshes and external or embedded buffers. .glb, skinning and animation are not implemented yet.

Collision

from speepy3d import AABB, Vec3, intersects

player = AABB.from_center_size(Vec3(0, 1, 0), Vec3(1, 2, 1))
wall = AABB.from_center_size(Vec3(2, 1, 0), Vec3(1, 2, 4))

if intersects(player, wall):
    print("hit")

UI

from speepy3d import Button, Label, Menu, MenuItem, UIManager

ui = UIManager()
ui.add(Label(20, 20, "SCORE 0"))
ui.add(Button(20, 55, 140, 36, text="START", on_click=start_game))
ui.add(Menu(20, 100, 120, 32, title="GAME", items=[
    MenuItem("RESET", reset_game),
    MenuItem("QUIT", win.close),
]))

while not win.should_close():
    win.poll_events()
    ui.update(win)
    render_3d()
    ui.draw(win)
    win.swap_buffers()

Immediate-mode UI:

ui = UIManager(win)

while not win.should_close():
    win.poll_events()
    render_3d()

    ui.begin()
    ui.panel(20, 20, 250, 180)
    ui.label("Score: 10")
    if ui.button("Restart"):
        restart()
    ui.end()

    win.swap_buffers()

Sound

from speepy3d import Sound

Sound("jump.wav").play()

Sound currently uses Python's Windows winsound, so WAV is the practical target.

Build

python setup.py sdist bdist_wheel

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

speepy3d-0.8.1.tar.gz (50.8 kB view details)

Uploaded Source

Built Distribution

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

speepy3d-0.8.1-cp312-cp312-win_amd64.whl (58.1 kB view details)

Uploaded CPython 3.12Windows x86-64

File details

Details for the file speepy3d-0.8.1.tar.gz.

File metadata

  • Download URL: speepy3d-0.8.1.tar.gz
  • Upload date:
  • Size: 50.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for speepy3d-0.8.1.tar.gz
Algorithm Hash digest
SHA256 0447f50d64f53f4bbeb4810043f4d203620aaf40c7b85f33716f8df1b4dc6984
MD5 598dface647f47caed01256e0868c2d1
BLAKE2b-256 3883a9d36272bbad2eded983a535df97254e875ae0dae3039873d52d73721595

See more details on using hashes here.

File details

Details for the file speepy3d-0.8.1-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: speepy3d-0.8.1-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 58.1 kB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for speepy3d-0.8.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 1711c591b6b24e00208c2912ef414c7b5bd3338213243489ea7a516820912123
MD5 39f57f21a41f5aea75cf87a95d888ce0
BLAKE2b-256 5b016853f155379dd2d64c563f4c3eebd432a791f411d08e7ae39b544a3bb6da

See more details on using hashes here.

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