Fully auto-generate Common Lisp bindings to Raylib (as well as Raymath, Rlgl and Raygui) using claw and cffi-object.
claw-raylib
and some of its dependencies are not available in Quicklisp currently.
You need to clone this repository and the following projects into the local-projects
folder of your Quicklisp installation path:
claw-raylib
does not ship any prebuilt binary files.
You need to ensure that Raylib, Raygui, and a compatible C compiler are installed on your system to manually build the adapters required by claw
.
Tip: You can clone the prebuild branch to skip steps 1 and 2.
- Clone, build and install libresect, raylib and raygui (optional).
- Generate the bindings using Clozure CL.
(ql:quickload :claw-raylib/gen) (pushnew :claw-regen-adapter *features*) (cffi:load-foreign-library #P"/path/to/libresect.so") (claw:load-wrapper :raylib) (claw:load-wrapper :raygui) (claw:load-wrapper :rlgl)
Notes for Windows:
Since
windows.h
contains many symbols that conflict with symbols inraylib.h
and also includes the notoriousnear
andfar
keywords, if you are developing on Windows or deploying an application for Windows, you need to manually perform the following operations on the generatedlibraylib-adapter.i686-pc-windows-gnu.c
andlibraylib-adapter.x86_64-pc-windows-gnu.c
(whose parent path can be retrieved by evaluating(merge-pathnames #P"lib/" (asdf:component-pathname (asdf:find-system '#:claw-raylib)))
in the Lisp REPL):- Replace the line like:
#include <windows.h>
with:
#include <minwindef.h> #define GetModuleHandle GetModuleHandleA HMODULE GetModuleHandleA(LPCTSTR name); FARPROC GetProcAddress( HMODULE hModule, LPCSTR lpProcName );
- Since
minwindef.h
still contains thenear
andfar
keywords, you also need to replace allnear
andfar
identifiers with identifiers like_near
and_far
. You can find them with RegExp\(near\|far\)\(,\|)\)
and replace them with_\1\2
in Emacs.
- Replace the line like:
- Compile the adapters.
(let ((arch "x86_64-pc-linux-gnu") (path (merge-pathnames #P"lib/" (asdf:component-pathname (asdf:find-system '#:claw-raylib))))) (dolist (lib '("raylib" "rlgl" "raygui")) (uiop:run-program (list "gcc" "-O3" "-fPIC" "-shared" "-o" (namestring (merge-pathnames (format nil "lib~A-adapter.so" lib) path)) (namestring (merge-pathnames (format nil "lib~A-adapter.~A.c" lib arch) path))))))
- Load the system.
(ql:quickload :claw-raylib)
Notes for SBCL:
During this process, SBCL may consume a significant amount of memory, potentially leading to heap exhaustion. You may need to add
--dynamic-space-size 4096
to the SBCL command-line arguments before the first load ofclaw-raylib
.
Raylib, Rlgl, Raymath, and Raygui’s low-level and high-level APIs are automatically generated using claw
and cffi-object
,
along with some tricks during the build process. You can use any released version or even the Git version of Raylib without changing the claw-raylib
version,
as claw-raylib
can automatically generate the both levels of APIs. This also means that all APIs of these libraries are available, just like in C.
(let ((camera (raylib:make-camera-3d :position (raylib:make-vector3 :x 10.0 :y 10.0 :z 10.0)
:target (raylib:vector3-zero)
:up (raylib:make-vector3 :x 0.0 :y 1.0 :z 0.0)
:fovy 33.3 :projection #.(cffi:foreign-enum-value 'raylib:camera-projection :perspective)))
(model (raylib:load-model "/path/to/model"))
(position (raylib:vector3-zero)))
(raylib:with-window ("Simple Model Viewer" (1280 720))
(loop :until (raylib:window-should-close)
:do (raylib:with-drawing
(raylib:update-camera camera #.(cffi:foreign-enum-value 'raylib:camera-mode :free))
(raylib:with-mode-3d camera
(raylib:clear-background raylib:+raywhite+)
(raylib:draw-grid 100 1.0)
(rlgl:disable-backface-culling)
(raylib:draw-model model position 1.0 raylib:+white+)
(rlgl:enable-backface-culling))))))
A complete example of using claw-raylib
to rewrite the control test suite from Raygui is available.
Thanks to the adapters generated by claw
, claw-raylib
does not use cffi-libffi
and incurs no expensive performance overhead (according to this post)
or heavy GC pressure when calling functions that accept C structures as parameters (which is the case for most functions in Raylib).
;;; The overhead of FFI calls is no longer a performance bottleneck for the system.
;; Self Total Cumul
;; Nr Count % Count % Count % Calls Function
;; ------------------------------------------------------------------------
;; 1 261 32.2 261 32.2 261 32.2 - foreign function rlVertex3f
;; 2 109 13.4 450 55.5 370 45.6 - foreign function DrawTexturePro
;; 3 43 5.3 56 6.9 413 50.9 - (LAMBDA (&OPTIONAL POSITION ORIGIN SCALE ROTATION TINT) :IN TILED-LAYER-RENDERER)
;; 4 31 3.8 277 34.2 444 54.7 - foreign function rlVertex2f
;; 5 23 2.8 23 2.8 467 57.6 - foreign function rlTexCoord2f
;; 6 18 2.2 18 2.2 485 59.8 - foreign function __sched_yield
;; 7 16 2.0 19 2.3 501 61.8 - foreign function rlSetTexture
;; 8 15 1.8 495 61.0 516 63.6 - foreign function __claw_DrawTexturePro
;; 9 14 1.7 14 1.7 530 65.4 - (LAMBDA (POSITION SCALE) :IN TILED-LAYER-RENDERER)
;; 10 11 1.4 11 1.4 541 66.7 - foreign function rlBegin
claw-raylib
utilizes cffi-object
to automatically wrap Raylib’s types, allowing you to completely disregard memory concerns.
All types from Raylib can be seamlessly integrated into CLOS, and the API style remains highly similar to Common Lisp,
and for all structure parameters in FFI functions, cffi-object
objects are passed by default instead of raw pointers,
greatly reducing the disconnect often associated with cross-language interoperations.
(raylib:vector2-normalize
(raylib:vector2-add
(raylib:make-vector2 :x 1.0 :y 2.0)
(raylib:vector2-one)))
;; => #<VECTOR2 :X 0.5547002 :Y 0.8320503 @0x00007FF59C000D70>
(raylib:fade (raylib:color-brightness (raylib:get-color #xCE42EFFF) -0.5) 0.5)
;; => #<COLOR :R 103 :G 33 :B 119 :A 127 @0x00007FF59C000E50>
(defgeneric vector-add (v1 v2))
(defmethod vector-add ((v1 raylib:vector2) (v2 raylib:vector2))
(raylib:vector2-add v1 v2))
(defmethod vector-add ((v1 raylib:vector3) (v2 raylib:vector3))
(raylib:vector3-add v1 v2))
(defmethod vector-add ((v1 raylib:vector4) (v2 raylib:vector4))
(raylib:quaternion-add v1 v2))
(vector-add (raylib:vector3-one) (raylib:vector3-one))
;; => #<VECTOR3 :X 2.0 :Y 2.0 :Z 2.0 @0x00007FF59C000ED0>
In performance-intensive scenarios, directly using the low-level functions exposed by claw-raylib
(whose names are prefixed with %
)
in conjunction with cffi-ops for GC-free programming is a better choice. Modules written using this approach can achieve performance levels close to that of C.
(use-package :cffi-ops)
(defun camera-3d-normalize (camera)
(declare (optimize (speed 3)
(debug 0)
(safety 0)))
(clet* ((camera (cthe (:pointer (:struct raylib:camera-3d)) (& camera)))
(up (& (-> camera raylib:up)))
(right up)
(look (foreign-alloca '(:struct raylib:vector3)))) ; Stack memory allocation
(raylib:%vector3-subtract look (& (-> camera raylib:target)) (& (-> camera raylib:position)))
(raylib:%vector3-cross-product right look up)
(raylib:%vector3-cross-product up right look)
(raylib:%vector3-normalize up up))
camera)
See the examples directory. To run all examples, eval this in your REPL:
(ql:quickload :claw-raylib/examples)
(do-external-symbols (symbol :claw-raylib.examples)
(funcall symbol))
Opening a PR for contributions is welcome. Encountering any problem, feel free to open an issue.
- cl-raylib: A manually written Raylib binding that uses 3d-matrics and 3d-vectors as the math library.
It offers high usability but is not suitable for performance-intensive scenarios due to its use of cffi-libffi.
For example, it may not be suitable for a Tiled map renderer that requires pretty frequent calls to
DrawTexturePro
. - claylib: A game framework based on Raylib that provides practical features for game development,
such as scenes and interactive programming. It also utilizes
claw
to automatically generate low-level bindings for Raylib and then manually writes the exported high-level APIs.claw-raylib
, on the other hand, is just a Lispy Raylib wrapper that does not offer any functionality beyond Raylib itself. It is suitable for cases where you only want to use Raylib or develop your own game engine/framework based on Raylib in Common Lisp.
- eon: An easy-to-use but flexible game framework for Common Lisp.
- cl-universal-tween-engine: Common Lisp port of the Universal Tween Engine, whose demo is made by
claw-raylib
.