Metadata-Version: 2.4
Name: speepy3d
Version: 0.8.1
Summary: A Python 3D game framework powered by a C++ OpenGL backend.
Author: speepy3d contributors
License-Expression: MIT
Keywords: opengl,3d,graphics,window,cpp-extension
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: C++
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Graphics :: 3D Rendering
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Pillow>=10
Dynamic: license-file

# 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

```powershell
python -m pip install -e .
```

## Modern Renderer Example

```python
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

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

## Input

```python
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

```python
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:

```python
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

```python
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

```python
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

```python
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:

```python
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

```python
from speepy3d import Sound

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

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

## Build

```powershell
python setup.py sdist bdist_wheel
```
