A game engine in the spirit of Godot, written in Common Lisp and scripted entirely in Common Lisp.
  • Common Lisp 100%
Find a file
dxtr 3cc4d246e8
All checks were successful
CI / test (push) Successful in 7m52s
Draw each canvas through its own transform, worked out in doubles
A canvas has a placement, a dmat3x2 from its draw function's coordinates
to its renderer's, which scene-2d sets each frame to its item's global
transform.  Its draw function records from a start made of the
placement's turn and stretch and of where the placement puts its origin
within a pixel; the rest of the placement is applied as it is drawn.  So
moving, turning or stretching a node draws nothing again, but what it
drew on the pixels, raster text and snapped shapes, when it moves off
them, and what it drew for the scale when it turns or stretches.

Each frame, each canvas's *canvas-transform*, the renderer's pixel
transform after its placement, is worked out in doubles and packed as
single-floats into slots of a uniform buffer the renderer keeps, one for
each painter the canvas draws with, which its bundle binds; the slots
that changed are written in one write.  A node a million units out,
under a view panned there, is drawn as precisely as one near the origin.

draw-canvases and prepare-canvases take their transform as a dmat3x2,
canvas-bounds gives a canvas's box in its own coordinates and takes a
dmat3x2, and clips are kept in doubles.  A control moved and resized at
once draws again, which moving alone no longer does.
2026-10-20 10:07:44 +02:00
.forgejo/workflows Run on SBCL only 2026-10-17 04:41:30 +02:00
2d Draw each canvas through its own transform, worked out in doubles 2026-10-20 10:07:44 +02:00
editor Keep 2D nodes, their view and ranges in double-floats 2026-10-20 09:26:42 +02:00
export Export one signal-pyssel-error from the core 2026-10-18 23:36:22 +02:00
font Export one signal-pyssel-error from the core 2026-10-18 23:36:22 +02:00
play Let a play's caller give the game's threads bindings 2026-10-19 08:21:33 +02:00
player Declare the full domain types in the project, play, export and player 2026-10-18 21:22:22 +02:00
program Keep the program's systems as it was built with them 2026-10-19 14:29:05 +02:00
project Stretch a window's content, as Godot's content scale 2026-10-20 01:43:24 +02:00
resource Keep 2D nodes, their view and ranges in double-floats 2026-10-20 09:26:42 +02:00
scene Keep 2D nodes, their view and ranges in double-floats 2026-10-20 09:26:42 +02:00
scene-2d Draw each canvas through its own transform, worked out in doubles 2026-10-20 10:07:44 +02:00
src Draw a render target only when what it shows changed 2026-10-20 03:57:05 +02:00
tests Draw each canvas through its own transform, worked out in doubles 2026-10-20 10:07:44 +02:00
ui Draw each canvas through its own transform, worked out in doubles 2026-10-20 10:07:44 +02:00
.gitattributes Keep LF line endings in every checkout 2026-10-16 15:49:29 +02:00
.gitignore Build the editor as a program, bin/pyssel 2026-10-18 00:04:28 +02:00
CLAUDE.md Draw canvas layers, as Godot's CanvasLayer 2026-10-20 01:14:38 +02:00
LICENSE Start Pyssel's repository 2026-10-10 17:11:07 +02:00
pyssel.asd Draw each canvas through its own transform, worked out in doubles 2026-10-20 10:07:44 +02:00
README.org Change values over time with tweens, as Godot's 2026-10-20 00:11:16 +02:00
stiftfile Move the pin on cl-textrender 2026-10-19 21:32:32 +02:00

Pyssel

A game engine in the spirit of Godot, written in Common Lisp and scripted entirely in Common Lisp. Pyssel is Swedish for crafting or tinkering.

(asdf:load-system :pyssel)

(defpackage #:triangle
  (:use #:cl #:clsl)
  (:local-nicknames (#:gfx #:cl-gfx)))
(in-package #:triangle)

(define-shader triangle-vertex :vertex ((position :vec2) (color :vec3))
  (values (vec4 position 0 1) color))

(define-shader triangle-fragment :fragment ((color :vec3))
  (vec4 color 1))

(define-program triangle
  "A triangle in its vertices' colours."
  :vertex triangle-vertex :fragment triangle-fragment)

(defvar *pipeline*)
(defvar *vertices*)

(defun setup (engine)
  (setf *pipeline* (pyssel:make-pipeline engine 'triangle)
        *vertices* (gfx:create-buffer (pyssel:engine-device engine)
                                      :size 60 :usage '(:vertex)))
  (gfx:write-buffer *vertices*
                    (make-array 15 :element-type 'single-float
                                   :initial-contents '( 0.0 -0.5  1.0 0.0 0.0
                                                       -0.5  0.5  0.0 1.0 0.0
                                                        0.5  0.5  0.0 0.0 1.0))))

(defun draw (engine pass)
  (declare (ignore engine))
  (pyssel:use-pipeline pass *pipeline*)
  (gfx:set-vertex-buffer pass 0 *vertices*)
  (gfx:draw pass 3))

(defvar *engine* (pyssel:start :title "Triangle" :setup 'setup :draw 'draw))

A window opens with a triangle in it, and the REPL stays free. Now redefine the fragment shader there:

(define-shader triangle-fragment :fragment ((color :vec3))
  (vec4 (- 1 color) 1))

The next frame draws the triangle in the inverted colours. Redefine draw and the next frame calls the new one. Close the window, or call (pyssel:stop *engine*), to stop.

Pyssel assembles small libraries that stand on their own: cl-wl and cl-vk bind Wayland and Vulkan, cl-windowing opens windows, cl-gfx draws with the GPU, clsl compiles shaders written in Lisp, cl-gmath does the math, cl-threads runs the threads, and the text libraries lay out and render text. The engine's own parts are ASDF systems named pyssel/... in this repository: pyssel is the core, pyssel/2d the 2D renderer, pyssel/resource resources, their files and signals, pyssel/font fonts as resources, pyssel/scene the scene tree, tweens and saved scenes, pyssel/scene-2d its 2D nodes, pyssel/ui the user interface's controls, containers and themes, pyssel/project projects and their settings, pyssel/play games played in your Lisp, pyssel/editor the editor, with its code editor, the Script screen, its REPL and its debugger, and pyssel/export, with pyssel/player, the player image exported games will start from.

Manual

The manual is the wiki. It starts with Getting Started, what Pyssel needs and its systems, then takes each in turn: the engines, 2D drawing, the scene tree, the user interface, projects, the editor, its Script screen, REPL and debugger.

Tests

stift test

The tests run engines offscreen on a Vulkan driver, such as Mesa's lavapipe (mesa-vulkan-drivers), and, where Wayland is, in a window on headless Weston (weston). They draw text in DejaVu Sans (fonts-dejavu-core); to use it from another location, set PYSSEL_TEST_FONT to its path.

Credit

pyssel/2d draws Slug text with shaders ported from Eric Lengyel's reference shaders (MIT License), through cl-slug. Software that uses it and is distributed must credit them; cl-slug's NOTICE has the text.

pyssel/ui ships two fonts under the SIL Open Font License 1.1: Inter, copyright The Inter Project Authors, and JetBrains Mono, copyright The JetBrains Mono Project Authors. Their licences are beside them in ui/fonts/, and go with them wherever they are distributed.

pyssel/scene's tweens ease with Robert Penner's easing equations, copyright 2001 Robert Penner, as Godot 4.5 has them, under a permissive licence whose notice is at the top of scene/easing.lisp and goes with them wherever they are distributed.

License

MIT. See LICENSE.