Runespoor is a flexible, straight forward, multi-paradigm game engine that is built from the ground up with developer experience in mind.
Project description
Runespoor Python 3D Game Engine
A python 3D game engine written in c++ using SDL, Assimp, OpenGL(GLAD), and GLM.
To install:
pip install Runespoor
Please note:
-
Runespoor is still in development and only dev versions are available right now. Everything is subject to change, be fixed, and improve.
-
When building from source
pkg-config
must be installed. In addition to this,GLM
must be installed and present in your c/c++ package manager's include directory. Lastly include the flag--RUNEcompiler=<clang|gcc|msvc>
or--config-settings=\"--RUNEcompiler=<clang|gcc|msvc>\"
with your default compiler on build. Successful attempts to compile have been made on windows withvcpkg
as the package manager.
About:
Runespoor is a flexible, straight forward, multi-paradigm game engine that is built from the ground up with developer experience in mind. Utilizing a game loop system similar to pygame, you are able to abstract elements of your game away from the game loop as much as you'd like.
For a taste of the api check out the test file:
from renderer import (
Vec3, Camera, Mesh, Object, Window, EVENT_FLAG,
Material, Shader, ShaderType, EVENT_STATE, Quaternion
)
import math
from copy import copy
# The meshes used in this testfile are not provided with the library or source files.
dim = (1280, 720)
focal_length = 10000
camera = Camera(Vec3(0.0,0.0,0.0), Vec3(0.0,0.0,0.0), *dim, focal_length, math.radians(60))
window = Window("FBX Car Test", camera, *dim, False)
# Materials are equivalent to shader programs.
default_material = Material(
Shader.from_file("./default_vertex.glsl", ShaderType.VERTEX),
Shader.from_file("./default_fragment.glsl", ShaderType.FRAGMENT)
)
default_material.set_uniform("focal_length", focal_length, "i")
# "i" means the uniform is an integer type
car_meshes = Mesh.from_file("./meshes/fbx_car/svj_PACKED.fbx")
car = Object(car_meshes,
Vec3(0.0,-100.0,500), Vec3(0,0,0), Vec3(1,1,1), material=default_material)
car2 = Object(car_meshes,
Vec3(300,0,500), Vec3(10,3.57,23.2), material=default_material)
teapot = Object(Mesh.from_file("./meshes/teapot/teapot.obj"),
Vec3(-100,0,200), Vec3(0,0,0), material=default_material)
pirate_ship = Object(Mesh.from_file("./meshes/pirate_ship/pirate_ship.obj"),
Vec3(-100,-100,300), Vec3(0,10,0), material=default_material)
render_list = [
car,
car2,
teapot,
pirate_ship
]
window.lock_mouse(True)
vel_yaw = 0.0
vel = 0.0
frict = 0.1
accel = 5
cam_dist = 300.0
magic_turn_dampener = 4
mouse_sensitivity = 10
counter = 0
counter_speed = 1
while not window.event.check_flag(EVENT_FLAG.QUIT) and window.event.get_flag(EVENT_FLAG.KEY_ESCAPE) != EVENT_STATE.PRESSED:
# Use WASD keys.
if window.event.get_flag(EVENT_FLAG.KEY_d) == EVENT_STATE.PRESSED:
# ROTATE RIGHT
vel_yaw -= 0.3
if window.event.get_flag(EVENT_FLAG.KEY_a) == EVENT_STATE.PRESSED:
# ROTATE LEFT
vel_yaw += 0.3
if window.event.get_flag(EVENT_FLAG.KEY_s) == EVENT_STATE.PRESSED:
# BACKWARDS
vel -= accel
if window.event.get_flag(EVENT_FLAG.KEY_w) == EVENT_STATE.PRESSED:
# FORWARD
vel += accel
# apply a quaternion rotation arround the vector vec3(1,1,0)
teapot.rotation = Quaternion.from_axis_angle(Vec3(1,1,0), math.radians(counter))
# Clamp and rotate, then apply friction.
vel_yaw = min(max(vel_yaw, -100), 100) if abs(vel_yaw) > frict else 0
car.rotation.rotate_yaw(-vel_yaw/magic_turn_dampener * window.dt)
vel_yaw -= math.copysign(frict, vel_yaw)
# Clamp the velocity.
vel = min(max(vel, -1000), 1000) if abs(vel) > frict else 0
# Move the car forwards with its forwards vector with a magnitude of `vel` and apply friction
car.position += -car.rotation.forward * vel * window.dt # window.dt is deltatime
vel -= math.copysign(frict, vel)
# Rotate the camera
cam_rot = Quaternion.from_quat(camera.rotation)
yaw_rot = 0 # capture the yaw rot so we can apply it to the quat later in the case where the camera is clamped
if window.event.check_flag(EVENT_FLAG.MOUSE_MOTION):
cam_rot.rotate_yaw(yaw_rot := -math.radians(window.event.mouse.rel_x * mouse_sensitivity * window.dt))
cam_rot.rotate(cam_rot.right, math.radians(window.event.mouse.rel_y * mouse_sensitivity * window.dt))
# zoom in and out
if window.event.check_flag(EVENT_FLAG.MOUSE_WHEEL):
cam_dist -= window.event.mouse.wheel.y * 10
# apply/clamp, position, and camera rotation
if cam_rot.up.y >= 0.7:
# Position the camera behind the car based on its forward vector
camera.position = car.position - (cam_rot.forward * cam_dist)
camera.rotation = cam_rot
else:
camera.rotation.rotate_yaw(yaw_rot)
# Position the camera behind the car based on its forward vector
camera.position = car.position - (camera.rotation.forward * cam_dist)
# Re-render the scene.
window.update(render_list)
# This also refreshes window.current_event.
counter += counter_speed
Then when we run it, it looks something like this:
TODO:
-
Refactor MVP matrix into their respective entities.
-
Figure out better solution for multimesh assimp imports. (Current solution groups all meshes from the scene in a python list.)
-
Add
from_raw
constructor to mesh to make procedural meshes possible. -
Make render list managed by Window object.
-
Add Sprite object and Object2D object for 2D games/HUD.
-
Add window/fullscreen scaling.
-
Add Matrix datastructures for GLM.
Future Plans:
- Switch to Vulkan.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for Runespoor-1.0.0.dev13-cp311-cp311-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | bd3060c6e1cd5ec22368cc92961964f54ade2615136ce7f6b7b3a38c0a3441c0 |
|
MD5 | 1a8d7999db92e58379fa0523026f9054 |
|
BLAKE2b-256 | 8a4a63d0abcce1a756db2e2a90c6e0f3b3a4f862094a21bc8fd8e6a8d56cf790 |